Properly support multiple languages

This commit is contained in:
Olivier 'reivilibre' 2023-03-03 23:47:43 +00:00
parent 811c06f5f8
commit 1256fef318
7 changed files with 23 additions and 14 deletions

View File

@ -38,16 +38,10 @@ async fn main() -> eyre::Result<()> {
Ok(()) Ok(())
} }
async fn say_hello(Path((_lang, name)): Path<(String, String)>) -> impl IntoResponse { async fn say_hello(Path((lang, name)): Path<(String, String)>) -> impl IntoResponse {
// TODO This should be translated with Fluent! Rendered(render_template_string!(TEMPLATING, say_hello, lang, {
//let template = templates.prepare("say_hello", None, Params::default().set("name", name));
Rendered(render_template_string!(TEMPLATING, say_hello, "en-GB", {
name: name name: name
})) }))
// HtmlTemplate(template)
} }
struct Rendered(Result<String, hornbeam::TemplateError>); struct Rendered(Result<String, hornbeam::TemplateError>);

View File

@ -0,0 +1 @@
hello = Bonjour { $name } !

View File

@ -10,7 +10,6 @@ use hornbeam_interpreter::localisation::fluent::{
FluentLocalisationError, FluentLocalisationSystem, FluentLocalisationError, FluentLocalisationSystem,
}; };
use hornbeam_interpreter::{InterpreterError, LoadedTemplates}; use hornbeam_interpreter::{InterpreterError, LoadedTemplates};
use notify::Watcher;
use std::convert::Infallible; use std::convert::Infallible;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -23,7 +22,6 @@ pub use hornbeam_interpreter::Params;
use crate::interpreted::hot_reload::start_hot_reloader; use crate::interpreted::hot_reload::start_hot_reloader;
pub use lazy_static::lazy_static; pub use lazy_static::lazy_static;
use tokio::io;
#[cfg(feature = "hot_reload")] #[cfg(feature = "hot_reload")]
pub mod hot_reload; pub mod hot_reload;
@ -174,7 +172,7 @@ macro_rules! render_template_string {
// TODO LOCALE // TODO LOCALE
{ {
let manager = $manager.load(); let manager = $manager.load();
let prepared = manager.prepare(stringify!($template_name), None, $crate::interpreter_params!{ $($params)* }); let prepared = manager.prepare(stringify!($template_name), None, $crate::interpreter_params!{ $($params)* }, $locale);
let finished_string = prepared.render_to_string(); let finished_string = prepared.render_to_string();
if $crate::is_hot_reload_enabled() { if $crate::is_hot_reload_enabled() {
// Hacky, but inject the auto-hot-reloader into the page :P // Hacky, but inject the auto-hot-reloader into the page :P

View File

@ -28,6 +28,7 @@ pub(crate) struct Interpreter<'a, O, LS> {
pub(crate) program: &'a BTreeMap<String, Arc<Vec<Step>>>, pub(crate) program: &'a BTreeMap<String, Arc<Vec<Step>>>,
pub(crate) output: O, pub(crate) output: O,
pub(crate) localisation: Arc<LS>, pub(crate) localisation: Arc<LS>,
pub(crate) locale: String,
pub(crate) scopes: Vec<Scope<'a>>, pub(crate) scopes: Vec<Scope<'a>>,
} }
@ -577,7 +578,7 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret
let localised_text = self let localised_text = self
.localisation .localisation
.lookup(trans_key, &parameters_evaluated) .lookup(trans_key, &parameters_evaluated, &self.locale)
.map_err(|underlying| InterpreterError::Localisation { .map_err(|underlying| InterpreterError::Localisation {
underlying, underlying,
trans_key: (trans_key as &str).to_owned(), trans_key: (trans_key as &str).to_owned(),

View File

@ -24,6 +24,7 @@ pub trait LocalisationSystem {
&self, &self,
trans_key: &str, trans_key: &str,
params: &BTreeMap<&str, Value>, params: &BTreeMap<&str, Value>,
locale: &str,
) -> Result<Cow<str>, Self::Error>; ) -> Result<Cow<str>, Self::Error>;
} }
@ -148,6 +149,7 @@ impl<'a, LS> LoadedTemplates<LS> {
template_name: &str, template_name: &str,
fragment_name: Option<&str>, fragment_name: Option<&str>,
params: Params, params: Params,
locale: String,
) -> PreparedTemplate<LS> { ) -> PreparedTemplate<LS> {
PreparedTemplate { PreparedTemplate {
all_instructions: Arc::new(self.template_functions.clone()), all_instructions: Arc::new(self.template_functions.clone()),
@ -158,6 +160,7 @@ impl<'a, LS> LoadedTemplates<LS> {
}, },
variables: params, variables: params,
localisation: self.localisation.clone(), localisation: self.localisation.clone(),
locale,
} }
} }
} }
@ -167,6 +170,7 @@ pub struct PreparedTemplate<LS> {
pub(crate) entrypoint: String, pub(crate) entrypoint: String,
pub(crate) variables: Params, pub(crate) variables: Params,
pub(crate) localisation: Arc<LS>, pub(crate) localisation: Arc<LS>,
pub(crate) locale: String,
} }
impl<LS: LocalisationSystem + Sync + Send> PreparedTemplate<LS> { impl<LS: LocalisationSystem + Sync + Send> PreparedTemplate<LS> {
@ -179,6 +183,7 @@ impl<LS: LocalisationSystem + Sync + Send> PreparedTemplate<LS> {
program: &self.all_instructions, program: &self.all_instructions,
output, output,
localisation: self.localisation, localisation: self.localisation,
locale: self.locale,
scopes: vec![Scope { scopes: vec![Scope {
variables: self.variables.params, variables: self.variables.params,
slots: Default::default(), slots: Default::default(),

View File

@ -18,6 +18,7 @@ impl LocalisationSystem for NoLocalisation {
&self, &self,
_trans_key: &str, _trans_key: &str,
_params: &BTreeMap<&str, Value>, _params: &BTreeMap<&str, Value>,
_locale: &str,
) -> Result<Cow<str>, Self::Error> { ) -> Result<Cow<str>, Self::Error> {
Err("No localisation support available.") Err("No localisation support available.")
} }
@ -34,6 +35,7 @@ impl LocalisationSystem for DebugLocalisationSystem {
&self, &self,
trans_key: &str, trans_key: &str,
params: &BTreeMap<&str, Value>, params: &BTreeMap<&str, Value>,
_locale: &str,
) -> Result<Cow<str>, Self::Error> { ) -> Result<Cow<str>, Self::Error> {
Ok(Cow::from(format!("@{trans_key}{{{params:?}}}"))) Ok(Cow::from(format!("@{trans_key}{{{params:?}}}")))
} }

View File

@ -6,7 +6,6 @@ use crate::engine::Value;
use crate::interface::LocalisationSystem; use crate::interface::LocalisationSystem;
use fluent_templates::fluent_bundle::types::FluentNumber; use fluent_templates::fluent_bundle::types::FluentNumber;
use fluent_templates::fluent_bundle::FluentValue; use fluent_templates::fluent_bundle::FluentValue;
use fluent_templates::loader::langid;
use fluent_templates::{ArcLoader, LanguageIdentifier, Loader}; use fluent_templates::{ArcLoader, LanguageIdentifier, Loader};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
@ -60,6 +59,9 @@ pub enum FluentLocalisationError {
#[error("no translation found for {trans_key} in {lang_id}")] #[error("no translation found for {trans_key} in {lang_id}")]
NoTranslation { trans_key: String, lang_id: String }, NoTranslation { trans_key: String, lang_id: String },
#[error("bad language ID {lang_id:?}")]
BadLocale { lang_id: String },
#[error("can't convert param {param_name} of type {param_type} to a fluent type")] #[error("can't convert param {param_name} of type {param_type} to a fluent type")]
IncompatibleValue { IncompatibleValue {
param_name: String, param_name: String,
@ -74,8 +76,14 @@ impl LocalisationSystem for FluentLocalisationSystem {
&self, &self,
trans_key: &str, trans_key: &str,
params: &BTreeMap<&str, Value>, params: &BTreeMap<&str, Value>,
locale: &str,
) -> Result<Cow<str>, Self::Error> { ) -> Result<Cow<str>, Self::Error> {
let li: LanguageIdentifier = langid!("en-GB"); // TODO get the language tag from somewhere let li: LanguageIdentifier =
LanguageIdentifier::from_bytes(locale.as_bytes()).map_err(|_| {
FluentLocalisationError::BadLocale {
lang_id: locale.to_owned(),
}
})?;
let mut mapped_params = HashMap::with_capacity(params.len()); let mut mapped_params = HashMap::with_capacity(params.len());
for (&k, v) in params { for (&k, v) in params {
if let Some(mapped_val) = interpreter_value_to_fluent_value(v) { if let Some(mapped_val) = interpreter_value_to_fluent_value(v) {