
Привет, друзья!
В данной статье я хочу рассказать вам об Internationalization API — интерфейсе, предоставляемом браузером, позволяющем выполнять интернационализацию и локализацию веб-приложений.
Статья состоит из 2 частей: теоретической и практической. В теоретической части мы кратко рассмотрим возможности, предоставляемые Internationalization API. В практической — создадим пример локализованного приложения с помощью разработанной мной утилиты.
Теория
Internationalization API предоставляет следующие возможности:
- локализованное (далее предполагается) сравнение строк
- форматирование чисел, включая валюту, различные единицы измерения и проценты
- форматирование даты и времени, включая относительные периоды, такие как завтра, вчера, через неделю и т.д.
- форматирование названий языков, регионов, скриптов и валют
- форматирование списков с соединительным союзом
Иили разделительным союзомИЛИ - "плюрализация" — перевод во множественное число
- преобразование регистра
В настоящее время Internationalization API поддерживается всеми современными браузерами.
Несмотря на то, что в JavaScript существуют такие методы для локализации как:
Array.toLocaleString()-
String.localeCompare(),.toLocalLowerCase(),.toLocalUpperCase() Number.toLocaleString()-
Date.toLocaleString(),.toLocaleDateString(),.toLocaleTimeString()
их зачастую оказывается недостаточно.
Кроме того, поведение данных методов определяется конкретной реализацией ECMAScript, т.е. браузером.
Функционал, определенный в Internationalization API, инкапсулирован в объекте Intl. Данный объект не имеет внутреннего метода [[Construct]], поэтому не может вызываться как конструктор с помощью ключевого слова new. Он также не имеет внутреннего метода [[Call]], поэтому не может вызываться как функция.
Intl включает в себя следующие интерфейсы-конструкторы:
-
Collator— сравнение строк -
DateTimeFormat— форматирование даты и времени -
DisplayNames— форматирование названий языков, регионов и т.д. на других языках -
ListFormat— форматирование списков -
Locale— определение локали -
NumberFormat— форматирование чисел -
PluralRules— плюрализация -
RelativeTimeFormat— форматирование относительных периодов времени
Возможные значения:
Пример получения текущей даты и времени в дефолтной локали
// [] означает текущую локаль (локаль по умолчанию)
const currentDateAndTime = new Intl.DateTimeFormat([], {
dateStyle: 'short',
timeStyle: 'short'
}).format()
console.log(currentDateAndTime) // 03.08.2021, 15:57
Locale
Конструктор Locale используется для создания экземпляров идентификаторов локали. Первым обязательным аргументом, передаваемым Locale, является локаль, которая может состоять из следующего:
- код языка
- код диалекта
- код региона или страны
- один или несколько уникальных вариантных подтегов (subtags)
- одна или несколько последовательностей из расширения
BCP 47 - последовательность из расширения для частного использования
В большинстве случаев достаточно указать код языка или код языка и код страны через дефис:
const ru = new Intl.Locale('ru-RU')
Вторым опциональным аргументом Locale является объект с настройками:
const ru = new Intl.Locale(
'ru',
{ region: 'RU', hourCycle: 'h24', calendar: 'gregory' }
)
Локаль может быть строкой или объектом. Пустой массив означает использование текущей локали пользователя:
const now = new Intl.DateTimeFormat([], { timeStyle: 'short' }).format()
DateTimeFormat
Конструктор DateTimeFormat используется для форматирования даты и времени. Он принимает локаль и объект с настройками.
Сигнатура
new Intl.DateTimeFormat(locale: object | string | [], options: object).format(date)
// [] - локаль по умолчанию
// date - дата, время или дата и время
Настройки
| Свойство | Описание |
|---|---|
| timeZone | часовой пояс: UTC, America/New_York, Europe/Paris и т.д. |
| calendar | календарь: chinese, gregory, hebrew, indian, islamic и т.д. |
| numberingSystem | система счисления: arab, beng, fullwide, latin и т.д. |
| localeMatcher | алгоритм для поиска совпадений: lookup, best fit
|
| formatMatcher | алгоритм для форматирования: basic, best fit
|
| hour12 | если имеет значение true, используется 12-часовой формат |
| hourCycle | часовой формат: h11, h12, h23, h24
|
| dateStyle | стиль форматирования даты: full, long, medium, short
|
| weekday | день недели: long, short, narrow
|
| day | день месяца: numeric, 2-digit
|
| month | месяц: numeric, 2-digit, long, short, narrow
|
| year | год: numeric, 2-digit
|
| era | эпоха: long, short, narrow
|
| timeStyle | стиль форматирования времени: full, long, medium, short
|
| hour | часы: numeric, 2-digit
|
| minute | минуты: numeric, 2-digit
|
| second | секунды: numeric, 2-digit
|
| dayPeriod | часть дня (утро, вечер и т.п.): narrow, short, long
|
| timeZoneName | название часового пояса (UTC, PTC): long, short
|
Настройки localeMatcher и formatMatcher могут передаваться любому конструктору, предоставляемому Intl, но используются редко.
По умолчанию new Intl.DateTimeFormat().format() возвращает текущую дату в кратком виде (dateStyle: short).
Примеры
const formatDateTime = ({ locale = [], date = Date.now(), ...options } = {}) =>
new Intl.DateTimeFormat(locale, options).format(date)
console.log(
'\n',
// русский
formatDateTime(), // 17.08.2021
'\n',
// американский английский
formatDateTime({ locale: 'en-US', dateStyle: 'short', timeStyle: 'short' }), // 8/17/21, 3:56 PM,
'\n',
// британский английский
formatDateTime({ locale: 'en-GB', dateStyle: 'long', timeStyle: 'short' }), // 17 August 2021 at 15:57
'\n',
// японский
formatDateTime({ locale: 'ja-JP', dateStyle: 'short' }), // 2021/08/17
'\n',
// испанский
formatDateTime({ locale: 'es-ES', dateStyle: 'full', timeStyle: 'full' }), // martes, 17 de agosto de 2021, 15:57:49 (hora estándar de Ekaterimburgo)
'\n',
// французский
formatDateTime({
locale: 'fr-FR',
weekday: 'long',
day: '2-digit',
month: 'long',
year: 'numeric',
hour: '2-digit'
}) // mardi 17 août 2021, 15 h
)
Другие методы
-
formatToParts()— возвращает массив объектов, содержащих форматированную дату в виде пар ключ/значение ({ type: 'weekday', value: 'Monday' }) -
formatRange(startDate, endDate)— возвращает диапазон, например,01/10/2022, 10:00 AM - 12:00 PM formatRangeToParts()-
resolveOptions()— возвращает объект со свойствами, значениями которых являются вычисленные настройки форматирования для локали, даты и времени
RelativeTimeFormat
Конструктор RelativeTimeFormat используется для локализации относительного времени, например, вчера, завтра, на следующей неделе, в прошлом месяце и т.д. Данный метод принимает локаль и объект с настройками.
Сигнатура
new Intl.RelativeTimeFormat(locale, options).format(amount, unit)
// amount - количество единиц времени
// unit - единица времени: `day`, `month`, `year` и т.д.
Положительное число в amount означает будущее, отрицательное — прошлое.
Настройки
| Свойство | Описание |
|---|---|
| numeric |
always — "1 день назад" (дефолтное значение), auto — "вчера" |
| style |
long (дефолтное значение), short, narrow
|
В данном случае метод format в качестве аргументов принимает число и единицу времени.
Примеры
const formatRelativeTime = ({
locale = [],
value = '1 day',
...options
} = {}) => {
const [amount, unit] = value.split(/[\s_]/)
return new Intl.RelativeTimeFormat(locale, options).format(amount, unit)
}
console.log(
'\n',
formatRelativeTime(), // через 1 день
'\n',
formatRelativeTime({ locale: 'en-US', value: '-1 day', numeric: 'auto' }), // yesterday
'\n',
formatRelativeTime({
locale: 'fr-FR',
value: '1 week',
style: 'long'
}), // dans 1 semaine
'\n',
formatRelativeTime({
locale: 'ja-JP',
value: '-1 month',
numeric: 'auto',
style: 'long'
}) // 先月
)
NumberFormat
Конструктор NumberFormat используется для форматирования чисел, валюты, процентов и единиц измерения, таких как длина, температура и др. Данный метод принимает локаль и объект с настройками.
Сигнатура
new Intl.NumberFormat(locale, options).format(number)
Настройки
| Свойство | Описание |
|---|---|
| style | вид единиц: decimal — число с плавающей точкой, currency — валюта, percent — проценты, unit — единицы измерения. От этой настройки зависят другие |
| notation | стиль форматирования: standard, scientific, engineering, compact
|
| numberingSystem | система счисления: arab, beng, deva, fullwide, latn и др. |
| minimumIntegerDigits | минимальное количество цифр целой части числа (от 1 до 21; по умолчанию 1) |
| minimumFractionDigits | минимальное количество цифр после запятой (от 0 до 20; по умолчанию 0) |
| maximumFractionDigits | максимальное количество цифр после запятой (от 0 до 20; по умолчанию наибольшее значение из minimumFractionDigits и 3) |
| minimumSignificantDigits | минимальное количество значащих цифр (от 1 до 21; по умолчанию 1) |
| maximumSignificantDigits | минимальное количество значащих цифр (от 1 до 21; по умолчанию minimumSignificantDigits) |
| signDisplay | отображение символов +/-: auto, never, always, exceptZero
|
| useGrouping | если имеет значение false, разделители тысяч будут игнорироваться |
| compactDisplay | форматирование при использовании нотации compact
|
| currency | код валюты при использовании стиля currency: USD, EUR, RUB и т.д. |
| currencyDisplay | отображение символа/названия валюты при использовании стиля currency: symbol, narrowSymbol, code, name
|
| currencySign | форматирование отрицательных значений при использовании стиля currency: standard, accounting
|
| unit | вид единицы измерения: centimeter, meter, minute, hour, byte и т.д. |
| unitDisplay | формат отображения единицы измерения: long, short, narrow
|
Примеры
const formatNumber = ({ locale = [], number = 1234.56, ...options } = {}) =>
new Intl.NumberFormat(locale, options).format(number)
console.log(
'\n',
formatNumber(),
'\n', // 1 234,56
formatNumber({ locale: 'en-US' }),
'\n', // 1,234.56
formatNumber({ locale: 'de-DE', style: 'currency', currency: 'EUR' }),
'\n', // 1.234,56 €
formatNumber({ locale: 'fr-FR', style: 'percent' }),
'\n', // 123 456 %
formatNumber({
locale: 'it-IT',
style: 'unit',
unit: 'celsius',
minimumFractionDigits: 3
}),
'\n' // 1.234,560 °C
)
DisplayNames
Конструктор DisplayNames используется для форматирования названий языков, диалектов, регионов и валют на другом языке. Данный метод принимает локаль и объект с настройками.
Сигнатура
new Intl.DisplayNames(locale, options).of(localeOf)
Настройки
| Свойство | Описание |
|---|---|
| type | тип названия: language, region, script, currency
|
| style | стиль форматирования: long, short, narrow
|
| fallback | резерв: code, none
|
Обратите внимание: настройка type является обязательной. При этом хорошо поддерживается только type со значением language.
Примеры
const formatName = ({
locale = [],
localeOf = 'en-US',
type = 'language',
...options
} = {}) => new Intl.DisplayNames(locale, { type, ...options }).of(localeOf)
console.log(
'\n',
formatName(),
'\n', // американский английский
formatName({
localeOf: 'Egyp',
type: 'script'
}),
'\n', // египетская иероглифическая
formatName({
locale: 'fr-FR',
localeOf: 'AU',
type: 'region'
}),
'\n', // Australie - Авcтралия по-французски
formatName({
locale: 'pl-PL',
localeOf: 'GBP',
type: 'currency',
style: 'long'
}),
'\n' // funt szterling - английские фунты стерлингов на польском
)
ListFormat
Конструктор ListFormat используется для форматирования списков путем подстановки соединительного союза И или разделительного союза ИЛИ. Данный метод принимает локаль и объект с настройками.
Сигнатура
new Intl.ListFormat(locale, options).format(list)
Настройки
| Свойство | Описание |
|---|---|
| type | формат вывода: conjunction (и; дефолтное значение), disjunction (или), unit (нет) |
| style | стиль форматирования: long, short, narrow
|
Примеры
const browsers = ['Chrome', 'Firefox', 'Safari']
const formatList = ({
locale = [],
list = browsers,
...options
} = {}) => new Intl.ListFormat(locale, options).format(list)
console.log(
'\n',
formatList(),
'\n', // Chrome, Firefox и Safari
formatList({ locale: 'en-US', style: 'short' }),
'\n', // Chrome, Firefox, & Safari
formatList({ locale: 'ja-JP', type: 'disjunction' }),
'\n' // Chrome、Firefox、またはSafari
)
Collator
Конструктор Collator используется для сравнения строк с учетом локали. Данный метод принимает локаль и объект с настройками.
Сигнатура
new Intl.Collator(locale, options).compare(str1, str2)
Настройки
| Свойство | Описание |
|---|---|
| usage |
sort — сортировка (дефолтное значение) или search — поиск |
| sensitivity | чувствительность: base, accent, case, variant
|
| collation | сопоставление вариантов для нескольких языков |
| numeric |
true означает сравнение чисел |
| ignorePunctuation |
true означает игнорирование пунктуации |
| caseFirst |
upper — сначала идут строки, начинающиеся с большой буквы, lower — сначала идут строки, начинающиеся с маленькой буквы |
Результат
-
0— строки равны -
-1— первая строка "меньше" второй -
1— первая строка "больше" второй
Примеры
const compareValues = ({ locale = [], values = [], ...options } = {}) =>
new Intl.Collator(locale, options).compare(...values)
console.log(
'\n',
compareValues({ values: ['a', 'á'], sensitivity: 'base' }),
'\n', // 0 -> одинаковые
compareValues({ values: ['2', '10'] }),
'\n', // 1 -> '2' > '10'
compareValues({ values: ['2', '10'], numeric: true }),
'\n' // -1 -> 2 < 10
)
PluralRules
Конструктор PluralRules используется для плюрализации (перевода во множественное число). Данный метод принимает локаль и объект с настройками.
Сигнатура
new Intl.PluralRules(locale, options).select(number)
Настройки
| Свойство | Описание |
|---|---|
| type |
cardinal — количество элементов (дефолтное значение), ordinal — порядок элемента (первый, второй, третий и т.д.) |
Примеры
const pluralize = ({ locale = [], number = 1, ...options } = {}) =>
new Intl.PluralRules(locale, options).select(number)
console.log(
'\n',
pluralize(),
'\n', // one
pluralize({ locale: 'ru-RU', type: 'ordinal' }),
'\n' // other
)
В настоящее время поддерживается только локаль en-US.
Практика
В данном разделе мы создадим небольшое локализованное приложение с помощью разработанной мной утилиты easy-intl.
Принцип работы утилиты подробно описан в документации. Многие вещи были рассмотрены в теоретической части. Поэтому, с вашего позволения, здесь я опишу только ключевые моменты.
Демо приложения:
Создаем директорию для проекта и устанавливаем easy-intl:
mkdir intl-app
cd !$
yarn add easy-intl
# or
npm i easy-intl
Структура проекта будет следующей:
- intl
- options.js - настройки для утилиты
- dictionary.js - словарь для кастомной локализации
- index.html
- style.css
- script.js
Как сказал бы Ватсон, все ЭЛЕМЕНТАРНО.
В стилях у нас не будет ничего интересного, поэтому
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Montserrat', sans-serif;
letter-spacing: 1px;
}
h1 {
margin: 1rem 0;
text-align: center;
font-size: 2rem;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.card {
margin: 1rem;
width: 320px;
padding: 0.75rem;
border-radius: 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
main > .card {
position: fixed;
top: 10%;
left: 50%;
transform: translateX(-50%);
margin: 1rem auto;
box-shadow: none;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 50%;
}
main > .card img {
position: absolute;
z-index: -1;
width: 100px;
opacity: 0.9;
filter: brightness(0.9);
}
select {
width: 50px;
padding: 0.25rem;
font-weight: bold;
border-radius: 4px;
opacity: 0.95;
cursor: pointer;
}
select:focus-visible {
outline: none;
}
h2 {
font-size: 1.4rem;
margin: 0.75rem 0;
}
h3,
h4 {
margin: 0.5rem 0;
}
h3 {
font-size: 1.2rem;
}
h4 {
font-size: 1.1rem;
}Что касается разметки, то в процессе локализации easy-intl ориентируется на следующие атрибуты:
-
data-intl_type— тип содержимого для локализации. Возможные значения:-
date— дата или время, или дата и время -
relative— относительный период времени -
number— число, включая валюту, проценты и единицы измерения -
names— название языка, региона, валюты и т.д. -
list— список -
compare— строки для сравнения -
plural— строка для перевода во множественное число -
custom— строка, которая используется в качестве ключа словаря для кастомной локализации
-
-
data-intl_value— собственно, содержимое для локализации; зависит отdata-intl_type -
data-intl_options— строковые (inline) настройки для локализации; зависят отdata-intl_type. Данные настройки имеют высший приоритет -
data-intl_root— индикатор (атрибут без значения) корневого HTML-элемента для локализации.easy-intlпозволяет выполнять разную локализацию для разных частей приложения — дочерние элементы нижележащего (в иерархииDOM)data-intl_rootне будут локализоваться вышестоящимdata-intl_root -
data-intl_map— по умолчаниюeasy-intlвыполняет локализацию значений свойстваtextContext, а также атрибутовtitleиplaceholderэлементов, имеющих атрибутdata-intl_type. Значение атрибутаdata-intl_mapстановится ключом объектаmapвида{ [data-intl_map]: локализованное data-intl_value }— геттера экземпляраEasyIntl. Данный объект позволяет выполнять ручную локализацию других свойств и атрибутов соответствующих элементов.
Вот как будет выглядеть наша разметка:
<main data-intl_root id="global_root">
<h1 data-intl_type="custom" data-intl_value="main_title" data-intl_map="main_title">
Easy Intl
</h1>
<section class="card">
<img src="https://cdn-icons-png.flaticon.com/512/814/814513.png" alt="" role="presentation" />
<select id="locale">
<option value="ru-RU">Ru</option>
<option value="en-US" selected>En</option>
<option value="de-DE">De</option>
<option value="ja-JP">Ja</option>
</select>
</section>
<div class="container">
<section class="card">
<h2 data-intl_type="custom" data-intl_value="date_title" data-intl_map="date_title">
Дата и время
</h2>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="without_options">
Без встроенных настроек
</h3>
<time data-intl_type="date" data-intl_value="2021-08-16 12:00"></time>
</div>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="with_options">
Со встроенными настройками
</h3>
<time data-intl_type="date" data-intl_value="2021-08-16 12:00" data-intl_options="dateStyle: long, timeStyle: 'short'"></time>
</div>
</section>
<section data-intl_root id="local_root" class="card">
<h2 data-intl_type="custom" data-intl_value="date_title">
Дата и время
</h2>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="without_options">
Без встроенных настроек
</h3>
<time data-intl_type="date" data-intl_value="2021-08-16 12:00"></time>
</div>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="with_options">
Со встроенными настройками
</h3>
<time data-intl_type="date" data-intl_value="2021-08-16 12:00" data-intl_options="day: 'numeric', month: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit'"></time>
</div>
</section>
<section class="card">
<h2 data-intl_type="custom" data-intl_value="relative_title">
Относительное время
</h2>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="without_options">
Без встроенных настроек
</h3>
<p data-intl_type="relative" data-intl_value="1 day" data-intl_map="1_day"></p>
</div>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="with_options">
Со встроенными настройками
</h3>
<p data-intl_type="relative" data-intl_value="1_day" data-intl_options='numeric: "always"'></p>
</div>
</section>
<section class="card">
<h2 data-intl_type="custom" data-intl_value="number_title">Число</h2>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="without_options">
Без встроенных настроек
</h3>
<p data-intl_type="number" data-intl_value="123.45"></p>
</div>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="with_options">
Со встроенными настройками
</h3>
<h4 data-intl_type="custom" data-intl_value="currency_title">
Валюта
</h4>
<p data-intl_type="number" data-intl_value="123.45" data-intl_options="style: currency; currency: USD"></p>
<h4 data-intl_type="custom" data-intl_value="unit_title">
Единица измерения
</h4>
<p data-intl_type="number" data-intl_value="123.45" data-intl_options="{ style: unit; unit: celsius }"></p>
</div>
</section>
<section class="card">
<h2 data-intl_type="custom" data-intl_value="names_title">
Названия языков и т.д.
</h2>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="without_options">
Без встроенных настроек
</h3>
<p data-intl_type="names" data-intl_value="ru"></p>
</div>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="with_options">
Со встроенными настройками
</h3>
<p data-intl_type="names" data-intl_value="EUR" data-intl_options="type: currency"></p>
</div>
</section>
<section class="card">
<h2 data-intl_type="custom" data-intl_value="list_title">Список</h2>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="without_options">
Без встроенных настроек
</h3>
<p data-intl_type="list" data-intl_value="Chrome, Firefox, Safari"></p>
</div>
<div class="box">
<h3 data-intl_type="custom" data-intl_value="with_options">
Со встроенными настройками
</h3>
<p data-intl_type="list" data-intl_value="[Chrome, 'Firefox', `Safari`]" data-intl_options="type: disjunction"></p>
</div>
</section>
</div>
</main>
Здесь у нас имеется следующее:
- два элемента с атрибутом
data-intl_root(с идентификаторамиglobal_rootиlocal_root) - переключатель локали (с идентификатором
locale) — по умолчаниюeasy-intlвыполняет автоматическую локализацию при создании экземпляра и изменении локали. Автоматическую локализацию можно отключить, передав в конструкторEasyIntlнастройкуautorunсо значениемfalse - разделы с датой и временем (один в
global_rootи еще один вlocal_root), относительным временем, числами (число, валюта и единица измерения), названием языка (язык и валюта) и списком - все заголовки имеют атрибут
data-intl_custom— перевод заголовков содержится в словаре - некоторые элементы имеют атрибут
data-intl_map - настройки могут содержать символы
'"{}` и разделяться запятой или точкой запятой. Ключ и значение должны разделяться двоеточием - список может содержать символы
'"[]` и разделяться запятой или точкой запятой
Определим некоторые настройки для глобальной локализации в intl/options.js (некоторые настройки являются дефолтными, но в данном случае большого значения это не имеет):
export default {
date: { dateStyle: 'short' },
relative: { numeric: 'auto' },
number: { style: 'decimal' },
names: { type: 'language' },
list: { type: 'conjunction' }
}
Определим словарь для заголовков в intl/dictionary.js для русского и американского английского языков:
export default {
'ru-RU': {
main_title: 'Пример использования Easy Intl',
locale_title: 'Локаль',
date_title: 'Дата и время',
relative_title: 'Относительное время',
number_title: 'Число',
currency_title: 'Валюта',
unit_title: 'Единицы измерения',
names_title: 'Названия языков и т.д.',
list_title: 'Список',
without_options: 'Без встроенных настроек',
with_options: 'Со встроенными настройками'
},
'en-US': {
main_title: 'Example of use Easy Intl',
locale_title: 'Locale',
date_title: 'Date and time',
relative_title: 'Relative time',
number_title: 'Number',
currency_title: 'Currency',
unit_title: 'Units',
names_title: 'Language names etc.',
list_title: 'List',
without_options: 'Without inline options',
with_options: 'With inline options'
}
}
Переходим к основному скрипту (script.js). Импортируем EasyIntl, настройки и словарь:
import { EasyIntl } from './node_modules/easy-intl/index.js'
import options from './intl/options.js'
import dictionary from './intl/dictionary.js'
Создаем экземпляр EasyIntl, передавая в конструктор локаль en-US, селектор корневого элемента для глобальной локализации, словарь и настройки (обратите внимание, что объект настроек должен быть распакован):
const globalIntl = new EasyIntl({
locale: 'en-US',
root: '#global_root',
dictionary,
...options
})
Поскольку мы не определяем настройку autorun со значением false, создание экземпляра EasyIntl приводит к автоматической локализации приложения (за исключением содержимого дочерних элементов local_root).
Прелесть easy-intl (и, конечно, самого Intl) заключается в том, что дефолтная локаль пользователя определяется автоматически. Это означает, что для локализации приложения на языке пользователя достаточно создать экземпляр EasyIntl (разумеется, при условии добавления к необходимым элементам атрибутов data-intl_type и data-intl_value). Поскольку для всех вспомогательных функций, используемых easy-intl, включая, корневой элемент, определены дефолтные настройки (многие дефолтные настройки определяются Intl), для локализации приложения достаточно выполнить:
new EasyIntl()
Посмотрим на локализуемые элементы, карту локализации, в целом, и локализацию основного заголовка, в частности:
console.log(globalIntl.elements) // [h1, h2, h3, ...]
console.log(globalIntl.map)
/*
{
1_day: "tomorrow"
date_title: "Date and time"
main_title: "Example of use Easy Intl"
}
*/
console.log(globalIntl.map['main_title']) // Example of use Easy Intl
Выполним ручную локализацию содержимого дочерних элементов local_root:
const localIntl = new EasyIntl({
// отключаем автоматическую локализацию
autorun: false,
dictionary,
// передаем другие настройки для даты
date: { day: '2-digit', month: 'long', year: 'numeric' }
})
// определяем локаль
localIntl.locale = 'ja-JP'
// определяем корневой элемент
localIntl.root = '#local_root'
// выполняем локализацию
localIntl.localize()
Получаем ссылку на переключатель локали и обрабатываем ее изменение:
const localeSelector = document.querySelector('#locale_selector')
localeSelector.onchange = (e) => {
// изменение свойства `locale` приводит к автоматической локализации
globalIntl.locale = e.target.value
}
Приоритет настроек следующий (от минимального к максимальному):
new EasyIntl(options)intl.localize(options)data-intl_options="options"
Получаем вполне работоспособное локализованное приложение:


Пожалуй, это все, чем я хотел поделиться с вами в данной статье.
Если вас заинтересовала утилита easy-intl, не стесняйтесь использовать ее в своих проектах. Как всегда, приветствуется любой фидбэк как в форме личных сообщений, так и на GitHub.
Что касается планов по дальнейшей разработке утилиты, то, кроме небольших доработок, связанных с преобразованием строковых значений атрибутов data-intl_value в пригодный для Intl формат, в них входит добавление типов для TypeScript и, возможно, адаптация утилиты под React в форме хука или связки провайдера и хука.
Как видите, Internationalization API предоставляет довольно интересные возможности, которые при правильном использовании могут существенно облегчить процесс интернационализации и локализации веб-приложений.
Вместе с тем надо понимать, что Internationalization API не предназначен для перевода (его задачи четко обозначены в спецификации), поэтому многие вещи придется делать вручную или прибегать к помощи таких интерфейсов, как Google Cloud Translation API или API Переводчика Яндекса.
Также важно учитывать, что Internationalization API является относительно новым интерфейсом, некоторые его возможности еще находятся в процессе реализации браузерами, и, вероятно, в будущем появятся новые "фичи".
Благодарю за внимание и хорошего дня!

danilovmy
Привет, спасибо за примеры. Локализация циферок у нас была сделана за счёт сил базы, а теперь это можно апи-зировать, это хорошо. Но намного больше сил уходит на поддержание мультиязычности приложения. В статье в примерах есть статические тексты. Скажи, а чем у вас обрабатывают подобные тексты как вы с ними работаете?