1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
//! Localization trait and UI-style enums.
//!
//! The core crate ships the [`KeyLocalizer`] trait and a [`MultiLocalizer`]
//! that aggregates the locale modules enabled via Cargo features. Each
//! locale module implements the trait and is wired in through the
//! internal `locales` module.
use Cow;
use crateKeyMod;
/// How a key should be rendered for display.
///
/// - `Textual` — words like `"Up"`, `"Haut"`, `"Command"`.
/// - `Symbolic` — single-glyph icons like `↑`, `⌘`, `⇧`.
///
/// Consumers pick the style that fits their UI.
/// Host operating system — required to pick the correct glyph and label
/// for modifier keys (e.g. `LGUI` → `⌘` on macOS, `⊞` on Windows,
/// `◇` / `Super` on Linux).
/// Logical modifier — platform-agnostic. The mapping to physical
/// `LCTRL` / `RCTRL` / `LGUI` / `LALT` / `MODE` / `LEVEL5` bits is
/// done inside [`resolve`](crate::resolve()) and
/// [`modifier_label`](crate::modifier_label).
/// The crate's single extension point — translate stable ids
/// (`"key_escape"`, `"mod_gui_mac"`, …) into display strings.
///
/// # Stable id contract
///
/// The id passed to [`translate`](Self::translate) is one of:
///
/// - `NamedKey::key_id()` — for non-printable keys, see
/// [`crate::NamedKey`] for the full list (`"key_escape"`,
/// `"key_arrow_up"`, `"key_kp_7"`, …).
/// - `"mod_<name>_<platform>"` — for held-modifier labels emitted by
/// [`crate::modifier_label`]: `"mod_ctrl"` / `"mod_shift"` /
/// `"mod_alt"` / `"mod_gui"` / `"mod_altgr"`, optionally suffixed
/// with `_mac` / `_win` / `_linux` / `_chromeos` / `_android`.
///
/// New ids may be added in future versions; existing ids will never be
/// renamed.
///
/// # Style awareness
///
/// Translations may differ based on [`LabelStyle`] — e.g. `Symbolic`
/// returns `↑` while `Textual` returns `"Haut"` (French). When your
/// translation is the same for both styles, return it for both.
///
/// # Fallback chain
///
/// A localizer answering for only a subset of ids (or only one locale)
/// must return `None` for everything else. The crate's runtime walks
/// this chain on every public-API call:
///
/// 1. Your custom localizer for `(key_id, locale, style)`.
/// 2. The compiled-in locale module for `locale`.
/// 3. The English (`"en"`) module.
/// 4. The raw id as a last resort.
///
/// # Implementing a custom localizer
///
/// Most callers use [`MultiLocalizer`] which already aggregates every
/// locale shipped via Cargo features. Implement your own only to
/// override or extend specific labels.
///
/// ```
/// use std::borrow::Cow;
/// use sdl_keybridge::{KeyLocalizer, LabelStyle, MultiLocalizer};
///
/// /// Renames every macOS GUI label to "⌘ Cmd" for our brand UI.
/// /// Falls through (returns `None`) for everything else so the runtime
/// /// chain still serves the rest from MultiLocalizer.
/// struct BrandedLocalizer;
///
/// impl KeyLocalizer for BrandedLocalizer {
/// fn translate(
/// &self,
/// key_id: &str,
/// _locale: &str,
/// _style: LabelStyle,
/// ) -> Option<Cow<'static, str>> {
/// match key_id {
/// "mod_gui_mac" => Some(Cow::Borrowed("⌘ Cmd")),
/// _ => None,
/// }
/// }
/// }
///
/// // Pass it where you'd normally pass MultiLocalizer.
/// let _ = BrandedLocalizer;
/// let _fallback = MultiLocalizer::new();
/// ```
///
/// # Implementing a single-locale localizer
///
/// To add a brand-new locale (rather than override one), follow the
/// `CONTRIBUTING.md` recipe: copy `src/locales/en.rs` and wire it up
/// behind a Cargo feature. That gets the locale into [`MultiLocalizer`]
/// for free, which is almost always what users want.
///
/// You can also implement [`KeyLocalizer`] yourself for an unsupported
/// locale — return `Some(_)` only when `locale` matches your code:
///
/// ```
/// use std::borrow::Cow;
/// use sdl_keybridge::{KeyLocalizer, LabelStyle};
///
/// struct EsperantoLocalizer;
///
/// impl KeyLocalizer for EsperantoLocalizer {
/// fn translate(&self, key_id: &str, locale: &str, style: LabelStyle)
/// -> Option<Cow<'static, str>>
/// {
/// if locale != "eo" { return None; }
/// use LabelStyle::*;
/// let s = match (key_id, style) {
/// ("key_escape", Textual) => "Eskapi",
/// ("key_return", Textual) => "Enen",
/// ("key_space", Textual) => "Spaco",
/// _ => return None,
/// };
/// Some(Cow::Borrowed(s))
/// }
/// }
/// ```
/// Aggregates all locale modules compiled into the crate and dispatches
/// calls by locale code. Use this as the default `KeyLocalizer` when you
/// don't need custom overrides.
/// Helper used by the public API: try the user-supplied localizer first,
/// then fall back to the compiled locale set, finally to English.
pub