mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 02:46:51 -06:00
Interface Language dialog: list the languages alphabetically by their own name (#1787)
* Interface Language dialog: list the languages alphabetically by their own name - English (and Auto where offered) stays first as the source language and fallback; the rest sort by native name with accents folded, so Čeština is under C and Magyar under M; Cyrillic and CJK names follow the Latin ones - The list used the registry order, which is the order the languages were added * Options dialog lists interface languages in the same order as the picker Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
Brandon Thomas
parent
dadd1f227f
commit
25faf34d28
@@ -414,7 +414,7 @@ fn insert_block(app: &mut CadApp, name: &str) {
|
||||
fn language(app: &mut CadApp, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(crate::tl!("Interface Language")).id(egui::Id::new("interface_language")).open(open).resizable(false).show(ctx, |ui| {
|
||||
egui::ComboBox::from_id_salt("interface_language_choice").selected_text(app.ui.interface_language.name()).show_ui(ui, |ui| {
|
||||
for preference in crate::i18n::Preference::ALL {
|
||||
for preference in crate::i18n::Preference::for_picker() {
|
||||
if ui.selectable_label(app.ui.interface_language == preference, preference.name()).clicked() {
|
||||
let _ = app.run("ui.language", serde_json::json!({"lang": preference.code()}));
|
||||
ui.ctx().request_repaint();
|
||||
|
||||
@@ -68,6 +68,23 @@ impl Preference {
|
||||
Self::Turkish,
|
||||
];
|
||||
|
||||
/// Every preference in the order the Interface Language dialog lists them: Auto, then English
|
||||
/// (the source language), then the rest by the language's own name with accents folded, so
|
||||
/// Čeština sits under C and Magyar under M. Names in other scripts follow the Latin ones in
|
||||
/// code point order: Greek, Cyrillic, Arabic, then the CJK languages. The order follows the
|
||||
/// registry's native name even where the dialog shows an English label instead (Korean
|
||||
/// without a Hangul face), so the list does not reorder when a font appears.
|
||||
pub fn for_picker() -> Vec<Self> {
|
||||
let mut rest: Vec<Self> = Self::ALL.into_iter().filter(|p| !matches!(p, Self::Auto | Self::English)).collect();
|
||||
rest.sort_by_cached_key(|p| name_sort_key(p.native_name()));
|
||||
[Self::Auto, Self::English].into_iter().chain(rest).collect()
|
||||
}
|
||||
|
||||
/// The language's own name from the registry, whatever the dialog currently shows for it.
|
||||
fn native_name(self) -> &'static str {
|
||||
LANGUAGES.iter().find(|l| l.code.eq_ignore_ascii_case(self.code())).map_or_else(|| self.name(), |l| l.name)
|
||||
}
|
||||
|
||||
/// The codes a preference can be given as, for messages ("auto, en or uk").
|
||||
pub fn codes() -> String {
|
||||
let codes: Vec<&str> = Self::ALL.iter().map(|p| p.code()).collect();
|
||||
@@ -377,3 +394,43 @@ macro_rules! tf {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// `name` lowercased with the Latin accents that occur in language names folded away, so a
|
||||
/// byte-wise sort reads alphabetically. Other scripts are left as they are.
|
||||
fn name_sort_key(name: &str) -> String {
|
||||
name.chars()
|
||||
.flat_map(char::to_lowercase)
|
||||
.map(|c| match c {
|
||||
'à'..='å' | 'ā' | 'ă' | 'ą' => 'a',
|
||||
'ç' | 'ć' | 'č' => 'c',
|
||||
'è'..='ë' | 'ē' | 'ě' | 'ę' | 'ế' | 'ệ' => 'e',
|
||||
'ì'..='ï' | 'ī' => 'i',
|
||||
'ñ' | 'ń' | 'ň' => 'n',
|
||||
'ò'..='ö' | 'ø' | 'ő' => 'o',
|
||||
'ś' | 'š' => 's',
|
||||
'ù'..='ü' | 'ū' | 'ů' | 'ű' => 'u',
|
||||
'ý' | 'ÿ' => 'y',
|
||||
'ź' | 'ż' | 'ž' => 'z',
|
||||
c => c,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod picker_order_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn picker_lists_auto_and_english_first_then_names_alphabetically() {
|
||||
let order = Preference::for_picker();
|
||||
assert_eq!(&order[..2], &[Preference::Auto, Preference::English]);
|
||||
assert_eq!(order.len(), Preference::ALL.len());
|
||||
let keys: Vec<String> = order[2..].iter().map(|p| name_sort_key(p.native_name())).collect();
|
||||
assert!(keys.windows(2).all(|w| w[0] <= w[1]), "{keys:?}");
|
||||
let pos = |p: Preference| order.iter().position(|q| *q == p).unwrap_or(usize::MAX);
|
||||
assert!(pos(Preference::Czech) < pos(Preference::German), "Čeština sorts under C");
|
||||
assert!(pos(Preference::Italian) < pos(Preference::Hungarian), "Magyar sorts under M");
|
||||
assert!(pos(Preference::Turkish) < pos(Preference::Greek), "Latin names come before Greek");
|
||||
assert_eq!(name_sort_key("Türkçe"), "turkce");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,7 +467,7 @@ fn preferences(app: &CadApp, ui: &mut egui::Ui, changes: &mut Map<String, Value>
|
||||
ui.label(crate::tl!("Interface language"));
|
||||
let mut lang = app.ui.interface_language;
|
||||
egui::ComboBox::from_id_salt("options_language").selected_text(lang.name()).show_ui(ui, |ui| {
|
||||
for p in crate::i18n::Preference::ALL {
|
||||
for p in crate::i18n::Preference::for_picker() {
|
||||
ui.selectable_value(&mut lang, p, p.name());
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user