public void Parse_NotAPattern() { var locale = Locale.Create("en"); var path = $"ldml/numbers/decimalFormats[@numberSystem='latn']/decimalFormatLength[@type='long']/decimalFormat"; ExceptionAssert.Throws <Exception>(() => NumberPattern.Parse(locale.Find(path))); }
public void Adjust_Simple() { var pattern = new NumberPattern { FormatString = "#,##0" }; Assert.AreEqual(12345, pattern.Adjust(12345)); }
public void Parse_Simple() { var locale = Locale.Create("en"); var path = $"ldml/numbers/decimalFormats[@numberSystem='latn']/decimalFormatLength[not(@type)]/decimalFormat/pattern"; var pattern = NumberPattern.Parse(locale.Find(path)); Assert.AreEqual("", pattern.Count); Assert.IsFalse(pattern.MinValue.HasValue); Assert.AreEqual("#,##0.###", pattern.FormatString); }
public void Adjust_ValueRange() { var pattern = new NumberPattern { MinValue = 10000, Count = "other", FormatString = "00 K" }; Assert.AreEqual(12, pattern.Adjust(12345)); }
public void Parse_ValueRange() { var locale = Locale.Create("en"); var path = $"ldml/numbers/decimalFormats[@numberSystem='latn']/decimalFormatLength[@type='long']/decimalFormat/pattern"; var pattern = NumberPattern.Parse(locale.Find(path)); Assert.AreEqual("one", pattern.Count); Assert.IsTrue(pattern.MinValue.HasValue); Assert.AreEqual(1000L, pattern.MinValue.Value); Assert.AreEqual("0 thousand", pattern.FormatString); }
/// <summary> /// Create a number pattern from the specified <see cref="XPathNavigator"/>. /// </summary> /// <param name="xml"> /// The XML representation of a number pattern. /// </param> /// <returns> /// A new number pattern, /// </returns> /// <remarks> /// The <paramref name="xml"/> must be on a "pattern" element. /// </remarks> public static NumberPattern Parse(XPathNavigator xml) { if (xml.LocalName != "pattern") { throw new Exception($"Expected a 'pattern' element, not '{xml.LocalName}'."); } var pattern = new NumberPattern { Count = xml.GetAttribute("count", ""), FormatString = xml.Value }; var s = xml.GetAttribute("type", ""); if (s != String.Empty) { pattern.MinValue = decimal.Parse(s, CultureInfo.InvariantCulture); } return(pattern); }
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); }