/// <summary> /// Creates the symbols for the specified <see cref="Locale"/>. /// </summary> /// <param name="locale"> /// The locale. /// </param> /// <returns> /// The symbols that are the best for the <paramref name="locale"/>. /// </returns> public static NumberSymbols Create(Locale locale) { var ns = NumberingSystem.Create(locale).Id; var path = $"ldml/numbers/symbols[@numberSystem='{ns}']/"; var symbols = new NumberSymbols { Decimal = locale.Find(path + "decimal").Value, Exponential = locale.Find(path + "exponential").Value, Group = locale.Find(path + "group").Value, Infinity = locale.Find(path + "infinity").Value, List = locale.Find(path + "list").Value, MinusSign = locale.Find(path + "minusSign").Value, NotANumber = locale.Find(path + "nan").Value, PercentSign = locale.Find(path + "percentSign").Value, PerMille = locale.Find(path + "perMille").Value, PlusSign = locale.Find(path + "plusSign").Value, SuperscriptingExponent = locale.Find(path + "superscriptingExponent").Value }; var found = locale.FindOrDefault(path + "currencyDecimal"); if (found != null) { symbols.CurrencyDecimal = found.Value; } found = locale.FindOrDefault(path + "currencyGroup"); if (found != null) { symbols.CurrencyGroup = found.Value; } return(symbols); }
static string[] getRowData(LocaleIdentifier locId) { var locStr = locId.ToString(); var loc = new Locale(locId); var values = new string[count]; Func <string, string> normalize = (string str) => { // wrong i if (locStr == "be-Cyrl-BY") { str = str.Replace('i', 'і'); } // convert \u00FF part of CLDR string to char return(decodeUnicodeLiteral(str)); }; // **** CALENDAR XPathNavigator gregorian; gregorian = loc.FindOrDefault("//calendar[@type=\"gregorian\"]//monthContext[@type=\"stand-alone\"]/monthWidth[@type=\"wide\"]"); if (gregorian != null) { gregorian.Select("./*/text()").Cast <object>().Select(o => normalize(o.ToString())).ToArray(values, monthsIdx); } gregorian = loc.FindOrDefault("//calendar[@type=\"gregorian\"]//monthContext[@type=\"format\"]/monthWidth[@type=\"wide\"]"); if (gregorian != null) { gregorian.Select("./*/text()").Cast <object>().Select(o => normalize(o.ToString())).ToArray(values, smonthsIdx); } gregorian = loc.FindOrDefault("//calendar[@type=\"gregorian\"]//dayContext[@type=\"stand-alone\"]/dayWidth[@type=\"wide\"]"); if (gregorian != null && loc.ToString() != "lrc-Arab-IQ" && loc.ToString() != "lrc-Arab-IR" && loc.ToString() != "mzn-Arab-IR") { gregorian.Select("./*/text()").Cast <object>().Select(o => normalize(o.ToString())).ToArray(values, daysIdx); } gregorian = loc.FindOrDefault("//calendar[@type=\"gregorian\"]//dayContext[@type=\"format\"]/dayWidth[@type=\"wide\"]"); if (gregorian != null && loc.ToString() != "lrc-Arab-IQ" && loc.ToString() != "lrc-Arab-IR" && loc.ToString() != "mzn-Arab-IR") { gregorian.Select("./*/text()").Cast <object>().Select(o => normalize(o.ToString())).ToArray(values, sdaysIdx); } // **** SPELL NUMBERS try { var spell = SpellingFormatter.Create(loc, new SpellingOptions { Style = SpellingStyle.Cardinal }); numsSource.Select(n => normalize(spell.Format(n))).ToArray(values, numsIdx); } catch { } try { var spell = SpellingFormatter.Create(loc, new SpellingOptions { Style = SpellingStyle.Ordinal }); numsSource.Select(n => normalize(spell.Format(n))).ToArray(values, snumsIdx); } catch { } return(values); }
static LangMatrixRow fromCldr(Locale loc, string path) { var finds = loc.FindOrDefault(path); if (finds == null) { return(null); } var res = new Dictionary <string, string>(); foreach (var kv in finds.SelectChildren(XPathNodeType.Element).OfType <XPathNavigator>()) { var key = kv.SelectSingleNode("./@type").Value; if (res.ContainsKey(key)) { continue; } var value = kv.SelectSingleNode("./text()").Value; if (UnicodeBlocks.checkBlockNames(value, loc.Id.Script) != null) { continue; } res[key] = value; } return(new LangMatrixRow { lang = loc.Id.ToString(), row = res.Values.ToArray(), columnNames = res.Keys.ToArray(), }); }
void NumberInfo(string currencyCode, int nDigits, out string pattern, out NumberFormatInfo nfi) { nfi = new NumberFormatInfo { NaNSymbol = Symbols.NotANumber, NegativeSign = Symbols.MinusSign, NegativeInfinitySymbol = Symbols.MinusSign + Symbols.Infinity, NumberDecimalSeparator = Symbols.Decimal, NumberGroupSeparator = Symbols.Group, // NativeDigits is NOT used the formatter!! //NativeDigits = NumberingSystem.Digits, PercentDecimalSeparator = Symbols.Decimal, PercentSymbol = Symbols.PercentSign, PerMilleSymbol = Symbols.PerMille, PositiveInfinitySymbol = Symbols.Infinity, PositiveSign = Symbols.PlusSign }; pattern = null; if (Options.Style == NumberStyle.Decimal) { var path = $"ldml/numbers/decimalFormats[@numberSystem='{NumberingSystem.Id}']/decimalFormatLength[not(@type)]/decimalFormat/pattern"; pattern = Locale.Find(path).Value; } else if (Options.Style == NumberStyle.Percent) { var path = $"ldml/numbers/percentFormats[@numberSystem='{NumberingSystem.Id}']/percentFormatLength[not(@type)]/percentFormat/pattern"; pattern = Locale.Find(path).Value; } else if (Options.Style == NumberStyle.Scientific) { var path = $"ldml/numbers/scientificFormats[@numberSystem='{NumberingSystem.Id}']/scientificFormatLength[not(@type)]/scientificFormat/pattern"; pattern = Locale.Find(path).Value; pattern = pattern == "#E0" ? "0.0######E0" : pattern; } else if (Options.Style == NumberStyle.CurrencyStandard || Options.Style == NumberStyle.CurrencyAccounting) { nfi.NumberDecimalSeparator = Symbols.CurrencyDecimal; nfi.NumberGroupSeparator = Symbols.CurrencyGroup; string path = null; if (Options.Style == NumberStyle.CurrencyStandard) { path = $"ldml/numbers/currencyFormats[@numberSystem='{NumberingSystem.Id}']/currencyFormatLength/currencyFormat[@type='standard']/pattern"; } else if (Options.Style == NumberStyle.CurrencyAccounting) { path = $"ldml/numbers/currencyFormats[@numberSystem='{NumberingSystem.Id}']/currencyFormatLength/currencyFormat[@type='accounting']/pattern"; } pattern = Locale.Find(path).Value; // Apply currency decimal places if (currencyCode == null) { currencyCode = Locale.CurrencyCode; } var digits = Cldr.Instance .GetDocuments("common/supplemental/supplementalData.xml") .FirstElementOrDefault($"supplementalData/currencyData/fractions/info[@iso4217='{currencyCode}']/@digits"); if (digits != null) { int significantDigits = Int32.Parse(digits.Value); pattern = significantDigitsPattern.Replace(pattern, "." + new String('0', significantDigits)); } } else { throw new NotImplementedException(); } // Grouping of digits. var useGrouping = Options.UseGrouping; if (useGrouping) { // Grouping digits, "#,##,##0" => [3, 2, 1] var parts = pattern.Split(';'); nfi.NumberGroupSizes = groupingPattern .Matches(parts[0]) .Cast <Match>() .Skip(1) .Reverse() .Select(m => m.Length - 1) .DefaultIfEmpty(3) .ToArray(); // Don't group if min grouping digits is not met. var minElement = Locale.FindOrDefault("ldml/numbers/minimumGroupingDigits"); if (minElement != null) { var minDigits = Int32.Parse(minElement.Value) + nfi.NumberGroupSizes[0]; useGrouping = nDigits > minDigits; } } if (!useGrouping) { nfi.NumberGroupSeparator = ""; } nfi.PercentGroupSizes = nfi.NumberGroupSizes; nfi.PercentGroupSeparator = nfi.NumberGroupSeparator; }
NumberPattern FindPattern(decimal value, NumberLength?nl = null) { if (!nl.HasValue) { nl = Options.Length; } value = Math.Abs(value); // Determine the format length type. var flt = nl == NumberLength.Default ? "[not(@type)]" : $"[@type='{nl.ToString().ToLowerInvariant()}']"; // Determine the path in the ldml to the pattern(s) var path = String.Empty; if (Options.Style == NumberStyle.Decimal) { path = $"ldml/numbers/decimalFormats[@numberSystem='{NumberingSystem.Id}']/decimalFormatLength{flt}/decimalFormat"; } else if (Options.Style == NumberStyle.Percent) { path = $"ldml/numbers/percentFormats[@numberSystem='{NumberingSystem.Id}']/percentFormatLength{flt}/percentFormat"; } else if (Options.Style == NumberStyle.Scientific) { path = $"ldml/numbers/scientificFormats[@numberSystem='{NumberingSystem.Id}']/scientificFormatLength{flt}/scientificFormat"; } else if (Options.Style == NumberStyle.CurrencyStandard) { path = $"ldml/numbers/currencyFormats[@numberSystem='{NumberingSystem.Id}']/currencyFormatLength{flt}/currencyFormat[@type='standard']"; } else if (Options.Style == NumberStyle.CurrencyAccounting) { path = $"ldml/numbers/currencyFormats[@numberSystem='{NumberingSystem.Id}']/currencyFormatLength{flt}/currencyFormat[@type='accounting']"; } else { throw new NotImplementedException($"Unknown NumberStyle '{Options.Style}'."); } var xml = Locale.FindOrDefault(path); // Fall back to default number length; if (xml == null && nl != NumberLength.Default) { return(FindPattern(value, NumberLength.Default)); } // Should not happen. if (xml == null) { throw new KeyNotFoundException($"Cannot find CLDR '{path}'."); } // Get the best pattern. var category = Plural.Create(Locale).Category(value); NumberPattern best = null; NumberPattern previous = null; var pattern = xml.SelectChildren("pattern", ""); while (pattern.MoveNext()) { var p = NumberPattern.Parse(pattern.Current); // If not a value range, then this is the best pattern. if (!p.MinValue.HasValue) { best = p; break; } // Only consider a pattern with the correct count. if (p.Count != category) { continue; } // Get closest value in the range. if (p.MinValue.Value > value) { best = previous; break; } previous = p; } best = best ?? previous ?? new NumberPattern { FormatString = "0" }; return(best); }
NumberFormatInfo NumberInfo(string currencyCode, int nDigits, NumberPattern pattern) { var nfi = new NumberFormatInfo { NaNSymbol = Symbols.NotANumber, NegativeSign = Symbols.MinusSign, NegativeInfinitySymbol = Symbols.MinusSign + Symbols.Infinity, NumberDecimalSeparator = Symbols.Decimal, NumberGroupSeparator = Symbols.Group, // NativeDigits is NOT by used the C# formatter!! //NativeDigits = NumberingSystem.Digits, PercentDecimalSeparator = Symbols.Decimal, PercentSymbol = Symbols.PercentSign, PerMilleSymbol = Symbols.PerMille, PositiveInfinitySymbol = Symbols.Infinity, PositiveSign = Symbols.PlusSign }; if (Options.Style == NumberStyle.Scientific && pattern.FormatString == "#E0") { pattern.FormatString = "0.0######E0"; } else if (Options.Style == NumberStyle.CurrencyStandard || Options.Style == NumberStyle.CurrencyAccounting) { nfi.NumberDecimalSeparator = Symbols.CurrencyDecimal; nfi.NumberGroupSeparator = Symbols.CurrencyGroup; // Apply currency decimal places if (currencyCode == null) { currencyCode = Locale.CurrencyCode; } var digits = Cldr.Instance .GetDocuments("common/supplemental/supplementalData.xml") .FirstElementOrDefault($"supplementalData/currencyData/fractions/info[@iso4217='{currencyCode}']/@digits"); if (digits != null) { int significantDigits = Int32.Parse(digits.Value); pattern.FormatString = significantDigitsPattern.Replace(pattern.FormatString, "." + new String('0', significantDigits)); } } // Grouping of digits. var useGrouping = Options.UseGrouping; if (useGrouping) { // Grouping digits, "#,##,##0" => [3, 2, 1] var parts = pattern.FormatString.Split(';'); nfi.NumberGroupSizes = groupingPattern .Matches(parts[0]) .Cast <Match>() .Skip(1) .Reverse() .Select(m => m.Length - 1) .DefaultIfEmpty(3) .ToArray(); // Don't group if min grouping digits is not met. var minElement = Locale.FindOrDefault("ldml/numbers/minimumGroupingDigits"); if (minElement != null) { var minDigits = Int32.Parse(minElement.Value) + nfi.NumberGroupSizes[0]; useGrouping = nDigits > minDigits; } } if (!useGrouping) { nfi.NumberGroupSeparator = ""; } nfi.PercentGroupSizes = nfi.NumberGroupSizes; nfi.PercentGroupSeparator = nfi.NumberGroupSeparator; return(nfi); }