| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 |
- 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`)
|