Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding localisation logic #49

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion crate/multiworld-gui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ use {
Kind as Frontend,
},
github::Repo,
localization::{
Locale,
Message::*,
},
ws::{
ServerError,
latest::{
Expand Down Expand Up @@ -380,6 +384,7 @@ enum Message {
SetExistingRoomSelection(RoomFormatter),
SetFrontend(Frontend),
SetLobbyView(LobbyView),
SetLocale(Locale),
SetNewRoomName(String),
SetPassword(String),
SetRoomView(RoomView),
Expand Down Expand Up @@ -438,6 +443,7 @@ struct State {
update_state: UpdateState,
send_all_path: String,
send_all_world: String,
locale: Locale,
}

impl State {
Expand Down Expand Up @@ -686,6 +692,7 @@ impl Application for State {
update_state: UpdateState::Pending,
send_all_path: String::default(),
send_all_world: String::default(),
locale: config.locale.unwrap_or_default(),
frontend, config_error, persistent_state_error, persistent_state,
}, cmd(future::ok(Message::CheckForUpdates)))
}
Expand Down Expand Up @@ -1418,6 +1425,15 @@ impl Application for State {
Message::SetCreateNewRoom(new_val) => if let SessionState::Lobby { ref mut create_new_room, .. } = self.server_connection { *create_new_room = new_val },
Message::SetExistingRoomSelection(name) => if let SessionState::Lobby { ref mut existing_room_selection, .. } = self.server_connection { *existing_room_selection = Some(name) },
Message::SetFrontend(new_frontend) => self.frontend.kind = new_frontend,
Message::SetLocale(new_locale) => {
self.locale = new_locale;
return cmd(async move {
let mut config = Config::load().await?;
config.locale = Some(new_locale);
config.save().await?;
Ok(Message::Nop)
})
},
Message::SetNewRoomName(name) => if let SessionState::Lobby { ref mut new_room_name, .. } = self.server_connection { *new_room_name = name },
Message::SetPassword(new_password) => if let SessionState::Lobby { ref mut password, .. } = self.server_connection { *password = new_password },
Message::SetSendAllPath(new_path) => self.send_all_path = new_path,
Expand Down Expand Up @@ -1535,7 +1551,7 @@ impl Application for State {
col = col.push(
Row::new()
.push("1. ")
.push(Button::new("Open Project64").on_press(Message::LaunchProject64))
.push(Button::new(Text::new(self.locale.message(OpenPj64Button))).on_press(Message::LaunchProject64))
.align_items(iced::Alignment::Center));
col = col.push("2. In Project64's Debugger menu, select Scripts\n3. In the Scripts window, select ootrmw.js and click Run\n4. Wait until the Output area says “Connected to multiworld app”. (This should take less than 5 seconds.) You can then close the Scripts window.")
} else {
Expand Down Expand Up @@ -1647,6 +1663,7 @@ impl Application for State {
.push(Button::new("Sign in with racetime.gg").on_press(Message::SetLobbyView(LobbyView::Login { provider: login::Provider::RaceTime, no_midos_house_account: false })))
.push(Button::new("Sign in with Discord").on_press(Message::SetLobbyView(LobbyView::Login { provider: login::Provider::Discord, no_midos_house_account: false })));
}
col = col.push(PickList::new(all::<Locale>().collect::<Vec<_>>(), Some(self.locale), Message::SetLocale)).into();
col.spacing(8)
}
SessionState::Lobby { view: LobbyView::Login { provider, no_midos_house_account: true }, wrong_password: false, .. } => Column::new()
Expand Down
66 changes: 54 additions & 12 deletions crate/multiworld-installer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ use {
config::Config,
frontend::Kind as Emulator, //TODO rename to Frontend?
github::Repo,
localization::{
Locale,
Message::*,
},
},
};
#[cfg(target_os = "linux")] use {
Expand Down Expand Up @@ -192,6 +196,7 @@ enum Message {
SetEmulator(Emulator),
SetInstallEmulator(bool),
SetOpenEmulator(bool),
SetLocale(Locale),
}

fn cmd(future: impl Future<Output = Result<Message, Error>> + Send + 'static) -> Command<Message> {
Expand Down Expand Up @@ -232,6 +237,12 @@ struct Pj64ConfigDebugger {
enum Page {
Error(Arc<Error>, bool),
Elevated,
SelectLocale {
emulator: Option<Emulator>,
install_emulator: Option<bool>,
emulator_path: Option<String>,
multiworld_path: Option<String>,
},
SelectEmulator {
emulator: Option<Emulator>,
install_emulator: Option<bool>,
Expand Down Expand Up @@ -291,6 +302,7 @@ struct State {
create_emulator_desktop_shortcut: bool,
// Page::AskLaunch
open_emulator: bool,
locale: Locale,
}

impl Application for State {
Expand All @@ -299,25 +311,25 @@ impl Application for State {
type Theme = Theme;
type Flags = Args;

fn new(Args { mut emulator }: Args) -> (Self, Command<Message>) {
fn new(Args { mut emulator , locale}: Args) -> (Self, Command<Message>) {
if let Ok(only_emulator) = all().filter(Emulator::is_supported).exactly_one() {
emulator.get_or_insert(only_emulator);
}
let page = match emulator {
Some(_) => Page::SelectEmulator { emulator, install_emulator: None, emulator_path: None, multiworld_path: None },
None => Page::SelectLocale { emulator, install_emulator: None, emulator_path: None, multiworld_path: None }
};
(Self {
http_client: reqwest::Client::builder()
.user_agent(concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")))
.use_rustls_tls()
.https_only(true)
.build().expect("failed to build HTTP client"),
page: Page::SelectEmulator {
install_emulator: None,
emulator_path: None,
multiworld_path: None,
emulator,
},
page,
create_multiworld_desktop_shortcut: true,
create_emulator_desktop_shortcut: true,
open_emulator: true,
locale: locale.unwrap_or_default(),
}, if emulator.is_some() {
cmd(future::ok(Message::Continue))
} else {
Expand Down Expand Up @@ -348,7 +360,8 @@ impl Application for State {
fn update(&mut self, msg: Message) -> Command<Message> {
match msg {
Message::Back => self.page = match self.page {
Page::Error(_, _) | Page::Elevated | Page::SelectEmulator { .. } => unreachable!(),
Page::Error(_, _) | Page::Elevated | Page::SelectLocale { .. } => unreachable!(),
Page::SelectEmulator { emulator, install_emulator, ref emulator_path, ref multiworld_path } => Page::SelectLocale { emulator, install_emulator, emulator_path: emulator_path.clone(), multiworld_path: multiworld_path.clone() },
Page::EmulatorWarning { emulator, install_emulator, ref emulator_path, ref multiworld_path } => Page::SelectEmulator { emulator: Some(emulator), install_emulator, emulator_path: emulator_path.clone(), multiworld_path: multiworld_path.clone() },
Page::LocateEmulator { emulator, install_emulator, ref emulator_path, ref multiworld_path } => Page::SelectEmulator { emulator: Some(emulator), install_emulator: Some(install_emulator), emulator_path: Some(emulator_path.clone()), multiworld_path: multiworld_path.clone() },
Page::AskBizHawkUpdate { ref emulator_path, ref multiworld_path } => Page::LocateEmulator { emulator: Emulator::BizHawk, install_emulator: false, emulator_path: emulator_path.clone(), multiworld_path: multiworld_path.clone() },
Expand Down Expand Up @@ -405,6 +418,9 @@ impl Application for State {
Message::ConfigWriteFailed => if let Page::InstallMultiworld { ref mut config_write_failed, .. } = self.page { *config_write_failed = true },
Message::Continue => match self.page {
Page::Error(_, _) | Page::Elevated | Page::Project64EmError { .. } => unreachable!(),
Page::SelectLocale { emulator, install_emulator, ref emulator_path, ref multiworld_path } => {
self.page = Page::SelectEmulator { emulator, install_emulator, emulator_path: emulator_path.clone(), multiworld_path: multiworld_path.clone() }
}
Page::SelectEmulator { emulator, install_emulator, ref emulator_path, ref multiworld_path } => {
let emulator = emulator.expect("emulator must be selected to continue here");
match emulator {
Expand All @@ -416,13 +432,18 @@ impl Application for State {
#[cfg(target_os = "windows")] Emulator::Pj64V3 | Emulator::Pj64V4 if !is_elevated() => {
// Project64 installation and plugin installation both require admin permissions (UAC)
self.page = Page::Elevated;
let locale = self.locale;
return cmd(async move {
let arg = match emulator {
let emulator_arg = match emulator {
Emulator::Pj64V3 => "--emulator=pj64v3",
Emulator::Pj64V4 => "--emulator=pj64v4",
_ => unreachable!(),
};
tokio::task::spawn_blocking(move || Ok::<_, Error>(runas::Command::new(env::current_exe()?).arg(arg).gui(true).status().at_command("runas")?.check("runas")?)).await??;
let locale_arg = match locale {
Locale::EN => "--locale=en",
Locale::FR => "--locale=fr",
};
tokio::task::spawn_blocking(move || Ok::<_, Error>(runas::Command::new(env::current_exe()?).arg(emulator_arg).arg(locale_arg).gui(true).status().at_command("runas")?.check("runas")?)).await??;
Ok(Message::Exit)
})
}
Expand Down Expand Up @@ -764,13 +785,15 @@ impl Application for State {
_ => unreachable!(),
};
self.page = Page::InstallMultiworld { emulator, emulator_path: emulator_path.clone(), multiworld_path: multiworld_path.clone(), config_write_failed: false };
let locale = self.locale;
match emulator {
Emulator::Dummy => unreachable!(),
Emulator::EverDrive => {
let create_desktop_shortcut = self.create_multiworld_desktop_shortcut;
return cmd(async move {
let mut new_mw_config = Config::load().await?;
new_mw_config.default_frontend = Some(Emulator::EverDrive);
new_mw_config.locale = Some(locale);
new_mw_config.save().await?;
let multiworld_path = PathBuf::from(multiworld_path.expect("multiworld app path must be set for Project64"));
fs::create_dir_all(multiworld_path.parent().ok_or(Error::Root)?).await?;
Expand All @@ -794,6 +817,7 @@ impl Application for State {
Emulator::BizHawk => return cmd(async move {
let mut new_mw_config = Config::load().await?;
new_mw_config.default_frontend = Some(Emulator::BizHawk);
new_mw_config.locale = Some(locale);
new_mw_config.save().await?;
let emulator_dir = PathBuf::from(emulator_path.expect("emulator path must be set for BizHawk"));
let external_tools_dir = emulator_dir.join("ExternalTools");
Expand Down Expand Up @@ -849,6 +873,7 @@ impl Application for State {
let mut new_mw_config = Config::load().await?;
new_mw_config.default_frontend = Some(Emulator::Pj64V3);
new_mw_config.pj64_script_path = Some(script_path);
new_mw_config.locale = Some(locale);
new_mw_config.save().await?;
let config_path = emulator_dir.join("Config");
fs::create_dir(&config_path).await.exist_ok()?;
Expand Down Expand Up @@ -938,6 +963,7 @@ impl Application for State {
Message::SetCreateEmulatorDesktopShortcut(create_desktop_shortcut) => self.create_emulator_desktop_shortcut = create_desktop_shortcut,
Message::SetCreateMultiworldDesktopShortcut(create_desktop_shortcut) => self.create_multiworld_desktop_shortcut = create_desktop_shortcut,
Message::SetEmulator(new_emulator) => if let Page::SelectEmulator { ref mut emulator, .. } = self.page { *emulator = Some(new_emulator) },
Message::SetLocale(new_locale) => self.locale = new_locale,
Message::SetInstallEmulator(new_install_emulator) => if let Page::LocateEmulator { ref mut install_emulator, .. } = self.page { *install_emulator = new_install_emulator },
Message::SetOpenEmulator(open_emulator) => self.open_emulator = open_emulator,
}
Expand Down Expand Up @@ -975,10 +1001,24 @@ impl Application for State {
None,
),
Page::Elevated => (
Text::new("The installer has been reopened with admin permissions. Please continue there.").into(),
Text::new(self.locale.message(InstallerReopenUAC)).into(),
false,
None,
),
Page::SelectLocale { .. } => (
{
let mut col = Column::new();
col = col.push("Select language");
col = col.push(PickList::new(all::<Locale>().collect::<Vec<_>>(), Some(self.locale), Message::SetLocale));
col.spacing(8).into()
},
false,
Some({
let mut row = Row::new();
row = row.push(Text::new("Continue"));
(Into::<Element<'_, Message>>::into(row.spacing(8)), true)
})
),
Page::SelectEmulator { emulator, .. } => (
{
let mut col = Column::new();
Expand All @@ -991,7 +1031,7 @@ impl Application for State {
col = col.push(Button::new(Text::new("See platform support status")).on_press(Message::PlatformSupport));
col.spacing(8).into()
},
false,
true,
Some({
let mut row = Row::new();
#[cfg(target_os = "windows")] if matches!(emulator, Some(Emulator::Pj64V3 | Emulator::Pj64V4)) && !is_elevated() {
Expand Down Expand Up @@ -1170,6 +1210,8 @@ impl Application for State {
struct Args {
#[clap(long, value_enum)]
emulator: Option<Emulator>,
#[clap(long, value_enum)]
locale: Option<Locale>,
}

#[derive(Debug, thiserror::Error)]
Expand Down
7 changes: 6 additions & 1 deletion crate/multiworld/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use {
Serialize,
},
url::Url,
crate::frontend::Kind as Frontend,
crate::{
frontend::Kind as Frontend,
localization::Locale,
},
};
#[cfg(unix)] use xdg::BaseDirectories;
#[cfg(windows)] use directories::ProjectDirs;
Expand All @@ -27,6 +30,7 @@ pub struct Config {
#[serde(default)]
pub refresh_tokens: BTreeMap<crate::IdentityProvider, String>,
pub pj64_script_path: Option<PathBuf>,
pub locale: Option<Locale>,
#[serde(default = "default_websocket_hostname")]
pub websocket_hostname: String,
}
Expand Down Expand Up @@ -112,6 +116,7 @@ impl Default for Config {
refresh_tokens: BTreeMap::default(),
pj64_script_path: None,
websocket_hostname: default_websocket_hostname(),
locale: None,
}
}
}
1 change: 1 addition & 0 deletions crate/multiworld/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ use {
#[cfg(feature = "sqlx")] use sqlx::PgPool;

pub mod config;
pub mod localization;
pub mod frontend;
pub mod github;
pub mod ws;
Expand Down
58 changes: 58 additions & 0 deletions crate/multiworld/src/localization.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use {
enum_iterator::Sequence, serde::{
Deserialize,
Serialize,
}, std::{borrow::Cow, fmt}
};

#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, clap::ValueEnum, Sequence)]
#[clap(rename_all = "lower")]
pub enum Locale {
#[default]
EN,
FR,
}

impl Locale {

pub fn message(&self, message: Message) -> Cow<'static, str> {
match message {
Message::InstallerReopenUAC => { //used in installer
match self {
Locale::EN => Cow::Borrowed("The installer has been reopened with admin permissions. Please continue there."),
Locale::FR => Cow::Borrowed("L'installateur a été ré-ouvert avec les permissions administrateur. Veuillez continuer dans la nouvelle fenêtre."),
}
},
Message::OpenPj64Button => { // used in gui
match self {
Locale::EN => Cow::Borrowed("Open Project64"),
Locale::FR => Cow::Borrowed("Ouvrir Project64"),
}
},
// TODO find a way to translate formatted text somehow
//Message::AMessageWithParameter(my_int, my_string) => {
// match self {
// Locale::EN => format!("My integer is: {} and my string is: {}",my_int,my_string).as_str(),
// Locale::FR => format!("My integer is: {} and my string is: {}",my_int,my_string).as_str(),
// }
//}
}
}
}

impl fmt::Display for Locale {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
// add local formating for display
Self::EN => write!(f, "English"),
Self::FR => write!(f, "Français"),
}
}
}

pub enum Message {
InstallerReopenUAC,
OpenPj64Button,
//AMessageWithParameter(i32,String),
}

36 changes: 36 additions & 0 deletions ootr-multiworld.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "crate", "crate", "{188837CE-D73F-4BFE-A160-2790D33D7AE1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "multiworld-bizhawk", "multiworld-bizhawk", "{FA858C4F-74A6-4080-B07E-29A5EDD7C077}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OotrMultiworld", "OotrMultiworld", "{E035DE8F-0231-4398-8869-85DF5E7BBBB3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OotrMultiworld", "crate\multiworld-bizhawk\OotrMultiworld\src\OotrMultiworld.csproj", "{50118B58-42C6-451E-AD9D-79D1366FA4D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{50118B58-42C6-451E-AD9D-79D1366FA4D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50118B58-42C6-451E-AD9D-79D1366FA4D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50118B58-42C6-451E-AD9D-79D1366FA4D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50118B58-42C6-451E-AD9D-79D1366FA4D1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{FA858C4F-74A6-4080-B07E-29A5EDD7C077} = {188837CE-D73F-4BFE-A160-2790D33D7AE1}
{E035DE8F-0231-4398-8869-85DF5E7BBBB3} = {FA858C4F-74A6-4080-B07E-29A5EDD7C077}
{50118B58-42C6-451E-AD9D-79D1366FA4D1} = {E035DE8F-0231-4398-8869-85DF5E7BBBB3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6BF49C3B-4303-4D20-88FE-2515BB62EB21}
EndGlobalSection
EndGlobal