mirror of
https://github.com/uutils/coreutils.git
synced 2025-07-07 21:45:01 +00:00
uucore: add functions to manage translations
This commit is contained in:
parent
cd9ce77098
commit
5a7c50d64e
8 changed files with 645 additions and 15 deletions
|
@ -25,6 +25,7 @@ getrandom
|
|||
globset
|
||||
indicatif
|
||||
itertools
|
||||
langid
|
||||
lscolors
|
||||
mdbook
|
||||
memchr
|
||||
|
@ -46,6 +47,7 @@ termsize
|
|||
termwidth
|
||||
textwrap
|
||||
thiserror
|
||||
unic
|
||||
ureq
|
||||
walkdir
|
||||
winapi
|
||||
|
|
148
Cargo.lock
generated
148
Cargo.lock
generated
|
@ -181,7 +181,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"rustc-hash 2.1.1",
|
||||
"shlex",
|
||||
"syn",
|
||||
]
|
||||
|
@ -857,6 +857,17 @@ dependencies = [
|
|||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlv-list"
|
||||
version = "0.5.2"
|
||||
|
@ -994,6 +1005,50 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[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 1.1.0",
|
||||
"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 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -1242,6 +1297,25 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[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.1"
|
||||
|
@ -2049,6 +2123,12 @@ dependencies = [
|
|||
"trim-in-place",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.1"
|
||||
|
@ -2111,6 +2191,15 @@ 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.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "1.2.0"
|
||||
|
@ -2438,6 +2527,16 @@ dependencies = [
|
|||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
|
@ -2461,12 +2560,39 @@ version = "0.1.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
|
||||
|
||||
[[package]]
|
||||
name = "type-map"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f"
|
||||
dependencies = [
|
||||
"rustc-hash 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05"
|
||||
dependencies = [
|
||||
"unic-langid-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid-impl"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658"
|
||||
dependencies = [
|
||||
"tinystr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
|
@ -3278,7 +3404,7 @@ dependencies = [
|
|||
"nix",
|
||||
"rand 0.9.1",
|
||||
"rayon",
|
||||
"self_cell",
|
||||
"self_cell 1.2.0",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"unicode-width 0.2.0",
|
||||
|
@ -3582,6 +3708,8 @@ dependencies = [
|
|||
"digest",
|
||||
"dns-lookup",
|
||||
"dunce",
|
||||
"fluent",
|
||||
"fluent-bundle",
|
||||
"glob",
|
||||
"hex",
|
||||
"iana-time-zone",
|
||||
|
@ -3602,6 +3730,7 @@ dependencies = [
|
|||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"unic-langid",
|
||||
"utmp-classic",
|
||||
"uucore_procs",
|
||||
"walkdir",
|
||||
|
@ -4080,6 +4209,21 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
|
||||
dependencies = [
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "4.0.0"
|
||||
|
|
|
@ -362,6 +362,11 @@ sm3 = "0.4.2"
|
|||
crc32fast = "1.4.2"
|
||||
digest = "0.10.7"
|
||||
|
||||
# Fluent dependencies
|
||||
fluent-bundle = "0.15.3"
|
||||
fluent = "0.16.1"
|
||||
unic-langid = "0.9.6"
|
||||
|
||||
uucore = { version = "0.0.30", package = "uucore", path = "src/uucore" }
|
||||
uucore_procs = { version = "0.0.30", package = "uucore_procs", path = "src/uucore_procs" }
|
||||
uu_ls = { version = "0.0.30", path = "src/uu/ls" }
|
||||
|
|
188
fuzz/Cargo.lock
generated
188
fuzz/Cargo.lock
generated
|
@ -436,6 +436,17 @@ dependencies = [
|
|||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlv-list"
|
||||
version = "0.5.2"
|
||||
|
@ -479,6 +490,50 @@ version = "2.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[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 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -560,6 +615,25 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[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.1"
|
||||
|
@ -963,6 +1037,12 @@ dependencies = [
|
|||
"trim-in-place",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
|
@ -982,6 +1062,15 @@ version = "1.0.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d"
|
||||
dependencies = [
|
||||
"self_cell 1.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "1.2.0"
|
||||
|
@ -1067,6 +1156,12 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
|
@ -1107,13 +1202,33 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1136,18 +1251,55 @@ dependencies = [
|
|||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trim-in-place"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
|
||||
|
||||
[[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 = "typenum"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05"
|
||||
dependencies = [
|
||||
"unic-langid-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid-impl"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658"
|
||||
dependencies = [
|
||||
"tinystr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
|
@ -1213,7 +1365,7 @@ dependencies = [
|
|||
"clap",
|
||||
"nix",
|
||||
"rust-ini",
|
||||
"thiserror",
|
||||
"thiserror 2.0.12",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
|
@ -1225,7 +1377,7 @@ dependencies = [
|
|||
"num-bigint",
|
||||
"num-traits",
|
||||
"onig",
|
||||
"thiserror",
|
||||
"thiserror 2.0.12",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
|
@ -1245,7 +1397,7 @@ dependencies = [
|
|||
"clap",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"thiserror",
|
||||
"thiserror 2.0.12",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
|
@ -1263,9 +1415,9 @@ dependencies = [
|
|||
"nix",
|
||||
"rand 0.9.1",
|
||||
"rayon",
|
||||
"self_cell",
|
||||
"self_cell 1.2.0",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"thiserror 2.0.12",
|
||||
"unicode-width",
|
||||
"uucore",
|
||||
]
|
||||
|
@ -1276,7 +1428,7 @@ version = "0.0.30"
|
|||
dependencies = [
|
||||
"clap",
|
||||
"memchr",
|
||||
"thiserror",
|
||||
"thiserror 2.0.12",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
|
@ -1306,7 +1458,7 @@ dependencies = [
|
|||
"clap",
|
||||
"libc",
|
||||
"nix",
|
||||
"thiserror",
|
||||
"thiserror 2.0.12",
|
||||
"unicode-width",
|
||||
"uucore",
|
||||
]
|
||||
|
@ -1326,6 +1478,8 @@ dependencies = [
|
|||
"data-encoding-macro",
|
||||
"digest",
|
||||
"dunce",
|
||||
"fluent",
|
||||
"fluent-bundle",
|
||||
"glob",
|
||||
"hex",
|
||||
"iana-time-zone",
|
||||
|
@ -1341,7 +1495,8 @@ dependencies = [
|
|||
"sha2",
|
||||
"sha3",
|
||||
"sm3",
|
||||
"thiserror",
|
||||
"thiserror 2.0.12",
|
||||
"unic-langid",
|
||||
"uucore_procs",
|
||||
"wild",
|
||||
"winapi-util",
|
||||
|
@ -1660,3 +1815,18 @@ dependencies = [
|
|||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
|
||||
dependencies = [
|
||||
"zerofrom",
|
||||
]
|
||||
|
|
|
@ -31,7 +31,6 @@ wild = "2.2.1"
|
|||
glob = { workspace = true, optional = true }
|
||||
iana-time-zone = { workspace = true, optional = true }
|
||||
itertools = { workspace = true, optional = true }
|
||||
thiserror = { workspace = true, optional = true }
|
||||
time = { workspace = true, optional = true, features = [
|
||||
"formatting",
|
||||
"local-offset",
|
||||
|
@ -60,6 +59,11 @@ bigdecimal = { workspace = true, optional = true }
|
|||
num-traits = { workspace = true, optional = true }
|
||||
selinux = { workspace = true, optional = true }
|
||||
|
||||
# Fluent dependencies
|
||||
fluent-bundle = { workspace = true }
|
||||
fluent = { workspace = true }
|
||||
unic-langid = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
walkdir = { workspace = true, optional = true }
|
||||
nix = { workspace = true, features = ["fs", "uio", "zerocopy", "signal"] }
|
||||
|
@ -87,7 +91,7 @@ default = []
|
|||
# * non-default features
|
||||
backup-control = []
|
||||
colors = []
|
||||
checksum = ["data-encoding", "thiserror", "sum"]
|
||||
checksum = ["data-encoding", "sum"]
|
||||
encoding = ["data-encoding", "data-encoding-macro", "z85"]
|
||||
entries = ["libc"]
|
||||
extendedbigdecimal = ["bigdecimal", "num-traits"]
|
||||
|
@ -114,7 +118,7 @@ proc-info = ["tty", "walkdir"]
|
|||
quoting-style = []
|
||||
ranges = []
|
||||
ringbuffer = []
|
||||
selinux = ["dep:selinux", "thiserror"]
|
||||
selinux = ["dep:selinux"]
|
||||
signals = []
|
||||
sum = [
|
||||
"digest",
|
||||
|
@ -136,4 +140,4 @@ version-cmp = []
|
|||
wide = []
|
||||
custom-tz-fmt = ["chrono", "chrono-tz", "iana-time-zone"]
|
||||
tty = []
|
||||
uptime = ["chrono", "libc", "windows-sys", "utmpx", "utmp-classic", "thiserror"]
|
||||
uptime = ["chrono", "libc", "windows-sys", "utmpx", "utmp-classic"]
|
||||
|
|
|
@ -27,6 +27,7 @@ pub use crate::mods::error;
|
|||
#[cfg(feature = "fs")]
|
||||
pub use crate::mods::io;
|
||||
pub use crate::mods::line_ending;
|
||||
pub use crate::mods::locale;
|
||||
pub use crate::mods::os;
|
||||
pub use crate::mods::panic;
|
||||
pub use crate::mods::posix;
|
||||
|
|
|
@ -9,6 +9,7 @@ pub mod error;
|
|||
#[cfg(feature = "fs")]
|
||||
pub mod io;
|
||||
pub mod line_ending;
|
||||
pub mod locale;
|
||||
pub mod os;
|
||||
pub mod panic;
|
||||
pub mod posix;
|
||||
|
|
303
src/uucore/src/lib/mods/locale.rs
Normal file
303
src/uucore/src/lib/mods/locale.rs
Normal file
|
@ -0,0 +1,303 @@
|
|||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
// spell-checker:ignore unic_langid
|
||||
|
||||
use crate::error::UError;
|
||||
use fluent::{FluentArgs, FluentBundle, FluentResource};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::OnceLock;
|
||||
use thiserror::Error;
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LocalizationError {
|
||||
#[error("I/O error loading '{path}': {source}")]
|
||||
Io {
|
||||
source: std::io::Error,
|
||||
path: PathBuf,
|
||||
},
|
||||
#[error("Parse error: {0}")]
|
||||
Parse(String),
|
||||
#[error("Bundle error: {0}")]
|
||||
Bundle(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for LocalizationError {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
LocalizationError::Io {
|
||||
source: error,
|
||||
path: PathBuf::from("<unknown>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a generic way to convert LocalizationError to UError
|
||||
impl UError for LocalizationError {
|
||||
fn code(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
pub const DEFAULT_LOCALE: &str = "en-US";
|
||||
|
||||
// A struct to handle localization
|
||||
struct Localizer {
|
||||
bundle: FluentBundle<FluentResource>,
|
||||
}
|
||||
|
||||
impl Localizer {
|
||||
fn new(bundle: FluentBundle<FluentResource>) -> Self {
|
||||
Self { bundle }
|
||||
}
|
||||
|
||||
fn format(&self, id: &str, args: Option<&FluentArgs>, default: &str) -> String {
|
||||
match self.bundle.get_message(id).and_then(|m| m.value()) {
|
||||
Some(value) => {
|
||||
let mut errs = Vec::new();
|
||||
self.bundle
|
||||
.format_pattern(value, args, &mut errs)
|
||||
.to_string()
|
||||
}
|
||||
None => default.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global localizer stored in thread-local OnceLock
|
||||
thread_local! {
|
||||
static LOCALIZER: OnceLock<Localizer> = const { OnceLock::new() };
|
||||
}
|
||||
|
||||
// Initialize localization with a specific locale and config
|
||||
fn init_localization(
|
||||
locale: &LanguageIdentifier,
|
||||
config: &LocalizationConfig,
|
||||
) -> Result<(), LocalizationError> {
|
||||
let bundle = create_bundle(locale, config)?;
|
||||
LOCALIZER.with(|lock| {
|
||||
let loc = Localizer::new(bundle);
|
||||
lock.set(loc)
|
||||
.map_err(|_| LocalizationError::Bundle("Localizer already initialized".into()))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Create a bundle for a locale with fallback chain
|
||||
fn create_bundle(
|
||||
locale: &LanguageIdentifier,
|
||||
config: &LocalizationConfig,
|
||||
) -> Result<FluentBundle<FluentResource>, LocalizationError> {
|
||||
// Create a new bundle with requested locale
|
||||
let mut bundle = FluentBundle::new(vec![locale.clone()]);
|
||||
|
||||
// Try to load the requested locale
|
||||
let mut locales_to_try = vec![locale.clone()];
|
||||
locales_to_try.extend_from_slice(&config.fallback_locales);
|
||||
|
||||
// Try each locale in the chain
|
||||
let mut tried_paths = Vec::new();
|
||||
|
||||
for try_locale in locales_to_try {
|
||||
let locale_path = config.get_locale_path(&try_locale);
|
||||
tried_paths.push(locale_path.clone());
|
||||
|
||||
if let Ok(ftl_file) = fs::read_to_string(&locale_path) {
|
||||
let resource = FluentResource::try_new(ftl_file).map_err(|_| {
|
||||
LocalizationError::Parse(format!(
|
||||
"Failed to parse localization resource for {}",
|
||||
try_locale
|
||||
))
|
||||
})?;
|
||||
|
||||
bundle.add_resource(resource).map_err(|_| {
|
||||
LocalizationError::Bundle(format!(
|
||||
"Failed to add resource to bundle for {}",
|
||||
try_locale
|
||||
))
|
||||
})?;
|
||||
|
||||
return Ok(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
let paths_str = tried_paths
|
||||
.iter()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
Err(LocalizationError::Io {
|
||||
source: std::io::Error::new(std::io::ErrorKind::NotFound, "No localization files found"),
|
||||
path: PathBuf::from(paths_str),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_message_internal(id: &str, args: Option<FluentArgs>, default: &str) -> String {
|
||||
LOCALIZER.with(|lock| {
|
||||
lock.get()
|
||||
.map(|loc| loc.format(id, args.as_ref(), default))
|
||||
.unwrap_or_else(|| default.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieves a localized message by its identifier.
|
||||
///
|
||||
/// Looks up a message with the given ID in the current locale bundle and returns
|
||||
/// the localized text. If the message ID is not found, returns the provided default text.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id` - The message identifier in the Fluent resources
|
||||
/// * `default` - Default text to use if the message ID isn't found
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `String` containing either the localized message or the default text
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use uucore::locale::get_message;
|
||||
///
|
||||
/// // Get a localized greeting or fall back to English
|
||||
/// let greeting = get_message("greeting", "Hello, World!");
|
||||
/// println!("{}", greeting);
|
||||
/// ```
|
||||
pub fn get_message(id: &str, default: &str) -> String {
|
||||
get_message_internal(id, None, default)
|
||||
}
|
||||
|
||||
/// Retrieves a localized message with variable substitution.
|
||||
///
|
||||
/// Looks up a message with the given ID in the current locale bundle,
|
||||
/// substitutes variables from the provided arguments map, and returns the
|
||||
/// localized text. If the message ID is not found, returns the provided default text.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id` - The message identifier in the Fluent resources
|
||||
/// * `ftl_args` - Key-value pairs for variable substitution in the message
|
||||
/// * `default` - Default text to use if the message ID isn't found
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `String` containing either the localized message with variable substitution or the default text
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use uucore::locale::get_message_with_args;
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
/// // For a Fluent message like: "Hello, { $name }! You have { $count } notifications."
|
||||
/// let mut args = HashMap::new();
|
||||
/// args.insert("name".to_string(), "Alice".to_string());
|
||||
/// args.insert("count".to_string(), "3".to_string());
|
||||
///
|
||||
/// let message = get_message_with_args(
|
||||
/// "notification",
|
||||
/// args,
|
||||
/// "Hello! You have notifications."
|
||||
/// );
|
||||
/// println!("{}", message);
|
||||
/// ```
|
||||
pub fn get_message_with_args(id: &str, ftl_args: HashMap<String, String>, default: &str) -> String {
|
||||
let args = ftl_args.into_iter().collect();
|
||||
get_message_internal(id, Some(args), default)
|
||||
}
|
||||
|
||||
// Configuration for localization
|
||||
#[derive(Clone)]
|
||||
struct LocalizationConfig {
|
||||
locales_dir: PathBuf,
|
||||
fallback_locales: Vec<LanguageIdentifier>,
|
||||
}
|
||||
|
||||
impl LocalizationConfig {
|
||||
// Create a new config with a specific locales directory
|
||||
fn new<P: AsRef<Path>>(locales_dir: P) -> Self {
|
||||
Self {
|
||||
locales_dir: locales_dir.as_ref().to_path_buf(),
|
||||
fallback_locales: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
// Set fallback locales
|
||||
fn with_fallbacks(mut self, fallbacks: Vec<LanguageIdentifier>) -> Self {
|
||||
self.fallback_locales = fallbacks;
|
||||
self
|
||||
}
|
||||
|
||||
// Get path for a specific locale
|
||||
fn get_locale_path(&self, locale: &LanguageIdentifier) -> PathBuf {
|
||||
self.locales_dir.join(format!("{}.ftl", locale))
|
||||
}
|
||||
}
|
||||
|
||||
// Function to detect system locale from environment variables
|
||||
fn detect_system_locale() -> Result<LanguageIdentifier, LocalizationError> {
|
||||
let locale_str = std::env::var("LANG")
|
||||
.unwrap_or_else(|_| DEFAULT_LOCALE.to_string())
|
||||
.split('.')
|
||||
.next()
|
||||
.unwrap_or(DEFAULT_LOCALE)
|
||||
.to_string();
|
||||
|
||||
LanguageIdentifier::from_str(&locale_str)
|
||||
.map_err(|_| LocalizationError::Parse(format!("Failed to parse locale: {}", locale_str)))
|
||||
}
|
||||
|
||||
/// Sets up localization using the system locale (or default) and project paths.
|
||||
///
|
||||
/// This function initializes the localization system based on the system's locale
|
||||
/// preferences (via the LANG environment variable) or falls back to the default locale
|
||||
/// if the system locale cannot be determined or is invalid.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `p` - Path to the directory containing localization (.ftl) files
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` if initialization succeeds
|
||||
/// * `Err(LocalizationError)` if initialization fails
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `LocalizationError` if:
|
||||
/// * The localization files cannot be read
|
||||
/// * The files contain invalid syntax
|
||||
/// * The bundle cannot be initialized properly
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use uucore::locale::setup_localization;
|
||||
///
|
||||
/// // Initialize localization using files in the "locales" directory
|
||||
/// match setup_localization("./locales") {
|
||||
/// Ok(_) => println!("Localization initialized successfully"),
|
||||
/// Err(e) => eprintln!("Failed to initialize localization: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn setup_localization(p: &str) -> Result<(), LocalizationError> {
|
||||
let locale = detect_system_locale().unwrap_or_else(|_| {
|
||||
LanguageIdentifier::from_str(DEFAULT_LOCALE).expect("Default locale should always be valid")
|
||||
});
|
||||
|
||||
let locales_dir = PathBuf::from(p);
|
||||
let fallback_locales = vec![
|
||||
LanguageIdentifier::from_str(DEFAULT_LOCALE)
|
||||
.expect("Default locale should always be valid"),
|
||||
];
|
||||
|
||||
let config = LocalizationConfig::new(locales_dir).with_fallbacks(fallback_locales);
|
||||
|
||||
init_localization(&locale, &config)?;
|
||||
Ok(())
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue