t1k:cocos:playable:localize
| Field | Value |
|---|---|
| Module | playable |
| Version | 2.14.4 |
| Effort | high |
| Tools | — |
Keywords: dich-thuat, i18n, language-switch, locale, localization, localize, multi-language, multilanguage, ngon-ngu, playable-localize, translate, translation
How to invoke
Section titled “How to invoke”/t1k:cocos:playable:localize[--langs es,pt-BR,ja] [--assets <path>] [--scan-only] [--no-prefab] [--json]Cocos Playable — End-to-End Localization
Section titled “Cocos Playable — End-to-End Localization”Turn an English-only playable into a multi-language one in a single run: scan → translate → generate → wire → verify. Built on @playablelabs/localization, whose auto-binder means ordinary labels need no component and no key.
Decision Tree
Section titled “Decision Tree”| Intent | Path |
|---|---|
| ”Localize this game” / “add multi-language” | Full run → Workflow |
| ”What text does this game have?” | --scan-only → stop after Phase 1 |
| ”Translate but don’t touch my prefabs” | --no-prefab → skip Phase 6 |
| ”Add one more language later” | Re-run; Phase 4 merges into existing tables, never overwrites |
| ”Text still shows English at runtime” | Gotchas rows 1-3 |
Default languages
Section titled “Default languages”es, pt-BR, fr, de, ja, ko, zh-CN, ru, id, vi — the ten markets that carry most playable-ad revenue. Override with --langs. English is never a table: it is the text already authored in the editor.
Workflow
Section titled “Workflow”Phase 0 — Preflight
Section titled “Phase 0 — Preflight”- Locate the Cocos
assets/dir (--assets, else find the dir containingscene/+scripts/). - Confirm
@playablelabs/localizationexists underassets/packages/@playablelabs/. If absent, STOP and tell the user to install it — this skill wires that package, it does not vendor a copy. - Inspect
git status --short; preserve unrelated user changes and keep localization edits separable.
Phase 1 — Scan
Section titled “Phase 1 — Scan”node <skill>/scripts/scan-text.cjs --assets <assets-dir> --jsonReports unique translatable strings, non-text strings it skipped, code sites assigning .string, and font coverage. Read references/scan-report-guide.md for how to interpret each field.
Never hand-roll this scan. A scene often contains zero labels because all text lives in prefabs, and prefab instances override text via CCPropertyOverrideInfo — the script handles both.
The scanner cannot read words baked into PNG/JPG pixels. Inspect title, CTA, win/lose, and logo sprites in the actual browser preview; replace baked UI copy with Labels or provide an explicit per-locale sprite system before declaring the game localized.
Phase 2 — Font gate (HARD GATE)
Section titled “Phase 2 — Font gate (HARD GATE)”Offer: per-language fonts (keep the designed face where it works, swap only for scripts it cannot draw — TextBinder.setFontMap) · a subset font covering every shipped language · drop the non-Latin languages · proceed anyway, explicitly accepting blank text. No override flag skips this gate.
State the visual cost of each option before asking. A font covering CJK is never a display face, so “one font for everything” always means the UI typeface changes — say so rather than presenting it as free.
Subsetting recipe when a covering font is needed: include the scanned strings plus printable ASCII, then subset an official TrueType source instead of renaming a CFF/OTF file. For Noto CJK, instantiate NotoSansCJKsc-VF.ttf at wght=400 with fontTools.varLib.instancer, then subset it with fontTools.subset and --no-hinting. Keep the SIL OFL license beside the project notices. Never ship an OS font — Windows and macOS faces are not redistributable.
Re-verify the written output, not the in-memory object: reopen it with TTFont, require glyf + loca, and compare its cmap against every locale value and printable ASCII. A typical playable subset is roughly 50–150KB depending on copy.
Phase 3 — Translate
Section titled “Phase 3 — Translate”Translate the scanned strings per references/translation-guide.md: keep length within the source’s budget (buttons do not reflow), preserve {placeholder} tokens exactly, match the game’s register, never translate brand names.
Phase 4 — Generate locale tables
Section titled “Phase 4 — Generate locale tables”Write assets/scripts/localization/locales/<lang>.ts plus a barrel, using the source English string as the key. Merge with any existing table — never drop a key a human edited. Shape and examples: references/wiring-guide.md.
Phase 5 — Wire the code
Section titled “Phase 5 — Wire the code”- Insert one
setupLocalization({ locales, fallback: 'en' })at the earliest bootstrap (usuallyGameView.onLoad, or right after ad parameters arrive). - Convert every code write the scan flagged. Use
Localization.setText(label, key, params)for translated copy andLocalization.setLiteralText(label, value)for percentages, timers, IDs, and other non-translatable dynamic text. Any rawlabel.string = ...can be overwritten by the next language or font sweep. - After a parameter tool applies
Label.string, rebind it immediately withsetTextorsetLiteralText. Include parameter defaults (text,title,subtitle,ctaText) in locale coverage. - Optional: add a language
SelectParameterso the ad network can pick the locale.
Phase 6 — Prefab roots (gated)
Section titled “Phase 6 — Prefab roots (gated)”Prefabs spawned through a pool or instantiate may not be in the scene when the binder sweeps. Add LocalizedRoot to those prefab roots (one per prefab, not per label). Mutating .prefab files requires an explicit AskUserQuestion confirmation and a git branch. Skipped entirely under --no-prefab.
Phase 7 — Verify and report
Section titled “Phase 7 — Verify and report”Run node --test <skill>/tests/scan-text.test.cjs, then run scan-text.cjs again: every raw .string site should now be gone. Preview the real game in a browser through gameplay and both end cards; a clean text scan does not prove image-baked copy is localized. Report per language: strings translated, keys skipped, placeholder mismatches, baked-text sprites, font risk, files changed, and anything left for a human.
Hard rules
Section titled “Hard rules”- Never mutate
.prefab/.sceneto inject translated text. The runtime translates; the editor keeps English. Rewriting authored text destroys the key the binder relies on. - Never write an
entable. Namingenasfallbackregisters it; a key missing everywhere resolves to itself. - Never translate numbers, percentages,
{placeholder}tokens, asset paths, or brand/product names. - Never leave numeric/dynamic text on raw
.string. Bind it withsetLiteralTextso a scene/font sweep cannot restore a stale captured value. - Never ship blank table values. Blank/invalid translations fall back safely but remain release-blocking diagnostics.
- Re-runs merge. Existing translated values win over freshly generated ones unless the source English string changed.
Gotchas
Section titled “Gotchas”| Symptom | Cause |
|---|---|
| Text still English at runtime | setupLocalization never called, or called after the labels enabled |
| One screen never translates | Its prefab is pooled/detached — needs LocalizedRoot on the root |
| Dynamic label shows a stale value after switching | Site still assigns .string directly instead of Localization.setText |
| Percentage/timer jumps back after font load | Non-translatable text needs Localization.setLiteralText |
| Parameter text reverts | Parameter apply wrote .string without rebinding it afterward |
pt-BR/zh-CN never selects | Use runtime tags with hyphens; identifier exports pt_BR/zh_CN are normalized by package v0.2+ |
Explicit key renders ui.some_key | Provide English fallback text to setText/LocalizedLabel |
| Whole playable renders in the wrong language | fallback names a language that has no table and an old package build; upgrade — current versions auto-register it |
| ja/ko/zh/ru shows empty boxes | Custom TTF lacks those glyphs — Phase 2 gate |
| A translated string overflows its button | Length budget ignored; see references/translation-guide.md |
| Scan finds text nobody sees | It read a source prefab whose instance overrides _string; the script prefers overrides — do not bypass it |
Cocos Creator 3.8.7 playable ads using @playablelabs/localization. Per-language fonts are supported via TextBinder.setFontMap. Localized sprites and RTL layout are not — the package does not support them.