mirror of
https://github.com/penpot/penpot.git
synced 2025-07-06 01:47:15 +02:00
♻️ Refactor all i18n subsystem.
This commit is contained in:
parent
b13488404e
commit
f2d475d3d3
18 changed files with 1829 additions and 228 deletions
|
@ -10,8 +10,6 @@
|
|||
[rumext.alpha :as mf]
|
||||
[uxbox.main.data.auth :refer [logout]]
|
||||
[uxbox.main.data.users :as udu]
|
||||
[uxbox.main.locales.en :as en]
|
||||
[uxbox.main.locales.fr :as fr]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui :as ui]
|
||||
[uxbox.main.ui.lightbox :refer [lightbox]]
|
||||
|
@ -19,7 +17,7 @@
|
|||
[uxbox.main.ui.loader :refer [loader]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.html.history :as html-history]
|
||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
[uxbox.util.i18n :as i18n]
|
||||
[uxbox.util.messages :as uum]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.util.storage :refer [storage]]
|
||||
|
@ -30,15 +28,15 @@
|
|||
(declare reinit)
|
||||
(s/check-asserts true)
|
||||
|
||||
(i18n/update-locales! (fn [locales]
|
||||
(-> locales
|
||||
(assoc "en" en/locales)
|
||||
(assoc "fr" fr/locales))))
|
||||
;; (i18n/update-locales! (fn [locales]
|
||||
;; (-> locales
|
||||
;; (assoc "en" en/locales)
|
||||
;; (assoc "fr" fr/locales))))
|
||||
|
||||
(i18n/on-locale-change!
|
||||
(fn [new old]
|
||||
(println "Locale changed from" old " to " new)
|
||||
(reinit)))
|
||||
;; (i18n/on-locale-change!
|
||||
;; (fn [new old]
|
||||
;; (println "Locale changed from" old " to " new)
|
||||
;; (reinit)))
|
||||
|
||||
;; --- Error Handling
|
||||
|
||||
|
@ -81,7 +79,8 @@
|
|||
(def app-sym (.for js/Symbol "uxbox.app"))
|
||||
|
||||
(defn ^:export init
|
||||
[]
|
||||
[translations]
|
||||
(i18n/init! (js/JSON.parse translations))
|
||||
(unchecked-set js/window app-sym "main")
|
||||
(st/init)
|
||||
(init-ui))
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
[{:keys [on-submit value] :as props}]
|
||||
(let [local (mf/use-var value)]
|
||||
[:div.lightbox-body
|
||||
[:h3 (tr "ds.color-lightbox.title")]
|
||||
[:h3 (tr "ds.color-lightbox.title" )]
|
||||
[:form
|
||||
[:div.row-flex.center
|
||||
[:& colorpicker {:value (or @local "#00ccff")
|
||||
|
|
|
@ -190,75 +190,75 @@
|
|||
[:li {:key (pr-str id)}
|
||||
[:a {:on-click #(on-select % id)} name]])])))
|
||||
|
||||
(mf/def grid-options
|
||||
:mixins [(mf/local) mf/memo]
|
||||
;; (mf/def grid-options
|
||||
;; :mixins [(mf/local) mf/memo]
|
||||
|
||||
:render
|
||||
(fn [{:keys [::mf/local] :as own}
|
||||
{:keys [id type selected] :as props}]
|
||||
(letfn [(delete []
|
||||
(st/emit! (di/delete-selected)))
|
||||
(on-delete [event]
|
||||
(modal/show! confirm-dialog {:on-accept delete}))
|
||||
(on-toggle-copy [event]
|
||||
(swap! local update :show-copy-tooltip not))
|
||||
(on-toggle-move [event]
|
||||
(swap! local update :show-move-tooltip not))
|
||||
(on-copy [selected]
|
||||
(swap! local assoc
|
||||
:show-move-tooltip false
|
||||
:show-copy-tooltip false)
|
||||
(st/emit! (di/copy-selected selected)))
|
||||
(on-move [selected]
|
||||
(swap! local assoc
|
||||
:show-move-tooltip false
|
||||
:show-copy-tooltip false)
|
||||
(st/emit! (di/move-selected selected)))
|
||||
(on-rename [event]
|
||||
(let [selected (first selected)]
|
||||
(st/emit! (di/update-opts :edition selected))))]
|
||||
;; :render
|
||||
;; (fn [{:keys [::mf/local] :as own}
|
||||
;; {:keys [id type selected] :as props}]
|
||||
;; (letfn [(delete []
|
||||
;; (st/emit! (di/delete-selected)))
|
||||
;; (on-delete [event]
|
||||
;; (modal/show! confirm-dialog {:on-accept delete}))
|
||||
;; (on-toggle-copy [event]
|
||||
;; (swap! local update :show-copy-tooltip not))
|
||||
;; (on-toggle-move [event]
|
||||
;; (swap! local update :show-move-tooltip not))
|
||||
;; (on-copy [selected]
|
||||
;; (swap! local assoc
|
||||
;; :show-move-tooltip false
|
||||
;; :show-copy-tooltip false)
|
||||
;; (st/emit! (di/copy-selected selected)))
|
||||
;; (on-move [selected]
|
||||
;; (swap! local assoc
|
||||
;; :show-move-tooltip false
|
||||
;; :show-copy-tooltip false)
|
||||
;; (st/emit! (di/move-selected selected)))
|
||||
;; (on-rename [event]
|
||||
;; (let [selected (first selected)]
|
||||
;; (st/emit! (di/update-opts :edition selected))))]
|
||||
|
||||
;; MULTISELECT OPTIONS BAR
|
||||
[:div.multiselect-bar
|
||||
(if (or (= type :own) (nil? id))
|
||||
;; if editable
|
||||
[:div.multiselect-nav {}
|
||||
[:span.move-item.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.copy")
|
||||
:on-click on-toggle-copy}
|
||||
(when (:show-copy-tooltip @local)
|
||||
(grid-options-tooltip {:selected id
|
||||
:title (tr "ds.multiselect-bar.copy-to-library")
|
||||
:on-select on-copy}))
|
||||
i/copy]
|
||||
[:span.move-item.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.move")
|
||||
:on-click on-toggle-move}
|
||||
(when (:show-move-tooltip @local)
|
||||
(grid-options-tooltip {:selected id
|
||||
:title (tr "ds.multiselect-bar.move-to-library")
|
||||
:on-select on-move}))
|
||||
i/move]
|
||||
(when (= 1 (count selected))
|
||||
[:span.move-item.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.rename")
|
||||
:on-click on-rename}
|
||||
i/pencil])
|
||||
[:span.delete.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.delete")
|
||||
:on-click on-delete}
|
||||
i/trash]]
|
||||
;; ;; MULTISELECT OPTIONS BAR
|
||||
;; [:div.multiselect-bar
|
||||
;; (if (or (= type :own) (nil? id))
|
||||
;; ;; if editable
|
||||
;; [:div.multiselect-nav {}
|
||||
;; [:span.move-item.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.copy")
|
||||
;; :on-click on-toggle-copy}
|
||||
;; (when (:show-copy-tooltip @local)
|
||||
;; (grid-options-tooltip {:selected id
|
||||
;; :title (tr "ds.multiselect-bar.copy-to-library")
|
||||
;; :on-select on-copy}))
|
||||
;; i/copy]
|
||||
;; [:span.move-item.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.move")
|
||||
;; :on-click on-toggle-move}
|
||||
;; (when (:show-move-tooltip @local)
|
||||
;; (grid-options-tooltip {:selected id
|
||||
;; :title (tr "ds.multiselect-bar.move-to-library")
|
||||
;; :on-select on-move}))
|
||||
;; i/move]
|
||||
;; (when (= 1 (count selected))
|
||||
;; [:span.move-item.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.rename")
|
||||
;; :on-click on-rename}
|
||||
;; i/pencil])
|
||||
;; [:span.delete.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.delete")
|
||||
;; :on-click on-delete}
|
||||
;; i/trash]]
|
||||
|
||||
;; if not editable
|
||||
[:div.multiselect-nav
|
||||
[:span.move-item.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.copy")
|
||||
:on-click on-toggle-copy}
|
||||
(when (:show-copy-tooltip @local)
|
||||
(grid-options-tooltip {:selected id
|
||||
:title (tr "ds.multiselect-bar.copy-to-library")
|
||||
:on-select on-copy}))
|
||||
i/organize]])])))
|
||||
;; ;; if not editable
|
||||
;; [:div.multiselect-nav
|
||||
;; [:span.move-item.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.copy")
|
||||
;; :on-click on-toggle-copy}
|
||||
;; (when (:show-copy-tooltip @local)
|
||||
;; (grid-options-tooltip {:selected id
|
||||
;; :title (tr "ds.multiselect-bar.copy-to-library")
|
||||
;; :on-select on-copy}))
|
||||
;; i/organize]])])))
|
||||
|
||||
;; --- Grid Item
|
||||
|
||||
|
@ -349,7 +349,7 @@
|
|||
:coll coll
|
||||
:opts opts}]
|
||||
(when (seq (:selected opts))
|
||||
[:& grid-options {:id id :type type :selected (:selected opts)}])]]))
|
||||
#_[:& grid-options {:id id :type type :selected (:selected opts)}])]]))
|
||||
|
||||
;; --- Icons Page
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.main.ui.keyboard :as kbd]
|
||||
[uxbox.main.ui.confirm :refer [confirm-dialog]]
|
||||
[uxbox.main.ui.dashboard.projects-forms :refer [create-project-dialog]]
|
||||
[uxbox.main.ui.dashboard.common :as common]
|
||||
[uxbox.util.data :refer [read-string]]
|
||||
[uxbox.util.dom :as dom]
|
||||
|
|
|
@ -1,72 +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) 2015-2019 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2019 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.dashboard.projects-forms
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.projects :as udp]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.forms :as fm]
|
||||
[uxbox.util.i18n :as t :refer [tr]]))
|
||||
|
||||
(s/def ::name ::fm/not-empty-string)
|
||||
(s/def ::width ::fm/number-str)
|
||||
(s/def ::height ::fm/number-str)
|
||||
|
||||
(s/def ::project-form
|
||||
(s/keys :req-un [::name]))
|
||||
|
||||
(def defaults {:name ""})
|
||||
|
||||
;; --- Create Project Form
|
||||
|
||||
(defn- on-submit
|
||||
[event form]
|
||||
(dom/prevent-default event)
|
||||
(let [data (:clean-data form)]
|
||||
(st/emit! (udp/create-project data))
|
||||
(modal/hide!)))
|
||||
|
||||
(defn- swap-size
|
||||
[event {:keys [data] :as form}]
|
||||
(swap! data assoc
|
||||
:width (:height data)
|
||||
:height (:width data)))
|
||||
|
||||
(mf/defc create-project-form
|
||||
[props]
|
||||
(let [{:keys [data] :as form} (fm/use-form ::project-form defaults)]
|
||||
[:form {:on-submit #(on-submit % form)}
|
||||
[:input.input-text
|
||||
{:placeholder (tr "ds.project.placeholder")
|
||||
:type "text"
|
||||
:name "name"
|
||||
:value (:name data)
|
||||
:class (fm/error-class form :name)
|
||||
:on-blur (fm/on-input-blur form :name)
|
||||
:on-change (fm/on-input-change form :name)
|
||||
:auto-focus true}]
|
||||
;; Submit
|
||||
[:input#project-btn.btn-primary
|
||||
{:value (tr "ds.go")
|
||||
:class (when-not (:valid form) "btn-disabled")
|
||||
:disabled (not (:valid form))
|
||||
:type "submit"}]]))
|
||||
|
||||
;; --- Create Project Lightbox
|
||||
|
||||
(mf/defc create-project-dialog
|
||||
[props]
|
||||
[:div.lightbox-body
|
||||
[:h3 (tr "ds.project.new")]
|
||||
[:& create-project-form]
|
||||
[:a.close {:on-click modal/hide!} i/close]])
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
[uxbox.main.ui.workspace.sortable :refer [use-sortable]]
|
||||
[uxbox.util.data :refer [classnames enumerate]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.i18n :refer [tr]]
|
||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
[uxbox.util.router :as rt]))
|
||||
|
||||
;; --- Page Item
|
||||
|
@ -128,7 +128,8 @@
|
|||
|
||||
(mf/defc sitemap-toolbox
|
||||
[{:keys [file page] :as props}]
|
||||
(let [on-create-click #(st/emit! dp/create-empty-page)]
|
||||
(let [on-create-click #(st/emit! dp/create-empty-page)
|
||||
tr (i18n/use-translations)]
|
||||
[:div.sitemap.tool-window
|
||||
[:div.tool-window-bar
|
||||
[:span (tr "workspace.sidebar.sitemap")]
|
||||
|
|
|
@ -7,72 +7,86 @@
|
|||
|
||||
(ns uxbox.util.i18n
|
||||
"A i18n foundation."
|
||||
(:require [cuerdas.core :as str]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.util.storage :refer [storage]]))
|
||||
(:require
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]
|
||||
[beicon.core :as rx]
|
||||
[goog.object :as gobj]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.util.transit :as t]
|
||||
[uxbox.util.storage :refer [storage]]))
|
||||
|
||||
(defonce locale (atom (get storage ::locale cfg/default-language)))
|
||||
(defonce state (atom {}))
|
||||
(defonce locale (get storage ::locale cfg/default-language))
|
||||
(defonce locale-sub (rx/subject))
|
||||
(defonce translations #js {})
|
||||
|
||||
(defn update-locales!
|
||||
[callback]
|
||||
(swap! state callback))
|
||||
;; The traslations `data` is a javascript object and should be treated
|
||||
;; with `goog.object` namespace functions instead of a standart
|
||||
;; clojure functions. This is for performance reasons because this
|
||||
;; code is executed in the critical part (application bootstrap) and
|
||||
;; used in many parts of the application.
|
||||
|
||||
(defn init!
|
||||
[data]
|
||||
(set! translations data))
|
||||
|
||||
(defn set-current-locale!
|
||||
[v]
|
||||
(swap! storage assoc ::locale v)
|
||||
(reset! locale v))
|
||||
(set! locale v)
|
||||
(rx/push! locale-sub v))
|
||||
|
||||
(defn set-default-locale!
|
||||
[]
|
||||
(set-current-locale! cfg/default-language))
|
||||
|
||||
(defn on-locale-change!
|
||||
[callback]
|
||||
(add-watch locale ::main (fn [_ _ old-locale new-locale]
|
||||
(when (not= old-locale new-locale)
|
||||
(callback new-locale old-locale)))))
|
||||
|
||||
;; A marker type that is used just for mark
|
||||
;; a parameter that reprsentes the counter.
|
||||
|
||||
(deftype C [val]
|
||||
IDeref
|
||||
(-deref [o] val))
|
||||
|
||||
(defn c
|
||||
[x]
|
||||
(C. x))
|
||||
|
||||
(defn ^boolean c?
|
||||
[r]
|
||||
(instance? C r))
|
||||
|
||||
(defn- internal-tr
|
||||
([locale code]
|
||||
(let [code (name code)
|
||||
value (gobj/getValueByKeys translations code locale)
|
||||
value (if (nil? value) code value)]
|
||||
(if (array? value)
|
||||
(aget value 0)
|
||||
value)))
|
||||
([locale code & args]
|
||||
(let [code (name code)
|
||||
value (gobj/getValueByKeys translations code locale)
|
||||
value (if (nil? value) code value)
|
||||
plural (first (filter c? args))
|
||||
value (if (array? value)
|
||||
(if (= @plural 1) (aget value 0) (aget value 1))
|
||||
value)]
|
||||
(apply str/format value (map #(if (c? %) @% %) args)))))
|
||||
|
||||
;; A main public api for translate strings.
|
||||
|
||||
;; A marker type that is used just for mark
|
||||
;; a parameter that reprsentes the counter.
|
||||
|
||||
(defn c
|
||||
[x]
|
||||
(C. x))
|
||||
|
||||
(defn tr
|
||||
"Translate the string."
|
||||
([t]
|
||||
(let [default (name t)
|
||||
locale (deref locale)
|
||||
value (or (get-in @state [locale t])
|
||||
default)]
|
||||
(if (vector? value)
|
||||
(or (second value) default)
|
||||
value)))
|
||||
([t & args]
|
||||
(let [locale (deref locale)
|
||||
value (get-in @state [locale t] (name t))
|
||||
plural (first (filter c? args))
|
||||
args (mapv #(if (c? %) @% %) args)
|
||||
value (cond
|
||||
(and (vector? value)
|
||||
(= 3 (count value)))
|
||||
(nth value (min 2 @plural))
|
||||
([code] (internal-tr locale code))
|
||||
([code & args] (apply internal-tr locale code args)))
|
||||
|
||||
(vector? value)
|
||||
(if (= @plural 1) (first value) (second value))
|
||||
(defn use-translations
|
||||
[]
|
||||
(let [[locale set-locale] (mf/useState locale)
|
||||
tr-fn (mf/useMemo (fn [] (partial internal-tr locale))
|
||||
#js [locale])]
|
||||
(mf/useEffect (fn []
|
||||
(let [sub (rx/sub! locale-sub #(set-locale %))]
|
||||
#(rx/dispose! sub)))
|
||||
#js [])
|
||||
tr-fn))
|
||||
|
||||
:else
|
||||
value)]
|
||||
(apply str/format value args))))
|
||||
|
|
|
@ -19,17 +19,17 @@
|
|||
[uxbox.view.ui.lightbox :refer [lightbox]]
|
||||
[uxbox.view.ui.loader :refer [loader]]))
|
||||
|
||||
(i18n/update-locales! (fn [locales]
|
||||
(-> locales
|
||||
(assoc "en" en/locales)
|
||||
(assoc "fr" fr/locales))))
|
||||
;; (i18n/update-locales! (fn [locales]
|
||||
;; (-> locales
|
||||
;; (assoc "en" en/locales)
|
||||
;; (assoc "fr" fr/locales))))
|
||||
|
||||
(declare reinit)
|
||||
|
||||
(i18n/on-locale-change!
|
||||
(fn [new old]
|
||||
(println "Locale changed from" old " to " new)
|
||||
(reinit)))
|
||||
;; (i18n/on-locale-change!
|
||||
;; (fn [new old]
|
||||
;; (println "Locale changed from" old " to " new)
|
||||
;; (reinit)))
|
||||
|
||||
(defn- on-navigate
|
||||
[router path]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue