0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-09 00:10:11 -05:00
penpot/frontend/locales.clj
2021-02-22 14:47:23 +01:00

145 lines
4.9 KiB
Clojure

(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*)