Add language switching, fix logout
This commit is contained in:
+20
-34
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use fluent::{FluentArgs, FluentBundle, FluentResource};
|
||||
use seed::log;
|
||||
use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
|
||||
use shared::datatypes::Lang;
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
// A struct containing the functions and the current language to query the localized strings.
|
||||
@@ -22,10 +22,8 @@ impl I18n {
|
||||
/// Create a new translator struct
|
||||
#[must_use]
|
||||
pub fn new(lang: Lang) -> Self {
|
||||
Self {
|
||||
lang,
|
||||
ftl_bundle: Arc::new(lang.create_ftl_bundle()),
|
||||
}
|
||||
let ftl_bundle = Arc::new(Self::create_ftl_bundle(lang));
|
||||
Self { lang, ftl_bundle }
|
||||
}
|
||||
|
||||
/// Get the current language
|
||||
@@ -35,10 +33,9 @@ impl I18n {
|
||||
}
|
||||
|
||||
/// Set the current language
|
||||
pub fn set_lang(&mut self, lang: Lang) -> &Self {
|
||||
pub fn set_lang(&mut self, lang: Lang) {
|
||||
self.lang = lang;
|
||||
self.ftl_bundle = Arc::new(lang.create_ftl_bundle());
|
||||
self
|
||||
self.ftl_bundle = Arc::new(Self::create_ftl_bundle(lang));
|
||||
}
|
||||
|
||||
/// Get a localized string. Optionally with parameters provided in `args`.
|
||||
@@ -57,55 +54,44 @@ impl I18n {
|
||||
}
|
||||
}
|
||||
|
||||
/// An `enum` containing the available languages.
|
||||
/// To add an additional language add it to this enum aswell as an appropriate file into the locales folder.
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug, Copy, Clone, Display, EnumIter, EnumString, AsRefStr, Eq, PartialEq)]
|
||||
pub enum Lang {
|
||||
#[strum(serialize = "en-US")]
|
||||
EnUS,
|
||||
#[strum(serialize = "de-DE")]
|
||||
DeDE,
|
||||
}
|
||||
|
||||
impl Lang {
|
||||
impl I18n {
|
||||
/// Prettyprint the language name
|
||||
#[must_use]
|
||||
pub const fn label(self) -> &'static str {
|
||||
match self {
|
||||
Self::EnUS => "English (US)",
|
||||
Self::DeDE => "Deutsch (Deutschland)",
|
||||
pub const fn label(&self) -> &'static str {
|
||||
match self.lang {
|
||||
Lang::EnUS => "English (US)",
|
||||
Lang::DeDE => "Deutsch (Deutschland)",
|
||||
}
|
||||
}
|
||||
|
||||
/// include the fluent messages into the binary
|
||||
#[must_use]
|
||||
pub const fn ftl_messages(self) -> &'static str {
|
||||
pub const fn ftl_messages(lang: Lang) -> &'static str {
|
||||
macro_rules! include_ftl_messages {
|
||||
( $lang_id:literal ) => {
|
||||
include_str!(concat!("../../locales/", $lang_id, "/main.ftl"))
|
||||
};
|
||||
}
|
||||
match self {
|
||||
Self::EnUS => include_ftl_messages!("en"),
|
||||
Self::DeDE => include_ftl_messages!("de"),
|
||||
match lang {
|
||||
Lang::EnUS => include_ftl_messages!("en"),
|
||||
Lang::DeDE => include_ftl_messages!("de"),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_language_identifier(self) -> LanguageIdentifier {
|
||||
self.as_ref()
|
||||
pub fn language_identifier(lang: Lang) -> LanguageIdentifier {
|
||||
lang.as_ref()
|
||||
.parse()
|
||||
.expect("parse Lang to LanguageIdentifier")
|
||||
}
|
||||
|
||||
/// Create and initialize a fluent bundle.
|
||||
#[must_use]
|
||||
pub fn create_ftl_bundle(self) -> FluentBundle<FluentResource> {
|
||||
let ftl_resource =
|
||||
FluentResource::try_new(self.ftl_messages().to_owned()).expect("parse FTL messages");
|
||||
pub fn create_ftl_bundle(lang: Lang) -> FluentBundle<FluentResource> {
|
||||
let ftl_resource = FluentResource::try_new(Self::ftl_messages(lang).to_owned())
|
||||
.expect("parse FTL messages");
|
||||
|
||||
let mut bundle = FluentBundle::new(vec![self.to_language_identifier()]);
|
||||
let mut bundle = FluentBundle::new(vec![Self::language_identifier(lang)]);
|
||||
bundle.add_resource(ftl_resource).expect("add FTL resource");
|
||||
bundle
|
||||
}
|
||||
|
||||
+55
-1
@@ -7,9 +7,10 @@ use pages::list_users;
|
||||
use seed::window;
|
||||
use seed::{attrs, button, div, input, label, log, prelude::*, App, Url, C};
|
||||
use shared::apirequests::users::LoginUser;
|
||||
use shared::datatypes::Lang;
|
||||
use shared::datatypes::{Loadable, User};
|
||||
|
||||
use crate::i18n::{I18n, Lang};
|
||||
use crate::i18n::I18n;
|
||||
|
||||
// ------ ------
|
||||
// Init
|
||||
@@ -47,6 +48,17 @@ struct Model {
|
||||
login_data: LoginUser,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
fn set_lang(&mut self, l: Lang) {
|
||||
self.i18n.set_lang(l);
|
||||
match &mut self.page {
|
||||
Page::Home(ref mut m) => m.set_lang(l),
|
||||
Page::ListUsers(ref mut m) => m.set_lang(l),
|
||||
Page::NotFound => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct LoginForm {
|
||||
username: ElRef<web_sys::HtmlInputElement>,
|
||||
@@ -104,6 +116,18 @@ impl Page {
|
||||
_other => Self::NotFound,
|
||||
};
|
||||
|
||||
orders.perform_cmd(async {
|
||||
// create request
|
||||
let request = Request::new("/admin/json/get_language/");
|
||||
// perform and get response
|
||||
let response = unwrap_or_return!(fetch(request).await, Msg::NoMessage);
|
||||
// validate response status
|
||||
let response = unwrap_or_return!(response.check_status(), Msg::NoMessage);
|
||||
let lang: Lang = unwrap_or_return!(response.json().await, Msg::NoMessage);
|
||||
|
||||
Msg::LanguageChanged(lang)
|
||||
});
|
||||
|
||||
log!("Page initialized");
|
||||
result
|
||||
}
|
||||
@@ -125,6 +149,8 @@ pub enum Msg {
|
||||
Login,
|
||||
UsernameChanged(String),
|
||||
PasswordChanged(String),
|
||||
SetLanguage(Lang),
|
||||
LanguageChanged(Lang),
|
||||
}
|
||||
|
||||
fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
@@ -163,6 +189,7 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
});
|
||||
}
|
||||
Msg::UserReceived(user) => {
|
||||
model.set_lang(user.language);
|
||||
model.user = Loadable::Data(Some(user));
|
||||
model.page = Page::init(
|
||||
model.location.current_url.clone(),
|
||||
@@ -175,6 +202,7 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
model.user = Loadable::Data(None);
|
||||
logout(orders)
|
||||
}
|
||||
model.user = Loadable::Data(None);
|
||||
}
|
||||
Msg::Logout => {
|
||||
model.user = Loadable::Data(None);
|
||||
@@ -183,9 +211,35 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
Msg::Login => login_user(model, orders),
|
||||
Msg::UsernameChanged(s) => model.login_data.username = s,
|
||||
Msg::PasswordChanged(s) => model.login_data.password = s,
|
||||
Msg::SetLanguage(l) => {
|
||||
change_language(l, orders);
|
||||
}
|
||||
Msg::LanguageChanged(l) => {
|
||||
log!("Changed Language", &l);
|
||||
model.set_lang(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn change_language(l: Lang, orders: &mut impl Orders<Msg>) {
|
||||
orders.perform_cmd(async move {
|
||||
// create request
|
||||
let request = unwrap_or_return!(
|
||||
Request::new("/admin/json/change_language/")
|
||||
.method(Method::Post)
|
||||
.json(&l),
|
||||
Msg::NoMessage
|
||||
);
|
||||
// perform and get response
|
||||
let response = unwrap_or_return!(fetch(request).await, Msg::NoMessage);
|
||||
// validate response status
|
||||
let response = unwrap_or_return!(response.check_status(), Msg::NoMessage);
|
||||
let l: Lang = unwrap_or_return!(response.json().await, Msg::NoMessage);
|
||||
|
||||
Msg::LanguageChanged(l)
|
||||
});
|
||||
}
|
||||
|
||||
fn logout(orders: &mut impl Orders<Msg>) {
|
||||
orders.perform_cmd(async {
|
||||
let request = Request::new("/admin/logout/");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use fluent::fluent_args;
|
||||
use seed::{a, attrs, div, li, nav, ol, prelude::*, Url};
|
||||
use shared::datatypes::User;
|
||||
use seed::{a, attrs, div, li, nav, ol, prelude::*, Url, C};
|
||||
use shared::datatypes::{Lang, User};
|
||||
|
||||
use crate::{i18n::I18n, Msg};
|
||||
|
||||
@@ -50,6 +50,12 @@ pub fn navigation(i18n: &I18n, base_url: &Url, user: &User) -> Node<Msg> {
|
||||
],],
|
||||
],
|
||||
ol![
|
||||
li![div![
|
||||
C!("languageselector"),
|
||||
t("language"),
|
||||
a![ev(Ev::Click, |_| Msg::SetLanguage(Lang::DeDE)), "de"],
|
||||
a![ev(Ev::Click, |_| Msg::SetLanguage(Lang::EnUS)), "en"]
|
||||
]],
|
||||
// The Welcome message
|
||||
li![div![welcome]],
|
||||
// The logout button
|
||||
|
||||
@@ -13,7 +13,7 @@ use shared::{
|
||||
general::{EditMode, Message, Operation, Status},
|
||||
links::{LinkDelta, LinkOverviewColumns, LinkRequestForm},
|
||||
},
|
||||
datatypes::{FullLink, Loadable},
|
||||
datatypes::{FullLink, Lang, Loadable},
|
||||
};
|
||||
|
||||
use crate::{get_host, i18n::I18n, unwrap_or_return};
|
||||
@@ -54,6 +54,12 @@ pub struct Model {
|
||||
handle_timeout: Option<CmdHandle>, // Rendering qr-codes takes time... it is aborted when this handle is dropped and replaced.
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn set_lang(&mut self, l: Lang) {
|
||||
self.i18n.set_lang(l);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Dialog {
|
||||
EditLink {
|
||||
|
||||
@@ -10,7 +10,7 @@ use shared::{
|
||||
general::{EditMode, Status},
|
||||
users::{UserDelta, UserOverviewColumns, UserRequestForm},
|
||||
},
|
||||
datatypes::User,
|
||||
datatypes::{Lang, User},
|
||||
};
|
||||
/*
|
||||
* init
|
||||
@@ -42,6 +42,12 @@ pub struct Model {
|
||||
last_message: Option<Status>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn set_lang(&mut self, l: Lang) {
|
||||
self.i18n.set_lang(l);
|
||||
}
|
||||
}
|
||||
|
||||
impl Model {
|
||||
fn clean_dialogs(&mut self) {
|
||||
self.last_message = None;
|
||||
|
||||
Reference in New Issue
Block a user