Skip to content

Commit c14aec4

Browse files
authored
Merge pull request #8112 from sylvestre/l10n-tr
l10n: port tr for translation + add french
2 parents 07a95aa + aba6128 commit c14aec4

File tree

5 files changed

+173
-46
lines changed

5 files changed

+173
-46
lines changed

src/uu/tr/locales/en-US.ftl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,41 @@
11
tr-about = Translate or delete characters
22
tr-usage = tr [OPTION]... SET1 [SET2]
33
tr-after-help = Translate, squeeze, and/or delete characters from standard input, writing to standard output.
4+
5+
# Help messages
6+
tr-help-complement = use the complement of SET1
7+
tr-help-delete = delete characters in SET1, do not translate
8+
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
9+
tr-help-truncate-set1 = first truncate SET1 to length of SET2
10+
11+
# Error messages
12+
tr-error-missing-operand = missing operand
13+
tr-error-missing-operand-translating = missing operand after { $set }
14+
Two strings must be given when translating.
15+
tr-error-missing-operand-deleting-squeezing = missing operand after { $set }
16+
Two strings must be given when deleting and squeezing.
17+
tr-error-extra-operand-deleting-without-squeezing = extra operand { $operand }
18+
Only one string may be given when deleting without squeezing repeats.
19+
tr-error-extra-operand-simple = extra operand { $operand }
20+
tr-error-read-directory = read error: Is a directory
21+
tr-error-write-error = write error
22+
23+
# Warning messages
24+
tr-warning-unescaped-backslash = warning: an unescaped backslash at end of string is not portable
25+
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 }
26+
27+
# Sequence parsing error messages
28+
tr-error-missing-char-class-name = missing character class name '[::]'
29+
tr-error-missing-equivalence-class-char = missing equivalence class character '[==]'
30+
tr-error-multiple-char-repeat-in-set2 = only one [c*] repeat construct may appear in string2
31+
tr-error-char-repeat-in-set1 = the [c*] repeat construct may not appear in string1
32+
tr-error-invalid-repeat-count = invalid repeat count { $count } in [c*n] construct
33+
tr-error-empty-set2-when-not-truncating = when not truncating set1, string2 must be non-empty
34+
tr-error-class-except-lower-upper-in-set2 = when translating, the only character classes that may appear in set2 are 'upper' and 'lower'
35+
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
36+
tr-error-set1-longer-set2-ends-in-class = when translating with string1 longer than string2,
37+
the latter string must not end with a character class
38+
tr-error-complement-more-than-one-unique = when translating with complemented character classes,
39+
string2 must map all characters in the domain to one
40+
tr-error-backwards-range = range-endpoints of '{ $start }-{ $end }' are in reverse collating sequence order
41+
tr-error-multiple-char-in-equivalence = { $chars }: equivalence class operand must be a single character

src/uu/tr/locales/fr-FR.ftl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
tr-about = Traduire ou supprimer des caractères
2+
tr-usage = tr [OPTION]... ENSEMBLE1 [ENSEMBLE2]
3+
tr-after-help = Traduire, compresser et/ou supprimer des caractères de l'entrée standard, en écrivant vers la sortie standard.
4+
5+
# Messages d'aide
6+
tr-help-complement = utiliser le complément d'ENSEMBLE1
7+
tr-help-delete = supprimer les caractères dans ENSEMBLE1, ne pas traduire
8+
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
9+
tr-help-truncate-set1 = d'abord tronquer ENSEMBLE1 à la longueur d'ENSEMBLE2
10+
11+
# Messages d'erreur
12+
tr-error-missing-operand = opérande manquant
13+
tr-error-missing-operand-translating = opérande manquant après { $set }
14+
Deux chaînes doivent être données lors de la traduction.
15+
tr-error-missing-operand-deleting-squeezing = opérande manquant après { $set }
16+
Deux chaînes doivent être données lors de la suppression et compression.
17+
tr-error-extra-operand-deleting-without-squeezing = opérande supplémentaire { $operand }
18+
Une seule chaîne peut être donnée lors de la suppression sans compression des répétitions.
19+
tr-error-extra-operand-simple = opérande supplémentaire { $operand }
20+
tr-error-read-directory = erreur de lecture : Est un répertoire
21+
tr-error-write-error = erreur d'écriture
22+
23+
# Messages d'avertissement
24+
tr-warning-unescaped-backslash = avertissement : une barre oblique inverse non échappée à la fin de la chaîne n'est pas portable
25+
tr-warning-ambiguous-octal-escape = l'échappement octal ambigu \{ $origin_octal } est en cours
26+
d'interprétation comme la séquence de 2 octets \0{ $actual_octal_tail }, { $outstand_char }
27+
28+
# Messages d'erreur d'analyse de séquence
29+
tr-error-missing-char-class-name = nom de classe de caractères manquant '[::]'
30+
tr-error-missing-equivalence-class-char = caractère de classe d'équivalence manquant '[==]'
31+
tr-error-multiple-char-repeat-in-set2 = seule une construction de répétition [c*] peut apparaître dans string2
32+
tr-error-char-repeat-in-set1 = la construction de répétition [c*] ne peut pas apparaître dans string1
33+
tr-error-invalid-repeat-count = nombre de répétitions invalide { $count } dans la construction [c*n]
34+
tr-error-empty-set2-when-not-truncating = quand on ne tronque pas set1, string2 doit être non-vide
35+
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'
36+
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
37+
tr-error-set1-longer-set2-ends-in-class = lors de la traduction avec string1 plus long que string2,
38+
cette dernière chaîne ne doit pas se terminer par une classe de caractères
39+
tr-error-complement-more-than-one-unique = lors de la traduction avec des classes de caractères complémentées,
40+
string2 doit mapper tous les caractères du domaine vers un seul
41+
tr-error-backwards-range = les points de fin de plage de '{ $start }-{ $end }' sont dans l'ordre inverse de la séquence de collation
42+
tr-error-multiple-char-in-equivalence = { $chars } : l'opérande de classe d'équivalence doit être un seul caractère

