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

App 的本地化策略

ChatGPT Apps
等級 9 , 課堂 0
開放

1. 為什麼要在 ChatGPT App 中特別重視本地化

如果你做過一般的網頁應用,本地化很可能讓你聯想到經典的 i18n:介面字串、一些日期與數字格式、字典——然後你把一切仔細翻譯好。在 ChatGPT App 中事情更有趣:這裡的第三個參與者是 LLM 模型本身。它會閱讀你的工具描述、prompts、結果,進行推理並做出決策。

也就是說,語言不僅是「如何把文字漂亮地展示給使用者」,還包括「模型如何理解你的工具是做什麼、什麼時候該呼叫、以及要帶入哪些參數」。像「購買」按鈕就算翻得勉強,使用者也多半看得懂。但如果你用含糊、混合中英的方式描述一個負責付款的 tool,模型可能永遠不會呼叫它,或是完全不是你預期的方式去呼叫。

還有一點:ChatGPT 已經會把使用者的語系與位置提示傳給你的 MCP 伺服器——_meta["openai/locale"]_meta["openai/userLocation"]。這是在呼叫工具的 MCP 請求層完成的,讓你可以依據使用者的語言與地區來調整文字與資料。也就是說,平台已經「把上下文送到你面前」,而開發者的任務就是善用它。

因此,在本模組中,我們把本地化視為 ChatGPT App 的架構面向,而不是「翻完 UI 就結束」。

2. 需要本地化的層

把 App 想成一個多層的派。每一層都可以(而且經常需要)被本地化。為了不被淹沒,先從地圖開始。

層次總覽

先看總表,然後逐一拆解。

是什麼 範例 影響
Widget UI 小工具中所有可見的前端面向 標題、按鈕、錯誤、提示 使用者體驗(UX)
模型文本與 prompts 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 文本與 prompts

下一層是系統與輔助文字,使用者不會直接看到,但對模型行為影響很大:

  • 你的 App 的 system prompt(「你是禮物推薦助理……」);
  • 你給模型的解釋模板(「請輸出簡短的選擇摘要」);
  • 預先規劃的 follow‑ups 與給模型的提示(「如果……則請使用者補充預算」)。

這些文字也可以(而且常常應該)被本地化。舉個簡單的例子:如果使用者用中文互動,而你的 system prompt 完全是英文,模型當然還能工作,但你就失去了對該語言的語氣與表達的精細控制。

稍後在關於本地化 prompts 與描述的課中,我們會看看如何妥善處理多語系的 system prompts。此處先強調: 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 伺服器時會再回到這點。

工具與 JSON Schema 的描述

第四層是工具與其參數的描述。模型正是透過它們來理解何時該呼叫你的 tool,以及要帶哪些參數。在 MCP 中,這是工具的 titledescription,以及 JSON 結構欄位的 description

Apps SDK 的文件強調,模型會利用參數的名稱、描述與文件,來選擇工具並構造引數。

GiftGenius 在 TypeScript MCP 伺服器中的一個假想工具範例:

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」時,區分兩個深度層次會很有幫助:表面與語義。

表面本地化

表面層指的是只改變外觀與可讀性,但幾乎不改變系統行為的東西。例子:

  • 翻譯過的標題與按鈕標籤;
  • 輸入框的 placeholder 翻譯;
  • UI 中「人性化」的錯誤訊息;
  • 小工具中的在地化行銷文案。

在傳統網頁應用中,很多時候做到這裡就停了。對 ChatGPT App 而言,這很重要,但也只是冰山的一角。

語義本地化

語義層指的是會改變模型行為與 App 邏輯的部分。語言會影響:

  • 模型會選擇哪個工具;
  • 模型如何填入工具的引數;
  • 模型認為哪些資料對該使用者是「正確的」。

語義本地化的例子:

  • 以使用者語言撰寫的 system prompt,設定溝通風格與規則;
  • 以使用者語言撰寫的工具與其欄位描述;
  • 依文化脈絡不同而改變的提示/指示文本;
  • 影響剖析與生成的日期/貨幣格式設定(31.12.2025 vs 12/31/2025)。

