Šķîþ ţö ḿàîñ çöñţéñţ

Ŕüñţîḿé ṽš. îñļîñé ḿöđé

Ţĥé þļüĝîñ ĥàš ţŵö þŕöđüçţîöñ ḿöđéš. Þîçķ ƃàšéđ öñ ĥöŵ ýöü šĥîþ ýöüŕ àþþ.

Ŕüñţîḿé ḿöđé

neokapi({ mode: "runtime" });

Öñé ƃüñđļé. Ţŕàñšļàţîöñš ļîṽé îñ þéŕ-ļöçàļé ĴŠÖÑ ƒîļéš ļöàđéđ àţ ŕüñţîḿé ṽîà fetch.

Éṽéŕý ţŕàñšļàţàƃļé ĴŠẊ šîţé ĝéţš ŕéŵŕîţţéñ ţö à __t(hash, fallback, params) çàļļ:

// Source
<h1>Welcome</h1>

// Output (runtime mode)
<h1>{__t("aB3", "Welcome")}</h1>

Ţĥé ŕüñţîḿé îš ~2 ķƂ ĝžîþþéđ; îţ ĥöļđš ţĥé àçţîṽé đîçţ àñđ à šüƃšçŕîƃéŕ šéţ. Ŵĥéñ loadTranslations(locale, url) ŕéšöļṽéš, éṽéŕý šüƃšçŕîƃéđ çöḿþöñéñţ ŕé-ŕéñđéŕš.

Ŵĥéñ ŕüñţîḿé ḿöđé ƒîţš

  • Ýöü šĥîþ à šîñĝļé ĴŠ ƃüñđļé ţö à ÇĐÑ àñđ ƒļîþ ļöçàļéš ƃàšéđ öñ üšéŕ þŕéƒéŕéñçé.
  • Ýöü ŵàñţ ţö àđđ ñéŵ ļöçàļéš ŵîţĥöüţ ŕé-đéþļöýîñĝ ýöüŕ ĴŠ.
  • Ýöü ĥàṽé ḿöŕé ţĥàñ à ĥàñđƒüļ öƒ ļöçàļéš; ţĥé þéŕ-ļöçàļé ĴŠÖÑ îš à šḿàļļ đöŵñļöàđ çöḿþàŕéđ ţö ýöüŕ ĴŠ ƃüñđļé.
  • Ýöü çàŕé àƃöüţ ĥöţ-šŵàþþîñĝ ļöçàļé îñ-þàĝé (ļàñĝüàĝé þîçķéŕ, À/Ƃ).

Ļöàđ öñçé, šüƃšçŕîƃé ţö çĥàñĝéš

import { loadTranslations, setTranslations, useNeokapi } from "@neokapi/i18n-react/runtime";

async function bootstrap() {
const locale = navigator.language.split("-")[0];
if (locale !== "en") {
await loadTranslations(locale, `/translations/${locale}.json`).catch(() => {});
}
ReactDOM.createRoot(root).render(<App />);
}

// Somewhere in the tree that should re-render on locale change
function AppRoot() {
useNeokapi(); // subscribe
return <Routes />;
}

useNeokapi() šüƃšçŕîƃéš ţö ţĥé ţŕàñšļàţîöñ šţöŕé ṽîà useSyncExternalStore. Ŵĥéñ ţĥé đîçţ üþđàţéš, àñý çöḿþöñéñţ ţĥàţ çàļļéđ ţĥé ĥööķ ŕé-ŕéñđéŕš, àñđ ţĥé çàšçàđé ţàķéš çàŕé öƒ ţĥé ŕéšţ.

Ƒöŕ à ļöçàļé šŵîţçĥéŕ ÜÎ: çàļļ loadTranslations öŕ setTranslations("en", {}) öñ çĥàñĝé, àñđ ḿàķé šüŕé useNeokapi() îš çàļļéđ ĥîĝĥ éñöüĝĥ îñ ţĥé ţŕéé ƒöŕ ţĥé ŵĥöļé ṽîšîƃļé šüŕƒàçé ţö ŕé-ŕéñđéŕ.

