Migrate from a map between locales and their messages to a table where each row heading is the locale, each column heading is the canonical message and each cell is an Option<Message>
. This reduces unnecessary work and makes it much cleaner to query all localizations of a message (.is_some()
for each row in the column for that message id).
BQ6N55O7RPG47G35YI37Z37456VKWT5KLGQKDQVAN2WI4K34TRBQC
impl LocalizationsForMessage {
fn new(canonical_message: Message<String>) -> Self {
Self {
canonical_message,
additional_messages: HashMap::new(),
}
}
fn to_syn(&self, canonical_locale: String) -> syn::ExprBlock {
let additional_locales = self
.additional_messages
.keys()
.map(|locale| locale.to_string())
.map(|locale_string| syn::LitStr::new(&locale_string, proc_macro2::Span::call_site()));
let additional_messages = self
.additional_messages
.values()
.map(crate::parse_fluent::message);
let canonical_locale = syn::LitStr::new(&canonical_locale, proc_macro2::Span::call_site());
let canonical_message = crate::parse_fluent::message(&self.canonical_message);
impl LocaleGroup {
fn new(
locale: Locale,
resource: Resource<String>,
canonical_messages: &[Message<String>],
) -> Self {
let mut messages = vec![None; canonical_messages.len()].into_boxed_slice();
parse_quote! {
{
#(if locale.normalizing_eq(#additional_locales) { return #additional_messages })else*
for entry in resource.body {
if let Entry::Message(message) = entry {
let index = canonical_messages
.iter()
.position(|canonical_message| canonical_message.id.name == message.id.name)
.expect("Message ID must be in canonical group");
for (locale, resource) in resources {
for entry in resource.body {
if let Entry::Message(message) = entry {
let localizations = messages.get_mut(&message.id.name).unwrap();
localizations
.additional_messages
.insert(locale.clone(), message);
}
}
}
let extra_locales = resources
.into_iter()
.map(|(locale, resource)| LocaleGroup::new(locale, resource, &canonical_messages))
.collect::<Vec<_>>();
pub fn canonical_message_for(&self, id: &str) -> &Message<String> {
&self.messages.get(id).unwrap().canonical_message
/// Returns an iterator over the localized messages paired with the relevant locale
fn messages_in_column(
&self,
message_column: usize,
) -> impl Iterator<Item = (&Locale, &Message<String>)> {
self.extra_locales.iter().filter_map(move |locale_group| {
locale_group
.messages
.get(message_column)
.unwrap()
.as_ref()
.map(|message| (&locale_group.locale, message))
})
self.messages
.get(id)
.unwrap()
.to_syn(self.canonical_locale.to_string())
let canonical_locale = syn::LitStr::new(
&self.canonical_locale.to_string(),
proc_macro2::Span::call_site(),
);
let message_column = self
.canonical_messages
.iter()
.position(|message| message.id.name == id)
.expect("Message id must be valid");
let canonical_message =
crate::parse_fluent::message(&self.canonical_messages[message_column]);
let additional_locale_names = self
.messages_in_column(message_column)
.map(|(locale, _message)| locale.to_string())
.map(|locale_string| syn::LitStr::new(&locale_string, proc_macro2::Span::call_site()));
let additional_locale_messages = self
.messages_in_column(message_column)
.map(|(_locale, message)| crate::parse_fluent::message(message));
parse_quote! {{
#(if locale.normalizing_eq(#additional_locale_names) { return #additional_locale_messages })else*
assert!(locale.normalizing_eq(#canonical_locale));
#canonical_message
}}