Библиотека Intl предоставляет набор встроенных
инструментов для корректной локализации текста, чисел, дат, валют и
правил языка. При работе со сложными скриптами её значение особенно
велико, поскольку многие письменные системы требуют специальной
обработки:
Под сложными скриптами обычно понимаются системы письма, которые не ограничиваются простой последовательностью символов слева направо. К ним относятся:
Модуль Intl основан на нескольких международных
стандартах:
Большинство современных движков Javascript используют ICU как внутренний механизм локализации.
Пример определения локали:
const locale = new Intl.Locale("ar-EG");
console.log(locale.language); // ar
console.log(locale.region); // EG
Объект Intl.Locale позволяет анализировать структуру
локали и управлять параметрами языка.
Многие символы могут быть представлены несколькими способами:
const a = "é";
const b = "e\u0301";
console.log(a === b); // false
Хотя визуально строки одинаковы, бинарно они различаются.
Для корректного сравнения используется нормализация:
const normalizedA = a.normalize("NFC");
const normalizedB = b.normalize("NFC");
console.log(normalizedA === normalizedB); // true
| Форма | Назначение |
|---|---|
| NFC | Каноническая композиция |
| NFD | Каноническая декомпозиция |
| NFKC | Совместимая композиция |
| NFKD | Совместимая декомпозиция |
Для сложных скриптов нормализация особенно важна при:
Арабский и иврит используют направление письма справа налево (RTL). При смешении RTL и LTR возникают сложные проблемы отображения.
Пример:
const text = "السعر 100 USD";
console.log(text);
Порядок отображения определяется алгоритмом Unicode Bidirectional Algorithm.
<div dir="rtl">
مرحبا بالعالم
</div>
<div dir="auto">
مرحبا Hello
</div>
const formatter = new Intl.DateTimeFormat("ar-SA");
console.log(formatter.format(new Date()));
Результат будет содержать:
Иногда необходимо вручную управлять направлением текста.
| Символ | Назначение |
|---|---|
| LRM | Left-to-Right Mark |
| RLM | Right-to-Left Mark |
| LRE | Left-to-Right Embedding |
| RLE | Right-to-Left Embedding |
| Pop Directional Formatting |
Пример:
const text = "\u200Fمرحبا 123";
Во многих языках используются собственные цифры:
| Локаль | Система |
|---|---|
| ar | Арабские |
| fa | Персидские |
| hi | Деванагари |
| bn | Бенгальские |
const formatter = new Intl.NumberFormat("ar-EG");
console.log(formatter.format(123456));
Результат:
١٢٣٤٥٦
const formatter = new Intl.NumberFormat("hi-IN-u-nu-deva");
console.log(formatter.format(2025));
Результат:
२०२५
Формат локали:
language-region-u-key-value
Пример:
"ar-SA-u-nu-arab"
Где:
nu — numbering system;arab — арабские цифры.Во многих языках слова не отделяются пробелами:
Обычный split(" ") становится бесполезным.
API сегментации текста решает эту проблему.
const segmenter = new Intl.Segmenter("ja-JP", {
granularity: "word"
});
const text = "私は学生です";
for (const item of segmenter.segment(text)) {
console.log(item.segment);
}
| granularity | Назначение |
|---|---|
| grapheme | Символы |
| word | Слова |
| sentence | Предложения |
Один визуальный символ может состоять из нескольких Unicode-кодов.
Пример эмодзи:
const text = "????";
console.log(text.length);
Результат:
11
Но визуально это один символ.
const segmenter = new Intl.Segmenter(undefined, {
granularity: "grapheme"
});
for (const item of segmenter.segment(text)) {
console.log(item.segment);
}
Простое сравнение строк работает некорректно:
["ä", "z", "a"].sort();
Unicode-порядок не соответствует языковым правилам.
const collator = new Intl.Collator("de");
const words = ["z", "ä", "a"];
words.sort(collator.compare);
console.log(words);
Китайский язык имеет несколько вариантов:
const collator = new Intl.Collator("zh");
const words = ["王", "李", "张"];
words.sort(collator.compare);
const collator = new Intl.Collator("fr", {
sensitivity: "base"
});
| Значение | Поведение |
|---|---|
| base | Игнорировать акценты |
| accent | Учитывать акценты |
| case | Учитывать регистр |
| variant | Максимальная точность |
const collator = new Intl.Collator("en", {
ignorePunctuation: true
});
В арабском символ меняет форму в зависимости от позиции:
Javascript не занимается этим напрямую — рендеринг выполняет движок браузера и шрифты.
Однако Intl должен учитывать эти особенности при:
Некоторые символы объединяются в единый графический элемент.
Пример арабской лигатуры:
لا
В Unicode это может быть:
Нормализация помогает избежать ошибок сравнения.
Многие локали используют не григорианский календарь.
| Локаль | Календарь |
|---|---|
| ar-SA | Исламский |
| th-TH | Буддийский |
| ja-JP | Японский |
| fa-IR | Персидский |
const formatter = new Intl.DateTimeFormat("ar-SA");
console.log(formatter.format(new Date()));
const formatter = new Intl.DateTimeFormat("ja-JP-u-ca-japanese", {
era: "long",
year: "numeric"
});
console.log(formatter.format(new Date()));
Результат может содержать:
令和8年
const locale = new Intl.Locale("fa-IR-u-ca-persian");
console.log(locale.calendar);
Разные языки используют:
const formatter = new Intl.DateTimeFormat("th-TH", {
dateStyle: "full",
timeStyle: "long"
});
console.log(formatter.format(new Date()));
API локализует:
const languageNames = new Intl.DisplayNames(["ar"], {
type: "language"
});
console.log(languageNames.of("en"));
Результат:
الإنجليزية
Арабский язык содержит шесть форм множественного числа.
const pluralRules = new Intl.PluralRules("ar");
console.log(pluralRules.select(0));
console.log(pluralRules.select(1));
console.log(pluralRules.select(2));
console.log(pluralRules.select(3));
| Категория | Назначение |
|---|---|
| zero | Ноль |
| one | Один |
| two | Два |
| few | Несколько |
| many | Много |
| other | Остальное |
Некоторые языки имеют нестандартные правила регистра.
console.log("i".toUpperCase());
Результат:
I
Но в турецком правильно:
İ
console.log("i".toLocaleUpperCase("tr"));
Метод использует правила локали.
const result = "ä".localeCompare("z", "de");
const collator = new Intl.Collator("en", {
numeric: true
});
const files = ["file2", "file10", "file1"];
files.sort(collator.compare);
console.log(files);
Результат:
["file1", "file2", "file10"]
| Ключ | Назначение |
|---|---|
| ca | Календарь |
| nu | Система цифр |
| hc | Формат часов |
| co | Сортировка |
const locale =
"zh-Hans-CN-u-ca-chinese-nu-hanidec";
Здесь:
zh — китайский;Hans — упрощённый;CN — Китай;ca-chinese — китайский календарь;nu-hanidec — китайские десятичные числа.Создание форматтеров дорогостоящее.
Плохая практика:
for (const number of numbers) {
const formatter =
new Intl.NumberFormat("ar");
console.log(formatter.format(number));
}
Правильный подход:
const formatter =
new Intl.NumberFormat("ar");
for (const number of numbers) {
console.log(formatter.format(number));
}
Большинство возможностей поддерживаются:
Некоторые API появились позже:
| API | Поддержка |
|---|---|
| Intl.Segmenter | Новые браузеры |
| Intl.DisplayNames | Современные версии |
| Intl.Locale | ES2020+ |
Node.js может собираться:
Минимальная версия ограничивает локализацию.
console.log(Intl.DateTimeFormat.supportedLocalesOf([
"ar",
"ja",
"th",
"fa"
]));
const text = "????";
console.log(text.substring(0, 1));
Результат может повредить grapheme cluster.
console.log("?".length);
Результат:
2
Причина — UTF-16 surrogate pairs.
words.sort();
Для локализованных данных необходимо:
words.sort(collator.compare);
Неправильно:
date.toString()
Правильно:
new Intl.DateTimeFormat(locale)
Недопустимые предположения:
text.normalize("NFC");
Особенно важно тестировать:
| Задача | API |
|---|---|
| Числа | Intl.NumberFormat |
| Даты | Intl.DateTimeFormat |
| Сортировка | Intl.Collator |
| Сегментация | Intl.Segmenter |
| Plural rules | Intl.PluralRules |
| Названия | Intl.DisplayNames |