΃ ýöü'đ ŕàţĥéŕ ñöţ ţĥîñķ àƃöüţ ĥöŵ ĥîĝĥ îš ĥîĝĥ éñöüĝĥ, ŵŕàþ ţĥé ţŕéé îñ <NeokapiProvider>. Îţ šüƃšçŕîƃéš öñ ýöüŕ ƃéĥàļƒ àñđ ŕé-ķéýš îţš çĥîļđŕéñ öñ éṽéŕý ļöçàļé çĥàñĝé, šö ţĥé ŵĥöļé šüƃţŕéé ŕéḿöüñţš àñđ ŕé-ŕéàđš ţĥé đîçţ, ŵîţĥ ñö ḿîššéđ çöŕñéŕš:

import { NeokapiProvider } from "@neokapi/i18n-react/runtime";

ReactDOM.createRoot(root).render(
<NeokapiProvider>
<App />
</NeokapiProvider>,
);

Ţĥé ţŕàđé-öƒƒ îš éẋàçţļý ŵĥàţ îţ šöüñđš ļîķé: à ŕéḿöüñţ đîšçàŕđš çöḿþöñéñţ šţàţé ƃéļöŵ îţ. Ţĥàţ'š üšüàļļý ŕîĝĥţ öñ à ļöçàļé šŵîţçĥ àñđ üšüàļļý ŵŕöñĝ ḿîđ-ƒöŕḿ. useNeokapi() îš ţĥé šüŕĝîçàļ öþţîöñ; NeokapiProvider îš ţĥé šàƒé öñé.

Ƒàļļƃàçķ çĥàîñ

loadTranslations ţàķéš à ļîšţ öƒ ÜŔĻš àš ŵéļļ àš à šîñĝļé öñé. Ţĥé ļîšţ îš à ƒàļļƃàçķ çĥàîñ: ƒàļļƃàçķš ƒîŕšţ, þŕîḿàŕý ļàšţ:

await loadTranslations("pt-BR", ["/translations/pt.json", "/translations/pt-BR.json"]);

Éṽéŕýţĥîñĝ îš ḿéŕĝéđ îñţö öñé đîçţ, ŵîţĥ ļàţéŕ éñţŕîéš öṽéŕŕîđîñĝ éàŕļîéŕ öñéš, šö pt-BR ŵîñš ŵĥéŕé îţ ĥàš à šţŕîñĝ, àñđ pt ƒîļļš ţĥé ŕéšţ. À ƒàļļƃàçķ ţĥàţ ƒàîļš ţö ļöàđ îš ţöļéŕàţéđ (ýöü ĝéţ ţĥé šţŕîñĝš ýöü çöüļđ ƒéţçĥ); à ƒàîļîñĝ þŕîḿàŕý ŕéĵéçţš, ƃéçàüšé ţĥàţ'š à ŕéàļ éŕŕöŕ ýöü ŵàñţ ţö šéé.

Ƃöţĥ àļšö þüšĥ ţĥé ñéŵ ļöçàļé öñţö <html lang> àñđ <html dir> àüţöḿàţîçàļļý, ĥàñđý ƒöŕ šçŕééñ ŕéàđéŕš, ƒöñţš, ĥýþĥéñàţîöñ, àñđ ŔŢĻ šüþþöŕţ. Öþţ öüţ ŵîţĥ { syncDocumentLocale: false } îƒ ýöüŕ àþþ öŵñš ţĥöšé àţţŕîƃüţéš. Đéţàîļš: Çöñƒîĝüŕàţîöñ → ĤŢḾĻ lang àñđ dir.

Ļàžý ļöàđîñĝ þéŕ ŕöüţé (çöđé šþļîţţîñĝ)

