0
Fork 0
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:
Andrey Antukh 2021-02-18 16:38:31 +01:00 committed by Andrés Moya
parent 344a7dfbaa
commit 299b29b66f
6 changed files with 102 additions and 59 deletions

View file

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

View file

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

View file

@ -119,7 +119,7 @@
ptk/EffectEvent
(effect [_ state s]
(reset! storage {})
(i18n/set-default-locale!))))
(i18n/reset-locale))))
(defn logout
[]

View file

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

View file

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

View file

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