diff --git a/.gitignore b/.gitignore index 174e213..b8c3764 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /target .env -links.db +links.db* launch.json settings.json links.session.sql diff --git a/Cargo.lock b/Cargo.lock index e470088..3129fa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "actix-codec" version = "0.3.0" @@ -412,6 +422,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "arc-swap" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d7d63395147b81a9e570bcc6243aaf71c017bd666d4909cfef0085bdda8d73" + [[package]] name = "argonautica" version = "0.2.0" @@ -1037,6 +1053,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dotenv" version = "0.15.0" @@ -1126,6 +1148,99 @@ dependencies = [ "miniz_oxide 0.4.3", ] +[[package]] +name = "fluent" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960ac6317b829b94c67f9a774e8b56db388405e174855a5a84d4b461ff85b281" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3cc2d1c59a0daaa93bb346db97e1ebad1067c5ffedc1af8b937a9d8caa6a77" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "ouroboros", + "rustc-hash", + "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.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784f660373ea898f712a7e67b43f35bf79608d46112747c29767d087611d716b" + +[[package]] +name = "fluent-template-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3051f88dd918b30a994177acc8f3b8b0b399c98ca5aa2ab985e79070a8841592" +dependencies = [ + "flume", + "ignore", + "once_cell", + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn", + "unic-langid", +] + +[[package]] +name = "fluent-templates" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fa6b2186b959236019d67fb839f036c83a799edd4389c505678ca1b8d41e9ed" +dependencies = [ + "arc-swap", + "fluent", + "fluent-bundle", + "fluent-langneg", + "fluent-syntax", + "fluent-template-macros", + "flume", + "heck", + "ignore", + "lazy_static", + "log", + "once_cell", + "serde_json", + "snafu", + "tera", + "unic-langid", +] + +[[package]] +name = "flume" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531a685ab99b8f60a271b44d5dd1a76e55124a8c9fa0407b7a8e9cd172d5b588" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project 1.0.4", + "spinning_top", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1327,8 +1442,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.10.2+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1589,6 +1706,26 @@ dependencies = [ "cfg-if 1.0.0", ] +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf" +dependencies = [ + "tinystr", + "unic-langid", +] + [[package]] name = "iovec" version = "0.1.4" @@ -1848,6 +1985,15 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "nanorand" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1378b66f7c93a1c0f8464a19bf47df8795083842e5090f4b7305973d5a22d0" +dependencies = [ + "getrandom 0.2.2", +] + [[package]] name = "net2" version = "0.2.37" @@ -1957,6 +2103,29 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "ouroboros" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d5c203fe8d786d9d7bec8203cbbff3eb2cf8410c0d70cfd05b3d5f5d545da" +dependencies = [ + "ouroboros_macro", + "stable_deref_trait", +] + +[[package]] +name = "ouroboros_macro" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129943a960e6a08c7e70ca5a09f113c273fe7f10ae8420992c78293e3dffdf65" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn", +] + [[package]] name = "parking_lot" version = "0.11.1" @@ -2145,6 +2314,30 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn", + "version_check 0.9.2", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.8", + "version_check 0.9.2", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -2187,6 +2380,8 @@ dependencies = [ "chrono", "clap", "dotenv", + "fluent-langneg", + "fluent-templates", "image", "qrcode", "rand 0.8.3", @@ -2566,6 +2761,12 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.2.3" @@ -2820,6 +3021,27 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +[[package]] +name = "snafu" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn", +] + [[package]] name = "socket2" version = "0.3.19" @@ -2837,6 +3059,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spinning_top" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e529d73e80d64b5f2631f9035113347c578a1c9c7774b83a2b880788459ab36" +dependencies = [ + "lock_api", +] + [[package]] name = "sqlformat" version = "0.1.6" @@ -2941,6 +3172,12 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "standback" version = "0.2.14" @@ -3220,6 +3457,12 @@ dependencies = [ "syn", ] +[[package]] +name = "tinystr" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1" + [[package]] name = "tinyvec" version = "1.1.1" @@ -3365,6 +3608,15 @@ dependencies = [ "trust-dns-proto", ] +[[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.12.0" @@ -3398,6 +3650,49 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" +[[package]] +name = "unic-langid" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5" +dependencies = [ + "unic-langid-impl", + "unic-langid-macros", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d" +dependencies = [ + "tinystr", +] + +[[package]] +name = "unic-langid-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5" +dependencies = [ + "proc-macro-hack", + "tinystr", + "unic-langid-impl", + "unic-langid-macros-impl", +] + +[[package]] +name = "unic-langid-macros-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29396ffd97e27574c3e01368b1a64267d3064969e4848e2e130ff668be9daa9f" +dependencies = [ + "proc-macro-hack", + "quote 1.0.8", + "syn", + "unic-langid-impl", +] + [[package]] name = "unic-segment" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 2757899..9f9640a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,8 @@ image = "0.23" rand="0.8" rpassword = "5.0" clap = "2.33" +fluent-templates = { version = "0.6", features = ["tera"] } +fluent-langneg ="0.13" [build-dependencies] actix-web-static-files = "3.0" diff --git a/locales/core.ftl b/locales/core.ftl new file mode 100644 index 0000000..42ebace --- /dev/null +++ b/locales/core.ftl @@ -0,0 +1,6 @@ +list-links = List of existing links +add-link = Add a new link +invite-user = Invite a new user +list-users = List of existing users +welcome-user = Wellcome {$username} +logout = Logout diff --git a/locales/de/main.ftl b/locales/de/main.ftl new file mode 100644 index 0000000..3b9f3c8 --- /dev/null +++ b/locales/de/main.ftl @@ -0,0 +1,6 @@ +list-links = Link Liste +add-link = Link hinzufügen +invite-user = Benutzer einladen +list-users = Liste der Benutzer +welcome-user = Herzlich willkommen {$username} +logout = Abmelden \ No newline at end of file diff --git a/locales/en/main.ftl b/locales/en/main.ftl new file mode 100644 index 0000000..42ebace --- /dev/null +++ b/locales/en/main.ftl @@ -0,0 +1,6 @@ +list-links = List of existing links +add-link = Add a new link +invite-user = Invite a new user +list-users = List of existing users +welcome-user = Wellcome {$username} +logout = Logout diff --git a/migrations/20210318172317_initialmigration.up.sql b/migrations/20210318172317_initialmigration.sql similarity index 100% rename from migrations/20210318172317_initialmigration.up.sql rename to migrations/20210318172317_initialmigration.sql diff --git a/migrations/20210330103013_addinglanguage.sql b/migrations/20210330103013_addinglanguage.sql new file mode 100644 index 0000000..ef49454 --- /dev/null +++ b/migrations/20210330103013_addinglanguage.sql @@ -0,0 +1,4 @@ +-- Add migration script here + +ALTER TABLE users +ADD COLUMN language Text NOT NULL DEFAULT "en"; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9171ffe..c494df2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ use std::{fmt::Display, path::PathBuf, str::FromStr}; use actix_identity::{CookieIdentityPolicy, IdentityService}; use actix_web::{web, App, HttpResponse, HttpServer}; +use fluent_templates::{static_loader, FluentLoader}; use qrcode::types::QrError; use sqlx::{Pool, Sqlite}; use tera::Tera; @@ -188,9 +189,20 @@ impl ServerConfig { } include!(concat!(env!("OUT_DIR"), "/generated.rs")); + +static_loader! { + static LOCALES = { + locales: "./locales", + fallback_language: "en", + }; +} + fn build_tera() -> Tera { let mut tera = Tera::default(); + // Add translation support + tera.register_function("fluent", FluentLoader::new(&*LOCALES)); + tera.add_raw_templates(vec![ ("admin.html", include_str!("../templates/admin.html")), ("base.html", include_str!("../templates/base.html")), diff --git a/src/models.rs b/src/models.rs index 43605e0..d1e85cd 100644 --- a/src/models.rs +++ b/src/models.rs @@ -11,6 +11,7 @@ pub struct User { pub email: String, pub password: String, pub role: i64, + pub language: String, } impl User { diff --git a/src/queries.rs b/src/queries.rs index 8575845..849f05f 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -81,6 +81,7 @@ pub(crate) async fn list_all_allowed( users.username as usern, users.email as uemail, users.role as urole, + users.language as ulang, count(clicks.id) as counter from links @@ -107,6 +108,7 @@ pub(crate) async fn list_all_allowed( email: v.get("uemail"), password: "invalid".to_owned(), role: v.get("urole"), + language: v.get("ulang"), }, clicks: Count { number: v.get("counter"), /* count is never None */ @@ -244,6 +246,7 @@ pub(crate) async fn update_user( email: data.email.clone(), password, role: unmodified_user.role, + language: unmodified_user.language, }; new_user.update_user(server_config).await?; let changed_user = User::get_user(uid, server_config).await?; diff --git a/templates/admin.html b/templates/admin.html index 649b3cd..fe55a15 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -11,13 +11,16 @@