Simplified localisation logic

This commit is contained in:
Joshua Barretto 2023-04-11 23:51:07 +01:00
parent 3484e156d1
commit 3582d86c70
7 changed files with 65 additions and 75 deletions

2
Cargo.lock generated
View File

@ -6692,6 +6692,7 @@ dependencies = [
"serde", "serde",
"tracing", "tracing",
"unic-langid", "unic-langid",
"veloren-common",
"veloren-common-assets", "veloren-common-assets",
] ]
@ -7173,7 +7174,6 @@ dependencies = [
name = "veloren-voxygen-i18n-helpers" name = "veloren-voxygen-i18n-helpers"
version = "0.10.0" version = "0.10.0"
dependencies = [ dependencies = [
"hashbrown 0.12.3",
"tracing", "tracing",
"veloren-client-i18n", "veloren-client-i18n",
"veloren-common", "veloren-common",

View File

@ -7,6 +7,7 @@ version = "0.13.0"
[dependencies] [dependencies]
# Assets # Assets
common = {package = "veloren-common", path = "../../common"}
common-assets = {package = "veloren-common-assets", path = "../../common/assets"} common-assets = {package = "veloren-common-assets", path = "../../common/assets"}
ron = "0.8" ron = "0.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@ -19,6 +19,7 @@ use std::{borrow::Cow, io};
use assets::{source::DirEntry, AssetExt, AssetGuard, AssetHandle, ReloadWatcher, SharedString}; use assets::{source::DirEntry, AssetExt, AssetGuard, AssetHandle, ReloadWatcher, SharedString};
use tracing::warn; use tracing::warn;
// Re-export because I don't like prefix // Re-export because I don't like prefix
use common::comp::Content;
use common_assets as assets; use common_assets as assets;
// Re-export for argument creation // 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 /// Get a localized text from the variation of given key with given
/// arguments /// arguments
/// ///

View File

@ -183,6 +183,9 @@ impl<G> ChatType<G> {
// - In-game notes/books (we could add a variant that allows structuring complex, novel textual // - 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 // information as a syntax tree or some other intermediate format that can be localised by the
// client) // 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)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Content { pub enum Content {
/// The content is a plaintext string that should be shown to the user /// The content is a plaintext string that should be shown to the user
@ -239,16 +242,6 @@ impl Content {
Self::Localized { .. } => None, Self::Localized { .. } => None,
} }
} }
pub fn localize<F>(&self, i18n_variation: F) -> String
where
F: Fn(&str, u16, &HashMap<String, Content>) -> String,
{
match self {
Content::Plain(text) => text.to_string(),
Content::Localized { key, seed, args } => i18n_variation(key, *seed, args),
}
}
} }
// Stores chat text, type // Stores chat text, type

View File

@ -11,4 +11,3 @@ common = {package = "veloren-common", path = "../../common"}
i18n = {package = "veloren-client-i18n", path = "../../client/i18n"} i18n = {package = "veloren-client-i18n", path = "../../client/i18n"}
# Utility # Utility
tracing = "0.1" tracing = "0.1"
hashbrown = { version = "0.12" }

View File

