🎉 New script to find unused translations

This commit is contained in:
Pablo Alba 2022-10-18 22:46:41 +02:00
parent 39c601a51f
commit 47be9a21f4
10 changed files with 316 additions and 5 deletions

View file

@ -21,6 +21,7 @@
"watch-main": "shadow-cljs watch main", "watch-main": "shadow-cljs watch main",
"watch-test": "clojure -M:dev:shadow-cljs watch test", "watch-test": "clojure -M:dev:shadow-cljs watch test",
"validate-translations": "node ./scripts/validate-translations.js", "validate-translations": "node ./scripts/validate-translations.js",
"find-unused-translations": "node ./scripts/find-unused-translations.js",
"test-e2e": "cypress run", "test-e2e": "cypress run",
"test-e2e-gui": "cypress open" "test-e2e-gui": "cypress open"
}, },

View file

@ -0,0 +1,90 @@
const fs = require('fs').promises;
const gt = require("gettext-parser");
const path = require('path');
const util = require('node:util');
const execFile = util.promisify(require('node:child_process').execFile);
async function processMsgId(msgId){
return execFile('grep', ['-r', '-o', msgId, './src'])
.catch(()=> { return msgId})
}
async function processFile(f) {
const content = await fs.readFile(f);
const data = gt.po.parse(content, "utf-8")
const translations = data.translations[''];
const badIds = [];
for (const property in translations) {
const data = await processMsgId(translations[property].msgid);
if (data!=null && data.stdout === undefined){
badIds.push(data)
}
}
return badIds;
}
async function cleanFile(f, badIds) {
console.log ("\n\nDoing automatic cleanup")
const content = await fs.readFile(f);
const data = gt.po.parse(content, "utf-8");
const translations = data.translations[''];
const keys = Object.keys(translations);
for (const key of keys) {
property = translations[key];
if (badIds.includes(property.msgid)){
console.log ('----> deleting', property.msgid)
delete data.translations[''][key];
}
}
const buff = gt.po.compile(data, {sort: true});
await fs.writeFile(f, buff);
}
async function findExecutionTimeTranslations() {
const { stdout } = await execFile('grep', ['-r', '-h', '-F', '(tr (', './src']);
console.log(stdout);
}
async function welcome() {
console.log ('####################################################################')
console.log ('# UNUSED TRANSLATIONS FINDER #')
console.log ('####################################################################')
console.log ('\n');
console.log ('DISCLAIMER: Some translations are only available at execution time.')
console.log (' This finder can\'t process them, so there can be')
console.log (' false positives.\n')
console.log (' If you want to do an automatic clean anyway,')
console.log (' call the script with:')
console.log (' npm run find-unused-translations -- --clean')
console.log (' For example:');
console.log ('--------------------------------------------------------------------');
await findExecutionTimeTranslations();
console.log ('--------------------------------------------------------------------');
}
const doCleanup = process.argv.slice(2)[0] == "--clean";
;(async () => {
await welcome();
const target = path.normalize("./translations/en.po");
const badIds = await processFile(target);
if (doCleanup){
cleanFile(target, badIds);
} else {
for (const badId of badIds){
console.log(badId);
}
}
})()

View file

