chore: 保存工作副本——客户端授权新契约(双端点/验签/令牌/周检)与桌面会话可靠性改进
This commit is contained in:
1 parent
f71eef0eba
commit
ae6093c9b3
163 files changed
+4911
-2494
No files matched your search
@@ -29,16 +29,94 @@ describe('i18n locale key parity', () => {
|
||||
for (const namespace of I18N_NAMESPACES) {
|
||||
for (const language of APP_LANGUAGES) {
|
||||
it(`${language} keys match en for ${namespace}`, async () => {
|
||||
const [locale, en] = await Promise.all([
|
||||
loadLocale(namespace, language),
|
||||
loadLocale(namespace, 'en'),
|
||||
])
|
||||
const [locale, en] = await Promise.all([loadLocale(namespace, language), loadLocale(namespace, 'en')])
|
||||
|
||||
const localeKeys = flattenKeys(locale).sort()
|
||||
const enKeys = flattenKeys(en).sort()
|
||||
|
||||
expect(localeKeys).toEqual(enKeys)
|
||||
|
||||
for (const key of localeKeys) {
|
||||
const value = key.split('.').reduce<unknown>((node, part) => {
|
||||
if (node && typeof node === 'object' && part in node) {
|
||||
return (node as Record<string, unknown>)[part]
|
||||
}
|
||||
return undefined
|
||||
}, locale)
|
||||
if (typeof value === 'string') {
|
||||
expect(value.trim().length, `${language}:${namespace}.${key}`).toBeGreaterThan(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function flattenValues(value: unknown, prefix = ''): Record<string, string> {
|
||||
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
||||
return prefix && typeof value === 'string' ? { [prefix]: value } : {}
|
||||
}
|
||||
|
||||
const out: Record<string, string> = {}
|
||||
for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {
|
||||
const next = prefix ? `${prefix}.${key}` : key
|
||||
if (nested !== null && typeof nested === 'object' && !Array.isArray(nested)) {
|
||||
Object.assign(out, flattenValues(nested, next))
|
||||
} else if (typeof nested === 'string') {
|
||||
out[next] = nested
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/** Product names, URLs, and format tokens that stay the same in every locale. */
|
||||
const SETTINGS_ALLOW_SAME_AS_EN = new Set([
|
||||
'language.zhCN',
|
||||
'language.zhTW',
|
||||
'language.en',
|
||||
'language.ja',
|
||||
'language.ko',
|
||||
'language.vi',
|
||||
'language.ru',
|
||||
'nav.ssh',
|
||||
'about.appVersion',
|
||||
'about.githubDesc',
|
||||
'about.license',
|
||||
'about.licenseLinkDesc',
|
||||
'about.wechat',
|
||||
])
|
||||
|
||||
describe('settings copy is translated', () => {
|
||||
it.each(APP_LANGUAGES.filter((language) => language !== 'en'))(
|
||||
'%s does not leave English settings/about strings',
|
||||
async (language) => {
|
||||
const [locale, en] = await Promise.all([loadLocale('settings', language), loadLocale('settings', 'en')])
|
||||
const localeValues = flattenValues(locale)
|
||||
const enValues = flattenValues(en)
|
||||
const leftover = Object.keys(enValues).filter((key) => {
|
||||
if (SETTINGS_ALLOW_SAME_AS_EN.has(key)) return false
|
||||
return localeValues[key] === enValues[key]
|
||||
})
|
||||
expect(leftover, leftover.join(', ')).toEqual([])
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
/** Product name and shared layout templates in Agent CLI. */
|
||||
const AI_ALLOW_SAME_AS_EN = new Set(['panel.title', 'panel.modelConfigured', 'panel.sshReady', 'step.confirmMessage'])
|
||||
|
||||
describe('ai copy is translated', () => {
|
||||
it.each(APP_LANGUAGES.filter((language) => language !== 'en'))(
|
||||
'%s does not leave English Agent CLI strings',
|
||||
async (language) => {
|
||||
const [locale, en] = await Promise.all([loadLocale('ai', language), loadLocale('ai', 'en')])
|
||||
const localeValues = flattenValues(locale)
|
||||
const enValues = flattenValues(en)
|
||||
const leftover = Object.keys(enValues).filter((key) => {
|
||||
if (AI_ALLOW_SAME_AS_EN.has(key)) return false
|
||||
return localeValues[key] === enValues[key]
|
||||
})
|
||||
expect(leftover, leftover.join(', ')).toEqual([])
|
||||
}
|
||||
)
|
||||
})
|
||||
Reference in new issue
Block a user