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

Merge pull request #8251 from sylvestre/l10n-mv

l10n: port mv for translation + add french
This commit is contained in:
Daniel Hofstetter 2025-06-25 09:43:24 +02:00 committed by GitHub
commit 6a30c74475
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 232 additions and 70 deletions

View file

@ -14,3 +14,50 @@ mv-after-help = When specifying more than one of -i, -f, -n, only the final one
- all This is the default operation when an --update option is not specified, and results in all existing files in the destination being replaced.
- none This is similar to the --no-clobber option, in that no files in the destination are replaced, but also skipping a file does not induce a failure.
- older This is the default operation when --update is specified, and results in files being replaced if theyre older than the corresponding source file.
# Error messages
mv-error-no-such-file = cannot stat {$path}: No such file or directory
mv-error-cannot-stat-not-directory = cannot stat {$path}: Not a directory
mv-error-same-file = {$source} and {$target} are the same file
mv-error-self-target-subdirectory = cannot move {$source} to a subdirectory of itself, {$target}
mv-error-directory-to-non-directory = cannot overwrite directory {$path} with non-directory
mv-error-non-directory-to-directory = cannot overwrite non-directory {$target} with directory {$source}
mv-error-not-directory = target {$path}: Not a directory
mv-error-target-not-directory = target directory {$path}: Not a directory
mv-error-failed-access-not-directory = failed to access {$path}: Not a directory
mv-error-backup-with-no-clobber = cannot combine --backup with -n/--no-clobber or --update=none-fail
mv-error-extra-operand = mv: extra operand {$operand}
mv-error-backup-might-destroy-source = backing up {$target} might destroy source; {$source} not moved
mv-error-will-not-overwrite-just-created = will not overwrite just-created '{$target}' with '{$source}'
mv-error-not-replacing = not replacing {$target}
mv-error-cannot-move = cannot move {$source} to {$target}
mv-error-directory-not-empty = Directory not empty
mv-error-dangling-symlink = can't determine symlink type, since it is dangling
mv-error-no-symlink-support = your operating system does not support symlinks
mv-error-permission-denied = Permission denied
mv-error-inter-device-move-failed = inter-device move failed: '{$from}' to '{$to}'; unable to remove target: {$err}
# Help messages
mv-help-force = do not prompt before overwriting
mv-help-interactive = prompt before override
mv-help-no-clobber = do not overwrite an existing file
mv-help-strip-trailing-slashes = remove any trailing slashes from each SOURCE argument
mv-help-target-directory = move all SOURCE arguments into DIRECTORY
mv-help-no-target-directory = treat DEST as a normal file
mv-help-verbose = explain what is being done
mv-help-progress = Display a progress bar.
Note: this feature is not supported by GNU coreutils.
mv-help-debug = explain how a file is copied. Implies -v
# Verbose messages
mv-verbose-renamed = renamed {$from} -> {$to}
mv-verbose-renamed-with-backup = renamed {$from} -> {$to} (backup: {$backup})
# Debug messages
mv-debug-skipped = skipped {$target}
# Prompt messages
mv-prompt-overwrite = overwrite {$target}?
# Progress messages
mv-progress-moving = moving

View file

@ -0,0 +1,63 @@
mv-about = Déplacer SOURCE vers DEST, ou plusieurs SOURCE(s) vers RÉPERTOIRE.
mv-usage = mv [OPTION]... [-T] SOURCE DEST
mv [OPTION]... SOURCE... RÉPERTOIRE
mv [OPTION]... -t RÉPERTOIRE SOURCE...
mv-after-help = Lors de la spécification de plus d'une option parmi -i, -f, -n, seule la dernière prend effet.
Ne pas déplacer un non-répertoire qui a une destination existante avec un horodatage de modification identique ou plus récent ;
au lieu de cela, ignorer silencieusement le fichier sans échouer. Si le déplacement traverse les limites du système de fichiers, la comparaison est
avec l'horodatage source tronqué aux résolutions du système de fichiers de destination et des appels système utilisés
pour mettre à jour les horodatages ; cela évite le travail en double si plusieurs commandes mv -u sont exécutées avec la même source
et destination. Cette option est ignorée si l'option -n ou --no-clobber est également spécifiée, qui donne plus de contrôle
sur quels fichiers existants dans la destination sont remplacés, et sa valeur peut être une des suivantes :
- all C'est l'opération par défaut quand une option --update n'est pas spécifiée, et résulte en tous les fichiers existants dans la destination étant remplacés.
- none C'est similaire à l'option --no-clobber, en ce que aucun fichier dans la destination n'est remplacé, mais aussi ignorer un fichier n'induit pas un échec.
- older C'est l'opération par défaut quand --update est spécifié, et résulte en des fichiers étant remplacés s'ils sont plus anciens que le fichier source correspondant.
# Messages d'erreur
mv-error-no-such-file = impossible de lire {$path} : Aucun fichier ou répertoire de ce nom
mv-error-cannot-stat-not-directory = impossible de lire {$path} : N'est pas un répertoire
mv-error-same-file = {$source} et {$target} sont le même fichier
mv-error-self-target-subdirectory = impossible de déplacer {$source} vers un sous-répertoire de lui-même, {$target}
mv-error-directory-to-non-directory = impossible d'écraser le répertoire {$path} avec un non-répertoire
mv-error-non-directory-to-directory = impossible d'écraser le non-répertoire {$target} avec le répertoire {$source}
mv-error-not-directory = cible {$path} : N'est pas un répertoire
mv-error-target-not-directory = répertoire cible {$path} : N'est pas un répertoire
mv-error-failed-access-not-directory = impossible d'accéder à {$path} : N'est pas un répertoire
mv-error-backup-with-no-clobber = impossible de combiner --backup avec -n/--no-clobber ou --update=none-fail
mv-error-extra-operand = mv : opérande supplémentaire {$operand}
mv-error-backup-might-destroy-source = sauvegarder {$target} pourrait détruire la source ; {$source} non déplacé
mv-error-will-not-overwrite-just-created = ne va pas écraser le fichier qui vient d'être créé '{$target}' avec '{$source}'
mv-error-not-replacing = ne remplace pas {$target}
mv-error-cannot-move = impossible de déplacer {$source} vers {$target}
mv-error-directory-not-empty = Répertoire non vide
mv-error-dangling-symlink = impossible de déterminer le type de lien symbolique, car il est suspendu
mv-error-no-symlink-support = votre système d'exploitation ne prend pas en charge les liens symboliques
mv-error-permission-denied = Permission refusée
mv-error-inter-device-move-failed = échec du déplacement inter-périphérique : '{$from}' vers '{$to}' ; impossible de supprimer la cible : {$err}
# Messages d'aide
mv-help-force = ne pas demander avant d'écraser
mv-help-interactive = demander avant d'écraser
mv-help-no-clobber = ne pas écraser un fichier existant
mv-help-strip-trailing-slashes = supprimer toutes les barres obliques de fin de chaque argument SOURCE
mv-help-target-directory = déplacer tous les arguments SOURCE dans RÉPERTOIRE
mv-help-no-target-directory = traiter DEST comme un fichier normal
mv-help-verbose = expliquer ce qui est fait
mv-help-progress = Afficher une barre de progression.
Note : cette fonctionnalité n'est pas supportée par GNU coreutils.
mv-help-debug = expliquer comment un fichier est copié. Implique -v
# Messages verbeux
mv-verbose-renamed = renommé {$from} -> {$to}
mv-verbose-renamed-with-backup = renommé {$from} -> {$to} (sauvegarde : {$backup})
# Messages de débogage
mv-debug-skipped = ignoré {$target}
# Messages de confirmation
mv-prompt-overwrite = écraser {$target} ?
# Messages de progression
mv-progress-moving = déplacement

View file

