diff --git a/frontend/package.json b/frontend/package.json index 9fe5b314bf..fd2b8aa2cf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "watch-main": "shadow-cljs watch main", "watch-test": "clojure -M:dev:shadow-cljs watch test", "validate-translations": "node ./scripts/validate-translations.js", + "find-unused-translations": "node ./scripts/find-unused-translations.js", "test-e2e": "cypress run", "test-e2e-gui": "cypress open" }, diff --git a/frontend/scripts/find-unused-translations.js b/frontend/scripts/find-unused-translations.js new file mode 100644 index 0000000000..3369e4dce9 --- /dev/null +++ b/frontend/scripts/find-unused-translations.js @@ -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); + } + } +})() diff --git a/frontend/src/app/main/ui/dashboard/export.cljs b/frontend/src/app/main/ui/dashboard/export.cljs index 8b7a9d610c..509f6ae60e 100644 --- a/frontend/src/app/main/ui/dashboard/export.cljs +++ b/frontend/src/app/main/ui/dashboard/export.cljs @@ -122,6 +122,13 @@ (let [selected? (= @selected-option type)] [:div.export-option {:class (when selected? "selected")} [: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"))] [:p (tr (str "dashboard.export.options." (d/name type) ".message"))] [:input {:type "radio" diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs index 73ce49dbbe..f79e61a7f3 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs @@ -67,7 +67,16 @@ [:div.attributes-stroke-row [:div.attributes-label (tr "handoff.attributes.stroke.width")] [: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))] + ;; 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))] [:& copy-button {:data (copy-stroke-data shape)}]])])) diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs index d52c5b26d9..b536610deb 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs @@ -142,12 +142,21 @@ (when (:text-decoration style) [:div.attributes-unit-row [: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))] [:& copy-button {:data (copy-style-data style :text-decoration)}]]) (when (:text-transform style) [:div.attributes-unit-row [: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))] [:& copy-button {:data (copy-style-data style :text-transform)}]])])) diff --git a/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs b/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs index a6e5cb4516..d40599c1cd 100644 --- a/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs @@ -45,6 +45,18 @@ [:* [:span.tool-window-bar-icon [:& 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))]])] [:div.tool-window-content [:& tab-container {:on-change-tab #(do diff --git a/frontend/src/app/main/ui/workspace/sidebar/history.cljs b/frontend/src/app/main/ui/workspace/sidebar/history.cljs index a22274c5d8..b90834ed34 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/history.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/history.cljs @@ -103,6 +103,36 @@ [locale type multiple?] (let [arity (if multiple? "multiple" "single") 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)))) (defn entry->message [locale entry] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index 3445970a76..2ed93ed90b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -77,6 +77,11 @@ :top (= :top dir) :bottom (= :bottom 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))) :on-click handle-on-click} i/auto-direction])) @@ -220,7 +225,7 @@ set-gap (fn [gap] (st/emit! (dwsl/update-layout ids {:layout-gap gap}))) - + change-padding-style (fn [type] (st/emit! (dwsl/update-layout ids {:layout-padding-type type}))) @@ -267,6 +272,13 @@ orientation (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))) ", " (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ") diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs index ce98a6761b..738ddb1dc1 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -170,6 +170,15 @@ (for [item [:layout-max-h :layout-min-h :layout-max-w :layout-min-w]] [:div.input-element {: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))) :title (tr (dm/str "workspace.options.layout-item." (d/name item))) :class (dom/classnames "maxH" (= item :layout-max-h) diff --git a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs index 5247f6696e..5492871b1f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs @@ -49,10 +49,142 @@ (defn translation-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 - :sc "shortcuts." - :sec "shortcut-section." - :sub-sec "shortcut-subsection.")] + :sc "shortcuts." + :sec "shortcut-section." + :sub-sec "shortcut-subsection.")] (tr (str translat-pre (d/name keyname))))) (defn add-translation @@ -60,7 +192,7 @@ (map (fn [[k v]] [k (assoc v :translation (translation-keyname type k))]) item)) (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" [shortcuts] (let [subsections (into #{} (mapcat :subsections) (vals shortcuts))