@serus/i18n

Internationalization helper

Usage no npm install needed!

<script type="module">
  import serusI18n from 'https://cdn.skypack.dev/@serus/i18n';
</script>

README

i18n

Internationalization helper

JavaScript Style Guide Build Status

Install

yarn add @serus/i18n

Overview

Roadmap

  • initialLocale helper
  • todo optimise default translations
  • docs
  • i18next plugin
  • WTI plugin
  • PhraseApp plugin
  • enable use key context !!TBD

Usage

This library supports only JSX format (because of context). If you need to have access to string typed value, use Render props or I18n.toString(<I18n ... />)

Common usage

import * as React from 'react';
import I18n from '@serus/i18n';

class Example extends React.Component {
  render() {
    return <I18n id="id1" d="Hello World!" />;
  }
}

Render props

<I18n id="id1" d="Hello World!">
  {placeholder => <input placeholder={placeholder} />}
</I18n>

I18n.toString()

this helper using ReactDOM to stringify component, but for consistent translation we need to have I18nContext available, so:

const placeholder = I18n.toString(<I18n id="key1" d="Defaut message" />);
console.log(typeof placeholder); // string

Context provider

Simple react context provider which enables you modularize your translations for each part separately (you neet to keep your current locale on your own, just pass locale property to provider)

import * as React from 'react';
import I18n, { Provider as I18nProvider } from 'i18n';

class App extends React.Component {
  render() {
    return (
      <I18nProvider locale="en">
        <I18n id="id1" d="Hello World!" />
      </I18nProvider>
    );
  }
}

Translation source

Best practice for internationationalization SPA is to serve translated app (per domain / per client locale accept-language header / ip pool / country / brand / etc. )

This lib supports production build with custom translation replaced right in js files (default localization is available instantly). Babel plugin will replace "default" messages and if user dont change his lang, app is delivered in right language as fast as possible.

In other case, we need to load translation, mostly in async mode because if server dont know which lang shoud be serverd, it will serve all supported languages and this behavior have negative inpact for page load speed.

So as a result internationalization provider expects Promise as source input (dynamic) dependency / code split / using cloud language tool wtc.)

Common usage

  import { Provider, sourceFactory } from 'i18n';

  const sourceMap = {
    // static json
    en: () => Promise.resolve({ key1: 'Val 1' }),
    // use dynamic import a.k.a code split
    sk: () => import('../i18n/sk.json').then(module => module.default)
  }

  const translationSource = sourceFactory(sourceMap);

  <Provider locale="en" source={translationSource}>

Each promise will be fired once, and in time when needed.

Custom

You can implement your own localization resolver, expected interface is map of resolvers for each locale, get & match so:

const enSource = { ke1: 'Translated text' };
const skSource = { key1: 'Prelozeny text' };

{
  en: {
    get: (key: string) => enSource[key];
    match: (search: string) => enSource; // *
  },
  sk: {
    get: (key: string) => skSource[key];
    match: (search: string) => skSource; // *
  }
}

* match exists for ENUM keys

Enums

custom feature of this lib is enum key handling. Imagine, if you have some dynamic context like response message which depends on Promise response value, so you can put

const result = 'err1'
<I18n
  d={{ done: "Wohoo!", err1: "Oops!", err2: "Yayy!" }}
  id="enumMsgquot;
  v={result}
  />

those keys must endsWith $ & have d defined as object & must pass v parameter

if you use enum keys, match resolver will be applied, so you can search any keys by your own rules, or by default, in case of render props usage, all keys which starts with %d%$ will be computed into single object by default match handler and passed as second parameter of render props

Render props

<I18n
  d={{ done: "Wohoo!", err1: "Oops!", err2: "Yayy!" }}
  id="enumMsgquot;
  v={result}
>
  {(value, object) => <>
    value: {value}
    all values:
    <pre>{JSON.stringify(object, null, 2)}</pre>
  </>}
</i18n>

License

MIT © serus22