You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

65 lines
2.1 KiB

/**
* Audit: t('...') keys in src vs en translation.
* Run: npx tsx scripts/i18n-audit.ts
*/
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import en from '../src/i18n/locales/en'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
function walk(dir: string, acc: string[] = []): string[] {
for (const name of fs.readdirSync(dir)) {
const p = path.join(dir, name)
const st = fs.statSync(p)
if (st.isDirectory()) {
if (name === 'node_modules' || name === 'dist') continue
walk(p, acc)
} else if (/\.(tsx|ts)$/.test(name)) acc.push(p)
}
return acc
}
function unquoteSingle(s: string) {
return s.replace(/\\'/g, "'").replace(/\\\\/g, '\\')
}
function unquoteDouble(s: string) {
return s.replace(/\\"/g, '"').replace(/\\\\/g, '\\')
}
function extractTKeys(content: string): Set<string> {
const keys = new Set<string>()
const re1 = /\bt\(\s*'((?:\\.|[^'\\])*)'/g
let m
while ((m = re1.exec(content)) !== null) {
const raw = unquoteSingle(m[1])
if (raw.length > 0 && raw.length < 400) keys.add(raw)
}
const re2 = /\bt\(\s*"((?:\\.|[^"\\])*)"/g
while ((m = re2.exec(content)) !== null) {
const raw = unquoteDouble(m[1])
if (raw.length > 0 && raw.length < 400) keys.add(raw)
}
return keys
}
const root = path.join(__dirname, '..')
const srcDir = path.join(root, 'src')
const files = walk(srcDir)
const used = new Set<string>()
for (const f of files) {
const c = fs.readFileSync(f, 'utf8')
for (const k of extractTKeys(c)) used.add(k)
}
const enKeys = new Set(Object.keys(en.translation))
const missingInEn = [...used].filter((k) => !enKeys.has(k)).sort()
const orphanInEn = [...enKeys].filter((k) => !used.has(k)).sort()
console.log('Used t() keys:', used.size)
console.log('en keys:', enKeys.size)
console.log('Used but missing in en:', missingInEn.length)
if (missingInEn.length) console.log(missingInEn.join('\n'))
console.log('In en but not in t()-scan (may be dynamic / false orphan):', orphanInEn.length)
if (orphanInEn.length) console.log(orphanInEn.slice(0, 100).join('\n'))