fix(localization): 修复紧凑数字格式化器处理未知选项的行为

- 修改 CompactNumberLocalizationFormatter 中 TryApplyOption 方法的返回逻辑
- 当键名未知时现在返回 true 而不是 false,允许忽略未知选项
- 更新 NumericDisplayFormatter 使用 ResolveRule 方法来确定格式化规则
- 添加对不同数值显示风格的支持和验证
- 在集成测试中添加未知选项的测试用例以验证正确行为
- 改进 NumericSuffixFormatRule 的文档注释以更清楚地描述功能
This commit is contained in:
GeWuYou 2026-03-21 15:01:47 +08:00
parent 5996ecf5f3
commit fca3808657
4 changed files with 47 additions and 12 deletions

View File

@ -52,6 +52,7 @@ public class LocalizationIntegrationTests
"status.health": "Health: {current}/{max}",
"status.gold": "Gold: {gold:compact}",
"status.damage": "Damage: {damage:compact:maxDecimals=2}",
"status.unknownCompact": "Gold: {gold:compact:maxDecimalss=2}",
"status.invalidCompact": "Gold: {gold:compact:maxDecimals=abc}"
}
""");
@ -63,6 +64,7 @@ public class LocalizationIntegrationTests
"status.health": "生命值: {current}/{max}",
"status.gold": "金币: {gold:compact}",
"status.damage": "伤害: {damage:compact:maxDecimals=2}",
"status.unknownCompact": "金币: {gold:compact:maxDecimalss=2}",
"status.invalidCompact": "金币: {gold:compact:maxDecimals=abc}"
}
""");
@ -134,6 +136,16 @@ public class LocalizationIntegrationTests
Assert.That(damage, Is.EqualTo("Damage: 1.23K"));
}
[Test]
public void GetString_WithUnknownCompactFormatterArgs_ShouldIgnoreUnknownOptions()
{
var gold = _manager!.GetString("common", "status.unknownCompact")
.WithVariable("gold", 1_250)
.Format();
Assert.That(gold, Is.EqualTo("Gold: 1.3K"));
}
[Test]
public void GetString_WithInvalidCompactFormatterArgs_ShouldFallbackToDefaultFormatting()
{

View File

@ -146,7 +146,7 @@ public sealed class CompactNumberLocalizationFormatter : ILocalizationFormatter
/// <param name="minDecimalPlaces">最小小数位数的引用参数</param>
/// <param name="trimTrailingZeros">是否去除尾随零的引用参数</param>
/// <param name="useGroupingBelowThreshold">是否在阈值以下使用分组的引用参数</param>
/// <returns>如果键名有效且值成功解析则返回true如果键名无效或值解析失败则返回false</returns>
/// <returns>如果值成功解析或键名未知则返回true如果键名已知但值解析失败则返回false</returns>
private static bool TryApplyOption(
string key,
string value,
@ -161,7 +161,7 @@ public sealed class CompactNumberLocalizationFormatter : ILocalizationFormatter
"minDecimals" => int.TryParse(value, out minDecimalPlaces),
"trimZeros" => bool.TryParse(value, out trimTrailingZeros),
"grouping" => bool.TryParse(value, out useGroupingBelowThreshold),
_ => false
_ => true
};
}
}

View File

@ -37,7 +37,7 @@ public sealed class NumericDisplayFormatter : INumericDisplayFormatter
}
var resolvedOptions = NormalizeOptions(options);
var rule = resolvedOptions.Rule ?? _defaultRule;
var rule = ResolveRule(resolvedOptions);
if (rule.TryFormat(value, resolvedOptions, out var result))
{
@ -113,6 +113,22 @@ public sealed class NumericDisplayFormatter : INumericDisplayFormatter
return resolved;
}
private INumericFormatRule ResolveRule(NumericFormatOptions options)
{
ArgumentNullException.ThrowIfNull(options);
if (options.Rule is not null)
{
return options.Rule;
}
return options.Style switch
{
NumericDisplayStyle.Compact => _defaultRule,
_ => throw new ArgumentOutOfRangeException(nameof(options), options.Style, "不支持的数值显示风格。")
};
}
private static string FormatFallback(object value, IFormatProvider? provider)
{
return value switch

View File

@ -33,7 +33,7 @@ public sealed class NumericSuffixFormatRule : INumericFormatRule
}
/// <summary>
/// 默认国际缩写规则
/// 默认国际缩写规则使用标准的K、M、B、T后缀表示千、百万、十亿、万亿
/// </summary>
public static NumericSuffixFormatRule InternationalCompact { get; } = new(
"compact",
@ -44,10 +44,19 @@ public sealed class NumericSuffixFormatRule : INumericFormatRule
new NumericSuffixThreshold(1_000_000_000_000m, "T")
]);
/// <inheritdoc/>
/// <summary>
/// 获取此格式化规则的名称。
/// </summary>
public string Name { get; }
/// <inheritdoc/>
/// <summary>
/// 尝试将指定的数值按照当前规则进行格式化。
/// </summary>
/// <typeparam name="T">数值的类型</typeparam>
/// <param name="value">要格式化的数值</param>
/// <param name="options">格式化选项,包含小数位数、舍入模式等设置</param>
/// <param name="result">格式化后的字符串结果</param>
/// <returns>如果格式化成功则返回true如果输入无效或格式化失败则返回false</returns>
public bool TryFormat<T>(T value, NumericFormatOptions options, out string result)
{
ArgumentNullException.ThrowIfNull(options);
@ -164,15 +173,13 @@ public sealed class NumericSuffixFormatRule : INumericFormatRule
}
catch (OverflowException)
{
try
var doubleValue = (double)value;
if (TryFormatSpecialFloatingPoint(doubleValue, options.FormatProvider, out result))
{
return TryFormatDouble((double)value, options, out result);
}
catch (OverflowException)
{
result = value.ToString(options.FormatProvider ?? CultureInfo.CurrentCulture);
return true;
}
return TryFormatDouble(doubleValue, options, out result);
}
}