WeblateOrg / WeblateOrg/weblate

Arabic "many" plural form is never filled by autotranslate, regardless of engine

Open
#20,994 0 comments 0 reactions 0 assignees View on GitHub
Area: Automatic translation Waiting for: Demand
Dominant language
Python
Stars
6.1k
Forks
1.4k
Avg merge
9h 53m
Merged PRs (30d)
395

Description

### Describe the issue

Autotranslating a plural string from English into Arabic (6 plural forms: zero/one/two/few/many/other) consistently leaves the CLDR **"many"** form (index 4, used for counts like 11, 25, 100) empty — across three unrelated engines tested (a classical NMT engine and two different LLMs), all producing byte-identical coverage: 5 of 6 forms filled, index 4 always `''`.

The fact that three architecturally unrelated engines fail on exactly the same form pointed at the cause being in Weblate's own source→target plural mapping rather than in any engine — confirmed via the Django shell:

```python
>>> from weblate.lang.models import Language, PluralMapper
>>> en, ar = Language.objects.get(code='en'), Language.objects.get(code='ar')
>>> en.plural.examples
{1: ['0', '2', '3', '4', '5', '6', '7', '8', '9', '10', '…'], 0: ['1']}
>>> ar.plural.examples
{0: ['0'], 1: ['1'], 2: ['2'], 3: ['3', '4', ..., '10', '103', '104', '…'], 4: ['11', '12', ..., '19', '20', '…'], 5: ['100', '101', '102', '200', '201', '202', '300', '301', '302', '400', '…']}
>>> PluralMapper(en.plural, ar.plural).target_map
index 0 -> (1, 0)
index 1 -> (0, None)
index 2 -> (1, 2)
index 3 -> (1, None)
index 4 -> (None, None) # Arabic 'many' — no source example set overlaps it
index 5 -> (-1, None)
```

English `other` = index 1, cached examples `0, 2–10`; Arabic `many` = index 4, cached examples `11–20`. The two windows never intersect. `Plural.examples` caches only ten numeric examples per plural form, so `PluralMapper.target_map` can't establish a mapping for that form and emits `(None, None)` at index 4. That maps to an empty string, and machinery skips empty source text **before any translation engine is ever invoked** — which is exactly why every engine showed the identical gap: none of them ever received the string for that form.

### I already tried
- [x] I've read and searched the documentation.
- [x] I've searched for similar filed issues in this repository.

(Related: Discussion #15602 covers incomplete automatic plural translation generally, and merged PR #16213 explicitly attempted to prevent blank target plural forms — its discussion states blank plurals break applications and an imperfect translation is preferable to an empty one, which is directly relevant to why this specific gap is worth closing.)

### Steps to reproduce the behavior

1. Create a component with a source plural string in English using `_one`/`_other` forms (e.g. `cart_one` / `cart_other`).
2. Add Arabic as a target language (6 plural forms).
3. Run autotranslate with `mode=translate`.
4. Observe 5 of 6 forms filled; index 4 (Arabic "many") is always empty, regardless of which machinery engine is configured.
5. Confirm via Django shell: `PluralMapper(Language.objects.get(code='en').plural, Language.objects.get(code='ar').plural).target_map` — index 4 is `(None, None)`.

Note when reproducing: Arabic `100` is actually the `other` form, not `many` — use `11`, `25`, `99`, or `111` as example counts that genuinely land in the `many` category, to avoid confusion when verifying.

### Expected behavior

The plural target-mapping logic should compute the mapping from the actual plural functions/rules over a domain wide enough to establish every form pairing, rather than relying on the ten cached display examples per side — so that a form like Arabic "many" (which requires seeing a number ≥ 11 in the *source* language's example set, not just the target's) isn't left permanently unmappable purely because of a display-cache size limit unrelated to the actual plural rule.

### Screenshots

N/A

### Exception traceback

N/A — no exception; the string is silently treated as empty and skipped before reaching any engine.

### How do you run Weblate?

Docker container

### Weblate version

2026.7.1 (confirmed against the current `latest` Docker tag as well)

### Weblate deploy checks

N/A — reproduced on a minimal, disposable single-node instance for isolation.

### Additional context

Secondary, smaller point worth noting in the same report: `autotranslate` returns `"1 string was updated"` for a plural unit left with 5 of 6 forms filled — technically accurate (a unit *was* updated) but gives no signal that the update is incomplete. Not requesting a separate report for this; flagging in case a partial-completeness indicator is a natural byproduct of fixing the mapping itself.

Contributor guide

Open the contributing guide

Research direction

Start with the PluralMapper entry point and inspect how target_map uses the cached plural examples exposed by Language.plural. Reproduce the Django shell mapping for English to Arabic, then add coverage for the Arabic “many” form and verify that autotranslate no longer leaves that form empty while preserving the other plural mappings.

Written by the indexing model from the issue text.

Assessment

Tech stack
django, python
Domain
internationalization, localization
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.