Skip to content

Commit a489b55

Browse files
committed
Merge branch 'spanish' into develop
# Conflicts: # .github/workflows/s3-deploy-development.yml # .github/workflows/s3-deploy-production.yml
2 parents 8833fdd + c302e8b commit a489b55

40 files changed

Lines changed: 2945 additions & 126 deletions

.claude/skills/localize/SKILL.md

Lines changed: 110 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -218,66 +218,132 @@ The translation script creates `.mdx` files and `.hashes/` automatically. You on
218218

219219
---
220220

221-
## Step 6 — Translate content (run in this order)
221+
## Step 6 — Translate content (phased, gated by native-speaker reviews)
222222

223-
> **Note on `CustomDocCardList`:** This component is already locale-aware — it reads titles from `_sidebar-labels.json` and descriptions from the translated article frontmatter automatically. No extra step is needed; it works correctly once sidebar labels (Step 6c `--sidebars`) and article translations are done.
223+
> **Flow overview:** (1) research dictionary terminology → (2) native speaker reviews word-pair list → (3) translate tutorial 7 articles → (4) deploy to develop → (5) native speaker reviews rendered pages → (6) translate the rest.
224+
>
225+
> **Note on `CustomDocCardList`:** This component is already locale-aware — it reads titles from `_sidebar-labels.json` and descriptions from the translated article frontmatter automatically. No extra step is needed; it works correctly once sidebar labels (Step 6g `--sidebars`) and article translations are done.
224226
225-
All commands require `ANTHROPIC_API_KEY` to be set.
227+
All `translate.mjs` commands require `ANTHROPIC_API_KEY` to be set.
226228

227-
### 6a. Translate the dictionary first
229+
### Phase 1 — Dictionary research and native-speaker word-list review
228230

229-
The dictionary (`src/locales/dictionary.json`) should already have translations added manually in Step 1d. Review it before running article translations — the script uses it as a glossary.
231+
#### 6a. Deeply research target-language mobile dev community terminology
232+
233+
Do not guess dictionary entries from training knowledge. **Run web searches** to verify how the target-language mobile dev community actually refers to each concept. This materially affects the quality of every translated article downstream.
234+
235+
**Sources to consult (in priority order):**
236+
237+
1. **Platform-official docs in the target locale** — authoritative terminology developers will expect:
238+
- App Store Connect help pages (`developer.apple.com/help/app-store-connect/` — often has locale routes)
239+
- Google Play Console help (`support.google.com/googleplay/android-developer?hl={LOCALE}`)
240+
- Firebase docs (`firebase.google.com/docs?hl={LOCALE}`)
241+
2. **Competitor docs in the target locale**, if available — RevenueCat, Superwall, Stripe.
242+
3. **Native-language mobile dev press** — e.g. iPhoneSoft, iGeneration, Citronnoir (FR); equivalents exist for most major locales. Search `"<term>" site:<locale-press-domain>` to see real usage.
243+
4. **Glossaries on affiliate-marketing / MMP sites** — AppsFlyer, Adjust, and similar publish locale-specific glossaries.
244+
5. **Professional translators' references** — Linguee and Reverso Context show real-world parallel translations across tech content.
245+
246+
**For each dictionary term, verify:**
247+
248+
- Which form does platform-official (Apple / Google) documentation use in the target locale?
249+
- Is there divergence between Apple FR/DE/etc. and Google Play FR/DE/etc.? (Yes — frequently. Flag these.)
250+
- What term does the target-locale mobile dev press actually use in practice?
251+
- Is the term commonly kept in English as a loanword? (`paywall`, `sandbox`, `onboarding`, `SDK`, `store`, `remote config`, `dashboard` are often kept; `subscription`, `access level`, `grace period`, `consumable` usually translate.)
252+
- Does the candidate translation conflict with another common meaning in the target locale? (e.g. "placement" = "investment" in French — check context fit.)
253+
254+
**Rules of thumb:**
255+
256+
- **Product names stay in English**`Paywall Builder`, `Remote Config`, `Adapty Dashboard`.
257+
- **Apple's in-app terms have official locale translations** — look up `introductory offer`, `promotional offer`, `win-back offer`, `grace period` in Apple FR/DE/etc. help before guessing.
258+
- **When mobile dev press keeps a term in English, follow them.** Formal dictionaries often propose alternatives (e.g. "bac à sable" for sandbox) that no French iOS developer actually uses.
259+
- **Match the feel of existing locales** — look at how `zh`, `tr`, `ru` handle each term; they already made similar loanword-vs-translation calls. If all three kept a term in English, the new locale probably should too.
260+
261+
#### 6b. Generate a word-pair list and send it for native-speaker review (BLOCKING)
262+
263+
After updating `src/locales/dictionary.json` with researched translations, **stop and generate a plain text list** of `English → {LOCALE}` pairs — one line per term, alphabetical by English source — and tell the user to copy it and send it to a native-speaking reviewer.
264+
265+
**Format to output:**
230266

