Humanizer 日期序数词本地化:IDateToOrdinalWordConverter 接口与 ToOrdinalWords 转换机制详解

发布时间:2026/10/7 15:06:35

Humanizer 日期序数词本地化:IDateToOrdinalWordConverter 接口与 ToOrdinalWords 转换机制详解 开发工具【免费下载链接】HumanizerHumanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities项目地址https://gitcode.com/gh_mirrors/hu/Humanizer点击查看免费下载本文以 Humanizer 3.0.10 版本文档中的IDateToOrdinalWordConverter接口为核心系统讲解该接口如何驱动DateTime.ToOrdinalWords()扩展方法实现日期序数词的本地化输出如英语的 1st of January, 2023并深入源码剖析默认实现、模式化Pattern转换器、文化注册解析机制、源码生成器以及对应的多文化测试体系。读完本文你将掌握该接口的完整调用链、各文化下日期序数词的渲染规则以及如何在自有项目中注册自定义日期序数词转换器。接口定义与核心职责IDateToOrdinalWordConverter是 Humanizer 中用于本地化ToOrdinalWords方法的接口定义于 src/Humanizer/Localisation/DateToOrdinalWords/IDateToOrdinalWordConverter.cs位于Humanizer命名空间下属于公开 API可通过 ApiApprover 验证文件 确认其公开签名。接口声明如下public interface IDateToOrdinalWordConverter { /// summary /// 将给定的日期转换为当前文化下的序数词表示。 /// /summary /// param namedate要格式化的日期。/param /// returns本地化的序数日期字符串。/returns string Convert(DateTime date); /// summary /// 使用指定的语法格grammatical case将日期转换为序数词表示。 /// /summary /// param namedate要格式化的日期。/param /// param namegrammaticalCase当区域设置支持按格变化日期形式时要应用的语法格。/param /// returns本地化的序数日期字符串。/returns string Convert(DateTime date, GrammaticalCase grammaticalCase); }Convert(DateTime) 方法签名string Convert(System.DateTime date)作用将System.DateTime转换为当前文化current culture下的序数词日期文本例如英语环境下2023-01-01输出1st of January, 2023。参数date—— 待格式化的System.DateTime值。返回System.String即本地化的序数日期字符串。Convert(DateTime, GrammaticalCase) 方法签名string Convert(System.DateTime date, Humanizer.GrammaticalCase grammaticalCase)作用在转换时额外指定 GrammaticalCase语法格如 Nominative 主格、Genitive 属格等让输出文本按指定的格形态呈现。关键点语法格参数主要服务于拥有格系统的语言如俄语、波兰语等对英语这类没有格的区分或日期表达不随格变化的语言该参数不会产生效果。具体说明见 DateToOrdinalWordsExtensions.cs 中扩展方法的 XML 注释。调用链接口如何驱动 ToOrdinalWords 扩展方法接口本身不直接暴露给使用者真正对外提供能力的是DateToOrdinalWordsExtensions静态类src/Humanizer/DateToOrdinalWordsExtensions.cs它定义了ToOrdinalWords扩展方法内部通过Configurator解析当前文化对应的转换器并调用接口方法public static string ToOrdinalWords(this DateTime input) Configurator.DateToOrdinalWordsConverter.Convert(input); public static string ToOrdinalWords(this DateTime input, GrammaticalCase grammaticalCase) Configurator.DateToOrdinalWordsConverter.Convert(input, grammaticalCase);由此形成一条清晰的调用链DateTime.ToOrdinalWords() └─ DateToOrdinalWordsExtensions.ToOrdinalWords() └─ Configurator.DateToOrdinalWordsConverter按当前文化解析 └─ IDateToOrdinalWordConverter.Convert(date[, grammaticalCase]) └─ 具体实现默认转换器 / 模式化转换器其中Configuratorsrc/Humanizer/Configuration/Configurator.cs暴露了公开注册属性DateToOrdinalWordsConverters类型为LocaliserRegistryIDateToOrdinalWordConverter并提供了内部解析入口DateToOrdinalWordsConverterpublic static LocaliserRegistryIDateToOrdinalWordConverter DateToOrdinalWordsConverters { get; } new DateToOrdinalWordsConverterRegistry(); internal static IDateToOrdinalWordConverter DateToOrdinalWordsConverter DateToOrdinalWordsConverters.ResolveForCulture(null); // null 表示使用当前线程文化从源码结构可以推断所有文化下的日期序数词转换都收敛到这一个注册表使用者无需感知具体实现类只需通过扩展方法与当前文化环境交互。默认实现DefaultDateToOrdinalWordConverter 的双分支逻辑当某个文化未注册专属转换器时将回退到默认实现 DefaultDateToOrdinalWordConverter.cs。该类的逻辑按是否为英语分为两个分支public virtual string Convert(DateTime date) { var culture CultureInfo.CurrentCulture; if (culture.TwoLetterISOLanguageName ! en) { // 非英语文化直接使用该文化自身的短日期模式 d // 并清理某些历法在格式化输出中嵌入的方向性控制字符。 return SanitizeNonEnglishDate(date.ToString(d, culture)); } return date.Day.Ordinalize() date.ToString( MMMM yyyy); }英语分支将日期中的“日”通过date.Day.Ordinalize()转换为序数词依赖 Humanizer 的 Ordinalize 能力得到 1st、22nd 等再拼接 MMMM yyyy格式的月与年最终得到如1st of January, 2023、22nd of December, 2020的经典英语日期序数词表达示例见扩展方法 XML 注释。非英语分支直接采用date.ToString(d, culture)即当前文化的短日期模式让各文化保留自己习惯的日期排序与分隔符随后通过SanitizeNonEnglishDate移除三类方向性控制字符保证文本被嵌入到更大序数短语中时仍清晰可读const char LeftToRightMark (char)0x200E; // LRM const char RightToLeftMark (char)0x200F; // RLM const char ArabicLetterMark (char)0x061C; // ALM static string SanitizeNonEnglishDate(string value) value.Replace(LeftToRightMark.ToString(), string.Empty) .Replace(RightToLeftMark.ToString(), string.Empty) .Replace(ArabicLetterMark.ToString(), string.Empty);语法格参数的默认处理默认实现的Convert(DateTime, GrammaticalCase)直接转发到单参数版本忽略grammaticalCasepublic virtual string Convert(DateTime date, GrammaticalCase grammaticalCase) Convert(date);这意味着对于未注册专属模式的文化无论传入何种语法格输出结果保持不变——这与 LocaleRegistrySweepTests.cs 中默认输出与 Genitive 输出一致的断言行为相印证见下文测试部分。模式化实现PatternDateToOrdinalWordsConverter 与 OrdinalDatePattern对于需要精细控制日期形态的文化如斯拉夫语族的属格月份、Hijri 历法月份名、特定日渲染方式等Humanizer 提供了模式化转换器 PatternDateToOrdinalWordsConverter.csclass PatternDateToOrdinalWordsConverter(OrdinalDatePattern pattern) : DefaultDateToOrdinalWordConverter { public override string Convert(DateTime date) pattern.Format(date); }它继承默认转换器并覆写Convert将实际工作委托给 OrdinalDatePattern.cs。OrdinalDatePattern的构造参数揭示了各文化的可配置维度sealed class OrdinalDatePattern( string template, // 日期模板可包含 {day} 占位符 OrdinalDateDayMode dayMode, // 日的渲染模式 OrdinalDateCalendarMode calendarMode OrdinalDateCalendarMode.Gregorian, string[]? months null, // 月份名数组主格/普通形式 string[]? monthsGenitive null, // 月份名属格数组斯拉夫语族 string[]? hijriMonths null) // Hijri/UmAlQura 历法月份名数组日渲染模式OrdinalDateDayModeOrdinalDatePattern.cs定义了五种日渲染方式覆盖不同文化对日的表达习惯模式行为Numeric将日渲染为文化感知的数值如 25Ordinal将日渲染为序数词如 1st、25thOrdinalWhenDayIsOne仅当月首1 日渲染为序数词其余为数值MasculineOrdinalWhenDayIsOne与上一项类似但月首使用阳性序数形式针对有词性的语言DotSuffix数值后附加点号后缀如德语式 25.日历模式OrdinalDateCalendarMode定义于 OrdinalDateCalendarMode.csGregorian默认无论文化的默认历法是什么强制使用本地化的公历new GregorianCalendar(GregorianCalendarTypes.Localized)使年份按公历呈现Native保留文化的默认历法如泰国佛历、希伯来历、波斯历让DateTime.ToString()输出本地历法的年份。属格月份与占位符替换机制OrdinalDatePattern.Format的核心流程是将模板中的{day}占位符替换为带标记的日格式串交由文化格式化后再把渲染好的日回填。其中两个值得关注的实现细节属格检测SubstituteMonth会定位模板中第一个未被引号转义的MMMM并依据IsDayAdjacentToMonth判断日说明符d/dd而非表示星期几的ddd/dddd是否紧邻月份说明符。当二者相邻如dDAY MMMM或MMMM d这类模式时会选择monthsGenitive属格形式——这正是斯拉夫语族日期中日 属格月份语法要求的底层支撑。方向性控制字符清理最终输出仍会通过StripDirectionalityControls移除\u200ELRM、\u200FRLM与\u061CALM保证混合方向文本的干净可读。注册与解析机制LocaliserRegistry 的文化回退DateToOrdinalWordsConverterRegistrysrc/Humanizer/Configuration/DateToOrdinalWordsConverterRegistry.cs继承自泛型基类LocaliserRegistryIDateToOrdinalWordConverter默认转换器为DefaultDateToOrdinalWordConverter随后通过源码生成器生成的注册代码把各文化的模式化转换器注入其中。解析过程由 LocaliserRegistry.cs 的ResolveForCulture/FindLocaliser完成其机制包括文化回退链从当前文化沿Parent逐级向上查找如zh-Hans-CN→zh-Hans→zh命中即返回最终未命中则回退到默认转换器冻结字典首次使用后将注册表冻结为FrozenDictionary后续读取走无锁快速路径兼顾并发安全与读取性能CultureInfo 级缓存使用ConditionalWeakTableCultureInfo, StrongBoxTLocaliser缓存每个文化已解析的转换器实例避免重复构造注册时机约束Register在注册表已被使用冻结后再调用会抛出InvalidOperationException因此自定义注册必须放在首次调用ToOrdinalWords之前如应用启动阶段或ModuleInitializer。此外LocaliserRegistry还支持UseGeneratedCultureResolver()在开启源码生成的解析器后可借助GeneratedCultureResolver.TryResolve将未精确命中的文化名映射到其区域资料属主LocaleProfileOwner文化从而覆盖更多区域变体如en-IN、sr-Latn等。源码生成器各文化日期模式从 YAML 到注册代码IDateToOrdinalWordConverter之所以能覆盖众多语言得益于 Humanizer.SourceGenerators 项目中的源码生成器。在 OrdinalDateProfileCatalogInput.cs 中可以看到生成目标EmitDateCatalog( context, dateProfiles, DateToOrdinalWordsProfileCatalog, IDateToOrdinalWordConverter, PatternDateToOrdinalWordsConverter, OrdinalDateProfileCatalog.DateTo.g.cs);即从各文化 YAML 资料位于 src/Humanizer/Locales 下的区域配置文件中读取日期序数词模板、月份数组、属格月份、Hijri 月份等信息编译期生成DateToOrdinalWordsProfileCatalog静态类为每种文化注册对应的PatternDateToOrdinalWordsConverter实例。生成代码仅在NET6_0_OR_GREATER条件下额外产出DateOnly变体对应IDateOnlyToOrdinalWordConverter见 IDateOnlyToOrdinalWordConverter.cs。测试验证跨文化的日期序数词期望矩阵仓库通过 LocaleRegistrySweepTests.cs 对接口行为做了跨文化理论测试Theory覆盖了多个具有代表性的日期[Theory] [MemberData(nameof(LocaleCoverageData.DateToOrdinalWords2022January25ExpectationTheoryData), MemberType typeof(LocaleCoverageData))] public void DateToOrdinalWords_2022January25_UsesExpectedForms(string localeName, DateExpectationRow expected) { using var _ new CultureSwap(new(localeName)); var date new DateTime(expected.Year, expected.Month, expected.Day); Assert.Equal(expected.Expected, date.ToOrdinalWords()); Assert.Equal(expected.Expected, date.ToOrdinalWords(GrammaticalCase.Genitive)); }测试要点多日期采样覆盖月首2015-01-01、闰日2020-02-29、月末2024-12-31以及普通日期2022-01-25、2020-03-02、2021-10-31 等用于验证不同日值在OrdinalWhenDayIsOne、MasculineOrdinalWhenDayIsOne等模式下的渲染差异双调用断言同时断言无参调用与GrammaticalCase.Genitive调用输出一致验证了模式化转换器以及默认转换器对语法格参数的稳定处理期望数据源期望值矩阵集中在 LocaleCoverageData.csDateToOrdinalWords2022January25ExpectationTheoryData等属性并以DateExpectationRow承载Year/Month/Day/Expected为各文化的输出提供逐项对照基准。自定义日期序数词转换器实战当内置文化资料无法满足需求时例如为某个自定义文化或业务场景提供专属格式可基于该接口实现自己的转换器并注册到Configuratorusing Humanizer; public sealed class MyDateToOrdinalWordConverter : IDateToOrdinalWordConverter { public string Convert(DateTime date) ${date.Day} of {date:MMMM yyyy}; // 自定义格式 public string Convert(DateTime date, GrammaticalCase grammaticalCase) Convert(date); }注册方式必须发生在首次使用注册表之前建议放在应用启动或ModuleInitializer中Configurator.DateToOrdinalWordsConverters.Register(xx-XX, new MyDateToOrdinalWordConverter());Register也接受工厂委托FuncCultureInfo, IDateToOrdinalWordConverter重载便于按文化上下文构造转换器。注册完成后在该文化环境下调用date.ToOrdinalWords()即会命中自定义实现。需要注意的是仓库本身是只读的以上注册属于使用方项目中的扩展行为无需也不应修改 Humanizer 源码。使用限制与注意事项文化敏感性输出格式完全取决于执行线程的当前文化CultureInfo.CurrentCulture同一日期在不同文化下结果不同如需固定文化可在调用前切换线程文化测试中的CultureSwap即采用此方式语法格的适用面GrammaticalCase仅对注册了区分格的模式的文化有意义英语及未注册格形态的文化下传入该参数不会改变输出注册时机LocaliserRegistry一旦被使用即冻结之后再Register会抛出InvalidOperationException务必在首次任何 Humanizer 日期转换操作之前完成注册平台差异DateOnly相关扩展及IDateOnlyToOrdinalWordConverter仅在NET6_0_OR_GREATER下可用面向旧框架.NET Framework 4.8 / .NET Standard 2.0时只能使用DateTime路径且代码中已为这些平台准备了对应的字符串拼接分支。总结IDateToOrdinalWordConverter是 Humanizer 日期序数词本地化的契约核心扩展方法ToOrdinalWords通过它向不同文化分发转换任务默认实现覆盖英语序数表达与非英语短日期模式模式化实现则借助模板、日渲染模式、属格月份与历法模式精确刻画各语言的日期形态而源码生成器与LocaliserRegistry共同保证了近百种文化的可扩展覆盖。理解这一接口及其调用链你便能在多语言应用中精准控制日期序数词的呈现并随时通过自定义注册扩展新的文化形态。赞分享开发工具【免费下载链接】HumanizerHumanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities项目地址https://gitcode.com/gh_mirrors/hu/Humanizer点击查看免费下载相关推荐Humanizer 的 IDateToOrdinalWordConverter日期转序数词文本的本地化转换接口解析Humanizer 的 IDateToOrdinalWordConverter日期转序数词文本的本地化转换接口解析 IDateToOrdinalWordCon开发工具Humanizer 本地化序数日期转换接口 IDateToOrdinalWordConverter 深入解析Humanizer 本地化序数日期转换接口 IDateToOrdinalWordConverter 深入解析 IDateToOrdinalWordConvert开发工具Humanizer 日期转序数词本地化接口深度解析IDateToOrdinalWordConverter 与 IDateOnlyToOrdinalWordConverterHumanizer 日期转序数词本地化接口深度解析IDateToOrdinalWordConverter 与 IDateOnlyToOrdinalWordCo开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/7 15:06:35

