localize-toolkit

A localization library that uses React's context API and the Polyglot localization library to provide robust localization tools for React projects.

Usage no npm install needed!

<script type="module">
  import localizeToolkit from 'https://cdn.skypack.dev/localize-toolkit';
</script>

README

Localize Toolkit Build Status NPM Version

A localization library that uses React's context API and the Polyglot localization library to provide robust localization tools for React projects.

Features:

  1. Updates every translated phrase on language switch without remounting any components
  2. Caching phrases to avoid repeat fetching
  3. Static localization method for non-React files

Examples:

  1. Minimal Example
    • The absolute bare-bones example of using Localize Toolkit
  2. "Kitchen Sink" Full Example
    • A full example with faked API calls to fetch new languages
  3. Overlay Pattern Example
    • A pattern to avoid remounting entirely, with an overlay for loading
  4. Pseudo Localization
    • An example using pseudo localization (useful for testing)

The toolkit exposes 5 items: LocalizeProvider, LocalizeContext, Localize, useLocalize and staticTranslate. Expand the table of contents to jump to a specific item within these.

Table of Contents
  1. LocalizeProvider
  2. LocalizeContext
  3. Localize
  4. useLocalize
  5. staticTranslate
Expand for dependency details
  1. This package has a peer dependency on version >16.8.0 of react and react-dom, as it uses the Hooks API.

  2. This package has a dependency on node-polyglot. You may have some type issues if you are using TypeScript, since some of the types are from node-polyglot. If this is an issue, you can install the @types/node-polyglot as a dev-dependency:

    # yarn
    yarn add @types/node-polyglot -D
    
    # npm
    npm i @types/node-polyglot -D
    


LocalizeProvider

Localize provider contains the core functionality, and provides localize methods to both the LocalizeContext and the Localize component.

LocalizeProvider Props

getPhrases: (language: string) => Promise<Phrases>

  • Provide this prop to give an API endpoint that can be called with language. This should asynchronously return a Phrases object.

noCache: boolean

  • By default, this is false. If set to true, none of the given or fetched phrases will be cached within the provider. Any subsequent attempts to switch to these languages will require a new phrases object be provided, or will make a call to getPhrases.

pseudolocalize: boolean

  • By default, this is false. If set to true, will apply pseudo localization to all returned strings. This is for testing if your application can adapt to longer strings from other languages. Do not enable this in production.

Example Initialization

ReactDom.render(
  <LocalizeProvider getPhrases={getLanguageAPI} noCache>
    <App />
  </LocalizeProvider>,
  document.getElementById('root'),
);


LocalizeContext

All methods for localization and updating the LocalizeProvider are accessed through this Context.

LocalizeContext API

loading: boolean

  • Returns true if language is being fetched.

error: Error | null

  • Returns any errors encountered in setting the language.

currentLanguage: string

  • Returns the current language string.

isLanguageCached: (language: string) => boolean

  • Check if there are cached phrases for a given language string. This can be called before setLanguage in order to check whether you will have to provide a phrases object.

setLanguage: (language: string, phrases?: Phrases) => Promise<void>

  • Call this method to set the language. You must provide a language string (ex: 'en'), and can optionally provide the corresponding language object. Once this method is called, there is a sequence of checks:

    • If the phrases are provided, they will be used for the given language.

    • If no phrases are provided:

      • If the phrases aren't cached, fetch them from getPhrases prop in LocalizeProvider.

        Note: If no phrases are cached and no getPhrases prop is provided, an error will occur as the localize toolkit has no way to set the phrases for the specified language.

      • If they are cached, use the cached phrases.

clearCache: (language?: string) => void

  • Clears a phrases object for the provided language from the cache if it exists. If no language is provided, this method clears all phrases from the cache.

t: (phrase: string, options?: number | Polyglot.InterpolationOptions) => string;

  • This is the Polyglot t method. For information on how to use this, check the documentation.