231267
```
232-
# Check dictionary for any missing {LOCALE} entries, then continue.
268+
A/B test → <target translation>
269+
access level → <target translation>
270+
...
233271
```
234272

235-
### 6b. Translate tutorial sidebar first 7 articles
273+
No sources, no rationale — just the pairs. The user will paste this into Slack/email verbatim.
274+
275+
**After outputting the list, STOP.** Do not proceed to article translation. Wait for the user to either:
276+
277+
- Confirm "we're good to go" (dictionary stands as-is), or
278+
- Apply native-speaker corrections to `dictionary.json` and then confirm.
279+
280+
### Phase 2 — Translate tutorial and deploy for rendered review
281+
282+
#### 6c. Generate the two translation commands (BLOCKING)
236283

237-
These are the entry-point articles every user sees first. Translate them as a batch:
284+
Once the user confirms the dictionary is approved, **output the following two commands** in a single message for the user to run in their own terminal. Do not execute them `ANTHROPIC_API_KEY` is a secret and translation is long-running and potentially costly; the user runs it themselves.
238285

239286
```bash
240-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --ids "what-is-adapty,is-adapty-right-for-me,integrate-payments,quickstart-products,quickstart-paywalls,quickstart-sdk,quickstart-test"
287+
export ANTHROPIC_API_KEY=sk-ant-...
241288
```
242289

243-
### 6c. Translate sidebar labels and step-by-step docs per sidebar
290+
```bash
291+
node scripts/translate.mjs --lang {LOCALE} --ids "what-is-adapty,is-adapty-right-for-me,integrate-payments,quickstart-products,quickstart-paywalls,quickstart-sdk,quickstart-test"
292+
```
293+
294+
These are the 7 tutorial entry-point articles every first-time user sees. After outputting, **wait for the user to say the translation finished.**
295+
296+
#### 6d. Deploy the tutorial translations to `develop` for rendered review
297+
298+
Once the user confirms translation is done, invoke the `sync-branch-to-develop` skill to commit the generated MDX files, push the feature branch, and merge into `develop` so the native-speaker reviewer can see them rendered on the staging URL.
299+
300+
#### 6e. Wait for the native speaker to review the rendered pages
301+
302+
Tell the user to share the staging URL with their reviewer. **Stop** until the user returns with approval or revision requests. If revisions are needed, iterate on the dictionary or specific article frontmatter/body; do not start Phase 3 until the tutorial 7 are approved.
244303

245-
Translate one sidebar at a time — each command is independent and can be run, reviewed, and committed separately.
304+
### Phase 3 — Full content translation
305+
306+
Only start this phase after the tutorial 7 are approved by the native-speaker reviewer. Same pattern as Phase 2: **output commands, do not execute** — the user runs them in their terminal with `ANTHROPIC_API_KEY` already exported from Phase 2.
307+
308+
#### 6f. Translate sidebar labels
309+
310+
All sidebars at once:
246311

247-
**Step-by-step sidebar labels only (all sidebars at once):**
248312
```bash
249-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebars
313+
node scripts/translate.mjs --lang {LOCALE} --sidebars
250314
```
251315

