Formatting
March 26, 2026 · View on GitHub
Starting with i18next>=21.3.0 you can use the built-in formatting functions based on the Intl API.
{% hint style="warning" %}
Starting with i18next v26.0.0, the legacy interpolation.format function will be removed. The built-in Formatter is now always used. For custom formatting, use i18next.services.formatter.add() or .addCached() as described below, or provide a custom Formatter module via .use().
{% endhint %}
You may need to polyfill the Intl API:
- Intl.PluralRules
- Intl.RelativeTimeFormat
- Intl.ListFormat
- Intl.DisplayNames
- Intl.NumberFormat (ES2020)
- Intl.Locale
- Intl.getCanonicalLocales
- Intl.DateTimeFormat (ES2020)
{% hint style="info" %} 🎓 Check out this topic in the i18next crash course video. {% endhint %}
Basic usage
The translation string has the following signature:
{
"key": "Some format {{value, formatname}}",
"keyWithOptions": "Some format {{value, formatname(option1Name: option1Value; option2Name: option2Value)}}"
}
{% hint style="info" %} Use a "semicolon" delimited list of options. {% endhint %}
{% tabs %} {% tab title="JavaScript" %} Passing options to the formatting:
- In the translation string using
{{value, formatname(options1: options1Value)}} - Using the root level options when calling
t('key', { option1: option1Value }) - Using the per value options like:
t('key', { formatParams: { value: { option1: option1Value } })
Samples
// JSON
{
"intlNumber": "Some {{val, number}}",
"intlNumberWithOptions": "Some {{val, number(minimumFractionDigits: 2)}}"
}
i18next.t('intlNumber', { val: 1000 });
// --> Some 1,000
i18next.t('intlNumber', { val: 1000.1, minimumFractionDigits: 3 });
// --> Some 1,000.100
i18next.t('intlNumber', { val: 1000.1, formatParams: { val: { minimumFractionDigits: 3 } } });
// --> Some 1,000.100
i18next.t('intlNumberWithOptions', { val: 2000 });
// --> Some 2,000.00
i18next.t('intlNumberWithOptions', { val: 2000, minimumFractionDigits: 3 });
// --> Some 2,000.000
{% endtab %}
{% tab title="TypeScript" %} Passing options to the formatting:
- In the translation string using
{{value, formatname(options1: options1Value)}} - Using the root level options when calling
t($ => $.key, { option1: option1Value }) - Using the per value options like:
t($ => $.key, { formatParams: { value: { option1: option1Value } })
Samples
// JSON
{
"intlNumber": "Some {{val, number}}",
"intlNumberWithOptions": "Some {{val, number(minimumFractionDigits: 2)}}"
}
i18next.t($ => $.intlNumber, { val: 1000 });
// --> Some 1,000
i18next.t($ => $.intlNumber, { val: 1000.1, minimumFractionDigits: 3 });
// --> Some 1,000.100
i18next.t($ => $.intlNumber, { val: 1000.1, formatParams: { val: { minimumFractionDigits: 3 } } });
// --> Some 1,000.100
i18next.t($ => $.intlNumberWithOptions, { val: 2000 });
// --> Some 2,000.00
i18next.t($ => $.intlNumberWithOptions, { val: 2000, minimumFractionDigits: 3 });
// --> Some 2,000.000
{% endtab %} {% endtabs %}
Overriding the language to use
The language can be overridden by passing it in t.options
{% tabs %} {% tab title="JavaScript" %}
i18next.t('intlNumber', { val: 1000.1, lng: 'de' });
// or: i18next.t('intlNumber', { val: 1000.1, locale: 'de' });
i18next.t('intlNumber', { val: 1000.1, formatParams: { val: { lng: 'de' } } });
// or: i18next.t('intlNumber', { val: 1000.1, formatParams: { val: { locale: 'de' } } });
{% endtab %}
{% tab title="TypeScript" %}
i18next.t($ => $.intlNumber, { val: 1000.1, lng: 'de' });
// or: i18next.t($ => $.intlNumber, { val: 1000.1, locale: 'de' });
i18next.t($ => $.intlNumber, { val: 1000.1, formatParams: { val: { lng: 'de' } } });
// or: i18next.t($ => $.intlNumber, { val: 1000.1, formatParams: { val: { locale: 'de' } } });
{% endtab %} {% endtabs %}
Adding custom format function
It's rather simple to add own function:
// after i18next.init(options);
i18next.services.formatter.add('lowercase', (value, lng, options) => {
return value.toLowerCase();
});
i18next.services.formatter.add('underscore', (value, lng, options) => {
return value.replace(/\s+/g, '_');
});
{% hint style="warning" %}
Make sure you add your custom format function AFTER the i18next.init() call.
{% endhint %}
There's also an addCached version for optimized performance:
i18next.services.formatter.addCached('specialformat', (lng, options) => {
const formatter = new Intl.NumberFormat(lng, options);
return (val) => formatter.format(val);
});
Using multiple formatters
{
"key": "Some format {{value, formatter1, formatter2}}"
}
Built-in formats
Number
{% tabs %} {% tab title="JavaScript" %}
// JSON
{
"intlNumber": "Some {{val, number}}",
"intlNumberWithOptions": "Some {{val, number(minimumFractionDigits: 2)}}"
}
i18next.t('intlNumber', { val: 1000 });
// --> Some 1,000
i18next.t('intlNumber', { val: 1000.1, minimumFractionDigits: 3 });
// --> Some 1,000.100
i18next.t('intlNumber', { val: 1000.1, formatParams: { val: { minimumFractionDigits: 3 } } });
// --> Some 1,000.100
i18next.t('intlNumberWithOptions', { val: 2000 });
// --> Some 2,000.00
i18next.t('intlNumberWithOptions', { val: 2000, minimumFractionDigits: 3 });
// --> Some 2,000.000
{% endtab %}
{% tab title="TypeScript" %}
// JSON
{
"intlNumber": "Some {{val, number}}",
"intlNumberWithOptions": "Some {{val, number(minimumFractionDigits: 2)}}"
}
i18next.t($ => $.intlNumber, { val: 1000 });
// --> Some 1,000
i18next.t($ => $.intlNumber, { val: 1000.1, minimumFractionDigits: 3 });
// --> Some 1,000.100
i18next.t($ => $.intlNumber, { val: 1000.1, formatParams: { val: { minimumFractionDigits: 3 } } });
// --> Some 1,000.100
i18next.t($ => $.intlNumberWithOptions, { val: 2000 });
// --> Some 2,000.00
i18next.t($ => $.intlNumberWithOptions, { val: 2000, minimumFractionDigits: 3 });
// --> Some 2,000.000
{% endtab %} {% endtabs %}
For options see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
Currency
{% tabs %} {% tab title="JavaScript" %}
// JSON
{
"intlCurrencyWithOptionsSimplified": "The value is {{val, currency(USD)}}",
"intlCurrencyWithOptions": "The value is {{val, currency(currency: USD)}}",
"twoIntlCurrencyWithUniqueFormatOptions": "The value is {{localValue, currency}} or {{altValue, currency}}",
}
i18next.t('intlCurrencyWithOptionsSimplified', { val: 2000 });
// --> The value is \$2,000.00
i18next.t('intlCurrencyWithOptions', { val: 2300 });
// --> The value is \$2,300.00
i18next.t('twoIntlCurrencyWithUniqueFormatOptions',
{
localValue: 12345.67,
altValue: 16543.21,
formatParams: {
localValue: { currency: 'USD', locale: 'en-US' },
altValue: { currency: 'CAD', locale: 'fr-CA' },
},
},);
// --> The value is \$12,345.67 or 16 543,21 $ CA
{% endtab %}
{% tab title="TypeScript" %}
// JSON
{
"intlCurrencyWithOptionsSimplified": "The value is {{val, currency(USD)}}",
"intlCurrencyWithOptions": "The value is {{val, currency(currency: USD)}}",
"twoIntlCurrencyWithUniqueFormatOptions": "The value is {{localValue, currency}} or {{altValue, currency}}",
}
i18next.t($ => $.intlCurrencyWithOptionsSimplified, { val: 2000 });
// --> The value is \$2,000.00
i18next.t($ => $.intlCurrencyWithOptions, { val: 2300 });
// --> The value is \$2,300.00
i18next.t($ => $.twoIntlCurrencyWithUniqueFormatOptions,
{
localValue: 12345.67,
altValue: 16543.21,
formatParams: {
localValue: { currency: 'USD', locale: 'en-US' },
altValue: { currency: 'CAD', locale: 'fr-CA' },
},
},);
// --> The value is \$12,345.67 or 16 543,21 $ CA
{% endtab %} {% endtabs %}
For options see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
DateTime
{% tabs %} {% tab title="JavaScript" %}
// JSON
{
"intlDateTime": "On the {{val, datetime}}",
}
i18next.t('intlDateTime', { val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) });
// --> On the 12/20/2012
i18next.t('intlDateTime',
{
val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)),
formatParams: {
val: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' },
},
});
// --> On the Thursday, December 20, 2012
{% endtab %}
{% tab title="TypeScript" %}
// JSON
{
"intlDateTime": "On the {{val, datetime}}",
}
i18next.t($ => $.intlDateTime, { val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) });
// --> On the 12/20/2012
i18next.t($ => $.intlDateTime,
{
val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)),
formatParams: {
val: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' },
},
});
// --> On the Thursday, December 20, 2012
{% endtab %} {% endtabs %}
For options see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
RelativeTime
{% tabs %} {% tab title="JavaScript" %}
// JSON
{
"intlRelativeTime": "Lorem {{val, relativetime}}",
"intlRelativeTimeWithOptions": "Lorem {{val, relativetime(quarter)}}",
"intlRelativeTimeWithOptionsExplicit": "Lorem {{val, relativetime(range: quarter; style: narrow;)}}",
}
i18next.t('intlRelativeTime', { val: 3 });
// --> Lorem in 3 days
i18next.t('intlRelativeTimeWithOptions', { val: -3 });
// --> Lorem 3 quarters ago
i18next.t('intlRelativeTimeWithOptionsExplicit', { val: -3 });
// --> Lorem 3 qtrs. ago
i18next.t('intlRelativeTimeWithOptionsExplicit', { val: -3, style: 'long' });
// --> Lorem 3 quarters ago
{% endtab %}
{% tab title="TypeScript" %}
// JSON
{
"intlRelativeTime": "Lorem {{val, relativetime}}",
"intlRelativeTimeWithOptions": "Lorem {{val, relativetime(quarter)}}",
"intlRelativeTimeWithOptionsExplicit": "Lorem {{val, relativetime(range: quarter; style: narrow;)}}",
}
i18next.t($ => $.intlRelativeTime, { val: 3 });
// --> Lorem in 3 days
i18next.t($ => $.intlRelativeTimeWithOptions, { val: -3 });
// --> Lorem 3 quarters ago
i18next.t($ => $.intlRelativeTimeWithOptionsExplicit, { val: -3 });
// --> Lorem 3 qtrs. ago
i18next.t($ => $.intlRelativeTimeWithOptionsExplicit, { val: -3, style: 'long' });
// --> Lorem 3 quarters ago
{% endtab %} {% endtabs %}
For options see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
List
{% tabs %} {% tab title="JavaScript" %}
// JSON
{
"intlList": "A list of {{val, list}}"
}
i18next.t('intlList', { val: ['locize', 'i18next', 'awesomeness'] });
// --> A list of locize, i18next, and awesomeness
{% endtab %}
{% tab title="TypeScript" %}
// JSON
{
"intlList": "A list of {{val, list}}"
}
i18next.t($ => $.intlList, { val: ['locize', 'i18next', 'awesomeness'] });
// --> A list of locize, i18next, and awesomeness
{% endtab %} {% endtabs %}
For options see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat
Additional options
Prefix/Suffix for interpolation and other options can be overridden in init option:
sample
i18next.init({
interpolation: { ... }
});
| option | default | description |
|---|---|---|
| alwaysFormat | false | used to always call the format function for all interpolated values |
| formatSeparator | ',' | used to separate format from interpolation value |
While there are a lot of options going with the defaults should get you covered.
Custom formatter
{% hint style="info" %} Do you want to create some sort of default formatter, that does not need a specific format to be passed via i18n resources? i18next gets you covered also with that... {% endhint %}
Create a custom formatter plugin, and pass it to i18next:
{% tabs %} {% tab title="JavaScript" %}
const myFormatter = {
type: 'formatter',
init(services, backendOptions, i18nextOptions) {}, // of you need some init stuff to be done...
format(value, format, lng, options) {
// do whatever you like here... and return the formatted string
if (!format && value instanceof Date) {
return value.toUTCString()
}
},
add(name, fc) {
// handle adding a new format, if you need it
},
addCached(name, fc) {
// handle adding a new cached format, if you need it
}
}
i18next
.use(myFormatter)
.init({
fallbackLng: "en",
interpolation: {
alwaysFormat: true // set alwaysFormat to true
},
resources: {
en: {
translation: {
testKeyWithoutFormat: 'On the "{val}"',
}
}
}
});
i18next.t("testKeyWithoutFormat", { val: new Date() }) // On the "Mon, 07 Jul 2025 11:44:50 GMT"
{% endtab %}
{% tab title="TypeScript" %}
const myFormatter = {
type: 'formatter',
init(services, backendOptions, i18nextOptions) {}, // of you need some init stuff to be done...
format(value, format, lng, options) {
// do whatever you like here... and return the formatted string
if (!format && value instanceof Date) {
return value.toUTCString()
}
},
add(name, fc) {
// handle adding a new format, if you need it
},
addCached(name, fc) {
// handle adding a new cached format, if you need it
}
}
i18next
.use(myFormatter)
.init({
fallbackLng: "en",
interpolation: {
alwaysFormat: true // set alwaysFormat to true
},
resources: {
en: {
translation: {
testKeyWithoutFormat: 'On the "{val}"',
}
}
}
});
i18next.t($ => $.testKeyWithoutFormat, { val: new Date() }) // On the "Mon, 07 Jul 2025 11:44:50 GMT"
{% endtab %} {% endtabs %}