加个“星标★”,每日 7:15,好文必达! 本文介绍 .NET 中的资源管理知识以及在 Visual Studio 中资源文件的使用。 一、程序中的资源几乎在所用应用中除了包含可执行文件之外,都需要一些包含非可执行的数据文件来完成既定的功能,这些数据可能是字符串、图片、视频、音频、数据等等。 而我们这里所要说的资源文件就是上述文件中那些与程序关系特别紧密,并且这些文件通常随应用程序一同发布与部署。比如游戏中的贴图、音频和窗体程序中的图标、消息文字等等。 二、全球化-本地化(Globalization and Localization).NET 提供的资源管理工具的最重要的目的是使你能够非常方便的让你的应用支持全球化与本地化(Globalization-Localization)。单纯的资源管理可以很简单,你可以将所有的文件包含在一个文件夹中然后直接访问即可。 但是在面对全球化和本地化时我们就需要储存不同文化(Cultrue)下的资源,通常是各种语言的提示,这样我们就需要管理不同的资源文件并在不同的Culture下获取对应的资源。这就带来了一套明显可以复用的编程模式,.NET 就把这种模式整合进了'标准库'中(System.Globalization 和 System.Resources)。 在介绍 .NET 的资源管理前,首先介绍下全球化与本地化这两个字面上'矛盾'的概念。 全球化(Globalization)包含了设计与开发适用于全球的应用,该应用可以为不同的文化提供本地化的接口和区域性的数据。简单的说应用程序的全球化指的就是该应用程序的设计和开发可以支持被全球不同区域的用户使用。要实现这一点首先你程序里任何和特定区域相关的内容都不能是'硬编码'的。下面的文档说明了如何在设计和实现阶段让你的应用能够符合全球化的要求。 Globalization - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/globalization 本地化(Localization)指的是将应用程序的资源转换为支持特定区域的本地化版本的过程。所以只有你的应用程序是支持全球化的,才能执行本地化的过程。 当然并不是所有应用程序都有全球化这样的需求,所以下面我们分两种场景来讨论 .NET 中资源的处理,一种是有全球化和本地化需求的场景,另一种则是没有这样需求的场景。 三、.NET 资源管理中的主要概念.NET 的资源管理功能位于 System.Resource 命名空间下,最终的类型为 ResourceManager。下面所列的是 .NET 资源管理中一些重要的概念和文件类型:
四、无需全球化-本地化的场景首先我们创建一个空白的控制台项目,这里使用了.net core 3.1 版本以及Visual Studio Community 2019 。 控制台项目 然后在项目中添加一个资源文件来保存所有用于显示的文本,命名为AllText。 资源文件添加界面 资源文件创建好之后可以看到 Visual Studio 还帮我们自动创建了一个 AllText.Designer.cs文件,其中.resx文件就是上面所说的 xml 格式的资源文件里面用于保存我们定义的资源,AllText.Designer.cs 则包含了访问 .resx 的代码,使我们可以方便的以强类型的方式访问我们定义的资源。 创建好资源文件后的项目结构 有了资源文件我就可以直接双击打开 .resx 资源文件,Visual Studio 为我们提供了一个图形化的资源文件管理界面可以让我们能方便的管理我们的资源,在这里我们添加了两个文本。 文本资源管理界面 除了文本之外 .resx 可以保存任何类型的资源并且会提供强类型的访问属性。 资源类型选择 在资源管理界面我们还可以选择是否生成访问资源的代码,以及资源访问类型是Internal还是Public。 资源文件创建完毕和定义好资源后我们来看看资源是如何保存的,以及自动生成的代码是如何访问资源的。 下图显示的 .resx 的部分内容,可以看到我们定义的内容是以 xml 格式保存的。 部分 AllText.resx 文件 下图所示的是 AllText.Designer.cs 文件中的部分代码,这里可以看待它使用字符串'ResxDemo.AllText'初始化了 ResourceManager 对象,并用其GetString方法获得了 SayGoodBye对应的文本。 ResourceManager 的实例化 获取 SayGoodBye 字符串 下面就开始使用我们定义的资源,有了强类型代码的帮助我们可以极其方便的使用我们定义的资源,直接使用 AllText 类中的静态方法即可。 ![]() 强类型方式访问资源 下图就是运行效果。 ![]() 运行效果 至此我们演示了在不需要支持多语言场景下在 .net 中使用资源文件的'标准'方式。这样做的优点有:
最后我们来回答一个问题:资源文件被保存到哪里了? 在项目的发布文件夹中并找不到 AllText.resx 文件,这个文件是被嵌入到了程序集中,使用 ildasm 工具查看程序集的信息,如图所示我们在程序集的清单(Manifest)中找到如下信息。 ![]() Visual Studio 在生成时会先将 .resx 资源文件转换为 .resources 格式的资源文件,然后嵌入到程序集中,可以看到 .resx 文件的的默认生成操作就是'嵌入的资源',如果你选择其他的操作那么上面的代码运行就会出错。 ![]() 资源文件的生成操作 三、需要全球化-本地化的场景下面演示如何让之前的项目支持中英文双语,在 VisualStudio 的加持下其实也非常简单,我们只需要如下图所示再创建一个资源文件并将其命名为 AllText.en-US.resources。 ![]() 并在内部添加之前相同的两个值,如下图, ![]() 并将Program.cs中的代码改成如下所示, ![]() 在运行之前我们先来查看生成的结果, ![]() 可以看到 VisualStudio 在bin目录创建了 en-US 子目录,里面包含一个 ResxDemo.resources.dll 卫星程序集(卫星程序集是只包含资源文件的程序集,用于保存特定Culture的资源)。下面看运行效果, ![]() 可以看到我们虽然使用了相同的代码,但是因为线程Culture的不同所以取得的值也是不同的,在 AllText.Designer.cs 内部的 ResourceManager 会根据当前线程的Culture自动读取对应的资源。 在了解内部机制之前,我们先来了解下'zh-CN'、'en-US'等这些代号的具体含义以及CultureInfo的相关内容。 这些代号被称为 Language Tag/语言标签,由 IETF BCP47 https://tools./html/bcp47定义,其中 BCP 的含义是Best Current Practice(当前最佳实践)。而 这份文档 https://docs.microsoft.com/zh-cn/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c 则列出了 Windows 支持的所有 Language Tag。Language Tag包含两部分:以'en-US'为例,横杠的前面部分'en'为语言标记,而'US'则称为区域标记,所以这个标签翻译成白话的意思就是:在美国使用的英语。但像 'zh-Hans' 这个语言标签中的'zh'为宏语言(Macrolanguage)它并不指定一种特定的语言,其后的Hans 也不是区域(rergion)标记而被称为 Script 标记,可理解会在 zh 这个宏语言中的简体中文,在Windows中zh作为前缀的语言标签有很多:
![]() Language Tag 结构与含义 CultureInfo 类型包含了一个区域/语言的各类本地化信息,如日历,数字,时间格式,排序方式等等,如下图所示: ![]() CultureInfo类型信息 .NET 运行时会根据系统环境为每个线程设置初始的 CultureInfo,并根据此信息载入对应的资源,当然你也可以通过设置 Thread.CurrentCulture.CurrentUICulture 属性来修改当前线程的 CultureInfo。 CultureInfo的构造函数包含一个字符串的参数,这个参数就是上文所说的语言标签,所以也可以理解为 CultureInfo 就是语言标签的'实体类'。 CultureInfo有三种类型的 ,分别是:
各种CultureInfo之间还存在继承关系,中性culture是对应特定culture的父级,而固定culture则是中性culture的父级,你可以通过CultureInfo.Parent来获取父级CultureInfo。 .NET 使用集线辐条(hub-and-spoke)模型来定位和获取对应的资源,其中主程序集包含了执行代码和一个默认的资源,而每个'辐条'对应到一个包含特定本地化资源的卫星程序集,因为卫星程序集不是主程序及的一部分,所以你可以很轻松的在不影响主程序集的情况下升级或替换一个卫星程序集。 在 .NET Core 中资源的加载逻辑如下(.NET Framework 因牵涉到 GAC 会更加复杂): 1,运行时尝试寻找并载入对应Culture的资源文件。检查当前执行程序集的文件夹中是否有符合要求的子文件夹,比如上例中的 en-US 文件夹,并尝试载入子文件夹中的卫星程序集。 2,如果上面的步骤找不到卫星程序集,运行时会寻找该Culture的父级Culture。对于en-US就是en,如果所有的父级都找不到对于的卫星程序集,运行时就会使用默认的资源文件,即主程序集中的资源文件。 四、进阶内容限于篇幅,进阶内容仅提供下面的文档供大家参考。 手动创建卫星程序集 Creating Satellite Assemblies for Desktop Apps - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/framework/resources/creating-satellite-assemblies-for-desktop-apps 自定义实现Resource Manager Implementing a Resource Manager - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/framework/data/transactions/implementing-a-resource-manager 五、特定的资源管理对于特别的应用场景 .NET 也提供了应用框架特定的资源管理,当然都是基于之前的内容。限于篇幅,这里只提供文档链接给大家参考。 ASP.NET How to: Create Resource Files for ASP.NET Web Sites -docs.microsoft.com https://docs.microsoft.com/en-us/previous-versions/aspnet/ms247246(v=vs.100) WPF Define XAML resources - WPF - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/desktop-wpf/fundamentals/xaml-resources-define 六、结语本篇的内容虽然没能涉及资源管理的方方面面,但是对于实际开发应该有较强的操作性。 可能对于大多数 .NET 开发者并不会经常碰到多语言支持的场景,但是在你碰到需要资源管理或者类似功能需求的情况下能用好这套工具必定会事半功倍。希望本文能对你理解和使用 .NET 的资源管理能够有所帮助。 fallback process Packaging and Deploying Resources in .NET Apps - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/framework/resources/packaging-and-deploying-resources-in-desktop-apps Satellite assembly loading algorithm - .NET Core - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/core/dependency-loading/loading-resources 总结resouce是如何保存的以及保存的格式 访问的方法 .NET 标准库对文件格式的支持 文本,图片,数据,声音,视频等 Resources in .NET Apps - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/framework/resources/ Create resource files for .NET apps - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/framework/resources/creating-resource-files-for-desktop-apps C# Resources资源详解 https://www.jb51.net/article/101845.htm Globalization - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/globalization Localization - docs.microsoft.com https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/localization 对于资源文件我们完全可以放在应用目录下面的一个文件夹里,然后通过 System.IO 下的 File 类型来访问和自行管理。 无需本地化的程序,这种情况1,自行管理资源文件 2,使用 需要程序(应用)的本地化 介绍全球化与本地化 对于这两种情况你都可以自行编写代码来管理资源文件,不过既然框架为你提供了对应的功能我们应该在减少代码里和增加重用的原则下使用宽假提供的功能,除非框架的功能无法满足你的需求。下面旧介绍两种场景下使用框架中的资源管理功能如何实现。 所以下面分别介绍两种场景下的资源管理的使用。 The .NET Framework uses a hub-and-spoke model to locate and retrieve localized resources. The hub is the main assembly that contains the non-localizable executable code and the resources for a single culture, which is called the neutral or default culture. The default culture is the fallback culture for the application; it is used when no localized resources are available. You use theNeutralResourcesLanguageAttributehttps://docs.microsoft.com/en-us/dotnet/api/system.resources.neutralresourceslanguageattributeattribute to designate the culture of the application's default culture. Each spoke connects to a satellite assembly that contains the resources for a single localized culture but does not contain any code. Because the satellite assemblies are not part of the main assembly, you can easily update or replace resources that correspond to a specific culture without replacing the main assembly for the application. 我们之前项目使用的是中文语言所以我们首先需要创建一个对应英文的资源文件,首先我们创建一个内容如下图所示的 txt 文件。
原文链接: https://zhuanlan.zhihu.com/p/86112804 https://zhuanlan.zhihu.com/p/86112804 (原作者: 6号咸鱼) |
|