Skip to content

A strongly-typed React i18n library based on simstate that supports dynamic language changing and text id autocomplete

Notifications You must be signed in to change notification settings

ddadaal/simstate-i18n

Repository files navigation

simstate-i18n

NPM types Build Status

simstate-i18n is a strongly-typed React i18n library based on simstate.

DEPRECATED!

This library has been superceded by react-typed-i18n. The new library:

  • has all features of this library!
  • does not depend on simstate
  • use string literals as id which are typechecked using template literal types

Please give it a try!

Features

  • Use text id in a strongly-typed manner
  • Support nested text id
  • Support placeholders on text definition
  • Support async language loading for code splitting
  • Hot change languages without page reloading
  • Hot change texts without restarting the application

Install

npm install --save simstate-i18n

Example

My blog ddadaal.me is created with simstate-i18n.

Try changing the language by the LanguageSelector.

Usage

This library requires setting up necessary files and folders before using the components and store.

Setup

Check out the example folder for recommended file structure.

.
├── App.tsx
└── i18n
    ├── cn.ts
    ├── en.ts
    └── index.ts
  1. Create a folder i18n (or anything you want) on your src folder
  2. Create a file ({language}.ts, for example cn.ts, en.ts) under i18n folder for each language to support with the following content:
    • Every such file defines a language object for one language
    • Language object contains the basic information (id, strings, names etc.) and the mappings from id to text
    • Every language objects should have exactly identical structure.
// src/i18n/en.ts
// example: example/i18n/{en,cn}.ts

export default {
  // The id of the language. Any unique string is acceptable.
  id: "en",

  // The name of the language
  name: "English",

  // The definitions of id and text template.
  // Use "{}" as the placeholder for dynamically set text or React component.
  definitions: {
    navbar: {
      home: "Home",
      about: "About",
    },
    content: "Current time: {}. Thanks for using simstate-i18n."
  }
}
  1. Create a index.ts under the i18n folder with the following content:
// src/i18n/index.ts
// example: example/i18n/index.ts

// Imports
import cn from "./cn";
import { createI18nContext, I18nStoreDef, I18nStore } from "simstate-i18n";
import { useStore } from "simstate";

// Load English dynamically to support code splitting
const en = () => import("./en").then((x) => x.default);

// The actual Language type,
// might be useful when the Language object is extended and the extra properties are needed
export type Language = typeof cn;

// Create the I18nContext with cn as the default language.
export const i18nContext = createI18nContext(cn, { en });

// Destruct and export the members for easier usage
// Recommendation: rename the idAccessor to lang for shorter typing
export const { getLanguage, idAccessor: lang } = i18nContext;

// This function is shortcut to use I18nStore,
// and also specify the exact types of Language objects,
// which helps avoid type casting.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function useI18nStore() {
  return useStore(I18nStore) as I18nStoreDef<Language["definitions"], Language>;
}
  1. Create and inject a new global simstate store with the i18nContext instance.
// example: example/App.tsx

import { i18nContext } from "./i18n";
import { createI18nStore } from "simstate-i18n";
import { StoreProvider } from "simstate";

const Root = () => {
  // Create global i18nStore instance.
  const [i18nStore] = useState(() => createI18nStore(i18nContext));
  return (
    <StoreProvider stores={[i18nStore]}>
      <App />
    </StoreProvider>
  )
}

Usage

When the configurations are completed and the global I18nStore is injected, it is possible to use the provided components and store.

Localized/LocalizedString component

<LocalizedString /> or <Localized /> component are used in place of raw texts to provide i18n capabilities to anywhere a React component can be. It shows the text of the specified id of the current language.

All LocalizedString components will be updated when the current language is changed.

Example:

// import the idAccessor (renamed to lang) from i18n folder
// which is used to access the id of a text strongly-typedly.
import { lang } from "./i18n";
import { LocalizedString } from "simstate-i18n";

// Set the id of text as accessing properties of the lang object
// If the text has placeholders {},
// set the replacements prop with the replacement elements
// that will be inserted into the placeholders in order.
<LocalizedString id={lang.content} replacements={[Date.now()]} />

// The same as above but name is shorter
<Localized id={lang.content} replacements={[Date.now()]} />

useLocalized hook

This hook is used to suffice more advanced usage.

The following example behaves the same as the LocalizedString example above, and will also be updated when the current language is updated.

Example:

import { lang } from "./i18n";
import { useLocalized } from "simstate-i18n";

const Component = () => {
  const content = useLocalized(lang.content, [Date.now()]);
  return content;
}

I18nStore store

The I18nStore instance of current provider scope can be acquired with useStore function provided by simstate, which can be used to control the current language as well as getting some information.

Example:

import { I18nStore } from "simstate-i18n";
import { useStore } from "simstate";

const ControlPanel = () => {
  const i18nStore = useStore(I18nStore);

  return (
    <div>
      <p>
        Current language: {i18nStore.currentLanguage.name}
      </p>
      {
        i18nStore.switchingToId && `Switching to ${i18nStore.switchingToId}`
      }
      <ul>
        {allLanguages.map((lang) => (
          <li key={lang.id}>
            <a onClick={() => i18nStore.changeLanguage(lang.id)}>
              {lang.name}
            </a>
          </li>
        ))}
      </ul>
    </div>
  )
}

Related

simstate: A Strongly-typed React State Management Tool Favoring React Hooks and TypeScript.

Strongly Typed i18n with TypeScript (English): This article of mine talks about the problems of using raw string as the text ids, and also introduces a proxy-based text id generation method which is now replaced with another method (src/i18nContext.ts) which should have better performance.

License

MIT © ddadaal

About

A strongly-typed React i18n library based on simstate that supports dynamic language changing and text id autocomplete

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published