Counts in the UI read "member(s)" / "card(s)" — four strings, both locales #144
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Grandiras/Ponente#144
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Four translated strings put the plural in brackets rather than picking a word, so a count of one reads as machine output:
admin.boards.access.restricted— "Shared with {0} member(s)" / "Für {0} Mitglied(er) freigegeben". This one is now the most visible of the four: it is the access chip on every row of/admin/boards.admin.boards.delete.cards— "Can't delete: this board still holds {0} card(s). Delete them first."card.comments— "{0} comment(s)"cal.day.count— "{0} card(s) due"All four are in
src/Ponente.Kanban/Services/LocaleService.cs, twice each (en and de). It is a consistent house convention rather than four separate slips, which is why the visual-polish pass for #141 left it alone — changing one would have made it the odd one out.The smallest fix that works for both locales this app ships is a singular/plural key pair (
…and….one) chosen in C# oncount == 1; English and German share the same rule for all four of these.LocaleTestswould want a matching check that a.onekey exists wherever the plural one does, so a third locale cannot quietly inherit English's rule.Filed by Claude while working on #141 (run).
🤖 On it — follow along in the action run; the reply will link the full session transcript.
📜 Live transcript — updating every ~5 minutes while the task runs.
Pushed
claude/issue-144.What changed
All four counted strings are now a pair of keys — the plural under the key it always had, the singular under
<key>.one— picked in C# by a newLocaleService.TCount(key, count)oncount == 1, in both locales:.oneadmin.boards.access.restrictedadmin.boards.delete.cardscard.commentscal.day.countTwo forms rather than a plural-rules library, because English and German agree on the rule for all four; the XML doc says where that stops being true (a language with more forms is the point to reach for real plural rules, not a third suffix). The five call sites — the board card's comment badge, the comment list header, the access chip, the delete error, the calendar day tooltip — now go through
TCount.The tests the issue asked for, plus the other direction
Every_counted_string_is_a_complete_pair_in_every_locale— for every locale, having the plural and having the.onemust be the same answer, so a third locale can't translate one half and quietly inherit English's rule for the other.Every_counted_string_the_app_asks_for_exists— scanssrc/forTCount("…")call sites and requires a pair for each. The dictionary can't say which keys are counted; only the call sites can.A_count_picks_the_word_that_fits_it— 0/1/4 in both locales, through a realLocaleService.No_copy_puts_its_plural_in_brackets— the house convention flipped: nothing goes back to(s)/(er).access.spec.jsgets a check that the chip on a one-member board contains1and no bracket — asserted that way rather than on the sentence, because that file deliberately never matches on translated copy.Also updated:
docs/i18n.md(a paragraph on the pair and why two forms is the ceiling), the i18n convention line inCLAUDE.md, and the unreleased changelog — folded into the existing tidy-up bullet rather than added as a ninth line, since that block is already at the seven-line capChangelogTestsenforces.Verified
dotnet buildclean,dotnet test389/389 passing. I also confirmed the parity test actually fires by deleting one German.oneand watching it fail. Then I ran the app for real (local Postgres +dotnet run, three boards, one restricted to one member and one to two) and drove it with Playwright in both locales — before ismainat HEAD, after is this branch, same database:The Playwright suite itself I did not run — it needs the compose stack for the PDS container and there's no podman/docker in this job container. The one assertion I added is a one-liner against the chip I checked by hand above.
⏱ 14m 38s · 92 turns · tokens: 110.9k in / 38.8k out (+7338.1k cache-read)
📜 Full session transcript
→ Opened PR: #145