Pslink/app/src/i18n.rs

113 lines
3.2 KiB
Rust
Raw Normal View History

use std::sync::Arc;
use fluent::{FluentArgs, FluentBundle, FluentResource};
2021-05-31 06:55:56 +02:00
use seed::log;
use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
use unic_langid::LanguageIdentifier;
2021-05-24 14:29:14 +02:00
// A struct containing the functions and the current language to query the localized strings.
#[derive(Clone)]
pub struct I18n {
lang: Lang,
ftl_bundle: Arc<FluentBundle<FluentResource>>,
}
impl std::fmt::Debug for I18n {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.lang)
}
}
impl I18n {
2021-05-24 14:29:14 +02:00
/// Create a new translator struct
#[must_use]
pub fn new(lang: Lang) -> Self {
Self {
lang,
ftl_bundle: Arc::new(lang.create_ftl_bundle()),
}
}
2021-05-24 14:29:14 +02:00
/// Get the current language
#[must_use]
pub const fn lang(&self) -> &Lang {
&self.lang
}
2021-05-24 14:29:14 +02:00
/// Set the current language
pub fn set_lang(&mut self, lang: Lang) -> &Self {
self.lang = lang;
self.ftl_bundle = Arc::new(lang.create_ftl_bundle());
self
}
2021-05-24 14:29:14 +02:00
/// Get a localized string. Optionally with parameters provided in `args`.
pub fn translate(&self, key: impl AsRef<str>, args: Option<&FluentArgs>) -> String {
2021-05-31 06:55:56 +02:00
log!(key.as_ref());
let msg = self
.ftl_bundle
.get_message(key.as_ref())
2021-05-24 14:29:14 +02:00
.expect("Failed to get fluent message for key {}");
2021-05-24 14:29:14 +02:00
let pattern = msg.value().expect("Failed to parse pattern");
self.ftl_bundle
.format_pattern(pattern, args, &mut vec![])
.to_string()
}
}
2021-05-24 14:29:14 +02:00
/// 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 {
2021-05-24 14:29:14 +02:00
/// Prettyprint the language name
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::EnUS => "English (US)",
Self::DeDE => "Deutsch (Deutschland)",
}
}
2021-05-24 14:29:14 +02:00
/// include the fluent messages into the binary
#[must_use]
pub const fn ftl_messages(self) -> &'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"),
}
}
#[must_use]
pub fn to_language_identifier(self) -> LanguageIdentifier {
self.as_ref()
.parse()
.expect("parse Lang to LanguageIdentifier")
}
2021-05-24 14:29:14 +02:00
/// 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");
let mut bundle = FluentBundle::new(vec![self.to_language_identifier()]);
bundle.add_resource(ftl_resource).expect("add FTL resource");
bundle
}
}