Compare commits

..

No commits in common. "5c7f7766def86d826724b772843030837f3e1ed7" and "15b1d4bfd67b627cb8bdca0ca0d7863f9933a8c4" have entirely different histories.

5 changed files with 50 additions and 123 deletions

2
Cargo.lock generated
View File

@ -2734,11 +2734,9 @@ dependencies = [
"icu", "icu",
"icu_locid", "icu_locid",
"icu_provider", "icu_provider",
"intl-memoizer",
"serde 1.0.193", "serde 1.0.193",
"serde_yaml", "serde_yaml",
"sys-locale", "sys-locale",
"thiserror",
"unic-langid", "unic-langid",
] ]

View File

@ -1,6 +1,7 @@
// This file was autogenerated from l10n-codegen-rust. Edits will be lost // This file was autogenerated from l10n-codegen-rust. Edits will be lost
// on next generation. // on next generation.
use l10n::Message; use l10n::Message;
use fluent_ergonomics::FluentErgo;
use fluent::FluentArgs; use fluent::FluentArgs;
pub struct Hello { pub struct Hello {
@ -8,25 +9,24 @@ pub struct Hello {
} }
impl Message for Hello { impl Message for Hello {
fn msgid(&self) -> &str { fn localize(&self, bundle: &FluentErgo) -> String {
"hello"
}
fn args(&self) -> Option<FluentArgs> {
let mut args = FluentArgs::new(); let mut args = FluentArgs::new();
args.set("name", self.name.clone()); args.set("name", self.name.clone());
Some(args) bundle.tr("hello", Some(&args)).unwrap()
}
}
pub struct NothingHere;
impl Message for NothingHere {
fn localize(&self, bundle: &FluentErgo) -> String {
bundle.tr("nothing-here", None).unwrap()
} }
} }
pub struct Welcome; pub struct Welcome;
impl Message for Welcome { impl Message for Welcome {
fn msgid(&self) -> &str { fn localize(&self, bundle: &FluentErgo) -> String {
"welcome" bundle.tr("welcome", None).unwrap()
}
fn args(&self) -> Option<FluentArgs> {
None
} }
} }
@ -35,24 +35,9 @@ pub struct GamesInDatabase {
} }
impl Message for GamesInDatabase { impl Message for GamesInDatabase {
fn msgid(&self) -> &str { fn localize(&self, bundle: &FluentErgo) -> String {
"games-in-database"
}
fn args(&self) -> Option<FluentArgs> {
let mut args = FluentArgs::new(); let mut args = FluentArgs::new();
args.set("count", self.count.clone()); args.set("count", self.count.clone());
Some(args) bundle.tr("games-in-database", Some(&args)).unwrap()
}
}
pub struct NothingHere;
impl Message for NothingHere {
fn msgid(&self) -> &str {
"nothing-here"
}
fn args(&self) -> Option<FluentArgs> {
None
} }
} }

View File

@ -15,11 +15,9 @@ fluent = { version = "0.16" }
icu_locid = { version = "1" } icu_locid = { version = "1" }
icu_provider = { version = "1" } icu_provider = { version = "1" }
icu = { version = "1" } icu = { version = "1" }
intl-memoizer = { version = "*" }
serde = { version = "1", features = [ "derive" ] } serde = { version = "1", features = [ "derive" ] }
serde_yaml = { version = "0.9" } serde_yaml = { version = "0.9" }
sys-locale = { version = "0.3" } sys-locale = { version = "0.3" }
thiserror = { version = "1" }
unic-langid = { version = "*" } unic-langid = { version = "*" }
[[bin]] [[bin]]

View File

@ -81,12 +81,8 @@ impl Message {
struct_strs.push(format!("pub struct {}; ", self.name.to_case(Case::Pascal))); struct_strs.push(format!("pub struct {}; ", self.name.to_case(Case::Pascal)));
struct_strs.push(format!("impl Message for {} {{ struct_strs.push(format!("impl Message for {} {{
fn msgid(&self) -> &str {{ fn localize(&self, bundle: &FluentErgo) -> String {{
\"{}\" bundle.tr(\"{}\", None).unwrap()
}}
fn args(&self) -> Option<FluentArgs> {{
None
}} }}
}}", }}",
self.name.to_case(Case::Pascal), self.name.to_case(Case::Pascal),
@ -118,19 +114,15 @@ impl Message {
struct_strs.push(format!( struct_strs.push(format!(
"impl Message for {} {{ "impl Message for {} {{
fn msgid(&self) -> &str {{ fn localize(&self, bundle: &FluentErgo) -> String {{
\"{}\"
}}
fn args(&self) -> Option<FluentArgs> {{
let mut args = FluentArgs::new(); let mut args = FluentArgs::new();
{} {}
Some(args) bundle.tr(\"{}\", Some(&args)).unwrap()
}} }}
}}", }}",
self.name.to_case(Case::Pascal), self.name.to_case(Case::Pascal),
self.name,
parameters, parameters,
self.name,
)); ));
struct_strs.join("\n") struct_strs.join("\n")
@ -152,6 +144,7 @@ fn main() {
println!("// This file was autogenerated from l10n-codegen-rust. Edits will be lost println!("// This file was autogenerated from l10n-codegen-rust. Edits will be lost
// on next generation."); // on next generation.");
println!("use l10n::Message;"); println!("use l10n::Message;");
println!("use fluent_ergonomics::FluentErgo;");
println!("use fluent::FluentArgs;"); println!("use fluent::FluentArgs;");
let messages = messages let messages = messages

View File

@ -1,13 +1,12 @@
use chrono::{Datelike, NaiveDate, Timelike}; use chrono::{Datelike, NaiveDate, Timelike};
use chrono_tz::Tz; use chrono_tz::Tz;
use fixed_decimal::FixedDecimal; use fixed_decimal::FixedDecimal;
use fluent::{bundle::FluentBundle, FluentResource}; use fluent::{FluentBundle, FluentResource};
use fluent_ergonomics::FluentErgo;
use icu::{datetime::options::length, decimal::FixedDecimalFormatter, locid::Locale}; use icu::{datetime::options::length, decimal::FixedDecimalFormatter, locid::Locale};
use icu_provider::DataLocale; use icu_provider::DataLocale;
use std::{fs::File, io::Read, ops::Deref}; use std::{collections::HashMap, ops::Deref, path::Path};
use sys_locale::get_locale; use sys_locale::get_locale;
use thiserror::Error;
use unic_langid::LanguageIdentifierError;
// Re-exports. I'm doing these so that clients of this library don't have to go tracking down // Re-exports. I'm doing these so that clients of this library don't have to go tracking down
// additional structures // additional structures
@ -47,9 +46,7 @@ impl<A> Deref for NonEmptyList<A> {
} }
} }
#[derive(Debug, Error)]
pub enum L10NError { pub enum L10NError {
#[error("Unparsable Locale")]
UnparsableLocale, UnparsableLocale,
} }
@ -59,33 +56,6 @@ impl From<icu::locid::Error> for L10NError {
} }
} }
#[derive(Debug, Error)]
pub enum FileLoadError {
#[error("Unparsable Locale")]
UnparsableLocale,
#[error("Source string file not found")]
FileNotFound,
#[error("The Fluent file is malformed")]
FluentParseError,
#[error("An unknown IO error was found")]
IOError(std::io::Error),
}
impl From<LanguageIdentifierError> for FileLoadError {
fn from(_: LanguageIdentifierError) -> Self {
Self::UnparsableLocale
}
}
impl From<std::io::Error> for FileLoadError {
fn from(err: std::io::Error) -> Self {
Self::IOError(err)
}
}
// Potential Message structure. // Potential Message structure.
// //
// Let's assume the application has an enumeration that implements Message. For each element of the // Let's assume the application has an enumeration that implements Message. For each element of the
@ -98,13 +68,12 @@ impl From<std::io::Error> for FileLoadError {
// However, I have not found a mechanism in Fluent to identify all of the placeholders within a // However, I have not found a mechanism in Fluent to identify all of the placeholders within a
// message, so I'm not even sure that I can automate this code generation. // message, so I'm not even sure that I can automate this code generation.
pub trait Message { pub trait Message {
fn msgid(&self) -> &str; fn localize(&self, bundle: &FluentErgo) -> String;
fn args(&self) -> Option<FluentArgs>;
} }
pub struct L10N { pub struct L10N {
messages_root: std::path::PathBuf, messages_root: std::path::PathBuf,
message_bundles: Vec<FluentBundle<FluentResource, intl_memoizer::concurrent::IntlLangMemoizer>>, message_bundle: FluentErgo,
locales: NonEmptyList<Locale>, locales: NonEmptyList<Locale>,
zone: chrono_tz::Tz, zone: chrono_tz::Tz,
@ -125,39 +94,31 @@ impl L10N {
let english_phrases = FluentResource::try_new let english_phrases = FluentResource::try_new
*/ */
let mut s = Self { let message_bundle = {
messages_root, let mut english_messages = messages_root.clone();
message_bundles: vec![], english_messages.push("en-US.ftl");
locales,
zone, let langid: unic_langid::LanguageIdentifier = english.to_string().parse().unwrap();
let mut messages = FluentErgo::new(&[langid.clone()]);
let _ = messages.add_from_file(langid, &english_messages);
messages
}; };
s.load_messages_from_file("en-US".to_owned()).unwrap(); Self {
messages_root,
s message_bundle,
locales,
zone,
}
} }
fn load_messages_from_file(&mut self, locale: String) -> Result<(), FileLoadError> { pub fn load_messages_from_file(
let langid: unic_langid::LanguageIdentifier = locale.parse()?; &mut self,
locale: String,
let mut path = self.messages_root.clone(); path: &Path,
path.push(locale); ) -> Result<(), L10NError> {
path.set_extension("ftl"); unimplemented!()
println!("{:?}", path);
let mut buffer = Vec::new();
let mut f = File::open(path)?;
f.read_to_end(&mut buffer)?;
let text = String::from_utf8(buffer).unwrap();
match FluentResource::try_new(text) {
Ok(resource) => {
let mut bundle = FluentBundle::new_concurrent(vec![langid]);
let _ = bundle.add_resource(resource);
self.message_bundles.push(bundle);
Ok(())
}
Err((_, _error)) => Err(FileLoadError::FluentParseError),
}
} }
// Now, whenever the user changes the locales, the list of messages has to change. How do we // Now, whenever the user changes the locales, the list of messages has to change. How do we
@ -195,20 +156,12 @@ impl L10N {
// parameters. In an ideal world, neither of these can be incorrect. Messages are all checked // parameters. In an ideal world, neither of these can be incorrect. Messages are all checked
// at compile time, as are their parameters. That implies an enumeration, with one element per // at compile time, as are their parameters. That implies an enumeration, with one element per
// message, and with each element knowing its parameters. // message, and with each element knowing its parameters.
// pub fn messages(&self) -> Vec<FluentBundle<FluentResource>> { pub fn messages(&self) -> FluentErgo {
// self.message_bundles.clone() self.message_bundle.clone()
// } }
pub fn tr(&self, message: impl Message) -> String { pub fn tr(&self, message: impl Message) -> String {
println!("message: {}", message.msgid()); message.localize(&self.message_bundle)
let msg = self.message_bundles[0]
.get_message(message.msgid())
.and_then(|msg| msg.value())
.unwrap();
let mut errors = vec![];
self.message_bundles[0]
.format_pattern(msg, message.args().as_ref(), &mut errors)
.to_string()
} }
pub fn format_date_time_utc( pub fn format_date_time_utc(