Repository navigation
fix(apps): "1 updates available" reads "1 update available", page strings pick their plural form - #30288
Conversation
…ings pick their plural form Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-5de8df9724303a872254
There was a problem hiding this comment.
4 issues found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApps/Resources/Localizable.xcstrings">
<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApps/Resources/Localizable.xcstrings:14985">
P3: The Arabic and Bosnian `other` forms do not keep the existing translations as the PR description says. Arabic moves the old "يتوفر %@ تحديثًا" to `many` and writes a new `other`; Bosnian replaces "Dostupno ažuriranja: %@" with a reworded `other`. Either keep the existing text as `other` or update the PR description to match.</violation>
<violation number="2" location="Packages/macOS/CmuxNext/Sources/CmuxNextApps/Resources/Localizable.xcstrings:15243">
P3: The `few` form renders `Доступно обновления: 2`, which mismatches the plural noun. Use `Доступны обновления: %@` for this category.</violation>
<violation number="3" location="Packages/macOS/CmuxNext/Sources/CmuxNextApps/Resources/Localizable.xcstrings:15297">
P3: The `few` form renders `Доступно оновлення: 2`, which mismatches the plural subject. Use `Доступні оновлення: %@` for this category.</violation>
</file>
<file name="webviews/src/pages/shared/i18n.ts">
<violation number="1" location="webviews/src/pages/shared/i18n.ts:61">
P1: This regresses seven locales (ja, km, ko, th, vi, zh-Hans, zh-Hant) on the Apps page. `formKey` picks a `#<category>` key whenever the English table has `#other`, and never checks whether the locale table has the plain key instead. Those locales have no plural variations, so their translation is stored under the plain key and `t` falls back to the English form. Check the locale table for the plain key before choosing a plural form.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| const formKey = (key: string, args: readonly string[]) => | ||
| `${key}#other` in english ? `${key}#${rules.select(Number(args[0] ?? 0))}` : key; |
There was a problem hiding this comment.
P1: This regresses seven locales (ja, km, ko, th, vi, zh-Hans, zh-Hant) on the Apps page. formKey picks a #<category> key whenever the English table has #other, and never checks whether the locale table has the plain key instead. Those locales have no plural variations, so their translation is stored under the plain key and t falls back to the English form. Check the locale table for the plain key before choosing a plural form.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/pages/shared/i18n.ts, line 61:
<comment>This regresses seven locales (ja, km, ko, th, vi, zh-Hans, zh-Hant) on the Apps page. `formKey` picks a `#<category>` key whenever the English table has `#other`, and never checks whether the locale table has the plain key instead. Those locales have no plural variations, so their translation is stored under the plain key and `t` falls back to the English form. Check the locale table for the plain key before choosing a plural form.</comment>
<file context>
@@ -55,10 +57,13 @@ export function createStrings(table: StringTable, tags: readonly string[] = navi
const english = table.en ?? {};
const t = (key: string) => local[key] ?? english[key] ?? key;
+ const rules = new Intl.PluralRules(language);
+ const formKey = (key: string, args: readonly string[]) =>
+ `${key}#other` in english ? `${key}#${rules.select(Number(args[0] ?? 0))}` : key;
return {
</file context>
| const formKey = (key: string, args: readonly string[]) => | |
| `${key}#other` in english ? `${key}#${rules.select(Number(args[0] ?? 0))}` : key; | |
| const formKey = (key: string, args: readonly string[]) => { | |
| const form = `${key}#${rules.select(Number(args[0] ?? 0))}`; | |
| return form in local || (!(key in local) && form in english) ? form : key; | |
| }; |
| "other" : { | ||
| "stringUnit" : { | ||
| "state" : "needs_review", | ||
| "value" : "يتوفر %@ تحديث" |
There was a problem hiding this comment.
P3: The Arabic and Bosnian other forms do not keep the existing translations as the PR description says. Arabic moves the old "يتوفر %@ تحديثًا" to many and writes a new other; Bosnian replaces "Dostupno ažuriranja: %@" with a reworded other. Either keep the existing text as other or update the PR description to match.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApps/Resources/Localizable.xcstrings, line 14985:
<comment>The Arabic and Bosnian `other` forms do not keep the existing translations as the PR description says. Arabic moves the old "يتوفر %@ تحديثًا" to `many` and writes a new `other`; Bosnian replaces "Dostupno ažuriranja: %@" with a reworded `other`. Either keep the existing text as `other` or update the PR description to match.</comment>
<file context>
@@ -14947,51 +14947,195 @@
+ "other" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "يتوفر %@ تحديث"
+ }
+ }
</file context>
| "few" : { | ||
| "stringUnit" : { | ||
| "state" : "needs_review", | ||
| "value" : "Доступно оновлення: %@" |
There was a problem hiding this comment.
P3: The few form renders Доступно оновлення: 2, which mismatches the plural subject. Use Доступні оновлення: %@ for this category.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApps/Resources/Localizable.xcstrings, line 15297:
<comment>The `few` form renders `Доступно оновлення: 2`, which mismatches the plural subject. Use `Доступні оновлення: %@` for this category.</comment>
<file context>
@@ -15043,15 +15265,51 @@
+ "few" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "Доступно оновлення: %@"
+ }
+ },
</file context>
| "value" : "Доступно оновлення: %@" | |
| "value" : "Доступні оновлення: %@" |
| "few" : { | ||
| "stringUnit" : { | ||
| "state" : "needs_review", | ||
| "value" : "Доступно обновления: %@" |
There was a problem hiding this comment.
P3: The few form renders Доступно обновления: 2, which mismatches the plural noun. Use Доступны обновления: %@ for this category.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApps/Resources/Localizable.xcstrings, line 15243:
<comment>The `few` form renders `Доступно обновления: 2`, which mismatches the plural noun. Use `Доступны обновления: %@` for this category.</comment>
<file context>
@@ -15013,27 +15157,105 @@
+ "few" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "Доступно обновления: %@"
+ }
+ },
</file context>
| "value" : "Доступно обновления: %@" | |
| "value" : "Доступны обновления: %@" |
|
Review (codex gpt-6.1-sol xhigh, router-review): BLOCK at 683581d BLOCK at 683581d Visual verdict: no images; Leo tries the build
Follow-ups: none |
….format falls back to a plain string before English Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-5de8df9724303a872254
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Gallery before/after for
States
Pick other states with a Not verified: macOS jobs did not run: compile and app tests were skipped on CI passes on Written by Shipped in:
|
|
Review (codex, router-review): SHIP at 25e2a4c Reviewer: codex gpt-6.1-sol xhigh, fast tier. SHIP at 25e2a4c Visual verdict: no images; Leo tries the build No correctness findings. Apps string generation produced all six plural categories for all 21 locales. Focused Bun tests were unavailable because Follow-ups: none |
|
Lead (perf lead agent): reverted by #30492. The plural form of store.updates.available uses %@ (and the Arabic zero/one/two forms have no placeholder), so the CmuxNext build fails on the feat tip: 21 "Plural variation requires referencing the number in the string" errors in CmuxNextApps/Resources/Localizable.xcstrings (fleet job 02a3c4719405abab16e779fc). To re-land: pass the count as an integer and use %lld in every plural form (each locale), or use two plain strings for one and for more than one. |
#30288) (#30493) * fix(apps): Arabic zero/one/two forms of store.updates.available reference the number (base red from #30288) Xcode's string catalog compiler refuses a plural variation that doesn't reference its number ("Plural variation requires referencing the number in the string", ar: store.updates.available), so the CmuxNext package test build failed at e6bfd26 and every Swift suite run after it went red before any test ran. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3a05ae6e302bba4a7be8 * fix(apps): store.updates.available's plural forms use %lld, which the page formatter now fills Xcode's catalog compiler wants an integer specifier in every plural variation, not %@: the package build failed on ar, then on bs, for every language of the key. formatString substitutes %d, %ld and %lld (and %N$lld) like %@. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3a05ae6e302bba4a7be8 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… (highlights, capabilities, step coverage, arm64 recorder) (#30624) * base red: tooling-tests, register test_ci_blacksmith_linux_sizes.py as manual and the unregistered tests tooling-tests failed on every feat-cmux-next pull request at "validate_test_execution_registry.py --pr-lanes feat-cmux-next": tests/test_ci_blacksmith_linux_sizes.py: linux-guard lane is not run by any workflow #30360 (d57f2fc) registered the test as linux-guard but added no ci-guards.yml step that runs it. CLAUDE.md forbids new CI tests (Lawrence 2026-10-23), so it becomes a manual entry with how to run it. The registry also warned about six tests with no entry. test_nightly_dedicated_mini.py is already run by ci-guards.yml, so it takes linux-guard; the other five are run by no workflow and enter as manual, each with its own run command. Red at 03423de: python3 scripts/ci/validate_test_execution_registry.py --pr-lanes feat-cmux-next --base-sha HEAD^1 exit 1 Green here: same command, exit 0 (594 tests, manual=31). * base red: Checks job structure, the preflight tests read the preflight step by id tests/test_cmux_next_checks_workflow.py read the preflight script as the last step of cmux-tui-artifacts.yml's tree-preflight job. #30221 (5bdd4c9) added "Record tree owner observation" and an upload step after it, so 14 tests failed with KeyError: 'run' on every pull request. They now take the step by its id (push-head-preflight's `head`, tree-preflight's `inspect`); the assertions do not change. Red at 03423de: python3 tests/test_cmux_next_checks_workflow.py FAILED (errors=14) Green here: same command, 165 tests OK. * base red: Tier routing, an agent pane on a dev server URL loads its own document test_no_suite_runs_page_javascript_on_a_built_bundle flagged AgentPageHibernationTests.swift after #30312 (b16b20c) made it call waitUntilLoaded. That suite's agent pane loads AgentPaneSource.devURLVariable, a loopback port nobody serves (Debug test builds honor it), not the built bundle, so it reads no bundle content. The check now counts AgentPaneSource.devURLVariable as a suite's own document, beside loadHTMLString and the fixture roots. Red at 03423de: python3 tests/test_cmux_next_route.py FAILED (failures=1) Green here: same command, 112 tests OK. * base red: daemon capabilities export lists agent-session-start-no-cwd-v1 #30363 (efd30f5) added the optional capability agent-session-start-no-cwd-v1 to DaemonEndpoint.swift without the export, which reddened "Daemon capabilities export is fresh" and the daemon-capabilities-fresh.test.sh script test. Regenerated with the check's own renderer (--write), as base-autofix does. Red at 03423de: python3 scripts/cmux-next/check-daemon-capabilities-fresh.py exit 1 Green here: same command, exit 0; bash scripts/cmux-next/tests/daemon-capabilities-fresh.test.sh ok. * base red: String tables, store.updates.available has English's plural forms in every language #30288 (e6bfd26) made store.updates.available a plural with `one` and `other`, but ja, km, ko, th, vi, zh-Hans and zh-Hant got only `other`, and check-l10n.sh requires English's forms in every language. Their `one` form is their `other` text, as home.stopAll.couldNotStop does. Also adds the 12 languages #27883 (16b7cc2) left out of action.sidebar.compactList.toggle (style debt CI only warns about), marked needs_review. Red at 03423de: scripts/cmux-next/check-l10n.sh . 7 errors (19 without L10N_STYLE_WARN_MAX) Green here: same command, 0 errors. * base red: release-notary, three highlights link their pull requests scripts/whats-new/test_digest.py needs a try-it action, docs or a pr link on every highlight. chat-title-plain (#29920), cloud-live-previews (#30334) and working-harness-tab-icons (#27905) had none; each now links its pull request, as #29939 did for priority-view-drag. Red at 03423de: python3 scripts/whats-new/test_digest.py FAILED (failures=1) Green here: same command, OK. * base red: release-notary, the static capability check skips agent-session-start-no-cwd-v1 #30363 (efd30f5) added agent-session-start-no-cwd-v1, which cmux-tui's identify_capabilities pushes at runtime (capabilities.rs), so the bundled static manifest never lists it. tests/test_cmux_next_static_capability_runtime_only.py (release-notary, "Validate nightly push throttle") failed, and every nightly's check-daemon-capabilities.sh --static-only would refuse the build. It joins runtime_only, as #30447 did for agent-session-settings-v1. Red at 03423de: python3 tests/test_cmux_next_static_capability_runtime_only.py FAILED (failures=1) Green here: same command, OK. * l10n: compact workspace list uses this table's word for workspace (it, pl, pt-BR, uk, th) * base red: release-notary, nightly step coverage follows #30200's visual verdict steps #30200 (86fd376) removed find-nightly-next-recovery's "Read the visual gate's verdicts" and renamed visual-gate-nightly-verdict's "Fail with the gate when it holds the build" to "Record failed advisory verdict" (same script, visual-gate-fail-when-held.sh). .github/nightly-step-coverage.json kept the old names, so tests/test_nightly_step_coverage.py failed. The stale entry goes and the renamed step keeps its plumbing entry. Red at 03423de: python3 tests/test_nightly_step_coverage.py FAILED (failures=1) Green here: same command, OK. * base red: release-notary, the nightly arm64-only recorder has the cache classifier the Release step sources #30107 (2379dd9) made nightly.yml's "Build nightly app (Release)" source scripts/cmux-next/lib/compilation-cache.sh. tests/test_nightly_next_arm64_only.py runs that step in a scratch tree with recorder executables, and the tree had no such file, so the step exited 1 before xcodebuild's recorder and the test failed with CalledProcessError. The scratch tree now gets the real file, as it does scripts/lib/rust-targets.sh. Every assertion is the same. Red at 03423de: GITHUB_STEP_SUMMARY=/tmp/s python3 tests/test_nightly_next_arm64_only.py CalledProcessError Green here: same command, PASS: nightly-next arm64 only. * base red: release-notary, nightly-next identity reads a YAML-anchored job env as its value #30278 (fe4577e) turned the publish jobs' NIGHTLY_FEEDS into a YAML anchor (`&pub-feeds ${{ needs.decide.outputs.feeds }}`) and aliases (`*pub-feeds`). tests/test_nightly_next_identity.py reads job env as raw text, so test_each_app_reads_the_feed_its_publisher_writes failed on the anchor prefix. job_env now resolves an alias to its one anchor and drops an anchor name, as YAML does; the asserted values are the same. Red at 03423de: python3 tests/test_nightly_next_identity.py 7/8 passed Green here: same command, 8/8 passed.
The Apps page's update count read "1 updates available": its string is one catalog entry for every count, and page strings (
Strings.format) had no way to pick a plural form, though the generated tables already carry them (key#<category>, gen-strings.mjs; the agent pane picks one in its owntranslate).Fix:
createStrings(...).format(key, count, ...)takes the form the language's plural rules pick for the first argument when the key is a plural string (itskey#otheris in the table); other keys format as before.store.updates.availablebecomes a plural catalog entry with every category each of the 21 locales' rules use (English "1 update available" / "N updates available"; Arabic, Bosnian, Polish, Russian and Ukrainian get their few and many forms; Romance languages theirmany). The existing translations stay as each locale'sother; the new forms are markedneeds_reviewlike the rest of the entry.Tests:
src/pages/shared/i18n.test.ts(English picks one and other, Russian one, few, many and 21 as one, a non-plural key formats as before); the Apps page and the generated tables still build.Not changed: other page strings that carry a count have none today (this is the only
%@ thingsentry in this catalog).Risk: low. Only a plural key's text changes; the new translations need a native-speaker pass, which
needs_reviewrecords.🤖 Generated with Claude Code
Agent session: agent session
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Fixes the Apps page so "1 updates available" reads "1 update available" and other counts use the correct plural form per locale.
createStrings(...).format()now picks the plural form the language's rules select for the count when the key is a plural entry (key#otherexists in the table); non-plural keys format as before.store.updates.availablebecomes a plural catalog entry with forms for all categories across the 21 locales; existing translations are kept asother, new forms are markedneeds_review.Written for commit 683581d. Summary will update on new commits.