mirror of
https://github.com/penpot/penpot.git
synced 2025-01-24 07:29:08 -05:00
🎉 Add browser language detection.
This commit is contained in:
parent
344a7dfbaa
commit
299b29b66f
6 changed files with 102 additions and 59 deletions
|
@ -33,7 +33,7 @@
|
|||
|
||||
(s/def ::email ::us/email)
|
||||
(s/def ::fullname ::us/not-empty-string)
|
||||
(s/def ::lang ::us/not-empty-string)
|
||||
(s/def ::lang (s/nilable ::us/not-empty-string))
|
||||
(s/def ::path ::us/string)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::password ::us/not-empty-string)
|
||||
|
|
|
@ -99,6 +99,10 @@
|
|||
(mf/unmount (dom/get-element "modal"))
|
||||
(init-ui))
|
||||
|
||||
(add-watch i18n/locale "locale" (fn [_ _ o v]
|
||||
(when (not= o v)
|
||||
(reinit))))
|
||||
|
||||
(defn ^:dev/after-load after-load
|
||||
[]
|
||||
(reinit))
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
ptk/EffectEvent
|
||||
(effect [_ state s]
|
||||
(reset! storage {})
|
||||
(i18n/set-default-locale!))))
|
||||
(i18n/reset-locale))))
|
||||
|
||||
(defn logout
|
||||
[]
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
(s/def ::fullname ::us/string)
|
||||
(s/def ::email ::us/email)
|
||||
(s/def ::password ::us/string)
|
||||
(s/def ::lang ::us/string)
|
||||
(s/def ::lang (s/nilable ::us/string))
|
||||
(s/def ::theme ::us/string)
|
||||
(s/def ::created-at ::us/inst)
|
||||
(s/def ::password-1 ::us/string)
|
||||
|
@ -50,43 +50,36 @@
|
|||
;; --- Profile Fetched
|
||||
|
||||
(defn profile-fetched
|
||||
([data] (profile-fetched nil data))
|
||||
([on-success {:keys [fullname] :as data}]
|
||||
(us/verify ::profile data)
|
||||
(ptk/reify ::profile-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state :profile
|
||||
(cond-> data
|
||||
(nil? (:lang data))
|
||||
(assoc :lang cfg/default-language)
|
||||
[{:keys [fullname] :as data}]
|
||||
(us/verify ::profile data)
|
||||
(ptk/reify ::profile-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state :profile
|
||||
(cond-> data
|
||||
(nil? (:theme data))
|
||||
(assoc :theme cfg/default-theme))))
|
||||
|
||||
(nil? (:theme data))
|
||||
(assoc :theme cfg/default-theme))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(let [profile (:profile state)]
|
||||
(swap! storage assoc :profile profile)
|
||||
(i18n/set-current-locale! (:lang profile))
|
||||
(theme/set-current-theme! (:theme profile))
|
||||
(when on-success
|
||||
(on-success)))))))
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(let [profile (:profile state)]
|
||||
(swap! storage assoc :profile profile)
|
||||
(i18n/set-locale! (:lang profile))
|
||||
(theme/set-current-theme! (:theme profile))))))
|
||||
|
||||
;; --- Fetch Profile
|
||||
|
||||
(defn fetch-profile
|
||||
([] (fetch-profile nil))
|
||||
([on-success]
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(->> (rp/query! :profile)
|
||||
(rx/map (partial profile-fetched on-success))
|
||||
(rx/catch (fn [error]
|
||||
(if (= (:type error) :not-found)
|
||||
(rx/of (rt/nav :auth-login))
|
||||
(rx/empty)))))))))
|
||||
[]
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(->> (rp/query! :profile)
|
||||
(rx/map profile-fetched)
|
||||
(rx/catch (fn [error]
|
||||
(if (= (:type error) :not-found)
|
||||
(rx/of (rt/nav :auth-login))
|
||||
(rx/empty))))))))
|
||||
|
||||
;; --- Update Profile
|
||||
|
||||
|
@ -95,15 +88,19 @@
|
|||
(us/assert ::profile data)
|
||||
(ptk/reify ::update-profile
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(let [mdata (meta data)
|
||||
(watch [_ state stream]
|
||||
(let [mdata (meta data)
|
||||
on-success (:on-success mdata identity)
|
||||
on-error (:on-error mdata identity)
|
||||
handle-error #(do (on-error (:payload %))
|
||||
(rx/empty))]
|
||||
(->> (rp/mutation :update-profile data)
|
||||
(rx/map (constantly (fetch-profile on-success)))
|
||||
(rx/catch rp/client-error? handle-error))))))
|
||||
on-error (:on-error mdata identity)]
|
||||
(rx/merge
|
||||
(->> (rp/mutation :update-profile data)
|
||||
(rx/map fetch-profile)
|
||||
(rx/catch on-error))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::profile-fetched))
|
||||
(rx/take 1)
|
||||
(rx/tap on-success)
|
||||
(rx/ignore)))))))
|
||||
|
||||
;; --- Request Email Change
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
(ns app.main.ui.settings.options
|
||||
(:require
|
||||
[app.common.spec :as us]
|
||||
[app.common.data :as d]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -21,7 +22,7 @@
|
|||
[cljs.spec.alpha :as s]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(s/def ::lang (s/nilable ::us/not-empty-string))
|
||||
(s/def ::lang (s/nilable ::us/string))
|
||||
(s/def ::theme (s/nilable ::us/not-empty-string))
|
||||
|
||||
(s/def ::options-form
|
||||
|
@ -38,6 +39,9 @@
|
|||
(defn- on-submit
|
||||
[form event]
|
||||
(let [data (:clean-data @form)
|
||||
data (cond-> data
|
||||
(empty? (:lang data))
|
||||
(assoc :lang nil))
|
||||
mdata {:on-success (partial on-success form)
|
||||
:on-error (partial on-error form)}]
|
||||
(st/emit! (du/update-profile (with-meta data mdata)))))
|
||||
|
@ -54,12 +58,10 @@
|
|||
[:h2 (t locale "labels.language")]
|
||||
|
||||
[:div.fields-row
|
||||
[:& fm/select {:options [{:label "English" :value "en"}
|
||||
{:label "Français" :value "fr"}
|
||||
{:label "Español" :value "es"}
|
||||
{:label "Русский" :value "ru"}]
|
||||
[:& fm/select {:options (d/concat [{:label "Auto (browser)" :value ""}]
|
||||
i18n/supported-locales)
|
||||
:label (t locale "dashboard.select-ui-language")
|
||||
:default "en"
|
||||
:default ""
|
||||
:name :lang}]]
|
||||
|
||||
[:h2 (t locale "dashboard.theme-change")]
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
;; 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-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.util.i18n
|
||||
"A i18n foundation."
|
||||
|
@ -17,9 +19,40 @@
|
|||
[app.util.storage :refer [storage]]
|
||||
[app.util.transit :as t]))
|
||||
|
||||
(defonce locale (l/atom (or (get storage ::locale)
|
||||
cfg/default-language)))
|
||||
(def supported-locales
|
||||
[{:label "English" :value "en"}
|
||||
{:label "Español" :value "es"}
|
||||
{:label "Français (community)" :value "fr"}
|
||||
{:label "Русский (community)" :value "ru"}
|
||||
{:label "简体中文 (community)" :value "zh_cn"}])
|
||||
|
||||
(defn- parse-locale
|
||||
[locale]
|
||||
(let [locale (-> (.-language js/navigator)
|
||||
(str/lower)
|
||||
(str/replace "-" "_"))]
|
||||
(cond-> [locale]
|
||||
(str/includes? locale "_")
|
||||
(conj (subs locale 0 2)))))
|
||||
|
||||
(def ^:private browser-locales
|
||||
(delay
|
||||
(-> (.-language js/navigator)
|
||||
(parse-locale))))
|
||||
|
||||
(defn- autodetect
|
||||
[]
|
||||
(let [supported (into #{} (map :value supported-locales))]
|
||||
(loop [locales (seq @browser-locales)]
|
||||
(if-let [locale (first locales)]
|
||||
(if (contains? supported locale)
|
||||
locale
|
||||
(recur (rest locales)))
|
||||
cfg/default-language))))
|
||||
|
||||
(defonce translations #js {})
|
||||
(defonce locale (l/atom (or (get storage ::locale)
|
||||
(autodetect))))
|
||||
|
||||
;; The traslations `data` is a javascript object and should be treated
|
||||
;; with `goog.object` namespace functions instead of a standart
|
||||
|
@ -31,14 +64,21 @@
|
|||
[data]
|
||||
(set! translations data))
|
||||
|
||||
(defn set-current-locale!
|
||||
[v]
|
||||
(swap! storage assoc ::locale v)
|
||||
(reset! locale v))
|
||||
(defn set-locale!
|
||||
[lang]
|
||||
(if lang
|
||||
(do
|
||||
(swap! storage assoc ::locale lang)
|
||||
(reset! locale lang))
|
||||
(do
|
||||
(reset! locale (autodetect)))))
|
||||
|
||||
(defn set-default-locale!
|
||||
(defn reset-locale
|
||||
"Set the current locale to the browser detected one if it is
|
||||
supported or default locale if not."
|
||||
[]
|
||||
(set-current-locale! cfg/default-language))
|
||||
(swap! storage dissoc ::locale)
|
||||
(reset! locale (autodetect)))
|
||||
|
||||
(deftype C [val]
|
||||
IDeref
|
||||
|
|
Loading…
Add table
Reference in a new issue