CodeGym /课程 /ChatGPT Apps /App 的本地化策略

App 的本地化策略

ChatGPT Apps
第 9 级 , 课程 0
可用

1. 为什么在 ChatGPT App 中要重视本地化

如果你做过普通的 Web 应用,本地化多半让你想到经典的 i18n:界面字符串、少量日期与数字格式、词典——然后你把它们逐一翻译好。在 ChatGPT App 中事情更有意思:第三位参与者是 LLM 模型本身。它会阅读你的工具描述、提示词、结果,进行推断并做出决策。

也就是说,语言不仅仅是“如何把文本优雅地展示给用户”,还关系到“模型如何理解你的工具在做什么、何时调用、要填写哪些参数”。按钮“购买”翻得一般用户也能看懂;但如果你用俄英混杂、表述含糊的方式去描述一个执行支付的 tool,模型可能要么永远不调用它,要么调用方式与预期完全不同。

还有一点:ChatGPT 已经会把用户的语言与所在地区提示传给你的 MCP 服务器——_meta["openai/locale"]_meta["openai/userLocation"]。这些发生在对工具的 MCP 请求层面,目的是让你可以按用户的语言和区域来适配文本与数据。也就是说平台已经把上下文“递给”你了,而开发者的任务是有策略地利用它。

因此,在本模块中我们将本地化视为 ChatGPT App 的一个架构层面,而不是“翻译一下 UI 就完事”的工作。

2. 需要本地化的各层

把 App 看成一块分层的“蛋糕”。每一层都可以(且通常应该)本地化。为避免迷失,先画一张地图。

各层概览

先给出总表,然后再逐一拆解。

是什么 示例 影响
Widget UI 小部件中所有可见的前端 标题、按钮、错误、占位符 用户的 UX
模型文本与提示词 System prompt 与预置短语 指令、回复模板 ChatGPT 的行为
数据与内容 App 展示与处理的文本 商品目录、描述、日期、价格 既影响 UX 又影响准确性
tools/模式的描述 工具与 JSON Schema 字段的元数据 description、类型提示 模型如何调用你的 tools
Commerce 与法律 一切与购买与政策相关的内容 SKU 名称、Terms、Privacy、邮件 法律合规与信任

这实际上是我们的第一层本地化地图:接下来会加上“深度”(装饰/语义)、语言以及具体元素。

现在按层逐一看。

Widget UI

最直观的一层是小部件的界面。在 GiftGenius 中包括:

  • 区块标题;
  • 字段标签(“收礼人”“预算”“兴趣”);
  • 按钮(“挑选礼物”“重置筛选”);
  • 输入框占位符(“例如,同事、妈妈……”);
  • 错误与空状态消息(“未找到礼物”)。

在普通的 React 应用里,这些是最先被抽到词典的候选。这里也是一样,只不过要记住 UI 不是整个 App,它只是其中一个面孔。

稍后在本模块里我们会为小部件做一个正规的 i18n 架构,但现在就需要明确: JSX 里不该直接写字符串。 即便你暂时只支持一种语言,也应尽早把 UI 文本结构化。

以我们的 GiftGenius 做一个迷你示例(暂不引入真实的 i18n 库):


type Locale = "en" | "ru";

const uiText = {
  en: {
    title: "GiftGenius: find a perfect present",
    recipientLabel: "Recipient",
  },
  ru: {
    title: "GiftGenius: 挑选理想礼物",
    recipientLabel: "收礼人",
  },
};

function GiftForm({ locale }: { locale: Locale }) {
  const t = uiText[locale];

  return (
    <div>
      <h2>{t.title}</h2>
      <label>{t.recipientLabel}</label>
      {/* 其余字段 */}
    </div>
  );
}

这里我们还没做“真正”的本地化,但已经明确把 UI 文本从其他层里分离出来。

GPT 文本与提示词

下一层是用户不可见、但强烈影响模型行为的系统与辅助性文本:

  • 你的 App 的 system prompt(“你是礼物推荐助手……”);
  • 你给模型的说明模板(“生成一个简短的推荐摘要”);
  • 预置的 follow-up 与提示(“如果……,请引导用户澄清预算”)。

这些文本也可以(且常常应该)本地化。一个简单的例子:如果用户用中文交流,而你的 system prompt 完全是英文,模型当然能工作,但你就失去了对该语言的文风与措辞的精细控制。

