diff --git a/package.json b/package.json index 53c0182fc..0c2c254b9 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,8 @@ "i18n-export": "node ./scripts/i18n-export.js", "i18n-update-push": "node ./scripts/i18n-upload.mjs", "i18n-update-pull": "node ./scripts/i18n-import.mjs", - "i18n-exclude": "node ./scripts/i18n-exclude.js" + "i18n-exclude": "node ./scripts/i18n-exclude.js", + "i18n-lokalise-json": "node ./scripts/i18n-lokalise-json.mjs" }, "dependencies": { "@codemirror/autocomplete": "6.3.0", diff --git a/scripts/i18n-lokalise-json.mjs b/scripts/i18n-lokalise-json.mjs new file mode 100644 index 000000000..b4336f7ca --- /dev/null +++ b/scripts/i18n-lokalise-json.mjs @@ -0,0 +1,66 @@ +// Generate Lokalise JSON files from TypeScript src files in src/livecodes/i18n/locales/ directory. + +import fs from 'fs'; +import path from 'path'; +import babel from '@babel/core'; +import parser from '@babel/parser'; +import { autoGeneratedWarning, sortedJSONify } from './i18n-export.js'; + +const flatten = (obj, prefix = '') => + Object.keys(obj).reduce((acc, key) => { + const value = obj[key]; + if (typeof value === 'object') { + return { ...acc, ...flatten(value, `${prefix}${key}.`) }; + } + return { ...acc, [`${prefix}${key}`]: value }; + }, {}); + +const generateLokaliseJSON = async () => { + const lang = process.argv[2]; + const srcDir = path.resolve('src/livecodes/i18n/locales/' + lang); + if (!fs.existsSync(srcDir)) { + console.error(`Language ${srcDir} does not exist.`); + return; + } + + const files = fs + .readdirSync(srcDir) + .filter((file) => file.endsWith('.ts')) + .map((file) => path.resolve(srcDir, file)); + + await Promise.all( + files.map(async (file) => { + try { + console.log(`Generating Lokalise JSON for ${file} in language ${lang}...`); + + const data = await fs.promises.readFile(file, 'utf8'); + const ast = parser.parse(data, { + sourceType: 'module', + plugins: ['typescript'], + }); + + // Find first ObjectExpression and load it + let translation; + babel.traverse(ast, { + ObjectExpression(path) { + const code = data.substring(path.node.start, path.node.end); + translation = eval(`(${code})`); + path.stop(); + }, + }); + + const result = { $comment: autoGeneratedWarning.substring(3) }; + for (const [key, value] of Object.entries(flatten(translation))) { + result[key] = { translation: value }; + } + + const outFile = path.resolve(srcDir, file.replace('.ts', '.lokalise.json')); + await fs.promises.writeFile(outFile, sortedJSONify(result).replace(/<(\/?)(\d+)>/g, '<$1tag-$2>')); + } catch (err) { + console.error(err); + } + }), + ); +}; + +generateLokaliseJSON();