Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ ropey = { version = "=2.0.0-beta.1", features = [
"metric_lines_lf",
"metric_utf16",
] }
rust-i18n = "4"
rust-i18n = "4.2.0"
schemars = "1"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1"
Expand Down
44 changes: 44 additions & 0 deletions crates/story/locales/ui.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
_version: 2
gpui_component:
Calendar:
week.0:
fr: Di
week.1:
fr: Lu
week.2:
fr: Ma
week.3:
fr: Me
week.4:
fr: Je
week.5:
fr: Ve
week.6:
fr: Sa
month.January:
fr: Janvier
month.February:
fr: Février
month.March:
fr: Mars
month.April:
fr: Avril
month.May:
fr: Mai
month.June:
fr: Juin
month.July:
fr: Juillet
month.August:
fr: Août
month.September:
fr: Septembre
month.October:
fr: Octobre
month.November:
fr: Novembre
month.December:
fr: Décembre
DatePicker:
placeholder:
fr: Sélectionner une date
1 change: 1 addition & 0 deletions crates/story/src/app_menus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ fn language_menu(_: &App) -> MenuItem {
items: vec![
MenuItem::action("English", SelectLocale("en".into())).checked(locale == "en"),
MenuItem::action("简体中文", SelectLocale("zh-CN".into())).checked(locale == "zh-CN"),
MenuItem::action("Français", SelectLocale("fr".into())).checked(locale == "fr"),
],
disabled: false,
})
Expand Down
20 changes: 20 additions & 0 deletions crates/story/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub use crate::title_bar::AppTitleBar;
pub use gallery::Gallery;
pub use stories::*;

rust_i18n::i18n!("locales", fallback = "en");

#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = story, no_json)]
pub struct SelectScrollbarShow(ScrollbarShow);
Expand Down Expand Up @@ -180,6 +182,7 @@ pub fn init(cx: &mut App) {
.try_init();
}

rust_i18n::extend!(gpui_component);
gpui_component::init(cx);
AppState::init(cx);
themes::init(cx);
Expand Down Expand Up @@ -716,3 +719,20 @@ impl Render for StoryRoot {
)
}
}

#[cfg(test)]
mod tests {
#[test]
fn extends_component_translations_with_story_locales() {
rust_i18n::extend!(gpui_component);

assert_eq!(
gpui_component::_rust_i18n_try_translate("fr", "Calendar.month.January"),
Some("Janvier".into())
);
assert_eq!(
gpui_component::_rust_i18n_try_translate("en", "Calendar.month.January"),
Some("January".into())
);
}
}
92 changes: 92 additions & 0 deletions docs/docs/i18n.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
title: Custom I18n
description: Add or override GPUI Component translations from your application.
order: -3
---

# Custom I18n

GPUI Component includes translations for its components. Applications can add a language or override individual translations without copying the complete built-in locale files.

This feature requires `rust-i18n` 4.2 or later.

## Add the dependency

Add `rust-i18n` to the application crate that owns your locale files:

```toml
[dependencies]
gpui-component = "0.5"
rust-i18n = "4.2"
```

## Create an application locale file

Create `locales/ui.yml` in your application crate. Put component translations under the `gpui_component` namespace:

```yaml
_version: 2
gpui_component:
Calendar:
week.0:
fr: Di
month.January:
fr: Janvier
DatePicker:
placeholder:
fr: Sélectionner une date
```

The namespace must be `gpui_component`, matching the Rust crate name `gpui-component` with hyphens converted to underscores. Translation keys can be found in [GPUI Component's built-in locale file](https://github.com/longbridge/gpui-component/blob/main/crates/ui/locales/ui.yml).

## Register the extension

Initialize the application's locales at the crate root:

```rust
rust_i18n::i18n!("locales", fallback = "en");
```

Register them before initializing GPUI Component:

```rust
app.run(move |cx| {
rust_i18n::extend!(gpui_component);
gpui_component::init(cx);

// Open windows and initialize the rest of the application.
});
```

Call `extend!` only once during application startup.

## Lookup priority

The application's translations take priority over the component's built-in translations:

```text
Application locales (locales/ui.yml)
│ key not found
GPUI Component built-in locales
```

This deep-merge behavior means that:

- A new locale, such as `fr`, can contain only the keys your application needs.
- A translation with the same locale and key overrides the built-in value.
- Keys not supplied by the application continue to use the built-in value.
- Future GPUI Component translations remain available without being copied into the application.

For example, defining only `gpui_component.Calendar.month.January.en` changes January's English label while all other English calendar labels still come from GPUI Component.

## Select a locale

Use rust-i18n to change the active locale:

```rust
rust_i18n::set_locale("fr");
```

Components will then resolve their translated labels using the selected locale and the priority described above.
92 changes: 92 additions & 0 deletions docs/zh-CN/docs/i18n.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
title: 自定义国际化
description: 在应用中新增或覆盖 GPUI Component 的翻译。
order: -3
---

# 自定义国际化

GPUI Component 为组件提供了内置翻译。应用可以新增语言或覆盖个别翻译,而不需要复制完整的内置 locale 文件。

此功能需要 `rust-i18n` 4.2 或更高版本。

## 添加依赖

在持有 locale 文件的应用 crate 中添加 `rust-i18n`:

```toml
[dependencies]
gpui-component = "0.5"
rust-i18n = "4.2"
```

## 创建应用 locale 文件

在应用 crate 中创建 `locales/ui.yml`,并将组件翻译放在 `gpui_component` namespace 下:

```yaml
_version: 2
gpui_component:
Calendar:
week.0:
fr: Di
month.January:
fr: Janvier
DatePicker:
placeholder:
fr: Sélectionner une date
```

namespace 必须是 `gpui_component`,它对应 Rust crate 名称 `gpui-component`,其中连字符转换为下划线。可在 [GPUI Component 内置 locale 文件](https://github.com/longbridge/gpui-component/blob/main/crates/ui/locales/ui.yml)中查看可用的翻译 key。

## 注册扩展

首先在应用的 crate root 初始化 locales:

```rust
rust_i18n::i18n!("locales", fallback = "en");
```

然后在初始化 GPUI Component 之前注册扩展:

```rust
app.run(move |cx| {
rust_i18n::extend!(gpui_component);
gpui_component::init(cx);

// 打开窗口并初始化应用的其他部分。
});
```

应用启动期间只需调用一次 `extend!`。

## 查找优先级

应用提供的翻译优先于组件的内置翻译:

```text
应用 locales(locales/ui.yml)
│ 未找到 key
GPUI Component 内置 locales
```

这种 deep merge 行为意味着:

- 新语言(例如 `fr`)只需提供应用需要的 key。
- locale 和 key 都相同时,应用提供的翻译会覆盖内置值。
- 应用未提供的 key 会继续使用组件内置值。
- GPUI Component 后续新增的翻译无需复制,也会自动可用。

例如,只定义 `gpui_component.Calendar.month.January.en` 会修改一月份的英文标签,其他英文日历标签仍然来自 GPUI Component。

## 切换语言

使用 rust-i18n 修改当前 locale:

```rust
rust_i18n::set_locale("fr");
```

组件随后会按照上述优先级,使用当前 locale 查找翻译文本。
Loading