在稍后关于提示词与描述(prompts/descriptions)本地化工具的讲解中,我们会看看如何优雅地处理多语言的 system prompts。这里要强调的是: 提示词和 UI 一样,也是可本地化的一层

数据与内容

接下来是你的数据。对于 GiftGenius 来说是礼物目录:名称、描述、类别,有时还有如何使用礼物的提示。对商业 App 来说,还有价格、货币、计量单位、日期格式等。在面向 ChatGPT 的 product feed 规范中(即你向平台描述商品与服务的格式),这些文本字段(titledescription)与价格被明确标注,以便平台在 ChatGPT 内正确展示给用户。

如果你想做全球化的 App,礼物目录至少会涉及这些问题:

  • 是否以多语言存储名称/描述;
  • 如何选择向用户返回哪种语言;
  • 如果翻译尚未就绪该如何回退(fallback);
  • 不同地区如何展示货币与日期/价格格式。

给目录的一个小型类型化示例:

type Locale = "en" | "ru";

interface LocalizedString {
  en: string;
  ru: string;
}

interface Gift {
  id: string;
  title: LocalizedString;
  description: LocalizedString;
  priceCents: number;
  currency: "USD" | "EUR" | "RUB";
}
function getLocalizedTitle(gift: Gift, locale: Locale) {
  return gift.title[locale] ?? gift.title.en;
}

所以本地化不仅是前端的事,还关系到数据库结构与 MCP 资源。等讲到 Gateway(连接 ChatGPT 与你服务的网关)与 MCP 服务器时我们再回到这一点。

tools 与 JSON Schema 的描述

第四层是工具及其参数的描述。模型正是依赖这些信息来判断何时调用你的 tool,以及要传哪些参数。在 MCP 中,这包括工具的 titledescription,以及 JSON 模式中各字段的 description

Apps SDK 的文档强调,模型会利用参数的名称、描述与文档来选择工具并构造参数。

下面是 TypeScript MCP 服务器中 GiftGenius 的一个示例工具:

server.registerTool(
  "suggest_gifts",
  {
    title: "Suggest gifts",
    description: "Suggest 3–5 gift ideas based on recipient profile.",
    inputSchema: {
      type: "object",
      properties: {
        recipient: {
          type: "string",
          description: "Who is the gift for (e.g. mother, colleague)?",
        },
      },
      required: ["recipient"],
    },
  },
  async ({ input }) => { /* ... */ }
);

现在一切都是英文,模型能很好地理解。但如果用户用中文交流呢?模型依然可以把“妈妈”之类的词与 recipient 对应起来,但当字段复杂、领域术语较多时,出错概率会变高。我们会在“描述本地化策略”的讲解中专门讨论:统一使用英文描述 vs 本地化描述。

在本地化地图中,此时只需记下: tools 与 JSON Schema 的描述同样可以本地化,并且会影响模型行为

Commerce 与法律

最后是经常被放到最后才想起来的一层——与金钱和法律文本有关的所有内容:

  • SKU 与订阅计划的名称;
  • commerce feed 中的 title/description 字段(商品、服务、订阅);
  • Terms of Service、Privacy Policy、Refund Policy;
  • 邮件与通知(email、推送),如果 App 会在 ChatGPT 之外发送;
  • 你展示给用户的订单状态与支付错误(“支付被拒”“在你所在地区不可用”)。

这里有两个方面:UX 与法律。用户需要用自己的语言理解他同意了什么、为哪些内容付费。同时翻译必须在法律上准确:有时法务会要求只有某一种语言(例如英文)的文本具有法律效力,其他语言仅作“参考”。

因此在本地化地图中,我们要把 commerce 与法律文本单列出来,因为它们往往需要不同的流程(法务、合规、与市场的文本协同)。

3. 本地化的深度:“装饰性”与“语义性”

当我们说“本地化 App”时,区分两个层级很有用:装饰性与语义性。

装饰性本地化

装饰性指改变外观与可读性,但几乎不改变系统行为的内容。例如:

  • 翻译后的标题与按钮标签;
  • 翻译后的输入框占位符;
  • UI 中“人性化”的错误消息;
  • 小部件中的本地化营销横幅文案。

在传统 Web 应用中,很多团队到这里就停了。但在 ChatGPT App 中,这只是冰山上部的一层。

语义性本地化

