From 074ab450490282e2ea793dc3e88d733a9f5dd718 Mon Sep 17 00:00:00 2001 From: Daniel Rainer Date: Tue, 25 Nov 2025 00:36:12 +0100 Subject: [PATCH] gettext: move `gettext_impl` into dedicated crate This is part of the larger effort of splitting up fish's huge main crate to improve incremental build speed. We could extract more logic from `src/wutil/gettext.rs` into the new crate, but this would require putting wide-string handling into that crate, which I'm not sure we want. Doing so would have the advantage that crates which don't depend on fish's main crate (i.e. all crates other than fish's main crate itself and the binary crates built on top of it) could then localize messages as well. This will be less relevant if we replace gettext with Fluent for messages originating from the Rust sources. Closes #12108 --- Cargo.lock | 12 +- Cargo.toml | 6 +- crates/gettext/Cargo.toml | 15 ++ crates/gettext/src/lib.rs | 268 ++++++++++++++++++++++++++++++++++ src/wutil/gettext.rs | 295 ++------------------------------------ 5 files changed, 308 insertions(+), 288 deletions(-) create mode 100644 crates/gettext/Cargo.toml create mode 100644 crates/gettext/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 99a927559..37eb15944 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,8 +160,8 @@ dependencies = [ "errno", "fish-build-helper", "fish-build-man-pages", + "fish-gettext", "fish-gettext-extraction", - "fish-gettext-maps", "fish-gettext-mo-file-parser", "fish-printf", "fish-tempfile", @@ -172,7 +172,6 @@ dependencies = [ "num-traits", "once_cell", "pcre2", - "phf 0.12.1", "phf_codegen 0.12.1", "portable-atomic", "rand 0.9.2", @@ -200,6 +199,15 @@ dependencies = [ "rsconf", ] +[[package]] +name = "fish-gettext" +version = "0.0.0" +dependencies = [ + "fish-gettext-maps", + "once_cell", + "phf 0.12.1", +] + [[package]] name = "fish-gettext-extraction" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 69b0ebb74..09b1a895f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ cfg-if = "1.0.3" errno = "0.3.0" fish-build-helper = { path = "crates/build-helper" } fish-build-man-pages = { path = "crates/build-man-pages" } +fish-gettext = { path = "crates/gettext" } fish-gettext-extraction = { path = "crates/gettext-extraction" } fish-gettext-maps = { path = "crates/gettext-maps" } fish-gettext-mo-file-parser = { path = "crates/gettext-mo-file-parser" } @@ -87,8 +88,8 @@ cfg-if.workspace = true errno.workspace = true fish-build-helper.workspace = true fish-build-man-pages = { workspace = true, optional = true } +fish-gettext = { workspace = true, optional = true } fish-gettext-extraction = { workspace = true, optional = true } -fish-gettext-maps = { workspace = true, optional = true } fish-printf.workspace = true fish-tempfile.workspace = true libc.workspace = true @@ -98,7 +99,6 @@ nix.workspace = true num-traits.workspace = true once_cell.workspace = true pcre2.workspace = true -phf = { workspace = true, optional = true } rand.workspace = true terminfo.workspace = true xterm-color.workspace = true @@ -156,7 +156,7 @@ benchmark = [] embed-manpages = ["dep:fish-build-man-pages"] # Enable gettext localization at runtime. Requires the `msgfmt` tool to generate catalog data at # build time. -localize-messages = ["dep:phf", "dep:fish-gettext-maps"] +localize-messages = ["dep:fish-gettext"] # This feature is used to enable extracting messages from the source code for localization. # It only needs to be enabled if updating these messages (and the corresponding PO files) is # desired. This happens when running tests via `build_tools/check.sh` and when calling diff --git a/crates/gettext/Cargo.toml b/crates/gettext/Cargo.toml new file mode 100644 index 000000000..155fbba32 --- /dev/null +++ b/crates/gettext/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "fish-gettext" +edition.workspace = true +rust-version.workspace = true +version = "0.0.0" +repository.workspace = true +license.workspace = true + +[dependencies] +fish-gettext-maps.workspace = true +once_cell.workspace = true +phf.workspace = true + +[lints] +workspace = true diff --git a/crates/gettext/src/lib.rs b/crates/gettext/src/lib.rs new file mode 100644 index 000000000..65c7e35da --- /dev/null +++ b/crates/gettext/src/lib.rs @@ -0,0 +1,268 @@ +use fish_gettext_maps::CATALOGS; +use once_cell::sync::Lazy; +use std::{collections::HashSet, sync::Mutex}; + +type Catalog = &'static phf::Map<&'static str, &'static str>; + +pub struct SetLanguageLints<'a> { + pub duplicates: Vec<&'a str>, + pub non_existing: Vec<&'a str>, +} + +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum LanguagePrecedenceOrigin { + Default, + LocaleVariable(LocaleVariable), + LanguageEnvVar, + StatusLanguage, +} + +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum LocaleVariable { + #[allow(clippy::upper_case_acronyms)] + LANG, + #[allow(non_camel_case_types)] + LC_MESSAGES, + #[allow(non_camel_case_types)] + LC_ALL, +} + +impl LocaleVariable { + fn as_language_precedence_origin(&self) -> LanguagePrecedenceOrigin { + LanguagePrecedenceOrigin::LocaleVariable(*self) + } + + pub fn as_str(&self) -> &'static str { + match self { + Self::LANG => "LANG", + Self::LC_MESSAGES => "LC_MESSAGES", + Self::LC_ALL => "LC_ALL", + } + } +} + +impl std::fmt::Display for LocaleVariable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +struct InternalLocalizationState { + precedence_origin: LanguagePrecedenceOrigin, + language_precedence: Vec<(String, Catalog)>, +} + +pub struct PublicLocalizationState { + pub precedence_origin: LanguagePrecedenceOrigin, + pub language_precedence: Vec, +} + +/// Stores the current localization status. +/// `is_active` indicates whether localization is currently active, and the reason if it is +/// not. +/// The `origin` indicates where the values in `language_precedence` were taken from. +/// `language_precedence` stores the catalogs in the order they should be used. +/// +/// This struct should be updated when the relevant variables change or `status language` is used +/// to modify the localization state. +static LOCALIZATION_STATE: Lazy> = + Lazy::new(|| Mutex::new(InternalLocalizationState::new())); + +impl InternalLocalizationState { + fn new() -> Self { + Self { + precedence_origin: LanguagePrecedenceOrigin::Default, + language_precedence: vec![], + } + } + + fn to_public(&self) -> PublicLocalizationState { + PublicLocalizationState { + precedence_origin: self.precedence_origin, + language_precedence: self + .language_precedence + .iter() + .map(|(lang, _)| lang.to_owned()) + .collect(), + } + } + + fn update_from_env( + &mut self, + message_locale: Option<(LocaleVariable, String)>, + language_var: Option>, + ) { + // Do not override values set via `status language`. + if self.precedence_origin == LanguagePrecedenceOrigin::StatusLanguage { + return; + } + + if let Some((precedence_origin, locale)) = &message_locale { + // Regular locale names start with lowercase letters (`ll_CC`, followed by some suffix). + // The C or POSIX locale is special, and often used to disable localization. + // Their names are upper-case, but variants with suffixes (`C.UTF-8`) exist. + // To ensure that such variants are accounted for, we match on prefixes of the + // locale name. + // https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_02 + fn is_c_locale(locale: &str) -> bool { + locale.starts_with('C') || locale.starts_with("POSIX") + } + if is_c_locale(locale) { + self.precedence_origin = + LanguagePrecedenceOrigin::LocaleVariable(*precedence_origin); + self.language_precedence.clear(); + return; + } + } + + let (precedence_origin, language_list) = if let Some(list) = language_var { + (LanguagePrecedenceOrigin::LanguageEnvVar, list) + } else if let Some((precedence_origin, locale)) = message_locale { + let mut normalized_name = String::new(); + // Strip off encoding and modifier. (We always expect UTF-8 and don't support modifiers.) + for c in locale.chars() { + if c.is_alphabetic() || c == '_' { + normalized_name.push(c); + } else { + break; + } + } + // At this point, the normalized_name should have the shape `ll` or `ll_CC`. + ( + precedence_origin.as_language_precedence_origin(), + vec![normalized_name], + ) + } else { + (LanguagePrecedenceOrigin::Default, vec![]) + }; + + let mut seen_languages = HashSet::new(); + self.language_precedence = language_list + .into_iter() + .flat_map(|lang| find_existing_catalogs(&lang)) + .filter(|(lang, _)| seen_languages.insert(lang.to_owned())) + .collect(); + self.precedence_origin = precedence_origin; + } + + fn update_from_status_language_builtin<'a, 'b: 'a, S: AsRef + 'a>( + &mut self, + langs: &'b [S], + ) -> SetLanguageLints<'a> { + let mut seen = HashSet::new(); + let mut duplicates = vec![]; + for lang in langs { + let lang = lang.as_ref(); + if !seen.insert(lang) { + duplicates.push(lang) + } + } + let mut existing_langs = vec![]; + let mut non_existing = vec![]; + for lang in langs { + let lang = lang.as_ref(); + if let Some(catalog) = CATALOGS.get(lang) { + existing_langs.push((lang.to_owned(), *catalog)); + } else { + non_existing.push(lang); + } + } + + let mut seen = HashSet::new(); + let unique_langs = existing_langs + .into_iter() + .filter(|(lang, _)| seen.insert(lang.to_owned())) + .collect(); + self.language_precedence = unique_langs; + self.precedence_origin = LanguagePrecedenceOrigin::StatusLanguage; + + SetLanguageLints { + duplicates, + non_existing, + } + } +} + +/// Tries to find catalogs for `language`. +/// `language` must be an ISO 639 language code, optionally followed by an underscore and an ISO +/// 3166 country/territory code. +/// Uses the catalog with the exact same name as `language` if it exists. +/// If a country code is present (`ll_CC`), only the catalog named `ll` will be considered as a fallback. +/// If no country code is present (`ll`), all catalogs whose names start with `ll_` will be used in +/// arbitrary order. +fn find_existing_catalogs(language: &str) -> Vec<(String, Catalog)> { + // Try the exact name first. + // If there already is a corresponding catalog return the language. + if let Some(catalog) = CATALOGS.get(language) { + return vec![(language.to_owned(), catalog)]; + } + let language_without_country_code = language.split_once('_').map_or(language, |(ll, _cc)| ll); + if language == language_without_country_code { + // We have `ll` format. In this case, try to find any catalog whose name starts with `ll_`. + // Note that it is important to include the underscore in the pattern, otherwise `ll` might + // fall back to `llx_CC`, where `llx` is a 3-letter language identifier. + let ll_prefix = format!("{language}_"); + let mut lang_catalogs = vec![]; + for (&lang_name, &catalog) in CATALOGS.entries() { + if lang_name.starts_with(&ll_prefix) { + lang_catalogs.push((lang_name.to_owned(), catalog)); + } + } + lang_catalogs + } else { + // If `language` contained a country code, we only try to fall back to a catalog + // without a country code. + if let Some(catalog) = CATALOGS.get(language_without_country_code) { + vec![(language_without_country_code.to_owned(), catalog)] + } else { + vec![] + } + } +} + +pub fn update_from_env( + locale: Option<(LocaleVariable, String)>, + language_var: Option>, +) { + let mut localization_state = LOCALIZATION_STATE.lock().unwrap(); + localization_state.update_from_env(locale, language_var); +} + +pub fn update_from_status_language_builtin<'a, 'b: 'a, S: AsRef + 'a>( + langs: &'b [S], +) -> SetLanguageLints<'a> { + let mut localization_state = LOCALIZATION_STATE.lock().unwrap(); + localization_state.update_from_status_language_builtin(langs) +} + +pub fn unset_from_status_language_builtin( + locale: Option<(LocaleVariable, String)>, + language_var: Option>, +) { + let mut localization_state = LOCALIZATION_STATE.lock().unwrap(); + localization_state.precedence_origin = LanguagePrecedenceOrigin::Default; + localization_state.update_from_env(locale, language_var); +} + +pub fn status_language() -> PublicLocalizationState { + let localization_state = LOCALIZATION_STATE.lock().unwrap(); + localization_state.to_public() +} + +pub fn gettext(message_str: &'static str) -> Option<&'static str> { + let localization_state = LOCALIZATION_STATE.lock().unwrap(); + + // Use the localization from the highest-precedence language that has one available. + for (_, catalog) in localization_state.language_precedence.iter() { + if let Some(localized_str) = catalog.get(message_str) { + return Some(localized_str); + } + } + None +} + +pub fn list_available_languages() -> Vec<&'static str> { + let mut langs: Vec<_> = CATALOGS.entries().map(|(&lang, _)| lang).collect(); + langs.sort(); + langs +} diff --git a/src/wutil/gettext.rs b/src/wutil/gettext.rs index 7c32186df..b193b74e7 100644 --- a/src/wutil/gettext.rs +++ b/src/wutil/gettext.rs @@ -6,279 +6,8 @@ use once_cell::sync::Lazy; #[cfg(feature = "localize-messages")] -mod gettext_impl { - use fish_gettext_maps::CATALOGS; - use once_cell::sync::Lazy; - use std::{collections::HashSet, sync::Mutex}; - - type Catalog = &'static phf::Map<&'static str, &'static str>; - - pub struct SetLanguageLints<'a> { - pub duplicates: Vec<&'a str>, - pub non_existing: Vec<&'a str>, - } - - #[derive(PartialEq, Eq, Clone, Copy)] - pub enum LanguagePrecedenceOrigin { - Default, - LocaleVariable(LocaleVariable), - LanguageEnvVar, - StatusLanguage, - } - - #[derive(PartialEq, Eq, Clone, Copy)] - pub enum LocaleVariable { - #[allow(clippy::upper_case_acronyms)] - LANG, - LC_MESSAGES, - LC_ALL, - } - - impl LocaleVariable { - fn as_language_precedence_origin(&self) -> LanguagePrecedenceOrigin { - LanguagePrecedenceOrigin::LocaleVariable(*self) - } - - pub fn as_str(&self) -> &'static str { - match self { - Self::LANG => "LANG", - Self::LC_MESSAGES => "LC_MESSAGES", - Self::LC_ALL => "LC_ALL", - } - } - } - - impl std::fmt::Display for LocaleVariable { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.as_str()) - } - } - - struct InternalLocalizationState { - precedence_origin: LanguagePrecedenceOrigin, - language_precedence: Vec<(String, Catalog)>, - } - - pub struct PublicLocalizationState { - pub precedence_origin: LanguagePrecedenceOrigin, - pub language_precedence: Vec, - } - - /// Stores the current localization status. - /// `is_active` indicates whether localization is currently active, and the reason if it is - /// not. - /// The `origin` indicates where the values in `language_precedence` were taken from. - /// `language_precedence` stores the catalogs in the order they should be used. - /// - /// This struct should be updated when the relevant variables change or `status language` is used - /// to modify the localization state. - static LOCALIZATION_STATE: Lazy> = - Lazy::new(|| Mutex::new(InternalLocalizationState::new())); - - impl InternalLocalizationState { - fn new() -> Self { - Self { - precedence_origin: LanguagePrecedenceOrigin::Default, - language_precedence: vec![], - } - } - - fn to_public(&self) -> PublicLocalizationState { - PublicLocalizationState { - precedence_origin: self.precedence_origin, - language_precedence: self - .language_precedence - .iter() - .map(|(lang, _)| lang.to_owned()) - .collect(), - } - } - - fn update_from_env( - &mut self, - message_locale: Option<(LocaleVariable, String)>, - language_var: Option>, - ) { - // Do not override values set via `status language`. - if self.precedence_origin == LanguagePrecedenceOrigin::StatusLanguage { - return; - } - - if let Some((precedence_origin, locale)) = &message_locale { - // Regular locale names start with lowercase letters (`ll_CC`, followed by some suffix). - // The C or POSIX locale is special, and often used to disable localization. - // Their names are upper-case, but variants with suffixes (`C.UTF-8`) exist. - // To ensure that such variants are accounted for, we match on prefixes of the - // locale name. - // https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_02 - fn is_c_locale(locale: &str) -> bool { - locale.starts_with('C') || locale.starts_with("POSIX") - } - if is_c_locale(locale) { - self.precedence_origin = - LanguagePrecedenceOrigin::LocaleVariable(*precedence_origin); - self.language_precedence.clear(); - return; - } - } - - let (precedence_origin, language_list) = if let Some(list) = language_var { - (LanguagePrecedenceOrigin::LanguageEnvVar, list) - } else if let Some((precedence_origin, locale)) = message_locale { - let mut normalized_name = String::new(); - // Strip off encoding and modifier. (We always expect UTF-8 and don't support modifiers.) - for c in locale.chars() { - if c.is_alphabetic() || c == '_' { - normalized_name.push(c); - } else { - break; - } - } - // At this point, the normalized_name should have the shape `ll` or `ll_CC`. - ( - precedence_origin.as_language_precedence_origin(), - vec![normalized_name], - ) - } else { - (LanguagePrecedenceOrigin::Default, vec![]) - }; - - let mut seen_languages = HashSet::new(); - self.language_precedence = language_list - .into_iter() - .flat_map(|lang| find_existing_catalogs(&lang)) - .filter(|(lang, _)| seen_languages.insert(lang.to_owned())) - .collect(); - self.precedence_origin = precedence_origin; - } - - fn update_from_status_language_builtin<'a, 'b: 'a, S: AsRef + 'a>( - &mut self, - langs: &'b [S], - ) -> SetLanguageLints<'a> { - let mut seen = HashSet::new(); - let mut duplicates = vec![]; - for lang in langs { - let lang = lang.as_ref(); - if !seen.insert(lang) { - duplicates.push(lang) - } - } - let mut existing_langs = vec![]; - let mut non_existing = vec![]; - for lang in langs { - let lang = lang.as_ref(); - if let Some(catalog) = CATALOGS.get(lang) { - existing_langs.push((lang.to_owned(), *catalog)); - } else { - non_existing.push(lang); - } - } - - let mut seen = HashSet::new(); - let unique_langs = existing_langs - .into_iter() - .filter(|(lang, _)| seen.insert(lang.to_owned())) - .collect(); - self.language_precedence = unique_langs; - self.precedence_origin = LanguagePrecedenceOrigin::StatusLanguage; - - SetLanguageLints { - duplicates, - non_existing, - } - } - } - - /// Tries to find catalogs for `language`. - /// `language` must be an ISO 639 language code, optionally followed by an underscore and an ISO - /// 3166 country/territory code. - /// Uses the catalog with the exact same name as `language` if it exists. - /// If a country code is present (`ll_CC`), only the catalog named `ll` will be considered as a fallback. - /// If no country code is present (`ll`), all catalogs whose names start with `ll_` will be used in - /// arbitrary order. - fn find_existing_catalogs(language: &str) -> Vec<(String, Catalog)> { - // Try the exact name first. - // If there already is a corresponding catalog return the language. - if let Some(catalog) = CATALOGS.get(language) { - return vec![(language.to_owned(), catalog)]; - } - let language_without_country_code = - language.split_once('_').map_or(language, |(ll, _cc)| ll); - if language == language_without_country_code { - // We have `ll` format. In this case, try to find any catalog whose name starts with `ll_`. - // Note that it is important to include the underscore in the pattern, otherwise `ll` might - // fall back to `llx_CC`, where `llx` is a 3-letter language identifier. - let ll_prefix = format!("{language}_"); - let mut lang_catalogs = vec![]; - for (&lang_name, &catalog) in CATALOGS.entries() { - if lang_name.starts_with(&ll_prefix) { - lang_catalogs.push((lang_name.to_owned(), catalog)); - } - } - lang_catalogs - } else { - // If `language` contained a country code, we only try to fall back to a catalog - // without a country code. - if let Some(catalog) = CATALOGS.get(language_without_country_code) { - vec![(language_without_country_code.to_owned(), catalog)] - } else { - vec![] - } - } - } - - pub(super) fn update_from_env( - locale: Option<(LocaleVariable, String)>, - language_var: Option>, - ) { - let mut localization_state = LOCALIZATION_STATE.lock().unwrap(); - localization_state.update_from_env(locale, language_var); - } - - pub(super) fn update_from_status_language_builtin<'a, 'b: 'a, S: AsRef + 'a>( - langs: &'b [S], - ) -> SetLanguageLints<'a> { - let mut localization_state = LOCALIZATION_STATE.lock().unwrap(); - localization_state.update_from_status_language_builtin(langs) - } - - pub(super) fn unset_from_status_language_builtin( - locale: Option<(LocaleVariable, String)>, - language_var: Option>, - ) { - let mut localization_state = LOCALIZATION_STATE.lock().unwrap(); - localization_state.precedence_origin = LanguagePrecedenceOrigin::Default; - localization_state.update_from_env(locale, language_var); - } - - pub(super) fn status_language() -> PublicLocalizationState { - let localization_state = LOCALIZATION_STATE.lock().unwrap(); - localization_state.to_public() - } - - pub(super) fn gettext(message_str: &'static str) -> Option<&'static str> { - let localization_state = LOCALIZATION_STATE.lock().unwrap(); - - // Use the localization from the highest-precedence language that has one available. - for (_, catalog) in localization_state.language_precedence.iter() { - if let Some(localized_str) = catalog.get(message_str) { - return Some(localized_str); - } - } - None - } - - pub(super) fn list_available_languages() -> Vec<&'static str> { - let mut langs: Vec<_> = CATALOGS.entries().map(|(&lang, _)| lang).collect(); - langs.sort(); - langs - } -} - -#[cfg(feature = "localize-messages")] -fn get_message_locale(vars: &EnvStack) -> Option<(gettext_impl::LocaleVariable, String)> { - use gettext_impl::LocaleVariable; +fn get_message_locale(vars: &EnvStack) -> Option<(fish_gettext::LocaleVariable, String)> { + use fish_gettext::LocaleVariable; let get = |var_str: &wstr, var: LocaleVariable| { vars.get_unless_empty(var_str) .map(|val| (var, val.as_string().to_string())) @@ -327,7 +56,7 @@ fn get_language_var(vars: &EnvStack) -> Option> { /// them will be used, in arbitrary order. #[cfg(feature = "localize-messages")] pub fn update_from_env(vars: &EnvStack) { - gettext_impl::update_from_env(get_message_locale(vars), get_language_var(vars)); + fish_gettext::update_from_env(get_message_locale(vars), get_language_var(vars)); } #[cfg(feature = "localize-messages")] @@ -350,8 +79,8 @@ pub struct SetLanguageLints<'a> { } #[cfg(feature = "localize-messages")] -impl<'a> From> for SetLanguageLints<'a> { - fn from(lints: gettext_impl::SetLanguageLints<'a>) -> Self { +impl<'a> From> for SetLanguageLints<'a> { + fn from(lints: fish_gettext::SetLanguageLints<'a>) -> Self { Self { duplicates: lints.duplicates, non_existing: lints.non_existing, @@ -396,12 +125,12 @@ pub fn display_all(&self) -> WString { pub fn update_from_status_language_builtin<'a, 'b: 'a, S: AsRef + 'a>( langs: &'b [S], ) -> SetLanguageLints<'a> { - gettext_impl::update_from_status_language_builtin(langs).into() + fish_gettext::update_from_status_language_builtin(langs).into() } #[cfg(feature = "localize-messages")] pub fn unset_from_status_language_builtin(vars: &EnvStack) { - gettext_impl::unset_from_status_language_builtin( + fish_gettext::unset_from_status_language_builtin( get_message_locale(vars), get_language_var(vars), ); @@ -409,8 +138,8 @@ pub fn unset_from_status_language_builtin(vars: &EnvStack) { #[cfg(feature = "localize-messages")] pub fn status_language() -> WString { - use gettext_impl::LanguagePrecedenceOrigin; - let localization_state = gettext_impl::status_language(); + use fish_gettext::LanguagePrecedenceOrigin; + let localization_state = fish_gettext::status_language(); let mut result = WString::new(); localizable_consts!( LANGUAGE_LIST_VARIABLE_ORIGIN "from variable %s" @@ -437,7 +166,7 @@ pub fn status_language() -> WString { #[cfg(feature = "localize-messages")] pub fn list_available_languages() -> WString { let mut languages = WString::new(); - for lang in gettext_impl::list_available_languages() { + for lang in fish_gettext::list_available_languages() { languages.push_str(lang); languages.push('\n'); } @@ -457,7 +186,7 @@ pub fn initialize_gettext() { env_stack_set_from_env!(vars, "LC_MESSAGES"); env_stack_set_from_env!(vars, "LANG"); - gettext_impl::update_from_env(get_message_locale(&vars), get_language_var(&vars)); + fish_gettext::update_from_env(get_message_locale(&vars), get_language_var(&vars)); } /// Use this function to localize a message. @@ -495,7 +224,7 @@ fn gettext(message: MaybeStatic) -> &'static wstr { let () = message_str; #[cfg(feature = "localize-messages")] { - if let Some(localized_str) = gettext_impl::gettext(message_str) { + if let Some(localized_str) = fish_gettext::gettext(message_str) { static LOCALIZATION_TO_WIDE: Lazy>> = Lazy::new(|| Mutex::new(HashMap::default())); let mut localizations_to_wide = LOCALIZATION_TO_WIDE.lock().unwrap();