@ -122,6 +122,13 @@
(let [selected? (= @selected-option type)] (let [selected? (= @selected-option type)]
[:div.export-option {:class (when selected? "selected")} [:div.export-option {:class (when selected? "selected")}
[:label.option-container [:label.option-container
;; Execution time translation strings:
;; dashboard.export.options.all.message
;; dashboard.export.options.all.title
;; dashboard.export.options.detach.message
;; dashboard.export.options.detach.title
;; dashboard.export.options.merge.message
;; dashboard.export.options.merge.title
[:h3 (tr (str "dashboard.export.options." (d/name type) ".title"))] [:h3 (tr (str "dashboard.export.options." (d/name type) ".title"))]
[:p (tr (str "dashboard.export.options." (d/name type) ".message"))] [:p (tr (str "dashboard.export.options." (d/name type) ".message"))]
[:input {:type "radio" [:input {:type "radio"

View file

@ -67,7 +67,16 @@
[:div.attributes-stroke-row [:div.attributes-stroke-row
[:div.attributes-label (tr "handoff.attributes.stroke.width")] [:div.attributes-label (tr "handoff.attributes.stroke.width")]
[:div.attributes-value (:stroke-width shape) "px"] [:div.attributes-value (:stroke-width shape) "px"]
;; Execution time translation strings:
;; handoff.attributes.stroke.style.dotted
;; handoff.attributes.stroke.style.mixed
;; handoff.attributes.stroke.style.none
;; handoff.attributes.stroke.style.solid
[:div.attributes-value (->> stroke-style d/name (str "handoff.attributes.stroke.style.") (tr))] [:div.attributes-value (->> stroke-style d/name (str "handoff.attributes.stroke.style.") (tr))]
;; Execution time translation strings:
;; handoff.attributes.stroke.alignment.center
;; handoff.attributes.stroke.alignment.inner
;; handoff.attributes.stroke.alignment.outer
[:div.attributes-label (->> stroke-alignment d/name (str "handoff.attributes.stroke.alignment.") (tr))] [:div.attributes-label (->> stroke-alignment d/name (str "handoff.attributes.stroke.alignment.") (tr))]
[:& copy-button {:data (copy-stroke-data shape)}]])])) [:& copy-button {:data (copy-stroke-data shape)}]])]))

View file

@ -142,12 +142,21 @@
(when (:text-decoration style) (when (:text-decoration style)
[:div.attributes-unit-row [:div.attributes-unit-row
[:div.attributes-label (tr "handoff.attributes.typography.text-decoration")] [:div.attributes-label (tr "handoff.attributes.typography.text-decoration")]
;; Execution time translation strings:
;; handoff.attributes.typography.text-decoration.none
;; handoff.attributes.typography.text-decoration.strikethrough
;; handoff.attributes.typography.text-decoration.underline
[:div.attributes-value (->> style :text-decoration (str "handoff.attributes.typography.text-decoration.") (tr))] [:div.attributes-value (->> style :text-decoration (str "handoff.attributes.typography.text-decoration.") (tr))]
[:& copy-button {:data (copy-style-data style :text-decoration)}]]) [:& copy-button {:data (copy-style-data style :text-decoration)}]])
(when (:text-transform style) (when (:text-transform style)
[:div.attributes-unit-row [:div.attributes-unit-row
[:div.attributes-label (tr "handoff.attributes.typography.text-transform")] [:div.attributes-label (tr "handoff.attributes.typography.text-transform")]
;; Execution time translation strings:
;; handoff.attributes.typography.text-transform.lowercase
;; handoff.attributes.typography.text-transform.none
;; handoff.attributes.typography.text-transform.titlecase
;; handoff.attributes.typography.text-transform.uppercase
[:div.attributes-value (->> style :text-transform (str "handoff.attributes.typography.text-transform.") (tr))] [:div.attributes-value (->> style :text-transform (str "handoff.attributes.typography.text-transform.") (tr))]
[:& copy-button {:data (copy-style-data style :text-transform)}]])])) [:& copy-button {:data (copy-style-data style :text-transform)}]])]))

View file

