Refactor `fluent_embed::Localize` to support overriding locales

finchie
Jul 31, 2024, 7:13 AM
7U2DXFMPZO4P53AMWYCVXG3EPB7UIAPEY4PDDINX4TTABHD5NGMQC

Dependencies

  • [2] KZLFC7OW Rename `fluent_embed_runtime` to `fluent_embed`
  • [3] NO3PDO7P Refactor `fluent_embed` to support structs
  • [4] HHJDRLLN Create `fluent_embed_runtime` crate
  • [5] BFL2Y7GN Add relative timestamps using `jiff` and `icu_relativetime`
  • [6] OCR4YRQ2 Parse group from fluent file specified by macro attribute
  • [7] XGNME3WR Move `Group::derive_enum` to new `crate::parse_macro` module
  • [8] 2XQ6ZB4W Store multiple locales in a single `Group`
  • [9] QSK7JRBA Add simple `attribute_path` function
  • [10] 4MRF5E76 Generate simple locale matching code in `localize()`
  • [11] 5FIVUZYF Unify `fluent_embed` macro API as `localize()`
  • [12] VNSHGQYN Support using glob paths in `localize` macro
  • [13] P6FW2GGO Remove unnecessary parameters in generated `localize()` function
  • [14] F5LG7WEN Emit compilation errors from Fluent source code
  • [15] V5S5K33A Add basic error handling for invalid paths in proc_macro attribute
  • [16] QFPQZR4K Refactor `fluent_embed`
  • [*] O77KA6C4 Create `fluent_embed` crate
  • [*] 5TEX4MNU Split `fluent_embed` into `group` and `parse` modules
  • [*] 3WEPY3OX Add `locale` parameter to derived `localize()` function

