W: interface language — German, French, Spanish, Japanese menus and panels
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6b2fa19f00
commit
428ac5a922
|
|
@ -94,6 +94,7 @@ DesignCraft aims at full Adobe InDesign parity — and to be better: faster, ope
|
|||
- Transparency flattener (`file.exportPdf {flatten: high|medium|low|ppi}`, Edit ▸ Transparency Flattener Presets): spreads with transparency are rasterised per page in the exported PDF. It doesn't yet keep text and vectors outside transparent areas.
|
||||
- Video and sound (place .mp4/.mov/.webm/.mp3/.wav…, `media.options/get`, Media panel): media frames show a poster or placeholder on screen, in print and in PDF, and export as HTML5 `<video>`/`<audio>` in EPUB. No in-app playback or PDF rich media.
|
||||
- Right-to-left text (World-Ready basics): right-to-left runs are shaped right to left, lines are reordered by the Unicode Bidirectional Algorithm (matched against the reference implementation), Paragraph Direction (`type.para {direction}`, Paragraph panel, IDML ParagraphDirection, EPUB `dir`). Kashida justification, caret movement in visual order and digit substitution aren't there yet.
|
||||
- Interface language (Edit ▸ Interface Language, `app.language`): German, French, Spanish and Japanese menu titles, common menu items and panel names (our own translations); dialogs and the rest of the UI stay English.
|
||||
- Drag and drop text editing: drag selected text to move it (Alt copies) with its formatting (`text.release`).
|
||||
- Image Import Options: place any page of a multi-page PDF (`file.place {pdfPage}`; the app asks which page), shown on screen and embedded as that page in PDF export.
|
||||
- Define Lists: named numbered lists that continue across stories in page order (`list.define`, paragraph `listName` / `startAt`), round-tripped through IDML; `null` in `type.para` / `type.char` now removes an override.
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ impl NativeMenu {
|
|||
items.insert("dc-discord".into(), ("help.discord".into(), serde_json::Value::Null, Handle::Plain(discord)));
|
||||
let _ = menu.append(&app_menu);
|
||||
for (title, entries) in menus::menu_tree() {
|
||||
let sub = Submenu::new(title, true);
|
||||
let sub = Submenu::new(designcraft_ui_egui::i18n::tr(&app.ui.language, title), true);
|
||||
build(app, &sub, &entries, &mut items, &mut counter);
|
||||
let _ = menu.append(&sub);
|
||||
}
|
||||
|
|
@ -103,7 +103,7 @@ fn build(app: &DesignApp, parent: &Submenu, entries: &[Item], items: &mut HashMa
|
|||
let _ = parent.append(&PredefinedMenuItem::separator());
|
||||
}
|
||||
Item::Sub(label, children) => {
|
||||
let sub = Submenu::new(label, true);
|
||||
let sub = Submenu::new(designcraft_ui_egui::i18n::tr(&app.ui.language, label), true);
|
||||
build(app, &sub, children, items, counter);
|
||||
let _ = parent.append(&sub);
|
||||
}
|
||||
|
|
@ -111,6 +111,7 @@ fn build(app: &DesignApp, parent: &Submenu, entries: &[Item], items: &mut HashMa
|
|||
*counter += 1;
|
||||
let mid = format!("dc{counter}");
|
||||
let sc = if params.is_null() { shortcut.and_then(accel) } else { None };
|
||||
let label = designcraft_ui_egui::i18n::tr(&app.ui.language, label);
|
||||
let handle = if menus::checked(app, id, params).is_some() {
|
||||
let c = CheckMenuItem::with_id(mid.clone(), label, true, false, sc);
|
||||
let _ = parent.append(&c);
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
|
|||
for (id, label, icon) in ICON_PANELS {
|
||||
let open = app.ui.open_panel.as_deref() == Some(*id);
|
||||
let floats = app.ui.floating.iter().any(|(p, _)| p == id);
|
||||
if icons::button(ui, icon, 28.0, open || floats, label).clicked() {
|
||||
if icons::button(ui, icon, 28.0, open || floats, &crate::i18n::tr(&app.ui.language, label)).clicked() {
|
||||
if floats {
|
||||
ui.ctx().move_to_top(egui::LayerId::new(egui::Order::Middle, egui::Id::new(("floating_panel", *id))));
|
||||
} else {
|
||||
|
|
@ -120,7 +120,7 @@ fn dock_header(ui: &mut egui::Ui, t: &Tokens, chevron: &str) {
|
|||
pub fn flyout(app: &mut DesignApp, ctx: &egui::Context) {
|
||||
let Some(id) = app.ui.open_panel.clone() else { return };
|
||||
let t = Tokens::get(ctx);
|
||||
let label = ICON_PANELS.iter().find(|p| p.0 == id).map(|p| p.1).unwrap_or("Panel");
|
||||
let label = crate::i18n::tr(&app.ui.language, ICON_PANELS.iter().find(|p| p.0 == id).map(|p| p.1).unwrap_or("Panel")).into_owned();
|
||||
let screen = ctx.content_rect();
|
||||
let dock_w = if app.ui.dock_expanded { ctx.memory(|m| m.area_rect(egui::Id::new("dock")).map(|r| r.width())).unwrap_or(280.0) } else { 0.0 };
|
||||
let pos = egui::pos2(screen.max.x - 37.0 - dock_w - 262.0, 120.0);
|
||||
|
|
@ -212,10 +212,10 @@ pub fn dock_panel(app: &mut DesignApp, id: &str) {
|
|||
pub fn floating(app: &mut DesignApp, ctx: &egui::Context) {
|
||||
let t = Tokens::get(ctx);
|
||||
for (id, at) in app.ui.floating.clone() {
|
||||
let label = ICON_PANELS.iter().find(|p| p.0 == id).map(|p| p.1).unwrap_or("Panel");
|
||||
let label = crate::i18n::tr(&app.ui.language, ICON_PANELS.iter().find(|p| p.0 == id).map(|p| p.1).unwrap_or("Panel")).into_owned();
|
||||
let mut open = true;
|
||||
let mut dock = false;
|
||||
let r = egui::Window::new(label)
|
||||
let r = egui::Window::new(label.clone())
|
||||
.id(egui::Id::new(("floating_panel", &id)))
|
||||
.title_bar(false)
|
||||
.default_pos(egui::pos2(at[0], at[1]))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
//! Interface language (Edit › Interface Language): translations of menu titles, the most used
|
||||
//! menu items and panel names. Untranslated strings stay English. The translations are our own.
|
||||
|
||||
/// Supported interface languages: (code, name in that language).
|
||||
pub const LANGUAGES: &[(&str, &str)] = &[("", "English"), ("de", "Deutsch"), ("fr", "Français"), ("es", "Español"), ("ja", "日本語")];
|
||||
|
||||
/// English → [German, French, Spanish, Japanese].
|
||||
const TABLE: &[(&str, [&str; 4])] = &[
|
||||
// Menus.
|
||||
("File", ["Datei", "Fichier", "Archivo", "ファイル"]),
|
||||
("Edit", ["Bearbeiten", "Édition", "Edición", "編集"]),
|
||||
("Layout", ["Layout", "Page", "Maquetación", "レイアウト"]),
|
||||
("Type", ["Schrift", "Texte", "Texto", "書式"]),
|
||||
("Object", ["Objekt", "Objet", "Objeto", "オブジェクト"]),
|
||||
("Table", ["Tabelle", "Tableau", "Tabla", "表"]),
|
||||
("View", ["Ansicht", "Affichage", "Ver", "表示"]),
|
||||
("Window", ["Fenster", "Fenêtre", "Ventana", "ウィンドウ"]),
|
||||
("Help", ["Hilfe", "Aide", "Ayuda", "ヘルプ"]),
|
||||
// File.
|
||||
("New", ["Neu", "Nouveau", "Nuevo", "新規"]),
|
||||
("New Document…", ["Neues Dokument …", "Nouveau document…", "Nuevo documento…", "新規ドキュメント…"]),
|
||||
("Open…", ["Öffnen …", "Ouvrir…", "Abrir…", "開く…"]),
|
||||
("Close", ["Schließen", "Fermer", "Cerrar", "閉じる"]),
|
||||
("Save", ["Speichern", "Enregistrer", "Guardar", "保存"]),
|
||||
("Save As…", ["Speichern unter …", "Enregistrer sous…", "Guardar como…", "別名で保存…"]),
|
||||
("Save a Copy…", ["Kopie speichern …", "Enregistrer une copie…", "Guardar una copia…", "コピーを保存…"]),
|
||||
("Revert", ["Zurück zur letzten Version", "Version précédente", "Volver a la versión guardada", "復帰"]),
|
||||
("Place…", ["Platzieren …", "Importer…", "Colocar…", "配置…"]),
|
||||
("Export PDF…", ["PDF exportieren …", "Exporter en PDF…", "Exportar PDF…", "PDF を書き出し…"]),
|
||||
("Print…", ["Drucken …", "Imprimer…", "Imprimir…", "プリント…"]),
|
||||
("Document Setup…", ["Dokument einrichten …", "Format de document…", "Ajustar documento…", "ドキュメント設定…"]),
|
||||
("Package…", ["Verpacken …", "Assembler…", "Empaquetar…", "パッケージ…"]),
|
||||
// Edit.
|
||||
("Undo", ["Rückgängig", "Annuler", "Deshacer", "取り消し"]),
|
||||
("Redo", ["Wiederholen", "Rétablir", "Rehacer", "やり直し"]),
|
||||
("Cut", ["Ausschneiden", "Couper", "Cortar", "カット"]),
|
||||
("Copy", ["Kopieren", "Copier", "Copiar", "コピー"]),
|
||||
("Paste", ["Einfügen", "Coller", "Pegar", "ペースト"]),
|
||||
("Clear", ["Löschen", "Effacer", "Borrar", "消去"]),
|
||||
("Duplicate", ["Duplizieren", "Dupliquer", "Duplicar", "複製"]),
|
||||
("Select All", ["Alles auswählen", "Tout sélectionner", "Seleccionar todo", "すべてを選択"]),
|
||||
("Deselect All", ["Auswahl aufheben", "Tout désélectionner", "Deseleccionar todo", "すべての選択を解除"]),
|
||||
("Find/Change…", ["Suchen/Ersetzen …", "Rechercher/Remplacer…", "Buscar/Cambiar…", "検索と置換…"]),
|
||||
("Check Spelling…", ["Rechtschreibprüfung …", "Vérifier l'orthographe…", "Revisar ortografía…", "スペルチェック…"]),
|
||||
("Preferences…", ["Voreinstellungen …", "Préférences…", "Preferencias…", "環境設定…"]),
|
||||
("Keyboard Shortcuts…", ["Tastaturbefehle …", "Raccourcis clavier…", "Métodos abreviados de teclado…", "キーボードショートカット…"]),
|
||||
("Interface Language", ["Sprache der Oberfläche", "Langue de l'interface", "Idioma de la interfaz", "インターフェイスの言語"]),
|
||||
// Layout.
|
||||
("Pages", ["Seiten", "Pages", "Páginas", "ページ"]),
|
||||
("Margins and Columns…", ["Ränder und Spalten …", "Marges et colonnes…", "Márgenes y columnas…", "マージン・段組…"]),
|
||||
("Create Guides…", ["Hilfslinien erstellen …", "Créer des repères…", "Crear guías…", "ガイドを作成…"]),
|
||||
(
|
||||
"Numbering & Section Options…",
|
||||
[
|
||||
"Nummerierungs- und Abschnittsoptionen …",
|
||||
"Options de numérotation et de section…",
|
||||
"Opciones de numeración y sección…",
|
||||
"ノンブルとセクション設定…",
|
||||
],
|
||||
),
|
||||
// Type.
|
||||
("Font", ["Schriftart", "Police", "Fuente", "フォント"]),
|
||||
("Size", ["Schriftgrad", "Corps", "Tamaño", "サイズ"]),
|
||||
("Character", ["Zeichen", "Caractère", "Carácter", "文字"]),
|
||||
("Paragraph", ["Absatz", "Paragraphe", "Párrafo", "段落"]),
|
||||
("Tabs", ["Tabulatoren", "Tabulations", "Tabulaciones", "タブ"]),
|
||||
("Glyphs", ["Glyphen", "Glyphes", "Glifos", "字形"]),
|
||||
("Story", ["Textabschnitt", "Article", "Artículo", "ストーリー"]),
|
||||
("Character Styles", ["Zeichenformate", "Styles de caractère", "Estilos de carácter", "文字スタイル"]),
|
||||
("Paragraph Styles", ["Absatzformate", "Styles de paragraphe", "Estilos de párrafo", "段落スタイル"]),
|
||||
("Create Outlines", ["In Pfade umwandeln", "Vectoriser", "Crear contornos", "アウトラインを作成"]),
|
||||
("Show Hidden Characters", ["Verborgene Zeichen einblenden", "Afficher les caractères masqués", "Mostrar caracteres ocultos", "制御文字を表示"]),
|
||||
// Object.
|
||||
("Transform", ["Transformieren", "Transformation", "Transformar", "変形"]),
|
||||
("Arrange", ["Anordnen", "Disposition", "Organizar", "重ね順"]),
|
||||
("Group", ["Gruppieren", "Grouper", "Agrupar", "グループ"]),
|
||||
("Ungroup", ["Gruppierung aufheben", "Dissocier", "Desagrupar", "グループ解除"]),
|
||||
("Lock", ["Sperren", "Verrouiller", "Bloquear", "ロック"]),
|
||||
("Hide", ["Ausblenden", "Masquer", "Ocultar", "隠す"]),
|
||||
("Effects", ["Effekte", "Effets", "Efectos", "効果"]),
|
||||
("Fitting", ["Anpassen", "Ajustement", "Encaje", "オブジェクトサイズの調整"]),
|
||||
("Text Frame Options…", ["Textrahmenoptionen …", "Options de bloc de texte…", "Opciones de marco de texto…", "テキストフレーム設定…"]),
|
||||
// Table.
|
||||
("Insert Table…", ["Tabelle einfügen …", "Insérer un tableau…", "Insertar tabla…", "表を挿入…"]),
|
||||
("Merge Cells", ["Zellen verbinden", "Fusionner les cellules", "Combinar celdas", "セルを結合"]),
|
||||
("Unmerge Cells", ["Zellverbindung aufheben", "Annuler la fusion des cellules", "Separar celdas", "セルの結合解除"]),
|
||||
("Insert", ["Einfügen", "Insérer", "Insertar", "挿入"]),
|
||||
("Delete", ["Löschen", "Supprimer", "Eliminar", "削除"]),
|
||||
// View.
|
||||
("Zoom In", ["Einzoomen", "Zoom avant", "Acercar", "ズームイン"]),
|
||||
("Zoom Out", ["Auszoomen", "Zoom arrière", "Alejar", "ズームアウト"]),
|
||||
("Fit Page in Window", ["Seite in Fenster einpassen", "Page entière", "Encajar página en ventana", "ページ全体"]),
|
||||
("Fit Spread in Window", ["Druckbogen in Fenster einpassen", "Planche entière", "Encajar pliego en ventana", "スプレッド全体"]),
|
||||
("Actual Size", ["Originalgröße", "Taille réelle", "Tamaño real", "100%"]),
|
||||
("Screen Mode", ["Bildschirmmodus", "Mode d'affichage", "Modo de pantalla", "スクリーンモード"]),
|
||||
("Grids & Guides", ["Raster und Hilfslinien", "Grilles et repères", "Cuadrículas y guías", "グリッドとガイド"]),
|
||||
("Show Rulers", ["Lineale einblenden", "Afficher les règles", "Mostrar reglas", "定規を表示"]),
|
||||
// Window and panels.
|
||||
("Properties", ["Eigenschaften", "Propriétés", "Propiedades", "プロパティ"]),
|
||||
("Layers", ["Ebenen", "Calques", "Capas", "レイヤー"]),
|
||||
("Links", ["Verknüpfungen", "Liens", "Vínculos", "リンク"]),
|
||||
("Swatches", ["Farbfelder", "Nuancier", "Muestras", "スウォッチ"]),
|
||||
("Color", ["Farbe", "Couleur", "Color", "カラー"]),
|
||||
("Stroke", ["Kontur", "Contour", "Trazo", "線"]),
|
||||
("Gradient", ["Verlauf", "Dégradé", "Degradado", "グラデーション"]),
|
||||
("Align", ["Ausrichten", "Alignement", "Alinear", "整列"]),
|
||||
("Pathfinder", ["Pathfinder", "Pathfinder", "Buscatrazos", "パスファインダー"]),
|
||||
("Info", ["Info", "Informations", "Información", "情報"]),
|
||||
("Library", ["Bibliothek", "Bibliothèque", "Biblioteca", "ライブラリ"]),
|
||||
("Book", ["Buch", "Livre", "Libro", "ブック"]),
|
||||
("Index", ["Index", "Index", "Índice", "索引"]),
|
||||
("Hyperlinks", ["Hyperlinks", "Hyperliens", "Hipervínculos", "ハイパーリンク"]),
|
||||
("Bookmarks", ["Lesezeichen", "Signets", "Marcadores", "ブックマーク"]),
|
||||
("Articles", ["Artikel", "Articles", "Artículos", "アーティクル"]),
|
||||
("Tags", ["Tags", "Balises", "Etiquetas", "タグ"]),
|
||||
("Media", ["Medien", "Média", "Medios", "メディア"]),
|
||||
("Object States", ["Objektstatus", "États d'objet", "Estados de objeto", "オブジェクトステート"]),
|
||||
("Buttons and Forms", ["Schaltflächen und Formulare", "Boutons et formulaires", "Botones y formularios", "ボタンとフォーム"]),
|
||||
("Liquid Layout", ["Flüssiges Layout", "Mise en page liquide", "Maquetación líquida", "リキッドレイアウト"]),
|
||||
("Workspace", ["Arbeitsbereich", "Espace de travail", "Espacio de trabajo", "ワークスペース"]),
|
||||
("Split Window", ["Fenster teilen", "Fractionner la fenêtre", "Dividir ventana", "ウィンドウを分割"]),
|
||||
("New Window", ["Neues Fenster", "Nouvelle fenêtre", "Nueva ventana", "新規ウィンドウ"]),
|
||||
(
|
||||
"Show All Menu Items",
|
||||
["Alle Menüelemente anzeigen", "Afficher toutes les commandes", "Mostrar todos los elementos de menú", "すべてのメニュー項目を表示"],
|
||||
),
|
||||
];
|
||||
|
||||
fn column(lang: &str) -> Option<usize> {
|
||||
match lang {
|
||||
"de" => Some(0),
|
||||
"fr" => Some(1),
|
||||
"es" => Some(2),
|
||||
"ja" => Some(3),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// `s` in `lang` (English, or the string itself, when there's no translation).
|
||||
pub fn tr<'a>(lang: &str, s: &'a str) -> std::borrow::Cow<'a, str> {
|
||||
let Some(c) = column(lang) else { return s.into() };
|
||||
match TABLE.iter().find(|(en, _)| *en == s) {
|
||||
Some((_, t)) => t[c].to_string().into(),
|
||||
None => s.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn translates_known_strings_and_keeps_the_rest() {
|
||||
assert_eq!(tr("de", "File"), "Datei");
|
||||
assert_eq!(tr("ja", "Swatches"), "スウォッチ");
|
||||
assert_eq!(tr("fr", "Something new"), "Something new");
|
||||
assert_eq!(tr("", "File"), "File");
|
||||
// Every row is unique and complete.
|
||||
for (i, (en, t)) in TABLE.iter().enumerate() {
|
||||
assert!(TABLE[..i].iter().all(|(e, _)| e != en), "duplicate {en}");
|
||||
assert!(t.iter().all(|x| !x.is_empty()), "{en}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ pub mod chrome;
|
|||
pub mod control;
|
||||
pub mod dialogs;
|
||||
pub mod dock;
|
||||
pub mod i18n;
|
||||
pub mod icons;
|
||||
pub mod menus;
|
||||
pub mod panels;
|
||||
|
|
@ -102,6 +103,8 @@ pub struct UiState {
|
|||
/// Edit › Menus: hidden menu items (`menu/label`), and Show Full Menus.
|
||||
pub hidden_menu_items: Vec<String>,
|
||||
pub show_full_menus: bool,
|
||||
/// Edit › Interface Language: "" (English), "de", "fr", "es" or "ja".
|
||||
pub language: String,
|
||||
/// Edit › Transparency Flattener Presets: "" (none), "high", "medium" or "low" for PDF export.
|
||||
pub flattener: String,
|
||||
/// View › Separations Preview: a process plate (0–3) and/or an ink limit (total, 0–4).
|
||||
|
|
@ -171,6 +174,7 @@ impl Default for UiState {
|
|||
tagged_frames: false,
|
||||
hidden_menu_items: Vec::new(),
|
||||
show_full_menus: false,
|
||||
language: String::new(),
|
||||
flattener: String::new(),
|
||||
separation: None,
|
||||
ink_limit: None,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ pub const UI_COMMANDS: &[(&str, &str, Option<&str>, &str)] = &[
|
|||
("app.fittingOptions", "Frame Fitting Options…", None, "{} — the dialog for object.fittingOptions"),
|
||||
("app.menus", "Menus…", None, "{} — show or hide menu items"),
|
||||
("window.hideMenuItem", "Hide Menu Item", None, "{item: \"Menu/Label\", hidden?: bool}"),
|
||||
("app.language", "Interface Language", None, "{lang: \"\"|de|fr|es|ja} — menus and panel names (the macOS menu bar follows on the next launch)"),
|
||||
(
|
||||
"app.flattener",
|
||||
"Transparency Flattener Presets",
|
||||
|
|
@ -176,6 +177,13 @@ pub const MENUS: &[(&str, &[&str])] = &[
|
|||
"cmd:edit.transparencyBlendSpace|Document RGB|{\"space\": \"rgb\"}",
|
||||
"cmd:edit.transparencyBlendSpace|Document CMYK|{\"space\": \"cmyk\"}",
|
||||
"<",
|
||||
">Interface Language",
|
||||
"ui:app.language|English|{\"lang\": \"\"}",
|
||||
"ui:app.language|Deutsch|{\"lang\": \"de\"}",
|
||||
"ui:app.language|Français|{\"lang\": \"fr\"}",
|
||||
"ui:app.language|Español|{\"lang\": \"es\"}",
|
||||
"ui:app.language|日本語|{\"lang\": \"ja\"}",
|
||||
"<",
|
||||
">Transparency Flattener Presets",
|
||||
"ui:app.flattener|None (keep transparency)|{\"preset\": \"\"}",
|
||||
"ui:app.flattener|High Resolution|{\"preset\": \"high\"}",
|
||||
|
|
@ -703,6 +711,14 @@ pub fn run_ui(app: &mut DesignApp, id: &str, p: &Value) -> Option<Result<Value,
|
|||
None => Ok(Value::Null),
|
||||
}
|
||||
}
|
||||
"app.language" => {
|
||||
let v = p.get("lang").and_then(Value::as_str).unwrap_or("");
|
||||
if !crate::i18n::LANGUAGES.iter().any(|(c, _)| *c == v) {
|
||||
return Some(Err(format!("unknown language `{v}`")));
|
||||
}
|
||||
app.ui.language = v.to_string();
|
||||
Ok(json!(v))
|
||||
}
|
||||
"app.flattener" => {
|
||||
let v = p.get("preset").and_then(Value::as_str).unwrap_or("");
|
||||
if !["", "high", "medium", "low"].contains(&v) {
|
||||
|
|
@ -1413,6 +1429,7 @@ pub fn checked(app: &DesignApp, id: &str, params: &Value) -> Option<bool> {
|
|||
"changes.track" => app.session.active().is_some_and(|d| d.doc.settings.track_changes),
|
||||
"window.controlBar" => app.ui.control_bar,
|
||||
"window.split" => app.split,
|
||||
"app.language" => app.ui.language == params.get("lang").and_then(Value::as_str).unwrap_or(""),
|
||||
"app.flattener" => app.ui.flattener == params.get("preset").and_then(Value::as_str).unwrap_or(""),
|
||||
"window.taskBar" => app.ui.task_bar,
|
||||
"window.toolsDoubleColumn" => app.ui.tools_double_column,
|
||||
|
|
@ -1460,13 +1477,14 @@ pub fn activate(app: &mut DesignApp, id: &str, params: &Value) {
|
|||
|
||||
/// The menu bar contents (inside the app bar; macOS uses the native menu instead).
|
||||
pub fn menu_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
|
||||
let lang = app.ui.language.clone();
|
||||
for (menu, entries) in menu_tree() {
|
||||
ui.menu_button(menu, |ui| {
|
||||
ui.menu_button(crate::i18n::tr(&lang, menu), |ui| {
|
||||
ui.set_min_width(240.0);
|
||||
let hidden = menu_items(app, ui, &entries, menu);
|
||||
if hidden > 0 && !app.ui.show_full_menus {
|
||||
ui.separator();
|
||||
if ui.button("Show All Menu Items").clicked() {
|
||||
if ui.button(crate::i18n::tr(&lang, "Show All Menu Items")).clicked() {
|
||||
app.ui.show_full_menus = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1489,16 +1507,18 @@ fn menu_items(app: &mut DesignApp, ui: &mut egui::Ui, items: &[Item], path: &str
|
|||
}
|
||||
Item::Sub(name, children) => {
|
||||
let sub = format!("{path}/{name}");
|
||||
ui.menu_button(name, |ui| {
|
||||
let shown = crate::i18n::tr(&app.ui.language, name).into_owned();
|
||||
ui.menu_button(shown, |ui| {
|
||||
hidden += menu_items(app, ui, children, &sub);
|
||||
});
|
||||
}
|
||||
Item::Cmd { label, .. } if !app.ui.show_full_menus && app.ui.hidden_menu_items.contains(&menu_key(path, label)) => hidden += 1,
|
||||
Item::Cmd { label, id, params, shortcut: _ } => {
|
||||
let label = crate::i18n::tr(&app.ui.language, label);
|
||||
let text = match checked(app, id, params) {
|
||||
Some(true) => format!("✓ {label}"),
|
||||
Some(false) => format!(" {label}"),
|
||||
None => label.clone(),
|
||||
None => label.into_owned(),
|
||||
};
|
||||
let mut b = egui::Button::new(text);
|
||||
if let Some(sc) = shortcut_of(app, id) {
|
||||
|
|
@ -1777,6 +1797,20 @@ mod tests {
|
|||
frame(&mut app);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_language_switches_and_validates() {
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
run_ui(&mut app, "app.language", &json!({"lang": "de"})).unwrap().unwrap();
|
||||
assert_eq!(app.ui.language, "de");
|
||||
assert_eq!(checked(&app, "app.language", &json!({"lang": "de"})), Some(true));
|
||||
assert_eq!(crate::i18n::tr(&app.ui.language, "Window"), "Fenster");
|
||||
assert!(run_ui(&mut app, "app.language", &json!({"lang": "xx"})).unwrap().is_err());
|
||||
// Every menu title has a translation (Japanese never matches the English).
|
||||
for (title, _) in menu_tree() {
|
||||
assert_ne!(crate::i18n::tr("ja", title), title, "{title}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panels_float_and_dock() {
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ Each row of the InDesign feature catalogue is scored **D** (done), **P** (partia
|
|||
|---|---|---|---|---|---|
|
||||
| P0 | 102 | 99 | 3 | 0 | 99% |
|
||||
| P1 | 97 | 92 | 5 | 0 | 97% |
|
||||
| P2 | 76 | 40 | 28 | 8 | 71% |
|
||||
| P2 | 76 | 40 | 29 | 7 | 72% |
|
||||
|
||||
| Area | Parity |
|
||||
|---|---|
|
||||
| Application shell & workspace | 91% |
|
||||
| Application shell & workspace | 92% |
|
||||
| Documents, pages, spreads | 94% |
|
||||
| Layers | 95% |
|
||||
| Frames, shapes & paths | 100% |
|
||||
|
|
@ -56,7 +56,7 @@ Open P0 items: Preferences, Vector/page formats (PDF/AI/SVG/EPS place), Export t
|
|||
| Application shell & workspace | Show Full Menus | P2 | D |
|
||||
| Application shell & workspace | Recovery / autosave | P0 | D |
|
||||
| Application shell & workspace | Preferences | P0 | P |
|
||||
| Application shell & workspace | Localization | P2 | M |
|
||||
| Application shell & workspace | Localization | P2 | P |
|
||||
| Application shell & workspace | Scripting (own API: commands, scripts, MCP) | P2 | D |
|
||||
| Documents, pages, spreads | New Document dialog | P0 | D |
|
||||
| Documents, pages, spreads | Document Setup | P0 | D |
|
||||
|
|
|
|||
Loading…
Reference in New Issue