mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-22 18:22:07 +00:00
Add Greek (el) interface localization (#1307)
Co-authored-by: Camthalion77 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
8 files changed
+1410
-6
No files matched your search
@@ -70,3 +70,4 @@ Generated-in-code assets are original and have no file to list:
|
||||
| `crates/ui-egui/src/i18n/ar.tsv` | @hasan-sdaif | Original Arabic translations of CADCraft English strings | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | No proprietary localization resources copied |
|
||||
| `crates/ui-egui/src/i18n/hu.tsv` | @Skaywalker | Original Hungarian translations of CADCraft English strings | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | No proprietary localization resources copied |
|
||||
| `crates/ui-egui/src/i18n/tr.tsv` | @ogzhnklvz (AI-assisted) | Original Turkish translations of CADCraft English strings | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | No proprietary localization resources copied; written from the English strings, no other catalog used as a source; the running app reviewed by a native speaker |
|
||||
| `crates/ui-egui/src/i18n/el.tsv` | @Camthalion77 with ChatGPT (AI-assisted) | Original Greek translations of CADCraft English strings | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | No proprietary localization resources copied; independent native proofreading pending |
|
||||
@@ -94,6 +94,7 @@ Ukrainian and German. Detail: [localization-parity.md](docs/localization-parity.
|
||||
| Portuguese | none | 0% |
|
||||
| Indonesian | none | 0% |
|
||||
| Japanese | none | 0% |
|
||||
| Greek | complete current catalog | 1,330 current source keys; independent proofreading pending |
|
||||
| German | partial | ≈ 100% of the catalog (989 rows) |
|
||||
| Korean | none | 0% |
|
||||
| Vietnamese | none | 0% |
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -15,6 +15,8 @@ pub enum Preference {
|
||||
Auto,
|
||||
#[serde(rename = "en")]
|
||||
English,
|
||||
#[serde(rename = "el")]
|
||||
Greek,
|
||||
#[serde(rename = "uk")]
|
||||
Ukrainian,
|
||||
#[serde(rename = "pt-BR")]
|
||||
@@ -46,9 +48,10 @@ pub enum Preference {
|
||||
}
|
||||
|
||||
impl Preference {
|
||||
pub const ALL: [Self; 16] = [
|
||||
pub const ALL: [Self; 17] = [
|
||||
Self::Auto,
|
||||
Self::English,
|
||||
Self::Greek,
|
||||
Self::Ukrainian,
|
||||
Self::BrazilianPortuguese,
|
||||
Self::German,
|
||||
@@ -78,6 +81,7 @@ impl Preference {
|
||||
match self {
|
||||
Self::Auto => "auto",
|
||||
Self::English => "en",
|
||||
Self::Greek => "el",
|
||||
Self::Ukrainian => "uk",
|
||||
Self::BrazilianPortuguese => "pt-BR",
|
||||
Self::German => "de",
|
||||
@@ -99,6 +103,7 @@ impl Preference {
|
||||
match self {
|
||||
Self::Auto => t("Auto (system language)"),
|
||||
Self::English => "English",
|
||||
Self::Greek => "Ελληνικά",
|
||||
Self::Ukrainian => "Українська",
|
||||
Self::BrazilianPortuguese => "Português (Brasil)",
|
||||
Self::German => "Deutsch",
|
||||
@@ -134,6 +139,7 @@ impl Preference {
|
||||
pub fn resolve(self, locales: &[String]) -> &'static str {
|
||||
match self {
|
||||
Self::English => "en",
|
||||
Self::Greek => "el",
|
||||
Self::Ukrainian => "uk",
|
||||
Self::BrazilianPortuguese => "pt-BR",
|
||||
Self::German => "de",
|
||||
@@ -164,8 +170,10 @@ pub struct Language {
|
||||
catalog: OnceLock<Catalog<'static>>,
|
||||
}
|
||||
|
||||
pub static LANGUAGES: [Language; 15] = [
|
||||
pub static LANGUAGES: [Language; 16] = [
|
||||
Language { code: "en", name: "English", source: "", plural: plural_en, plural_forms: 2, catalog: OnceLock::new() },
|
||||
// Greek: one for exactly 1; other for zero and all other integer counts.
|
||||
Language { code: "el", name: "Ελληνικά", source: include_str!("el.tsv"), plural: plural_en, plural_forms: 2, catalog: OnceLock::new() },
|
||||
Language {
|
||||
code: "uk", name: "Українська", source: include_str!("uk.tsv"), plural: plural_uk, plural_forms: 3, catalog: OnceLock::new()
|
||||
},
|
||||
|
||||
@@ -39,7 +39,7 @@ fn catalog_is_valid_and_preserves_placeholders_and_ellipses() {
|
||||
/// to English for what they don't cover yet; their gaps are reported, not enforced, so adding a
|
||||
/// command doesn't require a row in every language.
|
||||
const REQUIRED: &[&str] = &["uk"];
|
||||
const REPORTED: &[&str] = &["pt-BR", "de", "es", "cs", "ru", "fr", "zh", "it", "ko", "sv", "ar", "hu", "tr"];
|
||||
const REPORTED: &[&str] = &["pt-BR", "de", "es", "cs", "ru", "fr", "zh", "it", "ko", "sv", "ar", "hu", "tr", "el"];
|
||||
|
||||
fn check(language: &str, what: &str, missing: Vec<String>) {
|
||||
if REQUIRED.contains(&language) {
|
||||
@@ -1129,3 +1129,46 @@ fn default_fonts_cover_every_turkish_letter_in_the_catalog() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greek_catalog_locale_preferences_and_contexts() {
|
||||
assert_eq!(Preference::parse("el"), Some(Preference::Greek));
|
||||
assert_eq!(Preference::Greek.code(), "el");
|
||||
assert_eq!(Preference::Greek.name(), "Ελληνικά");
|
||||
for locale in ["el", "el-GR", "el_GR.UTF-8", "EL-cy"] {
|
||||
assert_eq!(locale_language(locale), Some("el"));
|
||||
}
|
||||
assert_eq!(locale_language("elvish"), None);
|
||||
assert_eq!(Preference::Greek.resolve(&["en-US".into()]), "el");
|
||||
let saved = serde_json::to_string(&Preference::Greek).unwrap();
|
||||
assert_eq!(saved, "\"el\"");
|
||||
assert_eq!(serde_json::from_str::<Preference>(&saved).unwrap(), Preference::Greek);
|
||||
assert_eq!(tr("el", "Layer"), "Επίπεδο (Layer)");
|
||||
assert_eq!(tr_menu("el", "Dimension"), "Διαστασιολόγηση");
|
||||
assert_eq!(tr("el", "Dimension"), "Διαστασιολόγηση (Dimension)");
|
||||
with_language("el", || {
|
||||
assert_eq!(t("Open"), "Άνοιγμα");
|
||||
assert_eq!(count(1, "{n} contributor", "{n} contributors"), "1 συνεισφέρων");
|
||||
assert_eq!(count(0, "{n} contributor", "{n} contributors"), "0 συνεισφέροντες");
|
||||
assert_eq!(count(2, "{n} contributor", "{n} contributors"), "2 συνεισφέροντες");
|
||||
assert_eq!(count(21, "{n} contributor", "{n} contributors"), "21 συνεισφέροντες");
|
||||
});
|
||||
assert_eq!(t("Open"), "Open", "language must not leak outside a render scope");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_fonts_cover_all_greek_letters_in_the_catalog() {
|
||||
let ctx = egui::Context::default();
|
||||
let mut output = ctx.run_ui(Default::default(), |_| {});
|
||||
output.textures_delta.clear();
|
||||
let letters: BTreeSet<_> =
|
||||
include_str!("el.tsv").chars().filter(|c| ('\u{0370}'..='\u{03ff}').contains(c) || ('\u{1f00}'..='\u{1fff}').contains(c)).collect();
|
||||
ctx.fonts_mut(|view| {
|
||||
for family in [egui::FontFamily::Proportional, egui::FontFamily::Monospace] {
|
||||
let mut font = view.fonts.font(&family);
|
||||
for c in &letters {
|
||||
assert!(font.characters().contains_key(c), "missing Greek glyph {c} in {family:?}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
//! |---|---|
|
||||
//! | `theme` | `"system"`, `"light"` or `"dark"` |
|
||||
//! | `animations` | `"system"`, `"on"` or `"off"`: dialog and menu fades and smoothed scrolling |
|
||||
//! | `interfaceLanguage` | `"auto"`, `"en"`, `"uk"`, `"pt-BR"`, `"de"`, `"es"`, `"cs"`, `"ru"`, `"fr"`, `"zh"`, `"it"`, `"ko"`, `"sv"`, `"ar"`, `"hu"` or `"tr"` |
|
||||
//! | `interfaceLanguage` | `"auto"`, `"en"`, `"uk"`, `"pt-BR"`, `"de"`, `"es"`, `"cs"`, `"ru"`, `"fr"`, `"zh"`, `"it"`, `"ko"`, `"sv"`, `"ar"`, `"hu"`, `"tr"` or `"el"` |
|
||||
//! | `saveFormat` | `"dxf"` or `"dwg"`: the format Save As suggests for new drawings |
|
||||
//! | `saveVersion` | `"2000"` … `"2018"`: the version drawings with no file yet are saved in |
|
||||
//! | `SAVETIME` | minutes between automatic saves, 0–600 (0: off) |
|
||||
|
||||
@@ -62,6 +62,7 @@ plus native review by a human; extending the catalog to the remaining English-on
|
||||
| Indonesian | id | 0 (0%) | 0% | Latin: yes | no | none | 3–5 h |
|
||||
| Japanese | ja | 0 (0%) | 0% | CJK glyphs render; IME pending; no vertical text | no | none | 4–6 h |
|
||||
| German | de | 989 catalog rows (≈ 100% of the catalog; ≈ 80% of all UI strings, estimated: engine diagnostics and uncatalogued text stay English) | dialogs and tooltips yes; command reference searchable in German | Latin: yes (bundled egui fonts cover ä ö ü Ä Ö Ü ß; long strings will need layout checks) | no native review | partial | 1–2 h |
|
||||
| Greek | el | 1,330 catalog rows (all collected existing source keys) | Menus, dialogs, tooltips, drawing prompts, command labels and contextual headings; English fallback for uncatalogued UI | Greek script; bundled egui glyph check added | Original AI-assisted translation by @Camthalion77, independent native review pending | complete current catalog; uncatalogued UI remains English | native proofreading and platform layout review |
|
||||
| Korean | ko | 1,235 rows (1,229 ordinary, 4 menu, 2 count); full existing catalog union and marked UI coverage | dialogs, tooltips, Options and translated command-reference labels | UI requires catalog-covering installed or optional craft-fonts Hang/Kore face; English fallback without one; IME fixes still pending | original AI-assisted translation; independent native proofreading pending | complete current catalog; English-only interface gaps remain | native review + platform layout/IME validation |
|
||||
| Vietnamese | vi | 0 (0%) | 0% | Latin with stacked diacritics: renders per code point, untested | no | none | 3–5 h |
|
||||
| Ukrainian | uk | 989 catalog rows (≈ 100% of the catalog; ≈ 80% of all UI strings, estimated: engine diagnostics and uncatalogued text stay English) | dialogs and tooltips yes; command reference searchable in Ukrainian | Cyrillic: yes (bundled egui fonts) | translated by a contributor; no second review | partial | 2–4 h |
|
||||
@@ -85,6 +86,7 @@ each language, which needs humans. Translations parallelise fully once the catal
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-15 | minor | Greek (`el`) catalog: 1,330 rows, context/plural/locale/font tests; review pending |
|
||||
| 2026-10-14 | minor | Swedish completed: 95 rows for strings added since (Hatch and Gradient dialog, Block Editor, REFEDIT, multileader editing, Options selection settings, MTEXT editor, recent files); 1,330 rows, every key of the Ukrainian reference catalog |
|
||||
| 2026-10-18 | minor | Turkish added: 1,330 rows (the existing catalog union), single count form, four menu headings, prompt keywords kept in English; locale/preferences/menu/live-switch/dialog/font tests; no gaps in the marked UI literals; AI-assisted, the running app reviewed by a native speaker |
|
||||
| 2026-10-13 | minor | Hungarian added: 1,326 rows, a single count form, menu headings, locale/preferences/menu/font/render tests; no gaps in the marked UI literals; native proofreading pending |
|
||||
|
||||
+19
-2
@@ -1,7 +1,7 @@
|
||||
# Interface languages
|
||||
|
||||
Choose **Edit → Interface Language** and select **Auto (system language)**, **English**,
|
||||
**Українська**, **Português (Brasil)**, **Deutsch**, **Español**, **Čeština** (Czech), **Русский**, **Français**, **简体中文**, **Italiano**, **한국어** (Korean), **Svenska** (Swedish), **العربية** (Arabic), **Magyar** (Hungarian), or **Türkçe** (Turkish).
|
||||
**Українська**, **Português (Brasil)**, **Deutsch**, **Español**, **Čeština** (Czech), **Русский**, **Français**, **简体中文**, **Italiano**, **한국어** (Korean), **Svenska** (Swedish), **العربية** (Arabic), **Magyar** (Hungarian), **Türkçe** (Turkish), or **Ελληνικά** (Greek).
|
||||
The interface updates without restarting, including the native macOS menus.
|
||||
The choice is saved with the other interface preferences (`cadcraft.prefs`, beside the theme):
|
||||
eframe storage on the desktop, browser local storage on the web. Installations without a saved
|
||||
@@ -22,7 +22,7 @@ Automation can select the language through the existing control channel:
|
||||
```
|
||||
|
||||
The `ui.set` method also accepts `{"interfaceLanguage":"cs"}`. Supported values are `auto`,
|
||||
`en`, `uk`, `pt-BR`, `de`, `es`, `cs`, `ru`, `fr`, `zh`, `it`, `ko`, `sv`, `ar`, `hu`, and `tr`; invalid values are rejected without changing the current
|
||||
`en`, `uk`, `pt-BR`, `de`, `es`, `cs`, `ru`, `fr`, `zh`, `it`, `ko`, `sv`, `ar`, `hu`, `tr`, and `el`; invalid values are rejected without changing the current
|
||||
selection. Resetting palettes keeps the language choice.
|
||||
|
||||
## Translation coverage
|
||||
@@ -98,6 +98,23 @@ catalog, the saved `ko` preference is kept but the interface falls back to Engli
|
||||
command reports that a font is needed, and the picker uses the readable label **Korean** wherever
|
||||
Hangul can't be shown. This change does not fix platform IME issues or localize engine output.
|
||||
|
||||
## Greek (`el`) interface
|
||||
|
||||
The Greek translation covers all **1,330** distinct source keys collected from the existing
|
||||
language catalogs: **1,324** ordinary interface labels, **4** contextual menu headings and
|
||||
**2** integer-count templates. The interface can be selected as **Ελληνικά** under
|
||||
Edit → Interface Language, or automatically when the system language is `el-GR` or `el-CY`.
|
||||
Greek integer plurals distinguish 1 from all other counts.
|
||||
|
||||
The terminology follows native Greek CAD vocabulary; specialist terms retain the English
|
||||
name in parentheses when it helps professionals match a known CAD command (for example,
|
||||
**Επίπεδο (Layer)**, **Πολυγραμμή (Polyline)** and **Διαγράμμιση (Hatch)**).
|
||||
Common actions such as Open and Save appear simply in Greek. Existing command identifiers,
|
||||
drawing content, names of user objects and file formats remain unchanged. The translation is
|
||||
original AI-assisted work contributed by @Camthalion77 with language review; it is not sourced
|
||||
from Autodesk localization files. Independent native proofreading and visual checks remain
|
||||
follow-up work.
|
||||
|
||||
## Adding translations
|
||||
|
||||
The implementation follows the display-time catalog pattern used by
|
||||
|
||||
Reference in new issue
Block a user