♻️ Refactor all i18n subsystem.

This commit is contained in:
Andrey Antukh 2020-01-11 16:28:54 +01:00
parent b13488404e
commit f2d475d3d3
18 changed files with 1829 additions and 228 deletions

View file

@ -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))

View file

@ -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")

View file

@ -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

View file

@ -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]

View file

@ -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]])

View file

@ -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")]

View file

@ -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))))

View file

@ -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]