I18n WP Plugin
Originally from i18n-calypso, but here moment.js is stripped due to build size.
This lib enables translations, exposing three public methods:
It also provides a Higher-Order Component named localize(). Wrapping your component in localize()
will give it the aforementioned functions as props. This is the suggested way of using them with React components.
Finally, this lib exposes a utility method for your React application:
Translate Method
translate()
accepts up to three arguments (string
, string
, object
), depending on the translation needs. The second and/or third parameter can be omitted:
/**
* @param {string} original - the string to translate, will be used as single version if plural passed
* @param {string} [plural] - the plural string to translate (if applicable)
* @param {object} [options] - properties describing translation requirements for given text
**/
Options
The following attributes can be set in the options object to alter the translation type. The attributes can be combined as needed for a particular case.
- options.args [string, array, or object] arguments you would pass into sprintf to be run against the text for string substitution. See docs
- options.components [object] markup must be added as React components and not with string substitution. See mixing strings and markup.
- options.comment [string] comment that will be shown to the translator for anything that may need to be explained about the translation.
- options.context [string] provides the ability for the translator to provide a different translation for the same text in two locations (dependent on context). Usually context should only be used after a string has been discovered to require different translations. If you want to provide help on how to translate (which is highly appreciated!), please use a comment.
Usage
If you pass a single string into translate
, it will trigger a simple translation without any context, pluralization, sprintf arguments, or comments. You would call it like this.
var i18n = ;var translation = i18n;
Strings Only
Translation strings are extracted from our codebase through a process of static analysis and imported into GlotPress where they are translated (more on that process here). So you must avoid passing a variable, ternary expression, function call, or other form of logic in place of a string value to the translate
method. The one exception is that you can split a long string into mulitple substrings concatenated with the +
operator.
/*----------------- Bad Examples -----------------*/ // don't pass a logical expression argumentvar translation = i18n; // don't pass a variable argumentvar translation = i18n; // don't pass a function call argumentvar translation = i18n; /*----------------- Good Examples -----------------*/ // do pass a string argumentvar example = i18n; // do concatenate long strings with the + operatorvar translation = i18n;
String Substitution
The translate()
method uses sprintf interpolation for string substitution (see docs for syntax details). The option.args
value is used to inject variable content into the string.
// named arguments (preferred approach)i18n;// 'My hat has 3 corners' // argument arrayi18n;// 'My hat has 3 corners' // single substitutioni18n;// 'My hat has 3 corners'
Mixing Strings And Markup
Because React tracks DOM nodes in the virtual DOM for rendering purposes, you cannot use string substitution with html markup as you might in a php scenario, because we don't render arbitrary html into the page, we are creating a virtual DOM in React.
Instead we use the interpolate-components module to inject components into the string using a component token as a placeholder in the string and a components object, similar to how string substitution works. The result of the translate()
method can then be inserted as a child into another React component. Component tokens are strings (containing letters, numbers, or underscores only) wrapped inside double-curly braces and have an opening, closing, and self-closing syntax, similar to html.
NOTE: Always use a JSX element for passing components. Otherwise you will need to wrap your React classes with createFactory
. Any wrapped content inside opening/closing component tokens will be inserted/replaced as the children of that component in the output. Component tokens must be unique:
// self-closing component syntaxvar example = i18n; // component that wraps part of the stringvar example2 = i18n; // components can nestvar example3 = i18n;
Pluralization
You must specify both the singular and plural variants of a string when it contains plurals. If the string uses placeholders that will be replaced with actual values, then both the plural and singular strings should include those placeholders. It might seem redundant, but it is necessary for languages where a singular version may be used for counts other than 1.
// An example where the translated string does not have// a number represented directly, but still depends on itvar numHats = // returns integer content = i18n; // An example where the translated string includes the actual number it depends onvar numDays = // returns integer content = i18n;
More translate() Examples
// simplest case... just a translation, no special optionsvar content = i18n; // sprintf-style string substitutionvar city = // returns string zip = // returns string content = i18n; // Mixing strings and markup// NOTE: This will return a React component, not a stringvar component = i18n; // Mixing strings with markup that has nested contentvar component = i18n; // add a comment to the translatorvar content = i18n; // providing contextvar content = i18n;
See the test cases for more example usage.
numberFormat Method
The numberFormat method is also available to format numbers using the loaded locale settings (i.e., locale-specific thousands and decimal separators). You pass in the number (integer or float) and (optionally) the number of decimal places you want (or an options object), and a string is returned with the proper formatting for the currently-loaded locale. You can also override the locale settings for a particular number if necessary by expanding the second argument into an object with the attributes you want to override.
Examples
// These examples assume a 'de' (German) locale to demonstrate// locale-formatted numbersi18n; // '2.500'i18n; // '2.500,10'i18n; // '2*500@330'
hasTranslation Method
Using the method hasTranslation
you can check whether a translation for a given string exists. As the translate()
function will always return screen text that can be displayed (will supply the source text if no translation exists), it is unsuitable to determine whether text is translated. Other factors are optional key hashing as well as purposeful translation to the source text.
Usage
var i18n = ;i18n; // truei18n; // false
Localize
localize
is a higher-order component which, when invoked as a function with a component,
returns a new component class. The new component wraps the original component, passing all
original props plus props to assist in localization (translate
, and numberFormat
).
The advantage of using a higher-order component instead of calling translate directly from
the i18n-wp-plugin
module is that the latter does not properly account for change events
which may be emitted by the state emitter object.
Usage
Typically, you'd wrap your exported function with localize
:
// greeting.jsximport React from 'react';import localize from 'i18n-wp-plugin'; { return <h1 => </h1> ;} Greeting ;
When the wrapped component is rendered, the render behavior of the original component is used, but with access to localization props.
// index.jsximport React from 'react';import render from 'react-dom';import Greeting from './greeting'; ;
Some Background
I18n accepts a language-specific locale json file that contains the whitelisted translation strings for your JS project, uses that data to instantiate a Jed instance, and exposes a single translate
method with sugared syntax for interacting with Jed.
Key Hashing
In order to reduce file-size, i18n-wp-plugin allows the usage of hashed keys for lookup. This is a non-standard extension of the Jed standard which is enabled by supplying a header key key-hash
to specify a hash method (currently only sha1
is supported), as well as a hash length. For example sha1-4
uses the first 4 hexadecimal chars of the sha1 hash of the standard Jed lookup string. As a further optimization, variable hash lengths are available, potentially requiring multiple lookups per string: sha1-3-7
specifies that hash lengths of 3 to 7 are used in the file.
Example
Instead of providing the full English text, like here:
just the hash is used for lookup, resulting in a shorter file.
The generator of the jed file would usually try to choose the smallest hash length at which no hash collisions occur. In the above example a hash length of 1 (d
short for d2306dd8970ff616631a3501791297f31475e416
) is enough because there is only one string.
Note that when generating the jed file, all possible strings need to be taken into consideration for the collision calculation, as otherwise an untranslated source string would be provided with the wrong translation.