Ƒöŕ ļàŕĝéŕ àþþš, ţĥé šîñĝļé-çàţàļöĝ-þéŕ-ļöçàļé ḿöđéļ đöŵñļöàđš éṽéŕý šţŕîñĝ éṽéñ ƒöŕ ŕöüţéš ţĥé üšéŕ ñéṽéŕ ṽîšîţš. Ţĥé þļüĝîñ + ŕüñţîḿé çàñ šþļîţ ţŕàñšļàţîöñš àļöñĝ ţĥé šàḿé ļîñéš ţĥé ƃüñđļéŕ šþļîţš çöđé:

  1. Îñ ŕüñţîḿé ḿöđé, ţĥé þļüĝîñ éḿîţš translations-manifest.json ñéẋţ ţö ýöüŕ ĴŠ çĥüñķš, à {chunkName: hashes[]} ḿàþ öƒ ŵĥîçĥ šţŕîñĝš éàçĥ çĥüñķ ñééđš. Ṽîţé, Ŕöļļüþ, ŵéƃþàçķ, Ŕšþàçķ, àñđ éšƃüîļđ àļļ éḿîţ îţ (éšƃüîļđ ñééđš metafile: true).
  2. neokapi-i18n split šļîçéš éàçĥ ḿàšţéŕ {locale}.json îñţö þéŕ-çĥüñķ šüƃšéţš ({locale}/{chunkName}.json), đüþļîçàţîñĝ šţŕîñĝš šĥàŕéđ àçŕöšš çĥüñķš šö éàçĥ ƒîļé îš îñđéþéñđéñţļý ļöàđàƃļé.
  3. Ţĥé ŕüñţîḿé'š loadTranslationChunk(locale, url) ƒéţçĥéš öñé šüƃšéţ àñđ ḿéŕĝéš îţ îñţö ţĥé àçţîṽé đîçţ. Çöñçüŕŕéñţ ŕéǫüéšţš ƒöŕ ţĥé šàḿé (locale, url) þàîŕ šĥàŕé à šîñĝļé ƒéţçĥ.

Ŵîŕé îţ îñţö à Ŕéàçţ Ŕöüţéŕ ļàžý ŕöüţé:

import { loadTranslationChunk } from "@neokapi/i18n-react/runtime";

const routes = [
{
path: "/settings",
lazy: async () => {
const [mod] = await Promise.all([
import("./SettingsPage"),
loadTranslationChunk(locale, `/translations/${locale}/SettingsPage.json`),
]);
return { Component: mod.default };
},
},
];

Ƃüîļđ þîþéļîñé:

vite build # emits dist/translations-manifest.json
neokapi-i18n compile i18n/ --out public/translations
neokapi-i18n split \
--manifest dist/translations-manifest.json \
--locales public/translations \
--out dist/translations

Ḿîššîñĝ ĥàšĥéš ƒàļļ ƃàçķ ţö ţĥé šöüŕçé ţéẋţ ƃàķéđ îñţö éàçĥ __t / __tx çàļļ àţ ƃüîļđ ţîḿé, šö à ļàţé-àŕŕîṽîñĝ çĥüñķ îš ñéṽéŕ ƒàţàļ. Üšéŕš šéé Éñĝļîšĥ ƒöŕ ~100ḿš ŵĥîļé ţĥé çĥüñķ šţŕéàḿš îñ, ŕàţĥéŕ ţĥàñ à ƃŕöķéñ ŕéñđéŕ.

΃ merge: true îš þàššéđ ţö setTranslations öŕ loadTranslations, ţĥé îñçöḿîñĝ éñţŕîéš ÖŔ îñţö ţĥé éẋîšţîñĝ đîçţ îñšţéàđ öƒ ŕéþļàçîñĝ îţ. loadTranslationChunk üšéš ţĥîš îñţéŕñàļļý. Šŵîţçĥîñĝ ļöçàļé (ŵîţĥöüţ merge) đŕöþš àñý îñ-ƒļîĝĥţ çĥüñķ ļöàđš ƒöŕ ţĥé þŕéṽîöüš ļöçàļé šö ţĥéîŕ þàýļöàđš çàñ'ţ þöîšöñ ţĥé ñéŵ đîçţ.

Ŕüñţîḿé þšéüđö-ţŕàñšļàţîöñ

Ŕüñţîḿé ḿöđé çàñ àþþļý þšéüđö-ţŕàñšļàţîöñ öñ ţĥé ƒļý, ŵîţĥ ñö ƃüîļđ šţéþ àñđ ñö çàţàļöĝ; üšéƒüļ ƒöŕ đéṽ éŕĝöñöḿîçš, ļàýöüţ çĥéçķš, àñđ đéƃüĝĝîñĝ ŵĥîçĥ šţŕîñĝš ƒļöŵ ţĥŕöüĝĥ ţĥé ţŕàñšļàţîöñ šýšţéḿ:

import { setPseudoMode } from "@neokapi/i18n-react/runtime/pseudo";

// Turn on with defaults (▒-wrapped, accented)
setPseudoMode({});

// Tune
setPseudoMode({
prefix: "« ",
suffix: " »",
expansion: 30, // +30% padding to test layout
alphabet: "accented", // ASCII → accented variants (the default)
});

// Off
setPseudoMode(null);

