diff --git a/common/src/comp/inventory/item/item_key.rs b/common/src/comp/inventory/item/item_key.rs index ea6cc5af88..28e33d2f8e 100644 --- a/common/src/comp/inventory/item/item_key.rs +++ b/common/src/comp/inventory/item/item_key.rs @@ -14,7 +14,7 @@ pub enum ItemKey { Empty, } -impl From<&T> for ItemKey { +impl From<&T> for ItemKey { fn from(item_desc: &T) -> Self { let item_definition_id = item_desc.item_definition_id(); diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index f4e09bc8a5..fe67ab0962 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -14,13 +14,15 @@ use crate::{ recipe::RecipeInput, terrain::Block, }; +use common_i18n::Content; use core::{ convert::TryFrom, mem, num::{NonZeroU32, NonZeroU64}, }; use crossbeam_utils::atomic::AtomicCell; -use hashbrown::Equivalent; +use hashbrown::{Equivalent, HashMap}; +use item_key::ItemKey; use serde::{de, Deserialize, Serialize, Serializer}; use specs::{Component, DenseVecStorage, DerefFlaggedStorage}; use std::{borrow::Cow, collections::hash_map::DefaultHasher, fmt, sync::Arc}; @@ -342,6 +344,7 @@ pub enum ItemKind { }, Ingredient { /// Used to generate names for modular items composed of this ingredient + #[deprecated] descriptor: String, }, TagExamples { @@ -383,6 +386,7 @@ impl ItemKind { }, ItemKind::Throwable { kind } => format!("Throwable: {:?}", kind), ItemKind::Utility { kind } => format!("Utility: {:?}", kind), + #[allow(deprecated)] ItemKind::Ingredient { descriptor } => format!("Ingredient: {}", descriptor), ItemKind::TagExamples { item_ids } => format!("TagExamples: {:?}", item_ids), } @@ -466,11 +470,28 @@ impl Hash for Item { } } +// at the time of writing, we use Fluent, which supports attributes +// and we can get both name and description using them +type I18nId = String; + #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ItemName { - Direct(String), - Modular, - Component(String), +// TODO: probably make a Resource if used outside of voxygen +// TODO: add hot-reloading similar to how ItemImgs does it? +// TODO: make it work with plugins (via Concatenate?) +/// To be used with ItemDesc::l10n +pub struct ItemL10n { + /// maps ItemKey to i18n identifier + map: HashMap, +} + +impl assets::Asset for ItemL10n { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; +} + +impl ItemL10n { + pub fn new_expect() -> Self { ItemL10n::load_expect("common.item_l10n").read().clone() } } #[derive(Clone, Debug)] @@ -1147,16 +1168,6 @@ impl Item { }) } - /// Generate a human-readable description of the item and amount. - #[deprecated] - pub fn describe(&self) -> String { - if self.amount() > 1 { - format!("{} x {}", self.amount(), self.name()) - } else { - self.name().to_string() - } - } - #[deprecated] pub fn name(&self) -> Cow { match &self.item_base { @@ -1400,6 +1411,7 @@ pub fn flatten_counted_items<'a>( pub trait ItemDesc { #[deprecated] fn description(&self) -> &str; + #[deprecated] fn name(&self) -> Cow; fn kind(&self) -> Cow; fn amount(&self) -> NonZeroU32; @@ -1420,6 +1432,18 @@ pub trait ItemDesc { None } } + + /// Return name's and description's localization descriptors + fn l10n(&self, l10n: &ItemL10n) -> (Content, Content) { + let item_key: ItemKey = self.into(); + + let _key = l10n.map.get(&item_key); + ( + // construct smth like Content::Attr + todo!(), + todo!(), + ) + } } impl ItemDesc for Item { diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 83c16f5f9e..2c1e50a0a4 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -3,7 +3,7 @@ use super::{ img_ids::{Imgs, ImgsRot}, item_imgs::ItemImgs, slots::{ArmorSlot, EquipSlot, InventorySlot, SlotManager}, - HudInfo, Show, CRITICAL_HP_COLOR, LOW_HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, + util, HudInfo, Show, CRITICAL_HP_COLOR, LOW_HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, }; use crate::{ game_input::GameInput, @@ -21,7 +21,7 @@ use common::{ combat::{combat_rating, perception_dist_multiplier_from_stealth, Damage}, comp::{ inventory::InventorySortOrder, - item::{ItemDef, ItemDesc, MaterialStatManifest, Quality}, + item::{ItemDef, ItemDesc, ItemL10n, MaterialStatManifest, Quality}, Body, Energy, Health, Inventory, Poise, SkillSet, Stats, }, }; @@ -81,6 +81,7 @@ pub struct InventoryScroller<'a> { slot_manager: &'a mut SlotManager, pulse: f32, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, show_stats: bool, show_bag_inv: bool, on_right: bool, @@ -105,6 +106,7 @@ impl<'a> InventoryScroller<'a> { slot_manager: &'a mut SlotManager, pulse: f32, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, show_stats: bool, show_bag_inv: bool, on_right: bool, @@ -127,6 +129,7 @@ impl<'a> InventoryScroller<'a> { slot_manager, pulse, localized_strings, + item_l10n, show_stats, show_bag_inv, on_right, @@ -365,8 +368,18 @@ impl<'a> InventoryScroller<'a> { items.sort_by_cached_key(|(_, item)| { ( item.is_none(), - item.as_ref() - .map(|i| (std::cmp::Reverse(i.quality()), i.name(), i.amount())), + item.as_ref().map(|i| { + ( + std::cmp::Reverse(i.quality()), + { + // TODO: we do double the work here, optimize? + let (name, _) = + util::item_text(i, self.localized_strings, self.item_l10n); + name + }, + i.amount(), + ) + }), ) }); } @@ -457,7 +470,8 @@ impl<'a> InventoryScroller<'a> { .set(state.ids.inv_slots[i], ui); } if self.details_mode { - Text::new(&item.name()) + let (name, _) = util::item_text(item, self.localized_strings, self.item_l10n); + Text::new(&name) .top_left_with_margins_on( state.ids.inv_alignment, 0.0 + y as f64 * slot_size, @@ -638,6 +652,7 @@ pub struct Bag<'a> { slot_manager: &'a mut SlotManager, pulse: f32, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, stats: &'a Stats, skill_set: &'a SkillSet, health: &'a Health, @@ -663,6 +678,7 @@ impl<'a> Bag<'a> { slot_manager: &'a mut SlotManager, pulse: f32, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, stats: &'a Stats, skill_set: &'a SkillSet, health: &'a Health, @@ -686,6 +702,7 @@ impl<'a> Bag<'a> { slot_manager, pulse, localized_strings, + item_l10n, stats, skill_set, energy, @@ -805,6 +822,7 @@ impl<'a> Widget for Bag<'a> { self.pulse, self.msm, self.localized_strings, + self.item_l10n, ) .title_font_size(self.fonts.cyri.scale(20)) .parent(ui.window) @@ -821,6 +839,7 @@ impl<'a> Widget for Bag<'a> { self.slot_manager, self.pulse, self.localized_strings, + self.item_l10n, self.show.stats, self.show.bag_inv, true, diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 1984985f12..b28a085389 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -3,7 +3,7 @@ use super::{ img_ids::{Imgs, ImgsRot}, item_imgs::{animate_by_pulse, ItemImgs}, slots::{CraftSlot, CraftSlotInfo, SlotManager}, - HudInfo, Show, TEXT_COLOR, TEXT_DULL_RED_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN, + util, HudInfo, Show, TEXT_COLOR, TEXT_DULL_RED_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN, }; use crate::ui::{ fonts::Fonts, @@ -19,8 +19,8 @@ use common::{ item_key::ItemKey, modular::{self, ModularComponent}, tool::{AbilityMap, ToolKind}, - Item, ItemBase, ItemDef, ItemDesc, ItemKind, ItemTag, MaterialStatManifest, Quality, - TagExampleInfo, + Item, ItemBase, ItemDef, ItemDesc, ItemKind, ItemL10n, ItemTag, MaterialStatManifest, + Quality, TagExampleInfo, }, slot::{InvSlotId, Slot}, Inventory, @@ -151,6 +151,7 @@ pub struct Crafting<'a> { imgs: &'a Imgs, fonts: &'a Fonts, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, pulse: f32, rot_imgs: &'a ImgsRot, item_tooltip_manager: &'a mut ItemTooltipManager, @@ -171,6 +172,7 @@ impl<'a> Crafting<'a> { imgs: &'a Imgs, fonts: &'a Fonts, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, pulse: f32, rot_imgs: &'a ImgsRot, item_tooltip_manager: &'a mut ItemTooltipManager, @@ -187,6 +189,7 @@ impl<'a> Crafting<'a> { imgs, fonts, localized_strings, + item_l10n, pulse, rot_imgs, item_tooltip_manager, @@ -355,6 +358,7 @@ impl<'a> Widget for Crafting<'a> { self.pulse, self.msm, self.localized_strings, + self.item_l10n, ) .title_font_size(self.fonts.cyri.scale(20)) .parent(ui.window) @@ -594,6 +598,7 @@ impl<'a> Widget for Crafting<'a> { .iter() .filter(|(_, recipe)| match search_filter { SearchFilter::None => { + #[allow(deprecated)] let output_name = recipe.output.0.name().to_lowercase(); search_keys .iter() @@ -608,9 +613,11 @@ impl<'a> Widget for Crafting<'a> { }; match input { + #[allow(deprecated)] RecipeInput::Item(def) => search(&def.name()), RecipeInput::Tag(tag) => search(tag.name()), RecipeInput::TagSameItem(tag) => search(tag.name()), + #[allow(deprecated)] RecipeInput::ListSameItem(defs) => { defs.iter().any(|def| search(&def.name())) }, @@ -667,6 +674,7 @@ impl<'a> Widget for Crafting<'a> { !is_craftable, !has_materials, recipe.output.0.quality(), + #[allow(deprecated)] recipe.output.0.name(), ) }); @@ -1214,6 +1222,8 @@ impl<'a> Widget for Crafting<'a> { }; if let Some(output_item) = output_item { + let (name, _) = + util::item_text(&output_item, self.localized_strings, self.item_l10n); Button::image(animate_by_pulse( &self .item_imgs @@ -1221,7 +1231,7 @@ impl<'a> Widget for Crafting<'a> { self.pulse, )) .w_h(55.0, 55.0) - .label(&output_item.name()) + .label(&name) .label_color(TEXT_COLOR) .label_font_size(self.fonts.cyri.scale(14)) .label_font_id(self.fonts.cyri.conrod_id) @@ -1926,6 +1936,7 @@ impl<'a> Widget for Crafting<'a> { .was_clicked() { events.push(Event::ChangeCraftingTab(CraftingTab::All)); + #[allow(deprecated)] events.push(Event::SearchRecipe(Some(item_def.name().to_string()))); } // Item image @@ -1963,7 +1974,13 @@ impl<'a> Widget for Crafting<'a> { .font_size(self.fonts.cyri.scale(14)) .color(TEXT_COLOR) .set(state.ids.req_text[i], ui); - Text::new(&item_def.name()) + + let (name, _) = util::item_text( + item_def.as_ref(), + self.localized_strings, + self.item_l10n, + ); + Text::new(&name) .right_from(state.ids.ingredient_frame[i], 10.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) @@ -1972,14 +1989,35 @@ impl<'a> Widget for Crafting<'a> { } else { // Ingredients let name = match recipe_input { - RecipeInput::Item(_) => item_def.name().to_string(), + RecipeInput::Item(_) => { + let (name, _) = util::item_text( + item_def.as_ref(), + self.localized_strings, + self.item_l10n, + ); + + name + }, RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag) => { + // TODO: Localize! format!("Any {} item", tag.name()) }, RecipeInput::ListSameItem(item_defs) => { + // TODO: Localize! format!( "Any of {}", - item_defs.iter().map(|def| def.name()).collect::() + item_defs + .iter() + .map(|def| { + let (name, _) = util::item_text( + def.as_ref(), + self.localized_strings, + self.item_l10n, + ); + + name + }) + .collect::() ) }, }; diff --git a/voxygen/src/hud/loot_scroller.rs b/voxygen/src/hud/loot_scroller.rs index 8799f2f8be..232dbe8c6b 100644 --- a/voxygen/src/hud/loot_scroller.rs +++ b/voxygen/src/hud/loot_scroller.rs @@ -2,11 +2,11 @@ use super::{ animate_by_pulse, get_quality_col, img_ids::{Imgs, ImgsRot}, item_imgs::ItemImgs, - HudInfo, Show, Windows, TEXT_COLOR, + util, HudInfo, Show, Windows, TEXT_COLOR, }; use crate::ui::{fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable}; use client::Client; -use common::comp::inventory::item::{Item, ItemDesc, MaterialStatManifest, Quality}; +use common::comp::inventory::item::{Item, ItemDesc, ItemL10n, MaterialStatManifest, Quality}; use conrod_core::{ color, position::Dimension, @@ -58,6 +58,7 @@ pub struct LootScroller<'a> { rot_imgs: &'a ImgsRot, fonts: &'a Fonts, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, msm: &'a MaterialStatManifest, item_tooltip_manager: &'a mut ItemTooltipManager, pulse: f32, @@ -76,6 +77,7 @@ impl<'a> LootScroller<'a> { rot_imgs: &'a ImgsRot, fonts: &'a Fonts, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, msm: &'a MaterialStatManifest, item_tooltip_manager: &'a mut ItemTooltipManager, pulse: f32, @@ -90,6 +92,7 @@ impl<'a> LootScroller<'a> { rot_imgs, fonts, localized_strings, + item_l10n, msm, item_tooltip_manager, pulse, @@ -153,6 +156,7 @@ impl<'a> Widget for LootScroller<'a> { self.pulse, self.msm, self.localized_strings, + self.item_l10n, ) .title_font_size(self.fonts.cyri.scale(20)) .parent(ui.window) @@ -351,7 +355,11 @@ impl<'a> Widget for LootScroller<'a> { &i18n::fluent_args! { "actor" => taken_by, "amount" => amount, - "item" => item.name(), + "item" => { + let (name, _) = + util::item_text(&item, self.localized_strings, self.item_l10n); + name + }, }, ); let label_font_size = 20; diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index f25efa52ef..e6b6edc18b 100755 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -98,7 +98,7 @@ use common::{ }, item::{ tool::{AbilityContext, ToolKind}, - ItemDesc, MaterialStatManifest, Quality, + ItemDefinitionIdOwned, ItemDesc, ItemL10n, MaterialStatManifest, Quality, }, loot_owner::LootOwnerKind, pet::is_mountable, @@ -1286,6 +1286,7 @@ pub struct Hud { world_map: (/* Id */ Vec, Vec2), imgs: Imgs, item_imgs: ItemImgs, + item_l10n: ItemL10n, fonts: Fonts, rot_imgs: ImgsRot, failed_block_pickups: HashMap, @@ -1341,6 +1342,8 @@ impl Hud { let rot_imgs = ImgsRot::load(&mut ui).expect("Failed to load rot images!"); // Load item images. let item_imgs = ItemImgs::new(&mut ui, imgs.not_found); + // Load item text ("reference" to name and description) + let item_l10n = ItemL10n::new_expect(); // Load fonts. let fonts = Fonts::load(global_state.i18n.read().fonts(), &mut ui) .expect("Impossible to load fonts!"); @@ -1380,6 +1383,7 @@ impl Hud { world_map, rot_imgs, item_imgs, + item_l10n, fonts, ids, failed_block_pickups: HashMap::default(), @@ -2008,7 +2012,7 @@ impl Hud { // Item overitem::Overitem::new( - item.describe().into(), + util::describe(item, i18n, &self.item_l10n).into(), quality, distance, fonts, @@ -2091,20 +2095,27 @@ impl Hud { )] }, BlockInteraction::Unlock(kind) => { + let item_name = |item_id: &ItemDefinitionIdOwned| { + item_id + .as_ref() + .itemdef_id() + .map(|id| { + let item = Item::new_from_asset_expect(id); + util::describe(&item, i18n, &self.item_l10n) + }) + .unwrap_or_else(|| "modular item".to_string()) + }; + vec![(Some(GameInput::Interact), match kind { UnlockKind::Free => i18n.get_msg("hud-open").to_string(), - UnlockKind::Requires(item) => i18n + UnlockKind::Requires(item_id) => i18n .get_msg_ctx("hud-unlock-requires", &i18n::fluent_args! { - "item" => item.as_ref().itemdef_id() - .map(|id| Item::new_from_asset_expect(id).describe()) - .unwrap_or_else(|| "modular item".to_string()), + "item" => item_name(item_id), }) .to_string(), - UnlockKind::Consumes(item) => i18n + UnlockKind::Consumes(item_id) => i18n .get_msg_ctx("hud-unlock-requires", &i18n::fluent_args! { - "item" => item.as_ref().itemdef_id() - .map(|id| Item::new_from_asset_expect(id).describe()) - .unwrap_or_else(|| "modular item".to_string()), + "item" => item_name(item_id), }) .to_string(), })] @@ -3166,6 +3177,7 @@ impl Hud { item_tooltip_manager, &mut self.slot_manager, i18n, + &self.item_l10n, &msm, self.floaters.combo_floater, &context, @@ -3213,6 +3225,7 @@ impl Hud { &mut self.slot_manager, self.pulse, i18n, + &self.item_l10n, player_stats, skill_set, health, @@ -3257,6 +3270,7 @@ impl Hud { item_tooltip_manager, &mut self.slot_manager, i18n, + &self.item_l10n, &msm, self.pulse, &mut self.show, @@ -3337,6 +3351,7 @@ impl Hud { &self.imgs, &self.fonts, i18n, + &self.item_l10n, self.pulse, &self.rot_imgs, item_tooltip_manager, @@ -3503,6 +3518,7 @@ impl Hud { &self.rot_imgs, &self.fonts, i18n, + &self.item_l10n, &msm, item_tooltip_manager, self.pulse, diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 80541ea8cc..5ba88056d1 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -19,7 +19,6 @@ use crate::{ GlobalState, }; use i18n::Localization; -use std::borrow::Cow; use client::{self, Client}; use common::comp::{ @@ -27,7 +26,7 @@ use common::comp::{ ability::{AbilityInput, Stance}, item::{ tool::{AbilityContext, ToolKind}, - ItemDesc, MaterialStatManifest, + ItemDesc, ItemL10n, MaterialStatManifest, }, skillset::SkillGroupKind, Ability, ActiveAbilities, Body, CharacterState, Combo, Energy, Health, Inventory, Poise, @@ -308,6 +307,7 @@ pub struct Skillbar<'a> { item_tooltip_manager: &'a mut ItemTooltipManager, slot_manager: &'a mut slots::SlotManager, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, pulse: f32, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -344,6 +344,7 @@ impl<'a> Skillbar<'a> { item_tooltip_manager: &'a mut ItemTooltipManager, slot_manager: &'a mut slots::SlotManager, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, msm: &'a MaterialStatManifest, combo_floater: Option, context: &'a AbilityContext, @@ -375,6 +376,7 @@ impl<'a> Skillbar<'a> { item_tooltip_manager, slot_manager, localized_strings, + item_l10n, msm, combo_floater, context, @@ -1008,6 +1010,7 @@ impl<'a> Skillbar<'a> { self.pulse, self.msm, self.localized_strings, + self.item_l10n, ) .title_font_size(self.fonts.cyri.scale(20)) .parent(ui.window) @@ -1028,9 +1031,12 @@ impl<'a> Skillbar<'a> { let (hotbar, inventory, _, skill_set, active_abilities, _, contexts, _, _, _) = content_source; hotbar.get(slot).and_then(|content| match content { - hotbar::SlotContents::Inventory(i, _) => inventory - .get_by_hash(i) - .map(|item| (item.name(), Cow::Borrowed(item.description()))), + hotbar::SlotContents::Inventory(i, _) => inventory.get_by_hash(i).map(|item| { + let (title, desc) = + util::item_text(item, self.localized_strings, self.item_l10n); + + (title.into(), desc.into()) + }), hotbar::SlotContents::Ability(i) => active_abilities .and_then(|a| { a.auxiliary_set(Some(inventory), Some(skill_set)) diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index 63599aec65..4f0f8bb450 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -10,7 +10,7 @@ use vek::*; use client::Client; use common::{ comp::{ - inventory::item::{ItemDesc, MaterialStatManifest, Quality}, + inventory::item::{ItemDesc, ItemL10n, MaterialStatManifest, Quality}, Inventory, Stats, }, trade::{PendingTrade, SitePrices, TradeAction, TradePhase}, @@ -35,7 +35,8 @@ use super::{ img_ids::{Imgs, ImgsRot}, item_imgs::ItemImgs, slots::{SlotKind, SlotManager, TradeSlot}, - Hud, HudInfo, Show, TradeAmountInput, TEXT_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN, + util, Hud, HudInfo, Show, TradeAmountInput, TEXT_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, + UI_MAIN, }; use std::borrow::Cow; @@ -98,6 +99,7 @@ pub struct Trade<'a> { common: widget::CommonBuilder, slot_manager: &'a mut SlotManager, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, msm: &'a MaterialStatManifest, pulse: f32, show: &'a mut Show, @@ -116,6 +118,7 @@ impl<'a> Trade<'a> { item_tooltip_manager: &'a mut ItemTooltipManager, slot_manager: &'a mut SlotManager, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, msm: &'a MaterialStatManifest, pulse: f32, show: &'a mut Show, @@ -132,6 +135,7 @@ impl<'a> Trade<'a> { common: widget::CommonBuilder::default(), slot_manager, localized_strings, + item_l10n, msm, pulse, show, @@ -345,6 +349,7 @@ impl<'a> Trade<'a> { self.pulse, self.msm, self.localized_strings, + self.item_l10n, ) .title_font_size(self.fonts.cyri.scale(20)) .parent(ui.window) @@ -362,6 +367,7 @@ impl<'a> Trade<'a> { self.slot_manager, self.pulse, self.localized_strings, + self.item_l10n, false, true, false, @@ -530,7 +536,11 @@ impl<'a> Trade<'a> { let itemname = slot .invslot .and_then(|i| inventory.get(i)) - .map(|i| i.name()) + .map(|i| { + let (name, _) = util::item_text(&i, self.localized_strings, self.item_l10n); + + Cow::Owned(name) + }) .unwrap_or(Cow::Borrowed("")); let is_present = slot.quantity > 0 && slot.invslot.is_some(); Text::new(&format!("{} x {}", slot.quantity, itemname)) diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 63c655805e..bcaf1198f3 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -5,7 +5,7 @@ use common::{ item::{ armor::{Armor, ArmorKind, Protection}, tool::{Hands, Tool, ToolKind}, - Effects, Item, ItemDefinitionId, ItemDesc, ItemKind, MaterialKind, + Effects, Item, ItemDefinitionId, ItemDesc, ItemKind, ItemL10n, MaterialKind, MaterialStatManifest, }, BuffKind, @@ -15,7 +15,7 @@ use common::{ }; use conrod_core::image; use i18n::{fluent_args, Localization}; -use std::{borrow::Cow, fmt::Write}; +use std::{borrow::Cow, fmt::Write, num::NonZeroU32}; pub fn price_desc<'a>( prices: &Option, @@ -61,6 +61,31 @@ pub fn price_desc<'a>( Some((buy_string, sell_string, deal_goodness)) } +pub fn item_text<'a, I: ItemDesc + ?Sized>( + item: &I, + i18n: &'a Localization, + l10n_spec: &'a ItemL10n, +) -> (String, String) { + let (title, desc) = item.l10n(l10n_spec); + + (i18n.get_content(&title), i18n.get_content(&desc)) +} + +pub fn describe<'a, I: ItemDesc + ?Sized>( + item: &I, + i18n: &'a Localization, + l10n_spec: &'a ItemL10n, +) -> String { + let (title, _) = item_text(item, i18n, l10n_spec); + let amount = item.amount(); + + if amount > NonZeroU32::new(1).unwrap() { + format!("{amount} x {title}") + } else { + title + } +} + pub fn kind_text<'a>(kind: &ItemKind, i18n: &'a Localization) -> Cow<'a, str> { match kind { ItemKind::Armor(armor) => armor_kind(armor, i18n), diff --git a/voxygen/src/ui/widgets/item_tooltip.rs b/voxygen/src/ui/widgets/item_tooltip.rs index 0235c9fe1f..087d73ccfb 100644 --- a/voxygen/src/ui/widgets/item_tooltip.rs +++ b/voxygen/src/ui/widgets/item_tooltip.rs @@ -10,7 +10,7 @@ use common::{ comp::{ item::{ armor::Protection, item_key::ItemKey, modular::ModularComponent, Item, ItemDesc, - ItemKind, ItemTag, MaterialStatManifest, Quality, + ItemKind, ItemL10n, ItemTag, MaterialStatManifest, Quality, }, Energy, }, @@ -296,6 +296,7 @@ pub struct ItemTooltip<'a> { item_imgs: &'a ItemImgs, pulse: f32, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, } #[derive(Clone, Debug, Default, PartialEq, WidgetStyle)] @@ -360,6 +361,7 @@ impl<'a> ItemTooltip<'a> { pulse: f32, msm: &'a MaterialStatManifest, localized_strings: &'a Localization, + item_l10n: &'a ItemL10n, ) -> Self { ItemTooltip { common: widget::CommonBuilder::default(), @@ -377,6 +379,7 @@ impl<'a> ItemTooltip<'a> { item_imgs, pulse, localized_strings, + item_l10n, } } @@ -450,6 +453,7 @@ impl<'a> Widget for ItemTooltip<'a> { } = args; let i18n = &self.localized_strings; + let item_l10n = &self.item_l10n; let inventories = self.client.inventories(); let inventory = match inventories.get(self.info.viewpoint_entity) { @@ -465,7 +469,7 @@ impl<'a> Widget for ItemTooltip<'a> { let equipped_item = inventory.equipped_items_replaceable_by(item_kind).next(); - let (title, desc) = (item.name().to_string(), item.description().to_string()); + let (title, desc) = util::item_text(item, i18n, item_l10n); let item_kind = util::kind_text(item_kind, i18n).to_string(); @@ -1266,7 +1270,8 @@ impl<'a> Widget for ItemTooltip<'a> { fn default_y_dimension(&self, ui: &Ui) -> Dimension { let item = &self.item; - let desc = item.description().to_string(); + // TODO: we do double work here, does it need optimization? + let (_, desc) = util::item_text(item, self.localized_strings, self.item_l10n); let (text_w, _image_w) = self.text_image_width(260.0);