---
name: locale-tui-localization
description: Use this skill when users request i18n/localization updates for Android string resources: adding localized keys, translating strings.xml, filling missing translations, or moving hardcoded UI text into resources.
---

# Localization

You write the translations yourself, or delegate them to a model you choose. `locale-tui` only
validates and writes `strings.xml`; this workflow never calls a translation API and needs no
`OPENAI_API_KEY`.

## When to use

- Adding a user-visible string (all configured languages, unless the user says otherwise).
- Translating or fixing `strings.xml` across locales.
- Finding untranslated text: missing keys, or Chinese literals hardcoded in Kotlin.

## Commands

Run from the repository root.

```bash
uv run --directory locale-tui src/main.py missing [-m app] [--json]   # missing keys, Chinese English sources
uv run --directory locale-tui src/main.py hardcoded [-m app]          # Chinese string literals in source
uv run --directory locale-tui src/main.py apply strings.json [-m app] [--dry-run]
```

Modules and languages come from `locale-tui/config.yml` (currently `values`, `values-zh`,
`values-zh-rTW`, `values-ja`, `values-ko-rKR`, `values-ru`; modules `app`, `search`, ...).

`apply` takes `{"key": {"values": "English", "values-zh": "…", …}}`. New keys need every
language; existing keys may update only some. It escapes `'` and `"`, rejects placeholder
mismatches (`%1$s`, `%d`, …), unknown language codes and Chinese English sources, and writes each
file once. Write the JSON under the job's temporary directory, not the repository.

## Workflow

1. **Collect** what needs text: new keys from the task, `missing`, or `hardcoded`. Of the
   `hardcoded` hits, move only user-visible text (labels, messages, content descriptions,
   accessibility labels) into resources. Leave search keywords, provider and brand names, prompts
   and data meant for models, log messages and debug-only pages in code.
2. **Name keys** by page or feature prefix (`setting_page_`, `im_`, `whale_theme_`, …), reusing
   an existing key when the text and meaning match.
3. **Translate.** Translation is cheap work; pick the cheapest model that does it well:
   - Up to about 10 strings: translate them yourself in the same turn; a sub-agent costs more
     to start than it saves.
   - Larger batches: split them by feature and hand each group to a sub-agent on a small model.
     Use `haiku` by default, including character and brand copy, and give it the tone notes
     from the guidelines below. Move up to `sonnet` only for a group that haiku already got
     wrong. Never use the top-tier model (Opus) for translation.
   - An agent without sub-agents translates the batches itself.
   - Sub-agents return JSON only. You merge their results and run `apply` once, so no two
     writers touch the same `strings.xml`.
   - Give whoever translates the glossary below and the surrounding screen, so short labels
     are translated in context.
4. **Apply** with `--dry-run` first, then for real. Replace the literals in code with
   `stringResource(R.string.…)` (Compose) or `context.getString(…)` outside composition.
5. **Verify**: `missing` reports the module complete, and `./gradlew :app:lintDebug
   :app:assembleDebug` passes. Report the files and keys changed.

## Translation guidelines

- App context: an AI chat client for many model providers, with an IM-style "Easy chat" mode and
  companion characters (Miffan the rice bowl, the "Blue Whale" 蓝色大肥鱼).
- Keep placeholders, `\n` and escapes exactly. Keep UI text concise; match the source's tone
  (friendly, plain, no exclamation spam).
- Do not translate brand or technical names: OpenAI, Anthropic, OpenRouter, MCP, API, Token.
- Chinese AI terms: prompt → 提示词 (not 提示), context → 上下文, model → 模型,
  inference → 推理. Easy chat → 轻松聊天 / 輕鬆聊天 / かんたんチャット / 간편 채팅 / Простой чат.
- Traditional Chinese uses Taiwan vocabulary (資料, 設定, 訊息), not converted Simplified.
- Language names in pickers are endonyms and stay untranslated (`translatable="false"`).

## Constraints

- English (`values`) is the source; never leave Chinese as the English text.
- If the user asks to skip localization, add only `values` (and `values-zh` when useful) and say so.
- Never commit API keys or translation scratch files.
