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

Merge pull request #8112 from sylvestre/l10n-tr

l10n: port tr for translation + add french
This commit is contained in:
Daniel Hofstetter 2025-06-09 15:06:09 +02:00 committed by GitHub
commit c14aec47f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 173 additions and 46 deletions

View file

@ -1,3 +1,41 @@
tr-about = Translate or delete characters
tr-usage = tr [OPTION]... SET1 [SET2]
tr-after-help = Translate, squeeze, and/or delete characters from standard input, writing to standard output.
# Help messages
tr-help-complement = use the complement of SET1
tr-help-delete = delete characters in SET1, do not translate
tr-help-squeeze = replace each sequence of a repeated character that is listed in the last specified SET, with a single occurrence of that character
tr-help-truncate-set1 = first truncate SET1 to length of SET2
# Error messages
tr-error-missing-operand = missing operand
tr-error-missing-operand-translating = missing operand after { $set }
Two strings must be given when translating.
tr-error-missing-operand-deleting-squeezing = missing operand after { $set }
Two strings must be given when deleting and squeezing.
tr-error-extra-operand-deleting-without-squeezing = extra operand { $operand }
Only one string may be given when deleting without squeezing repeats.
tr-error-extra-operand-simple = extra operand { $operand }
tr-error-read-directory = read error: Is a directory
tr-error-write-error = write error
# Warning messages
tr-warning-unescaped-backslash = warning: an unescaped backslash at end of string is not portable
tr-warning-ambiguous-octal-escape = the ambiguous octal escape \{ $origin_octal } is being interpreted as the 2-byte sequence \0{ $actual_octal_tail }, { $outstand_char }
# Sequence parsing error messages
tr-error-missing-char-class-name = missing character class name '[::]'
tr-error-missing-equivalence-class-char = missing equivalence class character '[==]'
tr-error-multiple-char-repeat-in-set2 = only one [c*] repeat construct may appear in string2
tr-error-char-repeat-in-set1 = the [c*] repeat construct may not appear in string1
tr-error-invalid-repeat-count = invalid repeat count { $count } in [c*n] construct
tr-error-empty-set2-when-not-truncating = when not truncating set1, string2 must be non-empty
tr-error-class-except-lower-upper-in-set2 = when translating, the only character classes that may appear in set2 are 'upper' and 'lower'
tr-error-class-in-set2-not-matched = when translating, every 'upper'/'lower' in set2 must be matched by a 'upper'/'lower' in the same position in set1
tr-error-set1-longer-set2-ends-in-class = when translating with string1 longer than string2,
the latter string must not end with a character class
tr-error-complement-more-than-one-unique = when translating with complemented character classes,
string2 must map all characters in the domain to one
tr-error-backwards-range = range-endpoints of '{ $start }-{ $end }' are in reverse collating sequence order
tr-error-multiple-char-in-equivalence = { $chars }: equivalence class operand must be a single character

View file

@ -0,0 +1,42 @@
tr-about = Traduire ou supprimer des caractères
tr-usage = tr [OPTION]... ENSEMBLE1 [ENSEMBLE2]
tr-after-help = Traduire, compresser et/ou supprimer des caractères de l'entrée standard, en écrivant vers la sortie standard.
# Messages d'aide
tr-help-complement = utiliser le complément d'ENSEMBLE1
tr-help-delete = supprimer les caractères dans ENSEMBLE1, ne pas traduire
tr-help-squeeze = remplacer chaque séquence d'un caractère répété qui est listé dans le dernier ENSEMBLE spécifié, avec une seule occurrence de ce caractère
tr-help-truncate-set1 = d'abord tronquer ENSEMBLE1 à la longueur d'ENSEMBLE2
# Messages d'erreur
tr-error-missing-operand = opérande manquant
tr-error-missing-operand-translating = opérande manquant après { $set }
Deux chaînes doivent être données lors de la traduction.
tr-error-missing-operand-deleting-squeezing = opérande manquant après { $set }
Deux chaînes doivent être données lors de la suppression et compression.
tr-error-extra-operand-deleting-without-squeezing = opérande supplémentaire { $operand }
Une seule chaîne peut être donnée lors de la suppression sans compression des répétitions.
tr-error-extra-operand-simple = opérande supplémentaire { $operand }
tr-error-read-directory = erreur de lecture : Est un répertoire
tr-error-write-error = erreur d'écriture
# Messages d'avertissement
tr-warning-unescaped-backslash = avertissement : une barre oblique inverse non échappée à la fin de la chaîne n'est pas portable
tr-warning-ambiguous-octal-escape = l'échappement octal ambigu \{ $origin_octal } est en cours
d'interprétation comme la séquence de 2 octets \0{ $actual_octal_tail }, { $outstand_char }
# Messages d'erreur d'analyse de séquence
tr-error-missing-char-class-name = nom de classe de caractères manquant '[::]'
tr-error-missing-equivalence-class-char = caractère de classe d'équivalence manquant '[==]'
tr-error-multiple-char-repeat-in-set2 = seule une construction de répétition [c*] peut apparaître dans string2
tr-error-char-repeat-in-set1 = la construction de répétition [c*] ne peut pas apparaître dans string1
tr-error-invalid-repeat-count = nombre de répétitions invalide { $count } dans la construction [c*n]
tr-error-empty-set2-when-not-truncating = quand on ne tronque pas set1, string2 doit être non-vide
tr-error-class-except-lower-upper-in-set2 = lors de la traduction, les seules classes de caractères qui peuvent apparaître dans set2 sont 'upper' et 'lower'
tr-error-class-in-set2-not-matched = lors de la traduction, chaque 'upper'/'lower' dans set2 doit être associé à un 'upper'/'lower' à la même position dans set1
tr-error-set1-longer-set2-ends-in-class = lors de la traduction avec string1 plus long que string2,
cette dernière chaîne ne doit pas se terminer par une classe de caractères
tr-error-complement-more-than-one-unique = lors de la traduction avec des classes de caractères complémentées,
string2 doit mapper tous les caractères du domaine vers un seul
tr-error-backwards-range = les points de fin de plage de '{ $start }-{ $end }' sont dans l'ordre inverse de la séquence de collation
tr-error-multiple-char-in-equivalence = { $chars } : l'opérande de classe d'équivalence doit être un seul caractère

View file

@ -24,6 +24,7 @@ use std::{
ops::Not,
};
use uucore::error::{FromIo, UError, UResult};
use uucore::locale::{get_message, get_message_with_args};
use uucore::show_warning;
#[derive(Debug, Clone)]
@ -45,44 +46,65 @@ pub enum BadSequence {
impl Display for BadSequence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingCharClassName => write!(f, "missing character class name '[::]'"),
Self::MissingCharClassName => {
write!(f, "{}", get_message("tr-error-missing-char-class-name"))
}
Self::MissingEquivalentClassChar => {
write!(f, "missing equivalence class character '[==]'")
write!(
f,
"{}",
get_message("tr-error-missing-equivalence-class-char")
)
}
Self::MultipleCharRepeatInSet2 => {
write!(f, "only one [c*] repeat construct may appear in string2")
write!(
f,
"{}",
get_message("tr-error-multiple-char-repeat-in-set2")
)
}
Self::CharRepeatInSet1 => {
write!(f, "the [c*] repeat construct may not appear in string1")
write!(f, "{}", get_message("tr-error-char-repeat-in-set1"))
}
Self::InvalidRepeatCount(count) => {
write!(f, "invalid repeat count '{count}' in [c*n] construct")
write!(
f,
"{}",
get_message_with_args(
"tr-error-invalid-repeat-count",
HashMap::from([("count".to_string(), format!("'{}'", count))])
)
)
}
Self::EmptySet2WhenNotTruncatingSet1 => {
write!(f, "when not truncating set1, string2 must be non-empty")
write!(
f,
"{}",
get_message("tr-error-empty-set2-when-not-truncating")
)
}
Self::ClassExceptLowerUpperInSet2 => {
write!(
f,
"when translating, the only character classes that may appear in set2 are 'upper' and 'lower'"
"{}",
get_message("tr-error-class-except-lower-upper-in-set2")
)
}
Self::ClassInSet2NotMatchedBySet1 => {
write!(
f,
"when translating, every 'upper'/'lower' in set2 must be matched by a 'upper'/'lower' in the same position in set1"
)
write!(f, "{}", get_message("tr-error-class-in-set2-not-matched"))
}
Self::Set1LongerSet2EndsInClass => {
write!(
f,
"when translating with string1 longer than string2,\nthe latter string must not end with a character class"
"{}",
get_message("tr-error-set1-longer-set2-ends-in-class")
)
}
Self::ComplementMoreThanOneUniqueInSet2 => {
write!(
f,
"when translating with complemented character classes,\nstring2 must map all characters in the domain to one"
"{}",
get_message("tr-error-complement-more-than-one-unique")
)
}
Self::BackwardsRange { end, start } => {
@ -94,17 +116,25 @@ impl Display for BadSequence {
}
}
}
write!(
f,
"range-endpoints of '{}-{}' are in reverse collating sequence order",
end_or_start_to_string(start),
end_or_start_to_string(end)
"{}",
get_message_with_args(
"tr-error-backwards-range",
HashMap::from([
("start".to_string(), end_or_start_to_string(start)),
("end".to_string(), end_or_start_to_string(end))
])
)
)
}
Self::MultipleCharInEquivalence(s) => write!(
f,
"{s}: equivalence class operand must be a single character"
"{}",
get_message_with_args(
"tr-error-multiple-char-in-equivalence",
HashMap::from([("chars".to_string(), s.clone())])
)
),
}
}
@ -364,11 +394,25 @@ impl Sequence {
let origin_octal: &str = std::str::from_utf8(input).unwrap();
let actual_octal_tail: &str = std::str::from_utf8(&input[0..2]).unwrap();
let outstand_char: char = char::from_u32(input[2] as u32).unwrap();
show_warning!("the ambiguous octal escape \\{origin_octal} is being\n interpreted as the 2-byte sequence \\0{actual_octal_tail}, {outstand_char}");
show_warning!(
"{}",
get_message_with_args(
"tr-warning-ambiguous-octal-escape",
HashMap::from([
("origin_octal".to_string(), origin_octal.to_string()),
(
"actual_octal_tail".to_string(),
actual_octal_tail.to_string()
),
("outstand_char".to_string(), outstand_char.to_string())
])
)
);
}
result
},
).parse(input)
)
.parse(input)
}
fn parse_octal_two_digits(input: &[u8]) -> IResult<&[u8], u8> {
@ -666,7 +710,7 @@ where
output
.write_all(&output_buf)
.map_err_context(|| "write error".into())?;
.map_err_context(|| get_message("tr-error-write-error"))?;
buf.clear();
output_buf.clear();

View file

@ -13,6 +13,7 @@ use clap::{Arg, ArgAction, Command, value_parser};
use operation::{
Sequence, SqueezeOperation, SymbolTranslator, TranslateOperation, translate_input,
};
use std::collections::HashMap;
use std::ffi::OsString;
use std::io::{BufWriter, Write, stdin, stdout};
use uucore::display::Quotable;
@ -20,7 +21,7 @@ use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::fs::is_stdin_directory;
use uucore::{format_usage, os_str_as_bytes, show};
use uucore::locale::get_message;
use uucore::locale::{get_message, get_message_with_args};
mod options {
pub const COMPLEMENT: &str = "complement";
@ -53,15 +54,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let sets_len = sets.len();
if sets.is_empty() {
return Err(UUsageError::new(1, "missing operand"));
return Err(UUsageError::new(1, get_message("tr-error-missing-operand")));
}
if !(delete_flag || squeeze_flag) && sets_len < 2 {
return Err(UUsageError::new(
1,
format!(
"missing operand after {}\nTwo strings must be given when translating.",
sets[0].quote()
get_message_with_args(
"tr-error-missing-operand-translating",
HashMap::from([("set".to_string(), sets[0].quote().to_string())]),
),
));
}
@ -69,29 +70,35 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if delete_flag & squeeze_flag && sets_len < 2 {
return Err(UUsageError::new(
1,
format!(
"missing operand after {}\nTwo strings must be given when deleting and squeezing.",
sets[0].quote()
get_message_with_args(
"tr-error-missing-operand-deleting-squeezing",
HashMap::from([("set".to_string(), sets[0].quote().to_string())]),
),
));
}
if sets_len > 1 {
let start = "extra operand";
if delete_flag && !squeeze_flag {
let op = sets[1].quote();
let msg = if sets_len == 2 {
format!(
"{start} {op}\nOnly one string may be given when deleting without squeezing repeats.",
get_message_with_args(
"tr-error-extra-operand-deleting-without-squeezing",
HashMap::from([("operand".to_string(), op.to_string())]),
)
} else {
format!("{start} {op}")
get_message_with_args(
"tr-error-extra-operand-simple",
HashMap::from([("operand".to_string(), op.to_string())]),
)
};
return Err(UUsageError::new(1, msg));
}
if sets_len > 2 {
let op = sets[2].quote();
let msg = format!("{start} {op}");
let msg = get_message_with_args(
"tr-error-extra-operand-simple",
HashMap::from([("operand".to_string(), op.to_string())]),
);
return Err(UUsageError::new(1, msg));
}
}
@ -103,7 +110,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// The trailing backslash has a non-backslash character before it.
show!(USimpleError::new(
0,
"warning: an unescaped backslash at end of string is not portable"
get_message("tr-warning-unescaped-backslash")
));
}
}
@ -125,7 +132,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
)?;
if is_stdin_directory(&stdin) {
return Err(USimpleError::new(1, "read error: Is a directory"));
return Err(USimpleError::new(1, get_message("tr-error-read-directory")));
}
// '*_op' are the operations that need to be applied, in order.
@ -156,7 +163,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
buffered_stdout
.flush()
.map_err_context(|| "write error".into())?;
.map_err_context(|| get_message("tr-error-write-error"))?;
Ok(())
}
@ -173,7 +180,7 @@ pub fn uu_app() -> Command {
.visible_short_alias('C')
.short('c')
.long(options::COMPLEMENT)
.help("use the complement of SET1")
.help(get_message("tr-help-complement"))
.action(ArgAction::SetTrue)
.overrides_with(options::COMPLEMENT),
)
@ -181,7 +188,7 @@ pub fn uu_app() -> Command {
Arg::new(options::DELETE)
.short('d')
.long(options::DELETE)
.help("delete characters in SET1, do not translate")
.help(get_message("tr-help-delete"))
.action(ArgAction::SetTrue)
.overrides_with(options::DELETE),
)
@ -189,11 +196,7 @@ pub fn uu_app() -> Command {
Arg::new(options::SQUEEZE)
.long(options::SQUEEZE)
.short('s')
.help(
"replace each sequence of a repeated character that is \
listed in the last specified SET, with a single occurrence \
of that character",
)
.help(get_message("tr-help-squeeze"))
.action(ArgAction::SetTrue)
.overrides_with(options::SQUEEZE),
)
@ -201,7 +204,7 @@ pub fn uu_app() -> Command {
Arg::new(options::TRUNCATE_SET1)
.long(options::TRUNCATE_SET1)
.short('t')
.help("first truncate SET1 to length of SET2")
.help(get_message("tr-help-truncate-set1"))
.action(ArgAction::SetTrue)
.overrides_with(options::TRUNCATE_SET1),
)

View file

@ -1521,7 +1521,7 @@ fn test_multibyte_octal_sequence() {
.args(&["-d", r"\501"])
.pipe_in("(1Ł)")
.succeeds()
.stderr_is("tr: warning: the ambiguous octal escape \\501 is being\n interpreted as the 2-byte sequence \\050, 1\n")
.stderr_is("tr: warning: the ambiguous octal escape \\501 is being interpreted as the 2-byte sequence \\050, 1\n")
.stdout_is("Ł)");
}