import assert from 'node:assert/strict' import { readFile, readdir } from 'node:fs/promises' import path from 'node:path' const root = new URL('../', import.meta.url) const read = p => readFile(new URL(p, root), 'utf8') const locales = {} for (const loc of ['cn', 'en', 'hk']) { locales[loc] = JSON.parse(await read(`src/views/lang/${loc}.json`)) } // 扫描源码中真正引用到的 ssArticle* 键,而不是维护一张会过期的手写清单 const scanRoots = [ 'src/views/Editor/EnglishSpeaking', 'src/views/Student/components/ArticleReadingClassPanel', 'src/views/components/element/FrameElement', 'src/views/Editor/CanvasTool', 'src/components/CollapsibleToolbar', 'src/views/Student', ] async function collectFiles(dir) { const out = [] let entries try { entries = await readdir(new URL(dir + '/', root), { withFileTypes: true }) } catch { return out } for (const entry of entries) { const child = path.posix.join(dir, entry.name) if (entry.isDirectory()) out.push(...await collectFiles(child)) else if (/\.(vue|ts)$/.test(entry.name)) out.push(child) } return out } const files = (await Promise.all(scanRoots.map(collectFiles))).flat() const used = new Set() for (const file of files) { const source = await read(file) for (const match of source.matchAll(/\blang\.(ssArticle[A-Za-z0-9]*)/g)) used.add(match[1]) } assert.ok(used.size > 20, `expected many article keys in use, found ${used.size}`) // 每个被引用的键在三个语言包里都必须存在、非空、无首尾空白 const missing = [] for (const key of [...used].sort()) { for (const loc of ['cn', 'en', 'hk']) { const value = locales[loc][key] if (typeof value !== 'string' || value.length === 0) { missing.push(`${loc}.json missing ${key}`) continue } assert.equal(value, value.trim(), `${loc}.json ${key} has surrounding whitespace`) } } assert.deepEqual(missing, [], `unresolved article i18n keys:\n${missing.join('\n')}`) // 三个语言包的 ssArticle* 键集合必须完全一致,防止某个语言漏翻 const keySets = Object.fromEntries( Object.entries(locales).map(([loc, pack]) => [ loc, Object.keys(pack).filter(key => key.startsWith('ssArticle')).sort(), ]), ) assert.deepEqual(keySets.en, keySets.cn, 'en.json article keys differ from cn.json') assert.deepEqual(keySets.hk, keySets.cn, 'hk.json article keys differ from cn.json') // en 与 cn 的文案不应逐字相同(除少数本就一致的符号型文案) const identical = keySets.cn.filter(key => locales.en[key] === locales.cn[key]) assert.ok( identical.length <= 2, `en.json looks untranslated for: ${identical.join(', ')}`, ) console.log(`article reading i18n: ${used.size} keys used, ${keySets.cn.length} defined, all three locales consistent`)