Skip to content

Commit cee27df

Browse files
Merge pull request #42 from trophyso/restructure-translations
Refactor for root-level en translations
2 parents b96ddb5 + 96ec980 commit cee27df

105 files changed

Lines changed: 959 additions & 750 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/rules/localization-workflow.mdc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ alwaysApply: true
55

66
# Localization Workflow
77

8-
- Treat `en/` as the canonical source locale and keep identical file paths/filenames across all locales.
8+
- Treat repo-root docs paths as the canonical English source and keep identical relative file paths/filenames across all locales.
99
- Keep all locale navigation in `docs.json` under `navigation.languages`; do not reintroduce `navigation.global` or top-level `navbar`.
1010
- Keep SEO metatags in English unless explicitly requested otherwise.
1111
- Do not translate code snippets, inline code, URLs, route slugs, or API identifiers.
12-
- Shared media lives under repo-root `assets/`. Locale pages are nested under `en/` or `es/` (typically `locale/<section>/<page>.mdx`), so relative `src` paths must account for that depth (for example `../../assets/...`). `scripts/localize-internal-links.mjs` rewrites markdown `](/...)` and JSX `href="/..."` only: for whichever **`locale` is being processed**, bare paths get that prefix, and paths prefixed with **any other** locale from `i18n.json` are re-prefixed to that same active locale; it does not change media `src` or snippet imports.
12+
- Shared media lives under repo-root `assets/`. Default-language pages are at repo-root section paths (for example `<section>/<page>.mdx`) and targets are under locale directories (for example `es/<section>/<page>.mdx`), so relative `src` paths must account for each file depth. `scripts/localize-internal-links.mjs` rewrites markdown `](/...)` and JSX `href="/..."` only: for target locales, bare paths get that locale prefix and paths prefixed with another locale are re-prefixed to the active locale; for source/default processing, locale prefixes are stripped back to root paths. It does not change media `src` or snippet imports.
1313
- `scripts/localize-component-imports.mjs` rewrites MDX import paths that reference `<locale>/components/*.jsx` so each locale page imports its own locale components (for whichever locale is being processed).
1414
- Shared MDX snippets live under repo-root `snippets/` (not per-locale). Import with relative paths from each page; do not add shared MDX snippets to Lingo `i18n.json` buckets unless you intend to translate them.
15-
- Locale-aware React components that can contain text live under `[locale]/components/*.jsx` and are manually maintained per locale (do not add them to Lingo `i18n.json` buckets; do not keep these in shared `snippets/`).
15+
- Locale-aware React components that can contain text live under `components/*.jsx` for default language and `<locale>/components/*.jsx` for targets, and are manually maintained per locale (do not add them to Lingo `i18n.json` buckets; do not keep these in shared `snippets/`).
1616
- Keep a **single** OpenAPI document at repo-root `openapi.yml` (not under `en/` or `es/`). Endpoint and webhook MDX must use Mintlify’s form `openapi: openapi.yml <method> <path>` or `openapi: openapi.yml webhook <eventKey>`. Do not translate the `openapi:` line.
1717
- English **nav titles** for those pages come from OpenAPI **`summary`** via `scripts/sync-openapi-titles.mjs` (`npm run translate:sync-openapi-titles`). It runs at the start of `translate:generate` and on the translate-on-main workflow. Lingo translates the `title` field for target locales; the script only fills a missing target `title` with English (bootstrap)—it does not overwrite existing translations.
18-
- Mintlify custom heading IDs use markdown `## Title {#slug}` (see Mintlify docs). Slugs are aligned from English via `scripts/sync-heading-anchors.mjs`. Never translate or alter `{#…}`; sync runs after Lingo in PIT/CI. Do not merge heading-count drift between `en/` and target locales without fixing structure first.
18+
- Mintlify custom heading IDs use markdown `## Title {#slug}` (see Mintlify docs). Slugs are aligned from English source root files via `scripts/sync-heading-anchors.mjs`. Never translate or alter `{#…}`; sync runs after Lingo in PIT/CI. Do not merge heading-count drift between source root and target locales without fixing structure first.
19+
- For Mintlify `<Accordion>` components that may be deep-linked, always set explicit `id` values (do not rely on title-derived hashes) and keep those `id`s identical across locales.
20+
- Do not use fragile OpenAPI parameter hash links like `#parameter-*` in docs links; link to the endpoint page and reference parameter names inline in prose/code.
1921
- Treat `lingo/brand-voice.md` as the source of truth for **brand voice** (tone and style) only. Do not add structural or tooling rules there (for example Mintlify `{#slug}` handling); those stay in this rule, README, and optionally in the Lingo engine **Instructions** field—not brand voice.
2022
- Do not add script-based brand voice sync; sync brand voice on demand via Lingo MCP tools from chat.
2123
- Treat `lingo/glossary.csv` as the glossary source of truth; use MCP sync with canonical key `sourceLocale|targetLocale|type|sourceText`.

