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
//! Localization service, sources and other types.
//!
//! Localized text is declared using the [`l10n!`] macro, it provides a read-only text variable that automatically
//! updates to be best localized text available given the current loaded localization and the app language.
//!
//! ```
//! use zng::prelude::*;
//! # fn example() {
//!
//! let click_count = var(0u32);
//! # let _ =
//! Window! {
//! title = l10n!("window-title", "Window Title");
//! child = Button! {
//! on_click = hn!(click_count, |_| click_count.set(click_count.get() + 1));
//! child = Text!(l10n!("click-count", "Clicked {$n} times", n = click_count.clone()));
//! };
//! }
//! # ; }
//! ```
//!
//! In the example above declares two localization messages, "window.title" and "btn.click_count", if
//! these messages are localized for the current language the localized text is used, otherwise the provided
//! fallback is used.
//!
//! The [`L10N`] service can be used to set the app language and load localization resources. The example below
//! sets the language to en-US and loads localization from a directory using [`L10N.load_dir`].
//!
//! ```no_run
//! use zng::prelude::*;
//!
//! APP.defaults().run_window("main", async {
//! // start loading localization resources
//! L10N.load_dir(zng::env::res("l10n"));
//! // set the app language, by default is the system language
//! L10N.app_lang().set(lang!("en-US"));
//! // preload the localization resources for a language
//! L10N.wait_first(lang!("en-US")).await;
//!
//! Window! {
//! // ..
//! }
//! });
//! ```
//!
//! The service also supports embedded localization resources in the `.tar` and `.tar.gz` formats using
//! [`L10N.load_tar`], see the [localize example] for more details. You can also implement more container formats using [`L10N.load`].
//!
//! [`L10N.load_dir`]: crate::l10n::L10N::load_dir
//! [`L10N.load_tar`]: crate::l10n::L10N::load_tar
//! [`L10N.load`]: crate::l10n::L10N::load
//! [localize example]: https://github.com/zng-ui/zng/blob/main/examples/localize/build.rs
//!
//! # Fluent
//!
//! The localization files are in the [Fluent](https://projectfluent.org/) format. Fluent empowers translators to
//! script things like plural forms, for this reason a localization file should be provided even for the same
//! language the `l10n!` fallback text is written in.
//!
//! ```ftl
//! click-count = {$n ->
//! [one] Clicked {$n} time
//! *[other] Clicked {$n} times
//! }
//! ```
//!
//! The example above demonstrates a localized message that provides plural alternatives for the English language.
//!
//! # Scraper
//!
//! The `cargo zng l10n` tool can be used to generate a Fluent file from source code, the Fluent file can be
//! used as a template for translators, it will include the fallback text and comments written close the key
//! declaration.
//!
//! ```
//! use zng::prelude::*;
//! # fn example() {
//!
//! // l10n-### This standalone comment is added to the scraped template file.
//!
//! let click_count = var(0u32);
//! # let _ =
//! Window! {
//! title = l10n!("window-title", "Window Title");
//! child = Button! {
//! on_click = hn!(click_count, |_| click_count.set(click_count.get() + 1));
//! // l10n-# This comment is added to the `"click-count"` entry.
//! child = Text!(l10n!("click-count", "Clicked {$n} times", n = click_count.clone()));
//! };
//! }
//! # ; }
//! ```
//!
//! When the example above is scrapped it generates:
//!
//! ```ftl
//! ### This standalone comment is added to all scraped template files.
//!
//! # This comment is added to the `"click-count"` entry.
//! click-count = Clicked {$n} times
//! ```
//!
//! See the [`l10n!`] documentation for a full explanation of how the Scraper converts comments and the
//! `l10n!` calls into Fluent files.
//!
//! [`l10n!`]: crate::l10n::l10n
//!
//! # Commands
//!
//! Commands metadata can be localized and scrapped, to enable this set `l10n!:` on the [`command!`](zng::event::command) declarations.
//!
//! If the first metadata is `l10n!:` the command init will attempt to localize the other string metadata. The `cargo zng l10n`
//! command line tool scraps commands that set this special metadata.
//!
//! ```
//! # use zng_app::{event::{command, CommandNameExt, CommandInfoExt}, shortcut::{CommandShortcutExt, shortcut}};
//! command! {
//! pub static FOO_CMD {
//! l10n!: true,
//! name: "Foo!",
//! info: "Does the foo thing",
//! };
//! }
//! ```
//!
//! The example above will be scrapped as:
//!
//! ```ftl
//! FOO_CMD =
//! .name = Foo!
//! .info = Does the foo thing.
//! ```
//!
//! The `l10n!:` meta can also be set to a localization file name:
//!
//! ```
//! # use zng_app::{event::{command, CommandNameExt, CommandInfoExt}, shortcut::{CommandShortcutExt, shortcut}};
//! command! {
//! pub static FOO_CMD {
//! l10n!: "file",
//! name: "Foo!",
//! };
//! }
//! ```
//!
//! The example above is scrapped to `{l10n-dir}/{lang}/file.ftl` files.
//!
//! ## Limitations
//!
//! Interpolation is not supported in command localization strings.
//!
//! The `l10n!:` value must be a *textual* literal, that is, it can be only a string literal or a `bool` literal, and it cannot be
//! inside a macro expansion.
//!
//! # Dependency Localization
//!
//! The `cargo zng l10n` tool copies all localization files from dependency crates by default. The Fluent files
//! are placed in `dir/{lang}/deps/*/*/*.ftl`, the [`L10N`] service knows to search these directories structure for localization.
//!
//! Note that dependency crates are **not scrapped**, the library crate authors are expected to scrap (and translate) text
//! to a `{crate}/l10n` directory, beside the `{crate}/Cargo.toml`.
//!
//! The copied dependency resources are excluded from Git source control by default, the recommended workflow is to rerun `cargo zng l10n`
//! after each `cargo update`. Projects created using `cargo zng new` can just run `cargo do update`, that is a shorthand for:
//!
//! ```console
//! cargo update
//! cargo zng l10n --no-local --no-pkg --output res/l10n
//! ```
//!
//! ## Packaging
//!
//! When packaging a release build for publish you want to only include the dependency resources for the locales supported by your
//! application. The easiest way to do this is `cargo zng res` with a `.zr-l10n` tool call, it will automatically filter out
//! languages that only have dependency resources and also. Call `cargo zng res --tool sh` to read detailed help.
//!
//! ### Subsetting
//!
//! Your app may not use all localization resources from dependencies, with some work you can collect a *subset* allow list that
//! can be used by `.zr-l10n` to only package the entries used, in this mode the dependency Fluent files are edited down to
//! include only the text actually used by the app.
//!
//! The easiest way to get started is to build with the `"l10n_usage_recorder"` Cargo feature, run the app and visit every
//! screen and state that uses localization text. The subset profile is saved to `res/optimization-profiles/zng-ext-l10n.rec.subset` by default,
//! you can change the location by setting the `ZNG_L10N_PROFILE_FILE` env var.
//!
//! The profile is a text file with format:
//!
//! ```txt
//! # comments
//!
//! {dependency}//{file}/{id}.{attribute}
//! ```
//!
//! The dependency is the crate package name, `{file}/{id}.{attribute}` is the same key syntax used by [`l10n!`].
//!
//! The generated file will has a comment header with instruction on how to manually add icons.
//!
//! The profile file can be added to source control, the recorded entries are sorted so changes are stable.
//!
//! # Full API
//!
//! See [`zng_ext_l10n`] for the full localization API.
pub use ;