Привет, друзья!
В данной статье я хочу рассказать вам об 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
является относительно новым интерфейсом, некоторые его возможности еще находятся в процессе реализации браузерами, и, вероятно, в будущем появятся новые "фичи".
Благодарю за внимание и хорошего дня!