- #265's German coverage test failed whenever any catalog key, command or control label, or line of What's New lacked German, so every feature PR would have had to ship German. It reports those gaps for every language now (display_label_gaps_are_reported), as the project's other gap test does; docs/localization.md says how release-note lines are matched and that an English copy in a formats catalog marks an untranslated message. - Russian: docs/localization-ru.md, the language lists in docs/localization.md and docs/parity.md, ru tag tests. - What's new: German and Russian UI. Removes a status sentence #265 put in parity.md's Top gaps list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8
This commit is contained in:
parent
9b5e771fd8
commit
0634782fbc
|
|
@ -426,6 +426,9 @@ mod tests {
|
|||
for tag in ["de", "de-DE", "de_AT.UTF-8", "de-CH"] {
|
||||
assert_eq!(Locale::parse_tag(tag), Some(Locale::De), "{tag}");
|
||||
}
|
||||
for tag in ["ru", "ru-RU", "ru_RU.UTF-8", "ru-UA"] {
|
||||
assert_eq!(Locale::parse_tag(tag), Some(Locale::Ru), "{tag}");
|
||||
}
|
||||
assert_eq!(Locale::parse_tag("xx"), None);
|
||||
}
|
||||
|
||||
|
|
@ -584,14 +587,12 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// Every command, control, rule and rename label, and every line of What's New, is a message a
|
||||
/// catalog can translate. What a language lacks is reported, not failed (like
|
||||
/// `catalogs_agree_on_placeholders_and_report_gaps`): a feature PR doesn't have to ship every
|
||||
/// language, and the translations catch up at their own pace.
|
||||
#[test]
|
||||
fn german_catalog_covers_catalogs_commands_controls_and_rule_labels() {
|
||||
let catalog = Locale::De.catalog();
|
||||
for language in Locale::ALL {
|
||||
for key in language.catalog().keys() {
|
||||
assert!(catalog.contains_key(key), "German lacks {key:?}");
|
||||
}
|
||||
}
|
||||
fn display_label_gaps_are_reported() {
|
||||
let mut labels: Vec<&str> = lightcraft_engine::command_specs().iter().map(|spec| spec.label).collect();
|
||||
labels.extend(crate::menus::ui_commands().map(|command| command.1).filter(|label| !Locale::ALL.iter().any(|locale| locale.name() == *label)));
|
||||
labels.extend(lightcraft_develop::CONTROLS.iter().map(|control| control.label));
|
||||
|
|
@ -601,6 +602,7 @@ mod tests {
|
|||
}
|
||||
labels.extend(lightcraft_engine::rename::TOKENS.iter().map(|token| token.meaning));
|
||||
labels.extend(lightcraft_engine::rename::TEMPLATE_NOTES);
|
||||
let ui_labels = labels.len();
|
||||
for line in crate::panels::dialogs::WHATS_NEW.lines().map(str::trim) {
|
||||
if line.is_empty() || line.starts_with("# ") {
|
||||
continue;
|
||||
|
|
@ -608,8 +610,19 @@ mod tests {
|
|||
let label = line.strip_prefix("### ").or_else(|| line.strip_prefix("## ")).or_else(|| line.strip_prefix("- ")).unwrap_or(line);
|
||||
labels.push(label);
|
||||
}
|
||||
for label in labels {
|
||||
assert!(catalog.contains_key(label), "German lacks display label {label:?}");
|
||||
for language in Locale::ALL.iter().filter(|language| **language != Locale::En) {
|
||||
let catalog = language.catalog();
|
||||
let missing: Vec<&&str> = labels[..ui_labels].iter().filter(|label| !catalog.contains_key(**label)).collect();
|
||||
let notes = labels[ui_labels..].iter().filter(|label| !catalog.contains_key(**label)).count();
|
||||
if !missing.is_empty() || notes > 0 {
|
||||
eprintln!("{} lacks {} display label(s) (shown in English): {missing:?}; and {notes} What's New line(s)", language.code(), missing.len());
|
||||
}
|
||||
}
|
||||
// the German catalog, which set out to cover them all, still covers every other catalog
|
||||
let german = Locale::De.catalog();
|
||||
let gaps: Vec<&String> = Locale::ALL.iter().flat_map(|language| language.catalog().keys()).filter(|key| !german.contains_key(key.as_str())).collect();
|
||||
if !gaps.is_empty() {
|
||||
eprintln!("de lacks {} message(s) another catalog has: {gaps:?}", gaps.len());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# LightCraft на русском
|
||||
|
||||
Выберите **Правка → Язык → Русский** или **Настройки → Общие → Язык**. Выбор применяется сразу
|
||||
и сохраняется для следующего запуска (`language: "ru"` в `ui.json`).
|
||||
|
||||
Русский каталог охватывает меню, сочетания клавиш, библиотеку, обработку, кадрирование, маски,
|
||||
импорт и экспорт, настройки, встроенные стили и профили, историю, даты и сообщения о ходе работы.
|
||||
Технические ошибки из нижних уровней, как и в других языках, остаются на английском; строки, которых
|
||||
ещё нет в каталоге, показываются по-английски.
|
||||
|
||||
Имена файлов, идентификаторы команд, шаблоны и названные вами альбомы, стили и метаданные не
|
||||
переводятся. Шрифт интерфейса Inter содержит кириллицу, дополнительные шрифты не нужны.
|
||||
|
||||
## Сопровождение и проверка
|
||||
|
||||
Постоянные тексты: `crates/ui-egui/locales/ru.json`. Сообщения со значениями:
|
||||
`crates/ui-egui/locales/ru-formats.json`. Английские ключи не меняются; заполнители проверяются
|
||||
компилятором Rust при сборке. Английские окончания множественного числа скрываются через `{:.0}`.
|
||||
|
||||
```sh
|
||||
cargo test -p lightcraft-ui-egui i18n::tests -- --nocapture
|
||||
LIGHTCRAFT_LANGUAGE=ru lightcraft-cli snapshot --demo -o russian.png --size 1600x1000
|
||||
```
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
The interface ships in the language it is written in (English) plus every language in the table in
|
||||
`crates/ui-egui/src/i18n.rs`. Today that is English, Simplified Chinese (`zh-hans`), Traditional
|
||||
Chinese (`zh-hant`, Taiwan), Japanese (`ja`), Brazilian Portuguese (`pt-br`) and German (`de`). This file is the reference for **adding or maintaining a language**; the per-language notes
|
||||
Chinese (`zh-hant`, Taiwan), Japanese (`ja`), Brazilian Portuguese (`pt-br`), German (`de`) and Russian (`ru`). This file is the reference for **adding or maintaining a language**; the per-language notes
|
||||
are in [`localization-zh-hans.md`](localization-zh-hans.md), [`localization-zh-hant.md`](localization-zh-hant.md),
|
||||
[`localization-ja.md`](localization-ja.md), [`localization-pt-br.md`](localization-pt-br.md) and
|
||||
[`localization-de.md`](localization-de.md).
|
||||
[`localization-ja.md`](localization-ja.md), [`localization-pt-br.md`](localization-pt-br.md),
|
||||
[`localization-de.md`](localization-de.md) and [`localization-ru.md`](localization-ru.md).
|
||||
|
||||
## Adding a language
|
||||
|
||||
|
|
@ -14,7 +14,8 @@ are in [`localization-zh-hans.md`](localization-zh-hans.md), [`localization-zh-h
|
|||
English source strings and must stay byte-for-byte identical. The plain catalog may grow at its
|
||||
own pace: a message it lacks shows in English, and `cargo test -p lightcraft-ui-egui i18n::tests
|
||||
-- --nocapture` lists the gaps (it fails on an empty entry or mismatched placeholders). The
|
||||
formats catalog must carry every message (the build fails otherwise), including the date
|
||||
formats catalog must carry every message (the build fails otherwise; a message copied in English,
|
||||
value equal to its key, is an untranslated placeholder until someone translates it), including the date
|
||||
patterns (`{year}`, `{month} {year}`, `{weekday}, {day} {month} {year}`…) that date headings and
|
||||
capture times use in every language but English; weekday names go in the plain catalog.
|
||||
2. **Add the table entry** in `crates/ui-egui/src/i18n.rs`:
|
||||
|
|
@ -27,6 +28,7 @@ are in [`localization-zh-hans.md`](localization-zh-hans.md), [`localization-zh-h
|
|||
Ja, "ja", "日本語", "Jpan", include_str!("../locales/ja.json");
|
||||
PtBr, "pt-br", "Português (Brasil)", "Latn", include_str!("../locales/pt-br.json");
|
||||
De, "de", "Deutsch", "Latn", include_str!("../locales/de.json");
|
||||
Ru, "ru", "Русский", "Cyrl", include_str!("../locales/ru.json");
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -87,3 +89,11 @@ The settings file stores the BCP-47 code, never the Rust variant name, so a lang
|
|||
in code without invalidating anyone's saved preference. `LIGHTCRAFT_LANGUAGE` accepts what a system
|
||||
locale looks like (`zh_Hans`, `zh-CN`, `en_US`, `ja_JP.UTF-8`) and falls back to the base language
|
||||
when a region has no dedicated entry.
|
||||
|
||||
## Release notes
|
||||
|
||||
Help ▸ What's New shows `docs/whats-new.md`, and each of its lines (a heading, or one wrapped line of a
|
||||
bullet, without the `- ` or `### `) is looked up like any message, so a catalog can translate the notes
|
||||
line by line (German does). A line that is re-wrapped or edited no longer matches its translation and
|
||||
shows in English until the catalog is updated; untranslated lines are reported by
|
||||
`display_label_gaps_are_reported`, never failed.
|
||||
|
|
|
|||
|
|
@ -84,8 +84,6 @@ Take the first one nobody is working on.
|
|||
Settings → Performance → Preview size (2,560 px by default). Render visible regions at native resolution so
|
||||
100% can reliably show original-pixel detail on larger photos.
|
||||
|
||||
German UI is available (M16, LR-BEHAV-LOCALIZE); German also translates release notes; remaining localisation gaps are technical errors and other locales.
|
||||
|
||||
## Shortcuts: conflicts and missing bindings
|
||||
|
||||
Compared `plan/lightroom/06-shortcuts.md` (desktop part) with our bindings: command specs in `crates/engine/src/cmd/`,
|
||||
|
|
@ -518,8 +516,8 @@ under the UI command that wraps it (a no-op error); the UI command now wins.
|
|||
| LR-BEHAV-EMPTY-STATES | Empty states | P1 | ✅ | `crates/ui-egui/src/panels/mod.rs` (`empty_message`) | |
|
||||
| LR-BEHAV-TOOLTIPS | Tooltips with shortcuts | P0 | ✅ | `crates/ui-egui/src/panels/bottombar.rs` | |
|
||||
| LR-BEHAV-ACCESS | Accessibility | P2 | 🟡 | `crates/ui-egui/src/widgets.rs`, `crates/ui-egui/src/panels/grid.rs` | AccessKit (VoiceOver / Narrator / AT-SPI): sliders announce control and value, buttons / icon buttons / dropdowns / section headers / sources their labels and state, grid thumbnails file name, rating, flag and label; the canvas tools (crop, masks) are pointer-only; not audited with a screen reader |
|
||||
| LR-BEHAV-LOCALE | Language options | P2 | ✅ | `cmd:app.language.english`, `cmd:app.language.simplifiedChinese`, `cmd:app.language.traditionalChinese`, `cmd:app.language.japanese`, `cmd:app.language.portuguese`, `cmd:app.language.german` | Edit > Language lists every language in the table and the active one is checked; shared with Settings > General (docs/localization.md) |
|
||||
| LR-BEHAV-LOCALIZE | Localisation | P2 | 🟡 | `crates/ui-egui/src/i18n.rs`, `crates/ui-egui/locales/`, `docs/localization.md`, `docs/localization-zh-hans.md`, `docs/localization-zh-hant.md`, `docs/localization-ja.md`, `docs/localization-pt-br.md`, `docs/localization-de.md` | One table entry per language (code, endonym, ISO 15924 script, catalog): English, Simplified Chinese, Traditional Chinese (Taiwan), Japanese, Brazilian Portuguese and German UI with a persisted language preference and craft-fonts regular/bold faces per script; menus, edit, crop/masks, settings, import/export and primary progress messages. Adding a language is a table entry plus two catalogs. Stock presets/profiles, history steps, library headings, date headings and capture times are localised (user-named items stay verbatim); the German catalog covers every existing catalog key, command/control label and rule-editor label; the other catalogs fall back to English for messages they lack. Technical errors remain English; release notes are translated in German; Traditional Chinese borrows the Simplified Chinese face (no TC face in craft-fonts yet), and the web build embeds only the Japanese face. |
|
||||
| LR-BEHAV-LOCALE | Language options | P2 | ✅ | `cmd:app.language.english`, `cmd:app.language.simplifiedChinese`, `cmd:app.language.traditionalChinese`, `cmd:app.language.japanese`, `cmd:app.language.portuguese`, `cmd:app.language.german`, `cmd:app.language.russian` | Edit > Language lists every language in the table and the active one is checked; shared with Settings > General (docs/localization.md) |
|
||||
| LR-BEHAV-LOCALIZE | Localisation | P2 | 🟡 | `crates/ui-egui/src/i18n.rs`, `crates/ui-egui/locales/`, `docs/localization.md`, `docs/localization-zh-hans.md`, `docs/localization-zh-hant.md`, `docs/localization-ja.md`, `docs/localization-pt-br.md`, `docs/localization-de.md`, `docs/localization-ru.md` | One table entry per language (code, endonym, ISO 15924 script, catalog): English, Simplified Chinese, Traditional Chinese (Taiwan), Japanese, Brazilian Portuguese, German and Russian UI with a persisted language preference and craft-fonts regular/bold faces per script; menus, edit, crop/masks, settings, import/export and primary progress messages. Adding a language is a table entry plus two catalogs. Stock presets/profiles, history steps, library headings, date headings and capture times are localised (user-named items stay verbatim); the German catalog covers every existing catalog key, command/control label and rule-editor label; the other catalogs fall back to English for messages they lack. Technical errors remain English; release notes are translated in German; Traditional Chinese borrows the Simplified Chinese face (no TC face in craft-fonts yet), and the web build embeds only the Japanese face. |
|
||||
| LR-BEHAV-LEARN | Tutorials | OOS | 🚫 | | |
|
||||
| LR-BEHAV-WHATSNEW | What's new | P2 | ✅ | `cmd:app.whatsNew`, `docs/whats-new.md` | Help ▸ What's New: release highlights |
|
||||
| LR-BEHAV-AI-EA | Early-access badges | P2 | ⬜ | | |
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
- The Import Photos review opens bigger and can be resized; its photo grid fills it (issue #337). Shift-click checks
|
||||
or unchecks a range of photos (issue #338).
|
||||
|
||||
### Languages
|
||||
- The interface is available in German and Russian (Edit ▸ Language), alongside English, Chinese (Simplified and
|
||||
Traditional), Japanese and Brazilian Portuguese.
|
||||
|
||||
### Reliability
|
||||
- On macOS, single-key shortcuts that appear in the menu bar now work: E, C, H, M, ⇧P, I, K, D, ratings 0–5,
|
||||
labels 6–9, P / U and the rest did nothing, because macOS only passes ⌘ / ⌃ combinations and function keys
|
||||
|
|
|
|||
Loading…
Reference in New Issue