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

l10n: port rm for translation + add french

This commit is contained in:
Sylvestre Ledru 2025-06-23 19:05:15 +02:00
parent 05eb7ee374
commit e79bc8790b
5 changed files with 181 additions and 41 deletions

1
Cargo.lock generated
View file

@ -3388,6 +3388,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"libc", "libc",
"thiserror 2.0.12",
"uucore", "uucore",
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]

View file

@ -18,6 +18,7 @@ workspace = true
path = "src/rm.rs" path = "src/rm.rs"
[dependencies] [dependencies]
thiserror = { workspace = true }
clap = { workspace = true } clap = { workspace = true }
uucore = { workspace = true, features = ["fs"] } uucore = { workspace = true, features = ["fs"] }

View file

@ -12,3 +12,35 @@ rm-after-help = By default, rm does not remove directories. Use the --recursive
Note that if you use rm to remove a file, it might be possible to recover Note that if you use rm to remove a file, it might be possible to recover
some of its contents, given sufficient expertise and/or time. For greater some of its contents, given sufficient expertise and/or time. For greater
assurance that the contents are truly unrecoverable, consider using shred. assurance that the contents are truly unrecoverable, consider using shred.
# Help text for options
rm-help-force = ignore nonexistent files and arguments, never prompt
rm-help-prompt-always = prompt before every removal
rm-help-prompt-once = prompt once before removing more than three files, or when removing recursively.
Less intrusive than -i, while still giving some protection against most mistakes
rm-help-interactive = prompt according to WHEN: never, once (-I), or always (-i). Without WHEN,
prompts always
rm-help-one-file-system = when removing a hierarchy recursively, skip any directory that is on a file
system different from that of the corresponding command line argument (NOT
IMPLEMENTED)
rm-help-no-preserve-root = do not treat '/' specially
rm-help-preserve-root = do not remove '/' (default)
rm-help-recursive = remove directories and their contents recursively
rm-help-dir = remove empty directories
rm-help-verbose = explain what is being done
# Error messages
rm-error-missing-operand = missing operand
Try '{$util_name} --help' for more information.
rm-error-invalid-interactive-argument = Invalid argument to interactive ({$arg})
rm-error-cannot-remove-no-such-file = cannot remove {$file}: No such file or directory
rm-error-cannot-remove-permission-denied = cannot remove {$file}: Permission denied
rm-error-cannot-remove-is-directory = cannot remove {$file}: Is a directory
rm-error-dangerous-recursive-operation = it is dangerous to operate recursively on '/'
rm-error-use-no-preserve-root = use --no-preserve-root to override this failsafe
rm-error-refusing-to-remove-directory = refusing to remove '.' or '..' directory: skipping '{$path}'
rm-error-cannot-remove = cannot remove {$file}
# Verbose messages
rm-verbose-removed = removed {$file}
rm-verbose-removed-directory = removed directory {$file}

View file

@ -0,0 +1,46 @@
rm-about = Supprimer (délier) le(s) FICHIER(s)
rm-usage = rm [OPTION]... FICHIER...
rm-after-help = Par défaut, rm ne supprime pas les répertoires. Utilisez l'option --recursive (-r ou -R)
pour supprimer également chaque répertoire listé, ainsi que tout son contenu
Pour supprimer un fichier dont le nom commence par un '-', par exemple '-foo',
utilisez une de ces commandes :
rm -- -foo
rm ./-foo
Notez que si vous utilisez rm pour supprimer un fichier, il pourrait être possible de récupérer
une partie de son contenu, avec suffisamment d'expertise et/ou de temps. Pour une meilleure
assurance que le contenu est vraiment irrécupérable, considérez utiliser shred.
# Texte d'aide pour les options
rm-help-force = ignorer les fichiers inexistants et les arguments, ne jamais demander
rm-help-prompt-always = demander avant chaque suppression
rm-help-prompt-once = demander une fois avant de supprimer plus de trois fichiers, ou lors d'une suppression récursive.
Moins intrusif que -i, tout en offrant une protection contre la plupart des erreurs
rm-help-interactive = demander selon QUAND : never, once (-I), ou always (-i). Sans QUAND,
demande toujours
rm-help-one-file-system = lors de la suppression récursive d'une hiérarchie, ignorer tout répertoire situé sur un
système de fichiers différent de celui de l'argument de ligne de commande correspondant (NON
IMPLÉMENTÉ)
rm-help-no-preserve-root = ne pas traiter '/' spécialement
rm-help-preserve-root = ne pas supprimer '/' (par défaut)
rm-help-recursive = supprimer les répertoires et leur contenu récursivement
rm-help-dir = supprimer les répertoires vides
rm-help-verbose = expliquer ce qui est fait
# Messages d'erreur
rm-error-missing-operand = opérande manquant
Essayez '{$util_name} --help' pour plus d'informations.
rm-error-invalid-interactive-argument = Argument invalide pour interactive ({$arg})
rm-error-cannot-remove-no-such-file = impossible de supprimer {$file} : Aucun fichier ou répertoire de ce type
rm-error-cannot-remove-permission-denied = impossible de supprimer {$file} : Permission refusée
rm-error-cannot-remove-is-directory = impossible de supprimer {$file} : C'est un répertoire
rm-error-dangerous-recursive-operation = il est dangereux d'opérer récursivement sur '/'
rm-error-use-no-preserve-root = utilisez --no-preserve-root pour outrepasser cette protection
rm-error-refusing-to-remove-directory = refus de supprimer le répertoire '.' ou '..' : ignorer '{$path}'
rm-error-cannot-remove = impossible de supprimer {$file}
# Messages verbeux
rm-verbose-removed = {$file} supprimé
rm-verbose-removed-directory = répertoire {$file} supprimé

View file

@ -6,6 +6,7 @@
// spell-checker:ignore (path) eacces inacc rm-r4 // spell-checker:ignore (path) eacces inacc rm-r4
use clap::{Arg, ArgAction, Command, builder::ValueParser, parser::ValueSource}; use clap::{Arg, ArgAction, Command, builder::ValueParser, parser::ValueSource};
use std::collections::HashMap;
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::fs::{self, Metadata}; use std::fs::{self, Metadata};
use std::io::{IsTerminal, stdin}; use std::io::{IsTerminal, stdin};
@ -16,11 +17,38 @@ use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::path::MAIN_SEPARATOR; use std::path::MAIN_SEPARATOR;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use thiserror::Error;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::error::{FromIo, UError, UResult};
use uucore::locale::get_message; use uucore::locale::{get_message, get_message_with_args};
use uucore::{format_usage, os_str_as_bytes, prompt_yes, show_error}; use uucore::{format_usage, os_str_as_bytes, prompt_yes, show_error};
#[derive(Debug, Error)]
enum RmError {
#[error("{}", get_message_with_args("rm-error-missing-operand",
HashMap::from([
("util_name".to_string(),
uucore::execution_phrase().to_string())
])))]
MissingOperand,
#[error("{}", get_message_with_args("rm-error-invalid-interactive-argument", HashMap::from([("arg".to_string(), _0.clone())])))]
InvalidInteractiveArgument(String),
#[error("{}", get_message_with_args("rm-error-cannot-remove-no-such-file", HashMap::from([("file".to_string(), _0.quote().to_string())])))]
CannotRemoveNoSuchFile(String),
#[error("{}", get_message_with_args("rm-error-cannot-remove-permission-denied", HashMap::from([("file".to_string(), _0.quote().to_string())])))]
CannotRemovePermissionDenied(String),
#[error("{}", get_message_with_args("rm-error-cannot-remove-is-directory", HashMap::from([("file".to_string(), _0.quote().to_string())])))]
CannotRemoveIsDirectory(String),
#[error("{}", get_message("rm-error-dangerous-recursive-operation"))]
DangerousRecursiveOperation,
#[error("{}", get_message("rm-error-use-no-preserve-root"))]
UseNoPreserveRoot,
#[error("{}", get_message_with_args("rm-error-refusing-to-remove-directory", HashMap::from([("path".to_string(), _0.to_string())])))]
RefusingToRemoveDirectory(String),
}
impl UError for RmError {}
#[derive(Eq, PartialEq, Clone, Copy)] #[derive(Eq, PartialEq, Clone, Copy)]
/// Enum, determining when the `rm` will prompt the user about the file deletion /// Enum, determining when the `rm` will prompt the user about the file deletion
pub enum InteractiveMode { pub enum InteractiveMode {
@ -117,7 +145,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if files.is_empty() && !force_flag { if files.is_empty() && !force_flag {
// Still check by hand and not use clap // Still check by hand and not use clap
// Because "rm -f" is a thing // Because "rm -f" is a thing
return Err(UUsageError::new(1, "missing operand")); return Err(RmError::MissingOperand.into());
} }
// If -f(--force) is before any -i (or variants) we want prompts else no prompts // If -f(--force) is before any -i (or variants) we want prompts else no prompts
@ -146,10 +174,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
"once" => InteractiveMode::Once, "once" => InteractiveMode::Once,
"always" => InteractiveMode::Always, "always" => InteractiveMode::Always,
val => { val => {
return Err(USimpleError::new( return Err(RmError::InvalidInteractiveArgument(val.to_string()).into());
1,
format!("Invalid argument to interactive ({val})"),
));
} }
} }
} else { } else {
@ -206,31 +231,27 @@ pub fn uu_app() -> Command {
Arg::new(OPT_FORCE) Arg::new(OPT_FORCE)
.short('f') .short('f')
.long(OPT_FORCE) .long(OPT_FORCE)
.help("ignore nonexistent files and arguments, never prompt") .help(get_message("rm-help-force"))
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(OPT_PROMPT_ALWAYS) Arg::new(OPT_PROMPT_ALWAYS)
.short('i') .short('i')
.help("prompt before every removal") .help(get_message("rm-help-prompt-always"))
.overrides_with_all([OPT_PROMPT_ONCE, OPT_INTERACTIVE]) .overrides_with_all([OPT_PROMPT_ONCE, OPT_INTERACTIVE])
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(OPT_PROMPT_ONCE) Arg::new(OPT_PROMPT_ONCE)
.short('I') .short('I')
.help("prompt once before removing more than three files, or when removing recursively. \ .help(get_message("rm-help-prompt-once"))
Less intrusive than -i, while still giving some protection against most mistakes")
.overrides_with_all([OPT_PROMPT_ALWAYS, OPT_INTERACTIVE]) .overrides_with_all([OPT_PROMPT_ALWAYS, OPT_INTERACTIVE])
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(OPT_INTERACTIVE) Arg::new(OPT_INTERACTIVE)
.long(OPT_INTERACTIVE) .long(OPT_INTERACTIVE)
.help( .help(get_message("rm-help-interactive"))
"prompt according to WHEN: never, once (-I), or always (-i). Without WHEN, \
prompts always",
)
.value_name("WHEN") .value_name("WHEN")
.num_args(0..=1) .num_args(0..=1)
.require_equals(true) .require_equals(true)
@ -240,22 +261,19 @@ pub fn uu_app() -> Command {
.arg( .arg(
Arg::new(OPT_ONE_FILE_SYSTEM) Arg::new(OPT_ONE_FILE_SYSTEM)
.long(OPT_ONE_FILE_SYSTEM) .long(OPT_ONE_FILE_SYSTEM)
.help( .help(get_message("rm-help-one-file-system"))
"when removing a hierarchy recursively, skip any directory that is on a file \ .action(ArgAction::SetTrue),
system different from that of the corresponding command line argument (NOT \
IMPLEMENTED)",
).action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(OPT_NO_PRESERVE_ROOT) Arg::new(OPT_NO_PRESERVE_ROOT)
.long(OPT_NO_PRESERVE_ROOT) .long(OPT_NO_PRESERVE_ROOT)
.help("do not treat '/' specially") .help(get_message("rm-help-no-preserve-root"))
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(OPT_PRESERVE_ROOT) Arg::new(OPT_PRESERVE_ROOT)
.long(OPT_PRESERVE_ROOT) .long(OPT_PRESERVE_ROOT)
.help("do not remove '/' (default)") .help(get_message("rm-help-preserve-root"))
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )
.arg( .arg(
@ -263,21 +281,21 @@ pub fn uu_app() -> Command {
.short('r') .short('r')
.visible_short_alias('R') .visible_short_alias('R')
.long(OPT_RECURSIVE) .long(OPT_RECURSIVE)
.help("remove directories and their contents recursively") .help(get_message("rm-help-recursive"))
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(OPT_DIR) Arg::new(OPT_DIR)
.short('d') .short('d')
.long(OPT_DIR) .long(OPT_DIR)
.help("remove empty directories") .help(get_message("rm-help-dir"))
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(OPT_VERBOSE) Arg::new(OPT_VERBOSE)
.short('v') .short('v')
.long(OPT_VERBOSE) .long(OPT_VERBOSE)
.help("explain what is being done") .help(get_message("rm-help-verbose"))
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )
// From the GNU source code: // From the GNU source code:
@ -337,8 +355,8 @@ pub fn remove(files: &[&OsStr], options: &Options) -> bool {
false false
} else { } else {
show_error!( show_error!(
"cannot remove {}: No such file or directory", "{}",
filename.quote() RmError::CannotRemoveNoSuchFile(filename.to_string_lossy().to_string())
); );
true true
} }
@ -425,7 +443,12 @@ fn remove_dir_recursive(path: &Path, options: &Options) -> bool {
match fs::remove_dir_all(path) { match fs::remove_dir_all(path) {
Ok(_) => return false, Ok(_) => return false,
Err(e) => { Err(e) => {
let e = e.map_err_context(|| format!("cannot remove {}", path.quote())); let e = e.map_err_context(|| {
get_message_with_args(
"rm-error-cannot-remove",
HashMap::from([("file".to_string(), path.quote().to_string())]),
)
});
show_error!("{e}"); show_error!("{e}");
return true; return true;
} }
@ -487,7 +510,12 @@ fn remove_dir_recursive(path: &Path, options: &Options) -> bool {
error = true; error = true;
} }
Err(e) if !error => { Err(e) if !error => {
let e = e.map_err_context(|| format!("cannot remove {}", path.quote())); let e = e.map_err_context(|| {
get_message_with_args(
"rm-error-cannot-remove",
HashMap::from([("file".to_string(), path.quote().to_string())]),
)
});
show_error!("{e}"); show_error!("{e}");
error = true; error = true;
} }
@ -497,7 +525,13 @@ fn remove_dir_recursive(path: &Path, options: &Options) -> bool {
// show another error message as we return from each level // show another error message as we return from each level
// of the recursion. // of the recursion.
} }
Ok(_) if options.verbose => println!("removed directory {}", normalize(path).quote()), Ok(_) if options.verbose => println!(
"{}",
get_message_with_args(
"rm-verbose-removed-directory",
HashMap::from([("file".to_string(), normalize(path).quote().to_string())])
)
),
Ok(_) => {} Ok(_) => {}
} }
@ -510,8 +544,8 @@ fn handle_dir(path: &Path, options: &Options) -> bool {
let path = clean_trailing_slashes(path); let path = clean_trailing_slashes(path);
if path_is_current_or_parent_directory(path) { if path_is_current_or_parent_directory(path) {
show_error!( show_error!(
"refusing to remove '.' or '..' directory: skipping '{}'", "{}",
path.display() RmError::RefusingToRemoveDirectory(path.display().to_string())
); );
return true; return true;
} }
@ -522,13 +556,13 @@ fn handle_dir(path: &Path, options: &Options) -> bool {
} else if options.dir && (!is_root || !options.preserve_root) { } else if options.dir && (!is_root || !options.preserve_root) {
had_err = remove_dir(path, options).bitor(had_err); had_err = remove_dir(path, options).bitor(had_err);
} else if options.recursive { } else if options.recursive {
show_error!("it is dangerous to operate recursively on '{MAIN_SEPARATOR}'"); show_error!("{}", RmError::DangerousRecursiveOperation);
show_error!("use --no-preserve-root to override this failsafe"); show_error!("{}", RmError::UseNoPreserveRoot);
had_err = true; had_err = true;
} else { } else {
show_error!( show_error!(
"cannot remove {}: Is a directory", // GNU's rm error message does not include help "{}",
path.quote() RmError::CannotRemoveIsDirectory(path.to_string_lossy().to_string())
); );
had_err = true; had_err = true;
} }
@ -547,7 +581,10 @@ fn remove_dir(path: &Path, options: &Options) -> bool {
// Called to remove a symlink_dir (windows) without "-r"/"-R" or "-d". // Called to remove a symlink_dir (windows) without "-r"/"-R" or "-d".
if !options.dir && !options.recursive { if !options.dir && !options.recursive {
show_error!("cannot remove {}: Is a directory", path.quote()); show_error!(
"{}",
RmError::CannotRemoveIsDirectory(path.to_string_lossy().to_string())
);
return true; return true;
} }
@ -555,12 +592,23 @@ fn remove_dir(path: &Path, options: &Options) -> bool {
match fs::remove_dir(path) { match fs::remove_dir(path) {
Ok(_) => { Ok(_) => {
if options.verbose { if options.verbose {
println!("removed directory {}", normalize(path).quote()); println!(
"{}",
get_message_with_args(
"rm-verbose-removed-directory",
HashMap::from([("file".to_string(), normalize(path).quote().to_string())])
)
);
} }
false false
} }
Err(e) => { Err(e) => {
let e = e.map_err_context(|| format!("cannot remove {}", path.quote())); let e = e.map_err_context(|| {
get_message_with_args(
"rm-error-cannot-remove",
HashMap::from([("file".to_string(), path.quote().to_string())]),
)
});
show_error!("{e}"); show_error!("{e}");
true true
} }
@ -572,13 +620,25 @@ fn remove_file(path: &Path, options: &Options) -> bool {
match fs::remove_file(path) { match fs::remove_file(path) {
Ok(_) => { Ok(_) => {
if options.verbose { if options.verbose {
println!("removed {}", normalize(path).quote()); println!(
"{}",
get_message_with_args(
"rm-verbose-removed",
HashMap::from([(
"file".to_string(),
normalize(path).quote().to_string()
)])
)
);
} }
} }
Err(e) => { Err(e) => {
if e.kind() == std::io::ErrorKind::PermissionDenied { if e.kind() == std::io::ErrorKind::PermissionDenied {
// GNU compatibility (rm/fail-eacces.sh) // GNU compatibility (rm/fail-eacces.sh)
show_error!("cannot remove {}: {}", path.quote(), "Permission denied"); show_error!(
"{}",
RmError::CannotRemovePermissionDenied(path.to_string_lossy().to_string())
);
} else { } else {
show_error!("cannot remove {}: {e}", path.quote()); show_error!("cannot remove {}: {e}", path.quote());
} }