Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<your-locale>.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`.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

<!-- LOCALE_QA_START -->
| 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) |
<!-- LOCALE_QA_END -->

🙋 **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:
Expand Down
53 changes: 53 additions & 0 deletions docs/TRANSLATION_QA.md
Original file line number Diff line number Diff line change
@@ -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.
36 changes: 36 additions & 0 deletions scripts/generate-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* <!-- LANG_COUNT_START -->...<!-- LANG_COUNT_END -->
* <!-- PREMIUM_LANGS_START -->...<!-- PREMIUM_LANGS_END -->
* <!-- FEATURES_START -->...<!-- FEATURES_END -->
* <!-- LOCALE_QA_START -->...<!-- LOCALE_QA_END --> (per-locale QA table from src/data/*.json _meta)
*
* Usage: node scripts/generate-docs.js
*/
Expand Down Expand Up @@ -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
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -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');

Expand Down
4 changes: 3 additions & 1 deletion src/data/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/data/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/data/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/data/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/data/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/data/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 아카데미",
Expand Down
4 changes: 3 additions & 1 deletion src/data/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/data/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/data/vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/data/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion src/data/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
9 changes: 9 additions & 0 deletions store-assets/RELEASE_CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading