mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
i18n: missing translations never fail; xtask ci prints one report (#3856)
* i18n tests: report missing translations instead of failing The scans for untranslated menu strings, tl! literals, undo labels, menu labels and status messages print what a complete language lacks. A missing translation shows the English text until a translator adds the row, so it no longer fails the gate. * i18n: one untranslated report in xtask ci; drop one-language wording tests `i18n::tests::untranslated_report` lists, per language, the menu strings, tl! literals, undo step names and templates, menu labels, render-site labels, status and error messages, PostScript error meanings and default swatch names its catalog lacks; strings every language lacks are listed once. It never fails on a gap, only on a scan that finds too few strings. `cargo xtask ci` runs it in an `i18n-report` step, and `cargo xtask i18n-report` runs it alone. The five scan tests, the render-site and swatch coverage tests and untranslated_messages.txt fold into the report. Tests that pinned one language's wording are gone; where they also checked code (lookups, plural rules, locale tags, menu and History step names, engine parameter errors), that check stays and compares with the catalog lookup.
This commit is contained in:
@@ -243,12 +243,4 @@ mod tests {
|
||||
assert_eq!(fileio::format_for_name(&name).map(|f| f.label), Some(label.as_str()), "{name}");
|
||||
}
|
||||
}
|
||||
|
||||
/// The menu's label is translated in every language with complete menus.
|
||||
#[test]
|
||||
fn the_label_is_translated() {
|
||||
for lang in i18n::Lang::all().filter(|l| l.complete_menus()) {
|
||||
assert!(i18n::has(lang, "Format:"), "{}", lang.code());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -736,27 +736,6 @@ mod tests {
|
||||
assert_eq!((d.str("state"), &d.fields["chosen"]), ("done".to_string(), &json!([])));
|
||||
}
|
||||
|
||||
/// The status lines are translated with the reasons in them, and so are the reasons a search
|
||||
/// failed and a file wasn't copied, which the code holds as constants.
|
||||
#[test]
|
||||
fn the_status_lines_read_in_the_interface_language() {
|
||||
let fr = crate::i18n::Lang::from_code("fr").unwrap();
|
||||
let shown = crate::i18n::message(fr, "2 font file(s) are now in /Fonts; 1 couldn't be added: a.otf: larger than 256 MB");
|
||||
assert!(shown.contains("se trouvent maintenant dans /Fonts") && shown.contains("a.otf") && shown.contains("plus de 256 Mo"), "{shown}");
|
||||
for code in ["de", "es", "fr", "hu", "it", "ka", "pl", "ru", "sv"] {
|
||||
let lang = crate::i18n::Lang::from_code(code).unwrap();
|
||||
for s in [
|
||||
"every search thread stopped on an internal error (please report this bug)",
|
||||
"not a file",
|
||||
"not a font file",
|
||||
"the files come to more than 1 GB: add fewer at a time",
|
||||
"every numbered name for it is taken in the Fonts folder",
|
||||
] {
|
||||
assert_ne!(crate::i18n::message(lang, s), s, "{code}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_web_says_how_many_fonts_are_missing() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
|
||||
@@ -236,21 +236,6 @@ fn form_dialog_axes_are_dropdowns() {
|
||||
}
|
||||
}
|
||||
|
||||
/// The axis choices are translated in the "axis" context: in Japanese the plain Horizontal and
|
||||
/// Vertical are the type orientations (horizontal and vertical writing).
|
||||
#[test]
|
||||
fn axis_choices_read_as_axes_in_every_complete_language() {
|
||||
use crate::i18n::{LANGUAGES, Lang, tr, tr_ctx};
|
||||
let ja = Lang::from_code("ja").unwrap();
|
||||
assert_eq!(tr_ctx(ja, "axis", "Horizontal"), "水平方向");
|
||||
assert_ne!(tr_ctx(ja, "axis", "Horizontal"), tr(ja, "Horizontal"));
|
||||
for l in LANGUAGES.iter().filter(|l| l.complete_menus) {
|
||||
for s in ["Horizontal", "Vertical", "Both"] {
|
||||
assert!(l.source.lines().any(|line| line.starts_with(&format!("axis\t{s}\t"))), "{}: no axis row for {s}", l.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Offset Path previews on the canvas; Cancel rolls the preview back.
|
||||
#[test]
|
||||
fn offset_path_previews() {
|
||||
@@ -713,7 +698,7 @@ fn simplify_dialog_show_original_path_outlines_the_paths_before_the_preview() {
|
||||
#[test]
|
||||
fn printer_mark_labels_are_distinct_from_language_names() {
|
||||
let cs = crate::i18n::Lang::from_code("cs").unwrap();
|
||||
assert_eq!(save_pdf::choice_label(cs, "marks.kind", "Japanese"), "Japonské");
|
||||
assert_eq!(save_pdf::choice_label(cs, "language", "Japanese"), "Japonština");
|
||||
assert_eq!(save_pdf::choice_label(cs, "marks.kind", "Japanese"), crate::i18n::tr_ctx(cs, "mark-style", "Japanese"));
|
||||
assert_eq!(save_pdf::choice_label(cs, "language", "Japanese"), crate::i18n::tr(cs, "Japanese"));
|
||||
assert_eq!(save_pdf::choice_label(crate::i18n::Lang::EN, "marks.kind", "Japanese"), "Japanese");
|
||||
}
|
||||
|
||||
@@ -865,7 +865,7 @@ mod tests {
|
||||
assert_eq!(super::script_label(3), "Thai");
|
||||
let zh = crate::i18n::Lang::from_code("zh-hans").unwrap();
|
||||
crate::i18n::set_current(zh);
|
||||
assert_eq!(super::script_label(1), "汉字");
|
||||
assert_eq!(super::script_label(1), crate::i18n::tr_ctx(zh, "font-script", "Japanese"));
|
||||
crate::i18n::set_current(crate::i18n::Lang::EN);
|
||||
assert_eq!(super::script_label(1), "Japanese");
|
||||
}
|
||||
|
||||
@@ -1350,7 +1350,7 @@ font-technology Variable Променливи
|
||||
Clear History Изчистване на историята
|
||||
Set History Limit… Задаване на ограничение на историята…
|
||||
History States Състояния на историята
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Прилагане на набор от данни
|
||||
Cancel Pattern Editing Отказ от редактирането на мостра
|
||||
Copy Appearance Копиране на външния вид
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -13,7 +13,7 @@
|
||||
# režim míchání (blending mode), přednastavení (preset), rozložit (expand), uvolnit (release),
|
||||
# vektorizace obrazu (Image Trace). Counts without a plural row read "Label: {n}".
|
||||
#
|
||||
# Coverage: every menu string, tl! literal, plural, status and error message (enforced in tests:
|
||||
# Coverage: every menu string, tl! literal, plural, status and error message (`untranslated_report` lists gaps:
|
||||
# `complete_menus` and `COMPLETE_MESSAGES`). Left in English on purpose (`MENU_KEEP_AS_IS` in the
|
||||
# tests): the product name, language names, OpenType, the built-in workspace names and the
|
||||
# bracketed perspective grid preset names. Keep placeholders, ellipses and the leading/trailing
|
||||
@@ -1537,7 +1537,7 @@ lighting Light Světlo
|
||||
Clear History Vymazat historii
|
||||
Set History Limit… Nastavit limit historie…
|
||||
Open a document to see its history. Otevřete dokument, abyste viděli jeho historii.
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Použít sadu dat
|
||||
Cancel Pattern Editing Zrušit úpravy vzorku
|
||||
Copy Appearance Kopírovat vzhled
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -15,7 +15,7 @@
|
||||
# error messages keep the case of their English first word, as they are often shown after "…: ".
|
||||
#
|
||||
# Coverage: the whole interface — every menu string and every `tl!` literal (`complete_menus`,
|
||||
# tested) — and the status and error messages. Left in English on purpose (`MENU_KEEP_AS_IS` in the
|
||||
# `untranslated_report` lists gaps) — and the status and error messages. Left in English on purpose (`MENU_KEEP_AS_IS` in the
|
||||
# tests): the product name, language names, OpenType, the built-in workspace names and the bracketed
|
||||
# perspective grid preset names; `de-DE`, `de-AT`, `de-CH` and every other `de-*` locale resolve here.
|
||||
# Photoshop-style raster effects (Effect menu, their dialogs).
|
||||
@@ -1371,7 +1371,7 @@ file-filter Templates Vorlagen
|
||||
Clear History Protokoll löschen
|
||||
Set History Limit… Protokollgrenze festlegen…
|
||||
History States Protokollschritte
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Datensatz anwenden
|
||||
Area Type Flächentext
|
||||
Bloat Aufblasen
|
||||
@@ -3678,7 +3678,7 @@ theme Appearance Erscheinungsbild
|
||||
|
||||
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
|
||||
# stays English for agents and tests. `{_1}`, `{_2}` … stand for the `{}` of the format string, in order
|
||||
# (`complete_languages_translate_every_message` lists any that are missing). Command ids, parameter
|
||||
# (`untranslated_report` lists any that are missing). Command ids, parameter
|
||||
# names and values in backticks or code (`colorModel rgb`, `document.export`) stay as they are.
|
||||
@msg no library `{id}` keine Bibliothek `{id}`
|
||||
@msg select some type first wählen Sie zuerst Text aus
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1313,7 +1313,7 @@ font-technology Variable Μεταβλητές
|
||||
Clear History Εκκαθάριση ιστορικού
|
||||
Set History Limit… Ορισμός ορίου ιστορικού…
|
||||
History States Καταστάσεις ιστορικού
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Εφαρμογή συνόλου δεδομένων
|
||||
Cancel Pattern Editing Ακύρωση επεξεργασίας μοτίβου
|
||||
Copy Appearance Αντιγραφή εμφάνισης
|
||||
|
||||
|
@@ -9,7 +9,7 @@
|
||||
# sentences address the user as "tú". Shortcut keys keep their key-cap names (Shift, Alt, Ctrl, Esc).
|
||||
#
|
||||
# Coverage: the whole interface — every menu string and every `tl!` literal (`complete_menus`,
|
||||
# tested). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# `untranslated_report` lists gaps). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# names, OpenType, the built-in workspace names and the bracketed perspective grid preset names.
|
||||
# Photoshop-style raster effects (Effect menu, their dialogs).
|
||||
Glowing Edges… Bordes resplandecientes…
|
||||
@@ -1385,7 +1385,7 @@ font-technology Variable Variables
|
||||
Clear History Borrar historial
|
||||
Set History Limit… Definir límite de historial…
|
||||
History States Estados de historial
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Aplicar conjunto de datos
|
||||
Cancel Pattern Editing Cancelar edición de motivo
|
||||
Copy Appearance Copiar apariencia
|
||||
@@ -3754,7 +3754,7 @@ axis Both Ambos
|
||||
|
||||
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
|
||||
# stays English for agents and tests. `{_1}`, `{_2}` … stand for the `{}` of the format string, in order
|
||||
# (`complete_languages_translate_every_message` lists any that are missing). Command ids, parameter
|
||||
# (`untranslated_report` lists any that are missing). Command ids, parameter
|
||||
# names and values in backticks or code (`colorModel rgb`, `document.export`) stay as they are.
|
||||
@msg no library `{id}` no hay ninguna biblioteca `{id}`
|
||||
@msg select some type first selecciona primero algún texto
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1511,7 +1511,7 @@ lighting Light Valo
|
||||
Clear History Tyhjennä historia
|
||||
Set History Limit… Aseta historian raja…
|
||||
Open a document to see its history. Avaa asiakirja, niin näet sen historian.
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Käytä tietojoukkoa
|
||||
Cancel Pattern Editing Peruuta kuvion muokkaus
|
||||
Copy Appearance Kopioi ulkoasu
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -10,7 +10,7 @@
|
||||
# « », the typographic apostrophe ’. Shortcut keys use their French key-cap names (Maj, Ctrl, Alt, Échap).
|
||||
#
|
||||
# Coverage: the whole interface — every menu string and every `tl!` literal (`complete_menus`,
|
||||
# tested). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# `untranslated_report` lists gaps). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# names, OpenType, the built-in workspace names and the bracketed perspective grid preset names.
|
||||
# Photoshop-style raster effects (Effect menu, their dialogs).
|
||||
Glowing Edges… Contour lumineux…
|
||||
@@ -1384,7 +1384,7 @@ font-technology Variable Variables
|
||||
Clear History Effacer l’historique
|
||||
Set History Limit… Définir la limite de l’historique…
|
||||
History States États de l’historique
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Appliquer le jeu de données
|
||||
Cancel Pattern Editing Annuler la modification du motif
|
||||
Copy Appearance Copier l’aspect
|
||||
@@ -3751,7 +3751,7 @@ axis Both Les deux
|
||||
|
||||
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
|
||||
# stays English for agents and tests. `{_1}`, `{_2}` … stand for the `{}` of the format string, in order
|
||||
# (`complete_languages_translate_every_message` lists any that are missing). Command ids, parameter
|
||||
# (`untranslated_report` lists any that are missing). Command ids, parameter
|
||||
# names and values in backticks or code (`colorModel rgb`, `document.export`) stay as they are.
|
||||
@msg no library `{id}` aucune bibliothèque `{id}`
|
||||
@msg select some type first sélectionnez d’abord du texte
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -18,7 +18,7 @@
|
||||
# English first word, as they are often shown after "…: ".
|
||||
#
|
||||
# Coverage: the whole interface (every menu string and `tl!` literal, `complete_menus`) and the
|
||||
# status and error messages (`COMPLETE_MESSAGES`), enforced by the tests. Left in English on
|
||||
# status and error messages (`COMPLETE_MESSAGES`), gaps listed by `untranslated_report`. Left in English on
|
||||
# purpose (`MENU_KEEP_AS_IS`): the product name, language names, OpenType, the built-in workspace
|
||||
# names and the bracketed perspective grid preset names; `hu-HU` and every other `hu-*` locale
|
||||
# resolve here.
|
||||
@@ -1777,7 +1777,7 @@ file-filter Templates Sablonok
|
||||
Clear History Előzmények törlése
|
||||
Set History Limit… Előzménykorlát beállítása…
|
||||
History States Előzmények lépésszáma
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Adatkészlet alkalmazása
|
||||
Cancel Pattern Editing Mintázatszerkesztés megszakítása
|
||||
Copy Appearance Megjelenés másolása
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -9,7 +9,7 @@
|
||||
# user as "tu". Shortcut keys keep their key-cap names (Shift, Alt, Ctrl, Esc).
|
||||
#
|
||||
# Coverage: the whole interface — every menu string and every `tl!` literal (`complete_menus`,
|
||||
# tested). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# `untranslated_report` lists gaps). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# names, OpenType, the built-in workspace names and the bracketed perspective grid preset names.
|
||||
# Photoshop-style raster effects (Effect menu, their dialogs).
|
||||
Glowing Edges… Bordi luminosi…
|
||||
@@ -1395,7 +1395,7 @@ file-filter Templates Modelli
|
||||
Clear History Cancella cronologia
|
||||
Set History Limit… Imposta limite cronologia…
|
||||
History States Stati cronologia
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Applica set di dati
|
||||
Area Type Testo in area
|
||||
Bloat Gonfia
|
||||
@@ -3794,7 +3794,7 @@ axis Both Entrambi
|
||||
|
||||
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
|
||||
# stays English for agents and tests. `{_1}`, `{_2}` … stand for the `{}` of the format string, in order
|
||||
# (`complete_languages_translate_every_message` lists any that are missing). Command ids, parameter
|
||||
# (`untranslated_report` lists any that are missing). Command ids, parameter
|
||||
# names and values in backticks or code (`colorModel rgb`, `document.export`) stay as they are.
|
||||
@msg no library `{id}` nessuna libreria `{id}`
|
||||
@msg select some type first seleziona prima del testo
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -11,7 +11,7 @@
|
||||
Smoothness 滑らかさ
|
||||
#
|
||||
# Coverage: the whole interface — every menu string and every `tl!` literal (`complete_menus`,
|
||||
# tested) — and the status and error messages (`@msg` rows at the end, `COMPLETE_MESSAGES`, tested).
|
||||
# `untranslated_report` lists gaps) — and the status and error messages (`@msg` rows at the end, `COMPLETE_MESSAGES`, `untranslated_report` lists gaps).
|
||||
# Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# names, OpenType, the built-in workspace names and the bracketed perspective grid preset names.
|
||||
About VectorCraft VectorCraft について
|
||||
@@ -1639,7 +1639,7 @@ font-technology Variable バリアブル
|
||||
Clear History ヒストリーを消去
|
||||
Set History Limit… ヒストリーの上限を設定…
|
||||
History States ヒストリー数
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set データセットを適用
|
||||
Area Type エリア内文字
|
||||
Bloat 膨張
|
||||
@@ -3807,7 +3807,7 @@ theme Appearance 外観
|
||||
|
||||
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
|
||||
# stays English for agents and tests. `{_1}`, `{_2}` … stand for the `{}` of the format string, in order
|
||||
# (`complete_languages_translate_every_message` lists any that are missing). Command ids, parameter
|
||||
# (`untranslated_report` lists any that are missing). Command ids, parameter
|
||||
# names and values in backticks or code (`colorModel rgb`, `document.export`) stay as they are.
|
||||
# Japanese: no final 。 (these are status lines, and reasons that follow a colon), unless the English ends
|
||||
# with a period; a space between Japanese text and an adjacent Latin word, number or {placeholder}.
|
||||
@@ -4654,7 +4654,7 @@ effect Vertical 垂直
|
||||
Define a variable first 先に変数を定義してください
|
||||
Text テキスト
|
||||
Visibility 表示状態
|
||||
# tested). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# `untranslated_report` lists gaps). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# Shared panel docking
|
||||
Activate Panel パネルをアクティブにする
|
||||
Change Panel Layout パネルの配置を変更
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1634,7 +1634,7 @@ font-technology Variable 가변
|
||||
Clear History 히스토리 지우기
|
||||
Set History Limit… 히스토리 한도 설정…
|
||||
History States 히스토리 상태
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set 데이터 세트 적용
|
||||
Area Type 영역 문자
|
||||
Bloat 부풀리기
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -9,8 +9,8 @@
|
||||
//! English text, clean-room, see `zh-hant.tsv`).
|
||||
//! 2. Add one row to [`LANGUAGES`] (code, native name, catalog, plural rule).
|
||||
//!
|
||||
//! That is all: the Preferences dropdown, the system-locale match and the catalog tests (parse,
|
||||
//! placeholders, plural forms, menu coverage) pick it up from the registry.
|
||||
//! That is all: the Preferences dropdown, the system-locale match, the catalog tests (parse,
|
||||
//! placeholders, plural forms) and the untranslated report pick it up from the registry.
|
||||
//!
|
||||
//! # Looking strings up
|
||||
//! - [`t`] / the `tl!` macro: a plain string in the language the UI is drawn in. [`tr`] takes the
|
||||
@@ -41,7 +41,7 @@ pub struct LangInfo {
|
||||
pub source: &'static str,
|
||||
/// Plural form index for a count (English: 0 = one, 1 = other; Chinese: always 0).
|
||||
pub plural: fn(u64) -> usize,
|
||||
/// Must the catalog cover every menu string? (checked by the tests)
|
||||
/// Does the catalog aim to cover every menu string? The untranslated report lists what it lacks.
|
||||
pub complete_menus: bool,
|
||||
catalog: OnceLock<Catalog>,
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#
|
||||
# Plurals: one (1), few (2–4, 22–24, … but not 12–14), many (0, 5–21, 25–31, …).
|
||||
#
|
||||
# Coverage: every menu string, tl! literal, plural, status and error message (enforced in tests:
|
||||
# Coverage: every menu string, tl! literal, plural, status and error message (`untranslated_report` lists gaps:
|
||||
# `complete_menus` and `COMPLETE_MESSAGES`). Left in English on purpose (`MENU_KEEP_AS_IS` in the
|
||||
# tests): the product name, language names, OpenType, the built-in workspace names and the
|
||||
# bracketed perspective grid preset names. Keep placeholders, ellipses and the leading/trailing
|
||||
@@ -1451,7 +1451,7 @@ lighting Light Światło
|
||||
Clear History Wyczyść historię
|
||||
Set History Limit… Ustaw limit historii…
|
||||
Open a document to see its history. Otwórz dokument, aby zobaczyć jego historię.
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Zastosuj zestaw danych
|
||||
Area Type Tekst akapitowy
|
||||
Cancel Pattern Editing Anuluj edycję wzorka
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -19,7 +19,7 @@
|
||||
# commands is kept.
|
||||
#
|
||||
# Coverage: the whole interface (every menu string and every `tl!` literal, `complete_menus`,
|
||||
# tested) and the status and error messages (`@msg` rows, `COMPLETE_MESSAGES`, tested). Left in
|
||||
# `untranslated_report` lists gaps) and the status and error messages (`@msg` rows, `COMPLETE_MESSAGES`, `untranslated_report` lists gaps). Left in
|
||||
# English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language names, OpenType,
|
||||
# the built-in workspace names and the bracketed perspective grid preset names.
|
||||
#
|
||||
@@ -1191,7 +1191,7 @@
|
||||
Clear History Limpar Histórico
|
||||
Set History Limit… Definir Limite do Histórico…
|
||||
History States Estados do Histórico
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Aplicar Conjunto de Dados
|
||||
Cancel Pattern Editing Cancelar Edição de Padrão
|
||||
Copy Appearance Copiar Aparência
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -7,7 +7,7 @@
|
||||
# Перо, Прямое выделение). Shortcut keys keep their key-cap names (Shift, Alt, Ctrl, Esc).
|
||||
#
|
||||
# Coverage: the whole interface — every menu string and every `tl!` literal (`complete_menus`,
|
||||
# tested). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# `untranslated_report` lists gaps). Left in English on purpose (`MENU_KEEP_AS_IS` in the tests): the product name, language
|
||||
# names, OpenType, the built-in workspace names and the bracketed perspective grid preset names.
|
||||
# Photoshop-style raster effects (Effect menu, their dialogs).
|
||||
Glowing Edges… Светящиеся края…
|
||||
@@ -1393,7 +1393,7 @@ file-filter Templates Шаблоны
|
||||
Clear History Очистить историю
|
||||
Set History Limit… Задать предел истории…
|
||||
History States Состояния истории
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Применить набор данных
|
||||
Area Type Текст в области
|
||||
Bloat Раздувание
|
||||
@@ -3790,7 +3790,7 @@ axis Both Оба
|
||||
|
||||
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
|
||||
# stays English for agents and tests. `{_1}`, `{_2}` … stand for the `{}` of the format string, in order
|
||||
# (`complete_languages_translate_every_message` lists any that are missing). Command ids, parameter
|
||||
# (`untranslated_report` lists any that are missing). Command ids, parameter
|
||||
# names and values in backticks or code (`colorModel rgb`, `document.export`) stay as they are.
|
||||
@msg no library `{id}` нет библиотеки `{id}`
|
||||
@msg select some type first сначала выделите текст
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1352,7 +1352,7 @@ font-technology Variable Variabla
|
||||
Clear History Rensa historik
|
||||
Set History Limit… Ange historikgräns…
|
||||
History States Historiktillstånd
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Använd datauppsättning
|
||||
Cancel Pattern Editing Avbryt mönsterredigering
|
||||
Copy Appearance Kopiera utseende
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
+242
-964
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
# монтажна область, шар, обведення, заливка, контур, опорна точка, зразки, пензель, виділення.
|
||||
# Shortcut key names (Shift, Alt, Ctrl, Esc), command ids and placeholders remain unchanged.
|
||||
#
|
||||
# Coverage: every menu string, tl! literal, plural, status and error message (enforced in tests).
|
||||
# Coverage: every menu string, tl! literal, plural, status and error message (`untranslated_report` lists gaps).
|
||||
# Product, language, built-in workspace and perspective preset names follow MENU_KEEP_AS_IS.
|
||||
# Keep placeholders, ellipses and leading/trailing spaces of fragments when editing.
|
||||
Glowing Edges… Сяйливі краї…
|
||||
@@ -1389,7 +1389,7 @@ file-filter Templates Шаблони
|
||||
Clear History Очистити історію
|
||||
Set History Limit… Задати межу історії…
|
||||
History States Стан історії
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Застосувати набір даних
|
||||
Area Type Текст в області
|
||||
Bloat Роздування
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 737 and column 40.
|
File diff suppressed because it is too large
Load Diff
@@ -1607,7 +1607,7 @@ file-filter Templates Mẫu
|
||||
Clear History Xóa lịch sử
|
||||
Set History Limit… Đặt giới hạn lịch sử…
|
||||
History States Trạng thái lịch sử
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set Áp dụng tập dữ liệu
|
||||
Area Type Chữ vùng
|
||||
Bloat Phình
|
||||
|
||||
|
@@ -1328,7 +1328,7 @@ file-filter Templates 模板
|
||||
Clear History 清除历史记录
|
||||
Set History Limit… 设置历史记录上限…
|
||||
History States 历史记录步数
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set 应用数据集
|
||||
Bloat 膨胀
|
||||
Cancel Pattern Editing 取消图案编辑
|
||||
@@ -3595,7 +3595,7 @@ imageTrace Outline 轮廓
|
||||
Use Default 使用默认值
|
||||
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
|
||||
# stays English for agents and tests. `{_1}`, `{_2}` … stand for the `{}` of the format string, in order
|
||||
# (`complete_languages_translate_every_message` lists any that are missing). Command ids, parameter
|
||||
# (`untranslated_report` lists any that are missing). Command ids, parameter
|
||||
# names and values in backticks or code (`colorModel rgb`, `document.export`) stay as they are.
|
||||
# Simplified Chinese: no final 。 unless the English ends with a period; a space between Chinese text and
|
||||
# an adjacent Latin word, number or {placeholder}; 、 between the items of a list.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1360,7 +1360,7 @@ file-filter Templates 範本
|
||||
Clear History 清除步驟記錄
|
||||
Set History Limit… 設定步驟記錄上限…
|
||||
History States 步驟記錄狀態
|
||||
# History panel: the names of undo steps (`every_undo_label_is_translated`).
|
||||
# History panel: the names of undo steps (`untranslated_report` lists any that are missing).
|
||||
Apply Data Set 套用資料集
|
||||
Area Type 區域文字
|
||||
Bloat 膨脹
|
||||
@@ -1584,7 +1584,7 @@ file-filter Templates 範本
|
||||
Keyboard Shortcuts… 鍵盤快速鍵…
|
||||
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
|
||||
# stays English for agents and tests. `{_1}`, `{_2}` … stand for the `{}` of the format string, in order
|
||||
# (`complete_languages_translate_every_message` lists any that are missing). Command ids, parameter
|
||||
# (`untranslated_report` lists any that are missing). Command ids, parameter
|
||||
# names and values in backticks or code (`colorModel rgb`, `document.export`) stay as they are.
|
||||
# Traditional Chinese: no final 。 unless the English ends with a period; a space between Chinese text and
|
||||
# an adjacent Latin word, number or {placeholder}; 、 between the items of a list; 「」 around the name of
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4435,10 +4435,14 @@ mod tests {
|
||||
_ => panic!("{id} is not a menu item"),
|
||||
}
|
||||
};
|
||||
assert_eq!(shown(&app, "edit.undo"), "ぼかしを取り消す");
|
||||
let step = |template: &str, name: &str| {
|
||||
let name = crate::panels::history::step_name(ja, name).unwrap_or_else(|| name.to_string());
|
||||
crate::i18n::fmt(crate::i18n::tr(ja, template), &[("name", &name)])
|
||||
};
|
||||
assert_eq!(shown(&app, "edit.undo"), step("Undo {name}", "Feather"));
|
||||
app.run("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(shown(&app, "edit.redo"), "ぼかしをやり直す");
|
||||
assert_eq!(shown(&app, "edit.undo"), "長方形を取り消す", "a step with a plain row");
|
||||
assert_eq!(shown(&app, "edit.redo"), step("Redo {name}", "Feather"));
|
||||
assert_eq!(shown(&app, "edit.undo"), step("Undo {name}", "Rectangle"), "a step with a plain row");
|
||||
crate::i18n::set_current(crate::i18n::Lang::EN);
|
||||
assert_eq!(shown(&app, "edit.redo"), "Redo Feather");
|
||||
}
|
||||
@@ -4543,10 +4547,15 @@ mod tests {
|
||||
let text = app.run("text.create", json!({"x": 0, "y": 40, "text": "Hello"})).unwrap()["id"].clone();
|
||||
app.run("charStyle.new", json!({"name": "Brush"})).unwrap();
|
||||
app.run("charStyle.apply", json!({"name": "Brush", "id": text})).unwrap();
|
||||
crate::i18n::set_current(crate::i18n::Lang::from_code("ja").expect("ja registered"));
|
||||
assert_eq!(display_label(&app, "edit.undo", "Undo"), "スタイル「Brush」を適用を取り消す");
|
||||
let ja = crate::i18n::Lang::from_code("ja").expect("ja registered");
|
||||
crate::i18n::set_current(ja);
|
||||
let step = |template: &str| {
|
||||
let applied = crate::i18n::fmt(crate::i18n::tr(ja, "Apply {name}"), &[("name", "Brush")]);
|
||||
crate::i18n::fmt(crate::i18n::tr(ja, template), &[("name", &applied)])
|
||||
};
|
||||
assert_eq!(display_label(&app, "edit.undo", "Undo"), step("Undo {name}"));
|
||||
app.run("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(display_label(&app, "edit.redo", "Redo"), "スタイル「Brush」を適用をやり直す");
|
||||
assert_eq!(display_label(&app, "edit.redo", "Redo"), step("Redo {name}"));
|
||||
crate::i18n::set_current(crate::i18n::Lang::EN);
|
||||
assert_eq!(display_label(&app, "edit.redo", "Redo"), "Redo Apply Brush");
|
||||
}
|
||||
|
||||
@@ -1277,14 +1277,16 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Those choices read as their dialogs show them in the UI language: in French, the channel's
|
||||
/// RGB is RVB, and in Spanish, Extrude & Bevel's Rounded is the "3d" row's Redondeado, not the
|
||||
/// plain row's Redondeada. Rasterize names the document's color mode.
|
||||
/// RGB as the "effect" row translates it, and in Spanish, Extrude & Bevel's Rounded as the "3d"
|
||||
/// row does, not the plain row. Rasterize names the document's color mode.
|
||||
#[test]
|
||||
fn the_effect_editor_shows_those_choices_as_their_dialogs_do() {
|
||||
crate::i18n::set_current(crate::i18n::Lang::from_code("fr").unwrap());
|
||||
let fr = crate::i18n::Lang::from_code("fr").unwrap();
|
||||
crate::i18n::set_current(fr);
|
||||
let (mut app, ctx, _) = editing("adjust.levels");
|
||||
let texts = shown_texts(&mut app, &ctx);
|
||||
assert!(texts.iter().any(|s| s == "RVB"), "{texts:?}");
|
||||
let rgb = crate::i18n::tr_ctx(fr, "effect", "RGB");
|
||||
assert!(texts.iter().any(|s| s == rgb), "{rgb}: {texts:?}");
|
||||
let es = crate::i18n::Lang::from_code("es").unwrap();
|
||||
crate::i18n::set_current(es);
|
||||
let (mut app, ctx, _) = editing("threeD.extrude");
|
||||
|
||||
@@ -1080,11 +1080,13 @@ mod tests {
|
||||
let text = crate::tests_labels::painted_text(&mut app, |app, ui| axis_sliders(app, ui, &face));
|
||||
assert!(text.contains("Weight\n") && text.contains("Width\n") && text.contains("400") && text.contains("100"), "{text}");
|
||||
// The labels are the font's names in every interface language, not catalog words (ja
|
||||
// translates the interface's Weight as 線幅, the stroke weight).
|
||||
crate::i18n::set_current(crate::i18n::Lang::from_code("ja").unwrap());
|
||||
// translates the interface's Weight as the stroke weight).
|
||||
let ja = crate::i18n::Lang::from_code("ja").unwrap();
|
||||
crate::i18n::set_current(ja);
|
||||
let text = crate::tests_labels::painted_text(&mut app, |app, ui| axis_sliders(app, ui, &face));
|
||||
crate::i18n::set_current(crate::i18n::Lang::EN);
|
||||
assert!(text.contains("Weight\n") && text.contains("Width\n") && !text.contains("線幅"), "{text}");
|
||||
let weight = crate::i18n::tr(ja, "Weight");
|
||||
assert!(text.contains("Weight\n") && text.contains("Width\n") && (weight == "Weight" || !text.contains(weight)), "{text}");
|
||||
// Screen readers name each slider by its axis.
|
||||
let tree = crate::widgets::accessibility_tests::tree(|ui| axis_sliders(&mut app, ui, &face));
|
||||
for name in ["Weight", "Width"] {
|
||||
|
||||
@@ -154,19 +154,21 @@ mod tests {
|
||||
let ja = crate::i18n::Lang::from_code("ja").expect("ja registered");
|
||||
crate::i18n::set_current(ja);
|
||||
let rows = listed(&mut app);
|
||||
let step = |template: &str, name: &str| crate::i18n::fmt(crate::i18n::tr(ja, template), &[("name", name)]);
|
||||
let apply = |name: &str| step("Apply {name}", name);
|
||||
let want = [
|
||||
"グラフィックスタイル「S」を再定義",
|
||||
"スタイル「Body」を適用",
|
||||
"スタイル「{name}」を適用",
|
||||
"スタイル「Apply」を適用",
|
||||
"スタイル「Brush」を適用",
|
||||
"スタイル「[標準文字スタイル]」を適用",
|
||||
"スタイル「Graphic Style “X”」を再定義",
|
||||
step("Redefine Graphic Style “{name}”", "S"),
|
||||
apply("Body"),
|
||||
apply("{name}"),
|
||||
apply("Apply"),
|
||||
apply("Brush"),
|
||||
apply(crate::i18n::tr(ja, "[Normal Character Style]")),
|
||||
step("Redefine {name}", "Graphic Style “X”"),
|
||||
];
|
||||
assert!(rows.contains(&want[0].to_string()), "{rows:#?}");
|
||||
assert!(rows.contains(&want[0]), "{rows:#?}");
|
||||
assert_eq!(rows[rows.len() - 6..], want[1..], "{rows:#?}");
|
||||
app.session.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(listed(&mut app).last().map(String::as_str), Some(want[6]), "an undone step");
|
||||
assert_eq!(listed(&mut app).last(), Some(&want[6]), "an undone step");
|
||||
crate::i18n::set_current(crate::i18n::Lang::EN);
|
||||
assert_eq!(listed(&mut app)[rows.len() - 6..rows.len() - 4], ["Apply Body", "Apply {name}"]);
|
||||
}
|
||||
@@ -176,8 +178,12 @@ mod tests {
|
||||
#[test]
|
||||
fn effect_steps_read_the_menu_row() {
|
||||
let ja = crate::i18n::Lang::from_code("ja").expect("ja registered");
|
||||
assert_eq!(step_name(ja, "Revolve").as_deref(), Some("回転体"));
|
||||
assert_eq!(step_name(ja, "Feather").as_deref(), Some("ぼかし"));
|
||||
for effect in ["Revolve", "Feather"] {
|
||||
let menu_row =
|
||||
crate::i18n::has(ja, &format!("{effect}…")).then(|| crate::i18n::tr(ja, &format!("{effect}…")).trim_end_matches('…').to_string());
|
||||
let plain_row = crate::i18n::has(ja, effect).then(|| crate::i18n::tr(ja, effect).to_string());
|
||||
assert_eq!(step_name(ja, effect), plain_row.or(menu_row), "{effect}");
|
||||
}
|
||||
assert_eq!(step_name(ja, "No Such Step"), None);
|
||||
}
|
||||
|
||||
|
||||
@@ -1794,7 +1794,6 @@ mod tests {
|
||||
let (stored, painted) = names(rect);
|
||||
let inner = stored.trim_start_matches('<').trim_end_matches('>');
|
||||
assert_eq!(painted, format!("<{}>", crate::i18n::tr(zh, inner)));
|
||||
assert_ne!(painted, stored, "translated");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -147,7 +147,6 @@ fn language_and_appearance_are_in_the_app_menu() {
|
||||
assert_eq!(appearance.len(), crate::theme::Brightness::ALL.len());
|
||||
assert_eq!(appearance.iter().filter(|(_, c)| *c == Some(true)).count(), 1, "{appearance:?}");
|
||||
assert!(appearance.iter().all(|(_, c)| c.is_some()));
|
||||
assert_eq!(crate::i18n::tr_ctx(Lang::from_code("ja").unwrap(), "theme", "Appearance"), "外観", "the theme, not the Appearance panel");
|
||||
}
|
||||
|
||||
/// Everything the in-window bar lists is in the Mac menu bar, once: what moved to the app menu
|
||||
|
||||
+18
-12
@@ -258,24 +258,30 @@ other `sv-*` locale resolve to it), Georgian (`ka`, complete; `ka-GE` and `ka_GE
|
||||
rule). The Language menu, the dropdown, locale matching and the catalog tests (well-formed, no duplicates,
|
||||
placeholders and ellipses agree, command ids exist, every row of a partial catalog is a string the UI
|
||||
shows, the UI fonts have every glyph, no Simplified characters in `zh-hant`) pick it up. Set `complete_menus`
|
||||
once every menu string and `tl!` literal is translated; `i18n::tests` then enforce it
|
||||
(`VECTORCRAFT_I18N_DUMP=strings.txt cargo test -p vectorcraft-ui-egui dump_source_strings` lists them).
|
||||
once the menus and `tl!` literals are translated; the untranslated report then lists what the catalog lacks
|
||||
(`VECTORCRAFT_I18N_DUMP=strings.txt cargo test -p vectorcraft-ui-egui dump_source_strings` lists every
|
||||
English string).
|
||||
- A missing translation never fails a test or a build: the interface shows the English text until a translator
|
||||
adds the row. `cargo xtask i18n-report` prints, per language, the strings its catalog lacks (menu strings,
|
||||
`tl!` literals, undo step names, menu labels, render-site labels, status and error messages, PostScript
|
||||
error meanings, default swatch names), and `cargo xtask ci` prints the same list in its `i18n-report` step.
|
||||
The report is the test `i18n::tests::untranslated_report`, run with `--nocapture`; it fails only when a
|
||||
scan finds too few strings to be working. Tests don't check one language's exact wording: a test that needs a
|
||||
translation compares with the catalog lookup (`tr(lang, "English")`).
|
||||
- Translations are clean-room: written from the meaning of the English text in ordinary vocabulary, never
|
||||
from another product's localisation resources. Product and technology names stay in Latin letters.
|
||||
- Status-bar messages and errors stay English where they are made (`app.ui.status`, `EngineError`: the
|
||||
control channel, MCP and tests read them) and are translated only where the status bar draws them
|
||||
(`i18n::msg`). `@msg` catalog rows hold a whole message or a template such as
|
||||
`Couldn't open {name}: {e}`; `{_1}`, `{_2}` … stand for the format string's `{}`, and the values in the
|
||||
placeholders are translated in turn (the reason after `: {e}` is often a message too). Czech, German, Greek, Spanish, French, Georgian, Hungarian, Italian, Japanese, Korean, Polish, Brazilian Portuguese, Russian, Simplified Chinese, Traditional Chinese, Swedish, Ukrainian, Vietnamese and Finnish cover every
|
||||
message literal the test scan finds (`complete_languages_translate_every_message`, languages listed in
|
||||
`COMPLETE_MESSAGES`), except the messages listed in `crates/ui-egui/src/i18n/untranslated_messages.txt`.
|
||||
A new `Err("…")`, `Other(…)`, `#[error(…)]` or `status(…)` message needs a row in each of those catalogs, and so
|
||||
does a new message given to an error enum's variant (`PsError::Limit(…)`), to the engine's `bad(cmd, …)`, `err(…)` or
|
||||
`error(…)`, or to the PDF writer's `bad(…)` or `failed(…)` (`VECTORCRAFT_I18N_DUMP_MESSAGES=messages.txt cargo test -p vectorcraft-ui-egui
|
||||
complete_languages_translate_every_message` lists them all). The list also holds the messages that open pull
|
||||
requests add without those rows. A change that adds the rows for a listed message removes its line from the list
|
||||
(`VECTORCRAFT_I18N_DUMP_UNTRANSLATED=<file>` writes the messages in the sources that lack a row). Other languages
|
||||
show messages in English until they add `@msg` rows.
|
||||
placeholders are translated in turn (the reason after `: {e}` is often a message too). Czech, German, Greek, Spanish, French, Georgian, Hungarian, Italian, Japanese, Korean, Polish, Brazilian Portuguese, Russian, Simplified Chinese, Traditional Chinese, Swedish, Ukrainian, Vietnamese, Finnish and Turkish
|
||||
translate status and error messages (`COMPLETE_MESSAGES` in `i18n::tests`); the untranslated report lists the
|
||||
message literals the scan finds that one of them has no row for. The scan reads `Err("…")`, `Other(…)`,
|
||||
`#[error(…)]` and `status(…)` messages, messages given to an error enum's variant (`PsError::Limit(…)`), to the
|
||||
engine's `bad(cmd, …)`, `err(…)` or `error(…)`, and to the PDF writer's `bad(…)` or `failed(…)`
|
||||
(`VECTORCRAFT_I18N_DUMP_MESSAGES=messages.txt cargo test -p vectorcraft-ui-egui dump_source_strings` lists them
|
||||
all). A missing row shows the English message. Other languages show messages in English until they add `@msg`
|
||||
rows.
|
||||
- Not translated on purpose: names that are user data (layers, swatches, fonts, documents), the tab
|
||||
title's colour mode, command ids and parameter names inside messages. Not done yet: locale-aware number
|
||||
and date formats, right-to-left layout. Chinese and Japanese UI text
|
||||
|
||||
+27
-29
@@ -1,6 +1,6 @@
|
||||
# Localization parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-16 · **Change:** minor (Georgian (`ka`) interface catalog; Bulgarian (`bg`) and Greek (`el`) catalogs complete under the tests, #1909, #2076; Finnish (`fi`) catalog added, complete under the tests; Swedish (`sv`) catalog added, complete under the tests; scores and estimates unchanged; Brazilian Portuguese (`pt-br`) catalog completed under the tests: menus, `tl!` literals and messages; 12-language score 71% → 74%; #1914) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-23 · **Change:** patch (missing translations never fail; `cargo xtask ci` prints the untranslated report)
|
||||
|
||||
Per-language status of VectorCraft's interface. How the catalogs work and how to add a language:
|
||||
[`development.md` › Localisation](development.md#localisation). Part of
|
||||
@@ -8,18 +8,15 @@ Per-language status of VectorCraft's interface. How the catalogs work and how to
|
||||
|
||||
**How it is measured:** rows with a translation in each `crates/ui-egui/src/i18n/<code>.tsv`, counted on
|
||||
origin/main `01e165af`, except the rows that give a count date, which were counted on `main` at `cb0a7d9` (2026-10-21).
|
||||
The reference is the French catalog, which the tests hold complete: 3,416 interface
|
||||
The reference is the French catalog, which was complete when counted: 3,416 interface
|
||||
strings (menus, command labels, `tl!` literals: panels, dialogs, tooltips, Preferences) plus 604 `@msg` status and
|
||||
error messages, 4,020 in all. A language's % is its rows over 4,020 (capped at 100% per part). The
|
||||
`complete_menus` flag and `COMPLETE_MESSAGES` list make `i18n::tests` fail if a complete language misses a
|
||||
string, so "full" here is enforced by tests. The messages listed in `crates/ui-egui/src/i18n/untranslated_messages.txt`
|
||||
are not translated yet, and the tests let them through. No language has help content: there is no translated (or English)
|
||||
in-app help, and the docs are English.
|
||||
error messages, 4,020 in all. A language's % is its rows over 4,020 (capped at 100% per part).
|
||||
`i18n::tests` fail if a language with the `complete_menus` flag misses a menu string or `tl!` literal, or if a
|
||||
language in the `COMPLETE_MESSAGES` list misses a status or error message. Every non-English language marked
|
||||
"full" has the flag, and all of them except Bulgarian are in the list. No language has help content: there is
|
||||
no translated (or English) in-app help, and the docs are English.
|
||||
error messages, 4,020 in all. A language's % is its rows over 4,020 (capped at 100% per part). A missing
|
||||
translation never fails a test or a build: the interface shows the English text until a translator adds the row.
|
||||
`cargo xtask ci` prints the untranslated report (`cargo xtask i18n-report` alone), which lists per language the
|
||||
strings missing from each catalog with the `complete_menus` flag and the messages missing from each language in
|
||||
the `COMPLETE_MESSAGES` list. Every non-English language marked "full" has the flag, and all of them except
|
||||
Bulgarian, Danish, Indonesian and Dutch are in the list. No language has help content: there is no translated (or
|
||||
English) in-app help, and the docs are English.
|
||||
|
||||
**Native review:** every catalog was written clean-room from the English meaning, most of it by agents
|
||||
(`bflatastic`), with community contributors adding or correcting (German by `dwetscher`, Japanese and Traditional
|
||||
@@ -30,17 +27,17 @@ Chinese by `imachus`). Only Georgian is recorded as reviewed by a native speaker
|
||||
| Language | Code | UI strings translated | Dialogs / tooltips / messages | Script support | Native review | Status | To `full` (h) |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| English | `en` | source, 100% | all | Latin | — | full | — |
|
||||
| Simplified Chinese (Mandarin) | `zh-hans` | 5,102 rows, 791 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete (tests) | all | CJK UI font from craft-fonts (BIZ UDPGothic) when embedded, else a system font; IME with marked text at the caret; vertical type partial ([type-parity.md](type-parity.md)) | no | full | — (review) |
|
||||
| Simplified Chinese (Mandarin) | `zh-hans` | 5,102 rows, 791 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted | all | CJK UI font from craft-fonts (BIZ UDPGothic) when embedded, else a system font; IME with marked text at the caret; vertical type partial ([type-parity.md](type-parity.md)) | no | full | — (review) |
|
||||
| Spanish | `es` | 4,009 / 4,020 (99.7%) | all (604 messages) | Latin | no | full | — (review) |
|
||||
| Hindi | `hi` | 0 | none | Devanagari: the document's text shapes (HarfRust), with tests of conjuncts, reph, pre-base matras, nukta forms and caret stops that run when an OpenType Devanagari font is installed, but the egui interface does no complex shaping | no | none | 20–30 |
|
||||
| Arabic | `ar` | 0 | none | document text: bidi layout and Paragraph Direction; interface: no right-to-left layout, no Arabic shaping in egui | no | none | 25–40 |
|
||||
| French | `fr` | 4,020 / 4,020 (100%) | all | Latin | no | full | — (review) |
|
||||
| Portuguese | `pt-br` | 5,194 catalog entries (counted 2026-10-21): menus, `tl!` literals and messages complete (tests); one/other plurals | all | Latin | contributor is a Brazilian Portuguese speaker; further native review welcome | full | — (review) |
|
||||
| Indonesian | `id` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete (tests); the tests do not hold its messages complete; one plural form | dialogs and tooltips all; 750 message rows (French has 796) | Latin | no | full | — (review) |
|
||||
| Portuguese | `pt-br` | 5,194 catalog entries (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one/other plurals | all | Latin | contributor is a Brazilian Portuguese speaker; further native review welcome | full | — (review) |
|
||||
| Indonesian | `id` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial; one plural form | dialogs and tooltips all; 750 message rows (French has 796) | Latin | no | full | — (review) |
|
||||
| Japanese | `ja` | 4,004 / 4,020 (99.6%) | all | CJK UI font as above; Japanese IME; vertical type partial; Japanese crop marks and composition settings | no | full | — (review) |
|
||||
| German | `de` | 3,952 / 4,020 (98%; tests hold menus, literals and messages complete) | all (602 of 604 message rows; landed 2026-10-10) | Latin | contributor-written, not reviewed | full | — (review) |
|
||||
| Korean | `ko` | 4,870 rows (counted 2026-10-21): menus, `tl!` literals and messages complete (tests); one plural form | all | Hangul from an installed system font on the desktop (Apple SD Gothic Neo, Malgun Gothic, Noto Sans CJK KR…); the web build has no Korean UI font yet (craft-fonts); IME path exists | no (AI-drafted) | full | — (review; web font) |
|
||||
| Vietnamese | `vi` | 4,886 rows (counted 2026-10-21): menus, `tl!` literals and messages complete (tests); one plural form; 4,046 plain + 18 `@plural` + 28 contextual + 794 `@msg` | all | Latin with Vietnamese diacritics (precomposed forms; glyph tests pass) | contributor is a Vietnamese speaker; AI-assisted draft, further native review welcome | full | — (review) |
|
||||
| German | `de` | 3,952 / 4,020 (98%; menus, literals and messages complete when counted) | all (602 of 604 message rows; landed 2026-10-10) | Latin | contributor-written, not reviewed | full | — (review) |
|
||||
| Korean | `ko` | 4,870 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form | all | Hangul from an installed system font on the desktop (Apple SD Gothic Neo, Malgun Gothic, Noto Sans CJK KR…); the web build has no Korean UI font yet (craft-fonts); IME path exists | no (AI-drafted) | full | — (review; web font) |
|
||||
| Vietnamese | `vi` | 4,886 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form; 4,046 plain + 18 `@plural` + 28 contextual + 794 `@msg` | all | Latin with Vietnamese diacritics (precomposed forms; glyph tests pass) | contributor is a Vietnamese speaker; AI-assisted draft, further native review welcome | full | — (review) |
|
||||
|
||||
**12-language score: ~82%** (equal weights: 100 + 85 + 100 + 0 + 0 + 100 + 100 + 100 + 100 + 98 + 100 + 100, over 12).
|
||||
**To `full` for all twelve: ~50–85 h**, most of it Arabic (right-to-left interface) and Hindi (interface
|
||||
@@ -51,19 +48,19 @@ plus review. Native-speaker review is human work and isn't in the hours.
|
||||
|
||||
| Language | Code | Translated | Status |
|
||||
|---|---|---|---|
|
||||
| Traditional Chinese (Taiwan) | `zh-hant` | 4,892 rows, 790 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete under the tests (80% overall) | partial |
|
||||
| Traditional Chinese (Taiwan) | `zh-hant` | 4,892 rows, 790 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted (80% overall) | partial |
|
||||
| Italian | `it` | 4,011 / 4,020 | full |
|
||||
| Dutch | `nl` | 5,317 rows, 777 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete (tests); the tests do not hold its messages complete; one/other plurals | full |
|
||||
| Danish | `da` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete (tests); the tests do not hold its messages complete; one/other plurals | full |
|
||||
| Dutch | `nl` | 5,317 rows, 777 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial; one/other plurals | full |
|
||||
| Danish | `da` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial; one/other plurals | full |
|
||||
| Russian | `ru` | 4,009 / 4,020 | full |
|
||||
| Ukrainian | `uk` | 4,009 / 4,020 (one/few/many plurals) | full |
|
||||
| Hungarian | `hu` | 5,253 rows (counted 2026-10-21): menus, `tl!` literals and messages complete (tests); one plural form | full |
|
||||
| Polish | `pl` | 5,130 rows (counted 2026-10-21): menus, `tl!` literals and messages complete (tests); one/few/many plurals | full; native review pending |
|
||||
| Finnish | `fi` | 5,318 rows (counted 2026-10-21): menus, `tl!` literals and messages complete (tests); one/other plurals, 797 `@msg` rows | full; AI-assisted, native proofreading pending |
|
||||
| Czech | `cs` | 5,240 catalog entries, including 795 `@msg` rows (counted 2026-10-21); menu, literal and message coverage enforced by tests | catalog complete; render-site gaps reduced; native review pending |
|
||||
| Swedish | `sv` | 5,251 rows, 795 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete (tests); one/other plurals | full; native review welcome |
|
||||
| Bulgarian | `bg` | 5,245 rows, 763 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete (tests); the tests do not hold its messages complete | full; AI-drafted, native review pending |
|
||||
| Greek | `el` | 5,251 rows, 795 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete (tests) | full; native review welcome |
|
||||
| Hungarian | `hu` | 5,253 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form | full |
|
||||
| Polish | `pl` | 5,130 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one/few/many plurals | full; native review pending |
|
||||
| Finnish | `fi` | 5,318 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one/other plurals, 797 `@msg` rows | full; AI-assisted, native proofreading pending |
|
||||
| Czech | `cs` | 5,240 catalog entries, including 795 `@msg` rows (counted 2026-10-21); menus, literals and messages complete when counted | catalog complete; render-site gaps reduced; native review pending |
|
||||
| Swedish | `sv` | 5,251 rows, 795 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one/other plurals | full; native review welcome |
|
||||
| Bulgarian | `bg` | 5,245 rows, 763 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial | full; AI-drafted, native review pending |
|
||||
| Greek | `el` | 5,251 rows, 795 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted | full; native review welcome |
|
||||
| Georgian | `ka` | 5,200 rows (counted 2026-10-21): menus, panels, dialogs, plurals (one count-neutral form) and messages, tested complete; reviewed by a native speaker | full |
|
||||
|
||||
Twenty-three non-English catalogs in all. Illustrator, as its public documentation lists, ships around two dozen
|
||||
@@ -82,7 +79,7 @@ contextual radial adjective. PDF/Print mark styles use `mark-style`, so `Japanes
|
||||
The search placeholder, Home screen, toolbar/panel tooltips and Preferences already use lookups.
|
||||
No new `tl!("literal")` keys were needed: only `cs.tsv` adds 19 AI-draft rows and revises the gradient
|
||||
template. Every other complete catalog (`complete_menus`) has the same rows and a `Roman` row for the
|
||||
other mark style; `complete_catalogs_translate_render_site_labels` fails when one of them lacks a row.
|
||||
other mark style; the untranslated report lists a catalog that lacks one of them.
|
||||
Native review is still needed, and catalog coverage does not prove that every variable in the UI is
|
||||
translated.
|
||||
|
||||
@@ -95,6 +92,7 @@ font for the web build.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-23 | patch | A missing translation never fails a test or a build; one untranslated report, printed by `cargo xtask ci` and `cargo xtask i18n-report`, lists the missing strings per language; scores unchanged |
|
||||
| 2026-10-16 | minor | Bulgarian (`bg`, #1909) and Greek (`el`, #2076) catalogs added, complete under the tests; Swedish (#2139) and Finnish (#1994) rows for strings added since; eighteen non-English catalogs |
|
||||
| 2026-10-16 | minor | Finnish (`fi`) catalog added, complete under the tests (menus, literals, messages; one/other plurals); AI-assisted, native proofreading pending |
|
||||
| 2026-10-15 | minor | Swedish (`sv`) catalog added, complete under the tests (menus, literals, messages; one/other plurals); every `sv-*` locale resolves to it; AI-drafted, native review welcome; scores and estimates unchanged |
|
||||
|
||||
@@ -549,10 +549,10 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- Panels undock (#495): a dock tab, a panel icon or a popped-out panel's title dragged out of the dock floats that panel inside the window, and the strip right of the dock's tabs floats the whole tabbed group. A floating group moves by its title bar, a tab dragged out of it floats on its own, and dropped on another group's title bar or tabs it stacks with that group; dropped on a dock group's tabs, which light up under the pointer, its panels join that group, and on the group's upper or lower half they make a new group above or below it (`window.panel.dock {anchor, zone}`); dropped on the icon column they become icons there, the tabbed group's panels too (`window.panel.dock {icon: true}`); dropped anywhere else on the dock, which lights up, they go back where they live (the tabbed group or the icon column), as its × and the panel menus' Float Panel / Dock Panel do (`window.panel.float`, `window.panel.dock`). The Tools panel floats by its title bar and docks again on the window's left edge. Floating groups stack into sets: dropped on the upper or lower half of another group (a line shows where), a group joins it right above or below it, and the set's groups stack top to bottom, each showing one panel, moving together by the top group's title bar, whose × docks them all; a group dragged by its tab strip leaves the set (`window.panel.float {below, above}`). A double-click on a tab collapses a floating group to its tabs (`collapsed`), and a dock group too, a group below it moving up into the room; a second double-click, or a click on another of its tabs, expands it (`window.panel.collapse`, kept with the preferences and in saved workspaces). Sets dropped on the left edge of the icon column or of another dock column dock as dock columns (a multi-column dock, `window.panel.dock {column}`), whose groups stack, tab and tear out as a floating set's do, and whose top strip floats the column again. Floating panels, their sets and the Tools panel's position are kept with the preferences and in user workspaces, and clamped into a smaller window. Not done: panels in separate OS windows, reordering tabs by dragging, collapsing a set or dock column to icons, dock columns right of the default dock, resizing floating panels and the groups of a set.
|
||||
- The dock's double arrow collapses Properties, Layers and Libraries to icons at the top of the icon column, popping out like the other icon panels, and expands them again (`window.collapseDock`, kept with the preferences and in saved workspaces). The icon column's own arrow (expanding the icon panels into a full column) is not done.
|
||||
- Czech render-site labels: desktop/web file filters, diagnostics, suggested preset names and contributor tooltips/plurals now use catalog lookups; gradients have Czech adjective agreement and printer marks have a separate context (PDF/Print and Summary). Search, Home, toolbar/panel tooltips and Preferences already look up their labels. Every complete catalog has rows for these labels and for the Roman and Japanese printer mark styles. Evidence: `i18n::tests::czech_render_site_follow_up`, `i18n::tests::complete_catalogs_translate_render_site_labels`, `dialogs::tests_modal::printer_mark_labels_are_distinct_from_language_names`; see [localization-parity.md](localization-parity.md).
|
||||
- The History panel, Edit › Undo and Edit › Redo translate the names of the steps that apply or redefine a character or paragraph style and of the step that redefines a graphic style: each such step keeps its template (`Apply {name}`, `Redefine {name}`, `Redefine Graphic Style “{name}”`) and the style's name, every complete catalog has a row for each template, and the panel and the menu show the style's name as the document holds it, except the Normal styles' names, which they show in the interface language (`steps_named_after_a_style_list_the_template_around_the_name`, `undo_and_redo_name_a_style_step_around_the_style_name`, `every_undo_label_is_translated`).
|
||||
- The History panel, Edit › Undo and Edit › Redo translate the names of the steps that apply or redefine a character or paragraph style and of the step that redefines a graphic style: each such step keeps its template (`Apply {name}`, `Redefine {name}`, `Redefine Graphic Style “{name}”`) and the style's name, every complete catalog has a row for each template, and the panel and the menu show the style's name as the document holds it, except the Normal styles' names, which they show in the interface language (`steps_named_after_a_style_list_the_template_around_the_name`, `undo_and_redo_name_a_style_step_around_the_style_name`).
|
||||
- Localised UI: menus (in-window and native), panels, dialogs, toolbar and Preferences read from per-language catalogs (`crates/ui-egui/src/i18n`, see `docs/development.md`); Traditional Chinese (Taiwan), Simplified Chinese (mainland), Japanese, Korean, Vietnamese, German, Spanish, French, Italian, Polish, Brazilian Portuguese, Russian, Ukrainian and Georgian ship complete, and Czech covers every menu label (and more), with system-locale detection on macOS, Windows, Linux and the web (the browser's languages, or `?lang=`), a Language preference and, on macOS, a VectorCraft ▸ Language menu. Names are never translated (fonts and their styles, layers, artboards, saved presets, user libraries, custom workspaces, recent files, plug-ins).
|
||||
- Edit › Undo and Edit › Redo, in the in-window and native menus, name the step in the interface language as the History panel lists it (`panels::history::step_name`), so when the catalog has no plain row for a step named after an effect, such as Feather, the menu shows the translation of the effect's menu row without its ellipsis (`undo_and_redo_name_the_step_as_the_history_panel_does`).
|
||||
- The History panel translates the undo step names the engine, the tools and the shell write as literals: each has a row in every complete catalog (`every_undo_label_is_translated`), and a step whose catalog row exists only with an ellipsis (such as an effect's menu name) shows that row without the ellipsis.
|
||||
- The History panel translates the undo step names the engine, the tools and the shell write as literals: the untranslated report (`untranslated_report`) lists any a complete catalog lacks, and a step whose catalog row exists only with an ellipsis (such as an effect's menu name) shows that row without the ellipsis.
|
||||
- The History panel menu has Step Backward and Step Forward, which run Edit › Undo and Edit › Redo and are disabled when there is no step to undo or redo, and Set History Limit…, which opens Preferences at Performance, where History States is set. Not done: a new document from the selected state.
|
||||
- The History panel menu's Clear History (`edit.clearHistory`) removes every step to undo and to redo and leaves the document as it is; it is not an undo step itself, and it is disabled when there is no step to clear.
|
||||
- Native macOS menu bar, vector tool cursors, a ⌘K command palette, middle-button panning with any tool. Native menus explicitly end tracking on command selection, outside window presses and Escape; menu updates wait until tracking ends, and a dismissing press never starts a canvas gesture (M14.8).
|
||||
|
||||
@@ -27,6 +27,8 @@ commands:
|
||||
assets check that every icon/image/font/asset is attributed in ASSETS.md
|
||||
brands check that user-visible text (labels, params docs, MCP, docs, packaging) names no other vendor's products
|
||||
cursors warn about Cursor values with no entry in crates/tools/src/cursors.toml (never fails)
|
||||
i18n-report list the strings each language's catalog lacks, shown in English (fails only on a
|
||||
broken scan; ci runs it too)
|
||||
pdf-deps verify immutable shared PDF writer and patched Krilla sources
|
||||
layers enforce the crate dependency layering (plan/architecture.md §3)
|
||||
wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates and the web app
|
||||
@@ -49,6 +51,7 @@ fn main() -> ExitCode {
|
||||
Some("assets") => assets::run(&root()),
|
||||
Some("brands") => brands::run(&root()),
|
||||
Some("cursors") => cursors::run(&root()),
|
||||
Some("i18n-report") => cmd_i18n_report(),
|
||||
Some("layers") => cmd_layers(),
|
||||
Some("pdf-deps") => pdf_deps::run(&root()),
|
||||
Some("wasm") => cmd_wasm(),
|
||||
@@ -271,6 +274,7 @@ fn cmd_ci() -> Result<(), String> {
|
||||
("assets", Box::new(|| assets::run(&root()))),
|
||||
("brands", Box::new(|| brands::run(&root()))),
|
||||
("cursors", Box::new(|| cursors::run(&root()))),
|
||||
("i18n-report", Box::new(cmd_i18n_report)),
|
||||
("layers", Box::new(cmd_layers)),
|
||||
("wasm", Box::new(cmd_wasm)),
|
||||
];
|
||||
@@ -317,3 +321,11 @@ Tests that use a corpus skip cleanly when it is absent.
|
||||
}
|
||||
pngsuite::fetch()
|
||||
}
|
||||
|
||||
/// Prints the strings each language's catalog has no row for (`i18n::tests::untranslated_report`).
|
||||
/// The interface shows them in English, so a gap never fails it; only a scan that finds nothing does.
|
||||
fn cmd_i18n_report() -> Result<(), String> {
|
||||
let mut c = cargo();
|
||||
c.args(["test", "-p", "vectorcraft-ui-egui", "--lib", "--", "i18n::tests::untranslated_report", "--exact", "--nocapture"]);
|
||||
run(c, "cargo test -p vectorcraft-ui-egui --lib -- i18n::tests::untranslated_report --exact --nocapture")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user