Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

18 Jul 2021

UWP资源管理文档阅读笔记【三】

Tailor your resources for language, scale, high contrast, and other qualifiers

描述了资源格量的整体慨念,如何使用它们,每个格量的目的。 ResourceContext.QualifierValues 给出了所有可能的格量。

应用可以根据运行情境加载用料(assets)以及资源(resources)。情境中包含显示语言,是否高反,显示缩放系数,以及其他格量。需要把资源目录或者文件匹配到格量名字。

关于本地化的相关喜喜,参考Globalization and localization

Qualifier name, qualifier value, and qualifier

格量名映射到一堆格量值,下面时个例子:

  • 情境:the high contrast setting
  • 格量名: contrast
  • 格量值: standard, high, black, white

格量本身是通过组合格量名和值来命名的,形如<qualifier name>-<qualifier value>。而且格量是大小写不敏感的,例子:contrast-standardContrast-Standard

Use qualifiers in folder names

如果一个格量对应多个用料文件,可以使用格量来命名包含用料的目录:

\Assets\Images\contrast-standard\<logo.png, and other image files>
\Assets\Images\contrast-high\<logo.png, and other image files>
\Assets\Images\contrast-black\<logo.png, and other image files>
\Assets\Images\contrast-white\<logo.png, and other image files>

Use qualifiers in file names

也可以直接用格量来命名文件:

\Assets\Images\logo.contrast-standard.png
\Assets\Images\logo.contrast-high.png
\Assets\Images\logo.contrast-black.png
\Assets\Images\logo.contrast-white.png

Reference a string or image resource by name

参考:

Actual and neutral qualifier matches

没有必要为每个格量提供资源文件,例如:

\Assets\Images\logo.contrast-high.png
\Assets\Images\logo.png

高对比度下,第一个文件是真实匹配。真实匹配只有在格量包含格量值的时候才发生。第二个文件没有指定额外格量,所以是中立的,作为候补匹配。在非高对比的其他任意情形,第二个文件就会被选中。如果将第二个文件改成logo.contrast-standard.png,那么正常对比度下,就会有真实匹配。

如果有多个文件,可以使用目录形式组织:

\Assets\Images\contrast-high\<logo.png, and other images to load when high contrast theme is not None>
\Assets\Images\<logo.png, and other images to load when high contrast theme is None>

Multiple qualifiers

不管是用于文件还是目录,都可以将格量捆绑:

\Assets\Images\contrast-high\scale-400\<logo.png, and other image files>

另一种方式,应用于目录:

\Assets\Images\contrast-high_scale-400\<logo.png, and other image files>

另一种方式,应用于文件:

\Assets\Images\logo.contrast-high_scale-400.png

AlternateForm

主要应用场景是在使用msft-phonetic的时候用于提供furigana 。参考“Support Furigana for Japanese strings that can be sorted”。

Configuration

可以用于指定特定情境下才使用的配置,比如用于测试的资源。

Configuration可用于加载与环境变量MS_CONFIGURATION_ATTRIBUTE_VALUE最匹配的值。此环境变量示例:designer, test。

Contrast

对比度相关。

Custom

用于自定义情境,可以SetGlobalQualifierValue来加载,一个使用场景是根据授权来加载相应的资源。

DeviceFamily

设备类型相关。

DXFeatureLevel

Direct3D相关。

HomeRegion

跟用户的国家以及地域设置有关。

相关识别码来自https://unstats.un.org/unsd/methods/m49/m49regin.htm

Language

语言相关

LayoutDirection

布局相关,从左到右还是从右到左。

Scale

图像的缩放。

TargetSize

图像相关。主要用于在File Explorer中显示文件类型图标。

Theme

主题相关。

Shell light theme and unplated resources

Windows 10 May 2019 Update为Windows Shell引入了一个新的亮色主题。可能需要检验应用在此亮色主题下是否有足够高的对比度。

Providing light theme specific assets

通过altform-lightunplate可以为新亮色主题提供资源。这个跟已有的altform-unplated格量是一致的。

Downlevel considerations

不要将theme-light和altform-unplated混在一起使用。

Compatibility behavior

出于向后兼容性的目的,Windows内建了侦测单色调图标是否与背景有足够对比度的逻辑。如果对比度不够,Windows回去寻找一个contrast-white版本。如果寻找的目标不存在,Windows会回退到plated版本。

Localize strings in your UI and app package manifest

Globalization and localization 也有很多内容值得参考。

字符串字面值,如果在XAML标签或者app package manifest(Package.appxmanifest)中使用的话,可以提取Resource File(.resw)中。这样可以为不同语言的版本提供支持。

