diff --git a/hornbeam_interpreter/src/engine.rs b/hornbeam_interpreter/src/engine.rs index 9ec8ab4..e4c8091 100644 --- a/hornbeam_interpreter/src/engine.rs +++ b/hornbeam_interpreter/src/engine.rs @@ -40,6 +40,18 @@ pub enum Value { Reflective(Box), } +impl Value { + pub fn get_type_name(&self) -> String { + String::from(match self { + Value::Str(_) => "Str", + Value::Int(_) => "Int", + Value::Bool(_) => "Bool", + Value::List(_) => "List", + Value::Reflective(reflective) => reflective.type_name(), + }) + } +} + lazy_static! { static ref U8_TYPEID: TypeId = TypeId::of::(); static ref U16_TYPEID: TypeId = TypeId::of::(); diff --git a/hornbeam_interpreter/src/localisation/fluent.rs b/hornbeam_interpreter/src/localisation/fluent.rs index ac0708e..cd5d74b 100644 --- a/hornbeam_interpreter/src/localisation/fluent.rs +++ b/hornbeam_interpreter/src/localisation/fluent.rs @@ -4,19 +4,81 @@ use crate::engine::Value; use crate::interface::LocalisationSystem; +use fluent_templates::fluent_bundle::types::FluentNumber; +use fluent_templates::fluent_bundle::FluentValue; +use fluent_templates::loader::langid; +use fluent_templates::{ArcLoader, LanguageIdentifier, Loader}; use std::borrow::Cow; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; -pub struct FluentLocalisationSystem {} +use thiserror::Error; + +fn interpreter_value_to_fluent_value(v: &Value) -> Option { + match v { + Value::Str(str) => Some(FluentValue::String(Cow::Borrowed(&str))), + Value::Int(int) => { + // This is an unstyled number + // TODO Support fancier numbers + Some(FluentValue::Number(FluentNumber::from(int))) + } + Value::Bool(_) => { + // TODO I can't believe there's no way to expose this to fluent! + None + } + Value::List(_) => None, + Value::Reflective(_) => None, + } + // TODO It might be good to support a `struct` type here with fancier numbers + // or to support FluentNumber types directly, then expose functions to Hornbeam + // templates to create them? +} + +pub struct FluentLocalisationSystem { + pub fluent: ArcLoader, +} + +impl FluentLocalisationSystem {} + +#[derive(Error, Clone, Debug)] +pub enum FluentLocalisationError { + #[error("no translation found for {trans_key} in {lang_id}")] + NoTranslation { trans_key: String, lang_id: String }, + + #[error("can't convert param {param_name} of type {param_type} to a fluent type")] + IncompatibleValue { + param_name: String, + param_type: String, + }, +} impl LocalisationSystem for FluentLocalisationSystem { - type Error = (); + type Error = FluentLocalisationError; fn lookup( &self, trans_key: &str, params: &BTreeMap<&str, Value>, ) -> Result, Self::Error> { - todo!() + let li: LanguageIdentifier = langid!("en-GB"); // TODO get the language tag from somewhere + let mut mapped_params = HashMap::with_capacity(params.len()); + for (&k, v) in params { + if let Some(mapped_val) = interpreter_value_to_fluent_value(v) { + mapped_params.insert(k, mapped_val); + } else { + return Err(FluentLocalisationError::IncompatibleValue { + param_name: String::from(k), + param_type: v.get_type_name(), + }); + } + } + match self.fluent.lookup_with_args(&li, trans_key, &mapped_params) { + Some(val) => Ok(Cow::Owned(val)), + None => { + return Err(FluentLocalisationError::NoTranslation { + trans_key: String::from(trans_key), + lang_id: li.to_string(), + }); + } + } } }