Change contents

  • edit in fluent_embed_derive/src/lib.rs at line 82
    [3.1384]
    [3.5596]
    let locales = match &derive_input.data {
    syn::Data::Struct(_struct_data) => derive::locales_for_ident(&group, &derive_input.ident),
    syn::Data::Enum(enum_data) => derive::locales_for_enum(&group, &enum_data.variants),
    syn::Data::Union(_) => todo!(),
    };
  • replacement in fluent_embed_derive/src/lib.rs at line 89
    [3.5597][3.5597:5639]()
    let body = match &derive_input.data {
    [3.5597]
    [3.7399]
    let message_body = match &derive_input.data {
  • replacement in fluent_embed_derive/src/lib.rs at line 91
    [3.7443][3.775:855]()
    derive::for_struct(group, &derive_input.ident, &struct_data.fields)
    [3.7443]
    [3.7531]
    derive::message_for_struct(group, &derive_input.ident, &struct_data.fields)
  • replacement in fluent_embed_derive/src/lib.rs at line 93
    [3.7541][3.856:940]()
    syn::Data::Enum(enum_data) => derive::for_enum(group, &enum_data.variants),
    [3.7541]
    [3.5830]
    syn::Data::Enum(enum_data) => derive::messages_for_enum(group, &enum_data.variants),
  • edit in fluent_embed_derive/src/lib.rs at line 103
    [2.268]
    [3.6021]
    fn message_for_locale(&self, locale: &::fluent_embed::icu_locid::LanguageIdentifier) -> String {
    #message_body
    }
  • replacement in fluent_embed_derive/src/lib.rs at line 109
    [3.6064][3.6064:6086]()
    #body
    [3.6064]
    [3.6086]
    let available_locales = #locales;
    let selected_locale = ::fluent_embed::locale_select::match_locales(&available_locales, &Self::CANONICAL_LOCALE);
    self.message_for_locale(&selected_locale)
  • edit in fluent_embed_derive/src/fluent/group.rs at line 113
    [20.1114]
    [19.6614]
    pub fn locales_for_message<'a>(&'a self, id: &'a str) -> impl Iterator<Item = &Locale> + 'a {
    self.locales
    .iter()
    .filter(|(_locale, messages)| messages.contains_expression(id))
    .map(|(locale, _source_file)| locale)
    }
  • edit in fluent_embed_derive/src/derive.rs at line 39
    [3.510][3.1729:1985](),[3.1985][2.372:434](),[2.434][3.2055:2066](),[3.2055][3.2055:2066](),[3.2066][2.435:532](),[2.532][3.625:626](),[3.902][3.625:626](),[3.2171][3.625:626](),[3.625][3.625:626]()
    // Create a list of available locales to choose from
    let available_locales = [
    // The canonical locale will always be available
    Self::CANONICAL_LOCALE,
    // Any additional locales that contain this message
    #(::fluent_embed::langid!(#additional_locales)),*
    ];
    let locale = ::fluent_embed::select_locale(&available_locales, &Self::CANONICAL_LOCALE);
  • replacement in fluent_embed_derive/src/derive.rs at line 41
    [3.2231][2.533:598]()
    const plural_rule_type: ::fluent_embed::PluralRuleType =
    [3.2231]
    [2.598]
    const PLURAL_RULE_TYPE: ::fluent_embed::PluralRuleType =
  • replacement in fluent_embed_derive/src/derive.rs at line 43
    [2.652][2.652:745]()
    let plural_rules = ::fluent_embed::plural_rules(&locale, plural_rule_type).unwrap();
    [2.652]
    [3.712]
    let plural_rules = ::fluent_embed::plural_rules(&locale, PLURAL_RULE_TYPE).unwrap();
  • replacement in fluent_embed_derive/src/derive.rs at line 68
    [3.45][3.9670:9689]()
    pub fn for_struct(
    [3.45]
    [3.9689]
    pub fn locales_for_ident(group: &fluent::Group, ident: &syn::Ident) -> TokenStream {
    let id = ident.to_string().to_kebab_case();
    let locale_literals = group
    .locales_for_message(&id)
    .map(|locale| locale.id.to_string())
    .map(|locale_string| syn::LitStr::new(&locale_string, proc_macro2::Span::call_site()));
    // There is only one message for this struct, so just list every supported locale
    quote!(vec![#(::fluent_embed::langid!(#locale_literals)),*])
    }
    pub fn message_for_struct(
  • replacement in fluent_embed_derive/src/derive.rs at line 101
    [3.116][3.10013:10030]()
    pub fn for_enum(
    [3.116]
    [3.10030]
    pub fn locales_for_enum(
    group: &fluent::Group,
    enum_variants: &Punctuated<syn::Variant, syn::token::Comma>,
    ) -> TokenStream {
    let mut match_arms: Vec<TokenStream> = Vec::with_capacity(enum_variants.len());
    for enum_variant in enum_variants {
    let variant_ident = &enum_variant.ident;
    // Simplify match code by always ignoring enum fields (even if they don't exist)
    // We are matching the variant name, not any data, so each arm will have something like:
    // Self::VariantName { .. }
    // Even if `Self::VariantName` doesn't contain any data
    let locales_for_variant = locales_for_ident(group, variant_ident);
    match_arms.push(quote!(Self::#variant_ident { .. } => #locales_for_variant));
    }
    quote! {
    match self {
    #(#match_arms),*
    }
    }
    }
    pub fn messages_for_enum(
  • edit in fluent_embed/src/time.rs at line 21
    [3.769]
    [3.769]
    const CANONICAL_LOCALE: LanguageIdentifier = DEFAULT_LOCALE;
  • edit in fluent_embed/src/time.rs at line 26
    [3.846][3.846:954]()
    }
    impl crate::Localize for RelativeTime {
    const CANONICAL_LOCALE: LanguageIdentifier = DEFAULT_LOCALE;
  • replacement in fluent_embed/src/time.rs at line 27
    [3.955][3.955:990]()
    fn localize(&self) -> String {
    [3.955]
    [3.990]
    pub fn localize(&self) -> String {
  • edit in fluent_embed/src/lib.rs at line 16
    [3.475][3.475:511]()
    fn localize(&self) -> String;
    }
  • replacement in fluent_embed/src/lib.rs at line 17
    [3.512][3.512:807]()
    /// Select which locale to use, falling back to the canonical locale if nothing matches
    pub fn select_locale(
    available_locales: &[LanguageIdentifier],
    canonical_locale: &LanguageIdentifier,
    ) -> LanguageIdentifier {
    locale_select::match_locales(available_locales, canonical_locale)
    [3.512]
    [3.807]
    fn message_for_locale(&self, locale: &LanguageIdentifier) -> String;
    fn localize(&self) -> String;