@ -45,6 +45,18 @@
[:* [:*
[:span.tool-window-bar-icon [:span.tool-window-bar-icon
[:& si/element-icon {:shape first-shape}]] [:& si/element-icon {:shape first-shape}]]
;; Execution time translation strings:
;; handoff.tabs.code.selected.circle
;; handoff.tabs.code.selected.component
;; handoff.tabs.code.selected.curve
;; handoff.tabs.code.selected.frame
;; handoff.tabs.code.selected.group
;; handoff.tabs.code.selected.image
;; handoff.tabs.code.selected.mask
;; handoff.tabs.code.selected.path
;; handoff.tabs.code.selected.rect
;; handoff.tabs.code.selected.svg-raw
;; handoff.tabs.code.selected.text
[:span.tool-window-bar-title (->> selected-type d/name (str "handoff.tabs.code.selected.") (tr))]])] [:span.tool-window-bar-title (->> selected-type d/name (str "handoff.tabs.code.selected.") (tr))]])]
[:div.tool-window-content [:div.tool-window-content
[:& tab-container {:on-change-tab #(do [:& tab-container {:on-change-tab #(do

View file

@ -103,6 +103,36 @@
[locale type multiple?] [locale type multiple?]
(let [arity (if multiple? "multiple" "single") (let [arity (if multiple? "multiple" "single")
attribute (name (or type :multiple))] attribute (name (or type :multiple))]
;; Execution time translation strings:
;; workspace.undo.entry.multiple.circle
;; workspace.undo.entry.multiple.color
;; workspace.undo.entry.multiple.component
;; workspace.undo.entry.multiple.curve
;; workspace.undo.entry.multiple.frame
;; workspace.undo.entry.multiple.group
;; workspace.undo.entry.multiple.media
;; workspace.undo.entry.multiple.multiple
;; workspace.undo.entry.multiple.page
;; workspace.undo.entry.multiple.path
;; workspace.undo.entry.multiple.rect
;; workspace.undo.entry.multiple.shape
;; workspace.undo.entry.multiple.text
;; workspace.undo.entry.multiple.typography
;; workspace.undo.entry.single.circle
;; workspace.undo.entry.single.color
;; workspace.undo.entry.single.component
;; workspace.undo.entry.single.curve
;; workspace.undo.entry.single.frame
;; workspace.undo.entry.single.group
;; workspace.undo.entry.single.image
;; workspace.undo.entry.single.media
;; workspace.undo.entry.single.multiple
;; workspace.undo.entry.single.page
;; workspace.undo.entry.single.path
;; workspace.undo.entry.single.rect
;; workspace.undo.entry.single.shape
;; workspace.undo.entry.single.text
;; workspace.undo.entry.single.typography
(t locale (str/format "workspace.undo.entry.%s.%s" arity attribute)))) (t locale (str/format "workspace.undo.entry.%s.%s" arity attribute))))
(defn entry->message [locale entry] (defn entry->message [locale entry]

View file

@ -77,6 +77,11 @@
:top (= :top dir) :top (= :top dir)
:bottom (= :bottom dir)) :bottom (= :bottom dir))
:key (dm/str "direction-" dir) :key (dm/str "direction-" dir)
;; Execution time translation strings:
;; workspace.options.layout.direction.bottom
;; workspace.options.layout.direction.left
;; workspace.options.layout.direction.right
;; workspace.options.layout.direction.top
:alt (tr (dm/str "workspace.options.layout.direction." (d/name dir))) :alt (tr (dm/str "workspace.options.layout.direction." (d/name dir)))
:on-click handle-on-click} :on-click handle-on-click}
i/auto-direction])) i/auto-direction]))
@ -220,7 +225,7 @@
set-gap set-gap
(fn [gap] (fn [gap]
(st/emit! (dwsl/update-layout ids {:layout-gap gap}))) (st/emit! (dwsl/update-layout ids {:layout-gap gap})))
change-padding-style change-padding-style
(fn [type] (fn [type]
(st/emit! (dwsl/update-layout ids {:layout-padding-type type}))) (st/emit! (dwsl/update-layout ids {:layout-padding-type type})))
@ -267,6 +272,13 @@
orientation orientation
(if (= type :packed) (if (= type :packed)
;; Execution time translation strings:
;; workspace.options.layout.h.center
;; workspace.options.layout.h.left
;; workspace.options.layout.h.right
;; workspace.options.layout.v.bottom
;; workspace.options.layout.v.center
;; workspace.options.layout.v.top
(dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", " (dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", "
(tr (dm/str "workspace.options.layout.h." (d/name h))) ", ") (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ")

View file

@ -170,6 +170,15 @@
(for [item [:layout-max-h :layout-min-h :layout-max-w :layout-min-w]] (for [item [:layout-max-h :layout-min-h :layout-max-w :layout-min-w]]
[:div.input-element [:div.input-element
{:key (d/name item) {:key (d/name item)
;; Execution time translation strings:
;; workspace.options.layout-item.layout-max-h
;; workspace.options.layout-item.layout-max-w
;; workspace.options.layout-item.layout-min-h
;; workspace.options.layout-item.layout-min-w
;; workspace.options.layout-item.title.layout-max-h
;; workspace.options.layout-item.title.layout-max-w
;; workspace.options.layout-item.title.layout-min-h
;; workspace.options.layout-item.title.layout-min-w
:alt (tr (dm/str "workspace.options.layout-item." (d/name item))) :alt (tr (dm/str "workspace.options.layout-item." (d/name item)))
:title (tr (dm/str "workspace.options.layout-item." (d/name item))) :title (tr (dm/str "workspace.options.layout-item." (d/name item)))
:class (dom/classnames "maxH" (= item :layout-max-h) :class (dom/classnames "maxH" (= item :layout-max-h)

View file

@ -49,10 +49,142 @@
(defn translation-keyname (defn translation-keyname
[type keyname] [type keyname]
;; Execution time translation strings:
;; shortcut-subsection.alignment
;; shortcut-subsection.edit
;; shortcut-subsection.general-dashboard
;; shortcut-subsection.general-viewer
;; shortcut-subsection.main-menu
;; shortcut-subsection.modify-layers
;; shortcut-subsection.navigation-dashboard
;; shortcut-subsection.navigation-viewer
;; shortcut-subsection.navigation-workspace
;; shortcut-subsection.panels
;; shortcut-subsection.path-editor
;; shortcut-subsection.shape
;; shortcut-subsection.tools
;; shortcut-subsection.zoom-viewer
;; shortcut-subsection.zoom-workspace
;; shortcuts.add-comment
;; shortcuts.add-node
;; shortcuts.align-bottom
;; shortcuts.align-hcenter
;; shortcuts.align-left
;; shortcuts.align-right
;; shortcuts.align-top
;; shortcuts.align-vcenter
;; shortcuts.artboard-selection
;; shortcuts.bool-difference
;; shortcuts.bool-exclude
;; shortcuts.bool-intersection
;; shortcuts.bool-union
;; shortcuts.bring-back
;; shortcuts.bring-backward
;; shortcuts.bring-forward
;; shortcuts.bring-front
;; shortcuts.clear-undo
;; shortcuts.copy
;; shortcuts.create-component
;; shortcuts.create-new-project
;; shortcuts.cut
;; shortcuts.decrease-zoom
;; shortcuts.delete
;; shortcuts.delete-node
;; shortcuts.detach-component
;; shortcuts.draw-curve
;; shortcuts.draw-ellipse
;; shortcuts.draw-frame
;; shortcuts.draw-nodes
;; shortcuts.draw-path
;; shortcuts.draw-rect
;; shortcuts.draw-text
;; shortcuts.duplicate
;; shortcuts.escape
;; shortcuts.export-shapes
;; shortcuts.fit-all
;; shortcuts.flip-horizontal
;; shortcuts.flip-vertical
;; shortcuts.go-to-drafts
;; shortcuts.go-to-libs
;; shortcuts.go-to-search
;; shortcuts.group
;; shortcuts.h-distribute
;; shortcuts.hide-ui
;; shortcuts.increase-zoom
;; shortcuts.insert-image
;; shortcuts.join-nodes
;; shortcuts.make-corner
;; shortcuts.make-curve
;; shortcuts.mask
;; shortcuts.merge-nodes
;; shortcuts.move
;; shortcuts.move-fast-down
;; shortcuts.move-fast-left
;; shortcuts.move-fast-right
;; shortcuts.move-fast-up
;; shortcuts.move-nodes
;; shortcuts.move-unit-down
;; shortcuts.move-unit-left
;; shortcuts.move-unit-right
;; shortcuts.move-unit-up
;; shortcuts.next-frame
;; shortcuts.opacity-0
;; shortcuts.opacity-1
;; shortcuts.opacity-2
;; shortcuts.opacity-3
;; shortcuts.opacity-4
;; shortcuts.opacity-5
;; shortcuts.opacity-6
;; shortcuts.opacity-7
;; shortcuts.opacity-8
;; shortcuts.opacity-9
;; shortcuts.open-color-picker
;; shortcuts.open-comments
;; shortcuts.open-dashboard
;; shortcuts.open-handoff
;; shortcuts.open-interactions
;; shortcuts.open-viewer
;; shortcuts.open-workspace
;; shortcuts.paste
;; shortcuts.prev-frame
;; shortcuts.redo
;; shortcuts.reset-zoom
;; shortcuts.select-all
;; shortcuts.separate-nodes
;; shortcuts.show-pixel-grid
;; shortcuts.show-shortcuts
;; shortcuts.snap-nodes
;; shortcuts.snap-pixel-grid
;; shortcuts.start-editing
;; shortcuts.start-measure
;; shortcuts.stop-measure
;; shortcuts.thumbnail-set
;; shortcuts.toggle-alignment
;; shortcuts.toggle-assets
;; shortcuts.toggle-colorpalette
;; shortcuts.toggle-focus-mode
;; shortcuts.toggle-grid
;; shortcuts.toggle-history
;; shortcuts.toggle-layers
;; shortcuts.toggle-lock
;; shortcuts.toggle-lock-size
;; shortcuts.toggle-rules
;; shortcuts.toggle-scale-text
;; shortcuts.toggle-snap-grid
;; shortcuts.toggle-snap-guide
;; shortcuts.toggle-textpalette
;; shortcuts.toggle-visibility
;; shortcuts.toggle-zoom-style
;; shortcuts.toogle-fullscreen
;; shortcuts.undo
;; shortcuts.ungroup
;; shortcuts.unmask
;; shortcuts.v-distribute
;; shortcuts.zoom-selected
(let [translat-pre (case type (let [translat-pre (case type
:sc "shortcuts." :sc "shortcuts."
:sec "shortcut-section." :sec "shortcut-section."
:sub-sec "shortcut-subsection.")] :sub-sec "shortcut-subsection.")]
(tr (str translat-pre (d/name keyname))))) (tr (str translat-pre (d/name keyname)))))
(defn add-translation (defn add-translation
@ -60,7 +192,7 @@
(map (fn [[k v]] [k (assoc v :translation (translation-keyname type k))]) item)) (map (fn [[k v]] [k (assoc v :translation (translation-keyname type k))]) item))
(defn shortcuts->subsections (defn shortcuts->subsections
"A function to obtain the list of subsections and their "A function to obtain the list of subsections and their
associated shortcus from the general map of shortcuts" associated shortcus from the general map of shortcuts"
[shortcuts] [shortcuts]
(let [subsections (into #{} (mapcat :subsections) (vals shortcuts)) (let [subsections (into #{} (mapcat :subsections) (vals shortcuts))