FrenchKiss.js is a blazing fast lightweight i18n library written in JavaScript, working both in the browser and NodeJS environments. It provides a simple and really fast solution for handling internationalization.
FrenchKiss is by now, the fastest i18n JS package out there, working 5 to 1000 times faster than any others by JIT compiling the translations, try it by running the benchmarks !
Minimum requirements:
🚀 Installation
Install with yarn:
$ yarn add frenchkiss
Or install using npm:
$ npm i frenchkiss
⏳ Running the tests
$ npm test
⚙️ Running the benchmarks
$ cd benchmark
$ yarn
$ yarn start
$ open ./result.html
📖 Documentation
Minimal code
Tell FrenchKiss what to return by simply giving it a table object, where the key is the search reference and the value is the already-translated string.
import frenchkiss from 'frenchkiss';
frenchkiss.locale('en');
frenchkiss.set('en', {
hello: 'Hello {name} !',
goodbye: 'Bye !',
});
frenchkiss.t('hello', {
name: 'John',
});
frenchkiss.locale(language?: string): string
Get or set the locale, it will define what table FrenchKiss have to work with.
Note: If you are working with NodeJS and concurrent requests, you can use the third parameter (language) of t()
to avoid language collision.
frenchkiss.set(language: string, table: object)
Define the translation table for the language. Any call to the specified language erase all the previously stored data.
frenchkiss.set('en', {
hello: 'Hi, ',
howareyou: 'How are you ?',
});
frenchkiss.t(key: string, params?: object, lang?: string): string
The most used method to returns translation. It's built with performance in mind
Here is what you should know about it :
- ✅ It does support multiple interpolation variable
- ✅ It supports interpolation.
- ✅ It supports
PLURAL
. - ✅ It supports
SELECT
. - ✅ It supports nested
PLURAL
, SELECT
and variables
. - ❌ It does not support nested keys (to keep it fast).
- ❌ It does not support date, number, currency formatting (maybe check for Intl.NumberFormat and Intl.DateTimeFormat).
set('en', {
hello: 'Hello {name} !',
});
t('hello');
t('hello', { name: 'John' });
t('hello', { name: 'Anna' });
Note: By default, if no parameters are given it will be interpreted as an empty string.
If you are working with concurrent connections it's also possible to use the third parameter lang
to force the language to use.
Doing a generator that forces the language use and pass it to your function can be what you are looking for.
frenchkiss.locale('fr');
frenchkiss.set('en', {
hello: 'Hello {name} !',
});
const generateLanguageTranslator = lang => {
return (key, params) => frenchkiss.t(key, params, lang);
};
const t = generateLanguageTranslator('en');
t('hello');
t('hello', { name: 'John' });
t('hello', { name: 'Anna' });
frenchkiss.extend(language: string, table: object)
Extend the translation table for the language. In contrary of set()
, the previously stored data will be kept.
frenchkiss.unset(language: string)
If you need to clean the data of a stored language for memory optimizations, unset is all you need.
frenchkiss.fallback(language?: string): string
Get or set the fallback. Define what table FrenchKiss will use to fallback in case the locale table doesn't have the required translation.
import { locale, fallback, set, t } from 'frenchkiss';
set('fr', {
hello: 'Bonjour, ',
});
set('en', {
hello: 'Hi, ',
howareyou: 'How are you ?',
});
locale('fr');
fallback('en');
t('hello');
t('howareyou');
frenchkiss.onMissingKey(fn: Function)
When the client requests a missing key, frenchKiss will returns the key as result. It's possible to handle it and return what you want or just send an event to your error reporting system.
frenchkiss.t('missingkey');
frenchkiss.onMissingKey(key => {
sendReport(`Missing the key "${key}" in ${frenchkiss.locale()} language.`);
return `An error happened (${key})`;
});
frenchkiss.t('missingkey');
frenchkiss.onMissingVariable(fn: Function)
It's possible to handle missing variables, sending errors to your monitoring server or handle it directly by returning something to replace with.
frenchkiss.set('en', {
hello: 'Hello {name} !',
});
frenchkiss.locale('en');
frenchkiss.t('hello');
frenchkiss.onMissingVariable((variable, key, language) => {
sendReport(`Missing the variable "${variable}" in ${language}->${key}.`);
return `[missing:${variable}]`;
});
frenchkiss.t('hello');
SELECT expression
If you need to display different text messages depending on the value of a variable, you need to translate all of those text messages... or you can handle this with a select ICU expression.
set('en', {
your_pet:
'You own {pet, select, dog{a good boy} cat{an evil cat} other{a {pet} ! What is that?}}!',
});
t('your_pet', { pet: 'dog' });
t('your_pet', { pet: 'cat' });
t('your_pet', { pet: 'rat' });
- The first parameter is the variable you want to check (
pet
). - The second parameter identifies this as a
select
expression type. - The third parameter is a pattern consisting of keys and their matching values.
Phrases support select expression, based on ICU FormatMessage.
PLURAL expression
It's basically the same as select, except you have to use the "=" symbol for direct checking.
set('en', {
bought_apple:
'I {count, plural, =0{bought no apples} =1{bought one apple} other{bought {count} apples}}!',
});
t('bought_apple', { count: 0 });
t('bought_apple', { count: 1 });
t('bought_apple', { count: 5 });
- The first parameter is the variable you want to check.
- The second parameter identifies this as a
plural
expression type. - The third parameter is a pattern consisting of keys and their matching values.
⚠️ Like the select expression, the plural is a lightweight version of ICU FormatMessage (offset:1
and #
are not integrated).
Plural Category
It's also possible to work with plural category. Multiple languages have multiple pluralization rules. You'll have to write a function returning the type to check.
The functions are not included by default in the package (not needed in most cases). But you can get some of them from PLURAL.md file.
import { locale, set, t, plural } from 'frenchkiss';
set('en', {
takemymoney:
'Take {N} dollar{N, plural, one{} =5{s! Take it} other{s}} please.',
});
set('fr', {
takemymoney:
"Prenez {N} dollar{N, plural, one{} =5{s! Prenez le} other{s}} s'il vous plait.",
});
plural('en', n => {
const i = Math.floor(Math.abs(n));
const v = n.toString().replace(/^[^.]*\.?/, '').length;
return i === 1 && v === 0 ? 'one' : 'other';
});
plural('fr', n => {
const i = Math.floor(Math.abs(n));
return i === 0 || i === 1 ? 'one' : 'other';
});
locale('en');
t('takemymoney', { N: 0 });
t('takemymoney', { N: 1 });
t('takemymoney', { N: 2 });
t('takemymoney', { N: 5 });
locale('fr');
t('takemymoney', { N: 0 });
t('takemymoney', { N: 1 });
t('takemymoney', { N: 2 });
t('takemymoney', { N: 5 });
Nested expressions
For advanced usage, it's also possible to do nested select, plural and interpolations.
set('fr', {
timeago: `Updated: {minutes, plural,
=0 {just now}
=1 {one minute ago}
other {
{minutes} minutes ago by {gender, select,
male {male}
female {female}
other {other}
}
}
}`,
});
t('timeago', { minutes: 0, gender: 'male' });
t('timeago', { minutes: 1, gender: 'male' });
t('timeago', { minutes: 5, gender: 'male' });
🔗 Related projects
- i18next-scanner: Scan your code, extract translation keys/values, and merge them into i18n resource files.
- i18n-extract: Manage localization with static analysis. (report unused/missing/duplicated key, extract them).