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

l10n: port install for translation + add french

This commit is contained in:
Sylvestre Ledru 2025-06-15 19:11:53 +02:00
parent 0a1a36d900
commit 19f1d18d3c
4 changed files with 233 additions and 64 deletions

View file

@ -1,3 +1,56 @@
install-about = Copy SOURCE to DEST or multiple SOURCE(s) to the existing
DIRECTORY, while setting permission modes and owner/group
install-usage = install [OPTION]... [FILE]...
# Help messages
install-help-ignored = ignored
install-help-compare = compare each pair of source and destination files, and in some cases, do not modify the destination at all
install-help-directory = treat all arguments as directory names. create all components of the specified directories
install-help-create-leading = create all leading components of DEST except the last, then copy SOURCE to DEST
install-help-group = set group ownership, instead of process's current group
install-help-mode = set permission mode (as in chmod), instead of rwxr-xr-x
install-help-owner = set ownership (super-user only)
install-help-preserve-timestamps = apply access/modification times of SOURCE files to corresponding destination files
install-help-strip = strip symbol tables (no action Windows)
install-help-strip-program = program used to strip binaries (no action Windows)
install-help-target-directory = move all SOURCE arguments into DIRECTORY
install-help-no-target-directory = treat DEST as a normal file
install-help-verbose = explain what is being done
install-help-preserve-context = preserve security context
install-help-context = set security context of files and directories
# Error messages
install-error-dir-needs-arg = { $util_name } with -d requires at least one argument.
install-error-create-dir-failed = failed to create { $path }
install-error-chmod-failed = failed to chmod { $path }
install-error-chmod-failed-detailed = { $path }: chmod failed with error { $error }
install-error-chown-failed = failed to chown { $path }: { $error }
install-error-invalid-target = invalid target { $path }: No such file or directory
install-error-target-not-dir = target { $path } is not a directory
install-error-backup-failed = cannot backup { $from } to { $to }
install-error-install-failed = cannot install { $from } to { $to }
install-error-strip-failed = strip program failed: { $error }
install-error-strip-abnormal = strip process terminated abnormally - exit code: { $code }
install-error-metadata-failed = metadata error
install-error-invalid-user = invalid user: { $user }
install-error-invalid-group = invalid group: { $group }
install-error-omitting-directory = omitting directory { $path }
install-error-not-a-directory = failed to access { $path }: Not a directory
install-error-override-directory-failed = cannot overwrite directory { $dir } with non-directory { $file }
install-error-same-file = '{ $file1 }' and '{ $file2 }' are the same file
install-error-extra-operand = extra operand { $operand }
{ $usage }
install-error-invalid-mode = Invalid mode string: { $error }
install-error-mutually-exclusive-target = Options --target-directory and --no-target-directory are mutually exclusive
install-error-mutually-exclusive-compare-preserve = Options --compare and --preserve-timestamps are mutually exclusive
install-error-mutually-exclusive-compare-strip = Options --compare and --strip are mutually exclusive
install-error-missing-file-operand = missing file operand
install-error-missing-destination-operand = missing destination file operand after '{ $path }'
install-error-failed-to-remove = Failed to remove existing file { $path }. Error: { $error }
# Verbose output
install-verbose-creating-directory = creating directory { $path }
install-verbose-creating-directory-step = install: creating directory { $path }
install-verbose-removed = removed { $path }
install-verbose-copy = { $from } -> { $to }
install-verbose-backup = (backup: { $backup })

View file

@ -0,0 +1,56 @@
install-about = Copier SOURCE vers DEST ou plusieurs SOURCE(s) vers le
RÉPERTOIRE existant, tout en définissant les modes de permission et propriétaire/groupe
install-usage = install [OPTION]... [FICHIER]...
# Messages d'aide
install-help-ignored = ignoré
install-help-compare = comparer chaque paire de fichiers source et destination, et dans certains cas, ne pas modifier la destination du tout
install-help-directory = traiter tous les arguments comme des noms de répertoires. créer tous les composants des répertoires spécifiés
install-help-create-leading = créer tous les composants principaux de DEST sauf le dernier, puis copier SOURCE vers DEST
install-help-group = définir la propriété du groupe, au lieu du groupe actuel du processus
install-help-mode = définir le mode de permission (comme dans chmod), au lieu de rwxr-xr-x
install-help-owner = définir la propriété (super-utilisateur uniquement)
install-help-preserve-timestamps = appliquer les temps d'accès/modification des fichiers SOURCE aux fichiers de destination correspondants
install-help-strip = supprimer les tables de symboles (aucune action Windows)
install-help-strip-program = programme utilisé pour supprimer les binaires (aucune action Windows)
install-help-target-directory = déplacer tous les arguments SOURCE dans RÉPERTOIRE
install-help-no-target-directory = traiter DEST comme un fichier normal
install-help-verbose = expliquer ce qui est fait
install-help-preserve-context = préserver le contexte de sécurité
install-help-context = définir le contexte de sécurité des fichiers et répertoires
# Messages d'erreur
install-error-dir-needs-arg = { $util_name } avec -d nécessite au moins un argument.
install-error-create-dir-failed = échec de la création de { $path }
install-error-chmod-failed = échec du chmod { $path }
install-error-chmod-failed-detailed = { $path } : échec du chmod avec l'erreur { $error }
install-error-chown-failed = échec du chown { $path } : { $error }
install-error-invalid-target = cible invalide { $path } : Aucun fichier ou répertoire de ce type
install-error-target-not-dir = la cible { $path } n'est pas un répertoire
install-error-backup-failed = impossible de sauvegarder { $from } vers { $to }
install-error-install-failed = impossible d'installer { $from } vers { $to }
install-error-strip-failed = échec du programme strip : { $error }
install-error-strip-abnormal = le processus strip s'est terminé anormalement - code de sortie : { $code }
install-error-metadata-failed = erreur de métadonnées
install-error-invalid-user = utilisateur invalide : { $user }
install-error-invalid-group = groupe invalide : { $group }
install-error-omitting-directory = omission du répertoire { $path }
install-error-not-a-directory = échec de l'accès à { $path } : N'est pas un répertoire
install-error-override-directory-failed = impossible d'écraser le répertoire { $dir } avec un non-répertoire { $file }
install-error-same-file = '{ $file1 }' et '{ $file2 }' sont le même fichier
install-error-extra-operand = opérande supplémentaire { $operand }
{ $usage }
install-error-invalid-mode = Chaîne de mode invalide : { $error }
install-error-mutually-exclusive-target = Les options --target-directory et --no-target-directory sont mutuellement exclusives
install-error-mutually-exclusive-compare-preserve = Les options --compare et --preserve-timestamps sont mutuellement exclusives
install-error-mutually-exclusive-compare-strip = Les options --compare et --strip sont mutuellement exclusives
install-error-missing-file-operand = opérande de fichier manquant
install-error-missing-destination-operand = opérande de fichier de destination manquant après '{ $path }'
install-error-failed-to-remove = Échec de la suppression du fichier existant { $path }. Erreur : { $error }
# Sortie détaillée
install-verbose-creating-directory = création du répertoire { $path }
install-verbose-creating-directory-step = install : création du répertoire { $path }
install-verbose-removed = supprimé { $path }
install-verbose-copy = { $from } -> { $to }
install-verbose-backup = (sauvegarde : { $backup })

View file

@ -10,6 +10,7 @@ mod mode;
use clap::{Arg, ArgAction, ArgMatches, Command};
use file_diff::diff;
use filetime::{FileTime, set_file_times};
use std::collections::HashMap;
use std::fmt::Debug;
use std::fs::File;
use std::fs::{self, metadata};
@ -33,7 +34,7 @@ use uucore::{format_usage, show, show_error, show_if_err};
use std::os::unix::fs::{FileTypeExt, MetadataExt};
#[cfg(unix)]
use std::os::unix::prelude::OsStrExt;
use uucore::locale::get_message;
use uucore::locale::{get_message, get_message_with_args};
const DEFAULT_MODE: u32 = 0o755;
const DEFAULT_STRIP_PROGRAM: &str = "strip";
@ -60,55 +61,55 @@ pub struct Behavior {
#[derive(Error, Debug)]
enum InstallError {
#[error("{} with -d requires at least one argument.", uucore::util_name())]
#[error("{}", get_message_with_args("install-error-dir-needs-arg", HashMap::from([("util_name".to_string(), uucore::util_name().to_string())])))]
DirNeedsArg,
#[error("failed to create {0}")]
#[error("{}", get_message_with_args("install-error-create-dir-failed", HashMap::from([("path".to_string(), .0.quote().to_string())])))]
CreateDirFailed(PathBuf, #[source] std::io::Error),
#[error("failed to chmod {}", .0.quote())]
#[error("{}", get_message_with_args("install-error-chmod-failed", HashMap::from([("path".to_string(), .0.quote().to_string())])))]
ChmodFailed(PathBuf),
#[error("failed to chown {}: {}", .0.quote(), .1)]
#[error("{}", get_message_with_args("install-error-chown-failed", HashMap::from([("path".to_string(), .0.quote().to_string()), ("error".to_string(), .1.clone())])))]
ChownFailed(PathBuf, String),
#[error("invalid target {}: No such file or directory", .0.quote())]
#[error("{}", get_message_with_args("install-error-invalid-target", HashMap::from([("path".to_string(), .0.quote().to_string())])))]
InvalidTarget(PathBuf),
#[error("target {} is not a directory", .0.quote())]
#[error("{}", get_message_with_args("install-error-target-not-dir", HashMap::from([("path".to_string(), .0.quote().to_string())])))]
TargetDirIsntDir(PathBuf),
#[error("cannot backup {0} to {1}")]
#[error("{}", get_message_with_args("install-error-backup-failed", HashMap::from([("from".to_string(), .0.to_string_lossy().to_string()), ("to".to_string(), .1.to_string_lossy().to_string())])))]
BackupFailed(PathBuf, PathBuf, #[source] std::io::Error),
#[error("cannot install {0} to {1}")]
#[error("{}", get_message_with_args("install-error-install-failed", HashMap::from([("from".to_string(), .0.to_string_lossy().to_string()), ("to".to_string(), .1.to_string_lossy().to_string())])))]
InstallFailed(PathBuf, PathBuf, #[source] std::io::Error),
#[error("strip program failed: {0}")]
#[error("{}", get_message_with_args("install-error-strip-failed", HashMap::from([("error".to_string(), .0.clone())])))]
StripProgramFailed(String),
#[error("metadata error")]
#[error("{}", get_message("install-error-metadata-failed"))]
MetadataFailed(#[source] std::io::Error),
#[error("invalid user: {}", .0.quote())]
#[error("{}", get_message_with_args("install-error-invalid-user", HashMap::from([("user".to_string(), .0.quote().to_string())])))]
InvalidUser(String),
#[error("invalid group: {}", .0.quote())]
#[error("{}", get_message_with_args("install-error-invalid-group", HashMap::from([("group".to_string(), .0.quote().to_string())])))]
InvalidGroup(String),
#[error("omitting directory {}", .0.quote())]
#[error("{}", get_message_with_args("install-error-omitting-directory", HashMap::from([("path".to_string(), .0.quote().to_string())])))]
OmittingDirectory(PathBuf),
#[error("failed to access {}: Not a directory", .0.quote())]
#[error("{}", get_message_with_args("install-error-not-a-directory", HashMap::from([("path".to_string(), .0.quote().to_string())])))]
NotADirectory(PathBuf),
#[error("cannot overwrite directory {} with non-directory {}", .0.quote(), .1.quote())]
#[error("{}", get_message_with_args("install-error-override-directory-failed", HashMap::from([("dir".to_string(), .0.quote().to_string()), ("file".to_string(), .1.quote().to_string())])))]
OverrideDirectoryFailed(PathBuf, PathBuf),
#[error("'{0}' and '{1}' are the same file")]
#[error("{}", get_message_with_args("install-error-same-file", HashMap::from([("file1".to_string(), .0.to_string_lossy().to_string()), ("file2".to_string(), .1.to_string_lossy().to_string())])))]
SameFile(PathBuf, PathBuf),
#[error("extra operand {}\n{}", .0.quote(), .1.quote())]
#[error("{}", get_message_with_args("install-error-extra-operand", HashMap::from([("operand".to_string(), .0.quote().to_string()), ("usage".to_string(), .1.clone())])))]
ExtraOperand(String, String),
#[cfg(feature = "selinux")]
@ -191,57 +192,48 @@ pub fn uu_app() -> Command {
.arg(
Arg::new(OPT_IGNORED)
.short('c')
.help("ignored")
.help(get_message("install-help-ignored"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_COMPARE)
.short('C')
.long(OPT_COMPARE)
.help(
"compare each pair of source and destination files, and in some cases, \
do not modify the destination at all",
)
.help(get_message("install-help-compare"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_DIRECTORY)
.short('d')
.long(OPT_DIRECTORY)
.help(
"treat all arguments as directory names. create all components of \
the specified directories",
)
.help(get_message("install-help-directory"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_CREATE_LEADING)
.short('D')
.help(
"create all leading components of DEST except the last, then copy \
SOURCE to DEST",
)
.help(get_message("install-help-create-leading"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_GROUP)
.short('g')
.long(OPT_GROUP)
.help("set group ownership, instead of process's current group")
.help(get_message("install-help-group"))
.value_name("GROUP"),
)
.arg(
Arg::new(OPT_MODE)
.short('m')
.long(OPT_MODE)
.help("set permission mode (as in chmod), instead of rwxr-xr-x")
.help(get_message("install-help-mode"))
.value_name("MODE"),
)
.arg(
Arg::new(OPT_OWNER)
.short('o')
.long(OPT_OWNER)
.help("set ownership (super-user only)")
.help(get_message("install-help-owner"))
.value_name("OWNER")
.value_hint(clap::ValueHint::Username),
)
@ -249,23 +241,20 @@ pub fn uu_app() -> Command {
Arg::new(OPT_PRESERVE_TIMESTAMPS)
.short('p')
.long(OPT_PRESERVE_TIMESTAMPS)
.help(
"apply access/modification times of SOURCE files to \
corresponding destination files",
)
.help(get_message("install-help-preserve-timestamps"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_STRIP)
.short('s')
.long(OPT_STRIP)
.help("strip symbol tables (no action Windows)")
.help(get_message("install-help-strip"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_STRIP_PROGRAM)
.long(OPT_STRIP_PROGRAM)
.help("program used to strip binaries (no action Windows)")
.help(get_message("install-help-strip-program"))
.value_name("PROGRAM")
.value_hint(clap::ValueHint::CommandName),
)
@ -274,7 +263,7 @@ pub fn uu_app() -> Command {
Arg::new(OPT_TARGET_DIRECTORY)
.short('t')
.long(OPT_TARGET_DIRECTORY)
.help("move all SOURCE arguments into DIRECTORY")
.help(get_message("install-help-target-directory"))
.value_name("DIRECTORY")
.value_hint(clap::ValueHint::DirPath),
)
@ -282,28 +271,28 @@ pub fn uu_app() -> Command {
Arg::new(OPT_NO_TARGET_DIRECTORY)
.short('T')
.long(OPT_NO_TARGET_DIRECTORY)
.help("treat DEST as a normal file")
.help(get_message("install-help-no-target-directory"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_VERBOSE)
.short('v')
.long(OPT_VERBOSE)
.help("explain what is being done")
.help(get_message("install-help-verbose"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_PRESERVE_CONTEXT)
.short('P')
.long(OPT_PRESERVE_CONTEXT)
.help("preserve security context")
.help(get_message("install-help-preserve-context"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_CONTEXT)
.short('Z')
.long(OPT_CONTEXT)
.help("set security context of files and directories")
.help(get_message("install-help-context"))
.value_name("CONTEXT")
.value_parser(clap::value_parser!(String))
.num_args(0..=1),
@ -336,7 +325,13 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
let specified_mode: Option<u32> = if matches.contains_id(OPT_MODE) {
let x = matches.get_one::<String>(OPT_MODE).ok_or(1)?;
Some(mode::parse(x, considering_dir, get_umask()).map_err(|err| {
show_error!("Invalid mode string: {err}");
show_error!(
"{}",
get_message_with_args(
"install-error-invalid-mode",
HashMap::from([("error".to_string(), err)])
)
);
1
})?)
} else {
@ -347,7 +342,7 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
let target_dir = matches.get_one::<String>(OPT_TARGET_DIRECTORY).cloned();
let no_target_dir = matches.get_flag(OPT_NO_TARGET_DIRECTORY);
if target_dir.is_some() && no_target_dir {
show_error!("Options --target-directory and --no-target-directory are mutually exclusive");
show_error!("{}", get_message("install-error-mutually-exclusive-target"));
return Err(1.into());
}
@ -355,11 +350,17 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
let compare = matches.get_flag(OPT_COMPARE);
let strip = matches.get_flag(OPT_STRIP);
if preserve_timestamps && compare {
show_error!("Options --compare and --preserve-timestamps are mutually exclusive");
show_error!(
"{}",
get_message("install-error-mutually-exclusive-compare-preserve")
);
return Err(1.into());
}
if compare && strip {
show_error!("Options --compare and --strip are mutually exclusive");
show_error!(
"{}",
get_message("install-error-mutually-exclusive-compare-strip")
);
return Err(1.into());
}
@ -456,7 +457,16 @@ fn directory(paths: &[String], b: &Behavior) -> UResult<()> {
}
if b.verbose {
println!("creating directory {}", path_to_create.quote());
println!(
"{}",
get_message_with_args(
"install-verbose-creating-directory",
HashMap::from([(
"path".to_string(),
path_to_create.quote().to_string()
)])
)
);
}
}
@ -511,7 +521,10 @@ fn is_potential_directory_path(path: &Path) -> bool {
fn standard(mut paths: Vec<String>, b: &Behavior) -> UResult<()> {
// first check that paths contains at least one element
if paths.is_empty() {
return Err(UUsageError::new(1, "missing file operand"));
return Err(UUsageError::new(
1,
get_message("install-error-missing-file-operand"),
));
}
if b.no_target_dir && paths.len() > 2 {
return Err(InstallError::ExtraOperand(
@ -531,9 +544,9 @@ fn standard(mut paths: Vec<String>, b: &Behavior) -> UResult<()> {
if paths.is_empty() {
return Err(UUsageError::new(
1,
format!(
"missing destination file operand after '{}'",
last_path.to_str().unwrap()
get_message_with_args(
"install-error-missing-destination-operand",
HashMap::from([("path".to_string(), last_path.to_str().unwrap().to_string())]),
),
));
}
@ -570,7 +583,16 @@ fn standard(mut paths: Vec<String>, b: &Behavior) -> UResult<()> {
result.push(part.as_os_str());
if !result.is_dir() {
// Don't display when the directory already exists
println!("install: creating directory {}", result.quote());
println!(
"{}",
get_message_with_args(
"install-verbose-creating-directory-step",
HashMap::from([(
"path".to_string(),
result.quote().to_string()
)])
)
);
}
}
}
@ -716,7 +738,13 @@ fn chown_optional_user_group(path: &Path, b: &Behavior) -> UResult<()> {
fn perform_backup(to: &Path, b: &Behavior) -> UResult<Option<PathBuf>> {
if to.exists() {
if b.verbose {
println!("removed {}", to.quote());
println!(
"{}",
get_message_with_args(
"install-verbose-removed",
HashMap::from([("path".to_string(), to.quote().to_string())])
)
);
}
let backup_path = backup_control::get_backup_path(b.backup_mode, to, &b.suffix);
if let Some(ref backup_path) = backup_path {
@ -777,8 +805,14 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> {
if let Err(e) = fs::remove_file(to) {
if e.kind() != std::io::ErrorKind::NotFound {
show_error!(
"Failed to remove existing file {}. Error: {e:?}",
to.display(),
"{}",
get_message_with_args(
"install-error-failed-to-remove",
HashMap::from([
("path".to_string(), to.display().to_string()),
("error".to_string(), format!("{e:?}"))
])
)
);
}
}
@ -832,9 +866,9 @@ fn strip_file(to: &Path, b: &Behavior) -> UResult<()> {
if !status.success() {
// Follow GNU's behavior: if strip fails, removes the target
let _ = fs::remove_file(to);
return Err(InstallError::StripProgramFailed(format!(
"strip process terminated abnormally - exit code: {}",
status.code().unwrap()
return Err(InstallError::StripProgramFailed(get_message_with_args(
"install-error-strip-abnormal",
HashMap::from([("code".to_string(), status.code().unwrap().to_string())]),
))
.into());
}
@ -940,9 +974,24 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
}
if b.verbose {
print!("{} -> {}", from.quote(), to.quote());
print!(
"{}",
get_message_with_args(
"install-verbose-copy",
HashMap::from([
("from".to_string(), from.quote().to_string()),
("to".to_string(), to.quote().to_string())
])
)
);
match backup_path {
Some(path) => println!(" (backup: {})", path.quote()),
Some(path) => println!(
" {}",
get_message_with_args(
"install-verbose-backup",
HashMap::from([("backup".to_string(), path.quote().to_string())])
)
),
None => println!(),
}
}

View file

@ -2,8 +2,10 @@
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use uucore::locale::get_message_with_args;
#[cfg(not(windows))]
use uucore::mode;
@ -25,7 +27,16 @@ pub fn chmod(path: &Path, mode: u32) -> Result<(), ()> {
use std::os::unix::fs::PermissionsExt;
use uucore::{display::Quotable, show_error};
fs::set_permissions(path, fs::Permissions::from_mode(mode)).map_err(|err| {
show_error!("{}: chmod failed with error {err}", path.maybe_quote());
show_error!(
"{}",
get_message_with_args(
"install-error-chmod-failed-detailed",
HashMap::from([
("path".to_string(), path.maybe_quote().to_string()),
("error".to_string(), err.to_string())
])
)
);
})
}