# @combos-fun/plugin-renderer-text

@combos-fun/plugin-renderer-text — part of the Combos Fun engine monorepo.

Keywords: `text`, `label`, `font`, `string`, `2d`.

## Documentation

- Per-package agent / developer notes: [`agent-skill.md`](./agent-skill.md)
- Machine-readable manifest: [`combos-plugin.json`](./combos-plugin.json)
  (validated against `schemas/combos-plugin.schema.json` in the repo
  root)
- Entry skill (single source of truth for AI agents working with this
  monorepo): `skills/combos-engine-development/SKILL.md`

When consumed from npm, the manifest and agent notes are exposed as
stable subpaths:

```ts
import manifest from '@combos-fun/plugin-renderer-text/plugin-manifest';
// or fetch the markdown directly:
// require.resolve('@combos-fun/plugin-renderer-text/agent-skill')
```

## Host-driven i18n

Configure the source locale on the text system. Locale names use BCP 47 tags.

```ts
const textSystem = new TextSystem({
  sourceLocale: 'zh-CN',
  sourceTexts: [
    '{player} 获得了 {score} 分',
    '开始游戏',
    '下一屏文案',
  ],
});
```

Text uses its source template as the translation lookup key. Named replacements
keep dynamic values independent from translated word order.

```ts
new Text({
  text: '{player} 获得了 {score} 分',
  replacements: {
    player: 'Alice',
    score: 100,
  },
});
```

The host injects `window.combos` into the game HTML head. The first
`requestI18n` runs in `beforeReady` and holds `combos-game:ready` / `start`
until the host replies. Labels stay empty until that reply so source copy does
not paint first. Put every later-screen template in `sourceTexts` so
those screens reuse the startup catalog instead of flashing source text.
The system keeps one growing session catalog of source templates and
translations; later `requestI18n` calls only send templates still missing
from that catalog.
If the host returns `needed: true` but a template is missing from
`translations`, the system sends only the still-untranslated texts and waits
for one more `requestI18n` before showing that label. Returning to copy that is already in
`translations` does not call `requestI18n` again. `needed: false` keeps source
text.
Translation is applied only when the result is `needed: true`:

```ts
if (typeof window.combos?.requestI18n !== 'function') return;

// First call (beforeReady): await, then start / ready
const result = await window.combos.requestI18n({
  sourceLocale: 'zh-CN',
  sourceTexts: [
    '{player} 获得了 {score} 分',
    '开始游戏',
  ],
});
// { needed: false } → keep source text
// { needed: true, locale, translations } → replace, then enter the game
```

Missing replacements fall back to the unchanged placeholder. Without the
injected SDK, the game stays on the source text.

## License

Internal workspace package, part of the Combos Fun engine monorepo.
