From ce9f703e0c98db1ac3cfa6ef58af30fd76934d13 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 21 Oct 2025 20:59:12 +0300 Subject: [PATCH] feat(scripts): import weblate CSVs into translations --- package.json | 1 + pnpm-lock.yaml | 46 ++++++++++++++++--- .../import-translations-from-weblate-csv.ts | 38 +++++++++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 scripts/import-translations-from-weblate-csv.ts diff --git a/package.json b/package.json index d4961fcbd..d974c5252 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "private": true, "devDependencies": { "@electron/rebuild": "4.0.1", + "@fast-csv/parse": "5.0.5", "@playwright/test": "1.56.1", "@triliumnext/server": "workspace:*", "@types/express": "5.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf4ecfca9..4b3dbf218 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,6 +40,9 @@ importers: '@electron/rebuild': specifier: 4.0.1 version: 4.0.1 + '@fast-csv/parse': + specifier: 5.0.5 + version: 5.0.5 '@playwright/test': specifier: 1.56.1 version: 1.56.1 @@ -2671,6 +2674,9 @@ packages: '@exercism/highlightjs-gdscript@0.0.1': resolution: {integrity: sha512-LiCFDhXCr3iIEGESHEsSCpCI7qNa2suHcrBWeOYSEtEwCXc+IQpEh5i4K8qPcOMJB9ckVOgLgbSNML8TyvPCVg==} + '@fast-csv/parse@5.0.5': + resolution: {integrity: sha512-M0IbaXZDbxfOnpVE5Kps/a6FGlILLhtLsvWd9qNH3d2TxNnpbNkFf3KD26OmJX6MHq7PdQAl5htStDwnuwHx6w==} + '@file-type/xml@0.4.3': resolution: {integrity: sha512-pGRmkHf+NofNy/52r06HOTsEwdNnBsFEhN6U95s33P+ezuoxZEyBTV9lOB1/Zr0So6/9vDVfWZXLpgd0fy8cOQ==} @@ -9533,14 +9539,29 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + lodash.isfunction@3.0.9: + resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} + + lodash.isnil@4.0.0: + resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==} + + lodash.isundefined@3.0.1: + resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==} + lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} @@ -15287,8 +15308,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-editor-classic@47.1.0': dependencies: @@ -15316,8 +15335,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-editor-multi-root@47.1.0': dependencies: @@ -15814,8 +15831,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.1.0 '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-restricted-editing@47.1.0': dependencies: @@ -16997,6 +17012,15 @@ snapshots: '@exercism/highlightjs-gdscript@0.0.1': {} + '@fast-csv/parse@5.0.5': + dependencies: + lodash.escaperegexp: 4.1.2 + lodash.groupby: 4.6.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + lodash.isundefined: 3.0.1 + lodash.uniq: 4.5.0 + '@file-type/xml@0.4.3': dependencies: sax: 1.4.1 @@ -25353,10 +25377,20 @@ snapshots: lodash.debounce@4.0.8: {} + lodash.escaperegexp@4.1.2: {} + lodash.get@4.4.2: {} + lodash.groupby@4.6.0: {} + lodash.isequal@4.5.0: {} + lodash.isfunction@3.0.9: {} + + lodash.isnil@4.0.0: {} + + lodash.isundefined@3.0.1: {} + lodash.memoize@4.1.2: {} lodash.merge@4.6.2: {} diff --git a/scripts/import-translations-from-weblate-csv.ts b/scripts/import-translations-from-weblate-csv.ts new file mode 100644 index 000000000..374f9ae67 --- /dev/null +++ b/scripts/import-translations-from-weblate-csv.ts @@ -0,0 +1,38 @@ +import { readFileSync, writeFileSync } from "fs"; +import { parseString } from '@fast-csv/parse'; + +const csvPath = process.argv[2]; +const translationPath = process.argv[3]; + +if (!csvPath || !translationPath) { + console.log("Usage: input.csv translation.json") + process.exit(1); +} + +const csvFile = readFileSync(csvPath, "utf-8"); +const translationFile = readFileSync(translationPath, "utf-8"); +const translation = JSON.parse(translationFile); + +parseString(csvFile, { headers: true }) + .on("error", error => { + console.error(error); + process.exit(2); + }) + .on("data", data => { + replaceTranslation(data.context, data.target); + }) + .on("end", () => { + writeFileSync(translationPath, JSON.stringify(translation, null, 2)); + }); + +function replaceTranslation(path: string, value: string) { + let cursor = translation; + const segments = path.split("."); + const lastSegment = segments.pop(); + for (const current of segments) { + if (!cursor[current]) cursor[current] = {}; + cursor = cursor[current]; + } + + cursor[lastSegment] = value; +}