3749f87c1d
Integrate new locale bundles, language cycling in the UI, SEO hreflang tags, and localized beta flyer HTML variants with scripts for batch translation and key validation. Co-authored-by: Cursor <cursoragent@cursor.com>
53 lines
1.6 KiB
JavaScript
53 lines
1.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Verify all locale JSON files have identical key sets.
|
|
* Usage: node scripts/validate-i18n-keys.mjs
|
|
*/
|
|
|
|
import { readFile } from 'node:fs/promises'
|
|
import { resolve, dirname } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { flattenTranslation } from './lib/deepl-translate.mjs'
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
const localesDir = resolve(__dirname, '../client/src/i18n/locales')
|
|
const localeFiles = ['de.json', 'en.json', 'da.json', 'sv.json', 'nb.json']
|
|
|
|
async function loadKeys(filename) {
|
|
const raw = await readFile(resolve(localesDir, filename), 'utf8')
|
|
const json = JSON.parse(raw)
|
|
return flattenTranslation(json.translation).map(([path]) => path).sort()
|
|
}
|
|
|
|
async function main() {
|
|
const keySets = {}
|
|
for (const file of localeFiles) {
|
|
keySets[file] = await loadKeys(file)
|
|
}
|
|
|
|
const master = keySets['de.json']
|
|
let failed = false
|
|
|
|
for (const file of localeFiles) {
|
|
if (file === 'de.json') continue
|
|
const keys = keySets[file]
|
|
const missing = master.filter((k) => !keys.includes(k))
|
|
const extra = keys.filter((k) => !master.includes(k))
|
|
if (missing.length || extra.length) {
|
|
failed = true
|
|
console.error(`\n${file}:`)
|
|
if (missing.length) console.error(` missing (${missing.length}):`, missing.slice(0, 10).join(', '))
|
|
if (extra.length) console.error(` extra (${extra.length}):`, extra.slice(0, 10).join(', '))
|
|
} else {
|
|
console.log(`${file}: OK (${keys.length} keys)`)
|
|
}
|
|
}
|
|
|
|
if (failed) process.exit(1)
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err)
|
|
process.exit(1)
|
|
})
|