Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed string duplication for defaulted values #206

Merged
merged 2 commits into from
Mar 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions leptos_i18n_build/src/datakey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,15 @@ pub fn find_used_datakey(keys: &BuildersKeysInner, used_icu_keys: &mut HashSet<O
for locale_value in keys.0.values() {
match locale_value {
LocaleValue::Subkeys { keys, .. } => find_used_datakey(keys, used_icu_keys),
LocaleValue::Value(InterpolOrLit::Lit(_)) => {} // skip literals
LocaleValue::Value(InterpolOrLit::Interpol(interpolation_keys)) => {
LocaleValue::Value {
// skip literals
value: InterpolOrLit::Lit(_),
..
} => {}
LocaleValue::Value {
value: InterpolOrLit::Interpol(interpolation_keys),
..
} => {
for (_, var_infos) in interpolation_keys.iter_vars() {
if matches!(var_infos.range_count, Some(RangeOrPlural::Plural)) {
used_icu_keys.insert(Options::Plurals);
Expand Down
69 changes: 56 additions & 13 deletions leptos_i18n_macro/src/load_locales/interpolate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use std::collections::BTreeMap;
use std::collections::BTreeSet;

use leptos_i18n_parser::parse_locales::locale::InterpolationKeys;
use leptos_i18n_parser::parse_locales::locale::Locale;
use leptos_i18n_parser::parse_locales::parsed_value::ParsedValue;
use leptos_i18n_parser::utils::Key;
use leptos_i18n_parser::utils::KeyPath;
use leptos_i18n_parser::utils::UnwrapAt;
Expand Down Expand Up @@ -238,6 +242,7 @@ impl Interpolation {
fields
}

#[allow(clippy::too_many_arguments)]
pub fn new(
key: &Key,
enum_ident: &syn::Ident,
Expand All @@ -246,7 +251,19 @@ impl Interpolation {
key_path: &KeyPath,
locale_type_ident: &syn::Ident,
interpolate_display: bool,
defaults: &BTreeMap<Key, BTreeSet<Key>>,
) -> Self {
// filter defaulted locales
let locales = locales
.iter()
.filter(|locale| {
locale
.keys
.get(key)
.is_some_and(|v| !matches!(v, ParsedValue::Default))
})
.collect::<Vec<_>>();

let builder_name = format!("{}_builder", key);

let ident = syn::Ident::new(&builder_name, Span::call_site());
Expand Down Expand Up @@ -287,9 +304,10 @@ impl Interpolation {
enum_ident,
&locale_field,
&fields,
locales,
&locales,
key_path,
locale_type_ident,
defaults,
);

let debug_impl = Self::debug_impl(&builder_name, &ident, &fields);
Expand All @@ -302,8 +320,9 @@ impl Interpolation {
enum_ident,
&locale_field,
&fields,
locales,
&locales,
locale_type_ident,
defaults,
);
let builder_display = Self::builder_string_build_fns(
enum_ident,
Expand Down Expand Up @@ -547,8 +566,9 @@ impl Interpolation {
enum_ident: &syn::Ident,
locale_field: &Key,
fields: &[Field],
locales: &[Locale],
locales: &[&Locale],
locale_type_ident: &syn::Ident,
defaults: &BTreeMap<Key, BTreeSet<Key>>,
) -> TokenStream {
let left_generics = fields.iter().filter_map(Field::as_string_bounded_generic);

Expand All @@ -569,6 +589,7 @@ impl Interpolation {
&translations_holder_enum_ident,
locales,
locale_type_ident,
defaults,
);

let str_name = display_struct_ident.to_string();
Expand Down Expand Up @@ -601,11 +622,16 @@ impl Interpolation {

let new_fn = if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
let match_arms = locales.iter().map(|locale| {
let defaulted = defaults.get(&locale.top_locale_name).map(|defaulted_locales| {
defaulted_locales.iter().map(|key| {
quote!(| #enum_ident::#key)
}).collect::<TokenStream>()
});
let top_locale = &locale.top_locale_name.ident;
let string_accessor = strings_accessor_method_name(locale);
let strings_count = locale.top_locale_string_count;
quote! {
#enum_ident::#top_locale => {
#enum_ident::#top_locale #defaulted => {
let translations: &'static [Box<str>; #strings_count] = super::#locale_type_ident::#string_accessor().await;
#translations_holder_enum_ident::#top_locale(translations)
}
Expand Down Expand Up @@ -669,9 +695,10 @@ impl Interpolation {
enum_ident: &syn::Ident,
locale_field: &Key,
fields: &[Field],
locales: &[Locale],
locales: &[&Locale],
key_path: &KeyPath,
locale_type_ident: &syn::Ident,
defaults: &BTreeMap<Key, BTreeSet<Key>>,
) -> TokenStream {
let left_generics = fields.iter().flat_map(Field::as_bounded_generic);

Expand All @@ -694,7 +721,8 @@ impl Interpolation {

let destructure = quote!(let Self { #(#fields_key,)* #locale_field, .. } = self;);

let locales_impls = Self::create_locale_impl(key, enum_ident, locales, locale_type_ident);
let locales_impls =
Self::create_locale_impl(key, enum_ident, locales, locale_type_ident, defaults);
if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
quote! {
#[allow(non_camel_case_types)]
Expand Down Expand Up @@ -729,8 +757,9 @@ impl Interpolation {
fn create_locale_impl<'a>(
key: &'a Key,
enum_ident: &'a syn::Ident,
locales: &'a [Locale],
locales: &'a [&Locale],
locale_type_ident: &'a syn::Ident,
defaults: &'a BTreeMap<Key, BTreeSet<Key>>,
) -> impl Iterator<Item = TokenStream> + 'a {
let either_wrapper = EitherOfWrapper::new(locales.len());
locales
Expand All @@ -753,23 +782,30 @@ impl Interpolation {

let string_accessor = strings_accessor_method_name(locale);
let strings_count = locale.top_locale_string_count;

let defaulted = defaults.get(&locale.top_locale_name).map(|defaulted_locales| {
defaulted_locales.iter().map(|key| {
quote!(| #enum_ident::#key)
}).collect::<TokenStream>()
});

if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
quote!{
#enum_ident::#locale_key => {
#enum_ident::#locale_key #defaulted => {
let #translations_key: &'static [Box<str>; #strings_count] = super::#locale_type_ident::#string_accessor().await;
#wrapped_value
}
}
} else if cfg!(all(feature = "dynamic_load", feature = "ssr")) {
quote!{
#enum_ident::#locale_key => {
#enum_ident::#locale_key #defaulted => {
let #translations_key: &'static [&'static str; #strings_count] = super::#locale_type_ident::#string_accessor();
#wrapped_value
}
}
} else {
quote!{
#enum_ident::#locale_key => {
#enum_ident::#locale_key #defaulted => {
const #translations_key: &[&str; #strings_count] = super::#locale_type_ident::#string_accessor();
#wrapped_value
}
Expand All @@ -781,8 +817,9 @@ impl Interpolation {
fn create_locale_string_impl<'a>(
key: &'a Key,
enum_ident: &'a syn::Ident,
locales: &'a [Locale],
locales: &'a [&Locale],
locale_type_ident: &'a syn::Ident,
defaults: &'a BTreeMap<Key, BTreeSet<Key>>,
) -> impl Iterator<Item = TokenStream> + 'a {
locales.iter().rev().map(move |locale| {
let locale_key = &locale.top_locale_name;
Expand All @@ -798,6 +835,12 @@ impl Interpolation {
let string_accessor = strings_accessor_method_name(locale);
let strings_count = locale.top_locale_string_count;

let defaulted = defaults.get(&locale.top_locale_name).map(|defaulted_locales| {
defaulted_locales.iter().map(|key| {
quote!(| #enum_ident::#key)
}).collect::<TokenStream>()
});

if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
quote!{
#enum_ident::#locale_key(#translations_key) => {
Expand All @@ -806,14 +849,14 @@ impl Interpolation {
}
} else if cfg!(all(feature = "dynamic_load", feature = "ssr")) {
quote!{
#enum_ident::#locale_key => {
#enum_ident::#locale_key #defaulted => {
let #translations_key: &[&str; #strings_count] = super::#locale_type_ident::#string_accessor();
#value
}
}
}else {
quote!{
#enum_ident::#locale_key => {
#enum_ident::#locale_key #defaulted => {
const #translations_key: &[&str; #strings_count] = super::#locale_type_ident::#string_accessor();
#value
}
Expand Down
50 changes: 31 additions & 19 deletions leptos_i18n_macro/src/load_locales/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,10 @@ use icu_locid::LanguageIdentifier;
use interpolate::Interpolation;
use leptos_i18n_parser::{
parse_locales::{
cfg_file::ConfigFile,
error::{Error, Result},
locale::{
cfg_file::ConfigFile, error::{Error, Result}, locale::{
BuildersKeys, BuildersKeysInner, InterpolOrLit, Locale, LocaleValue,
LocalesOrNamespaces, Namespace,
},
warning::Warnings,
ForeignKeysPaths,
}, parsed_value::ParsedValue, warning::Warnings, ForeignKeysPaths
},
utils::{
key::{Key, KeyPath},
Expand Down Expand Up @@ -593,14 +589,17 @@ fn create_locale_type_inner<const IS_TOP: bool>(
let literal_keys = keys
.iter()
.filter_map(|(key, value)| match value {
LocaleValue::Value(InterpolOrLit::Lit(t)) => Some((key.clone(), LiteralType::from(*t))),
LocaleValue::Value{
value: InterpolOrLit::Lit(t),
defaults,
} => Some((key, LiteralType::from(*t), defaults)),
_ => None,
})
.collect::<Vec<_>>();

let literal_accessors = literal_keys
.iter()
.map(|(key, literal_type)| {
.map(|(key, literal_type, defaults)| {
if cfg!(feature = "show_keys_only") {
let key_str = key_path.to_string_with_key(key);
if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
Expand All @@ -626,51 +625,60 @@ fn create_locale_type_inner<const IS_TOP: bool>(
}
}
} else {
let match_arms = locales.iter().map(|locale| {
let ident = &locale.top_locale_name;
let accessor = strings_accessor_method_name(locale);
let match_arms = locales.iter().filter_map(|locale| {
let lit = locale
.keys
.get(key)
.unwrap_at("create_locale_type_inner_1");
if matches!(lit, ParsedValue::Default) {
return None;
}
let ident = &locale.top_locale_name;
let accessor = strings_accessor_method_name(locale);
let defaulted = defaults.get(&locale.top_locale_name).map(|defaulted_locales| {
defaulted_locales.iter().map(|key| {
quote!(| #enum_ident::#key)
}).collect::<TokenStream>()
});
let lit = parsed_value::to_token_stream(lit, locale.top_locale_string_count);
if *literal_type == LiteralType::String {
let ts = if *literal_type == LiteralType::String {
let strings_count = locale.top_locale_string_count;
if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
quote! {
#enum_ident::#ident => {
#enum_ident::#ident #defaulted => {
let #translations_key: &'static [Box<str>; #strings_count] = #type_ident::#accessor().await;
l_i18n_crate::__private::LitWrapper::new(#lit)
}
}
} else if cfg!(all(feature = "dynamic_load", feature = "ssr")) {
quote! {
#enum_ident::#ident => {
#enum_ident::#ident #defaulted => {
let #translations_key: &'static [&'static str; #strings_count] = #type_ident::#accessor();
l_i18n_crate::__private::LitWrapperFut::new_not_fut(#lit)
}
}
} else {
quote! {
#enum_ident::#ident => {
#enum_ident::#ident #defaulted => {
const #translations_key: &[&str; #strings_count] = #type_ident::#accessor();
l_i18n_crate::__private::LitWrapper::new(#lit)
}
}
}
} else if cfg!(feature = "dynamic_load") {
quote! {
#enum_ident::#ident => {
#enum_ident::#ident #defaulted => {
l_i18n_crate::__private::LitWrapperFut::new_not_fut(#lit)
}
}
} else {
quote! {
#enum_ident::#ident => {
#enum_ident::#ident #defaulted => {
l_i18n_crate::__private::LitWrapper::new(#lit)
}
}
}
};
Some(ts)
});
if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
quote! {
Expand Down Expand Up @@ -773,7 +781,10 @@ fn create_locale_type_inner<const IS_TOP: bool>(
let builders = keys
.iter()
.filter_map(|(key, value)| match value {
LocaleValue::Value(InterpolOrLit::Interpol(keys)) => Some((
LocaleValue::Value {
value: InterpolOrLit::Interpol(keys),
defaults
} => Some((
key,
Interpolation::new(
key,
Expand All @@ -783,6 +794,7 @@ fn create_locale_type_inner<const IS_TOP: bool>(
key_path,
type_ident,
interpolate_display,
defaults
),
)),
_ => None,
Expand Down
6 changes: 4 additions & 2 deletions leptos_i18n_macro/src/load_locales/parsed_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ fn flatten(
strings_count: usize,
) {
match this {
ParsedValue::Subkeys(_) | ParsedValue::Default => {}
ParsedValue::Default => unreachable!("defaulted value should never have been rendered"),
ParsedValue::Subkeys(_) => unreachable!("subkeys should never have been rendered"),
ParsedValue::Literal(lit) => tokens.push(Literal::from(lit).to_token_stream(strings_count)),
ParsedValue::Ranges(ranges) => tokens.push(ranges::to_token_stream(ranges, strings_count)),
ParsedValue::Variable { key, formatter } => {
Expand Down Expand Up @@ -130,7 +131,8 @@ fn flatten_string(
strings_count: usize,
) {
match this {
ParsedValue::Subkeys(_) | ParsedValue::Default => {}
ParsedValue::Default => unreachable!("defaulted value should never have been rendered"),
ParsedValue::Subkeys(_) => unreachable!("subkeys should never have been rendered"),
ParsedValue::Literal(lit) => {
let ts = Literal::from(lit).to_token_stream(strings_count);
tokens.push(quote!(core::fmt::Display::fmt(&#ts, __formatter)))
Expand Down
Loading