From 702ecda8d61def9c816dff58c791134a512ca4db Mon Sep 17 00:00:00 2001 From: sbs20 Date: Wed, 15 Feb 2023 18:37:07 +0000 Subject: [PATCH] Utility: find missing translations Issue #518 Also updated a few loose ends in readme. --- README.md | 4 +- docs/development.md | 6 ++ package.json | 1 + packages/client/missing-translations.js | 77 +++++++++++++++++++++++++ packages/client/package.json | 1 + packages/client/src/locales/test.json | 6 +- packages/client/src/locales/zh.json | 5 +- 7 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 packages/client/missing-translations.js diff --git a/README.md b/README.md index 30c141fe..33929a20 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,8 @@ complicated installation. * Filters: Autolevels, Threshold, Blur * Configurable overrides for all defaults as well as filters and formats * Multipage scanning (with collation for double sided scans) -* International translations: Arabic, Czech, Dutch, French, German, Italian, Mandarin, - Polish, Portuguese (PT & BR), Russian, Spanish, Turkish; +* International translations: Arabic, Czech, Dutch, French, German, Italian, + Mandarin, Polish, Portuguese (PT & BR), Russian, Slovak, Spanish, Turkish; [Help requested](https://github.com/sbs20/scanservjs/issues/154) * Light and dark mode * Responsive design diff --git a/docs/development.md b/docs/development.md index 592c2e04..d6e0d212 100644 --- a/docs/development.md +++ b/docs/development.md @@ -59,6 +59,12 @@ Alternatively, create a local release package npm run release ``` +## Find missing translations + +``` +npm run missing-translations +``` + ## Updating node dependencies * `npm audit fix` or `npm update`. This won't remove old packages; to do so, diff --git a/package.json b/package.json index 7b276e80..c6d0124b 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "scanservjs is a simple web-based UI for SANE which allows you to share a scanner on a network without the need for drivers or complicated installation.", "scripts": { "clean": "rm -rf ./dist", + "missing-translations": "cd packages/client && npm run missing-translations", "version": "npm i && cd packages/client && npm --allow-same-version --no-git-tag-version version $npm_package_version && cd ../server && npm --allow-same-version --no-git-tag-version version $npm_package_version", "install": "cd packages/client && npm i --loglevel=error && cd ../server && npm i --loglevel=error", "lint": "cd packages/server && npm run lint", diff --git a/packages/client/missing-translations.js b/packages/client/missing-translations.js new file mode 100644 index 00000000..e62a3125 --- /dev/null +++ b/packages/client/missing-translations.js @@ -0,0 +1,77 @@ +const fs = require('fs'); + +const EXCLUDED_KEY = ['locales']; +const BASE = 'en.json'; + +function missingKeys(o1, o2) { + return Object.keys(o1) + .filter(k => !EXCLUDED_KEY.some(ex => ex === k)) + .flatMap(k => { + return (!(k in o2)) + ? [k] + : typeof o2[k] === 'object' + ? missingKeys(o1[k], o2[k]).map(s => `${k}.${s}`) + : []; + }); +} + +const locales = new class Locales { + constructor(path) { + this.path = path || 'src/locales'; + } + + /** + * @returns {Promise.} + */ + async list() { + return await new Promise((resolve, reject) => { + fs.readdir(this.path, (err, list) => { + if (err) { + reject(err); + } + resolve(list); + }); + }); + } + + /** + * @returns {Promise.} + */ + async all() { + if (this._all === undefined) { + this._all = (await this.list()) + .map(s => ({ + key: s, + data: require(`./${this.path}/${s}`) + })); + } + return this._all; + } + + /** + * @returns {Promise.} + */ + async base() { + return (await this.all()).filter(l => l.key === BASE)[0]; + } + + /** + * @returns {Promise.} + */ + async children() { + return (await this.all()).filter(l => l.key !== BASE); + } +} + +async function main() { + const en = await locales.base(); + const children = await locales.children(); + const report = children + .map(child => ({ + key: child.key, + missing: missingKeys(en.data, child.data) + })); + console.log(JSON.stringify(report)); +} + +main(); diff --git a/packages/client/package.json b/packages/client/package.json index 15b610e1..e7615ff0 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -4,6 +4,7 @@ "description": "scanservjs is a simple web-based UI for SANE which allows you to share a scanner on a network without the need for drivers or complicated installation.", "author": "Sam Strachan", "scripts": { + "missing-translations": "node missing-translations.js", "serve": "nodemon --exec 'vue-cli-service serve'", "build": "vue-cli-service build", "lint": "vue-cli-service lint", diff --git a/packages/client/src/locales/test.json b/packages/client/src/locales/test.json index ebd9ada2..7fda4376 100644 --- a/packages/client/src/locales/test.json +++ b/packages/client/src/locales/test.json @@ -1,11 +1,13 @@ { "global": { - "application-name": "##APPNAME" + "application-name": "##APPNAME", + "no-data-text": "##NODATA" }, "about": { "main": "##ABOUT.MAIN", "issue": "##ABOUT.ISSUE", + "api": "##SWAGGER API:", "system-info": "###ABOUT.SYSTEM-INFO" }, @@ -45,8 +47,10 @@ "size": "##SIZE", "items-per-page": "##ITEMS-PER-PAGE", "items-per-page-all": "##ALL", + "message:action": "##RUN {0} ON {1}", "message:deleted": "##MESSAGE:DELETED {0}", "message:renamed": "##FILE-RENAMED", + "button:action-selected": "##RUN-ACTION", "button:delete-selected": "##DELETE-SELECTED", "dialog:rename": "##RENAME", "dialog:rename-cancel": "##CANCEL", diff --git a/packages/client/src/locales/zh.json b/packages/client/src/locales/zh.json index 5f917ef9..b7c88eff 100644 --- a/packages/client/src/locales/zh.json +++ b/packages/client/src/locales/zh.json @@ -51,9 +51,7 @@ "dialog:rename": "更改文件名称", "dialog:rename-cancel": "取消", "dialog:rename-save": "保存", - "actions": "操作", - "thumbnail-show": "Show thumbnails", - "thumbnail-size": "Thumbnail size" + "actions": "操作" }, "navigation": { @@ -167,7 +165,6 @@ "theme:dark": "深色", "color": "颜色", "color:description": "切换顶部应用栏的颜色。", - "show-files-after-scan:description": "Show files after scan completes", "devices": "设备和存储", "reset:description": "清空扫描仪列表并强制重新加载设备", "reset": "重置",