Как убрать лишнюю информацию из идентификаторов локалей
Используйте метод minimize, чтобы создавать компактные идентификаторы локалей без потери смысла
Введение
Идентификаторы локалей, такие как en-Latn-US и zh-Hans-CN, содержат несколько компонентов, указывающих язык, письменность и регион. Но не все эти компоненты нужны для определения локали. Некоторые из них избыточны, потому что их можно вывести из других частей идентификатора.
Метод minimize() убирает такие избыточные компоненты, чтобы получить самый короткий эквивалент идентификатора локали. Это позволяет создавать компактные идентификаторы, которые сохраняют смысл, уменьшают размер хранения и улучшают читаемость.
Как понять избыточность в идентификаторах локалей
Идентификатор локали становится избыточным, если явно указывает информацию, которая уже подразумевается другими компонентами. Это происходит потому, что для каждого языка обычно есть значения по умолчанию для письменности и региона.
Рассмотрим идентификатор en-Latn-US. В нем указано:
- Язык: английский (
en) - Письменность: латиница (
Latn) - Регион: США (
US)
Английский всегда пишется латиницей, а если регион не указан, по умолчанию подразумевается США. И письменность, и регион здесь избыточны, потому что совпадают с типичными значениями по умолчанию для английского. Идентификатор en несет ту же информацию.
То же самое работает и для других языков. Корейский (ko) пишется на хангыле (Kore) и в основном используется в Южной Корее (KR). Идентификатор ko-Kore-KR содержит лишнюю информацию, потому что одного ko достаточно — он уже подразумевает эти значения по умолчанию.
Как работает метод minimize
Метод minimize() доступен у экземпляров Intl.Locale. Он анализирует идентификатор локали и убирает компоненты, совпадающие с типичными значениями по умолчанию.
const locale = new Intl.Locale("en-Latn-US");
const minimized = locale.minimize();
console.log(minimized.baseName);
// Output: "en"
Метод возвращает новый экземпляр Intl.Locale с удалёнными избыточными подметками. Оригинальный объект локали не изменяется.
Процесс минимизации следует алгоритму Unicode CLDR «Remove Likely Subtags». Этот алгоритм использует базу данных вероятных соответствий подметок, чтобы определить, какие компоненты можно удалить без потери информации.
Компоненты, на которые влияет минимизация
Метод minimize() влияет только на основные компоненты локали: язык, письменность и регион. Он не удаляет и не изменяет расширения Unicode, которые задают предпочтения форматирования.
const locale = new Intl.Locale("en-Latn-US-u-ca-gregory-nu-latn");
const minimized = locale.minimize();
console.log(minimized.toString());
// Output: "en-u-ca-gregory-nu-latn"
Расширения календаря (ca-gregory) и системы счисления (nu-latn) остаются без изменений. Удаляются только избыточные компоненты письменности (Latn) и региона (US).
Примеры минимизации
Разные идентификаторы локалей минимизируются до разной длины в зависимости от того, какие компоненты считаются избыточными.
Удаление письменности и региона
Когда и письменность, и регион совпадают с настройками по умолчанию, оба компонента удаляются:
const english = new Intl.Locale("en-Latn-US");
console.log(english.minimize().baseName);
// Output: "en"
const korean = new Intl.Locale("ko-Kore-KR");
console.log(korean.minimize().baseName);
// Output: "ko"
const japanese = new Intl.Locale("ja-Jpan-JP");
console.log(japanese.minimize().baseName);
// Output: "ja"
Сохранение нестандартных регионов
Если регион отличается от стандартного, он сохраняется в минимизированном идентификаторе:
const britishEnglish = new Intl.Locale("en-Latn-GB");
console.log(britishEnglish.minimize().baseName);
// Output: "en-GB"
const canadianFrench = new Intl.Locale("fr-Latn-CA");
console.log(canadianFrench.minimize().baseName);
// Output: "fr-CA"
const mexicanSpanish = new Intl.Locale("es-Latn-MX");
console.log(mexicanSpanish.minimize().baseName);
// Output: "es-MX"
Компонент письменности удаляется, так как совпадает с настройкой по умолчанию, а регион сохраняется, потому что указывает на нестандартный вариант языка.
Сохранение нестандартных письменностей
Если письменность отличается от стандартной, она сохраняется в минимизированном идентификаторе:
const simplifiedChinese = new Intl.Locale("zh-Hans-CN");
console.log(simplifiedChinese.minimize().baseName);
// Output: "zh-Hans"
const traditionalChinese = new Intl.Locale("zh-Hant-TW");
console.log(traditionalChinese.minimize().baseName);
// Output: "zh-Hant"
const serbianCyrillic = new Intl.Locale("sr-Cyrl-RS");
console.log(serbianCyrillic.minimize().baseName);
// Output: "sr-Cyrl"
Для китайского языка компонент письменности необходим, чтобы различать упрощённые и традиционные варианты. Для сербского — чтобы различать кириллицу и латиницу.
Уже минимальные идентификаторы
Если идентификатор локали уже минимален, метод возвращает эквивалентную локаль без изменений:
const minimal = new Intl.Locale("fr");
console.log(minimal.minimize().baseName);
// Output: "fr"
Связь с maximize
Метод minimize() является обратным к maximize(). Метод maximize() добавляет вероятные подметки для создания полного идентификатора, а minimize() удаляет избыточные подметки для создания компактного идентификатора.
Эти методы образуют пару, позволяя выполнять двустороннее преобразование между полными и компактными формами:
const compact = new Intl.Locale("en");
const complete = compact.maximize();
console.log(complete.baseName);
// Output: "en-Latn-US"
const compactAgain = complete.minimize();
console.log(compactAgain.baseName);
// Output: "en"
Преобразование из компактной формы в полную и обратно возвращает исходную форму.
Однако не все локали возвращаются к своему точному исходному виду после такого преобразования. Метод создает каноническую минимальную форму, а не сохраняет исходную структуру:
const locale = new Intl.Locale("en-US");
const maximized = locale.maximize();
console.log(maximized.baseName);
// Output: "en-Latn-US"
const minimized = maximized.minimize();
console.log(minimized.baseName);
// Output: "en"
Исходный идентификатор en-US содержал не избыточный регион, но после максимизации и минимизации он становится en. Это происходит потому, что США — наиболее вероятный регион по умолчанию для английского языка.
Когда использовать минимизацию
Используйте minimize(), если нужны компактные идентификаторы локалей, которые остаются однозначными. В ряде случаев минимизация особенно полезна.
Хранение языковых предпочтений
Минимизированные идентификаторы уменьшают объем хранимых данных в базах данных, локальном хранилище или конфигурационных файлах:
function saveUserLocale(localeString) {
const locale = new Intl.Locale(localeString);
const minimized = locale.minimize().toString();
localStorage.setItem("userLocale", minimized);
}
saveUserLocale("en-Latn-US");
// Stores "en" instead of "en-Latn-US"
Это уменьшает размер хранимых данных без потери информации.
Создание читаемых URL
Минимизированные идентификаторы делают URL для выбора языка более чистыми:
function createLocalizedURL(path, localeString) {
const locale = new Intl.Locale(localeString);
const minimized = locale.minimize().baseName;
return `/${minimized}${path}`;
}
const url = createLocalizedURL("/products", "en-Latn-US");
console.log(url);
// Output: "/en/products"
URL /en/products выглядит более читаемо, чем /en-Latn-US/products.
Сравнение идентификаторов локалей
Минимизация помогает определить, представляют ли два идентификатора одну и ту же локаль:
function areLocalesEquivalent(locale1String, locale2String) {
const locale1 = new Intl.Locale(locale1String).minimize();
const locale2 = new Intl.Locale(locale2String).minimize();
return locale1.toString() === locale2.toString();
}
console.log(areLocalesEquivalent("en", "en-Latn-US"));
// Output: true
console.log(areLocalesEquivalent("en-US", "en-Latn-US"));
// Output: true
console.log(areLocalesEquivalent("en-US", "en-GB"));
// Output: false
Минимизация создает каноническую форму, позволяющую сравнивать идентификаторы напрямую.
Нормализация пользовательского ввода
Если вы принимаете идентификаторы локалей от пользователей или внешних систем, минимизируйте их до стандартной формы:
function normalizeLocale(localeString) {
try {
const locale = new Intl.Locale(localeString);
return locale.minimize().toString();
} catch (error) {
return null;
}
}
console.log(normalizeLocale("en-US"));
// Output: "en"
console.log(normalizeLocale("en-Latn-US"));
// Output: "en"
console.log(normalizeLocale("en-GB"));
// Output: "en-GB"
Эта функция принимает разные варианты одной и той же локали и возвращает единое представление.
Совмещение минимизации с другими операциями над локалями
Метод minimize() работает вместе с другими возможностями Intl.Locale для гибкой работы с локалями.
Минимизация после изменения свойств локали
При создании локали из компонентов минимизируйте её, чтобы убрать лишние части:
const locale = new Intl.Locale("en", {
region: "US",
script: "Latn"
});
const minimized = locale.minimize();
console.log(minimized.baseName);
// Output: "en"
Это гарантирует, что итоговый идентификатор будет максимально компактным, насколько позволяют исходные компоненты.
Сохранение расширений при минимизации
Расширения сохраняются при минимизации, что позволяет минимизировать основные компоненты, сохраняя настройки форматирования:
function createCompactLocaleWithPreferences(language, region, preferences) {
const locale = new Intl.Locale(language, {
region: region,
...preferences
});
return locale.minimize().toString();
}
const localeString = createCompactLocaleWithPreferences("en", "US", {
hourCycle: "h23",
calendar: "gregory"
});
console.log(localeString);
// Output: "en-u-ca-gregory-hc-h23"
Основные компоненты минимизируются до en, но расширения календаря и формата времени остаются.