Localization system for Voxygen

- Added a localization system in voxygen
- Support English and French languages
- Added a configuration option in the interface settings to change
  the language
This commit is contained in:
Rémy PHELIPOT 2020-01-18 00:43:18 +01:00
parent ec0692249a
commit a6f9f533a5
21 changed files with 1357 additions and 420 deletions

View File

@ -30,6 +30,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added randomly selected Loading Screen background images
- Added options to disable clouds and to use cheaper water rendering
- Added client-side character saving
- Added a localization system to provide multi-language support
to voxygen
- Added French language for Voxygen
### Changed

7
Cargo.lock generated
View File

@ -760,6 +760,11 @@ dependencies = [
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "deunicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "directories"
version = "2.0.2"
@ -3251,6 +3256,7 @@ dependencies = [
"cpal 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"deunicode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dispatch 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"dot_vox 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3654,6 +3660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum daggy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9293a0da7d1bc1f30090ece4d9f9de79a07be7302ddb00e5eb1fefb6ee6409e2"
"checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4"
"checksum derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "942ca430eef7a3806595a6737bc388bf51adb888d3fc0dd1b50f1c170167ee3a"
"checksum deunicode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a0f5bbdedde60605d0719b998e282af68e2b1c50203110211fe4abe857560"
"checksum directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c"
"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"

337
assets/voxygen/i18n/en.ron Normal file
View File

@ -0,0 +1,337 @@
/// Translation document instructions
///
/// In order to keep localization documents readible please follow the following
/// rules:
/// - separate the string map sections using a commentary describing the purpose
/// of the next section
/// - prepend multi-line strings with a commentary
/// - append one blank lines after a multi-line strings and two after sections
///
/// To add a new language in Veloren, just write an additional `.ron` file in
/// `assets/voxygen/i18n` and that's it!
/// Localization for "global" English
VoxygenLocalization(
metadata: (
language_name: "English",
language_identifier: "en",
),
convert_utf8_to_ascii: false,
string_map: {
/// Start Common section
// Texts used in multiple locations with the same formatting
"common.username": "username",
"common.singleplayer": "Singleplayer",
"common.multiplayer": "Multiplayer",
"common.servers": "Servers",
"common.quit": "Quit",
"common.settings": "Settings",
"common.languages": "Languages",
"common.interface": "Interface",
"common.gameplay": "Gameplay",
"common.controls": "Controls",
"common.video": "Video",
"common.sound": "Sound",
"common.resume": "Resume",
"common.characters": "Characters",
"common.close": "Close",
"common.yes": "Yes",
"common.no": "No",
"common.back": "Back",
"common.create": "Create",
"common.okay": "Okay",
"common.disclaimer": "Disclaimer",
"common.cancel": "Cancel",
"common.none": "None",
// Message when connection to the server is lost
"common.connection_lost": r#"Connection lost!
Did the server restart?
Is the client up to date?"#,
"common.races.orc": "Orc",
"common.races.human": "Human",
"common.races.dwarf": "Dwarf",
"common.races.elf": "Elf",
"common.races.undead": "Undead",
"common.races.danari": "Danari",
"common.weapons.axe": "Axe",
"common.weapons.sword": "Sword",
"common.weapons.staff": "Staff",
"common.weapons.bow": "Bow",
"common.weapons.hammer": "Hammer",
/// End Common section
/// Start Main screen section
"main.connecting": "Connecting",
"main.creating_world": "Creating world",
// Welcome notice that appears the first time Veloren is started
"main.notice": r#"Welcome to the alpha version of Veloren!
Before you dive into the fun, please keep a few things in mind:
- This is a very early alpha. Expect bugs, extremely unfinished gameplay, unpolished mechanics, and missing features.
- If you have constructive feedback or bug reports, you can contact us via Reddit, GitLab, or our community Discord server.
- Veloren is licensed under the GPL 3 open-source licence. That means you're free to play, modify, and redistribute the game however you wish (provided derived work is also under GPL 3).
- Veloren is a non-profit community project, and everybody working on it is a volunteer.
If you like what you see, you're welcome to join the development or art teams!
- 'Voxel RPG' is a genre in its own right. First-person shooters used to be called Doom clones.
Like them, we're trying to build a niche. This game is not a clone, and its development will diverge from existing games in the future.
Thanks for taking the time to read this notice, we hope you enjoy the game!
~ The Veloren Devs"#,
// Login process description
"main.login_process": r#"Information on the Login Process:
The name you put in will be your character name ingame.
Character names and appearances will be saved on your computer.
Levels/Items are not saved yet."#,
/// End Main screen section
/// Start HUD Section
"hud.do_not_show_on_startup": "Don't show this on Startup",
"hud.show_tips": "Show Tips",
"hud.quests": "Quests",
"hud.you_died": "You Died",
"hud.press_key_to_show_keybindings_fmt": "Press {key} to show keybindings",
"hud.press_key_to_show_debug_info_fmt": "Press {key} to show debug info",
"hud.press_key_to_toggle_keybindings_fmt": "Press {key} to toogle keybindings",
"hud.press_key_to_toggle_debug_info_fmt": "Press {key} to toogle debug info",
// Respawn message
"hud.press_key_to_respawn": r#"Press {key} to respawn at your Waypoint.
Press Enter, type in /waypoint and confirm to set it here."#,
// Welcome message
"hud.welcome": r#"Welcome to the Veloren Alpha!,
Some tips before you start:
MOST IMPORTANTLY: To set your respawn point type /waypoint into the chat.
This can also be done when you are already dead!
Press F1 to see the available key commands.
Type /help into the chat to see chat commands
There are chests and other objects randomly spawning in the World!
Right-Click to collect them.
To actually use whatever you loot from those chests open your inventory with 'B'.
Double click the items in your bag to use or equip them.
Throw them away by clicking them once and clicking outside of the bag
Nights can get pretty dark in Veloren.
Light your lantern by typing /lantern into the chat
Want to free your cursor to close this window? Press TAB!
Enjoy your stay in the World of Veloren."#,
"hud.settings.general": "General",
"hud.settings.help_window": "Help Window",
"hud.settings.debug_info": "Debug Info",
"hud.settings.tips_on_startup": "Tips-On-Startup",
"hud.settings.ui_scale": "UI-Scale",
"hud.settings.relative_scaling": "Relative Scaling",
"hud.settings.custom_scaling": "Custom Scaling",
"hud.settings.crosshair": "Crosshair",
"hud.settings.transparency": "Transparency",
"hud.settings.hotbar": "Hotbar",
"hud.settings.toggle_bar_experience": "Toggle Experience Bar",
"hud.settings.toggle_shortcuts": "Toggle Shortcuts",
"hud.settings.toggle_bar_experience": "Toggle Shortcuts",
"hud.settings.scrolling_combat_text": "Scrolling Combat Text",
"hud.settings.single_damage_number": "Single Damage Numbers",
"hud.settings.cumulated_damage": "Cumulated Damage",
"hud.settings.incoming_damage": "Incoming Damage",
"hud.settings.cumulated_incoming_damage": "Cumulated Incoming Damage",
"hud.settings.energybar_numbers": "Energybar Numbers",
"hud.settings.values": "Values",
"hud.settings.percentages": "Percentages",
"hud.settings.chat": "Chat",
"hud.settings.background_transparency": "Background Transparency",
"hud.settings.pan_sensitivity": "Pan Sensitivity",
"hud.settings.zoom_sensitivity": "Zoom Sensitivity",
"hud.settings.invert_scroll_zoom": "Invert Scroll Zoom",
"hud.settings.invert_mouse_y_axis": "Invert Mouse Y Axis",
"hud.settings.view_distance": "View Distance",
"hud.settings.maximum_fps": "Maximum FPS",
"hud.settings.fov": "Field of View (deg)",
"hud.settings.antialiasing_mode": "AntiAliasing Mode",
"hud.settings.cloud_rendering_mode": "Cloud Rendering Mode",
"hud.settings.fluid_rendering_mode": "Fluid Rendering Mode",
"hud.settings.fluid_rendering_mode.cheap": "Cheap",
"hud.settings.fluid_rendering_mode.shiny": "Shiny",
"hud.settings.cloud_rendering_mode.regular": "Regular",
"hud.settings.music_volume": "Music Volume",
"hud.settings.sound_effect_volume": "Sound Effects Volume",
"hud.settings.audio_device": "Audio Device",
// Control list
"hud.settings.control_names": r#"Free Cursor
Toggle Help Window
Toggle Interface
Toggle FPS and Debug Info
Take Screenshot
Toggle Nametags
Toggle Fullscreen
Move Forward
Move Left
Move Right
Move Backwards
Jump
Glider
Dodge
Roll
Climb
Climb down
Auto Walk
Sheathe/Draw Weapons
Put on/Remove Helmet
Sit
Mount
Interact
Basic Attack
Secondary Attack/Block/Aim
Skillbar Slot 1
Skillbar Slot 2
Skillbar Slot 3
Skillbar Slot 4
Skillbar Slot 5
Skillbar Slot 6
Skillbar Slot 7
Skillbar Slot 8
Skillbar Slot 9
Skillbar Slot 10
Pause Menu
Settings
Social
Map
Spellbook
Character
Questlog
Bag
Send Chat Message
Scroll Chat
Chat commands:
/alias [Name] - Change your Chat Name
/tp [Name] - Teleports you to another player
/jump <dx> <dy> <dz> - Offset your position
/goto <x> <y> <z> - Teleport to a position
/kill - Kill yourself
/pig - Spawn pig NPC
/wolf - Spawn wolf NPC
/help - Display chat commands"#,
"hud.social": "Social",
"hud.social.online": "Online",
"hud.social.friends": "Friends",
"hud.social.not_yet_available": "Not yet available",
"hud.social.Faction": "Faction",
"hud.social.play_online_fmt": "{nb_player} player(s) online",
"hud.spell": "Spell",
/// End HUD section
/// Start chracter selection section
"char_selection.delete_permanently": "Permanently delete this Character?",
"char_selection.change_server": "Change Server",
"char_selection.enter_world": "Enter World",
"char_selection.logout": "Logout",
"char_selection.create_new_charater": "Create New Character",
"char_selection.character_creation": "Character Creation",
"char_selection.human_default": "Human Default",
"char_selection.level_fmt": "Level {level_nb}",
"char_selection.uncanny_valley": "Uncanny Valley",
"char_selection.plains_of_uncertainty": "Plains of Uncertainty",
"char_selection.beard": "Beard",
"char_selection.hair_style": "Beard",
"char_selection.hair_color": "Beard",
"char_selection.chest_color": "Chest color",
"char_selection.eye_color": "Eye color",
"char_selection.skin": "Skin",
"char_selection.eyebrows": "Eyebrows",
"char_selection.accessories": "Accessories",
/// End chracter selection section
/// Start character window section
"character_window.character_name": "Character Name",
// Charater stats
"character_window.character_stats": r#"Stamina
Strength
Dexterity
Intelligence"#,
/// Start character window section
/// Start Escape Menu Section
"esc_menu.logout": "Logout",
"esc_menu.quit_game": "Quit Game",
/// End Escape Menu Section
}
)

View File

@ -0,0 +1,303 @@
/// Localization for French (France locale)
VoxygenLocalization(
metadata: (
language_name: "Français",
language_identifier: "fr_FR",
),
convert_utf8_to_ascii: true,
string_map: {
// Common texts used in multiple locations
"common.username": "pseudo",
"common.singleplayer": "Solo",
"common.multiplayer": "Multijoueur",
"common.servers": "Serveurs",
"common.quit": "Quitter",
"common.settings": "Paramètres",
"common.languages": "Langues",
"common.interface": "Interface",
"common.gameplay": "Gameplay",
"common.controls": "Controles",
"common.video": "Video",
"common.sound": "Audio",
"common.resume": "Reprendre",
"common.characters": "Personages",
"common.close": "Fermer",
"common.create": "Créer",
"common.back": "Retour",
"common.yes": "Oui",
"common.no": "Non",
"common.okay": "Compris",
"common.cancel": "Annuler",
"common.none": "Aucun",
"common.races.orc": "Orc",
"common.races.human": "Humain",
"common.races.dwarf": "Nain",
"common.races.elf": "Elfe",
"common.races.undead": "Mort vivant",
"common.races.danari": "Danari",
"common.weapons.axe": "Hâche",
"common.weapons.sword": "Épée",
"common.weapons.staff": "Bâton",
"common.weapons.bow": "Arc",
"common.weapons.hammer": "Marteau",
// Message when connection to the server is lost
"common.connection_lost": r#"Connexion perdue!
Le serveur a-il redémarré?
Le client est-il à jour?"#,
// Main screen texts
"main.connecting": "Connexion",
"main.creating_world": "Création du monde",
"main.notice": r#"Bienvenue dans la version alpha de Veloren!
Avant de commencer à vous amuser, merci de garder les choses suivantes en tête:
- Il s'agit d'une version alpha très jeune. Attendez-vous à des bugs, un gameplay non terminé, des mécaniques non peaufinées et des fonctionalités manquantes.
- Si vous avez des retours constructifs ou avez detecté un bug, vous pouvez nous contacter via Reddit, GitLab ou notre communauté Discord.
- Veloren est un logiciel open-source sour licence GPL3. Cela signifit que vous êtes libre de jouer, modfier et redistribuer le jeu comme il vous semble (licence contaminante sous GPL 3 pour toute modification)
- Veloren est un projet communautaire à but non-lucratif développé par des bénévolles.
Si vous apprecier ce jeu, vous êtes les bienvenues pour rejoindre les équipes de développement ou d'artistes!
- Le genre 'Voxel RPG' est un genre à part entiere. Les FPS étaient appelé Doom-like. De la même façon, nous essayons de construire un genre à part entière. Ce jeu n'est pas un clone et ses mechaniques changeront au cours du developpement.
Merci d'avoir pris le temps de lire cette notice, nous esperons que vous apprecierez le jeu!
~ L'équipe de Veloren"#,
"main.login_process": r#"Information sur la procédure de connexion:
L'identifiant de connexion sera votre pseudo en jeu.
Le nom et l'apparence que vous choisirez seront
sauvegardés sur votre ordinateur.
Les niveaux/objets ne sont pas sauvegardés."#,
// HUD texts
"hud.you_died": "Vous êtes mort",
"hud.do_not_show_on_startup": "Ne pas afficher au démarage",
"hud.press_key_to_show_keybindings_fmt": "Appuyer sur {key} pour afficher les contrôles",
"hud.settings.toggle_shortcuts": "Activer les raccourcis",
"hud.show_tips": "Voir les astuces",
"hud.quests": "Quêtes",
"hud.spell": "Sorts",
"hud.press_key_to_toggle_debug_info_fmt": "Appuyer sur {key} pour activer les informations de debogage",
"hud.press_key_to_show_debug_info_fmt": "Appuyer sur {key} pour afficher les informations de debogage",
"hud.press_key_to_toggle_keybindings_fmt": "Appuyer sur {key} pour afficher les contrôles",
/// Respawn message
"hud.press_key_to_respawn": r#"Appuyer sur {key} pour revivre à votre point de repère.
Appuyez sur Entrée, taper /waypoint pour configurer le point de repère à l'endroit actuel"#,
/// Welcome message
"hud.welcome": r#"Bienvenue dans la version Alpha de Veloren!
Quelques astuces avant de démarrer:
POINT LE PLUS IMPORTANT: Pour configurer votre point de resurection tapez
/waypoint dans le chat. (y-compris si vous être déjà mort!)
Appuyer sur F1 pour voir les commandes disponibles.
Tapez /help dans le chat pour voir les commandes du chat.
Des coffres et autres objets sont disposés aléatoirement dans le monde!
Utilisez le click droit pour le collecter.
Pour utilisez ce que vous ramassez des coffres, ouvrez votre inventaire avec 'B'.
Double cliquez sur les éléments de votre sac pour les utiliser ou les équiper.
Jettez-les en cliquant sur un element puis en cliquant en dehors du sac.
Les nuits peuvent être très sombre à Veloren.
Allumez votre lanterne en tapant /lantern dans le chat.
Vous souhaitez libérer votre souris pour fermer cette fenêtre? Tapez sur TAB!
Profitez de votre séjour dans le monde de Veloren."#,
"hud.settings.general": "Général",
"hud.settings.help_window": "Fenêtre d'aide",
"hud.settings.debug_info": "Information de débogage",
"hud.settings.tips_on_startup": "Astuces au démarrage",
"hud.settings.ui_scale": "Echelle de l'interface",
"hud.settings.relative_scaling": "Echelle relative",
"hud.settings.custom_scaling": "Echelle personalisée",
"hud.settings.crosshair": "Réticule",
"hud.settings.transparency": "Transparence",
"hud.settings.hotbar": "Barre active",
"hud.settings.toggle_bar_experience": "Activer la barre d'experience",
"hud.settings.scrolling_combat_text": "Texte de combat défillant",
"hud.settings.single_damage_number": "Dégat adversaire (par dégat)",
"hud.settings.cumulated_damage": "Dégat adversaire (cumulé)",
"hud.settings.incoming_damage": "Dégat personnage (par dégat)",
"hud.settings.cumulated_incoming_damage": "Dégat personnage (cumulé)",
"hud.settings.energybar_numbers": "Nombre des barres d'energie",
"hud.settings.none": "Aucuns",
"hud.settings.values": "Valeurs",
"hud.settings.percentages": "Pourcentages",
"hud.settings.chat": "Tchat",
"hud.settings.background_transparency": "Transparence du fond",
"hud.settings.pan_sensitivity": "Sensibilité de la souris",
"hud.settings.zoom_sensitivity": "Sensibilité du zoom",
"hud.settings.invert_scroll_zoom": "Inverser la molette",
"hud.settings.invert_mouse_y_axis": "Inverser l'axe Y",
"hud.settings.view_distance": "Distance d'affichage",
"hud.settings.maximum_fps": "Limite FPS",
"hud.settings.fov": "Champs de vision (FOV)",
"hud.settings.antialiasing_mode": "Mode anticrénelage",
"hud.settings.cloud_rendering_mode": "Rendu des nuages",
"hud.settings.fluid_rendering_mode": "Rendu des fluides",
"hud.settings.fluid_rendering_mode.cheap": "Rapide",
"hud.settings.fluid_rendering_mode.shiny": "Qualité",
"hud.settings.cloud_rendering_mode.regular": "Normal",
"hud.settings.music_volume": "Volume de la musique",
"hud.settings.sound_effect_volume": "Volume des effets",
"hud.settings.audio_device": "Périphérique audio",
"hud.settings.control_names": r#"Libérer le curseur
Activer la fenêtre d'aide
Activer l'interface
Activer la fenêtre de débogage
Prendre une capture d'écran
Activer les noms de personnage
Activer le mode plein écran
Avancer
Aller à gauche
Aller à droite
Aller en arriere
Sauter
Planer
Esquiver
Roullade
Escalader
Desescalader
Marche automatique
Ranger/Sortir les armes
Mettre/Enlever le casque
S'assoir
Monter
Intéragir
Attaque basique
Attaque secondaire/Bloquer/Viser
Barre de compétence 1
Barre de compétence 2
Barre de compétence 3
Barre de compétence 4
Barre de compétence 5
Barre de compétence 6
Barre de compétence 7
Barre de compétence 8
Barre de compétence 9
Barre de compétence 10
Pause
Paramètres
Social
Carte
Livre de sorts
Personnages
Livre de quêtes
Sac à dos
Envoyer un message
Défiler dans le tchat
Commandes du tchat:
/alias [Nom] - Changer de nom
/tp [Nom] - Se téléporter vers un autre joueur
/jump <dx> <dy> <dz> - Sauter à partir de votre position
/goto <x> <y> <z> - Se téléporter à une position
/kill - Se suicider
/pig - Faire aparaitre un cochon PNJ
/wolf - Faire aparaitre un loup PNJ
/help - Afficher les commandes de tchat"#,
"hud.social": "Social",
"hud.social.friends": "Amis",
"hud.social.Faction": "Faction",
"hud.social.online": "Jeu en ligne",
"hud.social.not_yet_available": "Pas encore disponible",
"hud.social.play_online_fmt": "{nb_player} joueurs en ligne",
"char_selection.change_server": "Changer de serveur",
"char_selection.delete_permanently": "Supprimer",
"esc_menu.quit_game": "Quitter le jeu",
"esc_menu.logout": "Deconnexion",
"char_selection.beard": "Barbe",
"char_selection.hair_style": "Coupe de cheveux",
"char_selection.hair_color": "Couleur de cheveux",
"char_selection.skin": "Couleur de peau",
"char_selection.eyebrows": "Sourcils",
"char_selection.eye_color": "Couleur des yeux",
"char_selection.accessories": "Accessoires",
"char_selection.level_fmt": "Niveau {level_nb}",
"char_selection.create_new_charater": "Créer un nouveau personnage",
"char_selection.human_default": "Humain par défault",
"char_selection.uncanny_valley": "Vallée dérangeante",
"char_selection.chest_color": "Couleur du buste",
"char_selection.logout": "Se déconnecter",
"char_selection.enter_world": "Entrer dans le monde",
"char_selection.plains_of_uncertainty": "Plaines de l'incertitude",
"char_selection.character_creation": "Création de personnages",
"character_window.character_name": "Personnage",
"character_window.character_stats": r#"Endurance
Force
Dexterité
Intelligence"#,
}
)

View File

@ -63,6 +63,7 @@ hashbrown = { version = "0.6.2", features = ["serde", "nightly"] }
chrono = "0.4.9"
rust-argon2 = "0.5"
bincode = "1.2"
deunicode = "1.0"
[target.'cfg(target_os = "macos")'.dependencies]
dispatch = "0.1.4"

View File

@ -1,4 +1,5 @@
use super::{img_ids::Imgs, Fonts, Show, TEXT_COLOR, XP_COLOR};
use crate::i18n::VoxygenLocalization;
use common::comp::Stats;
use conrod_core::{
color,
@ -72,18 +73,26 @@ pub struct CharacterWindow<'a> {
imgs: &'a Imgs,
fonts: &'a Fonts,
stats: &'a Stats,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> CharacterWindow<'a> {
pub fn new(_show: &'a Show, stats: &'a Stats, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
pub fn new(
_show: &'a Show,
stats: &'a Stats,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
) -> Self {
Self {
_show,
imgs,
fonts,
stats,
localized_strings,
common: widget::CommonBuilder::default(),
}
}
@ -144,12 +153,16 @@ impl<'a> Widget for CharacterWindow<'a> {
// Title
// TODO: Use an actual character name.
Text::new("Character Name")
.mid_top_with_margin_on(state.charwindow_frame, 6.0)
.font_id(self.fonts.cyri)
.font_size(14)
.color(TEXT_COLOR)
.set(state.charwindow_title, ui);
Text::new(
&self
.localized_strings
.get("character_window.character_name"),
)
.mid_top_with_margin_on(state.charwindow_frame, 6.0)
.font_id(self.fonts.cyri)
.font_size(14)
.color(TEXT_COLOR)
.set(state.charwindow_title, ui);
// Content Alignment
Rectangle::fill_with([95.0 * 4.0, 108.0 * 4.0], color::TRANSPARENT)
@ -381,13 +394,9 @@ impl<'a> Widget for CharacterWindow<'a> {
// Stats
Text::new(
"Stamina\n\
\n\
Strength\n\
\n\
Dexterity\n\
\n\
Intelligence",
&self
.localized_strings
.get("character_window.character_stats"),
)
.top_left_with_margins_on(state.charwindow_rectangle, 140.0, 5.0)
.font_id(self.fonts.cyri)

View File

@ -1,10 +1,10 @@
use super::{img_ids::Imgs, settings_window::SettingsTab, Fonts, TEXT_COLOR};
use crate::i18n::VoxygenLocalization;
use conrod_core::{
widget::{self, Button, Image},
widget_ids, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
};
use super::{img_ids::Imgs, settings_window::SettingsTab, Fonts, TEXT_COLOR};
widget_ids! {
struct Ids {
esc_bg,
@ -22,15 +22,22 @@ widget_ids! {
pub struct EscMenu<'a> {
imgs: &'a Imgs,
_fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> EscMenu<'a> {
pub fn new(imgs: &'a Imgs, _fonts: &'a Fonts) -> Self {
pub fn new(
imgs: &'a Imgs,
_fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
) -> Self {
Self {
imgs,
_fonts,
localized_strings,
common: widget::CommonBuilder::default(),
}
}
@ -82,7 +89,7 @@ impl<'a> Widget for EscMenu<'a> {
.w_h(210.0, 50.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Resume")
.label(&self.localized_strings.get("common.resume"))
.label_y(conrod_core::position::Relative::Scalar(3.0))
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -98,7 +105,7 @@ impl<'a> Widget for EscMenu<'a> {
.w_h(210.0, 50.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Settings")
.label(&self.localized_strings.get("common.settings"))
.label_y(conrod_core::position::Relative::Scalar(3.0))
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -113,7 +120,7 @@ impl<'a> Widget for EscMenu<'a> {
.w_h(210.0, 50.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Controls")
.label(&self.localized_strings.get("common.controls"))
.label_y(conrod_core::position::Relative::Scalar(3.0))
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -128,7 +135,7 @@ impl<'a> Widget for EscMenu<'a> {
.w_h(210.0, 50.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Characters")
.label(&self.localized_strings.get("common.characters"))
.label_y(conrod_core::position::Relative::Scalar(3.0))
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -143,7 +150,7 @@ impl<'a> Widget for EscMenu<'a> {
.w_h(210.0, 50.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Logout")
.label(&self.localized_strings.get("esc_menu.logout"))
.label_y(conrod_core::position::Relative::Scalar(3.0))
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -158,7 +165,7 @@ impl<'a> Widget for EscMenu<'a> {
.w_h(210.0, 50.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Quit Game")
.label(&self.localized_strings.get("esc_menu.quit_game"))
.label_y(conrod_core::position::Relative::Scalar(3.0))
.label_color(TEXT_COLOR)
.label_font_size(20)

View File

@ -36,6 +36,7 @@ use spell::Spell;
use crate::{
ecs::comp as vcomp,
i18n::{i18n_asset_key, LanguageMetadata, VoxygenLocalization},
render::{AaMode, CloudMode, Consts, FluidMode, Globals, Renderer},
scene::camera::Camera,
//settings::ControlSettings,
@ -44,7 +45,7 @@ use crate::{
GlobalState,
};
use client::{Client, Event as ClientEvent};
use common::{comp, terrain::TerrainChunk, vol::RectRasterableVol};
use common::{assets::load_expect, comp, terrain::TerrainChunk, vol::RectRasterableVol};
use conrod_core::{
image::Id,
text::cursor::Index,
@ -225,6 +226,7 @@ pub enum Event {
DropInventorySlot(usize),
Logout,
Quit,
ChangeLanguage(LanguageMetadata),
}
// TODO: Are these the possible layouts we want?
@ -529,6 +531,10 @@ impl Hud {
common::util::GIT_VERSION.to_string()
);
let localized_strings = load_expect::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
));
if self.show.ingame {
let ecs = client.state().ecs();
let pos = ecs.read_storage::<comp::Pos>();
@ -1208,42 +1214,7 @@ impl Hud {
}
// Introduction Text
let intro_text: &'static str = "Welcome to the Veloren Alpha!\n\
\n\
\n\
Some tips before you start:\n\
\n\
\n\
MOST IMPORTANTLY: To set your respawn point type /waypoint into the chat.\n\
\n\
This can also be done when you are already dead!\n\
\n\
\n\
Press F1 to see the available key commands.\n\
\n\
Type /help into the chat to see chat commands\n\
\n\
\n\
There are chests and other objects randomly spawning in the World!\n\
\n\
Right-Click to collect them.\n\
\n\
To actually use whatever you loot from those chests open your inventory with 'B'.\n\
\n\
Double click the items in your bag to use or equip them.\n\
\n\
Throw them away by clicking them once and clicking outside of the bag\n\
\n\
\n\
Nights can get pretty dark in Veloren.\n\
\n\
Light your lantern by typing /lantern into the chat\n\
\n\
\n\
Want to free your cursor to close this window? Press TAB!\n\
\n\
\n\
Enjoy your stay in the World of Veloren.";
let intro_text = &localized_strings.get("hud.welcome");
if self.show.intro && !self.show.esc_menu && !self.intro_2 {
match global_state.settings.gameplay.intro_show {
Intro::Show => {
@ -1260,7 +1231,7 @@ impl Hud {
if Button::image(self.imgs.button)
.w_h(100.0, 50.0)
.mid_bottom_with_margin_on(self.ids.intro_bg, 10.0)
.label("Close")
.label(&localized_strings.get("common.close"))
.label_font_size(20)
.label_color(TEXT_COLOR)
.hover_image(self.imgs.button_hover)
@ -1297,7 +1268,7 @@ impl Hud {
{
self.never_show = !self.never_show
};
Text::new("Don't show this on Startup")
Text::new(&localized_strings.get("hud.do_not_show_on_startup"))
.right_from(self.ids.intro_check, 10.0)
.font_size(10)
.font_id(self.fonts.cyri)
@ -1343,7 +1314,7 @@ impl Hud {
if Button::image(self.imgs.button)
.w_h(100.0, 50.0)
.mid_bottom_with_margin_on(self.ids.intro_bg, 10.0)
.label("Close")
.label(&localized_strings.get("common.close"))
.label_font_size(20)
.label_color(TEXT_COLOR)
.hover_image(self.imgs.button_hover)
@ -1490,20 +1461,28 @@ impl Hud {
.set(self.ids.num_figures, ui_widgets);
// Help Window
Text::new(&format!(
"Press {:?} to show keybindings",
global_state.settings.controls.help
))
Text::new(
&localized_strings
.get("hud.press_key_to_toggle_keybindings_fmt")
.replace(
"{key}",
&format!("{:?}", global_state.settings.controls.help),
),
)
.color(TEXT_COLOR)
.down_from(self.ids.num_figures, 5.0)
.font_id(self.fonts.cyri)
.font_size(14)
.set(self.ids.help_info, ui_widgets);
// Info about Debug Shortcut
Text::new(&format!(
"Press {:?} to toggle debug info",
global_state.settings.controls.toggle_debug
))
Text::new(
&localized_strings
.get("hud.press_key_to_toggle_debug_info_fmt")
.replace(
"{key}",
&format!("{:?}", global_state.settings.controls.toggle_debug),
),
)
.color(TEXT_COLOR)
.down_from(self.ids.help_info, 5.0)
.font_id(self.fonts.cyri)
@ -1511,20 +1490,28 @@ impl Hud {
.set(self.ids.debug_info, ui_widgets);
} else {
// Help Window
Text::new(&format!(
"Press {:?} to show keybindings",
global_state.settings.controls.help
))
Text::new(
&localized_strings
.get("hud.press_key_to_show_keybindings_fmt")
.replace(
"{key}",
&format!("{:?}", global_state.settings.controls.help),
),
)
.color(TEXT_COLOR)
.top_left_with_margins_on(ui_widgets.window, 5.0, 5.0)
.font_id(self.fonts.cyri)
.font_size(16)
.set(self.ids.help_info, ui_widgets);
// Info about Debug Shortcut
Text::new(&format!(
"Press {:?} to toggle debug info",
global_state.settings.controls.toggle_debug
))
Text::new(
&localized_strings
.get("hud.press_key_to_show_debug_info_fmt")
.replace(
"{key}",
&format!("{:?}", global_state.settings.controls.toggle_debug),
),
)
.color(TEXT_COLOR)
.down_from(self.ids.help_info, 5.0)
.font_id(self.fonts.cyri)
@ -1563,7 +1550,7 @@ impl Hud {
.w_h(120.0, 50.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Show Tips")
.label(&localized_strings.get("hud.show_tips"))
.label_font_size(20)
.label_color(TEXT_COLOR)
.mid_bottom_with_margin_on(self.ids.help, 20.0)
@ -1702,8 +1689,14 @@ impl Hud {
// Settings
if let Windows::Settings = self.show.open_windows {
for event in SettingsWindow::new(&global_state, &self.show, &self.imgs, &self.fonts)
.set(self.ids.settings_window, ui_widgets)
for event in SettingsWindow::new(
&global_state,
&self.show,
&self.imgs,
&self.fonts,
&localized_strings,
)
.set(self.ids.settings_window, ui_widgets)
{
match event {
settings_window::Event::Sct(sct) => {
@ -1782,6 +1775,9 @@ impl Hud {
settings_window::Event::ChangeFluidMode(new_fluid_mode) => {
events.push(Event::ChangeFluidMode(new_fluid_mode));
}
settings_window::Event::ChangeLanguage(language) => {
events.push(Event::ChangeLanguage(language));
}
}
}
}
@ -1789,10 +1785,11 @@ impl Hud {
// Social Window
if self.show.social {
for event in Social::new(
/*&global_state,*/ &self.show,
&self.show,
client,
&self.imgs,
&self.fonts,
&localized_strings,
)
.set(self.ids.social_window, ui_widgets)
{
@ -1810,8 +1807,14 @@ impl Hud {
let ecs = client.state().ecs();
let stats = ecs.read_storage::<comp::Stats>();
let player_stats = stats.get(client.entity()).unwrap();
match CharacterWindow::new(&self.show, &player_stats, &self.imgs, &self.fonts)
.set(self.ids.character_window, ui_widgets)
match CharacterWindow::new(
&self.show,
&player_stats,
&self.imgs,
&self.fonts,
&localized_strings,
)
.set(self.ids.character_window, ui_widgets)
{
Some(character_window::Event::Close) => {
self.show.character_window(false);
@ -1823,8 +1826,14 @@ impl Hud {
// Spellbook
if self.show.spell {
match Spell::new(&self.show, client, &self.imgs, &self.fonts)
.set(self.ids.spell, ui_widgets)
match Spell::new(
&self.show,
client,
&self.imgs,
&self.fonts,
&localized_strings,
)
.set(self.ids.spell, ui_widgets)
{
Some(spell::Event::Close) => {
self.show.spell(false);
@ -1836,8 +1845,14 @@ impl Hud {
// Quest Log
if self.show.quest {
match Quest::new(&self.show, client, &self.imgs, &self.fonts)
.set(self.ids.quest, ui_widgets)
match Quest::new(
&self.show,
client,
&self.imgs,
&self.fonts,
&localized_strings,
)
.set(self.ids.quest, ui_widgets)
{
Some(quest::Event::Close) => {
self.show.quest(false);
@ -1868,7 +1883,9 @@ impl Hud {
}
if self.show.esc_menu {
match EscMenu::new(&self.imgs, &self.fonts).set(self.ids.esc_menu, ui_widgets) {
match EscMenu::new(&self.imgs, &self.fonts, &localized_strings)
.set(self.ids.esc_menu, ui_widgets)
{
Some(esc_menu::Event::OpenSettings(tab)) => {
self.show.open_setting_tab(tab);
}

View File

@ -1,4 +1,6 @@
use super::{img_ids::Imgs, Fonts, Show, TEXT_COLOR};
use crate::i18n::VoxygenLocalization;
use client::{self, Client};
use conrod_core::{
color,
widget::{self, Button, Image, Rectangle, Text},
@ -6,8 +8,6 @@ use conrod_core::{
Colorable, Positionable, Sizeable, Widget, WidgetCommon,
};
use client::{self, Client};
widget_ids! {
pub struct Ids {
quest_frame,
@ -25,17 +25,25 @@ pub struct Quest<'a> {
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> Quest<'a> {
pub fn new(show: &'a Show, _client: &'a Client, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
pub fn new(
show: &'a Show,
_client: &'a Client,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
) -> Self {
Self {
_show: show,
imgs,
_client,
fonts: fonts,
localized_strings,
common: widget::CommonBuilder::default(),
}
}
@ -93,7 +101,7 @@ impl<'a> Widget for Quest<'a> {
// Title
// TODO: Use an actual character name.
Text::new("Quest")
Text::new(&self.localized_strings.get("hud.quests"))
.mid_top_with_margin_on(state.quest_frame, 6.0)
.font_id(self.fonts.cyri)
.font_size(14)

View File

@ -3,6 +3,7 @@ use super::{
TEXT_COLOR,
};
use crate::{
i18n::{list_localizations, LanguageMetadata, VoxygenLocalization},
render::{AaMode, CloudMode, FluidMode},
ui::{ImageSlider, ScaleMode, ToggleButton},
GlobalState,
@ -41,6 +42,8 @@ widget_ids! {
absolute_scale_text,
gameplay,
controls,
languages,
languages_list,
rectangle,
general_txt,
debug_button,
@ -48,6 +51,7 @@ widget_ids! {
tips_button,
tips_button_label,
interface,
language_text,
mouse_pan_slider,
mouse_pan_label,
mouse_pan_value,
@ -146,11 +150,10 @@ pub enum SettingsTab {
#[derive(WidgetCommon)]
pub struct SettingsWindow<'a> {
global_state: &'a GlobalState,
show: &'a Show,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
@ -161,12 +164,14 @@ impl<'a> SettingsWindow<'a> {
show: &'a Show,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
) -> Self {
Self {
global_state,
show,
imgs,
fonts,
localized_strings,
common: widget::CommonBuilder::default(),
}
}
@ -205,6 +210,7 @@ pub enum Event {
Sct(bool),
SctPlayerBatch(bool),
SctDamageBatch(bool),
ChangeLanguage(LanguageMetadata),
}
pub enum ScaleChange {
@ -277,7 +283,7 @@ impl<'a> Widget for SettingsWindow<'a> {
}
// Title
Text::new("Settings")
Text::new(&self.localized_strings.get("common.settings"))
.mid_top_with_margin_on(state.ids.settings_bg, 5.0)
.font_size(14)
.color(TEXT_COLOR)
@ -301,7 +307,7 @@ impl<'a> Widget for SettingsWindow<'a> {
self.imgs.settings_button_press
})
.top_left_with_margins_on(state.ids.settings_l, 8.0 * 4.0, 2.0 * 4.0)
.label("Interface")
.label(&self.localized_strings.get("common.interface"))
.label_font_size(14)
.label_color(TEXT_COLOR)
.set(state.ids.interface, ui)
@ -317,7 +323,7 @@ impl<'a> Widget for SettingsWindow<'a> {
let ui_scale = self.global_state.settings.gameplay.ui_scale;
let chat_transp = self.global_state.settings.gameplay.chat_transp;
Text::new("General")
Text::new(&self.localized_strings.get("hud.settings.general"))
.top_left_with_margins_on(state.ids.settings_content, 5.0, 5.0)
.font_size(18)
.font_id(self.fonts.cyri)
@ -340,7 +346,7 @@ impl<'a> Widget for SettingsWindow<'a> {
events.push(Event::ToggleHelp);
}
Text::new("Help Window")
Text::new(&self.localized_strings.get("hud.settings.help_window"))
.right_from(state.ids.button_help, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -364,7 +370,7 @@ impl<'a> Widget for SettingsWindow<'a> {
events.push(Event::ToggleDebug);
}
Text::new("Debug Info")
Text::new(&self.localized_strings.get("hud.settings.debug_info"))
.right_from(state.ids.debug_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -391,7 +397,7 @@ impl<'a> Widget for SettingsWindow<'a> {
Intro::Never => events.push(Event::Intro(Intro::Show)),
}
};
Text::new("Tips on Startup")
Text::new(&self.localized_strings.get("hud.settings.tips_on_startup"))
.right_from(state.ids.tips_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -400,7 +406,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.tips_button_label, ui);
// Ui Scale
Text::new("UI-Scale")
Text::new(&self.localized_strings.get("hud.settings.ui_scale"))
.down_from(state.ids.tips_button, 20.0)
.font_size(18)
.font_id(self.fonts.cyri)
@ -646,13 +652,13 @@ impl<'a> Widget for SettingsWindow<'a> {
.graphics_for(state.ids.ch_3_bg)
.set(state.ids.crosshair_inner_3, ui);
// Crosshair Transparency Text and Slider
Text::new("Crosshair")
Text::new(&self.localized_strings.get("hud.settings.crosshair"))
.down_from(state.ids.absolute_scale_button, 20.0)
.font_size(18)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.ch_title, ui);
Text::new("Transparency")
Text::new(&self.localized_strings.get("hud.settings.transparency"))
.right_from(state.ids.ch_3_bg, 20.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -685,7 +691,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.ch_transp_value, ui);
// Hotbar text
Text::new("Hotbar")
Text::new(&self.localized_strings.get("hud.settings.hotbar"))
.down_from(state.ids.ch_1_bg, 20.0)
.font_size(18)
.font_id(self.fonts.cyri)
@ -714,13 +720,17 @@ impl<'a> Widget for SettingsWindow<'a> {
XpBar::OnGain => events.push(Event::ToggleXpBar(XpBar::Always)),
}
}
Text::new("Toggle Experience Bar")
.right_from(state.ids.show_xpbar_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.show_xpbar_button)
.color(TEXT_COLOR)
.set(state.ids.show_xpbar_text, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.toggle_bar_experience"),
)
.right_from(state.ids.show_xpbar_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.show_xpbar_button)
.color(TEXT_COLOR)
.set(state.ids.show_xpbar_text, ui);
// Show Shortcut Numbers
if Button::image(match self.global_state.settings.gameplay.shortcut_numbers {
ShortcutNumbers::On => self.imgs.checkbox_checked,
@ -748,7 +758,7 @@ impl<'a> Widget for SettingsWindow<'a> {
}
}
}
Text::new("Toggle Shortcuts")
Text::new(&self.localized_strings.get("hud.settings.toggle_shortcuts"))
.right_from(state.ids.show_shortcuts_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -773,12 +783,16 @@ impl<'a> Widget for SettingsWindow<'a> {
Number Display Duration: 1s ----I----5s
*/
// SCT/ Scrolling Combat Text
Text::new("Scrolling Combat Text")
.top_left_with_margins_on(state.ids.settings_content_r, 5.0, 5.0)
.font_size(18)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.sct_title, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.scrolling_combat_text"),
)
.top_left_with_margins_on(state.ids.settings_content_r, 5.0, 5.0)
.font_size(18)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.sct_title, ui);
// Generally toggle the SCT
let show_sct = ToggleButton::new(
self.global_state.settings.gameplay.sct,
@ -794,13 +808,17 @@ impl<'a> Widget for SettingsWindow<'a> {
if self.global_state.settings.gameplay.sct != show_sct {
events.push(Event::Sct(!self.global_state.settings.gameplay.sct))
}
Text::new("Scrolling Combat Text")
.right_from(state.ids.sct_show_radio, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.sct_show_radio)
.color(TEXT_COLOR)
.set(state.ids.sct_show_text, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.scrolling_combat_text"),
)
.right_from(state.ids.sct_show_radio, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.sct_show_radio)
.color(TEXT_COLOR)
.set(state.ids.sct_show_text, ui);
if self.global_state.settings.gameplay.sct {
// Toggle single damage numbers
let show_sct_damage_batch = !ToggleButton::new(
@ -814,13 +832,17 @@ impl<'a> Widget for SettingsWindow<'a> {
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.sct_single_dmg_radio, ui);
Text::new("Single Damage Numbers")
.right_from(state.ids.sct_single_dmg_radio, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.sct_single_dmg_radio)
.color(TEXT_COLOR)
.set(state.ids.sct_single_dmg_text, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.single_damage_number"),
)
.right_from(state.ids.sct_single_dmg_radio, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.sct_single_dmg_radio)
.color(TEXT_COLOR)
.set(state.ids.sct_single_dmg_text, ui);
// Toggle Batched Damage
let show_sct_damage_batch = ToggleButton::new(
show_sct_damage_batch,
@ -838,7 +860,7 @@ impl<'a> Widget for SettingsWindow<'a> {
!self.global_state.settings.gameplay.sct_damage_batch,
))
}
Text::new("Cumulated Damage")
Text::new(&self.localized_strings.get("hud.settings.cumulated_damage"))
.right_from(state.ids.sct_show_batch_radio, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -857,7 +879,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.sct_inc_dmg_radio, ui);
Text::new("Incoming Damage")
Text::new(&self.localized_strings.get("hud.settings.incoming_damage"))
.right_from(state.ids.sct_inc_dmg_radio, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -881,18 +903,22 @@ impl<'a> Widget for SettingsWindow<'a> {
!self.global_state.settings.gameplay.sct_player_batch,
))
}
Text::new("Cumulated Incoming Damage")
.right_from(state.ids.sct_batch_inc_radio, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.sct_batch_inc_radio)
.color(TEXT_COLOR)
.set(state.ids.sct_batch_inc_text, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.cumulated_incoming_damage"),
)
.right_from(state.ids.sct_batch_inc_radio, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.sct_batch_inc_radio)
.color(TEXT_COLOR)
.set(state.ids.sct_batch_inc_text, ui);
}
// Energybars Numbers
// Hotbar text
Text::new("Energybar Numbers")
Text::new(&self.localized_strings.get("hud.settings.energybar_numbers"))
.down_from(
if self.global_state.settings.gameplay.sct {
state.ids.sct_batch_inc_radio
@ -929,7 +955,7 @@ impl<'a> Widget for SettingsWindow<'a> {
{
events.push(Event::ToggleBarNumbers(BarNumbers::Off))
}
Text::new("None")
Text::new(&self.localized_strings.get("hud.settings.none"))
.right_from(state.ids.show_bar_numbers_none_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -960,7 +986,7 @@ impl<'a> Widget for SettingsWindow<'a> {
{
events.push(Event::ToggleBarNumbers(BarNumbers::Values))
}
Text::new("Values")
Text::new(&self.localized_strings.get("hud.settings.values"))
.right_from(state.ids.show_bar_numbers_values_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -991,7 +1017,7 @@ impl<'a> Widget for SettingsWindow<'a> {
{
events.push(Event::ToggleBarNumbers(BarNumbers::Percent))
}
Text::new("Percentages")
Text::new(&self.localized_strings.get("hud.settings.percentages"))
.right_from(state.ids.show_bar_numbers_percentage_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -1000,18 +1026,22 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.show_bar_numbers_percentage_text, ui);
// Chat Transp
Text::new("Chat")
Text::new(&self.localized_strings.get("hud.settings.chat"))
.down_from(state.ids.show_bar_numbers_percentage_button, 20.0)
.font_size(18)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.chat_transp_title, ui);
Text::new("Background Transparency")
.right_from(state.ids.chat_transp_slider, 20.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.chat_transp_text, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.background_transparency"),
)
.right_from(state.ids.chat_transp_slider, 20.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.chat_transp_text, ui);
if let Some(new_val) = ImageSlider::continuous(
chat_transp,
@ -1029,6 +1059,34 @@ impl<'a> Widget for SettingsWindow<'a> {
{
events.push(Event::ChatTransp(new_val));
}
Text::new(&self.localized_strings.get("common.languages"))
.down_from(state.ids.chat_transp_slider, 20.0)
.font_size(18)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.language_text, ui);
let selected_language = &self.global_state.settings.language.selected_language;
let language_list = list_localizations();
let language_list_str: Vec<String> = language_list
.iter()
.map(|e| e.language_name.clone())
.collect();
let selected = language_list
.iter()
.position(|x| x.language_identifier.contains(selected_language));
if let Some(clicked) = DropDownList::new(&language_list_str, selected)
.down_from(state.ids.language_text, 8.0)
.w_h(200.0, 22.0)
.color(MENU_BG)
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri)
.set(state.ids.languages_list, ui)
{
events.push(Event::ChangeLanguage(language_list[clicked].to_owned()));
}
}
// 2) Gameplay Tab --------------------------------
@ -1049,7 +1107,7 @@ impl<'a> Widget for SettingsWindow<'a> {
self.imgs.settings_button_press
})
.right_from(state.ids.interface, 0.0)
.label("Gameplay")
.label(&self.localized_strings.get("common.gameplay"))
.label_font_size(14)
.label_color(TEXT_COLOR)
.set(state.ids.gameplay, ui)
@ -1064,7 +1122,7 @@ impl<'a> Widget for SettingsWindow<'a> {
let display_zoom = self.global_state.settings.gameplay.zoom_sensitivity;
// Mouse Pan Sensitivity
Text::new("Pan Sensitivity")
Text::new(&self.localized_strings.get("hud.settings.pan_sensitivity"))
.top_left_with_margins_on(state.ids.settings_content, 10.0, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -1096,7 +1154,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.mouse_pan_value, ui);
// Mouse Zoom Sensitivity
Text::new("Zoom Sensitivity")
Text::new(&self.localized_strings.get("hud.settings.zoom_sensitivity"))
.down_from(state.ids.mouse_pan_slider, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -1145,13 +1203,17 @@ impl<'a> Widget for SettingsWindow<'a> {
));
}
Text::new("Invert Scroll Zoom")
.right_from(state.ids.mouse_zoom_invert_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.button_help)
.color(TEXT_COLOR)
.set(state.ids.mouse_zoom_invert_label, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.invert_scroll_zoom"),
)
.right_from(state.ids.mouse_zoom_invert_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.button_help)
.color(TEXT_COLOR)
.set(state.ids.mouse_zoom_invert_label, ui);
// Mouse Y Inversion
let mouse_y_inverted = ToggleButton::new(
@ -1171,13 +1233,17 @@ impl<'a> Widget for SettingsWindow<'a> {
));
}
Text::new("Invert Mouse Y Axis")
.right_from(state.ids.mouse_y_invert_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.button_help)
.color(TEXT_COLOR)
.set(state.ids.mouse_y_invert_label, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.invert_mouse_y_axis"),
)
.right_from(state.ids.mouse_y_invert_button, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.graphics_for(state.ids.button_help)
.color(TEXT_COLOR)
.set(state.ids.mouse_y_invert_label, ui);
}
// 3) Controls Tab --------------------------------
@ -1198,7 +1264,7 @@ impl<'a> Widget for SettingsWindow<'a> {
self.imgs.settings_button_press
})
.right_from(state.ids.gameplay, 0.0)
.label("Controls")
.label(&self.localized_strings.get("common.controls"))
.label_font_size(14)
.label_color(TEXT_COLOR)
.set(state.ids.controls, ui)
@ -1210,94 +1276,12 @@ impl<'a> Widget for SettingsWindow<'a> {
// Contents
if let SettingsTab::Controls = self.show.settings_tab {
let controls = &self.global_state.settings.controls;
Text::new(
"Free Cursor\n\
Toggle Help Window\n\
Toggle Interface\n\
Toggle FPS and Debug Info\n\
Take Screenshot\n\
Toggle Nametags\n\
Toggle Fullscreen\n\
\n\
\n\
Move Forward\n\
Move Left\n\
Move Right\n\
Move Backwards\n\
\n\
Jump\n\
\n\
Glider
\n\
Dodge\n\
\n\
Roll\n\
\n\
Climb\n\
\n\
Climb down\n\
\n\
Auto Walk\n\
\n\
Sheathe/Draw Weapons\n\
\n\
Put on/Remove Helmet\n\
\n\
Sit\n\
\n\
Mount\n\
\n\
Interact\n\
\n\
\n\
Basic Attack\n\
Secondary Attack/Block/Aim\n\
\n\
\n\
Skillbar Slot 1\n\
Skillbar Slot 2\n\
Skillbar Slot 3\n\
Skillbar Slot 4\n\
Skillbar Slot 5\n\
Skillbar Slot 6\n\
Skillbar Slot 7\n\
Skillbar Slot 8\n\
Skillbar Slot 9\n\
Skillbar Slot 10\n\
\n\
\n\
Pause Menu\n\
Settings\n\
Social\n\
Map\n\
Spellbook\n\
Character\n\
Questlog\n\
Bag\n\
\n\
\n\
\n\
Send Chat Message\n\
Scroll Chat\n\
\n\
\n\
Chat commands: \n\
\n\
/alias [Name] - Change your Chat Name \n\
/tp [Name] - Teleports you to another player \n\
/jump <dx> <dy> <dz> - Offset your position \n\
/goto <x> <y> <z> - Teleport to a position \n\
/kill - Kill yourself \n\
/pig - Spawn pig NPC \n\
/wolf - Spawn wolf NPC \n\
/help - Display chat commands
",
)
.color(TEXT_COLOR)
.top_left_with_margins_on(state.ids.settings_content, 5.0, 5.0)
.font_id(self.fonts.cyri)
.font_size(18)
.set(state.ids.controls_text, ui);
Text::new(&self.localized_strings.get("hud.settings.control_names"))
.color(TEXT_COLOR)
.top_left_with_margins_on(state.ids.settings_content, 5.0, 5.0)
.font_id(self.fonts.cyri)
.font_size(18)
.set(state.ids.controls_text, ui);
// TODO: Replace with buttons that show actual keybinds and allow the user to change them.
Text::new(&format!(
"{}\n\
@ -1446,7 +1430,7 @@ impl<'a> Widget for SettingsWindow<'a> {
self.imgs.settings_button_press
})
.right_from(state.ids.controls, 0.0)
.label("Video")
.label(&self.localized_strings.get("common.video"))
.parent(state.ids.settings_r)
.label_font_size(14)
.label_color(TEXT_COLOR)
@ -1459,7 +1443,7 @@ impl<'a> Widget for SettingsWindow<'a> {
// Contents
if let SettingsTab::Video = self.show.settings_tab {
// View Distance
Text::new("View Distance")
Text::new(&self.localized_strings.get("hud.settings.view_distance"))
.top_left_with_margins_on(state.ids.settings_content, 10.0, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -1494,7 +1478,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.vd_value, ui);
// Max FPS
Text::new("Maximum FPS")
Text::new(&self.localized_strings.get("hud.settings.maximum_fps"))
.down_from(state.ids.vd_slider, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -1561,7 +1545,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.fov_value, ui);
// AaMode
Text::new("AntiAliasing Mode")
Text::new(&self.localized_strings.get("hud.settings.antialiasing_mode"))
.down_from(state.ids.fov_slider, 8.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -1602,15 +1586,24 @@ impl<'a> Widget for SettingsWindow<'a> {
}
// CloudMode
Text::new("Cloud Rendering Mode")
.down_from(state.ids.aa_mode_list, 8.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.cloud_mode_text, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.cloud_rendering_mode"),
)
.down_from(state.ids.aa_mode_list, 8.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.cloud_mode_text, ui);
let mode_list = [CloudMode::None, CloudMode::Regular];
let mode_label_list = ["None", "Regular"];
let mode_label_list = [
&self.localized_strings.get("common.none"),
&self
.localized_strings
.get("hud.settings.cloud_rendering_mode.regular"),
];
// Get which cloud rendering mode is currently active
let selected = mode_list
@ -1629,15 +1622,26 @@ impl<'a> Widget for SettingsWindow<'a> {
}
// FluidMode
Text::new("Fluid Rendering Mode")
.down_from(state.ids.cloud_mode_list, 8.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.fluid_mode_text, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.fluid_rendering_mode"),
)
.down_from(state.ids.cloud_mode_list, 8.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.fluid_mode_text, ui);
let mode_list = [FluidMode::Cheap, FluidMode::Shiny];
let mode_label_list = ["Cheap", "Shiny"];
let mode_label_list = [
&self
.localized_strings
.get("hud.settings.fluid_rendering_mode.cheap"),
&self
.localized_strings
.get("hud.settings.fluid_rendering_mode.shiny"),
];
// Get which fluid rendering mode is currently active
let selected = mode_list
@ -1675,7 +1679,7 @@ impl<'a> Widget for SettingsWindow<'a> {
})
.right_from(state.ids.video, 0.0)
.parent(state.ids.settings_r)
.label("Sound")
.label(&self.localized_strings.get("common.sound"))
.label_font_size(14)
.label_color(TEXT_COLOR)
.set(state.ids.sound, ui)
@ -1687,7 +1691,7 @@ impl<'a> Widget for SettingsWindow<'a> {
// Contents
if let SettingsTab::Sound = self.show.settings_tab {
// Music Volume -----------------------------------------------------
Text::new("Music Volume")
Text::new(&self.localized_strings.get("hud.settings.music_volume"))
.top_left_with_margins_on(state.ids.settings_content, 10.0, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
@ -1712,12 +1716,16 @@ impl<'a> Widget for SettingsWindow<'a> {
}
// SFX Volume -------------------------------------------------------
Text::new("Sound Effects Volume")
.down_from(state.ids.audio_volume_slider, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.sfx_volume_text, ui);
Text::new(
&self
.localized_strings
.get("hud.settings.sound_effect_volume"),
)
.down_from(state.ids.audio_volume_slider, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(state.ids.sfx_volume_text, ui);
if let Some(new_val) = ImageSlider::continuous(
self.global_state.settings.audio.sfx_volume,
@ -1739,7 +1747,7 @@ impl<'a> Widget for SettingsWindow<'a> {
// Audio Device Selector --------------------------------------------
let device = &self.global_state.audio.device;
let device_list = &self.global_state.audio.device_list;
Text::new("Audio Device")
Text::new(&self.localized_strings.get("hud.settings.audio_device"))
.down_from(state.ids.sfx_volume_slider, 10.0)
.font_size(14)
.font_id(self.fonts.cyri)

View File

@ -2,7 +2,9 @@ use super::{
img_ids::Imgs, BarNumbers, Fonts, ShortcutNumbers, XpBar, CRITICAL_HP_COLOR,
/*FOCUS_COLOR, RAGE_COLOR,*/ HP_COLOR, LOW_HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR,
};
use crate::i18n::{i18n_asset_key, VoxygenLocalization};
use crate::GlobalState;
use common::assets::load_expect;
use common::comp::{
item::Debug, item::Tool, ActionState, CharacterState, ControllerInputs, Energy, ItemKind, Stats,
};
@ -189,6 +191,10 @@ impl<'a> Widget for Skillbar<'a> {
let hp_ani = (self.pulse * 4.0/*speed factor*/).cos() * 0.5 + 0.8; //Animation timer
let crit_hp_color: Color = Color::Rgba(0.79, 0.19, 0.17, hp_ani);
let localized_strings = load_expect::<VoxygenLocalization>(&i18n_asset_key(
&self.global_state.settings.language.selected_language,
));
// Stamina Wheel
/*
let stamina_percentage =
@ -277,34 +283,30 @@ impl<'a> Widget for Skillbar<'a> {
.set(state.ids.level_down, ui);
// Death message
if self.stats.is_dead {
Text::new("You Died")
Text::new(&localized_strings.get("hud.you_died"))
.middle_of(ui.window)
.font_size(50)
.font_id(self.fonts.cyri)
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
.set(state.ids.death_message_1_bg, ui);
Text::new(&format!(
"Press {:?} to respawn at your Waypoint.\n\
\n\
Press Enter, type in /waypoint and confirm to set it here.",
self.global_state.settings.controls.respawn
Text::new(&localized_strings.get("hud.press_key_to_respawn").replace(
"{key}",
&format!("{:?}", self.global_state.settings.controls.respawn),
))
.mid_bottom_with_margin_on(state.ids.death_message_1_bg, -120.0)
.font_size(30)
.font_id(self.fonts.cyri)
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
.set(state.ids.death_message_2_bg, ui);
Text::new("You Died")
Text::new(&localized_strings.get("hud.you_died"))
.bottom_left_with_margins_on(state.ids.death_message_1_bg, 2.0, 2.0)
.font_size(50)
.font_id(self.fonts.cyri)
.color(CRITICAL_HP_COLOR)
.set(state.ids.death_message_1, ui);
Text::new(&format!(
"Press {:?} to respawn at your Waypoint.\n\
\n\
Press Enter, type in /waypoint and confirm to set it here.",
self.global_state.settings.controls.respawn
Text::new(&localized_strings.get("hud.press_key_to_respawn").replace(
"{key}",
&format!("{:?}", self.global_state.settings.controls.respawn),
))
.bottom_left_with_margins_on(state.ids.death_message_2_bg, 2.0, 2.0)
.font_size(30)

View File

@ -1,5 +1,7 @@
use super::{img_ids::Imgs, Fonts, Show, TEXT_COLOR, TEXT_COLOR_3};
use crate::i18n::VoxygenLocalization;
use client::{self, Client};
use conrod_core::{
color,
widget::{self, Button, Image, Rectangle, Scrollbar, Text},
@ -7,8 +9,6 @@ use conrod_core::{
Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
};
use client::{self, Client};
widget_ids! {
pub struct Ids {
social_frame,
@ -41,26 +41,31 @@ pub struct Social<'a> {
client: &'a Client,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> Social<'a> {
pub fn new(show: &'a Show, client: &'a Client, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
pub fn new(
show: &'a Show,
client: &'a Client,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
) -> Self {
Self {
show: show,
show,
client,
imgs,
client: client,
fonts: fonts,
fonts,
localized_strings,
common: widget::CommonBuilder::default(),
}
}
}
/*pub struct State {
ids: Ids,
}*/
pub enum Event {
Close,
ChangeSocialTab(SocialTab),
@ -113,7 +118,7 @@ impl<'a> Widget for Social<'a> {
}
// Title
Text::new("Social")
Text::new(&self.localized_strings.get("hud.social"))
.mid_top_with_margin_on(ids.social_frame, 6.0)
.font_id(self.fonts.cyri)
.font_size(14)
@ -159,7 +164,7 @@ impl<'a> Widget for Social<'a> {
self.imgs.social_button_press
})
.top_left_with_margins_on(ids.align, 4.0, 0.0)
.label("Online")
.label(&self.localized_strings.get("hud.social.online"))
.label_font_size(14)
.parent(ids.frame)
.label_color(TEXT_COLOR)
@ -183,12 +188,17 @@ impl<'a> Widget for Social<'a> {
.resize(count, &mut ui.widget_id_generator())
})
}
Text::new(&format!("{} player(s) online\n", count))
.top_left_with_margins_on(ids.content_align, -2.0, 7.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(ids.online_title, ui);
Text::new(
&self
.localized_strings
.get("hud.social.play_online_fmt")
.replace("{nb_player}", &format!("{:?}", count)),
)
.top_left_with_margins_on(ids.content_align, -2.0, 7.0)
.font_size(14)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(ids.online_title, ui);
for (i, (_, player_alias)) in self.client.player_list.iter().enumerate() {
Text::new(player_alias)
.down(3.0)
@ -218,7 +228,7 @@ impl<'a> Widget for Social<'a> {
self.imgs.social_button
})
.right_from(ids.online_tab, 0.0)
.label("Friends")
.label(&self.localized_strings.get("hud.social.friends"))
.label_font_size(14)
.parent(ids.frame)
.label_color(TEXT_COLOR_3)
@ -231,7 +241,7 @@ impl<'a> Widget for Social<'a> {
// Contents
if let SocialTab::Friends = self.show.social_tab {
Text::new("Not yet available")
Text::new(&self.localized_strings.get("hud.social.not_yet_available"))
.middle_of(ids.content_align)
.font_size(18)
.font_id(self.fonts.cyri)
@ -248,7 +258,7 @@ impl<'a> Widget for Social<'a> {
if Button::image(button_img)
.w_h(30.0 * 4.0, 12.0 * 4.0)
.right_from(ids.friends_tab, 0.0)
.label("Faction")
.label(&self.localized_strings.get("hud.social.faction"))
.parent(ids.frame)
.label_font_size(14)
.label_color(TEXT_COLOR_3)
@ -261,7 +271,7 @@ impl<'a> Widget for Social<'a> {
// Contents
if let SocialTab::Faction = self.show.social_tab {
Text::new("Not yet available")
Text::new(&self.localized_strings.get("hud.social.not_yet_available"))
.middle_of(ids.content_align)
.font_size(18)
.font_id(self.fonts.cyri)

View File

@ -8,6 +8,8 @@ use conrod_core::{
use client::{self, Client};
use crate::i18n::VoxygenLocalization;
widget_ids! {
pub struct Ids {
spell_frame,
@ -25,17 +27,26 @@ pub struct Spell<'a> {
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> Spell<'a> {
pub fn new(show: &'a Show, _client: &'a Client, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
pub fn new(
show: &'a Show,
_client: &'a Client,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a std::sync::Arc<VoxygenLocalization>,
) -> Self {
Self {
_show: show,
imgs,
_client,
fonts: fonts,
imgs,
fonts,
localized_strings,
common: widget::CommonBuilder::default(),
}
}
@ -93,7 +104,7 @@ impl<'a> Widget for Spell<'a> {
// Title
// TODO: Use an actual character name.
Text::new("Spell")
Text::new(&self.localized_strings.get("hud.spell"))
.mid_top_with_margin_on(state.spell_frame, 6.0)
.font_id(self.fonts.cyri)
.font_size(14)

113
voxygen/src/i18n.rs Normal file
View File

@ -0,0 +1,113 @@
use common::assets;
use common::assets::{load_expect, load_glob, Asset};
use deunicode::deunicode;
use ron::de::from_reader;
use serde_derive::*;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::BufReader;
/// The reference language, aka the more up-to-date localization data.
/// Also the default language at first startup.
pub const REFERENCE_LANG: &str = "en";
/// How a language can be described
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct LanguageMetadata {
/// A human friendly language name (e.g. "English (US)")
pub language_name: String,
/// A short text identifier for this language (e.g. "en_US")
///
/// On the opposite of `language_name` that can change freely,
/// `language_identifier` value shall be stable in time as it
/// is used by setting components to store the language
/// selected by the user.
pub language_identifier: String,
}
/// Store internationalization data
///
/// TODO: store the font locations here (Font asset path for instance)
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct VoxygenLocalization {
/// A map storing the localized texts
///
/// Localized content can be access using a String key
pub string_map: HashMap<String, String>,
/// Either to convert the input text encoded in UTF-8
/// into a ASCII version by using the `deunicode` crate.
pub convert_utf8_to_ascii: bool,
pub metadata: LanguageMetadata,
}
impl VoxygenLocalization {
/// Get a localized text from the given key
///
/// If the key is not present in the localization object
/// then the key is returned.
pub fn get(&self, key: &str) -> String {
match self.string_map.get(key) {
Some(localized_text) => localized_text.to_owned(),
None => key.to_string(),
}
}
/// Return the missing keys compared to the reference language and return
/// them
pub fn list_missing_entries(&self) -> HashSet<String> {
let reference_localization =
load_expect::<VoxygenLocalization>(i18n_asset_key(REFERENCE_LANG).as_ref());
let reference_keys: HashSet<_> =
reference_localization.string_map.keys().cloned().collect();
let current_keys: HashSet<_> = self.string_map.keys().cloned().collect();
reference_keys.difference(&current_keys).cloned().collect()
}
/// Log missing entries (compared to the reference language) as warnings
pub fn log_missing_entries(&self) {
for missing_key in self.list_missing_entries() {
log::warn!(
"[{:?}] Missing key {:?}",
self.metadata.language_identifier,
missing_key
);
}
}
}
impl Asset for VoxygenLocalization {
const ENDINGS: &'static [&'static str] = &["ron"];
/// Load the translations located in the input buffer and convert them
/// into a `VoxygenLocalization` object.
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
let mut asked_localization: VoxygenLocalization = from_reader(buf_reader).unwrap();
// Update the text if UTF-8 to ASCII conversion is enabled
if asked_localization.convert_utf8_to_ascii {
for value in asked_localization.string_map.values_mut() {
*value = deunicode(value);
}
}
asked_localization.metadata.language_name =
deunicode(&asked_localization.metadata.language_name);
Ok(asked_localization)
}
}
/// Load all the available languages located in the Voxygen asset directory
pub fn list_localizations() -> Vec<LanguageMetadata> {
let voxygen_locales_assets = "voxygen.i18n.*";
let lang_list = load_glob::<VoxygenLocalization>(voxygen_locales_assets).unwrap();
lang_list.iter().map(|e| (*e).metadata.clone()).collect()
}
/// Return the asset associated with the language_id
pub fn i18n_asset_key(language_id: &str) -> String {
"voxygen.i18n.".to_string() + language_id
}

View File

@ -9,6 +9,7 @@ pub mod audio;
mod ecs;
pub mod error;
pub mod hud;
pub mod i18n;
pub mod key_state;
mod logging;
pub mod menu;
@ -26,8 +27,14 @@ pub mod window;
pub use crate::error::Error;
use crate::{
audio::AudioFrontend, menu::main::MainMenuState, meta::Meta, settings::Settings, window::Window,
audio::AudioFrontend,
i18n::{i18n_asset_key, VoxygenLocalization},
menu::main::MainMenuState,
meta::Meta,
settings::Settings,
window::Window,
};
use common::assets::load_expect;
use log::{debug, error};
use std::{mem, panic, str::FromStr};
@ -131,6 +138,12 @@ fn main() {
info_message: None,
};
// Try to load the localization and log missing entries
let localized_strings = load_expect::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
));
localized_strings.log_missing_entries();
// Set up panic handler to relay swish panic messages to the user
let default_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {

View File

@ -1,6 +1,7 @@
mod scene;
mod ui;
use crate::i18n::{i18n_asset_key, VoxygenLocalization};
use crate::{
session::SessionState, window::Event as WinEvent, Direction, GlobalState, PlayState,
PlayStateResult,
@ -108,15 +109,15 @@ impl PlayState for CharSelectionState {
.render(global_state.window.renderer_mut(), self.scene.globals());
// Tick the client (currently only to keep the connection alive).
let localized_strings = assets::load_expect::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
));
if let Err(err) = self.client.borrow_mut().tick(
comp::ControllerInputs::default(),
clock.get_last_delta(),
|_| {},
) {
global_state.info_message = Some(
"Connection lost!\nDid the server restart?\nIs the client up to date?"
.to_owned(),
);
global_state.info_message = Some(localized_strings.get("common.connection_lost"));
error!("[session] Failed to tick the scene: {:?}", err);
return PlayStateResult::Pop;

View File

@ -1,5 +1,6 @@
use crate::window::{Event as WinEvent, PressState};
use crate::{
i18n::{i18n_asset_key, VoxygenLocalization},
meta::CharacterData,
render::{Consts, Globals, Renderer},
ui::{
@ -9,6 +10,7 @@ use crate::{
GlobalState,
};
use client::Client;
use common::assets::load_expect;
use common::comp::{self, humanoid};
use conrod_core::{
color,
@ -256,6 +258,7 @@ pub struct CharSelectionUi {
fonts: Fonts,
character_creation: bool,
info_content: InfoContent,
selected_language: String,
//deletion_confirmation: bool,
pub character_name: String,
pub character_body: humanoid::Body,
@ -287,6 +290,7 @@ impl CharSelectionUi {
info_content: InfoContent::None,
//deletion_confirmation: false,
character_creation: false,
selected_language: global_state.settings.language.selected_language.clone(),
character_name: "Character Name".to_string(),
character_body: humanoid::Body::random(),
character_tool: Some(STARTER_SWORD),
@ -302,6 +306,9 @@ impl CharSelectionUi {
env!("CARGO_PKG_VERSION"),
common::util::GIT_VERSION.to_string()
);
let localized_strings =
load_expect::<VoxygenLocalization>(&i18n_asset_key(&self.selected_language));
// Tooltip
let tooltip_human = Tooltip::new({
// Edge images [t, b, r, l]
@ -337,7 +344,7 @@ impl CharSelectionUi {
match self.info_content {
InfoContent::None => unreachable!(),
InfoContent::Deletion(character_index) => {
Text::new("Permanently delete this Character?")
Text::new(&localized_strings.get("char_selection.delete_permanently"))
.mid_top_with_margin_on(self.ids.info_frame, 40.0)
.font_size(24)
.font_id(self.fonts.cyri)
@ -349,7 +356,7 @@ impl CharSelectionUi {
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label_y(Relative::Scalar(2.0))
.label("No")
.label(&localized_strings.get("common.no"))
.label_font_id(self.fonts.cyri)
.label_font_size(18)
.label_color(TEXT_COLOR)
@ -364,8 +371,7 @@ impl CharSelectionUi {
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label_y(Relative::Scalar(2.0))
.label("Yes")
.label_font_id(self.fonts.cyri)
.label(&localized_strings.get("common.yes"))
.label_font_size(18)
.label_color(TEXT_COLOR)
.set(self.ids.info_ok, ui_widgets)
@ -435,7 +441,7 @@ impl CharSelectionUi {
.parent(self.ids.charlist_bg)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Change Server")
.label(&localized_strings.get("char_selection.change_server"))
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri)
.label_font_size(18)
@ -452,7 +458,7 @@ impl CharSelectionUi {
.w_h(250.0, 60.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Enter World")
.label(&localized_strings.get("char_selection.enter_world"))
.label_color(TEXT_COLOR)
.label_font_size(26)
.label_font_id(self.fonts.cyri)
@ -469,7 +475,7 @@ impl CharSelectionUi {
.w_h(150.0, 40.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Logout")
.label(&localized_strings.get("char_selection.logout"))
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -486,7 +492,7 @@ impl CharSelectionUi {
.w_h(270.0, 50.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Create Character")
.label(&localized_strings.get("char_selection.create_charater"))
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -599,7 +605,7 @@ impl CharSelectionUi {
.w_h(386.0, 80.0)
.hover_image(self.imgs.selection_hover)
.press_image(self.imgs.selection_press)
.label("Create New Character")
.label(&localized_strings.get("char_selection.create_new_charater"))
.label_color(Color::Rgba(0.38, 1.0, 0.07, 1.0))
.label_font_id(self.fonts.cyri)
.image_color(Color::Rgba(0.38, 1.0, 0.07, 1.0))
@ -619,7 +625,7 @@ impl CharSelectionUi {
.w_h(150.0, 40.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Back")
.label(&localized_strings.get("common.back"))
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -635,7 +641,7 @@ impl CharSelectionUi {
.w_h(150.0, 40.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Create")
.label(&localized_strings.get("common.create"))
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -704,7 +710,7 @@ impl CharSelectionUi {
.set(self.ids.selection_scrollbar, ui_widgets);
// Male/Female/Race Icons
Text::new("Character Creation")
Text::new(&localized_strings.get("char_selection.character_creation"))
.mid_top_with_margin_on(self.ids.creation_alignment, 10.0)
.font_size(24)
.font_id(self.fonts.cyri)
@ -794,7 +800,12 @@ impl CharSelectionUi {
.middle_of(self.ids.human)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Human", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.races.human"),
"",
&tooltip_human,
)
/*.tooltip_image(
if let humanoid::BodyType::Male = self.character_body.body_type {
self.imgs.human_m
@ -822,7 +833,12 @@ impl CharSelectionUi {
.middle_of(self.ids.orc)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Orc", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.races.orc"),
"",
&tooltip_human,
)
.set(self.ids.race_2, ui_widgets)
.was_clicked()
{
@ -842,7 +858,12 @@ impl CharSelectionUi {
.middle_of(self.ids.dwarf)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Dwarf", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.races.dwarf"),
"",
&tooltip_human,
)
.set(self.ids.race_3, ui_widgets)
.was_clicked()
{
@ -862,7 +883,12 @@ impl CharSelectionUi {
.middle_of(self.ids.elf)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Elf", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.races.elf"),
"",
&tooltip_human,
)
.set(self.ids.race_4, ui_widgets)
.was_clicked()
{
@ -882,7 +908,12 @@ impl CharSelectionUi {
.middle_of(self.ids.undead)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Undead", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.races.undead"),
"",
&tooltip_human,
)
.set(self.ids.race_5, ui_widgets)
.was_clicked()
{
@ -902,7 +933,12 @@ impl CharSelectionUi {
.middle_of(self.ids.danari)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Danari", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.races.danari"),
"",
&tooltip_human,
)
.set(self.ids.race_6, ui_widgets)
.was_clicked()
{
@ -924,7 +960,12 @@ impl CharSelectionUi {
.middle_of(self.ids.hammer)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Hammer", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.weapons.hammer"),
"",
&tooltip_human,
)
.set(self.ids.hammer_button, ui_widgets)
.was_clicked()
{
@ -949,7 +990,12 @@ impl CharSelectionUi {
.middle_of(self.ids.bow)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Bow", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.weapons.bow"),
"",
&tooltip_human,
)
.set(self.ids.bow_button, ui_widgets)
.was_clicked()
{
@ -972,7 +1018,12 @@ impl CharSelectionUi {
.middle_of(self.ids.staff)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Staff", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.weapons.staff"),
"",
&tooltip_human,
)
.set(self.ids.staff_button, ui_widgets)
.was_clicked()
{
@ -995,7 +1046,12 @@ impl CharSelectionUi {
.middle_of(self.ids.sword)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Sword", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.weapons.sword"),
"",
&tooltip_human,
)
.set(self.ids.sword_button, ui_widgets)
.was_clicked()
{
@ -1038,7 +1094,12 @@ impl CharSelectionUi {
.middle_of(self.ids.axe)
.hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press)
.with_tooltip(tooltip_manager, "Axe", "", &tooltip_human)
.with_tooltip(
tooltip_manager,
&localized_strings.get("common.weapons.axe"),
"",
&tooltip_human,
)
.set(self.ids.axe_button, ui_widgets)
.was_clicked()
{
@ -1056,13 +1117,13 @@ impl CharSelectionUi {
self.imgs.slider_range,
);
let char_slider = move |prev_id,
text,
text: String,
text_id,
max,
selected_val,
slider_id,
ui_widgets: &mut UiCell| {
Text::new(text)
Text::new(&text.clone())
.down_from(prev_id, 22.0)
.align_middle_x_of(prev_id)
.font_size(18)
@ -1081,7 +1142,7 @@ impl CharSelectionUi {
// Hair Style
if let Some(new_val) = char_slider(
self.ids.creation_buttons_alignment_2,
"Hair Style",
localized_strings.get("char_selection.hair_style"),
self.ids.hairstyle_text,
self.character_body
.race
@ -1096,7 +1157,7 @@ impl CharSelectionUi {
// Hair Color
if let Some(new_val) = char_slider(
self.ids.hairstyle_slider,
"Hair Color",
localized_strings.get("char_selection.hair_color"),
self.ids.haircolor_text,
self.character_body.race.num_hair_colors() as usize - 1,
self.character_body.hair_color as usize,
@ -1108,7 +1169,7 @@ impl CharSelectionUi {
// Skin
if let Some(new_val) = char_slider(
self.ids.haircolor_slider,
"Skin",
localized_strings.get("char_selection.skin"),
self.ids.skin_text,
self.character_body.race.num_skin_colors() as usize - 1,
self.character_body.skin as usize,
@ -1121,7 +1182,7 @@ impl CharSelectionUi {
let current_eyebrows = self.character_body.eyebrows;
if let Some(new_val) = char_slider(
self.ids.skin_slider,
"Eyebrows",
localized_strings.get("char_selection.eyebrows"),
self.ids.eyebrows_text,
humanoid::ALL_EYEBROWS.len() - 1,
humanoid::ALL_EYEBROWS
@ -1136,7 +1197,7 @@ impl CharSelectionUi {
// EyeColor
if let Some(new_val) = char_slider(
self.ids.eyebrows_slider,
"Eye Color",
localized_strings.get("char_selection.eye_color"),
self.ids.eyecolor_text,
self.character_body.race.num_eye_colors() as usize - 1,
self.character_body.eye_color as usize,
@ -1149,7 +1210,7 @@ impl CharSelectionUi {
let _current_accessory = self.character_body.accessory;
if let Some(new_val) = char_slider(
self.ids.eyecolor_slider,
"Accessories",
localized_strings.get("char_selection.accessories"),
self.ids.accessories_text,
self.character_body
.race
@ -1170,7 +1231,7 @@ impl CharSelectionUi {
{
if let Some(new_val) = char_slider(
self.ids.accessories_slider,
"Beard",
localized_strings.get("char_selection.beard"),
self.ids.beard_text,
self.character_body
.race
@ -1183,7 +1244,7 @@ impl CharSelectionUi {
self.character_body.beard = new_val as u8;
}
} else {
Text::new("Beard")
Text::new(&localized_strings.get("char_selection.beard"))
.mid_bottom_with_margin_on(self.ids.accessories_slider, -40.0)
.font_size(18)
.font_id(self.fonts.cyri)
@ -1203,7 +1264,7 @@ impl CharSelectionUi {
let current_chest = self.character_body.chest;
if let Some(new_val) = char_slider(
self.ids.beard_slider,
"Chest Color",
localized_strings.get("char_selection.chest_color"),
self.ids.chest_text,
humanoid::ALL_CHESTS.len() - 1,
humanoid::ALL_CHESTS

View File

@ -4,11 +4,14 @@ mod ui;
use super::char_selection::CharSelectionState;
use crate::{
singleplayer::Singleplayer, window::Event, Direction, GlobalState, PlayState, PlayStateResult,
i18n::{i18n_asset_key, VoxygenLocalization},
singleplayer::Singleplayer,
window::Event,
Direction, GlobalState, PlayState, PlayStateResult,
};
use argon2::{self, Config};
use client_init::{ClientInit, Error as InitError};
use common::{clock::Clock, comp};
use common::{assets::load_expect, clock::Clock, comp};
use log::warn;
#[cfg(feature = "singleplayer")]
use std::time::Duration;
@ -147,9 +150,13 @@ impl PlayState for MainMenuState {
}
}
}
let localized_strings = load_expect::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
));
if let Some(info) = global_state.info_message.take() {
self.main_menu_ui.show_info(info);
self.main_menu_ui
.show_info(info, localized_strings.get("common.okay"));
}
// Draw the UI to the screen.

View File

@ -1,3 +1,4 @@
use crate::i18n::{i18n_asset_key, VoxygenLocalization};
use crate::ui::Graphic;
use crate::{
render::Renderer,
@ -219,13 +220,11 @@ impl MainMenuUi {
const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
const TEXT_COLOR_2: Color = Color::Rgba(1.0, 1.0, 1.0, 0.2);
//const INACTIVE: Color = Color::Rgba(0.47, 0.47, 0.47, 0.47);
let intro_text: &'static str = "Information on the Login Process:\n\
\n\
The name you put in will be your character name ingame.\n\
\n\
Character names and appearances will be saved on your computer.\n\
\n\
Levels/Items are not saved yet.";
let localized_strings = load_expect::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
));
let intro_text = &localized_strings.get("main.login_process");
// Tooltip
let _tooltip = Tooltip::new({
@ -349,39 +348,18 @@ impl MainMenuUi {
.scroll_kids_vertically()
.set(self.ids.disc_window, ui_widgets);
Text::new("Disclaimer")
Text::new(&localized_strings.get("common.disclaimer"))
.top_left_with_margins_on(self.ids.disc_window, 30.0, 40.0)
.font_size(35)
.font_id(self.fonts.alkhemi)
.color(TEXT_COLOR)
.set(self.ids.disc_text_1, ui_widgets);
Text::new(
"Welcome to the alpha version of Veloren!\n\
\n\
\n\
Before you dive into the fun, please keep a few things in mind:\n\
\n\
- This is a very early alpha. Expect bugs, extremely unfinished gameplay, unpolished mechanics, and missing features. \n\
\n\
-If you have constructive feedback or bug reports, you can contact us via Reddit, GitLab, or our community Discord server.\n\
\n\
- Veloren is licensed under the GPL 3 open-source licence. That means you're free to play, modify, and redistribute the game however you wish \n\
(provided derived work is also under GPL 3).
\n\
- Veloren is a non-profit community project, and everybody working on it is a volunteer.\n\
If you like what you see, you're welcome to join the development or art teams!
\n\
- 'Voxel RPG' is a genre in its own right. First-person shooters used to be called Doom clones.\n\
Like them, we're trying to build a niche. This game is not a clone, and its development will diverge from existing games in the future.\n\
\n\
Thanks for taking the time to read this notice, we hope you enjoy the game!\n\
\n\
~ The Veloren Devs")
.top_left_with_margins_on(self.ids.disc_window, 110.0, 40.0)
.font_size(26)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(self.ids.disc_text_2, ui_widgets);
Text::new(&localized_strings.get("main.notice"))
.top_left_with_margins_on(self.ids.disc_window, 110.0, 40.0)
.font_size(26)
.font_id(self.fonts.cyri)
.color(TEXT_COLOR)
.set(self.ids.disc_text_2, ui_widgets);
if Button::image(self.imgs.button)
.w_h(300.0, 50.0)
.mid_bottom_with_margin_on(self.ids.disc_window, 30.0)
@ -407,8 +385,8 @@ impl MainMenuUi {
self.connect = true;
self.connecting = Some(std::time::Instant::now());
self.popup = Some(PopupData {
msg: "Connecting...".to_string(),
button_text: "Cancel".to_string(),
msg: localized_strings.get("main.connecting") + "...",
button_text: localized_strings.get("common.cancel"),
popup_type: PopupType::ConnectionInfo,
});
@ -445,8 +423,8 @@ impl MainMenuUi {
self.connect = true;
self.connecting = Some(std::time::Instant::now());
self.popup = Some(PopupData {
msg: "Creating World...".to_string(),
button_text: "Cancel".to_string(),
msg: localized_strings.get("main.creating_world") + "...",
button_text: localized_strings.get("common.cancel"),
popup_type: PopupType::ConnectionInfo,
});
};
@ -565,7 +543,7 @@ impl MainMenuUi {
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label_y(Relative::Scalar(2.0))
.label("Close")
.label(&localized_strings.get("common.close"))
.label_font_size(20)
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR)
@ -610,7 +588,7 @@ impl MainMenuUi {
.w_h(258.0, 55.0)
.down_from(self.ids.address_bg, 20.0)
.align_middle_x_of(self.ids.address_bg)
.label("Multiplayer")
.label(&localized_strings.get("common.multiplayer"))
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR)
.label_font_size(22)
@ -637,7 +615,7 @@ impl MainMenuUi {
.w_h(258.0, 55.0)
.down_from(self.ids.login_button, 20.0)
.align_middle_x_of(self.ids.address_bg)
.label("Singleplayer")
.label(&localized_strings.get("common.singleplayer"))
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR)
.label_font_size(22)
@ -655,7 +633,7 @@ impl MainMenuUi {
.bottom_left_with_margins_on(ui_widgets.window, 60.0, 30.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Quit")
.label(&localized_strings.get("common.quit"))
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -672,7 +650,7 @@ impl MainMenuUi {
.up_from(self.ids.quit_button, 8.0)
//.hover_image(self.imgs.button_hover)
//.press_image(self.imgs.button_press)
.label("Settings")
.label(&localized_strings.get("common.settings"))
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR_2)
.label_font_size(20)
@ -689,7 +667,7 @@ impl MainMenuUi {
.up_from(self.ids.settings_button, 8.0)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.label("Servers")
.label(&localized_strings.get("common.servers"))
.label_font_id(self.fonts.cyri)
.label_color(TEXT_COLOR)
.label_font_size(20)
@ -705,10 +683,10 @@ impl MainMenuUi {
events
}
pub fn show_info(&mut self, msg: String) {
pub fn show_info(&mut self, msg: String, button_text: String) {
self.popup = Some(PopupData {
msg,
button_text: "Okay".to_string(),
button_text: button_text,
popup_type: PopupType::Error,
});
self.connecting = None;

View File

@ -1,3 +1,4 @@
use crate::i18n::{i18n_asset_key, VoxygenLocalization};
use crate::{
ecs::MyEntity,
hud::{DebugInfo, Event as HudEvent, Hud},
@ -9,6 +10,8 @@ use crate::{
};
use client::{self, Client, Event::Chat};
use common::{
assets::load_watched,
assets::watch,
clock::Clock,
comp,
comp::{Pos, Vel},
@ -132,6 +135,14 @@ impl PlayState for SessionState {
}
}
// Keep a watcher on the language
let mut localization_watcher = watch::ReloadIndicator::new();
let mut localized_strings = load_watched::<VoxygenLocalization>(
&i18n_asset_key(&global_state.settings.language.selected_language),
&mut localization_watcher,
)
.unwrap();
// Game loop
let mut current_client_state = self.client.borrow().get_client_state();
while let ClientState::Pending | ClientState::Character = current_client_state {
@ -363,10 +374,7 @@ impl PlayState for SessionState {
// Perform an in-game tick.
if let Err(err) = self.tick(clock.get_avg_delta()) {
global_state.info_message = Some(
"Connection lost!\nDid the server restart?\nIs the client up to date?"
.to_owned(),
);
global_state.info_message = Some(localized_strings.get("common.connection_lost"));
error!("[session] Failed to tick the scene: {:?}", err);
return PlayStateResult::Pop;
@ -376,7 +384,7 @@ impl PlayState for SessionState {
global_state.maintain(clock.get_last_delta().as_secs_f32());
// Extract HUD events ensuring the client borrow gets dropped.
let hud_events = self.hud.maintain(
let mut hud_events = self.hud.maintain(
&self.client.borrow(),
global_state,
DebugInfo {
@ -407,6 +415,11 @@ impl PlayState for SessionState {
clock.get_last_delta(),
);
// Look for changes in the localization files
if localization_watcher.reloaded() {
hud_events.push(HudEvent::ChangeLanguage(localized_strings.metadata.clone()));
}
// Maintain the UI.
for event in hud_events {
match event {
@ -560,6 +573,16 @@ impl PlayState for SessionState {
global_state.settings.graphics.fluid_mode = new_fluid_mode;
global_state.settings.save_to_file_warn();
}
HudEvent::ChangeLanguage(new_language) => {
global_state.settings.language.selected_language =
new_language.language_identifier;
localized_strings = load_watched::<VoxygenLocalization>(
&i18n_asset_key(&global_state.settings.language.selected_language),
&mut localization_watcher,
)
.unwrap();
localized_strings.log_missing_entries();
}
}
}

View File

@ -1,6 +1,8 @@
use crate::{
hud::{BarNumbers, CrosshairType, Intro, ShortcutNumbers, XpBar},
render::{AaMode, CloudMode, FluidMode},
i18n,
render::AaMode,
render::{CloudMode, FluidMode},
ui::ScaleMode,
window::KeyMouse,
};
@ -234,6 +236,20 @@ impl Default for AudioSettings {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct LanguageSettings {
pub selected_language: String,
}
impl Default for LanguageSettings {
fn default() -> Self {
Self {
selected_language: i18n::REFERENCE_LANG.to_string(),
}
}
}
/// `Settings` contains everything that can be configured in the settings.ron file.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
@ -248,6 +264,7 @@ pub struct Settings {
pub send_logon_commands: bool,
// TODO: Remove at a later date, for dev testing
pub logon_commands: Vec<String>,
pub language: LanguageSettings,
}
impl Default for Settings {
@ -262,6 +279,7 @@ impl Default for Settings {
show_disclaimer: true,
send_logon_commands: false,
logon_commands: Vec::new(),
language: LanguageSettings::default(),
}
}
}