mirror of
https://github.com/penpot/penpot.git
synced 2025-01-08 16:00:19 -05:00
📎 Minor improvements to translations scripts.
This commit is contained in:
parent
5a49ce2028
commit
bb719d6211
5 changed files with 52 additions and 170 deletions
|
@ -1,151 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(require '[clojure.pprint :as pp :refer [pprint]])
|
||||
|
||||
(require '[clojure.edn :as edn]
|
||||
'[clojure.set :as set]
|
||||
'[clojure.java.io :as io])
|
||||
|
||||
(require '[datoteka.core :as fs]
|
||||
'[jsonista.core :as json]
|
||||
'[parcera.core :as pa])
|
||||
|
||||
(import 'java.nio.file.Paths
|
||||
'java.nio.file.Path
|
||||
'java.nio.file.Files
|
||||
'java.nio.file.SimpleFileVisitor
|
||||
'java.nio.file.FileVisitResult
|
||||
'com.fasterxml.jackson.databind.ObjectMapper
|
||||
'com.fasterxml.jackson.databind.SerializationFeature)
|
||||
|
||||
(defmulti task first)
|
||||
|
||||
(defn- find-translations-in-form
|
||||
[form]
|
||||
(reduce (fn [messages node]
|
||||
(let [found (->> node
|
||||
(filter #(and (seq? %) (= :string (first %))))
|
||||
(map (fn [item]
|
||||
(let [mdata (meta item)]
|
||||
{:code (edn/read-string (second item))
|
||||
:line (get-in mdata [::pa/start :row])}))))]
|
||||
(into messages found)))
|
||||
[]
|
||||
(->> (tree-seq seq? seq form)
|
||||
(filter #(and (seq? %)
|
||||
(seq? (second %))
|
||||
(= :list (first %))
|
||||
(= :symbol (first (second %)))
|
||||
(or (= "t" (second (second %)))
|
||||
(= "tr" (second (second %)))))))))
|
||||
|
||||
(defn- find-translations
|
||||
[path]
|
||||
(let [forms (pa/ast (slurp path))
|
||||
spath (str path)]
|
||||
(->> forms
|
||||
(filter #(and (seq? %) (= :list (first %))))
|
||||
(reduce (fn [messages form]
|
||||
(->> (find-translations-in-form form)
|
||||
(map #(assoc % :file spath))
|
||||
(into messages))) []))))
|
||||
|
||||
(defn- collect-translations
|
||||
[path]
|
||||
(let [messages (atom [])]
|
||||
(->> (proxy [SimpleFileVisitor] []
|
||||
(visitFile [path attrs]
|
||||
(when (= (fs/ext path) "cljs")
|
||||
(swap! messages into (find-translations path)))
|
||||
FileVisitResult/CONTINUE)
|
||||
(postVisitDirectory [dir exc]
|
||||
FileVisitResult/CONTINUE))
|
||||
(Files/walkFileTree (fs/path path)))
|
||||
@messages))
|
||||
|
||||
(defn- read-json-file
|
||||
[path]
|
||||
(when (fs/regular-file? path)
|
||||
(let [content (json/read-value (io/as-file path))]
|
||||
(reduce-kv (fn [res k v]
|
||||
(let [v (into (sorted-map) v)
|
||||
v (update v "translations" #(into (sorted-map) %))]
|
||||
(assoc res k v)))
|
||||
(sorted-map)
|
||||
content))))
|
||||
|
||||
(defn- add-translation
|
||||
[data {:keys [code file line] :as translation}]
|
||||
(let [rpath (str file)]
|
||||
(if (contains? data code)
|
||||
(update data code (fn [state]
|
||||
(if (get state "permanent")
|
||||
state
|
||||
(-> state
|
||||
(dissoc "unused")
|
||||
(update "used-in" conj rpath)))))
|
||||
(assoc data code {"translations" (sorted-map "en" nil "es" nil)
|
||||
"used-in" [rpath]}))))
|
||||
|
||||
(defn- clean-removed-translations
|
||||
[data imported]
|
||||
(let [existing (into #{} (keys data))
|
||||
toremove (set/difference existing imported)]
|
||||
(reduce (fn [data code]
|
||||
(if (get-in data [code "permanent"])
|
||||
data
|
||||
(-> data
|
||||
(update code dissoc "used-in")
|
||||
(update code assoc "unused" true))))
|
||||
data
|
||||
toremove)))
|
||||
|
||||
(defn- initial-cleanup
|
||||
[data]
|
||||
(reduce-kv (fn [data k v]
|
||||
(if (string? v)
|
||||
(assoc data k {"used-in" []
|
||||
"translations" {:en v}})
|
||||
(update data k assoc "used-in" [])))
|
||||
data
|
||||
data))
|
||||
|
||||
(defn- synchronize-translations
|
||||
[data translations]
|
||||
(loop [data (initial-cleanup data)
|
||||
imported #{}
|
||||
c (first translations)
|
||||
r (rest translations)]
|
||||
(if (nil? c)
|
||||
(clean-removed-translations data imported)
|
||||
(recur (add-translation data c)
|
||||
(conj imported (:code c))
|
||||
(first r)
|
||||
(rest r)))))
|
||||
|
||||
(defn- write-result!
|
||||
[data output-path]
|
||||
(binding [*out* (io/writer (fs/path output-path))]
|
||||
(let [mapper (doto (ObjectMapper.)
|
||||
(.enable SerializationFeature/ORDER_MAP_ENTRIES_BY_KEYS))
|
||||
mapper (json/object-mapper {:pretty true :mapper mapper})]
|
||||
(println (json/write-value-as-string data mapper))
|
||||
(flush))))
|
||||
|
||||
(defn- update-translations
|
||||
[{:keys [find-directory output-path] :as props}]
|
||||
(let [data (read-json-file output-path)
|
||||
translations (collect-translations find-directory)
|
||||
data (synchronize-translations data translations)]
|
||||
(write-result! data output-path)))
|
||||
|
||||
(defmethod task "collect"
|
||||
[[_ in-path out-path]]
|
||||
(update-translations {:find-directory in-path
|
||||
:output-path out-path}))
|
||||
|
||||
(task *command-line-args*)
|
|
@ -12,7 +12,7 @@
|
|||
"defaults"
|
||||
],
|
||||
"scripts": {
|
||||
"collect-locales": "clojure -M:dev locales.clj collect src/app/main/ resources/locales.json"
|
||||
"validate-translations": "node ./scripts/validate-translations.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.2.4",
|
||||
|
|
31
frontend/scripts/validate-translations.js
Normal file
31
frontend/scripts/validate-translations.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
const fs = require('fs').promises;
|
||||
const gt = require("gettext-parser");
|
||||
const l = require("lodash");
|
||||
const path = require('path');
|
||||
|
||||
async function* getFiles(dir) {
|
||||
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
||||
for (const dirent of dirents) {
|
||||
const res = path.resolve(dir, dirent.name);
|
||||
if (dirent.isDirectory()) {
|
||||
yield* getFiles(res);
|
||||
} else {
|
||||
yield res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const fileRe = /.+\.po$/;
|
||||
const target = path.normalize("./translations/");
|
||||
const parent = path.join(target, "..");
|
||||
for await (const f of getFiles(target)) {
|
||||
if (!fileRe.test(f)) continue;
|
||||
const entry = path.relative(parent, f);
|
||||
console.log(`=> processing: ${entry}`);
|
||||
const content = await fs.readFile(f);
|
||||
const data = gt.po.parse(content, "utf-8")
|
||||
const buff = gt.po.compile(data, {sort: true});
|
||||
await fs.writeFile(f, buff);
|
||||
}
|
||||
})()
|
|
@ -1423,6 +1423,14 @@ msgstr "Colors"
|
|||
msgid "workspace.assets.components"
|
||||
msgstr "Components"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.create-group"
|
||||
msgstr "Create a group"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.create-group-hint"
|
||||
msgstr "Your items are going to be named automatically as \"group name / item name\""
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.delete"
|
||||
msgstr "Delete"
|
||||
|
@ -1447,18 +1455,10 @@ msgstr "Graphics"
|
|||
msgid "workspace.assets.group"
|
||||
msgstr "Group"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.create-group"
|
||||
msgstr "Create a group"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.group-name"
|
||||
msgstr "Group name"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.create-group-hint"
|
||||
msgstr "Your items are going to be named automatically as \"group name / item name\""
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.libraries"
|
||||
msgstr "Libraries"
|
||||
|
@ -2457,4 +2457,4 @@ msgid "workspace.updates.update"
|
|||
msgstr "Update"
|
||||
|
||||
msgid "workspace.viewport.click-to-close-path"
|
||||
msgstr "Click to close the path"
|
||||
msgstr "Click to close the path"
|
|
@ -1403,6 +1403,16 @@ msgstr "Colores"
|
|||
msgid "workspace.assets.components"
|
||||
msgstr "Componentes"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.create-group"
|
||||
msgstr "Crear un grupo"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.create-group-hint"
|
||||
msgstr ""
|
||||
"Tus elementos se renombrarán automáticamente a \"nombre grupo / nombre "
|
||||
"elemento\""
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.delete"
|
||||
msgstr "Borrar"
|
||||
|
@ -1427,18 +1437,10 @@ msgstr "Gráficos"
|
|||
msgid "workspace.assets.group"
|
||||
msgstr "Agrupar"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.create-group"
|
||||
msgstr "Crear un grupo"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.group-name"
|
||||
msgstr "Nombre del grupo"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.create-group-hint"
|
||||
msgstr "Tus elementos se renombrarán automáticamente a \"nombre grupo / nombre elemento\""
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets.cljs
|
||||
msgid "workspace.assets.libraries"
|
||||
msgstr "Bibliotecas"
|
||||
|
@ -2439,4 +2441,4 @@ msgid "workspace.updates.update"
|
|||
msgstr "Actualizar"
|
||||
|
||||
msgid "workspace.viewport.click-to-close-path"
|
||||
msgstr "Pulsar para cerrar la ruta"
|
||||
msgstr "Pulsar para cerrar la ruta"
|
Loading…
Reference in a new issue