252-
**Or one sidebar's labels at a time:**
316+
Or one sidebar's labels at a time, if you want to review incrementally:
317+
253318
```bash
254-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebar tutorial
255-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebar ios
256-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebar android
257-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebar react-native
258-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebar flutter
259-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebar unity
260-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebar kmp
261-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebar capacitor
262-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --sidebar api
263-
```
264-
265-
**Article docs, one platform/sidebar at a time:**
319+
node scripts/translate.mjs --lang {LOCALE} --sidebar tutorial
320+
node scripts/translate.mjs --lang {LOCALE} --sidebar ios
321+
node scripts/translate.mjs --lang {LOCALE} --sidebar android
322+
node scripts/translate.mjs --lang {LOCALE} --sidebar react-native
323+
node scripts/translate.mjs --lang {LOCALE} --sidebar flutter
324+
node scripts/translate.mjs --lang {LOCALE} --sidebar unity
325+
node scripts/translate.mjs --lang {LOCALE} --sidebar kmp
326+
node scripts/translate.mjs --lang {LOCALE} --sidebar capacitor
327+
node scripts/translate.mjs --lang {LOCALE} --sidebar api
328+
```
329+
330+
#### 6g. Translate platform article docs, one platform at a time
331+
266332
```bash
267-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --platform tutorial
268-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --platform ios
269-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --platform android
270-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --platform react-native
271-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --platform flutter
272-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --platform unity
273-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --platform kmp
274-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --platform capacitor
333+
node scripts/translate.mjs --lang {LOCALE} --platform tutorial
334+
node scripts/translate.mjs --lang {LOCALE} --platform ios
335+
node scripts/translate.mjs --lang {LOCALE} --platform android
336+
node scripts/translate.mjs --lang {LOCALE} --platform react-native
337+
node scripts/translate.mjs --lang {LOCALE} --platform flutter
338+
node scripts/translate.mjs --lang {LOCALE} --platform unity
339+
node scripts/translate.mjs --lang {LOCALE} --platform kmp
340+
node scripts/translate.mjs --lang {LOCALE} --platform capacitor
275341
```
276342

277-
### 6d. Translate API docs
343+
#### 6h. Translate API docs
278344

279345
```bash
280-
ANTHROPIC_API_KEY=sk-... node scripts/translate.mjs --lang {LOCALE} --api-specs
346+
node scripts/translate.mjs --lang {LOCALE} --api-specs
281347
```
282348

283349
---
@@ -389,10 +455,14 @@ strategy:
389455
| 3 | Create sitemap files + update astro.config.mjs | `src/pages/sitemap-{LOCALE}*.xml.ts`, `astro.config.mjs` |
390456
| 4 | Add npm scripts (optional) | `package.json` |
391457
| 5 | Create content directory | `src/locales/{LOCALE}/` |
392-
| 6a | Review/complete dictionary | `src/locales/dictionary.json` |
393-
| 6b | Translate first 7 tutorial articles | `--ids` command |
394-
| 6c | Translate sidebar labels + platform docs | `--sidebars` + `--platform` commands |
395-
| 6d | Translate API specs | `--api-specs` command |
458+
| 6a | Deeply research target-language mobile dev terminology (web searches) | `src/locales/dictionary.json` |
459+
| 6b | **BLOCKING:** Generate word-pair list; wait for native-speaker review | conversation |
460+
| 6c | **BLOCKING:** Output two commands (export key + translate tutorial 7); wait for user to run them | conversation |
461+
| 6d | Deploy tutorial translations to develop via `sync-branch-to-develop` skill | `sync-branch-to-develop` |
462+
| 6e | **BLOCKING:** Wait for native speaker to review rendered tutorial pages on staging | — |
463+
| 6f | Output command to translate sidebar labels | `--sidebars` |
464+
| 6g | Output commands to translate platform article docs | `--platform` per sidebar |
465+
| 6h | Output command to translate API specs | `--api-specs` |
396466
| 7a | Update English crawler exclusion | Algolia dashboard |
397467
| 7b | Create new locale crawler | Algolia dashboard |
398468
| 7c | Create new Algolia index | Algolia dashboard |

.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ PUBLIC_ALGOLIA_ANALYTICS_KEY=5e3fd9357b98f9f0d44bab0f0b7634c0
77
# Locale-specific Algolia indices
88
PUBLIC_ALGOLIA_INDEX_NAME_ZH=adapty_zh
99
PUBLIC_ALGOLIA_INDEX_NAME_TR=adapty_tr
10-
PUBLIC_ALGOLIA_INDEX_NAME_RU=adapty_ru
10+
PUBLIC_ALGOLIA_INDEX_NAME_RU=adapty_ru
11+
PUBLIC_ALGOLIA_INDEX_NAME_ES=adapty_es

.github/workflows/s3-deploy-development.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
runs-on: ubuntu-latest
4949
strategy:
5050
matrix:
51-
locale: [zh, tr, ru]
51+
locale: [zh, tr, ru, es]
5252
concurrency:
5353
group: build-locale-${{ matrix.locale }}-${{ github.workflow }}-${{ github.ref }}
5454
cancel-in-progress: true
@@ -117,6 +117,11 @@ jobs:
117117
name: build-ru
118118
path: build/ru/
119119

120+
- uses: actions/download-artifact@v4
121+
with:
122+
name: build-es
123+
path: build/es/
124+
120125
- name: Deploy
121126
uses: reggionick/s3-deploy@v4
122127
with:

.github/workflows/s3-deploy-production.yml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ jobs:
104104
runs-on: ubuntu-latest
105105
strategy:
106106
matrix:
107-
locale: [zh, tr, ru]
107+
locale: [zh, tr, ru, es]
108108
concurrency:
109109
group: build-locale-full-${{ matrix.locale }}-${{ github.workflow }}-${{ github.ref }}
110110
cancel-in-progress: true
@@ -173,6 +173,11 @@ jobs:
173173
name: build-ru
174174
path: build/ru/
175175

176+
- uses: actions/download-artifact@v4
177+
with:
178+
name: build-es
179+
path: build/es/
180+
176181
- name: Deploy
177182
uses: reggionick/s3-deploy@v4
178183
with:
@@ -198,7 +203,7 @@ jobs:
198203
runs-on: ubuntu-latest
199204
strategy:
200205
matrix:
201-
locale: [zh, tr, ru]
206+
locale: [zh, tr, ru, es]
202207
concurrency:
203208
group: build-locale-only-${{ matrix.locale }}-${{ github.workflow }}-${{ github.ref }}
204209
cancel-in-progress: true
@@ -262,19 +267,25 @@ jobs:
262267
name: build-tr-only-ru
263268
path: build/ru/
264269

270+
- uses: actions/download-artifact@v4
271+
with:
272+
name: build-tr-only-es
273+
path: build/es/
274+
265275
# Sync locale directories to S3 using AWS CLI —
266-
# only overwrites zh/, tr/, and ru/ prefixes, leaves English untouched.
276+
# only overwrites zh/, tr/, ru/, and es/ prefixes, leaves English untouched.
267277
- name: Deploy translations to S3
268278
run: |
269279
aws s3 sync build/zh/ s3://${{ secrets.S3_BUCKET }}/zh/ --delete
270280
aws s3 sync build/tr/ s3://${{ secrets.S3_BUCKET }}/tr/ --delete
271281
aws s3 sync build/ru/ s3://${{ secrets.S3_BUCKET }}/ru/ --delete
282+
aws s3 sync build/es/ s3://${{ secrets.S3_BUCKET }}/es/ --delete
272283
273284
- name: Invalidate locale paths
274285
run: |
275286
aws cloudfront create-invalidation \
276287
--distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
277-
--paths "/zh/*" "/tr/*" "/ru/*"
288+
--paths "/zh/*" "/tr/*" "/ru/*" "/es/*"
278289
279290
- name: Update deployment tag
280291
run: |

astro.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export default defineConfig({
8787
react(),
8888
sitemap({
8989
// Exclude localized pages — they are covered by locale-specific sitemaps (e.g. sitemap-zh-index.xml)
90-
filter: (page) => !page.includes('/docs/zh/') && !page.includes('/docs/tr/') && !page.includes('/docs/ru/'),
90+
filter: (page) => !page.includes('/docs/zh/') && !page.includes('/docs/tr/') && !page.includes('/docs/ru/') && !page.includes('/docs/es/'),
9191
// Strip trailing slashes so sitemap URLs match the canonical tags emitted by DocsLayout.
9292
// The canonical strips trailing slashes (DocsLayout.astro:35-37), so the sitemap must too.
9393
// Mismatch causes the Algolia crawler (ignoreCanonicalTo: false) to skip sitemap entries.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"type": "module",
44
"version": "0.0.1",
55
"scripts": {
6-
"dev": "npm run prebuild && npm run build:md && NODE_OPTIONS='--max-old-space-size=8192' BUILD_LOCALES=none astro dev",
6+
"dev": "npm run prebuild && npm run build:md && NODE_OPTIONS='--max-old-space-size=8192' astro dev",
77
"prebuild": "mkdir -p public/api-specs && rm -rf public/FF_img public/img_webhook_flows public/img && [ -d src/assets/shared/img ] && cp -r src/assets/shared/img public/ || true && [ -d src/assets/shared/FF_img ] && cp -r src/assets/shared/FF_img public/ || true && [ -d src/content/docs/version-3.0/img_webhook_flows ] && cp -r src/content/docs/version-3.0/img_webhook_flows public/ || true && [ -d src/api-reference/specs ] && cp -r src/api-reference/specs/* public/api-specs/ || true",
88
"build": "astro build && npm run build:md:prod",
99
"build:md": "node scripts/generate-md.mjs && node scripts/generate-llms.mjs && node scripts/generate-platform-llms-full.mjs",
@@ -14,6 +14,8 @@
1414
"translate:tr:build": "node scripts/translate.mjs --lang tr --incremental",
1515
"translate:ru": "node scripts/translate.mjs --lang ru",
1616
"translate:ru:build": "node scripts/translate.mjs --lang ru --incremental",
17+
"translate:es": "node scripts/translate.mjs --lang es",
18+
"translate:es:build": "node scripts/translate.mjs --lang es --incremental",
1719
"preview": "astro preview",
1820
"astro": "astro",
1921
"check-links": "node scripts/check-links/index.mjs",

scripts/translate.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const LANGUAGE_NAMES = {
4040
ja: 'Japanese (ja-JP)',
4141
tr: 'Turkish (tr-TR)',
4242
ru: 'Russian (ru-RU)',
43+
es: 'Spanish (es-ES)',
4344
};
4445

4546
// Locale-specific suffix for metadataTitle values (the part after the page title)
@@ -48,6 +49,7 @@ const METADATA_TITLE_SUFFIXES = {
4849
ja: '| Adapty ドキュメント',
4950
tr: '| Adapty Dokümanları',
5051
ru: '| Документация Adapty',
52+
es: '| Documentación de Adapty',
5153
};
5254

5355
// ---------------------------------------------------------------------------

src/components/Homepage.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,31 @@ const T = {
102102
kmp: "Интеграция Adapty с Kotlin Multiplatform",
103103
capacitor: "Интеграция Adapty с Capacitor",
104104
},
105+
es: {
106+
hero: "Adapty es una plataforma de compras in-app potente y flexible que te ayuda a hacer crecer tu base de suscriptores. Tanto si estás empezando como si ya tienes millones de usuarios, Adapty te permite establecer fácilmente los mejores precios de suscripción, probar distintos enfoques y descubrir qué funciona mejor para el éxito de tu app.",
107+
discoverTitle: "¿Es Adapty adecuado para mí?",
108+
discoverDesc: "Encuentra tu caso de uso — tanto si vas a lanzar una nueva app, optimizar los ingresos o migrar desde otra herramienta.",
109+
quickstartTitle: "Guía de inicio rápido",
110+
quickstartDesc: "Conecta Adapty con tus cuentas de tienda, configura tus productos y deja que Adapty gestione las compras en tu app.",
111+
quickstartBtn: "Configurar Adapty en mi app",
112+
nextTitle: "¿Qué quieres hacer a continuación?",
113+
abTitle: "Lanza tu primer test A/B",
114+
abDesc: "Crea tests A/B, despliégalos a tus usuarios y haz seguimiento de las variantes con mejor rendimiento.",
115+
analyticsTitle: "Explora las analíticas",
116+
analyticsDesc: "Revisa métricas detalladas sobre la monetización de tu app.",
117+
integrationsTitle: "Conecta integraciones",
118+
integrationsDesc: "Envía eventos a los servicios externos de analítica y atribución que tu equipo ya utiliza.",
119+
paywallTitle: "Diseña paywalls en el builder no-code",
120+
paywallDesc: "Crea paywalls en minutos con Adapty Paywall Builder. Modifica tus paywalls sin publicar una nueva versión de tu app.",
121+
platformsTitle: "SDKs para cada plataforma",
122+
ios: "Guía para integrar Adapty con iOS",
123+
android: "Guía para integrar Adapty con Android",
124+
reactNative: "Guía para integrar Adapty con React Native",
125+
flutter: "Guía para integrar Adapty con Flutter",
126+
unity: "Guía para integrar Adapty con Unity",
127+
kmp: "Guía para integrar Adapty con Kotlin Multiplatform",
128+
capacitor: "Guía para integrar Adapty con Capacitor",
129+
},
105130
} as const;
106131

107132
interface HomepageProps {

0 commit comments

Comments
 (0)