Skip to main content

Localization

Localizer is used for two things: translating text and locale-aware formatting of numbers, dates, and relative time.

Quick Start

To get localization working in the simplest way:

  1. set lang on <html>,
  2. create window.translations = new Map(),
  3. register translations with Localizer.registerTranslation(...),
  4. create new Localizer(...),
  5. call translate(), translateWithParams(), translatePlural(), formatNumber(), formatDate(), and relativeTime().

index.html

<!doctype html>
<html lang="en-gb">
<head>
<script>
window.translations = new Map();
</script>
<script type="module" src="/src/main.js"></script>
</head>
<body></body>
</html>

main.js

import 'wj-elements';
import { Localizer } from 'wj-elements';

Localizer.registerTranslation({
code: 'en-gb',
name: 'English',
dir: 'ltr',
'app.hello': 'Hello',
'app.greeting': 'Hello {name}',
'cart.items.one': '{count} item',
'cart.items.other': '{count} items',
});

const localizer = new Localizer(document.documentElement);

console.log(localizer.translate('app.hello'));
console.log(localizer.translateWithParams('app.greeting', { name: 'John' }));

const template = localizer.translatePlural('cart.items', 3);
console.log(template.replace('{count}', 3));

console.log(localizer.formatNumber(1234.56, {
style: 'currency',
currency: 'EUR',
}));

console.log(localizer.formatDate(new Date(), { dateStyle: 'long' }));
console.log(localizer.relativeTime(undefined, -1, 'day'));

In this example, the language comes from <html lang="en-gb">, translations are registered once during app startup, and localizer can then be used anywhere in the code.

Language

Localizer resolves the language in this order:

  1. element.lang
  2. document.documentElement.lang
  3. internal default en-GB
important

In practice, do not rely on the implicit default. Always set lang explicitly and use lowercase language codes such as sk-sk and en-gb. Registered translations are stored in lowercase and the current implementation expects the same format in lang.

Component

Inside a web component, this is the most common pattern:

import { Localizer, WJElement } from 'wj-elements';

export default class AppGreeting extends WJElement {
constructor() {
super();
this.localizer = new Localizer(this);
}

draw() {
const div = document.createElement('div');
div.textContent = this.localizer.translateWithParams('app.greeting', {
name: 'John',
});

return div;
}
}

Outside components, the usual pattern is new Localizer(document.body) or new Localizer(document.documentElement).

Setup

new Localizer()

Creates a new localizer instance.

ParameterTypeDescription
elementHTMLElement | Element | { lang?: string; dir?: string }Element used to read lang and dir.

Common usage:

const appLocalizer = new Localizer(document.documentElement);
const serviceLocalizer = new Localizer(document.body);
const componentLocalizer = new Localizer(this);

registerTranslation()

Registers one or more translation objects.

ParameterTypeDescription
...translationobject[]One or more objects with a code field.

A translation is just a plain object:

FieldTypeRequiredDescription
codestringyesLanguage code such as sk-sk, en-gb, de-de.
namestringnoHuman-readable language name.
dirstringnoText direction, usually ltr or rtl.
other fieldsstringnoTranslation keys and values.

Example:

window.translations = new Map();

Localizer.registerTranslation(
{
code: 'sk-sk',
name: 'Slovenčina',
'global.save': 'Uložiť',
},
{
code: 'en-gb',
name: 'English',
'global.save': 'Save',
}
);

Behavior:

  • window.translations must exist before the first call,
  • registering the same language multiple times merges keys,
  • if the new object contains an existing key, it overrides it,
  • if the object has no code, an error is printed to the console.

localizer.setLanguage()

Re-evaluates the active language from localizer.lang.

Use it after changing lang at runtime.

document.documentElement.lang = 'en-gb';
localizer.lang = 'en-gb';
localizer.setLanguage();

convertLangCode()

Converts a frontend language code into the shape often used in API payloads.

localizer.convertLangCode('en-gb');
// en_GB

Practical example:

const apiKey = localizer.convertLangCode(localizer.currentLang);
const label = item.name?.values?.[apiKey];

Translation

translate()

Returns the translation for a key.

ParameterTypeDescription
keystringTranslation key.

If the key does not exist, the original key is returned.

localizer.translate('app.hello');
// 'Hello'

translateWithParams()

Translates text and fills placeholders in the {name} format.

ParameterTypeDefaultDescription
keystring-Translation key.
paramsRecord<string, unknown>{}Placeholder values.

Behavior:

  • placeholders are replaced when values exist,
  • values are converted to strings,
  • if a value is missing or null, the placeholder stays unchanged.
localizer.translateWithParams('app.greeting', {
name: 'John',
});
// 'Hello John'

translatePlural()

Selects the correct plural form through Intl.PluralRules.

