-
Notifications
You must be signed in to change notification settings - Fork 3
DF-833: language prioritization, rollout playbook + i18n/ index #212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: sonyapark-df-831-m3-build-ci-delta-translate-on-merge-to-main-open-pr
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,152 @@ | ||||||||||||||||||||
| # M4 · Language prioritization and rollout playbook | ||||||||||||||||||||
|
|
||||||||||||||||||||
| **Linear:** [DF-833](https://linear.app/mixpanel/issue/DF-833), [DF-834](https://linear.app/mixpanel/issue/DF-834) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| --- | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Which language is next | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### The signal Mixpanel already has | ||||||||||||||||||||
|
|
||||||||||||||||||||
| `mixpanel.com` publishes `hreflang` alternates for exactly four locales: | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ``` | ||||||||||||||||||||
| en-US ko-KR ja-JP id-ID | ||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Marketing has already decided which non-English markets are worth translating for, and paid for it. Docs | ||||||||||||||||||||
| should follow that list rather than invent a new one — same markets, same sales motion, same customers. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| (Note for whoever picks this up: `mixpanel.com/ko/` currently returns 200 but serves English. The alternate | ||||||||||||||||||||
| is declared, the content is not there yet. Worth flagging to whoever owns the marketing site — and worth | ||||||||||||||||||||
| knowing that a Korean docs launch may land before a Korean marketing site.) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Recommended order | ||||||||||||||||||||
|
|
||||||||||||||||||||
| | # | Locale | Why | Confidence | | ||||||||||||||||||||
| | --- | --- | --- | --- | | ||||||||||||||||||||
| | 1 | `ko` | In flight. Marketing-declared market. | — | | ||||||||||||||||||||
| | 2 | `ja` | Marketing-declared; large enterprise analytics market; Mintlify supports `ja` and `ja-JP` | high | | ||||||||||||||||||||
| | 3 | `id` | Marketing-declared. Verify with docs traffic first — a declared alternate is weaker evidence than pageviews | medium | | ||||||||||||||||||||
| | 4 | `es` / `pt-BR` | Large developer populations, no marketing signal yet. Only on traffic evidence | low | | ||||||||||||||||||||
| | 5 | `de` / `fr` | Strong English proficiency among the developer audience; usually the lowest-yield locales for developer docs | low | | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Confirm with data before locale 3 | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Do not extend past `ja` on inference. `docs.json` already reports into a Mixpanel project | ||||||||||||||||||||
| (`integrations.mixpanel.projectToken: "metrics-1"`), so the answer is a report away: | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - Docs pageviews broken down by `$country_code` / browser language, over 90 days | ||||||||||||||||||||
| - Trend of `/ko` pageviews after Korean launch — the actual measured lift from localizing, which is the | ||||||||||||||||||||
| only honest input to "is the next language worth it" | ||||||||||||||||||||
| - Whether non-English-locale readers reach `/reference` at all → settles | ||||||||||||||||||||
| [wave 2](02-scope-and-preservation-spec.md#waves) for every language at once | ||||||||||||||||||||
|
|
||||||||||||||||||||
| One report answers the prioritization question for the rest of the project. We sell this product; we should | ||||||||||||||||||||
| use it. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| --- | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Per-language rollout playbook | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Once Korean is live and CI is running, a new language should be a checklist, not a project. Budget **~1 week**, | ||||||||||||||||||||
| most of it native review. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### 1 · Configure — ~1 hour | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ```bash | ||||||||||||||||||||
| # translate the 128 nav labels for the new locale | ||||||||||||||||||||
| cp i18n/nav/labels.ko.json i18n/nav/labels.<locale>.json | ||||||||||||||||||||
| # ...translate the values... | ||||||||||||||||||||
|
|
||||||||||||||||||||
| node i18n/scripts/build-locale-nav.mjs <locale> # → i18n/nav/<locale>.json | ||||||||||||||||||||
| node i18n/scripts/build-locale-nav.mjs <locale> --check # must exit 0 | ||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Then add the locale to `docs.json` (`{ "$ref": "./<locale>.json" }`) and to the engine's target list | ||||||||||||||||||||
| (`gt.config.json` → `locales`, or the Mintlify dashboard). | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Confirm the code is on [Mintlify's supported list](https://www.mintlify.com/docs/guides/internationalization) | ||||||||||||||||||||
| first. `ko`, `ja`/`ja-JP`, `id`, `es`, `pt-BR`, `de`, `fr`, `zh-Hans`, `zh-Hant` all are. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### 2 · Extend the glossary — ~half a day | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ```bash | ||||||||||||||||||||
| cp i18n/glossary/ko.md i18n/glossary/<locale>.md | ||||||||||||||||||||
| # ...translate the term column; keep the section structure and the Never-translate list... | ||||||||||||||||||||
| node i18n/scripts/build-glossary.mjs <locale> | ||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| **The do-not-translate list and the pitfalls carry over unchanged** — "retention" is ambiguous in every | ||||||||||||||||||||
| language, and brand names are brand names everywhere. What changes is the target column and the | ||||||||||||||||||||
| language-specific note at the end of `glossary/ko.md` (Korean particles; for Japanese, katakana vs kanji for | ||||||||||||||||||||
| feature names; for German, compound-noun length in the sidebar). | ||||||||||||||||||||
|
|
||||||||||||||||||||
| **Do this before bulk translation, not after.** Fixing terminology across 295 already-translated pages costs | ||||||||||||||||||||
| far more than deciding it once. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### 3 · Translate — hours, unattended | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ```bash | ||||||||||||||||||||
| npx gtx-cli translate --config gt.config.json # or: Mintlify dashboard → add locale | ||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| 352,500 words in the wave-1 tier. Half of it is in five sections, and `docs/tracking-methods` (91.5k words, | ||||||||||||||||||||
| 68 files) repeats the same headings across every SDK — so translation memory makes the big block the cheap | ||||||||||||||||||||
| block. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### 4 · Validate — minutes | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ```bash | ||||||||||||||||||||
| node i18n/scripts/validate-mdx-parity.mjs \ | ||||||||||||||||||||
| --source docs --target <locale>/docs \ | ||||||||||||||||||||
| --locale <locale> --link-prefix --require-anchors --allow-code-comments | ||||||||||||||||||||
|
Comment on lines
+101
to
+103
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a rollout contains both docs and guides, this step validates only
Suggested change
|
||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Must exit 0. Non-negotiable, and it is not a matter of judgement — if it fails, the engine is misconfigured, | ||||||||||||||||||||
| not the translation. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### 5 · Native review — the real cost, ~3–5 days | ||||||||||||||||||||
|
|
||||||||||||||||||||
| This is the only step that does not compress. Sequence it so terminology is fixed before volume: | ||||||||||||||||||||
|
|
||||||||||||||||||||
| 1. Quickstart + intro pages (~6k words) — sets the vocabulary for everything after | ||||||||||||||||||||
| 2. Conceptual core: data model, reports, boards, users | ||||||||||||||||||||
| 3. `docs/tracking-methods` as **one batch with one reviewer** — 68 files of near-identical headings; split | ||||||||||||||||||||
| it across reviewers and the inconsistency will be visible on adjacent pages | ||||||||||||||||||||
| 4. The rest | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### 6 · Ship and watch | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Merge, confirm in `mintlify dev`, then check the language switcher round-trips and internal links stay | ||||||||||||||||||||
| inside the locale. Two weeks later, pull pageviews for the new prefix — that number is the input to whether | ||||||||||||||||||||
| locale N+1 happens. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| --- | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Reviewing a translation PR | ||||||||||||||||||||
|
|
||||||||||||||||||||
| For reviewers landing on an automated PR from [`config/translate.yml`](config/translate.yml). Structure is | ||||||||||||||||||||
| already machine-verified — **your job is only whether the language is right.** | ||||||||||||||||||||
|
|
||||||||||||||||||||
| 1. **Terminology** — spot-check against [`glossary/ko.csv`](glossary/ko.csv). Grep the PR for the five | ||||||||||||||||||||
| highest-frequency terms and confirm one rendering each: 이벤트 / 속성 / 코호트 / 보드 / 사용자. | ||||||||||||||||||||
| 2. **The `retention` trap** — 리텐션 (the report) and 데이터 보관 (the policy) are different words. Machines | ||||||||||||||||||||
| collapse them. Highest-yield single check in the review. | ||||||||||||||||||||
| 3. **Particles after Latin words** — `Mixpanel을` not `Mixpanel를`, `SDK를` not `SDK을`. Chosen by how the | ||||||||||||||||||||
| Latin word is *read aloud* in Korean. Frequent, visible, and consistently gotten wrong by machines. | ||||||||||||||||||||
| 4. **Register** — `-합니다 / -하세요`. Flag any drift into 해요체. | ||||||||||||||||||||
| 5. **Untranslated leftovers** — search the diff for runs of English prose outside code and brand names. | ||||||||||||||||||||
| 6. **Render it** — `mintlify dev` on two or three changed pages. Korean is denser than English; check that | ||||||||||||||||||||
| sidebar labels, `<Card>` grids, and table cells still lay out. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| What you do **not** need to check, because CI already did: JSX components, code fences, imports, image | ||||||||||||||||||||
| paths, link targets, heading anchors, frontmatter keys. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| --- | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Exit criteria for M4 | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - [ ] Locale 2 (`ja`, most likely) live and maintained by the same CI as `ko`, with no new pipeline | ||||||||||||||||||||
| - [ ] Adding it required only the steps above — any deviation gets folded back into this playbook | ||||||||||||||||||||
| - [ ] Docs traffic by locale is a report someone can pull on demand, and it drives locale 3 | ||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| # Docs localization (Mintlify i18n) | ||
|
|
||
| Working directory for [Docs Localization (Mintlify i18n)](https://linear.app/mixpanel/project/docs-localization-mintlify-i18n-25fabd3b5c76) — shipping `docs.mixpanel.com` in Korean, then scaling to more languages. | ||
|
|
||
| **Nothing here is live.** `docs.json` is untouched, no workflow is installed, and no `ko/` content tree | ||
| exists at the repo root. Everything is staged so the switch-on is a deliberate, reviewable step. | ||
|
|
||
| --- | ||
|
|
||
| ## Read this first | ||
|
|
||
| Two findings change the shape of the project: | ||
|
|
||
| **1. Mintlify sells the pipeline we were about to build.** Native AI translations (Pro/Enterprise) already | ||
| bulk-translates docs and opens a PR per change, on a continuous schedule, never publishing without a human | ||
| merge — which is M2's bulk translation plus all of M3. Every observable signal says Mixpanel is on | ||
| Enterprise. **Before DF-828 starts, someone should open the Mintlify dashboard → Settings → Translations.** | ||
| → [`01-plan-and-entitlements.md`](01-plan-and-entitlements.md) | ||
|
|
||
| **2. 710 internal links will break silently on translation.** Every heading anchor in this repo derives from | ||
| its English text; translate the heading and the anchor changes, and 710 deep links land at the top of the | ||
| page instead of the right section. No error, nothing in the diff. The fix is mechanical — pin | ||
| `## 제목 [#english-anchor]` — but the engine has to do it, and not every engine can. | ||
| → [`02-scope-and-preservation-spec.md`](02-scope-and-preservation-spec.md#heading-anchors--the-failure-nobody-sees-in-review) | ||
|
|
||
| --- | ||
|
|
||
| ## Contents | ||
|
|
||
| | | Covers | Linear | | ||
| | --- | --- | --- | | ||
| | [`01-plan-and-entitlements.md`](01-plan-and-entitlements.md) | Is localization on our plan; what Mintlify's own translation feature does; the decision gate it opens | DF-823 | | ||
| | [`02-scope-and-preservation-spec.md`](02-scope-and-preservation-spec.md) | What gets translated, what must survive byte-for-byte, and how that is enforced | DF-826 | | ||
| | [`03-tooling-evaluation.md`](03-tooling-evaluation.md) | Mintlify-native vs General Translation vs Lingo.dev, and a 12-page bake-off to settle it | DF-827 | | ||
| | [`04-rollout-playbook.md`](04-rollout-playbook.md) | Which language is next, the per-language checklist, and the reviewer's checklist | DF-833, DF-834 | | ||
| | [`glossary/ko.md`](glossary/ko.md) | 121 Korean terms, 60 do-not-translate, and the pitfalls that need a human | DF-826 | | ||
| | [`nav/`](nav/README.md) | Korean navigation tree + the exact `docs.json` edit, ready to apply | DF-824 | | ||
| | [`config/`](config/README.md) | Staged `gt.config.json` and translation workflow | DF-830, DF-831 | | ||
| | [`pilot/`](pilot/) | Three hand-translated pages — the quality bar engines get scored against | DF-828 | | ||
| | [`scripts/`](scripts/) | The tooling below | | | ||
|
|
||
| ## Tooling | ||
|
|
||
| Zero dependencies — plain Node, no `package.json` needed. | ||
|
|
||
| ```bash | ||
| # Verify a translation preserves structure. The M3 CI gate, and the bake-off scoring function. | ||
| node i18n/scripts/validate-mdx-parity.mjs --source docs --target ko/docs \ | ||
| --locale ko --link-prefix --require-anchors --allow-code-comments | ||
|
|
||
| # Rebuild the Korean navigation from docs.json (--check fails if labels drift) | ||
| node i18n/scripts/build-locale-nav.mjs ko | ||
|
|
||
| # Rebuild machine-readable glossary artifacts from the human glossary | ||
| node i18n/scripts/build-glossary.mjs ko | ||
|
|
||
| # Show the docs.json change that enables i18n — without making it | ||
| node i18n/scripts/preview-docsjson-patch.mjs ko --write | ||
| ``` | ||
|
|
||
| `validate-mdx-parity.mjs` catches what review cannot: renamed JSX components, translated code identifiers, | ||
| localized image paths, dropped imports, unprefixed links, drifted heading anchors, mangled `{…}` | ||
| expressions. It is quiet on a correct translation and specific on a broken one. All three pilot pages pass | ||
| it on the strictest settings. | ||
|
|
||
| ## Where the project stands | ||
|
|
||
| | Milestone | State | | ||
| | --- | --- | | ||
| | **M1 · Mintlify config** | Plan question answered on the evidence; one dashboard check left. Korean nav built and verified. `docs.json` edit staged, not applied. | | ||
| | **M2 · Korean translation** | Scope, preservation spec, and glossary done. Engine recommended, pending a 12-page bake-off. Three pages hand-translated and passing. Bulk translation blocked on the engine decision. | | ||
| | **M3 · CI on merge** | Config and workflow written and staged. Redundant if Mintlify-native wins — which is the point of deciding first. | | ||
| | **M4 · Other languages** | Prioritization and playbook done. Blocked on M2/M3, as intended. | | ||
|
|
||
| ## Open decisions | ||
|
|
||
| 1. **Which engine?** Mintlify-native vs General Translation. Run the | ||
| [bake-off](03-tooling-evaluation.md#the-bake-off) — 12 pages, scored by the validator, one afternoon. | ||
| *Blocks DF-828, DF-830, DF-831.* | ||
| 2. **Does the API reference get translated?** It is two artifacts (137 MDX files + 14 OpenAPI specs, ~20k | ||
| words) and translating one without the other is worse than translating neither. Recommended: defer, then | ||
| let docs traffic decide. *Affects DF-824's Korean nav shape.* | ||
| 3. **Who reviews Korean, and how much of their time is available?** The only step in the whole pipeline that | ||
| does not compress. ~3–5 days per language. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an engineer copies
labels.ko.jsonas instructed, the retainedCommunitykey is not consumed because the builder traverses onlynavigation.tabs;--checktherefore reports it as unused and exits nonzero, blocking the rollout.