Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
The i18n npm package is a lightweight internationalization library for Node.js applications. It provides a simple way to manage translations and localization for your application, supporting multiple languages and locales.
Basic Setup
This code demonstrates how to set up the i18n package in a Node.js application. It configures the locales and the directory where translation files are stored, and initializes i18n middleware for use in an Express app.
const i18n = require('i18n');
// Configure i18n
i18n.configure({
locales: ['en', 'es'],
directory: __dirname + '/locales'
});
// Initialize i18n
app.use(i18n.init);
Translation
This feature allows you to translate strings based on the current locale. You can also include variables in the translation strings.
i18n.__('Hello'); // Returns 'Hello' in the current locale
// With variables
i18n.__('Hello %s', 'John'); // Returns 'Hello John' in the current locale
Changing Locale
This code demonstrates how to change the current locale dynamically. After setting the locale to 'es' (Spanish), the translation for 'Hello' will be returned in Spanish.
i18n.setLocale('es');
i18n.__('Hello'); // Returns 'Hola' if the Spanish translation is available
Using in Templates
This feature shows how to use i18n translations within template engines like EJS. The translated string is passed to the template as a variable.
app.get('/', function(req, res) {
res.render('index', { title: res.__('Hello') });
});
i18next is a powerful internationalization framework for JavaScript, including both client-side and server-side support. It offers more advanced features compared to i18n, such as nested translations, pluralization, and context-based translations.
Polyglot.js is a small i18n helper library that provides a simple way to manage translations. It is less feature-rich than i18n but is very lightweight and easy to integrate into small projects.
Node-Polyglot is a Node.js version of Polyglot.js, offering similar functionality for server-side applications. It is a good alternative for projects that require a minimalistic approach to internationalization.
Lightweight simple translation module with dynamic json storage. Supports plain vanilla node.js apps and should work with any framework (like express, restify and probably more) that exposes an app.use()
method passing in res
and req
objects.
Uses common __('...') syntax in app and templates.
Stores language files in json files compatible to webtranslateit json format.
Adds new strings on-the-fly when first used in your app.
No extra parsing needed.
npm install i18n --save
npm test
// load modules
var express = require('express'),
i18n = require("i18n");
Minimal example, just setup two locales and a project specific directory
i18n.configure({
locales:['en', 'de'],
directory: __dirname + '/locales'
});
now you are ready to use a global i18n.__('Hello')
.
In your cli, when not registered to a specific object:
var greeting = i18n.__('Hello');
Global assumes you share a common state of localization in any time and any part of your app. This is usually fine in cli-style scripts. When serving responses to http requests you'll need to make sure that scope is NOT shared globally but attached to your request object.
In an express app, you might use i18n.init to gather language settings of your visitors and also bind your helpers to response object honoring request objects locale, ie:
// Configuration
app.configure(function() {
[...]
// default: using 'accept-language' header to guess language settings
app.use(i18n.init);
[...]
});
in your apps methods:
app.get('/de', function(req, res){
var greeting = res.__('Hello');
});
in your templates (depending on your template engine)
<%= __('Hello') %>
${__('Hello')}
See tested examples inside /examples
for some inspiration in node 4.x / 5.x or browse these gists:
PLEASE NOTE: Those gist examples worked until node 0.12.x only
For serving the same static files with different language url, you could:
app.use(express.static(__dirname + '/www'));
app.use('/en', express.static(__dirname + '/www'));
app.use('/de', express.static(__dirname + '/www'));
The api is subject of incremental development. That means, it should not change nor remove any aspect of the current api but new features and options will get added that don't break compatibility backwards within a major version.
You should configure your application once to bootstrap all aspects of i18n
. You should not configure i18n in each loop when used in an http based scenario. During configuration, i18n
reads all known locales into memory and prepares to keep that superfast object in sync with your files in filesystem as configured
i18n.configure({
locales:['en', 'de'],
directory: __dirname + '/locales'
});
Since 0.7.0 you may even omit the locales
setting and just configure a directory
. i18n
will read all files within that directory and detect all given locales by their filenames.
i18n.configure({
directory: __dirname + '/locales'
});
i18n.configure({
// setup some locales - other locales default to en silently
locales:['en', 'de'],
// fall back from Dutch to German
fallbacks:{'nl': 'de'},
// you may alter a site wide default locale
defaultLocale: 'de',
// sets a custom cookie name to parse locale settings from - defaults to NULL
cookie: 'yourcookiename',
// query parameter to switch locale (ie. /home?lang=ch) - defaults to NULL
queryParameter: 'lang',
// where to store json files - defaults to './locales' relative to modules directory
directory: './mylocales',
// controll mode on directory creation - defaults to NULL which defaults to umask of process user. Setting has no effect on win.
directoryPermissions: '755',
// watch for changes in json files to reload locale on updates - defaults to false
autoReload: true,
// whether to write new locale information to disk - defaults to true
updateFiles: false,
// what to use as the indentation unit - defaults to "\t"
indent: "\t",
// setting extension of json files - defaults to '.json' (you might want to set this to '.js' according to webtranslateit)
extension: '.js',
// setting prefix of json files name - default to none '' (in case you use different locale files naming scheme (webapp-en.json), rather then just en.json)
prefix: 'webapp-',
// enable object notation
objectNotation: false,
// setting of log level DEBUG - default to require('debug')('i18n:debug')
logDebugFn: function (msg) {
console.log('debug', msg);
},
// setting of log level WARN - default to require('debug')('i18n:warn')
logWarnFn: function (msg) {
console.log('warn', msg);
},
// setting of log level ERROR - default to require('debug')('i18n:error')
logErrorFn: function (msg) {
console.log('error', msg);
}
// object or [obj1, obj2] to bind the i18n api and current locale to - defaults to null
register: global
});
The locale itself is gathered directly from the browser by header, cookie or query parameter depending on your setup.
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:
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.
register
optionEsp. 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.
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:
i18n.configure({
locales: ['en', 'de'],
register: global
});
i18n.setLocale('de');
__('Hello'); // --> Hallo`
When used as middleware in frameworks like express to setup the current environment for each loop. In contrast to configure the i18n.init()
should be called within each request-response-cycle.
var app = express();
app.use(cookieParser());
app.use(i18n.init);
When i18n is used like this, the i18n.init()
tries to
Express would call i18n.init(req, res, next)
, which is "classic" and adopted by many frameworks. Thus i18n
will attach it's api to that schema:
{
req: {
locals: {},
res: {
locals: {},
}
}
}
and add it's extra attributes and methods, like so:
{
req: {
locals: {
locale: "de",
__: [function],
__n: [function],
[...]
},
res: {
locals: {
locale: "de",
__: [function],
__n: [function],
[...]
},
locale: "de",
__: [function],
__n: [function],
[...]
},
locale: "de",
__: [function],
__n: [function],
[...]
}
}
Now each local object (ie. res.locals) is setup with it's own "private" locale and methods to get the appropriate translation from the global catalog.
Translates a single phrase and adds it to locales if unknown. Returns translated parsed and substituted string.
// template and global (this.locale == 'de')
__('Hello'); // Hallo
__('Hello %s', 'Marcus'); // Hallo Marcus
__('Hello {{name}}', { name: 'Marcus' }); // Hallo Marcus
// scoped via req object (req.locale == 'de')
req.__('Hello'); // Hallo
req.__('Hello %s', 'Marcus'); // Hallo Marcus
req.__('Hello {{name}}', { name: 'Marcus' }); // Hallo Marcus
// scoped via res object (res.locale == 'de')
res.__('Hello'); // Hallo
res.__('Hello %s', 'Marcus'); // Hallo Marcus
res.__('Hello {{name}}', { name: 'Marcus' }); // Hallo Marcus
// passing specific locale
__({phrase: 'Hello', locale: 'fr'}); // Salut
__({phrase: 'Hello %s', locale: 'fr'}, 'Marcus'); // Salut Marcus
__({phrase: 'Hello {{name}}', locale: 'fr'}, { name: 'Marcus' }); // Salut Marcus
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 count
parameter.
// template and global (this.locale == 'de')
__n("%s cat", "%s cats", 1); // 1 Katze
__n("%s cat", "%s cats", 3); // 3 Katzen
// scoped via req object (req.locale == 'de')
req.__n("%s cat", "%s cats", 1); // 1 Katze
req.__n("%s cat", "%s cats", 3); // 3 Katzen
// scoped via res object (res.locale == 'de')
res.__n("%s cat", "%s cats", 1); // 1 Katze
res.__n("%s cat", "%s cats", 3); // 3 Katzen
// passing specific locale
__n({singular: "%s cat", plural: "%s cats", locale: "fr"}, 1); // 1 chat
__n({singular: "%s cat", plural: "%s cats", locale: "fr"}, 3); // 3 chat
__n({singular: "%s cat", plural: "%s cats", locale: "fr", count: 1}); // 1 chat
__n({singular: "%s cat", plural: "%s cats", locale: "fr", count: 3}); // 3 chat
Returns a list of translations for a given phrase in each language.
i18n.__l('Hello'); // --> [ 'Hallo', 'Hello' ]
This will be usefull when setting up localized routes for example (kudos to @xpepermint, #150):
// this will match routes
// EN --> /:locale/products/:id?
// ES --> /:locale/productos/:id?
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...
Returns a hashed list of translations for a given phrase in each language.
i18n.__h('Hello'); // --> [ { de: 'Hallo' }, { en: 'Hello' } ]
i18n.__hn() to get plurals will come up in another release...
Setting the current locale (ie.: en
) globally or in current scope.
setLocale('de');
setLocale(req, 'de');
req.setLocale('de');
Use setLocale to change any initial locale that was set in i18n.init()
. You get more control on how when and which objects get setup with a given locale. Locale values are inherited within the given schema like in i18n.init()
Let's see some examples:
i18n.setLocale(req, 'ar'); // --> req: مرحبا res: مرحبا res.locals: مرحبا
i18n.setLocale(res, 'ar'); // --> req: Hallo res: مرحبا res.locals: مرحبا
i18n.setLocale(res.locals, 'ar'); // --> req: Hallo res: Hallo res.locals: مرحبا
You'll get even more controll when passing an array of objects:
i18n.setLocale([req, res.locals], req.params.lang); // --> req: مرحبا res: Hallo res.locals: مرحبا
or disable inheritance by passing true as third parameter:
i18n.setLocale(res, 'ar', true); // --> req: Hallo res: مرحبا res.locals: Hallo
Getting the current locale (ie.: en
) from current scope or globally.
getLocale(); // --> de
getLocale(req); // --> de
req.getLocale(); // --> de
Returns a whole catalog optionally based on current scope and locale.
getCatalog(); // returns all locales
getCatalog('de'); // returns just 'de'
getCatalog(req); // returns current locale of req
getCatalog(req, 'de'); // returns just 'de'
req.getCatalog(); // returns current locale of req
req.getCatalog('de'); // returns just 'de'
In general i18n has to be attached to the response object to let it's public api get accessible in your templates and methods. As of 0.4.0 i18n tries to do so internally via i18n.init
, as if you were doing it in app.configure
on your own:
app.use(function(req, res, next) {
// express helper for natively supported engines
res.locals.__ = res.__ = function() {
return i18n.__.apply(req, arguments);
};
[...]
next();
});
Different engines need different implementations, so yours might miss or not work with the current default helpers. This one showing an example for mustache in express:
// register helper as a locals function wrapped as mustache expects
app.use(function (req, res, next) {
// mustache helper
res.locals.__ = function () {
return function (text, render) {
return i18n.__.apply(req, arguments);
};
};
[...]
next();
});
You could still setup your own implementation. Please refer to Examples below, post an issue or contribute your setup.
As inspired by gettext there is currently support for sprintf-style expressions. You can also use mustache syntax for named parameters.
var greeting = __('Hello %s, how are you today?', 'Marcus');
this puts Hello Marcus, how are you today?. You might add endless arguments and even nest it.
var greeting = __('Hello %s, how are you today? How was your %s.', 'Marcus', __('weekend'));
which puts Hello Marcus, how are you today? How was your weekend.
You might need to have repeated references to the same argument, which can be done with sprintf.
var example = __('%1$s, %1$s, %1$s', 'repeat');
which puts
repeat, repeat, repeat
In some cases the argument order will need to be switched for different locales. The arguments can be strings, floats, numbers, etc.
var example = __('%2$d then %1$s then %3$.2f', 'First', 2, 333.333);
which puts
2 then First then 333.33
You may also use mustache syntax for your message strings. To pass named parameters to your message, just provide an object as the last parameter. You can still pass unnamed parameters by adding additional arguments.
var greeting = __('Hello {{name}}, how are you today?', { name: 'Marcus' });
this puts Hello Marcus, how are you today?. You might also combine it with sprintf arguments...
var greeting = __('Hello {{name}}, how was your %s.', __('weekend'), { name: 'Marcus' });
and even nest it...
var greeting = __( __('Hello {{name}}, how was your %s?', { name: 'Marcus' }), __('weekend') );
which both put Hello Marcus, how was your weekend.
you might even use dynamic variables as they get interpreted on the fly. Better make sure no user input finds it's way to that point as they all get added to the en.js
file if not yet existing.
var greetings = ['Hi', 'Hello', 'Howdy'];
for (var i=0; i < greetings.length; i++) {
console.log( __(greetings[i]) );
};
which puts
Hi
Hello
Howdy
two different plural forms are supported as response to count
:
var singular = __n('%s cat', '%s cats', 1);
var plural = __n('%s cat', '%s cats', 3);
this puts 1 cat or 3 cats and again these could get nested:
var singular = __n('There is one monkey in the %%s', 'There are %d monkeys in the %%s', 1, 'tree');
var plural = __n('There is one monkey in the %%s', 'There are %d monkeys in the %%s', 3, 'tree');
putting There is one monkey in the tree or There are 3 monkeys in the tree
Will get modular support for different storage engines, currently just json files are stored in filesystem.
the above will automatically generate a en.json
by default inside ./locales/
which looks like
{
"Hello": "Hello",
"Hello %s, how are you today?": "Hello %s, how are you today?",
"weekend": "weekend",
"Hello %s, how are you today? How was your %s.": "Hello %s, how are you today? How was your %s.",
"Hi": "Hi",
"Howdy": "Howdy",
"%s cat": {
"one": "%s cat",
"other": "%s cats"
},
"There is one monkey in the %%s": {
"one": "There is one monkey in the %%s",
"other": "There are %d monkeys in the %%s"
},
"tree": "tree"
}
that file can be edited or just uploaded to webtranslateit for any kind of collaborative translation workflow:
{
"Hello": "Hallo",
"Hello %s, how are you today?": "Hallo %s, wie geht es dir heute?",
"weekend": "Wochenende",
"Hello %s, how are you today? How was your %s.": "Hallo %s, wie geht es dir heute? Wie war dein %s.",
"Hi": "Hi",
"Howdy": "Hallöchen",
"%s cat": {
"one": "%s Katze",
"other": "%s Katzen"
},
"There is one monkey in the %%s": {
"one": "Im %%s sitzt ein Affe",
"other": "Im %%s sitzen %d Affen"
},
"tree": "Baum"
}
Logging any kind of output is moved to debug module. To let i18n output anything run your app with DEBUG
env set like so:
$ DEBUG=i18n:* node app.js
i18n exposes three log-levels:
if you only want to get errors and warnings reported start your node server like so:
$ DEBUG=i18n:warn,i18n:error node app.js
Combine those settings with you existing application if any of you other modules or libs also uses debug
You can configure i18n to use a custom logger. For example attach some simple console
-logging:
i18n.configure({
...
// setting of log level DEBUG - default to require('debug')('i18n:debug')
logDebugFn: function (msg) {
console.log('debug', msg);
},
// setting of log level WARN - default to require('debug')('i18n:warn')
logWarnFn: function (msg) {
console.log('warn', msg);
},
// setting of log level ERROR - default to require('debug')('i18n:error')
logErrorFn: function (msg) {
console.log('error', msg);
}
});
In addition to the traditional, linear translation lists, i18n also supports hierarchical translation catalogs.
To enable this feature, be sure to set objectNotation
to true
in your configure()
call. Note: If you can't or don't want to use .
as a delimiter, set objectNotation
to any other delimiter you like.
Instead of calling __("Hello")
you might call __("greeting.formal")
to retrieve a formal greeting from a translation document like this one:
"greeting": {
"formal": "Hello",
"informal": "Hi",
"placeholder": {
"formal": "Hello %s",
"informal": "Hi %s"
}
}
In the document, the translation terms, which include placeholders, are nested inside the "greeting" translation. They can be accessed and used in the same way, like so __('greeting.placeholder.informal', 'Marcus')
.
Object notation also supports pluralization. When making use of it, the "one" and "other" entries are used implicitly for an object in the translation document. For example, consider the following document:
"cat": {
"one": "Katze",
"other": "Katzen"
}
When accessing these, you would use __n("cat", "cat", 3)
to tell i18n to use both the singular and plural form of the "cat" entry. Naturally, you could also access these members explicitly with __("cat.one")
and __("cat.other")
.
When starting a project from scratch, your translation documents will probably be empty. i18n takes care of filling your translation documents for you. Whenever you use an unknown object, it is added to the translation documents.
By default, when using object notation, the provided string literal will be inserted and returned as the default string. As an example, this is what the "greeting" object shown earlier would look like by default:
"greeting": {
"formal": "greeting.formal",
"informal": "greeting.informal"
}
In case you would prefer to have a default string automatically inserted and returned, you can provide that default string by appending it to your object literal, delimited by a :
. For example:
__("greeting.formal:Hello")
__("greeting.placeholder.informal:Hi %s")
i18n.setLocale()
and i18n.init()
refactored to comply with most common use cases, much better test coverage and docsautoReload
, directoryPermissions
, register
, queryParameter
, read locales from filenames with empty locales
option (#134)i18n.setLocale()
i18n.getLocales()
, custom logger, fallback[s];i18n.setLocale()
for req
and res
i18n.getCatalog
this.locale
to __
and __n
Copyright (c) 2011-2016 Marcus Spiegel marcus.spiegel@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
FAQs
lightweight translation module with dynamic json storage
The npm package i18n receives a total of 120,270 weekly downloads. As such, i18n popularity was classified as popular.
We found that i18n demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.