ParameterTypeDefaultAllowed valuesDescription
keystring--Base key without suffix.
countnumber0any numberCount used to select the form.
typestring'cardinal''cardinal', 'ordinal'Pluralization type.

The method looks for keys in this form:

  • <key>.zero
  • <key>.one
  • <key>.two
  • <key>.few
  • <key>.many
  • <key>.other

If the exact form does not exist, it falls back to <key>.other.

const template = localizer.translatePlural('cart.items', 3);
const text = template.replace('{count}', 3);
// '3 items'
note

translatePlural() only selects the correct string. If the string contains placeholders such as {count}, replace them afterwards yourself.

Formatting

formatNumber()

The number-formatting function is called formatNumber(). There is no separate translateNumber() in this API.

Formats a number through Intl.NumberFormat.

ParameterTypeDescription
numbernumberNumber to format.
optionsIntl.NumberFormatOptionsFormatting options.

Most common options:

OptionAllowed values
style'decimal', 'currency', 'percent', 'unit'
currencyfor example 'EUR', 'USD'
currencyDisplay'code', 'symbol', 'narrowSymbol', 'name'
currencySign'standard', 'accounting'
unitfor example 'byte', 'kilobyte', 'meter', 'celsius'
unitDisplay'short', 'long', 'narrow'
notation'standard', 'scientific', 'engineering', 'compact'
compactDisplay'short', 'long'
signDisplay'auto', 'never', 'always', 'exceptZero', 'negative'
useGroupingtrue, false, 'auto', 'always', 'min2'
minimumIntegerDigitsnumber
minimumFractionDigitsnumber
maximumFractionDigitsnumber
minimumSignificantDigitsnumber
maximumSignificantDigitsnumber
roundingPriority'auto', 'morePrecision', 'lessPrecision'
roundingIncrement1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000
roundingMode'ceil', 'floor', 'expand', 'trunc', 'halfCeil', 'halfFloor', 'halfExpand', 'halfTrunc', 'halfEven'
trailingZeroDisplay'auto', 'stripIfInteger'

Examples:

localizer.formatNumber(1234.56, {
style: 'currency',
currency: 'EUR',
});

localizer.formatNumber(2048, {
style: 'unit',
unit: 'kilobyte',
unitDisplay: 'short',
});

formatDate()

Formats dates through Intl.DateTimeFormat.

ParameterTypeDescription
datestring | Date | numberDate, timestamp, or parseable string.
optionsIntl.DateTimeFormatOptionsFormatting options.

Most common options:

OptionAllowed values
dateStyle'full', 'long', 'medium', 'short'
timeStyle'full', 'long', 'medium', 'short'
weekday'long', 'short', 'narrow'
era'long', 'short', 'narrow'
year'numeric', '2-digit'
month'numeric', '2-digit', 'long', 'short', 'narrow'
day'numeric', '2-digit'
dayPeriod'narrow', 'short', 'long'
hour'numeric', '2-digit'
minute'numeric', '2-digit'
second'numeric', '2-digit'
fractionalSecondDigits1, 2, 3
hour12true, false
hourCycle'h11', 'h12', 'h23', 'h24'
timeZonefor example 'Europe/Bratislava', 'UTC'
timeZoneName'short', 'long', 'shortOffset', 'longOffset', 'shortGeneric', 'longGeneric'
calendarfor example 'gregory'
numberingSystemfor example 'latn', 'arab'
localeMatcher'lookup', 'best fit'
formatMatcher'basic', 'best fit'

Examples:

localizer.formatDate(new Date(), {
dateStyle: 'long',
});

localizer.formatDate('2026-03-29T15:45:00Z', {
dateStyle: 'short',
timeStyle: 'short',
timeZone: 'Europe/Bratislava',
});

relativeTime()

Formats relative time through Intl.RelativeTimeFormat.

ParameterTypeDefaultAllowed valuesDescription
langstringthis.currentLanglocale code or undefinedOutput language.
valuenumber0positive or negative numbersTime delta.
unitstring'day''year', 'years', 'quarter', 'quarters', 'month', 'months', 'week', 'weeks', 'day', 'days', 'hour', 'hours', 'minute', 'minutes', 'second', 'seconds'Time unit.
optionsIntl.RelativeTimeFormatOptions{ numeric: 'auto' }see belowFormatting options.

options values:

OptionAllowed values
numeric'always', 'auto'
style'long', 'short', 'narrow'
localeMatcher'lookup', 'best fit'

Examples:

localizer.relativeTime(undefined, -1, 'day');
// for example 'yesterday'

localizer.relativeTime('en-gb', 3, 'week', {
numeric: 'always',
style: 'short',
});
// 'in 3 wk.'
note

The first parameter is lang. If you want to use the current localizer language, pass undefined, null, or an empty string.