diff --git a/CHANGELOG.md b/CHANGELOG.md index 8891f3b..e3a5a64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,28 +4,47 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.4.0] - Unreleased +[Fourth milestone](https://github.com/mrtryhard/qt-ts-tools/milestone/4). + +### Added + +- Shell auto-completion scripts can now be auto-generated when using `shell-completion ` command line option. [#32](https://github.com/mrtryhard/qt-ts-tools/issues/32) +- Localization is now supported. French and English are supported [#28](https://github.com/mrtryhard/qt-ts-tools/issues/28), [#63](https://github.com/mrtryhard/qt-ts-tools/issues/28) + +### Changed + +- Fixed a lot of clippy errors, simplified the sorting algorithm for messages [#60](https://github.com/mrtryhard/qt-ts-tools/issues/60) +- Bumped Serde, Itertools versions [#72](https://github.com/mrtryhard/qt-ts-tools/issues/72) + +### Fixed + +### Known issues + +- Command lines _errors_ are not translated for now. [#73](https://github.com/mrtryhard/qt-ts-tools/issues/73) + ## [0.3.1] - 2024-05-12 -Contains minor fixes to the command line tool. +[Fourth milestone](https://github.com/mrtryhard/qt-ts-tools/milestone/5). Contains minor fixes to the command line tool. ## Fixed -- Sorting now consider messages' id. [#42](https://github.com/mrtryhard/qt-ts-tools/issues/42) +- Sorting now consider messages' id [#42](https://github.com/mrtryhard/qt-ts-tools/issues/42) ## [0.3.0] - 2024-04-22 [Third milestone](https://github.com/mrtryhard/qt-ts-tools/milestone/3). This introduces the `merge` command and improved documentation. -## Added +### Added - Merge mechanism to merge two translation files [#24](https://github.com/mrtryhard/qt-ts-tools/issues/24) - `extra-*` fields support in `TS` and `message` nodes [#4](https://github.com/mrtryhard/qt-ts-tools/issues/4) -## Changed +### Changed - Improved command line documentation [#25](https://github.com/mrtryhard/qt-ts-tools/issues/25), [#27](https://github.com/mrtryhard/qt-ts-tools/issues/27) - Updated Clap dependencies [#26](https://github.com/mrtryhard/qt-ts-tools/issues/26) - Updated Serde dependencies -## Fixed +### Fixed ## [0.2.0] - 2024-01-01 diff --git a/Cargo.lock b/Cargo.lock index d9f6bc8..23a3481 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,54 +2,86 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys", ] +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "clap" version = "4.5.4" @@ -72,6 +104,57 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_complete_command" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183495371ea78d4c9ff638bfc6497d46fed2396e4f9c50aebc1278a4a9919a3d" +dependencies = [ + "clap", + "clap_complete", + "clap_complete_fig", + "clap_complete_nushell 0.1.11", +] + +[[package]] +name = "clap_complete_fig" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b3e65f91fabdd23cac3d57d39d5d938b4daabd070c335c006dccb866a61110" +dependencies = [ + "clap", + "clap_complete", +] + +[[package]] +name = "clap_complete_nushell" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d02bc8b1a18ee47c4d2eec3fb5ac034dc68ebea6125b1509e9ccdffcddce66e" +dependencies = [ + "clap", + "clap_complete", +] + +[[package]] +name = "clap_complete_nushell" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0e48e026ce7df2040239117d25e4e79714907420c70294a5ce4b6bbe6a7b6" +dependencies = [ + "clap", + "clap_complete", +] + [[package]] name = "clap_derive" version = "4.5.4" @@ -92,15 +175,155 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "either" -version = "1.10.0" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "fluent" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell 0.10.3", + "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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" +dependencies = [ + "thiserror", +] + +[[package]] +name = "fluent-template-macros" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d2bcae1f3ec390c50161fcf130d3228750e9ecf965618584e046d884199b83" +dependencies = [ + "flume", + "ignore", + "once_cell", + "proc-macro2", + "quote", + "syn", + "unic-langid", +] + +[[package]] +name = "fluent-templates" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197feb1e37209c6b3d29f0754b11fc070890efb2b1d761caac4e5287a200e9db" +dependencies = [ + "arc-swap", + "fluent", + "fluent-bundle", + "fluent-langneg", + "fluent-syntax", + "fluent-template-macros", + "flume", + "heck", + "ignore", + "intl-memoizer", + "log", + "once_cell", + "serde_json", + "thiserror", + "unic-langid", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + +[[package]] +name = "globset" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] [[package]] name = "heck" @@ -108,38 +331,132 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" +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 = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] [[package]] name = "qt-ts-tools" -version = "0.3.0" +version = "0.4.0" dependencies = [ "clap", + "clap_complete", + "clap_complete_command", + "clap_complete_nushell 4.5.1", + "fluent", + "fluent-templates", "itertools", + "lazy_static", "quick-xml", "serde", + "sys-locale", ] [[package]] @@ -154,50 +471,225 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "self_cell" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.4", +] + +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + [[package]] name = "serde" -version = "1.0.200" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.48" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sys-locale" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" +dependencies = [ + "libc", +] + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" +dependencies = [ + "displaydoc", +] + +[[package]] +name = "type-map" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "unic-langid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +dependencies = [ + "unic-langid-impl", + "unic-langid-macros", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +dependencies = [ + "tinystr", +] + +[[package]] +name = "unic-langid-macros" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da1cd2c042d3c7569a1008806b02039e7a4a2bdf8f8e96bd3c792434a0e275e" +dependencies = [ + "proc-macro-hack", + "tinystr", + "unic-langid-impl", + "unic-langid-macros-impl", +] + +[[package]] +name = "unic-langid-macros-impl" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" +dependencies = [ + "proc-macro-hack", + "quote", + "syn", + "unic-langid-impl", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -210,6 +702,25 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -221,13 +732,14 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -236,42 +748,48 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml index 73a72f6..caac75f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,18 +5,25 @@ repository = "https://github.com/mrtryhard/qt-ts-tools" keywords = ["qt", "translation"] homepage = "https://github.com/mrtryhard/qt-ts-tools" license = "MIT OR Apache-2.0" -version = "0.3.0" +version = "0.4.0" edition = "2021" description = "Small command line utility to manipulate Qt's translation files with diverse operations." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.5.4", features = ["derive"] } +clap = { version = "4.5.4", features = ["derive", "cargo", "string"] } +clap_complete = "4.5.2" +clap_complete_nushell = "4.5.1" +clap_complete_command = "0.5.1" +fluent = "0.16.1" +fluent-templates = "0.9.4" +itertools = "0.13.0" +lazy_static = "1.4.0" quick-xml = { version = "0.31.0", features = ["serialize"] } -serde = { version = "1.0.200", features = ["derive"] } -itertools = "0.12.1" +serde = { version = "1.0.202", features = ["derive"] } +sys-locale = "0.3.1" [profile.release] strip = true -lto = true \ No newline at end of file +lto = true diff --git a/deny.toml b/deny.toml index ae2892a..bd297fc 100644 --- a/deny.toml +++ b/deny.toml @@ -52,9 +52,7 @@ db-urls = ["https://github.com/rustsec/advisory-db"] yanked = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. -ignore = [ - #"RUSTSEC-0000-0000", -] +ignore = [] # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. @@ -69,8 +67,9 @@ ignore = [ version = 2 # List of explicitly allowed licenses allow = [ - "MIT", "Apache-2.0", + "MIT", + "Unicode-3.0" ] # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the @@ -89,6 +88,18 @@ exceptions = [ ], name = "unicode-ident", version = "*" }, ] +[[licenses.clarify]] +name = "ring" +# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses +# https://spdx.org/licenses/OpenSSL.html +# ISC - Both BoringSSL and ring use this for their new files +# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT +# license, for third_party/fiat, which, unlike other third_party directories, is +# compiled into non-test libraries, is included below." +# OpenSSL - Obviously +expression = "ISC AND MIT AND OpenSSL" +license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] + [licenses.private] # If true, ignores workspace crates that aren't published, or are only # published to private registries. diff --git a/resources/locales/en/core.ftl b/resources/locales/en/core.ftl new file mode 100644 index 0000000..a3d7f1d --- /dev/null +++ b/resources/locales/en/core.ftl @@ -0,0 +1,23 @@ +cli-about = Small command line utility to manipulate Qt's translation files with diverse operations. +cli-extract-desc = Extracts a translation type messages and contexts from the input translation file. +cli-extract-input = File path to extract translations from. +cli-extract-translation-type = Translation type list to extract into a single, valid translation output. +cli-extract-output = If specified, will produce output in a file at designated location instead of stdout. +cli-headers-commands = Commands +cli-headers-options = Options +cli-headers-usage = Usage +cli-headers-arguments = Arguments +cli-help = Prints help information. +cli-merge-desc = Merges two translation file contexts and messages into a single output. +cli-merge-input-left = File to receive the merge. +cli-merge-input-right = File to include changes from. +cli-merge-output = If specified, will produce output in a file at designated location instead of stdout. +cli-shell-completion-desc = Print a shell completion for supported shells. +cli-sort-desc = Sorts the input translation file by context, then by messages. +cli-sort-input = File path to sort translations from. +cli-sort-output = If specified, will produce output in a file at designated location instead of stdout. +cli-version = Prints the version of this tool. +open-or-parse-error = Could not open or parse input file "{ $file }". Reason: { $error }. +sort-parse-error = Could not parse input file "{ $input_path }". Reason: { $error }. +ts-error-write-output-open = Error occured while opening output file { $output_path }. Reason: { $error }. +ts-error-write-serialize = Problem occured while serializing output translation file. Reason: { $error }. diff --git a/resources/locales/fr/core.ftl b/resources/locales/fr/core.ftl new file mode 100644 index 0000000..719adc0 --- /dev/null +++ b/resources/locales/fr/core.ftl @@ -0,0 +1,23 @@ +cli-about = Petit utilitaire de ligne de commands pour manipuler les fichiers de traduction TS de Qt. +cli-extract-desc = Extrait les messages et contextes correspondants au type de tranduction spécifié. +cli-extract-input = Chemin d'accès du fichier de traduction à extraire. +cli-extract-translation-type = Liste des types de traductions à extraire. +cli-extract-output = Si spécifié, chemin d'accès du fichier de sortie. +cli-headers-commands = Commandes +cli-headers-options = Options +cli-headers-usage = Utilisation +cli-headers-arguments = Arguments +cli-help = Affiche l'aide. +cli-merge-desc = Fusionne les contextes et message des deux fichiers de traductions spécifié en un seul. +cli-merge-input-left = Fichier qui reçoit les changements. +cli-merge-input-right = Fichier qui possède les changements. +cli-merge-output = Si spécificé, chemin d'accès du fichier de sortie. +cli-shell-completion-desc = Affiche le script d'auto-complétion pour un shell choisi. +cli-sort-desc = Trie le fichier de traduction par contextes, puis messages. +cli-sort-input = Chemin d'accès du fichier de traduction à trier. +cli-sort-output = Si spécifié, chemin d'accès du fichier de sortie. +cli-version = Affiche la version de l'utilitaire. +open-or-parse-error = Échec de lecture ou décodage du fichier "{ $file }". Raison: { $error },. +sort-parse-error = Échec de lecture du fichier "{ $file }". Raison: { $error }. +ts-error-write-output-open = Erreur lors de l'ouverture en écriture du fichier "{ $file }". Raison: { $error }. +ts-error-write-serialize = Un problème est survenue lors de la sérialization du fichier. Raison: { $error }. diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..4b3b64e --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,98 @@ +use clap::{ArgAction, CommandFactory, Parser, Subcommand, ValueEnum}; +use clap_complete::{Generator, Shell}; +use clap_complete_nushell::Nushell; + +use crate::extract::{extract_main, ExtractArgs}; +use crate::locale::tr; +use crate::merge::{merge_main, MergeArgs}; +use crate::sort::{sort_main, SortArgs}; + +#[derive(Parser)] +#[command(author, + version, + about = tr("cli-about"), + disable_help_flag = true, + disable_help_subcommand = true, + disable_version_flag = true)] +pub struct Cli { + #[command(subcommand)] + command: Commands, + #[arg(short, long, action = ArgAction::Help, help = tr("cli-help"), help_heading = tr("cli-headers-options"))] + pub help: bool, + #[arg(short, long, short_alias = 'v', action = ArgAction::Version, help = tr("cli-version"))] + version: bool, +} + +#[derive(Subcommand)] +#[command(subcommand_help_heading = tr("cli-headers-commands"), + next_help_heading = tr("cli-headers-options"))] +enum Commands { + #[command(about = tr("cli-sort-desc"))] + Sort(SortArgs), + #[command(about = tr("cli-extract-desc"))] + Extract(ExtractArgs), + #[command(about = tr("cli-merge-desc"))] + Merge(MergeArgs), + #[command(name = "shell-completion", about = tr("cli-shell-completion-desc"), disable_help_flag = true)] + ShellCompletion { + #[arg(value_enum)] + shell: clap_complete_command::Shell, + #[arg(short, long, action = ArgAction::Help, help = tr("cli-help"), help_heading = tr("cli-headers-options"))] + help: bool, + }, +} + +#[derive(Debug, Clone, ValueEnum)] +#[value(rename_all = "lower")] +pub enum GenShell { + Bash, + Elvish, + Fish, + Nushell, + PowerShell, + Zsh, +} + +impl Generator for GenShell { + fn file_name(&self, name: &str) -> String { + match self { + // clap_complete + Self::Bash => Shell::Bash.file_name(name), + Self::Elvish => Shell::Elvish.file_name(name), + Self::Fish => Shell::Fish.file_name(name), + Self::PowerShell => Shell::PowerShell.file_name(name), + Self::Zsh => Shell::Zsh.file_name(name), + + // clap_complete_nushell + Self::Nushell => Nushell.file_name(name), + } + } + + fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::prelude::Write) { + match self { + // clap_complete + Self::Bash => Shell::Bash.generate(cmd, buf), + Self::Elvish => Shell::Elvish.generate(cmd, buf), + Self::Fish => Shell::Fish.generate(cmd, buf), + Self::PowerShell => Shell::PowerShell.generate(cmd, buf), + Self::Zsh => Shell::Zsh.generate(cmd, buf), + + // clap_complete_nushell + Self::Nushell => Nushell.generate(cmd, buf), + } + } +} + +pub fn get_cli_result() -> Result<(), String> { + let cli = Cli::parse(); + + match cli.command { + Commands::Sort(args) => sort_main(&args), + Commands::Extract(args) => extract_main(&args), + Commands::Merge(args) => merge_main(&args), + Commands::ShellCompletion { shell, help: _ } => { + shell.generate(&mut Cli::command(), &mut std::io::stdout()); + Ok(()) + } + } +} diff --git a/src/extract.rs b/src/extract.rs index 2574835..5733a2e 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -1,18 +1,24 @@ +use clap::{ArgAction, Args}; + +use crate::locale::{tr, tr_args}; use crate::ts; use crate::ts::{TSNode, TranslationType}; -use clap::Args; /// Extracts a translation type messages and contexts from the input translation file. #[derive(Args)] +#[command(disable_help_flag = true)] pub struct ExtractArgs { - /// File path to extract translations from. + /// File path to exthelpract translations from. + #[arg(help = tr("cli-extract-input"), help_heading = tr("cli-headers-arguments"))] pub input_path: String, /// Translation type list to extract into a single, valid translation output. - #[arg(short('t'), long, value_enum, num_args = 1..)] + #[arg(short('t'), long, value_enum, num_args = 1.., help = tr("cli-extract-translation-type"), help_heading = tr("cli-headers-arguments"))] pub translation_type: Vec, /// If specified, will produce output in a file at designated location instead of stdout. - #[arg(short, long)] + #[arg(short, long, help = tr("cli-extract-output"), help_heading = tr("cli-headers-options"))] pub output_path: Option, + #[arg(short, long, action = ArgAction::Help, help = tr("cli-help"), help_heading = tr("cli-headers-options"))] + pub help: bool, } #[derive(clap::ValueEnum, PartialEq, Debug, Clone)] @@ -33,19 +39,27 @@ pub fn extract_main(args: &ExtractArgs) -> Result<(), String> { .translation_type .iter() .map(to_translation_type) - .collect(); + .collect::>(); retain_ts_node(&mut ts_node, &wanted_types); ts::write_to_output(&args.output_path, &ts_node) } - Err(e) => Err(format!( - "Could not parse input file \"{}\". Error: {e:?}.", - args.input_path + Err(e) => Err(tr_args( + "open-or-parse-error", + [ + ("file", args.input_path.as_str().into()), + ("error", e.to_string().into()), + ] + .into(), )), } } - Err(e) => Err(format!( - "Could not open or parse input file \"{}\". Error: {e:?}", - args.input_path + Err(e) => Err(tr_args( + "open-or-parse-error", + [ + ("file", args.input_path.as_str().into()), + ("error", e.to_string().into()), + ] + .into(), )), } } @@ -59,25 +73,24 @@ fn to_translation_type(value: &TranslationTypeArg) -> TranslationType { } /// Keep only the desired translation type from the node (if it matches one in `wanted_types`). -fn retain_ts_node(ts_node: &mut TSNode, wanted_types: &Vec) { +fn retain_ts_node(ts_node: &mut TSNode, wanted_types: &[TranslationType]) { ts_node.contexts.retain_mut(|context| { context.messages.retain(|message| { message.translation.as_ref().is_some_and(|translation| { translation .translation_type .as_ref() - .is_some_and(|translation_type| wanted_types.contains(&translation_type)) + .is_some_and(|translation_type| wanted_types.contains(translation_type)) }) }); - context.messages.len() > 0 + !context.messages.is_empty() }); } #[cfg(test)] mod extract_test { use super::*; - use quick_xml; #[test] fn test_extract_ts_node() { diff --git a/src/locale.rs b/src/locale.rs new file mode 100644 index 0000000..2619567 --- /dev/null +++ b/src/locale.rs @@ -0,0 +1,66 @@ +use std::collections::HashMap; +use std::str::FromStr; + +use fluent::FluentValue; +use fluent_templates::{static_loader, LanguageIdentifier, Loader}; +use lazy_static::lazy_static; + +static_loader! { + pub(crate) static LOCALES = { + locales: "./resources/locales", + fallback_language: "en", + customise: |bundle| bundle.set_use_isolating(false), + }; +} + +lazy_static! { + static ref CURRENT_LANG: LanguageIdentifier = { + LanguageIdentifier::from_str( + sys_locale::get_locale() + .unwrap_or("en".to_string()) + .as_str(), + ) + .expect("No locale found") + }; +} + +/// Look up text identifier in the translation dictionary for the **current system** locale. +/// +/// ### Parameters +/// * `text_id`: Identifier to look up into the `.ftl` translation file. +/// +/// ### Returns +/// +/// Returns the translation corresponding to `text_id` translated, otherwise falls back on the english +/// translation. +/// +/// ### Example +/// +/// ```rust +/// tr("some-text-id") +/// ``` +#[allow(unused)] +pub fn tr(text_id: &str) -> String { + LOCALES.lookup(&CURRENT_LANG, text_id) +} + +/// Look up text identifier in the translation dictionary for the **current system** locale. +/// Supports passing arguments. +/// +/// ### Parameters +/// * `text_id`: Identifier to look up into the `.ftl` translation file. +/// * `args`: Argument to substitute in the translated string. +/// +/// ### Returns +/// +/// Returns the translation corresponding to `text_id` translated, otherwise falls back on the english +/// translation. +/// +/// ### Example +/// +/// ```rust +/// tr_args("some-text-id", [("variablename", value.into())].into()) +/// ``` +pub fn tr_args>(text_id: &str, args: HashMap) -> String { + LOCALES.lookup_with_args(&CURRENT_LANG, text_id, &args) +} diff --git a/src/main.rs b/src/main.rs index b36c6fa..d3b5572 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,37 +1,14 @@ +use crate::cli::get_cli_result; + +mod cli; mod extract; +mod locale; mod merge; mod sort; mod ts; -use crate::extract::{extract_main, ExtractArgs}; -use crate::merge::{merge_main, MergeArgs}; -use crate::sort::{sort_main, SortArgs}; -use clap::{Parser, Subcommand}; - -#[derive(Parser)] -#[command(author, version, about)] -struct Cli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand)] -enum Commands { - Sort(SortArgs), - Extract(ExtractArgs), - Merge(MergeArgs), -} - -fn get_cli_result(cli: Cli) -> Result<(), String> { - match &cli.command { - Commands::Sort(args) => sort_main(&args), - Commands::Extract(args) => extract_main(&args), - Commands::Merge(args) => merge_main(&args), - } -} - fn main() { - if let Err(e) = get_cli_result(Cli::parse()) { + if let Err(e) = get_cli_result() { println!("{e}"); std::process::exit(1); } diff --git a/src/merge.rs b/src/merge.rs index cd3267c..1280538 100644 --- a/src/merge.rs +++ b/src/merge.rs @@ -1,21 +1,27 @@ use std::hash::{Hash, Hasher}; -use clap::Args; +use clap::{ArgAction, Args}; use itertools::Itertools; +use crate::locale::{tr, tr_args}; use crate::ts; use crate::ts::{MessageNode, TSNode}; /// Merges two translation file contexts and messages into a single output. #[derive(Args)] +#[command(disable_help_flag = true)] pub struct MergeArgs { /// File to receive the merge + #[arg(help = tr("cli-merge-input-left"), help_heading = tr("cli-headers-arguments"))] pub input_left: String, /// File to include changes from + #[arg(help = tr("cli-merge-input-right"), help_heading = tr("cli-headers-arguments"))] pub input_right: String, /// If specified, will produce output in a file at designated location instead of stdout. - #[arg(short, long)] + #[arg(short, long, help = tr("cli-merge-output"), help_heading = tr("cli-headers-options"))] pub output_path: Option, + #[arg(short, long, action = ArgAction::Help, help = tr("cli-help"), help_heading = tr("cli-headers-options"))] + pub help: bool, } /// MessageNode that can be `eq(...)`. @@ -54,18 +60,24 @@ pub fn merge_main(args: &MergeArgs) -> Result<(), String> { let right = load_file(&args.input_right); if let Err(e) = left { - return Err(format!( - "Could not process left file '{}'. Error: {}", - &args.input_left, - e.to_string() + return Err(tr_args( + "open-or-parse-error", + [ + ("file", args.input_left.as_str().into()), + ("error", e.to_string().as_str().into()), + ] + .into(), )); } if let Err(e) = right { - return Err(format!( - "Could not process right file '{}'. Error: {}", - &args.input_right, - e.to_string() + return Err(tr_args( + "open-or-parse-error", + [ + ("file", args.input_right.as_str().into()), + ("error", e.to_string().as_str().into()), + ] + .into(), )); } @@ -122,25 +134,31 @@ fn merge_messages( if let Some(left_message) = left_message { if right_message.node.source != left_message.node.source { - right_message.node.oldsource = left_message.node.source.clone(); + right_message + .node + .oldsource + .clone_from(&left_message.node.source); } if right_message.node.comment != left_message.node.comment { - right_message.node.oldcomment = left_message.node.comment.clone(); + right_message + .node + .oldcomment + .clone_from(&left_message.node.comment); } } }); unique_messages_left .drain(0..) - .filter(|a| !unique_messages_right.contains(&a)) + .filter(|message| !unique_messages_right.contains(message)) .merge(unique_messages_right.iter().cloned()) .map(|node| node.node) .collect() } fn load_file(path: &String) -> Result { - match quick_xml::Reader::from_file(&path) { + match quick_xml::Reader::from_file(path) { Ok(reader) => { let nodes: Result = quick_xml::de::from_reader(reader.into_inner()); match nodes { diff --git a/src/sort.rs b/src/sort.rs index 32c4efe..292f4b9 100644 --- a/src/sort.rs +++ b/src/sort.rs @@ -1,16 +1,20 @@ -use clap::Args; +use clap::{ArgAction, Args}; +use crate::locale::{tr, tr_args}; use crate::ts; use crate::ts::TSNode; -/// Sorts the input translation file by context, then by messages. #[derive(Args)] +#[command(disable_help_flag = true)] pub struct SortArgs { /// File path to sort translations from. + #[arg(help = tr("cli-sort-input"), help_heading = tr("cli-headers-arguments"))] pub input_path: String, /// If specified, will produce output in a file at designated location instead of stdout. - #[arg(short, long)] + #[arg(short, long, help = tr("cli-sort-output"), help_heading = tr("cli-headers-options"))] pub output_path: Option, + #[arg(short, long, action = ArgAction::Help, help = tr("cli-help"), help_heading = tr("cli-headers-options"))] + pub help: bool, } /// Sorts an input TS file by context, then by messages. @@ -28,15 +32,23 @@ pub fn sort_main(args: &SortArgs) -> Result<(), String> { sort_ts_node(&mut ts_node); ts::write_to_output(&args.output_path, &ts_node) } - Err(e) => Err(format!( - "Could not parse input file \"{}\". Error: {e:?}.", - args.input_path + Err(e) => Err(tr_args( + "sort-parse-error", + [ + ("file", args.input_path.as_str().into()), + ("error", e.to_string().into()), + ] + .into(), )), } } - Err(e) => Err(format!( - "Could not open or parse input file \"{}\". Error: {e:?}", - args.input_path + Err(e) => Err(tr_args( + "open-or-parse-error", + [ + ("file", args.input_path.as_str().into()), + ("error", e.to_string().into()), + ] + .into(), )), } } @@ -59,8 +71,6 @@ fn sort_ts_node(ts_node: &mut TSNode) { #[cfg(test)] mod sort_test { - use quick_xml; - use super::*; #[test] diff --git a/src/ts.rs b/src/ts.rs index d9e1586..6a24b2c 100644 --- a/src/ts.rs +++ b/src/ts.rs @@ -3,6 +3,8 @@ use std::io::{BufWriter, Write}; use serde::{Deserialize, Serialize}; +use crate::locale::tr_args; + // This file defines the schema matching (or trying to match?) Qt's XSD // Eventually when a proper Rust code generator exists it would be great to use that instead. // For now they can't handle Qt's semi-weird XSD. @@ -196,20 +198,26 @@ pub struct NumerusFormNode { impl PartialOrd for MessageNode { fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for MessageNode { + fn cmp(&self, other: &Self) -> Ordering { let id_cmp = other.id.cmp(&self.id); if id_cmp != Ordering::Equal { - return Some(id_cmp); + return id_cmp; } - let min_self = self + let (filename, line) = self .locations .iter() .min_by_key(|location| (location.filename.as_ref(), location.line)) .map(|location| (location.filename.as_ref(), location.line.as_ref())) .unwrap_or_default(); - let min_other = other + let (other_filename, other_line) = other .locations .iter() .min_by_key(|location| (location.filename.as_ref(), location.line)) @@ -218,66 +226,50 @@ impl PartialOrd for MessageNode { // Counterintuitive, but we want to have locationless message at the end: // handle `None` differently from default. - if min_self.0 == None && min_other.0 != None { - Some(Ordering::Greater) - } else if min_self.0 == min_other.0 && min_self.1 == None && min_other.1 != None { - Some(Ordering::Greater) + if filename.is_none() && other_filename.is_some() { + Ordering::Greater } else { - min_self.partial_cmp(&min_other) + (filename, line).cmp(&(other_filename, other_line)) } } } -impl Ord for MessageNode { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(&other) - .expect("PartialOrd should always return a value for MessageNode") - } -} - impl Ord for LocationNode { fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(&other) - .expect("PartialOrd should always return a value for LocationNode") - } -} - -impl PartialOrd for LocationNode { - fn partial_cmp(&self, other: &Self) -> Option { match self .filename .as_ref() .unwrap_or(&"".to_owned()) .to_lowercase() - .partial_cmp( + .cmp( &other .filename .as_ref() .unwrap_or(&"".to_owned()) .to_lowercase(), - ) - .expect("LocationNode::filename should have an ordering") - { - Ordering::Less => Some(Ordering::Less), - Ordering::Greater => Some(Ordering::Greater), - Ordering::Equal => self.line.partial_cmp(&other.line), + ) { + Ordering::Equal => self.line.cmp(&other.line), + ordering => ordering, } } } +impl PartialOrd for LocationNode { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl PartialOrd for ContextNode { fn partial_cmp(&self, other: &Self) -> Option { - // Contexts are generally module or classes names; let's assume they don't need any special collation treatment. - self.name - .to_lowercase() - .partial_cmp(&other.name.to_lowercase()) + Some(self.cmp(other)) } } impl Ord for ContextNode { fn cmp(&self, other: &Self) -> Ordering { // Contexts are generally module or classes names; let's assume they don't need any special collation treatment. - self.name.cmp(&other.name) + self.name.to_lowercase().cmp(&other.name.to_lowercase()) } } @@ -289,13 +281,19 @@ pub fn write_to_output(output_path: &Option, node: &TSNode) -> Result<() None => BufWriter::new(Box::new(std::io::stdout().lock())), Some(output_path) => match std::fs::File::options() .create(true) + .truncate(true) .write(true) .open(output_path) { Ok(file) => BufWriter::new(Box::new(file)), Err(e) => { - return Err(format!( - "Error occured while opening output file \"{output_path}\": {e:?}" + return Err(tr_args( + "ts-error-write-output-open", + [ + ("output_path", output_path.into()), + ("error", e.to_string().into()), + ] + .into(), )) } }, @@ -311,17 +309,21 @@ pub fn write_to_output(output_path: &Option, node: &TSNode) -> Result<() let res = inner_writer.write_all(output_buffer.as_bytes()); match res { Ok(_) => Ok(()), - Err(e) => Err(format!("Problem occured while serializing output: {e:?}")), + Err(e) => Err(tr_args( + "ts-error-write-serialize", + [("error", e.to_string().into())].into(), + )), } } - Err(e) => Err(format!("Problem occured while serializing output: {e:?}")), + Err(e) => Err(tr_args( + "ts-error-write-serialize", + [("error", e.to_string().into())].into(), + )), } } #[cfg(test)] mod write_file_test { - use quick_xml; - use super::*; #[test] @@ -346,8 +348,6 @@ mod write_file_test { #[cfg(test)] mod test { - use quick_xml; - use super::*; // TODO: Data set. https://github.com/qt/qttranslations/