From 7b9290c92c7f3482713a9d9cbd686f6017aeb8d5 Mon Sep 17 00:00:00 2001 From: scarf Date: Mon, 13 Mar 2023 11:44:54 +0900 Subject: [PATCH] Localization support (#820) find locale set with [sys-locale](https://crates.io/crates/sys-locale), then format with locales. --- CHANGELOG.md | 1 + Cargo.lock | 171 +++++++++++++++++++++++++++++++++++++------ Cargo.toml | 4 +- src/meta/date.rs | 16 ++-- src/meta/inode.rs | 2 +- src/meta/links.rs | 2 +- src/meta/locale.rs | 15 ++++ src/meta/mod.rs | 3 +- src/meta/name.rs | 4 +- src/meta/symlink.rs | 2 +- src/sort.rs | 2 +- tests/integration.rs | 8 +- 12 files changed, 189 insertions(+), 41 deletions(-) create mode 100644 src/meta/locale.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 922c2a9dc..1d0118bab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `--system-protected` to include files with the Windows `system` flag set, on other platform the same as `--all` [#752](https://github.com/Peltoche/lsd/issues/752) - Add many icons from https://github.com/Peltoche/lsd/issues/764 [@TruncatedDinosour](https://ari-web.xyz/gh) +- Add support for localization from [scarf](https://github.com/scarf005) ### Fixed - Do not quote filename when piping into another program from [TeamTamoad](https://github.com/TeamTamoad) diff --git a/Cargo.lock b/Cargo.lock index 8b3f8082c..38322808e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + [[package]] name = "cfg-if" version = "1.0.0" @@ -97,6 +103,7 @@ dependencies = [ "libc", "num-integer", "num-traits", + "pure-rust-locales", "time", "winapi", ] @@ -367,6 +374,15 @@ dependencies = [ "either", ] +[[package]] +name = "js-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -428,10 +444,12 @@ dependencies = [ "human-sort", "libc", "lscolors", + "once_cell", "predicates 1.0.8", "serde", "serde_yaml", "serial_test", + "sys-locale", "tempfile", "term_grid", "terminal_size", @@ -468,7 +486,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -498,9 +516,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "os_str_bytes" @@ -553,7 +571,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -611,6 +629,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "pure-rust-locales" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45c49fc4f91f35bae654f85ebb3a44d60ac64f11b3166ffa609def390c732d8" + [[package]] name = "quote" version = "1.0.17" @@ -800,6 +824,19 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "sys-locale" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee" +dependencies = [ + "js-sys", + "libc", + "wasm-bindgen", + "web-sys", + "windows-sys 0.45.0", +] + [[package]] name = "tempfile" version = "3.3.0" @@ -997,6 +1034,70 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" + +[[package]] +name = "web-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wild" version = "2.0.4" @@ -1044,12 +1145,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc 0.42.1", ] [[package]] @@ -1065,11 +1166,35 @@ dependencies = [ "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" @@ -1079,9 +1204,9 @@ checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" @@ -1091,9 +1216,9 @@ checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" @@ -1103,9 +1228,9 @@ checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" @@ -1115,15 +1240,15 @@ checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" @@ -1133,9 +1258,9 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "xattr" diff --git a/Cargo.toml b/Cargo.toml index 996c7d2e2..ef472b73d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,9 @@ human-sort = "0.2.2" term_grid = "0.1.*" terminal_size = "0.1.*" thiserror = "1.0" -chrono = "0.4.*" +sys-locale = "0.2.4" +once_cell = "1.17.1" +chrono = { version = "0.4.*", features = ["unstable-locales"] } chrono-humanize = "0.1.*" unicode-width = "0.1.*" lscolors = "0.9.0" diff --git a/src/meta/date.rs b/src/meta/date.rs index b6f447ce0..5675eacfc 100644 --- a/src/meta/date.rs +++ b/src/meta/date.rs @@ -1,3 +1,4 @@ +use super::locale::current_locale; use crate::color::{ColoredString, Colors, Elem}; use crate::flags::{DateFlag, Flags}; use chrono::{DateTime, Duration, Local}; @@ -42,9 +43,11 @@ impl Date { } fn date_string(&self, flags: &Flags) -> String { + let locale = current_locale(); + if let Date::Date(val) = self { match &flags.date { - DateFlag::Date => val.format("%c").to_string(), + DateFlag::Date => val.format_localized("%c", locale).to_string(), DateFlag::Relative => HumanTime::from(*val - Local::now()).to_string(), DateFlag::Iso => { // 365.2425 * 24 * 60 * 60 = 31556952 seconds per year @@ -55,7 +58,7 @@ impl Date { val.format("%F").to_string() } } - DateFlag::Formatted(format) => val.format(format).to_string(), + DateFlag::Formatted(format) => val.format_localized(format, locale).to_string(), } } else { String::from('-') @@ -68,6 +71,7 @@ mod test { use super::Date; use crate::color::{Colors, ThemeOption}; use crate::flags::{DateFlag, Flags}; + use crate::meta::locale::current_locale; use chrono::{DateTime, Duration, Local}; use crossterm::style::{Color, Stylize}; use std::io; @@ -80,7 +84,7 @@ mod test { Command::new("touch") .arg("-t") .arg(date.format("%Y%m%d%H%M.%S").to_string()) - .arg(&path) + .arg(path) .status() } @@ -127,7 +131,7 @@ mod test { assert_eq!( creation_date - .format("%c") + .format_localized("%c", current_locale()) .to_string() .with(Color::AnsiValue(40)), date.render(&colors, &flags) @@ -154,7 +158,7 @@ mod test { assert_eq!( creation_date - .format("%c") + .format_localized("%c", current_locale()) .to_string() .with(Color::AnsiValue(42)), date.render(&colors, &flags) @@ -181,7 +185,7 @@ mod test { assert_eq!( creation_date - .format("%c") + .format_localized("%c", current_locale()) .to_string() .with(Color::AnsiValue(36)), date.render(&colors, &flags) diff --git a/src/meta/inode.rs b/src/meta/inode.rs index 7aac80594..c7d2dae47 100644 --- a/src/meta/inode.rs +++ b/src/meta/inode.rs @@ -41,7 +41,7 @@ mod tests { use std::process::{Command, ExitStatus}; fn cross_platform_touch(path: &Path) -> io::Result { - Command::new("touch").arg(&path).status() + Command::new("touch").arg(path).status() } #[test] diff --git a/src/meta/links.rs b/src/meta/links.rs index 237292874..6ee86129b 100644 --- a/src/meta/links.rs +++ b/src/meta/links.rs @@ -41,7 +41,7 @@ mod tests { use std::process::{Command, ExitStatus}; fn cross_platform_touch(path: &Path) -> io::Result { - Command::new("touch").arg(&path).status() + Command::new("touch").arg(path).status() } #[test] diff --git a/src/meta/locale.rs b/src/meta/locale.rs new file mode 100644 index 000000000..71bb6c01b --- /dev/null +++ b/src/meta/locale.rs @@ -0,0 +1,15 @@ +use chrono::Locale; +use once_cell::sync::OnceCell; +use sys_locale::get_locale; + +fn locale_str() -> String { + get_locale().unwrap_or_default().replace('-', "_") +} + +/// Finds current locale +pub fn current_locale() -> Locale { + const DEFAULT: Locale = Locale::en_US; + static CACHE: OnceCell = OnceCell::new(); + + *CACHE.get_or_init(|| Locale::try_from(locale_str().as_str()).unwrap_or(DEFAULT)) +} diff --git a/src/meta/mod.rs b/src/meta/mod.rs index 00090dcb2..697cecb5c 100644 --- a/src/meta/mod.rs +++ b/src/meta/mod.rs @@ -4,6 +4,7 @@ mod filetype; mod indicator; mod inode; mod links; +mod locale; pub mod name; mod owner; mod permissions; @@ -329,7 +330,7 @@ mod tests { let path_c = tmp_dir.path().join("ccc.cc"); #[cfg(unix)] - std::os::unix::fs::symlink(&path_c, &path_b).expect("failed to create broken symlink"); + std::os::unix::fs::symlink(path_c, &path_b).expect("failed to create broken symlink"); // this needs to be tested on Windows // likely to fail because of permission issue diff --git a/src/meta/name.rs b/src/meta/name.rs index c8472d823..07cc4c7ae 100644 --- a/src/meta/name.rs +++ b/src/meta/name.rs @@ -117,7 +117,7 @@ impl Name { // HyperlinkOption::Auto gets converted to None or Always in core.rs based on tty_available match std::fs::canonicalize(&self.path) { Ok(rp) => { - if let Ok(url) = Url::from_file_path(&rp) { + if let Ok(url) = Url::from_file_path(rp) { // Crossterm does not support hyperlinks as of now // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda format!("\x1B]8;;{}\x1B\x5C{}\x1B]8;;\x1B\x5C", url, name) @@ -426,7 +426,7 @@ mod test { let colors = Colors::new(color::ThemeOption::NoColor); let real_path = std::fs::canonicalize(&file_path).expect("canonicalize"); - let expected_url = Url::from_file_path(&real_path).expect("absolute path"); + let expected_url = Url::from_file_path(real_path).expect("absolute path"); let expected_text = format!( "\x1B]8;;{}\x1B\x5C{}\x1B]8;;\x1B\x5C", expected_url, "file.txt" diff --git a/src/meta/symlink.rs b/src/meta/symlink.rs index f7b296b5f..c46432eeb 100644 --- a/src/meta/symlink.rs +++ b/src/meta/symlink.rs @@ -12,7 +12,7 @@ pub struct SymLink { impl From<&Path> for SymLink { fn from(path: &Path) -> Self { if let Ok(target) = read_link(path) { - if target.is_absolute() || path.parent() == None { + if target.is_absolute() || path.parent().is_none() { return Self { valid: target.exists(), target: Some( diff --git a/src/sort.rs b/src/sort.rs index 6fb39cb60..87092830f 100644 --- a/src/sort.rs +++ b/src/sort.rs @@ -367,7 +367,7 @@ mod tests { let path_d = tmp_dir.path().join("ddd.dd"); #[cfg(unix)] - std::os::unix::fs::symlink(&path_d, &path_c).expect("failed to create broken symlink"); + std::os::unix::fs::symlink(path_d, &path_c).expect("failed to create broken symlink"); // this needs to be tested on Windows // likely to fail because of permission issue diff --git a/tests/integration.rs b/tests/integration.rs index c2d9cca5c..a43ce3565 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -297,7 +297,7 @@ fn test_dereference_link_broken_link_output() { let target = dir.path().join("target"); #[cfg(unix)] - fs::symlink(&target, &link).unwrap(); + fs::symlink(target, &link).unwrap(); // this needs to be tested on Windows // likely to fail because of permission issue @@ -369,7 +369,7 @@ fn test_show_folder_of_symlink_for_long_multi() { let dir = tempdir(); dir.child("target").child("inside").touch().unwrap(); let link = dir.path().join("link"); - fs::symlink("target", &link).unwrap(); + fs::symlink("target", link).unwrap(); cmd() .arg("-l") @@ -552,7 +552,7 @@ fn test_tree_no_dereference() { tmp.child("one.d").create_dir_all().unwrap(); tmp.child("one.d/samplefile").touch().unwrap(); let link = tmp.path().join("link"); - fs::symlink("one.d", &link).unwrap(); + fs::symlink("one.d", link).unwrap(); cmd() .arg("--tree") @@ -571,7 +571,7 @@ fn test_tree_dereference() { tmp.child("one.d").create_dir_all().unwrap(); tmp.child("one.d/samplefile").touch().unwrap(); let link = tmp.path().join("link"); - fs::symlink("one.d", &link).unwrap(); + fs::symlink("one.d", link).unwrap(); cmd() .arg("--ignore-config")