.github/workflows/validate-translations.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,23 @@ on:
77
- "i18n.json"
88
- "openapi.yml"
99
- "package.json"
10-
- "en/**"
10+
- "account/**"
11+
- "admin-api/**"
12+
- "api-reference/**"
13+
- "components/**"
14+
- "experimentation/**"
15+
- "getting-started/**"
16+
- "guides/**"
17+
- "platform/**"
18+
- "webhooks/**"
1119
- "es/**"
1220
- ".github/workflows/translate-on-main.yml"
1321
- ".github/workflows/validate-translations.yml"
1422
- "scripts/*.mjs"
1523

1624
jobs:
1725
validate:
18-
if: ${{ !contains(fromJson('["translations-setup"]'), github.head_ref) }}
26+
if: ${{ !contains(fromJson('["translations-setup", "restructure-translations"]'), github.head_ref) }}
1927
runs-on: ubuntu-latest
2028
steps:
2129
- name: Checkout
@@ -49,6 +57,9 @@ jobs:
4957
- name: Ensure component imports are locale-localized
5058
run: npm run translate:localize-component-imports:check
5159

60+
- name: Ensure fragile link fragments are not used
61+
run: npm run translate:validate-links
62+
5263
- name: Ensure heading anchors match English slugs
5364
run: npm run translate:sync-anchors:check
5465

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,16 @@ Notes:
6262
- `scripts/localize-internal-links.mjs`: Rewrites internal absolute **navigation** links in each locale’s MDX for whatever locale is being processed (`--target`, `--all`, or default targets): bare paths get that locale’s prefix (e.g. `/platform/points``/es/platform/points` when processing `es/`), and any path already prefixed with **another** locale from `i18n.json` (`en`, `es`, future `fr`, etc.) is re-prefixed to the active locale (so `/en/...` or stale `/es/...` on a `fr/` page become `/fr/...`). Longer codes are matched first (e.g. `en-US` before `en`). It does **not** change relative image or video `src` paths, MDX/JSX **import** paths, or shared **MDX snippets**; those must be correct in source so all locales stay aligned. Prefer `npm run translate:localize-links --` with `--target`, `--all`, or `--all --check`.
6363
- `scripts/localize-component-imports.mjs`: Rewrites MDX `import ... from ".../<locale>/components/...jsx"` paths so they match the active locale being processed (`--target`, `--all`, or default targets). This keeps locale pages importing locale-local components after PIT/CI translation.
6464
- **Shared MDX snippets (`snippets/*.mdx`)**: Reusable MDX blocks stay in repo-root `snippets/` (not per-locale). Import them with relative paths from each page (for example `../../snippets/foo.mdx` from `locale/<section>/<page>.mdx`, or more `../` segments for deeper pages). They are excluded from Lingo buckets in `i18n.json` so they stay English and identical everywhere.
65-
- **Localized React components (`[locale]/components/*.jsx`)**: UI components that can contain locale text are stored per locale (for example `en/components/rate-limit-badge.jsx`, `es/components/rate-limit-badge.jsx`) and manually maintained per locale (not translated by PIT/CI automation).
66-
- **Media paths**: Pages live under `en/<section>/...` or `es/<section>/...`, while shared files sit in repo-root `assets/`. Use a path like `../../assets/...` from a typical `locale/<section>/<page>.mdx` file.
65+
- **Localized React components (`components/*.jsx` for default language, `<locale>/components/*.jsx` for targets)**: UI components that can contain locale text are stored per locale (for example `components/rate-limit-badge.jsx`, `es/components/rate-limit-badge.jsx`) and manually maintained per locale (not translated by PIT/CI automation).
66+
- **Media paths**: Default-language pages live at repo root paths like `<section>/<page>.mdx` and target locales live under `<locale>/<section>/<page>.mdx`; shared files sit in repo-root `assets/`. Keep relative media paths correct for each file depth.
6767
- **`openapi.yml`**: One OpenAPI 3.1 spec at the **repository root** (alongside `docs.json`). API and webhook pages reference it explicitly in frontmatter, for example `openapi: openapi.yml get /users/{id}` or `openapi: openapi.yml webhook points.changed`. Do not duplicate the YAML under locale folders; Lingo must not alter `openapi:` lines.
6868
- **`scripts/sync-openapi-titles.mjs`**: Copies each operation/webhook **`summary`** from `openapi.yml` into the English page’s **`title:`** frontmatter (Mintlify’s default when `title` is omitted). Target locales get an English `title` only if missing (bootstrap); run **`npm run translate:generate`** so Lingo translates those titles. Runs automatically at the start of `translate:generate` and in translate-on-main before Lingo.
6969
- `scripts/sync-heading-anchors.mjs`: Writes Mintlify [custom heading IDs](https://www.mintlify.com/docs/create/text#custom-heading-ids) as **`## Title {#slug}`** markdown. Slugs match Mintlify’s auto rules from the **English** title so hashes like `#pro-plan` stay stable across locales. Run `npm run translate:sync-anchors` after bulk heading edits; translation pipelines run it automatically (see **Heading anchors and Lingo** below). The script can also migrate one-line **`<h2 id="slug">…</h2>`** left from older tooling back to `{#slug}` syntax.
7070
- `scripts/validate-glossary-csv.mjs`: Validates glossary schema and duplicate canonical keys. Prefer `npm run lingo:validate-glossary`.
7171

7272
#### Heading anchors and Lingo
7373

74-
- **Canonical behavior** is enforced by **`scripts/sync-heading-anchors.mjs`** (runs at the end of `translate:generate` and on the translate-on-main workflow). It rewrites `en/` from current English titles and reapplies the **same** `{#slug}` suffixes to translated headings in document order. Hash links stay aligned even if Lingo alters titles or fragments.
74+
- **Canonical behavior** is enforced by **`scripts/sync-heading-anchors.mjs`** (runs at the end of `translate:generate` and on the translate-on-main workflow). It rewrites default-language root files from current English titles and reapplies the **same** `{#slug}` suffixes to translated headings in document order. Hash links stay aligned even if Lingo alters titles or fragments.
7575
- **Lingo:** Keep **`lingo/brand-voice.md`** to tone and style only. For structural rules (for example Mintlify `{#slug}` on headings), optionally add a separate line in your Lingo engine **Instructions** field (not brand voice), such as: “Preserve `{#…}` heading fragments exactly—do not translate or alter the slug.” That only reduces churn; **the script + `translate:sync-anchors:check` are authoritative.**
7676
- **Strict MDX parsers** (for example unconfigured `mdx-js`) can treat `{` as JSX and error on `{#slug}`; **Mintlify’s `mint dev` / deploy pipeline** is expected to handle this documented syntax. If you see Acorn errors locally, update the Mintlify CLI (`npm i -D mint@latest` or global `mintlify`) or check [Mintlify support](https://mintlify.com/docs); do not switch to raw HTML headings unless Mintlify asks you to.
7777
- **Do not** fold this into `localize-internal-links.mjs`: that script only handles absolute path `href`s; heading anchors are a separate structural pass and must run **after** Lingo (and after link localization) so all three stay consistent.
@@ -89,12 +89,12 @@ Use `npm run <script> -- <args>` when a script needs CLI flags (the `--` forward
8989
| `translate:localize-links:check` | CI-style check: all locales, no writes (`--all --check`). |
9090
| `translate:localize-component-imports` | Localize MDX imports that reference `<locale>/components/*.jsx` to the active locale. |
9191
| `translate:localize-component-imports:check` | CI-style check: fail if any MDX imports point at another locale’s components. |
92-
| `translate:sync-anchors` | Apply English-derived `{#slug}` on ATX headings in `en/` and target locales (see `i18n.json` `locale.targets`). |
92+
| `translate:sync-anchors` | Apply English-derived `{#slug}` on default-language root MDX and target locales (see `i18n.json` `locale.targets`). |
9393
| `translate:sync-anchors:check` | Fail if any MDX needs re-sync (CI). |
94-
| `translate:sync-openapi-titles` | Set `en/` API & webhook `title:` from `openapi.yml` summaries; bootstrap missing target `title` from English. |
94+
| `translate:sync-openapi-titles` | Set default-language API & webhook `title:` from `openapi.yml` summaries; bootstrap missing target `title` from English. |
9595
| `translate:sync-openapi-titles:check` | CI: fail if any OpenAPI-backed page title differs from the spec. |
9696
| `lingo:validate-glossary` | Validate `lingo/glossary.csv`. |
97-
| `translate:validate` | MDX parity and frontmatter checks (`en` / `es`). No `.env` required. |
97+
| `translate:validate` | MDX parity and frontmatter checks (source root -> target locales, e.g. `es`). No `.env` required. |
9898
| `translate:verify` | `lingo.dev run --frozen` (freshness gate). |
9999
| `mint:validate` | Mintlify `validate`. |
100100
| `mint:broken-links` | Mintlify `broken-links` with anchor and snippet checks. |
@@ -134,13 +134,13 @@ Glossary sync workflow (manual via Cursor + MCP):
134134

135135
1. Add the locale to `docs.json`:
136136
- In `navigation.languages`, add a new object with `language: "<locale>"`.
137-
- Copy the structure from `en` (tabs/groups/pages) and prefix pages with `<locale>/...`.
137+
- Copy the default-language structure (tabs/groups/pages) and prefix pages with `<locale>/...`.
138138
- Add language-specific `anchors` and `navbar` fields in that locale block.
139139
2. Add the locale to `i18n.json`:
140140
- In `locale.targets`, append the locale code (for example `fr` or `de`).
141141
3. Create the language content directory:
142-
- Mirror the `en/` structure under `<locale>/` with the same filenames.
143-
- Example: `en/platform/overview.mdx` -> `fr/platform/overview.mdx`.
142+
- Mirror the root default-language structure under `<locale>/` with the same filenames.
143+
- Example: `platform/overview.mdx` -> `fr/platform/overview.mdx`.
144144
- Keep MDX snippets shared in repo-root `snippets/` (no per-locale snippet trees).
145145
- Keep React components locale-local under `<locale>/components/*.jsx` and mirror updates manually across locales (not via Lingo automation).
146146
4. Configure Lingo.dev engine controls for the new locale:
@@ -160,5 +160,5 @@ Glossary sync workflow (manual via Cursor + MCP):
160160

161161
Notes:
162162
- The workflow `.github/workflows/translate-on-main.yml` automatically reads locales from `i18n.json` `locale.targets`.
163-
- Keep `en` as source/default and preserve identical file paths across locales.
163+
- Keep English as source/default and preserve identical relative file paths across locales.
164164
- `--force` on `translate:generate` purges **all** Lingo-managed content for that target locale, then re-runs translation—use only for recovery/backfill (for example, cache stuck after bootstrap). Do not use `--force` in CI; CI should run delta translation based on `i18n.lock`.
Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Learn how Trophy controls costs by charging based on monthly active
55
icon: gauge
66
---
77

8-
import { PlanBadge } from "../../en/components/plan-badge.jsx";
8+
import { PlanBadge } from "../components/plan-badge.jsx";
99

1010
## Usage-based Billing {#usage-based-billing}
1111

@@ -18,7 +18,7 @@ Trophy follows a usage-based pricing model where customers only pay for the unit
1818

1919
## Monthly Active Users (MAUs) {#monthly-active-users-maus}
2020

21-
Trophy defines an MAU as a single user that sends at least one [metric event](/en/platform/events) to Trophy in a given month.
21+
Trophy defines an MAU as a single user that sends at least one [metric event](/platform/events) to Trophy in a given month.
2222

2323
<Tip>
2424
Bear in mind **you never pay for churned users**. If a user signs up for your
@@ -83,21 +83,21 @@ Here's a list of all Trophy features and the plan they become available on:
8383

8484
<PlanBadge plan="free" />
8585

86-
- [Achievements](/en/platform/achievements)
87-
- [Streaks](/en/platform/streaks)
88-
- [Points](/en/platform/points)
89-
- [Leaderboards](/en/platform/leaderboards)
90-
- [Emails](/en/platform/emails)
91-
- [Push Notifications](/en/platform/push-notifications)
86+
- [Achievements](/platform/achievements)
87+
- [Streaks](/platform/streaks)
88+
- [Points](/platform/points)
89+
- [Leaderboards](/platform/leaderboards)
90+
- [Emails](/platform/emails)
91+
- [Push Notifications](/platform/push-notifications)
9292

9393
<PlanBadge plan="starter" />
9494

95-
- [DNS Verification](/en/platform/emails#dns-verification-advanced)
95+
- [DNS Verification](/platform/emails#dns-verification-advanced)
9696

9797
<PlanBadge plan="pro" />
9898

99-
- [Webhooks](/en/webhooks)
100-
- [Custom Attributes](/en/platform/users#custom-user-attributes)
99+
- [Webhooks](/webhooks)
100+
- [Custom Attributes](/platform/users#custom-user-attributes)
101101

102102
## Custom Contracts {#custom-contracts}
103103

@@ -112,7 +112,7 @@ You can view your usage for the current billing period on the [billing page](htt
112112
height="200"
113113
width="50%"
114114
noZoom
115-
src="../../assets/account/billing/plan-usage.png"
115+
src="../assets/account/billing/plan-usage.png"
116116
/>
117117
</Frame>
118118

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ icon: palette
77

88
## Configure Branding {#configure-branding}
99

10-
The [branding page](https://app.trophy.so/branding) in the Trophy dashboard allows you to configure the following account-level settings. These setting will be used in any communications you configure Trophy to send to users, like [Emails](/en/platform/emails).
10+
The [branding page](https://app.trophy.so/branding) in the Trophy dashboard allows you to configure the following account-level settings. These setting will be used in any communications you configure Trophy to send to users, like [Emails](/platform/emails).
1111

1212
<Frame>
13-
<img height="200" noZoom src="../../assets/account/branding/branding_page.png" />
13+
<img height="200" noZoom src="../assets/account/branding/branding_page.png" />
1414
</Frame>
1515

1616
- **Logo**: Upload your brand’s logo to be displayed in the header of Trophy emails. We recommend using a horizontal logo with a transparent background or a plain background color with rounded corners.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ To invite a team member to your Trophy organization, open up the organization ma
1818
loop
1919
playsInline
2020
className="w-full aspect-15/4"
21-
src="../../assets/account/members/add_new_member.mp4"
21+
src="../assets/account/members/add_new_member.mp4"
2222
></video>
2323
</Frame>
2424

@@ -46,7 +46,7 @@ To manage roles, open up the organization management dialog and head into the _M
4646
loop
4747
playsInline
4848
className="w-full aspect-15/4"
49-
src="../../assets/account/members/manage_roles.mp4"
49+
src="../assets/account/members/manage_roles.mp4"
5050
></video>
5151
</Frame>
5252

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ description: Learn how to manage your Trophy account.
66
From inviting team members to understanding billing, this section of the documentation is dedicated to managing your Trophy account.
77

88
<CardGroup>
9-
<Card title="Members" icon="user-plus" href="/en/account/members">
9+
<Card title="Members" icon="user-plus" href="/account/members">
1010
Give team members access to Trophy and manage your account settings.
1111
</Card>
12-
<Card title="Branding" icon="palette" href="/en/account/branding">
12+
<Card title="Branding" icon="palette" href="/account/branding">
1313
Configure your logo and brand colors used across features built with Trophy.
1414
</Card>
15-
<Card title="Billing" icon="gauge" href="/en/account/billing">
15+
<Card title="Billing" icon="gauge" href="/account/billing">
1616
Learn how Trophy tracks usage according to the number of users that use your
1717
product.
1818
</Card>

0 commit comments

Comments
 (0)