function i18nTranslate(phrase) { var msg; var argv = parseArgv(arguments); var namedValues = argv[0]; var args = argv[1]; // called like __({phrase: "Hello", locale: "en"}) if (typeof phrase === 'object') { if (typeof phrase.locale === 'string' && typeof phrase.phrase === 'string') { msg = translate(phrase.locale, phrase.phrase); } } // called like __("Hello") else { // get translated message with locale from scope (deprecated) or object msg = translate(getLocaleFromObject(this), phrase); } // postprocess to get compatible to plurals if (typeof msg === 'object' && msg.one) { msg = msg.one; } // in case there is no 'one' but an 'other' rule if (typeof msg === 'object' && msg.other) { msg = msg.other; } // head over to postProcessing return postProcess(msg, namedValues, args); }
...
```js
i18n.configure({
locales:['en', 'de'],
directory: __dirname + '/locales'
});
```
now you are ready to use a global `i18n.__('Hello')`.
## Example usage in global scope
In your cli, when not registered to a specific object:
```js
var greeting = i18n.__('Hello');
...
function i18nTranslationHash(phrase) { var translations = []; Object.keys(locales).sort().forEach(function(l) { var hash = {}; hash[l] = i18n.__({ phrase: phrase, locale: l }); translations.push(hash); }); return translations; }
...
app.get( __l('/:locale/products/:id?'), function (req, res) {
// guess what you might use req.params.locale for?
});
```
> i18n.__ln() to get plurals will come up in another release...
### i18n.__h()
Returns a hashed list of translations for a given phrase in each language.
```js
i18n.__h('Hello'); // --> [ { de: 'Hallo' }, { en: 'Hello' } ]
```
...
function i18nTranslationList(phrase) { var translations = []; Object.keys(locales).sort().forEach(function(l) { translations.push(i18n.__({ phrase: phrase, locale: l })); }); return translations; }
...
* Simple Variable Replacement (similar to mustache placeholders)
* SelectFormat (ie. switch msg based on gender)
* PluralFormat (see above and [ranges](#ranged-interval-support))
Combinations of those give superpower, but should get tested well (contribute your use case, please!) on integration.
### i18n.__l()
Returns a list of translations for a given phrase in each language.
```js
i18n.__l('Hello'); // --> [ 'Hallo', 'Hello' ]
```
...
function i18nMessageformat(phrase) { var msg, mf, f; var targetLocale = defaultLocale; var argv = parseArgv(arguments); var namedValues = argv[0]; var args = argv[1]; // called like __({phrase: "Hello", locale: "en"}) if (typeof phrase === 'object') { if (typeof phrase.locale === 'string' && typeof phrase.phrase === 'string') { msg = phrase.phrase; targetLocale = phrase.locale; } } // called like __("Hello") else { // get translated message with locale from scope (deprecated) or object msg = phrase; targetLocale = getLocaleFromObject(this); } msg = translate(targetLocale, msg); // --- end get msg // now head over to Messageformat // and try to cache instance if (MessageformatInstanceForLocale[targetLocale]) { mf = MessageformatInstanceForLocale[targetLocale]; } else { mf = new Messageformat(targetLocale); mf.compiledFunctions = {}; MessageformatInstanceForLocale[targetLocale] = mf; } // let's try to cache that function if (mf.compiledFunctions[msg]) { f = mf.compiledFunctions[msg]; } else { f = mf.compile(msg); mf.compiledFunctions[msg] = f; } return postProcess(f(namedValues), namedValues, args); }
...
__n('%s cat', 5); // --> 5 кошек
__n('%s cat', 6); // --> 6 кошек
__n('%s cat', 21); // --> 21 кошка
```
> __Note__ i18n.__n() will add a blueprint ("one, other" or "one, few, other" for eaxmple) for each locale
to your json on updateFiles in a future version.
### i18n.__mf()
Supports the advanced MessageFormat as provided by excellent [messageformat module](https://www.npmjs.com/package/messageformat).
You should definetly head over to [messageformat.github.io](https://messageformat.github.io) for a guide to MessageFormat. i18n
takes care of `new MessageFormat('en').compile(msg);` with the current `msg` loaded from it's json files and cache
that complied fn in memory. So in short you might use it similar to `__()` plus extra object to accomblish MessageFormat's
formating. Ok, some examples:
```js
// assume res is set to german
res.setLocale('de');
...
function i18nTranslatePlural(singular, plural, count) { var msg, namedValues, targetLocale, args = []; // Accept an object with named values as the last parameter if (argsEndWithNamedObject(arguments)) { namedValues = arguments[arguments.length - 1]; args = arguments.length >= 5 ? Array.prototype.slice.call(arguments, 3, -1) : []; } else { namedValues = {}; args = arguments.length >= 4 ? Array.prototype.slice.call(arguments, 3) : []; } // called like __n({singular: "%s cat", plural: "%s cats", locale: "en"}, 3) if (typeof singular === 'object') { if ( typeof singular.locale === 'string' && typeof singular.singular === 'string' && typeof singular.plural === 'string' ) { targetLocale = singular.locale; msg = translate(singular.locale, singular.singular, singular.plural); } args.unshift(count); // some template engines pass all values as strings -> so we try to convert them to numbers if (typeof plural === 'number' || parseInt(plural, 10) + '' === plural) { count = plural; } // called like __n({singular: "%s cat", plural: "%s cats", locale: "en", count: 3}) if (typeof singular.count === 'number' || typeof singular.count === 'string') { count = singular.count; args.unshift(plural); } } else { // called like __n('cat', 3) if (typeof plural === 'number' || parseInt(plural, 10) + '' === plural) { count = plural; // we add same string as default // which efectivly copies the key to the plural.value // this is for initialization of new empty translations plural = singular; args.unshift(count); args.unshift(plural); } // called like __n('%s cat', '%s cats', 3) // get translated message with locale from scope (deprecated) or object msg = translate(getLocaleFromObject(this), singular, plural); targetLocale = getLocaleFromObject(this); } if (count === null) count = namedValues.count; // enforce number count = parseInt(count, 10); // find the correct plural rule for given locale if (typeof msg === 'object') { var p; // create a new Plural for locale // and try to cache instance if (PluralsForLocale[targetLocale]) { p = PluralsForLocale[targetLocale]; } else { // split locales with a region code var lc = targetLocale.toLowerCase().split(/[_-\s]+/) .filter(function(el){ return true && el; }); // take the first part of locale, fallback to full locale p = new MakePlural(lc[0] || targetLocale); PluralsForLocale[targetLocale] = p; } // fallback to 'other' on case of missing translations msg = msg[p(count)] || msg.other; } // head over to postProcessing return postProcess(msg, namedValues, args, count); }
... // passing specific locale __({phrase: 'Hello', locale: 'fr'}); // Salut __({phrase: 'Hello %s', locale: 'fr'}, 'Marcus'); // Salut Marcus __({phrase: 'Hello function i18n.__n', locale: 'fr'}, { name: 'Marcus' }); // Salut Marcus ``` ### i18n.__n() Plurals translation of a single phrase. Singular and plural forms will get added to locales if unknown. Returns translated parsed and substituted string based on last `count` parameter. ```js // short syntax is best suited for reading // --> writes '%s cat' to both `one` and `other` plurals __n('%s cat', 1) // --> 1 Katze ...
function i18nAddLocale(locale) { read(locale); }
n/a
function i18nConfigure(opt) { // reset locales locales = {}; // Provide custom API method aliases if desired // This needs to be processed before the first call to applyAPItoObject() if (opt.api && typeof opt.api === 'object') { for (var method in opt.api) { if (opt.api.hasOwnProperty(method)) { var alias = opt.api[method]; if (typeof api[method] !== 'undefined') { api[method] = alias; } } } } // you may register i18n in global scope, up to you if (typeof opt.register === 'object') { register = opt.register; // or give an array objects to register to if (Array.isArray(opt.register)) { register = opt.register; register.forEach(function(r) { applyAPItoObject(r); }); } else { applyAPItoObject(opt.register); } } // sets a custom cookie name to parse locale settings from cookiename = (typeof opt.cookie === 'string') ? opt.cookie : null; // query-string parameter to be watched - @todo: add test & doc queryParameter = (typeof opt.queryParameter === 'string') ? opt.queryParameter : null; // where to store json files directory = (typeof opt.directory === 'string') ? opt.directory : path.join(__dirname, 'locales'); // permissions when creating new directories directoryPermissions = (typeof opt.directoryPermissions === 'string') ? parseInt(opt.directoryPermissions, 8) : null; // write new locale information to disk updateFiles = (typeof opt.updateFiles === 'boolean') ? opt.updateFiles : true; // sync locale information accros all files syncFiles = (typeof opt.syncFiles === 'boolean') ? opt.syncFiles : false; // what to use as the indentation unit (ex: "\t", " ") indent = (typeof opt.indent === 'string') ? opt.indent : '\t'; // json files prefix prefix = (typeof opt.prefix === 'string') ? opt.prefix : ''; // where to store json files extension = (typeof opt.extension === 'string') ? opt.extension : '.json'; // setting defaultLocale defaultLocale = (typeof opt.defaultLocale === 'string') ? opt.defaultLocale : 'en'; // auto reload locale files when changed autoReload = (typeof opt.autoReload === 'boolean') ? opt.autoReload : false; // enable object notation? objectNotation = (typeof opt.objectNotation !== 'undefined') ? opt.objectNotation : false; if (objectNotation === true) objectNotation = '.'; // read language fallback map fallbacks = (typeof opt.fallbacks === 'object') ? opt.fallbacks : {}; // setting custom logger functions logDebugFn = (typeof opt.logDebugFn === 'function') ? opt.logDebugFn : debug; logWarnFn = (typeof opt.logWarnFn === 'function') ? opt.logWarnFn : warn; logErrorFn = (typeof opt.logErrorFn === 'function') ? opt.logErrorFn : error; // when missing locales we try to guess that from directory opt.locales = opt.locales || guessLocales(directory); // implicitly read all locales if (Array.isArray(opt.locales)) { opt.locales.forEach(function(l) { read(l); }); // auto reload locale files when changed if (autoReload) { // watch changes of locale files (it's called twice because fs.watch is still unstable) fs.watch(directory, function(event, filename) { var localeFromFile = guessLocaleFromFile(filename); if (localeFromFile && opt.locales.indexOf(localeFromFile) > -1) { logDebug('Auto reloading locale file "' + filename + '".'); read(localeFromFile); } }); } } }
...
```
## Configure
Minimal example, just setup two locales and a project specific directory
```js
i18n.configure({
locales:['en', 'de'],
directory: __dirname + '/locales'
});
```
now you are ready to use a global `i18n.__('Hello')`.
## Example usage in global scope
...
function i18nGetCatalog(object, locale) { var targetLocale; // called like i18n.getCatalog(req) if (typeof object === 'object' && typeof object.locale === 'string' && locale === undefined) { targetLocale = object.locale; } // called like i18n.getCatalog(req, 'en') if (!targetLocale && typeof object === 'object' && typeof locale === 'string') { targetLocale = locale; } // called like req.getCatalog('en') if (!targetLocale && locale === undefined && typeof object === 'string') { targetLocale = object; } // called like req.getCatalog() if (!targetLocale && object === undefined && locale === undefined && typeof this.locale === 'string' ) { if (register && register.GLOBAL) { targetLocale = ''; } else { targetLocale = this.locale; } } // called like i18n.getCatalog() if (targetLocale === undefined || targetLocale === '') { return locales; } if (!locales[targetLocale] && fallbacks[targetLocale]) { targetLocale = fallbacks[targetLocale]; } if (locales[targetLocale]) { return locales[targetLocale]; } else { logWarn('No catalog found for "' + targetLocale + '"'); return false; } }
...
```js
getLocale(); // --> de
getLocale(req); // --> de
req.getLocale(); // --> de
```
### i18n.getCatalog()
Returns a whole catalog optionally based on current scope and locale.
```js
getCatalog(); // returns all locales
getCatalog('de'); // returns just 'de'
...
function i18nGetLocale(request) { // called like i18n.getLocale(req) if (request && request.locale) { return request.locale; } // called like req.getLocale() return this.locale || defaultLocale; }
...
or disable inheritance by passing true as third parameter:
```js
i18n.setLocale(res, 'ar', true); // --> req: Hallo res: مرحبا res.locals: Hallo
```
### i18n.getLocale()
Getting the current locale (ie.: `en`) from current scope or globally.
```js
getLocale(); // --> de
getLocale(req); // --> de
req.getLocale(); // --> de
...
function i18nGetLocales() { return Object.keys(locales); }
...
* __fixed__: typos, missing and wrong docs, plural bugs like: #210, #191, #190
* 0.7.0:
* __improved__: `i18n.setLocale()` and `i18n.init()` refactored to comply with most common use cases, much better test coverage
and docs
* __new__: options: `autoReload`, `directoryPermissions`, `register`, `queryParameter`, read locales from filenames with empty
`locales` option (#134)
* __fixed__: typos, missing and wrong docs, issues related to `i18n.setLocale()`
* 0.6.0:
* __improved__: Accept-Language header parsing to ICU, delimiters with object notation, jshint, package.json, README;
* __new__: prefix for locale files, `i18n.getLocales()`, custom logger, fallback[
s];
* __fixed__: typos, badges, plural (numbers), `i18n.setLocale()` for `req` _and_ `res`
* 0.5.0: feature release; added {{mustache}} parsing by #85, added "object.notation" by #110, fixed buggy req.__() implementation
by #111 and closed 13 issues
* 0.4.1: stable release; merged/closed: #57, #60, #67 typo fixes; added more examples and new features: #53, #65, #66 - and some
more api reference
* 0.4.0: stable release; closed: #22, #24, #4, #10, #54; added examples, clarified concurrency usage in different template engines
, added `i18n.getCatalog`
* 0.3.9: express.js usage, named api, jscoverage + more test, refactored configure, closed: #51, #20, #16, #49
* 0.3.8: fixed: #44, #49; merged: #47, #45, #50; added: #33; updated: README
* 0.3.7: tests by mocha.js, added `this.locale` to `__` and `__n`
...
function i18nInit(request, response, next) { if (typeof request === 'object') { // guess requested language/locale guessLanguage(request); // bind api to req applyAPItoObject(request); // looks double but will ensure schema on api refactor i18n.setLocale(request, request.locale); } else { return logError('i18n.init must be called with one parameter minimum, ie. i18n.init(req)'); } if (typeof response === 'object') { applyAPItoObject(response); // and set that locale to response too i18n.setLocale(response, request.locale); } // head over to next callback when bound as middleware if (typeof next === 'function') { return next(); } }
...
In case of cookie you will also need to enable cookies for your application. For express this done by adding `app.use(express.cookieParser
())`). Now use the same cookie name when setting it in the user preferred language, like here:
```js
res.cookie('yourcookiename', 'de', { maxAge: 900000, httpOnly: true });
```
After this and until the cookie expires, `i18n.init()` will get the value of the cookie
to set that language instead of default for every page.
#### Some words on `register` option
Esp. when used in a cli like scriptyou won't use any `i18n.init()` to guess language settings from your user. Thus `i18n` won
't bind itself to any `res` or `req` object and will work like a static module.
```js
var anyObject = {};
...
function i18nRemoveLocale(locale) { delete locales[locale]; }
n/a
function i18nSetLocale(object, locale, skipImplicitObjects) { // when given an array of objects => setLocale on each if (Array.isArray(object) && typeof locale === 'string') { for (var i = object.length - 1; i >= 0; i--) { i18n.setLocale(object[i], locale, true); } return i18n.getLocale(object[0]); } // defaults to called like i18n.setLocale(req, 'en') var targetObject = object; var targetLocale = locale; // called like req.setLocale('en') or i18n.setLocale('en') if (locale === undefined && typeof object === 'string') { targetObject = this; targetLocale = object; } // consider a fallback if (!locales[targetLocale] && fallbacks[targetLocale]) { targetLocale = fallbacks[targetLocale]; } // now set locale on object targetObject.locale = locales[targetLocale] ? targetLocale : defaultLocale; // consider any extra registered objects if (typeof register === 'object') { if (Array.isArray(register) && !skipImplicitObjects) { register.forEach(function(r) { r.locale = targetObject.locale; }); } else { register.locale = targetObject.locale; } } // consider res if (targetObject.res && !skipImplicitObjects) { // escape recursion // @see - https://github.com/balderdashy/sails/pull/3631 // - https://github.com/mashpie/i18n-node/pull/218 if (targetObject.res.locals) { i18n.setLocale(targetObject.res, targetObject.locale, true); i18n.setLocale(targetObject.res.locals, targetObject.locale, true); } else { i18n.setLocale(targetObject.res, targetObject.locale); } } // consider locals if (targetObject.locals && !skipImplicitObjects) { // escape recursion // @see - https://github.com/balderdashy/sails/pull/3631 // - https://github.com/mashpie/i18n-node/pull/218 if (targetObject.locals.res) { i18n.setLocale(targetObject.locals, targetObject.locale, true); i18n.setLocale(targetObject.locals.res, targetObject.locale, true); } else { i18n.setLocale(targetObject.locals, targetObject.locale); } } return i18n.getLocale(targetObject); }
...
var anyObject = {};
i18n.configure({
locales: ['en', 'de'],
register: anyObject
});
anyObject.setLocale('de');
anyObject.__('Hallo'); // --> Hallo`
```
Cli usage is a special use case, as we won't need to maintain any transaction / concurrency aware setting of locale, so you
could even choose to bind `i18n` to _global_ scope of node:
```js
i18n.configure({
...