Agent Skills › openchamber/openchamber › locale-ui-patterns

locale-ui-patterns

GitHub

规范前端 UI 文本的国际化流程,强制所有用户可见字符串通过 i18n 库处理,禁止硬编码英文。规定翻译必须即时完成,提供从添加键值到组件调用的标准步骤及命名约定,确保多语言应用的一致性与正确性。

.agents/skills/locale-ui-patterns/SKILL.md openchamber/openchamber

Trigger Scenarios

创建或修改 UI 文案、标签、按钮文字 涉及多语言翻译或国际化配置时

Install

npx skills add openchamber/openchamber --skill locale-ui-patterns -g -y
More Options

Non-standard path

npx skills add https://github.com/openchamber/openchamber/tree/main/.agents/skills/locale-ui-patterns -g -y

Use without installing

npx skills use openchamber/openchamber@locale-ui-patterns

指定 Agent (Claude Code)

npx skills add openchamber/openchamber --skill locale-ui-patterns -a claude-code -g -y

安装 repo 全部 skill

npx skills add openchamber/openchamber --all -g -y

预览 repo 内 skill

npx skills add openchamber/openchamber --list

SKILL.md

Frontmatter
{
    "name": "locale-ui-patterns",
    "description": "Use when creating or modifying OpenChamber UI text, labels, buttons, placeholders, aria labels, empty states, toasts, dialogs, settings copy, navigation labels, or any user-facing strings."
}

Locale UI Patterns

Core Rule

User-facing UI text must go through @/lib/i18n; do not hardcode English strings in components.

What the text says

UI text tells the person what they get and what to do: what an option gives them, what happened, what fixes it. Engineering reasons (layout jumps, races, what the code avoids) belong in code comments and commit messages. Example: "Shows the model's thinking as it arrives. When off, the block stays folded and opens with a click." An error names the known cause and the action ("reconnect the repository's account in Settings"), never a generic "something went wrong" when the cause is known; correcting such text is ordinary implementation work, not a product decision.

Translate everything immediately (no English placeholders)

Every key you add to a non-English dictionary MUST contain a real translation in that language — never the English source string as a stand-in. There is NO "leave it in English for now" convention in this project; if an agent told you there was, it was wrong. Copying the English value into es.ts/fr.ts/ko.ts/pl.ts/pt-BR.ts/uk.ts/zh-CN.ts/zh-TW.ts is a defect, not a deferral. The app ships every locale at once, so an untranslated key is a visible bug for those users.

If you genuinely cannot translate a language, say so explicitly to the user instead of silently pasting English. Do not invent a fallback policy.

