在舊版應用程式或遊戲中使用 Windows 10 資源管理系統

.NET 和 Win32 應用程式和遊戲通常會當地語系化為不同的語言,以擴展其可尋址市場總數。 如需有關將您的應用程式當地語系化的價值主張的詳細資訊,請參閱全球化和當地語系化。 將您的 .NET 或 Win32 應用程式或遊戲封裝成 .msix 或 .appx 套件,即可利用「資源管理系統」載入為執行階段內容量身打造的應用程式資源。 這個深入主題說明技術。

當地語系化傳統 Win32 應用程式的方法有很多種,但 Windows 8 引入了一種新的資源管理系統,該系統可以跨程式語言、跨應用程式類型工作,並提供除簡單當地語系化之外的功能。 此系統在本主題中將稱為「MRT」。 從歷史上看,這代表「現代資源技術」,但「現代」一詞已經停止。 資源管理員可能也稱為 MRM (新式資源管理員) 或 PRI (套件資源索引)。

MRT 結合 MSIX 型或 .appx 型部署 (例如,從 Microsoft Store),可以自動為指定的使用者/裝置提供最適用的資源,以將應用程式的下載和安裝大小降到最低。 對於具有大量當地語系化內容的應用程式來說,這種大小的減少可能非常重要,對於 AAA 遊戲來說,可能會達到幾 gigabytes 的量級。 MRT 的其他優點包括 Windows Shell 和 Microsoft Store 中的當地語系化清單,當使用者偏好的語言不符合可用資源時,自動後援邏輯。

本文件說明 MRT 的高階架構,並提供移植指南,以協助以最少的程式碼變更將舊版 Win32 應用程式移至 MRT。 移動 MRT 之後,開發人員便能獲得其他優點 (例如依縮放比例或系統主題分割資源的能力)。 請注意,MRT 型當地語系化適用於由傳統型橋接器 (又稱「Centennial」) 處理的 UWP 應用程式和 Win32 應用程式。

在許多情況下,您可以繼續使用現有的當地語系化格式和原始程式碼,同時與 MRT 整合,以在執行階段解析資源,並將下載大小降到最低 - 這不是全有或全無的方法。 以下資料表摘要說明每個階段的工作和預估成本/優點。 以下資料表不包含非當地語系化工作,例如提供高解析度或高對比應用程式圖示。 如需為圖塊、圖示等提供多個資產的詳細資訊,請參閱針對語言、縮放比例、高對比及其他限定詞量身打造您的資源

工作 優點 預估成本
當地語系化套件資訊清單 讓當地語系化內容出現在 Windows Shell 和 Microsoft Store 中所需的最低工作 Small
使用 MRT 來識別和尋找資源 將下載和安裝大小降到最低的必要條件;自動語言後援
建置資源套件 將下載和安裝大小降到最低的最後一個步驟 Small
移轉至 MRT 資源格式和 API 檔案大小明顯較小 (視現有的資源技術而定) 大型

簡介

大多數重要的應用程式都包含稱為資源的使用者介面元素,這些元素與應用程式的程式碼分離 (與在原始程式碼本身中編寫的 硬編碼值相比)。 例如,有數個理由偏好使用資源而不是硬編碼值-例如,非開發人員輕鬆編輯,但其中一個主要原因是讓應用程式在執行階段挑選相同邏輯資源的不同表示法。 例如,在按鈕上顯示的文字 (或要在圖示中顯示的影像) 可能會因使用者了解的語言、顯示裝置的特性,或使用者是否已啟用任何輔助技術而有所不同。

因此,任何資源管理技術的主要目的,是在執行階段將對邏輯或符號資源名稱 (例如SAVE_BUTTON_LABEL) 的請求轉換為來自一組可能的候選項目 (例如,「儲存」、「Speichern」或「저장」) 的最佳可能實際 (例如「儲存」)。 MRT 提供了這樣的功能,並使應用程式能夠使用稱為限定詞的各種屬性來識別資源候選項目,例如使用者的語言、顯示比例因子、使用者選取的主題和其他環境因素。 MRT 甚至支援需要它的應用程式的自定義限定詞 (例如,應用程式可以為已使用帳戶登入的使用者提供不同的圖形資產,而不需明確將這項檢查新增至其應用程式的每個部分)。 MRT 同時使用字串資源和檔案型資源,其中以檔案為基礎的資源會實做為外部資料的參考 (檔案本身)。

範例

以下是一個簡單的應用程式範例,該應用程式在兩個按鈕 (openButtonsaveButton) 上有文字標籤,並且有一個用於標誌 (logoImage) 的 PNG 檔案。 文字標籤會當地語系化為英文和德文,而且標誌已針對標準桌面顯示器 (100% 縮放比例) 和高解析度手機 (300%縮放比例) 最佳化。 請注意,此圖表呈現模型的概念性高階檢視;它不會完全對應至實作。

Screenshot of a Source code label, a Lookup table label, and a Files on disk label.

在圖形中,應用程式程式碼會參考三個邏輯資源名稱。 在執行階段,GetResource 虛擬函式會使用 MRT 在資源資料表中尋找這些資源名稱 (稱為 PRI 檔案),並根據環境條件尋找最適當的候選項目 (使用者的語言和顯示器的縮放比例)。 在標籤的情況下,會直接使用字串。 在標誌影像的情況下,字串會解譯為檔名,而且檔案會讀取磁碟。

如果使用者使用英文或德文以外的語言,或顯示縮放比例不是 100% 或 300%,MRT 會根據一組後備規則選擇「最接近」的相符候選項目 (有關更多背景資訊,請參閱資源管理系統)。

請注意,MRT 支援針對多個限定詞量身打造的資源,例如,如果標誌影像包含也需當地語系化的內嵌文字,則標誌會有四個候選項目:EN/Scale-100、DE/Scale-100、EN/Scale-300 和 DE/Scale-300。

本文件中的章節

下列各節概述整合 MRT 與應用程式所需的高階工作。

階段 0:建置應用程式套件

本節概述如何以應用程式套件的形式建置現有的傳統型應用程式。 在這個階段不會使用 MRT 功能。

階段 1:將應用程式資訊清單當地語系化

本節概述如何將應用程式的資訊清單當地語系化 (使其在 Windows Shell 中正確顯示),同時仍使用舊版資源格式和 API 來封裝和尋找資源。

階段 2:使用 MRT 來識別和尋找資源

本節概述如何修改應用程式程式碼 (以及可能的資源配置) 以使用 MRT 尋找資源,同時仍使用現有的資源格式和 API 來載入和使用資源。

階段3:建置資源套件

本節概述將資源分成個別 資源套件所需的最終變更,將應用程式的下載 (和安裝) 大小降到最低。

本文件中未涵蓋

完成上述階段 0-3 之後,您將會有可提交至 Microsoft Store 的應用程式「套件組合」,藉由省略不需要的資源來將下載和安裝大小降到最低 (例如,不需要的語言)。 您可以採取最後一個步驟,進一步改善應用程式大小和功能。

階段 4:移轉至 MRT 資源格式和 API

此階段超出本文件的範圍;它需要將資源 (特別是字串) 從舊版格式移動,例如 MUI DLL 或 .NET 資源組件到 PRI 檔案。 這可能會導致下載&安裝大小的進一步節省空間。 它也允許使用其他 MRT 功能,例如根據縮放比例、輔助功能設定等,將影像檔案的下載和安裝降到最低。

階段 0:建置應用程式套件

在對應用程式資源進行任何變更之前,您必須先使用標準的 UWP 封裝和部署技術來取代目前的封裝和安裝技術。 有三種方式來完成此動作:

  • 如果您有具有複雜安裝程式的大型傳統型應用程式,或使用大量的 OS 擴充點,您可以使用桌面應用程式轉換器工具,從現有的應用程式安裝程式產生 UWP 檔案配置和資訊清單資訊 (例如MSI)。
  • 如果您有較小的傳統型應用程式,且檔案相對較少,或是簡單的安裝程式,而且沒有擴充性攔截程序,您可以手動建立檔案配置和資訊清單資訊。
  • 如果您要從來源重建,並想要將應用程式更新為純 UWP 應用程式,您可以在 Visual Studio 中建立新的專案,並依賴 IDE 為您執行大部分的工作。

如果要使用 Desktop App Converter,請參閱使用 Desktop App Converter 封裝桌面應用程式,以了解有關轉換過程的更多資訊。 完整的 Desktop Converter 範例集可以在 傳統型橋接器至 UWP 範例 GitHub 儲存庫中找到

如果您想要手動建立套件,您必須建立目錄結構,其中包含您應用程式的所有檔案 (可執行檔和內容,但不是原始程式碼),以及套件資訊清單檔案 (.appxmanifest)。 可以在 Hello, World GitHub 範例中找到範例,但執行名為ContosoDemo.exe的桌面執行檔的基本套件清單檔案如下,其中 反白顯示的文字將替換為您自己的值。

<?xml version="1.0" encoding="utf-8" ?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
         xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
         xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
         xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
         IgnorableNamespaces="uap mp rescap">
    <Identity Name="Contoso.Demo"
              Publisher="CN=Contoso.Demo"
              Version="1.0.0.0" />
    <Properties>
    <DisplayName>Contoso App</DisplayName>
    <PublisherDisplayName>Contoso, Inc</PublisherDisplayName>
    <Logo>Assets\StoreLogo.png</Logo>
  </Properties>
    <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14393.0" 
                        MaxVersionTested="10.0.14393.0" />
  </Dependencies>
    <Resources>
    <Resource Language="en-US" />
  </Resources>
    <Applications>
    <Application Id="ContosoDemo" Executable="ContosoDemo.exe" 
                 EntryPoint="Windows.FullTrustApplication">
    <uap:VisualElements DisplayName="Contoso Demo" BackgroundColor="#777777" 
                        Square150x150Logo="Assets\Square150x150Logo.png" 
                        Square44x44Logo="Assets\Square44x44Logo.png" 
        Description="Contoso Demo">
      </uap:VisualElements>
    </Application>
  </Applications>
    <Capabilities>
    <rescap:Capability Name="runFullTrust" />
  </Capabilities>
</Package>

如需套件資訊清單檔案和套件配置的詳細資訊,請參閱應用程式套件資訊清單

最後,如果您使用 Visual Studio 來建立新的專案,並移轉現有的程式碼,請參閱建立「Hello, world」應用程式。 您可以將現有的程式碼包含在新的專案中,但您可能必須進行重要的程式碼變更 )特別是在使用者介面中),才能以純 UWP 應用程式的形式執行。 這些變更超出本文件的範圍。

階段 1:將資訊清單當地語系化

步驟 1.1:更新資訊清單中的字串&資產

在階段 0 中,您已為您的應用程式建立基本套件資訊清單 (.appxmanifest) 檔案 (根據提供給轉換器的值、從 MSI 擷取或手動輸入資訊清單),但不包含當地語系化的資訊,也不會支援其他功能,例如高解析度的開始圖塊資產等等。

為了確保應用程式的名稱和描述正確當地語系化,您必須在一組資源檔案中定義一些資源,並更新套件資訊清單以參照它們。

建立預設資源檔

第一個步驟是使用預設語言建立預設資源檔 (例如美式英文)。 您可以使用文字編輯器,或透過 Visual Studio 中的資源設計工具手動執行這項操作。

如果您要手動建立資源:

  1. 建立名為 resources.resw 的 XML 檔案,並將它放在專案的 Strings\en-us 子資料夾中。 如果您的預設語言不是美式英文,請使用適當的 BCP-47 程式碼。
  2. 在 XML 檔案中,新增下列內容,其中 反白顯示的文字會以預設語言取代為應用程式的適當文字。

注意

這些字串的長度有一些限制。 如需詳細資訊,請參閱 VisualElements

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="ApplicationDescription">
    <value>Contoso Demo app with localized resources (English)</value>
  </data>
  <data name="ApplicationDisplayName">
    <value>Contoso Demo Sample (English)</value>
  </data>
  <data name="PackageDisplayName">
    <value>Contoso Demo Package (English)</value>
  </data>
  <data name="PublisherDisplayName">
    <value>Contoso Samples, USA</value>
  </data>
  <data name="TileShortName">
    <value>Contoso (EN)</value>
  </data>
</root>

如果您想要在 Visual Studio 中使用設計工具:

  1. 在專案中建立 Strings\en-us 資料夾 (或其他適當的語言),然後使用預設名稱 resources.resw 新項目 新增至專案的根資料夾。 請務必選擇 資源檔案 (.resw) 而不是資源字典 - 資源字典是 XAML 應用程式使用的檔案。
  2. 使用設計工具輸入以下字串 (使用相同的 Names,但將其替換Values為適合您的應用程式的文字):

Screenshot showing the Resources.resw file showing the Name and Value columns. for the resources.

注意

如果您從 Visual Studio 設計工具開始,則一律可以按下 F7 直接編輯 XML。 但是,如果從最小的 XML 檔案開始,設計工具將無法識別該檔案,因為它缺少許多附加中繼資料;您可以將樣板 XSD 資訊從設計工具產生的檔案複製到手動編輯的 XML 檔案中來解決此問題。

更新資訊清單以參考資源

在檔案中 .resw 定義值之後,下一個步驟是更新資訊清單以參考資源字串。 同樣地,您可以直接編輯 XML 檔案,或依賴 Visual Studio 資訊清單設計工具。

如果您要直接編輯 XML,請開啟 AppxManifest.xml 檔案,並對反白顯示的值進行下列變更- 使用此確切文字,而不是應用程式特有的文字。 您不需要使用這些確切的資源名稱,您可以選擇自己的名稱,但您選擇的任何名稱都必須完全符合 .resw 檔案中的任何名稱。 這些名稱應與您在 .resw 檔案中建立的 Names 相符,並以 ms-resource: 配置和 Resources/ 命名空間為首碼。

注意

資訊清單的許多元素都已從此代碼段省略 - 請勿刪除任何項目!

<?xml version="1.0" encoding="utf-8"?>
<Package>
  <Properties>
    <DisplayName>ms-resource:Resources/PackageDisplayName</DisplayName>
    <PublisherDisplayName>ms-resource:Resources/PublisherDisplayName</PublisherDisplayName>
  </Properties>
  <Applications>
    <Application>
      <uap:VisualElements DisplayName="ms-resource:Resources/ApplicationDisplayName"
        Description="ms-resource:Resources/ApplicationDescription">
        <uap:DefaultTile ShortName="ms-resource:Resources/TileShortName">
          <uap:ShowNameOnTiles>
            <uap:ShowOn Tile="square150x150Logo" />
          </uap:ShowNameOnTiles>
        </uap:DefaultTile>
      </uap:VisualElements>
    </Application>
  </Applications>
</Package>

如果您使用的是 Visual Studio 資訊清單設計工具,請開啟 .appxmanifest 檔案並變更 *Application 索引標籤和 Packaging 索引標籤中反白顯示的值

Screenshot of the Visual Studio Manifest Designer showing the Application tab with the Display name and Description text boxes called out.

Screenshot of the Visual Studio Manifest Designer showing the Packaging tab with the Package display name and Publisher display name text boxes called out.

步驟 1.2:建置 PRI 檔案、建立 MSIX 套件,並驗證其運作正常

您現在應該能夠建置 .pri 檔案並部署應用程式,以驗證正確的資訊 (以預設語言顯示) 出現在 [開始] 功能表中。

如果您要在 Visual Studio 中建置,只要按下 Ctrl+Shift+B 即可建置專案,然後在專案上按下滑鼠右鍵,即可從內容功能表中選擇 Deploy

如果您要手動建置,請按照以下步驟為 MakePRI 工具建立設定檔,並產生 .pri 檔案本身 (更多資訊可以在手動應用程式封裝中找到):

  1. 從「開始」功能表中的 Visual Studio 2017Visual Studio 2019 資料夾開啟開發人員命令提示字元。

  2. 切換到專案根目錄 (包含 .appxmanifest 檔案和 Strings 資料夾的目錄)。

  3. 輸入下列命令,以適合您專案的名稱取代「contoso_demo.xml」,並以「en-US」取代您應用程式的預設語言 (如果適用的話,請將它保留為 en-US)。 請注意,XML 檔案是在上層目錄中建立的 (不在 專案目錄中),因為它不是應用程式的一部分 (您可以選擇任何其他您想要的目錄,但請務必在未來的命令中取代該檔案)。

    makepri createconfig /cf ..\contoso_demo.xml /dq en-US /pv 10.0 /o
    

    您可以輸入 makepri createconfig /? 以查看每個參數的功能,但摘要如下:

    • /cf 會設定群設定檔名 (此命令的輸出)
    • /dq 會設定預設限定詞,在此案例中為語言 en-US
    • /pv 會設定平台版本,在此案例中為 Windows 10
    • /o 會將其設定為覆寫輸出檔案 (如果存在)
  4. 現在您已擁有設定檔,請再次執行 MakePRI,以實際搜尋磁碟中的資源,並將其封裝到 PRI 檔案中。 將「contoso_demop.xml」取代為您在上一個步驟中使用的 XML 檔名,並確定同時指定輸入和輸出的上層目錄:

    makepri new /pr . /cf ..\contoso_demo.xml /of ..\resources.pri /mf AppX /o
    

    您可以輸入 makepri new /? 以查看每個參數的功能,但簡言之:

    • /pr 會設定專案根目錄 (在此案例中為目前的目錄)
    • /cf 會設定在上一個步驟中建立的設定檔名
    • /of 會設定輸出檔案
    • /mf 會建立對應檔案 (因此我們可以在稍後的步驟中排除套件中的檔案)
    • /o 會將其設定為覆寫輸出檔案 (如果存在)
  5. 現在您有一個具有預設語言資源的 .pri 檔案 (例如 en-US)。 若要驗證其是否運作正常,您可以執行下列命令:

    makepri dump /if ..\resources.pri /of ..\resources /o
    

    您可以輸入 makepri dump /? 以查看每個參數的功能,但簡言之:

    • /if 會設定輸入檔名
    • /of 會設定輸出檔名稱 (.xml 將會自動附加)
    • /o 會將其設定為覆寫輸出檔案 (如果存在)
  6. 最後,您可以在文字編輯器中開啟 ..\resources.xml 並驗證它列出您的 <NamedResource> 值 (例如 ApplicationDescriptionPublisherDisplayName),以及您選擇的預設語言的 <Candidate> 值 (檔案開頭會有其他內容;暫時忽略它)。

您可以開啟對應檔案 ..\resources.map.txt,驗證它包含專案所需的檔案 (包括 PRI 檔案,不屬於專案目錄的一部分)。 重要的是,對應檔案不會包含檔案 resources.resw 的參考,因為該檔案的內容已經內嵌在 PRI 檔案中。 不過,它會包含其他資源,例如影像的檔名。

建置和簽署套件

現在已建置 PRI 檔案,您可以建置並簽署套件:

  1. 若要建立應用程式套件,請執行下列命令,以取代 contoso_demo.appx 為您想要建立的 .msix/.appx 檔案名稱,並確保為檔案選擇不同的目錄 (此範例使用上層目錄;它可以是任何地方,但應該是專案目錄)。

    makeappx pack /m AppXManifest.xml /f ..\resources.map.txt /p ..\contoso_demo.appx /o
    

    您可以輸入 makeappx pack /? 以查看每個參數的功能,但簡言之:

    • /m 會設定要使用的資訊清單檔案
    • /f 會設定要使用的對應檔案 (在上一個步驟中建立)
    • /p 會設定輸出套件名稱
    • /o 會將其設定為覆寫輸出檔案 (如果存在)
  2. 建立套件之後,必須簽署它。 取得簽章憑證最簡單的方法是在 Visual Studio 中建立一個空的通用 Windows 專案,並複製它建立的 .pfx 檔案,但您可以使用 MakeCertPvk2Pfx 公用程式手動建立一個,如如何建立應用程式套件簽署憑證中所述。

    重要

    如果您手動建立簽署憑證,請務必將檔案放在與來源專案或套件來源不同的目錄中,否則它可能會包含在套件中,包括私密金鑰!

  3. 若要對套件進行簽署,請使用以下命令。 請注意,AppxManifest.xmlIdentity 元素中指定的 Publisher 必須與憑證的 Subject 相符 (這不是<PublisherDisplayName> 元素,它是向使用者顯示的當地語系化顯示名稱)。 像往常一樣,將 contoso_demo... 檔名替換為適合您的專案的名稱,並且 (非常重要) 確保 .pfx 檔案不在目前目錄中 (否則它將做為套件的一部分建立,包括私密簽署金鑰!):

    signtool sign /fd SHA256 /a /f ..\contoso_demo_key.pfx ..\contoso_demo.appx
    

    您可以輸入 signtool sign /? 以查看每個參數的功能,但簡言之:

    • /fd 會設定檔案摘要演算法 (SHA256 是 .appx 的預設值)
    • /a 將自動選取最佳憑證
    • /f 會指定包含簽署憑證的輸入檔案

最後,您現在可以按兩下 .appx 檔案來安裝它,或者如果您偏好命令列,您可以開啟 PowerShell 提示字元、變更至包含套件的目錄,然後輸入下列命令 (以您的套件名稱取代 contoso_demo.appx):

add-appxpackage contoso_demo.appx

如果您收到有關憑證不受信任的錯誤,請確定憑證已新增至電腦存放區 (而非使用者存放區)。 若要將憑證新增至電腦存放區,您可以使用命令列或 Windows 檔案總管。

若要使用命令列:

  1. 以管理員 istrator 身分執行 Visual Studio 2017 或 Visual Studio 2019 命令提示字元。

  2. 切換到包含 .cer 檔案的目錄 (請記得確保此目錄位於來源或專案目錄之外!)

  3. 輸入下列命令,將 contoso_demo.cer 取代為您的檔名:

    certutil -addstore TrustedPeople contoso_demo.cer
    

    您可以執行 certutil -addstore /? 以查看每個參數的功能,但簡言之:

    • -addstore 會將憑證新增至憑證存放區
    • TrustedPeople 會指出憑證放置所在的存放區

若要使用 Windows 檔案總管:

  1. 導覽至包含 .pfx 檔案的資料夾。
  2. 按兩下 .pfx 檔案,憑證匯入精靈應該會出現
  3. 選擇 Local Machine 並按一下 Next
  4. 如果出現,請接受使用者帳戶控制系統管理員提高權限提示,然後按一下 Next
  5. 如果有私鑰,請輸入私鑰的密碼,然後按一下 Next
  6. 選取 Place all certificates in the following store
  7. 按一下 Browse,然後選擇 Trusted People 資料夾 (不是「受信任的發行者」)
  8. 按一下 Next,然後 Finish

將憑證新增至 Trusted People 存放區之後,請嘗試再次安裝套件。

您現在應該會看到您的應用程式出現在 [開始] 功能表的 [所有應用程式] 清單中,其中包含 .resw / .pri 檔案中的正確資訊。 如果您看到空白字串或字串 ms-resource:...,則代表發生錯誤 - 請仔細檢查您的編輯,並確保其正確無誤。 如果您在 [開始] 功能表中以滑鼠右鍵按下您的應用程式,您可以將它釘選為圖塊,並驗證該處也顯示正確的資訊。

步驟 1.3:新增更多支援的語言

對套件資訊清單進行變更並建立初始 resources.resw 檔案之後,新增其他語言很容易。

建立其他當地語系化資源

首先,建立額外的當地語系化資源值。

Strings 資料夾內,使用適當的 BCP-47 程式碼,為每個支援的語言建立額外的資料夾 (例如,Strings\de-DE)。 在每一個資料夾中,建立一個 resources.resw 檔案 (使用 XML 編輯器或 Visual Studio 設計工具),其中包含翻譯的資源值。 假設您已在某處提供當地語系化的字串,您只需要將它們複製到 .resw 檔案中,本文件不會涵蓋翻譯步驟本身。

例如,Strings\de-DE\resources.resw 檔案看起來可能像這樣,反白顯示的文字 已從 en-US 變更:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="ApplicationDescription">
    <value>Contoso Demo app with localized resources (German)</value>
  </data>
  <data name="ApplicationDisplayName">
    <value>Contoso Demo Sample (German)</value>
  </data>
  <data name="PackageDisplayName">
    <value>Contoso Demo Package (German)</value>
  </data>
  <data name="PublisherDisplayName">
    <value>Contoso Samples, DE</value>
  </data>
  <data name="TileShortName">
    <value>Contoso (DE)</value>
  </data>
</root>

下列步驟假設您已為 de-DEfr-FR 新增資源,但任何語言都可以遵循相同的模式。

更新套件資訊清單以列出支援的語言

套件資訊清單必須更新,才能列出應用程式支援的語言。 Desktop App Converter 會新增預設語言,但必須明確新增其他語言。 如果您要直接編輯 AppxManifest.xml 檔案,請依照下列方式更新 Resources 節點、視需要新增多個元素,取代 您支援 的適當語言,並確保清單中的第一個項目是預設 (後援) 語言。 在此範例中,預設值為英文 (US),另外支援德文 (德國) 和法文 (法國):

<Resources>
  <Resource Language="EN-US" />
  <Resource Language="DE-DE" />
  <Resource Language="FR-FR" />
</Resources>

如果您使用 Visual Studio,則不需要執行任何動作;如果您查看 Package.appxmanifest,應該會看到特殊的 x 產生 值,這會導致建置程序插入專案中找到的語言 (根據名為 BCP-47 代碼的資料夾)。 請注意,這不是實際套件資訊清單的有效值;它僅適用於 Visual Studio 專案:

<Resources>
  <Resource Language="x-generate" />
</Resources>

使用當地語系化值重新建置

現在您可以再次建置和部署應用程式,而且如果您在 Windows 中變更語言喜好設定,您應該會看到新當地語系化的值出現在 [開始] 功能表中 (如何變更語言的指示如下)。

針對 Visual Studio,您可以再次使用 Ctrl+Shift+B 來建置,並以滑鼠右鍵按一下專案以 Deploy

如果您要手動建置專案,請遵循與上述相同的步驟,但在建立設定檔時,以底線分隔的其他語言新增至預設限定詞清單 (/dq)。 例如,若要支援上一個步驟中新增的英文、德文和法文資源:

makepri createconfig /cf ..\contoso_demo.xml /dq en-US_de-DE_fr-FR /pv 10.0 /o

這會建立一個 PRI 檔案,其中包含您可以輕鬆地用於測試的所有指定語言。 如果您的資源總規模很小,或者您只支援少量語言,這對於您的運輸應用程式來說可能是可以接受的;只有當您希望最大限度地減少資源的安裝/下載大小時,您才需要執行建置單獨語言套件的額外工作。

使用當地語系化值進行測試

若要測試新的當地語系化變更,您只需將新的偏好 UI 語言新增至 Windows 即可。 不需要下載語言套件、重新啟動系統,或讓整個 Windows UI 以外語顯示。

  1. 執行 Settings 應用程式 (Windows + I)
  2. 請前往 Time & language
  3. 請前往 Region & language
  4. 按一下 Add a language
  5. 輸入 (或選取) 您要的語言 (例如 DeutschGerman)
  • 如果有子語言,請選擇您想要的語言 (例如 Deutsch / Deutschland
  1. 在清單中選取新語言
  2. 按一下 Set as default

現在開啟 [開始] 功能表並搜尋您的應用程式,您應該會看到所選語言的當地語系化值 (其他應用程式可能也顯示為當地語系化)。 如果您沒有立即看到當地語系化的名稱,請等候幾分鐘,直到 [開始] 功能表的快取重新整理為止。 若要返回您的原生語言,只要將其設為語言清單中的預設語言即可。

步驟 1.4:將套件資訊清單的更多部分當地語系化 (選擇性)

套件資訊清單的其他區段可以當地語系化。 例如,如果您的應用程式處理檔案副檔名,那麼它應該在清單中具有 windows.fileTypeAssociation 副檔名,完全按照所示使用綠色醒目顯示的文字 (因為它將參照資源),並將黃色醒目顯示的文字替換為特定於您的應用程式的資訊:

<Extensions>
  <uap:Extension Category="windows.fileTypeAssociation">
    <uap:FileTypeAssociation Name="default">
      <uap:DisplayName>ms-resource:Resources/FileTypeDisplayName</uap:DisplayName>
      <uap:Logo>Assets\StoreLogo.png</uap:Logo>
      <uap:InfoTip>ms-resource:Resources/FileTypeInfoTip</uap:InfoTip>
      <uap:SupportedFileTypes>
        <uap:FileType ContentType="application/x-contoso">.contoso</uap:FileType>
      </uap:SupportedFileTypes>
    </uap:FileTypeAssociation>
  </uap:Extension>
</Extensions>

您也可以使用 Visual Studio 資訊清單設計工具,使用 Declarations 索引標籤來新增此資訊,並記下 醒目顯示的值

Screenshot of the Visual Studio Manifest Designer showing the Declarations tab with the Display name and Info tip text boxes called out.

現在,將相應的資源名稱新增到每個 .resw 檔案中,將醒目顯示的文字替換為適合您的應用程式的文字 (請記住對每種支援的語言執行此操作!):

... existing content...
<data name="FileTypeDisplayName">
  <value>Contoso Demo File</value>
</data>
<data name="FileTypeInfoTip">
  <value>Files used by Contoso Demo App</value>
</data>

這會接著顯示在 Windows 殼層的一部分,例如檔案總管:

Screenshot of File Explorer showing a tooltip that says Files used by Contoso Demo App.

建置並測試套件,並執行任何應該顯示新 UI 字串的新案例。

階段 2:使用 MRT 來識別和尋找資源

上一節示範如何使用 MRT 將應用程式的資訊清單檔當地語系化,讓 Windows Shell 可以正確顯示應用程式的名稱和其他中繼資料。 此動作不需要任何程式碼變更;它只需要使用 .resw 檔案和一些額外的工具。 本節將示範如何使用 MRT 來找出現有資源格式的資源,以及以最少變更使用現有的資源處理程式碼。

現有檔案配置&應用程式程式碼的假設

由於有許多方式可將 Win32 傳統型應用程式當地語系化,因此本文會針對現有應用程式結構進行一些簡化的假設,而您必須對應至您的特定環境。 您可能需要對現有的程式碼基底或資源配置進行一些變更,以符合 MRT 的要求,而這些變更基本上不屬於本文件的範圍。

資源檔配置

本文假設您的當地語系化資源都具有相同的檔名 (例如,contoso_demo.exe.muicontoso_strings.dllcontoso.strings.xml),但它們放置在具有 BCP-47 名稱 (en-USde-DE 等) 的不同資料夾中。 您有多少個資源檔案、它們的名稱是什麼、它們的檔案格式/關聯的 API 是什麼等等並不重要。唯一重要的是每個邏輯資源都有相同的檔案名稱 (但放置在不同的 實體目錄中)。

做為一個反例,如果您的應用程式使用平面檔案結構,其中單一 Resources 目錄包含檔案 english_strings.dllfrench_strings.dll,則它不會很好地對應到 MRT。 更好的結構是 Resources 具有子目錄和檔案 en\strings.dll 和的 fr\strings.dll目錄。 您也可以使用相同的基底檔名,但搭配內嵌限定詞,例如 strings.lang-en.dllstrings.lang-fr.dll,但搭配語言代碼使用目錄在概念上更簡單,因此我們會將焦點放在其中。

注意

即使您無法遵循此檔案命名慣例,仍可使用 MRT 和封裝的優點;它只需要更多工作。

例如,應用程式可能會在名為 ui.txt 的簡單文字檔案中包含一組自訂 UI 命令 (用於按鈕標籤等),該檔案位於 UICommands 資料夾下:

+ ProjectRoot
|--+ Strings
|  |--+ en-US
|  |  \--- resources.resw
|  \--+ de-DE
|     \--- resources.resw
|--+ UICommands
|  |--+ en-US
|  |  \--- ui.txt
|  \--+ de-DE
|     \--- ui.txt
|--- AppxManifest.xml
|--- ...rest of project...

資源載入程式碼

本文假設在程式碼中的某個時間點,您想要找出包含當地語系化資源的檔案、載入它,然後使用它。 用來載入資源的 API、用來擷取資源的 API 等等並不重要。 在虛擬程式碼中,基本上有三個步驟:

set userLanguage = GetUsersPreferredLanguage()
set resourceFile = FindResourceFileForLanguage(MY_RESOURCE_NAME, userLanguage)
set resource = LoadResource(resourceFile) 
    
// now use 'resource' however you want

MRT 只需要變更此程式中的前兩個步驟 -- 如何判斷最佳候選資源,以及如何找出它們。 它不需要您變更載入或使用資源的方式 (不過,如果您想要利用資源,它提供執行該作業的設施)。

例如,應用程式可能使用 Win32 API GetUserPreferredUILanguages、CRT 函數sprintf和 Win32 API CreateFile 來取代上面的三個虛擬程式碼函數,然後手動剖析文字檔案以尋找 name=value 配對。 (細節並不重要;這只是為了說明 MRT 不會影響在找到資源後用來處理資源的技術。)

步驟 2.1:使用 MRT 尋找檔案的程式碼變更

將程式碼切換為使用 MRT 來尋找資源並不容易。 它需要使用少數 WinRT 類型和幾列程式碼。 您將使用的主要類型如下:

  • ResourceContext,封裝目前使用中的限定詞值集 (語言、縮放比例等)
  • ResourceManager (WinRT 版本,而非 .NET 版本),可讓您從 PRI 檔案存取所有資源
  • ResourceMap,代表 PRI 檔案中資源的特定子集 (在此範例中,檔案型資源與字串資源)
  • NamedResource,代表邏輯資源及其所有可能的候選項目
  • ResourceCandidate,代表單一具體候選資源

在虛擬程式碼中,您將解析指定資源檔案名稱的方式 (如上述範例所示之 UICommands\ui.txt),如下所示:

// Get the ResourceContext that applies to this app
set resourceContext = ResourceContext.GetForViewIndependentUse()
    
// Get the current ResourceManager (there's one per app)
set resourceManager = ResourceManager.Current
    
// Get the "Files" ResourceMap from the ResourceManager
set fileResources = resourceManager.MainResourceMap.GetSubtree("Files")
    
// Find the NamedResource with the logical filename we're looking for,
// by indexing into the ResourceMap
set desiredResource = fileResources["UICommands\ui.txt"]
    
// Get the ResourceCandidate that best matches our ResourceContext
set bestCandidate = desiredResource.Resolve(resourceContext)
   
// Get the string value (the filename) from the ResourceCandidate
set absoluteFileName = bestCandidate.ValueAsString

請特別注意,程式碼不會要求特定的語言資料夾 (例如 UICommands\en-US\ui.txt),儘管檔案就是這樣存在於磁碟上的。 相反地,它會要求 邏輯 檔名 UICommands\ui.txt,並依賴 MRT 在其中一個語言目錄中尋找適當的磁碟上檔案。

從這裡開始,範例應用程式可以像以前一樣繼續使用 CreateFile 載入 absoluteFileName 並解析 name=value 配對;應用程式中的所有邏輯都不需要變更。 如果您以 C# 或 C++/CX 撰寫,實際程式碼並不比這個複雜得多 (事實上許多中繼變數都可以經過處理) - 請參閱下方的載入 .NET 資源一節。 C++/WRL 型應用程式會因為用來啟動和呼叫 WinRT API 的低階 COM 型 API 而更加複雜,但您採取的基本步驟相同 - 請參閱下方載入 Win32 MUI 資源一節。

載入 .NET 資源

因為 .NET 有一個內建機制用來尋找和載入資源 (稱為「附屬組件」),所以上述綜合範例中沒有明確的程式碼可取代 - 在 .NET 中,您只需要適當目錄中的資源 DLL,它們會自動為您找到。 當使用資源套件將應用程式封裝為 MSIX 或 .appx 時,目錄結構會有所不同 - 資源目錄不是主應用程式目錄的子目錄,而是與主應用程式目錄同級 (或如果使用者沒有在其偏好設定中列出語言,則根本不存在)。

例如,假設 .NET 應用程式具有下列配置,其中所有檔案都存在於 MainApp 資料夾底下:

+ MainApp
|--+ en-us
|  \--- MainApp.resources.dll
|--+ de-de
|  \--- MainApp.resources.dll
|--+ fr-fr
|  \--- MainApp.resources.dll
\--- MainApp.exe

轉換成 .appx 之後,配置看起來會像這樣,假設 en-US 是預設語言,而且使用者的語言清單中同時列出德文和法文:

+ WindowsAppsRoot
|--+ MainApp_neutral
|  |--+ en-us
|  |  \--- MainApp.resources.dll
|  \--- MainApp.exe
|--+ MainApp_neutral_resources.language_de
|  \--+ de-de
|     \--- MainApp.resources.dll
\--+ MainApp_neutral_resources.language_fr
   \--+ fr-fr
      \--- MainApp.resources.dll

由於當地語系化的資源已不存在於主要可執行檔安裝位置下方的子目錄中,因此內建的 .NET 資源解析會失敗。 幸運的是,.NET 有一個妥善定義的機制來處理失敗的組件載入嘗試 - AssemblyResolve 事件。 使用 MRT 的 .NET 應用程式必須註冊此事件,並提供 .NET 資源子系統的遺漏組件。

如何使用 WinRT API 來尋找 .NET 所使用的附屬組件,其簡潔範例如下:所呈現的程式碼會刻意壓縮以顯示最少的實作,不過您可以看到它與上述虛擬程式碼緊密對應,而傳入 ResolveEventArgs 提供我們需要尋找的組件名稱。 此程式碼的可執行版本 (具有詳細註解和錯誤處理) 可以在 GitHub 上的 .NET Assembly Resolver 範例的檔案 PriResourceRsolver.cs 中找到。

static class PriResourceResolver
{
  internal static Assembly ResolveResourceDll(object sender, ResolveEventArgs args)
  {
    var fullAssemblyName = new AssemblyName(args.Name);
    var fileName = string.Format(@"{0}.dll", fullAssemblyName.Name);

    var resourceContext = ResourceContext.GetForViewIndependentUse();
    resourceContext.Languages = new[] { fullAssemblyName.CultureName };

    var resource = ResourceManager.Current.MainResourceMap.GetSubtree("Files")[fileName];

    // Note use of 'UnsafeLoadFrom' - this is required for apps installed with .appx, but
    // in general is discouraged. The full sample provides a safer wrapper of this method
    return Assembly.UnsafeLoadFrom(resource.Resolve(resourceContext).ValueAsString);
  }
}

在上述類別中,您會在應用程式的啟動程式碼中早期新增下列內容 (在任何當地語系化的資源需要載入之前):

void EnableMrtResourceLookup()
{
  AppDomain.CurrentDomain.AssemblyResolve += PriResourceResolver.ResolveResourceDll;
}

.NET 執行階段會在找不到資源 DLL 時引發 AssemblyResolve 事件,此時提供的事件處理常式會透過 MRT 找出所需的檔案並傳回組件。

注意

如果您的應用程式已經有 AssemblyResolve 其他用途的處理常式,您必須整合資源解析程式碼與現有的程式碼。

載入 Win32 MUI 資源

載入 Win32 MUI 資源基本上與載入 .NET 附屬組件相同,但改用 C++/CX 或 C++/WRL 程式碼。 使用 C++/CX 可讓您使用與上述 C# 程式代碼緊密相符的簡單程式碼,但它使用了 C++ 語言擴充、編譯器開關以及您可能想要避免的額外執行階段。 如果是這種情況,使用 C++/WRL 會以更詳細的程式碼為代價,提供影響較低的解決方案。 不過,如果您熟悉 ATL 程式設計 (或一般的 COM),則 WRL 應該感到熟悉。

以下範例函數示範如何使用 C++/WRL 載入特定資源 DLL 並傳回 HINSTANCE,這可用於使用常用 Win32 資源 API 載入更多資源。 請注意,不同於以 .NET 執行階段所要求語言明確初始化 ResourceContext 的 C# 範例,此程式碼取決於使用者的目前語言。

#include <roapi.h>
#include <wrl\client.h>
#include <wrl\wrappers\corewrappers.h>
#include <Windows.ApplicationModel.resources.core.h>
#include <Windows.Foundation.h>
   
#define IF_FAIL_RETURN(hr) if (FAILED((hr))) return hr;
    
HRESULT GetMrtResourceHandle(LPCWSTR resourceFilePath,  HINSTANCE* resourceHandle)
{
  using namespace Microsoft::WRL;
  using namespace Microsoft::WRL::Wrappers;
  using namespace ABI::Windows::ApplicationModel::Resources::Core;
  using namespace ABI::Windows::Foundation;
    
  *resourceHandle = nullptr;
  HRESULT hr{ S_OK };
  RoInitializeWrapper roInit{ RO_INIT_SINGLETHREADED };
  IF_FAIL_RETURN(roInit);
    
  // Get Windows.ApplicationModel.Resources.Core.ResourceManager statics
  ComPtr<IResourceManagerStatics> resourceManagerStatics;
  IF_FAIL_RETURN(GetActivationFactory(
    HStringReference(
    RuntimeClass_Windows_ApplicationModel_Resources_Core_ResourceManager).Get(),
    &resourceManagerStatics));
    
  // Get .Current property
  ComPtr<IResourceManager> resourceManager;
  IF_FAIL_RETURN(resourceManagerStatics->get_Current(&resourceManager));
    
  // get .MainResourceMap property
  ComPtr<IResourceMap> resourceMap;
  IF_FAIL_RETURN(resourceManager->get_MainResourceMap(&resourceMap));
    
  // Call .GetValue with supplied filename
  ComPtr<IResourceCandidate> resourceCandidate;
  IF_FAIL_RETURN(resourceMap->GetValue(HStringReference(resourceFilePath).Get(),
    &resourceCandidate));
    
  // Get .ValueAsString property
  HString resolvedResourceFilePath;
  IF_FAIL_RETURN(resourceCandidate->get_ValueAsString(
    resolvedResourceFilePath.GetAddressOf()));
    
  // Finally, load the DLL and return the hInst.
  *resourceHandle = LoadLibraryEx(resolvedResourceFilePath.GetRawBuffer(nullptr),
    nullptr, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
    
  return S_OK;
}

階段3:建置資源套件

既然您有包含所有資源的「豐富套件」,有兩個路徑可用來建置不同的主要套件和資源套件,以將下載和安裝大小降到最低:

  • 取得現有的豐富套件並透過 套件組合產生器工具執行它,以自動建立資源套件。 如果您有已經產生豐富套件的建置系統,而且您想要在後置處理以產生資源套件,則這是偏好的方法。
  • 直接產生個別的資源套件,並將其建置成套件組合。 如果您有更多對建置系統的控制權,而且可以直接建置套件,則這是偏好的方法。

步驟 3.1:建立套件組合

使用套件組合產生器工具

若要使用套件組合產生器工具,必須手動更新為套件建立的 PRI 設定檔,才能移除 <packaging> 區段。

如果您使用的是 Visual Studio,請參閱確保無論裝置是否需要資源,都安裝在裝置上,以了解如何透過建立檔案 priconfig.packaging.xmlpriconfig.default.xml 將所有語言建置到主套件中。

如果您要手動編輯檔案,請遵循下列步驟:

  1. 使用與之前相同的方式建立設定檔,並取代正確的路徑、檔名和語言:

    makepri createconfig /cf ..\contoso_demo.xml /dq en-US_de-DE_es-MX /pv 10.0 /o
    
  2. 手動開啟已建立的 .xml 檔案並刪除整個 &lt;packaging&rt; 區段 (但保留其他所有專案保持不變):

    <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
    <resources targetOsVersion="10.0.0" majorVersion="1">
      <!-- Packaging section has been deleted... -->
      <index root="\" startIndexAt="\">
        <default>
        ...
        ...
    
  3. 像以前一樣,使用更新的設定檔以及適當的目錄和檔案名稱建置 .pri 檔案和 .appx 套件 (有關這些命令的更多資訊,請參閱上文):

    makepri new /pr . /cf ..\contoso_demo.xml /of ..\resources.pri /mf AppX /o
    makeappx pack /m AppXManifest.xml /f ..\resources.map.txt /p ..\contoso_demo.appx /o
    
  4. 建立套件後,使用以下命令並使用適當的目錄和檔案名稱來建立套件:

    BundleGenerator.exe -Package ..\contoso_demo.appx -Destination ..\bundle -BundleName contoso_demo
    

現在您可以移至最後一個步驟,簽署 (請參閱下方)。

手動建立資源套件

手動建立資源套件需要執行一組略有不同的命令,來建立單獨的 .pri.appx 檔案 - 這些都與上面用於建立豐富套件的命令類似,因此只給予最少的解釋。 注意:所有命令都假設目前目錄是包含 AppXManifest.xml 檔案的目錄,但所有檔案都放入上層目錄 (如有必要,您可以使用不同的目錄,但不應該使用任何這些檔案污染專案目錄)。 一如往常,將「Contoso」檔名取代為您自己的檔名。

  1. 使用以下命令建立一個設定檔,將預設語言命名為預設限定詞 - 在本案例中為 en-US

    makepri createconfig /cf ..\contoso_demo.xml /dq en-US /pv 10.0 /o
    
  2. 使用下列命令,為主要套件建立預設 .pri.map.txt 檔案,並為專案中找到的每個語言,加上一組額外的檔案:

    makepri new /pr . /cf ..\contoso_demo.xml /of ..\resources.pri /mf AppX /o
    
  3. 使用下列命令來建立主要套件 (其中包含可執行的程式碼和預設語言資源)。 像往常一樣,根據您的需求變更名稱,但您應該將套件放在單獨的目錄中,以便以後更輕鬆地建立套件組合 (本範例使用 ..\bundle 目錄):

    makeappx pack /m .\AppXManifest.xml /f ..\resources.map.txt /p ..\bundle\contoso_demo.main.appx /o
    
  4. 建立主要套件之後,請針對每個其他語言使用下列命令一次 (例如,針對上一個步驟中產生的每個語言對應檔案重複此命令)。 同樣地,輸出應該位於不同的目錄中 (與主要套件相同)。 請注意,語言 /f 選項和 /p 選項中指定,並使用新的 /r 引數 (表示需要資源套件):

    makeappx pack /r /m .\AppXManifest.xml /f ..\resources.language-de.map.txt /p ..\bundle\contoso_demo.de.appx /o
    
  5. 將套件組合目錄中的所有套件合併成單一 .appxbundle 檔案。 新 /d 選項會指定要用於套件組合中所有檔案的目錄 (這就是為什麼 .appx 檔案在上一個步驟中放入單獨目錄的原因):

    makeappx bundle /d ..\bundle /p ..\contoso_demo.appxbundle /o
    

建置套件的最後一個步驟是簽署。

步驟 3.2:簽署套件組合

一旦您建立 .appxbundle 檔案 (透過套件組合產生器工具或手動),您就會有一個包含主要套件加上所有資源套件的單一檔案。 最後一個步驟是簽署檔案,讓 Windows 安裝它:

signtool sign /fd SHA256 /a /f ..\contoso_demo_key.pfx ..\contoso_demo.appxbundle

這會產生包含主要套件加上所有語言特定資源套件的已簽署 .appxbundle 檔案。 您可以按兩下套件檔案,以根據使用者的 Windows 語言喜好設定來安裝應用程式以及任何適當的語言。