Internationalize all hardcoded English strings in shortcut redesign

Replace 24 hardcoded English UI strings with (t :keymap/...) translation
calls and add corresponding keys to en.edn. Thread a :chord-separator
prop through the shui shortcut component so chord sequence "then" text
is translatable without adding i18n awareness to the presentation layer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
scheinriese
2026-03-10 13:49:02 +01:00
committed by Tienson Qin
parent 5b08feecfa
commit c3df1d857d
3 changed files with 67 additions and 36 deletions

View File

@@ -270,7 +270,8 @@
(rum/defc chord-sequence-keys
"Renders a chord sequence (multi-step key combinations) with 'then' separators.
E.g., [['⌘' 'c'] ['⌘' 'r']] renders as: [⌘ C] then [⌘ R]"
[groups binding {:keys [aria-label aria-hidden? glow?]}]
[groups binding {:keys [aria-label aria-hidden? glow? chord-separator]
:or {chord-separator "then"}}]
(let [normalized-binding (normalize-binding binding)
container-attrs (cond-> {:class "shui-shortcut-chord"
:data-shortcut-binding normalized-binding
@@ -288,7 +289,7 @@
{:key (str "chord-sep-" gi)
:style {:font-size "10px"
:opacity 0.45}}
"then"])
chord-separator])
(let [key-elements (map print-shortcut-key group)]
[:span
{:key (str "chord-group-" gi)
@@ -315,7 +316,7 @@
- :glow? - if true, adds inner glow effect to combo/separate keys (default: true)
- :raw-binding - raw binding format for data-shortcut-binding (for animation matching).
If not provided, will normalize from shortcut prop."
[shortcut & {:keys [style aria-label aria-hidden? glow? raw-binding]
[shortcut & {:keys [style aria-label aria-hidden? glow? raw-binding chord-separator]
:or {style :auto
aria-hidden? false
glow? true}}]
@@ -327,9 +328,10 @@
[shortcut] ; single shortcut string
shortcut)) ; multiple shortcuts
(parse-shortcuts shortcut))
opts {:aria-label aria-label
:aria-hidden? aria-hidden?
:glow? glow?}]
opts (cond-> {:aria-label aria-label
:aria-hidden? aria-hidden?
:glow? glow?}
chord-separator (assoc :chord-separator chord-separator))]
(for [[index binding] (map-indexed vector shortcuts)]
(let [;; Chord sequence: multiple nested groups like [["⌘" "c"] ["⌘" "r"]]
chord-sequence? (and (coll? binding)

View File

@@ -102,7 +102,6 @@
;; Esc: close the popup
is-esc?
(do (.stopPropagation e)
(clear!)
(close-fn))
;; Backspace during accumulation: remove last key from sequence
@@ -178,23 +177,23 @@
(ui/icon "x" {:size 12})]])
;; Placeholder
(when-not has-keystroke?
[:span.shortcut-input-placeholder "Press keys to filter\u2026"])]
[:span.shortcut-input-placeholder (t :keymap/press-keys-to-filter)])]
;; SEPARATOR + TOOLBAR
(shui/separator)
[:div.shortcut-toolbar
[:div
(when has-keystroke?
[:button.shortcut-toolbar-action
[:button.shortcut-toolbar-action.shortcut-toolbar-reset
{:on-click clear!}
(ui/icon "rotate" {:size 12})
[:span "Clear"]])]
[:span (t :keymap/clear)]])]
[:div.flex.items-center
(when has-keystroke?
[:span.shortcut-toolbar-hint
"Remove " (shui/shortcut "backspace" {:style :compact})])
(t :keymap/hint-remove) (shui/shortcut "backspace" {:style :compact})])
[:span.shortcut-toolbar-hint
"Close " (shui/shortcut "escape" {:style :compact})]]]]))
(t :keymap/hint-close) (shui/shortcut "escape" {:style :compact})]]]]))
(rum/defc pane-controls
[q set-q! filter-key set-filter-key! keystroke set-keystroke! toggle-categories-fn pill-counts]
@@ -207,7 +206,7 @@
[:span.search-input-wrap
[:span.search-icon (ui/icon "search" {:size 15})]
[:input.form-input.is-small
{:placeholder "Search shortcuts..."
{:placeholder (t :keymap/search-placeholder)
:ref *search-ref
:value (or q "")
:auto-focus true
@@ -264,7 +263,7 @@
[:button.shortcut-keystroke-inactive
{:on-click open-filter!}
(ui/icon "keyboard" {:size 14})
[:span "Search by keys"]]))]
[:span (t :keymap/search-by-keys)]]))]
;; Row 2: filter pills + fold + refresh
[:div.shortcut-pills-row
@@ -273,7 +272,12 @@
:let [active? (or (and (= k :All) (nil? filter-key))
(= filter-key k))
cnt (get pill-counts k 0)
title (if (= k :All) "All" (name k))]]
title (case k
:All (t :keymap/all)
:Custom (t :keymap/custom)
:Unset (t :keymap/unset)
:Disabled (t :keymap/disabled)
(name k))]]
[:button.shortcut-filter-pill
{:key (name k)
:class (when active? "shortcut-filter-pill--active")
@@ -653,7 +657,7 @@
(for [[idx x] (map-indexed vector current-binding)
:when (string? x)]
[:div.shortcut-input-binding {:key x}
(shui/shortcut x)
(shui/shortcut x {:chord-separator (t :keymap/chord-separator)})
(when (#{:idle :accepted :esc-hint :removed :reset} rec-state)
[:button.shortcut-binding-remove
{:aria-label "Remove binding"
@@ -681,7 +685,7 @@
(ui/icon "x" {:size 12})])])
;; Placeholder
(when (#{:idle :recording :accepted :removed :reset} rec-state)
[:span.shortcut-input-placeholder "Press a shortcut\u2026"])]
[:span.shortcut-input-placeholder (t :keymap/press-a-shortcut)])]
;; FEEDBACK BANNER (conditional)
(let [undo-link
@@ -693,56 +697,56 @@
(set-current-binding! (:previous-binding own)))
(set-undo-snapshot! nil)
(set-rec-state! :idle))}
"Undo"])]
(t :keymap/undo)])]
(case rec-state
:conflict-cross
[:div.shortcut-feedback.shortcut-feedback--error
[:span "Used by "
[:span (t :keymap/used-by)
[:span.shortcut-feedback-name (str "\u201c" (conflict-action-names key-conflicts) "\u201d")]]
(ui/tooltip
(shui/button {:variant :destructive
:size :xs
:on-click override-fn!}
"Reassign")
"Remove from the other action and assign here")]
(t :keymap/reassign))
(t :keymap/reassign-tooltip))]
:conflict-same
[:div.shortcut-feedback.shortcut-feedback--error
[:span "Already bound to this action"]]
[:span (t :keymap/already-bound)]]
:accepted
(cond
(:cross-context? accepted-info)
[:div.shortcut-feedback.shortcut-feedback--warning
[:span "Also used for "
[:span (t :keymap/also-used-for)
[:span.shortcut-feedback-name
(str "\u201c" (:cross-action-name accepted-info) "\u201d")]
(when-let [ctx (:cross-context-label accepted-info)]
(str " in " ctx))]]
(str (t :keymap/in-context) ctx))]]
(:from accepted-info)
[:div.shortcut-feedback.shortcut-feedback--success
[:span "Reassigned from "
[:span (t :keymap/reassigned-from)
[:span.shortcut-feedback-name (str "\u201c" (:from accepted-info) "\u201d")]]
undo-link]
:else
[:div.shortcut-feedback.shortcut-feedback--success
[:span "Shortcut added"]])
[:span (t :keymap/shortcut-added)]])
:removed
[:div.shortcut-feedback.shortcut-feedback--muted
[:span "Shortcut removed"]
[:span (t :keymap/shortcut-removed)]
undo-link]
:reset
[:div.shortcut-feedback.shortcut-feedback--muted
[:span "Reset to default"]
[:span (t :keymap/reset-to-default)]
undo-link]
:esc-hint
[:div.shortcut-feedback.shortcut-feedback--muted
[:span "Esc is reserved"]]
[:span (t :keymap/esc-is-reserved)]]
nil))
@@ -756,22 +760,22 @@
[:button.shortcut-toolbar-action.shortcut-toolbar-reset
{:on-click reset-fn!}
(ui/icon "rotate" {:size 12})
[:span "Reset"]])]
[:span (t :keymap/reset)]])]
[:div.shortcut-toolbar-right
;; Reassign hint (conflict-cross only)
(when (= :conflict-cross rec-state)
[:span.shortcut-toolbar-hint
"Reassign "
(t :keymap/hint-reassign)
(shui/shortcut (if util/mac? "meta+enter" "ctrl+enter") {:style :compact})])
;; Remove hint (idle/accepted/removed/reset with bindings, or conflict states)
(when (or (and (#{:idle :accepted :removed :reset} rec-state) has-bindings?)
(#{:conflict-cross :conflict-same} rec-state))
[:span.shortcut-toolbar-hint
"Remove "
(t :keymap/hint-remove)
(shui/shortcut "backspace" {:style :compact})])
;; Close/Cancel hint
[:span.shortcut-toolbar-hint
(if (= :recording rec-state) "Cancel " "Close ")
(if (= :recording rec-state) (t :keymap/hint-cancel) (t :keymap/hint-close))
(shui/shortcut "escape" {:style :compact})]]]]))
(defn- classify-shortcut
@@ -913,7 +917,7 @@
(when (and ready? no-results?)
[:div.shortcut-empty-state
(ui/icon "list-search" {:size 24})
[:span.text-sm "No matching shortcuts"]])
[:span.text-sm (t :keymap/no-matching-shortcuts)]])
(when (and ready? (not no-results?))
[:ul.list-none.m-0.py-3
@@ -988,11 +992,12 @@
(for [b user-binding
:when (string? b)]
[:span {:key b :style {:display "contents"}}
(shui/shortcut b)]))]
(shui/shortcut b {:chord-separator (t :keymap/chord-separator)})]))]
:else
(for [b binding
:when (string? b)]
[:span {:key b :style {:display "contents"}}
(shui/shortcut (dh/binding-for-display id b)
{:raw-binding [b]})]))]])))))])])]]))
{:raw-binding [b]
:chord-separator (t :keymap/chord-separator)})]))]])))))])])]]))

View File

@@ -401,6 +401,30 @@
:keymap/restore-to-default "Restore to the default"
:keymap/customize-for-label "Customize shortcuts"
:keymap/conflicts-for-label "Keymap conflicts for"
:keymap/search-placeholder "Search shortcuts..."
:keymap/search-by-keys "Search by keys"
:keymap/press-keys-to-filter "Press keys to filter\u2026"
:keymap/press-a-shortcut "Press a shortcut\u2026"
:keymap/clear "Clear"
:keymap/reset "Reset"
:keymap/undo "Undo"
:keymap/reassign "Reassign"
:keymap/reassign-tooltip "Remove from the other action and assign here"
:keymap/used-by "Used by "
:keymap/already-bound "Already bound to this action"
:keymap/also-used-for "Also used for "
:keymap/in-context " in "
:keymap/reassigned-from "Reassigned from "
:keymap/shortcut-added "Shortcut added"
:keymap/shortcut-removed "Shortcut removed"
:keymap/reset-to-default "Reset to default"
:keymap/esc-is-reserved "Esc is reserved"
:keymap/no-matching-shortcuts "No matching shortcuts"
:keymap/chord-separator "then"
:keymap/hint-remove "Remove "
:keymap/hint-close "Close "
:keymap/hint-cancel "Cancel "
:keymap/hint-reassign "Reassign "
:window/minimize "Minimize"
:window/maximize "Maximize"