/// <summary> /// BNF in RFC5464 /// </summary> /// <remarks> /// Language-Tag = langtag ; normal language tags /// / privateuse ; private use tag /// / grandfathered ; grandfathered tags /// /// /// langtag = language /// ["-" script] /// ["-" region] /// *("-" variant) /// *("-" extension) /// ["-" privateuse] /// /// language = 2*3ALPHA ; shortest ISO 639 code /// ["-" extlang] ; sometimes followed by /// ; extended language subtags /// / 4ALPHA ; or reserved for future use /// / 5*8ALPHA ; or registered language subtag /// /// extlang = 3ALPHA ; selected ISO 639 codes /// *2("-" 3ALPHA) ; permanently reserved /// /// script = 4ALPHA ; ISO 15924 code /// /// region = 2ALPHA ; ISO 3166-1 code /// / 3DIGIT ; UN M.49 code /// /// variant = 5*8alphanum ; registered variants /// / (DIGIT 3alphanum) /// /// extension = singleton 1*("-" (2*8alphanum)) /// /// ; Single alphanumerics /// ; "x" reserved for private use /// singleton = DIGIT ; 0 - 9 /// / %x41-57 ; A - W /// / %x59-5A ; Y - Z /// / %x61-77 ; a - w /// / %x79-7A ; y - z /// /// privateuse = "x" 1*("-" (1*8alphanum)) /// </remarks> public static LanguageTag Parse(string languageTag, ParseStatus sts) { if (sts == null) { sts = new ParseStatus(); } else { sts.Reset(); } StringTokenEnumerator itr; bool isGrandfathered = false; // Check if the tag is grandfathered string[] gfmap; if (GRANDFATHERED.TryGetValue(new AsciiUtil.CaseInsensitiveKey(languageTag), out gfmap) && gfmap != null) { // use preferred mapping itr = new StringTokenEnumerator(gfmap[1], SEP); isGrandfathered = true; } else { itr = new StringTokenEnumerator(languageTag, SEP); } // ICU4N: Move to the first element itr.MoveNext(); LanguageTag tag = new LanguageTag(); // langtag must start with either language or privateuse if (tag.ParseLanguage(itr, sts)) { tag.ParseExtlangs(itr, sts); tag.ParseScript(itr, sts); tag.ParseRegion(itr, sts); tag.ParseVariants(itr, sts); tag.ParseExtensions(itr, sts); } tag.ParsePrivateuse(itr, sts); if (isGrandfathered) { // Grandfathered tag is replaced with a well-formed tag above. // However, the parsed length must be the original tag length. Debug.Assert(itr.IsDone); Debug.Assert(!sts.IsError); sts.ParseLength = languageTag.Length; } else if (!itr.IsDone && !sts.IsError) { string s = itr.Current; sts.ErrorIndex = itr.CurrentStart; if (s.Length == 0) { sts.ErrorMessage = "Empty subtag"; } else { sts.ErrorMessage = "Invalid subtag: " + s; } } return(tag); }