diff --git a/Cargo.lock b/Cargo.lock index 375a1a2..6d16440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,9 +69,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -116,9 +116,9 @@ checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002" [[package]] name = "async-channel" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" dependencies = [ "concurrent-queue", "event-listener", @@ -137,23 +137,23 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" dependencies = [ + "async-lock", "async-task", "concurrent-queue", "fastrand", "futures-lite", - "once_cell", "slab", ] [[package]] name = "async-global-executor" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da5b41ee986eed3f524c380e6d64965aea573882a8907682ad100f7859305ca" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ "async-channel", "async-executor", @@ -182,31 +182,32 @@ dependencies = [ [[package]] name = "async-io" -version = "1.9.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" +checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" dependencies = [ + "async-lock", "autocfg", "concurrent-queue", "futures-lite", "libc", "log", - "once_cell", "parking", "polling", "slab", "socket2", "waker-fn", - "winapi", + "windows-sys", ] [[package]] name = "async-lock" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" dependencies = [ "event-listener", + "futures-lite", ] [[package]] @@ -217,20 +218,20 @@ checksum = "72faff1fdc615a0199d7bf71e6f389af54d46a66e9beb5d76c39e48eda93ecce" [[package]] name = "async-process" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c" +checksum = "6381ead98388605d0d9ff86371043b5aa922a3905824244de40dc263a14fcba4" dependencies = [ "async-io", + "async-lock", "autocfg", "blocking", "cfg-if", "event-listener", "futures-lite", "libc", - "once_cell", "signal-hook", - "winapi", + "windows-sys", ] [[package]] @@ -303,9 +304,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.58" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" dependencies = [ "proc-macro2", "quote", @@ -373,11 +374,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "blake2" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" +checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -400,16 +401,16 @@ dependencies = [ [[package]] name = "blocking" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" dependencies = [ "async-channel", + "async-lock", "async-task", "atomic-waker", "fastrand", "futures-lite", - "once_cell", ] [[package]] @@ -435,21 +436,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" - -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" [[package]] name = "cfg-if" @@ -459,9 +454,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "num-integer", @@ -503,9 +498,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.18" +version = "4.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b" +checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" dependencies = [ "bitflags", "clap_derive", @@ -515,9 +510,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.18" +version = "4.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" dependencies = [ "heck", "proc-macro-error", @@ -557,11 +552,11 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.4" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" dependencies = [ - "cache-padded", + "crossbeam-utils", ] [[package]] @@ -629,9 +624,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.11" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg", "cfg-if", @@ -642,9 +637,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] @@ -690,9 +685,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.80" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" +checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" dependencies = [ "cc", "cxxbridge-flags", @@ -702,9 +697,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.80" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" +checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" dependencies = [ "cc", "codespan-reporting", @@ -717,15 +712,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.80" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" +checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" [[package]] name = "cxxbridge-macro" -version = "1.0.80" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" +checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" dependencies = [ "proc-macro2", "quote", @@ -734,9 +729,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" +checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" dependencies = [ "darling_core", "darling_macro", @@ -744,9 +739,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f" +checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" dependencies = [ "fnv", "ident_case", @@ -758,9 +753,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" +checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" dependencies = [ "darling_core", "quote", @@ -777,7 +772,7 @@ dependencies = [ "hashbrown", "lock_api", "once_cell", - "parking_lot_core 0.9.4", + "parking_lot_core 0.9.5", ] [[package]] @@ -828,9 +823,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", "crypto-common", @@ -863,6 +858,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "displaydoc" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.8.0" @@ -918,6 +924,40 @@ dependencies = [ "web-sys", ] +[[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 = "fnv" version = "1.0.7" @@ -1137,9 +1177,9 @@ dependencies = [ [[package]] name = "gloo-timers" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +checksum = "98c4a8d6391675c6b2ee1a6c8d06e8e2d03605c44cec1270675985a4c2a5500b" dependencies = [ "futures-channel", "futures-core", @@ -1286,9 +1326,9 @@ checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -1310,9 +1350,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d" dependencies = [ "http", "hyper", @@ -1323,9 +1363,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.51" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1381,9 +1421,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -1406,10 +1446,29 @@ dependencies = [ ] [[package]] -name = "ipnet" -version = "2.5.0" +name = "intl-memoizer" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +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 = "ipnet" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" [[package]] name = "itertools" @@ -1470,9 +1529,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.136" +version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55edcf6c0bb319052dea84732cf99db461780fd5e8d3eb46ab6ff312ab31f197" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" [[package]] name = "link-cplusplus" @@ -1601,9 +1660,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] @@ -1647,9 +1706,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ "hermit-abi", "libc", @@ -1657,9 +1716,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "opaque-debug" @@ -1669,9 +1728,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "parking" @@ -1706,9 +1765,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" dependencies = [ "cfg-if", "libc", @@ -1745,9 +1804,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.4.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" +checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" dependencies = [ "thiserror", "ucd-trie", @@ -1755,9 +1814,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.4.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2" +checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344" dependencies = [ "pest", "pest_generator", @@ -1765,9 +1824,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.4.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db" +checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c" dependencies = [ "pest", "pest_meta", @@ -1778,9 +1837,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.4.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d" +checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20" dependencies = [ "once_cell", "pest", @@ -1866,16 +1925,16 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "polling" -version = "2.4.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2" +checksum = "166ca89eb77fd403230b9c156612965a81e094ec6ec3aa13663d4c8b113fa748" dependencies = [ "autocfg", "cfg-if", "libc", "log", "wepoll-ffi", - "winapi", + "windows-sys", ] [[package]] @@ -1891,9 +1950,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" @@ -2053,9 +2112,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", @@ -2064,15 +2123,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" dependencies = [ "base64", "bytes", @@ -2130,9 +2189,20 @@ checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e" [[package]] name = "rpassword" -version = "7.1.0" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c9f5d2a0c3e2ea729ab3706d22217177770654c3ef5056b68b69d07332d3f5" +checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +dependencies = [ + "libc", + "rtoolbox", + "winapi", +] + +[[package]] +name = "rtoolbox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" dependencies = [ "libc", "winapi", @@ -2235,6 +2305,12 @@ dependencies = [ "toml", ] +[[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" @@ -2302,6 +2378,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "self_cell" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" + [[package]] name = "semver" version = "0.9.0" @@ -2319,18 +2401,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.147" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" dependencies = [ "proc-macro2", "quote", @@ -2348,9 +2430,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ "itoa", "ryu", @@ -2397,7 +2479,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -2427,7 +2509,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -2607,9 +2689,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" dependencies = [ "proc-macro2", "quote", @@ -2748,6 +2830,15 @@ dependencies = [ "syn", ] +[[package]] +name = "tinystr" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aeafdfd935e4a7fe16a91ab711fa52d54df84f9c8f7ca5837a9d1d902ef4c2" +dependencies = [ + "displaydoc", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2765,9 +2856,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.21.2" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" dependencies = [ "autocfg", "bytes", @@ -2783,9 +2874,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -2892,6 +2983,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[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 = "typed-sled" version = "0.2.0" @@ -2947,6 +3047,49 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" +[[package]] +name = "unic-langid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +dependencies = [ + "unic-langid-impl", + "unic-langid-macros", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" +dependencies = [ + "tinystr", +] + +[[package]] +name = "unic-langid-macros" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "055e618bf694161ffff0466d95cef3e1a5edc59f6ba1888e97801f2b4ebdc4fe" +dependencies = [ + "proc-macro-hack", + "tinystr", + "unic-langid-impl", + "unic-langid-macros-impl", +] + +[[package]] +name = "unic-langid-macros-impl" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5cdec05b907f4e2f6843f4354f4ce6a5bebe1a56df320a49134944477ce4d8" +dependencies = [ + "proc-macro-hack", + "quote", + "syn", + "unic-langid-impl", +] + [[package]] name = "unic-segment" version = "0.9.0" @@ -3039,9 +3182,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" dependencies = [ "getrandom 0.2.8", ] @@ -3207,6 +3350,9 @@ dependencies = [ "clap", "crossbeam-channel", "directories", + "fluent-bundle", + "fluent-langneg", + "intl-memoizer", "log", "matrix-sdk", "rand 0.8.5", @@ -3221,6 +3367,7 @@ dependencies = [ "tokio", "toml_edit", "typed-sled", + "unic-langid", ] [[package]] @@ -3365,9 +3512,9 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index c5bd4d0..3a4fdc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,20 +10,24 @@ edition = "2021" [dependencies] argon2 = "0.4.1" base64 = "0.13.1" -clap = { version = "4.0.18", default-features = false, features = ["derive", "std"] } +clap = { version = "4.0.29", default-features = false, features = ["derive", "error-context", "help", "std", "usage"] } crossbeam-channel = "0.5.6" directories = "4.0.1" +fluent-bundle = "0.15.2" +fluent-langneg = "0.13.0" +intl-memoizer = "0.5.1" log = "0.4.17" matrix-sdk = { version = "0.6.2", default-features = false, features = ["rustls-tls"] } rand = "0.8.5" rand_core = { version = "0.6.4", features = ["std"] } -rpassword = "7.1.0" -serde = { version = "1.0.147", features = ["derive", "rc"] } +rpassword = "7.2.0" +serde = { version = "1.0.148", features = ["derive", "rc"] } sha2 = "0.10.6" sled = "0.34.7" static-rc = "0.6.1" tera = { version = "1.17.1", features = ["builtins", "date-locale"] } tide = { version = "0.16.0", default-features = false, features = ["h1-server", "cookies", "logger"] } -tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.22.0", features = ["macros", "rt-multi-thread"] } toml_edit = { version = "0.15.0", features = ["easy"] } typed-sled = "0.2.0" +unic-langid = { version = "0.9.1", features = ["macros"] } diff --git a/README.md b/README.md index e00c5cc..7c119de 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ Rust webserver for comments, that you can easily embed in a website. * Admin approval * Admin notification on new comment via Matrix * Embedded one-file webserver -* [Tera](https://github.com/Keats/tera) templates +* Customizable [Tera](https://github.com/Keats/tera) templates * Comment frequency limit per IP +* i18n ## Use diff --git a/locales/en.ftl b/locales/en.ftl new file mode 100644 index 0000000..611d147 --- /dev/null +++ b/locales/en.ftl @@ -0,0 +1,14 @@ +admin-comment-approve = Approve +admin-comment-edit = Edit +admin-comment-remove = Remove +admin_login-password_prompt = Password: +admin_login-submit_button = Login +admin_login-title = Admin login | Comments +error-antispam = The edition quota from your IP is reached. You will be unblocked in { $antispam_timeout }s. +error-list = Whoops, the following error occurred: +comment_form-author = Your name: +comment_form-email = Your email: +comment_form-edit_button = Edit comment +comment_form-new_button = Post comment +comment_form-text = Your comment: +title = Comments diff --git a/locales/fr.ftl b/locales/fr.ftl new file mode 100644 index 0000000..2bfd3d1 --- /dev/null +++ b/locales/fr.ftl @@ -0,0 +1,14 @@ +admin-comment-approve = Approuver +admin-comment-edit = Modifier +admin-comment-remove = Supprimer +admin_login-password_prompt = Mot de passe : +admin_login-submit_button = S'authentifier +admin_login-title = Authentification admin | Commentaires +error-antispam = Le quota d'édition de votre adresse IP est atteint, elle sera débloquée dans { $antispam_timeout }s. +error-list = Oups, l'erreur suivante est survenue : +comment_form-author = Votre nom : +comment_form-email = Votre e-mail : +comment_form-edit_button = Modifier +comment_form-new_button = Envoyer +comment_form-text = Votre commentaire : +title = Commentaires diff --git a/src/config.rs b/src/config.rs index d93392e..ee21dd8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -36,8 +36,9 @@ pub struct Config { pub cookies_https_only: bool, #[serde(default = "Config::default_cookies_domain")] pub cookies_domain: Option, - #[serde(default = "Config::default_lang")] - pub lang: String, + /// Format: "language_REGION" + #[serde(default = "Config::default_default_lang")] + pub default_lang: String, #[serde(default = "Config::default_listen")] pub listen: SocketAddr, /// Send a matrix message on new comment @@ -102,8 +103,8 @@ impl Config { fn default_cookies_domain() -> Option { None } - fn default_lang() -> String { - "en_GB".into() + fn default_default_lang() -> String { + "en_US".into() } fn default_listen() -> SocketAddr { SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 31720) @@ -152,7 +153,7 @@ impl Default for Config { comment_text_max_len: Self::default_comment_text_max_len(), cookies_https_only: Self::default_cookies_https_only(), cookies_domain: Self::default_cookies_domain(), - lang: Self::default_lang(), + default_lang: Self::default_default_lang(), listen: Self::default_listen(), matrix_notify: Self::default_matrix_notify(), matrix_password: Self::default_matrix_password(), diff --git a/src/locales.rs b/src/locales.rs new file mode 100644 index 0000000..f68f2d2 --- /dev/null +++ b/src/locales.rs @@ -0,0 +1,103 @@ +use crate::config::Config; + +use fluent_bundle::{bundle::FluentBundle, FluentArgs, FluentResource}; +use fluent_langneg::{ + accepted_languages, negotiate::filter_matches, negotiate_languages, NegotiationStrategy, +}; +use intl_memoizer::concurrent::IntlLangMemoizer; +use log::error; +use std::{borrow::Cow, collections::HashMap, ops::Deref, str::FromStr}; +use unic_langid::{langid, LanguageIdentifier}; + +static LOCALE_FILES: &[(LanguageIdentifier, &str)] = &[ + (langid!("en"), include_str!("../locales/en.ftl")), + (langid!("fr"), include_str!("../locales/fr.ftl")), +]; + +pub struct Locales { + bundles: HashMap>, + pub default_lang: LanguageIdentifier, + langs: Vec, +} + +impl Locales { + pub fn new(config: &Config) -> Self { + let mut langs = Vec::new(); + Self { + bundles: LOCALE_FILES + .iter() + .map(|(lang, raw)| { + let mut bundle = FluentBundle::new_concurrent(vec![lang.clone()]); + bundle + .add_resource( + FluentResource::try_new(raw.to_string()).unwrap_or_else(|e| { + panic!("Failed parsing `{lang}` locale: {e:?}") + }), + ) + .unwrap(); + langs.push(lang.clone()); + (lang.clone(), bundle) + }) + .collect::>>( + ), + default_lang: filter_matches( + &[LanguageIdentifier::from_str(&config.default_lang) + .expect("Invalid default language")], + &langs, + NegotiationStrategy::Filtering, + ) + .get(0) + .expect("Unavailable default language") + .deref() + .clone(), + langs, + } + } + + // TODO fix fluent-langneg's weird API + pub fn tr<'a>( + &'a self, + langs: &[LanguageIdentifier], + key: &str, + args: Option<&'a FluentArgs>, + ) -> Option> { + for prefered_lang in negotiate_languages( + langs, + &self.langs, + Some(&self.default_lang), + NegotiationStrategy::Filtering, + ) { + if let Some(bundle) = self.bundles.get(prefered_lang.as_ref()) { + println!("got bundle"); + if let Some(message) = bundle.get_message(key) { + let mut errors = Vec::new(); + let ret = bundle.format_pattern(message.value().unwrap(), args, &mut errors); + for error in errors { + error!("Formatting message `{key}` in lang `{prefered_lang}`: {error}"); + } + return Some(ret); + } + } + } + None + } +} + +pub fn get_client_langs(req: &tide::Request) -> Vec { + if let Some(header) = req.header("Accept-Language") { + accepted_languages::parse(header.as_str()) + } else { + println!("NO HEADER"); + Vec::new() + } +} + +/// Get the first language that is likely to be usable with chrono +pub fn get_time_lang(langs: &[LanguageIdentifier]) -> Option { + for lang in langs { + if let Some(region) = &lang.region { + return Some(format!("{}_{}", lang.language.as_str(), region.as_str())); + } + } + None +} diff --git a/src/main.rs b/src/main.rs index d02f6c8..318f141 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod cli; mod config; mod db; mod helpers; +mod locales; mod notify; mod queries; mod server; @@ -13,6 +14,9 @@ use argon2::{ Argon2, }; use clap::Parser; +use log::warn; +use std::{collections::HashMap, str::FromStr}; +use unic_langid::LanguageIdentifier; #[tokio::main] async fn main() { @@ -28,9 +32,38 @@ async fn main() { // These will never be dropped nor mutated let templates = Box::leak(Box::new(templates)); let config = Box::leak(Box::new(config)); + let locales = Box::leak(Box::new(locales::Locales::new(config))); + + // TODO args + templates.tera.register_function( + "tr", + Box::new( + |args: &HashMap| -> tera::Result { + let langs = if let Some(tera::Value::Array(langs)) = args.get("l") { + langs + .iter() + .filter_map(|lang| lang.as_str()) + .filter_map(|lang| LanguageIdentifier::from_str(lang).ok()) + .collect() + } else { + vec![locales.default_lang.clone()] + }; + let key = args + .get("k") + .ok_or_else(|| tera::Error::from("Missing argument `k`"))? + .as_str() + .ok_or_else(|| tera::Error::from("Argument `k` must be string"))?; + let res = locales.tr(&langs, key, None); + if res.is_none() { + warn!("(calling `tr` in template) translation key `{key}` not found"); + } + Ok(res.into()) + }, + ), + ); tokio::spawn(cleaner::run_cleaner(config, dbs.clone())); - server::run_server(config, dbs, templates).await; + server::run_server(config, dbs, templates, locales).await; } cli::MainSubcommand::Psw => { let mut config = config::read_config(&opt.opt.dir.0); diff --git a/src/server.rs b/src/server.rs index 33b8cdb..ddb0030 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,11 +1,20 @@ -use crate::{config::*, db::*, helpers, queries::*, templates::*}; +#![allow(clippy::too_many_arguments)] + +use crate::{config::*, db::*, helpers, locales::*, queries::*, templates::*}; use argon2::{Argon2, PasswordHash, PasswordVerifier}; use crossbeam_channel::Sender; +use fluent_bundle::FluentArgs; use log::{error, warn}; use tera::Context; +use unic_langid::LanguageIdentifier; -pub async fn run_server(config: &'static Config, dbs: Dbs, templates: &'static Templates) { +pub async fn run_server( + config: &'static Config, + dbs: Dbs, + templates: &'static Templates, + locales: &'static Locales, +) { tide::log::start(); let (notify_send, notify_recv) = crossbeam_channel::bounded(10); @@ -15,17 +24,36 @@ pub async fn run_server(config: &'static Config, dbs: Dbs, templates: &'static T app.at(&format!("{}t/:topic", config.root_url)).get({ let dbs = dbs.clone(); move |req: tide::Request<()>| { - serve_comments(req, config, templates, dbs.clone(), Context::new(), 200) + let client_langs = get_client_langs(&req); + serve_comments( + req, + config, + templates, + dbs.clone(), + client_langs, + Context::new(), + 200, + ) } }); app.at(&format!("{}t/:topic", config.root_url)).post({ let dbs = dbs.clone(); move |req: tide::Request<()>| { - handle_post_comments(req, config, templates, dbs.clone(), notify_send.clone()) + handle_post_comments( + req, + config, + templates, + dbs.clone(), + locales, + notify_send.clone(), + ) } }); app.at(&format!("{}admin", config.root_url)) - .get(move |req: tide::Request<()>| serve_admin_login(req, config, templates)); + .get(move |req: tide::Request<()>| { + let client_langs = get_client_langs(&req); + serve_admin_login(req, config, templates, client_langs) + }); app.at(&format!("{}admin", config.root_url)).post({ let dbs = dbs.clone(); move |req: tide::Request<()>| handle_post_admin(req, config, templates, dbs.clone()) @@ -38,6 +66,7 @@ async fn serve_comments<'a>( config: &Config, templates: &Templates, dbs: Dbs, + client_langs: Vec, mut context: Context, status_code: u16, ) -> tide::Result { @@ -53,6 +82,18 @@ async fn serve_comments<'a>( context.insert("config", &config); context.insert("admin", &admin); + let time_lang = get_time_lang(&client_langs); + context.insert( + "time_lang", + time_lang.as_ref().unwrap_or(&config.default_lang), + ); + context.insert( + "l", + &client_langs + .iter() + .map(|lang| lang.language.as_str()) + .collect::>(), + ); if admin { if let Ok(query) = req.query::() { @@ -120,10 +161,23 @@ async fn serve_admin<'a>( config: &Config, templates: &Templates, dbs: Dbs, + client_langs: &[LanguageIdentifier], ) -> tide::Result { let mut context = Context::new(); context.insert("config", &config); context.insert("admin", &true); + let time_lang = get_time_lang(client_langs); + context.insert( + "time_lang", + time_lang.as_ref().unwrap_or(&config.default_lang), + ); + context.insert( + "l", + &client_langs + .iter() + .map(|lang| lang.language.as_str()) + .collect::>(), + ); context.insert( "comments", @@ -164,9 +218,22 @@ async fn serve_admin_login( _req: tide::Request<()>, config: &Config, templates: &Templates, + client_langs: Vec, ) -> tide::Result { let mut context = Context::new(); context.insert("config", &config); + let time_lang = get_time_lang(&client_langs); + context.insert( + "time_lang", + time_lang.as_ref().unwrap_or(&config.default_lang), + ); + context.insert( + "l", + &client_langs + .iter() + .map(|lang| lang.language.as_str()) + .collect::>(), + ); Ok(tide::Response::builder(200) .content_type(tide::http::mime::HTML) @@ -179,12 +246,15 @@ async fn handle_post_comments( config: &Config, templates: &Templates, dbs: Dbs, + locales: &Locales, notify_send: Sender<()>, ) -> tide::Result { let admin = req.cookie("admin").map_or(false, |psw| { check_admin_password_hash(config, &String::from(psw.value())) }); + let client_langs = get_client_langs(&req); + let client_addr = if !admin && config.antispam_enable { match helpers::get_client_addr(config, &req) { Some(Ok(addr)) => { @@ -222,10 +292,19 @@ async fn handle_post_comments( if let Some(antispam_timeout) = helpers::antispam_check_client_mutation(client_addr, &dbs, config).unwrap() { - errors.push(format!( - "The edition quota from your IP is reached. You will be unblocked in {}s.", - antispam_timeout - )); + errors.push( + locales + .tr( + &client_langs, + "error-antispam", + Some(&FluentArgs::from_iter([( + "antispam_timeout", + antispam_timeout, + )])), + ) + .unwrap() + .into_owned(), + ); } } @@ -283,21 +362,33 @@ async fn handle_post_comments( return Err(tide::Error::from_str(404, "Not found")); }; - if let Some(client_addr) = &client_addr { + // We're admin + /*if let Some(client_addr) = &client_addr { if let Some(antispam_timeout) = helpers::antispam_check_client_mutation(client_addr, &dbs, config).unwrap() { - errors.push(format!( - "The edition quota from your IP is reached. You will be unblocked in {}s.", - antispam_timeout - )); + let client_langs = get_client_langs(&req); + errors.push( + locales + .tr( + &client_langs, + "error-antispam", + Some(&FluentArgs::from_iter([( + "antispam_timeout", + antispam_timeout, + )])), + ) + .unwrap() + .into_owned(), + ); } - } + }*/ if errors.is_empty() { - if let Some(client_addr) = &client_addr { + // We're admin + /*if let Some(client_addr) = &client_addr { helpers::antispam_update_client_mutation(client_addr, &dbs).unwrap(); - } + }*/ let time = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) @@ -328,6 +419,7 @@ async fn handle_post_comments( config, templates, dbs, + client_langs, context, if errors.is_empty() { 200 } else { 400 }, ) @@ -344,31 +436,40 @@ async fn handle_post_admin( if check_admin_password(config, &String::from(psw.value())).is_some() { #[allow(clippy::match_single_binding)] match req.body_form::().await? { - _ => serve_admin(req, config, templates, dbs).await, + _ => { + let client_langs = get_client_langs(&req); + serve_admin(req, config, templates, dbs, &client_langs).await + } } } else { - serve_admin_login(req, config, templates).await + let client_langs = get_client_langs(&req); + serve_admin_login(req, config, templates, client_langs).await } } else if let AdminQuery::Login(query) = req.body_form::().await? { if let Some(password_hash) = check_admin_password(config, &query.psw) { - serve_admin(req, config, templates, dbs).await.map(|mut r| { - let mut cookie = tide::http::Cookie::new("admin", password_hash); - cookie.set_http_only(Some(true)); - cookie.set_path(config.root_url.clone()); - if let Some(domain) = &config.cookies_domain { - cookie.set_domain(domain.clone()); - } - if config.cookies_https_only { - cookie.set_secure(Some(true)); - } - r.insert_cookie(cookie); - r - }) + let client_langs = get_client_langs(&req); + serve_admin(req, config, templates, dbs, &client_langs) + .await + .map(|mut r| { + let mut cookie = tide::http::Cookie::new("admin", password_hash); + cookie.set_http_only(Some(true)); + cookie.set_path(config.root_url.clone()); + if let Some(domain) = &config.cookies_domain { + cookie.set_domain(domain.clone()); + } + if config.cookies_https_only { + cookie.set_secure(Some(true)); + } + r.insert_cookie(cookie); + r + }) } else { - serve_admin_login(req, config, templates).await + let client_langs = get_client_langs(&req); + serve_admin_login(req, config, templates, client_langs).await } } else { - serve_admin_login(req, config, templates).await + let client_langs = get_client_langs(&req); + serve_admin_login(req, config, templates, client_langs).await } } diff --git a/templates/admin_login.html b/templates/admin_login.html index f8594b5..06d339e 100644 --- a/templates/admin_login.html +++ b/templates/admin_login.html @@ -1,14 +1,14 @@ - + - Admin login | Comments + {{ tr(l=l,k="admin_login-title")|safe }}
- +
- +
diff --git a/templates/comments.html b/templates/comments.html index 70a720b..a1363af 100644 --- a/templates/comments.html +++ b/templates/comments.html @@ -1,8 +1,8 @@ - + - Comments + {{ tr(l=l,k="title")|safe }} {% if comments_pending %} @@ -10,15 +10,15 @@ {% for comment in comments_pending %}
{{ comment.author }} - {{ comment.post_time | date(format="%F %R", locale=config.lang) }} + {{ comment.post_time | date(format="%F %R", locale=time_lang) }} {% if comment.editable %} - Edit + {{ tr(l=l,k="admin-comment-edit")|safe }} {% endif %} {% if admin and comment.needs_approval %} - Approve + {{ tr(l=l,k="admin-comment-approve")|safe }} {% endif %} {% if admin %} - Remove + {{ tr(l=l,k="admin-comment-remove")|safe }} {% endif %}

{{ comment.text }}

@@ -29,15 +29,15 @@ {% for comment in comments %}
{{ comment.author }} - {{ comment.post_time | date(format="%F %R", locale=config.lang) }} + {{ comment.post_time | date(format="%F %R", locale=time_lang) }} {% if comment.editable %} - Edit + {{ tr(l=l,k="admin-comment-edit")|safe }} {% endif %} {% if admin and comment.needs_approval %} - Approve + {{ tr(l=l,k="admin-comment-approve")|safe }} {% endif %} {% if admin %} - Remove + {{ tr(l=l,k="admin-comment-remove")|safe }} {% endif %}

{{ comment.text }}

@@ -45,25 +45,25 @@
{% if new_comment_errors %} -

Whoops, the following error occurred:

+

{{ tr(l=l,k="error-list")|safe }}

    {% for error in new_comment_errors %}
  • {{ error | safe }}
  • {% endfor %}
{% endif %} - +
- +
-
+

- +
{% if edit_comment %}
{% if edit_comment_errors %} -

Whoops, the following error occurred:

+

{{ tr(l=l,k="error-list")|safe }}

    {% for error in edit_comment_errors %}
  • {{ error | safe }}
  • @@ -71,13 +71,13 @@
{% endif %} - +
- +
-
+

- +
{% endif %}