字符串资源和图像资源的差异是,一个文件对应一个图像资源,但是一个文件却可以包含多个字符串资源。通常把字符串资源文件放置在\Strings目录下。

Store strings in a resources file

  1. 设置应用的默认语言,假设为en-US
  2. 创建默认语言的资源文件
  3. 在资源文件中创建字符串

资源标识符不区分大小写,必须唯一。

Refer to a string resource identifier from XAML

通过x:Uid 指令可以将一个控件或者元素关联到一个字符串资源标识符。当资源文件在运行时被加载的时候,x:Uid会触发相应的查找操作。如果资源文件中有:

  • Greeting.Width
  • Greeting.Text

对于一个TextBlock,如果其x:Uid是Greeting,那么就会从资源文件中找到上面两项,并应用到TextBlock之上。如果只有Greeting且没有属性的话,是没办法应用到对应的对象上的。

如果将一个Button的x:Uid设置成Greeting,由于Button没有Text辖属,所以在应用Greeting.Text的时候就会报错。

对于挂靠的辖属,需要用特定的语法。比如在Greeting上设置AutomationProperties.Name ,需要使用以下语法:

Greeting.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name

Refer to a string resource identifier from code

auto resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView() };
myXAMLTextBlockElement().Text(resourceLoader.GetString(L"Farewell"));

必须在UI线程调用GetForCurrentView ,否则会出现异常"<typename> may not be created on threads that do not have a CoreWindow."。可以用if (Windows.UI.Core.CoreWindow.GetForCurrentThread() != null)判断是否在UI线程。

此操作也可以应用在Class Library或者Windows Runtime Library项目上。运行的时候,库的加载者所拥有的资源能被库代码查询到。建议库从其宿主加载资源,因为宿主作为应用会有更好的本地化支持。如果库需要提供资源的话,那么应该提供一个选项,让宿主可以替换其提供的资源。

如果资源名字是分段的(也就是包含.号),那么需要将.号替换成/

this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Fare/Well"); // <data name="Fare.Well" ...> ...
<ResourceMapSubtree name="Fare"><NamedResource name="Well" uri="ms-resource://<GUID>/Resources/Fare/Well">...

Refer to a string resource identifier from your app package manifest

在Package.appxmanifest中引用资源,需要采用诸如ms-resource:AppDisplayName此形式,其中AppDisplayName是资源名。

Localizable manifest items 列出了所有可以本地化的条目。

Localize the string resources

Test your app

可以通过修改Windows设置Settings > Time & Language > Region & language > Languages来告诉应用所需要加载的资源。

Factoring strings into multiple Resources Files

可以把所有字符串资源放置在一个resw文件中,或者也可以放在多个resw文件中。比如,你可能想把错误信息放在一个resw,app包自述字符串放在一个resw,UI字符串放在另一个resw。

如果要限定从一个resw中获取字符串资源,那么可以在x:Uid名字之前添加/<resources-file-name>/,例如:

<TextBlock x:Uid="/ErrorMessages/PasswordTooWeak"/>

如果采用的是默认的Resources.resw,则不需要此限定。

下面的例子假设ErrorMessages.resw中有一个名为MismatchedPasswords的字符串资源。

auto resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(L"ErrorMessages") };
myXAMLTextBlockElement().Text(resourceLoader.GetString(L"MismatchedPasswords"));

同理,如果将自述文件中的AppDisplayName从Resources.resw移到ManifestResources.resw,那么它的引用方式就变成了:ms-resource:/ManifestResources/AppDisplayName

var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("Err.Msgs");

如果资源文件名字中包含点号,那么引用文件名的时候需要保留点号,不要将其替换成/

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">...

Load a string for a specific language or other context

从ResourceContext.GetForCurrentView获取的ResourceContext带有每种格量的格量名(根据当前机器以及用户的配置得出的)。如果你的应用想覆盖默认的设置,比如允许用户设置一个特定的语言,你需要创建一个新的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;

剩余略。

Updating strings in response to qualifier value change events

通过ResourceContext.QualifierValues的MapChanged可以侦测系统设置的改变。

剩余略。

Load strings from a Class Library or a Windows Runtime Library

略。

Loading strings from other packages

可以通过ResourceMap来让当前包访问其他app包中的顶层资源,通过顶层资源就可以使用ResourceMap.GetSubtree来访问下级资源树。

也可以使用https://docs.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes以绝对路径的方式来访问资源。

Loading strings in non-packaged applications

从Windows Version 1903 (May 2019 Update)开始,非打包应用也可以使用RMS。

剩余略。

(未完待续)