语义性是会改变模型行为与 App 逻辑的因素。语言在这里会影响:

  • 模型会选用哪个工具;
  • 模型如何填写工具参数;
  • 模型认定对该用户“正确”的数据是什么。

语义性本地化的例子:

  • 使用用户语言的 system prompt,设定交流风格与规则;
  • 用用户语言书写的 tools 及其字段 descriptions;
  • 根据文化语境调整的提示/说明文本;
  • 影响解析与生成的日期/货币格式设置(31.12.2025 vs 12/31/2025)。

如果你只做了装饰性、没做语义性,你的 App 看起来像是本地化了,但“内在”依然像是“英文应用”。在本地化地图中,最好明确标注哪些元素对模型行为至关重要。

以 GiftGenius 为例:

  • JSON Schema 中 budget 字段的描述(“Budget in the user’s currency”)——语义性;
  • “挑选礼物”按钮标签——装饰性(对 UX 重要,但模型看不到)。

有了装饰 vs 语义的区分之后,下一个顺理成章的问题是:你到底想让 App 支持多少种语言。

4. 单语 vs 多语的 ChatGPT App

在画地图之前,先明确目标:你要做严格单语的 App,还是面向多语言用户。

单语 App

单语 App 指你明确只支持一种语言。比如只支持英文。

UI 小部件、提示词、工具描述与数据——全部使用一种语言。这样会极大简化工作:

  • 单一代码库,无需按语言分叉;
  • 单一的目录模式(不需要 title_entitle_ru 之类);
  • 维护与测试更容易。

但显然,用户覆盖面受限。对 ChatGPT App 来说,这还意味着:当用户使用其他本地化设置进入时,ChatGPT 仍然可能展示你的 App,但它需要持续把用户语言“转换”为 App 内部语言。在一些垂直领域这可以接受,但对面向大众的礼物推荐服务而言——大概率不行。

多语 App

多语 App 则是一个架构决策。此时:

  • UI 与文本会基于用户 locale 正确展示;
  • 数据(目录、商品描述)也与语言/地区绑定;
  • tools 的 descriptions 与 system prompts 可以按语言变化;
  • commerce 场景会考虑本地货币、税费与限制。

在这种情况下,全局散落一个 if (locale === "ru") 显然远远不够。你需要的是架构:词典、可本地化资源、集中存储与处理 localeuserLocation 的位置,以及小部件与 MCP 服务器间的约定。

Apps SDK 文档明确强调:当调用你的工具时,ChatGPT 会把 localeuserLocation 放在 _meta 里传给你,使你能在服务器端选择正确的语言与数据格式。这正是多语 App 的“燃料”。

小对比

为了直观起见——一个迷你对比:

特性 单语 App 多语 App
代码量 更少 更多(词典、选择逻辑)
受众覆盖 受限 全球
测试复杂度 更低 更高
与 commerce/法律的协作 更简单 需要流程与法务介入
与 GPT 行为的协作 单语 prompt 多语言 prompts/descriptions

在课程层面,我们会假定 GiftGenius 变为多语(至少 EN/RU),以展示“成体系”的方案。但许多技巧对想要随时具备扩展能力的精致单语 App 也同样适用。

5. 语言在哪些地方会真正影响模型

现在来标出语言会直接影响 ChatGPT 行为的关键点。

用户输入语言 vs tools 描述语言

设想:

  • 用户写道:“为同事挑一份50 欧元的礼物”;
  • 你的 tool suggest_gifts 只用英文描述;
  • Schema 字段:recipientbudgetcurrencyinterests

模型需要:

  1. 决定是否该调用 suggest_gifts
  2. 抽取 recipient = "colleague"budget = 50currency = "EUR"
  3. 把这些正确序列化为 JSON 参数。

如果 descriptions 很简短又是另一种语言,模型仍可完成任务,但填错字段的概率更高。比如把 budgetprice_limit 搞混,或者因为字段描述太模糊(比如 “Any extra info about the gift”)而把文本塞进 interests

当用户文本是中文、描述是英文时,模型还得在两种语言之间不断“切换”。

使用本地化 schema 的一种做法:

const locale = _meta?.["openai/locale"] ?? "en"; // 来自 ChatGPT 
const isRu = locale.startsWith("ru");

server.registerTool(
  "suggest_gifts",
  {
    title: isRu ? "礼物推荐" : "Suggest gifts",
    description: isRu
      ? "基于收礼人画像推荐 3–5 个礼物创意。"
      : "Suggest 3–5 gift ideas based on recipient profile.",
    inputSchema: { /* ... */ },
  },
  async ({ input }) => { /* ... */ }
);