@ -4,50 +4,18 @@ use common::comp::{
BuffKind, ChatMsg, ChatType, Content, BuffKind, ChatMsg, ChatType, Content,
}; };
use common_net::msg::{ChatTypeContext, PlayerInfo}; use common_net::msg::{ChatTypeContext, PlayerInfo};
use hashbrown::HashMap;
use i18n::Localization; use i18n::Localization;
pub fn make_localizer(
localization: &Localization,
) -> impl Fn(&str, u16, &HashMap<String, Content>) -> String + Copy + '_ {
fn get(
localization: &Localization,
key: &str,
seed: u16,
args: &HashMap<String, Content>,
) -> 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<String, Content>| get(localization, key, seed, args)
}
pub fn localize_chat_message( pub fn localize_chat_message(
msg: ChatMsg, msg: ChatMsg,
lookup_fn: impl Fn(&ChatMsg) -> ChatTypeContext, lookup_fn: impl Fn(&ChatMsg) -> ChatTypeContext,
localisation: &Localization, localization: &Localization,
show_char_name: bool, show_char_name: bool,
) -> (ChatType<String>, String) { ) -> (ChatType<String>, String) {
let info = lookup_fn(&msg); let info = lookup_fn(&msg);
let localizer = make_localizer(localisation);
let name_format = |uid: &common::uid::Uid| match info.player_alias.get(uid).cloned() { 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 None => info
.entity_name .entity_name
.get(uid) .get(uid)
@ -62,7 +30,7 @@ pub fn localize_chat_message(
} else { } else {
None None
}; };
let message = content.localize(localizer); let message = localization.get_content(content);
match (group, name) { match (group, name) {
(Some(group), None) => format!("({group}) [{alias}]: {message}"), (Some(group), None) => format!("({group}) [{alias}]: {message}"),
(None, None) => format!("[{alias}]: {message}"), (None, None) => format!("[{alias}]: {message}"),
@ -72,29 +40,35 @@ pub fn localize_chat_message(
}; };
let new_msg = match &msg.chat_type { 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! { .get_msg_ctx("hud-chat-online_msg", &i18n::fluent_args! {
"name" => name_format(uid), "name" => name_format(uid),
}) })
.into_owned(), .into_owned(),
ChatType::Offline(uid) => localisation ChatType::Offline(uid) => localization
.get_msg_ctx("hud-chat-offline_msg", &i18n::fluent_args! { .get_msg_ctx("hud-chat-offline_msg", &i18n::fluent_args! {
"name" => name_format(uid "name" => name_format(uid
), ),
}) })
.into_owned(), .into_owned(),
ChatType::CommandError => msg.content().localize(localizer), ChatType::CommandError => localization.get_content(msg.content()),
ChatType::CommandInfo => msg.content().localize(localizer), ChatType::CommandInfo => localization.get_content(msg.content()),
ChatType::FactionMeta(_) => msg.content().localize(localizer), ChatType::FactionMeta(_) => localization.get_content(msg.content()),
ChatType::GroupMeta(_) => msg.content().localize(localizer), ChatType::GroupMeta(_) => localization.get_content(msg.content()),
ChatType::Tell(from, to) => { ChatType::Tell(from, to) => {
let from_alias = name_format(from); let from_alias = name_format(from);
let to_alias = name_format(to); let to_alias = name_format(to);
// TODO: internationalise // TODO: internationalise
if *from == info.you { if *from == info.you {
format!("To [{to_alias}]: {}", msg.content().localize(localizer)) format!(
"To [{to_alias}]: {}",
localization.get_content(msg.content())
)
} else { } 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), ChatType::Say(uid) => message_format(uid, msg.content(), None),
@ -111,12 +85,18 @@ pub fn localize_chat_message(
let to_alias = name_format(to); let to_alias = name_format(to);
// TODO: internationalise // TODO: internationalise
if *from == info.you { if *from == info.you {
format!("To [{to_alias}]: {}", msg.content().localize(localizer)) format!(
"To [{to_alias}]: {}",
localization.get_content(msg.content())
)
} else { } 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) => { ChatType::Kill(kill_source, victim) => {
let i18n_buff = |buff| match buff { let i18n_buff = |buff| match buff {
BuffKind::Burning => "hud-outcome-burning", BuffKind::Burning => "hud-outcome-burning",
@ -155,9 +135,9 @@ pub fn localize_chat_message(
// Buff deaths // Buff deaths
KillSource::Player(attacker, KillType::Buff(buff_kind)) => { KillSource::Player(attacker, KillType::Buff(buff_kind)) => {
let i18n_buff = i18n_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), "victim" => name_format(victim),
"died_of_buff" => buff, "died_of_buff" => buff,
"attacker" => name_format(attacker), "attacker" => name_format(attacker),
@ -165,9 +145,9 @@ pub fn localize_chat_message(
}, },
KillSource::NonPlayer(attacker_name, KillType::Buff(buff_kind)) => { KillSource::NonPlayer(attacker_name, KillType::Buff(buff_kind)) => {
let i18n_buff = i18n_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), "victim" => name_format(victim),
"died_of_buff" => buff, "died_of_buff" => buff,
"attacker" => attacker_name, "attacker" => attacker_name,
@ -175,9 +155,9 @@ pub fn localize_chat_message(
}, },
KillSource::NonExistent(KillType::Buff(buff_kind)) => { KillSource::NonExistent(KillType::Buff(buff_kind)) => {
let i18n_buff = i18n_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", "hud-chat-died_of_buff_nonexistent_msg",
&i18n::fluent_args! { &i18n::fluent_args! {
"victim" => name_format(victim), "victim" => name_format(victim),
@ -195,7 +175,7 @@ pub fn localize_chat_message(
KillType::Other => "hud-chat-pvp_other_kill_msg", KillType::Other => "hud-chat-pvp_other_kill_msg",
&KillType::Buff(_) => unreachable!("handled above"), &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), "victim" => name_format(victim),
"attacker" => name_format(attacker), "attacker" => name_format(attacker),
}) })
@ -210,30 +190,30 @@ pub fn localize_chat_message(
KillType::Other => "hud-chat-npc_other_kill_msg", KillType::Other => "hud-chat-npc_other_kill_msg",
&KillType::Buff(_) => unreachable!("handled above"), &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), "victim" => name_format(victim),
"attacker" => attacker_name, "attacker" => attacker_name,
}) })
}, },
// Other deaths // Other deaths
KillSource::Environment(environment) => { 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), "name" => name_format(victim),
"environment" => environment, "environment" => environment,
}) })
}, },
KillSource::FallDamage => { 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), "name" => name_format(victim),
}) })
}, },
KillSource::Suicide => { 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), "name" => name_format(victim),
}) })
}, },
KillSource::NonExistent(_) | KillSource::Other => { 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), "name" => name_format(victim),
}) })
}, },
@ -245,14 +225,14 @@ pub fn localize_chat_message(
(msg.chat_type, new_msg) (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"; const YOU: &str = "hud-chat-you";
// Leave space for a mod badge icon. // Leave space for a mod badge icon.
const MOD_SPACING: &str = " "; const MOD_SPACING: &str = " ";
match (info.is_moderator, you) { match (info.is_moderator, you) {
(false, false) => info.player_alias, (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, 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),),
} }
} }

View File

@ -20,7 +20,6 @@ use conrod_core::{
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon, widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
}; };
use i18n::Localization; use i18n::Localization;
use i18n_helpers::make_localizer;
use keyboard_keynames::key_layout::KeyLayout; use keyboard_keynames::key_layout::KeyLayout;
const MAX_BUBBLE_WIDTH: f64 = 250.0; const MAX_BUBBLE_WIDTH: f64 = 250.0;
@ -535,7 +534,7 @@ impl<'a> Widget for Overhead<'a> {
// Speech bubble // Speech bubble
if let Some(bubble) = self.bubble { if let Some(bubble) = self.bubble {
let dark_mode = self.settings.speech_bubble_dark_mode; 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 (text_color, shadow_color) = bubble_color(bubble, dark_mode);
let mut text = Text::new(&bubble_contents) let mut text = Text::new(&bubble_contents)
.color(text_color) .color(text_color)