tt: <Lang>(options?: number | Polyglot.InterpolationOptions): (...args: string[]) => string;

  • tt stands for typed-t and it has some special properties and use cases. If you have a TypeScript type definition for the structure of your phrases object you can use tt to throw errors if you use incorrect keys or change the keys in your language object. Every member of ...args must be a valid key in the Lang object provided (the actual type definition is much too verbose to write here, check out the source code if you want to see it).

  • The use is slightly different than t. If you aren't sure which to use, or if you aren't using TypeScript, definitely use t. This function is solely for ensuring type safety across large applications with rapidly changing phrase objects. Here's an example of the use cases compared to t.

    // An example phrases object.
    const phrases = {
      hello_world: 'Hello World!',
      sizes: {
        mb: '%{smart_count}MB',
      },
      names: {
        by_name: 'By %{name}',
      },
    };
    
    // Get the TypeScript type of the object.
    type Phrases = typeof phrases;
    
    // Examples using `tt`.
    const hiWrld = localize.tt<Phrases>()('hello_world');
    const sizeMb = localize.tt<Phrases>(4)('sizes', 'mb');
    const byName = localize.tt<Phrases>({name: 'John Doe'})('names', 'by_name');
    
    // Examples using `t`.
    const hiWrld = localize.t('hello_world');
    const sizeMb = localize.t('sizes.mb', 4);
    const byName = localize.t('names.by_name', {name: 'John Doe'});
    

    As you can see, it is much more verbose, but it does help ensure you're correct:

    const sizeMb = localize.tt<Phrases>(4)('sizes', 'mbzzzz');
    // error: Argument of type '"mbzzzz"' is not assignable to parameter
    // of type '"mb" | undefined'. ts(2345)
    
    const sizeMb = localize.t('sizes.mbzzzz', 4);
    // no error thrown, as it's just a string.
    

Example Use

Here are some examples for using the localize context within your components.

How to consume context

There are three ways to use the localize context:

  1. The exported useLocalize hook (a nice wrapper for the useContext hook so you don't need two imports):

    const localize = useLocalize();
    localize.t('some_word');
    
  2. The useContext hook (we recommend you use useLocalize instead though):

    const localize = useContext(LocalizeContext);
    localize.t('some_word');
    
  3. The exported Localize component (more info below):

Example in Functional Component

import React, {useContext, useEffect} from 'react';
import {Localize, useLocalize} from 'localize-toolkit';

function MyComponent({}) {
  const {setLanguage, t} = useLocalize();

  useEffect(() => {
    setLanguage('en');
  }, [setLanguage]);

  return (
    <>
      {/* To translate using the context: */}
      {t('translate_token')}

      {/* To translate as a JSX element: */}
      <Localize t={'translate_token'} />
    </>
  );
}

Example in Class Component

import React, {Component} from 'react';
import {LocalizeContext, Localize} from 'localize-toolkit';

class MyComponent extends Component {
  static contextType = LocalizeContext;

  componentDidMount() {
    const {setLanguage} = this.context;
    setLanguage('en');
  }

  render() {
    const {t} = this.context;

    return (
      <>
        {/* To translate using the context: */}
        {t('translate_token')}

        {/* To translate as a JSX element: */}
        <Localize t={'translate_token'} />
      </>
    );
  }
}


Localize

Localize Props

t: string

  • The phrase string you wish to translate.

options: number | Polyglot.InterpolationOptions

  • Any options for the translated phrase. This acts the same as a second argument given to Polyglot. For information on how to use this, check the documentation;

transformString: (translated: string) => string

  • A function that takes in the translated string and returns the string that will be rendered by the component. For example, you can convert the translated string to uppercase.

Example Component

// Returns "HI JOHN" if language is "en" or "BONJOUR JOHN" if language is "fr".
<Localize
  t="hi_name"
  options={{name: 'John'}}
  transformString={translated => translated.toUpperCase()}
/>


useLocalize

This is simply a hook to wrap useContext. As such, these are equivalent:

useLocalize example:

import React from 'react';
import {useLocalize} from 'localize';

function Component() {
  const localize = useLocalize();
}

useContext example:

import React, {useContext} from 'react';
import {LocalizeContext} from 'localize';

function Component() {
  const localize = useContext(LocalizeContext);
}

As you can see, it just simplifies it slightly by allowing one less import and less code written.



staticTranslate

Note: This should only be used in cases where you are unable to use the context components. This would most likely be outside of React, such as inside of a Redux reducer. If you are using this inside a React file, you can probably use LocalizeContext or Localize instead.

You can call static translate in order to translate a phrase outside of React. For example, you can use it for translating something within Redux to be stored in the state. However is one important thing to consider: This will be a static translation. It will not update when you update the language using the Localize context.

Static translate only exposes the method t, which exacly matches the t method within Polyglot. For information on how to use this, check the documentation. This method can be used as follows:

// Returns "Hi John" if language is "en" or "Bonjour John" if language is "fr".
const translatedPhrase = staticTranslate.t('hi_name', {name: 'John'});