// The dictionaries and the translator. The key sets are asserted equal because // the translator falls back to whichever dictionary has a key: an asymmetric key // would silently switch language instead of failing, and a key nobody reads is a // defect this repository has removed once already. import { test } from 'node:test' import assert from 'node:assert/strict' import { createTranslator, dictionaries, registerDictionaries } from '../../src/client/i18n/index.js' import { zh } from '../../src/client/i18n/zh.js' import { en } from '../../src/client/i18n/en.js' test('both dictionaries carry exactly the same keys', () => { assert.deepEqual(Object.keys(zh).sort(), Object.keys(en).sort()) assert.deepEqual(Object.keys(dictionaries).sort(), ['en', 'zh']) }) test('every key carries copy in both languages', () => { for (const [key, value] of Object.entries(zh)) { assert.equal(typeof value, 'string', `${key} is not a string in zh`) assert.notEqual(value, '', `${key} is empty in zh`) } for (const [key, value] of Object.entries(en)) { assert.equal(typeof value, 'string', `${key} is not a string in en`) assert.notEqual(value, '', `${key} is empty in en`) } }) test('the seat translation wins, and a key it does not know falls back to the dictionary', () => { const locale = { bind: (namespace) => { assert.equal(namespace, 'session-notify') return (key, params) => (key === 'notification.completion' ? `seat:${params?.title ?? ''}` : key) }, getSnapshot: () => ({ active: 'zh' }), } const t = createTranslator(locale) assert.equal(t('notification.completion', { title: 'X' }), 'seat:X') assert.equal(t('body.untitled'), '未命名会话') }) test('the active language selects the dictionary, and an unknown key renders itself', () => { const t = createTranslator({ bind: () => undefined, getSnapshot: () => ({ active: 'zh-CN' }) }) assert.equal(t('body.untitled'), '未命名会话') const english = createTranslator(undefined) assert.equal(english('body.untitled'), 'Untitled conversation') assert.equal(english('nope.not.a.key'), 'nope.not.a.key') }) test('interpolation replaces every known placeholder and leaves unknown ones alone', () => { const t = createTranslator({ getSnapshot: () => ({ active: 'en' }) }) assert.equal(t('body.approval', { title: 'A', tool: 'Bash' }), 'A: Bash is waiting for your approval') assert.equal(t('body.approval', { title: 'A' }), 'A: {tool} is waiting for your approval') assert.equal(t('body.untitled', { unused: 'x' }), 'Untitled conversation') }) test('a locale service that throws on every call keeps the copy renderable', () => { const t = createTranslator({ bind: () => { throw new Error('no such namespace') }, getSnapshot: () => { throw new Error('no snapshot') }, }) assert.equal(t('body.untitled'), 'Untitled conversation') }) test('registration prefers the typed form and falls back to one call per locale', () => { const typed = [] const disposeTyped = registerDictionaries({ register: (ns, dict) => { typed.push([ns, dict]); return () => {} } }) assert.deepEqual(typed, [['session-notify', dictionaries]]) assert.equal(typeof disposeTyped, 'function') const single = [] let disposed = 0 const disposeSingle = registerDictionaries({ register: (ns, id, dict) => { if (typeof id === 'object') throw new Error('typed form unsupported') single.push([ns, id, dict]) return () => { disposed += 1 } }, }) assert.deepEqual(single.map(([ns, id]) => [ns, id]), [['session-notify', 'zh'], ['session-notify', 'en']]) disposeSingle() assert.equal(disposed, 2) }) test('a profile without a locale service is not an error', () => { assert.equal(typeof registerDictionaries(undefined), 'function') assert.doesNotThrow(() => registerDictionaries(undefined)()) assert.equal(createTranslator(undefined)('body.untitled'), 'Untitled conversation') })