Esempio n. 1
0
		private static string GetImplicitScriptCode(LanguageSubtag languageSubtag, RegionSubtag regionSubtag)
		{
			if (languageSubtag == null || languageSubtag.IsPrivateUse)
				return null;

			string regionCode = null;
			if (regionSubtag != null && !regionSubtag.IsPrivateUse)
				regionCode = regionSubtag;
			return GetImplicitScriptCode(languageSubtag.Code, regionCode);
		}
Esempio n. 2
0
		/// <summary>
		/// Gets the subtags of the specified language tag.
		/// </summary>
		/// <param name="langTag">The language tag.</param>
		/// <param name="languageSubtag">The language subtag.</param>
		/// <param name="scriptSubtag">The script subtag.</param>
		/// <param name="regionSubtag">The region subtag.</param>
		/// <param name="variantSubtags">The variant and private-use subtags.</param>
		/// <returns></returns>
		public static bool TryGetSubtags(string langTag, out LanguageSubtag languageSubtag, out ScriptSubtag scriptSubtag,
			out RegionSubtag regionSubtag, out IEnumerable<VariantSubtag> variantSubtags)
		{
			if (langTag == null)
				throw new ArgumentNullException("langTag");

			languageSubtag = null;
			scriptSubtag = null;
			regionSubtag = null;
			variantSubtags = null;

			Match match = LangTagPattern.Match(langTag);
			if (!match.Success)
				return false;

			var privateUseCodes = new List<string>();
			Group privateUseGroup = match.Groups["privateuse"];
			if (privateUseGroup.Success)
				privateUseCodes.AddRange(privateUseGroup.Value.Split(new[] {'-'}, StringSplitOptions.RemoveEmptyEntries).Skip(1));

			Group languageGroup = match.Groups["language"];
			if (languageGroup.Success)
			{
				string languageCode = languageGroup.Value;
				if (languageCode.Equals(WellKnownSubtags.UnlistedLanguage, StringComparison.OrdinalIgnoreCase))
				{
					// In our own WS dialog, we don't allow no language, but if it isn't a standard one, a language like xkal
					// produces an identifier like qaa-x-kal, and we interepret the first thing after the x as a private
					// language code (not allowed as the first three characters according to the standard).
					// If it's NOT a valid language code (e.g., too many characters), probably came from some other
					// program. Treating it as a language code will fail if we try to create such a writing system,
					// since we will detect the invalid language code. So only interpret the first element
					// after the x as a language code if it is a valid one. Otherwise, we just let qaa be the language.
					if (privateUseCodes.Count > 0 && LangPattern.IsMatch(privateUseCodes[0])
						&& !StandardSubtags.CommonPrivateUseVariants.Contains(privateUseCodes[0]))
					{
						languageSubtag = new LanguageSubtag(privateUseCodes[0]);
						privateUseCodes.RemoveAt(0);
					}
					else
					{
						languageSubtag = WellKnownSubtags.UnlistedLanguage; // We do allow just plain qaa.
					}
				}
				else
				{
					if (!StandardSubtags.IsValidIso639LanguageCode(languageCode))
						return false;

					languageSubtag = languageCode;
				}
			}

			Group scriptGroup = match.Groups["script"];
			if (scriptGroup.Success)
			{
				string scriptCode = scriptGroup.Value;
				if (scriptCode.Equals("Qaaa", StringComparison.OrdinalIgnoreCase) && privateUseCodes.Count > 0
					&& ScriptPattern.IsMatch(privateUseCodes[0]))
				{

					scriptSubtag = new ScriptSubtag(privateUseCodes[0]);
					privateUseCodes.RemoveAt(0);
				}
				else
				{
					if (!StandardSubtags.IsValidIso15924ScriptCode(scriptCode))
						return false;

					scriptSubtag = scriptCode;
				}
			}

			Group regionGroup = match.Groups["region"];
			if (regionGroup.Success)
			{
				string regionCode = regionGroup.Value;
				if (regionCode.Equals("QM", StringComparison.OrdinalIgnoreCase) && privateUseCodes.Count > 0
					&& RegionPattern.IsMatch(privateUseCodes[0]))
				{
					regionSubtag = new RegionSubtag(privateUseCodes[0]);
					privateUseCodes.RemoveAt(0);
				}
				else
				{
					if (!StandardSubtags.IsValidIso3166RegionCode(regionCode))
						return false;

					regionSubtag = regionCode;
				}
			}

			if (scriptSubtag == null)
				scriptSubtag = GetImplicitScriptCode(languageSubtag, regionSubtag);

			var variants = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
			var variantSubtagsList = new List<VariantSubtag>();
			Group variantGroup = match.Groups["variant"];
			if (variantGroup.Success)
			{
				foreach (string variantCode in variantGroup.Value.Split(new[] {'-'}, StringSplitOptions.RemoveEmptyEntries))
				{
					if (variants.Contains(variantCode))
						return false;
					VariantSubtag variantSubtag;
					if (!StandardSubtags.RegisteredVariants.TryGet(variantCode, out variantSubtag))
						return false;
					variantSubtagsList.Add(variantSubtag);
					variants.Add(variantCode);
				}
			}

			variants.Clear();
			foreach (string privateUseCode in privateUseCodes)
			{
				if (variants.Contains(privateUseCode) || privateUseCode.Equals("x", StringComparison.InvariantCultureIgnoreCase))
					return false;
				VariantSubtag variantSubtag;
				if (!StandardSubtags.CommonPrivateUseVariants.TryGet(privateUseCode, out variantSubtag))
					variantSubtag = new VariantSubtag(privateUseCode);
				variantSubtagsList.Add(variantSubtag);
				variants.Add(privateUseCode);
			}
			variantSubtags = variantSubtagsList;
			return true;
		}
