- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
导读
IDateToOrdinalWordConverter是 Humanizer 中负责把DateTime转换为本地化"序数词日期"(Ordinal Words)的公开接口,是ToOrdinalWords()扩展方法背后的本地化契约。本文以该接口为中心,完整讲解它的两个方法签名、参数语义,并深入src/Humanizer的源码实现——从默认转换器、模式化转换器到配置注册表与调用链,最后给出自定义实现并接入 Humanizer 配置体系的完整方案,帮助你掌握如何在多语言场景下精确控制日期序数词的输出格式。
接口概览:本地化ToOrdinalWords的契约
IDateToOrdinalWordConverter位于命名空间Humanizer.Localisation.DateToOrdinalWords(即Humanizer.Localisation.DateToOrdinalWords命名空间下的IDateToOrdinalWordConverter),其定义如下:
public interface IDateToOrdinalWordConverter接口的 XML 文档说明其职责为:将日期转换为ToOrdinalWords所用的本地化文本(Converts dates into the localized text used byToOrdinalWords)。也就是说,Humanizer 对外暴露的DateTime.ToOrdinalWords()扩展方法本身不做任何格式化逻辑,而是把工作委托给当前文化(culture)对应的这个转换器实例,这正是 Humanizer 一贯的"本地化注册表 + 策略接口"设计模式。
接口共声明了两个方法:
| 方法签名 | 说明 |
|---|---|
string Convert(DateTime date) | 将日期转换为当前文化的序数词日期文本 |
string Convert(DateTime date, GrammaticalCase grammaticalCase) | 使用指定的语法格(grammatical case)转换日期 |
对应接口源码见 IDateToOrdinalWordConverter.cs,两个方法的实际定义与文档完全一致:
public interface IDateToOrdinalWordConverter { string Convert(DateTime date); string Convert(DateTime date, GrammaticalCase grammaticalCase); }方法详解
Convert(DateTime date)
- 签名:
string Convert(System.DateTime date); - 参数:
date—— 要格式化的System.DateTime实例。 - 返回:
System.String—— 本地化的序数词日期字符串。 - 语义:将日期转换为"序数词"形式,例如英文文化下
2023 年 1 月 1 日会输出"1st of January, 2023";输出样式完全取决于当前线程的CultureInfo.CurrentCulture。
Convert(DateTime date, GrammaticalCase grammaticalCase)
- 签名:
string Convert(System.DateTime date, Humanizer.GrammaticalCase grammaticalCase); - 参数:
date—— 要格式化的日期;grammaticalCase—— 要应用的语法格,取值来自Humanizer.GrammaticalCase枚举。
- 返回:
System.String—— 指定语法格下的本地化序数词日期字符串。 - 语义:对俄语、波兰语等具有格系统的语言,序数词会随语法格(主格、属格等)变化,例如俄语中
Nominative与Genitive会产生不同的日期表达。对英语这类没有格系统的语言,该参数不产生实际影响。
关于GrammaticalCase的完整枚举值说明,可参考仓库中的 GrammaticalCase.md API 文档。
从接口到输出的完整调用链
要理解这个接口在项目中的真实地位,需要沿着调用链往下看。Humanizer 对使用者暴露的入口是 DateToOrdinalWordsExtensions.cs 中的扩展方法:
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);也就是说,ToOrdinalWords()在内部从Configurator取回当前已解析的转换器,再调用其Convert方法。而Configurator中对应属性的定义位于 Configurator.cs:
public static LocaliserRegistry<IDateToOrdinalWordConverter> DateToOrdinalWordsConverters { get; } = new DateToOrdinalWordsConverterRegistry();内部访问器Configurator.DateToOrdinalWordsConverter则通过DateToOrdinalWordsConverters.ResolveForCulture(null)解析当前文化对应的转换器实例(见 Configurator.cs)。
注册表的实现是 DateToOrdinalWordsConverterRegistry.cs,它继承自LocaliserRegistry<IDateToOrdinalWordConverter>,其构造函数以DefaultDateToOrdinalWordConverter作为所有文化的默认兜底,并通过DateToOrdinalWordsConverterRegistryRegistrations.Register(this)注入各语言特定的注册项:
class DateToOrdinalWordsConverterRegistry : LocaliserRegistry<IDateToOrdinalWordConverter> { public DateToOrdinalWordsConverterRegistry() : base(_ => new DefaultDateToOrdinalWordConverter()) => DateToOrdinalWordsConverterRegistryRegistrations.Register(this); }这里的DateToOrdinalWordsConverterRegistryRegistrations由Humanizer.SourceGenerators项目基于src/Humanizer/Locales下的 YAML 语言数据生成,这就是各语言能够获得"开箱即用"本地化行为的底层机制。
默认实现:DefaultDateToOrdinalWordConverter的分支逻辑
默认转换器 DefaultDateToOrdinalWordConverter.cs 是本接口最直接的参考实现,其Convert(DateTime)逻辑非常清晰:
public virtual string Convert(DateTime date) { var culture = CultureInfo.CurrentCulture; if (culture.TwoLetterISOLanguageName != "en") { return SanitizeNonEnglishDate(date.ToString("d", culture)); } return date.Day.Ordinalize() + date.ToString(" MMMM yyyy"); }可以拆解为两条分支:
- 英语文化(
TwoLetterISOLanguageName == "en"):日份使用date.Day.Ordinalize()生成序数词(如1st、22nd),再拼接" MMMM yyyy"格式的月份与年份,最终得到"1st of January, 2023"这样的输出。 - 非英语文化:直接采用当前文化的短日期模式
date.ToString("d", culture)(短日期模式本身已包含各文化的日期顺序),随后通过SanitizeNonEnglishDate清理输出中可能混入的排版方向控制符。
SanitizeNonEnglishDate会剔除三种 Unicode 控制字符:LeftToRightMark(U+200E)、RightToLeftMark(U+200F)和ArabicLetterMark(U+061C)。原因在于某些日历系统(如阿拉伯语、希伯来语相关日历)在短日期输出中会嵌入方向性标记,若不清理,当日期文本被嵌入更大的序数短语时会影响可读性。
值得注意的是,带语法格的重载Convert(DateTime, GrammaticalCase)在默认实现中直接忽略grammaticalCase参数并转发给无格版本:
public virtual string Convert(DateTime date, GrammaticalCase grammaticalCase) => Convert(date);这是因为默认转换器不随语法格变化措辞。对应地,Humanizer 还为 .NET 6+ 提供了DateOnly版本的默认实现 DefaultDateOnlyToOrdinalWordConverter.cs,逻辑完全一致,仅把输入类型换成DateOnly。
模式化实现:PatternDateToOrdinalWordsConverter与OrdinalDatePattern
从源码结构看,Humanizer 的本地化体系还为需要精确控制日期模板的语言提供了更灵活的实现——PatternDateToOrdinalWordsConverter.cs。它继承自DefaultDateToOrdinalWordConverter,构造时接收一个OrdinalDatePattern模式对象:
class PatternDateToOrdinalWordsConverter(OrdinalDatePattern pattern) : DefaultDateToOrdinalWordConverter { public override string Convert(DateTime date) => pattern.Format(date); }OrdinalDatePattern(见 OrdinalDatePattern.cs)将"日期模板"与"日份渲染模式"解耦,其核心由两部分组成:
日份渲染模式
OrdinalDateDayMode(枚举定义于同一文件),支持五种渲染方式:Numeric:文化感知的数字;Ordinal:序数词;OrdinalWhenDayIsOne:仅当月第一天使用序数词(如部分语言中1er),其余日期用数字;MasculineOrdinalWhenDayIsOne:同前,但第一天的序数词强制使用阳性(GrammaticalGender.Masculine)形式;DotSuffix:数字后带点号后缀(如德式1.)。
模板字符串:模板内可包含
{day}占位符。格式化时先按文化格式生成完整日期(含真实的d日份说明符,以保证斯拉夫语系中与月份相邻时能正确触发属格月份名),再把<<DAY>>标记与数字日份一并替换为实际渲染的日份文本。该模式还支持通过months/monthsGenitive/hijriMonths数组覆盖月份名称,并针对希吉拉历(Hijri / UmAlQura)自动切换到对应的月份数组。
此外,OrdinalDateCalendarMode枚举(见 OrdinalDateCalendarMode.cs)控制日历解析方式:Gregorian强制使用格里高利历,Native则保留文化的默认日历(如泰历、希伯来历、波斯历),使年份输出符合当地习惯。
需要说明的是:PatternDateToOrdinalWordsConverter、OrdinalDatePattern等类是 Humanizer 内部实现细节(非public),主要用于支撑由 YAML 语言数据生成出来的各语言注册项,开发者一般无需直接使用它们。
自定义实现并接入 Humanizer:实操指南
接口是public的,因此你可以完全自定义日期序数词的输出规则。完整步骤分三步:
第一步:实现接口
using Humanizer; using Humanizer.Localisation.DateToOrdinalWords; public sealed class MyDateToOrdinalWordConverter : IDateToOrdinalWordConverter { public string Convert(DateTime date) => $"Day {date.Day} of {date:MMMM yyyy}"; public string Convert(DateTime date, GrammaticalCase grammaticalCase) { // 若你的语言没有语法格,可直接复用无格版本 return Convert(date); } }第二步:注册到配置注册表
Configurator.DateToOrdinalWordsConverters是公开的LocaliserRegistry<IDateToOrdinalWordConverter>,可按文化注册自定义转换器:
Configurator.DateToOrdinalWordsConverters .Register("en", () => new MyDateToOrdinalWordConverter());对于需要语法格变体的语言,可以在Convert(date, grammaticalCase)中读取GrammaticalCase枚举值(如Nominative、Genitive、Dative等)分支处理,以满足俄语、波兰语等格系统语言的日期表达需求。
第三步:通过扩展方法验证效果
注册完成后,调用ToOrdinalWords()扩展方法时,Configurator.DateToOrdinalWordsConverter会按当前文化解析到你的实现:
var text = new DateTime(2023, 1, 1).ToOrdinalWords(); // 在你的自定义实现下输出 "Day 1 of January 2023"与DateOnly变体的关系
接口在 .NET 6.0 及以上还拥有对应的DateOnly版本 IDateOnlyToOrdinalWordConverter.cs:
public interface IDateOnlyToOrdinalWordConverter { string Convert(DateOnly date); string Convert(DateOnly date, GrammaticalCase grammaticalCase); }它在命名上与IDateToOrdinalWordConverter一一对应,只是输入类型换成DateOnly;对应的扩展方法见 DateToOrdinalWordsExtensions.cs,且仅当目标框架为NET6_0_OR_GREATER时可用。注册表侧同样有配套的Configurator.DateOnlyToOrdinalWordsConverters属性(见 Configurator.cs)。如果你要同时支持DateTime与DateOnly,通常需要分别实现两个接口。
小结
IDateToOrdinalWordConverter虽只是一个两方法的小接口,却是 Humanizer 日期序数词本地化体系的关键枢纽:
- 对外,它是
DateTime.ToOrdinalWords()扩展方法(DateToOrdinalWordsExtensions.cs)的实际执行者; - 对内,它由 DateToOrdinalWordsConverterRegistry.cs 按文化解析,默认回退到 DefaultDateToOrdinalWordConverter.cs;
- 扩展,实现该接口并通过
Configurator.DateToOrdinalWordsConverters.Register(...)注册,即可为任意文化提供完全自定义的日期序数词输出,包括语法格敏感的变体。
理解了这个接口,你就掌握了 Humanizer 中"日期 → 序数词文本"这一条本地化管线的钥匙,也就能在任何需要多语言日期表达的项目中平滑接入或替换默认行为。
- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
相关推荐
Humanizer 的 IDateToOrdinalWordConverter:日期转序数词文本的本地化转换接口解析
Humanizer 的 IDateToOrdinalWordConverter:日期转序数词文本的本地化转换接口解析 IDateToOrdinalWordCon
开发工具Humanizer 本地化日期序数词转换:IDateOnlyToOrdinalWordConverter 接口全解
Humanizer 本地化日期序数词转换:IDateOnlyToOrdinalWordConverter 接口全解 在 Humanizer 中,把日期(如 20
开发工具Humanizer 日期序数词本地化指南:解析 IDateToOrdinalWordConverter 与 ToOrdinalWords 的完整实现链路
Humanizer 日期序数词本地化指南:解析 IDateToOrdinalWordConverter 与 ToOrdinalWords 的完整实现链路 导读
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考