fix(time): icu locale requires hyphenated lang codes
The recent icu 2.0.0 update broke `icu::locale::Locale` parsing. Lang codes
containing underscores are no longer valid, so we need to hyphenate the lang
code before parsing it. I've added `replacen('_', "-", 1)` everywhere that an
`icu::locale::Locale` is parsed from a string.
To prevent the need to re-parse the locale on every view update, I've also
added `icu::locale::Locale`s for `LC_TIME` and `LC_NUMERIC` directly to
`region::Page` the moment the page is refreshed.
Closes #1384
This commit is contained in:
parent
24796fc46a
commit
6274a66ae9
2 changed files with 47 additions and 81 deletions
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright 2023 System76 <info@system76.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::{Datelike, Timelike};
|
||||
use cosmic::{
|
||||
Apply, Element, Task,
|
||||
|
|
@ -507,14 +505,15 @@ fn timezone() -> Section<crate::pages::Message> {
|
|||
}
|
||||
|
||||
fn locale() -> Result<Locale, Box<dyn std::error::Error>> {
|
||||
let locale = std::env::var("LC_TIME").or_else(|_| std::env::var("LANG"))?;
|
||||
let locale = locale
|
||||
let locale_env = std::env::var("LC_TIME").or_else(|_| std::env::var("LANG"))?;
|
||||
|
||||
locale_env
|
||||
.split('.')
|
||||
.next()
|
||||
.ok_or(format!("Can't split the locale {locale}"))?;
|
||||
|
||||
let locale = Locale::from_str(locale).map_err(|e| format!("{e:?}"))?;
|
||||
Ok(locale)
|
||||
.ok_or(format!("Can't split the locale {locale_env}"))?
|
||||
.replacen("_", "-", 1)
|
||||
.parse::<Locale>()
|
||||
.map_err(|e| format!("{e:?}").into())
|
||||
}
|
||||
|
||||
fn format_date(date: &DateTime<Gregorian>, military: bool, show_seconds: bool) -> String {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cosmic::app::{ContextDrawer, context_drawer};
|
||||
|
|
@ -32,7 +31,6 @@ pub enum Message {
|
|||
AddLanguage(DefaultKey),
|
||||
AddLanguageContext,
|
||||
AddLanguageSearch(String),
|
||||
SystemLocales(SlotMap<DefaultKey, SystemLocale>),
|
||||
ExpandLanguagePopover(Option<usize>),
|
||||
InstallAdditionalLanguages,
|
||||
SelectRegion(DefaultKey),
|
||||
|
|
@ -115,6 +113,10 @@ pub struct Page {
|
|||
registry: Option<locale::Registry>,
|
||||
expanded_source_popover: Option<usize>,
|
||||
add_language_search: String,
|
||||
/// Cached LC_NUMERIC locale in icu locale format.
|
||||
numeric_locale: Option<Locale>,
|
||||
/// Cached LC_TIME locale in icu locale format.
|
||||
time_locale: Option<Locale>,
|
||||
}
|
||||
|
||||
impl page::Page<crate::pages::Message> for Page {
|
||||
|
|
@ -251,10 +253,6 @@ impl Page {
|
|||
self.add_language_search = search;
|
||||
}
|
||||
|
||||
Message::SystemLocales(languages) => {
|
||||
self.available_languages = languages;
|
||||
}
|
||||
|
||||
Message::ExpandLanguagePopover(id) => {
|
||||
self.expanded_source_popover = id;
|
||||
}
|
||||
|
|
@ -277,6 +275,8 @@ impl Page {
|
|||
self.language = page_refresh.language;
|
||||
self.region = page_refresh.region;
|
||||
self.registry = Some(page_refresh.registry.0);
|
||||
self.numeric_locale = self.icu_locale_from_env("LC_NUMERIC");
|
||||
self.time_locale = self.icu_locale_from_env("LC_TIME");
|
||||
}
|
||||
|
||||
Err(why) => {
|
||||
|
|
@ -397,17 +397,21 @@ impl Page {
|
|||
list.apply(Element::from).map(crate::pages::Message::Region)
|
||||
}
|
||||
|
||||
fn formatted_date(&self) -> String {
|
||||
let time_locale = self
|
||||
.system_locales
|
||||
.get("LC_TIME")
|
||||
fn icu_locale_from_env(&self, key: &'static str) -> Option<Locale> {
|
||||
self.system_locales
|
||||
.get(key)
|
||||
.or_else(|| self.system_locales.get("LANG"))
|
||||
.map_or("en_US", |locale| &locale.lang_code)
|
||||
.map_or("en-US", |locale| &locale.lang_code)
|
||||
.split('.')
|
||||
.next()
|
||||
.unwrap_or("en_US");
|
||||
.unwrap_or("en-US")
|
||||
.replacen('_', "-", 1)
|
||||
.parse::<Locale>()
|
||||
.ok()
|
||||
}
|
||||
|
||||
let Ok(locale) = Locale::from_str(time_locale) else {
|
||||
fn formatted_date(&self) -> String {
|
||||
let Some(locale) = self.time_locale.as_ref() else {
|
||||
return String::new();
|
||||
};
|
||||
|
||||
|
|
@ -423,16 +427,7 @@ impl Page {
|
|||
}
|
||||
|
||||
fn formatted_dates_and_times(&self) -> String {
|
||||
let time_locale = self
|
||||
.system_locales
|
||||
.get("LC_TIME")
|
||||
.or_else(|| self.system_locales.get("LANG"))
|
||||
.map_or("en_US", |locale| &locale.lang_code)
|
||||
.split('.')
|
||||
.next()
|
||||
.unwrap_or("en_US");
|
||||
|
||||
let Ok(locale) = Locale::from_str(time_locale) else {
|
||||
let Some(locale) = self.time_locale.as_ref() else {
|
||||
return String::new();
|
||||
};
|
||||
|
||||
|
|
@ -448,16 +443,7 @@ impl Page {
|
|||
}
|
||||
|
||||
fn formatted_time(&self) -> String {
|
||||
let time_locale = self
|
||||
.system_locales
|
||||
.get("LC_TIME")
|
||||
.or_else(|| self.system_locales.get("LANG"))
|
||||
.map_or("en_US", |locale| &locale.lang_code)
|
||||
.split('.')
|
||||
.next()
|
||||
.unwrap_or("en_US");
|
||||
|
||||
let Ok(locale) = Locale::from_str(time_locale) else {
|
||||
let Some(locale) = self.time_locale.as_ref() else {
|
||||
return String::new();
|
||||
};
|
||||
|
||||
|
|
@ -473,16 +459,7 @@ impl Page {
|
|||
}
|
||||
|
||||
fn formatted_numbers(&self) -> String {
|
||||
let numerical_locale = self
|
||||
.system_locales
|
||||
.get("LC_NUMERIC")
|
||||
.or_else(|| self.system_locales.get("LANG"))
|
||||
.map_or("en_US", |locale| &locale.lang_code)
|
||||
.split('.')
|
||||
.next()
|
||||
.unwrap_or("en_US");
|
||||
|
||||
let Ok(locale) = Locale::from_str(numerical_locale) else {
|
||||
let Some(locale) = self.numeric_locale.as_ref() else {
|
||||
return String::new();
|
||||
};
|
||||
|
||||
|
|
@ -621,22 +598,13 @@ mod formatting {
|
|||
pub fn section() -> Section<crate::pages::Message> {
|
||||
crate::slab!(descriptions {
|
||||
formatting_txt = fl!("formatting");
|
||||
dates_txt = fl!("formatting", "dates");
|
||||
time_txt = fl!("formatting", "time");
|
||||
date_and_time_txt = fl!("formatting", "date-and-time");
|
||||
numbers_txt = fl!("formatting", "numbers");
|
||||
// measurement_txt = fl!("formatting", "measurement");
|
||||
// paper_txt = fl!("formatting", "paper");
|
||||
dates_txt = [&fl!("formatting", "dates"), ":"].concat();
|
||||
time_txt = [&fl!("formatting", "time"), ":"].concat();
|
||||
date_and_time_txt = [&fl!("formatting", "date-and-time"), ":"].concat();
|
||||
numbers_txt = [&fl!("formatting", "numbers"), ":"].concat();
|
||||
region_txt = fl!("region");
|
||||
});
|
||||
|
||||
let dates_label = [&descriptions[dates_txt], ":"].concat();
|
||||
let time_label = [&descriptions[time_txt], ":"].concat();
|
||||
let date_and_time_label = [&descriptions[date_and_time_txt], ":"].concat();
|
||||
let numbers_label = [&descriptions[numbers_txt], ":"].concat();
|
||||
// let measurement_label = [&descriptions[measurement_txt], ":"].concat();
|
||||
// let paper_label = [&descriptions[paper_txt], ":"].concat();
|
||||
|
||||
Section::default()
|
||||
.title(fl!("formatting"))
|
||||
.descriptions(descriptions)
|
||||
|
|
@ -644,17 +612,17 @@ mod formatting {
|
|||
let desc = §ion.descriptions;
|
||||
|
||||
let dates = widget::row::with_capacity(2)
|
||||
.push(widget::text::body(dates_label.clone()))
|
||||
.push(widget::text::body(&desc[dates_txt]))
|
||||
.push(widget::text::body(page.formatted_date()).font(cosmic::font::bold()))
|
||||
.spacing(4);
|
||||
|
||||
let time = widget::row::with_capacity(2)
|
||||
.push(widget::text::body(time_label.clone()))
|
||||
.push(widget::text::body(&desc[time_txt]))
|
||||
.push(widget::text::body(page.formatted_time()).font(cosmic::font::bold()))
|
||||
.spacing(4);
|
||||
|
||||
let dates_and_times = widget::row::with_capacity(2)
|
||||
.push(widget::text::body(date_and_time_label.clone()))
|
||||
.push(widget::text::body(&desc[date_and_time_txt]))
|
||||
.push(
|
||||
widget::text::body(page.formatted_dates_and_times())
|
||||
.font(cosmic::font::bold()),
|
||||
|
|
@ -662,7 +630,7 @@ mod formatting {
|
|||
.spacing(4);
|
||||
|
||||
let numbers = widget::row::with_capacity(2)
|
||||
.push(widget::text::body(numbers_label.clone()))
|
||||
.push(widget::text::body(&desc[numbers_txt]))
|
||||
.push(widget::text::body(page.formatted_numbers()).font(cosmic::font::bold()))
|
||||
.spacing(4);
|
||||
|
||||
|
|
@ -934,18 +902,17 @@ pub async fn set_locale(lang: String, region: String) {
|
|||
.await;
|
||||
}
|
||||
|
||||
fn parse_locale(locale: String) -> Result<Locale, Box<dyn std::error::Error>> {
|
||||
let locale = locale
|
||||
fn parse_locale(locale: &str) -> Option<Locale> {
|
||||
locale
|
||||
.split('.')
|
||||
.next()
|
||||
.ok_or(format!("Can't split the locale {locale}"))?;
|
||||
|
||||
let locale = Locale::from_str(locale).map_err(|e| format!("{e:?}"))?;
|
||||
Ok(locale)
|
||||
.next()?
|
||||
.replacen('_', "-", 1)
|
||||
.parse::<Locale>()
|
||||
.ok()
|
||||
}
|
||||
|
||||
fn get_default_24h(locale: String) -> bool {
|
||||
let Ok(locale) = parse_locale(locale) else {
|
||||
fn get_default_24h(locale: &str) -> bool {
|
||||
let Some(locale) = parse_locale(locale) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
|
@ -966,8 +933,8 @@ fn get_default_24h(locale: String) -> bool {
|
|||
formatted.contains("13")
|
||||
}
|
||||
|
||||
fn get_default_first_day(locale: String) -> usize {
|
||||
let Ok(locale) = parse_locale(locale) else {
|
||||
fn get_default_first_day(locale: &str) -> usize {
|
||||
let Some(locale) = parse_locale(locale) else {
|
||||
return 6;
|
||||
};
|
||||
let Ok(week_info) = week::WeekInformation::try_new(week::WeekPreferences::from(&locale)) else {
|
||||
|
|
@ -1000,13 +967,13 @@ fn update_time_settings_after_region_change(region: String) {
|
|||
};
|
||||
|
||||
// Update military_time based on new locale
|
||||
let new_military_time = get_default_24h(region.clone());
|
||||
let new_military_time = get_default_24h(®ion);
|
||||
if let Err(why) = cosmic_applet_config.set("military_time", new_military_time) {
|
||||
tracing::error!(?why, "Failed to update military_time after region change");
|
||||
}
|
||||
|
||||
// Update first_day_of_week based on new locale
|
||||
let new_first_day = get_default_first_day(region);
|
||||
let new_first_day = get_default_first_day(®ion);
|
||||
if let Err(why) = cosmic_applet_config.set("first_day_of_week", new_first_day) {
|
||||
tracing::error!(
|
||||
?why,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue