當地語系化您的 UI 及應用程式套件資訊清單中的字串
如需有關將您的應用程式當地語系化的價值主張的詳細資訊,請參閱全球化和當地語系化。
如果希望應用程式支援不同的顯示語言,而且您的程式碼、XAML 標記或應用程式套件資訊清單也含有字串常值時,請將這些字串移入資源檔案 (.resw)。 您可以接著針對應用程式支援的每一種語言建立該資源檔案的翻譯複本。
硬編碼的字串常值可顯示在命令式程式碼或 XAML 標記中,例如當做 TextBlock 的 Text 屬性。 它們也可能顯示在應用程式套件資訊清單來源檔 (Package.appxmanifest
檔案),例如在 Visual Studio 資訊清單設計工具的 [應用程式] 索引標籤中顯示為顯示名稱的值。 將這些檔案移到資源檔案 (.resw),然後將應用程式和資訊清單中的硬編碼字串常值替換為資源識別碼的參照。
與僅包含一個影像資源的影像資源檔案不同,字串資源檔案中包含多個字串資源。 字串資源檔案是一種資源檔案 (.resw),您通常會在專案的 \Strings 資料夾建立這種資源檔案。 如需了解在資源檔案 (.resw) 的名稱中使用限定詞的背景資訊,請參閱量身打造語言、比例和其他限定詞適用的資源。
在資源檔案存放字串
設定應用程式的預設語言。
- 在解決方案已於 Visual Studio 開啟的情況下,開啟
Package.appxmanifest
。 - 確認 [應用程式] 索引標籤的預設語言已設定妥當 (例如「en」或「en-US」)。 剩下的步驟皆假定您已將預設語言設為「en-US」。
注意
您至少必須為該預設語言提供當地語言化的字串資源。 如果系統無法從使用者的偏好語言或顯示語言設定找到較好的比對結果,就會載入這些資源。
- 在解決方案已於 Visual Studio 開啟的情況下,開啟
為預設語言建立資源檔案 (.resw)。
- 在專案節點之下建立新資料夾,並命名為
Strings
。 - 在
Strings
之下建立新子資料夾,並命名為en-US
。 - 在
en-US
之下建立新資源檔案 (.resw) (位於「新增項目」對話方塊的 XAML 檔案類型之下),並確認它已命名為Resources.resw
。
注意
如果您有想要移植的 .NET 資源檔案 (.resx),請參閱移植 XAML 和 UI。
- 在專案節點之下建立新資料夾,並命名為
開啟
Resources.resw
並新增這些字串資源。Strings/en-US/Resources.resw
在此範例中,「Greeting」是您可以從標記中參照的字串資源識別碼,我們接下來將會示範操作。 在「Greeting」識別碼中,會有一個為 Text 屬性而設的字串,以及一個為 Width 屬性而設的字串。 「Greeting.Text」是屬性識別碼的一種範例,它正好對應 UI 元素的屬性。 舉例來說,您也可以在「名稱」欄加入「Greeting.Foreground」,並將值設為「Red」。 「Farewell」識別碼是一組簡單的字串資源識別碼,沒有子屬性,且可從命令式程式碼載入,我們接下來將會示範操作。 「評論」欄是很適合用來向譯者提供特別指示的位置。
在此範例中,我們已經有一組名稱為「Farewell」的簡單字串資源識別碼項目,所以不能再加入以相同識別碼為依據的屬性識別碼。 如果加入「Farewell.Text」,組建
Resources.resw
時會發生「重複項目」錯誤。資源識別碼需區分大小寫,且在每個資源檔案中都不得重複。 提供額外脈絡給譯者時,請務必使用有效的資源識別碼。 此外,字串資源送交翻譯後,請勿再變更資源識別碼。 當地語系化團隊會使用資源識別碼來追蹤資源中的增補、刪除和更新項目。 變更資源識別碼即為「資源識別碼移位」,會導致字串需要重新翻譯,因為它的呈現方式宛如刪除字串後新增其他字串。
參照 XAML 的字串資源識別碼
將標記中的控制項或其他元素與字串資源識別碼建立關聯時,您會使用 x:Uid directive。
<TextBlock x:Uid="Greeting"/>
\Strings\en-US\Resources.resw
會在執行階段載入 (因為此時它是專案中唯一的資源檔案)。 TextBlock 的指示詞 x:Uid 會產生查詢動作,尋找 Resources.resw
當中包含字串資源識別碼「Greeting」的屬性識別碼。 系統會找到「Greeting.Text」和「Greeting.Width」屬性識別碼,它們的值都會套用到 TextBlock,覆寫任何在標記中已完成本機設定的值。 如果您有新增「Greeting.Foreground」,該值也一樣會經過套用。 但只有屬性識別碼會用於設定 XAML 標記元素的屬性,所以在這個 TextBlock 將 x:Uid 設為「Farewell」不會發生任何效果。 Resources.resw
會包含字串資源標識碼 「Farewell」,但不包含它的屬性識別碼。
將字串資源識別碼指派給 XAML 元素時,請確認該識別碼的所有屬性識別碼都適用於 XAML 元素。 舉例來說,如果您在 TextBlock 設定了 x:Uid="Greeting"
,「Greeting.Text」就可解析,因為 TextBlock 類型含有 Text 屬性。 但如果您在 Button 設定 x:Uid="Greeting"
,「Greeting.Text」就會造成執行階段錯誤,因為 Button 類型不含 Text 屬性。 這種情況的一種解決方案是編寫名稱為「ButtonGreeting.Content」的屬性識別碼,然後在 Button 設定 x:Uid="ButtonGreeting"
。
與其設定資源檔案的 Width,建議您讓控制項對內容動態縮放大小。
注意:對於附加屬性,您需要在 .resw 檔案的「名稱」欄位中使用特殊語法。 舉例來說,如果要為「Greeting」識別碼設定附加屬性 AutomationProperties.Name 的值,您就必須在「名稱」欄輸入這種語法。
Greeting.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name
參照程式碼的字串資源識別碼
您可以根據簡單字串資源識別碼來明確載入字串資源。
注意
如果您呼叫了任何可能在背景/工作執行緒上執行的 GetForCurrentView 方法,請透過if (Windows.UI.Core.CoreWindow.GetForCurrentThread() != null)
測試來保護該呼叫。 從背景/工作執行緒呼叫 GetForCurrentView 會導致例外狀況「可能無法在沒有 CoreWindow 的執行緒上建立 <typename>」。
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Farewell");
auto resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView() };
myXAMLTextBlockElement().Text(resourceLoader.GetString(L"Farewell"));
auto resourceLoader = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView();
this->myXAMLTextBlockElement->Text = resourceLoader->GetString("Farewell");
您可以在類別庫 (Universal Windows) 或 Windows 執行階段程式庫 (Universal Windows) 專案中使用相同的程式碼。 到了執行階段,主控類別庫的應用程式資源就會載入。 由於應用程式的當地語系化程度可能較大,我們建議讓類別庫載入主控應用程式的資源。 如果類別庫需要提供資源,就必須為它的主控應用程式提供選項,讓應用程式在輸入中替換掉這些資源。
如果資源名稱有分段 (內含「.」字元),則請將資源名稱中的點替換成往前的斜線 (「/」)。 舉例來說,屬性識別碼包含點,您就必須執行上述替換作業,才能載入程式碼中的其一屬性。
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Fare/Well"); // <data name="Fare.Well" ...> ...
如有疑慮,您可以使用 MakePri.exe 來傾印應用程式的 PRI 檔案。 每個資源的 uri
都會顯示在傾印檔案中。
<ResourceMapSubtree name="Fare"><NamedResource name="Well" uri="ms-resource://<GUID>/Resources/Fare/Well">...
參照應用程式套件資訊清單的字串資源識別碼
開啟應用程式套件資訊清單資源檔案 (
Package.appxmanifest
檔案);根據預設,應用程式的Display name
會在該檔案中表達為字串常值的格式。若要製作該字串的可當地語系化版本,請開啟
Resources.resw
,並以名稱「AppDisplayName」和值「Adventure Works Cycles」新增字串資源。以您剛建立的字串資源識別碼 (「AppDisplayName」) 參照來替換顯示名稱字串常值。 執行此作業時您會使用
ms-resource
URI (統一資源識別項) 配置。針對您在資訊清單中想要當地語系化的每個字串重複此程序。 例如您應用程式的短名稱 (您可以設定讓它顯示在「開始」的應用程式圖磚)。 如需應用程式套件資訊清單的完整項目清單,以便您當地語系化,請參閱可當地語系化的資訊清單項目。
將字串資源當地語系化
製作資源檔案 (.resw) 的複本供其他語言使用。
- 在「字串」之下建立新的子資料夾,然後命名為「de-DE」供德語使用。
注意:您可以在資料夾名稱使用任何 BCP-47 語言標籤。 請參閱針對語言、比例和其他限定詞量身打造資源詳細了解語言限定詞和常用語言標籤清單。 - 在
Strings/de-DE
資料夾製作Strings/en-US/Resources.resw
複本。
- 在「字串」之下建立新的子資料夾,然後命名為「de-DE」供德語使用。
翻譯字串。
- 開啟
Strings/de-DE/Resources.resw
並翻譯「值」欄中的值。 您不需要翻譯評論。
Strings/de-DE/Resources.resw
- 開啟
如有需要,您可以針對其他語言重複步驟 1 和 2。
Strings/fr-FR/Resources.resw
測試您的應用程式
測試應用程式的預設顯示語言。 您接著可以在 [設定]>[時間]&[語言]>[區域]&[語言]>[語言] 變更顯示語言,然後重新測試應用程式。 請注意 UI 和殼層中的字串 (例如標題列,亦即顯示名稱,以及圖磚中的短名稱)。
注意:如果找到的資料夾名稱與顯示名稱設定相符,則資料夾中的資源檔案就會載入。 否則系統會進行遞補,以應用程式預設語言的資源結尾。
將字串分解到多個資源檔案中
您可以保留單一資源檔案 (resw) 中的所有字串,也可以將字串分解到多個資源檔案。 舉例來說,您可以分別將錯誤訊息、應用程式套件資訊清單字串和 UI 字串保留在三個資源檔案。 這種情況下,資料夾結構如下。
若要讓字串資源識別碼參照特定檔案,在識別碼前加上 /<resources-file-name>/
即可。 下方的標記範例假定 ErrorMessages.resw
包含名稱為「PasswordTooWeak.Text」的資源,且該值描述錯誤。
<TextBlock x:Uid="/ErrorMessages/PasswordTooWeak"/>
您只需要在 以外的Resources.resw
資源檔案字串資源識別碼之前新增 /<resources-file-name>/
。 這是因為「Resources.resw」是預設檔案名稱,如果您略過檔案名稱 (正如我們在本主題前文的範例所做的示範),系統就會做出如上假設。
下方的程式碼範例假定 ErrorMessages.resw
包含名稱為「MismatchedPasswords」的資源,且該值描述錯誤。
注意
如果您呼叫了任何可能在背景/工作執行緒上執行的 GetForCurrentView 方法,請透過if (Windows.UI.Core.CoreWindow.GetForCurrentThread() != null)
測試來保護該呼叫。 從背景/工作執行緒呼叫 GetForCurrentView 會導致例外狀況「可能無法在沒有 CoreWindow 的執行緒上建立 <typename>」。
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("ErrorMessages");
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("MismatchedPasswords");
auto resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(L"ErrorMessages") };
myXAMLTextBlockElement().Text(resourceLoader.GetString(L"MismatchedPasswords"));
auto resourceLoader = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView("ErrorMessages");
this->myXAMLTextBlockElement->Text = resourceLoader->GetString("MismatchedPasswords");
如果您要將 Resources.resw
資源中的「AppDisplayName」移到 ManifestResources.resw
,則您必須在應用程式套件資訊清單中將 ms-resource:AppDisplayName
變更為 ms-resource:/ManifestResources/AppDisplayName
。
如果資源檔案名稱有分段 (包含「.」字元),您在參照此名稱時請保留點。 請勿將點替換成 (「/」) 字元,該動作適用於資源名稱。
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("Err.Msgs");
如有疑慮,您可以使用 MakePri.exe 來傾印應用程式的 PRI 檔案。 每個資源的 uri
都會顯示在傾印檔案中。
<ResourceMapSubtree name="Err.Msgs"><NamedResource name="MismatchedPasswords" uri="ms-resource://<GUID>/Err.Msgs/MismatchedPasswords">...
載入特定語言或其他脈絡的字串
預設 ResourceContext (從ResourceContext.GetForCurrentView 取得) 包含每個限定詞名稱的限定詞值,表示預設執行階段內容 (也就是目前使用者和電腦的設定)。 系統會依據資源檔案 (.resw) 名稱中的限定詞比對執行階段脈絡中的限定詞值。
但有些時候,您可能想在尋找要載入的資源檔案時,讓應用程式覆寫系統設定,並明確指定要使用的語言、比例或其他限定詞值。 舉例來說,您可能想讓使用者為工具提示或錯誤訊息選取替代語言。
您可以透過建構一個新的 ResourceContext (而不是使用預設的)、覆寫其值,然後在字串查詢中使用該內容物件來達成。
var resourceContext = new Windows.ApplicationModel.Resources.Core.ResourceContext(); // not using ResourceContext.GetForCurrentView
resourceContext.QualifierValues["Language"] = "de-DE";
var resourceMap = Windows.ApplicationModel.Resources.Core.ResourceManager.Current.MainResourceMap.GetSubtree("Resources");
this.myXAMLTextBlockElement.Text = resourceMap.GetValue("Farewell", resourceContext).ValueAsString;
按照上方程式碼範例使用 QualifierValues,對於任何限定詞皆適用。 遇到特殊的語言案例,您就可以改為使用這種方法來替代。
resourceContext.Languages = new string[] { "de-DE" };
對於在全域層級達到相同的效果,您可以覆寫預設 ResourceContext 中的限定詞值。 但建議您改為呼叫 ResourceContext.SetGlobalQualifierValue。 您可以使用對 SetGlobalQualifierValue 的呼叫來設定值一次,然後在每次用於查詢時,這些值都會在預設 ResourceContext 上生效。
Windows.ApplicationModel.Resources.Core.ResourceContext.SetGlobalQualifierValue("Language", "de-DE");
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Farewell");
某些限定詞具有系統資料提供者。 因此,您可以透過自己的 API 調整提供提供者,而不是呼叫 SetGlobalQualifierValue。 例如,此程式碼示範如何設定 PrimaryLanguageOverride。
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "de-DE";
更新字串以回應限定詞值變更事件
您正在執行的應用程式可以回應影響預設 ResourceContext 中限定詞值的系統設定變更。 這些系統設定中的任何一個都會呼叫 ResourceContext.QualifierValues 上的 MapChanged 事件。
為了回應此事件,您可以從預設 ResourceContext 重新載入字串。
public MainPage()
{
this.InitializeComponent();
...
// Subscribe to the event that's raised when a qualifier value changes.
var qualifierValues = Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().QualifierValues;
qualifierValues.MapChanged += new Windows.Foundation.Collections.MapChangedEventHandler<string, string>(QualifierValues_MapChanged);
}
private async void QualifierValues_MapChanged(IObservableMap<string, string> sender, IMapChangedEventArgs<string> @event)
{
var dispatcher = this.myXAMLTextBlockElement.Dispatcher;
if (dispatcher.HasThreadAccess)
{
this.RefreshUIText();
}
else
{
await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => this.RefreshUIText());
}
}
private void RefreshUIText()
{
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Farewell");
}
從類別庫或 Windows 執行階段程式庫載入字串
參考的類別庫 (通用 Windows) 或 Windows 執行階段程式庫 (通用 Windows) 的字串資源,通常會新增到建置程序中包含它們的套件的子資料夾中。 這類字串的資源識別碼通常會採用 LibraryName/ResourcesFileName/ResourceIdentifier 的格式。
類別庫可能會為自己的資源取得 ResourceLoader。 舉例來說,下列程式碼展示了參照此程式碼的類別庫或應用程式如何為類別庫的字串資源取得 ResourceLoader。
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("ContosoControl/Resources");
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("exampleResourceName");
對於 Windows 執行階段程式庫 (通用 Windows),如果預設命名空間已分割 (其中包含「.」字元),則請使用資源地圖名稱中的點。
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("Contoso.Control/Resources");
您不需要針對類別庫 (通用 Windows) 執行該動作。 如果有疑問,您可以指定 MakePri.exe command line options 命令列選項來傾印元件或類別庫的 PRI 檔案。 每個資源的 uri
都會顯示在傾印檔案中。
<NamedResource name="exampleResourceName" uri="ms-resource://Contoso.Control/Contoso.Control/ReswFileName/exampleResourceName">...
載入其他套件的字串
應用程式套件的資源是透過套件自有的最頂層 ResourceMap 來管理與存取,該字串可從目前的 ResourceManager 存取。 在每個套件中,各種元件都可能有自己的 ResourceMap 樹狀子目錄,可經由 ResourceMap.GetSubtree 存取。
架構套件可透過絕對資源識別碼 URI 存取自己的資源。 另請參閱 URI 配置。
載入未封裝應用程式的字串
至 Windows 版本 1903 (2019 年 5 月更新版),未封裝的應用程式也可以運用資源管理系統。
您只要建立 UWP 的使用者控制項/程式庫,然後在資源檔案存放任何字串即可。 您接著就能參照 XAML 的字串資源識別碼、參照程式碼的字串資源識別碼,或載入類別庫或 Windows 執行階段程式庫的字串。
若要使用未封裝應用程式的資源,您需要採取幾個動作:
- 從程式碼解析資源時,請使用 GetForViewIndependentUse,而不是 GetForCurrentView,因為在未封裝的案例中沒有目前視圖。 如果您在未封裝的案例中呼叫 GetForCurrentView,就會發生下列例外狀況:資源內容可能不會建立在沒有 CoreWindow 的執行緒上。
- 使用 MakePri.exe 手動產生應用程式的 resources.pri 檔案。
makepri new /pr <PROJECTROOT> /cf <PRICONFIG> /of resources.pri
執行- <PRICONFIG> 必須刪去<「packaging」>區段,這樣才能讓所有資源都封裝在單一 resources.pri 檔案。 如果使用 createconfig 建立的預設 MakePri.exe 設定檔,您必須在建立後手動刪除<「packaging」>區段。
- <PRICONFIG> 必須包含所有相關的必需索引子,才能將專案的所有資源合併到單一 resources.pri 檔案。 createconfig 建立的預設 MakePri.exe 設定檔就包含所有索引子。
- 如果不使用預設設定,請確保 PRI 索引子已啟用 (請檢視預設設定了解操作方法),以便合併從位在專案根目錄當中的 UWP 專案參照、NuGet 參照等來源找到的 PRI。
注意
刪去
/IndexName
之後,加上專案沒有應用程式資訊清單,PRI 檔案的 IndexName/root 命名空間會自動設為 Application,亦即執行階段理解了未封裝應用程式的需求 (這個動作移除了之前對套件 ID 的硬相依性)。 指定資源 URI 時,刪去根目錄命名空間的 ms-resource:/// 參照會將 Application 推論為未封裝應用程式的根目錄命名空間 (您也可以在 ms-resource://Application/ 明確指定 Application)。
- 將 PRI 檔案複製到 .exe 的組建輸出目錄
- 執行 .exe
注意
資源管理系統在根據未封裝應用程式的語言解析資源時,會使用系統顯示語言,而非使用者偏好語言清單。 使用者偏好語言清單僅用於 UWP 應用程式。
重要
只要資源經過修改,您就必須手動重新建立 PRI 檔案。 建議使用建立後指令碼來處理 MakePri.exe 命令,並將 resources.pri 輸出複製到 .exe 目錄。