veloren/voxygen/i18n-helpers/src/lib.rs
Xiretza 06111612a3 voxygen: better translation of buff-death messages
This removes interpolation of translated strings, which is bad practice
because it simply doesn't work in many languages.

Translation files were updated using the following fish script. Some
resulting translations are truly terrible, but they always have been -
now it's just a lot more obvious.

```
function getbuff --argument langfile buff
    grep hud-outcome-$buff $langfile | cut -d= -f2- | string trim
end

set buffs burning bleeding curse crippled frozen mysterious

function replacebuffs --argument langfile entity
	set slug 'hud-chat-died_of_'$entity'_buff_msg'
    for buff in $buffs[-1..1]
        set bstr (getbuff $langfile $buff)
        or continue
        sed -i -e '/'$slug'/{p; s/[^=]* = \(.*\)/ .'$buff' = \1/; s/{ \$died_of_buff }/'(string replace / '\\/' $bstr)'/}' $langfile
    end
    sed -i -E 's/('$slug' \=).*/\1/' $langfile
end

function dofile --argument langfile
    sed -i -e s/buff_nonexistent/nonexistent_buff/ $langfile
    for entity in pvp npc nonexistent
        replacebuffs $langfile $entity
    end
    sed -i -e s/nonexistent_buff/buff_nonexistent/ $langfile
    sed -i -e /hud-outcome-/d $langfile
    sed -i -e '/#.*outcomes/d' $langfile
end

for langfile in assets/voxygen/i18n/*/hud/chat.ftl
    echo doing $langfile
    dofile $langfile
end
```
2023-08-29 17:09:48 +00:00

263 lines
11 KiB
Rust

#![feature(let_chains)]
use std::borrow::Cow;
use common::comp::{
chat::{KillSource, KillType},
BuffKind, ChatMsg, ChatType, Content,
};
use common_net::msg::{ChatTypeContext, PlayerInfo};
use i18n::Localization;
pub fn localize_chat_message(
msg: ChatMsg,
lookup_fn: impl Fn(&ChatMsg) -> ChatTypeContext,
localization: &Localization,
show_char_name: bool,
) -> (ChatType<String>, String) {
let info = lookup_fn(&msg);
let name_format = |uid: &common::uid::Uid| match info.player_alias.get(uid).cloned() {
Some(pi) => insert_alias(info.you == *uid, pi, localization),
None => info
.entity_name
.get(uid)
.cloned()
.expect("client didn't proved enough info"),
};
let message_format = |from: &common::uid::Uid, content: &Content, group: Option<&String>| {
let alias = name_format(from);
let name = if let Some(pi) = info.player_alias.get(from).cloned() && show_char_name {
pi.character.map(|c| c.name)
} else {
None
};
let message = localization.get_content(content);
match (group, name) {
(Some(group), None) => format!("({group}) [{alias}]: {message}"),
(None, None) => format!("[{alias}]: {message}"),
(Some(group), Some(name)) => format!("({group}) [{alias}] {name}: {message}"),
(None, Some(name)) => format!("[{alias}] {name}: {message}"),
}
};
let new_msg = match &msg.chat_type {
ChatType::Online(uid) => localization
.get_msg_ctx("hud-chat-online_msg", &i18n::fluent_args! {
"name" => name_format(uid),
})
.into_owned(),
ChatType::Offline(uid) => localization
.get_msg_ctx("hud-chat-offline_msg", &i18n::fluent_args! {
"name" => name_format(uid
),
})
.into_owned(),
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}]: {}",
localization.get_content(msg.content())
)
} else {
format!(
"From [{from_alias}]: {}",
localization.get_content(msg.content())
)
}
},
ChatType::Say(uid) => message_format(uid, msg.content(), None),
ChatType::Group(uid, s) => message_format(uid, msg.content(), Some(s)),
ChatType::Faction(uid, s) => message_format(uid, msg.content(), Some(s)),
ChatType::Region(uid) => message_format(uid, msg.content(), None),
ChatType::World(uid) => message_format(uid, msg.content(), None),
// NPCs can't talk. Should be filtered by hud/mod.rs for voxygen and
// should be filtered by server (due to not having a Pos) for chat-cli
ChatType::Npc(uid) => message_format(uid, msg.content(), None),
ChatType::NpcSay(uid) => message_format(uid, msg.content(), None),
ChatType::NpcTell(from, to) => {
let from_alias = name_format(from);
let to_alias = name_format(to);
// TODO: internationalise
if *from == info.you {
format!(
"To [{to_alias}]: {}",
localization.get_content(msg.content())
)
} else {
format!(
"From [{from_alias}]: {}",
localization.get_content(msg.content())
)
}
},
ChatType::Meta => localization.get_content(msg.content()),
ChatType::Kill(kill_source, victim) => {
let get_buff_ident = |buff| match buff {
BuffKind::Burning => "burning",
BuffKind::Bleeding => "bleeding",
BuffKind::Cursed => "curse",
BuffKind::Crippled => "crippled",
BuffKind::Frozen => "frozen",
BuffKind::Regeneration
| BuffKind::Saturation
| BuffKind::Potion
| BuffKind::CampfireHeal
| BuffKind::EnergyRegen
| BuffKind::IncreaseMaxEnergy
| BuffKind::IncreaseMaxHealth
| BuffKind::Invulnerability
| BuffKind::ProtectingWard
| BuffKind::Frenzied
| BuffKind::Hastened
| BuffKind::Fortitude
| BuffKind::Reckless
| BuffKind::Flame
| BuffKind::Frigid
| BuffKind::Lifesteal
// | BuffKind::SalamanderAspect
| BuffKind::ImminentCritical
| BuffKind::Fury
| BuffKind::Sunderer
| BuffKind::Defiance
| BuffKind::Bloodfeast
| BuffKind::Berserk => {
tracing::error!("Player was killed by a positive buff!");
"mysterious"
},
BuffKind::Wet
| BuffKind::Ensnared
| BuffKind::Poisoned
| BuffKind::Parried
| BuffKind::PotionSickness
| BuffKind::Polymorphed(_) => {
tracing::error!("Player was killed by a debuff that doesn't do damage!");
"mysterious"
},
};
match kill_source {
// Buff deaths
KillSource::Player(attacker, KillType::Buff(buff_kind)) => {
let buff_ident = get_buff_ident(*buff_kind);
let s = localization
.get_attr_ctx(
"hud-chat-died_of_pvp_buff_msg",
buff_ident,
&i18n::fluent_args! {
"victim" => name_format(victim),
"attacker" => name_format(attacker),
},
)
.into_owned();
Cow::Owned(s)
},
KillSource::NonPlayer(attacker_name, KillType::Buff(buff_kind)) => {
let buff_ident = get_buff_ident(*buff_kind);
let s = localization
.get_attr_ctx(
"hud-chat-died_of_npc_buff_msg",
buff_ident,
&i18n::fluent_args! {
"victim" => name_format(victim),
"attacker" => attacker_name,
},
)
.into_owned();
Cow::Owned(s)
},
KillSource::NonExistent(KillType::Buff(buff_kind)) => {
let buff_ident = get_buff_ident(*buff_kind);
let s = localization
.get_attr_ctx(
"hud-chat-died_of_buff_nonexistent_msg",
buff_ident,
&i18n::fluent_args! {
"victim" => name_format(victim),
},
)
.into_owned();
Cow::Owned(s)
},
// PvP deaths
KillSource::Player(attacker, kill_type) => {
let key = match kill_type {
KillType::Melee => "hud-chat-pvp_melee_kill_msg",
KillType::Projectile => "hud-chat-pvp_ranged_kill_msg",
KillType::Explosion => "hud-chat-pvp_explosion_kill_msg",
KillType::Energy => "hud-chat-pvp_energy_kill_msg",
KillType::Other => "hud-chat-pvp_other_kill_msg",
&KillType::Buff(_) => unreachable!("handled above"),
};
localization.get_msg_ctx(key, &i18n::fluent_args! {
"victim" => name_format(victim),
"attacker" => name_format(attacker),
})
},
// PvE deaths
KillSource::NonPlayer(attacker_name, kill_type) => {
let key = match kill_type {
KillType::Melee => "hud-chat-npc_melee_kill_msg",
KillType::Projectile => "hud-chat-npc_ranged_kill_msg",
KillType::Explosion => "hud-chat-npc_explosion_kill_msg",
KillType::Energy => "hud-chat-npc_energy_kill_msg",
KillType::Other => "hud-chat-npc_other_kill_msg",
&KillType::Buff(_) => unreachable!("handled above"),
};
localization.get_msg_ctx(key, &i18n::fluent_args! {
"victim" => name_format(victim),
"attacker" => attacker_name,
})
},
// Other deaths
KillSource::Environment(environment) => {
localization.get_msg_ctx("hud-chat-environment_kill_msg", &i18n::fluent_args! {
"name" => name_format(victim),
"environment" => environment,
})
},
KillSource::FallDamage => {
localization.get_msg_ctx("hud-chat-fall_kill_msg", &i18n::fluent_args! {
"name" => name_format(victim),
})
},
KillSource::Suicide => {
localization.get_msg_ctx("hud-chat-suicide_msg", &i18n::fluent_args! {
"name" => name_format(victim),
})
},
KillSource::NonExistent(_) | KillSource::Other => {
localization.get_msg_ctx("hud-chat-default_death_msg", &i18n::fluent_args! {
"name" => name_format(victim),
})
},
}
.into_owned()
},
};
(msg.chat_type, new_msg)
}
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) => localization.get_msg(YOU).to_string(),
(true, false) => format!("{}{}", MOD_SPACING, info.player_alias),
(true, true) => format!("{}{}", MOD_SPACING, &localization.get_msg(YOU),),
}
}