pl: inflect unit names according to the numeral, not with an English "s" - #744
Merged
Conversation
Mechanical change: 2435 text keys raised from t/ot/ct to T/OT/CT in the two unicode files. No text is touched. The Polish rules were written before the lowercase/uppercase convention was in use, so entries that have been translated all along still carried the "needs review" key. The audit tool therefore reported them as untranslated, which made the real gaps impossible to see: audit-translations pl, untranslated text: 3162 -> 727 The remaining 727 are in the rule files, where the entries need reading rather than a mechanical pass, so they are deliberately left alone. Every raised entry was checked to differ from the English source, i.e. it really is translated. Entries whose text legitimately equals English (Roman numerals, ligatures, proper names, whitespace) were handled separately in the previous PR; entries still holding English text are NOT raised, since that would assert a translation that does not exist. Verification: cargo test --test languages Languages::pl 609 passed, 0 failed diff: 2436 insertions, 2436 deletions, zero changes outside key case The last point is checked by normalising the key case in the diff and confirming no unpaired lines remain (without normalising, 4710 remain, so the check does discriminate).
The previous commit raised the key on entries written on a single line
(`- "±": [t: "..."]`), but not on entries whose body is a block:
- "°":
- test:
if: "parent::*[name(.)='msup' ...]"
then: [t: "stopni"]
else: [t: "stopień"]
Those are exactly the entries carrying inflection or a TTS condition, so
312 of the most deliberate translations in the file were left looking
unreviewed. This raises them: 64 in unicode.yaml, 247 in unicode-full.yaml
and one single-line entry (U+23E6) missed earlier.
One entry is deliberately left with a lower-case key: `t: ""` for U+00A0,
the else-branch of the `@data-empty-in-2D` test. That is silence rather
than text, and an upper-case key means "a translator checked this
wording" - there is no wording to check. English keeps a lower-case key
there too.
No content changes; only key case. `cargo test --test languages
Languages::pl` gives 609 passed, 0 failed, which is the same result as
before this commit, as expected for a change the engine treats as
equivalent (src/speech.rs matches "t"|"T" in one match arm).
Two loose ends in the Polish localization.
**Rule-file keys.** 248 entries in the rule files still used a lower-case
key while carrying translated Polish text. Content is untouched - every
one of the 248 changed lines differs from its predecessor only in the
case of the key name, verified line by line after normalizing the key.
17 entries are deliberately left lower-case: five are the letters C, N,
Q, R, Z in `dimension-number-sets` (the letter itself is the wording, as
in English); `ct: "s"`/`ct: "-"` in the unit rule are the plural suffix
and prefix separator; `navigate.yaml:176` is a Polish euphony variant
("ze" before "st-"); the remaining seven are XPath expressions rather
than speech.
**Tests for the number-set rules.** These rules had no Polish test at
all, in a file where two silent defects were already found. New
`tests/Languages/pl/number_sets.rs` covers the bare symbols, the
dimension form, both sign variants, and ClearSpeak agreeing with
SimpleSpeak.
The `number_set_with_variable_exponent` case is the negative control:
`dimension-number-sets` fires on `count(*)=2`, so a non-numeric exponent
must fall through to the full phrase ("liczby całkowite do potęgi n")
rather than being read as a dimension. Without it, a rule that swallowed
every superscript would still pass.
Polish keeps the letter with a dimension ("R 3"), matching en, de, fr,
nb, sv, fi and es; Hungarian is the only language that translates in that
position too. Measured with the SAPI voice NVDA actually uses: "R 3" is
byte-identical to a phonetic "er 3" and "N 4" to "en 4", so the letters
are pronounced as their Polish names.
cargo test --test languages Languages::pl 614 passed, 0 failed
The `unit` rule appended `ct: "s"` whenever a unit had no entry in
`PluralForms`, and the Polish `PluralForms` contained no SI base unit at
all. Measured output of the engine before this change:
1 metr, 2 metrs, 5 metrs, 21 metrs, 2 grams, 2 sekundas,
2 ampers, 2 mols, 2 kilo-metrs
So a Polish NVDA user heard an English plural ending on essentially every
quantity. Hungarian avoids this with `CT: ""` (it appends nothing);
French and German legitimately use "s"; Russian, whose plurals work like
Polish, does not rely on a suffix at all and fills in `PluralForms`
instead. Polish was in the wrong group.
Polish does not have one plural form. The noun follows the numeral:
1 metr | 2, 3, 4 metry (nominative plural) | 5 metrów (genitive plural)
and the teens are the exception that breaks the naive rule: 12, 13, 14
metrów, likewise 112-114, while 22 and 102 take the nominative again.
One extra plural column is therefore not enough, so this adds a second
definition section, `PluralGenitive`, next to `PluralForms` (now the
nominative plural). No Rust change is needed: `build_values` accepts any
definition name as a hashmap.
The `unit` rule (both the bare and the prefixed branch) and the
`currency` rule now read the value of the numeral and pick the section
accordingly. When the numeral cannot be determined - a variable
coefficient, as in "x ms" - `$Coefficient` is 0 and the genitive is
used, which is also what Polish uses for unknown quantities ("x
mili-sekund").
Where no inflected form is known the singular is spoken rather than a
guessed ending: a wrong ending is worse for a screen reader user than a
missing one.
Both sections cover all 131 unit and currency names that the Polish
`SIUnits`, `UnitsWithoutPrefixes`, `EnglishUnits` and `CurrencySymbols`
can produce. The forms are written out because Polish inflection cannot
be derived from the ending: metr/metry but litr/litry, om/omy,
dzień/dni, tydzień/tygodnie, rok/lata. Multi-word units inflect the head
noun and keep the dependent word's case ("5 lat świetlnych", "5 minut
łuku"). They were checked against hunspell pl_PL; the only words it does
not know are the binary-prefix neologisms (kibibajt, gibibit, ...) whose
singular forms already existed in the file.
Three base forms were themselves wrong - plurals used as singulars, so
the engine said "1 herce" and "1 konie mechaniczne":
Hz herce -> herc
hp konie mechaniczne -> koń mechaniczny
℧ M h o -> mho
Every other language has a singular here (en hertz, ru герц, fi hertsi,
es hercio; hp: ru "лошадиная сила", hu "lóerő", nb "hestekraft"; ℧:
de/fr "mho").
Why this survived: the existing Polish expectations in
tests/Languages/pl/units.rs recorded the broken output, so 609 tests
passed while the speech was wrong ("2 dzieńs", "2 tydzieńs",
"kwetta-grams"). Expectations harvested from real engine output are a
good regression net but prove nothing about correctness unless they are
read as a user would hear them. 21 test expectations are updated here,
each difference reviewed word by word.
Verification:
cargo test --test languages Languages::pl 615 passed, 0 failed
reverting general.yaml alone -> 6 of 6 new tests fail
The new tests in tests/Languages/pl/unit_plurals.rs cover one class of
phenomenon each: the three numeral classes including the teens
exception, SI base units, the prefixed branch of the rule, irregular
nouns, the corrected base forms, and multi-word units.
michaldziwisz
force-pushed
the
pl-unit-plurals
branch
from
September 2, 2026 21:00
c27028f to
33ccce7
Compare
Collaborator
|
The wrong plural ending is why checking the speech output in the tests is important. I'm glad you were able to resolve this without requiring Rust code. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Polish unit names took an English plural ending. The
unitrule appendsct: "s"when a unit has no entry inPluralForms, and the PolishPluralFormscontained no SI base unit at all. Measured engine output before this change:So a Polish NVDA user heard an English suffix on essentially every quantity, including after 5 and 21.
How other languages handle this, measured in
SharedRules/general.yaml: Hungarian appends nothing (CT: ""), French and German legitimately use "s", and Russian - whose plurals work like Polish - does not rely on a suffix at all, filling inPluralFormsinstead (метр→метра). Polish was in the wrong group.Why one plural column is not enough
Polish inflects the noun according to the numeral:
and the teens are the exception that breaks the naive rule: 12, 13, 14 metrów (genitive despite ending in 2, 3, 4), likewise 112-114, while 22 and 102 take the nominative again.
This PR therefore adds a second definition section,
PluralGenitive, next toPluralForms(which is now the nominative plural). No Rust change is needed -build_valuesinsrc/definitions.rsaccepts any definition name as a hashmap.The
unitrule (both the bare and the prefixed branch) and thecurrencyrule now read the value of the numeral and pick the section accordingly. When the numeral cannot be determined - a variable coefficient, as inx ms-$Coefficientis 0 and the genitive is used, which is also the form Polish uses for unknown quantities ("x mili-sekund").Where no inflected form is known, the singular is spoken rather than a guessed ending: for a screen reader user a wrong ending is worse than a missing one.
Three base forms were wrong as well
Plurals were being used as singulars, so the engine said "1 herce" and "1 konie mechaniczne":
Every other language has a singular here (en
hertz, ruгерц, fihertsi, eshercio; hp: ruлошадиная сила, hulóerő, nbhestekraft; ℧: de/frmho).Why this survived 609 passing tests
The existing Polish expectations in
tests/Languages/pl/units.rsrecorded the broken output, so the suite was green while the speech was wrong -"2 dzieńs","2 tydzieńs","kwetta-grams". Expectations harvested from real engine output are a good regression net but prove nothing about correctness unless they are read as a user would hear them. 21 expectations are updated here, each difference reviewed word by word.Verification
The second run is the validity control: without it the new tests would not be shown to discriminate.
Both definition sections cover all 131 unit and currency names that the Polish
SIUnits,UnitsWithoutPrefixes,EnglishUnitsandCurrencySymbolscan produce - checked programmatically against those sections, which is how the three broken base forms were found. The forms are written out because Polish inflection cannot be derived from the ending: metr/metry but litr/litry, om/omy, dzień/dni, tydzień/tygodnie, rok/lata. Multi-word units inflect the head noun and keep the dependent word's case ("5 lat świetlnych", "5 minut łuku"). All forms were checked against hunspellpl_PL; the only words it does not know are the binary-prefix neologisms (kibibajt, gibibit, ...) whose singular forms already existed in the file.New tests in
tests/Languages/pl/unit_plurals.rscover one class of phenomenon each: the three numeral classes including the teens exception, SI base units, the prefixed branch of the rule, irregular nouns, the corrected base forms, and multi-word units.Not fixed here
Compound units read the second noun in the nominative instead of the accusative: "62 mile na godzina" should be "na godzinę". That predates this branch (
git showconfirms the old expectation was"62 milas na godzina") and lives in theper-fractionrule, so it is a different class of defect and will be a separate PR.Based on #741
Rebased onto #741 (
pl-verified-keys) rather thanmain: both branches add amodline at the same place intests/Languages/pl.rs, so stacking keeps that trivial conflict out of your way. Please merge #741 first. Test count here (620) includes the 5 number-set tests that #741 adds.