1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 19:17:43 +00:00

Merge pull request #8175 from sylvestre/l10n-shuf

l10n: port shuf for translation + add french
This commit is contained in:
Daniel Hofstetter 2025-06-23 14:40:37 +02:00 committed by GitHub
commit ce617a5375
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 87 additions and 19 deletions

View file

@ -4,3 +4,22 @@ shuf-about = Shuffle the input by outputting a random permutation of input lines
shuf-usage = shuf [OPTION]... [FILE] shuf-usage = shuf [OPTION]... [FILE]
shuf -e [OPTION]... [ARG]... shuf -e [OPTION]... [ARG]...
shuf -i LO-HI [OPTION]... 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

View file

@ -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

View file

@ -10,7 +10,7 @@ use clap::{Arg, ArgAction, Command};
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use rand::seq::IndexedRandom; use rand::seq::IndexedRandom;
use rand::{Rng, RngCore}; use rand::{Rng, RngCore};
use std::collections::HashSet; use std::collections::{HashMap, HashSet};
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::fs::File; use std::fs::File;
use std::io::{BufWriter, Error, Read, Write, stdin, stdout}; use std::io::{BufWriter, Error, Read, Write, stdin, stdout};
@ -20,7 +20,7 @@ use std::str::FromStr;
use uucore::display::{OsWrite, Quotable}; use uucore::display::{OsWrite, Quotable};
use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::format_usage; use uucore::format_usage;
use uucore::locale::get_message; use uucore::locale::{get_message, get_message_with_args};
mod rand_read_adapter; mod rand_read_adapter;
@ -71,7 +71,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if let Some(second_file) = operands.next() { if let Some(second_file) = operands.next() {
return Err(UUsageError::new( return Err(UUsageError::new(
1, 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()) Mode::Default(file.into())
@ -101,8 +104,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let mut output = BufWriter::new(match options.output { let mut output = BufWriter::new(match options.output {
None => Box::new(stdout()) as Box<dyn OsWrite>, None => Box::new(stdout()) as Box<dyn OsWrite>,
Some(ref s) => { Some(ref s) => {
let file = File::create(s) let file = File::create(s).map_err_context(|| {
.map_err_context(|| format!("failed to open {} for writing", s.quote()))?; 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<dyn OsWrite> Box::new(file) as Box<dyn OsWrite>
} }
}); });
@ -114,8 +121,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let mut rng = match options.random_source { let mut rng = match options.random_source {
Some(ref r) => { Some(ref r) => {
let file = File::open(r) let file = File::open(r).map_err_context(|| {
.map_err_context(|| format!("failed to open random source {}", r.quote()))?; 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)) WrappedRng::RngFile(rand_read_adapter::ReadRng::new(file))
} }
None => WrappedRng::RngDefault(rand::rng()), None => WrappedRng::RngDefault(rand::rng()),
@ -149,7 +160,7 @@ pub fn uu_app() -> Command {
Arg::new(options::ECHO) Arg::new(options::ECHO)
.short('e') .short('e')
.long(options::ECHO) .long(options::ECHO)
.help("treat each ARG as an input line") .help(get_message("shuf-help-echo"))
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.overrides_with(options::ECHO) .overrides_with(options::ECHO)
.conflicts_with(options::INPUT_RANGE), .conflicts_with(options::INPUT_RANGE),
@ -159,7 +170,7 @@ pub fn uu_app() -> Command {
.short('i') .short('i')
.long(options::INPUT_RANGE) .long(options::INPUT_RANGE)
.value_name("LO-HI") .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) .value_parser(parse_range)
.conflicts_with(options::FILE_OR_ARGS), .conflicts_with(options::FILE_OR_ARGS),
) )
@ -169,7 +180,7 @@ pub fn uu_app() -> Command {
.long(options::HEAD_COUNT) .long(options::HEAD_COUNT)
.value_name("COUNT") .value_name("COUNT")
.action(ArgAction::Append) .action(ArgAction::Append)
.help("output at most COUNT lines") .help(get_message("shuf-help-head-count"))
.value_parser(usize::from_str), .value_parser(usize::from_str),
) )
.arg( .arg(
@ -177,7 +188,7 @@ pub fn uu_app() -> Command {
.short('o') .short('o')
.long(options::OUTPUT) .long(options::OUTPUT)
.value_name("FILE") .value_name("FILE")
.help("write result to FILE instead of standard output") .help(get_message("shuf-help-output"))
.value_parser(ValueParser::path_buf()) .value_parser(ValueParser::path_buf())
.value_hint(clap::ValueHint::FilePath), .value_hint(clap::ValueHint::FilePath),
) )
@ -185,7 +196,7 @@ pub fn uu_app() -> Command {
Arg::new(options::RANDOM_SOURCE) Arg::new(options::RANDOM_SOURCE)
.long(options::RANDOM_SOURCE) .long(options::RANDOM_SOURCE)
.value_name("FILE") .value_name("FILE")
.help("get random bytes from FILE") .help(get_message("shuf-help-random-source"))
.value_parser(ValueParser::path_buf()) .value_parser(ValueParser::path_buf())
.value_hint(clap::ValueHint::FilePath), .value_hint(clap::ValueHint::FilePath),
) )
@ -193,7 +204,7 @@ pub fn uu_app() -> Command {
Arg::new(options::REPEAT) Arg::new(options::REPEAT)
.short('r') .short('r')
.long(options::REPEAT) .long(options::REPEAT)
.help("output lines can be repeated") .help(get_message("shuf-help-repeat"))
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.overrides_with(options::REPEAT), .overrides_with(options::REPEAT),
) )
@ -201,7 +212,7 @@ pub fn uu_app() -> Command {
Arg::new(options::ZERO_TERMINATED) Arg::new(options::ZERO_TERMINATED)
.short('z') .short('z')
.long(options::ZERO_TERMINATED) .long(options::ZERO_TERMINATED)
.help("line delimiter is NUL, not newline") .help(get_message("shuf-help-zero-terminated"))
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.overrides_with(options::ZERO_TERMINATED), .overrides_with(options::ZERO_TERMINATED),
) )
@ -218,7 +229,7 @@ fn read_input_file(filename: &Path) -> UResult<Vec<u8>> {
let mut data = Vec::new(); let mut data = Vec::new();
stdin() stdin()
.read_to_end(&mut data) .read_to_end(&mut data)
.map_err_context(|| "read error".into())?; .map_err_context(|| get_message("shuf-error-read-error"))?;
Ok(data) Ok(data)
} else { } else {
std::fs::read(filename).map_err_context(|| filename.maybe_quote().to_string()) 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]> { impl<'a> Shufable for Vec<&'a [u8]> {
type Item = &'a [u8]; type Item = &'a [u8];
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
(**self).is_empty() (**self).is_empty()
} }
fn choose(&self, rng: &mut WrappedRng) -> Self::Item { fn choose(&self, rng: &mut WrappedRng) -> Self::Item {
// Note: "copied()" only copies the reference, not the entire [u8]. // Note: "copied()" only copies the reference, not the entire [u8].
// Returns None if the slice is empty. We checked this before, so // Returns None if the slice is empty. We checked this before, so
// this is safe. // this is safe.
(**self).choose(rng).unwrap() (**self).choose(rng).unwrap()
} }
fn partial_shuffle<'b>( fn partial_shuffle<'b>(
&'b mut self, &'b mut self,
rng: &'b mut WrappedRng, rng: &'b mut WrappedRng,
@ -271,12 +285,15 @@ impl<'a> Shufable for Vec<&'a [u8]> {
impl<'a> Shufable for Vec<&'a OsStr> { impl<'a> Shufable for Vec<&'a OsStr> {
type Item = &'a OsStr; type Item = &'a OsStr;
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
(**self).is_empty() (**self).is_empty()
} }
fn choose(&self, rng: &mut WrappedRng) -> Self::Item { fn choose(&self, rng: &mut WrappedRng) -> Self::Item {
(**self).choose(rng).unwrap() (**self).choose(rng).unwrap()
} }
fn partial_shuffle<'b>( fn partial_shuffle<'b>(
&'b mut self, &'b mut self,
rng: &'b mut WrappedRng, rng: &'b mut WrappedRng,
@ -288,12 +305,15 @@ impl<'a> Shufable for Vec<&'a OsStr> {
impl Shufable for RangeInclusive<usize> { impl Shufable for RangeInclusive<usize> {
type Item = usize; type Item = usize;
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.is_empty() self.is_empty()
} }
fn choose(&self, rng: &mut WrappedRng) -> usize { fn choose(&self, rng: &mut WrappedRng) -> usize {
rng.random_range(self.clone()) rng.random_range(self.clone())
} }
fn partial_shuffle<'b>( fn partial_shuffle<'b>(
&'b mut self, &'b mut self,
rng: &'b mut WrappedRng, rng: &'b mut WrappedRng,
@ -420,10 +440,14 @@ fn shuf_exec(
rng: &mut WrappedRng, rng: &mut WrappedRng,
output: &mut BufWriter<Box<dyn OsWrite>>, output: &mut BufWriter<Box<dyn OsWrite>>,
) -> UResult<()> { ) -> UResult<()> {
let ctx = || "write failed".to_string(); let ctx = || get_message("shuf-error-write-failed");
if opts.repeat { if opts.repeat {
if input.is_empty() { 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 { for _ in 0..opts.head_count {
let r = input.choose(rng); let r = input.choose(rng);
@ -449,10 +473,10 @@ fn parse_range(input_range: &str) -> Result<RangeInclusive<usize>, String> {
if begin <= end || begin == end + 1 { if begin <= end || begin == end + 1 {
Ok(begin..=end) Ok(begin..=end)
} else { } else {
Err("start exceeds end".into()) Err(get_message("shuf-error-start-exceeds-end"))
} }
} else { } else {
Err("missing '-'".into()) Err(get_message("shuf-error-missing-dash"))
} }
} }