src/uu/tr/src/operation.rs

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use std::{
2424
ops::Not,
2525
};
2626
use uucore::error::{FromIo, UError, UResult};
27+
use uucore::locale::{get_message, get_message_with_args};
2728
use uucore::show_warning;
2829

2930
#[derive(Debug, Clone)]
@@ -45,44 +46,65 @@ pub enum BadSequence {
4546
impl Display for BadSequence {
4647
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4748
match self {
48-
Self::MissingCharClassName => write!(f, "missing character class name '[::]'"),
49+
Self::MissingCharClassName => {
50+
write!(f, "{}", get_message("tr-error-missing-char-class-name"))
51+
}
4952
Self::MissingEquivalentClassChar => {
50-
write!(f, "missing equivalence class character '[==]'")
53+
write!(
54+
f,
55+
"{}",
56+
get_message("tr-error-missing-equivalence-class-char")
57+
)
5158
}
5259
Self::MultipleCharRepeatInSet2 => {
53-
write!(f, "only one [c*] repeat construct may appear in string2")
60+
write!(
61+
f,
62+
"{}",
63+
get_message("tr-error-multiple-char-repeat-in-set2")
64+
)
5465
}
5566
Self::CharRepeatInSet1 => {
56-
write!(f, "the [c*] repeat construct may not appear in string1")
67+
write!(f, "{}", get_message("tr-error-char-repeat-in-set1"))
5768
}
5869
Self::InvalidRepeatCount(count) => {
59-
write!(f, "invalid repeat count '{count}' in [c*n] construct")
70+
write!(
71+
f,
72+
"{}",
73+
get_message_with_args(
74+
"tr-error-invalid-repeat-count",
75+
HashMap::from([("count".to_string(), format!("'{}'", count))])
76+
)
77+
)
6078
}
6179
Self::EmptySet2WhenNotTruncatingSet1 => {
62-
write!(f, "when not truncating set1, string2 must be non-empty")
63-
}
64-
Self::ClassExceptLowerUpperInSet2 => {
6580
write!(
6681
f,
67-
"when translating, the only character classes that may appear in set2 are 'upper' and 'lower'"
82+
"{}",
83+
get_message("tr-error-empty-set2-when-not-truncating")
6884
)
6985
}
70-
Self::ClassInSet2NotMatchedBySet1 => {
86+
Self::ClassExceptLowerUpperInSet2 => {
7187
write!(
7288
f,
73-
"when translating, every 'upper'/'lower' in set2 must be matched by a 'upper'/'lower' in the same position in set1"
89+
"{}",
90+
get_message("tr-error-class-except-lower-upper-in-set2")
7491
)
7592
}
93+
Self::ClassInSet2NotMatchedBySet1 => {
94+
write!(f, "{}", get_message("tr-error-class-in-set2-not-matched"))
95+
}
7696
Self::Set1LongerSet2EndsInClass => {
7797
write!(
7898
f,
79-
"when translating with string1 longer than string2,\nthe latter string must not end with a character class"
99+
"{}",
100+
get_message("tr-error-set1-longer-set2-ends-in-class")
80101
)
81102
}
82103
Self::ComplementMoreThanOneUniqueInSet2 => {
83104
write!(
84105
f,
85-
"when translating with complemented character classes,\nstring2 must map all characters in the domain to one"
106+
"{}",
107+
get_message("tr-error-complement-more-than-one-unique")
86108
)
87109
}
88110
Self::BackwardsRange { end, start } => {
@@ -94,17 +116,25 @@ impl Display for BadSequence {
94116
}
95117
}
96118
}
97-
98119
write!(
99120
f,
100-
"range-endpoints of '{}-{}' are in reverse collating sequence order",
101-
end_or_start_to_string(start),
102-
end_or_start_to_string(end)
121+
"{}",
122+
get_message_with_args(
123+
"tr-error-backwards-range",
124+
HashMap::from([
125+
("start".to_string(), end_or_start_to_string(start)),
126+
("end".to_string(), end_or_start_to_string(end))
127+
])
128+
)
103129
)
104130
}
105131
Self::MultipleCharInEquivalence(s) => write!(
106132
f,
107-
"{s}: equivalence class operand must be a single character"
133+
"{}",
134+
get_message_with_args(
135+
"tr-error-multiple-char-in-equivalence",
136+
HashMap::from([("chars".to_string(), s.clone())])
137+
)
108138
),
109139
}
110140
}
@@ -364,11 +394,25 @@ impl Sequence {
364394
let origin_octal: &str = std::str::from_utf8(input).unwrap();
365395
let actual_octal_tail: &str = std::str::from_utf8(&input[0..2]).unwrap();
366396
let outstand_char: char = char::from_u32(input[2] as u32).unwrap();
367-
show_warning!("the ambiguous octal escape \\{origin_octal} is being\n interpreted as the 2-byte sequence \\0{actual_octal_tail}, {outstand_char}");
397+
show_warning!(
398+
"{}",
399+
get_message_with_args(
400+
"tr-warning-ambiguous-octal-escape",
401+
HashMap::from([
402+
("origin_octal".to_string(), origin_octal.to_string()),
403+
(
404+
"actual_octal_tail".to_string(),
405+
actual_octal_tail.to_string()
406+
),
407+
("outstand_char".to_string(), outstand_char.to_string())
408+
])
409+
)
410+
);
368411
}
369412
result
370413
},
371-
).parse(input)
414+
)
415+
.parse(input)
372416
}
373417