Esempio n. 3
0
		private static bool TryCreate(LanguageSubtag languageSubtag, ScriptSubtag scriptSubtag, RegionSubtag regionSubtag, IEnumerable<VariantSubtag> variantSubtags,
			out string langTag, out string message, out string paramName)
		{
			message = null;
			paramName = null;
			VariantSubtag[] variantSubtagsArray = variantSubtags.ToArray();
			if (languageSubtag == null && (scriptSubtag != null || regionSubtag != null || variantSubtagsArray.Length == 0 || variantSubtagsArray.Any(v => !v.IsPrivateUse)))
			{
				message = "A language subtag is required.";
				paramName = "languageSubtag";
			}

			var sb = new StringBuilder();

			bool isCustomLanguage = false;
			if (languageSubtag != null)
			{
				// Insert non-custom language, script, region into main part of code.
				if (languageSubtag.IsPrivateUse && languageSubtag.Code != WellKnownSubtags.UnlistedLanguage)
				{
					if (!LangPattern.IsMatch(languageSubtag.Code))
					{
						message = "The private use language code is invalid.";
						paramName = "languageSubtag";
					}
					sb.Append("qaa");
					isCustomLanguage = true;
				}
				else
				{
					sb.Append(languageSubtag.Code);
				}
			}

			bool isCustomScript = false;
			if (scriptSubtag != null && GetImplicitScriptCode(languageSubtag, regionSubtag) != scriptSubtag.Code)
			{
				if (sb.Length > 0)
					sb.Append("-");
				// Qaaa is our flag to expect a script in private-use. If the actual value is Qaaa, we need to treat it as custom,
				// so we don't confuse some other private-use tag with a custom script.
				if (scriptSubtag.IsPrivateUse && !StandardSubtags.IsPrivateUseScriptCode(scriptSubtag.Code))
				{
					if (message == null && !ScriptPattern.IsMatch(scriptSubtag.Code))
					{
						message = "The private use script code is invalid.";
						paramName = "scriptSubtag";
					}
					sb.Append("Qaaa");
					isCustomScript = true;
				}
				else
				{
					sb.Append(scriptSubtag.Code);
				}
			}

			bool isCustomRegion = false;
			if (regionSubtag != null)
			{
				if (sb.Length > 0)
					sb.Append("-");
				// QM is our flag to expect a region in private-use. If the actual value is QM, we need to treat it as custom,
				// so we don't confuse some other private-use tag with a custom region.
				if (regionSubtag.IsPrivateUse && !StandardSubtags.IsPrivateUseRegionCode(regionSubtag.Code))
				{
					if (message == null && !RegionPattern.IsMatch(regionSubtag.Code))
					{
						message = "The private use region code is invalid.";
						paramName = "regionSubtag";
					}
					sb.Append("QM");
					isCustomRegion = true;
				}
				else
				{
					sb.Append(regionSubtag.Code);
				}
			}

			var variants = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
			foreach (VariantSubtag variantSubtag in variantSubtagsArray.Where(vs => !vs.IsPrivateUse))
			{
				if (message == null && variants.Contains(variantSubtag.Code))
				{
					message = "Duplicate variants are not allowed.";
					paramName = "variantSubtags";
				}
				if (sb.Length > 0)
					sb.Append("-");
				sb.Append(variantSubtag.Code);
				variants.Add(variantSubtag.Code);
			}

			// Insert custom language, script, or variant into private=use.
			bool inPrivateUse = false;
			if (isCustomLanguage)
			{
				inPrivateUse = true;
				if (sb.Length > 0)
					sb.Append("-");
				sb.Append("x-");
				sb.Append(languageSubtag.Code);
			}

			if (isCustomScript)
			{
				if (sb.Length > 0)
					sb.Append("-");
				if (!inPrivateUse)
				{
					inPrivateUse = true;
					sb.Append("x-");
				}
				sb.Append(scriptSubtag.Code);
			}

			if (isCustomRegion)
			{
				if (sb.Length > 0)
					sb.Append("-");
				if (!inPrivateUse)
				{
					inPrivateUse = true;
					sb.Append("x-");
				}
				sb.Append(regionSubtag.Code);
			}

			variants.Clear();
			foreach (VariantSubtag variantSubtag in variantSubtagsArray.Where(vs => vs.IsPrivateUse))
			{
				if (message == null && !PrivateUsePattern.IsMatch(variantSubtag.Code))
				{
					message = "The private use subtags contains an invalid subtag.";
					paramName = "variantSubtags";
				}
				if (message == null && variants.Contains(variantSubtag.Code))
				{
					message = "Duplicate private use subtags are not allowed.";
					paramName = "variantSubtags";
				}

				if (sb.Length > 0)
					sb.Append("-");
				if (!inPrivateUse)
				{
					inPrivateUse = true;
					sb.Append("x-");
				}
				sb.Append(variantSubtag.Code);
				variants.Add(variantSubtag.Code);
			}

			langTag = sb.ToString();
			return message == null;
		}
Esempio n. 4
0
		/// <summary>
		/// Validates the creation of an IETF language tag.
		/// </summary>
		public static bool Validate(LanguageSubtag languageSubtag, ScriptSubtag scriptSubtag, RegionSubtag regionSubtag, IEnumerable<VariantSubtag> variantSubtags, out string message)
		{
			string langTag, paramName;
			return TryCreate(languageSubtag, scriptSubtag, regionSubtag, variantSubtags, out langTag, out message, out paramName);
		}
Esempio n. 5
0
		/// <summary>
		/// Creates a language tag from the specified subtags.
		/// </summary>
		public static string Create(LanguageSubtag languageSubtag, ScriptSubtag scriptSubtag, RegionSubtag regionSubtag, IEnumerable<VariantSubtag> variantSubtags,
			bool validate = true)
		{
			string langTag, message, paramName;
			if (!TryCreate(languageSubtag, scriptSubtag, regionSubtag, variantSubtags, out langTag, out message, out paramName))
			{
				if (validate)
					throw new ArgumentException(message, paramName);
			}

			return langTag;
		}
Esempio n. 6
0
		/// <summary>
		/// Tries to create a language tag from the specified subtags.
		/// </summary>
		public static bool TryCreate(LanguageSubtag languageSubtag, ScriptSubtag scriptSubtag, RegionSubtag regionSubtag, IEnumerable<VariantSubtag> variantSubtags,
			out string langTag)
		{
			string message, paramName;
			if (!TryCreate(languageSubtag, scriptSubtag, regionSubtag, variantSubtags, out langTag, out message, out paramName))
			{
				langTag = null;
				return false;
			}
			return true;
		}