From 9e55aaa70260148e9dad956a98dd50dca98b17c9 Mon Sep 17 00:00:00 2001 From: coffee-compiler <22056268-coffee-compiler@users.noreply.gitlab.com> Date: Sat, 17 Aug 2024 18:12:55 +0000 Subject: [PATCH] Localize command descriptions & help summaries --- CHANGELOG.md | 2 +- Cargo.lock | 1 + assets/voxygen/i18n/en/command.ftl | 113 ++++++++++++ common/src/cmd.rs | 281 ++++++++++++++++------------- server/src/cmd.rs | 141 ++++++--------- voxygen/Cargo.toml | 1 + voxygen/src/bin/cmd_doc_gen.rs | 13 +- voxygen/src/cmd.rs | 40 ++-- 8 files changed, 355 insertions(+), 237 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d701bddc49..8b78b76b0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Craftable orichalcum helmet - Protocol to query game server information (player count, version, etc.) and make ping tests. - Unlockable recipes -- Localization support for prompt dialogs, diary sections, trade and group invitations. +- Localization support for prompt dialogs, diary sections, trade and group invitations, command descriptions. - Added Freezing Potion - Clear command to delete chat messages. diff --git a/Cargo.lock b/Cargo.lock index 3bd11b544d..368039b98f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7503,6 +7503,7 @@ dependencies = [ "veloren-common-base", "veloren-common-ecs", "veloren-common-frontend", + "veloren-common-i18n", "veloren-common-net", "veloren-common-state", "veloren-common-systems", diff --git a/assets/voxygen/i18n/en/command.ftl b/assets/voxygen/i18n/en/command.ftl index 3881a5ad8e..a9cb81d81f 100644 --- a/assets/voxygen/i18n/en/command.ftl +++ b/assets/voxygen/i18n/en/command.ftl @@ -1,3 +1,116 @@ +# Descriptions and Help + +command-help-template = { $usage } { $description } +command-help-additional-shortcuts = Additionally, you can use the following shortcuts: + +## Server Commands + +command-adminify-desc = Temporarily gives a player a restricted admin role or removes the current one (if not given) +command-airship-desc = Spawns an airship +command-alias-desc = Change your alias +command-area_add-desc = Adds a new build area +command-area_list-desc = List all build areas +command-area_remove-desc = Removes specified build area +command-aura-desc = Create an aura +command-body-desc = Change your body to different species +command-buff-desc = Cast a buff on player +command-build-desc = Toggles build mode on and off +command-ban-desc = Ban a player with a given username, for a given duration (if provided). Pass true for overwrite to alter an existing ban. +command-battlemode-desc = Set your battle mode to: + + pvp (player vs player) + + pve (player vs environment). + If called without arguments will show current battle mode. +command-battlemode_force-desc = Change your battle mode flag without any checks +command-campfire-desc = Spawns a campfire +command-clear_persisted_terrain-desc = Clears nearby persisted terrain +command-create_location-desc = Create a location at the current position +command-debug_column-desc = Prints some debug information about a column +command-debug_ways-desc = Prints some debug information about a column's ways +command-delete_location-desc = Delete a location +command-destroy_tethers-desc = Destroy all tethers connected to you +command-disconnect_all_players-desc = Disconnects all players from the server +command-dismount-desc = Dismount if you are riding, or dismount anything riding you +command-dropall-desc = Drops all your items on the ground +command-dummy-desc = Spawns a training dummy +command-explosion-desc = Explodes the ground around you +command-faction-desc = Send messages to your faction +command-give_item-desc = Give yourself some items. For an example or to auto complete use Tab. +command-goto-desc = Teleport to a position +command-group-desc = Send messages to your group +command-group_invite-desc = Invite a player to join a group +command-group_kick-desc = Remove a player from a group +command-group_leave-desc = Leave the current group +command-group_promote-desc = Promote a player to group leader +command-health-desc = Set your current health +command-into_npc-desc = Convert yourself to an NPC. Be careful! +command-join_faction-desc = Join/leave the specified faction +command-jump-desc = Offset your current position +command-kick-desc = Kick a player with a given username +command-kill-desc = Kill yourself +command-kill_npcs-desc = Kill the NPCs +command-kit-desc = Place a set of items into your inventory. +command-lantern-desc = Change your lantern's strength and color +command-light-desc = Spawn entity with light +command-lightning-desc = Lightning strike at current position +command-location-desc = Teleport to a location +command-make_block-desc = Make a block at your location with a color +command-make_npc-desc = Spawn entity from config near you. + For an example or to auto complete use Tab. +command-make_sprite-desc = Make a sprite at your location +command-make_volume-desc = Create a volume (experimental) +command-motd-desc = View the server description +command-mount-desc = Mount an entity +command-object-desc = Spawn an object +command-permit_build-desc = Grants player a bounded box they can build in +command-players-desc = Lists players currently online +command-portal-desc = Spawns a portal +command-region-desc = Send messages to everyone in your region of the world +command-reload_chunks-desc = Reloads chunks loaded on the server +command-remove_lights-desc = Removes all lights spawned by players +command-repair_equipment-desc = Repairs all equipped items +command-reset_recipes-desc = Resets your recipe book +command-respawn-desc = Teleport to your waypoint +command-revoke_build-desc = Revokes build area permission for player +command-revoke_build_all-desc = Revokes all build area permissions for player +command-safezone-desc = Creates a safezone +command-say-desc = Send messages to everyone within shouting distance +command-scale-desc = Scale your character +command-server_physics-desc = Set/unset server-authoritative physics for an account +command-set_motd-desc = Set the server description +command-ship-desc = Spawns a ship +command-site-desc = Teleport to a site +command-skill_point-desc = Give yourself skill points for a particular skill tree +command-skill_preset-desc = Gives your character desired skills. +command-spawn-desc = Spawn a test entity +command-sudo-desc = Run command as if you were another entity +command-tell-desc = Send a message to another player +command-tether-desc = Tether another entity to yourself +command-time-desc = Set the time of day +command-time_scale-desc = Set scaling of delta time +command-tp-desc = Teleport to another entity +command-rtsim_chunk-desc = Display information about the current chunk from rtsim +command-rtsim_info-desc = Display information about an rtsim NPC +command-rtsim_npc-desc = List rtsim NPCs that fit a given query (e.g: simulated,merchant) in order of distance +command-rtsim_purge-desc = Purge rtsim data on next startup +command-rtsim_tp-desc = Teleport to an rtsim npc +command-unban-desc = Remove the ban for the given username +command-version-desc = Prints server version +command-waypoint-desc = Set your waypoint to your current position +command-weather_zone-desc = Create a weather zone +command-whitelist-desc = Adds/removes username to whitelist +command-wiring-desc = Create wiring element +command-world-desc = Send messages to everyone on the server + +## Voxygen Client Commands + +command-clear-desc = Clears all messages in chat. Affects all chat tabs. +command-experimental_shader-desc = Toggles an experimental shader. +command-help-desc = Display information about commands +command-mute-desc = Mutes chat messages from a player. +command-unmute-desc = Unmutes a player muted with the 'mute' command. + +# Results and Warning + command-no-permission = You don't have permission to use '/{ $command_name }' command-position-unavailable = Cannot get position for { $target } command-player-role-unavailable = Cannot get administrator roles for { $target } diff --git a/common/src/cmd.rs b/common/src/cmd.rs index a5b96c135a..b9c01f4a96 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -10,6 +10,7 @@ use crate::{ recipe::RecipeBookManifest, terrain, }; +use common_i18n::Content; use hashbrown::HashMap; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; @@ -24,18 +25,14 @@ use tracing::warn; pub struct ChatCommandData { /// A list of arguments useful for both tab completion and parsing pub args: Vec, - /// A one-line message that explains what the command does - pub description: &'static str, + /// The i18n content for the description of the command + pub description: Content, /// Whether the command requires administrator permissions. pub needs_role: Option, } impl ChatCommandData { - pub fn new( - args: Vec, - description: &'static str, - needs_role: Option, - ) -> Self { + pub fn new(args: Vec, description: Content, needs_role: Option) -> Self { Self { args, description, @@ -358,7 +355,6 @@ pub enum ServerChatCommand { GroupLeave, GroupPromote, Health, - Help, IntoNpc, JoinFaction, Jump, @@ -427,8 +423,7 @@ impl ServerChatCommand { match self { ServerChatCommand::Adminify => cmd( vec![PlayerName(Required), Enum("role", ROLES.clone(), Optional)], - "Temporarily gives a player a restricted admin role or removes the current one \ - (if not given)", + Content::localized("command-adminify-desc"), Some(Admin), ), ServerChatCommand::Airship => cmd( @@ -443,12 +438,12 @@ impl ServerChatCommand { ), Float("destination_degrees_ccw_of_east", 90.0, Optional), ], - "Spawns an airship", + Content::localized("command-airship-desc"), Some(Admin), ), ServerChatCommand::Alias => cmd( vec![Any("name", Required)], - "Change your alias", + Content::localized("command-alias-desc"), Some(Moderator), ), ServerChatCommand::Aura => cmd( @@ -460,7 +455,7 @@ impl ServerChatCommand { Enum("aura_kind", AuraKindVariant::all_options(), Required), Any("aura spec", Optional), ], - "Create an aura", + Content::localized("command-aura-desc"), Some(Admin), ), ServerChatCommand::Buff => cmd( @@ -470,7 +465,7 @@ impl ServerChatCommand { Float("duration", 10.0, Optional), Any("buff data spec", Optional), ], - "Cast a buff on player", + Content::localized("command-buff-desc"), Some(Admin), ), ServerChatCommand::Ban => cmd( @@ -480,8 +475,7 @@ impl ServerChatCommand { Any("ban duration", Optional), Message(Optional), ], - "Ban a player with a given username, for a given duration (if provided). Pass \ - true for overwrite to alter an existing ban..", + Content::localized("command-ban-desc"), Some(Moderator), ), #[rustfmt::skip] @@ -491,10 +485,7 @@ impl ServerChatCommand { vec!["pvp".to_owned(), "pve".to_owned()], Optional, )], - "Set your battle mode to:\n\ - * pvp (player vs player)\n\ - * pve (player vs environment).\n\ - If called without arguments will show current battle mode.", + Content::localized("command-battlemode-desc"), None, ), @@ -505,12 +496,12 @@ impl ServerChatCommand { ENTITY_CONFIGS.clone(), Required, )], - "Convert yourself to an NPC. Be careful!", + Content::localized("command-into_npc-desc"), Some(Admin), ), ServerChatCommand::Body => cmd( vec![Enum("body", ENTITIES.clone(), Required)], - "Change your body to different species", + Content::localized("command-body-desc"), Some(Admin), ), ServerChatCommand::BattleModeForce => cmd( @@ -519,10 +510,10 @@ impl ServerChatCommand { vec!["pvp".to_owned(), "pve".to_owned()], Required, )], - "Change your battle mode flag without any checks", + Content::localized("command-battlemode_force-desc"), Some(Admin), ), - ServerChatCommand::Build => cmd(vec![], "Toggles build mode on and off", None), + ServerChatCommand::Build => cmd(vec![], Content::localized("command-build-desc"), None), ServerChatCommand::AreaAdd => cmd( vec![ Any("name", Required), @@ -534,53 +525,65 @@ impl ServerChatCommand { Integer("zlo", 0, Required), Integer("zhi", 10, Required), ], - "Adds a new build area", + Content::localized("command-area_add-desc"), + Some(Admin), + ), + ServerChatCommand::AreaList => cmd( + vec![], + Content::localized("command-area_list-desc"), Some(Admin), ), - ServerChatCommand::AreaList => cmd(vec![], "List all build areas", Some(Admin)), ServerChatCommand::AreaRemove => cmd( vec![ Any("name", Required), Enum("kind", AREA_KINDS.clone(), Required), ], - "Removes specified build area", + Content::localized("command-area_remove-desc"), + Some(Admin), + ), + ServerChatCommand::Campfire => cmd( + vec![], + Content::localized("command-campfire-desc"), Some(Admin), ), - ServerChatCommand::Campfire => cmd(vec![], "Spawns a campfire", Some(Admin)), ServerChatCommand::ClearPersistedTerrain => cmd( vec![Integer("chunk_radius", 6, Required)], - "Clears nearby persisted terrain", + Content::localized("command-clear_persisted_terrain-desc"), Some(Admin), ), ServerChatCommand::DebugColumn => cmd( vec![Integer("x", 15000, Required), Integer("y", 15000, Required)], - "Prints some debug information about a column", + Content::localized("command-debug_column-desc"), Some(Admin), ), ServerChatCommand::DebugWays => cmd( vec![Integer("x", 15000, Required), Integer("y", 15000, Required)], - "Prints some debug information about a column's ways", + Content::localized("command-debug_ways-desc"), Some(Admin), ), ServerChatCommand::DisconnectAllPlayers => cmd( vec![Any("confirm", Required)], - "Disconnects all players from the server", + Content::localized("command-disconnect_all_players-desc"), Some(Admin), ), ServerChatCommand::DropAll => cmd( vec![], - "Drops all your items on the ground", + Content::localized("command-dropall-desc"), Some(Moderator), ), - ServerChatCommand::Dummy => cmd(vec![], "Spawns a training dummy", Some(Admin)), + ServerChatCommand::Dummy => cmd( + vec![], + Content::localized("command-dummy-desc"), + Some(Admin), + ), ServerChatCommand::Explosion => cmd( vec![Float("radius", 5.0, Required)], - "Explodes the ground around you", + Content::localized("command-explosion-desc"), Some(Admin), ), ServerChatCommand::Faction => cmd( vec![Message(Optional)], - "Send messages to your faction", + Content::localized("command-faction-desc"), None, ), ServerChatCommand::GiveItem => cmd( @@ -588,7 +591,7 @@ impl ServerChatCommand { AssetPath("item", "common.items.", ITEM_SPECS.clone(), Required), Integer("num", 1, Optional), ], - "Give yourself some items.\nFor an example or to auto complete use Tab.", + Content::localized("command-give_item-desc"), Some(Admin), ), ServerChatCommand::Goto => cmd( @@ -598,42 +601,45 @@ impl ServerChatCommand { Float("z", 0.0, Required), Boolean("Dismount from ship", "true".to_string(), Optional), ], - "Teleport to a position", + Content::localized("command-goto-desc"), Some(Admin), ), - ServerChatCommand::Group => { - cmd(vec![Message(Optional)], "Send messages to your group", None) - }, + ServerChatCommand::Group => cmd( + vec![Message(Optional)], + Content::localized("command-group-desc"), + None, + ), ServerChatCommand::GroupInvite => cmd( vec![PlayerName(Required)], - "Invite a player to join a group", + Content::localized("command-group_invite-desc"), None, ), ServerChatCommand::GroupKick => cmd( vec![PlayerName(Required)], - "Remove a player from a group", + Content::localized("command-group_kick-desc"), None, ), - ServerChatCommand::GroupLeave => cmd(vec![], "Leave the current group", None), + ServerChatCommand::GroupLeave => { + cmd(vec![], Content::localized("command-group_leave-desc"), None) + }, ServerChatCommand::GroupPromote => cmd( vec![PlayerName(Required)], - "Promote a player to group leader", + Content::localized("command-group_promote-desc"), None, ), ServerChatCommand::Health => cmd( vec![Integer("hp", 100, Required)], - "Set your current health", + Content::localized("command-health-desc"), Some(Admin), ), - ServerChatCommand::Help => ChatCommandData::new( - vec![Command(Optional)], - "Display information about commands", - None, + ServerChatCommand::Respawn => cmd( + vec![], + Content::localized("command-respawn-desc"), + Some(Moderator), ), - ServerChatCommand::Respawn => cmd(vec![], "Teleport to your waypoint", Some(Moderator)), - ServerChatCommand::JoinFaction => ChatCommandData::new( + ServerChatCommand::JoinFaction => cmd( vec![Any("faction", Optional)], - "Join/leave the specified faction", + Content::localized("command-join_faction-desc"), None, ), ServerChatCommand::Jump => cmd( @@ -643,23 +649,23 @@ impl ServerChatCommand { Float("z", 0.0, Required), Boolean("Dismount from ship", "true".to_string(), Optional), ], - "Offset your current position", + Content::localized("command-jump-desc"), Some(Admin), ), ServerChatCommand::Kick => cmd( vec![PlayerName(Required), Message(Optional)], - "Kick a player with a given username", + Content::localized("command-kick-desc"), Some(Moderator), ), - ServerChatCommand::Kill => cmd(vec![], "Kill yourself", None), + ServerChatCommand::Kill => cmd(vec![], Content::localized("command-kill-desc"), None), ServerChatCommand::KillNpcs => cmd( vec![Float("radius", 100.0, Optional), Flag("--also-pets")], - "Kill the NPCs", + Content::localized("command-kill_npcs-desc"), Some(Admin), ), ServerChatCommand::Kit => cmd( vec![Enum("kit_name", KITS.to_vec(), Required)], - "Place a set of items into your inventory.", + Content::localized("command-kit-desc"), Some(Admin), ), ServerChatCommand::Lantern => cmd( @@ -669,7 +675,7 @@ impl ServerChatCommand { Float("g", 1.0, Optional), Float("b", 1.0, Optional), ], - "Change your lantern's strength and color", + Content::localized("command-lantern-desc"), Some(Admin), ), ServerChatCommand::Light => cmd( @@ -682,7 +688,7 @@ impl ServerChatCommand { Float("z", 0.0, Optional), Float("strength", 5.0, Optional), ], - "Spawn entity with light", + Content::localized("command-light-desc"), Some(Admin), ), ServerChatCommand::MakeBlock => cmd( @@ -692,7 +698,7 @@ impl ServerChatCommand { Integer("g", 255, Optional), Integer("b", 255, Optional), ], - "Make a block at your location with a color", + Content::localized("command-make_block-desc"), Some(Admin), ), ServerChatCommand::MakeNpc => cmd( @@ -705,26 +711,28 @@ impl ServerChatCommand { ), Integer("num", 1, Optional), ], - "Spawn entity from config near you.\nFor an example or to auto complete use Tab.", + Content::localized("command-make_npc-desc"), Some(Admin), ), ServerChatCommand::MakeSprite => cmd( vec![Enum("sprite", SPRITE_KINDS.clone(), Required)], - "Make a sprite at your location", + Content::localized("command-make_sprite-desc"), Some(Admin), ), - ServerChatCommand::Motd => cmd(vec![], "View the server description", None), + ServerChatCommand::Motd => cmd(vec![], Content::localized("command-motd-desc"), None), ServerChatCommand::Object => cmd( vec![Enum("object", OBJECTS.clone(), Required)], - "Spawn an object", + Content::localized("command-object-desc"), Some(Admin), ), ServerChatCommand::PermitBuild => cmd( vec![Any("area_name", Required)], - "Grants player a bounded box they can build in", + Content::localized("command-permit_build-desc"), Some(Admin), ), - ServerChatCommand::Players => cmd(vec![], "Lists players currently online", None), + ServerChatCommand::Players => { + cmd(vec![], Content::localized("command-players-desc"), None) + }, ServerChatCommand::Portal => cmd( vec![ Float("x", 0., Required), @@ -733,43 +741,47 @@ impl ServerChatCommand { Boolean("requires_no_aggro", "true".to_string(), Optional), Float("buildup_time", 5., Optional), ], - "Spawns a portal", + Content::localized("command-portal-desc"), Some(Admin), ), ServerChatCommand::ReloadChunks => cmd( vec![Integer("chunk_radius", 6, Optional)], - "Reloads chunks loaded on the server", + Content::localized("command-reload_chunks-desc"), + Some(Admin), + ), + ServerChatCommand::ResetRecipes => cmd( + vec![], + Content::localized("command-reset_recipes-desc"), Some(Admin), ), - ServerChatCommand::ResetRecipes => cmd(vec![], "Resets your recipe book", Some(Admin)), ServerChatCommand::RemoveLights => cmd( vec![Float("radius", 20.0, Optional)], - "Removes all lights spawned by players", + Content::localized("command-remove_lights-desc"), Some(Admin), ), ServerChatCommand::RevokeBuild => cmd( vec![Any("area_name", Required)], - "Revokes build area permission for player", + Content::localized("command-revoke_build-desc"), Some(Admin), ), ServerChatCommand::RevokeBuildAll => cmd( vec![], - "Revokes all build area permissions for player", + Content::localized("command-revoke_build_all-desc"), Some(Admin), ), ServerChatCommand::Region => cmd( vec![Message(Optional)], - "Send messages to everyone in your region of the world", + Content::localized("command-region-desc"), None, ), ServerChatCommand::Safezone => cmd( vec![Float("range", 100.0, Optional)], - "Creates a safezone", + Content::localized("command-safezone-desc"), Some(Moderator), ), ServerChatCommand::Say => cmd( vec![Message(Optional)], - "Send messages to everyone within shouting distance", + Content::localized("command-say-desc"), None, ), ServerChatCommand::ServerPhysics => cmd( @@ -777,12 +789,12 @@ impl ServerChatCommand { PlayerName(Required), Boolean("enabled", "true".to_string(), Optional), ], - "Set/unset server-authoritative physics for an account", + Content::localized("command-server_physics-desc"), Some(Moderator), ), ServerChatCommand::SetMotd => cmd( vec![Any("locale", Optional), Message(Optional)], - "Set the server description", + Content::localized("command-set_motd-desc"), Some(Admin), ), ServerChatCommand::Ship => cmd( @@ -802,7 +814,7 @@ impl ServerChatCommand { ), Float("destination_degrees_ccw_of_east", 90.0, Optional), ], - "Spawns a ship", + Content::localized("command-ship-desc"), Some(Admin), ), // Uses Message because site names can contain spaces, @@ -812,7 +824,7 @@ impl ServerChatCommand { SiteName(Required), Boolean("Dismount from ship", "true".to_string(), Optional), ], - "Teleport to a site", + Content::localized("command-site-desc"), Some(Moderator), ), ServerChatCommand::SkillPoint => cmd( @@ -820,12 +832,12 @@ impl ServerChatCommand { Enum("skill tree", SKILL_TREES.clone(), Required), Integer("amount", 1, Optional), ], - "Give yourself skill points for a particular skill tree", + Content::localized("command-skill_point-desc"), Some(Admin), ), ServerChatCommand::SkillPreset => cmd( vec![Enum("preset_name", PRESET_LIST.to_vec(), Required)], - "Gives your character desired skills.", + Content::localized("command-skill_preset-desc"), Some(Admin), ), ServerChatCommand::Spawn => cmd( @@ -837,27 +849,27 @@ impl ServerChatCommand { Float("scale", 1.0, Optional), Boolean("tethered", "false".to_string(), Optional), ], - "Spawn a test entity", + Content::localized("command-spawn-desc"), Some(Admin), ), ServerChatCommand::Sudo => cmd( vec![EntityTarget(Required), SubCommand], - "Run command as if you were another entity", + Content::localized("command-sudo-desc"), Some(Moderator), ), ServerChatCommand::Tell => cmd( vec![PlayerName(Required), Message(Optional)], - "Send a message to another player", + Content::localized("command-tell-desc"), None, ), ServerChatCommand::Time => cmd( vec![Enum("time", TIMES.clone(), Optional)], - "Set the time of day", + Content::localized("command-time-desc"), Some(Admin), ), ServerChatCommand::TimeScale => cmd( vec![Float("time scale", 1.0, Optional)], - "Set scaling of delta time", + Content::localized("command-time_scale-desc"), Some(Admin), ), ServerChatCommand::Tp => cmd( @@ -865,7 +877,7 @@ impl ServerChatCommand { EntityTarget(Optional), Boolean("Dismount from ship", "true".to_string(), Optional), ], - "Teleport to another entity", + Content::localized("command-tp-desc"), Some(Moderator), ), ServerChatCommand::RtsimTp => cmd( @@ -873,18 +885,17 @@ impl ServerChatCommand { Integer("npc index", 0, Required), Boolean("Dismount from ship", "true".to_string(), Optional), ], - "Teleport to an rtsim npc", + Content::localized("command-rtsim_tp-desc"), Some(Admin), ), ServerChatCommand::RtsimInfo => cmd( vec![Integer("npc index", 0, Required)], - "Display information about an rtsim NPC", + Content::localized("command-rtsim_info-desc"), Some(Admin), ), ServerChatCommand::RtsimNpc => cmd( vec![Any("query", Required), Integer("max number", 20, Optional)], - "List rtsim NPCs that fit a given query (e.g: simulated,merchant) in order of \ - distance", + Content::localized("command-rtsim_npc-desc"), Some(Admin), ), ServerChatCommand::RtsimPurge => cmd( @@ -893,52 +904,60 @@ impl ServerChatCommand { true.to_string(), Required, )], - "Purge rtsim data on next startup", + Content::localized("command-rtsim_purge-desc"), Some(Admin), ), ServerChatCommand::RtsimChunk => cmd( vec![], - "Display information about the current chunk from rtsim", + Content::localized("command-rtsim_chunk-desc"), Some(Admin), ), ServerChatCommand::Unban => cmd( vec![PlayerName(Required)], - "Remove the ban for the given username", + Content::localized("command-unban-desc"), Some(Moderator), ), - ServerChatCommand::Version => cmd(vec![], "Prints server version", None), + ServerChatCommand::Version => { + cmd(vec![], Content::localized("command-version-desc"), None) + }, ServerChatCommand::Waypoint => cmd( vec![], - "Set your waypoint to your current position", + Content::localized("command-waypoint-desc"), + Some(Admin), + ), + ServerChatCommand::Wiring => cmd( + vec![], + Content::localized("command-wiring-desc"), Some(Admin), ), - ServerChatCommand::Wiring => cmd(vec![], "Create wiring element", Some(Admin)), ServerChatCommand::Whitelist => cmd( vec![Any("add/remove", Required), PlayerName(Required)], - "Adds/removes username to whitelist", + Content::localized("command-whitelist-desc"), Some(Moderator), ), ServerChatCommand::World => cmd( vec![Message(Optional)], - "Send messages to everyone on the server", + Content::localized("command-world-desc"), None, ), ServerChatCommand::MakeVolume => cmd( vec![Integer("size", 15, Optional)], - "Create a volume (experimental)", + Content::localized("command-make_volume-desc"), Some(Admin), ), - ServerChatCommand::Location => { - cmd(vec![Any("name", Required)], "Teleport to a location", None) - }, + ServerChatCommand::Location => cmd( + vec![Any("name", Required)], + Content::localized("command-location-desc"), + None, + ), ServerChatCommand::CreateLocation => cmd( vec![Any("name", Required)], - "Create a location at the current position", + Content::localized("command-create_location-desc"), Some(Moderator), ), ServerChatCommand::DeleteLocation => cmd( vec![Any("name", Required)], - "Delete a location", + Content::localized("command-delete_location-desc"), Some(Moderator), ), ServerChatCommand::WeatherZone => cmd( @@ -947,40 +966,48 @@ impl ServerChatCommand { Float("radius", 500.0, Optional), Float("time", 300.0, Optional), ], - "Create a weather zone", + Content::localized("command-weather_zone-desc"), + Some(Admin), + ), + ServerChatCommand::Lightning => cmd( + vec![], + Content::localized("command-lightning-desc"), Some(Admin), ), - ServerChatCommand::Lightning => { - cmd(vec![], "Lightning strike at current position", Some(Admin)) - }, ServerChatCommand::Scale => cmd( vec![ Float("factor", 1.0, Required), Boolean("reset_mass", true.to_string(), Optional), ], - "Scale your character", + Content::localized("command-scale-desc"), + Some(Admin), + ), + ServerChatCommand::RepairEquipment => cmd( + vec![], + Content::localized("command-repair_equipment-desc"), Some(Admin), ), - ServerChatCommand::RepairEquipment => { - cmd(vec![], "Repairs all equipped items", Some(Admin)) - }, ServerChatCommand::Tether => cmd( vec![ EntityTarget(Required), Boolean("automatic length", "true".to_string(), Optional), ], - "Tether another entity to yourself", + Content::localized("command-tether-desc"), + Some(Admin), + ), + ServerChatCommand::DestroyTethers => cmd( + vec![], + Content::localized("command-destroy_tethers-desc"), + Some(Admin), + ), + ServerChatCommand::Mount => cmd( + vec![EntityTarget(Required)], + Content::localized("command-mount-desc"), Some(Admin), ), - ServerChatCommand::DestroyTethers => { - cmd(vec![], "Destroy all tethers connected to you", Some(Admin)) - }, - ServerChatCommand::Mount => { - cmd(vec![EntityTarget(Required)], "Mount an entity", Some(Admin)) - }, ServerChatCommand::Dismount => cmd( vec![EntityTarget(Required)], - "Dismount if you are riding, or dismount anything riding you", + Content::localized("command-dismount-desc"), Some(Admin), ), } @@ -1019,7 +1046,6 @@ impl ServerChatCommand { ServerChatCommand::GroupLeave => "group_leave", ServerChatCommand::GroupPromote => "group_promote", ServerChatCommand::Health => "health", - ServerChatCommand::Help => "help", ServerChatCommand::IntoNpc => "into_npc", ServerChatCommand::JoinFaction => "join_faction", ServerChatCommand::Jump => "jump", @@ -1102,13 +1128,18 @@ impl ServerChatCommand { pub fn iter() -> impl Iterator + Clone { ::iter() } /// A message that explains what the command does - pub fn help_string(&self) -> String { + pub fn help_content(&self) -> Content { let data = self.data(); + let usage = std::iter::once(format!("/{}", self.keyword())) .chain(data.args.iter().map(|arg| arg.usage_string())) .collect::>() .join(" "); - format!("{}: {}", usage, data.description) + + Content::localized_with_args("command-help-template", [ + ("usage", Content::Plain(usage)), + ("description", data.description), + ]) } /// Produce an iterator that first goes over all the short keywords diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 34b78de8fc..d15b1a3a39 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -166,7 +166,6 @@ fn do_command( ServerChatCommand::GroupLeave => handle_group_leave, ServerChatCommand::GroupPromote => handle_group_promote, ServerChatCommand::Health => handle_health, - ServerChatCommand::Help => handle_help, ServerChatCommand::IntoNpc => handle_into_npc, ServerChatCommand::JoinFaction => handle_join_faction, ServerChatCommand::Jump => handle_jump, @@ -604,7 +603,7 @@ fn handle_give_item( )])) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -638,7 +637,7 @@ fn handle_make_block( )) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -662,7 +661,7 @@ fn handle_into_npc( } let Some(entity_config) = parse_cmd_args!(args, String) else { - return Err(Content::Plain(action.help_string())); + return Err(action.help_content()); }; let config = match EntityConfig::load(&entity_config) { @@ -711,7 +710,7 @@ fn handle_make_npc( ) -> CmdResult<()> { let (entity_config, number) = parse_cmd_args!(args, String, i8); - let entity_config = entity_config.ok_or_else(|| action.help_string())?; + let entity_config = entity_config.ok_or_else(|| action.help_content())?; let number = match number { // Number of entities must be larger than 1 Some(i8::MIN..=0) => { @@ -815,7 +814,7 @@ fn handle_make_sprite( )])) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -903,7 +902,7 @@ fn handle_set_motd( unreachable!("edit always returns Some") }) }, - _ => Err(Content::Plain(action.help_string())), + _ => Err(action.help_content()), } } @@ -922,7 +921,7 @@ fn handle_jump( current_pos.0 += Vec3::new(x, y, z) }) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -941,7 +940,7 @@ fn handle_goto( current_pos.0 = Vec3::new(x, y, z) }) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -989,7 +988,7 @@ fn handle_site( current_pos.0 = site_pos }) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -1366,7 +1365,7 @@ fn handle_alias( } Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -1383,7 +1382,7 @@ fn handle_tp( } else if client != target { client } else { - return Err(Content::Plain(action.help_string())); + return Err(action.help_content()); }; let player_pos = position(server, player, "player")?; server @@ -1415,7 +1414,7 @@ fn handle_rtsim_tp( .ok_or_else(|| format!("No NPC has the id {id}"))? .wpos } else { - return Err(Content::Plain(action.help_string())); + return Err(action.help_content()); }; server .state @@ -1479,7 +1478,7 @@ fn handle_rtsim_info( Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -1551,7 +1550,7 @@ fn handle_rtsim_npc( Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -1588,7 +1587,7 @@ fn handle_rtsim_purge( ); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -1780,7 +1779,7 @@ fn handle_spawn( ); Ok(()) }, - _ => Err(Content::Plain(action.help_string())), + _ => Err(action.help_content()), } } @@ -2045,7 +2044,7 @@ fn handle_clear_persisted_terrain( action: &ServerChatCommand, ) -> CmdResult<()> { let Some(radius) = parse_cmd_args!(args, i32) else { - return Err(Content::Plain(action.help_string())); + return Err(action.help_content()); }; // Clamp the radius to prevent accidentally passing too large radiuses let radius = radius.clamp(0, 64); @@ -2148,7 +2147,7 @@ fn handle_permit_build( ); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -2189,7 +2188,7 @@ fn handle_revoke_build( Err(Content::localized("command-no-buid-perms")) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -2279,7 +2278,7 @@ fn handle_spawn_portal( ); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -2364,7 +2363,7 @@ fn handle_area_add( server.notify_client(client, msg); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -2433,48 +2432,10 @@ fn handle_area_remove( ); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } -fn handle_help( - server: &mut Server, - client: EcsEntity, - _target: EcsEntity, - args: Vec, - _action: &ServerChatCommand, -) -> CmdResult<()> { - if let Some(cmd) = parse_cmd_args!(args, ServerChatCommand) { - server.notify_client( - client, - ServerGeneral::server_msg(ChatType::CommandInfo, cmd.help_string()), - ) - } else { - let mut message = String::new(); - let entity_role = server.entity_admin_role(client); - - // Iterate through all commands you have permission to use. - ServerChatCommand::iter() - .filter(|cmd| cmd.needs_role() <= entity_role) - .for_each(|cmd| { - message += &cmd.help_string(); - message += "\n"; - }); - message += "Additionally, you can use the following shortcuts:"; - ServerChatCommand::iter() - .filter_map(|cmd| cmd.short_keyword().map(|k| (k, cmd))) - .for_each(|(k, cmd)| { - message += &format!(" /{} => /{}", k, cmd.keyword()); - }); - - server.notify_client( - client, - ServerGeneral::server_msg(ChatType::CommandInfo, message), - ) - } - Ok(()) -} - fn parse_alignment(owner: Uid, alignment: &str) -> CmdResult { match alignment { "wild" => Ok(Alignment::Wild), @@ -2632,7 +2593,7 @@ fn handle_kit( ServerGeneral::server_msg(ChatType::CommandInfo, format!("Gave kit: {}", kit_name)), ); }; - let name = parse_cmd_args!(args, String).ok_or_else(|| action.help_string())?; + let name = parse_cmd_args!(args, String).ok_or_else(|| action.help_content())?; match name.as_str() { "all" => { @@ -2910,7 +2871,7 @@ fn handle_lantern( Err(Content::localized("command-lantern-unequiped")) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -3090,7 +3051,7 @@ fn handle_adminify( "admin" => AdminRole::Admin, "moderator" => AdminRole::Moderator, _ => { - return Err(Content::Plain(action.help_string())); + return Err(action.help_content()); }, }) } else { @@ -3182,7 +3143,7 @@ fn handle_adminify( server.state.notify_players(msg); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -3221,7 +3182,7 @@ fn handle_tell( server.notify_client(target, ServerGeneral::ChatMode(mode)); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -3317,7 +3278,7 @@ fn handle_group_invite( ); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -3338,7 +3299,7 @@ fn handle_group_kick( .emit_event_now(GroupManipEvent(target, comp::GroupManip::Kick(uid))); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -3372,7 +3333,7 @@ fn handle_group_promote( .emit_event_now(GroupManipEvent(target, comp::GroupManip::AssignLeader(uid))); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -3393,7 +3354,7 @@ fn handle_reset_recipes( server.notify_client(target, ServerGeneral::UpdateRecipes); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -3724,7 +3685,7 @@ fn handle_skill_point( Err("Entity has no stats!".into()) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -3918,7 +3879,7 @@ fn handle_sudo( Err(Content::localized("command-unknown")) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4008,10 +3969,10 @@ fn handle_whitelist( }); edit_setting_feedback(server, client, edit, || format!("{}{}", err_info, username)) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4067,7 +4028,7 @@ fn handle_kick( ); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4145,7 +4106,7 @@ fn handle_ban( } Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4161,7 +4122,7 @@ fn handle_aura( let (Some(aura_radius), aura_duration, new_entity, aura_target, Some(aura_kind_variant), spec) = parse_cmd_args!(args, f32, f32, bool, GroupTarget, AuraKindVariant, ..Vec) else { - return Err(Content::Plain(action.help_string())); + return Err(action.help_content()); }; let new_entity = new_entity.unwrap_or(false); let aura_kind = match aura_kind_variant { @@ -4377,7 +4338,7 @@ fn handle_battlemode_force( if !settings.gameplay.battle_mode.allow_choosing() { return Err(Content::localized("command-disabled-by-settings")); } - let mode = parse_cmd_args!(args, String).ok_or_else(|| action.help_string())?; + let mode = parse_cmd_args!(args, String).ok_or_else(|| action.help_content())?; let mode = match mode.as_str() { "pvp" => BattleMode::PvP, "pve" => BattleMode::PvE, @@ -4442,7 +4403,7 @@ fn handle_unban( format!("{} was already unbanned", username) }) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4474,7 +4435,7 @@ fn handle_server_physics( ); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4488,7 +4449,7 @@ fn handle_buff( let (Some(buff), strength, duration, misc_data_spec) = parse_cmd_args!(args, String, f32, f64, String) else { - return Err(Content::Plain(action.help_string())); + return Err(action.help_content()); }; let strength = strength.unwrap_or(0.01); @@ -4656,7 +4617,7 @@ fn handle_skill_preset( Err("Player has no stats!".into()) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4755,7 +4716,7 @@ fn handle_create_location( Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4782,7 +4743,7 @@ fn handle_delete_location( Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4866,7 +4827,7 @@ fn handle_weather_zone( _ => Err(Content::localized("command-weather-valid-values")), } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4910,7 +4871,7 @@ fn handle_body( } Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4946,7 +4907,7 @@ fn handle_scale( ); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -4978,7 +4939,7 @@ fn handle_repair_equipment( ); Ok(()) } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -5037,7 +4998,7 @@ fn handle_tether( Err("Tether members don't have Uids.".into()) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } @@ -5097,7 +5058,7 @@ fn handle_mount( Err("Mount and/or rider doesn't have an Uid component.".into()) } } else { - Err(Content::Plain(action.help_string())) + Err(action.help_content()) } } diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 934db0e54a..1ffeb758a2 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -56,6 +56,7 @@ common-assets = { package = "veloren-common-assets", path = "../common/assets", common-base = { package = "veloren-common-base", path = "../common/base" } common-ecs = { package = "veloren-common-ecs", path = "../common/ecs" } common-frontend = { package = "veloren-common-frontend", path = "../common/frontend" } +common-i18n = { package = "veloren-common-i18n", path = "../common/i18n" } common-net = { package = "veloren-common-net", path = "../common/net" } common-state = { package = "veloren-common-state", path = "../common/state" } common-systems = { package = "veloren-common-systems", path = "../common/systems" } diff --git a/voxygen/src/bin/cmd_doc_gen.rs b/voxygen/src/bin/cmd_doc_gen.rs index 1d7f8e0e60..bc680b7b9e 100644 --- a/voxygen/src/bin/cmd_doc_gen.rs +++ b/voxygen/src/bin/cmd_doc_gen.rs @@ -1,9 +1,14 @@ use common::cmd::{ChatCommandData, ServerChatCommand}; +use i18n::{LocalizationGuard, LocalizationHandle}; use veloren_voxygen::cmd::ClientChatCommand; /// This binary generates the markdown tables used for the `players/commands.md` /// page in the Veloren Book. It can be run with `cargo cmd-doc-gen`. fn main() { + let i18n = LocalizationHandle::load(i18n::REFERENCE_LANG) + .unwrap() + .read(); + let table_header = "|Command|Description|Requires|Arguments|"; let table_seperator = "|-|-|-|-|"; @@ -11,7 +16,7 @@ fn main() { println!("{table_seperator}"); for cmd in ServerChatCommand::iter() { - println!("{}", format_row(cmd.keyword(), &cmd.data())) + println!("{}", format_row(cmd.keyword(), &cmd.data(), &i18n)) } println!(); @@ -20,11 +25,11 @@ fn main() { println!("{table_seperator}"); for cmd in ClientChatCommand::iter() { - println!("{}", format_row(cmd.keyword(), &cmd.data())) + println!("{}", format_row(cmd.keyword(), &cmd.data(), &i18n)) } } -fn format_row(keyword: &str, data: &ChatCommandData) -> String { +fn format_row(keyword: &str, data: &ChatCommandData, i18n: &LocalizationGuard) -> String { let args = data .args .iter() @@ -35,7 +40,7 @@ fn format_row(keyword: &str, data: &ChatCommandData) -> String { format!( "|/{}|{}|{}|{}|", keyword, - data.description, + i18n.get_content(&data.description), data.needs_role .map_or("".to_string(), |role| format!("{:?}", role)), if !args.is_empty() { diff --git a/voxygen/src/cmd.rs b/voxygen/src/cmd.rs index e093762fcf..3b577983f1 100644 --- a/voxygen/src/cmd.rs +++ b/voxygen/src/cmd.rs @@ -16,6 +16,7 @@ use common::{ uid::Uid, uuid::Uuid, }; +use common_i18n::Content; use common_net::sync::WorldSyncExt; use itertools::Itertools; use levenshtein::levenshtein; @@ -38,11 +39,9 @@ impl ClientChatCommand { use Requirement::*; let cmd = ChatCommandData::new; match self { - ClientChatCommand::Clear => cmd( - Vec::new(), - "Clears all messages in chat. Affects all chat tabs.", - None, - ), + ClientChatCommand::Clear => { + cmd(Vec::new(), Content::localized("command-clear-desc"), None) + }, ClientChatCommand::ExperimentalShader => cmd( vec![Enum( "Shader", @@ -51,22 +50,22 @@ impl ClientChatCommand { .collect(), Optional, )], - "Toggles an experimental shader.", + Content::localized("command-experimental_shader-desc"), None, ), ClientChatCommand::Help => cmd( vec![Command(Optional)], - "Display information about commands", + Content::localized("command-help-desc"), None, ), ClientChatCommand::Mute => cmd( vec![PlayerName(Required)], - "Mutes chat messages from a player.", + Content::localized("command-mute-desc"), None, ), ClientChatCommand::Unmute => cmd( vec![PlayerName(Required)], - "Unmutes a player muted with the 'mute' command.", + Content::localized("command-unmute-desc"), None, ), } @@ -83,13 +82,18 @@ impl ClientChatCommand { } /// A message that explains what the command does - pub fn help_string(&self) -> String { + pub fn help_content(&self) -> Content { let data = self.data(); + let usage = std::iter::once(format!("/{}", self.keyword())) .chain(data.args.iter().map(|arg| arg.usage_string())) .collect::>() .join(" "); - format!("{}: {}", usage, data.description) + + Content::localized_with_args("command-help-template", [ + ("usage", Content::Plain(usage)), + ("description", data.description), + ]) } /// Returns a format string for parsing arguments with scan_fmt @@ -377,13 +381,15 @@ fn handle_clear( fn handle_help( session_state: &mut SessionState, - _global_state: &mut GlobalState, + global_state: &mut GlobalState, args: Vec, ) -> CommandResult { + let i18n = global_state.i18n.read(); + if let Some(cmd) = parse_cmd_args!(&args, ServerChatCommand) { - Ok(Some(cmd.help_string())) + Ok(Some(i18n.get_content(&cmd.help_content()))) } else if let Some(cmd) = parse_cmd_args!(&args, ClientChatCommand) { - Ok(Some(cmd.help_string())) + Ok(Some(i18n.get_content(&cmd.help_content()))) } else { let client = &mut session_state.client.borrow_mut(); @@ -395,17 +401,17 @@ fn handle_help( .map(|admin| admin.0); ClientChatCommand::iter().for_each(|cmd| { - message += &cmd.help_string(); + message += &i18n.get_content(&cmd.help_content()); message += "\n"; }); // Iterate through all ServerChatCommands you have permission to use. ServerChatCommand::iter() .filter(|cmd| cmd.needs_role() <= entity_role) .for_each(|cmd| { - message += &cmd.help_string(); + message += &i18n.get_content(&cmd.help_content()); message += "\n"; }); - message += "Additionally, you can use the following shortcuts:"; + message += &i18n.get_msg("command-help-additional-shortcuts"); ServerChatCommand::iter() .filter(|cmd| cmd.needs_role() <= entity_role) .filter_map(|cmd| cmd.short_keyword().map(|k| (k, cmd)))