From 625dec0be15b8fc884f43f69b8fc860d8b19e361 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 17 Jun 2025 21:50:11 +0200 Subject: [PATCH] l10n: port env for translation + add french --- src/uu/env/locales/en-US.ftl | 40 +++++++ src/uu/env/locales/fr-FR.ftl | 43 +++++++ src/uu/env/src/env.rs | 224 ++++++++++++++++++++++++----------- 3 files changed, 240 insertions(+), 67 deletions(-) create mode 100644 src/uu/env/locales/fr-FR.ftl diff --git a/src/uu/env/locales/en-US.ftl b/src/uu/env/locales/en-US.ftl index 4204cb2e4..dd7cf2176 100644 --- a/src/uu/env/locales/en-US.ftl +++ b/src/uu/env/locales/en-US.ftl @@ -1,3 +1,43 @@ env-about = Set each NAME to VALUE in the environment and run COMMAND env-usage = env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...] env-after-help = A mere - implies -i. If no COMMAND, print the resulting environment. + +# Help messages +env-help-ignore-environment = start with an empty environment +env-help-chdir = change working directory to DIR +env-help-null = end each output line with a 0 byte rather than a newline (only valid when printing the environment) +env-help-file = read and set variables from a ".env"-style configuration file (prior to any unset and/or set) +env-help-unset = remove variable from the environment +env-help-debug = print verbose information for each processing step +env-help-split-string = process and split S into separate arguments; used to pass multiple arguments on shebang lines +env-help-argv0 = Override the zeroth argument passed to the command being executed. Without this option a default value of `command` is used. +env-help-ignore-signal = set handling of SIG signal(s) to do nothing + +# Error messages +env-error-missing-closing-quote = no terminating quote in -S string at position { $position } for quote '{ $quote }' +env-error-invalid-backslash-at-end = invalid backslash at end of string in -S at position { $position } in context { $context } +env-error-backslash-c-not-allowed = '\c' must not appear in double-quoted -S string at position { $position } +env-error-invalid-sequence = invalid sequence '\{ $char }' in -S at position { $position } +env-error-missing-closing-brace = Missing closing brace at position { $position } +env-error-missing-variable = Missing variable name at position { $position } +env-error-missing-closing-brace-after-value = Missing closing brace after default value at position { $position } +env-error-unexpected-number = Unexpected character: '{ $char }', expected variable name must not start with 0..9 at position { $position } +env-error-expected-brace-or-colon = Unexpected character: '{ $char }', expected a closing brace ('{"}"}') or colon (':') at position { $position } +env-error-cannot-specify-null-with-command = cannot specify --null (-0) with command +env-error-invalid-signal = { $signal }: invalid signal +env-error-config-file = { $file }: { $error } +env-error-variable-name-issue = variable name issue (at { $position }): { $error } +env-error-generic = Error: { $error } +env-error-no-such-file = { $program }: No such file or directory +env-error-use-s-shebang = use -[v]S to pass options in shebang lines +env-error-cannot-unset = cannot unset '{ $name }': Invalid argument +env-error-cannot-unset-invalid = cannot unset { $name }: Invalid argument +env-error-must-specify-command-with-chdir = must specify command with --chdir (-C) +env-error-cannot-change-directory = cannot change directory to { $directory }: { $error } +env-error-argv0-not-supported = --argv0 is currently not supported on this platform +env-error-permission-denied = { $program }: Permission denied +env-error-unknown = unknown error: { $error } +env-error-failed-set-signal-action = failed to set signal action for signal { $signal }: { $error } + +# Warning messages +env-warning-no-name-specified = no name specified for value { $value } diff --git a/src/uu/env/locales/fr-FR.ftl b/src/uu/env/locales/fr-FR.ftl new file mode 100644 index 000000000..cb422a35a --- /dev/null +++ b/src/uu/env/locales/fr-FR.ftl @@ -0,0 +1,43 @@ +env-about = Définir chaque NOM à VALEUR dans l'environnement et exécuter COMMANDE +env-usage = env [OPTION]... [-] [NOM=VALEUR]... [COMMANDE [ARG]...] +env-after-help = Un simple - implique -i. Si aucune COMMANDE, afficher l'environnement résultant. + +# Messages d'aide +env-help-ignore-environment = commencer avec un environnement vide +env-help-chdir = changer le répertoire de travail vers RÉP +env-help-null = terminer chaque ligne de sortie avec un octet 0 plutôt qu'un retour à la ligne (valide uniquement lors de l'affichage de l'environnement) +env-help-file = lire et définir les variables à partir d'un fichier de configuration de style ".env" (avant toute suppression et/ou définition) +env-help-unset = supprimer la variable de l'environnement +env-help-debug = afficher des informations détaillées pour chaque étape de traitement +env-help-split-string = traiter et diviser S en arguments séparés ; utilisé pour passer plusieurs arguments sur les lignes shebang +env-help-argv0 = Remplacer le zéroième argument passé à la commande en cours d'exécution. Sans cette option, une valeur par défaut de `command` est utilisée. +env-help-ignore-signal = définir la gestion du/des signal/signaux SIG pour ne rien faire + +# Messages d'erreur +env-error-missing-closing-quote = aucune guillemet de fermeture dans la chaîne -S à la position { $position } pour la guillemet '{ $quote }' +env-error-invalid-backslash-at-end = barre oblique inverse invalide à la fin de la chaîne dans -S à la position { $position } dans le contexte { $context } +env-error-backslash-c-not-allowed = '\\c' ne doit pas apparaître dans une chaîne -S entre guillemets doubles à la position { $position } +env-error-invalid-sequence = séquence invalide '\\{ $char }' dans -S à la position { $position } +env-error-missing-closing-brace = Accolade fermante manquante à la position { $position } +env-error-missing-variable = Nom de variable manquant à la position { $position } +env-error-missing-closing-brace-after-value = Accolade fermante manquante après la valeur par défaut à la position { $position } +env-error-unexpected-number = Caractère inattendu : '{ $char }', le nom de variable attendu ne doit pas commencer par 0..9 à la position { $position } +env-error-expected-brace-or-colon = Caractère inattendu : '{ $char }', accolade fermante ('}') ou deux-points (':') attendu à la position { $position } +env-error-cannot-specify-null-with-command = impossible de spécifier --null (-0) avec une commande +env-error-invalid-signal = { $signal } : signal invalide +env-error-config-file = { $file } : { $error } +env-error-variable-name-issue = problème de nom de variable (à { $position }) : { $error } +env-error-generic = Erreur : { $error } +env-error-no-such-file = { $program } : Aucun fichier ou répertoire de ce type +env-error-use-s-shebang = utilisez -[v]S pour passer des options dans les lignes shebang +env-error-cannot-unset = impossible de supprimer '{ $name }' : Argument invalide +env-error-cannot-unset-invalid = impossible de supprimer { $name } : Argument invalide +env-error-must-specify-command-with-chdir = doit spécifier une commande avec --chdir (-C) +env-error-cannot-change-directory = impossible de changer de répertoire vers { $directory } : { $error } +env-error-argv0-not-supported = --argv0 n'est actuellement pas supporté sur cette plateforme +env-error-permission-denied = { $program } : Permission refusée +env-error-unknown = erreur inconnue : { $error } +env-error-failed-set-signal-action = échec de la définition de l'action du signal pour le signal { $signal } : { $error } + +# Messages d'avertissement +env-warning-no-name-specified = aucun nom spécifié pour la valeur { $value } diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 7bb877b72..df3aabf40 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -22,6 +22,7 @@ use nix::sys::signal::{ SaFlags, SigAction, SigHandler, SigHandler::SigIgn, SigSet, Signal, raise, sigaction, signal, }; use std::borrow::Cow; +use std::collections::HashMap; use std::env; use std::ffi::{OsStr, OsString}; use std::io::{self, Write}; @@ -34,6 +35,7 @@ use std::process::{self}; use uucore::display::Quotable; use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError}; use uucore::line_ending::LineEnding; +use uucore::locale::{get_message, get_message_with_args}; #[cfg(unix)] use uucore::signals::signal_by_name_or_value; use uucore::{format_usage, show_warning}; @@ -42,23 +44,23 @@ use thiserror::Error; #[derive(Debug, Error, PartialEq)] pub enum EnvError { - #[error("no terminating quote in -S string")] + #[error("{}", get_message_with_args("env-error-missing-closing-quote", HashMap::from([("position".to_string(), .0.to_string()), ("quote".to_string(), .1.to_string())])))] EnvMissingClosingQuote(usize, char), - #[error("invalid backslash at end of string in -S")] + #[error("{}", get_message_with_args("env-error-invalid-backslash-at-end", HashMap::from([("position".to_string(), .0.to_string()), ("context".to_string(), .1.clone())])))] EnvInvalidBackslashAtEndOfStringInMinusS(usize, String), - #[error("'\\c' must not appear in double-quoted -S string")] + #[error("{}", get_message_with_args("env-error-backslash-c-not-allowed", HashMap::from([("position".to_string(), .0.to_string())])))] EnvBackslashCNotAllowedInDoubleQuotes(usize), - #[error("invalid sequence '\\{}' in -S",.1)] + #[error("{}", get_message_with_args("env-error-invalid-sequence", HashMap::from([("position".to_string(), .0.to_string()), ("char".to_string(), .1.to_string())])))] EnvInvalidSequenceBackslashXInMinusS(usize, char), - #[error("Missing closing brace")] + #[error("{}", get_message_with_args("env-error-missing-closing-brace", HashMap::from([("position".to_string(), .0.to_string())])))] EnvParsingOfVariableMissingClosingBrace(usize), - #[error("Missing variable name")] + #[error("{}", get_message_with_args("env-error-missing-variable", HashMap::from([("position".to_string(), .0.to_string())])))] EnvParsingOfMissingVariable(usize), - #[error("Missing closing brace after default value at {}",.0)] + #[error("{}", get_message_with_args("env-error-missing-closing-brace-after-value", HashMap::from([("position".to_string(), .0.to_string())])))] EnvParsingOfVariableMissingClosingBraceAfterValue(usize), - #[error("Unexpected character: '{}', expected variable name must not start with 0..9",.1)] + #[error("{}", get_message_with_args("env-error-unexpected-number", HashMap::from([("position".to_string(), .0.to_string()), ("char".to_string(), .1.clone())])))] EnvParsingOfVariableUnexpectedNumber(usize, String), - #[error("Unexpected character: '{}', expected a closing brace ('}}') or colon (':')",.1)] + #[error("{}", get_message_with_args("env-error-expected-brace-or-colon", HashMap::from([("position".to_string(), .0.to_string()), ("char".to_string(), .1.clone())])))] EnvParsingOfVariableExceptedBraceOrColon(usize, String), #[error("")] EnvReachedEnd, @@ -74,8 +76,6 @@ impl From for EnvError { } } -use uucore::locale::get_message; - mod options { pub const IGNORE_ENVIRONMENT: &str = "ignore-environment"; pub const CHDIR: &str = "chdir"; @@ -88,8 +88,6 @@ mod options { pub const IGNORE_SIGNAL: &str = "ignore-signal"; } -const ERROR_MSG_S_SHEBANG: &str = "use -[v]S to pass options in shebang lines"; - struct Options<'a> { ignore_env: bool, line_ending: LineEnding, @@ -131,7 +129,7 @@ fn parse_program_opt<'a>(opts: &mut Options<'a>, opt: &'a OsStr) -> UResult<()> if opts.line_ending == LineEnding::Nul { Err(UUsageError::new( 125, - "cannot specify --null (-0) with command".to_string(), + get_message("env-error-cannot-specify-null-with-command"), )) } else { opts.program.push(opt); @@ -143,7 +141,13 @@ fn parse_program_opt<'a>(opts: &mut Options<'a>, opt: &'a OsStr) -> UResult<()> fn parse_signal_value(signal_name: &str) -> UResult { let signal_name_upcase = signal_name.to_uppercase(); let optional_signal_value = signal_by_name_or_value(&signal_name_upcase); - let error = USimpleError::new(125, format!("{}: invalid signal", signal_name.quote())); + let error = USimpleError::new( + 125, + get_message_with_args( + "env-error-invalid-signal", + HashMap::from([("signal".to_string(), signal_name.quote().to_string())]), + ), + ); match optional_signal_value { Some(sig_val) => { if sig_val == 0 { @@ -177,7 +181,10 @@ fn parse_signal_opt<'a>(opts: &mut Options<'a>, opt: &'a OsStr) -> UResult<()> { let Some(sig_str) = sig.to_str() else { return Err(USimpleError::new( 1, - format!("{}: invalid signal", sig.quote()), + get_message_with_args( + "env-error-invalid-signal", + HashMap::from([("signal".to_string(), sig.quote().to_string())]), + ), )); }; let sig_val = parse_signal_value(sig_str)?; @@ -201,8 +208,18 @@ fn load_config_file(opts: &mut Options) -> UResult<()> { Ini::load_from_file(file) }; - let conf = - conf.map_err(|e| USimpleError::new(1, format!("{}: {e}", file.maybe_quote())))?; + let conf = conf.map_err(|e| { + USimpleError::new( + 1, + get_message_with_args( + "env-error-config-file", + HashMap::from([ + ("file".to_string(), file.maybe_quote().to_string()), + ("error".to_string(), e.to_string()), + ]), + ), + ) + })?; for (_, prop) in &conf { // ignore all INI section lines (treat them as comments) @@ -229,7 +246,7 @@ pub fn uu_app() -> Command { Arg::new(options::IGNORE_ENVIRONMENT) .short('i') .long(options::IGNORE_ENVIRONMENT) - .help("start with an empty environment") + .help(get_message("env-help-ignore-environment")) .action(ArgAction::SetTrue), ) .arg( @@ -240,16 +257,13 @@ pub fn uu_app() -> Command { .value_name("DIR") .value_parser(ValueParser::os_string()) .value_hint(clap::ValueHint::DirPath) - .help("change working directory to DIR"), + .help(get_message("env-help-chdir")), ) .arg( Arg::new(options::NULL) .short('0') .long(options::NULL) - .help( - "end each output line with a 0 byte rather than a newline (only \ - valid when printing the environment)", - ) + .help(get_message("env-help-null")) .action(ArgAction::SetTrue), ) .arg( @@ -260,10 +274,7 @@ pub fn uu_app() -> Command { .value_hint(clap::ValueHint::FilePath) .value_parser(ValueParser::os_string()) .action(ArgAction::Append) - .help( - "read and set variables from a \".env\"-style configuration file \ - (prior to any unset and/or set)", - ), + .help(get_message("env-help-file")), ) .arg( Arg::new(options::UNSET) @@ -272,14 +283,14 @@ pub fn uu_app() -> Command { .value_name("NAME") .action(ArgAction::Append) .value_parser(ValueParser::os_string()) - .help("remove variable from the environment"), + .help(get_message("env-help-unset")), ) .arg( Arg::new(options::DEBUG) .short('v') .long(options::DEBUG) .action(ArgAction::Count) - .help("print verbose information for each processing step"), + .help(get_message("env-help-debug")), ) .arg( Arg::new(options::SPLIT_STRING) // split string handling is implemented directly, not using CLAP. But this entry here is needed for the help information output. @@ -288,8 +299,9 @@ pub fn uu_app() -> Command { .value_name("S") .action(ArgAction::Set) .value_parser(ValueParser::os_string()) - .help("process and split S into separate arguments; used to pass multiple arguments on shebang lines") - ).arg( + .help(get_message("env-help-split-string")), + ) + .arg( Arg::new(options::ARGV0) .overrides_with(options::ARGV0) .short('a') @@ -297,13 +309,12 @@ pub fn uu_app() -> Command { .value_name("a") .action(ArgAction::Set) .value_parser(ValueParser::os_string()) - .help("Override the zeroth argument passed to the command being executed. \ - Without this option a default value of `command` is used.") + .help(get_message("env-help-argv0")), ) .arg( Arg::new("vars") .action(ArgAction::Append) - .value_parser(ValueParser::os_string()) + .value_parser(ValueParser::os_string()), ) .arg( Arg::new(options::IGNORE_SIGNAL) @@ -311,7 +322,7 @@ pub fn uu_app() -> Command { .value_name("SIG") .action(ArgAction::Append) .value_parser(ValueParser::os_string()) - .help("set handling of SIG signal(s) to do nothing") + .help(get_message("env-help-ignore-signal")), ) } @@ -325,22 +336,63 @@ pub fn parse_args_from_str(text: &NativeIntStr) -> UResult> USimpleError::new(125, e.to_string()) } EnvError::EnvMissingClosingQuote(_, _) => USimpleError::new(125, e.to_string()), - EnvError::EnvParsingOfVariableMissingClosingBrace(pos) => { - USimpleError::new(125, format!("variable name issue (at {pos}): {e}")) - } - EnvError::EnvParsingOfMissingVariable(pos) => { - USimpleError::new(125, format!("variable name issue (at {pos}): {e}")) - } - EnvError::EnvParsingOfVariableMissingClosingBraceAfterValue(pos) => { - USimpleError::new(125, format!("variable name issue (at {pos}): {e}")) - } - EnvError::EnvParsingOfVariableUnexpectedNumber(pos, _) => { - USimpleError::new(125, format!("variable name issue (at {pos}): {e}")) - } - EnvError::EnvParsingOfVariableExceptedBraceOrColon(pos, _) => { - USimpleError::new(125, format!("variable name issue (at {pos}): {e}")) - } - _ => USimpleError::new(125, format!("Error: {e:?}")), + EnvError::EnvParsingOfVariableMissingClosingBrace(pos) => USimpleError::new( + 125, + get_message_with_args( + "env-error-variable-name-issue", + HashMap::from([ + ("position".to_string(), pos.to_string()), + ("error".to_string(), e.to_string()), + ]), + ), + ), + EnvError::EnvParsingOfMissingVariable(pos) => USimpleError::new( + 125, + get_message_with_args( + "env-error-variable-name-issue", + HashMap::from([ + ("position".to_string(), pos.to_string()), + ("error".to_string(), e.to_string()), + ]), + ), + ), + EnvError::EnvParsingOfVariableMissingClosingBraceAfterValue(pos) => USimpleError::new( + 125, + get_message_with_args( + "env-error-variable-name-issue", + HashMap::from([ + ("position".to_string(), pos.to_string()), + ("error".to_string(), e.to_string()), + ]), + ), + ), + EnvError::EnvParsingOfVariableUnexpectedNumber(pos, _) => USimpleError::new( + 125, + get_message_with_args( + "env-error-variable-name-issue", + HashMap::from([ + ("position".to_string(), pos.to_string()), + ("error".to_string(), e.to_string()), + ]), + ), + ), + EnvError::EnvParsingOfVariableExceptedBraceOrColon(pos, _) => USimpleError::new( + 125, + get_message_with_args( + "env-error-variable-name-issue", + HashMap::from([ + ("position".to_string(), pos.to_string()), + ("error".to_string(), e.to_string()), + ]), + ), + ), + _ => USimpleError::new( + 125, + get_message_with_args( + "env-error-generic", + HashMap::from([("error".to_string(), format!("{e:?}"))]), + ), + ), }) } @@ -385,9 +437,15 @@ struct EnvAppData { impl EnvAppData { fn make_error_no_such_file_or_dir(&self, prog: &OsStr) -> Box { - uucore::show_error!("{}: No such file or directory", prog.quote()); + uucore::show_error!( + "{}", + get_message_with_args( + "env-error-no-such-file", + HashMap::from([("program".to_string(), prog.quote().to_string())]) + ) + ); if !self.had_string_argument { - uucore::show_error!("{ERROR_MSG_S_SHEBANG}"); + uucore::show_error!("{}", get_message("env-error-use-s-shebang")); } ExitCode::new(127) } @@ -461,7 +519,10 @@ impl EnvAppData { { return Err(USimpleError::new( 125, - format!("cannot unset '{}': Invalid argument", &arg_str[2..]), + get_message_with_args( + "env-error-cannot-unset", + HashMap::from([("name".to_string(), arg_str[2..].to_string())]), + ), )); } @@ -493,7 +554,7 @@ impl EnvAppData { let s = s.trim_end(); uucore::show_error!("{s}"); } - uucore::show_error!("{ERROR_MSG_S_SHEBANG}"); + uucore::show_error!("{}", get_message("env-error-use-s-shebang")); ExitCode::new(125) } } @@ -578,7 +639,7 @@ impl EnvAppData { #[cfg(not(unix))] return Err(USimpleError::new( 2, - "--argv0 is currently not supported on this platform", + get_message("env-error-argv0-not-supported"), )); } @@ -629,11 +690,23 @@ impl EnvAppData { Err(self.make_error_no_such_file_or_dir(&prog)) } io::ErrorKind::PermissionDenied => { - uucore::show_error!("{}: Permission denied", prog.quote()); + uucore::show_error!( + "{}", + get_message_with_args( + "env-error-permission-denied", + HashMap::from([("program".to_string(), prog.quote().to_string())]) + ) + ); Err(126.into()) } _ => { - uucore::show_error!("unknown error: {err:?}"); + uucore::show_error!( + "{}", + get_message_with_args( + "env-error-unknown", + HashMap::from([("error".to_string(), format!("{err:?}"))]) + ) + ); Err(126.into()) } }; @@ -722,7 +795,10 @@ fn apply_unset_env_vars(opts: &Options<'_>) -> Result<(), Box> { { return Err(USimpleError::new( 125, - format!("cannot unset {}: Invalid argument", name.quote()), + get_message_with_args( + "env-error-cannot-unset-invalid", + HashMap::from([("name".to_string(), name.quote().to_string())]), + ), )); } unsafe { @@ -737,7 +813,7 @@ fn apply_change_directory(opts: &Options<'_>) -> Result<(), Box> { if opts.program.is_empty() && opts.running_directory.is_some() { return Err(UUsageError::new( 125, - "must specify command with --chdir (-C)".to_string(), + get_message("env-error-must-specify-command-with-chdir"), )); } @@ -747,7 +823,13 @@ fn apply_change_directory(opts: &Options<'_>) -> Result<(), Box> { Err(error) => { return Err(USimpleError::new( 125, - format!("cannot change directory to {}: {error}", d.quote()), + get_message_with_args( + "env-error-cannot-change-directory", + HashMap::from([ + ("directory".to_string(), d.quote().to_string()), + ("error".to_string(), error.to_string()), + ]), + ), )); } }; @@ -781,7 +863,13 @@ fn apply_specified_env_vars(opts: &Options<'_>) { */ if name.is_empty() { - show_warning!("no name specified for value {}", val.quote()); + show_warning!( + "{}", + get_message_with_args( + "env-warning-no-name-specified", + HashMap::from([("value".to_string(), val.quote().to_string())]) + ) + ); continue; } unsafe { @@ -809,10 +897,12 @@ fn ignore_signal(sig: Signal) -> UResult<()> { if let Err(err) = result { return Err(USimpleError::new( 125, - format!( - "failed to set signal action for signal {}: {}", - sig as i32, - err.desc() + get_message_with_args( + "env-error-failed-set-signal-action", + HashMap::from([ + ("signal".to_string(), (sig as i32).to_string()), + ("error".to_string(), err.desc().to_string()), + ]), ), )); }