Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ddadaal/react-typed-i18n

A strongly typed i18n library for react
https://github.com/ddadaal/react-typed-i18n

i18n react strongly-typed

Last synced: 22 days ago
JSON representation

A strongly typed i18n library for react

Awesome Lists containing this project

README

        

react-typed-i18n

![npm](https://img.shields.io/npm/v/react-typed-i18n)
[![Build and Test](https://github.com/ddadaal/react-typed-i18n/actions/workflows/build.yml/badge.svg)](https://github.com/ddadaal/react-typed-i18n/actions/workflows/build.yml)
[![Coverage Status](https://coveralls.io/repos/github/ddadaal/react-typed-i18n/badge.svg?branch=master)](https://coveralls.io/github/ddadaal/react-typed-i18n?branch=master)


A strongly typed i18n library for react.

![Demo](docs/demo.gif)

# Install

```
npm install --save react-typed-i18n
```

# Features

- **Typechecked** text id using TypeScript's [Template Literal Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html)
- **Interpolation** with `string` and `React.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`](https://github.com/ddadaal/simstate-i18n). Most concepts and functionalities remain unchanged, but this library

- removes the [`simstate`](https://github.com/ddadaal/simstate) dependency
- use Template Literal Types to typecheck the text id
- is way easier to setup

# Example

My personal website [ddadaal.me](https://ddadaal.me) is built with this library.

A example project is provided under the `example` folder. Run the following commands to run it.

```bash
# On the library project root
npm install
npm run build
cd example
npm run dev
```

# Usage

1. 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`

```tsx
// ./src/i18n/en
export default {
hello: {
world: "Hello {} World {}",
}
}

// ./src/i18n/cn
export default {
hello: {
world: "你好 {} 世界 {}",
}
}
```

2. Define all your languages and create elements from `createI18n`
- The key of `languages` is the **id** of the language;
- The value of `languages` is `Language` or `() => Promise`
- Use `languageDictionary` helper to create the initialization arg
```tsx
// ./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);
```

3. Wrap the component tree with `Provider` component
- A `Language` object and its corresponding id must be provided for the `Provider` compoennt
- In some circumstances (like SSR), rather than importing `Language` directly, `Language` can be asyncly loaded and provided.

```tsx
// ./src/Root.tsx
import React from "react";
import en from "./i18n/en";
import { Provider } from "./i18n";
import App from "./App";

export default () => {
return (



);
}
```

4. 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 the `createI18n` is called (for example, `./src/i18n`)
- The below displays: Hello **AAA** World **BBB**

```tsx
// ./src/App.tsx
import React from "react";
import { Localized } from "./src/i18n";

export default () => {
return (



AAA,
BBB,
]}
/>



);
}
```

5. Use `useI18n` hook to get helper functions like `setLanguageById`
- After clicking the button, the p will display: 你好 **AAA** 世界 **BBB**

```tsx
// ./src/App.tsx
import React from "react";
import { Localized, useI18n } from "./src/i18n";

export default () => {
const { setLanguageById } = useI18n();
return (



AAA,
BBB,
]}
/>


setLanguageById("cn")}>
Change to cn


);
}
```

# Helpers

## Helpers functions to generate text id

```tsx
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");
```

## All ids type

```tsx
// 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;
```

# License

MIT