T48j Docs · Internationalization: catalogs and messages, locales, right to left; the i18n example compiles
This commit is contained in:
4 files changed
+130
-1
No files matched your search
@@ -115,3 +115,7 @@ path = "examples/docs/icons.rs"
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "docs_overlays"
|
name = "docs_overlays"
|
||||||
path = "examples/docs/overlays.rs"
|
path = "examples/docs/overlays.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "docs_i18n"
|
||||||
|
path = "examples/docs/i18n.rs"
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
use ely_gpui_component::{
|
||||||
|
Assets,
|
||||||
|
buttons::Button,
|
||||||
|
i18n::{Direction, I18n},
|
||||||
|
primitives::FocusScope,
|
||||||
|
theme::ActiveTheme,
|
||||||
|
};
|
||||||
|
use gpui::{
|
||||||
|
App, AppContext, Context, FocusHandle, IntoElement, ParentElement, Render, Styled, Window,
|
||||||
|
WindowOptions, div,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Words {
|
||||||
|
focus: FocusHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for Words {
|
||||||
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let theme = cx.theme();
|
||||||
|
let i18n = cx.global::<I18n>();
|
||||||
|
let locale = i18n.locale();
|
||||||
|
let greeting = i18n.text("greeting", &[("name", "Ada")]);
|
||||||
|
let files = i18n.text("files", &[("count", &locale.number(1234.0, 0))]);
|
||||||
|
let next = if locale.direction == Direction::Rtl {
|
||||||
|
"en-US"
|
||||||
|
} else {
|
||||||
|
"he-IL"
|
||||||
|
};
|
||||||
|
let row = locale
|
||||||
|
.direction
|
||||||
|
.row(div())
|
||||||
|
.gap_4()
|
||||||
|
.child(greeting)
|
||||||
|
.child(files);
|
||||||
|
FocusScope::new(&self.focus)
|
||||||
|
.root()
|
||||||
|
.size_full()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_4()
|
||||||
|
.p_8()
|
||||||
|
.bg(theme.colors.bg)
|
||||||
|
.text_color(theme.colors.fg)
|
||||||
|
.child(row)
|
||||||
|
.child(
|
||||||
|
Button::new("locale", i18n.text("switch", &[]))
|
||||||
|
.on_click(move |_, _, cx| I18n::set_locale(next, cx)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
gpui_platform::application()
|
||||||
|
.with_assets(Assets)
|
||||||
|
.run(|cx: &mut App| {
|
||||||
|
ely_gpui_component::init(cx);
|
||||||
|
cx.set_global(
|
||||||
|
I18n::new("en-US")
|
||||||
|
.catalog(
|
||||||
|
"en-US",
|
||||||
|
&[
|
||||||
|
("greeting", "Hello, {name}"),
|
||||||
|
("files", "{count} files"),
|
||||||
|
("switch", "עברית"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.catalog(
|
||||||
|
"he-IL",
|
||||||
|
&[
|
||||||
|
("greeting", "שלום, {name}"),
|
||||||
|
("files", "{count} קבצים"),
|
||||||
|
("switch", "English"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
cx.open_window(WindowOptions::default(), |window, cx| {
|
||||||
|
cx.new(|cx| {
|
||||||
|
let focus = cx.focus_handle();
|
||||||
|
window.focus(&focus, cx);
|
||||||
|
Words { focus }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.expect("window failed to open");
|
||||||
|
cx.activate(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import { Marked, type Tokens } from "marked";
|
|||||||
const at = (relative: string) => fileURLToPath(new URL(relative, import.meta.url));
|
const at = (relative: string) => fileURLToPath(new URL(relative, import.meta.url));
|
||||||
|
|
||||||
/** The guides in reading order, by file name. */
|
/** The guides in reading order, by file name. */
|
||||||
const ORDER = ["introduction", "installation", "getting-started", "theme", "focus", "motion", "icons", "overlays"];
|
const ORDER = ["introduction", "installation", "getting-started", "theme", "focus", "motion", "icons", "overlays", "i18n"];
|
||||||
|
|
||||||
export interface Guide {
|
export interface Guide {
|
||||||
/** The path segment; the introduction has none. */
|
/** The path segment; the introduction has none. */
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Internationalization
|
||||||
|
|
||||||
|
`i18n::I18n` holds an app's messages in catalogs, one per locale, and the locale on show. The app sets it as a GPUI global; views read their words from it.
|
||||||
|
|
||||||
|
## Catalogs and messages
|
||||||
|
|
||||||
|
| Call | Does |
|
||||||
|
| --- | --- |
|
||||||
|
| `I18n::new("en-US")` | Starts with the locale on show. |
|
||||||
|
| `.catalog(tag, &[(key, message)])` | Adds a locale's messages. Every catalog holds the first one's keys, no more and no fewer, or it panics naming them. |
|
||||||
|
| `cx.set_global(i18n)` | Makes it the app's. |
|
||||||
|
| `cx.global::<I18n>().text(key, &[(name, value)])` | The message for `key`, each `{name}` filled. A missing key, argument or catalog panics. |
|
||||||
|
| `I18n::set_locale(tag, cx)` | Shows another locale in every window, logs the change, and panics on a locale without a catalog. |
|
||||||
|
|
||||||
|
Ely's components take their words from the app: a `Button`'s label, a `Dialog`'s title. Some carry English words of their own, such as a toast's Undo, the Cancel of `ConfirmDialog` and `PromptDialog`, and the Close tip on a fullscreen dialog.
|
||||||
|
|
||||||
|
## Locales
|
||||||
|
|
||||||
|
`LOCALES` lists the seven locales Ely formats for: en-US, en-GB, de-DE, fr-FR, ja-JP, zh-CN and he-IL. `Locale::of(tag)` returns one and panics on a tag outside the list.
|
||||||
|
|
||||||
|
| Item | Holds |
|
||||||
|
| --- | --- |
|
||||||
|
| `tag`, `name` | `"de-DE"`, `"Deutsch"`. |
|
||||||
|
| `number(value, decimals)` | The value with the locale's separators: `number(1234.5, 1)` is `1.234,5` in de-DE. |
|
||||||
|
| `date(at, zone)` | A numeric date in CLDR's short form. |
|
||||||
|
| `direction` | `Direction::Ltr` or `Direction::Rtl`. |
|
||||||
|
|
||||||
|
## Right to left
|
||||||
|
|
||||||
|
He-IL reads right to left. `direction.row(element)` lays a flex row from the reading side, and `direction.align(element)` aligns text to that side.
|
||||||
|
|
||||||
|
GPUI sets no paragraph direction, so a line runs the way its first strong letter does. `text` leads a right-to-left message with a right-to-left mark, so its words suit labels, not a field's value. `number` leads a right-to-left minus with a left-to-right mark, as CLDR does. GPUI wraps right-to-left lines in the wrong order, so keep such text on one line, an `Ellipsis`.
|
||||||
|
|
||||||
|
## Two locales
|
||||||
|
|
||||||
|
The example greets in English and Hebrew, formats a count by the locale, mirrors its row, and switches locale with a button.
|
||||||
|
|
||||||
|
```rust example=i18n
|
||||||
|
```
|
||||||
Reference in new issue
Block a user