Skip to content

Korean localization groundwork (staged, nothing live) - #204

Closed
spjtls9 wants to merge 1 commit into
mainfrom
spjtls9/https-linear.app-mixpanel-project-docs-localization-mintlify-i18n-25fabd3b5c76-overview
Closed

Korean localization groundwork (staged, nothing live)#204
spjtls9 wants to merge 1 commit into
mainfrom
spjtls9/https-linear.app-mixpanel-project-docs-localization-mintlify-i18n-25fabd3b5c76-overview

Conversation

@spjtls9

@spjtls9 spjtls9 commented Aug 29, 2026

Copy link
Copy Markdown

Groundwork for shipping docs.mixpanel.com in Korean — Docs Localization (Mintlify i18n).

This is a no-op for readers

Everything lands under i18n/ and is deliberately inert:

  • docs.json is untouched — verified, not claimed
  • no workflow in .github/workflows/
  • no ko/ tree at the repo root
  • nothing under docs/, guides/, reference/, or snippets/ is modified

Merging this changes nothing a reader sees. Per CODEOWNERS this is all @mixpanel/docs-reviewers — it touches none of the @mixpanel/tofu paths (/scripts/ is root-anchored, and these are .mjs, not .jsx).

Two findings worth a reviewer's attention

Mintlify already sells the pipeline M2/M3 assumed we'd build. Native AI translations (Pro/Enterprise) bulk-translates the docs and opens a PR in this repo on every change, never publishing without a human merge — that's M2's bulk run plus all of M3. Every signal says Mixpanel is on Enterprise, decisively the README putting the Mintlify web editor behind Okta. One person opening the dashboard → Settings → Translations settles whether we build a pipeline we may already own. (DF-823)

710 internal links will break silently the moment a heading is translated. Anchors derive from English heading text; this repo has 3,723 headings with zero explicit anchors and 710 links carrying a #fragment. Translate a heading and those links start landing at the top of the page — no error, nothing visible in a diff. Fixed by pinning ## 제목 [#english-anchor], but the engine has to do it. This became the disqualifying criterion for engine choice. (DF-826, DF-827)

What's in here

Path What it is Issue
scripts/validate-mdx-parity.mjs Verifies JSX components, code fences, imports, image paths, link targets, heading anchors, frontmatter, and brace balance against the English source DF-827 + the M3 gate
nav/ Korean nav tree — 128 labels, 390 page paths — plus a generator whose --check fails CI if labels drift DF-824
02-scope-and-preservation-spec.md What's translated, what survives byte-for-byte DF-826
glossary/ 121 Korean terms + 60 do-not-translate, generated from the human glossary so they can't drift DF-826
pilot/ko/ 3 hand-translated pages — the quality bar engines get scored against DF-828
config/ GT config + CI workflow, staged outside the paths that would make them run DF-830, DF-831
03-tooling-evaluation.md, 04-rollout-playbook.md, 01-plan-and-entitlements.md Engine comparison + bake-off, next-language playbook, entitlement findings DF-827, DF-833, DF-823

Start at i18n/README.md.

Reviewing this

validate-mdx-parity.mjs is the load-bearing piece — it's the scoring function for the engine bake-off and the gate for the M3 pipeline, whichever engine wins. Zero dependencies, no package.json needed:

# all three pilot pages, strictest settings — expect 3 clean, 0 warnings
node i18n/scripts/build-locale-nav.mjs ko --check
node i18n/scripts/build-glossary.mjs ko --check
node i18n/scripts/validate-mdx-parity.mjs \
  --source docs/data-structure/concepts.mdx \
  --target i18n/pilot/ko/docs/data-structure/concepts.mdx \
  --locale ko --link-prefix --require-anchors --allow-code-comments

The Korean itself is the part that most needs a native reader — pilot/ko/ and nav/labels.ko.json especially. Terminology policy and its rationale are in glossary/ko.md; the short version is that Mixpanel's UI is English-only, so feature names stay phonetically recognisable against the button the reader is looking at.

Two things to push back on

  • i18n/status-brief.html is the source for a status page linked from the Linear project. It will go stale. Happy to drop it if a one-off status page doesn't belong in the docs repo.
  • DF-825's "seed ko/ with English fallback" step should be dropped. Mintlify's docs explicitly warn: "Do not use the same page path in more than one language. Duplicating paths across languages results in undefined behavior." Flagged on the issue; noted here because it changes that issue's scope.

Linear

DF-823, DF-824, DF-825, DF-826, DF-827, DF-828, DF-829, DF-830, DF-831, DF-832, DF-833, DF-834

🤖 Generated with Claude Code

Groundwork for shipping docs.mixpanel.com in Korean. Everything lands under
i18n/ and is deliberately inert: docs.json is untouched, no workflow is
installed, and no ko/ tree exists at the repo root. Merging this changes
nothing a reader sees.

Two findings that reshape the project:

- Mintlify sells native AI translations (Pro/Enterprise) that bulk-translate
  the docs and open a PR per change with a human merge gate — which is M2's
  bulk run plus all of M3. Every signal says Mixpanel is on Enterprise. Needs
  one dashboard check before we build a pipeline we may already own.

- 3,723 headings in this repo have no explicit anchors, and 710 internal links
  carry a #fragment. Translating a heading silently changes its anchor, so
  those links start landing at the top of the page. No error, nothing visible
  in a diff. Fixed by pinning `## 제목 [#english-anchor]`, but the engine has
  to do it — this became the disqualifying criterion for engine choice.

Contents:

  01-plan-and-entitlements.md   plan/entitlement findings        DF-823
  02-scope-and-preservation-spec.md  scope + preservation rules  DF-826
  03-tooling-evaluation.md      engine comparison + bake-off     DF-827
  04-rollout-playbook.md        next language + playbook         DF-833
  glossary/                     121 KO terms, 60 do-not-translate DF-826
  nav/                          128 labels, 390 paths, staged     DF-824
  config/                       GT config + CI workflow, staged   DF-830/831
  pilot/ko/                     3 hand-translated pages           DF-828
  scripts/                      4 zero-dependency Node tools

scripts/validate-mdx-parity.mjs is the load-bearing piece: it verifies JSX
components, code fences, imports, image paths, link targets, heading anchors,
frontmatter keys, and brace balance against the English source. It is the
scoring function for the engine bake-off and the gate for the M3 pipeline,
whichever engine wins. All three pilot pages pass it on strictest settings.

Also flags that DF-825's "seed ko/ with English fallback" step should be
dropped — Mintlify's docs explicitly warn against duplicate page paths across
languages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@spjtls9
spjtls9 requested a review from a team as a code owner August 29, 2026 20:33
@spjtls9
spjtls9 requested a review from argenisf August 29, 2026 20:33
@mintlify

mintlify Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
mixpanel-docs 🟢 Ready View Preview Aug 29, 2026, 8:36 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@greptile-apps

greptile-apps Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 1/5

The PR should not merge until the parity gate handles deferred reference links, verifies inline-code contents, and the staged workflow uses immutable dependencies.

The mandatory validator currently rejects valid wave-1 links while allowing changed inline identifiers, and the documented workflow activation would expose credentials and write permissions to mutable dependencies.

Files Needing Attention: i18n/scripts/validate-mdx-parity.mjs, i18n/config/translate.yml

Security Review

The staged workflow should pin all actions and the translation CLI before activation; as written, mutable dependencies execute with translation credentials and repository write permissions.

Important Files Changed

Filename Overview
i18n/scripts/validate-mdx-parity.mjs Adds the structural gate, but over-prefixes links to deferred surfaces and does not verify inline-code contents.
i18n/config/translate.yml Stages the translation workflow, but its mutable dependencies would run with secrets and write permissions when activated.
i18n/scripts/build-locale-nav.mjs Deterministically generates localized navigation and checks label completeness.
i18n/scripts/build-glossary.mjs Generates synchronized CSV and do-not-translate artifacts from the human glossary.
i18n/config/gt.config.json Stages wave-1 translation transforms and locale behavior without activating them.
i18n/02-scope-and-preservation-spec.md Defines the wave boundaries and machine-checkable preservation contract used to assess the validator.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[English docs and guides] --> B[Translation engine]
  B --> C[Korean tree]
  C --> D[MDX parity validator]
  D -->|pass| E[Navigation and glossary checks]
  E --> F[Translation pull request]
  D -->|fail| G[Block pull request]
Loading

Reviews (1): Last reviewed commit: "Korean localization groundwork (staged, ..." | Re-trigger Greptile

Comment on lines +317 to +319
const expected = LINK_PREFIX && LOCALE
? s.mdLinks.map((l) => (l.startsWith("/") && !l.startsWith(`/${LOCALE}/`) && !l.startsWith("/images/") ? `/${LOCALE}${l}` : l))
: s.mdLinks;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Deferred links fail validation

When a wave-1 page links to the deferred /reference surface, --link-prefix expects a nonexistent /ko/reference/... target, causing valid translations to fail the mandatory parity gate. Exclude untranslated surfaces from prefixing until Korean counterparts exist.

Comment on lines +349 to +351
/* inline code */
if (s.inlineCode.length !== t.inlineCode.length)
W(`inline code span count ${s.inlineCode.length} → ${t.inlineCode.length}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Inline code changes pass

When translation changes an identifier inside an inline-code span without changing the span count, this check emits no error, allowing incorrect API identifiers such as distinct_id to pass the preservation gate. Compare inline-code contents rather than only their counts.

Source Used: Linear — [M2] Evaluate & select MDX-aware translation tooling

Comment thread i18n/config/translate.yml
Comment on lines +41 to +54
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-node@v4
with:
node-version: "22"

# gt-lock.json is what makes this a delta: only content whose hash changed is sent.
- name: Translate changed content
env:
GT_API_KEY: ${{ secrets.GT_API_KEY }}
GT_PROJECT_ID: ${{ secrets.GT_PROJECT_ID }}
run: npx gtx-cli translate --config gt.config.json ${{ inputs.full && '--no-cache' || '' }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Mutable workflow dependencies

When this staged workflow is activated, mutable action tags and an unversioned npx gtx-cli execute with translation credentials and repository write permissions, allowing a compromised or retargeted dependency to disclose credentials or modify repository content. Pin actions to commit SHAs and the CLI to an audited version before activation.

How this was verified: The activation instructions move this workflow unchanged, and its floating dependencies run with GT secrets plus contents: write and pull-requests: write.

@linear-code

linear-code Bot commented Aug 29, 2026

Copy link
Copy Markdown

@spjtls9

spjtls9 commented Aug 29, 2026

Copy link
Copy Markdown
Author

Closing this in favour of a properly scoped stack — one PR per Linear issue, with explicit merge ordering.

This PR was 23 files spanning 9 issues, which is not reviewable. Replaced by:

PR Issue Scope Files
1 #205 DF-823 Plan & entitlement findings 1
2 #206 DF-826 Scope, spec, glossary, parity validator 6
3 #207 DF-827 Engine evaluation 1
4 #208 DF-824 Korean navigation tree 5
5 #209 DF-828 Pilot Korean translations 4
6 #210 DF-830 Staged translation config 2
7 #211 DF-831 Staged CI workflow 2
8 #212 DF-833 Prioritization, playbook, index 2

Each is stacked on the one above, and each description carries the full merge order plus the operational
enablement sequence (what actually has to happen, in what order, to turn Korean on — separate from PR order).

One content change while splitting: i18n/status-brief.html is dropped. It was a status snapshot that
would go stale, and it didn't belong in the docs repo — the point I flagged for pushback in this PR's own
description. The published status page it generated is still linked from the Linear project.

Otherwise the stack's final state is byte-identical to this PR's, verified by diff.

@spjtls9 spjtls9 closed this Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant