stalwartlabs/stalwart · error
{key}: {language} repeats the plural category {name:?}
Error message
{key}: {language} repeats the plural category {name:?} What it means
The build script validates that within a single translation value no plural category is repeated. It walks the `category=text` segments of a value, inserting each category name into a set; a duplicate name means ambiguous translations for one category, so it panics naming the key, language, and duplicated category. This prevents silently losing one of the two conflicting forms during code generation.
Source
Thrown at crates/common/build.rs:101
for (key, translations) in locales {
if !translations
.values()
.any(|value| split_plural_forms(value).is_some())
{
continue;
}
for (language, value) in translations {
let Some(forms) = split_plural_forms(value) else {
panic!(
"{key}: {language} has no plural categories while other languages do: {value:?}"
);
};
let mut seen = HashSet::new();
for (name, _) in &forms {
if !seen.insert(*name) {
panic!("{key}: {language} repeats the plural category {name:?}");
}
}
if !seen.contains("other") {
panic!("{key}: {language} is missing the required \"other\" plural category");
}
}
keys.insert(key.clone());
}
keys
}
fn plural_forms_literal(value: &str) -> String {
let forms = split_plural_forms(value).expect("validated above");
let other = forms
.iter()
.find(|(name, _)| *name == "other")View on GitHub (pinned to e962003857)
Solutions
- Locate the key/language in i18n.yml from the panic message and remove the duplicate category segment, keeping the correct translation.
- Merge the two texts of the duplicated category into a single `category=text` segment.
- Re-run the build to confirm validation passes.
Example fix
# before fr: one=un article;one=une pièce;other=articles # after fr: one=un article;other=articles
Defensive patterns
Strategy: validation
Validate before calling
let names: Vec<&str> = value.split(';').filter_map(|s| s.split_once('=').map(|(n, _)| n)).collect();
let dup: HashSet<_> = names.iter().filter(|n| names.iter().filter(|m| *m == *n).count() > 1).collect();
assert!(dup.is_empty(), "duplicate plural categories: {dup:?}"); Prevention
- Never duplicate a category segment within one value; merge texts instead.
- Run a YAML linter/pre-commit hook over i18n.yml.
- Review merge conflicts in i18n.yml line by line.
When it happens
Trigger: A value in `resources/locales/i18n.yml` contains the same plural category twice, e.g. `one=an item;one=un article;other=items` — split_plural_forms yields two entries named `one` and the `seen.insert` check fails.
Common situations: Copy-paste errors when translating, manual merge of i18n.yml that duplicated a line segment, scripted edits that append forms without deduplicating.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- {key}: {language} has no plural categories while other langu
- {key}: {language} is missing the required "other" plural cat
- Failed to read {yaml_path:?}
- Missing: {}
AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06).
Data as JSON: /api/errors/30e2ce9b6e8e3b50.
Report an issue: GitHub.