Алиасы и нормализация
Как любое написание кода попадает на правильный язык — включая сломанные.
Коды приходят из экспортов TMS, клиентских API, таблиц и десятилетия накопленных интеграций. Они мало в чём согласны между собой. Нормализация — это то, что делает их сравнимыми.
Две формы кода
Ключ поиска — нижний регистр, разделитель -, пробелы срезаны. pt_BR, PT-BR, pt-br и pt - br становятся pt-br. Алиасы хранятся в этой форме и сверяются точным равенством, так что горячий путь — одно индексированное сравнение, а не LIKE-скан.
Каноническая форма — регистр BCP-47: первичный сабтег строчными, скрипт с заглавной, регион прописными. pt-BR, zh-Hans, sr-Latn-RS. Именно это возвращает API и именно это стоит хранить.
Канонизация также исправляет порядок сабтегов. Консоль держит и sr-Latn-RS, и sr-RS-Latn для одного языка; BCP-47 ставит скрипт перед регионом, так что оба парсятся в один тег и рендерятся как sr-Latn-RS.
Исправление гомоглифов
Перед вычислением обеих форм коды проходят через проверку гомоглифов.
Это не гипотетически. База консоли содержит сeb для себуанского, где первый символ — U+0441 CYRILLIC SMALL LETTER ES, а не ASCII c. Он выглядит идентично в любом шрифте и никогда не совпадает с тем, что присылает вызывающий, — именно так себуанский стал фактически недостижимым для нескольких провайдеров.
Исправление мапит кириллические буквы, визуально идентичные латинским — а е о р с х у і ј, — и только когда результат становится чистым ASCII, так что действительно кириллическое значение остаётся нетронутым, а не искажённым. Полноширинная латиница (en) сворачивается через NFKC в том же проходе.
Откуда берутся алиасы
| Источник | Примеры | Зачем |
|---|---|---|
canonical |
собственный код языка | всегда присутствует |
console |
es-la, ES-LA для es-419 |
все написания из легаси-базы, включая проигравших в неоднозначных парах |
curated |
deu, ger, zho, iw, in, ji, mo |
ISO 639-2/3 и устаревшие ISO 639-1, которые до сих пор шлют экспорты TMS |
provider |
написание провайдера, отличное от победителя | держит проигравшую сторону свёрнутой пары достижимой |
manual |
то, с чем пришла новая интеграция | добавляется из [[Admin-Panel |
Уникальность
Алиас принадлежит ровно одному языку, глобально. Это обеспечивает база данных, а не договорённость.
Это важно, потому что входящий резолвинг обязан быть функцией. Консоль допускала, чтобы zh-CN был одновременно в «Chinese (PRC)» и «Chinese (Simplified)», fa-AF — в «Dari» и «Farsi (Afghanistan)», bn-BD — в двух бенгальских строках. get_language_by_code резолвил это через .first() — неупорядоченный запрос.
Билдер сида решает каждый конфликт один раз:
- язык, чей канонический код и есть этот алиас,
- далее тот, который консоль пометила
is_default, - далее тот, у кого больше маппингов провайдеров — то есть тот, который прод действительно использует,
- далее наименьший легаси-id, то есть то, что
.first()отдаёт сегодня.
Каждое решение перечислено в data/seed/REPORT.md, так что те, что стоят спора, видны, а не похоронены.
Для чего алиас не нужен
Для вариантов регистра и разделителей. 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 — что происходит после определения языка