Repository navigation
Normalization and Equality
Enbrea.Bcp47 normalizes language tags without consulting the IANA Language Subtag Registry.
This provides deterministic casing and ordering while keeping the core package independent of registry snapshots and Preferred-Value mappings.
Use LanguageTag.Normalize():
var normalized = LanguageTag.Normalize("ZH-hans-cn");
Console.WriteLine(normalized); // zh-Hans-CNThe equivalent parser API is:
var normalized = Bcp47Parser.Normalize("ZH-hans-cn");Use TryNormalize() when invalid input is expected:
if (LanguageTag.TryNormalize(value, out var normalized))
{
Console.WriteLine(normalized);
}Normalization applies the conventional BCP 47 casing by component:
| Component | Normalized form |
|---|---|
| Language | lowercase |
| Extlang | lowercase |
| Script | title case |
| Alphabetic region | uppercase |
| Numeric region | unchanged |
| Variant | lowercase |
| Extension singleton and subtags | lowercase |
| Private use | lowercase |
For example:
var normalized = LanguageTag.Normalize("ZH-CMN-hANS-cn-VARIANT-X-PRIVATE");
Console.WriteLine(normalized); // zh-cmn-Hans-CN-variant-x-privateExtension sequences are normalized into deterministic singleton order.
For example:
var normalized = LanguageTag.Normalize("en-b-bbb-a-aaa");
Console.WriteLine(normalized); // en-a-aaa-b-bbbThis means normalization is not only a casing operation.
Grandfathered tags are normalized to their registered spelling:
var tag = LanguageTag.Parse("I-KLINGON");
Console.WriteLine(tag.Value); // I-KLINGON
Console.WriteLine(tag); // i-klingonThey are not decomposed into ordinary components.
A parsed tag retains both concepts:
var tag = LanguageTag.Parse("DE-de");
Console.WriteLine(tag.Value); // DE-de
Console.WriteLine(tag); // de-DEUse Value when the original input matters. Use ToString() for the normalized representation.
LanguageTag implements IEquatable<LanguageTag> and compares tags by normalized representation.
For example:
var first = LanguageTag.Parse("DE-de");
var second = LanguageTag.Parse("de-DE");
Console.WriteLine(first == second); // true
Console.WriteLine(first.Equals(second)); // trueEqual tags also produce compatible hash codes, so they can be used as keys in dictionaries or values in hash sets.
var tags = new HashSet<LanguageTag>
{
LanguageTag.Parse("DE-de"),
LanguageTag.Parse("de-DE")
};
Console.WriteLine(tags.Count); // 1Core normalization deliberately does not apply registry metadata such as:
Preferred-ValueSuppress-Script- deprecation state
- macrolanguage relationships
For example, if an old registered language subtag has a preferred replacement, LanguageTag.Normalize() does not replace it automatically.
Use Enbrea.Bcp47.Iana to inspect such metadata:
using Enbrea.Bcp47.Iana;
if (IanaLanguageSubtagRegistry.TryGetPreferredValue(IanaLanguageSubtagType.Language, "iw", out var preferred))
{
Console.WriteLine(preferred);
}Keeping normalization and registry metadata separate makes the result of core parsing independent of the bundled registry snapshot.
See IANA Registry and IANA Validation.