Алиасы и нормализация
Как любое написание кода попадает на правильный язык — включая сломанные.
Коды приходят из экспортов TMS, клиентских API, таблиц и десятилетия накопленных интеграций. Они мало в чём согласны между собой. Нормализация — это то, что делает их сравнимыми.
Две формы кода
Ключ поиска — нижний регистр, разделитель -, пробелы срезаны. pt_BR, PT-BR, pt-br и pt - br становятся pt-br. Алиасы хранятся в этой форме и сверяются точным равенством, так что горячий путь — одно индексированное сравнение, а не LIKE-скан.
Каноническая форма — регистр BCP-47: первичный сабтег строчными, скрипт с заглавной, регион прописными. pt-BR, zh-Hans, sr-Latn-RS. Именно это возвращает API и именно это стоит хранить.
Канонизация также исправляет порядок сабтегов. BCP-47 ставит скрипт перед регионом, так что sr-RS-Latn и sr-Latn-RS парсятся в один тег и оба рендерятся как sr-Latn-RS.
Исправление гомоглифов
Перед вычислением обеих форм коды проходят через проверку гомоглифов.
Это не гипотетически. Код, первый символ которого — U+0441 CYRILLIC SMALL LETTER ES, а не ASCII c, выглядит идентично в любом шрифте; без исправления он никогда не совпал бы с тем, что присылает вызывающий, — именно такой дефект делает язык просто отсутствующим.
Исправление мапит кириллические буквы, визуально идентичные латинским — а е о р с х у і ј, — и только когда результат становится чистым ASCII, так что действительно кириллическое значение остаётся нетронутым, а не искажённым. Полноширинная латиница (en) сворачивается через NFKC в том же проходе.
Что покрывают алиасы
| Вид | Примеры |
|---|---|
| Собственный канонический код языка | pt-BR |
| Другие написания того же языка | es-la, ES-LA для es-419 |
| ISO 639-2/3 и устаревшие ISO 639-1 | deu, ger, zho, iw, in, ji, mo |
| То, с чем приходит конкретная интеграция | por-br |
GET /v1/languages/{code} возвращает полный перечень в поле aliases, так что можно проверить, покрыто ли уже написание, которое вы получаете.
Уникальность
Алиас принадлежит ровно одному языку, глобально. Это обеспечивает база данных, а не договорённость.
Это важно, потому что входящий резолвинг обязан быть функцией: один код всегда означает один язык — в каждом запросе и в каждом процессе. Если бы код мог претендовать на два языка, ответ зависел бы от того, какую строку прочитали первой, и одна и та же задача могла бы зарезолвиться дважды по-разному.
Поэтому когда два языка оба могли бы претендовать на одно написание, конфликт решается один раз, заранее, и фиксируется — а не во время запроса.
Для чего алиас не нужен
Для вариантов регистра и разделителей. pt_BR уже совпадает с алиасом pt-br; добавлять pt_br отдельной строкой избыточно.
Проверить код, не резолвя его
/v1/normalize отвечает на вопрос без провайдера — что это за язык и как мы его называем?
curl "https://languages.service.custom.mt/v1/normalize?language=ZH_HANS"
{ "query": "ZH_HANS", "known": true, "matched_alias": "zh-hans",
"language": { "code": "zh-CN", "name": "Chinese (Simplified)", "aliases": ["chs", "zh-hans", …] } }
В отличие от других эндпоинтов, неизвестный код здесь не ошибка — known: false приходит с 200. Это позволяет классифицировать весь список за один проход, а не ловить 404. См. Нормализация входящих кодов.
Далее
- Language Catalog — что в каталоге
- Resolution Ladder — что происходит после определения языка