Repository navigation
Parsing and Validation
Enbrea.Bcp47 separates RFC 5646 grammar checking, registry-independent structural validation, and optional IANA registry validation.
This distinction is intentional: a language tag can satisfy the RFC 5646 grammar without every subtag being registered in the IANA Language Subtag Registry.
The most convenient entry point is LanguageTag.Parse():
var tag = LanguageTag.Parse("zh-Hans-CN");The equivalent parser API is:
var tag = Bcp47Parser.Parse("zh-Hans-CN");Both methods require a structurally valid language tag and throw FormatException if the input cannot be parsed.
For input that may be invalid, use TryParse():
if (LanguageTag.TryParse(value, out var tag))
{
Console.WriteLine(tag);
}The equivalent lower-level call is:
if (Bcp47Parser.TryParse(value, out var tag))
{
Console.WriteLine(tag);
}A parsed LanguageTag exposes normalized components:
var tag = LanguageTag.Parse("ZH-cmn-hans-cn-variant-a-aaa-x-private");
Console.WriteLine(tag.Language); // zh
Console.WriteLine(tag.Script); // Hans
Console.WriteLine(tag.Region); // CNCollection-valued components are available through:
tag.Extlangs
tag.Variants
tag.Extensions
tag.PrivateUseFor example:
var tag = LanguageTag.Parse("zh-cmn-Hans-CN-x-example");
foreach (var extlang in tag.Extlangs)
{
Console.WriteLine(extlang);
}
foreach (var privateUse in tag.PrivateUse)
{
Console.WriteLine(privateUse);
}Each extension is represented by LanguageTagExtension:
var tag = LanguageTag.Parse("en-u-ca-gregory");
var extension = tag.Extensions[0];
Console.WriteLine(extension.Singleton); // u
foreach (var subtag in extension.Subtags)
{
Console.WriteLine(subtag);
}LanguageTag.Value preserves the original input exactly:
var tag = LanguageTag.Parse("ZH-hans-cn");
Console.WriteLine(tag.Value); // ZH-hans-cnToString() returns the registry-independent normalized form:
Console.WriteLine(tag.ToString()); // zh-Hans-CNSee Normalization and Equality for the normalization rules.
IsWellFormed() checks the RFC 5646 grammar:
bool result = LanguageTag.IsWellFormed("de-DE");The parser API provides the same operation:
bool result = Bcp47Parser.IsWellFormed("de-DE");Well-formedness intentionally follows the grammar only. RFC 5646 grammar permits up to three extended-language positions, for example:
LanguageTag.IsWellFormed("aaa-bbb-ccc-ddd"); // trueThat does not mean the tag satisfies all registry-independent validity constraints.
IsStructurallyValid() adds registry-independent validity checks on top of the grammar:
LanguageTag.IsStructurallyValid("de-DE"); // trueStructural validation additionally rejects cases such as:
- more than one extlang subtag
- duplicate variant subtags
- duplicate extension singletons
For example:
LanguageTag.IsWellFormed("aaa-bbb-ccc-ddd"); // true
LanguageTag.IsStructurallyValid("aaa-bbb-ccc-ddd"); // false
LanguageTag.IsWellFormed("sl-rozaj-rozaj"); // true
LanguageTag.IsStructurallyValid("sl-rozaj-rozaj"); // false
LanguageTag.IsWellFormed("en-a-aaa-a-bbb"); // true
LanguageTag.IsStructurallyValid("en-a-aaa-a-bbb"); // falseStructural validation does not ask whether a syntactically valid subtag is present in the IANA registry.
For example, a four-letter subtag in script position satisfies the script grammar independently of whether that script is registered:
LanguageTag.IsStructurallyValid("de-Abcd");Use Enbrea.Bcp47.Iana when registry membership matters. See IANA Validation.
A tag can consist entirely of private-use subtags:
var tag = LanguageTag.Parse("x-company-internal");
Console.WriteLine(tag.IsPrivateUseOnly); // true
Console.WriteLine(tag.Language); // nullThe private-use subtags are exposed separately:
foreach (var value in tag.PrivateUse)
{
Console.WriteLine(value);
}RFC 5646 preserves a fixed set of grandfathered language tags.
They are recognized before the regular language-tag grammar is applied:
var tag = LanguageTag.Parse("I-KLINGON");
Console.WriteLine(tag.IsGrandfathered); // true
Console.WriteLine(tag.Value); // I-KLINGON
Console.WriteLine(tag); // i-klingonGrandfathered tags are treated as complete tags and are not decomposed into ordinary language, script, region, and variant components.
Parse() and Normalize() throw:
-
ArgumentNullExceptionfornull -
FormatExceptionfor invalid language-tag syntax or structure
Use the corresponding Try... methods when invalid input is expected.
See also: