WinForms/WPF 應用程式的多語言化 ── resx、附屬組件與文化特性切換的實務
· Go Komura · CSharp, .NET, WinForms, WPF, 多語言化, 在地化, 資源, UI, Windows 開發, 技術諮詢
「希望在海外據點也能使用相同的應用程式」「工廠現場的外語員工變多了,想把畫面也改成英文」──Windows 桌面應用程式的多語言化,乍看是簡單的需求,但一旦真正著手,就會不斷遇到「resx 該怎麼用」「WPF 有好幾種方式,不知道哪個才對」「執行中能否切換語言」等令人猶豫不決的判斷點。
本文以 WinForms/WPF 業務應用程式為前提,依照實務上容易感到迷惘的順序,整理 .NET 在地化的基礎(文化特性與資源回退)、resx 與附屬組件的機制、依框架而異的現實可行方式、執行時切換,以及字串以外(格式、版面配置、RTL)的議題。
1. 先講結論(判斷表)
多語言化的方式,取決於框架與需求的組合。
| 情境 | 建議方式 | 理由 |
|---|---|---|
| WinForms・以畫面上的標籤/按鈕為主 | 表單(Form)的 Localizable = true + 各語言 resx(設計工具方式) |
控制項的字串與大小、位置皆可依語言分別調整,僅在 Visual Studio 內即可完成 |
| WinForms/WPF 共通・訊息或共用文字較多 | 共用 resx(Resources.resx + Resources.ja.resx 等)+ 自動產生的型別化類別 |
可從程式碼以型別安全的方式參照字串。畫面與邏輯可共用相同文字 |
| WPF | 共用 resx + x:Static 繫結 |
LocBaml 方式無法用於 .NET(Core)的 WPF1。採用 resx 方式可與 WinForms 共用知識 |
| 必須支援執行中切換語言 | resx + 變更通知機制(或重新產生畫面) | 在選定方式之前,應先確認需求上「是否真的需要不重新啟動就切換」(第 6 章) |
| 委外翻譯 | 以 resx 為基礎設計,以鍵值與譯文對照表交付 | resx 本身是 XML,可直接交付,但附上情境資訊(是哪個畫面的哪個按鈕)的運作方式,會左右翻譯品質 |
結論先寫在這裡。
CurrentCulture(格式)與CurrentUICulture(資源語言)是不同的東西。 這個區別,是整個多語言化設計的基礎(第 2 章)。- 資源的實體是「中性語言資源 + 各語言的附屬組件(Satellite Assembly)」,回退(Fallback)是自動進行的。 系統會依照
ja-JP→ja→ 中性語言的順序搜尋,因此即使翻譯有缺漏,應用程式也不會當機,而是會以預設語言顯示2。 - WinForms 的實務解法是併用設計工具方式(
Localizable = true)與共用 resx 方式。 畫面採用設計工具方式,訊息方塊或共用文字則整合到共用 resx(第 4 章)。 - 官方逐步操作指南中提到的 LocBaml 方式,無法用於 .NET(Core)1。目前實務上的首選是 resx +
x:Static(或繫結)方式(第 5 章)。 - 執行時的語言切換,應以「下次啟動時套用」為第一選項。 因為目前顯示中的畫面資源不會自動替換(第 6 章)。
- 字串翻譯只是多語言化的一半而已。 剩下的一半是:版面配置要能因應翻譯後字串長度的變化、日期與數值的格式、字型,以及由右至左書寫語言(RTL)的支援(第 7 章)。
2. 基礎 ── CurrentCulture 與 CurrentUICulture 的差異
.NET 的文化特性(Culture,地區設定)有兩個角色不同的屬性3。
| 屬性 | 決定的內容 | 範例 |
|---|---|---|
CultureInfo.CurrentCulture |
日期、數值、貨幣的格式 | 顯示為 2026/07/07 還是 7/7/2026 |
CultureInfo.CurrentUICulture |
讀取哪個語言的資源 | 按鈕顯示「保存」還是「Save」 |
這兩者可以獨立設定。例如「畫面用英文,但日期與貨幣採用日本的格式」這種組合(日本國內外語員工使用的業務應用程式中常見的需求),可以透過 CurrentUICulture = en 且 CurrentCulture = ja-JP 自然地實現。
兩者的預設值都源自 OS 的設定,因此「什麼都不做就跟隨 OS」是出發點。若要在整個應用程式範圍明確切換語言,不應逐一設定各執行緒(Thread),而應替換預設值本身。
using System.Globalization;
// 替換之後所有新建立執行緒的預設文化特性
// (需在 UI 執行緒建立之前,即 Application.Run 之前執行)
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en"); // 資源語言
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("ja-JP"); // 格式
對 Thread.CurrentThread.CurrentUICulture 賦值,只會影響該執行緒。在會橫跨多個執行緒的業務應用程式中(例如使用 async/await 或背景處理),透過 DefaultThreadCurrentUICulture / DefaultThreadCurrentCulture 連同預設值一起變更,較不容易出錯3。
另外,記錄檔、檔案輸出、系統間介接的格式不應跟隨使用者的文化特性,應固定使用 CultureInfo.InvariantCulture。關於日期格式設計的整體考量,請參考「業務應用程式的日期時間與時區設計」。
3. 資源機制 ── resx、附屬組件與回退(Fallback)
.NET 的在地化採用「主軸與分支(Hub & Spoke)」模型。預設語言(中性語言)的資源會內嵌於主組件(Assembly)中,各語言的資源則會分離到稱為附屬組件(Satellite Assembly)的獨立 DLL 中4。
建置後,各語言的 resx 會產生以下的輸出結構。
MyApp.exe ← 主體(包含中性語言資源)
MyApp.dll
ja/MyApp.resources.dll ← 日文附屬組件
en/MyApp.resources.dll ← 英文附屬組件
執行時,資源管理員(Resource Manager)會以 CurrentUICulture 為起點,依照特定文化特性(ja-JP)→ 中性文化特性(ja)→ 預設(主體)的順序搜尋資源2。多虧了這個自動回退機制,即使少了一筆翻譯,應用程式也不會當機,而是會顯示預設語言的字串。反過來說,「明明應該是英文畫面,卻混雜著部分日文」正是翻譯遺漏的徵兆,可以作為測試的檢查重點(第 8 章)。
實務上要留意以下兩點。
- 透過
NeutralResourcesLanguage屬性宣告預設語言屬於哪個文化特性,可以避免在預設文化特性時仍去搜尋附屬組件的浪費,並讓回退的終點更加明確4。 - 附屬組件必須與主體一起發布才能生效,因此安裝程式或複製部署的設計中,必須包含各語言的資料夾(第 8 章)。
4. WinForms 實務 ── 設計工具方式與共用 resx 方式
WinForms 有兩種方式,實務上會併用。
4.1 畫面採用設計工具方式(Localizable = true)
在表單(Form)的屬性中將 Localizable 設為 true,並將 Language 屬性切換到目標語言(例如英文)後編輯標籤或按鈕的 Text,Visual Studio 就會自動產生像 Form1.en.resx 這樣的各語言 resx5。
此方式的優點在於,不只是字串,連控制項的大小與位置也會依語言分別保存。日文的「登錄」翻成英文的「Register」後若超出按鈕範圍,也能在設計工具上依語言個別調整。另一方面,語言數 × 表單數會讓 resx 檔案數量增加,每次修改表單版面配置時都需要確認所有語言,這也會帶來維護成本。若預期未來會增加語言數,建議優先採用後面版面配置方面的對策(第 7 章),打造「即使不調整也不會跑版」的畫面結構。
4.2 訊息與共用文字採用共用 resx 方式
訊息方塊的文字、確認對話框、狀態列顯示等從程式碼中使用的字串,應集中放到專案共用的 Resources.resx 中,並依語言新增 Resources.en.resx 等檔案。resx 會自動產生型別化類別,因此可以從程式碼中參照,不會有鍵值打錯的問題。
// 透過自動產生的型別化類別參照。
// 回傳的字串會依 CurrentUICulture 自動切換
MessageBox.Show(
Properties.Resources.ConfirmDeleteMessage,
Properties.Resources.ConfirmTitle,
MessageBoxButtons.YesNo);
這裡最重要的規則是,不要把顯示給使用者看的字串直接寫死在程式碼中。多語言化的工時,並非花在導入機制本身,而是花在「回收散落各處、寫死的字串」。即使目前只用單一語言營運,經由 resx 管理仍有其價值。
5. WPF 實務 ── 首選 resx 而非 LocBaml
WPF 的多語言化是資訊容易混雜的領域。官方文件的逐步操作指南中,記載了在專案檔設定 UICulture 以產生 BAML 資源,再用 LocBaml 工具插入翻譯的方式,但LocBaml 是僅適用於 .NET Framework 版 WPF 的範例工具,無法在 .NET(Core)的 WPF 中運作1。請不要將其視為目前新專案應選擇的方式。
實務上的首選,是從 XAML 參照與 WinForms 相同的共用 resx 方式。
<!-- 將 resx 的存取修飾詞設為 Public,並以 x:Static 參照 -->
<Window xmlns:res="clr-namespace:MyApp.Properties"
Title="{x:Static res:Resources.MainWindowTitle}">
<Button Content="{x:Static res:Resources.SaveButton}" />
</Window>
此方式的優點是,資源機制(附屬組件、回退)與 WinForms 完全共通,即使是混合方案(Solution)也能統一管理文字。x:Static 是在啟動時解析的靜態參照,若需要執行時切換,則要進一步發展成繫結(Binding)+ 變更通知的方式(第 6 章)。
在 XAML 這一側,同時設定根元素的 xml:lang,可以讓斷字(Hyphenation)、字型回退、數字顯示等 WPF 中與語言相關的功能正常運作1。
至於該用 WinForms 還是 WPF 開發的判斷,請參考「WinForms / WPF / WinUI 的選用判斷表」。
6. 執行時的語言切換 ── 以「下次啟動時套用」為第一選項
在設定畫面讓使用者選擇語言的需求中,套用時機可以從兩個方案中選擇。
- 方案 A:下次啟動時套用(建議)。 將所選語言儲存到設定檔中,在啟動時的
Application.Run之前(WPF 則是在Application啟動之前)設定DefaultThreadCurrentUICulture。實作單純,也不會因重建畫面而在結構上引發問題(例如輸入中資料遺失、事件處理常式重複註冊等)。 - 方案 B:立即套用。 即使變更了
CurrentUICulture,已顯示畫面上的字串也不會自動替換。若是 WinForms 的設計工具方式,需要用ComponentResourceManager.ApplyResources對已開啟的表單重新套用;若是 WPF,則需要透過繫結 + 變更通知來提供字串。無論哪種做法,前提都是「所有畫面都支援資源重新套用」這樣的實作。
在業務應用程式中,相對於變更語言的頻率(通常導入時只需一次),方案 B 的實作與測試成本往往不划算,因此建議以方案 A 為預設,只有在需求上真的必要時才採用方案 B。語言設定的儲存位置,由於是依使用者而異的設定,適合放在 %LOCALAPPDATA% 之下。關於設定存放位置的判斷,請參考「Windows 業務應用程式的組態管理實務」的判斷表。
7. 字串以外的多語言化 ── 版面配置、格式、字型、RTL
即使翻譯完成,多語言化仍有一半尚未完成。
- 版面配置的伸縮。 即使是相同意思的文字,不同語言的長度也會有很大差異(日文「保存」→德文「Speichern」)。固定座標、固定大小的版面配置,每次翻譯都會跑版,因此在 WinForms 中,官方建議的做法是搭配
TableLayoutPanel與AutoSize,做出能因應字串長度的版面配置6。WPF 的版面配置系統本身就具有彈性,只要避免寫死寬度,大部分情況都能因應。同時建議與高 DPI 環境下的跑版對策(「WinForms 高 DPI 支援指南」「WPF 高 DPI 支援指南」)一併設計,可以避免之後的返工。 - 日期、數值、貨幣的格式。 畫面顯示應交由
CurrentCulture處理,盡量減少畫面程式碼中寫死的ToString("yyyy/MM/dd")。反之,檔案、記錄檔、介接資料則固定使用InvariantCulture(第 2 章)。 - 字型。 需確認目標語言的文字是否包含在字型中。新增中文(簡體/繁體)或韓文時,若仍沿用針對日文設計的字型指定,字形可能會顯得不自然(所謂的 CJK 字形問題)。將 UI 字型的指定集中在一處管理,可以讓新增語言時的變更更輕鬆。
- 由右至左書寫的語言(RTL)。 支援阿拉伯文、希伯來文時,除了翻譯字串之外,還需要將畫面左右翻轉。WinForms 透過
RightToLeft/RightToLeftLayout屬性控制,WPF 則透過FlowDirection屬性控制。RTL 支援若是「事後才加上」,將需要重新檢視所有畫面,因此若目標語言有可能包含 RTL 語言,請從一開始就將其納入需求。 - 圖片、圖示中的文字。 燒錄在圖片中的字串,無法透過資源機制切換。應避免使用含文字的圖片,採用「圖片 + 文字標籤」的組合較為安全。
UI 設計整體的判斷,亦請參考「Windows 應用程式的 UX 設計判斷表」。
8. 維運 ── 翻譯交付、遺漏檢測與發布
最後是機制建立完成之後的維運層面。
- 交付翻譯時附上情境資訊。 若只交付 resx 的鍵值與原文,「OK」「開啟」這類短詞的譯文品質會不穩定。實務上較可行的做法是,用附有「顯示的畫面與用途」的鍵值、原文、譯文對照表(如試算表)進行交換,收到後再反映到 resx 中。
- 透過回退機制找出翻譯遺漏。 由於資源回退機制的緣故,翻譯遺漏會以「該語言的畫面中混雜著預設語言」的形式出現2。在 CI 中加入一個簡單的檢查(腳本或測試程式碼),將各語言 resx 的鍵值清單與中性語言 resx 進行比對,就能在發布前以機械化方式偵測出來。
- 發布時務必包含附屬組件。 若沒有連同
ja/en/等語言資料夾一併發布,該語言就會回退到預設語言,出現「明明翻譯了卻沒有生效」的狀況。手動複製時漏掉語言資料夾是常見的事故,因此應將整個目錄結構一併納入安裝程式(MSI/MSIX 等)中。以 ClickOnce 發布時,預設就會將所有語言的附屬組件納入部署7。關於發布方式整體的判斷,請參考「Windows 應用程式發布方式的選擇」。
結語
桌面應用程式的多語言化,建立在 resx 與附屬組件這套成熟穩定的機制之上,機制本身並不困難。實務上成敗的關鍵在於:區分 CurrentCulture 與 CurrentUICulture 的設計、不將顯示給使用者的字串寫死在程式碼中的紀律、能承受翻譯後字串變長的版面配置,以及「是否真的需要立即切換」這項需求的判斷。在 WPF 中,不要被舊有的 LocBaml 方式相關資訊牽著走,以 resx 為基礎建構才是目前的實務解法。
既有應用程式多語言化的方式選定與影響範圍評估、面向海外據點展開時畫面、報表、介接資料的多語言對應,通常需要一邊觀察程式碼庫的現況一邊做判斷,若有疑問歡迎與我們諮詢。
相關文章
- WinForms / WPF / WinUI 的選用判斷表
- Windows 應用程式的 UX 設計判斷表
- 業務應用程式的日期時間與時區設計
- WinForms 高 DPI 支援指南
- WPF 高 DPI 支援指南
- 不只是 appsettings.json ── Windows 業務應用程式的組態管理實務
- Windows 應用程式發布方式的選擇(MSI / MSIX / ClickOnce / xcopy / 自製更新程式)
- Windows 使用者設定檔(User Profile)的機制
相關諮詢領域
合同會社小村軟體提供 WinForms/WPF 應用程式多語言對應的設計、實作,以及既有應用程式事後多語言化時方式選定與影響範圍整理等技術諮詢服務。
參考資料
-
Microsoft Learn,WPF Globalization and Localization Overview。說明透過專案檔的
UICulture設定產生附屬組件、根元素xml:lang的作用,以及 LocBaml 是僅適用於 .NET Framework 版 WPF 的範例工具、無法在 .NET 版 WPF 中運作。 ↩ ↩2 ↩3 ↩4 -
Microsoft Learn,Retrieve resources in .NET apps。說明資源回退流程(依特定文化特性 → 中性文化特性 → 主組件預設資源的順序搜尋)。 ↩ ↩2 ↩3
-
Microsoft Learn,CultureInfo.CurrentUICulture Property。說明
CurrentUICulture如何用於資源管理員(Resource Manager)搜尋特定文化特性資源、其預設值源自 OS 的使用者介面語言,以及透過DefaultThreadCurrentUICulture設定所有執行緒預設值的方式。 ↩ ↩2 -
Microsoft Learn,Resources in .NET apps。說明 resx 資源的建立與封裝、主組件與附屬組件所構成的主軸與分支(Hub & Spoke)模型,以及
NeutralResourcesLanguage屬性。 ↩ ↩2 -
Microsoft Learn,Walkthrough: Localizing a Hybrid Application。說明在 Windows Forms 設計工具中將
Localizable屬性設為true並切換Language屬性以產生各語言 resx(例如 Form1.es-ES.resx)的步驟,以及在呼叫InitializeComponent之前設定CurrentUICulture的範例。 ↩ -
Microsoft Learn,How to: Design a Windows Forms Layout that Responds Well to Localization。說明如何使用
TableLayoutPanel,打造控制項大小能因應在地化造成Text屬性字串長度變化的版面配置。 ↩ -
Microsoft Learn,Localize ClickOnce applications。說明將所有附屬組件納入單一部署是 Visual Studio 的預設方式,以及執行時會依 OS 的文化特性使用對應的附屬組件。 ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
Windows 業務應用程式的列印與 PDF 輸出 ── System.Drawing.Printing / WPF / 報表函式庫的取捨
本文以要件別判斷表整理 PrintDocument 的 WinForms 列印、WPF 的 FlowDocument/FixedDocument 列印,以及 PDF 輸出的各種選項。並從實務角度說明分頁控制、DPI 落差等實作陷阱,以及從 Windows 服務列印、依賴預設...
CSV不是「純文字」而已 ── C# 業務應用程式的 CSV 實務(字元編碼・Excel 相容性・注入攻擊防範)
整理業務應用程式在 CSV 讀寫時容易出事故的典型模式──用 Split(',') 自行解析、無 BOM 的 UTF-8 在 Excel 中亂碼、開頭零消失、CSV 注入攻擊──從 RFC 4180 的規則、.NET 中 Shift_JIS 的處理方式(CodePagesE...
不要用 using 包住 HttpClient ── C# 商用應用程式的 HTTP 通訊實務(生成模式・逾時・重試)
C# 的 HttpClient 若每次都用 using 生成,會導致 Socket 枯竭;改用 static 又無法跟上 DNS 變更──本文從 Windows 商用應用程式的實務角度,整理這兩大問題的成因,以及透過 PooledConnectionLifetime、IHt...
用 PerfView 與 dotnet-trace 找出「變慢」的原因 ── .NET 效能調查實務入門
當商用應用程式「變慢」「CPU 卡住不動」「偶爾當掉」時,該用哪個工具看什麼?本文整理 PerfView 與 dotnet-trace 的角色分工、CPU 取樣的讀法(inclusive/exclusive)、用 ThreadTime 調查阻塞時間,以及與 EventSou...
不只是 appsettings.json ── Windows 商用應用程式的組態管理實務(環境別設定・機密資訊・寫入位置)
本文從實務角度整理 Windows 商用應用程式的組態管理,涵蓋 appsettings.json 的分層、IConfiguration 與 IOptions/IOptionsSnapshot/IOptionsMonitor 的選用方式、環境別設定、可寫入設定的存放位置、機...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
UI 執行緒 & 計時器
整理 WPF / WinForms UI 執行緒、非同步流程、Dispatcher 使用、計時器判斷的主題頁面。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
桌面應用程式多語言對應的設計與實作,屬於 Windows 應用程式開發的實務諮詢範疇。
技術諮詢 & 設計審查
既有應用程式事後多語言化時的方式選定與影響範圍評估,涉及需要設計審查的技術諮詢。
常見問題
整理諮詢這個主題時常見的問題。
- 只用日文開發的既有應用程式,之後還能多語言化嗎?
- 可以,但作業的大部分不是「導入資源機制」,而是「清查並替換寫死在程式碼中的字串」。除了畫面上的文字之外,訊息方塊、記錄檔(log)、例外訊息、報表、儲存在資料庫中供顯示用的字串都需要盤點,規模取決於字串的散落程度,而不是畫面數量。若能在新開發階段就將所有面向使用者顯示的字串經由 resx 管理,未來多語言化的成本會大幅降低。
- 翻譯可以只靠機器翻譯完成嗎?
- 若是內部工具或暫時性因應措施,從機器翻譯開始是可行的。不過業務應用程式中有許多按鈕與選單上的短詞(例如「登錄」「取消」),缺乏上下文時機器翻譯的誤譯率會提高。建議至少安排一輪讓實際使用該語言的人操作畫面的審查。此外,比翻譯品質更重要的是,要先透過 TableLayoutPanel 或 AutoSize 確保「翻譯後字串變長也不會讓版面跑掉」。
- 記錄檔(log)或錯誤訊息也應該翻譯嗎?
- 實務上的慣例是:顯示給使用者看的訊息要翻譯,但寫入記錄檔或事件記錄的內容不翻譯(固定使用單一語言)。記錄檔是給開發者、維運人員閱讀的,而非使用者,多語言的記錄檔會破壞調查時的可搜尋性。記錄檔的格式(日期、數值)也應固定使用 CultureInfo.InvariantCulture。只要在設計上將 CurrentCulture 與 CurrentUICulture 分開處理,就能自然地做到這種區分。
- 除了 OS 的顯示語言之外,應該讓應用程式本身也能切換語言嗎?
- 對於拓展海外據點,或是日本國內有外語員工使用的業務應用程式而言,擁有應用程式內部的語言設定很有價值。穩妥的做法是採取兩段式設計:預設情況下跟隨 OS 的顯示語言(即 CurrentUICulture 的預設值),只有在設定畫面中明確選擇時才覆寫。將切換的生效時機設計為「下次啟動時套用」最為安全,不論是實作還是對使用者的說明都會更單純。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。