From 51017b60769d1ba19e4ca8d1f2311fd2aa6c2e2f Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 14 Jun 2025 10:14:18 +0200 Subject: [PATCH] l10n: port shuf for translation + add french --- src/uu/shuf/locales/en-US.ftl | 19 +++++++++++ src/uu/shuf/locales/fr-FR.ftl | 25 ++++++++++++++ src/uu/shuf/src/shuf.rs | 62 ++++++++++++++++++++++++----------- 3 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 src/uu/shuf/locales/fr-FR.ftl diff --git a/src/uu/shuf/locales/en-US.ftl b/src/uu/shuf/locales/en-US.ftl index 5348f1f7e..24876e6a3 100644 --- a/src/uu/shuf/locales/en-US.ftl +++ b/src/uu/shuf/locales/en-US.ftl @@ -4,3 +4,22 @@ shuf-about = Shuffle the input by outputting a random permutation of input lines shuf-usage = shuf [OPTION]... [FILE] shuf -e [OPTION]... [ARG]... shuf -i LO-HI [OPTION]... + +# Help messages +shuf-help-echo = treat each ARG as an input line +shuf-help-input-range = treat each number LO through HI as an input line +shuf-help-head-count = output at most COUNT lines +shuf-help-output = write result to FILE instead of standard output +shuf-help-random-source = get random bytes from FILE +shuf-help-repeat = output lines can be repeated +shuf-help-zero-terminated = line delimiter is NUL, not newline + +# Error messages +shuf-error-unexpected-argument = unexpected argument { $arg } found +shuf-error-failed-to-open-for-writing = failed to open { $file } for writing +shuf-error-failed-to-open-random-source = failed to open random source { $file } +shuf-error-read-error = read error +shuf-error-no-lines-to-repeat = no lines to repeat +shuf-error-start-exceeds-end = start exceeds end +shuf-error-missing-dash = missing '-' +shuf-error-write-failed = write failed diff --git a/src/uu/shuf/locales/fr-FR.ftl b/src/uu/shuf/locales/fr-FR.ftl new file mode 100644 index 000000000..bbe470859 --- /dev/null +++ b/src/uu/shuf/locales/fr-FR.ftl @@ -0,0 +1,25 @@ +shuf-about = Mélanger l'entrée en affichant une permutation aléatoire des lignes d'entrée. + Chaque permutation de sortie est également probable. + Sans FICHIER, ou quand FICHIER est -, lire l'entrée standard. +shuf-usage = shuf [OPTION]... [FICHIER] + shuf -e [OPTION]... [ARG]... + shuf -i MIN-MAX [OPTION]... + +# Messages d'aide +shuf-help-echo = traiter chaque ARG comme une ligne d'entrée +shuf-help-input-range = traiter chaque nombre de MIN à MAX comme une ligne d'entrée +shuf-help-head-count = afficher au maximum NOMBRE lignes +shuf-help-output = écrire le résultat dans FICHIER au lieu de la sortie standard +shuf-help-random-source = obtenir des octets aléatoires depuis FICHIER +shuf-help-repeat = les lignes de sortie peuvent être répétées +shuf-help-zero-terminated = le délimiteur de ligne est NUL, pas nouvelle ligne + +# Messages d'erreur +shuf-error-unexpected-argument = argument inattendu { $arg } trouvé +shuf-error-failed-to-open-for-writing = échec de l'ouverture de { $file } en écriture +shuf-error-failed-to-open-random-source = échec de l'ouverture de la source aléatoire { $file } +shuf-error-read-error = erreur de lecture +shuf-error-no-lines-to-repeat = aucune ligne à répéter +shuf-error-start-exceeds-end = le début dépasse la fin +shuf-error-missing-dash = '-' manquant +shuf-error-write-failed = échec de l'écriture diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index db4463615..ebfb8c3bd 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -10,7 +10,7 @@ use clap::{Arg, ArgAction, Command}; use rand::prelude::SliceRandom; use rand::seq::IndexedRandom; use rand::{Rng, RngCore}; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::ffi::{OsStr, OsString}; use std::fs::File; use std::io::{BufWriter, Error, Read, Write, stdin, stdout}; @@ -20,7 +20,7 @@ use std::str::FromStr; use uucore::display::{OsWrite, Quotable}; use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::format_usage; -use uucore::locale::get_message; +use uucore::locale::{get_message, get_message_with_args}; mod rand_read_adapter; @@ -71,7 +71,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if let Some(second_file) = operands.next() { return Err(UUsageError::new( 1, - format!("unexpected argument {} found", second_file.quote()), + get_message_with_args( + "shuf-error-unexpected-argument", + HashMap::from([("arg".to_string(), second_file.quote().to_string())]), + ), )); }; Mode::Default(file.into()) @@ -101,8 +104,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let mut output = BufWriter::new(match options.output { None => Box::new(stdout()) as Box, Some(ref s) => { - let file = File::create(s) - .map_err_context(|| format!("failed to open {} for writing", s.quote()))?; + let file = File::create(s).map_err_context(|| { + get_message_with_args( + "shuf-error-failed-to-open-for-writing", + HashMap::from([("file".to_string(), s.quote().to_string())]), + ) + })?; Box::new(file) as Box } }); @@ -114,8 +121,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let mut rng = match options.random_source { Some(ref r) => { - let file = File::open(r) - .map_err_context(|| format!("failed to open random source {}", r.quote()))?; + let file = File::open(r).map_err_context(|| { + get_message_with_args( + "shuf-error-failed-to-open-random-source", + HashMap::from([("file".to_string(), r.quote().to_string())]), + ) + })?; WrappedRng::RngFile(rand_read_adapter::ReadRng::new(file)) } None => WrappedRng::RngDefault(rand::rng()), @@ -149,7 +160,7 @@ pub fn uu_app() -> Command { Arg::new(options::ECHO) .short('e') .long(options::ECHO) - .help("treat each ARG as an input line") + .help(get_message("shuf-help-echo")) .action(ArgAction::SetTrue) .overrides_with(options::ECHO) .conflicts_with(options::INPUT_RANGE), @@ -159,7 +170,7 @@ pub fn uu_app() -> Command { .short('i') .long(options::INPUT_RANGE) .value_name("LO-HI") - .help("treat each number LO through HI as an input line") + .help(get_message("shuf-help-input-range")) .value_parser(parse_range) .conflicts_with(options::FILE_OR_ARGS), ) @@ -169,7 +180,7 @@ pub fn uu_app() -> Command { .long(options::HEAD_COUNT) .value_name("COUNT") .action(ArgAction::Append) - .help("output at most COUNT lines") + .help(get_message("shuf-help-head-count")) .value_parser(usize::from_str), ) .arg( @@ -177,7 +188,7 @@ pub fn uu_app() -> Command { .short('o') .long(options::OUTPUT) .value_name("FILE") - .help("write result to FILE instead of standard output") + .help(get_message("shuf-help-output")) .value_parser(ValueParser::path_buf()) .value_hint(clap::ValueHint::FilePath), ) @@ -185,7 +196,7 @@ pub fn uu_app() -> Command { Arg::new(options::RANDOM_SOURCE) .long(options::RANDOM_SOURCE) .value_name("FILE") - .help("get random bytes from FILE") + .help(get_message("shuf-help-random-source")) .value_parser(ValueParser::path_buf()) .value_hint(clap::ValueHint::FilePath), ) @@ -193,7 +204,7 @@ pub fn uu_app() -> Command { Arg::new(options::REPEAT) .short('r') .long(options::REPEAT) - .help("output lines can be repeated") + .help(get_message("shuf-help-repeat")) .action(ArgAction::SetTrue) .overrides_with(options::REPEAT), ) @@ -201,7 +212,7 @@ pub fn uu_app() -> Command { Arg::new(options::ZERO_TERMINATED) .short('z') .long(options::ZERO_TERMINATED) - .help("line delimiter is NUL, not newline") + .help(get_message("shuf-help-zero-terminated")) .action(ArgAction::SetTrue) .overrides_with(options::ZERO_TERMINATED), ) @@ -218,7 +229,7 @@ fn read_input_file(filename: &Path) -> UResult> { let mut data = Vec::new(); stdin() .read_to_end(&mut data) - .map_err_context(|| "read error".into())?; + .map_err_context(|| get_message("shuf-error-read-error"))?; Ok(data) } else { std::fs::read(filename).map_err_context(|| filename.maybe_quote().to_string()) @@ -250,15 +261,18 @@ trait Shufable { impl<'a> Shufable for Vec<&'a [u8]> { type Item = &'a [u8]; + fn is_empty(&self) -> bool { (**self).is_empty() } + fn choose(&self, rng: &mut WrappedRng) -> Self::Item { // Note: "copied()" only copies the reference, not the entire [u8]. // Returns None if the slice is empty. We checked this before, so // this is safe. (**self).choose(rng).unwrap() } + fn partial_shuffle<'b>( &'b mut self, rng: &'b mut WrappedRng, @@ -271,12 +285,15 @@ impl<'a> Shufable for Vec<&'a [u8]> { impl<'a> Shufable for Vec<&'a OsStr> { type Item = &'a OsStr; + fn is_empty(&self) -> bool { (**self).is_empty() } + fn choose(&self, rng: &mut WrappedRng) -> Self::Item { (**self).choose(rng).unwrap() } + fn partial_shuffle<'b>( &'b mut self, rng: &'b mut WrappedRng, @@ -288,12 +305,15 @@ impl<'a> Shufable for Vec<&'a OsStr> { impl Shufable for RangeInclusive { type Item = usize; + fn is_empty(&self) -> bool { self.is_empty() } + fn choose(&self, rng: &mut WrappedRng) -> usize { rng.random_range(self.clone()) } + fn partial_shuffle<'b>( &'b mut self, rng: &'b mut WrappedRng, @@ -420,10 +440,14 @@ fn shuf_exec( rng: &mut WrappedRng, output: &mut BufWriter>, ) -> UResult<()> { - let ctx = || "write failed".to_string(); + let ctx = || get_message("shuf-error-write-failed"); + if opts.repeat { if input.is_empty() { - return Err(USimpleError::new(1, "no lines to repeat")); + return Err(USimpleError::new( + 1, + get_message("shuf-error-no-lines-to-repeat"), + )); } for _ in 0..opts.head_count { let r = input.choose(rng); @@ -449,10 +473,10 @@ fn parse_range(input_range: &str) -> Result, String> { if begin <= end || begin == end + 1 { Ok(begin..=end) } else { - Err("start exceeds end".into()) + Err(get_message("shuf-error-start-exceeds-end")) } } else { - Err("missing '-'".into()) + Err(get_message("shuf-error-missing-dash")) } }