From 019ff2b278881e76e804caa158d749ad9e30dd7f Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 11 Nov 2026 15:34:17 -0800 Subject: [PATCH] i18n: Hindi and Turkish messages keep the command ids they name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seventeen @msg rows in hi.tsv and one in tr.tsv translated or dropped the command id the English names outside backticks. For example, "missing `id` (see plugin.list)" read "(information.list)" in Hindi, and flattener.presets.list became "düzleştirici.presets.list" in Turkish. The rows now keep each id as written. Several of the Hindi rows also said the opposite of the English ("a profile is called" for "no profile is called") and are corrected. A new test checks every catalog: each command id a status or error message names (dot-separated lowercase words outside backticks) appears unchanged in its translation. --- crates/ui-egui/src/i18n/hi.tsv | 34 ++++++++++---------- crates/ui-egui/src/i18n/tests.rs | 55 ++++++++++++++++++++++++++++++++ crates/ui-egui/src/i18n/tr.tsv | 2 +- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index 572a19779..710767b0e 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -5577,7 +5577,7 @@ antiAlias Sharp शार्प @msg `{key}` must be a whole number between {min} and {max} (got {x}) `{key}` को {min} और {max} ({x}) के बीच पूरी संख्या होना चाहिए। @msg `{key}` must be a whole number of at least 0, not {n} `{key}` को कम से कम 0 की पूरी संख्या होना चाहिए, नहीं {n} @msg `{key}` must be an appearance item index (paint order) or null `{key}` एक उपस्थिति आइटम सूचकांक (पेंट ऑर्डर) या null होना चाहिए -@msg `{key}` must be an array of asset ids (see assets.list) `{key}` को परिसंपत्ति ids की एक सरणी होना चाहिए (देखें परिसंपत्तियों.list) +@msg `{key}` must be an array of asset ids (see assets.list) `{key}` को परिसंपत्ति ids की एक सरणी होना चाहिए (assets.list देखें) @msg `{key}` must be an array of these names: {_1} `{key}` इन नामों की एक सरणी होना चाहिए: {_1} @msg `{key}` must be between {min} and {max} (got {x}) `{key}` को {min} और {max} ({x}) के बीच होना चाहिए। @msg `{key}` must be from {_1} to {shift_max} `{key}` को {_1} से {shift_max} होना चाहिए @@ -5649,7 +5649,7 @@ antiAlias Sharp शार्प @msg `artboards` must be a list of artboard indices `artboards` को आर्टबोर्ड इंडेक्स की सूची होना चाहिए @msg `artboards`: all or a range such as 1, 3-5, not {v} `artboards`: all या 1, 3-5 जैसी सीमा दें, {v} नहीं @msg `aspect` must be a number (%) `aspect` को संख्या (%) होना चाहिए -@msg `asset` is the asset id (see assets.list) `asset` परिसंपत्ति आईडी है (aset.list देखें) +@msg `asset` is the asset id (see assets.list) `asset` परिसंपत्ति आईडी है (assets.list देखें) @msg `at` must be [x, y] `at` होना चाहिए [x, y] @msg `at` must be a byte offset `at` to be a byte ऑफसेट @msg `autoLeading` must be a number `autoLeading` को संख्या होना चाहिए @@ -6169,7 +6169,7 @@ antiAlias Sharp शार्प @msg closed must be a boolean closed: बंद होना चाहिए एक boolean @msg closing needs three anchors closing: बंद करने की जरूरत है तीन एंकर @msg collapsed must be true or false collapsed को true या false होना चाहिए -@msg color.profile: no RGB or CMYK profile is called “{profile}” Color.profile: कोई RGB या CMYK प्रोफ़ाइल को "{profile}" कहा जाता है। +@msg color.profile: no RGB or CMYK profile is called “{profile}” color.profile: "{profile}" नाम की कोई RGB या CMYK प्रोफ़ाइल नहीं है @msg colorMode must be rgb or cmyk, not `{m}` colorMode को rgb या cmyk होना चाहिए, `{m}` नहीं @msg colorModel must be {own} (the document's mode), grayscale or bitmap colorModel को {own} (डॉक्यूमेंट का मोड), grayscale या bitmap होना चाहिए @msg colorTable.{what}: `{h}` is not a colour such as "#ff8800" colorTable.{what}: `{h}`, "#ff8800" जैसा रंग नहीं है @@ -6497,11 +6497,11 @@ antiAlias Sharp शार्प @msg highlight two or more rows in one layer or group एक लेयर या समूह में दो या अधिक पंक्तियों को हाइलाइट करें @msg highlight two or more rows to merge दो या अधिक पंक्तियों को मर्ज करने के लिए हाइलाइट करें @msg HTML Text applies to object slices of type only एचटीएमएल टेक्स्ट केवल टाइप के ऑब्जेक्ट स्लाइस पर लागू होता है -@msg icon docks a panel in the icon column: use window.panel.dock आइकन कॉलम में एक पैनल डॉक करता है: विंडो का उपयोग करें.पैनल.dock +@msg icon docks a panel in the icon column: use window.panel.dock icon पैनल को आइकन कॉलम में डॉक करता है: window.panel.dock का उपयोग करें @msg icon must be true or false icon को true या false होना चाहिए @msg icon only docks a panel in the icon column, with no anchor आइकन केवल आइकन कॉलम में एक पैनल को डॉक करता है, जिसमें कोई एंकर नहीं होता है @msg id {value} is a layer, not an object Id {value} एक लेयर है, कोई ऑब्जेक्ट नहीं है -@msg id {value} is a layer, not an object; move layers with layer.move Id {value} एक लेयर है, कोई ऑब्जेक्ट नहीं है; लेयर के साथ लेयर्स को स्थानांतरित करें। +@msg id {value} is a layer, not an object; move layers with layer.move id {value} एक लेयर है, ऑब्जेक्ट नहीं; लेयर्स को layer.move से स्थानांतरित करें @msg id must be a dimension id id: आईडी एक आयाम आईडी होना चाहिए @msg Identify Objects By is XML ID: the name must be a valid XML ID (start with a letter, underscore or colon; no spaces) ऑब्जेक्ट्स को XML ID से पहचानें: नाम एक वैध XML ID होना चाहिए (एक पत्र, अंडरस्कोर या कॉलोन के साथ शुरू; कोई स्थान नहीं) @msg ids must contain 1–10000 object ids ids में 1–10000 ऑब्जेक्ट ids होना चाहिए @@ -6610,7 +6610,7 @@ antiAlias Sharp शार्प @msg kerning must be a number, "auto", or "optical" kerning एक संख्या, "auto", या "optical" होना चाहिए @msg keywords must be a list of words or a comma-separated string keywords: कीवर्ड शब्दों की एक सूची होना चाहिए या एक अल्पसंख्यक स्ट्रिंग होना चाहिए @msg keywords must be at most {MAX_KEYWORD} characters, without control characters keywords: कीवर्ड नियंत्रण वर्णों के बिना, अधिकांश {MAX_KEYWORD} वर्णों पर होना चाहिए -@msg kind must be 1, 2 or 3, or give a preset `name` (see perspective.presets.list) kind: टाइप 1, 2 या 3 होना चाहिए, या प्रीसेट `name` देना चाहिए (see पर्सपेक्टिव.presets.list) +@msg kind must be 1, 2 or 3, or give a preset `name` (see perspective.presets.list) kind 1, 2 या 3 होना चाहिए, या प्रीसेट `name` दें (perspective.presets.list देखें) @msg kind must be graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle or paraStyle kind को graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle या paraStyle होना चाहिए @msg knockout must be "on", "off", "neutral" or a bool, not {v} knockout: नॉकआउट को "on", "off", "neutral" या एक बोल, न {v} होना चाहिए @msg lang must be one of {_1} lang: लैंग को {_1} में से एक होना चाहिए @@ -6675,7 +6675,7 @@ antiAlias Sharp शार्प @msg missing `gid` (a glyph id, or null for the default glyph) लापता `gid` (a glyph id, या डिफ़ॉल्ट glyph के लिए null) @msg missing `group` लापता `group` @msg missing `id` लापता `id` -@msg missing `id` (see plugin.list) अनुपलब्ध `id` (information.list) +@msg missing `id` (see plugin.list) अनुपलब्ध `id` (plugin.list देखें) @msg missing `id`: give one, or select one type object लापता `id`: एक देना, या एक प्रकार का ऑब्जेक्ट चुनें @msg missing `index` (a point index, or null to clear) लापता `index` (a बिंदु सूचकांक, या स्पष्ट करने के लिए null) @msg missing `index` (a stop index, or null to clear) लापता `index` (a स्टॉप इंडेक्स, या स्पष्ट करने के लिए null) @@ -6790,7 +6790,7 @@ antiAlias Sharp शार्प @msg no artboards named नामित कोई आर्टबोर्ड नहीं @msg no artwork कोई आर्टवर्क नहीं @msg no asset {id} कोई परिसंपत्ति {id} -@msg no asset {id} (see assets.list) कोई परिसंपत्ति {id} (Ession.list) नहीं है। +@msg no asset {id} (see assets.list) कोई परिसंपत्ति {id} नहीं है (assets.list देखें) @msg no assets to export निर्यात करने के लिए कोई संपत्ति नहीं @msg no assets yet: collect art for export first (assets.add) अभी तक कोई संपत्ति नहीं: पहले निर्यात के लिए कला इकट्ठा (assets.add) @msg no blend options कोई मिश्रण ऑप्शन्स नहीं @@ -6887,7 +6887,7 @@ antiAlias Sharp शार्प @msg no pattern named `{choice}` `{choice}` नाम का कोई पैटर्न नहीं @msg no pattern swatch `{name}` कोई पैटर्न नहीं `{name}` @msg no PDF preset named `{n}` कोई PDF प्रीसेट नहीं है जिसका नाम `{n}` है -@msg no perspective grid preset named `{b}` (see perspective.presets.list) कोई पर्सपेक्टिव ग्रिड प्रीसेट जिसका नाम `{b}` है (see पर्सपेक्टिव.presets.list) +@msg no perspective grid preset named `{b}` (see perspective.presets.list) `{b}` नाम का कोई पर्सपेक्टिव ग्रिड प्रीसेट नहीं है (perspective.presets.list देखें) @msg no perspective grid preset named `{n}` कोई पर्सपेक्टिव ग्रिड प्रीसेट जिसका नाम `{n}` है @msg no perspective grid preset named `{name}` कोई पर्सपेक्टिव ग्रिड प्रीसेट जिसका नाम `{name}` है @msg no plug-in {0} is installed कोई प्लग-इन {0} स्थापित नहीं है @@ -6940,7 +6940,7 @@ antiAlias Sharp शार्प @msg no selection bounds कोई सिलेक्शन सीमा नहीं @msg no series {i} कोई श्रृंखला {i} @msg no similar objects found (rotated or reflected copies are not matched); change the Global Edit Options and try again कोई समान ऑब्जेक्ट नहीं पाया (घूर्णित या परिलक्षित प्रतियां मिलान नहीं की जाती हैं); वैश्विक संपादन ऑप्शन्स बदलें और फिर से प्रयास करें -@msg no slice {n} in the image (see slice.list) इमेज में कोई स्लाइस {n} नहीं ( स्लाइसलिस्ट देखें) +@msg no slice {n} in the image (see slice.list) इमेज में कोई स्लाइस {n} नहीं है (slice.list देखें) @msg no slice selected सिलेक्टेड कोई स्लाइस नहीं @msg no source art कोई स्रोत कला @msg no stop {i} (the gradient has {n}) नहीं रोक {i} (ग्रेडेंट {n} है) @@ -6950,7 +6950,7 @@ antiAlias Sharp शार्प @msg no such action step ऐसी कोई कार्रवाई नहीं @msg no such anchor ऐसा कोई एंकर नहीं @msg no such artboard ऐसा कोई आर्टबोर्ड नहीं -@msg no such assets (see assets.list) ऐसी कोई संपत्ति नहीं (देखें परिसंपत्तियां)। +@msg no such assets (see assets.list) ऐसी कोई परिसंपत्ति नहीं है (assets.list देखें) @msg no such color ऐसा कोई रंग नहीं @msg no such document ऐसा कोई डॉक्यूमेंट नहीं @msg no such frame anchor, or the frame would reach past the canvas इस तरह के फ्रेम एंकर नहीं, या फ्रेम कैनवास तक पहुंच जाएगा @@ -7130,7 +7130,7 @@ antiAlias Sharp शार्प @msg only this Affinity file's embedded preview could be read: use File › Open to see it with its warning, or export SVG or PDF from Affinity first केवल इस Affinity फ़ाइल के एम्बेडेड प्रीव्यू को पढ़ा जा सकता है: फ़ाइल का उपयोग करें > इसे अपनी चेतावनी के साथ देखने के लिए ओपन करें, या पहले Affinity से SVG या PDF निर्यात करें @msg onto must name a floating panel onto: पर एक फ्लोटिंग पैनल का नाम होना चाहिए @msg onto must name a panel floating in another group onto: किसी अन्य समूह में फ्लोटिंग पैनल का नाम होना चाहिए -@msg onto, above, below, icon and a column place belong to window.panel.float and window.panel.dock ऊपर, नीचे, आइकन और एक स्तंभ स्थान खिड़की से संबंधित है। पैनल.फ्लोट और विंडो.पैनल.डॉक +@msg onto, above, below, icon and a column place belong to window.panel.float and window.panel.dock onto, above, below, icon और column स्थान window.panel.float और window.panel.dock के लिए हैं @msg onto, above, below, icon and a column place take no anchor, side, zone or before ऊपर, नीचे, आइकन और एक स्तंभ स्थान पर कोई एंकर, साइड, जोन या पहले नहीं होता है @msg onto, above, below, icon and a column place take no x, y, width or height ऊपर, नीचे, आइकन और एक स्तंभ स्थान पर कोई एक्स, वाई, चौड़ाई या ऊंचाई नहीं होती है @msg open a document to look for its linked files अपनी लिंक्ड फ़ाइलों को देखने के लिए एक डॉक्यूमेंट ओपन करें @@ -7144,10 +7144,10 @@ antiAlias Sharp शार्प @msg outlineText {v}: true or false outlineText {v}: true या false @msg output intent profile: {e} उत्पादन आशय प्रोफ़ाइल: {e} @msg output size ({what} is {len} bytes, limit {max}) उत्पादन का आकार ({what} {len} बाइट्स है, {max} को सीमित करें) -@msg output.destination: {_1} files are CMYK, and “{name}” is an RGB profile उत्पादन: {_1} फ़ाइलें CMYK हैं, और "{name}" एक RGB प्रोफ़ाइल है -@msg output.destination: {e} उत्पादन.destination: {e} -@msg output.destination: no RGB or CMYK profile is called “{name}” आउटपुट: कोई RGB या CMYK प्रोफ़ाइल को "{name}" कहा जाता है। -@msg output.inks: no ink is set to print आउटपुट.ink: प्रिंट करने के लिए कोई स्याही सेट नहीं है +@msg output.destination: {_1} files are CMYK, and “{name}” is an RGB profile output.destination: {_1} फ़ाइलें CMYK हैं, और "{name}" एक RGB प्रोफ़ाइल है +@msg output.destination: {e} output.destination: {e} +@msg output.destination: no RGB or CMYK profile is called “{name}” output.destination: "{name}" नाम की कोई RGB या CMYK प्रोफ़ाइल नहीं है +@msg output.inks: no ink is set to print output.inks: प्रिंट करने के लिए कोई स्याही सेट नहीं है @msg output.outputIntent: “{name}” is a grey profile, which can't be a {std} output intent output.outputIntent: “{name}” ग्रे प्रोफ़ाइल है, जो {std} आउटपुट इंटेंट नहीं हो सकती @msg output.outputIntent: {std} files print in CMYK, and “{name}” is an RGB profile output.outputIntent: {std} फ़ाइलें CMYK में प्रिंट होती हैं और “{name}” RGB प्रोफ़ाइल है @msg output.outputIntent: no profile is called “{name}”, and a {std} output intent embeds its profile output.outputIntent: “{name}” नाम की प्रोफ़ाइल नहीं है और {std} आउटपुट इंटेंट अपनी प्रोफ़ाइल एम्बेड करता है @@ -7483,7 +7483,7 @@ antiAlias Sharp शार्प @msg text.{key} `{v}`: use "{_1}" or "{_2}" Text.{key} `{v}`: "{_1}" या "{_2}" का उपयोग करें। @msg text.replaceSpaces must be a whole number from 2 to 100 text.replaceSpaces को 2 से 100 के बीच पूर्णांक होना चाहिए @msg textureImage: {e} textureImage: {e} -@msg textureImage: give {path} or {name, dataBase64} of an image file, or a texture as effect.list gives it textureImage: एक इमेज फ़ाइल के {path} या {name, dataBase64}, या इफ़ेक्ट के रूप में बनावट देते हैं। +@msg textureImage: give {path} or {name, dataBase64} of an image file, or a texture as effect.list gives it textureImage: किसी इमेज फ़ाइल का {path} या {name, dataBase64} दें, या effect.list द्वारा दिया गया टेक्सचर दें @msg that anchor can't slide: an end of an open path has nowhere to go कि एंकर स्लाइड नहीं कर सकते: एक खुले रास्ते का अंत कहीं नहीं जाना है @msg that document is no longer open यह डॉक्यूमेंट अब खुला नहीं है @msg that makes more than {MAX_PIECES} slices जो {MAX_PIECES} स्लाइस से अधिक बनाता है diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs index 79cbd7a64..baca06cac 100644 --- a/crates/ui-egui/src/i18n/tests.rs +++ b/crates/ui-egui/src/i18n/tests.rs @@ -2510,6 +2510,61 @@ fn translated_messages_keep_the_keys_and_values_their_english_names() { assert!(missing.is_empty(), "{} rows drop a key or value their English names:\n{}", missing.len(), missing.join("\n")); } +/// The command ids that `s` names outside its backticked spans, such as `assets.list` or +/// `window.panel.dock`: words of two or more dot-separated parts, each an ASCII letter or digit +/// run that starts with a lowercase letter. +fn command_ids(s: &str) -> Vec<&str> { + let mut ids = Vec::new(); + for part in s.split('`').step_by(2) { + for word in part.split(|c: char| !(c.is_ascii_alphanumeric() || c == '.' || c == '_')) { + let word = word.trim_end_matches('.'); + let segments: Vec<&str> = word.split('.').collect(); + let dotted = segments.len() > 1 + && segments + .iter() + .all(|seg| seg.chars().next().is_some_and(|c| c.is_ascii_lowercase()) && seg.chars().all(|c| c.is_ascii_alphanumeric())); + if dotted && word != "e.g" && word != "i.e" { + ids.push(word); + } + } + } + ids +} + +#[test] +fn command_ids_finds_dotted_ids_outside_backticks() { + assert_eq!(command_ids("no slice {n} in the image (see slice.list)"), ["slice.list"]); + assert_eq!(command_ids("use window.panel.dock."), ["window.panel.dock"]); + assert_eq!(command_ids("output.destination: {e}"), ["output.destination"]); + assert_eq!(command_ids("(assets.list.old देखें)"), ["assets.list.old"]); + assert_eq!(command_ids("see `plugin.list`, e.g. 1.5 or {name}.svg"), Vec::<&str>::new()); +} + +/// A translated status or error message keeps each command id its English text names without +/// backticks, spelled as the English spells it, since that is the id a script or the command +/// palette runs. +#[test] +fn translated_messages_keep_the_command_ids_their_english_names() { + let mut missing = Vec::new(); + for l in &LANGUAGES { + let (entries, _) = parse_entries(l.source); + for (ctx, src, tr) in entries.iter().filter(|(ctx, _, _)| ctx == "@msg" || ctx == "@msgplural") { + let plural = ctx == "@msgplural"; + let english = if plural { src.rsplit('|').next().unwrap_or(src) } else { src.as_str() }; + let forms: Vec<&str> = if plural { tr.split('|').collect() } else { vec![tr.as_str()] }; + for form in forms { + let kept: Vec<&str> = command_ids(form).into_iter().chain(backticked(form)).collect(); + for id in command_ids(english) { + if !kept.contains(&id) { + missing.push(format!("{}: {id} in {src:?} -> {form:?}", l.code)); + } + } + } + } + } + assert!(missing.is_empty(), "{} rows drop a command id their English names:\n{}", missing.len(), missing.join("\n")); +} + /// Languages whose catalogs translate status and error messages; [`untranslated_report`] lists /// the messages each one lacks. const COMPLETE_MESSAGES: &[&str] = &[ diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index d45a171cc..a9736063f 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -4471,7 +4471,7 @@ axis Both Her İkisi @msg `prefs.import` runs on its own, not as a batch step `prefs.import` tek başına çalışır, toplu iş adımı olarak değil @msg unknown preset (one of {_1}) bilinmeyen ön ayar ({_1}'den biri) @msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) bilinmeyen ön ayar `{name}` (yüksek, orta, düşük veya kaydedilmiş bir ayar: bkz. flattener.presets.list) -@msg unknown preset `{name}` (see flattener.presets.list) bilinmeyen ön ayar `{name}` (bkz. düzleştirici.presets.list) +@msg unknown preset `{name}` (see flattener.presets.list) bilinmeyen ön ayar `{name}` (bkz. flattener.presets.list) @msg unknown preset `{name}` (see webExport.presets.list) bilinmeyen ön ayar `{name}` (bkz. webExport.presets.list) @msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) bilinmeyen ön ayar `{name}`: yerleşik ya da kayıtlı bir ön ayar değil (bkz. imageTrace.presets) @msg unknown side `{other}` (left|right|top|bottom) bilinmeyen taraf `{other}` (sol|sağ|üst|alt)