vtex-io-messages-and-i18n

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Messages & Internationalization

消息与国际化

When this skill applies

本技能适用场景

Use this skill when a VTEX IO app needs translated copy instead of hardcoded strings.
  • Adding localized UI text to storefront or Admin apps
  • Creating or updating
    /messages/*.json
    translation files
  • Defining message keys in
    context.json
  • Reviewing React, Admin, or backend code that currently hardcodes user-facing copy
  • Integrating app-specific translations with
    vtex.messages
Do not use this skill for:
  • general UI layout or component composition
  • authorization, policies, or auth tokens
  • service runtime sizing
  • choosing between HTTP, GraphQL, and event-driven APIs
当VTEX IO应用需要使用翻译文案而非硬编码字符串时使用本技能。
  • 为店面或管理后台应用添加本地化UI文本
  • 创建或更新
    /messages/*.json
    翻译文件
  • context.json
    中定义消息键
  • 评审当前硬编码了用户可见文案的React、管理后台或后端代码
  • 将应用专属翻译与
    vtex.messages
    集成
本技能不适用于:
  • 通用UI布局或组件组合
  • 授权、权限策略或身份令牌
  • 服务运行时规格调整
  • HTTP、GraphQL和事件驱动API的选型

Decision rules

决策规则

  • Use the
    messages
    builder and translation files for user-facing copy instead of hardcoding labels, button text, or UI messages in source code.
  • Keep translation keys stable, explicit, and scoped to the app domain instead of generic keys such as
    title
    or
    button
    . The exact format may vary, but keys should remain specific, descriptive, and clearly owned by the app.
  • Prefix message IDs according to their UI surface or domain, for example
    store/...
    for storefront messages and
    admin/...
    for Admin or Site Editor messages, so keys stay organized and do not collide across contexts.
  • Define message keys in
    /messages/context.json
    so VTEX IO can discover and manage the app’s translation surface. Keep it as a flat map of
    messageId -> description
    and include the keys the app actually uses.
  • Keep translated message payloads small and app-focused. Do not turn the messages system into a general content store.
  • In React or Admin UIs, prefer message IDs and localization helpers over literal copy in JSX.
  • In backend or GraphQL flows, translate only when the app boundary truly needs localized text; otherwise return stable machine-oriented data and let the caller localize the presentation.
  • Use app-level overrides of
    vtex.messages
    only when the app truly needs to customize translation behavior or message resolution beyond normal app-local message files.
  • 用户可见文案使用
    messages
    构建器和翻译文件管理,不要在源代码中硬编码标签、按钮文本或UI消息。
  • 翻译键要保持稳定、明确,且归属到应用域名下,不要使用
    title
    button
    这类通用键。具体格式可以灵活调整,但键必须是具体、描述性强,且明确归属于当前应用的。
  • 消息ID要根据其所属UI界面或域名添加前缀,例如店面消息用
    store/...
    前缀,管理后台或站点编辑器消息用
    admin/...
    前缀,保证键有序且不会在不同上下文下冲突。
  • /messages/context.json
    中定义消息键,以便VTEX IO发现和管理应用的翻译范围。该文件要保存为
    消息ID -> 描述
    的扁平映射,且仅包含应用实际使用的键。
  • 翻译消息负载要保持精简、聚焦于应用本身。不要将消息系统用作通用内容存储。
  • 在React或管理后台UI中,优先使用消息ID和本地化辅助工具,不要在JSX中直接写字面量文案。
  • 在后端或GraphQL流程中,仅当应用边界确实需要本地化文本时才做翻译;否则返回稳定的面向机器的数据,由调用方自行处理展示层的本地化。
  • 仅当应用确实需要定制超出常规应用本地消息文件的翻译行为或消息解析逻辑时,才在应用层重写
    vtex.messages

Hard constraints

硬性约束

Constraint: User-facing strings must come from the messages infrastructure

约束:用户可见字符串必须来自消息基础设施

User-facing strings MUST come from the messages infrastructure instead of being hardcoded in components, handlers, or resolvers.
Why this matters
Hardcoded copy breaks localization, makes message review harder, and creates inconsistent behavior across storefront, Admin, and backend flows.
Detection
If you see labels, buttons, headings, alerts, or other user-facing text embedded directly in JSX or backend response formatting for a localized app, STOP and move that copy to message files.
Correct
tsx
<FormattedMessage id="admin/my-app.save" />
Wrong
tsx
<button>Save</button>
用户可见字符串必须来自消息基础设施,不能硬编码在组件、处理器或解析器中。
重要性
硬编码的文案会破坏本地化能力,增加消息评审难度,还会导致店面、管理后台和后端流程的行为不一致。
检测方式
如果在面向本地化的应用中,发现JSX或后端响应格式化代码中直接嵌入了标签、按钮、标题、提示或其他用户可见文本,请立即停止开发,将该文案迁移到消息文件中。
正确示例
tsx
<FormattedMessage id="admin/my-app.save" />
错误示例
tsx
<button>Save</button>

Constraint: Message keys must be declared and organized explicitly

约束:消息键必须显式声明并有序组织

Message keys MUST be app-scoped and represented in the app’s message configuration instead of being invented ad hoc in code.
Why this matters
Unstructured keys become hard to maintain, collide across app areas, and make message ownership unclear.
Detection
If code introduces new message IDs with no corresponding translation files or
context.json
entry, STOP and add the message contract explicitly.
Correct
json
{
  "admin/my-app.save": "Save"
}
Wrong
json
{
  "save": "Save"
}
消息键必须归属到应用下,且在应用的消息配置中声明,不能在代码中临时随意创建。
重要性
非结构化的键难以维护,会在应用不同模块间发生冲突,也会导致消息归属不清晰。
检测方式
如果代码中引入了新的消息ID,但没有对应的翻译文件或
context.json
条目,请立即停止开发,显式添加消息约定。
正确示例
json
{
  "admin/my-app.save": "Save"
}
错误示例
json
{
  "save": "Save"
}

Constraint: The messages system must not be used as a general content or configuration store

约束:消息系统不得用作通用内容或配置存储

Translation files MUST contain localized copy, not operational configuration, secrets, or large content payloads.
Why this matters
The messages infrastructure is designed for translated strings. Using it for other data creates maintenance confusion and mixes localization concerns with configuration or content storage.
Detection
If message files contain API URLs, credentials, business rules, or long structured content blobs, STOP and move that data to app settings, configuration apps, or a content-specific mechanism.
Correct
json
{
  "store/my-app.emptyState.title": "No records found"
}
Wrong
json
{
  "apiBaseUrl": "https://partner.example.com",
  "featureFlags": {
    "betaMode": true
  }
}
翻译文件必须只存放本地化文案,不能存放运营配置、密钥或大容量内容负载。
重要性
消息基础设施是为翻译字符串设计的。将其用于其他数据会导致维护混乱,还会将本地化逻辑与配置或内容存储逻辑耦合。
检测方式
如果消息文件中包含API URL、凭据、业务规则或长结构化内容块,请立即停止开发,将该数据迁移到应用设置、配置应用或专属内容存储机制中。
正确示例
json
{
  "store/my-app.emptyState.title": "No records found"
}
错误示例
json
{
  "apiBaseUrl": "https://partner.example.com",
  "featureFlags": {
    "betaMode": true
  }
}

Preferred pattern

推荐模式

Recommended file layout:
text
.
├── messages/
│   ├── context.json
│   ├── en.json
│   └── pt.json
└── react/
    └── components/
        └── SaveButton.tsx
Minimal messages setup:
json
// messages/context.json
{
  "admin/my-app.save": "Label for the save action in the admin settings page"
}
json
// messages/en.json
{
  "admin/my-app.save": "Save",
  "store/my-app.emptyState.title": "No records found"
}
json
// messages/pt.json
{
  "admin/my-app.save": "Salvar"
}
tsx
import { FormattedMessage } from 'react-intl'

export function SaveButton() {
  return <FormattedMessage id="admin/my-app.save" />
}
Backend or GraphQL translation pattern:
graphql
scalar IOMessage

type ProductLabel {
  id: ID
  label: IOMessage
}

type Query {
  productLabel(id: ID!): ProductLabel
}
typescript
export const resolvers = {
  Query: {
    productLabel: async (_: unknown, { id }: { id: string }) => {
      return {
        id,
        label: {
          content: 'store/my-app.product-label',
          description: 'Label for product badge',
          from: 'en-US',
        },
      }
    },
  },
}
Keep a complete
en.json
as the default fallback, even when the app’s main audience uses another locale, so the messages system has a stable base for resolution and auto-translation behavior.
Use translated IDs in code, keep translation files explicit, and centralize user-facing copy in the messages system instead of scattering literals through the app.
推荐的文件结构:
text
.
├── messages/
│   ├── context.json
│   ├── en.json
│   └── pt.json
└── react/
    └── components/
        └── SaveButton.tsx
最小化消息配置:
json
// messages/context.json
{
  "admin/my-app.save": "Label for the save action in the admin settings page"
}
json
// messages/en.json
{
  "admin/my-app.save": "Save",
  "store/my-app.emptyState.title": "No records found"
}
json
// messages/pt.json
{
  "admin/my-app.save": "Salvar"
}
tsx
import { FormattedMessage } from 'react-intl'

export function SaveButton() {
  return <FormattedMessage id="admin/my-app.save" />
}
后端或GraphQL翻译模式:
graphql
scalar IOMessage

type ProductLabel {
  id: ID
  label: IOMessage
}

type Query {
  productLabel(id: ID!): ProductLabel
}
typescript
export const resolvers = {
  Query: {
    productLabel: async (_: unknown, { id }: { id: string }) => {
      return {
        id,
        label: {
          content: 'store/my-app.product-label',
          description: 'Label for product badge',
          from: 'en-US',
        },
      }
    },
  },
}
即使应用的主要受众使用其他语言,也要保留完整的
en.json
作为默认回退版本,这样消息系统才有稳定的解析和自动翻译基础。
代码中使用翻译ID,显式维护翻译文件,将用户可见文案集中存放在消息系统中,不要在应用中散落字面量文案。

Common failure modes

常见错误场景

  • Hardcoding user-facing strings in JSX, resolvers, or handler responses.
  • Adding new message IDs in code without updating
    context.json
    or the message files.
  • Using generic or collision-prone keys such as
    title
    ,
    save
    , or
    button
    .
  • Storing configuration values or non-localized business payloads in message files.
  • Treating
    vtex.messages
    overrides as the default path instead of app-local message management.
  • 在JSX、解析器或处理器响应中硬编码用户可见字符串。
  • 在代码中添加新的消息ID,但未更新
    context.json
    或消息文件。
  • 使用
    title
    save
    button
    这类通用或容易冲突的键。
  • 在消息文件中存储配置值或非本地化的业务负载。
  • 默认重写
    vtex.messages
    ,而非优先使用应用本地消息管理能力。

Review checklist

评审检查清单

  • Are user-facing strings sourced from the messages infrastructure instead of hardcoded in code?
  • Are message keys explicit, app-scoped, and declared consistently?
  • Does
    context.json
    reflect the translation surface used by the app?
  • Are message files limited to localized copy rather than configuration or operational data?
  • Is any customization of
    vtex.messages
    truly necessary for this app?
  • 用户可见字符串是否来自消息基础设施,而非代码中硬编码?
  • 消息键是否是显式的、归属到应用下、且声明方式统一?
  • context.json
    是否覆盖了应用实际使用的所有翻译范围?
  • 消息文件是否仅存放本地化文案,没有存放配置或运营数据?
  • 所有对
    vtex.messages
    的定制是否都是应用确实需要的?

Related skills

相关技能

  • vtex-io-storefront-react
    - Use when the main question is storefront component structure and shopper-facing UI behavior
  • vtex-io-admin-react
    - Use when the main question is Admin UI structure and operational interaction patterns
  • vtex-io-graphql-api
    - Use when the main question is GraphQL schema and resolver design rather than translation infrastructure
  • vtex-io-storefront-react
    - 当核心问题是店面组件结构和面向消费者的UI行为时使用
  • vtex-io-admin-react
    - 当核心问题是管理后台UI结构和运营交互模式时使用
  • vtex-io-graphql-api
    - 当核心问题是GraphQL schema和解析器设计而非翻译基础设施时使用

Reference

参考资料