diff --git a/Cargo.nix b/Cargo.nix index e64dbbf..10cfaf9 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -8735,6 +8735,14 @@ rec { name = "cairo-rs"; packageId = "cairo-rs"; } + { + name = "fluent"; + packageId = "fluent"; + } + { + name = "fluent-ergonomics"; + packageId = "fluent-ergonomics"; + } { name = "gio"; packageId = "gio"; @@ -8867,6 +8875,10 @@ rec { name = "icu_provider"; packageId = "icu_provider"; } + { + name = "intl-memoizer"; + packageId = "intl-memoizer"; + } { name = "serde"; packageId = "serde 1.0.193"; @@ -8880,6 +8892,10 @@ rec { name = "sys-locale"; packageId = "sys-locale"; } + { + name = "thiserror"; + packageId = "thiserror"; + } { name = "unic-langid"; packageId = "unic-langid"; diff --git a/flake.nix b/flake.nix index 7cc2ded..7e0e951 100644 --- a/flake.nix +++ b/flake.nix @@ -17,6 +17,21 @@ let pkgs = import nixpkgs { system = "x86_64-linux"; }; pkgs-unstable = import unstable { system = "x86_64-linux"; }; + + cargo_nix = pkgs.callPackage ./Cargo.nix { + nixpkgs = pkgs; + }; + + l10n-codegen-rust = pkgs.stdenv.mkDerivation { + name = "l10n-codegen-rust"; + src = ./.; + + installPhase = '' + mkdir -p $out/bin + cp ${cargo_nix.workspaceMembers.l10n.build}/bin/codegen-rust $out/bin/l10n-codegen-rust + ''; + }; + in pkgs.mkShell { name = "ld-tools-devshell"; @@ -45,6 +60,8 @@ pkgs.udev pkgs.wasm-pack typeshare.packages."x86_64-linux".default + + l10n-codegen-rust ]; LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib"; ENV = "dev"; @@ -86,17 +103,6 @@ file-service = cargo_nix.workspaceMembers.file-service.build; fitnesstrax = cargo_nix.workspaceMembers.fitnesstrax.build; kifu-gtk = cargo_nix.workspaceMembers.kifu-gtk.build; - l10n = cargo_nix.workspaceMembers.l10n.build; - l10n-codegen-rust = pkgs.stdenv.mkDerivation { - name = "l10n-codegen-rust"; - src = ./.; - buildInputs = [ l10n ]; - - installPhase = '' - mkdir -p $out/bin - cp ${l10n}/bin/codegen-rust $out/bin/l10n-codegen-rust - ''; - }; xdg-test = cargo_nix.workspaceMembers.xdg-test.build; all = pkgs.symlinkJoin { @@ -107,8 +113,6 @@ file-service fitnesstrax # kifu-gtk - l10n - l10n-codegen-rust xdg-test ]; }; diff --git a/kifu/gtk/build.rs b/kifu/gtk/build.rs index 5a48d02..7c7ce8d 100644 --- a/kifu/gtk/build.rs +++ b/kifu/gtk/build.rs @@ -1,7 +1,29 @@ +use std::{path::PathBuf, env, process, fs::File, io::Write}; + +fn generate_message_types(dest: &PathBuf) { + println!("message types"); + let output: Vec = process::Command::new("l10n-codegen-rust") + .arg("messages/en.yaml") + .output() + .unwrap() + .stdout; + + let mut file = File::create(dest).unwrap(); + file.write(&output).unwrap(); +} + fn main() { glib_build_tools::compile_resources( &["resources"], "gresources.xml", "com.luminescent-dreams.kifu-gtk.gresource", ); + + // println!("OUT_DIR={}", env::var("OUT_DIR").unwrap()); + let mut message_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + message_path.push("messages.rs"); + + generate_message_types(&message_path); + + println!("cargo:rustc-env=KIFU_GTK_MESSAGES={}", message_path.to_string_lossy()); } diff --git a/kifu/gtk/resources/eo.ftl b/kifu/gtk/resources/eo.ftl index aed6515..8c3aabb 100644 --- a/kifu/gtk/resources/eo.ftl +++ b/kifu/gtk/resources/eo.ftl @@ -1,7 +1,7 @@ nothing-here = Nenio estas ĉi tie hello = Saluton, ${name} welcome = Bonvenon -games-in-database = {count -> +games-in-database = {$count -> [one] Estas unu ludon en la datumbazo. - [other] Estas {count} ludojn en la datumbazo. + *[other] Estas ${count} ludojn en la datumbazo. } diff --git a/kifu/gtk/src/lib.rs b/kifu/gtk/src/lib.rs index d062dc2..1f0364b 100644 --- a/kifu/gtk/src/lib.rs +++ b/kifu/gtk/src/lib.rs @@ -1,4 +1,6 @@ -mod messages; +mod messages { + include!(env!("KIFU_GTK_MESSAGES")); +} pub mod ui; diff --git a/kifu/gtk/src/messages.rs b/kifu/gtk/src/messages.rs deleted file mode 100644 index 6d72d9b..0000000 --- a/kifu/gtk/src/messages.rs +++ /dev/null @@ -1,58 +0,0 @@ -// This file was autogenerated from l10n-codegen-rust. Edits will be lost -// on next generation. -use l10n::Message; -use fluent::FluentArgs; - -pub struct Hello { - name: String, -} - -impl Message for Hello { - fn msgid(&self) -> &str { - "hello" - } - - fn args(&self) -> Option { - let mut args = FluentArgs::new(); - args.set("name", self.name.clone()); - Some(args) - } -} - -pub struct Welcome; -impl Message for Welcome { - fn msgid(&self) -> &str { - "welcome" - } - - fn args(&self) -> Option { - None - } -} - -pub struct GamesInDatabase { - count: usize, -} - -impl Message for GamesInDatabase { - fn msgid(&self) -> &str { - "games-in-database" - } - - fn args(&self) -> Option { - let mut args = FluentArgs::new(); - args.set("count", self.count.clone()); - Some(args) - } -} - -pub struct NothingHere; -impl Message for NothingHere { - fn msgid(&self) -> &str { - "nothing-here" - } - - fn args(&self) -> Option { - None - } -} diff --git a/kifu/gtk/src/ui/mod.rs b/kifu/gtk/src/ui/mod.rs index c448b82..0fde919 100644 --- a/kifu/gtk/src/ui/mod.rs +++ b/kifu/gtk/src/ui/mod.rs @@ -2,7 +2,7 @@ use adw::prelude::*; use gio::resources_lookup_data; use glib::IsA; use gtk::STYLE_PROVIDER_PRIORITY_USER; -use l10n::L10N; +use l10n::{L10N, NonEmptyList}; use std::path::PathBuf; use crate::messages; @@ -78,7 +78,8 @@ impl AppWindow { header.pack_end(&hamburger); let content = adw::Bin::builder().css_classes(vec!["content"]).build(); - let l10n = L10N::new(PathBuf::from("resources")); + let mut l10n = L10N::new(PathBuf::from("resources")); + l10n.set_locales(NonEmptyList::from_iter(vec!["en-US", "eo"]).unwrap()); content.set_child(Some( &adw::StatusPage::builder().title(l10n.tr(messages::NothingHere)).build(), )); diff --git a/l10n/src/lib.rs b/l10n/src/lib.rs index 65ee465..51b98a8 100644 --- a/l10n/src/lib.rs +++ b/l10n/src/lib.rs @@ -26,7 +26,9 @@ impl NonEmptyList { Self(vec![elem]) } - fn from_iter(iter: impl IntoIterator) -> Result, NonEmptyListError> { + pub fn from_iter( + iter: impl IntoIterator, + ) -> Result, NonEmptyListError> { let lst = iter.into_iter().collect::>(); if lst.len() > 0 { Ok(NonEmptyList(lst)) @@ -38,6 +40,10 @@ impl NonEmptyList { fn first(&self) -> &A { &self.0[0] } + + fn iter<'a>(&'a self) -> impl Iterator { + self.0.iter() + } } impl Deref for NonEmptyList { @@ -68,7 +74,7 @@ pub enum FileLoadError { FileNotFound, #[error("The Fluent file is malformed")] - FluentParseError, + FluentParseError(String), #[error("An unknown IO error was found")] IOError(std::io::Error), @@ -156,7 +162,13 @@ impl L10N { self.message_bundles.push(bundle); Ok(()) } - Err((_, _error)) => Err(FileLoadError::FluentParseError), + Err((_, errors)) => Err(FileLoadError::FluentParseError( + errors + .into_iter() + .map(|err| err.to_string()) + .collect::>() + .join("\n"), + )), } } @@ -178,7 +190,13 @@ impl L10N { .iter() .map(|locale| Locale::try_from_bytes(locale.as_bytes())) .collect::, icu::locid::Error>>()?; + + for locale in locales.iter() { + self.load_messages_from_file(locale.to_string()).unwrap(); + } + self.locales = NonEmptyList(locales); + Ok(()) } @@ -200,15 +218,21 @@ impl L10N { // } pub fn tr(&self, message: impl Message) -> String { - println!("message: {}", message.msgid()); - 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() + for bundle in self.message_bundles.iter().rev() { + let msg = bundle + .get_message(message.msgid()) + .and_then(|msg| msg.value()); + match msg { + Some(msg) => { + let mut errors = vec![]; + return self.message_bundles[0] + .format_pattern(msg, message.args().as_ref(), &mut errors) + .to_string(); + } + None => continue, + } + } + unreachable!("The message {} is missing", message.msgid()); } pub fn format_date_time_utc(