Add option to load English string as fallback if string missing

This commit is contained in:
Ada Lovegirls 2021-04-24 14:39:35 +00:00 committed by Marcel
parent c6205875ee
commit 01c30868eb
17 changed files with 267 additions and 103 deletions

View File

@ -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

View File

@ -101,6 +101,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",

View File

@ -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

View File

@ -797,7 +797,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;
@ -889,7 +889,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
@ -913,7 +913,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 {
@ -2438,7 +2438,7 @@ impl Hud {
client,
&self.imgs,
&self.fonts,
i18n,
&*i18n,
self.pulse,
&self.rot_imgs,
item_tooltip_manager,

View File

@ -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

View File

@ -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
}
}

View File

@ -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)

View File

@ -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)
impl Default for Language {
fn default() -> Self {
Self {
sub_directories: Vec::default(),
string_map: HashMap::default(),
vector_map: HashMap::default(),
..Default::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
);
}
}
}
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>);

View File

@ -40,13 +40,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.
@ -61,7 +61,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

View File

@ -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!");

View File

@ -166,7 +166,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(),

View File

@ -1,5 +1,5 @@
use crate::{
i18n::Localization,
i18n::{Localization, LocalizationHandle},
render::Renderer,
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) {

View File

@ -5,7 +5,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,
@ -18,7 +18,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 std::{fmt::Debug, sync::Arc};
use tokio::runtime;
@ -295,10 +295,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);
},

View File

@ -5,7 +5,7 @@ mod login;
mod servers;
use crate::{
i18n::{LanguageMetadata, Localization},
i18n::{LanguageMetadata, LocalizationHandle},
render::Renderer,
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<'a> 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<'a> 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<'a> 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()

View File

@ -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(),
));
}

View File

@ -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 {},
}

View File

@ -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,
}
}
}