mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add option to load English string as fallback if string missing
This commit is contained in:
parent
76daf7a395
commit
a4dc52eeb2
@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Buoyancy is calculated from the difference in density between an entity and surrounding fluid
|
||||
- Drag is now calculated based on physical properties
|
||||
- Terrain chunks are now deflate-compressed when sent over the network.
|
||||
- Missing translations can be displayed in English.
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -105,6 +105,8 @@
|
||||
"hud.settings.audio_device": "Audio Device",
|
||||
"hud.settings.reset_sound": "Reset to Defaults",
|
||||
|
||||
"hud.settings.english_fallback": "Display English for missing translations",
|
||||
|
||||
"hud.settings.awaitingkey": "Press a key...",
|
||||
"hud.settings.unbound": "None",
|
||||
"hud.settings.reset_keybinds": "Reset to Defaults",
|
||||
|
@ -26,6 +26,7 @@ lazy_static! {
|
||||
pub fn start_hot_reloading() { ASSETS.enhance_hot_reloading(); }
|
||||
|
||||
pub type AssetHandle<T> = assets_manager::Handle<'static, T>;
|
||||
pub type AssetGuard<T> = assets_manager::AssetGuard<'static, T>;
|
||||
pub type AssetDir<T> = assets_manager::DirReader<'static, T, source::FileSystem>;
|
||||
|
||||
/// The Asset trait, which is implemented by all structures that have their data
|
||||
|
@ -798,7 +798,7 @@ impl Hud {
|
||||
// Load item images.
|
||||
let item_imgs = ItemImgs::new(&mut ui, imgs.not_found);
|
||||
// Load fonts.
|
||||
let fonts = Fonts::load(&global_state.i18n.read().fonts, &mut ui)
|
||||
let fonts = Fonts::load(global_state.i18n.read().fonts(), &mut ui)
|
||||
.expect("Impossible to load fonts!");
|
||||
// Get the server name.
|
||||
let server = &client.server_info().name;
|
||||
@ -890,7 +890,7 @@ impl Hud {
|
||||
}
|
||||
|
||||
pub fn update_fonts(&mut self, i18n: &Localization) {
|
||||
self.fonts = Fonts::load(&i18n.fonts, &mut self.ui).expect("Impossible to load fonts!");
|
||||
self.fonts = Fonts::load(i18n.fonts(), &mut self.ui).expect("Impossible to load fonts!");
|
||||
}
|
||||
|
||||
#[allow(clippy::assign_op_pattern)] // TODO: Pending review in #587
|
||||
@ -914,7 +914,7 @@ impl Hud {
|
||||
// FPS
|
||||
let fps = global_state.clock.stats().average_tps;
|
||||
let version = common::util::DISPLAY_VERSION_LONG.clone();
|
||||
let i18n = &*global_state.i18n.read();
|
||||
let i18n = &global_state.i18n.read();
|
||||
let key_layout = &global_state.window.key_layout;
|
||||
|
||||
if self.show.ingame {
|
||||
@ -2466,7 +2466,7 @@ impl Hud {
|
||||
client,
|
||||
&self.imgs,
|
||||
&self.fonts,
|
||||
i18n,
|
||||
&*i18n,
|
||||
self.pulse,
|
||||
&self.rot_imgs,
|
||||
item_tooltip_manager,
|
||||
|
@ -1,11 +1,10 @@
|
||||
use super::{img_ids::Imgs, TEXT_COLOR, UI_HIGHLIGHT_0};
|
||||
use crate::{
|
||||
hud::{Event, PromptDialogSettings},
|
||||
i18n::Localization,
|
||||
i18n::LocalizationHandle,
|
||||
settings::Settings,
|
||||
ui::fonts::Fonts,
|
||||
window::GameInput,
|
||||
AssetHandle,
|
||||
};
|
||||
use conrod_core::{
|
||||
widget::{self, Button, Image, Text},
|
||||
@ -32,7 +31,7 @@ pub struct PromptDialog<'a> {
|
||||
fonts: &'a Fonts,
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
localized_strings: &'a AssetHandle<Localization>,
|
||||
localized_strings: &'a LocalizationHandle,
|
||||
settings: &'a Settings,
|
||||
prompt_dialog_settings: &'a PromptDialogSettings,
|
||||
key_layout: &'a Option<KeyLayout>,
|
||||
@ -43,7 +42,7 @@ impl<'a> PromptDialog<'a> {
|
||||
pub fn new(
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
localized_strings: &'a AssetHandle<Localization>,
|
||||
localized_strings: &'a LocalizationHandle,
|
||||
settings: &'a Settings,
|
||||
prompt_dialog_settings: &'a PromptDialogSettings,
|
||||
key_layout: &'a Option<KeyLayout>,
|
||||
@ -85,7 +84,7 @@ impl<'a> Widget for PromptDialog<'a> {
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
let _localized_strings = self.localized_strings;
|
||||
let _localized_strings = &self.localized_strings;
|
||||
let mut event: Option<DialogOutcomeEvent> = None;
|
||||
|
||||
let accept_key = self
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::{
|
||||
hud::{img_ids::Imgs, TEXT_COLOR},
|
||||
i18n::list_localizations,
|
||||
i18n::{list_localizations, Localization},
|
||||
session::settings_change::{Language as LanguageChange, Language::*},
|
||||
ui::fonts::Fonts,
|
||||
ui::{fonts::Fonts, ToggleButton},
|
||||
GlobalState,
|
||||
};
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Rectangle, Scrollbar},
|
||||
widget::{self, Button, Rectangle, Scrollbar, Text},
|
||||
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
@ -15,6 +15,8 @@ widget_ids! {
|
||||
struct Ids {
|
||||
window,
|
||||
window_r,
|
||||
english_fallback_button,
|
||||
english_fallback_button_label,
|
||||
window_scrollbar,
|
||||
language_list[],
|
||||
}
|
||||
@ -23,15 +25,22 @@ widget_ids! {
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Language<'a> {
|
||||
global_state: &'a GlobalState,
|
||||
localized_strings: &'a Localization,
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
impl<'a> Language<'a> {
|
||||
pub fn new(global_state: &'a GlobalState, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
pub fn new(
|
||||
global_state: &'a GlobalState,
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
localized_strings: &'a Localization,
|
||||
) -> Self {
|
||||
Self {
|
||||
global_state,
|
||||
localized_strings,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
@ -79,6 +88,7 @@ impl<'a> Widget for Language<'a> {
|
||||
|
||||
// List available languages
|
||||
let selected_language = &self.global_state.settings.language.selected_language;
|
||||
let english_fallback = self.global_state.settings.language.use_english_fallback;
|
||||
let language_list = list_localizations();
|
||||
if state.ids.language_list.len() < language_list.len() {
|
||||
state.update(|state| {
|
||||
@ -117,6 +127,36 @@ impl<'a> Widget for Language<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// English as fallback language
|
||||
let show_english_fallback = ToggleButton::new(
|
||||
english_fallback,
|
||||
self.imgs.checkbox,
|
||||
self.imgs.checkbox_checked,
|
||||
)
|
||||
.w_h(18.0, 18.0);
|
||||
let show_english_fallback = if let Some(id) = state.ids.language_list.last() {
|
||||
show_english_fallback.down_from(*id, 8.0)
|
||||
//mid_bottom_with_margin_on(id, -button_h)
|
||||
} else {
|
||||
show_english_fallback.mid_top_with_margin_on(state.ids.window, 20.0)
|
||||
};
|
||||
let show_english_fallback = show_english_fallback
|
||||
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||
.set(state.ids.english_fallback_button, ui);
|
||||
|
||||
if english_fallback != show_english_fallback {
|
||||
events.push(ToggleEnglishFallback(show_english_fallback));
|
||||
}
|
||||
|
||||
Text::new(&self.localized_strings.get("hud.settings.english_fallback"))
|
||||
.right_from(state.ids.english_fallback_button, 10.0)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.graphics_for(state.ids.english_fallback_button)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.english_fallback_button_label, ui);
|
||||
|
||||
events
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
||||
}
|
||||
},
|
||||
SettingsTab::Lang => {
|
||||
for change in language::Language::new(global_state, imgs, fonts)
|
||||
for change in language::Language::new(global_state, imgs, fonts, localized_strings)
|
||||
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
|
||||
.wh_of(state.ids.settings_content_align)
|
||||
.set(state.ids.language, ui)
|
||||
|
@ -1,4 +1,4 @@
|
||||
use common::assets::{self, AssetExt};
|
||||
use common::assets::{self, AssetExt, AssetGuard, AssetHandle};
|
||||
use deunicode::deunicode;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -45,7 +45,7 @@ pub type Fonts = HashMap<String, Font>;
|
||||
|
||||
/// Raw localization data, expect the strings to not be loaded here
|
||||
/// However, metadata informations are correct
|
||||
/// See `Localization` for more info on each attributes
|
||||
/// See `Language` for more info on each attributes
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
pub struct RawLocalization {
|
||||
pub sub_directories: Vec<String>,
|
||||
@ -58,7 +58,7 @@ pub struct RawLocalization {
|
||||
|
||||
/// Store internationalization data
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Localization {
|
||||
struct Language {
|
||||
/// A list of subdirectories to lookup for localization files
|
||||
pub sub_directories: Vec<String>,
|
||||
|
||||
@ -83,7 +83,7 @@ pub struct Localization {
|
||||
}
|
||||
|
||||
/// Store internationalization maps
|
||||
/// These structs are meant to be merged into a Localization
|
||||
/// These structs are meant to be merged into a Language
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct LocalizationFragment {
|
||||
/// A map storing the localized texts
|
||||
@ -97,16 +97,13 @@ pub struct LocalizationFragment {
|
||||
pub vector_map: HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
impl Localization {
|
||||
impl Language {
|
||||
/// 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<'a>(&'a self, key: &'a str) -> &str {
|
||||
match self.string_map.get(key) {
|
||||
Some(localized_text) => localized_text,
|
||||
None => key,
|
||||
}
|
||||
pub fn get<'a>(&'a self, key: &'a str) -> Option<&str> {
|
||||
self.string_map.get(key).map(|s| s.as_str())
|
||||
}
|
||||
|
||||
/// Get a variation of localized text from the given key
|
||||
@ -115,55 +112,32 @@ impl Localization {
|
||||
///
|
||||
/// If the key is not present in the localization object
|
||||
/// then the key is returned.
|
||||
pub fn get_variation<'a>(&'a self, key: &'a str, index: u16) -> &str {
|
||||
match self.vector_map.get(key) {
|
||||
Some(v) if !v.is_empty() => &v[index as usize % v.len()],
|
||||
_ => key,
|
||||
}
|
||||
pub fn get_variation<'a>(&'a self, key: &'a str, index: u16) -> Option<&str> {
|
||||
self.vector_map
|
||||
.get(key)
|
||||
.map(|v| {
|
||||
if !v.is_empty() {
|
||||
Some(v[index as usize % v.len()].as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the missing keys compared to the reference language
|
||||
fn list_missing_entries(&self) -> (HashSet<String>, HashSet<String>) {
|
||||
let reference_localization =
|
||||
Localization::load_expect(&i18n_asset_key(REFERENCE_LANG)).read();
|
||||
|
||||
let reference_string_keys: HashSet<_> =
|
||||
reference_localization.string_map.keys().cloned().collect();
|
||||
let string_keys: HashSet<_> = self.string_map.keys().cloned().collect();
|
||||
let strings = reference_string_keys
|
||||
.difference(&string_keys)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let reference_vector_keys: HashSet<_> =
|
||||
reference_localization.vector_map.keys().cloned().collect();
|
||||
let vector_keys: HashSet<_> = self.vector_map.keys().cloned().collect();
|
||||
let vectors = reference_vector_keys
|
||||
.difference(&vector_keys)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
(strings, vectors)
|
||||
}
|
||||
|
||||
/// Log missing entries (compared to the reference language) as warnings
|
||||
pub fn log_missing_entries(&self) {
|
||||
let (missing_strings, missing_vectors) = self.list_missing_entries();
|
||||
for missing_key in missing_strings {
|
||||
warn!(
|
||||
"[{:?}] Missing string key {:?}",
|
||||
self.metadata.language_identifier, missing_key
|
||||
);
|
||||
}
|
||||
for missing_key in missing_vectors {
|
||||
warn!(
|
||||
"[{:?}] Missing vector key {:?}",
|
||||
self.metadata.language_identifier, missing_key
|
||||
);
|
||||
impl Default for Language {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sub_directories: Vec::default(),
|
||||
string_map: HashMap::default(),
|
||||
vector_map: HashMap::default(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<RawLocalization> for Localization {
|
||||
|
||||
impl From<RawLocalization> for Language {
|
||||
fn from(raw: RawLocalization) -> Self {
|
||||
Self {
|
||||
sub_directories: raw.sub_directories,
|
||||
@ -195,7 +169,7 @@ impl assets::Asset for LocalizationFragment {
|
||||
const EXTENSION: &'static str = "ron";
|
||||
}
|
||||
|
||||
impl assets::Compound for Localization {
|
||||
impl assets::Compound for Language {
|
||||
fn load<S: assets::source::Source>(
|
||||
cache: &assets::AssetCache<S>,
|
||||
asset_key: &str,
|
||||
@ -203,7 +177,7 @@ impl assets::Compound for Localization {
|
||||
let raw = cache
|
||||
.load::<RawLocalization>(&[asset_key, ".", LANG_MANIFEST_FILE].concat())?
|
||||
.cloned();
|
||||
let mut localization = Localization::from(raw);
|
||||
let mut localization = Language::from(raw);
|
||||
|
||||
// Walk through files in the folder, collecting localization fragment to merge
|
||||
// inside the asked_localization
|
||||
@ -247,6 +221,139 @@ impl assets::Compound for Localization {
|
||||
}
|
||||
}
|
||||
|
||||
/// the central data structure to handle localization in veloren
|
||||
// inherit Copy+Clone from AssetHandle
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub struct LocalizationHandle {
|
||||
active: AssetHandle<Language>,
|
||||
fallback: Option<AssetHandle<Language>>,
|
||||
pub use_english_fallback: bool,
|
||||
}
|
||||
|
||||
// RAII guard returned from Localization::read(), resembles AssetGuard
|
||||
pub struct LocalizationGuard {
|
||||
active: AssetGuard<Language>,
|
||||
fallback: Option<AssetGuard<Language>>,
|
||||
}
|
||||
|
||||
// arbitrary choice to minimize changing all of veloren
|
||||
pub type Localization = LocalizationGuard;
|
||||
|
||||
impl LocalizationGuard {
|
||||
/// 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<'a>(&'a self, key: &'a str) -> &str {
|
||||
self.active.get(key).unwrap_or_else(|| {
|
||||
self.fallback
|
||||
.as_ref()
|
||||
.map(|f| f.get(key))
|
||||
.flatten()
|
||||
.unwrap_or(key)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a variation of localized text from the given key
|
||||
///
|
||||
/// `index` should be a random number from `0` to `u16::max()`
|
||||
///
|
||||
/// If the key is not present in the localization object
|
||||
/// then the key is returned.
|
||||
pub fn get_variation<'a>(&'a self, key: &'a str, index: u16) -> &str {
|
||||
self.active.get_variation(key, index).unwrap_or_else(|| {
|
||||
self.fallback
|
||||
.as_ref()
|
||||
.map(|f| f.get_variation(key, index))
|
||||
.flatten()
|
||||
.unwrap_or(key)
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the missing keys compared to the reference language
|
||||
fn list_missing_entries(&self) -> (HashSet<String>, HashSet<String>) {
|
||||
if let Some(ref_lang) = &self.fallback {
|
||||
let reference_string_keys: HashSet<_> = ref_lang.string_map.keys().cloned().collect();
|
||||
let string_keys: HashSet<_> = self.active.string_map.keys().cloned().collect();
|
||||
let strings = reference_string_keys
|
||||
.difference(&string_keys)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let reference_vector_keys: HashSet<_> = ref_lang.vector_map.keys().cloned().collect();
|
||||
let vector_keys: HashSet<_> = self.active.vector_map.keys().cloned().collect();
|
||||
let vectors = reference_vector_keys
|
||||
.difference(&vector_keys)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
(strings, vectors)
|
||||
} else {
|
||||
(HashSet::default(), HashSet::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Log missing entries (compared to the reference language) as warnings
|
||||
pub fn log_missing_entries(&self) {
|
||||
let (missing_strings, missing_vectors) = self.list_missing_entries();
|
||||
for missing_key in missing_strings {
|
||||
warn!(
|
||||
"[{:?}] Missing string key {:?}",
|
||||
self.metadata().language_identifier,
|
||||
missing_key
|
||||
);
|
||||
}
|
||||
for missing_key in missing_vectors {
|
||||
warn!(
|
||||
"[{:?}] Missing vector key {:?}",
|
||||
self.metadata().language_identifier,
|
||||
missing_key
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fonts(&self) -> &Fonts { &self.active.fonts }
|
||||
|
||||
pub fn metadata(&self) -> &LanguageMetadata { &self.active.metadata }
|
||||
}
|
||||
|
||||
impl LocalizationHandle {
|
||||
pub fn set_english_fallback(&mut self, use_english_fallback: bool) {
|
||||
self.use_english_fallback = use_english_fallback;
|
||||
}
|
||||
|
||||
pub fn read(&self) -> LocalizationGuard {
|
||||
LocalizationGuard {
|
||||
active: self.active.read(),
|
||||
fallback: if self.use_english_fallback {
|
||||
self.fallback.map(|f| f.read())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(specifier: &str) -> Result<Self, common::assets::Error> {
|
||||
let default_key = i18n_asset_key(REFERENCE_LANG);
|
||||
let is_default = specifier == default_key;
|
||||
Ok(Self {
|
||||
active: Language::load(specifier)?,
|
||||
fallback: if is_default {
|
||||
None
|
||||
} else {
|
||||
Language::load(&default_key).ok()
|
||||
},
|
||||
use_english_fallback: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_expect(specifier: &str) -> Self {
|
||||
Self::load(specifier).expect("Can't load language files")
|
||||
}
|
||||
|
||||
pub fn reloaded(&mut self) -> bool { self.active.reloaded() }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct LocalizationList(Vec<LanguageMetadata>);
|
||||
|
||||
|
@ -41,13 +41,13 @@ pub use crate::error::Error;
|
||||
use crate::singleplayer::Singleplayer;
|
||||
use crate::{
|
||||
audio::AudioFrontend,
|
||||
i18n::Localization,
|
||||
i18n::LocalizationHandle,
|
||||
profile::Profile,
|
||||
render::Renderer,
|
||||
settings::Settings,
|
||||
window::{Event, Window},
|
||||
};
|
||||
use common::{assets::AssetHandle, clock::Clock};
|
||||
use common::clock::Clock;
|
||||
use common_base::span;
|
||||
|
||||
/// A type used to store state that is shared between all play states.
|
||||
@ -62,7 +62,7 @@ pub struct GlobalState {
|
||||
#[cfg(feature = "singleplayer")]
|
||||
pub singleplayer: Option<Singleplayer>,
|
||||
// TODO: redo this so that the watcher doesn't have to exist for reloading to occur
|
||||
pub i18n: AssetHandle<Localization>,
|
||||
pub i18n: LocalizationHandle,
|
||||
pub clipboard: Option<iced_winit::Clipboard>,
|
||||
// NOTE: This can be removed from GlobalState if client state behavior is refactored to not
|
||||
// enter the game before confirmation of successful character load
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
use veloren_voxygen::{
|
||||
audio::AudioFrontend,
|
||||
i18n::{self, i18n_asset_key, Localization},
|
||||
i18n::{self, i18n_asset_key, LocalizationHandle},
|
||||
profile::Profile,
|
||||
run,
|
||||
scene::terrain::SpriteRenderContext,
|
||||
@ -15,7 +15,7 @@ use veloren_voxygen::{
|
||||
};
|
||||
|
||||
use common::{
|
||||
assets::{self, AssetExt},
|
||||
assets::{self},
|
||||
clock::Clock,
|
||||
};
|
||||
use std::panic;
|
||||
@ -163,7 +163,7 @@ fn main() {
|
||||
// Load the profile.
|
||||
let profile = Profile::load();
|
||||
|
||||
let i18n = Localization::load(&i18n_asset_key(&settings.language.selected_language))
|
||||
let mut i18n = LocalizationHandle::load(&i18n_asset_key(&settings.language.selected_language))
|
||||
.unwrap_or_else(|error| {
|
||||
let selected_language = &settings.language.selected_language;
|
||||
warn!(
|
||||
@ -172,9 +172,10 @@ fn main() {
|
||||
"Impossible to load language: change to the default language (English) instead.",
|
||||
);
|
||||
settings.language.selected_language = i18n::REFERENCE_LANG.to_owned();
|
||||
Localization::load_expect(&i18n_asset_key(&settings.language.selected_language))
|
||||
LocalizationHandle::load_expect(&i18n_asset_key(&settings.language.selected_language))
|
||||
});
|
||||
i18n.read().log_missing_entries();
|
||||
i18n.set_english_fallback(settings.language.use_english_fallback);
|
||||
|
||||
// Create window
|
||||
let (mut window, event_loop) = Window::new(&settings).expect("Failed to create window!");
|
||||
|
@ -171,7 +171,7 @@ impl PlayState for CharSelectionState {
|
||||
}
|
||||
|
||||
// Tick the client (currently only to keep the connection alive).
|
||||
let localized_strings = &*global_state.i18n.read();
|
||||
let localized_strings = &global_state.i18n.read();
|
||||
|
||||
match self.client.borrow_mut().tick(
|
||||
comp::ControllerInputs::default(),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
i18n::Localization,
|
||||
i18n::{Localization, LocalizationHandle},
|
||||
render::UiDrawer,
|
||||
ui::{
|
||||
self,
|
||||
@ -22,7 +22,6 @@ use crate::{
|
||||
};
|
||||
use client::{Client, ServerInfo};
|
||||
use common::{
|
||||
assets::AssetHandle,
|
||||
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER, MAX_NAME_LENGTH},
|
||||
comp::{self, humanoid, inventory::slot::EquipSlot, Inventory, Item},
|
||||
LoadoutBuilder,
|
||||
@ -1467,7 +1466,7 @@ impl CharSelectionUi {
|
||||
let i18n = global_state.i18n.read();
|
||||
|
||||
// TODO: don't add default font twice
|
||||
let font = ui::ice::load_font(&i18n.fonts.get("cyri").unwrap().asset_key);
|
||||
let font = ui::ice::load_font(&i18n.fonts().get("cyri").unwrap().asset_key);
|
||||
|
||||
let mut ui = Ui::new(
|
||||
&mut global_state.window,
|
||||
@ -1476,7 +1475,7 @@ impl CharSelectionUi {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let fonts = Fonts::load(&i18n.fonts, &mut ui).expect("Impossible to load fonts");
|
||||
let fonts = Fonts::load(i18n.fonts(), &mut ui).expect("Impossible to load fonts");
|
||||
|
||||
#[cfg(feature = "singleplayer")]
|
||||
let default_name = match global_state.singleplayer {
|
||||
@ -1538,13 +1537,13 @@ impl CharSelectionUi {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_language(&mut self, i18n: AssetHandle<Localization>) {
|
||||
pub fn update_language(&mut self, i18n: LocalizationHandle) {
|
||||
let i18n = i18n.read();
|
||||
let font = ui::ice::load_font(&i18n.fonts.get("cyri").unwrap().asset_key);
|
||||
let font = ui::ice::load_font(&i18n.fonts().get("cyri").unwrap().asset_key);
|
||||
|
||||
self.ui.clear_fonts(font);
|
||||
self.controls.fonts =
|
||||
Fonts::load(&i18n.fonts, &mut self.ui).expect("Impossible to load fonts!");
|
||||
Fonts::load(i18n.fonts(), &mut self.ui).expect("Impossible to load fonts!");
|
||||
}
|
||||
|
||||
pub fn set_scale_mode(&mut self, scale_mode: ui::ScaleMode) {
|
||||
|
@ -6,7 +6,7 @@ use super::char_selection::CharSelectionState;
|
||||
#[cfg(feature = "singleplayer")]
|
||||
use crate::singleplayer::Singleplayer;
|
||||
use crate::{
|
||||
i18n::{i18n_asset_key, Localization},
|
||||
i18n::{i18n_asset_key, Localization, LocalizationHandle},
|
||||
render::Renderer,
|
||||
settings::Settings,
|
||||
window::Event,
|
||||
@ -19,7 +19,7 @@ use client::{
|
||||
ServerInfo,
|
||||
};
|
||||
use client_init::{ClientConnArgs, ClientInit, Error as InitError, Msg as InitMsg};
|
||||
use common::{assets::AssetExt, comp};
|
||||
use common::comp;
|
||||
use common_base::span;
|
||||
use scene::Scene;
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
@ -299,10 +299,13 @@ impl PlayState for MainMenuState {
|
||||
MainMenuEvent::ChangeLanguage(new_language) => {
|
||||
global_state.settings.language.selected_language =
|
||||
new_language.language_identifier;
|
||||
global_state.i18n = Localization::load_expect(&i18n_asset_key(
|
||||
global_state.i18n = LocalizationHandle::load_expect(&i18n_asset_key(
|
||||
&global_state.settings.language.selected_language,
|
||||
));
|
||||
global_state.i18n.read().log_missing_entries();
|
||||
global_state
|
||||
.i18n
|
||||
.set_english_fallback(global_state.settings.language.use_english_fallback);
|
||||
self.main_menu_ui
|
||||
.update_language(global_state.i18n, &global_state.settings);
|
||||
},
|
||||
|
@ -5,7 +5,7 @@ mod login;
|
||||
mod servers;
|
||||
|
||||
use crate::{
|
||||
i18n::{LanguageMetadata, Localization},
|
||||
i18n::{LanguageMetadata, LocalizationHandle},
|
||||
render::UiDrawer,
|
||||
ui::{
|
||||
self,
|
||||
@ -19,7 +19,7 @@ use crate::{
|
||||
use iced::{text_input, Column, Container, HorizontalAlignment, Length, Row, Space};
|
||||
//ImageFrame, Tooltip,
|
||||
use crate::settings::Settings;
|
||||
use common::assets::{self, AssetExt, AssetHandle};
|
||||
use common::assets::{self, AssetExt};
|
||||
use rand::{seq::SliceRandom, thread_rng};
|
||||
use std::time::Duration;
|
||||
|
||||
@ -124,7 +124,7 @@ struct Controls {
|
||||
fonts: Fonts,
|
||||
imgs: Imgs,
|
||||
bg_img: widget::image::Handle,
|
||||
i18n: AssetHandle<Localization>,
|
||||
i18n: LocalizationHandle,
|
||||
// Voxygen version
|
||||
version: String,
|
||||
// Alpha disclaimer
|
||||
@ -169,7 +169,7 @@ impl Controls {
|
||||
fonts: Fonts,
|
||||
imgs: Imgs,
|
||||
bg_img: widget::image::Handle,
|
||||
i18n: AssetHandle<Localization>,
|
||||
i18n: LocalizationHandle,
|
||||
settings: &Settings,
|
||||
) -> Self {
|
||||
let version = common::util::DISPLAY_VERSION_LONG.clone();
|
||||
@ -480,9 +480,9 @@ pub struct MainMenuUi {
|
||||
impl MainMenuUi {
|
||||
pub fn new(global_state: &mut GlobalState) -> Self {
|
||||
// Load language
|
||||
let i18n = &*global_state.i18n.read();
|
||||
let i18n = &global_state.i18n.read();
|
||||
// TODO: don't add default font twice
|
||||
let font = load_font(&i18n.fonts.get("cyri").unwrap().asset_key);
|
||||
let font = load_font(&i18n.fonts().get("cyri").unwrap().asset_key);
|
||||
|
||||
let mut ui = Ui::new(
|
||||
&mut global_state.window,
|
||||
@ -491,7 +491,7 @@ impl MainMenuUi {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let fonts = Fonts::load(&i18n.fonts, &mut ui).expect("Impossible to load fonts");
|
||||
let fonts = Fonts::load(&i18n.fonts(), &mut ui).expect("Impossible to load fonts");
|
||||
|
||||
let bg_img_spec = BG_IMGS.choose(&mut thread_rng()).unwrap();
|
||||
|
||||
@ -507,13 +507,13 @@ impl MainMenuUi {
|
||||
Self { ui, controls }
|
||||
}
|
||||
|
||||
pub fn update_language(&mut self, i18n: AssetHandle<Localization>, settings: &Settings) {
|
||||
pub fn update_language(&mut self, i18n: LocalizationHandle, settings: &Settings) {
|
||||
self.controls.i18n = i18n;
|
||||
let i18n = &*i18n.read();
|
||||
let font = load_font(&i18n.fonts.get("cyri").unwrap().asset_key);
|
||||
let i18n = &i18n.read();
|
||||
let font = load_font(&i18n.fonts().get("cyri").unwrap().asset_key);
|
||||
self.ui.clear_fonts(font);
|
||||
self.controls.fonts =
|
||||
Fonts::load(&i18n.fonts, &mut self.ui).expect("Impossible to load fonts!");
|
||||
Fonts::load(&i18n.fonts(), &mut self.ui).expect("Impossible to load fonts!");
|
||||
let language_metadatas = crate::i18n::list_localizations();
|
||||
self.controls.selected_language_index = language_metadatas
|
||||
.iter()
|
||||
|
@ -914,7 +914,7 @@ impl PlayState for SessionState {
|
||||
// Look for changes in the localization files
|
||||
if global_state.i18n.reloaded() {
|
||||
hud_events.push(HudEvent::SettingsChange(
|
||||
ChangeLanguage(Box::new(global_state.i18n.read().metadata.clone())).into(),
|
||||
ChangeLanguage(Box::new(global_state.i18n.read().metadata().clone())).into(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
BarNumbers, BuffPosition, CrosshairType, Intro, PressBehavior, ScaleChange,
|
||||
ShortcutNumbers, XpBar,
|
||||
},
|
||||
i18n::{i18n_asset_key, LanguageMetadata, Localization},
|
||||
i18n::{i18n_asset_key, LanguageMetadata, LocalizationHandle},
|
||||
render::RenderMode,
|
||||
settings::{
|
||||
AudioSettings, ControlSettings, Fps, GamepadSettings, GameplaySettings, GraphicsSettings,
|
||||
@ -14,7 +14,6 @@ use crate::{
|
||||
window::{FullScreenSettings, GameInput},
|
||||
GlobalState,
|
||||
};
|
||||
use common::assets::AssetExt;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -118,6 +117,7 @@ pub enum Interface {
|
||||
#[derive(Clone)]
|
||||
pub enum Language {
|
||||
ChangeLanguage(Box<LanguageMetadata>),
|
||||
ToggleEnglishFallback(bool),
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub enum Networking {}
|
||||
@ -470,12 +470,21 @@ impl SettingsChange {
|
||||
SettingsChange::Language(language_change) => match language_change {
|
||||
Language::ChangeLanguage(new_language) => {
|
||||
settings.language.selected_language = new_language.language_identifier;
|
||||
global_state.i18n = Localization::load_expect(&i18n_asset_key(
|
||||
global_state.i18n = LocalizationHandle::load_expect(&i18n_asset_key(
|
||||
&settings.language.selected_language,
|
||||
));
|
||||
global_state.i18n.read().log_missing_entries();
|
||||
global_state
|
||||
.i18n
|
||||
.set_english_fallback(settings.language.use_english_fallback);
|
||||
session_state.hud.update_fonts(&global_state.i18n.read());
|
||||
},
|
||||
Language::ToggleEnglishFallback(toggle_fallback) => {
|
||||
settings.language.use_english_fallback = toggle_fallback;
|
||||
global_state
|
||||
.i18n
|
||||
.set_english_fallback(settings.language.use_english_fallback);
|
||||
},
|
||||
},
|
||||
SettingsChange::Networking(networking_change) => match networking_change {},
|
||||
}
|
||||
|
@ -5,12 +5,14 @@ use serde::{Deserialize, Serialize};
|
||||
#[serde(default)]
|
||||
pub struct LanguageSettings {
|
||||
pub selected_language: String,
|
||||
pub use_english_fallback: bool,
|
||||
}
|
||||
|
||||
impl Default for LanguageSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
selected_language: i18n::REFERENCE_LANG.to_string(),
|
||||
use_english_fallback: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user