尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Humanizer 本地化格式化核心:DefaultFormatter 类实现原理与扩展指南

发布时间:2026/9/29 7:29:09

资讯中心
01
ARTICLE

Humanizer 本地化格式化核心:DefaultFormatter 类实现原理与扩展指南

Humanizer 本地化格式化核心:DefaultFormatter 类实现原理与扩展指南
开发工具【免费下载链接】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点击查看免费下载DefaultFormatter是 Humanizer 中IFormatter接口的默认实现负责将日期、时间跨度、时间单位与数据单位按指定区域文化Culture渲染为本地化文本。本文以官方 API 文档 Humanizer.DefaultFormatter.md 为主体结合仓库源码逐项解析其构造函数、公开方法、可扩展点与数据驱动架构帮助你理解 Humanizer 多语言格式化机制并掌握如何为自定义区域文化接入或替换格式化逻辑。一、类概览DefaultFormatter 在 Humanizer 中的定位public class DefaultFormatter : Humanizer.IFormatter从 API 文档给出的声明可以看出DefaultFormatter继承自System.Object并实现Humanizer.IFormatter接口。它在 Humanizer 的本地化体系中的职责是把时间单位 数量 时态等结构化输入翻译成符合当前区域文化习惯的自然语言短语例如英语的 3 days ago、俄语的 3 дня назад。在配置层面DefaultFormatter是Configurator.Formatters注册表的默认兜底实现。源码 FormatterRegistry.cs 中可以看到class FormatterRegistry : LocaliserRegistryIFormatter { public FormatterRegistry() : base(c new DefaultFormatter(c)) FormatterRegistryRegistrations.Register(this); }即当某个区域文化没有注册专门的格式化器时LocaliserRegistryIFormatter会直接以new DefaultFormatter(culture)兜底。而 Configurator.cs 通过public static LocaliserRegistryIFormatter Formatters { get; } new FormatterRegistry();将这一注册表暴露为全局配置入口。二、构造函数两种创建方式与区域文化解析API 文档定义了两种构造函数public DefaultFormatter(string localeCode); public DefaultFormatter(System.Globalization.CultureInfo culture);对应源码 DefaultFormatter.cspublic DefaultFormatter(CultureInfo culture) { Culture culture; phraseTable LocalePhraseTableCatalog.Resolve(culture) ?? throw new InvalidOperationException(The generated locale phrase tables are missing the required English fallback.); } public DefaultFormatter(string localeCode) : this(new CultureInfo(localeCode)) { }要点如下localeCode构造方式直接传入区域代码字符串例如ru-RU、de内部会先转换为CultureInfo再委托给另一个构造函数。culture构造方式接收完整的CultureInfo实例例如CultureInfo.GetCultureInfo(en)。短语表解析构造函数通过LocalePhraseTableCatalog.Resolve(culture)解析出该区域文化的生成式短语表LocalePhraseTable如果解析失败连英语兜底表都缺失则抛出InvalidOperationException。LocalePhraseTableCatalog.Resolve的解析策略在 LocalePhraseTable.cs 中实现它会从当前文化沿Parent链向上回溯例如zh-CN→zh逐步尝试解析找不到任何匹配时最终回退到en英语表。这意味着任何未显式覆盖的区域文化都会得到英语短语作为最后的兜底。三、Culture 属性格式化使用的区域文化protected System.Globalization.CultureInfo Culture { protected get; }Culture是一个protected属性仅在类内部及派生类中可读。它在构造函数中被赋值用于解析数字词的本地化形式NumberToWords决定数字的格式化方式如number.ToString(Culture)作为生成式短语表解析的输入。对派生类作者而言Culture是感知当前格式化器服务于哪种语言的主要入口。四、日期相对化方法Now / Never / 相对时间4.1 DateHumanize_Now 与 DateHumanize_Neverpublic virtual string DateHumanize_Now(); public virtual string DateHumanize_Never();这两个无参方法分别返回此刻与从不的本地化文本。从源码看它们直接读取短语表并提供默认值public virtual string DateHumanize_Now() phraseTable.DateNow ?? now; public virtual string DateHumanize_Never() phraseTable.DateNever ?? never;也就是说英语区域默认输出now/never俄语区域则输出сейчас/никогда见 ru.yml 中now: сейчас、never: никогда的配置。4.2 DateHumanize(TimeUnit, Tense, int)相对日期短语public virtual string DateHumanize(Humanizer.TimeUnit timeUnit, Humanizer.Tense timeUnitTense, int unit);这是日期相对化2 days ago / in 3 hours的核心方法参数含义为timeUnit时间单位来自TimeUnit枚举Millisecond、Second、Minute、Hour、Day、Week、Month、YeartimeUnitTense时态来自Tense枚举Past 过去 / Future 将来unit单位数量。源码实现DefaultFormatter.cs为public virtual string DateHumanize(TimeUnit timeUnit, Tense timeUnitTense, int unit) TryFormatDateFromPhraseTable(timeUnit, timeUnitTense, unit, out var result) ? result : throw new InvalidOperationException($Missing generated relative-date phrase for {Culture.Name} and unit {timeUnit}.);内部逻辑TryFormatDateFromPhraseTable遵循以下优先级数量为 0 时直接返回此刻短语数量为 1 且存在单数形式时返回Single短语例如俄语миллисекунду назад数量为 2 且存在名为two的精确模板时使用双数模板否则按数量选择单数 / 双数 / 少数 / 复数等语法形式并通过{count}占位符渲染出完整短语。五、时间跨度方法零值、常规值与年龄表达5.1 TimeSpanHumanize_Zero零时长表示public virtual string TimeSpanHumanize_Zero();文档明确说明其语义为0 seconds零秒的字符串表示。源码读取短语表的TimeSpanZero字段默认值为no timepublic virtual string TimeSpanHumanize_Zero() phraseTable.TimeSpanZero ?? no time;俄语区域的配置在 ru.yml 中为zero: нет времени。5.2 TimeSpanHumanize(TimeUnit, int, bool)常规时长格式化public virtual string TimeSpanHumanize(Humanizer.TimeUnit timeUnit, int unit, bool toWordsfalse);参数说明timeUnit要表示的时间单位unit单位数量toWordsfalse默认时数量以数字呈现true时数量以单词呈现如 three hours 而非 3 hours。注意3.0.10 版本 API 文档标注该方法在timeUnit大于TimeUnit.Week时会抛出ArgumentOutOfRangeException即文档时代该方法的合法输入范围为毫秒到周。当前仓库源码已重构为数据驱动实现DefaultFormatter.cs当短语表缺少对应短语时抛出InvalidOperationException且不再限制在周以内——使用时应以你所引用版本的实际行为为准。源码中的词形选择逻辑FormatTimeSpanPhrase会依据toWords在数字变体与单词变体之间切换SingleWordsVariant/MultipleWordsVariant并把数量渲染为数字或本地化数字词。5.3 TimeSpanHumanize_Age年龄后缀格式public virtual string TimeSpanHumanize_Age();文档给出的示例非常直观英语中该方法返回把时长变成年龄表达的格式40 years通过添加 old后缀变成40 years old。源码实现为public virtual string TimeSpanHumanize_Age() { return phraseTable.TimeSpanAge ?? {0}; }它返回的是一个格式模板{0}占位符由调用方把已人性化的时长文本填入。俄语区域在 ru.yml 中将其配置为template: {value}无后缀。六、单位方法时间单位符号与数据单位6.1 TimeUnitHumanize(TimeUnit)时间单位符号public virtual string TimeUnitHumanize(Humanizer.TimeUnit timeUnit);该方法返回给定时间单位的本地化符号例如英语的s、m、h、d等。源码从短语表的时间单位条目中取Symbol字段public virtual string TimeUnitHumanize(TimeUnit timeUnit) { if (phraseTable.TryGetTimeUnitPhrase(timeUnit, out var generatedPhrase) generatedPhrase.Symbol is { } generatedSymbol) { return generatedSymbol; } throw new InvalidOperationException($Missing generated time-unit phrase for {Culture.Name} and unit {timeUnit}.); }6.2 DataUnitHumanize(DataUnit, double, bool)数据单位格式化public virtual string DataUnitHumanize(Humanizer.DataUnit dataUnit, double count, bool toSymboltrue);文档对参数的解释dataUnit数据单位count单位数量用于调整单复数形式toSymboltrue默认时以符号形式表达数据单位false时以完整单词表达。源码实现DefaultFormatter.cs有一个值得注意的细节——英语兜底回退public virtual string DataUnitHumanize(DataUnit dataUnit, double count, bool toSymbol true) { if (TryFormatDataUnitFromPhraseTable(dataUnit, count, toSymbol, out var generated)) { return generated; } if (dataUnit is DataUnit.Petabyte or DataUnit.Exabyte or DataUnit.Pebibyte or DataUnit.Kibibyte or DataUnit.Mebibyte or DataUnit.Gibibyte or DataUnit.Tebibyte !Culture.Name.Equals(en, StringComparison.OrdinalIgnoreCase)) { return EnglishFallback.DataUnitHumanize(dataUnit, count, toSymbol); } throw new InvalidOperationException($Missing generated>protected virtual string Format(Humanizer.TimeUnit unit, string resourceKey, int number, bool toWordsfalse); protected virtual string Format(string resourceKey); protected virtual string GetResourceKey(string resourceKey); protected virtual string GetResourceKey(string resourceKey, int number);文档对Format(TimeUnit, string, int, bool)的说明是格式化指定的资源键且当指定文化下资源不存在时抛出ArgumentException。对GetResourceKey(string, int)的说明是如果你的区域文化围绕多单位有复杂规则请重写此方法例如阿拉伯语、俄语——这正是 3.x 早期版本支持阿拉伯语双数2 天为 يومين、俄语格变化等特性的机制所在。需要特别说明的是当前仓库主分支源码中这些基于资源键拼接的扩展点已被数据驱动架构取代。现在由ProfiledFormatterProfiledFormatter.cs继承自DefaultFormatter承担区域定制逻辑它通过声明式的FormatterProfile记录词形检测器FormatterNumberDetectorKind、精确数字规则FormatterDateFormRule/FormatterTimeSpanFormRule、介词模式如罗马尼亚语de、性别表UnitGenders等实现了同一套格式化内核、按区域数据差异化输出的效果。NumberToWords仍保留为 protected 扩展点protected virtual string NumberToWords(TimeUnit unit, int number, CultureInfo culture) number.ToWords(culture);ProfiledFormatter对其进行了性别感知覆盖——当配置了UnitGenders时调用number.ToWords(gender, culture)。这也解释了为什么俄语 ru.yml 中要为每个时间单位声明timeUnitGenders如hour: masculine、second: feminine。八、语法形式解析单数、双数、少数与复数DefaultFormatter之所以能输出正确的本地化短语依赖LocalePhraseTable中按语法形式存储的多套文本。数据结构定义于 LocalePhraseTable.csreadonly record struct LocalizedPhraseForms( string Default, string? Zero null, string? Singular null, string? Dual null, string? Paucal null, string? Plural null, string? Many null);而ProfiledFormatter.DetectNumberFormProfiledFormatter.cs实现了多种语言的数字→语法形式映射SingularPlural1 用单数其余用复数英语风格ArabicLike1 单数、2 双数、3–10 复数ArabicCardinal0 零、1 单数、2 双数、%1003–10 复数、%10011–99 多数Between2And4Paucal1 单数、2–4 少数Polish以 2–4 结尾且非 12–14 时用少数SouthSlavic塞尔维亚/克罗地亚式单数、少数规则Slovenian单数、双数、少数3、4Russian调用RussianGrammaticalNumberDetectorLithuanian调用LithuanianNumberFormDetector。短语表本身由源生成器根据 Locales 目录下的 YAML 文件在编译期生成LocalePhraseTableCatalog.ResolveCore为源生成器实现的分部方法运行时仅做查表与模板渲染从而保持轻量。俄语 ru.yml 中past.millisecond的forms配置default/singular/dual即是对上述多形式短语数据的直观示例。九、实战注册与替换自定义格式化器在实际项目中DefaultFormatter通常不需要直接实例化——Humanizer 的扩展方法如TimeSpan.Humanize()、DateTime.Humanize()会通过 Configurator.cs 的GetFormatter(culture)自动解析当前线程区域文化对应的格式化器internal static IFormatter GetFormatter(CultureInfo? culture) Formatters.ResolveForCulture(culture);如果需要为特定区域文化注入自定义格式化逻辑可以通过Configurator.Formatters注册表覆盖默认实现注册表基类LocaliserRegistryT按文化名解析例如在应用启动时Configurator.Formatters.RegisterMyLocaleCode, MyFormatter();其中MyFormatter可以继承DefaultFormatter并重写DateHumanize、TimeSpanHumanize、TimeUnitHumanize等virtual方法也可以直接实现IFormatter接口。需要注意当前源码中若自定义格式化器需要支持语法格感知的时长IGrammaticalCaseTimeSpanFormatter必须显式实现该接口否则会抛出NotSupportedException见 DefaultFormatter.cs。十、小结DefaultFormatter是 Humanizer 本地化体系的中枢实现它以CultureInfo为输入以生成式短语表为数据源统一承载日期相对化、时长人性化、时间单位符号与数据单位符号的本地化输出同时通过virtual方法保留了派生扩展能力。从 3.0.10 的资源键 GetResourceKey重写模式演进到当前主分支的声明式FormatterProfile 源生成短语表模式其核心目标始终如一——让新增语言的成本从写 C# 代码降为补充 YAML 数据。理解DefaultFormatter就等于掌握了 Humanizer 多语言输出的底层逻辑与扩展路径。赞分享开发工具【免费下载链接】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 本地化格式化体系解析IFormatter 接口、DefaultFormatter 实现与扩展指南Humanizer 本地化格式化体系解析IFormatter 接口、DefaultFormatter 实现与扩展指南 本篇文章以 Humanizer 2.11开发工具Humanizer 本地化格式化核心IFormatter 接口与 DefaultFormatter 实现深度解析Humanizer 本地化格式化核心IFormatter 接口与 DefaultFormatter 实现深度解析 本指南围绕 Humanizer 的 Huma开发工具Humanizer 多语言格式化核心DefaultFormatter 类全面解析Humanizer 多语言格式化核心DefaultFormatter 类全面解析 DefaultFormatter 是 Humanizer 本地化格式化体系的开发工具上一篇DDrawCompat 使用教程免费修复 DirectDraw 老游戏Win10/11 上稳定满 60 帧下一篇Video Subtitle Master 1.4.0 版本解析翻译通道扩展与 Core ML 加速批量字幕工作流升级指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。