Required Flow

  1. Add or reuse a key in packages/ui/src/lib/i18n/messages/en.ts.
  2. Add the same key — fully translated, not the English text — to every non-English dictionary in packages/ui/src/lib/i18n/messages/.
  3. In components, call const { t } = useI18n() from @/lib/i18n and render t('key').
  4. For locale names or language picker labels, use label(locale) from useI18n().
  5. Keep locale state in packages/ui/src/lib/i18n/*; do not add locale fields to broad stores like useUIStore.
  6. Do not remount the app to update language. Components must re-render through useI18n().

Component Usage Rules

  • Import from @/lib/i18n, not deep files.
  • Keep t(...) calls inside React render/hook scope so locale changes re-render text.
  • Do not resolve translated text at module scope.
  • For static option arrays, store labelKey / descriptionKey; resolve with t(...) inside the component.
  • For non-React helpers, pass translated strings in from the component or pass t explicitly.

Key Style

Use stable semantic keys, not English text as keys.

Keys should describe location + UI role + meaning. They should not encode current copy wording.

Use existing nearby naming when extending a surface. If no nearby pattern exists, choose a short path that mirrors the UI ownership.

Namespaces like layout.*, settings.*, chat.*, git.*, session.*, toast.*, and dialog.* are examples, not a fixed exhaustive list.

Good:

'settings.appearance.language.label': 'Language'
'layout.mainTab.chat': 'Chat'
'chat.input.placeholder': 'Ask OpenChamber...'

Bad:

'Language': 'Language'
'chatLabel': 'Chat'
'askOpenChamberDotDotDot': 'Ask OpenChamber...'

Avoid overly generic keys unless the text is truly global and context-independent. Prefer specific keys when button meaning can vary by surface.

Parameters

Use {name} placeholders for dynamic values.

'toast.language.changed': 'Language changed to {language}'
t('toast.language.changed', { language: label(locale) })

Do not pass grammar fragments as params. Never use params like {suffix}, {plural}, {article}, {prefix}, {dateSuffix}, or pieces of words/sentences.

Bad:

t('dialog.delete.description', { count, suffix: count === 1 ? '' : 's' })

Good:

count === 1
  ? t('dialog.delete.descriptionSingle', { count })
  : t('dialog.delete.descriptionPlural', { count })

Plural/count-dependent text must use separate complete-message keys unless all supported locales can use one identical complete sentence. Placeholders are only for real values ({count}, {name}, {path}), not grammar.

Optional clauses must also be complete-message keys. Do not build a sentence by injecting a translated phrase into another translated sentence.

Bad:

t('dialog.delete.description', {
  dateLabel: date ? t('dialog.delete.dateSuffix', { date }) : '',
})

Good:

date
  ? t('dialog.delete.descriptionWithDate', { count, date })
  : t('dialog.delete.description', { count })

Desktop Native Menus

The Electron app menu and the right-click context menu are native, outside @/lib/i18n. Their labels live in packages/electron/menu-locales.mjs (MENU_LOCALE_DICTIONARIES, CONTEXT_MENU_LABEL_DICTIONARIES), keyed by the same locale codes as the UI. A new menu item gets its label in every locale there, and a new UI language gets its own entry there in the same change; menu-locales.test.mjs checks key parity.

Adding a UI Language

A new locale code lands in all of these in one change:

  • packages/ui/src/lib/i18n/runtime.ts: the Locale union, LOCALES, LOCALE_LABEL_KEYS, and normalizeLocale (include legacy tags the platforms still send, such as iw for Hebrew). A right-to-left language also joins RTL_LOCALES; the layout side is owned by rtl-layout.
  • store.ts (lazy import), intl.ts (Intl tag), bootstrap.ts (startup strings), messages.test.ts.
  • messages/<locale>.ts and messages/<locale>.settings.ts, a <locale> block in every messages/*.i18n.ts, and a common.language.<name> label in every existing dictionary.
  • packages/electron/menu-locales.mjs (see above).
  • packages/web/server/lib/opencode/settings-helpers.js UI_LOCALE_VALUES: without it the server drops the saved choice. packages/web/server/lib/walkthrough/languages.js names the language for walkthrough prompts.

Translation Boundary

Translate visible text, placeholders, tooltips, dialogs, toasts, empty/error/loading states, and user-facing aria-label, title, and alt text.

Keep these literal:

  • Product names: OpenChamber, OpenCode, GitHub
  • Protocol/tool acronyms: MCP, SSE, WebSocket, API
  • Model/provider names
  • File paths, command names, environment variables
  • User/generated content

Completion Criteria

  • No new hardcoded user-facing English in changed UI files.
  • Every new key exists in all dictionaries with a real translation, including packages/electron/menu-locales.mjs for native menu labels.
  • All translated values are resolved inside a reactive render/hook boundary.
  • No locale state added to broad/shared stores.
  • No full app remount for locale changes.
  • Locale switch preserves current UI state.

Version History

  • bf977d2 Current 2026-10-19 13:04

    新增阿拉伯语和希伯来语支持,实现 RTL 布局镜像适配及移动端抽屉方向调整

  • 2db90f7 2026-08-20 04:55

    细化 Agent Skill 指导文档,明确技能用途和核心规则。

  • 74b1bd8 2026-07-25 10:36

Same Skill Collection

.agents/skills/changelog-authoring/SKILL.md
.agents/skills/clack-cli-patterns/SKILL.md
.agents/skills/communication-style/SKILL.md
.agents/skills/desktop-shell/SKILL.md
.agents/skills/drag-to-reorder/SKILL.md
.agents/skills/enterprise-boundary/SKILL.md
.agents/skills/isolated-space-boundary/SKILL.md
.agents/skills/openchamber-change-discipline/SKILL.md
.agents/skills/opencode-v2/SKILL.md
.agents/skills/performance-engineering/SKILL.md
.agents/skills/pr-review/SKILL.md
.agents/skills/relay-transport/SKILL.md
.agents/skills/rtl-layout/SKILL.md
.agents/skills/serve-sim/SKILL.md
.agents/skills/settings-ui-patterns/SKILL.md
.agents/skills/sync-state-invariants/SKILL.md
.agents/skills/theme-system/SKILL.md
.agents/skills/triage-issues/SKILL.md
.agents/skills/triage-prs/SKILL.md
.agents/skills/ui-api-decoupling/SKILL.md
.agents/skills/update-changelog/SKILL.md
.agents/skills/writing-for-agents/SKILL.md

Metadata

Files
0
Version
bf977d2
Hash
7fb78ea3
Indexed
2026-07-25 10:36

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-11-01 14:18
浙ICP备14020137号-1