From f3900faddef466d938d0ce82c299924f18a5d8e9 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 8 Jun 2025 11:04:18 +0200 Subject: [PATCH] l10n: port uniq to translation + add french --- src/uu/uniq/locales/en-US.ftl | 36 +++++++++++++++ src/uu/uniq/locales/fr-FR.ftl | 41 +++++++++++++++++ src/uu/uniq/src/uniq.rs | 84 ++++++++++++++++++++--------------- 3 files changed, 124 insertions(+), 37 deletions(-) create mode 100644 src/uu/uniq/locales/fr-FR.ftl diff --git a/src/uu/uniq/locales/en-US.ftl b/src/uu/uniq/locales/en-US.ftl index 4ede870d1..558fecb05 100644 --- a/src/uu/uniq/locales/en-US.ftl +++ b/src/uu/uniq/locales/en-US.ftl @@ -5,3 +5,39 @@ uniq-after-help = Filter adjacent matching lines from INPUT (or standard input), Note: uniq does not detect repeated lines unless they are adjacent. You may want to sort the input first, or use sort -u without uniq. + +# Help messages +uniq-help-all-repeated = print all duplicate lines. Delimiting is done with blank lines. [default: none] +uniq-help-group = show all items, separating groups with an empty line. [default: separate] +uniq-help-check-chars = compare no more than N characters in lines +uniq-help-count = prefix lines by the number of occurrences +uniq-help-ignore-case = ignore differences in case when comparing +uniq-help-repeated = only print duplicate lines +uniq-help-skip-chars = avoid comparing the first N characters +uniq-help-skip-fields = avoid comparing the first N fields +uniq-help-unique = only print unique lines +uniq-help-zero-terminated = end lines with 0 byte, not newline + +# Error messages +uniq-error-write-line-terminator = Could not write line terminator +uniq-error-write-error = write error +uniq-error-invalid-argument = Invalid argument for { $opt_name }: { $arg } +uniq-error-try-help = Try 'uniq --help' for more information. +uniq-error-group-mutually-exclusive = --group is mutually exclusive with -c/-d/-D/-u +uniq-error-group-badoption = invalid argument 'badoption' for '--group' + Valid arguments are: + - 'prepend' + - 'append' + - 'separate' + - 'both' + +uniq-error-all-repeated-badoption = invalid argument 'badoption' for '--all-repeated' + Valid arguments are: + - 'none' + - 'prepend' + - 'separate' + +uniq-error-counts-and-repeated-meaningless = printing all duplicated lines and repeat counts is meaningless + Try 'uniq --help' for more information. + +uniq-error-could-not-open = Could not open { $path } diff --git a/src/uu/uniq/locales/fr-FR.ftl b/src/uu/uniq/locales/fr-FR.ftl new file mode 100644 index 000000000..f9f1cf130 --- /dev/null +++ b/src/uu/uniq/locales/fr-FR.ftl @@ -0,0 +1,41 @@ +uniq-about = Signaler ou omettre les lignes répétées. +uniq-usage = uniq [OPTION]... [ENTRÉE [SORTIE]] +uniq-after-help = Filtrer les lignes adjacentes correspondantes de ENTRÉE (ou l'entrée standard), + en écrivant vers SORTIE (ou la sortie standard). + Note : uniq ne détecte les lignes répétées que si elles sont adjacentes. + Vous pourriez vouloir trier l'entrée d'abord, ou utiliser sort -u sans uniq. + +# Messages d'aide +uniq-help-all-repeated = afficher toutes les lignes dupliquées. La délimitation se fait avec des lignes vides. [défaut : none] +uniq-help-group = afficher tous les éléments, en séparant les groupes avec une ligne vide. [défaut : separate] +uniq-help-check-chars = comparer au maximum N caractères dans les lignes +uniq-help-count = préfixer les lignes par le nombre d'occurrences +uniq-help-ignore-case = ignorer les différences de casse lors de la comparaison +uniq-help-repeated = afficher seulement les lignes dupliquées +uniq-help-skip-chars = éviter de comparer les N premiers caractères +uniq-help-skip-fields = éviter de comparer les N premiers champs +uniq-help-unique = afficher seulement les lignes uniques +uniq-help-zero-terminated = terminer les lignes avec un octet 0, pas une nouvelle ligne + +# Messages d'erreur +uniq-error-write-line-terminator = Impossible d'écrire le terminateur de ligne +uniq-error-write-error = erreur d'écriture +uniq-error-invalid-argument = Argument invalide pour { $opt_name } : { $arg } +uniq-error-try-help = Essayez 'uniq --help' pour plus d'informations. +uniq-error-group-mutually-exclusive = --group est mutuellement exclusif avec -c/-d/-D/-u +uniq-error-group-badoption = argument invalide 'badoption' pour '--group' + Arguments valides : + - 'prepend' + - 'append' + - 'separate' + - 'both' + +uniq-error-all-repeated-badoption = argument invalide 'badoption' pour '--all-repeated' + Arguments valides : + - 'none' + - 'prepend' + - 'separate' + +uniq-error-counts-and-repeated-meaningless = afficher toutes les lignes dupliquées et les nombres de répétitions n'a pas de sens + Essayez 'uniq --help' pour plus d'informations. +uniq-error-could-not-open = Impossible d'ouvrir { $path } diff --git a/src/uu/uniq/src/uniq.rs b/src/uu/uniq/src/uniq.rs index 00ca6751b..f2caa9765 100644 --- a/src/uu/uniq/src/uniq.rs +++ b/src/uu/uniq/src/uniq.rs @@ -7,6 +7,7 @@ use clap::{ Arg, ArgAction, ArgMatches, Command, builder::ValueParser, error::ContextKind, error::Error, error::ErrorKind, }; +use std::collections::HashMap; use std::ffi::{OsStr, OsString}; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Write, stdin, stdout}; @@ -17,7 +18,7 @@ use uucore::format_usage; use uucore::parser::shortcut_value_parser::ShortcutValueParser; use uucore::posix::{OBSOLETE, posix_version}; -use uucore::locale::get_message; +use uucore::locale::{get_message, get_message_with_args}; pub mod options { pub static ALL_REPEATED: &str = "all-repeated"; @@ -60,7 +61,7 @@ macro_rules! write_line_terminator { ($writer:expr, $line_terminator:expr) => { $writer .write_all(&[$line_terminator]) - .map_err_context(|| "Could not write line terminator".to_string()) + .map_err_context(|| get_message("uniq-error-write-line-terminator")) }; } @@ -108,7 +109,9 @@ impl Uniq { { write_line_terminator!(writer, line_terminator)?; } - writer.flush().map_err_context(|| "write error".into())?; + writer + .flush() + .map_err_context(|| get_message("uniq-error-write-error"))?; Ok(()) } @@ -155,7 +158,7 @@ impl Uniq { // Skip self.slice_start bytes (if -s was used). // self.slice_start is how many characters to skip, but historically - // uniq’s `-s N` means “skip N *bytes*,” so do that literally: + // uniq's `-s N` means "skip N *bytes*," so do that literally: let skip_bytes = self.slice_start.unwrap_or(0); let fields_to_check = if skip_bytes < fields_to_check.len() { &fields_to_check[skip_bytes..] @@ -167,7 +170,7 @@ impl Uniq { // Convert the leftover bytes to UTF-8 for character-based -w // If invalid UTF-8, just compare them as individual bytes (fallback). let Ok(string_after_skip) = std::str::from_utf8(fields_to_check) else { - // Fallback: if invalid UTF-8, treat them as single-byte “chars” + // Fallback: if invalid UTF-8, treat them as single-byte "chars" return closure(&mut fields_to_check.iter().map(|&b| b as char)); }; @@ -225,7 +228,7 @@ impl Uniq { } else { writer.write_all(line) } - .map_err_context(|| "write error".to_string())?; + .map_err_context(|| get_message("uniq-error-write-error"))?; write_line_terminator!(writer, line_terminator) } @@ -239,7 +242,13 @@ fn opt_parsed(opt_name: &str, matches: &ArgMatches) -> UResult> { IntErrorKind::PosOverflow => Ok(Some(usize::MAX)), _ => Err(USimpleError::new( 1, - format!("Invalid argument for {opt_name}: {}", arg_str.maybe_quote()), + get_message_with_args( + "uniq-error-invalid-argument", + HashMap::from([ + ("opt_name".to_string(), opt_name.to_string()), + ("arg".to_string(), arg_str.maybe_quote().to_string()), + ]), + ), )), }, }, @@ -509,11 +518,11 @@ fn handle_extract_obs_skip_chars( /// for `uniq` hardcode and require the exact wording of the error message /// and it is not compatible with how Clap formats and displays those error messages. fn map_clap_errors(clap_error: Error) -> Box { - let footer = "Try 'uniq --help' for more information."; - let override_arg_conflict = - "--group is mutually exclusive with -c/-d/-D/-u\n".to_string() + footer; - let override_group_badoption = "invalid argument 'badoption' for '--group'\nValid arguments are:\n - 'prepend'\n - 'append'\n - 'separate'\n - 'both'\n".to_string() + footer; - let override_all_repeated_badoption = "invalid argument 'badoption' for '--all-repeated'\nValid arguments are:\n - 'none'\n - 'prepend'\n - 'separate'\n".to_string() + footer; + let footer = get_message("uniq-error-try-help"); + let override_arg_conflict = get_message("uniq-error-group-mutually-exclusive") + "\n" + &footer; + let override_group_badoption = get_message("uniq-error-group-badoption") + "\n" + &footer; + let override_all_repeated_badoption = + get_message("uniq-error-all-repeated-badoption") + "\n" + &footer; let error_message = match clap_error.kind() { ErrorKind::ArgumentConflict => override_arg_conflict, @@ -578,7 +587,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if uniq.show_counts && uniq.all_repeated { return Err(USimpleError::new( 1, - "printing all duplicated lines and repeat counts is meaningless\nTry 'uniq --help' for more information.", + get_message("uniq-error-counts-and-repeated-meaningless"), )); } @@ -599,12 +608,8 @@ pub fn uu_app() -> Command { Arg::new(options::ALL_REPEATED) .short('D') .long(options::ALL_REPEATED) - .value_parser(ShortcutValueParser::new([ - "none", - "prepend", - "separate" - ])) - .help("print all duplicate lines. Delimiting is done with blank lines. [default: none]") + .value_parser(ShortcutValueParser::new(["none", "prepend", "separate"])) + .help(get_message("uniq-help-all-repeated")) .value_name("delimit-method") .num_args(0..=1) .default_missing_value("none") @@ -614,12 +619,9 @@ pub fn uu_app() -> Command { Arg::new(options::GROUP) .long(options::GROUP) .value_parser(ShortcutValueParser::new([ - "separate", - "prepend", - "append", - "both", + "separate", "prepend", "append", "both", ])) - .help("show all items, separating groups with an empty line. [default: separate]") + .help(get_message("uniq-help-group")) .value_name("group-method") .num_args(0..=1) .default_missing_value("separate") @@ -628,63 +630,63 @@ pub fn uu_app() -> Command { options::REPEATED, options::ALL_REPEATED, options::UNIQUE, - options::COUNT + options::COUNT, ]), ) .arg( Arg::new(options::CHECK_CHARS) .short('w') .long(options::CHECK_CHARS) - .help("compare no more than N characters in lines") + .help(get_message("uniq-help-check-chars")) .value_name("N"), ) .arg( Arg::new(options::COUNT) .short('c') .long(options::COUNT) - .help("prefix lines by the number of occurrences") + .help(get_message("uniq-help-count")) .action(ArgAction::SetTrue), ) .arg( Arg::new(options::IGNORE_CASE) .short('i') .long(options::IGNORE_CASE) - .help("ignore differences in case when comparing") + .help(get_message("uniq-help-ignore-case")) .action(ArgAction::SetTrue), ) .arg( Arg::new(options::REPEATED) .short('d') .long(options::REPEATED) - .help("only print duplicate lines") + .help(get_message("uniq-help-repeated")) .action(ArgAction::SetTrue), ) .arg( Arg::new(options::SKIP_CHARS) .short('s') .long(options::SKIP_CHARS) - .help("avoid comparing the first N characters") + .help(get_message("uniq-help-skip-chars")) .value_name("N"), ) .arg( Arg::new(options::SKIP_FIELDS) .short('f') .long(options::SKIP_FIELDS) - .help("avoid comparing the first N fields") + .help(get_message("uniq-help-skip-fields")) .value_name("N"), ) .arg( Arg::new(options::UNIQUE) .short('u') .long(options::UNIQUE) - .help("only print unique lines") + .help(get_message("uniq-help-unique")) .action(ArgAction::SetTrue), ) .arg( Arg::new(options::ZERO_TERMINATED) .short('z') .long(options::ZERO_TERMINATED) - .help("end lines with 0 byte, not newline") + .help(get_message("uniq-help-zero-terminated")) .action(ArgAction::SetTrue), ) .arg( @@ -721,8 +723,12 @@ fn get_delimiter(matches: &ArgMatches) -> Delimiters { fn open_input_file(in_file_name: Option<&OsStr>) -> UResult> { Ok(match in_file_name { Some(path) if path != "-" => { - let in_file = File::open(path) - .map_err_context(|| format!("Could not open {}", path.maybe_quote()))?; + let in_file = File::open(path).map_err_context(|| { + get_message_with_args( + "uniq-error-could-not-open", + HashMap::from([("path".to_string(), path.maybe_quote().to_string())]), + ) + })?; Box::new(BufReader::new(in_file)) } _ => Box::new(stdin().lock()), @@ -733,8 +739,12 @@ fn open_input_file(in_file_name: Option<&OsStr>) -> UResult> { fn open_output_file(out_file_name: Option<&OsStr>) -> UResult> { Ok(match out_file_name { Some(path) if path != "-" => { - let out_file = File::create(path) - .map_err_context(|| format!("Could not open {}", path.maybe_quote()))?; + let out_file = File::create(path).map_err_context(|| { + get_message_with_args( + "uniq-error-could-not-open", + HashMap::from([("path".to_string(), path.maybe_quote().to_string())]), + ) + })?; Box::new(BufWriter::new(out_file)) } _ => Box::new(stdout().lock()),