多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

.NET WASM 全球化 ICU 数据加载实战指南:icudt 分片文件、自定义构建与 Blazor 配置

.NET WASM 全球化 ICU 数据加载实战指南:icudt 分片文件、自定义构建与 Blazor 配置 .NET WASM 全球化 ICU 数据加载实战指南icudt 分片文件、自定义构建与 Blazor 配置【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime导读在 WebAssemblyWASM应用场景下.NET 运行时无法像桌面端那样依赖操作系统自带的 ICUInternational Components for Unicode数据而是需要把区域设置culture数据文件随应用一起打包加载。本文以 dotnet/runtime 仓库的 WASM Globalization Icu 设计文档 为主线系统讲解icudt*.dat四种数据分片的区别、WasmIcuDataFileName/BlazorIcuDataFileName/BlazorWebAssemblyLoadAllGlobalizationData等 MSBuild 属性的用法、如何利用dotnet/icu仓库裁剪出自定义 ICU 数据文件并结合仓库中的构建目标文件与自动化测试源码验证行为细节。读完本文你将能够在 WASM Console、WASM Browser 与 Blazor WebAssembly 项目中精准控制全球化数据的体积与加载策略。背景WASM 场景下 ICU 数据的特殊性在传统桌面与服务器场景中.NET 的全球化功能依赖操作系统提供的 ICU 数据Linux 上通常以libicu包提供约 28 MBWindows 与 macOS 作为系统组件内置。但在 WASM 应用中运行时运行在浏览器等沙箱环境里既没有操作系统级的 ICU 数据可用也不可能打包体积过大的完整数据因此必须采用随应用携带数据文件的策略。与之配套的还有 Globalization Invariant Mode全球化不变模式当InvariantGlobalization开启时应用完全不加载任何 ICU 数据改用简化行为如仅支持 ASCII 大小写转换、字符串比较退化为 ordinal 序数比较、日期时间使用固定格式等适用于对全球化正确性要求不高、但极度在意包体大小的场景。当不变模式关闭默认时WASM 应用就需要加载 ICU 数据文件这正是本文讨论的icudt*.dat文件体系。四种 ICU 数据分片按需裁剪区域数据仓库文档明确了四类基础数据文件它们是对完整 ICU 数据的区域子集裁剪sharding文件包含的区域locale适用场景icudt.dat全部数据需要完整全球化能力的应用icudt_EFIGS.daten-*、fr-FR、es-ES、it-IT、de-DE主要面向英语及法、意、德、西等欧洲语言应用icudt_CJK.daten、ja、ko、zh面向中日韩CJK用户icudt_no_CJK.daticudt.dat的全部区域但排除ja、ko、zh需要全量区域但不含 CJK 的应用这种分片设计让开发者可以在数据完整性与包体大小之间做权衡例如一个只面向英语用户的工具类应用加载icudt_EFIGS.dat即可而面向中文、日文、韩文用户的应用则应选择icudt_CJK.dat。在仓库的自动化测试中各分片包含的区域范围被直接验证。见 IcuTestsBase.cs例如 EFIGS 分片测试覆盖en-US、fr-FR、es-ES并断言pl-PL、ko-KR、cs-CZ等不在分片内的区域会回退到en-US的星期日名称SundayCJK 分片测试覆盖zh-CN、ja-JP并断言fr-FR、hr-HR、it-IT回退no_CJK分片则验证en-AU、fr-FR、sk-SK正常、而ja-JP、ko-KR、zh-CN回退。这从测试层面印证了上述分片表的行为。Wasm Console 与 Wasm Browser用 WasmIcuDataFileName 指定数据文件对于 WASM 控制台应用与 WASM 浏览器应用可以通过在.csproj中添加 MSBuild 属性来指定要加载的数据文件WasmIcuDataFileNameicudt_no_CJK.dat/WasmIcuDataFileName使用要点如下只能设置一个值WasmIcuDataFileName不接受多个文件每次构建只加载一个数据文件。支持自定义文件该属性可以指向开发者自行构建的 ICU 数据文件构建方法见下文自定义 ICU一节文件名不限于四种内置分片。未指定时的自动匹配如果不设置WasmIcuDataFileName构建系统会根据应用当前 culture 自动选择对应分片并加载。例如应用文化为en-US时加载icudt_EFIGS.dat为zh-CN时加载icudt_CJK.dat。相关 MSBuild 属性的完整语义在仓库的 WASM 构建目标文件 WasmApp.Common.targets 中对 ICU 相关属性有明确注释两个属性配合使用WasmIncludeFullIcuData—— 加载完整 ICU 数据icudt.dat默认值为false仅当InvariantGlobalizationfalse时生效。WasmIcuDataFileName—— 指定要加载的 ICU 全球化数据文件名/路径仅当InvariantGlobalizationfalse且WasmIncludeFullIcuDatafalse时生效。因此三者的优先级关系为InvariantGlobalizationtrue不加载任何数据WasmIncludeFullIcuDatatrue加载全量icudt.datWasmIcuDataFileName加载指定分片或自定义文件 均未设置按应用 culture 自动匹配分片。这一点也与测试 IcuTests.cs 中的验证逻辑一致测试通过组合InvariantGlobalization与BlazorWebAssemblyLoadAllGlobalizationData两个开关断言不变模式下所有区域缺失、非不变模式下按分片或全量数据返回正确的本地化星期日名称。自定义 ICU 数据文件从 dotnet/icu 仓库裁剪当四种内置分片都无法满足需求时开发者可以自行构建自定义 ICU 数据文件。构建入口与过滤器最便捷的方式是在 Codespaces 中打开dotnet/icu仓库可参考仓库的 Codespaces 使用说明。该仓库的icu-filters目录下存放着过滤器文件构建系统会据此对 ICU 数据进行 locale slicing区域切片。构建前建议先阅读 ICU 官方用户手册中关于Locale Slicing构建工具的章节了解过滤器语法。eng/icu.mk文件用于选择要构建哪些过滤器。裁剪过滤器的重要建议来自仓库设计文档务必遵守只通过增删localeFilter/includelist中的区域来编辑过滤器避免误删其他关键数据如脚本、日历、数字格式等非区域维度的数据。强烈建议不要把en-US从localeFilter/includelist中移除因为它被用作回退fallback区域。移除它会引发两类后果当设置了PredefinedCulturesOnlytrue/PredefinedCulturesOnly时抛出Encountered infinite recursion while looking for resource in System.Private.Corelib.异常无限递归查找资源当未启用 PredefinedCulturesOnly 时运行时将从 ICU 的root.txt文件解析数据导致本地化结果退化例如CultureInfo.DateTimeFormat.GetDayName(DateTime.Today.DayOfWeek)只返回缩写形式Mon而不是完整的Monday。若删除了特定功能数据运行时可能抛出以[CultureData.IcuGetLocaleInfo(LocaleStringData)] Failed开头的异常含义是你删除了提取区域基本信息所必需的数据。为浏览器构建Browser / wasm前置条件运行.devcontainer/postCreateCommand.sh若使用 Codespaces容器创建时会自动执行。构建命令./build.sh /p:TargetOSBrowser /p:TargetArchitecturewasm输出位置artifacts/bin/icu-browser-wasm。为移动端构建Mobiles / Android前置条件先准备 Android NDK文档给出的步骤如下export ANDROID_NDK_ROOT$PWD/artifacts/ndk/ mkdir $ANDROID_NDK_ROOT wget https://dl.google.com/android/repository/android-ndk-r25b-linux.zip unzip android-ndk-r25b-linux.zip -d $ANDROID_NDK_ROOT rm android-ndk-r25b-linux.zip mv $ANDROID_NDK_ROOT/*/* $ANDROID_NDK_ROOT rmdir $ANDROID_NDK_ROOT/android-ndk-r25b构建命令./build.sh /p:TargetOSAndroid /p:TargetArchitecturex64输出位置两类构建Browser 与 Android的输出均位于artifacts/bin的子目录中。在项目中引用自定义数据文件把生成的.dat文件复制到项目目录然后在.csproj中给出路径支持相对路径与绝对路径两种写法!-- 相对路径 -- WasmIcuDataFileNameicudt_custom.dat/WasmIcuDataFileName !-- 绝对路径$(MSBuildThisFileDirectory) 指向 .csproj 所在目录 -- WasmIcuDataFileName$(MSBuildThisFileDirectory)icudt_custom.dat/WasmIcuDataFileName仓库测试对自定义文件的预期行为做了详细建模。见 IcuTestsBase.cs测试用自定义文件icudt_custom.dat只包含cy-GB、is-IS、bs-BA、lb-LU四个区域以及回退区域en-US随后断言这四者的星期日名称Dydd Sul、sunnudagur、nedjelja、Sonndeg能被正确解析而fr-FR、hr-HR、ko-KR等未包含区域回退到en-US的Sunday。Blazor WebAssembly按文化加载与全量数据开关Blazor WebAssembly 场景下数据文件的加载策略有所不同。默认行为按应用文化加载Blazor 应用默认基于应用当前的 culture 加载对应分片文件与 WASM Console/Browser 的自动匹配逻辑一致。强制加载全量数据如果希望无论应用文化如何都加载完整 ICU 数据在.csproj中添加BlazorWebAssemblyLoadAllGlobalizationDatatrue/BlazorWebAssemblyLoadAllGlobalizationData加载自定义文件Blazor 加载自定义文件时使用BlazorIcuDataFileName属性取值同样可以是相对路径或完整路径写法与WasmIcuDataFileName一致。但有一个硬性限制Blazor 只支持加载文件名以icudt开头的文件例如icudt_custom.dat。这一限制在测试中有明确断言见 IcuTests.cs 的NonExistingCustomFileAssertError测试它覆盖了两种错误场景文件不存在如icudtNonExisting.dat构建报错Could not find $(BlazorIcuDataFileName)..., or when used as a path relative to the runtime pack提示查找失败该文件既不在项目目录也不在 runtime pack 的相对路径下。文件名不合规如incorrectName.dat构建报错File name in $(BlazorIcuDataFileName) has to start with icudt.即文件名必须以icudt开头。另外测试 FullIcuFromRuntimePackWithCustomIcu 还验证了一个属性优先级细节当BlazorWebAssemblyLoadAllGlobalizationData设为true时BlazorIcuDataFileName不再生效构建输出会给出警告$(BlazorIcuDataFileName) has no effect when $(BlazorWebAssemblyLoadAllGlobalizationData) is set to true.。这提醒我们全量数据开关优先于自定义文件属性二者不应同时使用。行为验证用仓库测试理解运行时语义仓库在 src/mono/wasm/Wasm.Build.Tests 目录下提供了完整的 ICU 相关集成测试可作为理解运行时行为的权威参考IcuTests.cs覆盖不变模式 vs 全量数据、自定义文件 vs 全量数据优先级、非法文件名与文件不存在等错误路径。IcuTestsBase.cs定义了各类分片EFIGS / CJK / no_CJK / full / custom的测试区域清单以及测试程序模板 —— 通过new CultureInfo(code)构造文化与culture.DateTimeFormat.GetDayName(...)读取本地化星期日名称并对比期望值。IcuShardingTests2.cs针对分片shard加载行为的分片级测试。GlobalizationMode.cs定义Invariant、FullIcu、Sharded、Custom等全局化模式枚举供测试驱动不同加载策略。测试程序的核心断言逻辑见 IcuTestsBase.cs值得留意当目标区域不在已加载数据分片中时若PredefinedCulturesOnly未开启new CultureInfo(code)不会抛异常但所有数据如星期日名称都会回退到en-US的值而一旦开启PredefinedCulturesOnlytrue构造不在数据中的区域会直接抛出CultureNotFoundException。这正好呼应了设计文档中关于移除en-US回退区域后出现异常与数据退化的警告。配置速查与注意事项将上文要点汇总为一份速查表场景MSBuild 属性说明WASM Console/Browser 指定文件WasmIcuDataFileName只能设一个值支持自定义.datInvariantGlobalizationfalse且WasmIncludeFullIcuDatafalse时生效WASM Console/Browser 加载全量数据WasmIncludeFullIcuData默认false置true时加载icudt.datWASM 应用开启不变模式InvariantGlobalization置true时不加载任何 ICU 数据相关属性全部失效Blazor 强制全量数据BlazorWebAssemblyLoadAllGlobalizationData置true时忽略BlazorIcuDataFileNameBlazor 加载自定义文件BlazorIcuDataFileName文件名必须以icudt开头支持相对/绝对路径除项目文件外不变模式还可以通过 runtimeconfig.json 的System.Globalization.Invariant配置或DOTNET_SYSTEM_GLOBALIZATION_INVARIANT环境变量开启且项目文件/runtimeconfig 中的设置优先级高于环境变量PredefinedCulturesOnly则控制缺失区域是抛异常还是回退到不变文化/root.txt数据。深入理解这两者与 ICU 数据加载的交互可阅读 Globalization Invariant Mode 设计文档。最后提醒自定义 ICU 数据裁剪时务必保留en-US回退区域、只增删includelist中的区域否则轻则出现Mon这类缩写退化、重则抛出Encountered infinite recursion ...或[CultureData.IcuGetLocaleInfo(LocaleStringData)] Failed异常。在调整分片与属性组合后建议参照仓库 IcuTestsBase.cs 的断言模式用CultureInfoDateTimeFormat.GetDayName对目标区域逐一验证本地化输出确保裁剪结果符合预期。【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表