From 3582d86c70d29935a7c8a5649eac9b31a2786a02 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 11 Apr 2023 23:51:07 +0100 Subject: [PATCH] Simplified localisation logic --- Cargo.lock | 2 +- client/i18n/Cargo.toml | 1 + client/i18n/src/lib.rs | 18 ++++++ common/src/comp/chat.rs | 13 +--- voxygen/i18n-helpers/Cargo.toml | 1 - voxygen/i18n-helpers/src/lib.rs | 102 +++++++++++++------------------- voxygen/src/hud/overhead.rs | 3 +- 7 files changed, 65 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 506a5a51c4..84bf7dc083 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6692,6 +6692,7 @@ dependencies = [ "serde", "tracing", "unic-langid", + "veloren-common", "veloren-common-assets", ] @@ -7173,7 +7174,6 @@ dependencies = [ name = "veloren-voxygen-i18n-helpers" version = "0.10.0" dependencies = [ - "hashbrown 0.12.3", "tracing", "veloren-client-i18n", "veloren-common", diff --git a/client/i18n/Cargo.toml b/client/i18n/Cargo.toml index 329daed3fc..593fcd7adb 100644 --- a/client/i18n/Cargo.toml +++ b/client/i18n/Cargo.toml @@ -7,6 +7,7 @@ version = "0.13.0" [dependencies] # Assets +common = {package = "veloren-common", path = "../../common"} common-assets = {package = "veloren-common-assets", path = "../../common/assets"} ron = "0.8" serde = { version = "1.0", features = ["derive"] } diff --git a/client/i18n/src/lib.rs b/client/i18n/src/lib.rs index f83b0ba54f..fd1615dc30 100644 --- a/client/i18n/src/lib.rs +++ b/client/i18n/src/lib.rs @@ -19,6 +19,7 @@ use std::{borrow::Cow, io}; use assets::{source::DirEntry, AssetExt, AssetGuard, AssetHandle, ReloadWatcher, SharedString}; use tracing::warn; // Re-export because I don't like prefix +use common::comp::Content; use common_assets as assets; // Re-export for argument creation @@ -331,6 +332,23 @@ impl LocalizationGuard { }) } + /// Localize the given context. + pub fn get_content(&self, content: &Content) -> String { + match content { + Content::Plain(text) => text.clone(), + Content::Localized { key, seed, args } => self + .get_variation_ctx( + key, + *seed, + &args + .iter() + .map(|(k, content)| (k, self.get_content(content))) + .collect(), + ) + .into_owned(), + } + } + /// Get a localized text from the variation of given key with given /// arguments /// diff --git a/common/src/comp/chat.rs b/common/src/comp/chat.rs index 88218a38e4..92639e5a38 100644 --- a/common/src/comp/chat.rs +++ b/common/src/comp/chat.rs @@ -183,6 +183,9 @@ impl ChatType { // - In-game notes/books (we could add a variant that allows structuring complex, novel textual // information as a syntax tree or some other intermediate format that can be localised by the // client) +// TODO: We probably want to have this type be able to represent similar things to +// `fluent::FluentValue`, such as numeric values, so that they can be properly localised in whatever +// manner is required. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Content { /// The content is a plaintext string that should be shown to the user @@ -239,16 +242,6 @@ impl Content { Self::Localized { .. } => None, } } - - pub fn localize(&self, i18n_variation: F) -> String - where - F: Fn(&str, u16, &HashMap) -> String, - { - match self { - Content::Plain(text) => text.to_string(), - Content::Localized { key, seed, args } => i18n_variation(key, *seed, args), - } - } } // Stores chat text, type diff --git a/voxygen/i18n-helpers/Cargo.toml b/voxygen/i18n-helpers/Cargo.toml index 1ffaff8df5..ed9e50e3f3 100644 --- a/voxygen/i18n-helpers/Cargo.toml +++ b/voxygen/i18n-helpers/Cargo.toml @@ -11,4 +11,3 @@ common = {package = "veloren-common", path = "../../common"} i18n = {package = "veloren-client-i18n", path = "../../client/i18n"} # Utility tracing = "0.1" -hashbrown = { version = "0.12" } diff --git a/voxygen/i18n-helpers/src/lib.rs b/voxygen/i18n-helpers/src/lib.rs index 32614a3c46..0218a57aef 100644 --- a/voxygen/i18n-helpers/src/lib.rs +++ b/voxygen/i18n-helpers/src/lib.rs @@ -4,50 +4,18 @@ use common::comp::{ BuffKind, ChatMsg, ChatType, Content, }; use common_net::msg::{ChatTypeContext, PlayerInfo}; -use hashbrown::HashMap; use i18n::Localization; -pub fn make_localizer( - localization: &Localization, -) -> impl Fn(&str, u16, &HashMap) -> String + Copy + '_ { - fn get( - localization: &Localization, - key: &str, - seed: u16, - args: &HashMap, - ) -> String { - localization - .get_variation_ctx( - key, - seed, - &args - .iter() - .map(|(k, v)| { - ( - k, - v.localize(move |key, seed, args| get(localization, key, seed, args)), - ) - }) - .collect(), - ) - .into_owned() - } - - move |key: &str, seed: u16, args: &HashMap| get(localization, key, seed, args) -} - pub fn localize_chat_message( msg: ChatMsg, lookup_fn: impl Fn(&ChatMsg) -> ChatTypeContext, - localisation: &Localization, + localization: &Localization, show_char_name: bool, ) -> (ChatType, String) { let info = lookup_fn(&msg); - let localizer = make_localizer(localisation); - let name_format = |uid: &common::uid::Uid| match info.player_alias.get(uid).cloned() { - Some(pi) => insert_alias(info.you == *uid, pi, localisation), + Some(pi) => insert_alias(info.you == *uid, pi, localization), None => info .entity_name .get(uid) @@ -62,7 +30,7 @@ pub fn localize_chat_message( } else { None }; - let message = content.localize(localizer); + let message = localization.get_content(content); match (group, name) { (Some(group), None) => format!("({group}) [{alias}]: {message}"), (None, None) => format!("[{alias}]: {message}"), @@ -72,29 +40,35 @@ pub fn localize_chat_message( }; let new_msg = match &msg.chat_type { - ChatType::Online(uid) => localisation + ChatType::Online(uid) => localization .get_msg_ctx("hud-chat-online_msg", &i18n::fluent_args! { "name" => name_format(uid), }) .into_owned(), - ChatType::Offline(uid) => localisation + ChatType::Offline(uid) => localization .get_msg_ctx("hud-chat-offline_msg", &i18n::fluent_args! { "name" => name_format(uid ), }) .into_owned(), - ChatType::CommandError => msg.content().localize(localizer), - ChatType::CommandInfo => msg.content().localize(localizer), - ChatType::FactionMeta(_) => msg.content().localize(localizer), - ChatType::GroupMeta(_) => msg.content().localize(localizer), + ChatType::CommandError => localization.get_content(msg.content()), + ChatType::CommandInfo => localization.get_content(msg.content()), + ChatType::FactionMeta(_) => localization.get_content(msg.content()), + ChatType::GroupMeta(_) => localization.get_content(msg.content()), ChatType::Tell(from, to) => { let from_alias = name_format(from); let to_alias = name_format(to); // TODO: internationalise if *from == info.you { - format!("To [{to_alias}]: {}", msg.content().localize(localizer)) + format!( + "To [{to_alias}]: {}", + localization.get_content(msg.content()) + ) } else { - format!("From [{from_alias}]: {}", msg.content().localize(localizer)) + format!( + "From [{from_alias}]: {}", + localization.get_content(msg.content()) + ) } }, ChatType::Say(uid) => message_format(uid, msg.content(), None), @@ -111,12 +85,18 @@ pub fn localize_chat_message( let to_alias = name_format(to); // TODO: internationalise if *from == info.you { - format!("To [{to_alias}]: {}", msg.content().localize(localizer)) + format!( + "To [{to_alias}]: {}", + localization.get_content(msg.content()) + ) } else { - format!("From [{from_alias}]: {}", msg.content().localize(localizer)) + format!( + "From [{from_alias}]: {}", + localization.get_content(msg.content()) + ) } }, - ChatType::Meta => msg.content().localize(localizer), + ChatType::Meta => localization.get_content(msg.content()), ChatType::Kill(kill_source, victim) => { let i18n_buff = |buff| match buff { BuffKind::Burning => "hud-outcome-burning", @@ -155,9 +135,9 @@ pub fn localize_chat_message( // Buff deaths KillSource::Player(attacker, KillType::Buff(buff_kind)) => { let i18n_buff = i18n_buff(*buff_kind); - let buff = localisation.get_msg(i18n_buff); + let buff = localization.get_msg(i18n_buff); - localisation.get_msg_ctx("hud-chat-died_of_pvp_buff_msg", &i18n::fluent_args! { + localization.get_msg_ctx("hud-chat-died_of_pvp_buff_msg", &i18n::fluent_args! { "victim" => name_format(victim), "died_of_buff" => buff, "attacker" => name_format(attacker), @@ -165,9 +145,9 @@ pub fn localize_chat_message( }, KillSource::NonPlayer(attacker_name, KillType::Buff(buff_kind)) => { let i18n_buff = i18n_buff(*buff_kind); - let buff = localisation.get_msg(i18n_buff); + let buff = localization.get_msg(i18n_buff); - localisation.get_msg_ctx("hud-chat-died_of_npc_buff_msg", &i18n::fluent_args! { + localization.get_msg_ctx("hud-chat-died_of_npc_buff_msg", &i18n::fluent_args! { "victim" => name_format(victim), "died_of_buff" => buff, "attacker" => attacker_name, @@ -175,9 +155,9 @@ pub fn localize_chat_message( }, KillSource::NonExistent(KillType::Buff(buff_kind)) => { let i18n_buff = i18n_buff(*buff_kind); - let buff = localisation.get_msg(i18n_buff); + let buff = localization.get_msg(i18n_buff); - localisation.get_msg_ctx( + localization.get_msg_ctx( "hud-chat-died_of_buff_nonexistent_msg", &i18n::fluent_args! { "victim" => name_format(victim), @@ -195,7 +175,7 @@ pub fn localize_chat_message( KillType::Other => "hud-chat-pvp_other_kill_msg", &KillType::Buff(_) => unreachable!("handled above"), }; - localisation.get_msg_ctx(key, &i18n::fluent_args! { + localization.get_msg_ctx(key, &i18n::fluent_args! { "victim" => name_format(victim), "attacker" => name_format(attacker), }) @@ -210,30 +190,30 @@ pub fn localize_chat_message( KillType::Other => "hud-chat-npc_other_kill_msg", &KillType::Buff(_) => unreachable!("handled above"), }; - localisation.get_msg_ctx(key, &i18n::fluent_args! { + localization.get_msg_ctx(key, &i18n::fluent_args! { "victim" => name_format(victim), "attacker" => attacker_name, }) }, // Other deaths KillSource::Environment(environment) => { - localisation.get_msg_ctx("hud-chat-environment_kill_msg", &i18n::fluent_args! { + localization.get_msg_ctx("hud-chat-environment_kill_msg", &i18n::fluent_args! { "name" => name_format(victim), "environment" => environment, }) }, KillSource::FallDamage => { - localisation.get_msg_ctx("hud-chat-fall_kill_msg", &i18n::fluent_args! { + localization.get_msg_ctx("hud-chat-fall_kill_msg", &i18n::fluent_args! { "name" => name_format(victim), }) }, KillSource::Suicide => { - localisation.get_msg_ctx("hud-chat-suicide_msg", &i18n::fluent_args! { + localization.get_msg_ctx("hud-chat-suicide_msg", &i18n::fluent_args! { "name" => name_format(victim), }) }, KillSource::NonExistent(_) | KillSource::Other => { - localisation.get_msg_ctx("hud-chat-default_death_msg", &i18n::fluent_args! { + localization.get_msg_ctx("hud-chat-default_death_msg", &i18n::fluent_args! { "name" => name_format(victim), }) }, @@ -245,14 +225,14 @@ pub fn localize_chat_message( (msg.chat_type, new_msg) } -fn insert_alias(you: bool, info: PlayerInfo, localisation: &Localization) -> String { +fn insert_alias(you: bool, info: PlayerInfo, localization: &Localization) -> String { const YOU: &str = "hud-chat-you"; // Leave space for a mod badge icon. const MOD_SPACING: &str = " "; match (info.is_moderator, you) { (false, false) => info.player_alias, - (false, true) => localisation.get_msg(YOU).to_string(), + (false, true) => localization.get_msg(YOU).to_string(), (true, false) => format!("{}{}", MOD_SPACING, info.player_alias), - (true, true) => format!("{}{}", MOD_SPACING, &localisation.get_msg(YOU),), + (true, true) => format!("{}{}", MOD_SPACING, &localization.get_msg(YOU),), } } diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 425ef488b3..3eac9169a2 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -20,7 +20,6 @@ use conrod_core::{ widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon, }; use i18n::Localization; -use i18n_helpers::make_localizer; use keyboard_keynames::key_layout::KeyLayout; const MAX_BUBBLE_WIDTH: f64 = 250.0; @@ -535,7 +534,7 @@ impl<'a> Widget for Overhead<'a> { // Speech bubble if let Some(bubble) = self.bubble { let dark_mode = self.settings.speech_bubble_dark_mode; - let bubble_contents: String = bubble.content().localize(make_localizer(self.i18n)); + let bubble_contents: String = self.i18n.get_content(bubble.content()); let (text_color, shadow_color) = bubble_color(bubble, dark_mode); let mut text = Text::new(&bubble_contents) .color(text_color)