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:
authored and GitHub committed 2026-10-19 03:02:49 -04:00
1 parent c02027abc8
commit 323f4629eb
8 files changed
+1410 -6

No files matched your search

+1
View File
@@ -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 |
+1
View File
@@ -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
+10 -2
View File
@@ -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()
},
+44 -1
View File
@@ -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:?}");
}
}
});
}
+1 -1
View File
@@ -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) |
+2
View File
@@ -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
View File
@@ -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