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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
//! The page's own i18n furniture: [`html_lang`], and the six components —
//! the preload link, the catalog map, the islands gate, the `hreflang`
//! block, the switcher and its options.
//!
//! The six are the active Leptos line's helper crate's (`mf2-leptos-ui-0-9`
//! or `mf2-leptos-ui-0-8`): Leptos's `view!` and `#[component]` write
//! `::leptos` into the crate that uses them, and this crate reaches its two
//! lines under names of its own. The helpers cannot depend on this crate,
//! so each component is generic over their `Layer` trait — what it reads
//! from here — and [`Mf2`] implements it. Each function below is the
//! helper's component with [`Mf2`] chosen: it takes the helper's props, so
//! `view!` builds it exactly as it builds any component, and the calls into
//! this crate are resolved when it is compiled. Nothing is installed at
//! start-up, and an application that renders none of the six links none of
//! them.
//!
//! WCAG 2.2 AA is a requirement here, not a nicety, and two of these
//! components exist because getting them right by hand is fiddly:
//!
//! * `<html lang dir>` must be correct and must **update on a switch**
//! (3.1.1). `set_document_lang` (a client function: `hydrate` or `csr`)
//! does the update; [`html_lang`] gives the shell the pair to render.
//! * A language control must be a real labelled control, and each language
//! must be named **in its own language, with its own `lang`** — otherwise
//! a screen reader pronounces "Français" with an English voice.
//!
//! **Where the option text comes from, and why it is not in the wasm.**
//! Each language's name is a message of the application's own catalog —
//! `language.fr` in *every* locale's catalog — and never a literal in the
//! client: the generated `setup()` names each one ([`Setup::with_names`](crate::leptos::Setup::with_names)),
//! and a [`LocaleOption`] takes its text as children. What keeps the
//! autonyms out of the wasm is that they are catalog data.
use String;
use Vec;
use Dir;
use crateIntoView;
use crateui;
/// The layer the six components read: this crate. What each component's
/// props are generic over; an application never names it.
;
/// A client's switch that failed, logged once: the page is as it was.
pub
/// The language a [`LocaleOption`] selects: the generated `Locale` converts
/// into it (`tag=Locale::Fr`), and so does a `&'static str`.
pub use LocaleTag;
/// The props of [`CatalogPreload`] (none).
pub type CatalogPreloadProps = CatalogPreloadProps;
/// The props of [`CatalogLinks`] (none).
pub type CatalogLinksProps = CatalogLinksProps;
/// The props of [`IslandsGate`] (none).
pub type IslandsGateProps = IslandsGateProps;
/// The props of [`AlternateLinks`].
pub type AlternateLinksProps = AlternateLinksProps;
/// The props of [`LocaleSwitcher`].
pub type LocaleSwitcherProps = LocaleSwitcherProps;
/// The props of [`LocaleOption`].
pub type LocaleOptionProps = LocaleOptionProps;
/// The `lang` and `dir` the shell should put on `<html>`: the catalog's own
/// locale, so the page always says what it is in (WCAG 3.1.1).
///
/// With no catalog — before boot, or a build with none — it is the source
/// locale and `ltr`.
/// The preload link for the catalog of the locale this page is being
/// rendered in — the boot data, and the reason the client needs no inline
/// script and makes no extra request.
///
/// It renders only on the server: the client reads this link, it does not
/// write it, and the shell's `<head>` is not hydrated.
///
/// When the request was rendered in the reader's time zone, the link says
/// which (`data-mf2-zone`), so that the client knows whether its dates need
/// correcting. Absent, the page was rendered in [`Setup`]'s zone.
///
/// [`Setup`]: crate::leptos::Setup
/// The in-page tag → URL map: one `<link>` per locale, so that a switch needs
/// no round trip to learn the hashed URL. Server only, like
/// [`CatalogPreload`].
///
/// A site that would rather keep its pages a few bytes smaller simply does
/// not render this, and the switch redirects through `/i18n/<tag>` instead.
///
/// **Every** locale, including the one the page was rendered in: after one
/// switch the page's locale is no longer the one the reader may want back.
/// The first thing in an islands page's `<body>`: an empty island that
/// Leptos' island walk awaits until the catalog is installed, so that every
/// island after it hydrates against the catalog the page was rendered with
/// (`hydrate_islands`, with `hydrate`, says why nothing else can wait).
/// Pair it with [`islands_gate!`](crate::leptos::islands_gate) in the
/// client.
///
/// It must come before every island in document order, and outside all of
/// them. It has no content and no role, so assistive technology never meets
/// it.
/// `<link rel="alternate" hreflang>` for a site whose locales have their own
/// URLs (a path prefix).
///
/// Its prop, `href_of: fn(&str) -> String`, maps a tag to that locale's URL
/// for the page being rendered. `x-default` points at the source locale,
/// which is what a crawler uses when it has no better match.
/// A labelled native control that switches locale, applied by a button.
///
/// A `<form method="get">` holding a `<select name="lang">` (the name the
/// server's `QueryParam` reads) inside its
/// `<label>` and a submit button. Choosing a language changes nothing until
/// the button is pressed: the keyboard fires a `<select>`'s `change` on
/// every arrow key, so switching on it would change the page's language —
/// or, under `static-locale`, reload it — per keypress (WCAG 3.2.2, failure
/// F37).
///
/// With no client code — before the wasm loads, after a failed boot, or on
/// an islands page where the switcher is not an island — the form's own
/// `GET ?lang=…` is the switch, which `mf2::axum`'s `QueryParam` negotiates;
/// the `<select>` takes its name from that installed `QueryParam`.
/// Under `hydrate` and `csr` the submit is intercepted and becomes
/// `set_locale`, live, with focus left on the button.
///
/// The `<select>` is inside its `<label>`, so there is no fixed `id` and a
/// page may carry two switchers (a header and a footer). With no children
/// it offers every language, in `Locale::ALL`'s order, each named by its
/// `language.<tag>` message (its tag when the corpus has none); children
/// are a list of one's own, [`LocaleOption`]s. Each option carries its own
/// `lang`, and takes no fallback-language span.
///
/// Its props:
/// * `label` (into `TextProp`) — the control's accessible name: a `<label>`,
/// not a `placeholder` and not an `aria-label` on its own, so that it is
/// visible as well as announced (WCAG 3.3.2);
/// * `button` (into `TextProp`) — the submit button's text, the
/// application's own message in the page's language;
/// * `children` (optional) — a list of one's own: one [`LocaleOption`] per
/// locale offered;
/// * `href_of` (optional, `fn(&str) -> String`) — for a site whose
/// languages live in its URLs (`/fr/…`, `mf2::axum`'s `PathPrefix`), the
/// URL of the current page in the given locale. Each option then carries
/// it as `data-mf2-href`, and the submit navigates there instead of
/// switching in place — a `?lang=` cannot outrank the path. Without the
/// wasm the form's `?lang=` still goes to the server, and
/// `mf2::axum::path_prefix_redirect` sends it on to that URL.
///
/// ```ignore
/// <LocaleSwitcher label=tr!("choose-language") button=tr!("apply-language")/>
/// <LocaleSwitcher label=tr!("choose-language") button=tr!("apply-language")>
/// <LocaleOption tag=Locale::En>{Locale::En.name()}</LocaleOption>
/// <LocaleOption tag=Locale::Fr>{Locale::Fr.name()}</LocaleOption>
/// </LocaleSwitcher>
/// ```
/// One `<option>`, named in its own language and marked as being in it.
///
/// Its props: `tag`, the language this option selects (the generated
/// `Locale`, or its BCP 47 tag as a `&'static str`: [`LocaleTag`]), and
/// `children`, the language's name **in that language** — from the
/// application's own catalog, so it is never a literal in the client.
///
/// The option of the page's locale is `selected` in the markup, so that the
/// control shows the right language before any client code runs — the form
/// submits it as it stands.