From 9b008a961effd42c39673339662b95b536244a99 Mon Sep 17 00:00:00 2001 From: J Date: Tue, 21 Jul 2026 19:34:11 +0900 Subject: [PATCH] fix(frontend): require string externalization for multi-locale targets An end-to-end build against issue #28 (EQ Preset Studio, Qt Quick, github.com/beefiker/qt-audio-eq) reproduced complaint 3: the skill shipped hardcoded single-locale copy for an open-source distribution target and even recorded the mismatched language in visual QA without treating it as a finding. The contract covered localization only as layout survival (CJK/RTL/long translations), never as a string externalization obligation. Name the obligation in the shared i18n contract and the Qt checklist: distribution-targeted surfaces ship every user-visible string through the platform translation mechanism from the first artifact, and a shipped language that differs from the brief's records its locale decision. Pin both in the contract test and bump to 0.12.1. --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- skills/superloopy-frontend/references/qt.md | 1 + skills/superloopy-frontend/references/ux.md | 2 +- test/frontend-ux-contract.test.js | 10 ++++++++++ 8 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 563e307..dce73d7 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ "name": "superloopy", "source": "./", "description": "Strict evidence loop harness: repo-local loop state, success criteria, append-only ledgers, evidence-gated completion, plus skills (loop, research, frontend, clone) and a read-only/worker crew. Works on Claude Code and Codex from one repo.", - "version": "0.12.0", + "version": "0.12.1", "license": "MIT", "keywords": [ "claude-code", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 01a89f9..f6ede93 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "superloopy", - "version": "0.12.0", + "version": "0.12.1", "description": "Lightweight loop harness with strict evidence gates — Claude Code edition.", "homepage": "https://github.com/beefiker/superloopy#readme", "repository": "https://github.com/beefiker/superloopy", diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 4f41fc7..0a52dae 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "superloopy", - "version": "0.12.0", + "version": "0.12.1", "description": "Lightweight loop harness with strict evidence gates.", "homepage": "https://github.com/beefiker/superloopy#readme", "repository": "https://github.com/beefiker/superloopy", diff --git a/package-lock.json b/package-lock.json index 6773744..e2190ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "superloopy", - "version": "0.12.0", + "version": "0.12.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "superloopy", - "version": "0.12.0", + "version": "0.12.1", "license": "MIT", "bin": { "superloopy": "src/cli.js" diff --git a/package.json b/package.json index 7d411ef..ff1f58e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "superloopy", - "version": "0.12.0", + "version": "0.12.1", "description": "A lightweight loop harness for Codex and Claude Code with strict evidence gates.", "type": "module", "bin": { diff --git a/skills/superloopy-frontend/references/qt.md b/skills/superloopy-frontend/references/qt.md index 8baccc0..752bbb1 100644 --- a/skills/superloopy-frontend/references/qt.md +++ b/skills/superloopy-frontend/references/qt.md @@ -63,6 +63,7 @@ For Qt-owned non-Web pixels, this checklist replaces the Web anti-slop, browser- - [ ] Newer APIs have guards, a fallback, and a testable boundary. - [ ] The existing design source remains authoritative; any `DESIGN.md` is either that established source or a synchronized scoped mapping, and app semantics do not freeze the system palette, platform font, native metrics, or accessibility preferences. - [ ] Every supported input path (pointer, keyboard, touch, pen, switch, or assistive action as applicable) reaches the same truthful semantic outcome. +- [ ] User-visible strings pass through `tr()`/`qsTr()` with a translation setup when the target implies more than one locale; a mismatched shipped language records its locale decision. - [ ] CJK, RTL, emoji/font fallback, long translations, and enlarged text remain usable. - [ ] High-DPI geometry and assets are correct across each target-supported DPR and display transition; unsupported transitions are recorded as not applicable. - [ ] Motion preferences and target-native system chrome are preserved or an explicit replacement is fully verified. diff --git a/skills/superloopy-frontend/references/ux.md b/skills/superloopy-frontend/references/ux.md index 0da7a1f..b533980 100644 --- a/skills/superloopy-frontend/references/ux.md +++ b/skills/superloopy-frontend/references/ux.md @@ -139,7 +139,7 @@ Use consequence-scaled error handling. Preserve input, identify the specific cor ## Internationalization and bidirectionality -Internationalization begins during discovery. Avoid display-string concatenation; use locale-aware formatting, sorting, searching, pluralization, and input. Carry language and direction metadata, and define whether a product language override follows the system, persists locally, syncs, or is unavailable. +Internationalization begins during discovery. When the brief, audience, or distribution intent implies more than one locale (public release, open-source distribution, app-store or marketplace delivery), every user-visible string ships through the platform's translation mechanism (`tr()`/`qsTr()`, resource bundles, message catalogs) from the first artifact; hardcoded single-locale copy in a distribution-targeted surface is a defect, not a follow-up. When copy ships in a language different from the brief's language, record the locale decision and its reason instead of defaulting silently. Avoid display-string concatenation; use locale-aware formatting, sorting, searching, pluralization, and input. Carry language and direction metadata, and define whether a product language override follows the system, persists locally, syncs, or is unavailable. Test representative long text, unbroken content, bidirectional content, and taller-script content without assuming one universal expansion percentage. Mirroring is selective: media controls, charts, maps, and user-authored direction may not mirror with surrounding navigation. diff --git a/test/frontend-ux-contract.test.js b/test/frontend-ux-contract.test.js index 84c05ad..dd4594d 100644 --- a/test/frontend-ux-contract.test.js +++ b/test/frontend-ux-contract.test.js @@ -535,3 +535,13 @@ test("new UX references and contract tests are inventoried as original Superloop assert.match(fileAudit, /references\/ux\.md.*Superloopy-native.*original prose/is); assert.match(golden, /test\/frontend-ux-contract\.test\.js.*node --test/is); }); + +test("i18n contract requires string externalization for distribution-targeted surfaces", async () => { + const ux = await read(reference("ux")); + const qt = await read(reference("qt")); + + assert.match(ux, /more than one locale.*every user-visible string ships through the platform's translation mechanism/is); + assert.match(ux, /hardcoded single-locale copy in a distribution-targeted surface is a defect, not a follow-up/iu); + assert.match(ux, /language different from the brief's language.*record the locale decision/is); + assert.match(qt, /strings pass through `tr\(\)`\/`qsTr\(\)` with a translation setup/iu); +});