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 規格(你用來向平台描述你的商品與服務的格式)中,文字欄位(title、description)與價格被明確標示,便於在 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 中,這是工具的 title、description,以及 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_en、title_ru 等);
- 維護與測試更簡單。
但受眾顯然有限。對 ChatGPT App 而言,這還意味著:若使用者帶著其他語系而來,ChatGPT 仍可能展示你的 App,但它必須不斷把使用者語言「轉換」成 App 的內部語言。在某些利基情境可以接受,但對大眾消費型的禮物服務來說,多半不理想。
多語系 App
多語系 App 是一個架構性決策。在這裡:
- UI 與文本會依使用者的 locale 正確顯示;
- 資料(目錄、商品描述)也綁定語言/地區;
- tools 的描述與 system prompts 可能隨語言變化;
- commerce 情境會考慮在地貨幣、稅務與限制。
這種情況下,單靠 if (locale === "ru") 在各處分支顯然不夠。你需要架構:字典、可本地化的資源、集中管理 locale 與 userLocation 的位置、以及小工具和 MCP 伺服器之間的約定。
Apps SDK 的文件明確指出,ChatGPT 會在呼叫你的工具時,透過 _meta 傳遞 locale 與 userLocation,正是為了讓你能在伺服端選擇正確的語言與資料格式。這就是多語系 App 的「燃料」。
小比較
為了更直觀——一個迷你比較:
| 特性 | 單語系 App | 多語系 App |
|---|---|---|
| 程式碼量 | 較少 | 較多(字典、選擇邏輯) |
| 受眾覆蓋 | 受限 | 全球 |
| 測試複雜度 | 較低 | 較高 |
| 與 commerce/法務的協作 | 較簡單 | 需要流程與法務 |
| GPT 行為處理 | 單語系 prompt | 多語系 prompts/descriptions |
在本課程中,我們假設 GiftGenius 會成為多語系(至少 EN/RU),以示範「成熟」的做法。不過,許多技巧對謹慎設計的單語系 App 也同樣有用,尤其是當你想隨時擴充時。
5. 語言在哪些地方會真正影響模型
現在讓我們指出語言會直接影響 ChatGPT 行為的關鍵點。
使用者輸入語言 vs 工具描述語言
想像:
- 使用者輸入:「為同事挑個 50 歐元 的禮物」;
- 你的 tool suggest_gifts 僅以英文描述;
- Schema 欄位有:recipient、budget、currency、interests。
模型需要:
- 判斷確實應該呼叫 suggest_gifts;
- 抽取 recipient = "colleague"、budget = 50、currency = "EUR";
- 正確序列化成 JSON 引數。
若描述簡短且與使用者不同語言,模型仍能處理,但填錯欄位的機率更高。比如把 budget 與 price_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 gift ideas based on profile… | 根據個人檔案挑選禮物點子…… | 語義 |
| Tools/schema | |
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。如果本地化地圖裡沒有標示各層的負責人,修改就會互相衝突,有些文字在某處更新了、在另一處卻沒跟上。
GO TO FULL VERSION