律师个人 IP GEO 实操:让 AI 在当事人咨询时引用你的专业观点

1. 法律服务 GEO 合规前提:不承诺胜诉、不虚假承诺案件结果法律服务行业做 GEO(生成式引擎优化)与普通行业有本质区别:律师输出的内容直接关系到当事人的重大利益,也受到《律师法》《广告法》以及律师执业规范的严格约…

2026/10/7 15:56:39

Dell服务器RAID在线扩容实战指南:硬件兼容性与固件协同

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 15:56:39

Cadence Sigrity 信号完整性仿真实操:从 PCB 到 TDR 分析全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 15:56:39

水果识别毕设实战:CNN模型训练到Flask系统部署全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 15:56:39

深入理解 Microsoft Store 安装包:AppX/MSIX 封装原理与离线部署

简介:本资源是专为Windows系统用户设计的Microsoft Store离线重装包,适用于因系统更新异常、应用被误卸载或组件缺失导致Store无法启动(如PowerShell中搜索不到)的修复场景,尤其适合IT支持人员、系统运维新手及需快速恢…

2026/10/7 15:51:38

毫米波雷达目标识别:从FMCW原理到CFAR与点云聚类实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/7 8:18:33

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/6 17:46:51

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

ESP32免重刷固件:浏览器直接修改NVS键值实现WiFi配置更新

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

SAP HANA查询结果导出CSV:避开乱码、性能与权限的实用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

数字后端Placement阶段Density与Congestion控制实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