- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
本篇技术指南围绕 Humanizer 中的WordForm枚举展开,讲解该枚举如何用于指定同一单词在不同变体下的形式(正常形式、缩写形式以及卢森堡语的 Eifeler 规则形式),并结合仓库源码深入剖析其在序数化(Ordinalize)、数字转单词(ToWords/ToOrdinalWords)等本地化场景中的实际应用。读完本文,你将掌握WordForm三个枚举值的精确语义、在西班牙语与卢森堡语等语言中的典型调用方式,以及 Humanizer 内部序数化器如何消费该枚举实现“一词多形”的机制。
什么是 WordForm:为“一词多形”而生
在不少自然语言中,同一个词会因为语法位置、搭配对象的不同而存在多种变体。例如西班牙语中“第一”有primero(完整形式)与primer(缩写形式)之分,“21”有veintiuno与veintiún之别;卢森堡语中的数字组合还需要遵循著名的 Eifeler 规则(Eifeler Reegel)做发音驱动的拼写变化。
Humanizer 为此专门定义了WordForm枚举,作为为同一单词的不同变体提供选择入口的选项类型。该枚举定义在 src/Humanizer/WordForm.cs,其官方 API 文档见 website/versioned_docs/version-2.14.1/api/Humanizer.WordForm.md:
namespace Humanizer; /// <summary> /// Options for specifying the form of the word when different variations of the same word exists. /// </summary> public enum WordForm { /// <summary> /// Indicates the normal form of a written word. /// </summary> Normal, /// <summary> /// Indicates the shortened form of a written word. /// </summary> Abbreviation, /// <summary> /// Indicates the Eifeler Rule form of a word. /// https://lb.wikipedia.org/wiki/Eifeler_Reegel /// </summary> Eifeler, }三个成员一览
| 枚举成员 | 数值 | 语义 | 典型语言场景 |
|---|---|---|---|
Normal | 0 | 书面单词的正常(完整)形式 | 西班牙语1.º、primero、veintiuno |
Abbreviation | 1 | 书面单词的缩短形式 | 西班牙语1.er、primer、veintiún |
Eifeler | 2 | 遵循 Eifeler 规则(Eifeler Reegel)的词形 | 卢森堡语(lb语言环境)数字组合 |
从源码结构看,Normal与Abbreviation是文档中明确标注数值(0 与 1)的两个成员;Eifeler作为第三个成员未显式赋值,按 C# 枚举默认递增规则其值为 2。该成员在 API 参考文档中未展开描述,但它服务于卢森堡语这类对连读音变有严格要求的语言。
核心消费入口:Ordinalize 扩展方法族
WordForm最主要的消费方是 src/Humanizer/OrdinalizeExtensions.cs 中针对string、int、long三组重载提供的序数化扩展方法。源码的 XML 文档注释给出了最直观的西班牙语示例:
"1".Ordinalize(WordForm.Abbreviation) -> 1.er // As in "Vivo en el 1.er piso" "1".Ordinalize(WordForm.Normal) -> 1.º // As in "Fui el 1º de mi promoción"在西班牙语中,1.er(如“住在 1 楼”)与1.º(如“我是班里第 1 名”)虽然都表示“第一”,但书写形式完全不同——这正是WordForm要解决的问题。当调用Ordinalize(WordForm.Abbreviation)时(见 OrdinalizeExtensions.cs#L34-L35),Humanizer 会把枚举透传给当前语言环境的序数化器处理:
public static string Ordinalize(this string numberString, WordForm wordForm) => Configurator.Ordinalizer.Convert(int.Parse(numberString), NormalizeOrdinalNumberString(numberString), wordForm);OrdinalizeExtensions为WordForm提供了成体系的重载矩阵,覆盖三种输入类型与三种可选维度:
- 仅指定词形:
"1".Ordinalize(WordForm.Abbreviation)/1.Ordinalize(WordForm.Normal)/long.Ordinalize(...) - 词形 + 语言环境:
1.Ordinalize(new CultureInfo("es-ES"), WordForm.Abbreviation) - 词形 + 语法性别(
GrammaticalGender):1.Ordinalize(GrammaticalGender.Masculine, WordForm.Abbreviation)、1.Ordinalize(GrammaticalGender.Feminine, WordForm.Normal)→1.ª - 词形 + 性别 + 语言环境:
1.Ordinalize(GrammaticalGender.Masculine, new CultureInfo("es-ES"), WordForm.Normal)→1.º
其中语法性别(GrammaticalCase/GrammaticalGender)主要服务于巴西葡萄牙语(1ºvs1ª)和西班牙语等区分阴阳性的语言;culture参数为null时回退到CultureInfo.CurrentCulture(见 OrdinalizeExtensions.cs#L184-L188)。
注意:Ordinalize 系列只接受整数值。源码类注释明确提醒——调用方若持有小数,必须先自行决定取整与转换策略(OrdinalizeExtensions.cs#L6-L9)。
数字转单词:ToWords 与 ToOrdinalWords 中的词形选择
除序数化后缀外,WordForm同样出现在数字转单词的扩展方法中,定义于 src/Humanizer/NumberToWordsExtension.cs。
ToWords:基数词变体
以西班牙语数字 21 为例(NumberToWordsExtension.cs#L115-L123):
21.ToWords(WordForm.Normal) -> veintiuno // as in "Mi número favorito es el veintiuno". 21.ToWords(WordForm.Abbreviation) -> veintiún // as in "En total, conté veintiún coches"同一个 21,完整形式veintiuno用于“我最喜欢的数字是 21”,缩写形式veintiún则用于“我总共数了 21 辆车”(因为后面跟了阳性名词coches)。ToWords同样支持与GrammaticalGender组合,得到阴性变体veintiuna(如“veintiuna personas”)。该扩展为int与long均提供了(number, WordForm, culture)与(number, WordForm, gender, culture)两组重载(见 NumberToWordsExtension.cs#L269-L311)。
ToOrdinalWords:序数词变体
西班牙语序数词“第三”同样受词形影响(NumberToWordsExtension.cs#L36-L44):
1.ToOrdinalWords(WordForm.Normal) -> "primero" // As in "He llegado el primero". 3.ToOrdinalWords(WordForm.Abbreviation) -> "tercer" // As in "Vivo en el tercer piso"结合性别后:
3.ToOrdinalWords(GrammaticalGender.Masculine, WordForm.Normal) -> "tercero" 3.ToOrdinalWords(GrammaticalGender.Masculine, WordForm.Abbreviation) -> "tercer" 3.ToOrdinalWords(GrammaticalGender.Feminine, WordForm.Normal) -> "tercera" 3.ToOrdinalWords(GrammaticalGender.Feminine, WordForm.Abbreviation) -> "tercera"可见“缩写”并非简单截断,而是由各语言自己的形态规则决定:阳性缩写为tercer(置于名词前),阴性两种形式均为tercera。这正是WordForm设计意图的体现——由语言环境(locale)决定如何响应枚举值。
Eifeler 规则:WordForm.Eifeler 与卢森堡语
WordForm.Eifeler是三个成员中最特殊的一个,服务于卢森堡语(lb)的 Eifeler 规则。该规则是卢森堡语中著名的发音连读音变规则:某些词尾辅音在特定后续音的影响下会被省略或变化。
在 Humanizer 中,Eifeler 规则的实现位于 src/Humanizer/Localisation/EifelerRule.cs,而WordForm.Eifeler的消费点在 src/Humanizer/Localisation/NumberToWords/UnitLeadingCompoundNumberToWordsConverter.cs(“单位前置复合数词转换器”,服务于德语、卢森堡语等“个位在前、十位在后”的语言族):
var wordForm = profile.TensJoinerTransform == CompoundTensJoinerTransform.Eifeler && scale.CountWordFormNextWord is { Length: > 0 } nextWord && EifelerRule.DoesApply(nextWord) ? WordForm.Eifeler : WordForm.Normal;关键逻辑(见 UnitLeadingCompoundNumberToWordsConverter.cs#L168-L239):
- 当语言配置声明十位连接符需要 Eifeler 变换(
tensJoinerTransform: 'eifeler')且后接单词命中 Eifeler 规则时,词形切换为WordForm.Eifeler; - 在
GetUnit中,Eifeler 规则被刻意限制在语言配置声明的触发数字上——源码注释明确说明:只有数字1或7时才对单位词应用EifelerRule.Apply(unit)(UnitLeadingCompoundNumberToWordsConverter.cs#L232-L238),若扩散到所有单位反而会破坏语言特有的复合词构成; - 十位连接符本身也可能通过
EifelerRule.ApplyIfNeeded变换(UnitLeadingCompoundNumberToWordsConverter.cs#L244-L249)。
对应的语言配置位于 src/Humanizer/Locales/lb.yml,其中包含tensJoinerTransform: 'eifeler'、supportsEifelerRule: true、applyEifelerRule: true等开关(见该文件第 317、322、602 行附近),并有secondaryPlaceholderMode: 'luxembourgish-eifeler-n'的特殊占位符模式(第 35 行)。这些 YAML 语言数据经 Humanizer 的源生成器(src/Humanizer.SourceGenerators)编译为内部 Profile,供转换器在运行时读取。
从设计上可以看出,WordForm.Eifeler与Normal/Abbreviation不同:它不是调用方直接传入的用户选项,而是由转换器根据语言规则自动选定的内部词形状态,用来把“Eifeler 规则是否生效”这一判定结果传递给底层单词拼写逻辑。
底层机制:序数化器如何消费 WordForm
理解WordForm的传递链路,需要看两个接口与一个实现:
- 契约接口 src/Humanizer/Localisation/Ordinalizers/IOrdinalizer.cs:定义了
Convert(int/long, string, WordForm)与Convert(int/long, string, GrammaticalGender, WordForm)两组带词形参数的签名(见该文件第 23、42、70、89 行),是OrdinalizeExtensions重载与语言实现之间的桥接层; - 模板实现 src/Humanizer/Localisation/Ordinalizers/WordFormTemplateOrdinalizer.cs:一个“按语法性别和词形应用模板模式”的序数化器,其
GetPattern(gender, wordForm)方法决定最终选用哪套前后缀模板:
Pattern GetPattern(GrammaticalGender gender, WordForm wordForm) { var set = gender switch { GrammaticalGender.Feminine => options.Feminine, GrammaticalGender.Neuter => options.Neuter, _ => options.Masculine }; return wordForm == WordForm.Abbreviation ? set.Abbreviation : set.Normal; }可见在WordFormTemplateOrdinalizer中,WordForm只区分Abbreviation与其余值(Normal兜底),且每种语法性别都维护Normal/Abbreviation两套PatternSet。每套Pattern包含四类规则数据(WordFormTemplateOrdinalizer.cs#L114-L126):
Prefix:序数前要前置的文本;DefaultSuffix:无特殊规则命中时的兜底后缀;ExactReplacements:对特定整数的整体替换(绕过输入文本);ExactSuffixes:对特定整数的后缀追加;LastDigitSuffixes:按绝对值的末位数字映射的后缀。
格式化时(Format方法)依次尝试:精确替换 → 精确后缀 → 末位数字后缀 → 默认后缀(WordFormTemplateOrdinalizer.cs#L58-L80)。此外Options还支持ZeroAsPlainNumber、MinValueAsPlainNumber与NegativeNumberMode(负数归一化模式:None/AbsoluteInvariant/AbsoluteCulture,见 WordFormTemplateOrdinalizer.cs#L164-L183),用于处理 0、int.MinValue与负数的边界情况。
值得一提的是,OrdinalizeExtensions内部对long的处理存在兼容性设计:若当前语言环境的序数化器实现了ILongOrdinalizer则直接调用 64 位重载,否则将值安全转换到int范围,超出范围时抛出NotSupportedException(OrdinalizeExtensions.cs#L360-L394)。
测试验证:词形行为有据可查
WordForm的行为在仓库测试中得到了直接验证,可参考:
- tests/Humanizer.Tests/OrdinalizeTests.cs:覆盖西班牙语
1.er/1.º/1.ª等带词形与性别的序数化断言; - tests/Humanizer.Tests/NumberToWordsTests.cs:覆盖
veintiuno/veintiún、primero/primer等数字转单词断言。
如需在本地复现这些行为,可按 readme.md 中的方式克隆仓库并运行对应测试项目(例如dotnet test tests/Humanizer.Tests)。
使用要点总结
WordForm是一个“请求式”选项:Normal(0)与Abbreviation(1)由调用方显式传入,具体如何响应由语言环境决定——同一枚举值在西班牙语得到1.er,在其他语言可能没有任何差异;WordForm.Eifeler(2)是“内部状态”:主要由卢森堡语数字转换器根据 Eifeler 规则自动选定,普通调用方通常无需直接使用;- 组合维度:词形常与
GrammaticalGender(性别)和CultureInfo(语言环境)联合使用,Humanizer 为string/int/long三种输入类型提供了完整的重载矩阵,culture为null时回退当前线程语言; - 适用范围:目前词形选择主要落地在序数化(
Ordinalize)与数字转单词(ToWords/ToOrdinalWords)两条扩展路径上,其中模板序数化器(WordFormTemplateOrdinalizer)是Abbreviation的核心消费实现; - 语言开关来自 YAML 数据:各语言的词形行为(如 Eifeler 规则的
tensJoinerTransform、supportsEifelerRule等)通过 src/Humanizer/Locales 下的语言配置文件声明,并由源生成器编译为运行时 Profile,新增语言变体时通常只需调整语言数据而非 C# 逻辑。
- 开发工具
【免费下载链接】Humanizer
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
相关推荐
Humanizer WordForm 枚举全解析:Normal、Abbreviation 与 Eifeler 词形选择指南
Humanizer WordForm 枚举全解析:Normal、Abbreviation 与 Eifeler 词形选择指南 WordForm 是 Humaniz
开发工具Humanizer 枚举人性化指南:全面解析 EnumHumanizeExtensions 的 Humanize 方法与底层原理
Humanizer 枚举人性化指南:全面解析 EnumHumanizeExtensions 的 Humanize 方法与底层原理 本文基于 Humanizer
开发工具Humanizer 枚举反向解析指南:EnumDehumanizeExtensions.DehumanizeTo 完整用法与匹配原理
Humanizer 枚举反向解析指南:EnumDehumanizeExtensions.DehumanizeTo 完整用法与匹配原理 导读 EnumDehuma
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考