Ţĥé ţŕàñšƒöŕḿ šţàçķš öñ ţöþ öƒ ŵĥàţéṽéŕ'š îñ ţĥé ŕüñţîḿé đîçţ, šö ýöü çàñ ļöàđ à ŕéàļ Ƒŕéñçĥ çàţàļöĝ àñđ ŢĤÉÑ ƒļîþ þšéüđö öñ ţö šéé ŵĥàţ Ƒŕéñçĥ ļööķš ļîķé àţ +30% ļéñĝţĥ, ŵîţĥ ḿàŕķéŕš šĥöŵîñĝ ŵĥîçĥ šţŕîñĝš ĝöţ ţŕàñšļàţéđ ṽš. ŵĥîçĥ ƒéļļ ţĥŕöüĝĥ ţö šöüŕçé. {param} / {=m0} ţöķéñš àŕé þŕéšéŕṽéđ ṽéŕƃàţîḿ šö þàŕàḿ šüƃšţîţüţîöñ šţîļļ ŵöŕķš.

Ţéẋţ îñšîđé à <code>, à <kbd>, à <samp> öŕ à <var> çöḿéš ţĥŕöüĝĥ àš ţĥé àüţĥöŕ ŵŕöţé îţ, ţĥé šàḿé ƃýţéš à kapi pseudo-translate çàţàļöĝ çàŕŕîéš. Run <code>kapi check --ship</code> first. ŕéàđš àš àççéñţéđ þŕöšé àŕöüñđ à çöḿḿàñđ ţĥàţ šţîļļ ŕüñš. À translate="yes" öñ ţĥé šþàñ çļàîḿš îţš ţéẋţ ƃàçķ ƒöŕ ţĥé ţŕàñšƒöŕḿ. Þàđđîñĝ šķîþš ţĥöšé šþàñš ţöö: à çöḿḿàñđ îš ñöţ ĝöîñĝ ţö ĝŕöŵ îñ à ŕéàļ ţŕàñšļàţîöñ, šö îţ îš ļéƒţ öüţ öƒ ţĥé éẋþàñšîöñ çöüñţ.

Ŵöŕķš ŵîţĥöüţ à çàţàļöĝ. Ţĥé šöüŕçé šţŕîñĝ ļàñđš îñ ţĥé __t / __tx çàļļ àš ţĥé fallback àŕĝüḿéñţ àţ ƃüîļđ ţîḿé. Ŵĥéñ ţĥé đîçţ îš éḿþţý ţĥé ŕüñţîḿé üšéš ţĥé ƒàļļƃàçķ, àñđ þšéüđö ţŕàñšƒöŕḿš îţ. Éđîţ <h1>Welcome</h1> → šàṽé → ĤḾŔ ŕéþļàçéš ţĥé ḿöđüļé → Ŕéàçţ ŕé-ŕéñđéŕš → "▒ Ŵéļçöḿé ▒". Ñö éẋţŕàçţ šţéþ, ñö çöḿþîļé šţéþ, ñö ŕéƃüîļđ; ĵüšţ ýöüŕ šöüŕçé ţéẋţ ƒļöŵîñĝ ţĥŕöüĝĥ ţĥé ţŕàñšƒöŕḿ. À þļàîñ neokapi({ mode: "runtime" }) îñ vite.config.ts îš ţĥé öñļý þŕéŕéǫüîšîţé; ŵîţĥöüţ ŕüñţîḿé ḿöđé ţĥé þļüĝîñ ñö-öþš àñđ ţĥéŕé'š ñö __t ŵŕàþþéŕ ƒöŕ þšéüđö ţö ĥööķ îñţö.

Ţĥé þàñéļ ƃéļöŵ ŕüñš ţĥé ŕéàļ neokapi-î18ñ ŕüñţîḿé îñ ýöüŕ ƃŕöŵšéŕ, ŵîţĥ ñö çàţàļöĝ ļöàđéđ. Ţöĝĝļé þšéüđö ḿöđé àñđ ţĥé šàḿé šţŕîñĝš ƒļîþ ţö àççéñţéđ, éẋþàñđéđ ţéẋţ; {name} šţàýš ļîţéŕàļ ƃéçàüšé {param} ţöķéñš àŕé þŕéšéŕṽéđ ţĥŕöüĝĥ ţĥé ţŕàñšƒöŕḿ:

΃ ýöü ŵàñţ þšéüđö ţö ƃé ţĥé đéƒàüļţ îñ đéṽ, ŵîŕé îţ àţ ţĥé ţöþ öƒ main.tsx ĝüàŕđéđ öñ import.meta.env.DEV, ţĥéñ ķééþ ţĥé đéṽ çöñšöļé ĥàñđļé àṽàîļàƃļé ƒöŕ ţüñîñĝ:

import { setPseudoMode } from "@neokapi/i18n-react/runtime/pseudo";

if (import.meta.env.DEV) {
setPseudoMode({ expansion: 30 });
// @ts-expect-error: dev-only global so you can re-tune from the console
window.setPseudoMode = setPseudoMode;
}

Ţĥé þšéüđö ḿöđüļé ļîṽéš àţ à šéþàŕàţé šüƃþàţĥ (@neokapi/i18n-react/runtime/pseudo) šö îḿþöŕţîñĝ îţ îš öþţ-îñ; ţĥé ḿàîñ ŕüñţîḿé šţàýš ~2 ķƂ. Îñţéŕñàļļý îţ üšéš setStringTransform, à ĝéñéŕàļ þöšţ-ļööķüþ ĥööķ àļšö éẋþöŕţéđ ƒŕöḿ ţĥé ḿàîñ ŕüñţîḿé ƒöŕ çüšţöḿ ţŕàñšƒöŕḿš (đéƃüĝ ḿàŕķéŕš, ļéţţéŕ-šþàçîñĝ àüđîţš, éţç.).

À çüšţöḿ ţŕàñšƒöŕḿ ţàķéš ţĥé ḿéššàĝé ţéẋţ àñđ à çöñţéẋţ. Ţĥé çöñţéẋţ çàŕŕîéš markers, öñé éñţŕý þéŕ îñļîñé éļéḿéñţ ţĥàţ àñšŵéŕš ƒöŕ ţĥé ţéẋţ îñšîđé îţ: "no" ƒöŕ à šþàñ ĥöļđîñĝ à çöḿḿàñđ, à ķéý öŕ àñ îđéñţîƒîéŕ, "yes" ŵĥéŕé ţĥé šöüŕçé çļàîḿéđ îţ ƃàçķ. protectionMask(text, markers), éẋþöŕţéđ ƒŕöḿ ţĥé ŕüñţîḿé, ţüŕñš ţĥöšé àñšŵéŕš îñţö à þéŕ-çĥàŕàçţéŕ àŕŕàý šö à ţŕàñšƒöŕḿ çàñ ŕéŵŕîţé ţĥé þŕöšé àñđ ļéàṽé ţĥé ŕéšţ:

import { setStringTransform, protectionMask } from "@neokapi/i18n-react/runtime";

setStringTransform((text, context) => {
const guarded = protectionMask(text, context?.markers);
return [...text].map((ch, i) => (guarded?.[i] ? ch : ch.toUpperCase())).join("");
});

Îñļîñé ḿöđé

neokapi({
mode: "inline",
locale: "fr",
translationsDir: "./translations",
});

Öñé ƃüñđļé þéŕ ļöçàļé. Éṽéŕý ĴŠẊ ţéẋţ ñöđé îš ŕéþļàçéđ àţ ƃüîļđ ţîḿé ŵîţĥ ţĥé ţŕàñšļàţéđ ļîţéŕàļ. Ñö ŕüñţîḿé đîçţ ļööķüþ, ñö šüƃšçŕîþţîöñ, ñö loadTranslations():

// Source
<h1>Welcome</h1>

// Output (inline mode, locale=fr)
<h1>Bienvenue</h1>

Ŕîçĥ ţéẋţ îš ŕéƃüîļţ àš ĴŠẊ, šö à ţŕàñšļàţöŕ îš ƒŕéé ţö ḿöṽé ţĥé ļîñķ, àñđ ţĥé ḿàŕķüþ ƒöļļöŵš:

// Source
<p>Read the <a href="/docs">docs</a> first.</p>

// Output (inline mode, locale=de): the link moved, the href stayed
<p>Lies zuerst die <a href="/docs">Dokumentation</a>.</p>

Ţĥé ÎÇÜ éẋçéþţîöñ

Îñļîñé ḿöđé îš žéŕö-ŕüñţîḿé ŵîţĥ öñé đöçüḿéñţéđ éẋçéþţîöñ: <Plural>, <Select>, àñđ àñý šţŕîñĝ à ţŕàñšļàţöŕ ĝàṽé àñ ÎÇÜ ƒöŕḿàţ ţö ķééþ à ŕüñţîḿé çàļļ.

Ţĥé ŕéàšöñ îš šţŕüçţüŕàļ. À þļüŕàļ'š þîṽöţ (ţĥé çöüñţ) îš öñļý ķñöŵñ ŵĥéñ ţĥé çöḿþöñéñţ ŕéñđéŕš, šö ñö ƃüîļđ šţéþ çàñ þîçķ ţĥé ƒöŕḿ. Îñļîñé ḿöđé đöéš ţĥé ĥàļƒ îţ çàñ: ţĥé ţŕàñšļàţéđ ÎÇÜ ţéḿþļàţé îš ƃàķéđ îñţö ţĥé çàļļ àš îţš ƒàļļƃàçķ, šö ţĥéŕé'š ñö đîçţîöñàŕý ţö ƒéţçĥ àñđ ñö ñéţŵöŕķ öñ ţĥé çŕîţîçàļ þàţĥ. Ŵĥàţ šţàýš îš ţĥé ~2 ķƂ ÎÇÜ ŕéšöļṽéŕ.

΃ ýöü ĥàṽé ñö þļüŕàļš àñđ ñö ÎÇÜ ƒöŕḿàţš, îñļîñé ḿöđé šĥîþš ñöţĥîñĝ. ΃ ýöü ĥàṽé öñé, ýöü šĥîþ ţĥé ŕéšöļṽéŕ. Îţ îš ñéṽéŕ îñṽàļîđ ĴŠẊ àñđ ñéṽéŕ à šîļéñţ ƒàļļƃàçķ ţö šöüŕçé.

Ŵĥéñ îñļîñé ḿöđé ƒîţš

  • Ýöü šĥîþ þéŕ-ļöçàļé ƃüîļđš (www-fr.example.com, www-de.example.com).
  • Ýöü çàŕé àƃöüţ ƒîŕšţ-þàîñţ ƃüñđļé šîžé: ñö ŕüñţîḿé, ñö đîçţ ƒéţçĥ.
  • Ýöü ĥàṽé ŠŠŔ / ŠŠĜ àñđ ŵàñţ þŕé-ŕéñđéŕéđ ĤŢḾĻ îñ ţĥé ţàŕĝéţ ļöçàļé.
  • Ýöüŕ ļöçàļé šéţ îš šḿàļļ àñđ ŕàŕéļý çĥàñĝéš.

Ţýþîçàļ îñļîñé šéţüþ

# Build one app per locale in CI
for locale in en fr de ja; do
LOCALE=$locale vite build --outDir dist/$locale
done
vite.config.ts
export default defineConfig({
plugins: [
neokapi({
mode: "inline",
locale: process.env.LOCALE ?? "en",
translationsDir: "./translations",
strict: "error", // fail the build on missing translations
}),
react(),
],
});

strict: "error" ţüŕñš ḿîššîñĝ ţŕàñšļàţîöñš îñţö à ƃüîļđ éŕŕöŕ: ñöţĥîñĝ üñţŕàñšļàţéđ šĥîþš. Ƒöŕ ḿàŕķéţ-ƃý-ḿàŕķéţ ŕöļļöüţš ýöü'đ ķééþ strict: "warn" (đéƒàüļţ) đüŕîñĝ đéṽéļöþḿéñţ, àñđ ƒļîþ ţö "error" ƃéƒöŕé ţĥé ƒîñàļ ŕéļéàšé ƃüîļđ.

Ƃüîļđ-ţîḿé ƒàļļƃàçķ çĥàîñ

Îñļîñé ḿöđé ĥàš îţš öŵñ ƒàļļƃàçķ çĥàîñ, ŕéšöļṽéđ àţ ƃüîļđ ŕàţĥéŕ ţĥàñ àţ ļöàđ (ţĥé ŕüñţîḿé-ḿöđé éǫüîṽàļéñţ îš loadTranslations ŵîţĥ à ÜŔĻ ļîšţ). Ŵĥéñ à ţŕàñšļàţîöñ îš ḿîššîñĝ îñ ţĥé þŕîḿàŕý ļöçàļé, îţ çöñšüļţš ƒàļļƃàçķ ļöçàļéš ƃéƒöŕé ĝîṽîñĝ üþ:

neokapi({
mode: "inline",
locale: "de-AT",
fallbackLocales: ["de", "en"],
});

Ƒöŕ ţĥé Àüšţŕîàñ-Ĝéŕḿàñ ƃüîļđ, à ḿîššîñĝ de-AT éñţŕý ƒàļļš ƃàçķ ţö de, ţĥéñ ţö en, ţĥéñ ţö ţĥé šöüŕçé ţéẋţ.

Ĥýƃŕîđ: îñļîñé çöŕé, ŕüñţîḿé öþţîöñàļ ļöçàļéš

À çöḿḿöñ þàţţéŕñ îš ţö îñļîñé ţĥé þŕîḿàŕý ļöçàļé àţ ƃüîļđ ţîḿé (šö ţĥé đéƒàüļţ ḿàŕķéţ ļöàđš îñšţàñţļý) àñđ ḿàķé šéçöñđàŕý ļöçàļéš àṽàîļàƃļé ṽîà ţĥé ŕüñţîḿé đîçţ:

// Primary build
neokapi({ mode: "inline", locale: "en" });

// Secondary locales still get an OTA dict loaded via loadTranslations
// when the user switches, compiled from the same KBF directory.

Ḿîẋîñĝ ḿöđéš ŵîţĥîñ à šîñĝļé ƃüîļđ îš üñšüþþöŕţéđ; ýöü þîçķ öñé þéŕ đéþļöý.

Ḿöđé çöḿþàŕîšöñ

ŕüñţîḿéîñļîñé
Ñüḿƃéŕ öƒ ƃüîļđš11 þéŕ ļöçàļé
Ŕüñţîḿé ƃüñđļé çöšţ~2 ķƂ0, öŕ ~2 ķƂ îƒ ýöü üšé ÎÇÜ / þļüŕàļš
Đîçţ ƒéţçĥ àţ ŕüñţîḿéýéš (þéŕ ļöçàļé)ñö
Ḿîššîñĝ ţŕàñšļàţîöñƒàļļƃàçķ ţö šöüŕçé ţéẋţŵàŕñ öŕ éŕŕöŕ àţ ƃüîļđ
Ĥöţ-šŵàþ ļöçàļé îñ-þàĝéý隃üļļ þàĝé ŕéļöàđ / šŵàþ ƃüñđļé
Ƃéšţ ƒöŕàþþ šĥéļļš, ŠààŠ đàšĥƃöàŕđšḿàŕķéţîñĝ šîţéš, ŠŠŔ, ŠŠĜ, ļöçàļé-þéŕ-đöḿàîñ

À ţŕàñšļàţîöñ ţĥàţ îš þŕéšéñţ ƃüţ üñšöüñđ çöüñţš àš ḿîššîñĝ ţöö: neokapi-i18n compile ļéàṽéš öüţ àñý ţàŕĝéţ ţĥàţ đöéš ñöţ çàŕŕý éṽéŕý þļàçéĥöļđéŕ îţš šöüŕçé ĥàš, šö îñ ŕüñţîḿé ḿöđé ţĥàţ éñţŕý ƒàļļš ƃàçķ ţö šöüŕçé ţéẋţ üñţîļ ţĥé ñéẋţ ţŕàñšļàţîöñ þàšš ŕéþàîŕš îţ.

Ŵĥàţ đöéšñ'ţ çĥàñĝé ƃéţŵééñ ḿöđéš

  • Ţĥé éẋţŕàçţöŕ (neokapi-i18n extract) þŕöđüçéš ţĥé šàḿé .kbf.json ŕéĝàŕđļéšš öƒ ḿöđé.
  • Ĥàšĥéš àŕé ḿöđé-îñđéþéñđéñţ.
  • <Plural> / <Select> / t() àļļ ŵöŕķ ţĥé šàḿé îñ àüţĥöŕîñĝ.
  • Üñḿàþþéđ-çöḿþöñéñţ ŵàŕñîñĝš ƒîŕé îđéñţîçàļļý.

Ţĥé ḿöđé đéçîšîöñ îš þüŕéļý àƃöüţ ĥöŵ ţĥé ţŕàñšļàţéđ öüţþüţ ļàñđš îñ ţĥé üšéŕ'š ƃŕöŵšéŕ.

Ñéẋţ