From 7338ac8c571d4a15252d72b30c0305bc49a7e61c Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 27 Jun 2023 17:40:14 +0200 Subject: [PATCH] i18n support --- Cargo.lock | 320 ++++++++++++++++++++++++++++++ Cargo.toml | 3 + i18n.toml | 4 + resources/i18n/en/cosmic_comp.ftl | 3 + src/state.rs | 29 +++ 5 files changed, 359 insertions(+) create mode 100644 i18n.toml create mode 100644 resources/i18n/en/cosmic_comp.ftl diff --git a/Cargo.lock b/Cargo.lock index 01a9835e..3f6ce1c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,6 +148,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + [[package]] name = "arrayref" version = "0.3.7" @@ -480,6 +486,8 @@ dependencies = [ "edid-rs", "egui", "glow 0.11.2", + "i18n-embed", + "i18n-embed-fl", "iced_tiny_skia", "id_tree", "indexmap 1.9.3", @@ -495,6 +503,7 @@ dependencies = [ "regex", "renderdoc", "ron 0.7.1", + "rust-embed", "sendfd", "serde", "serde_json", @@ -722,6 +731,19 @@ dependencies = [ "syn 2.0.22", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown 0.12.3", + "lock_api", + "once_cell", + "parking_lot_core 0.9.8", +] + [[package]] name = "data-url" version = "0.2.0" @@ -807,6 +829,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.22", +] + [[package]] name = "dlib" version = "0.5.2" @@ -1124,6 +1157,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml 0.5.11", +] + [[package]] name = "flate2" version = "1.0.26" @@ -1149,6 +1191,50 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +[[package]] +name = "fluent" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +dependencies = [ + "thiserror", +] + [[package]] name = "flume" version = "0.10.14" @@ -1602,6 +1688,76 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "i18n-config" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b987084cadad6e2f2b1e6ea62c44123591a3c044793a1beabf71a8356ea768d5" +dependencies = [ + "log", + "serde", + "serde_derive", + "thiserror", + "toml 0.7.5", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.13.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92a86226a7a16632de6723449ee5fe70bac5af718bc642ee9ca2f0f6e14fa1fa" +dependencies = [ + "arc-swap", + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "lazy_static", + "locale_config", + "log", + "parking_lot 0.12.1", + "rust-embed", + "thiserror", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26a3d3569737dfaac7fc1c4078e6af07471c3060b8e570bcd83cdd5f4685395" +dependencies = [ + "dashmap", + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.22", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9a95d065e6be4499e50159172395559a388d20cf13c84c77e4a1e341786f219" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "iced" version = "0.9.0" @@ -1863,6 +2019,25 @@ dependencies = [ "web-sys", ] +[[package]] +name = "intl-memoizer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -2101,6 +2276,19 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "locale_config" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" +dependencies = [ + "lazy_static", + "objc", + "objc-foundation", + "regex", + "winapi", +] + [[package]] name = "lock_api" version = "0.4.10" @@ -2539,6 +2727,17 @@ dependencies = [ "objc_exception", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc-sys" version = "0.2.0-beta.2" @@ -2574,6 +2773,15 @@ dependencies = [ "cc", ] +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "object" version = "0.30.4" @@ -3246,6 +3454,40 @@ dependencies = [ "xmlparser", ] +[[package]] +name = "rust-embed" +version = "6.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73e721f488c353141288f223b599b4ae9303ecf3e62923f40a492f0634a4dc3" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22ce362f5561923889196595504317a4372b84210e6e335da529a65ea5452b5" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.22", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rust-ini" version = "0.18.0" @@ -3343,6 +3585,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "self_cell" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" + [[package]] name = "sendfd" version = "0.4.3" @@ -3383,6 +3631,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "sha2" version = "0.10.7" @@ -3840,6 +4097,15 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tinystr" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ac3f5b6856e931e15e07b478e98c8045239829a65f9156d4fa7e7788197a5ef" +dependencies = [ + "displaydoc", +] + [[package]] name = "tokio" version = "1.28.2" @@ -3852,11 +4118,35 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -3865,6 +4155,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" dependencies = [ "indexmap 2.0.0", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -3965,6 +4257,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "type-map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +dependencies = [ + "rustc-hash", +] + [[package]] name = "typenum" version = "1.16.0" @@ -3982,6 +4283,25 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "unic-langid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" +dependencies = [ + "serde", + "tinystr", +] + [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/Cargo.toml b/Cargo.toml index 7eb85222..56807e24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,9 @@ puffin = { version = "0.14.3", optional = true } puffin_egui = { version = "0.21.0", optional = true } cosmic-time = { git = "https://github.com/pop-os/cosmic-time", rev = "39c96ac", default-features = false, features = ["libcosmic"] } once_cell = "1.18.0" +i18n-embed = { version = "0.13", features = ["fluent-system", "desktop-requester"] } +i18n-embed-fl = "0.6" +rust-embed = "6.6" [dependencies.id_tree] git = "https://github.com/Drakulix/id-tree.git" diff --git a/i18n.toml b/i18n.toml new file mode 100644 index 00000000..746887d6 --- /dev/null +++ b/i18n.toml @@ -0,0 +1,4 @@ +fallback_language = "en" + +[fluent] +assets_dir = "resources/i18n" \ No newline at end of file diff --git a/resources/i18n/en/cosmic_comp.ftl b/resources/i18n/en/cosmic_comp.ftl new file mode 100644 index 00000000..219db51a --- /dev/null +++ b/resources/i18n/en/cosmic_comp.ftl @@ -0,0 +1,3 @@ +grow-window = Grow +shrink-window = Shrink +unknown-keybinding = \ No newline at end of file diff --git a/src/state.rs b/src/state.rs index 2492ff5e..9382fb0f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -17,7 +17,14 @@ use crate::{ }, xwayland::XWaylandState, }; +use anyhow::Context; use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_manager_v1::CursorMode; +use i18n_embed::{ + fluent::{fluent_language_loader, FluentLanguageLoader}, + DesktopLanguageRequester, +}; +use once_cell::sync::Lazy; +use rust_embed::RustEmbed; #[cfg(feature = "debug")] use smithay::utils::Rectangle; use smithay::{ @@ -69,6 +76,23 @@ use tracing::error; use std::{cell::RefCell, ffi::OsString, time::Duration}; use std::{collections::VecDeque, time::Instant}; +#[derive(RustEmbed)] +#[folder = "resources/i18n"] +struct Localizations; + +pub static LANG_LOADER: Lazy = Lazy::new(|| fluent_language_loader!()); + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::state::LANG_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),*) => {{ + i18n_embed_fl::fl!($crate::state::LANG_LOADER, $message_id, $($args), *) + }}; +} + pub struct ClientState { pub compositor_client_state: CompositorClientState, pub workspace_client_state: WorkspaceClientState, @@ -241,6 +265,11 @@ impl State { handle: LoopHandle<'static, Data>, signal: LoopSignal, ) -> State { + let requested_languages = DesktopLanguageRequester::requested_languages(); + i18n_embed::select(&*LANG_LOADER, &Localizations, &requested_languages) + .with_context(|| "Failed to load languages") + .unwrap(); + let clock = Clock::new().expect("Failed to initialize clock"); let config = Config::load(); let compositor_state = CompositorState::new::(dh);