@ -2,36 +2,30 @@
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use std::collections::HashMap;
use thiserror::Error;
use uucore::error::UError;
use uucore::locale::get_message_with_args;
#[derive(Debug, Error)]
pub enum MvError {
#[error("cannot stat {0}: No such file or directory")]
#[error("{}", get_message_with_args("mv-error-no-such-file", HashMap::from([("path".to_string(), .0.clone())])))]
NoSuchFile(String),
#[error("cannot stat {0}: Not a directory")]
#[error("{}", get_message_with_args("mv-error-cannot-stat-not-directory", HashMap::from([("path".to_string(), .0.clone())])))]
CannotStatNotADirectory(String),
#[error("{0} and {1} are the same file")]
#[error("{}", get_message_with_args("mv-error-same-file", HashMap::from([("source".to_string(), .0.clone()), ("target".to_string(), .1.clone())])))]
SameFile(String, String),
#[error("cannot move {0} to a subdirectory of itself, {1}")]
#[error("{}", get_message_with_args("mv-error-self-target-subdirectory", HashMap::from([("source".to_string(), .0.clone()), ("target".to_string(), .1.clone())])))]
SelfTargetSubdirectory(String, String),
#[error("cannot overwrite directory {0} with non-directory")]
#[error("{}", get_message_with_args("mv-error-directory-to-non-directory", HashMap::from([("path".to_string(), .0.clone())])))]
DirectoryToNonDirectory(String),
#[error("cannot overwrite non-directory {1} with directory {0}")]
#[error("{}", get_message_with_args("mv-error-non-directory-to-directory", HashMap::from([("source".to_string(), .0.clone()), ("target".to_string(), .1.clone())])))]
NonDirectoryToDirectory(String, String),
#[error("target {0}: Not a directory")]
#[error("{}", get_message_with_args("mv-error-not-directory", HashMap::from([("path".to_string(), .0.clone())])))]
NotADirectory(String),
#[error("target directory {0}: Not a directory")]
#[error("{}", get_message_with_args("mv-error-target-not-directory", HashMap::from([("path".to_string(), .0.clone())])))]
TargetNotADirectory(String),
#[error("failed to access {0}: Not a directory")]
#[error("{}", get_message_with_args("mv-error-failed-access-not-directory", HashMap::from([("path".to_string(), .0.clone())])))]
FailedToAccessNotADirectory(String),
}

View file

