mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 21:31:46 +00:00
126f2df21b
* chore: move i18n to shared package * chore: move server translations to shared package and apply linter and prettier on entire shared package
118 lines
4.4 KiB
TypeScript
118 lines
4.4 KiB
TypeScript
#!/usr/bin/env node
|
|
/**
|
|
* Extracts client locale files into per-namespace files under shared/src/i18n/{locale}/.
|
|
* Run with: npx tsx scripts/migrate-i18n.mts
|
|
*
|
|
* Safe to re-run — locale dirs are cleaned first. Hand-authored files
|
|
* (types.ts, languages.ts, index.ts) in shared/src/i18n/ are never touched.
|
|
*/
|
|
import { mkdir, rm, writeFile } from 'fs/promises'
|
|
import { dirname, join } from 'path'
|
|
import { fileURLToPath, pathToFileURL } from 'url'
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
const ROOT = join(__dirname, '..')
|
|
const TRANSLATIONS_DIR = join(ROOT, 'client/src/i18n/translations')
|
|
const I18N_OUT = join(ROOT, 'shared/src/i18n')
|
|
|
|
// Maps locale code → source filename (without .ts) in client/src/i18n/translations/
|
|
const LOCALE_FILE_MAP: Record<string, string> = {
|
|
de: 'de', en: 'en', es: 'es', fr: 'fr', hu: 'hu',
|
|
it: 'it', tr: 'tr', ru: 'ru', zh: 'zh', 'zh-TW': 'zhTw',
|
|
nl: 'nl', id: 'id', ar: 'ar', br: 'br', cs: 'cs',
|
|
pl: 'pl', ja: 'ja', ko: 'ko', uk: 'uk',
|
|
}
|
|
|
|
type TranslationValue = string | { name: string; category: string }[]
|
|
type LocaleStrings = Record<string, TranslationValue>
|
|
|
|
async function loadLocale(code: string): Promise<LocaleStrings> {
|
|
const filename = LOCALE_FILE_MAP[code]
|
|
if (!filename) throw new Error(`Unknown locale code: ${code}`)
|
|
const file = join(TRANSLATIONS_DIR, `${filename}.ts`)
|
|
const mod = await import(pathToFileURL(file).href)
|
|
return mod.default as LocaleStrings
|
|
}
|
|
|
|
function serializeValue(value: TranslationValue, innerIndent: string): string {
|
|
if (Array.isArray(value)) {
|
|
// Pretty-print the array then re-indent each line after the first
|
|
const lines = JSON.stringify(value, null, 2).split('\n')
|
|
return lines.map((l, i) => (i === 0 ? l : innerIndent + l)).join('\n')
|
|
}
|
|
return JSON.stringify(value)
|
|
}
|
|
|
|
async function writeLocaleDir(code: string, strings: LocaleStrings): Promise<void> {
|
|
const outDir = join(I18N_OUT, code)
|
|
await mkdir(outDir, { recursive: true })
|
|
|
|
// Group keys by top-level namespace prefix (everything before the first dot)
|
|
const namespaces = new Map<string, Array<[string, TranslationValue]>>()
|
|
for (const [key, value] of Object.entries(strings)) {
|
|
const ns = key.split('.')[0] ?? key
|
|
if (!namespaces.has(ns)) namespaces.set(ns, [])
|
|
namespaces.get(ns)!.push([key, value])
|
|
}
|
|
|
|
// Write one file per namespace
|
|
for (const [ns, entries] of namespaces) {
|
|
const lines: string[] = [
|
|
`import type { TranslationStrings } from '../types'`,
|
|
``,
|
|
`const ${ns}: TranslationStrings = {`,
|
|
...entries.map(([k, v]) => ` ${JSON.stringify(k)}: ${serializeValue(v, ' ')},`),
|
|
`}`,
|
|
`export default ${ns}`,
|
|
]
|
|
await writeFile(join(outDir, `${ns}.ts`), lines.join('\n') + '\n')
|
|
}
|
|
|
|
// Write index.ts that merges all namespace files into a single locale object
|
|
const nsNames = [...namespaces.keys()]
|
|
const indexLines: string[] = [
|
|
...nsNames.map(ns => `import ${ns} from './${ns}'`),
|
|
``,
|
|
`const locale = {`,
|
|
...nsNames.map(ns => ` ...${ns},`),
|
|
`}`,
|
|
`export default locale`,
|
|
]
|
|
await writeFile(join(outDir, 'index.ts'), indexLines.join('\n') + '\n')
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
console.log('Loading English base...')
|
|
const en = await loadLocale('en')
|
|
const codes = Object.keys(LOCALE_FILE_MAP)
|
|
|
|
// Clean existing locale dirs; leave hand-authored files (types.ts, languages.ts, index.ts) alone
|
|
await Promise.all(codes.map(code => rm(join(I18N_OUT, code), { recursive: true, force: true })))
|
|
|
|
for (const code of codes) {
|
|
process.stdout.write(`Processing ${code}...`)
|
|
let strings = await loadLocale(code)
|
|
|
|
if (code === 'ar') {
|
|
// ar.ts spreads en — keep only keys that ar actually translates (value differs from en)
|
|
const pruned: LocaleStrings = {}
|
|
for (const [key, val] of Object.entries(strings)) {
|
|
if (JSON.stringify(val) !== JSON.stringify(en[key])) {
|
|
pruned[key] = val
|
|
}
|
|
}
|
|
strings = pruned
|
|
console.log(` ${Object.keys(strings).length} own keys (pruned from ${Object.keys(en).length} en total)`)
|
|
} else {
|
|
const nsCount = new Set(Object.keys(strings).map(k => k.split('.')[0])).size
|
|
console.log(` ${Object.keys(strings).length} keys, ${nsCount} namespaces`)
|
|
}
|
|
|
|
await writeLocaleDir(code, strings)
|
|
}
|
|
|
|
console.log('\nDone! Run: cd shared && npm run build')
|
|
}
|
|
|
|
main().catch(err => { console.error(err); process.exit(1) })
|