4.3.14 • Published 28 days ago

@keeex/js-localize v4.3.14

Weekly downloads
-
License
SEE LICENSE IN LI...
Repository
-
Last release
28 days ago

@keeex/js-localize

This library provides tooling and functions to handle translation of UI in most places (server, React, HTML).

Requirement for development

This package requires the development-tools scripts to be available on the path.

Description

This package provide two tools :

  • A CLI tool to generate and update localization files
  • A JavaScript library to provide localization to an application

Main concept

On a given project, all translatable strings must be wrapped in one of the following:

  • as the first string literal argument of a function named either _L(), _l() or _translate() (coincidentally those are the names of functions exposed to actually translate content)
  • as the child of a JSX component named <Localize> (where again only a single string is accepted)

Once the project is configured, running the localize_fetch binary will collect all applicable strings and create two sets of JSON files: one for editing the local translations and one for bundling in the build. The strings fetched for a given project are automatically prefixed with the project name. This ensure compatibility when a dependency also provides its own localization files.

Code-wise, you need to create an instance of LocalizeCore to setup and configure all aspects of localization. Usually this is done in a single module file to ensure that the whole project uses the same object. This core instance is then used for every other operations, including embedding in React and/or browser environments.

Usage

Code example

This short example shows the minimum setup to get going.

First, you have to configure supported locales in package.json:

{
  "name": "@org/name",
  "localization": {
    "default": "en",
    "locales": ["fr"],
    "langsDir": "langs",
    "srcDirs": [
      "react",
      "webres"
    ],
    "outputDir": "gen/langs"
  }
}

The above configuration will enable the "en" and "fr" locales, generate/update localization files in langs/<locale>.json and generate merged localization files in gen/langs/<locale>.json.

The following content can be put in a file (usually named localize.js) to provide the core instance:

import {Core} from "@keeex/js-localize/lib/core.js";
import langEN from "../gen/langs/en.json";
import langFR from "../gen/langs/fr.json";

export const localizeCore = await Core.create({
  prefixes: ["@org/name"],
  supportedLocales: ["en", "fr"],
  initialLocale: "en",
  preloadedLocalizations: {
    "en": langEN,
    "fr": langFR,
  }
});

export const _l = r => r;
export const _L = localizeCore._L;

(note that if you do not use lazy loading of locales and/or provide the initial locale at initialization time, you can call Core.createSync() as an alternative)

Then, in all places that requires localization you can use these functions/components:

import React from "react";
import Localize from "@keeex/js-localize/lib/react/localize.js";
import {_L} from "./localize.js";

// Example component using localized strings
class SomeComp extends React.Component {
  render() {
    return <div>
      This is localized: <Localize>a sentence in english</Localize>
      This have parameters: <Localize data-user="name">Your name is %%%name%%%</Localize>
    </div>;
  }
}

// Example using localized string directly (without React)
const someString = _L("translate this");
const someString2 = _L(
  "Page {currentPage} / {totalPage}",
  {
    currentPage: 34,
    totalPage: 63,
  },
);

(note that the placeholder are marked with {} in _L() and with %%% in JSX).

To manually change locale:

import {localizeCore} from "./localize.js";

// Can be called anywhere
localizeCore.setLocale("en");

Collecting strings

With the package installed and configured, call npx localize_fetch. This will create/update the localization files for this project and create output files as setup.

Full configuration

In the package.json of your project, add a localization section with the following properties:

  • default: default locale
  • locales: list of extra locales
  • langsDir: directory to store the output localization files
  • extraModules: list of modules from node_modules from which we want to add the localization into this project's file.
  • srcDirs: array of path with source files to parse to find localized strings
  • outputDir: path where to put the final localization file (including local translations and dependencies'). These are the files that will be used with addLocale() and the likes.

Delayed translations

Sometimes you want to setup a string using some logic, then localize it on display. For such use cases, you can use a function named _l() to identify the strings to localize without actually transforming them, then passing a variable to either <Localize /> or _L().

const message = actionLogin
  ? _l("Welcome {user}")
  : _l("Farewell {user}");
return <Localize data-user={userName}>{message}</Localize>;

Handling locale change

It is possible to register event handlers for when the locale is changed or when some translations are updated using localizeCore.on().

When using React those are handled automatically using the appropriate providers that exposes a callback prop so any locale change can be handled that way.

Import from web or lib

The library is fully available under the lib directory for usage in Node, and under the web directory for bundling in recent browsers. Do not mix both in the same JavaScript environment.

Writing a localizable library

If you write a library that must work with @keeex/js-localize, you have to take care of the following:

  • if using directly Core functions, you have to provide a way to get the Core instance from the caller
  • if using React contexts and <Localize> (and for other translation functions too) make sure to force the prefix used when generating your library's translations

Extra localization tools

localize_fetch

This script will parse the specified directory for files with candidate strings and generate/update localization files. The configuration is extracted from package.json.

localize_replace

Change a base string in the localization files. This is useful if for example a string in the code have to change (so the default value remains acceptable in case no locale is available).

If the replaced string is the same as the string identifier, the string is updated alongside.

localize_json2csv

This will produce a file named langs.csv containing all strings to be edited using any spreadsheet software.

localize_csv2json

Read the translations from langs.csv (produced by the localize_json2csv command) and update the local JSON translation files.

Library initialization

Creating LocalizeCore instance

The LocalizeCore instance can be created using Core.create(). It is possible to create multiple instances although there will usually be only one per environment.

When creating the instance, it is not fully initialized until either of the following happens:

  • A value for initialLocale was provided and the locale was available (either preloaded or through the lazy provider)
  • LocalizeCore.setLocale() is called
  • A locale is added using LocalizeCore.addLocale()

Providing locales

Translations are sourced from the output JSON files produced by the localize_fetch command.

They can be added in three different ways:

  • preloaded, as arguments to Core.create()
  • after initialization using LocalizeCore.addLocale()
  • using the lazy provider that will be called as needed to get the translations data

Changing the active locale

All ways of using js-localize (including React and browser) uses the same call to change the active locale.

Calling LocalizeCore.setLocale() will update all parts accordingly.

Generic React environment

This document describe specific point to use the library with React. documentation.

Locale Provider

In React (but not in browser) you should render the <BasicLocaleProvider /> component above any use of the <Localize /> component.

import React from "react";
import BasicLocaleProvider from "@keeex/js-localize/web/react/basiclocaleprovider.js";
import {localizeCore} from "./localize.js";

// Be careful to initialize the library before rendering your application, as it is still required. 

export default class App extends React.Component {
  render() {
    return <BasicLocaleProvider localizeCore={localizeCore}>
      <AppContent />
    </BasicLocaleProvider>;
  }
}

This component accept an extra prop named onLocaleChange that is called when the locale is changed.

Localization

For React, a component named <Localize> is provided. It must have a single children node that is a string, and will be translated.

Substitutions in the translated strings must be indicated with %%%string%%% instead of {string} because these characters have special meaning in JSX. The substitution strings can be passed to the <Localize /> component through props named data-<key>. It is possible to pass React nodes as the value for these props.

Changing locale

Changing locale is done as usual by calling LocalizeCore.setLocale(locale).

Usage in browser

With React

When using this library with React in a browser environment, you should use <BrowserLocaleProvider />. This component should be rendered above any <Localize> usage and take as props the LocalizeCore instance. It will try to automatically uses the user's locale setting and save further changes in local storage.

import React from "react";
import BrowserLocaleProvider from "@keeex/js-localize/web/react/browserlocaleprovider.js";
import {localizeCore} from "./localize.js";

export default class App extends React.Component {
  render() {
    return <BrowserLocaleProvider localizeCore={localizeCore}>
      <RestOfYourApp />
    </BrowserLocaleProvider>
  }
}

As with the rest of the library updating the current locale is done using LocalizeCore.setLocale().

This component supports the following additional properties:

  • onLocaleReady: called the first time when the locale is ready to be displayed
  • onLocaleChange: called when the locale changes with the new locale as argument
  • loadingChildren: React render to use before initialization is complete. Note that this initialization process is usually very fast if the locales are preloaded, and depends on the lazy provider otherwise.

Without react

The library provides some tools to integrate with browser environments. These functions are provided in the web/browser/browser.js.

import {browserInitLocales} from "@keeex/js-localize/web/browser/browser.js";
import {localizeCore} from "./localize.js";

const browserIntegration = await browserInitLocales(localizeCore);
browserIntegration.registerDOM("@org/name", document.querySelector("body"));

With the sample code above any element that have a lang="keeex" and contains only a string will be translated when the locale is changed using LocalizeCore.setLocale().

Multiple prefix

The default behavior of the library is to use the project's name (from package.json) and use it as a prefix to distinguish locally translated strings from translations inherited from other projects.

It is possible to use custom prefixes to create "groups" of translations.

This way you can "separate" prefixes to isolate layers of translations (like CSS). The later elements in the prefixes provided at initialization are used in priority. If for example you have a string with a general translation and want at some point to override it with a local version (for example, based on user preferences) then you can use a secondary prefix and add these using LocalizeContext.addLocale() while specifying this additional prefix. (note that the prefix must be set when initializing LocalizeCore).

Strings from additional prefix can be removed without removing the main prefix using LocalizeCore.clearLocale().

Migration from v3 to v4

The following things have changed from v3:

  • The initLocalize() all-in-one function is removed. Instead create a LocalizeCore instance using Core.create(). Some original initialization arguments are passed to create(). DOM elements are initialized in a completely different way.
  • There are no global functions; all previously available functions are available on the LocalizeCore instance.
  • There is no need to "generate" the _L() function and the <Localize /> component. The former is available on LocalizeCore._L(), the later is available as an import from @keeex/js-localize/lib/react/localize.js
  • The <BasicLocaleProvider /> and <BrowserLocaleProvider /> both receive the instance of LocalizeCore as a props.
  • Changing locale is only done using LocalizeCore.setLocale() instead of the multiple different ways used before
  • Registering DOM elements (for raw HTML translation integration) requires the creation of a browser registration object calling browserInitLocales(), then calling registerDOM() on this object.