@ -11,7 +11,7 @@ use clap::builder::ValueParser;
use clap::{Arg, ArgAction, ArgMatches, Command, error::ErrorKind};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::env;
use std::ffi::OsString;
use std::fs;
@ -48,7 +48,7 @@ use fs_extra::dir::{
};
use crate::error::MvError;
use uucore::locale::get_message;
use uucore::locale::{get_message, get_message_with_args};
/// Options contains all the possible behaviors and flags for mv.
///
@ -167,7 +167,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
{
return Err(UUsageError::new(
1,
"cannot combine --backup with -n/--no-clobber or --update=none-fail",
get_message("mv-error-backup-with-no-clobber"),
));
}
@ -214,7 +214,7 @@ pub fn uu_app() -> Command {
Arg::new(OPT_FORCE)
.short('f')
.long(OPT_FORCE)
.help("do not prompt before overwriting")
.help(get_message("mv-help-force"))
.overrides_with_all([OPT_INTERACTIVE, OPT_NO_CLOBBER])
.action(ArgAction::SetTrue),
)
@ -222,7 +222,7 @@ pub fn uu_app() -> Command {
Arg::new(OPT_INTERACTIVE)
.short('i')
.long(OPT_INTERACTIVE)
.help("prompt before override")
.help(get_message("mv-help-interactive"))
.overrides_with_all([OPT_FORCE, OPT_NO_CLOBBER])
.action(ArgAction::SetTrue),
)
@ -230,14 +230,14 @@ pub fn uu_app() -> Command {
Arg::new(OPT_NO_CLOBBER)
.short('n')
.long(OPT_NO_CLOBBER)
.help("do not overwrite an existing file")
.help(get_message("mv-help-no-clobber"))
.overrides_with_all([OPT_FORCE, OPT_INTERACTIVE])
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_STRIP_TRAILING_SLASHES)
.long(OPT_STRIP_TRAILING_SLASHES)
.help("remove any trailing slashes from each SOURCE argument")
.help(get_message("mv-help-strip-trailing-slashes"))
.action(ArgAction::SetTrue),
)
.arg(backup_control::arguments::backup())
@ -249,7 +249,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("mv-help-target-directory"))
.value_name("DIRECTORY")
.value_hint(clap::ValueHint::DirPath)
.conflicts_with(OPT_NO_TARGET_DIRECTORY)
@ -259,24 +259,21 @@ 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("mv-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("mv-help-verbose"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_PROGRESS)
.short('g')
.long(OPT_PROGRESS)
.help(
"Display a progress bar. \n\
Note: this feature is not supported by GNU coreutils.",
)
.help(get_message("mv-help-progress"))
.action(ArgAction::SetTrue),
)
.arg(
@ -290,7 +287,7 @@ pub fn uu_app() -> Command {
.arg(
Arg::new(OPT_DEBUG)
.long(OPT_DEBUG)
.help("explain how a file is copied. Implies -v")
.help(get_message("mv-help-debug"))
.action(ArgAction::SetTrue),
)
}
@ -326,10 +323,12 @@ fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()>
if opts.backup == BackupMode::Simple && source_is_target_backup(source, target, &opts.suffix) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!(
"backing up {} might destroy source; {} not moved",
target.quote(),
source.quote()
get_message_with_args(
"mv-error-backup-might-destroy-source",
HashMap::from([
("target".to_string(), target.quote().to_string()),
("source".to_string(), source.quote().to_string()),
]),
),
)
.into());
@ -359,7 +358,13 @@ fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()>
if opts.no_target_dir {
if source.is_dir() {
rename(source, target, opts, None).map_err_context(|| {
format!("cannot move {} to {}", source.quote(), target.quote())
get_message_with_args(
"mv-error-cannot-move",
HashMap::from([
("source".to_string(), source.quote().to_string()),
("target".to_string(), target.quote().to_string()),
]),
)
})
} else {
Err(MvError::DirectoryToNonDirectory(target.quote().to_string()).into())
@ -371,7 +376,13 @@ fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()>
match opts.overwrite {
OverwriteMode::NoClobber => return Ok(()),
OverwriteMode::Interactive => {
if !prompt_yes!("overwrite {}? ", target.quote()) {
if !prompt_yes!(
"{}",
get_message_with_args(
"mv-prompt-overwrite",
HashMap::from([("target".to_string(), target.quote().to_string())]),
)
) {
return Err(io::Error::other("").into());
}
}
@ -473,7 +484,10 @@ fn handle_multiple_paths(paths: &[PathBuf], opts: &Options) -> UResult<()> {
if opts.no_target_dir {
return Err(UUsageError::new(
1,
format!("mv: extra operand {}", paths[2].quote()),
get_message_with_args(
"mv-error-extra-operand",
HashMap::from([("operand".to_string(), paths[2].quote().to_string())]),
),
));
}
let target_dir = paths.last().unwrap();
@ -511,11 +525,17 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, options: &Options)
let count_progress = if let Some(ref multi_progress) = multi_progress {
if files.len() > 1 {
Some(multi_progress.add(
ProgressBar::new(files.len().try_into().unwrap()).with_style(
ProgressStyle::with_template("moving {msg} {wide_bar} {pos}/{len}").unwrap(),
Some(
multi_progress.add(
ProgressBar::new(files.len().try_into().unwrap()).with_style(
ProgressStyle::with_template(&format!(
"{} {{msg}} {{wide_bar}} {{pos}}/{{len}}",
get_message("mv-progress-moving")
))
.unwrap(),
),
),
))
)
} else {
None
}
@ -545,10 +565,12 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, options: &Options)
// If the target file was already created in this mv call, do not overwrite
show!(USimpleError::new(
1,
format!(
"will not overwrite just-created '{}' with '{}'",
targetpath.display(),
sourcepath.display()
get_message_with_args(
"mv-error-will-not-overwrite-just-created",
HashMap::from([
("target".to_string(), targetpath.display().to_string()),
("source".to_string(), sourcepath.display().to_string())
])
),
));
continue;
@ -565,10 +587,12 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, options: &Options)
Err(e) if e.to_string().is_empty() => set_exit_code(1),
Err(e) => {
let e = e.map_err_context(|| {
format!(
"cannot move {} to {}",
sourcepath.quote(),
targetpath.quote()
get_message_with_args(
"mv-error-cannot-move",
HashMap::from([
("source".to_string(), sourcepath.quote().to_string()),
("target".to_string(), targetpath.quote().to_string()),
]),
)
});
match multi_progress {
@ -597,7 +621,13 @@ fn rename(
if to.exists() {
if opts.update == UpdateMode::None {
if opts.debug {
println!("skipped {}", to.quote());
println!(
"{}",
get_message_with_args(
"mv-debug-skipped",
HashMap::from([("target".to_string(), to.quote().to_string())])
)
);
}
return Ok(());
}
@ -609,19 +639,34 @@ fn rename(
}
if opts.update == UpdateMode::NoneFail {
let err_msg = format!("not replacing {}", to.quote());
let err_msg = get_message_with_args(
"mv-error-not-replacing",
HashMap::from([("target".to_string(), to.quote().to_string())]),
);
return Err(io::Error::other(err_msg));
}
match opts.overwrite {
OverwriteMode::NoClobber => {
if opts.debug {
println!("skipped {}", to.quote());
println!(
"{}",
get_message_with_args(
"mv-debug-skipped",
HashMap::from([("target".to_string(), to.quote().to_string())])
)
);
}
return Ok(());
}
OverwriteMode::Interactive => {
if !prompt_yes!("overwrite {}?", to.quote()) {
if !prompt_yes!(
"{}",
get_message_with_args(
"mv-prompt-overwrite",
HashMap::from([("target".to_string(), to.quote().to_string())]),
)
) {
return Err(io::Error::other(""));
}
}
@ -641,7 +686,9 @@ fn rename(
if is_empty_dir(to) {
fs::remove_dir(to)?;
} else {
return Err(io::Error::other("Directory not empty"));
return Err(io::Error::other(get_message(
"mv-error-directory-not-empty",
)));
}
}
}
@ -650,13 +697,21 @@ fn rename(
if opts.verbose {
let message = match backup_path {
Some(path) => format!(
"renamed {} -> {} (backup: {})",
from.quote(),
to.quote(),
path.quote()
Some(path) => get_message_with_args(
"mv-verbose-renamed-with-backup",
HashMap::from([
("from".to_string(), from.quote().to_string()),
("to".to_string(), to.quote().to_string()),
("backup".to_string(), path.quote().to_string()),
]),
),
None => get_message_with_args(
"mv-verbose-renamed",
HashMap::from([
("from".to_string(), from.quote().to_string()),
("to".to_string(), to.quote().to_string()),
]),
),
None => format!("renamed {} -> {}", from.quote(), to.quote()),
};
match multi_progress {
@ -751,7 +806,7 @@ fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"can't determine symlink type, since it is dangling",
get_message("mv-error-dangling-symlink"),
))
}
}
@ -761,7 +816,7 @@ fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
let path_symlink_points_to = fs::read_link(from)?;
Err(io::Error::new(
io::ErrorKind::Other,
"your operating system does not support symlinks",
get_message("mv-error-no-symlink-support"),
))
}
@ -802,8 +857,7 @@ fn rename_dir_fallback(
};
#[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))]
let xattrs =
fsxattr::retrieve_xattrs(from).unwrap_or_else(|_| std::collections::HashMap::new());
let xattrs = fsxattr::retrieve_xattrs(from).unwrap_or_else(|_| HashMap::new());
let result = if let Some(ref pb) = progress_bar {
move_dir_with_progress(from, to, &options, |process_info: TransitProcess| {
@ -822,7 +876,7 @@ fn rename_dir_fallback(
Err(err) => match err.kind {
fs_extra::error::ErrorKind::PermissionDenied => Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Permission denied",
get_message("mv-error-permission-denied"),
)),
_ => Err(io::Error::other(format!("{err:?}"))),
},
@ -837,9 +891,13 @@ fn rename_file_fallback(from: &Path, to: &Path) -> io::Result<()> {
let from = from.to_string_lossy();
io::Error::new(
err.kind(),
format!(
"inter-device move failed: '{from}' to '{to}'\
; unable to remove target: {err}"
get_message_with_args(
"mv-error-inter-device-move-failed",
HashMap::from([
("from".to_string(), from.to_string()),
("to".to_string(), to.to_string()),
("err".to_string(), err.to_string()),
]),
),
)
})?;