如果你只做了表面層而沒有語義層,你的 App 可能看起來是本地化的,但內部行為仍像是「英文版」。在本地化地圖中,標註哪些元素會影響模型行為很有幫助。

以 GiftGenius 為例:

  • JSON Schema 中 budget 欄位的描述(「Budget in the user’s currency」)——屬於語義;
  • 「挑選禮物」按鈕的標籤——屬於表面(對 UX 重要,但模型看不到)。

既然我們能區分表面與語義,接著就該回答:你的 App 想要支援多少種語言。

4. 單語系 vs 多語系的 ChatGPT App

在畫地圖之前,要先釐清你的企圖心:你要做嚴格單語系的 App,還是面向多語系的受眾。

單語系 App

單語系 App 表示你有意只支援一種語言。比方說,只支援英文。

UI 小工具、prompts、工具描述與資料——全部使用同一語言。這會大幅簡化工作:

  • 一套程式碼,沒有語言分支;
  • 一份目錄結構(沒有 title_entitle_ru 等);
  • 維護與測試更簡單。

但受眾顯然有限。對 ChatGPT App 而言,這還意味著:若使用者帶著其他語系而來,ChatGPT 仍可能展示你的 App,但它必須不斷把使用者語言「轉換」成 App 的內部語言。在某些利基情境可以接受,但對大眾消費型的禮物服務來說,多半不理想。

多語系 App

多語系 App 是一個架構性決策。在這裡:

  • UI 與文本會依使用者的 locale 正確顯示;
  • 資料(目錄、商品描述)也綁定語言/地區;
  • tools 的描述與 system prompts 可能隨語言變化;
  • commerce 情境會考慮在地貨幣、稅務與限制。

這種情況下,單靠 if (locale === "ru") 在各處分支顯然不夠。你需要架構:字典、可本地化的資源、集中管理 localeuserLocation 的位置、以及小工具和 MCP 伺服器之間的約定。

Apps SDK 的文件明確指出,ChatGPT 會在呼叫你的工具時,透過 _meta 傳遞 localeuserLocation,正是為了讓你能在伺服端選擇正確的語言與資料格式。這就是多語系 App 的「燃料」。

小比較

為了更直觀——一個迷你比較:

特性 單語系 App 多語系 App
程式碼量 較少 較多(字典、選擇邏輯)
受眾覆蓋 受限 全球
測試複雜度 較低 較高
與 commerce/法務的協作 較簡單 需要流程與法務
GPT 行為處理 單語系 prompt 多語系 prompts/descriptions

在本課程中,我們假設 GiftGenius 會成為多語系(至少 EN/RU),以示範「成熟」的做法。不過,許多技巧對謹慎設計的單語系 App 也同樣有用,尤其是當你想隨時擴充時。

5. 語言在哪些地方會真正影響模型

現在讓我們指出語言會直接影響 ChatGPT 行為的關鍵點。

使用者輸入語言 vs 工具描述語言

想像:

  • 使用者輸入:「為同事挑個 50 歐元 的禮物」;
  • 你的 tool suggest_gifts 僅以英文描述;
  • Schema 欄位有:recipientbudgetcurrencyinterests

模型需要:

  1. 判斷確實應該呼叫 suggest_gifts
  2. 抽取 recipient = "colleague"budget = 50currency = "EUR"
  3. 正確序列化成 JSON 引數。

若描述簡短且與使用者不同語言,模型仍能處理,但填錯欄位的機率更高。比如把 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‑ups 模板(EN/RU);
  • 任何在工具 prompts 中的「硬性」提示語。

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… 可自動加熱並可透過 App 控制的馬克杯…… 同時影響 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) 告示:僅 EN 文本具法律效力 語義/法律
Errors (backend) 付款錯誤訊息 Payment failed, please try again later 付款失敗,請稍後再試 表面 + UX

左側依層分組,接著是具體元素。最後一欄有助於提醒哪些內容不能隨意更動、需要與 prompt 設計/模型專家協調:凡標記為語義者,都會影響 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": "以下是這些禮物適合的原因:",
  },
};

接著,這些鍵可以同時用在小工具與伺服器端的 prompt 組裝(前提是你能把 locale 在整個堆疊中傳遞——這是下一堂課)。如此一來,你的「本地化地圖」會逐步變成具型別的字典,而不是零散的字串集合。

