tr: align [:upper:]/[:lower:] the way GNU does, and say so - #14400
Socialpranker wants to merge 1 commit into
Conversation
|
GNU testsuite comparison: |
| 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-class-in-set2-not-matched = construction [:upper:] et/ou [:lower:] mal alignée |
There was a problem hiding this comment.
does this match the french translation of gnu in meaning ? Could you post it here ?
There was a problem hiding this comment.
GNU's official translation (translationproject.org fr.po):
msgid "misaligned [:upper:] and/or [:lower:] construct"
msgstr "les structures [:upper:] ou [:lower:] sont mal alignées"
Our fr string was close in meaning but not the official wording. Updated fr-FR.ftl to match GNU's translation exactly, in 828738d.
|
GNU's official translation (translationproject.org fr.po): Our previous fr string was close in meaning but not the official wording. Updated fr-FR.ftl to match GNU's translation exactly, in 828738d. |
anastygnome
left a comment
There was a problem hiding this comment.
You must NOT copy code from GNU for licencing reasons.
Just to be in the clear
Replace
sont mal alignées
By
ne sont pas alignées correctement
|
Understood — replaced with your wording in f773c6b: |
|
please read the AI policy |
f773c6b to
49a0eb2
Compare
| 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. | ||
| Two strings must be given when both deleting and squeezing repeats. |
There was a problem hiding this comment.
unrelated to the upper/lower change, a separate pr would have been better
| // Only when translating: with -d, set2 is the squeeze set and lines up with nothing. | ||
| for (set2_pos, set2_item) in set2.iter().enumerate() { | ||
| if matches!(set2_item, Self::Class(_)) { | ||
| if translating && matches!(set2_item, Self::Class(_)) { |
There was a problem hiding this comment.
could you wrap the whole loop in if translating { ... } instead of checking it on every item?
| let mut class_matches = false; | ||
| for (set1_pos, set1_item) in set1.iter().enumerate() { | ||
| if matches!(set1_item, Self::Class(_)) { | ||
| // Only an 'upper'/'lower' in set1 can line up with one in |
There was a problem hiding this comment.
could be a one line comment, no? e.g. // only upper/lower can pair with upper/lower
| /// The alignment rule is about translating only: with -d, SET2 is the squeeze | ||
| /// set and lines up with nothing. | ||
| #[test] | ||
| fn test_misaligned_construct_not_checked_when_deleting() { |
There was a problem hiding this comment.
please also test -s alone (still translating, so still rejected) and plain -d with a class in set2
|
@Socialpranker Please test against updated GNU coreutils 9.12. |
Merging this PR will not alter performance
Comparing Footnotes
|
371cf81 to
d22dd2c
Compare
|
wrapped the loop in if translating, comment is one line, added -s alone and plain -d with a class in set2. checked against 9.12, the upper/lower messages match. you're right about the ftl bit, should i split it out into its own pr? |
Three related gaps in how SET1/SET2 character classes are validated, found
by diffing against GNU:
The fix
upper/lowerin SET2. Only anupper/lowercan be one — anythingelse leaves the mapping undefined, which is why GNU rejects it. With
[:alpha:]/[:upper:]the bogus match let the input fall through tothe length check and produce the wrong message; with
[:digit:]/[:upper:]it produced no error at all.-d, SET2 is thesqueeze set and lines up with nothing, so both are now gated on
translating.
tr-error-class-in-set2-not-matchedrestated the rule where GNU namesthe fault:
misaligned [:upper:] and/or [:lower:] construct.The
-dsmissing-operand wording, which was part of this PR at first,is now in #14873.
The
when translating with string1 longer than string2message is notremoved — GNU has it too, for
tr '[:upper:][:lower:]' '[:lower:]', and anew test pins that it still fires there.
How the GNU behavior was established
By running the installed GNU coreutils 9.11 binary (Homebrew,
gtr) as ablack box over 14 SET1/SET2 pairs: classes aligned and misaligned, a class
in SET2 against a literal, a non-
upper/lowerclass in SET1, SET1longer than SET2, and each of those again under
-d,-s,-dsand-dcs. Exit status and stderr diffed against uutils. I did not read GNUcoreutils source.
Testing
tests/by-util/test_tr.rs:test_misaligned_upper_lower_construct(five pairs, exact message and exit status 1),
test_misaligned_construct_not_checked_when_deleting,test_misaligned_construct_checked_when_squeezing(-salone stilltranslates),
test_delete_with_class_in_set2(plain-dreports theextra operand), and
test_set1_longer_than_set2_ending_in_classguarding the message that must stay.
Mutation-checked: reverting
operation.rsalone fails three of them,reverting the
.ftlalone fails two.cargo test --no-default-features --features tr --test tests test_tr:165 passed, 0 failed.
cargo clippy -p uu_tr --all-targets -- -D warnings: clean.cargo fmt --check: clean.trinvocationsin my harness: mismatches 2 -> 0 (measured before the
-dswordingmoved to tr: word the -ds missing-operand hint the way GNU does #14873).
French translations updated to match.
Disclosure
Prepared with AI assistance (Claude Opus 5, via Claude Code), per the AI
policy in CONTRIBUTING.md. Every GNU behavior quoted above came from
running the installed binary, not from reading GPL source. All testing was
run locally.