diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 38ce205..7502f4c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -434,3 +434,21 @@ By contributing, you agree that your contributions will be licensed under the [M Open a [Discussion](../../discussions) or file an issue. We're happy to help you get started! > 💡 **This document is in English only.** Want to translate it into your language? That PR is welcome too! + +## Native language reviewers + +The premium dictionaries are curated and LLM-audited, but **no locale has had +a native-speaker pass yet** — and that's the highest-leverage contribution a +non-coder can make here. One pass takes ~1–2 hours: + +1. Open `src/data/.json`. Every key is the English source string; + every value is the translation. Skim for wrong meaning, unnatural register, + brand terms that should stay English (Claude, Anthropic, MCP, Claude Code, + Managed Agents, …), and the same English term rendered inconsistently. +2. Send corrections as a PR (JSON value edits only — CI guards the structure, + so you cannot break anything), or just open an issue listing them. +3. Your locale's `_meta.nativeReview` flips to `"reviewed"` and you're credited + in the README's Terminology QA table. + +Claim a locale on [issue #202](https://github.com/heznpc/skillBridge/issues/202). +The QA model around your review is described in `docs/TRANSLATION_QA.md`. diff --git a/README.md b/README.md index 936b29e..5f4d6b3 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,44 @@ The full "things we will not do" list is kept public on purpose in [POSITIONING. > Want to add your language as Premium? Contribute a curated dictionary — see [CONTRIBUTING.md](CONTRIBUTING.md). +### Terminology QA — how accuracy is enforced, not just promised + +New Academy content is covered by a standing pipeline, not by hand-checking: +a CI watcher polls the live catalog twice a day and **fails loudly + opens an +issue** the moment a course appears that the dictionaries don't cover; the +course gets wired into all 11 premium dictionaries; structural CI gates +(`check:i18n`, `check:dict-coverage`, `check:locales`) and a real-dictionary +regression suite guard every merge after that. Proven turnaround: on +**2026-06-10** the watcher flagged the brand-new *Claude Platform 101* course +in the morning ([#196](https://github.com/heznpc/skillBridge/issues/196)) and +all 11 locales were wired the same day +([#201](https://github.com/heznpc/skillBridge/pull/201)). + +Beyond structure, dictionary *content* goes through layered review — CI gates +catch shape/contamination drift on every PR, a full per-locale LLM audit runs +before every store release (see `docs/TRANSLATION_QA.md`), and native-speaker +review is the final layer: + + +| Language | Code | Entries | Last curated | Last LLM audit | Native review | +|---|---|---:|---|---|---| +| 한국어 | `ko` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| 日本語 | `ja` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| 中文(简体) | `zh-CN` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| 中文(繁體) | `zh-TW` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| Español | `es` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| Français | `fr` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| Italiano | `it` | 1129 | 2026-06-03 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| Deutsch | `de` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| Português (BR) | `pt-BR` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| Русский | `ru` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | +| Tiếng Việt | `vi` | 1129 | 2026-04-02 | 2026-06-10 | 🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202) | + + +🙋 **Native speakers wanted** — a first native pass on your locale takes +~1–2 hours, needs no coding, and gets you credited here. See +[#202](https://github.com/heznpc/skillBridge/issues/202). + ## Privacy & Security SkillBridge is designed with privacy first: diff --git a/docs/TRANSLATION_QA.md b/docs/TRANSLATION_QA.md new file mode 100644 index 0000000..8b901a5 --- /dev/null +++ b/docs/TRANSLATION_QA.md @@ -0,0 +1,53 @@ +# Translation QA — the three assurance layers + +SkillBridge's core promise is AI-terminology fidelity. That promise is backed +by three layers, each catching what the previous one structurally cannot. +This doc is honest about what each layer does **and does not** guarantee. + +## Layer 1 — automated, every PR (CI) + +| Gate | Catches | +|---|---| +| `check:i18n` / `check:dict-coverage` | Key/section parity across all 11 dictionaries, `_meta.version` sync with the manifest | +| `check:locales` | Cross-locale contamination — any locale sharing >8% of its long strings with another (the bug class behind the 2026-06 "Italian was 51% Spanish" incident) | +| `check:glossary` / `validate` | `_protected` structure, value types, possibly-untranslated heuristics | +| `tests/protected-terms.test.js` | Real-dictionary regression: ordinary prose in **all 11 locales** must survive protected-term restoration uncorrupted | +| Academy drift watcher (cron, 2×/day) | A live course slug missing from `FLASHCARD_COURSE_MAP` — fails the run and auto-opens an issue | + +**Does not catch:** a translation that is structurally valid but semantically +wrong ("uniquely wrong" values — e.g. `Slack → "Lento"` survived every +structural gate until Layer 2 found it). + +## Layer 2 — full LLM audit, every store release + +Before each store submission, every premium dictionary gets a full-depth LLM +review (one reviewer per locale over all ~1,100 entries, findings re-verified +against the file before fixing — reviewer claims are *not* trusted blindly). +The completed audit is stamped machine-readably in each dictionary: +`_meta.lastAudited`. The README QA table is generated from that field by +`npm run docs`, so the public table cannot drift from reality. + +The 2026-06-10 audit fixed 89 verified errors across 7 locales (brand-name +mistranslations, a `responsskill` regex scar, "AI Fluency" rendered three +different ways in one file, …) — see PRs #197/#199. + +**Does not catch:** native-register subtleties and domain idiom an LLM judges +imperfectly. That's Layer 3. + +## Layer 3 — native-speaker review (recruiting) + +One native reviewer per locale does a first full pass (~1–2 hours, JSON value +edits only; CI guards the structure so nothing can break). On completion the +locale's `_meta.nativeReview` flips from `"recruiting"` to `"reviewed"` and the +reviewer is credited in the README QA table. + +Status and sign-up: [issue #202](https://github.com/heznpc/skillBridge/issues/202). +No locale has completed a native pass yet — the table says so honestly rather +than implying review that hasn't happened. + +## Why there is no paid review / API gate + +SkillBridge is free forever for both users and the maintainer — no paid +translation-QA API can sit in CI. The layered design above is the strongest +assurance available under that constraint: structure by machine, semantics by +release-time LLM audit, idiom by community native review. diff --git a/scripts/generate-docs.js b/scripts/generate-docs.js index c7b60e2..bd0c121 100644 --- a/scripts/generate-docs.js +++ b/scripts/generate-docs.js @@ -11,6 +11,7 @@ * ... * ... * ... + * ... (per-locale QA table from src/data/*.json _meta) * * Usage: node scripts/generate-docs.js */ @@ -126,6 +127,40 @@ function buildLangTagsHtml(langs) { // The script only updates version/lang counts/premium list; feature cards are maintained // manually but wrapped in markers so they *could* be generated in the future. +// Per-locale QA table — generated from each dictionary's _meta so the README +// table can never drift from the machine-readable QA state. `lastAudited` is +// stamped by the pre-release LLM dictionary audit (see RELEASE_CHECKLIST step 0); +// `nativeReview` flips to "reviewed" when a native speaker completes a pass +// (recruiting: issue #202). +function countLeaves(obj) { + let n = 0; + for (const [k, v] of Object.entries(obj)) { + if (k === '_meta' || k === '_protected') continue; + if (typeof v === 'string') n++; + else if (v && typeof v === 'object' && !Array.isArray(v)) n += countLeaves(v); + } + return n; +} +function buildLocaleQaTable() { + const rows = []; + for (const { code, label } of premiumLangs) { + const dict = JSON.parse(fs.readFileSync(path.join(ROOT, `src/data/${code}.json`), 'utf8')); + const m = dict._meta || {}; + const review = + m.nativeReview === 'reviewed' + ? '✅ reviewed' + : '🙋 [recruiting](https://github.com/heznpc/skillBridge/issues/202)'; + rows.push( + `| ${label} | \`${code}\` | ${countLeaves(dict)} | ${m.lastUpdated || '—'} | ${m.lastAudited || '—'} | ${review} |`, + ); + } + return [ + '| Language | Code | Entries | Last curated | Last LLM audit | Native review |', + '|---|---|---:|---|---|---|', + ...rows, + ].join('\n'); +} + // --------------------------------------------------------------------------- // 4. Replace between markers // --------------------------------------------------------------------------- @@ -156,6 +191,7 @@ let readme = fs.readFileSync(readmePath, 'utf8'); readme = replaceBetweenMarkers(readme, 'VERSION', `v${version}`); readme = replaceBetweenMarkers(readme, 'LANG_COUNT', `${translatedCount} languages`); +readme = replaceBetweenMarkers(readme, 'LOCALE_QA', '\n' + buildLocaleQaTable() + '\n'); fs.writeFileSync(readmePath, readme, 'utf8'); diff --git a/src/data/de.json b/src/data/de.json index 263bbe1..7f5338f 100644 --- a/src/data/de.json +++ b/src/data/de.json @@ -3,7 +3,9 @@ "lang": "de", "langName": "Deutsch", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/src/data/es.json b/src/data/es.json index acad211..4fed20b 100644 --- a/src/data/es.json +++ b/src/data/es.json @@ -3,7 +3,9 @@ "lang": "es", "langName": "Español", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/src/data/fr.json b/src/data/fr.json index 5cdc61c..294395e 100644 --- a/src/data/fr.json +++ b/src/data/fr.json @@ -3,7 +3,9 @@ "lang": "fr", "langName": "Français", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/src/data/it.json b/src/data/it.json index 19dc25c..855b644 100644 --- a/src/data/it.json +++ b/src/data/it.json @@ -4,7 +4,9 @@ "langName": "Italiano", "version": "3.5.40", "lastUpdated": "2026-06-03", - "translation_provenance": "Re-translated from the English source via Google Translate (2026-06), with brand/technical terms restored to canonical English; Italian↔Spanish overlap is now 0.1% (parity with the other 10 locales). Earlier v1 was a Spanish-derived regex transform — fully replaced. Structural keys must remain identical to other premium dictionaries (enforced by scripts/check-dict-coverage.js); native Italian PRs welcome." + "translation_provenance": "Re-translated from the English source via Google Translate (2026-06), with brand/technical terms restored to canonical English; Italian↔Spanish overlap is now 0.1% (parity with the other 10 locales). Earlier v1 was a Spanish-derived regex transform — fully replaced. Structural keys must remain identical to other premium dictionaries (enforced by scripts/check-dict-coverage.js); native Italian PRs welcome.", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/src/data/ja.json b/src/data/ja.json index 38a6b0a..88f84e4 100644 --- a/src/data/ja.json +++ b/src/data/ja.json @@ -3,7 +3,9 @@ "lang": "ja", "langName": "日本語", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/src/data/ko.json b/src/data/ko.json index 5fd7b91..1fefa96 100644 --- a/src/data/ko.json +++ b/src/data/ko.json @@ -3,7 +3,9 @@ "lang": "ko", "langName": "한국어", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic 아카데미", diff --git a/src/data/pt-BR.json b/src/data/pt-BR.json index ac5df4b..3b88eaf 100644 --- a/src/data/pt-BR.json +++ b/src/data/pt-BR.json @@ -3,7 +3,9 @@ "lang": "pt-BR", "langName": "Português (BR)", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/src/data/ru.json b/src/data/ru.json index 952713d..bc1b2af 100644 --- a/src/data/ru.json +++ b/src/data/ru.json @@ -3,7 +3,9 @@ "lang": "ru", "langName": "Русский", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/src/data/vi.json b/src/data/vi.json index 9c694fb..b76382c 100644 --- a/src/data/vi.json +++ b/src/data/vi.json @@ -3,7 +3,9 @@ "lang": "vi", "langName": "Tiếng Việt", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/src/data/zh-CN.json b/src/data/zh-CN.json index 4d3ce68..e4c3f59 100644 --- a/src/data/zh-CN.json +++ b/src/data/zh-CN.json @@ -3,7 +3,9 @@ "lang": "zh-CN", "langName": "简体中文", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/src/data/zh-TW.json b/src/data/zh-TW.json index e306138..0e0b992 100644 --- a/src/data/zh-TW.json +++ b/src/data/zh-TW.json @@ -3,7 +3,9 @@ "lang": "zh-TW", "langName": "中文(繁體)", "version": "3.5.40", - "lastUpdated": "2026-04-02" + "lastUpdated": "2026-04-02", + "lastAudited": "2026-06-10", + "nativeReview": "recruiting" }, "ui": { "Anthropic Academy": "Anthropic Academy", diff --git a/store-assets/RELEASE_CHECKLIST.md b/store-assets/RELEASE_CHECKLIST.md index be0b2de..17e8485 100644 --- a/store-assets/RELEASE_CHECKLIST.md +++ b/store-assets/RELEASE_CHECKLIST.md @@ -41,6 +41,15 @@ Everything code-side is ready and pre-built. ## What needs your hands +### 0. Pre-release dictionary audit (LLM) — release convention + +Before every store submission, run the full per-locale LLM dictionary audit +(one reviewer per premium locale over all entries; re-verify every finding +against the file before fixing — the 2026-06-10 audit caught `Slack → "Lento"` +-class errors that all structural gates miss). After fixes land, stamp each +dictionary's `_meta.lastAudited` and run `npm run docs` so the README QA table +reflects it. Three-layer QA model: `docs/TRANSLATION_QA.md`. + ### 1. Icon — resolved Status: **resolved**. The non-infringing icon shipped in v3.5.35 (a rising