mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
🌐 Translate the compare dialog labels and counts (#12566)
The compare dialog named changed properties, booleans and collection counts from English literals in the UI namespace, so no locale could replace them; it also passed bare numbers to tr, so a plural catalogue entry could never reach its singular form. Property labels now come from workspace.branches.compare.attr.*, the true and false values from .value-true and .value-false, and every count travels through i18n/c, which makes the singular msgstr[0] reachable. Numbers are rendered with the active locale, so Spanish reads 439,5 and groups thousands. An attr the catalogue does not name still falls back to its own key, humanised: a technical identifier rather than prose. The en.po entries for the counts gain their plural forms. es.po does not carry the compare area yet, so it stays untouched. AI-assisted-by: claude-opus-5-5 Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>
This commit is contained in:
parent
1c9baa0b06
commit
d0414481ef
@ -88,21 +88,26 @@
|
||||
(str (subs s 0 40) "…")
|
||||
s)))
|
||||
|
||||
(def ^:private attr->label
|
||||
"Friendlier names for attrs whose raw key is unclear; every other attr
|
||||
is humanized generically (kebab-case -> Capitalized words)."
|
||||
{:rx "Corner radius" :ry "Corner radius"
|
||||
:grow-type "Text auto-grow" :blend-mode "Blend mode"
|
||||
:shape-ref "Component reference" :component-id "Component"
|
||||
:component-file "Component library" :hidden "Visibility"
|
||||
:blocked "Locked" :proportion-lock "Lock proportions"
|
||||
:parent-id "Parent" :frame-id "Container"})
|
||||
|
||||
(defn- attr-label
|
||||
"Human-readable label for a changed property key."
|
||||
"Human-readable label for a changed property key. The named attrs are
|
||||
the ones whose raw key means nothing to a user, so each carries a
|
||||
catalogue entry. Every other attr keeps its own key, humanised
|
||||
(kebab-case -> Capitalized words): a technical identifier rather than
|
||||
prose, and the last resort for an attr the catalogue does not name."
|
||||
[attr]
|
||||
(or (get attr->label attr)
|
||||
(-> (name attr) (str/replace #"-" " ") str/capital)))
|
||||
(case attr
|
||||
(:rx :ry) (tr "workspace.branches.compare.attr.corner-radius")
|
||||
:grow-type (tr "workspace.branches.compare.attr.text-auto-grow")
|
||||
:blend-mode (tr "workspace.branches.compare.attr.blend-mode")
|
||||
:shape-ref (tr "workspace.branches.compare.attr.component-reference")
|
||||
:component-id (tr "workspace.branches.compare.attr.component")
|
||||
:component-file (tr "workspace.branches.compare.attr.component-library")
|
||||
:hidden (tr "workspace.branches.compare.attr.visibility")
|
||||
:blocked (tr "workspace.branches.compare.attr.locked")
|
||||
:proportion-lock (tr "workspace.branches.compare.attr.lock-proportions")
|
||||
:parent-id (tr "workspace.branches.compare.attr.parent")
|
||||
:frame-id (tr "workspace.branches.compare.attr.container")
|
||||
(-> (name attr) (str/replace #"-" " ") str/capital)))
|
||||
|
||||
;; --- Compare: grouping by component + per-item type labels
|
||||
|
||||
@ -181,10 +186,12 @@
|
||||
|
||||
(defn- fmt-number
|
||||
"At most two decimals, trailing zeros stripped (89.99999 -> \"90\",
|
||||
439.4999 -> \"439.5\", 386 -> \"386\")."
|
||||
439.4999 -> \"439.5\", 386 -> \"386\"), grouped and punctuated the way
|
||||
the active locale writes numbers (\"439,5\" in Spanish)."
|
||||
[v]
|
||||
(-> (.toFixed v 2)
|
||||
(str/replace #"\.?0+$" "")))
|
||||
(let [locale (-> i18n/*current-locale*
|
||||
(str/replace "_" "-"))]
|
||||
(.toLocaleString v locale #js {:maximumFractionDigits 2})))
|
||||
|
||||
(defn- display-val
|
||||
"Compact, human-readable rendering of a property value. Raw uuids and
|
||||
@ -195,12 +202,14 @@
|
||||
(nil? v) "—"
|
||||
(string? v) (if (> (count v) 32) (str (subs v 0 32) "…") v)
|
||||
(number? v) (fmt-number v)
|
||||
(boolean? v) (if v "true" "false")
|
||||
(boolean? v) (if v
|
||||
(tr "workspace.branches.compare.value-true")
|
||||
(tr "workspace.branches.compare.value-false"))
|
||||
(keyword? v) (name v)
|
||||
(ct/inst? v) (ct/format-inst v :localized-date-time)
|
||||
(uuid? v) (str (subs (str v) 0 8) "…")
|
||||
(sequential? v) (tr "workspace.branches.compare.n-items" (dm/str (count v)))
|
||||
(map? v) (tr "workspace.branches.compare.n-props" (dm/str (count v)))
|
||||
(sequential? v) (tr "workspace.branches.compare.n-items" (i18n/c (count v)))
|
||||
(map? v) (tr "workspace.branches.compare.n-props" (i18n/c (count v)))
|
||||
:else (short-str v)))
|
||||
|
||||
(defn- status-matches?
|
||||
@ -1083,7 +1092,7 @@
|
||||
(= :added status) (tr "workspace.branches.compare.subtitle-new"
|
||||
(if type-lbl (tr type-lbl) ""))
|
||||
(and (= :modified status) (seq attrs))
|
||||
(tr "workspace.branches.compare.subtitle-mods" (count attrs))
|
||||
(tr "workspace.branches.compare.subtitle-mods" (i18n/c (count attrs)))
|
||||
type-lbl (tr type-lbl)
|
||||
:else nil)]
|
||||
[:li {:class (stl/css-case :compare-item true
|
||||
@ -1256,14 +1265,14 @@
|
||||
(when stats
|
||||
[:div {:class (stl/css :compare-stats)}
|
||||
[:span {:class (stl/css :stat-badge :badge-added)}
|
||||
(dm/str (:added stats) " " (tr "workspace.branches.compare.stats.added"))]
|
||||
(tr "workspace.branches.compare.stats.added" (i18n/c (:added stats)))]
|
||||
[:span {:class (stl/css :stat-badge :badge-modified)}
|
||||
(dm/str (:modified stats) " " (tr "workspace.branches.compare.stats.modified"))]
|
||||
(tr "workspace.branches.compare.stats.modified" (i18n/c (:modified stats)))]
|
||||
[:span {:class (stl/css :stat-badge :badge-deleted)}
|
||||
(dm/str (:deleted stats) " " (tr "workspace.branches.compare.stats.deleted"))]
|
||||
(tr "workspace.branches.compare.stats.deleted" (i18n/c (:deleted stats)))]
|
||||
(when (pos? (:conflicts stats))
|
||||
[:span {:class (stl/css :stat-badge :badge-conflict)}
|
||||
(dm/str (:conflicts stats) " " (tr "workspace.branches.compare.stats.conflicts"))])])]
|
||||
(tr "workspace.branches.compare.stats.conflicts" (i18n/c (:conflicts stats)))])])]
|
||||
|
||||
[:> modal-content* {:class (stl/css :compare-content)}
|
||||
(cond
|
||||
@ -1345,14 +1354,14 @@
|
||||
(mf/html
|
||||
[:div {:class (stl/css :compare-footer-info)}
|
||||
[:> i/icon* {:icon-id i/info :size "s"}]
|
||||
[:span (tr "workspace.branches.compare.footer-changes" total)]
|
||||
[:span (tr "workspace.branches.compare.footer-changes" (i18n/c total))]
|
||||
(when (seq all-keys)
|
||||
[:span {:class (stl/css :compare-footer-selected)}
|
||||
(tr "workspace.branches.compare.footer-selected"
|
||||
(str kept-count) (str (count all-keys)))])
|
||||
(str kept-count) (i18n/c (count all-keys)))])
|
||||
(when conflicts?
|
||||
[:span {:class (stl/css :compare-footer-conflicts)}
|
||||
(dm/str " · " (tr "workspace.branches.compare.footer-conflicts" conflicts))])])
|
||||
(dm/str " · " (tr "workspace.branches.compare.footer-conflicts" (i18n/c conflicts)))])])
|
||||
:end
|
||||
(mf/html
|
||||
[:*
|
||||
|
||||
@ -110,6 +110,7 @@
|
||||
[frontend-tests.tokens.style-dictionary-test]
|
||||
[frontend-tests.tokens.token-errors-test]
|
||||
[frontend-tests.tokens.workspace-tokens-remap-test]
|
||||
[frontend-tests.ui.branches-compare-labels-test]
|
||||
[frontend-tests.ui.check-updates-test]
|
||||
[frontend-tests.ui.color-token-position-test]
|
||||
[frontend-tests.ui.colorpicker-token-set-order-test]
|
||||
@ -267,6 +268,7 @@
|
||||
'frontend-tests.tokens.token-errors-test
|
||||
'frontend-tests.tokens.logic.tokens-status-test
|
||||
'frontend-tests.tokens.workspace-tokens-remap-test
|
||||
'frontend-tests.ui.branches-compare-labels-test
|
||||
'frontend-tests.ui.check-updates-test
|
||||
'frontend-tests.ui.color-token-position-test
|
||||
'frontend-tests.ui.colorpicker-token-set-order-test
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
;; 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) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
(ns frontend-tests.ui.branches-compare-labels-test
|
||||
"The compare dialog names properties, booleans, numbers and collection
|
||||
counts through the catalogue, so a translation replaces every one of
|
||||
them. The catalogue here is a stand-in for en.po: its values are picked
|
||||
to differ from the English the dialog used to hardcode."
|
||||
(:require
|
||||
[app.main.ui.workspace.sidebar.branches :as branches]
|
||||
[app.util.i18n :as i18n]
|
||||
[cljs.test :as t :include-macros true]))
|
||||
|
||||
;; Access the private formatters for direct testing.
|
||||
(def ^:private attr-label @#'branches/attr-label)
|
||||
(def ^:private display-val @#'branches/display-val)
|
||||
(def ^:private get-translations @#'i18n/get-translations)
|
||||
|
||||
(def ^:private catalogue
|
||||
#js {"workspace.branches.compare.attr.corner-radius" "Radio de esquina"
|
||||
"workspace.branches.compare.attr.component-reference" "Referencia de componente"
|
||||
"workspace.branches.compare.value-true" "verdadero"
|
||||
"workspace.branches.compare.value-false" "falso"
|
||||
"workspace.branches.compare.n-items" #js ["1 elemento" "%s elementos"]
|
||||
"workspace.branches.compare.n-props" #js ["1 propiedad" "%s propiedades"]})
|
||||
|
||||
(defn- with-catalogue
|
||||
"Install the stand-in catalogue for one test and put the previous one
|
||||
back, so other test namespaces keep their own strings."
|
||||
[f]
|
||||
(let [translations (get-translations)
|
||||
previous (unchecked-get translations "en")]
|
||||
(i18n/set-default-translations catalogue)
|
||||
(try
|
||||
(f)
|
||||
(finally
|
||||
(i18n/set-translations "en" previous)))))
|
||||
|
||||
(t/use-fixtures :each with-catalogue)
|
||||
|
||||
(t/deftest property-labels-come-from-the-catalogue
|
||||
(t/is (= "Radio de esquina" (attr-label :rx)))
|
||||
(t/is (= "Radio de esquina" (attr-label :ry)))
|
||||
(t/is (= "Referencia de componente" (attr-label :shape-ref))))
|
||||
|
||||
(t/deftest an-attr-with-no-name-of-its-own-falls-back-to-its-key
|
||||
(t/is (= "Some internal attr" (attr-label :some-internal-attr))
|
||||
"an attr the catalogue does not name stays a humanised key"))
|
||||
|
||||
(t/deftest boolean-values-come-from-the-catalogue
|
||||
(t/is (= "verdadero" (display-val true)))
|
||||
(t/is (= "falso" (display-val false))))
|
||||
|
||||
(t/deftest collection-counts-choose-the-plural-form
|
||||
(t/is (= "1 elemento" (display-val [1]))
|
||||
"a single item reads in the singular")
|
||||
(t/is (= "3 elementos" (display-val [1 2 3])))
|
||||
(t/is (= "1 propiedad" (display-val {:x 1})))
|
||||
(t/is (= "2 propiedades" (display-val {:x 1 :y 2}))))
|
||||
|
||||
(t/deftest numbers-are-punctuated-by-the-locale
|
||||
(binding [i18n/*current-locale* "es"]
|
||||
(t/is (= "439,5" (display-val 439.5))
|
||||
"a Spanish locale reads the decimal comma"))
|
||||
(binding [i18n/*current-locale* "en"]
|
||||
(t/is (= "439.5" (display-val 439.5)))
|
||||
(t/is (= "90" (display-val 89.99999)) "at most two decimals")
|
||||
(t/is (= "386" (display-val 386)))))
|
||||
@ -6411,6 +6411,50 @@ msgstr "Compare"
|
||||
msgid "workspace.branches.compare.attr"
|
||||
msgstr "Property"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:99
|
||||
msgid "workspace.branches.compare.attr.blend-mode"
|
||||
msgstr "Blend mode"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:101
|
||||
msgid "workspace.branches.compare.attr.component"
|
||||
msgstr "Component"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:102
|
||||
msgid "workspace.branches.compare.attr.component-library"
|
||||
msgstr "Component library"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:100
|
||||
msgid "workspace.branches.compare.attr.component-reference"
|
||||
msgstr "Component reference"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:107
|
||||
msgid "workspace.branches.compare.attr.container"
|
||||
msgstr "Container"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:97
|
||||
msgid "workspace.branches.compare.attr.corner-radius"
|
||||
msgstr "Corner radius"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:105
|
||||
msgid "workspace.branches.compare.attr.lock-proportions"
|
||||
msgstr "Lock proportions"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:104
|
||||
msgid "workspace.branches.compare.attr.locked"
|
||||
msgstr "Locked"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:106
|
||||
msgid "workspace.branches.compare.attr.parent"
|
||||
msgstr "Parent"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:98
|
||||
msgid "workspace.branches.compare.attr.text-auto-grow"
|
||||
msgstr "Text auto-grow"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:103
|
||||
msgid "workspace.branches.compare.attr.visibility"
|
||||
msgstr "Visibility"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:465
|
||||
#, unused
|
||||
msgid "workspace.branches.compare.branch"
|
||||
@ -6428,17 +6472,23 @@ msgstr "Could not load the changes of this branch"
|
||||
msgid "workspace.branches.compare.export"
|
||||
msgstr "Export diff"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1118
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1351
|
||||
msgid "workspace.branches.compare.footer-changes"
|
||||
msgstr "%s changes"
|
||||
msgid_plural "workspace.branches.compare.footer-changes"
|
||||
msgstr[0] "1 change"
|
||||
msgstr[1] "%s changes"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1121
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1358
|
||||
msgid "workspace.branches.compare.footer-conflicts"
|
||||
msgstr "%s to resolve before merging"
|
||||
msgid_plural "workspace.branches.compare.footer-conflicts"
|
||||
msgstr[0] "1 to resolve before merging"
|
||||
msgstr[1] "%s to resolve before merging"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1391
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1354
|
||||
msgid "workspace.branches.compare.footer-selected"
|
||||
msgstr "%s of %s selected"
|
||||
msgid_plural "workspace.branches.compare.footer-selected"
|
||||
msgstr[0] "%s of 1 selected"
|
||||
msgstr[1] "%s of %s selected"
|
||||
|
||||
#, unused
|
||||
msgid "workspace.branches.compare.group.colors"
|
||||
@ -6481,13 +6531,17 @@ msgstr "Computing changes…"
|
||||
msgid "workspace.branches.compare.main"
|
||||
msgstr "main"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:220
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:209
|
||||
msgid "workspace.branches.compare.n-items"
|
||||
msgstr "%s items"
|
||||
msgid_plural "workspace.branches.compare.n-items"
|
||||
msgstr[0] "1 item"
|
||||
msgstr[1] "%s items"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:221
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:210
|
||||
msgid "workspace.branches.compare.n-props"
|
||||
msgstr "%s properties"
|
||||
msgid_plural "workspace.branches.compare.n-props"
|
||||
msgstr[0] "1 property"
|
||||
msgstr[1] "%s properties"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:934
|
||||
msgid "workspace.branches.compare.no-props"
|
||||
@ -6556,25 +6610,35 @@ msgstr "Text"
|
||||
msgid "workspace.branches.compare.shape.variant"
|
||||
msgstr "Variant"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1036
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1262
|
||||
msgid "workspace.branches.compare.stats.added"
|
||||
msgstr "added"
|
||||
msgid_plural "workspace.branches.compare.stats.added"
|
||||
msgstr[0] "1 added"
|
||||
msgstr[1] "%s added"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1043
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1269
|
||||
msgid "workspace.branches.compare.stats.conflicts"
|
||||
msgstr "conflicts"
|
||||
msgid_plural "workspace.branches.compare.stats.conflicts"
|
||||
msgstr[0] "1 conflict"
|
||||
msgstr[1] "%s conflicts"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1040
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1266
|
||||
msgid "workspace.branches.compare.stats.deleted"
|
||||
msgstr "deleted"
|
||||
msgid_plural "workspace.branches.compare.stats.deleted"
|
||||
msgstr[0] "1 deleted"
|
||||
msgstr[1] "%s deleted"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1038
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1264
|
||||
msgid "workspace.branches.compare.stats.modified"
|
||||
msgstr "modified"
|
||||
msgid_plural "workspace.branches.compare.stats.modified"
|
||||
msgstr[0] "1 modified"
|
||||
msgstr[1] "%s modified"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:879
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1089
|
||||
msgid "workspace.branches.compare.subtitle-mods"
|
||||
msgstr "%s property changes"
|
||||
msgid_plural "workspace.branches.compare.subtitle-mods"
|
||||
msgstr[0] "1 property change"
|
||||
msgstr[1] "%s property changes"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:876
|
||||
msgid "workspace.branches.compare.subtitle-new"
|
||||
@ -6612,6 +6676,14 @@ msgstr "Token"
|
||||
msgid "workspace.branches.compare.type.typography"
|
||||
msgstr "Typography"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:205
|
||||
msgid "workspace.branches.compare.value-false"
|
||||
msgstr "false"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:204
|
||||
msgid "workspace.branches.compare.value-true"
|
||||
msgstr "true"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/branches.cljs:1430
|
||||
msgid "workspace.branches.conflicts.all-branch"
|
||||
msgstr "Use branch for all"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user