// cSpell:ignore Tolgee import fs from 'node:fs/promises'; import path from 'node:path'; import { format } from 'prettier'; import { getAllProjectLanguages, getRemoteTranslations } from './api'; import type { TranslationRes } from './utils'; const RES_DIR = path.resolve(process.cwd(), 'src', 'resources'); const countKeys = (obj: TranslationRes) => { let count = 0; // eslint-disable-next-line @typescript-eslint/no-unused-vars Object.entries(obj).forEach(([_, value]) => { if (typeof value === 'string') { count++; } else { count += countKeys(value); } }); return count; }; const getBaseTranslations = async (baseLanguage: { tag: string }) => { try { const baseTranslationsStr = await fs.readFile( path.resolve(RES_DIR, `${baseLanguage.tag}.json`), { encoding: 'utf8' } ); const baseTranslations = JSON.parse(baseTranslationsStr); return baseTranslations; } catch (e) { console.error('base language:', JSON.stringify(baseLanguage)); console.error('Failed to read base language', e); const translations = await getRemoteTranslations(baseLanguage.tag); await fs.writeFile( path.resolve(RES_DIR, `${baseLanguage.tag}.json`), JSON.stringify(translations, null, 4) ); } }; const main = async () => { console.log('Loading project languages...'); const languages = await getAllProjectLanguages(); const baseLanguage = languages.find(language => language.base); if (!baseLanguage) { console.error(JSON.stringify(languages)); throw new Error('Could not find base language'); } console.log(`Loading ${baseLanguage.tag} languages translations as base...`); const baseTranslations = await getBaseTranslations(baseLanguage); const baseKeyNum = countKeys(baseTranslations); const languagesWithTranslations = await Promise.all( languages.map(async language => { console.log(`Loading ${language.tag} translations...`); const translations = await getRemoteTranslations(language.tag); const keyNum = countKeys(translations); return { ...language, translations, completeRate: keyNum / baseKeyNum, }; }) ); const availableLanguages = languagesWithTranslations.filter( language => language.completeRate > 0 ); availableLanguages // skip base language .filter(i => !i.base) .forEach(async language => { await fs.writeFile( path.resolve(RES_DIR, `${language.tag}.json`), JSON.stringify( { '// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.': '', ...language.translations, }, null, 4 ) + '\n' ); }); console.log('Generating meta data...'); const code = `// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. // Run \`pnpm run download-resources\` to regenerate. // To overwrite this, please overwrite ${path.basename(__filename)} ${availableLanguages .map( language => `import ${language.tag.replaceAll('-', '_')} from './${ language.tag }.json'` ) .join('\n')} export const LOCALES = [ ${availableLanguages // eslint-disable-next-line @typescript-eslint/no-unused-vars .map(({ translations, ...language }) => JSON.stringify({ ...language, res: '__RES_PLACEHOLDER', }).replace( '"__RES_PLACEHOLDER"', language.tag.replaceAll('-', '_') ) ) .join(',\n')} ] as const; `; await fs.writeFile( path.resolve(RES_DIR, 'index.ts'), format(code, { parser: 'typescript', singleQuote: true, trailingComma: 'es5', tabWidth: 4, arrowParens: 'avoid', }) ); console.log('Done'); }; main();