这里是简化版:实际上 descriptions 最好在服务器启动时生成一次,而不是每次调用都生成,但思路很清楚。我们可以根据 locale 返回不同的 descriptions,让模型更容易理解用户。

数据语言 vs 查询语言

如果你的礼物目录只有英文,而用户用中文交流,模型会选择英文的名称与描述。有时这样也行,有时则不行。但更重要的是输出的格式化方式:

  • 你是否把服务器的原始 title/description 直接展示给用户;
  • 还是让模型在自己的文本中用用户语言转述它们;
  • 抑或你的 tool 就根据 locale 返回已本地化的文本。

在 Apps SDK 中,structured content(你从 tools 返回的结构化数据)与文本回复可以分别存在。你可以返回结构化数据(例如包含商品字段的 JSON)与单独的用户文本,模型会决定如何渲染或转述。

本地化可以发生在服务器层(数据),也可以发生在模型层(按需重述)。在制定地图时要决定你希望把“最终权威”放在哪里。

System prompt 与 follow-ups

如果你的 system prompt 只有英文,而用户是中文用户,模型会在两种语言间不断平衡。这也许没问题,但有时你希望更严格地设定语气:比如中文版本更口语化、英文版本更正式。

因此在本地化地图中要标出:

  • system prompt(EN);
  • system prompt(RU);
  • follow-up 模板(EN/RU);
  • 对工具提示词中的任何“硬性”提示。

6. GiftGenius 的本地化地图

现在把我们讨论过的各层、深度与语言合并,给 GiftGenius 做一张明确的本地化地图。也就是你之后要为自己的 App 做的事:做一张本地化地图。思路很简单:表格里按列放层与实体类型,按行放具体元素。

示例地图

下面是 GiftGenius 的简化地图(EN/RU):

类别 元素 示例值(EN) 示例(RU) 装饰或语义
UI 小部件标题 GiftGenius: find a perfect present GiftGenius: 挑选理想礼物 装饰
UI 收礼人标签 Recipient 收礼人 装饰
UI 空列表错误 No gifts found 未找到礼物 装饰
Prompts System prompt You are GiftGenius, a gift assistant… 你是 GiftGenius,一名礼物推荐助手…… 语义
Prompts 选择摘要模板 Here’s why these gifts fit… 这些礼物之所以合适,是因为…… 语义
Data 礼物名称 Smart mug 智能保温杯 兼具 UX 与语义
Data 礼物描述 Self‑heating mug with app control… 可通过应用控制的自加热保温杯…… 兼具 UX 与语义
Data 货币 59.99 USD 5,499 ₽ / 59.99 € 语义(格式/货币)
Tools/schema
suggest_gifts.description
Suggest gift ideas based on profile… 基于用户画像推荐礼物创意…… 语义
Tools/schema
budget.description
Budget in user’s currency 以用户的货币表示预算 语义
Commerce feed 中的 SKU 名称 “Premium subscription – 1 year” “高级订阅 — 1 年” 兼具 UX 与法律
Commerce Terms 页面 Terms of Service (EN only) 提示:具有法律效力的仅为英文文本 语义/法律
Errors (backend) 支付错误消息 Payment failed, please try again later 支付失败,请稍后重试 装饰 + UX

左侧按层分组,接着是具体元素。最后一列有助于判断哪些内容不能在未经与提示词设计/建模人员确认的情况下随意修改:凡标注为“语义”的,都会影响 GPT 的行为。

小型代码草图

为了把地图与代码关联起来,可以给可本地化实体定义一个简单的类型:

type LocalizedTextKey =
  | "ui.title"
  | "ui.recipient_label"
  | "error.no_gifts"
  | "prompt.summary_intro";

type Locale = "en" | "ru";

type Messages = Record<Locale, Record<LocalizedTextKey, string>>;
const messages: Messages = {
  en: {
    "ui.title": "GiftGenius: find a perfect present",
    "ui.recipient_label": "Recipient",
    "error.no_gifts": "No gifts found",
    "prompt.summary_intro": "Here’s why these gifts fit:",
  },
  ru: {
    "ui.title": "GiftGenius: 挑选理想礼物",
    "ui.recipient_label": "收礼人",
    "error.no_gifts": "未找到礼物",
    "prompt.summary_intro": "这些礼物之所以合适,是因为:",
  },
};