374418
fn parse_octal_two_digits(input: &[u8]) -> IResult<&[u8], u8> {
@@ -666,7 +710,7 @@ where
666710

667711
output
668712
.write_all(&output_buf)
669-
.map_err_context(|| "write error".into())?;
713+
.map_err_context(|| get_message("tr-error-write-error"))?;
670714

671715
buf.clear();
672716
output_buf.clear();

src/uu/tr/src/tr.rs

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ use clap::{Arg, ArgAction, Command, value_parser};
1313
use operation::{
1414
Sequence, SqueezeOperation, SymbolTranslator, TranslateOperation, translate_input,
1515
};
16+
use std::collections::HashMap;
1617
use std::ffi::OsString;
1718
use std::io::{BufWriter, Write, stdin, stdout};
1819
use uucore::display::Quotable;
1920
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
2021
use uucore::fs::is_stdin_directory;
2122
use uucore::{format_usage, os_str_as_bytes, show};
2223

23-
use uucore::locale::get_message;
24+
use uucore::locale::{get_message, get_message_with_args};
2425

2526
mod options {
2627
pub const COMPLEMENT: &str = "complement";
@@ -53,45 +54,51 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
5354
let sets_len = sets.len();
5455

5556
if sets.is_empty() {
56-
return Err(UUsageError::new(1, "missing operand"));
57+
return Err(UUsageError::new(1, get_message("tr-error-missing-operand")));
5758
}
5859

5960
if !(delete_flag || squeeze_flag) && sets_len < 2 {
6061
return Err(UUsageError::new(
6162
1,
62-
format!(
63-
"missing operand after {}\nTwo strings must be given when translating.",
64-
sets[0].quote()
63+
get_message_with_args(
64+
"tr-error-missing-operand-translating",
65+
HashMap::from([("set".to_string(), sets[0].quote().to_string())]),
6566
),
6667
));
6768
}
6869

6970
if delete_flag & squeeze_flag && sets_len < 2 {
7071
return Err(UUsageError::new(
7172
1,
72-
format!(
73-
"missing operand after {}\nTwo strings must be given when deleting and squeezing.",
74-
sets[0].quote()
73+
get_message_with_args(
74+
"tr-error-missing-operand-deleting-squeezing",
75+
HashMap::from([("set".to_string(), sets[0].quote().to_string())]),
7576
),
7677
));
7778
}
7879

7980
if sets_len > 1 {
80-
let start = "extra operand";
8181
if delete_flag && !squeeze_flag {
8282
let op = sets[1].quote();
8383
let msg = if sets_len == 2 {
84-
format!(
85-
"{start} {op}\nOnly one string may be given when deleting without squeezing repeats.",
84+
get_message_with_args(
85+
"tr-error-extra-operand-deleting-without-squeezing",
86+
HashMap::from([("operand".to_string(), op.to_string())]),
8687
)
8788
} else {
88-
format!("{start} {op}")
89+
get_message_with_args(
90+
"tr-error-extra-operand-simple",
91+
HashMap::from([("operand".to_string(), op.to_string())]),
92+
)
8993
};
9094
return Err(UUsageError::new(1, msg));
9195
}
9296
if sets_len > 2 {
9397
let op = sets[2].quote();
94-
let msg = format!("{start} {op}");
98+
let msg = get_message_with_args(
99+
"tr-error-extra-operand-simple",
100+
HashMap::from([("operand".to_string(), op.to_string())]),
101+
);
95102
return Err(UUsageError::new(1, msg));
96103
}
97104
}
@@ -103,7 +110,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
103110
// The trailing backslash has a non-backslash character before it.
104111
show!(USimpleError::new(
105112
0,
106-
"warning: an unescaped backslash at end of string is not portable"
113+
get_message("tr-warning-unescaped-backslash")
107114
));
108115
}
109116
}
@@ -125,7 +132,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
125132
)?;
126133

127134
if is_stdin_directory(&stdin) {
128-
return Err(USimpleError::new(1, "read error: Is a directory"));
135+
return Err(USimpleError::new(1, get_message("tr-error-read-directory")));
129136
}
130137

131138
// '*_op' are the operations that need to be applied, in order.
@@ -156,7 +163,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
156163

157164
buffered_stdout
158165
.flush()
159-
.map_err_context(|| "write error".into())?;
166+
.map_err_context(|| get_message("tr-error-write-error"))?;
160167

161168
Ok(())
162169
}
@@ -173,35 +180,31 @@ pub fn uu_app() -> Command {
173180
.visible_short_alias('C')
174181
.short('c')
175182
.long(options::COMPLEMENT)
176-
.help("use the complement of SET1")
183+
.help(get_message("tr-help-complement"))
177184
.action(ArgAction::SetTrue)
178185
.overrides_with(options::COMPLEMENT),
179186
)
180187
.arg(
181188
Arg::new(options::DELETE)
182189
.short('d')
183190
.long(options::DELETE)
184-
.help("delete characters in SET1, do not translate")
191+
.help(get_message("tr-help-delete"))
185192
.action(ArgAction::SetTrue)
186193
.overrides_with(options::DELETE),
187194
)
188195
.arg(
189196
Arg::new(options::SQUEEZE)
190197
.long(options::SQUEEZE)
191198
.short('s')
192-
.help(
193-
"replace each sequence of a repeated character that is \
194-
listed in the last specified SET, with a single occurrence \
195-
of that character",
196-
)
199+
.help(get_message("tr-help-squeeze"))
197200
.action(ArgAction::SetTrue)
198201
.overrides_with(options::SQUEEZE),
199202
)
200203
.arg(
201204
Arg::new(options::TRUNCATE_SET1)
202205
.long(options::TRUNCATE_SET1)
203206
.short('t')
204-
.help("first truncate SET1 to length of SET2")
207+
.help(get_message("tr-help-truncate-set1"))
205208
.action(ArgAction::SetTrue)
206209
.overrides_with(options::TRUNCATE_SET1),
207210
)

tests/by-util/test_tr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1521,7 +1521,7 @@ fn test_multibyte_octal_sequence() {
15211521
.args(&["-d", r"\501"])
15221522
.pipe_in("(1Ł)")
15231523
.succeeds()
1524-
.stderr_is("tr: warning: the ambiguous octal escape \\501 is being\n interpreted as the 2-byte sequence \\050, 1\n")
1524+
.stderr_is("tr: warning: the ambiguous octal escape \\501 is being interpreted as the 2-byte sequence \\050, 1\n")
15251525
.stdout_is("Ł)");
15261526
}
15271527

0 commit comments

Comments
 (0)