Files
zeekay 595bb3b930 79 locales: right-to-left by script, regional variants in resolve
Add 43 locales to the registry, their loaders and catalogs: Persian,
Dari, Hebrew, Urdu, Pashto, Central Kurdish, Sindhi, Uyghur, Punjabi in
both scripts, four regional Arabics, Hong Kong Chinese, Mexican and
Argentine Spanish, European Portuguese, Canadian French, and 24 more.
34 wait for a native reader in review.json; ti, qu, gn, ay, bo and dv
are not shipped.

Every locale in Arabic or Hebrew script is rtl, and the registry test
derives the script from the tag. rtl.css selects on :dir(rtl), so it
holds inside a right-to-left region of a left-to-right page.

resolve no longer leans on registry order: after the exact tag and the
script, a request takes its own region, then the variant that serves it
(CLDR parents for es-419 and pt-PT, Macau to zh-HK, the Arabic dialect
areas), then the variant with no region, then the language's own region.
pnb reaches pa-PK.
2026-10-22 05:30:36 -07:00

48 lines
2.1 KiB
TypeScript

import assert from 'node:assert/strict'
import { test } from 'node:test'
import { SUPPORTED_LOCALES, resolve } from '../src/index.ts'
import { detect, preferred } from '../src/native.ts'
test('a device preference becomes a locale we ship', () => {
assert.equal(detect({ locales: () => ['ja-JP'] }), 'ja-JP')
assert.equal(detect({ locales: () => ['en-GB'] }), 'en-US')
assert.equal(detect({ locales: () => ['zh-Hant-HK'] }), 'zh-HK')
assert.equal(detect({ locales: () => ['zh-Hant-TW'] }), 'zh-Hant')
})
test('the phone answers with a list, and order is preference', () => {
// iOS hands back AppleLanguages, which is the whole ordered list.
assert.equal(detect({ locales: () => ['fr-BE', 'en-US'] }), 'fr-FR')
})
test('no device is the default, not a crash', () => {
// A native screen rendered on a server, or a unit test, has no device. It must
// still render.
assert.equal(detect({ locales: () => [] }), 'en-US')
})
test('detect and resolve cannot disagree', () => {
// native exists to ANSWER a question, not to re-answer the negotiation. If it
// ever grew its own matching, a phone and a browser set to the same language
// would render differently, which is the bug this package is shaped to avoid.
for (const tag of ['zh-Hant-HK', 'en-GB', 'es-MX', 'ko-KR', 'nope']) {
assert.equal(detect({ locales: () => [tag] }), resolve(tag), tag)
}
})
test('preferred() answers a usable tag on any runtime, and never throws', () => {
// The previous assertion here could not fail — `Array.isArray(x) || typeof x
// === 'object'` is true for every array — so it tested nothing while looking
// like it tested the fallback. What is actually worth pinning is that whatever
// this returns is something `resolve` can take: a list of tags, possibly empty
// on a runtime with no Intl, and never an exception on the render path.
const got = preferred()
assert.ok(Array.isArray(got), 'a list, so resolve can read it as preferences')
for (const tag of got) {
assert.equal(typeof tag, 'string')
assert.ok(tag.length > 0)
}
assert.ok(SUPPORTED_LOCALES.includes(resolve(got) as never), 'always lands on a locale we ship')
})