7. 實作:為你的 App 製作本地化地圖

在深入 i18n 技術與 Gateway/MCP 架構之前,先做一件乍看無聊但非常有用的事:老實列出你打算本地化的東西。

好方法是打開任何編輯器(Google Sheets 或 Notion 都行),建立一張表,欄位包含:

  • 類別/層(UI、prompts、資料、tools、commerce 與法務、錯誤);
  • 元素(具體按鈕、欄位描述、帶文字的 endpoint);
  • EN 範例值;
  • 第二語言的範例值(若已有或至少草稿);
  • 標記「表面/語義/法律重要」;
  • 負責人(誰負責修改:前端、MCP 伺服器、產品、法務)。

然後走過你的 App,把所有有文字、或語言會影響資料格式的地方都誠實地列出來。

對 GiftGenius 來說,大約會是上面表格的擴充版。在過程中你幾乎一定會發現幾個「隱藏點」:

  • MCP 伺服器程式碼裡的文字常數(例如錯誤訊息);
  • structuredContent 中的預設值(例如你沒在 UI 直接顯示的分類);
  • tools 的舊標籤已與實際行為不符。

這個練習特別適合在你把本地化接上真實商務流程(付款、訂閱啟用、法律文件)之前完成。若已進到多語系、ACP 與法律文本等,你才去把 tool charge_user 改名,會痛得多。

總之,若你能事先畫好本地化地圖,並誠實標示你要在哪些語言支援哪些內容,當 MCP、Gateway、commerce 與 Store 加入戰局時,你會省掉非常多麻煩。

8. 本地化規劃的常見錯誤

錯誤一:以為本地化 =「翻譯按鈕」。
這很常見:團隊把 UI 字串抽到字典、翻好就很開心。但 system prompt 仍只有英文、tools 的描述也是、商品目錄也只有英文名稱。結果 App 看起來已本地化,但模型內部依舊活在自己的世界,行為仍以英文為中心。實務上會表現為奇怪的建議,或 tools 引數填錯。

錯誤二:不區分表面與語義。
有時產品經理要求「小改一下措辭」於工具描述或 system prompt,開發者就當作 UI 標籤一樣改。但 JSON Schema 欄位的 description 或 system prompt 中的一句話,其實是你與模型的契約。這種更動會徹底改變 GPT 呼叫你工具的方式。若不在本地化地圖中預先標記語義元素,很容易不小心把 App 的行為改壞。

錯誤三:在沒有架構的情況下貿然做多語系。
一開始很容易想在程式各處塞 if (locale === "ru") 來替換中文字串。結果幾週後應用變成「本地化地獄」:某個元件用字典、另一個元件把字串寫在 JSX、伺服器又是第三套鍵名規則。之後要導入正常的 i18n 系統並統一做法,就會變得困難許多。

錯誤四:忘了資料與金流。
即使是有經驗的團隊,往往先從翻 UI 與 prompts 開始,卻忽略商品目錄、價格、貨幣與法律文本也要考慮 locale 與 userLocation。在 ChatGPT 的 product feed 規格裡,正是嚴格定義了哪些文字欄位與價格是必要的,以便向使用者正確呈現商品。若你不在資料層預留多語支持,之後不是得複製 feed,就是得做痛苦的遷移。

錯誤五:忽視平台提供的語系與位置訊號。
ChatGPT 已經會在 MCP 呼叫中傳遞 _meta["openai/locale"]_meta["openai/userLocation"], 讓你瞭解正在與你互動的語言與地區。有些開發者仍會在第一次啟動時要使用者「選擇介面語言」,卻完全沒用這些訊號來選擇資源與價格。結果 UX 變差,架構也比需要的更複雜。

錯誤六:沒有標註可本地化元素的「負責人」。
一旦上線後,翻譯很容易分散到不同人手上:前端改 UI 文字、後端改 tools 描述、ML 人員改 system prompt,法務則會送來修訂後的 Terms。如果本地化地圖裡沒有標示各層的負責人,修改就會互相衝突,有些文字在某處更新了、在另一處卻沒跟上。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION