@keeex/js-localize
v4.3.15
Published
Extract strings from source code for i18n
Downloads
13
Readme
@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 displayedonLocaleChange
: called when the locale changes with the new locale as argumentloadingChildren
: 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 aLocalizeCore
instance usingCore.create()
. Some original initialization arguments are passed tocreate()
. 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 onLocalizeCore._L()
, the later is available as an import from@keeex/js-localize/lib/react/localize.js
- The
<BasicLocaleProvider />
and<BrowserLocaleProvider />
both receive the instance ofLocalizeCore
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 callingregisterDOM()
on this object.