随后,这些相同的键既可在小部件中使用,也可在服务器端构造提示词时使用(前提是你把 locale 贯穿栈传递——这会在下一讲中介绍)。这样,你的“本地化地图”会逐步变成一个类型化的词典,而不是零散的字符串集合。

7. 实践:为你的 App 制作一张本地化地图

在深入具体 i18n 技术与 Gateway/MCP 架构之前,先做一个“朴素却非常有用”的练习:如实写下你打算本地化的具体内容。

一个好方法是打开任意编辑器(Google Sheets 或 Notion 都行)建一张表,包含这些列:

  • 类别/层(UI、提示词、数据、tools、commerce 与法律、错误);
  • 元素(具体按钮、具体字段描述、带文本的具体 endpoint);
  • EN 示例;
  • 第二语言示例(如果已有或至少草稿);
  • 标注“装饰/语义/法律重要”;
  • 负责人(谁负责修改:前端、MCP 服务器、产品、法务)。

然后走查你的 App,认真把所有含文本或受语言影响的数据格式记录下来。

对 GiftGenius 来说,会得到类似上文表格的扩展版。在此过程中,你几乎肯定会发现一些“隐蔽”之处:

  • MCP 服务器代码中的文本常量(例如错误消息);
  • structuredContent 里的默认值(例如你没展示在 UI 中的类别);
  • tools 的旧描述,已经与其实际行为不匹配。

这个练习在你把本地化与真实业务流程(支付、订阅激活、法律文档)打通之前尤其有用。在有 ACP 与法律文本的多语言系统里,事后再去重命名 tool charge_user 会痛苦许多。

总体而言,如果你提前绘制本地化地图,并如实标注你打算支持的对象与语言,那么当 MCP、Gateway、commerce 与 Store 加入进来时,你能省下大量的精力与麻烦。

8. 规划本地化时的常见错误

错误 1:认为本地化 = “把按钮翻译了”。
很常见的场景:团队把所有 UI 字符串仔细抽到词典并翻译,然后就很开心。但 system prompt 仍然只有英文,tools 的 descriptions 也只是英文,商品目录也仅有英文名称。结果是 App 看起来本地化了,但模型内部依旧“活在自己的世界里”,行为仍然偏向英文。实践中的表现就是奇怪的推荐与 tools 参数填写错误。

错误 2:不区分装饰与语义。
有时产品会说“把这句描述稍微改一改”,于是开发像改 UI 标签那样去改工具描述或 system prompt。但 JSON Schema 字段的 description 或 system prompt 里的某句话,都是与模型的“契约”一部分。这些改动可能会显著改变 GPT 调用你工具的方式。如果不在地图中提前标注语义性元素,就很容易在无意间破坏 App 的行为。

错误 3:没有架构就一头扎进多语言,导致混乱。
一开始非常容易在代码里随处写 if (locale === "ru") 来插入中文字符串。几周后,应用就变成“本地化地狱”:有些组件用词典,有些组件把字符串写在 JSX 里,服务器又是另一套键名风格。等到你要引入正规的 i18n 系统并统一风格时,会困难得多。

错误 4:忽视数据与金钱。
即便是经验丰富的团队,常常从翻译 UI 与提示词开始,但忽略了商品目录、价格、货币与法律文本也必须考虑 locale 与 userLocation。面向 ChatGPT 的 product feed 规范会严格要求哪些文本字段与价格对用户的正确展示是必须的。如果你不在数据层提前设计多语言,后面要么得复制 feed,要么得做痛苦的迁移。

错误 5:忽略平台提供的语言与位置信号。
在 MCP 调用中,ChatGPT 已经传递 _meta["openai/locale"]_meta["openai/userLocation"], 让你知晓对话使用的语言与地区。但一些开发者仍然在首次启动时让用户“选择界面语言”,且完全不利用这些信号来选择资源与价格。结果是 UX 受损,架构也比需要的更复杂。

错误 6:没有明确“负责人”。
一旦上线,你会发现翻译分散在不同的人手里:前端改 UI 文本,后端改 tools 的 descriptions,ML 同事改 system prompt,而法务会给出修订后的 Terms。如果在本地化地图中不标注每个层的负责人,改动就会相互冲突,有些文本在一个地方更新了而另一个地方却没更新。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION