A strongly typed i18n library for react.
npm install --save react-typed-i18n
- Typechecked text id using TypeScript's Template Literal Types
- Interpolation with
string
andReact.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
simstate
dependency - 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
{}
as a placeholder for 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
languages
is the id of the language; - The value of
languages
isLanguage
or() => Promise<Language>
- Use
languageDictionary
helper 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
Provider
component- A
Language
object and its corresponding id must be provided for theProvider
compoennt - In some circumstances (like SSR), rather than importing
Language
directly,Language
can 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
Localized
in places of raw texts- Use
args
prop to interpolate args into the placeholders - A type error will be reported if the id is not valid
- The
Localized
must be imported from where thecreateI18n
is 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
useI18n
hook 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>
);
}
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