A strongly typed i18n library for react.
npm install --save react-typed-i18n
- Typechecked text id using TypeScript's Template Literal Types
- Interpolation with
stringandReact.ReactNode - Async language loading for code splitting
- Hot language reloading without reloading page
- No external dependency and 1.3 KiB gzipped
- 100% line and branch test coverage
This library is the successor of simstate-i18n. Most concepts and functionalities remain unchanged, but this library
- removes the
simstatedependency - use Template Literal Types to typecheck the text id
- is way easier to setup
My personal website ddadaal.me is built with this library.
A example project is provided under the example folder. Run the following commands to run it.
# On the library project root
npm install
npm run build
cd example
npm run dev- Define your definitions (one file per language)
- use
{}or{key}as placeholders for interpolation (more details about interpolation) - object can be nested
- all languages should have identical structures
- this object is called
Language
- use
// ./src/i18n/en
export default {
hello: {
world: "Hello {} World {}",
}
}
// ./src/i18n/cn
export default {
hello: {
world: "你好 {} 世界 {}",
}
}- Define all your languages and create elements from
createI18n- The key of
languagesis the id of the language; - The value of
languagesisLanguageor() => Promise<Language> - Use
languageDictionaryhelper to create the initialization arg
- The key of
// ./src/i18n/index.ts
import { createI18n, languageDictionary } from "react-typed-i18n";
const cn = () => import("./cn").then((x) => x.default);
const en = () => import("./en").then((x) => x.default);
export const languages = languageDictionary({
cn,
en,
});
export const { Localized, Provider, id, prefix, useI18n } = createI18n(languages);- Wrap the component tree with
Providercomponent- A
Languageobject and its corresponding id must be provided for theProvidercompoennt - In some circumstances (like SSR), rather than importing
Languagedirectly,Languagecan be asyncly loaded and provided.
- A
// ./src/Root.tsx
import React from "react";
import en from "./i18n/en";
import { Provider } from "./i18n";
import App from "./App";
export default () => {
return (
<Provider initialLanguage={{
id: "en",
definitions: en,
}}>
<App />
</Provider>
);
}- Use
Localizedin places of raw texts- Use
argsprop to interpolate args into the placeholders (you can pass array or object as arguments. Learn More) - A type error will be reported if the id is not valid
- The
Localizedmust be imported from where thecreateI18nis called (for example,./src/i18n) - The below displays: Hello AAA World BBB
- Use
// ./src/App.tsx
import React from "react";
import { Localized } from "./src/i18n";
export default () => {
return (
<div>
<p>
<Localized
id="hello.world"
args={[
<strong key="1">AAA</strong>,
<strong key="2">BBB</strong>,
]}
/>
</p>
</div>
);
}- Use
useI18nhook to get helper functions likesetLanguageById- After clicking the button, the p will display: 你好 AAA 世界 BBB
// ./src/App.tsx
import React from "react";
import { Localized, useI18n } from "./src/i18n";
export default () => {
const { setLanguageById } = useI18n();
return (
<div>
<p>
<Localized
id="hello.world"
args={[
<strong key="1">AAA</strong>,
<strong key="2">BBB</strong>,
]}
/>
</p>
<button onClick={() => setLanguageById("cn")}>
Change to cn
</button>
</div>
);
}react-typed-i18 uses {} or {key} as placeholders for interpolations.
{key}will be replaced withargs[key]{}will be replaced withargs[i], whereiisith occurrence of{}(not counting{key}) in the definition string- if
argsis not an array, it will be replaced withObject.values(args)[i].- It's not recommended since the order of the resulting array may not be trivial.
- if
- If the string to be replaced is
undefined, it will be replaced as empty string - If
{}or{key}is prefixed with\, it will be escaped. Prefix a\with a\will not escape the following{}or{key}
{} and {key} can co-exist in one definition.
See the examples:
| definition | args |
result |
|---|---|---|
{} {} |
["1", "2"] |
1 2 |
{1} {0} |
["1", "2"] |
2 1 |
{key2} {key1} |
{"key1": "value1", "key2": "value2"} |
value2 value1 |
{1} {0} |
{"key1": "value1", "key2": "value2"} |
value2 value1 |
{} {1} {0} {} |
["1", "2"] |
1 2 1 2 |
{} {key2} {1} {} |
["1", "2"] |
1 2 1 |
{} {key2} {1} {} |
{"key1": "value1", "key2": "value2"} |
value1 value2 value2 |
\{0} \\{0} \\\{0} |
["1"] |
\{0} \1 \\{0} |
import { prefix, id } from "./i18n";
// id is just an identity function with typecheck
const i = id("hello.world"); // id === "hello.world"
// prefix generates a prefix function.
// When the function is called,
// two part are concatenated.
// both part are typechecked.
const p = prefix("hello.");
const fullId = p("world");// src/i18n/en.ts
export default {
a: "a",
b: {
c: "c",
},
};
// src/i18n/index.ts
import { TextIdFromLangDict } from "react-typed-i18n";
// "a" | "b.c"
export type TextId = TextIdFromLangDict<typeof languages>;MIT
