mirror of
https://github.com/penpot/penpot.git
synced 2025-06-04 05:21:37 +02:00
🎉 New script to find unused translations
This commit is contained in:
parent
39c601a51f
commit
47be9a21f4
10 changed files with 316 additions and 5 deletions
|
@ -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"
|
||||
},
|
||||
|
|
90
frontend/scripts/find-unused-translations.js
Normal file
90
frontend/scripts/find-unused-translations.js
Normal 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);
|
||||
}
|
||||
}
|
||||
})()
|
|
@ -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"
|
||||
|
|
|
@ -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)}]])]))
|
||||
|
||||
|
|
|
@ -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)}]])]))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]))
|
||||
|
@ -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))) ", ")
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue