From 9c6df8eb43eda63400dfbf74176d137d60dd4b45 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Wed, 12 Aug 2026 17:42:30 +0200 Subject: [PATCH 01/14] Implement formatMoney template function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Format an amount together with its currency code, so a template can show the price a customer actually paid without the author hard-coding a currency sign. ${ formatMoney(Order.CustomField.Total, Order.CustomField.Currency) } Two overloads by arity, the way substring is done: the two-argument form uses the narrow symbol, the three-argument form takes a display mode of narrowSymbol, symbol or code. An unsupported constant mode is a static template error; a mode arriving through the model falls back to narrowSymbol rather than failing the render. Symbols, symbol placement, digit grouping and decimal places come from a table rather than from RenderSettings.CultureInfo. The currency is a property of the order while the culture is a property of the project, so deriving the format from the render culture is what makes a shop's emails show the wrong money today. .NET also exposes no way to map an ISO 4217 code to a symbol without enumerating cultures, which is neither stable across ICU versions nor available under invariant globalization. Grouping follows the currency, since it is the only signal about the reader: $1,234.56 but 1.234,56 € and 12,34,567.89 for the Indian lakh grouping. Separators are plain spaces, not U+00A0, because the same engine renders SMS and a non-breaking space forces UCS-2, halving the segment length. An unknown or absent currency code renders the amount followed by the code as written, so an unmapped currency degrades instead of taking the whole message down. Co-Authored-By: Claude Fable 5 --- Engine/Directory.Build.props | 2 +- .../Standard/Money/CurrencyFormat.cs | 43 ++++ .../Standard/Money/CurrencyFormats.cs | 79 ++++++++ .../Standard/Money/CurrencyNumberStyle.cs | 25 +++ .../Money/FormatMoneyTemplateFunction.cs | 34 ++++ ...matMoneyWithDisplayModeTemplateFunction.cs | 45 +++++ .../Standard/Money/MoneyFormatter.cs | 80 ++++++++ Engine/Quokka.Core/Template.cs | 2 + .../FormatMoneyTemplateFunctionTests.cs | 185 ++++++++++++++++++ 9 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberStyle.cs create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyWithDisplayModeTemplateFunction.cs create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs create mode 100644 Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs diff --git a/Engine/Directory.Build.props b/Engine/Directory.Build.props index b0e18f5..ac66d0e 100644 --- a/Engine/Directory.Build.props +++ b/Engine/Directory.Build.props @@ -16,6 +16,6 @@ - 8.3.0 + 8.4.0 diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs new file mode 100644 index 0000000..5940703 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs @@ -0,0 +1,43 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +namespace Mindbox.Quokka +{ + internal sealed class CurrencyFormat + { + public CurrencyFormat( + string narrowSymbol, + string symbol, + int decimalPlaces, + CurrencyNumberStyle numberStyle = CurrencyNumberStyle.CommaGroupDotDecimal, + bool symbolFollowsAmount = false) + { + NarrowSymbol = narrowSymbol; + Symbol = symbol; + DecimalPlaces = decimalPlaces; + NumberStyle = numberStyle; + SymbolFollowsAmount = symbolFollowsAmount; + } + + public string NarrowSymbol { get; } + + public string Symbol { get; } + + public int DecimalPlaces { get; } + + public CurrencyNumberStyle NumberStyle { get; } + + public bool SymbolFollowsAmount { get; } + } +} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs new file mode 100644 index 0000000..cb5d7e4 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs @@ -0,0 +1,79 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +using System; +using System.Collections.Generic; + +namespace Mindbox.Quokka +{ + internal static class CurrencyFormats + { + private const CurrencyNumberStyle DotGroup = CurrencyNumberStyle.DotGroupCommaDecimal; + private const CurrencyNumberStyle SpaceGroup = CurrencyNumberStyle.SpaceGroupCommaDecimal; + private const CurrencyNumberStyle Apostrophe = CurrencyNumberStyle.ApostropheGroupDotDecimal; + private const CurrencyNumberStyle Indian = CurrencyNumberStyle.IndianGroupDotDecimal; + + private static readonly IReadOnlyDictionary formatsByCode = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["AED"] = new("AED", "AED", 2), + ["ARS"] = new("$", "ARS", 2, DotGroup), + ["AUD"] = new("$", "A$", 2), + ["BHD"] = new("BHD", "BHD", 3), + ["BRL"] = new("R$", "R$", 2, DotGroup), + ["CAD"] = new("$", "CA$", 2), + ["CHF"] = new("CHF", "CHF", 2, Apostrophe), + ["CLP"] = new("$", "CLP", 0, DotGroup), + ["CNY"] = new("¥", "CN¥", 2), + ["COP"] = new("$", "COP", 0, DotGroup), + ["CZK"] = new("Kč", "CZK", 2, SpaceGroup, symbolFollowsAmount: true), + ["DKK"] = new("kr", "DKK", 2, DotGroup, symbolFollowsAmount: true), + ["EUR"] = new("€", "€", 2, DotGroup, symbolFollowsAmount: true), + ["GBP"] = new("£", "£", 2), + ["HKD"] = new("$", "HK$", 2), + ["HUF"] = new("Ft", "HUF", 0, SpaceGroup, symbolFollowsAmount: true), + ["IDR"] = new("Rp", "IDR", 0, DotGroup), + ["ILS"] = new("₪", "₪", 2), + ["INR"] = new("₹", "₹", 2, Indian), + ["ISK"] = new("kr", "ISK", 0, DotGroup, symbolFollowsAmount: true), + ["JOD"] = new("JOD", "JOD", 3), + ["JPY"] = new("¥", "JP¥", 0), + ["KRW"] = new("₩", "₩", 0), + ["KWD"] = new("KWD", "KWD", 3), + ["MXN"] = new("$", "MX$", 2), + ["MYR"] = new("RM", "MYR", 2), + ["NOK"] = new("kr", "NOK", 2, SpaceGroup, symbolFollowsAmount: true), + ["NZD"] = new("$", "NZ$", 2), + ["OMR"] = new("OMR", "OMR", 3), + ["PHP"] = new("₱", "₱", 2), + ["PLN"] = new("zł", "PLN", 2, SpaceGroup, symbolFollowsAmount: true), + ["RUB"] = new("₽", "RUB", 2, SpaceGroup, symbolFollowsAmount: true), + ["SAR"] = new("SAR", "SAR", 2), + ["SEK"] = new("kr", "SEK", 2, SpaceGroup, symbolFollowsAmount: true), + ["SGD"] = new("$", "S$", 2), + ["THB"] = new("฿", "THB", 2), + ["TRY"] = new("₺", "TRY", 2, DotGroup), + ["TWD"] = new("$", "NT$", 2), + ["UAH"] = new("₴", "UAH", 2, SpaceGroup, symbolFollowsAmount: true), + ["USD"] = new("$", "US$", 2), + ["VND"] = new("₫", "₫", 0, DotGroup, symbolFollowsAmount: true), + ["ZAR"] = new("R", "ZAR", 2, SpaceGroup) + }; + + public static CurrencyFormat TryGet(string currencyCode) => + currencyCode != null && formatsByCode.TryGetValue(currencyCode, out var format) + ? format + : null; + } +} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberStyle.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberStyle.cs new file mode 100644 index 0000000..116c700 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberStyle.cs @@ -0,0 +1,25 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +namespace Mindbox.Quokka +{ + internal enum CurrencyNumberStyle + { + CommaGroupDotDecimal, + DotGroupCommaDecimal, + SpaceGroupCommaDecimal, + ApostropheGroupDotDecimal, + IndianGroupDotDecimal + } +} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs new file mode 100644 index 0000000..b37bb01 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs @@ -0,0 +1,34 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +using Mindbox.Quokka.Abstractions; + +namespace Mindbox.Quokka +{ + internal class FormatMoneyTemplateFunction : ScalarTemplateFunction + { + public FormatMoneyTemplateFunction() + : base( + "formatMoney", + new DecimalFunctionArgument("amount"), + new StringFunctionArgument("currencyCode", allowsNull: true)) + { + } + + public override string Invoke(RenderSettings settings, decimal amount, string currencyCode) + { + return MoneyFormatter.Format(amount, currencyCode, MoneyFormatter.NarrowSymbolDisplayMode); + } + } +} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyWithDisplayModeTemplateFunction.cs b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyWithDisplayModeTemplateFunction.cs new file mode 100644 index 0000000..e736df2 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyWithDisplayModeTemplateFunction.cs @@ -0,0 +1,45 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +using Mindbox.Quokka.Abstractions; + +namespace Mindbox.Quokka +{ + internal class FormatMoneyWithDisplayModeTemplateFunction : ScalarTemplateFunction + { + public FormatMoneyWithDisplayModeTemplateFunction() + : base( + "formatMoney", + new DecimalFunctionArgument("amount"), + new StringFunctionArgument("currencyCode", allowsNull: true), + new StringFunctionArgument("displayMode", valueValidator: ValidateDisplayMode)) + { + } + + public override string Invoke(RenderSettings settings, decimal amount, string currencyCode, string displayMode) + { + return MoneyFormatter.Format(amount, currencyCode, displayMode); + } + + private static ArgumentValueValidationResult ValidateDisplayMode(string displayMode) + { + return MoneyFormatter.IsSupportedDisplayMode(displayMode) + ? ArgumentValueValidationResult.Valid + : new ArgumentValueValidationResult( + false, + $"Display mode should be one of: {MoneyFormatter.NarrowSymbolDisplayMode}, " + + $"{MoneyFormatter.SymbolDisplayMode}, {MoneyFormatter.CodeDisplayMode}"); + } + } +} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs b/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs new file mode 100644 index 0000000..52aaab0 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs @@ -0,0 +1,80 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Mindbox.Quokka +{ + internal static class MoneyFormatter + { + public const string NarrowSymbolDisplayMode = "narrowSymbol"; + public const string SymbolDisplayMode = "symbol"; + public const string CodeDisplayMode = "code"; + + private const string MinusSign = "-"; + private const string Space = " "; + + private static readonly IReadOnlyDictionary numberFormatsByStyle = + new Dictionary + { + [CurrencyNumberStyle.CommaGroupDotDecimal] = CreateNumberFormat(",", ".", [3]), + [CurrencyNumberStyle.DotGroupCommaDecimal] = CreateNumberFormat(".", ",", [3]), + [CurrencyNumberStyle.SpaceGroupCommaDecimal] = CreateNumberFormat(Space, ",", [3]), + [CurrencyNumberStyle.ApostropheGroupDotDecimal] = CreateNumberFormat("'", ".", [3]), + [CurrencyNumberStyle.IndianGroupDotDecimal] = CreateNumberFormat(",", ".", [3, 2]) + }; + + public static bool IsSupportedDisplayMode(string displayMode) => + displayMode is NarrowSymbolDisplayMode or SymbolDisplayMode or CodeDisplayMode; + + public static string Format(decimal amount, string currencyCode, string displayMode) + { + var trimmedCode = currencyCode?.Trim(); + var format = CurrencyFormats.TryGet(trimmedCode); + var numberFormat = numberFormatsByStyle[format?.NumberStyle ?? CurrencyNumberStyle.CommaGroupDotDecimal]; + var formattedAmount = Math.Abs(amount).ToString("N" + (format?.DecimalPlaces ?? 2), numberFormat); + var sign = amount < 0 ? MinusSign : string.Empty; + + if (format == null) + return string.IsNullOrEmpty(trimmedCode) + ? sign + formattedAmount + : sign + formattedAmount + Space + trimmedCode.ToUpperInvariant(); + + if (displayMode == CodeDisplayMode) + return sign + formattedAmount + Space + trimmedCode.ToUpperInvariant(); + + var symbol = displayMode == SymbolDisplayMode ? format.Symbol : format.NarrowSymbol; + + if (format.SymbolFollowsAmount) + return sign + formattedAmount + Space + symbol; + + var symbolSeparator = char.IsLetter(symbol[symbol.Length - 1]) ? Space : string.Empty; + + return sign + symbol + symbolSeparator + formattedAmount; + } + + private static NumberFormatInfo CreateNumberFormat( + string groupSeparator, + string decimalSeparator, + int[] groupSizes) => + new() + { + NumberGroupSeparator = groupSeparator, + NumberDecimalSeparator = decimalSeparator, + NumberGroupSizes = groupSizes + }; + } +} diff --git a/Engine/Quokka.Core/Template.cs b/Engine/Quokka.Core/Template.cs index 04d3e05..fe117f9 100644 --- a/Engine/Quokka.Core/Template.cs +++ b/Engine/Quokka.Core/Template.cs @@ -217,6 +217,8 @@ internal static IEnumerable GetStandardFunctions() yield return new ToLowerTemplateFunction(); yield return new ReplaceIfEmptyTemplateFunction(); yield return new FormatDecimalTemplateFunction(); + yield return new FormatMoneyTemplateFunction(); + yield return new FormatMoneyWithDisplayModeTemplateFunction(); yield return new FormatDateTimeTemplateFunction(); yield return new FormatTimeTemplateFunction(); yield return new IfTemplateFunction(); diff --git a/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs new file mode 100644 index 0000000..fdf38e0 --- /dev/null +++ b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs @@ -0,0 +1,185 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +using System.Globalization; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Mindbox.Quokka.Abstractions; + +namespace Mindbox.Quokka.Tests +{ + [TestClass] + public class FormatMoneyTemplateFunctionTests + { + [TestMethod] + [DataRow("USD", "$1,234,567.89")] + [DataRow("GBP", "£1,234,567.89")] + [DataRow("SGD", "$1,234,567.89")] + [DataRow("RUB", "1 234 567,89 ₽")] + [DataRow("PLN", "1 234 567,89 zł")] + [DataRow("EUR", "1.234.567,89 €")] + [DataRow("BRL", "R$1.234.567,89")] + [DataRow("CHF", "CHF 1'234'567.89")] + [DataRow("INR", "₹12,34,567.89")] + public void FormatMoney_GroupsDigitsTheWayTheCurrencyDoes(string currencyCode, string expected) + { + Assert.AreEqual(expected, RenderMoney("1234567.89", currencyCode)); + } + + [TestMethod] + [DataRow("JPY", "¥9,072")] + [DataRow("KRW", "₩9,072")] + [DataRow("VND", "9.072 ₫")] + [DataRow("HUF", "9 072 Ft")] + [DataRow("IDR", "Rp 9.072")] + public void FormatMoney_WithZeroDecimalCurrency_DropsDecimals(string currencyCode, string expected) + { + Assert.AreEqual(expected, RenderMoney("9072.00", currencyCode)); + } + + [TestMethod] + public void FormatMoney_WithThreeDecimalCurrency_KeepsThreeDecimals() + { + Assert.AreEqual("KWD 1,234.560", RenderMoney("1234.56", "KWD")); + } + + [TestMethod] + [DataRow("USD", "-$1,234.56")] + [DataRow("RUB", "-1 234,56 ₽")] + [DataRow("KWD", "-KWD 1,234.560")] + public void FormatMoney_WithNegativeAmount_PutsTheSignFirst(string currencyCode, string expected) + { + Assert.AreEqual(expected, RenderMoney("-1234.56", currencyCode)); + } + + [TestMethod] + [DataRow("USD", "US$1,234.56")] + [DataRow("CAD", "CA$1,234.56")] + [DataRow("AUD", "A$1,234.56")] + [DataRow("RUB", "1 234,56 RUB")] + [DataRow("THB", "THB 1,234.56")] + public void FormatMoney_WithSymbolDisplayMode_DisambiguatesSharedSymbols(string currencyCode, string expected) + { + Assert.AreEqual(expected, RenderMoney("1234.56", currencyCode, "symbol")); + } + + [TestMethod] + [DataRow("EUR", "1.234,56 €")] + [DataRow("GBP", "£1,234.56")] + public void FormatMoney_WithSymbolDisplayMode_KeepsUnambiguousSymbols(string currencyCode, string expected) + { + Assert.AreEqual(expected, RenderMoney("1234.56", currencyCode, "symbol")); + } + + [TestMethod] + [DataRow("USD", "1,234.56 USD")] + [DataRow("usd", "1,234.56 USD")] + [DataRow("xyz", "1,234.56 XYZ")] + public void FormatMoney_WithCodeDisplayMode_AppendsUppercasedIsoCode(string currencyCode, string expected) + { + Assert.AreEqual(expected, RenderMoney("1234.56", currencyCode, "code")); + } + + [TestMethod] + [DataRow("jpy")] + [DataRow(" JPY ")] + public void FormatMoney_WithLowercaseOrPaddedCode_ResolvesTheSameFormat(string currencyCode) + { + Assert.AreEqual("¥9,072", RenderMoney("9072.00", currencyCode)); + } + + [TestMethod] + public void FormatMoney_WithUnknownCurrencyCode_FallsBackToAmountAndCode() + { + Assert.AreEqual("1,234.56 XYZ", RenderMoney("1234.56", "XYZ")); + } + + [TestMethod] + public void FormatMoney_WithoutCurrencyCode_RendersBareAmount() + { + var template = new Template("${ formatMoney(Amount, Currency) }"); + + var result = template.Render( + new CompositeModelValue( + new ModelField("Amount", 1234.56m), + new ModelField("Currency", (string)null))); + + Assert.AreEqual("1,234.56", result); + } + + [TestMethod] + [DataRow("1.005", "$1.01")] + [DataRow("2.005", "$2.01")] + [DataRow("-1.005", "-$1.01")] + public void FormatMoney_RoundsHalfAwayFromZero(string amount, string expected) + { + Assert.AreEqual(expected, RenderMoney(amount, "USD")); + } + + [TestMethod] + [DataRow("en-US")] + [DataRow("ru-RU")] + [DataRow("de-DE")] + public void FormatMoney_IsIndependentOfRenderCulture(string locale) + { + var template = new Template("${ formatMoney(Amount, 'USD') }${ formatMoney(Amount, 'EUR') }"); + + var result = template.Render( + new CompositeModelValue(new ModelField("Amount", 1234.56m)), + new RenderSettings { CultureInfo = new CultureInfo(locale) }); + + Assert.AreEqual("$1,234.56" + "1.234,56 €", result); + } + + [TestMethod] + public void FormatMoney_SeparatesWithPlainSpaces_SoSmsStaysInTheGsmAlphabet() + { + var result = RenderMoney("1234.56", "RUB"); + + Assert.AreEqual("1 234,56 ₽", result); + Assert.IsFalse(result.Contains('\u00A0')); + } + + [TestMethod] + public void FormatMoney_WithDisplayModeFromModel_FallsBackToNarrowSymbolWhenUnsupported() + { + var template = new Template("${ formatMoney(Amount, 'USD', DisplayMode) }"); + + var result = template.Render( + new CompositeModelValue( + new ModelField("Amount", 1234.56m), + new ModelField("DisplayMode", "fancy"))); + + Assert.AreEqual("$1,234.56", result); + } + + [TestMethod] + [ExpectedException(typeof(TemplateContainsErrorsException))] + public void FormatMoney_WithUnsupportedConstantDisplayMode_ReportsStaticError() + { + new Template("${ formatMoney(Amount, 'USD', 'fancy') }"); + } + + private static string RenderMoney(string amount, string currencyCode, string displayMode = null) + { + var displayModeArgument = displayMode == null ? string.Empty : $", '{displayMode}'"; + var template = new Template($"${{ formatMoney(Amount, '{currencyCode}'{displayModeArgument}) }}"); + + return template.Render( + new CompositeModelValue( + new ModelField("Amount", decimal.Parse(amount, CultureInfo.InvariantCulture)))); + } + } +} From ed7e0f13c10deb20d29e9b54660900f3e5142da3 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Thu, 13 Aug 2026 17:07:00 +0200 Subject: [PATCH 02/14] Correct currency data and harden formatMoney Review pass over the currency table against CLDR, plus the edge cases a money formatter meets in production. Data: SGD's disambiguated symbol was invented (CLDR has no "S$"), ILS follows the amount in its own locale, and ARS/BRL/COP take a space after the symbol that the letter heuristic cannot express. Currencies outside the table now take their minor units from ISO 4217 instead of assuming two, so PKR no longer grows centimes and TND no longer loses a digit. Negative amounts put the sign before the symbol rather than between it and the digits, and an amount that rounds to zero is no longer rendered as a negative zero. Display mode is matched case-insensitively, like the currency code already was, and a null mode falls back like an unsupported one instead of failing the render. A null amount renders as zero: these fields are legitimately absent on orders without shipping or tax, and taking the message down over one of them is worse than showing zero. The number formats are precomputed per currency and made read-only, so the shared instances cannot be mutated by a later edit and each call stops allocating its format string. Co-Authored-By: Claude Fable 5 --- .../Standard/Money/CurrencyAmountFormats.cs | 63 +++++++++++++++++++ .../Standard/Money/CurrencyFormat.cs | 17 +++-- .../Standard/Money/CurrencyFormats.cs | 10 +-- .../Standard/Money/CurrencyNumberFormats.cs | 52 +++++++++++++++ .../Money/FormatMoneyTemplateFunction.cs | 2 +- ...matMoneyWithDisplayModeTemplateFunction.cs | 4 +- .../Standard/Money/MoneyFormatter.cs | 59 +++++++---------- .../FormatMoneyTemplateFunctionTests.cs | 50 ++++++++++++++- 8 files changed, 209 insertions(+), 48 deletions(-) create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyAmountFormats.cs create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberFormats.cs diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyAmountFormats.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyAmountFormats.cs new file mode 100644 index 0000000..5fbb295 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyAmountFormats.cs @@ -0,0 +1,63 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +using System; +using System.Collections.Generic; + +namespace Mindbox.Quokka +{ + internal static class CurrencyAmountFormats + { + public const int DefaultDecimalPlaces = 2; + + private static readonly string[] formatsByDecimalPlaces = ["N0", "N1", "N2", "N3", "N4"]; + + private static readonly IReadOnlyDictionary decimalPlacesByCodeOutsideTheTable = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["AFN"] = 0, + ["ALL"] = 0, + ["BIF"] = 0, + ["CLF"] = 4, + ["DJF"] = 0, + ["GNF"] = 0, + ["IQD"] = 0, + ["IRR"] = 0, + ["KMF"] = 0, + ["LAK"] = 0, + ["LYD"] = 3, + ["MGA"] = 0, + ["MMK"] = 0, + ["PKR"] = 0, + ["PYG"] = 0, + ["RWF"] = 0, + ["SOS"] = 0, + ["SYP"] = 0, + ["TND"] = 3, + ["UGX"] = 0, + ["VUV"] = 0, + ["XAF"] = 0, + ["XOF"] = 0, + ["XPF"] = 0, + ["YER"] = 0 + }; + + public static string ForDecimalPlaces(int decimalPlaces) => formatsByDecimalPlaces[decimalPlaces]; + + public static int DecimalPlacesForUnlistedCode(string currencyCode) => + currencyCode != null && decimalPlacesByCodeOutsideTheTable.TryGetValue(currencyCode, out var decimalPlaces) + ? decimalPlaces + : DefaultDecimalPlaces; + } +} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs index 5940703..19245d7 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs @@ -12,6 +12,8 @@ // // See the License for the specific language governing permissions and // // limitations under the License. +using System.Globalization; + namespace Mindbox.Quokka { internal sealed class CurrencyFormat @@ -21,13 +23,16 @@ public CurrencyFormat( string symbol, int decimalPlaces, CurrencyNumberStyle numberStyle = CurrencyNumberStyle.CommaGroupDotDecimal, - bool symbolFollowsAmount = false) + bool symbolFollowsAmount = false, + bool spaceAfterSymbol = false) { NarrowSymbol = narrowSymbol; Symbol = symbol; DecimalPlaces = decimalPlaces; - NumberStyle = numberStyle; SymbolFollowsAmount = symbolFollowsAmount; + SpaceAfterSymbol = spaceAfterSymbol; + AmountFormat = CurrencyAmountFormats.ForDecimalPlaces(decimalPlaces); + NumberFormat = CurrencyNumberFormats.Get(numberStyle); } public string NarrowSymbol { get; } @@ -36,8 +41,12 @@ public CurrencyFormat( public int DecimalPlaces { get; } - public CurrencyNumberStyle NumberStyle { get; } - public bool SymbolFollowsAmount { get; } + + public bool SpaceAfterSymbol { get; } + + public string AmountFormat { get; } + + public NumberFormatInfo NumberFormat { get; } } } diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs index cb5d7e4..b5977ff 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs @@ -28,15 +28,15 @@ internal static class CurrencyFormats new Dictionary(StringComparer.OrdinalIgnoreCase) { ["AED"] = new("AED", "AED", 2), - ["ARS"] = new("$", "ARS", 2, DotGroup), + ["ARS"] = new("$", "ARS", 2, DotGroup, spaceAfterSymbol: true), ["AUD"] = new("$", "A$", 2), ["BHD"] = new("BHD", "BHD", 3), - ["BRL"] = new("R$", "R$", 2, DotGroup), + ["BRL"] = new("R$", "R$", 2, DotGroup, spaceAfterSymbol: true), ["CAD"] = new("$", "CA$", 2), ["CHF"] = new("CHF", "CHF", 2, Apostrophe), ["CLP"] = new("$", "CLP", 0, DotGroup), ["CNY"] = new("¥", "CN¥", 2), - ["COP"] = new("$", "COP", 0, DotGroup), + ["COP"] = new("$", "COP", 0, DotGroup, spaceAfterSymbol: true), ["CZK"] = new("Kč", "CZK", 2, SpaceGroup, symbolFollowsAmount: true), ["DKK"] = new("kr", "DKK", 2, DotGroup, symbolFollowsAmount: true), ["EUR"] = new("€", "€", 2, DotGroup, symbolFollowsAmount: true), @@ -44,7 +44,7 @@ internal static class CurrencyFormats ["HKD"] = new("$", "HK$", 2), ["HUF"] = new("Ft", "HUF", 0, SpaceGroup, symbolFollowsAmount: true), ["IDR"] = new("Rp", "IDR", 0, DotGroup), - ["ILS"] = new("₪", "₪", 2), + ["ILS"] = new("₪", "₪", 2, symbolFollowsAmount: true), ["INR"] = new("₹", "₹", 2, Indian), ["ISK"] = new("kr", "ISK", 0, DotGroup, symbolFollowsAmount: true), ["JOD"] = new("JOD", "JOD", 3), @@ -61,7 +61,7 @@ internal static class CurrencyFormats ["RUB"] = new("₽", "RUB", 2, SpaceGroup, symbolFollowsAmount: true), ["SAR"] = new("SAR", "SAR", 2), ["SEK"] = new("kr", "SEK", 2, SpaceGroup, symbolFollowsAmount: true), - ["SGD"] = new("$", "S$", 2), + ["SGD"] = new("$", "SGD", 2), ["THB"] = new("฿", "THB", 2), ["TRY"] = new("₺", "TRY", 2, DotGroup), ["TWD"] = new("$", "NT$", 2), diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberFormats.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberFormats.cs new file mode 100644 index 0000000..2377300 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberFormats.cs @@ -0,0 +1,52 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +using System; +using System.Globalization; + +namespace Mindbox.Quokka +{ + internal static class CurrencyNumberFormats + { + private const string Space = " "; + + private static readonly NumberFormatInfo commaGroupDotDecimal = Create(",", ".", [3]); + private static readonly NumberFormatInfo dotGroupCommaDecimal = Create(".", ",", [3]); + private static readonly NumberFormatInfo spaceGroupCommaDecimal = Create(Space, ",", [3]); + private static readonly NumberFormatInfo apostropheGroupDotDecimal = Create("'", ".", [3]); + private static readonly NumberFormatInfo indianGroupDotDecimal = Create(",", ".", [3, 2]); + + public static NumberFormatInfo Default => commaGroupDotDecimal; + + public static NumberFormatInfo Get(CurrencyNumberStyle style) => + style switch + { + CurrencyNumberStyle.CommaGroupDotDecimal => commaGroupDotDecimal, + CurrencyNumberStyle.DotGroupCommaDecimal => dotGroupCommaDecimal, + CurrencyNumberStyle.SpaceGroupCommaDecimal => spaceGroupCommaDecimal, + CurrencyNumberStyle.ApostropheGroupDotDecimal => apostropheGroupDotDecimal, + CurrencyNumberStyle.IndianGroupDotDecimal => indianGroupDotDecimal, + _ => throw new ArgumentOutOfRangeException(nameof(style), style, null) + }; + + private static NumberFormatInfo Create(string groupSeparator, string decimalSeparator, int[] groupSizes) => + NumberFormatInfo.ReadOnly( + new NumberFormatInfo + { + NumberGroupSeparator = groupSeparator, + NumberDecimalSeparator = decimalSeparator, + NumberGroupSizes = groupSizes + }); + } +} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs index b37bb01..4bd26d8 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs @@ -21,7 +21,7 @@ internal class FormatMoneyTemplateFunction : ScalarTemplateFunction numberFormatsByStyle = - new Dictionary - { - [CurrencyNumberStyle.CommaGroupDotDecimal] = CreateNumberFormat(",", ".", [3]), - [CurrencyNumberStyle.DotGroupCommaDecimal] = CreateNumberFormat(".", ",", [3]), - [CurrencyNumberStyle.SpaceGroupCommaDecimal] = CreateNumberFormat(Space, ",", [3]), - [CurrencyNumberStyle.ApostropheGroupDotDecimal] = CreateNumberFormat("'", ".", [3]), - [CurrencyNumberStyle.IndianGroupDotDecimal] = CreateNumberFormat(",", ".", [3, 2]) - }; - public static bool IsSupportedDisplayMode(string displayMode) => - displayMode is NarrowSymbolDisplayMode or SymbolDisplayMode or CodeDisplayMode; + displayMode == null || IsMode(displayMode, NarrowSymbolDisplayMode) + || IsMode(displayMode, SymbolDisplayMode) + || IsMode(displayMode, CodeDisplayMode); public static string Format(decimal amount, string currencyCode, string displayMode) { - var trimmedCode = currencyCode?.Trim(); - var format = CurrencyFormats.TryGet(trimmedCode); - var numberFormat = numberFormatsByStyle[format?.NumberStyle ?? CurrencyNumberStyle.CommaGroupDotDecimal]; - var formattedAmount = Math.Abs(amount).ToString("N" + (format?.DecimalPlaces ?? 2), numberFormat); - var sign = amount < 0 ? MinusSign : string.Empty; + var code = currencyCode?.Trim(); + var format = CurrencyFormats.TryGet(code); + var decimalPlaces = format?.DecimalPlaces ?? CurrencyAmountFormats.DecimalPlacesForUnlistedCode(code); + var absolute = Math.Round(Math.Abs(amount), decimalPlaces, MidpointRounding.AwayFromZero); + + var formattedAmount = absolute.ToString( + format?.AmountFormat ?? CurrencyAmountFormats.ForDecimalPlaces(decimalPlaces), + format?.NumberFormat ?? CurrencyNumberFormats.Default); + + var sign = amount < 0 && absolute != decimal.Zero ? MinusSign : string.Empty; if (format == null) - return string.IsNullOrEmpty(trimmedCode) + return string.IsNullOrEmpty(code) ? sign + formattedAmount - : sign + formattedAmount + Space + trimmedCode.ToUpperInvariant(); + : sign + formattedAmount + Space + code.ToUpperInvariant(); - if (displayMode == CodeDisplayMode) - return sign + formattedAmount + Space + trimmedCode.ToUpperInvariant(); + if (IsMode(displayMode, CodeDisplayMode)) + return sign + formattedAmount + Space + code.ToUpperInvariant(); - var symbol = displayMode == SymbolDisplayMode ? format.Symbol : format.NarrowSymbol; + var symbol = IsMode(displayMode, SymbolDisplayMode) ? format.Symbol : format.NarrowSymbol; if (format.SymbolFollowsAmount) return sign + formattedAmount + Space + symbol; - var symbolSeparator = char.IsLetter(symbol[symbol.Length - 1]) ? Space : string.Empty; + var needsSpace = format.SpaceAfterSymbol || char.IsLetter(symbol[symbol.Length - 1]); - return sign + symbol + symbolSeparator + formattedAmount; + return needsSpace + ? sign + symbol + Space + formattedAmount + : sign + symbol + formattedAmount; } - private static NumberFormatInfo CreateNumberFormat( - string groupSeparator, - string decimalSeparator, - int[] groupSizes) => - new() - { - NumberGroupSeparator = groupSeparator, - NumberDecimalSeparator = decimalSeparator, - NumberGroupSizes = groupSizes - }; + private static bool IsMode(string displayMode, string mode) => + string.Equals(displayMode?.Trim(), mode, StringComparison.OrdinalIgnoreCase); } } diff --git a/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs index fdf38e0..6d8b315 100644 --- a/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs +++ b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs @@ -30,7 +30,7 @@ public class FormatMoneyTemplateFunctionTests [DataRow("RUB", "1 234 567,89 ₽")] [DataRow("PLN", "1 234 567,89 zł")] [DataRow("EUR", "1.234.567,89 €")] - [DataRow("BRL", "R$1.234.567,89")] + [DataRow("BRL", "R$ 1.234.567,89")] [DataRow("CHF", "CHF 1'234'567.89")] [DataRow("INR", "₹12,34,567.89")] public void FormatMoney_GroupsDigitsTheWayTheCurrencyDoes(string currencyCode, string expected) @@ -106,6 +106,54 @@ public void FormatMoney_WithUnknownCurrencyCode_FallsBackToAmountAndCode() Assert.AreEqual("1,234.56 XYZ", RenderMoney("1234.56", "XYZ")); } + [TestMethod] + [DataRow("PKR", "1,235 PKR")] + [DataRow("TND", "1,234.560 TND")] + public void FormatMoney_WithUnlistedCurrency_StillUsesItsMinorUnits(string currencyCode, string expected) + { + Assert.AreEqual(expected, RenderMoney("1234.56", currencyCode)); + } + + [TestMethod] + public void FormatMoney_WithAmountRoundingToZero_DoesNotRenderSignedZero() + { + Assert.AreEqual("$0.00", RenderMoney("-0.004", "USD")); + } + + [TestMethod] + [DataRow("SYMBOL")] + [DataRow(" code ")] + public void FormatMoney_WithDifferentlyCasedDisplayMode_IsAccepted(string displayMode) + { + var result = RenderMoney("1234.56", "USD", displayMode); + + Assert.AreNotEqual("$1,234.56", result); + } + + [TestMethod] + public void FormatMoney_WithNullAmount_RendersZero() + { + var template = new Template("${ formatMoney(Amount, 'USD') }"); + + var result = template.Render( + new CompositeModelValue(new ModelField("Amount", (string)null))); + + Assert.AreEqual("$0.00", result); + } + + [TestMethod] + public void FormatMoney_WithNullDisplayMode_FallsBackToNarrowSymbol() + { + var template = new Template("${ formatMoney(Amount, 'USD', DisplayMode) }"); + + var result = template.Render( + new CompositeModelValue( + new ModelField("Amount", 1234.56m), + new ModelField("DisplayMode", (string)null))); + + Assert.AreEqual("$1,234.56", result); + } + [TestMethod] public void FormatMoney_WithoutCurrencyCode_RendersBareAmount() { From f5e14d8ca5c06c27adef41d4532afb0c314aeea2 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Thu, 13 Aug 2026 17:22:35 +0200 Subject: [PATCH 03/14] Cover the currencies whose formatting nothing exercised Eleven currencies carried a non-default grouping style that no test touched, so their assignment could be changed without failing anything. Adds them at an amount large enough to show two group separators, which also pins their symbol placement. Display-mode casing is now asserted per mode rather than by inequality, and the round-to-zero rule is checked on a zero-decimal currency too. Co-Authored-By: Claude Fable 5 --- .../FormatMoneyTemplateFunctionTests.cs | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs index 6d8b315..fd85857 100644 --- a/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs +++ b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs @@ -33,6 +33,17 @@ public class FormatMoneyTemplateFunctionTests [DataRow("BRL", "R$ 1.234.567,89")] [DataRow("CHF", "CHF 1'234'567.89")] [DataRow("INR", "₹12,34,567.89")] + [DataRow("ARS", "$ 1.234.567,89")] + [DataRow("CLP", "$1.234.568")] + [DataRow("COP", "$ 1.234.568")] + [DataRow("CZK", "1 234 567,89 Kč")] + [DataRow("DKK", "1.234.567,89 kr")] + [DataRow("ISK", "1.234.568 kr")] + [DataRow("NOK", "1 234 567,89 kr")] + [DataRow("SEK", "1 234 567,89 kr")] + [DataRow("TRY", "₺1.234.567,89")] + [DataRow("UAH", "1 234 567,89 ₴")] + [DataRow("ZAR", "R 1 234 567,89")] public void FormatMoney_GroupsDigitsTheWayTheCurrencyDoes(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("1234567.89", currencyCode)); @@ -115,19 +126,24 @@ public void FormatMoney_WithUnlistedCurrency_StillUsesItsMinorUnits(string curre } [TestMethod] - public void FormatMoney_WithAmountRoundingToZero_DoesNotRenderSignedZero() + [DataRow("-0.004", "USD", "$0.00")] + [DataRow("-0.4", "JPY", "¥0")] + [DataRow("0", "USD", "$0.00")] + public void FormatMoney_WithAmountRoundingToZero_DoesNotRenderSignedZero( + string amount, + string currencyCode, + string expected) { - Assert.AreEqual("$0.00", RenderMoney("-0.004", "USD")); + Assert.AreEqual(expected, RenderMoney(amount, currencyCode)); } [TestMethod] - [DataRow("SYMBOL")] - [DataRow(" code ")] - public void FormatMoney_WithDifferentlyCasedDisplayMode_IsAccepted(string displayMode) + [DataRow("SYMBOL", "US$1,234.56")] + [DataRow(" code ", "1,234.56 USD")] + [DataRow("NarrowSymbol", "$1,234.56")] + public void FormatMoney_WithDifferentlyCasedDisplayMode_ResolvesTheSameMode(string displayMode, string expected) { - var result = RenderMoney("1234.56", "USD", displayMode); - - Assert.AreNotEqual("$1,234.56", result); + Assert.AreEqual(expected, RenderMoney("1234.56", "USD", displayMode)); } [TestMethod] From e21d00fcdc8adb52e489966a3589a916d3c74096 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Thu, 13 Aug 2026 18:21:14 +0200 Subject: [PATCH 04/14] Cover every currency Shopify supports, generated from CLDR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hand-written table held 42 currencies against the 161 Shopify accepts, so a merchant selling in naira or Pakistani rupees got the bare ISO code and, worse, invented decimals. The table is now generated from CLDR for all 152 usable Shopify codes, which also removes the hand-curation that produced an "S$" symbol CLDR has never had. Generating from one locale collapsed most of the model. In CLDR's en-001 every currency puts the symbol first and groups in thousands with a dot decimal, so per-currency grouping styles, symbol placement and the Indian lakh special case all disappear; what actually varies is the symbol, the number of decimals and whether a space follows the symbol. This also settles a question the previous table answered by accident. Formatting now follows the language the email is written in rather than each currency's home locale, so a Vietnamese amount reads as "₫1,731,200" in an English message instead of "1.731.200 ₫". That matches the acceptance examples in the effort card, which were written that way. Co-Authored-By: Claude Fable 5 --- .../Standard/Money/CurrencyAmountFormats.cs | 42 +---- .../Standard/Money/CurrencyFormat.cs | 16 +- .../Standard/Money/CurrencyFormats.cs | 171 ++++++++++++++---- .../Standard/Money/CurrencyNumberFormats.cs | 52 ------ .../Standard/Money/CurrencyNumberStyle.cs | 25 --- .../Standard/Money/MoneyFormatter.cs | 19 +- .../FormatMoneyTemplateFunctionTests.cs | 60 +++--- 7 files changed, 178 insertions(+), 207 deletions(-) delete mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberFormats.cs delete mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberStyle.cs diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyAmountFormats.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyAmountFormats.cs index 5fbb295..bcc200e 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyAmountFormats.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyAmountFormats.cs @@ -12,8 +12,7 @@ // // See the License for the specific language governing permissions and // // limitations under the License. -using System; -using System.Collections.Generic; +using System.Globalization; namespace Mindbox.Quokka { @@ -23,41 +22,14 @@ internal static class CurrencyAmountFormats private static readonly string[] formatsByDecimalPlaces = ["N0", "N1", "N2", "N3", "N4"]; - private static readonly IReadOnlyDictionary decimalPlacesByCodeOutsideTheTable = - new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly NumberFormatInfo NumberFormat = NumberFormatInfo.ReadOnly( + new NumberFormatInfo { - ["AFN"] = 0, - ["ALL"] = 0, - ["BIF"] = 0, - ["CLF"] = 4, - ["DJF"] = 0, - ["GNF"] = 0, - ["IQD"] = 0, - ["IRR"] = 0, - ["KMF"] = 0, - ["LAK"] = 0, - ["LYD"] = 3, - ["MGA"] = 0, - ["MMK"] = 0, - ["PKR"] = 0, - ["PYG"] = 0, - ["RWF"] = 0, - ["SOS"] = 0, - ["SYP"] = 0, - ["TND"] = 3, - ["UGX"] = 0, - ["VUV"] = 0, - ["XAF"] = 0, - ["XOF"] = 0, - ["XPF"] = 0, - ["YER"] = 0 - }; + NumberGroupSeparator = ",", + NumberDecimalSeparator = ".", + NumberGroupSizes = [3] + }); public static string ForDecimalPlaces(int decimalPlaces) => formatsByDecimalPlaces[decimalPlaces]; - - public static int DecimalPlacesForUnlistedCode(string currencyCode) => - currencyCode != null && decimalPlacesByCodeOutsideTheTable.TryGetValue(currencyCode, out var decimalPlaces) - ? decimalPlaces - : DefaultDecimalPlaces; } } diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs index 19245d7..368b09c 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormat.cs @@ -12,27 +12,17 @@ // // See the License for the specific language governing permissions and // // limitations under the License. -using System.Globalization; - namespace Mindbox.Quokka { internal sealed class CurrencyFormat { - public CurrencyFormat( - string narrowSymbol, - string symbol, - int decimalPlaces, - CurrencyNumberStyle numberStyle = CurrencyNumberStyle.CommaGroupDotDecimal, - bool symbolFollowsAmount = false, - bool spaceAfterSymbol = false) + public CurrencyFormat(string narrowSymbol, string symbol, int decimalPlaces, bool spaceAfterSymbol = false) { NarrowSymbol = narrowSymbol; Symbol = symbol; DecimalPlaces = decimalPlaces; - SymbolFollowsAmount = symbolFollowsAmount; SpaceAfterSymbol = spaceAfterSymbol; AmountFormat = CurrencyAmountFormats.ForDecimalPlaces(decimalPlaces); - NumberFormat = CurrencyNumberFormats.Get(numberStyle); } public string NarrowSymbol { get; } @@ -41,12 +31,8 @@ public CurrencyFormat( public int DecimalPlaces { get; } - public bool SymbolFollowsAmount { get; } - public bool SpaceAfterSymbol { get; } public string AmountFormat { get; } - - public NumberFormatInfo NumberFormat { get; } } } diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs index b5977ff..c85045e 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs @@ -19,56 +19,161 @@ namespace Mindbox.Quokka { internal static class CurrencyFormats { - private const CurrencyNumberStyle DotGroup = CurrencyNumberStyle.DotGroupCommaDecimal; - private const CurrencyNumberStyle SpaceGroup = CurrencyNumberStyle.SpaceGroupCommaDecimal; - private const CurrencyNumberStyle Apostrophe = CurrencyNumberStyle.ApostropheGroupDotDecimal; - private const CurrencyNumberStyle Indian = CurrencyNumberStyle.IndianGroupDotDecimal; - private static readonly IReadOnlyDictionary formatsByCode = new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AED"] = new("AED", "AED", 2), - ["ARS"] = new("$", "ARS", 2, DotGroup, spaceAfterSymbol: true), + ["AED"] = new("AED", "AED", 2, spaceAfterSymbol: true), + ["AFN"] = new("؋", "AFN", 0), + ["ALL"] = new("ALL", "ALL", 0, spaceAfterSymbol: true), + ["AMD"] = new("֏", "AMD", 2), + ["ANG"] = new("ANG", "ANG", 2, spaceAfterSymbol: true), + ["AOA"] = new("Kz", "AOA", 2, spaceAfterSymbol: true), + ["ARS"] = new("$", "ARS", 2), ["AUD"] = new("$", "A$", 2), - ["BHD"] = new("BHD", "BHD", 3), - ["BRL"] = new("R$", "R$", 2, DotGroup, spaceAfterSymbol: true), + ["AWG"] = new("AWG", "AWG", 2, spaceAfterSymbol: true), + ["AZN"] = new("₼", "AZN", 2), + ["BAM"] = new("KM", "BAM", 2, spaceAfterSymbol: true), + ["BBD"] = new("$", "BBD", 2), + ["BDT"] = new("৳", "BDT", 2), + ["BGN"] = new("BGN", "BGN", 2, spaceAfterSymbol: true), + ["BHD"] = new("BHD", "BHD", 3, spaceAfterSymbol: true), + ["BIF"] = new("BIF", "BIF", 0, spaceAfterSymbol: true), + ["BMD"] = new("$", "BMD", 2), + ["BND"] = new("$", "BND", 2), + ["BOB"] = new("Bs", "BOB", 2, spaceAfterSymbol: true), + ["BRL"] = new("R$", "R$", 2), + ["BSD"] = new("$", "BSD", 2), + ["BTN"] = new("BTN", "BTN", 2, spaceAfterSymbol: true), + ["BWP"] = new("P", "BWP", 2, spaceAfterSymbol: true), + ["BYN"] = new("BYN", "BYN", 2, spaceAfterSymbol: true), + ["BZD"] = new("$", "BZD", 2), ["CAD"] = new("$", "CA$", 2), - ["CHF"] = new("CHF", "CHF", 2, Apostrophe), - ["CLP"] = new("$", "CLP", 0, DotGroup), + ["CDF"] = new("CDF", "CDF", 2, spaceAfterSymbol: true), + ["CHF"] = new("CHF", "CHF", 2, spaceAfterSymbol: true), + ["CLP"] = new("$", "CLP", 0), ["CNY"] = new("¥", "CN¥", 2), - ["COP"] = new("$", "COP", 0, DotGroup, spaceAfterSymbol: true), - ["CZK"] = new("Kč", "CZK", 2, SpaceGroup, symbolFollowsAmount: true), - ["DKK"] = new("kr", "DKK", 2, DotGroup, symbolFollowsAmount: true), - ["EUR"] = new("€", "€", 2, DotGroup, symbolFollowsAmount: true), + ["COP"] = new("$", "COP", 0), + ["CRC"] = new("₡", "CRC", 2), + ["CVE"] = new("CVE", "CVE", 2, spaceAfterSymbol: true), + ["CZK"] = new("Kč", "CZK", 2, spaceAfterSymbol: true), + ["DJF"] = new("DJF", "DJF", 0, spaceAfterSymbol: true), + ["DKK"] = new("kr", "DKK", 2, spaceAfterSymbol: true), + ["DOP"] = new("$", "DOP", 2), + ["DZD"] = new("DZD", "DZD", 2, spaceAfterSymbol: true), + ["EGP"] = new("E£", "EGP", 2), + ["ERN"] = new("ERN", "ERN", 2, spaceAfterSymbol: true), + ["ETB"] = new("ETB", "ETB", 2, spaceAfterSymbol: true), + ["EUR"] = new("€", "€", 2), + ["FJD"] = new("$", "FJD", 2), + ["FKP"] = new("£", "FKP", 2), ["GBP"] = new("£", "£", 2), + ["GEL"] = new("₾", "GEL", 2), + ["GHS"] = new("GH₵", "GHS", 2), + ["GIP"] = new("£", "GIP", 2), + ["GMD"] = new("GMD", "GMD", 2, spaceAfterSymbol: true), + ["GNF"] = new("FG", "GNF", 0, spaceAfterSymbol: true), + ["GTQ"] = new("Q", "GTQ", 2, spaceAfterSymbol: true), + ["GYD"] = new("$", "GYD", 2), ["HKD"] = new("$", "HK$", 2), - ["HUF"] = new("Ft", "HUF", 0, SpaceGroup, symbolFollowsAmount: true), - ["IDR"] = new("Rp", "IDR", 0, DotGroup), - ["ILS"] = new("₪", "₪", 2, symbolFollowsAmount: true), - ["INR"] = new("₹", "₹", 2, Indian), - ["ISK"] = new("kr", "ISK", 0, DotGroup, symbolFollowsAmount: true), - ["JOD"] = new("JOD", "JOD", 3), + ["HNL"] = new("L", "HNL", 2, spaceAfterSymbol: true), + ["HTG"] = new("HTG", "HTG", 2, spaceAfterSymbol: true), + ["HUF"] = new("Ft", "HUF", 0, spaceAfterSymbol: true), + ["IDR"] = new("Rp", "IDR", 0, spaceAfterSymbol: true), + ["ILS"] = new("₪", "₪", 2), + ["INR"] = new("₹", "₹", 2), + ["IQD"] = new("IQD", "IQD", 0, spaceAfterSymbol: true), + ["IRR"] = new("IRR", "IRR", 0, spaceAfterSymbol: true), + ["ISK"] = new("kr", "ISK", 0, spaceAfterSymbol: true), + ["JEP"] = new("JEP", "JEP", 2, spaceAfterSymbol: true), + ["JMD"] = new("$", "JMD", 2), + ["JOD"] = new("JOD", "JOD", 3, spaceAfterSymbol: true), ["JPY"] = new("¥", "JP¥", 0), + ["KES"] = new("KES", "KES", 2, spaceAfterSymbol: true), + ["KGS"] = new("⃀", "KGS", 2), + ["KHR"] = new("៛", "KHR", 2), + ["KMF"] = new("CF", "KMF", 0, spaceAfterSymbol: true), ["KRW"] = new("₩", "₩", 0), - ["KWD"] = new("KWD", "KWD", 3), + ["KWD"] = new("KWD", "KWD", 3, spaceAfterSymbol: true), + ["KYD"] = new("$", "KYD", 2), + ["KZT"] = new("₸", "KZT", 2), + ["LAK"] = new("₭", "LAK", 0), + ["LBP"] = new("L£", "LBP", 0), + ["LKR"] = new("Rs", "LKR", 2, spaceAfterSymbol: true), + ["LRD"] = new("$", "LRD", 2), + ["LSL"] = new("LSL", "LSL", 2, spaceAfterSymbol: true), + ["LYD"] = new("LYD", "LYD", 3, spaceAfterSymbol: true), + ["MAD"] = new("MAD", "MAD", 2, spaceAfterSymbol: true), + ["MDL"] = new("MDL", "MDL", 2, spaceAfterSymbol: true), + ["MGA"] = new("Ar", "MGA", 0, spaceAfterSymbol: true), + ["MKD"] = new("MKD", "MKD", 2, spaceAfterSymbol: true), + ["MMK"] = new("K", "MMK", 0, spaceAfterSymbol: true), + ["MNT"] = new("₮", "MNT", 2), + ["MOP"] = new("MOP", "MOP", 2, spaceAfterSymbol: true), + ["MRU"] = new("MRU", "MRU", 2, spaceAfterSymbol: true), + ["MUR"] = new("Rs", "MUR", 2, spaceAfterSymbol: true), + ["MVR"] = new("MVR", "MVR", 2, spaceAfterSymbol: true), + ["MWK"] = new("MWK", "MWK", 2, spaceAfterSymbol: true), ["MXN"] = new("$", "MX$", 2), - ["MYR"] = new("RM", "MYR", 2), - ["NOK"] = new("kr", "NOK", 2, SpaceGroup, symbolFollowsAmount: true), + ["MYR"] = new("RM", "MYR", 2, spaceAfterSymbol: true), + ["MZN"] = new("MZN", "MZN", 2, spaceAfterSymbol: true), + ["NAD"] = new("$", "NAD", 2), + ["NGN"] = new("₦", "NGN", 2), + ["NIO"] = new("C$", "NIO", 2), + ["NOK"] = new("kr", "NOK", 2, spaceAfterSymbol: true), + ["NPR"] = new("Rs", "NPR", 2, spaceAfterSymbol: true), ["NZD"] = new("$", "NZ$", 2), - ["OMR"] = new("OMR", "OMR", 3), + ["OMR"] = new("OMR", "OMR", 3, spaceAfterSymbol: true), + ["PAB"] = new("PAB", "PAB", 2, spaceAfterSymbol: true), + ["PEN"] = new("PEN", "PEN", 2, spaceAfterSymbol: true), + ["PGK"] = new("PGK", "PGK", 2, spaceAfterSymbol: true), ["PHP"] = new("₱", "₱", 2), - ["PLN"] = new("zł", "PLN", 2, SpaceGroup, symbolFollowsAmount: true), - ["RUB"] = new("₽", "RUB", 2, SpaceGroup, symbolFollowsAmount: true), - ["SAR"] = new("SAR", "SAR", 2), - ["SEK"] = new("kr", "SEK", 2, SpaceGroup, symbolFollowsAmount: true), + ["PKR"] = new("Rs", "PKR", 0, spaceAfterSymbol: true), + ["PLN"] = new("zł", "PLN", 2, spaceAfterSymbol: true), + ["PYG"] = new("₲", "PYG", 0), + ["QAR"] = new("QAR", "QAR", 2, spaceAfterSymbol: true), + ["RON"] = new("lei", "RON", 2, spaceAfterSymbol: true), + ["RSD"] = new("RSD", "RSD", 2, spaceAfterSymbol: true), + ["RUB"] = new("₽", "RUB", 2), + ["RWF"] = new("RF", "RWF", 0, spaceAfterSymbol: true), + ["SAR"] = new("SAR", "SAR", 2, spaceAfterSymbol: true), + ["SBD"] = new("$", "SBD", 2), + ["SCR"] = new("SCR", "SCR", 2, spaceAfterSymbol: true), + ["SDG"] = new("SDG", "SDG", 2, spaceAfterSymbol: true), + ["SEK"] = new("kr", "SEK", 2, spaceAfterSymbol: true), ["SGD"] = new("$", "SGD", 2), + ["SHP"] = new("£", "SHP", 2), + ["SLL"] = new("SLL", "SLL", 0, spaceAfterSymbol: true), + ["SOS"] = new("SOS", "SOS", 0, spaceAfterSymbol: true), + ["SRD"] = new("$", "SRD", 2), + ["SSP"] = new("£", "SSP", 2), + ["STN"] = new("Db", "STN", 2, spaceAfterSymbol: true), + ["SYP"] = new("£", "SYP", 0), + ["SZL"] = new("SZL", "SZL", 2, spaceAfterSymbol: true), ["THB"] = new("฿", "THB", 2), - ["TRY"] = new("₺", "TRY", 2, DotGroup), + ["TJS"] = new("TJS", "TJS", 2, spaceAfterSymbol: true), + ["TMT"] = new("TMT", "TMT", 2, spaceAfterSymbol: true), + ["TND"] = new("TND", "TND", 3, spaceAfterSymbol: true), + ["TOP"] = new("T$", "TOP", 2), + ["TRY"] = new("₺", "TRY", 2), + ["TTD"] = new("$", "TTD", 2), ["TWD"] = new("$", "NT$", 2), - ["UAH"] = new("₴", "UAH", 2, SpaceGroup, symbolFollowsAmount: true), + ["TZS"] = new("TZS", "TZS", 2, spaceAfterSymbol: true), + ["UAH"] = new("₴", "UAH", 2), + ["UGX"] = new("UGX", "UGX", 0, spaceAfterSymbol: true), ["USD"] = new("$", "US$", 2), - ["VND"] = new("₫", "₫", 0, DotGroup, symbolFollowsAmount: true), - ["ZAR"] = new("R", "ZAR", 2, SpaceGroup) + ["UYU"] = new("$", "UYU", 2), + ["UZS"] = new("UZS", "UZS", 2, spaceAfterSymbol: true), + ["VES"] = new("VES", "VES", 2, spaceAfterSymbol: true), + ["VND"] = new("₫", "₫", 0), + ["VUV"] = new("VUV", "VUV", 0, spaceAfterSymbol: true), + ["WST"] = new("WST", "WST", 2, spaceAfterSymbol: true), + ["XAF"] = new("FCFA", "FCFA", 0, spaceAfterSymbol: true), + ["XCD"] = new("$", "EC$", 2), + ["XOF"] = new("F CFA", "F CFA", 0, spaceAfterSymbol: true), + ["XPF"] = new("CFPF", "CFPF", 0, spaceAfterSymbol: true), + ["YER"] = new("YER", "YER", 0, spaceAfterSymbol: true), + ["ZAR"] = new("R", "ZAR", 2, spaceAfterSymbol: true), + ["ZMW"] = new("ZK", "ZMW", 2, spaceAfterSymbol: true) }; public static CurrencyFormat TryGet(string currencyCode) => diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberFormats.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberFormats.cs deleted file mode 100644 index 2377300..0000000 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberFormats.cs +++ /dev/null @@ -1,52 +0,0 @@ -// // Copyright 2022 Mindbox Ltd -// // -// // Licensed under the Apache License, Version 2.0 (the "License"); -// // you may not use this file except in compliance with the License. -// // You may obtain a copy of the License at -// // -// // http://www.apache.org/licenses/LICENSE-2.0 -// // -// // Unless required by applicable law or agreed to in writing, software -// // distributed under the License is distributed on an "AS IS" BASIS, -// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// // See the License for the specific language governing permissions and -// // limitations under the License. - -using System; -using System.Globalization; - -namespace Mindbox.Quokka -{ - internal static class CurrencyNumberFormats - { - private const string Space = " "; - - private static readonly NumberFormatInfo commaGroupDotDecimal = Create(",", ".", [3]); - private static readonly NumberFormatInfo dotGroupCommaDecimal = Create(".", ",", [3]); - private static readonly NumberFormatInfo spaceGroupCommaDecimal = Create(Space, ",", [3]); - private static readonly NumberFormatInfo apostropheGroupDotDecimal = Create("'", ".", [3]); - private static readonly NumberFormatInfo indianGroupDotDecimal = Create(",", ".", [3, 2]); - - public static NumberFormatInfo Default => commaGroupDotDecimal; - - public static NumberFormatInfo Get(CurrencyNumberStyle style) => - style switch - { - CurrencyNumberStyle.CommaGroupDotDecimal => commaGroupDotDecimal, - CurrencyNumberStyle.DotGroupCommaDecimal => dotGroupCommaDecimal, - CurrencyNumberStyle.SpaceGroupCommaDecimal => spaceGroupCommaDecimal, - CurrencyNumberStyle.ApostropheGroupDotDecimal => apostropheGroupDotDecimal, - CurrencyNumberStyle.IndianGroupDotDecimal => indianGroupDotDecimal, - _ => throw new ArgumentOutOfRangeException(nameof(style), style, null) - }; - - private static NumberFormatInfo Create(string groupSeparator, string decimalSeparator, int[] groupSizes) => - NumberFormatInfo.ReadOnly( - new NumberFormatInfo - { - NumberGroupSeparator = groupSeparator, - NumberDecimalSeparator = decimalSeparator, - NumberGroupSizes = groupSizes - }); - } -} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberStyle.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberStyle.cs deleted file mode 100644 index 116c700..0000000 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyNumberStyle.cs +++ /dev/null @@ -1,25 +0,0 @@ -// // Copyright 2022 Mindbox Ltd -// // -// // Licensed under the Apache License, Version 2.0 (the "License"); -// // you may not use this file except in compliance with the License. -// // You may obtain a copy of the License at -// // -// // http://www.apache.org/licenses/LICENSE-2.0 -// // -// // Unless required by applicable law or agreed to in writing, software -// // distributed under the License is distributed on an "AS IS" BASIS, -// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// // See the License for the specific language governing permissions and -// // limitations under the License. - -namespace Mindbox.Quokka -{ - internal enum CurrencyNumberStyle - { - CommaGroupDotDecimal, - DotGroupCommaDecimal, - SpaceGroupCommaDecimal, - ApostropheGroupDotDecimal, - IndianGroupDotDecimal - } -} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs b/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs index 9e1e3c5..e0a8bda 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs @@ -26,7 +26,8 @@ internal static class MoneyFormatter private const string Space = " "; public static bool IsSupportedDisplayMode(string displayMode) => - displayMode == null || IsMode(displayMode, NarrowSymbolDisplayMode) + displayMode == null + || IsMode(displayMode, NarrowSymbolDisplayMode) || IsMode(displayMode, SymbolDisplayMode) || IsMode(displayMode, CodeDisplayMode); @@ -34,12 +35,12 @@ public static string Format(decimal amount, string currencyCode, string displayM { var code = currencyCode?.Trim(); var format = CurrencyFormats.TryGet(code); - var decimalPlaces = format?.DecimalPlaces ?? CurrencyAmountFormats.DecimalPlacesForUnlistedCode(code); + var decimalPlaces = format?.DecimalPlaces ?? CurrencyAmountFormats.DefaultDecimalPlaces; var absolute = Math.Round(Math.Abs(amount), decimalPlaces, MidpointRounding.AwayFromZero); var formattedAmount = absolute.ToString( format?.AmountFormat ?? CurrencyAmountFormats.ForDecimalPlaces(decimalPlaces), - format?.NumberFormat ?? CurrencyNumberFormats.Default); + CurrencyAmountFormats.NumberFormat); var sign = amount < 0 && absolute != decimal.Zero ? MinusSign : string.Empty; @@ -49,18 +50,12 @@ public static string Format(decimal amount, string currencyCode, string displayM : sign + formattedAmount + Space + code.ToUpperInvariant(); if (IsMode(displayMode, CodeDisplayMode)) - return sign + formattedAmount + Space + code.ToUpperInvariant(); + return sign + code.ToUpperInvariant() + Space + formattedAmount; var symbol = IsMode(displayMode, SymbolDisplayMode) ? format.Symbol : format.NarrowSymbol; + var separator = format.SpaceAfterSymbol || char.IsLetter(symbol[symbol.Length - 1]) ? Space : string.Empty; - if (format.SymbolFollowsAmount) - return sign + formattedAmount + Space + symbol; - - var needsSpace = format.SpaceAfterSymbol || char.IsLetter(symbol[symbol.Length - 1]); - - return needsSpace - ? sign + symbol + Space + formattedAmount - : sign + symbol + formattedAmount; + return sign + symbol + separator + formattedAmount; } private static bool IsMode(string displayMode, string mode) => diff --git a/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs index fd85857..81af96e 100644 --- a/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs +++ b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs @@ -26,25 +26,15 @@ public class FormatMoneyTemplateFunctionTests [TestMethod] [DataRow("USD", "$1,234,567.89")] [DataRow("GBP", "£1,234,567.89")] - [DataRow("SGD", "$1,234,567.89")] - [DataRow("RUB", "1 234 567,89 ₽")] - [DataRow("PLN", "1 234 567,89 zł")] - [DataRow("EUR", "1.234.567,89 €")] - [DataRow("BRL", "R$ 1.234.567,89")] - [DataRow("CHF", "CHF 1'234'567.89")] - [DataRow("INR", "₹12,34,567.89")] - [DataRow("ARS", "$ 1.234.567,89")] - [DataRow("CLP", "$1.234.568")] - [DataRow("COP", "$ 1.234.568")] - [DataRow("CZK", "1 234 567,89 Kč")] - [DataRow("DKK", "1.234.567,89 kr")] - [DataRow("ISK", "1.234.568 kr")] - [DataRow("NOK", "1 234 567,89 kr")] - [DataRow("SEK", "1 234 567,89 kr")] - [DataRow("TRY", "₺1.234.567,89")] - [DataRow("UAH", "1 234 567,89 ₴")] - [DataRow("ZAR", "R 1 234 567,89")] - public void FormatMoney_GroupsDigitsTheWayTheCurrencyDoes(string currencyCode, string expected) + [DataRow("EUR", "€1,234,567.89")] + [DataRow("RUB", "₽1,234,567.89")] + [DataRow("INR", "₹1,234,567.89")] + [DataRow("CHF", "CHF 1,234,567.89")] + [DataRow("KWD", "KWD 1,234,567.890")] + [DataRow("NGN", "₦1,234,567.89")] + [DataRow("RON", "lei 1,234,567.89")] + [DataRow("PKR", "Rs 1,234,568")] + public void FormatMoney_PutsTheSymbolFirstAndGroupsInThousands(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("1234567.89", currencyCode)); } @@ -52,9 +42,9 @@ public void FormatMoney_GroupsDigitsTheWayTheCurrencyDoes(string currencyCode, s [TestMethod] [DataRow("JPY", "¥9,072")] [DataRow("KRW", "₩9,072")] - [DataRow("VND", "9.072 ₫")] - [DataRow("HUF", "9 072 Ft")] - [DataRow("IDR", "Rp 9.072")] + [DataRow("VND", "₫9,072")] + [DataRow("HUF", "Ft 9,072")] + [DataRow("IDR", "Rp 9,072")] public void FormatMoney_WithZeroDecimalCurrency_DropsDecimals(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("9072.00", currencyCode)); @@ -68,7 +58,7 @@ public void FormatMoney_WithThreeDecimalCurrency_KeepsThreeDecimals() [TestMethod] [DataRow("USD", "-$1,234.56")] - [DataRow("RUB", "-1 234,56 ₽")] + [DataRow("RUB", "-₽1,234.56")] [DataRow("KWD", "-KWD 1,234.560")] public void FormatMoney_WithNegativeAmount_PutsTheSignFirst(string currencyCode, string expected) { @@ -79,7 +69,7 @@ public void FormatMoney_WithNegativeAmount_PutsTheSignFirst(string currencyCode, [DataRow("USD", "US$1,234.56")] [DataRow("CAD", "CA$1,234.56")] [DataRow("AUD", "A$1,234.56")] - [DataRow("RUB", "1 234,56 RUB")] + [DataRow("RUB", "RUB 1,234.56")] [DataRow("THB", "THB 1,234.56")] public void FormatMoney_WithSymbolDisplayMode_DisambiguatesSharedSymbols(string currencyCode, string expected) { @@ -87,7 +77,7 @@ public void FormatMoney_WithSymbolDisplayMode_DisambiguatesSharedSymbols(string } [TestMethod] - [DataRow("EUR", "1.234,56 €")] + [DataRow("EUR", "€1,234.56")] [DataRow("GBP", "£1,234.56")] public void FormatMoney_WithSymbolDisplayMode_KeepsUnambiguousSymbols(string currencyCode, string expected) { @@ -95,10 +85,10 @@ public void FormatMoney_WithSymbolDisplayMode_KeepsUnambiguousSymbols(string cur } [TestMethod] - [DataRow("USD", "1,234.56 USD")] - [DataRow("usd", "1,234.56 USD")] + [DataRow("USD", "USD 1,234.56")] + [DataRow("usd", "USD 1,234.56")] [DataRow("xyz", "1,234.56 XYZ")] - public void FormatMoney_WithCodeDisplayMode_AppendsUppercasedIsoCode(string currencyCode, string expected) + public void FormatMoney_WithCodeDisplayMode_PrefixesUppercasedIsoCode(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("1234.56", currencyCode, "code")); } @@ -118,9 +108,9 @@ public void FormatMoney_WithUnknownCurrencyCode_FallsBackToAmountAndCode() } [TestMethod] - [DataRow("PKR", "1,235 PKR")] - [DataRow("TND", "1,234.560 TND")] - public void FormatMoney_WithUnlistedCurrency_StillUsesItsMinorUnits(string currencyCode, string expected) + [DataRow("TND", "TND 1,234.560")] + [DataRow("UGX", "UGX 1,235")] + public void FormatMoney_WithNonTwoDecimalCurrency_UsesItsMinorUnits(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("1234.56", currencyCode)); } @@ -139,7 +129,7 @@ public void FormatMoney_WithAmountRoundingToZero_DoesNotRenderSignedZero( [TestMethod] [DataRow("SYMBOL", "US$1,234.56")] - [DataRow(" code ", "1,234.56 USD")] + [DataRow(" code ", "USD 1,234.56")] [DataRow("NarrowSymbol", "$1,234.56")] public void FormatMoney_WithDifferentlyCasedDisplayMode_ResolvesTheSameMode(string displayMode, string expected) { @@ -204,15 +194,15 @@ public void FormatMoney_IsIndependentOfRenderCulture(string locale) new CompositeModelValue(new ModelField("Amount", 1234.56m)), new RenderSettings { CultureInfo = new CultureInfo(locale) }); - Assert.AreEqual("$1,234.56" + "1.234,56 €", result); + Assert.AreEqual("$1,234.56" + "€1,234.56", result); } [TestMethod] public void FormatMoney_SeparatesWithPlainSpaces_SoSmsStaysInTheGsmAlphabet() { - var result = RenderMoney("1234.56", "RUB"); + var result = RenderMoney("1234.56", "CHF"); - Assert.AreEqual("1 234,56 ₽", result); + Assert.AreEqual("CHF 1,234.56", result); Assert.IsFalse(result.Contains('\u00A0')); } From 53f166eb0041ec329b9bd515c75a448ed7dac755 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Thu, 13 Aug 2026 19:43:22 +0200 Subject: [PATCH 05/14] Generate the currency table from CLDR instead of curating it The table was written by hand, which is how it ended up claiming an "S$" symbol that exists in no CLDR locale and giving the forint two decimal places it lost in 1999. It is now generated from the Unicode CLDR data packages, pinned by version in tools/currency-table. The generated file stays committed so builds need no network and every data change arrives as a reviewable diff. CI re-runs the generator and fails if the committed file differs, so the table can neither be edited by hand nor drift from CLDR unnoticed; refreshing after a CLDR release is a version bump and a rerun. Taking the data straight from CLDR also fixes what the hand-written version got wrong: fourteen currencies, the forint and the Pakistani rupee among them, no longer show a fractional part they do not have. Co-Authored-By: Claude Fable 5 --- .github/workflows/pull_request.yml | 9 +- .../Standard/Money/CurrencyFormats.cs | 184 ----------------- .../Standard/Money/CurrencyFormats.g.cs | 187 ++++++++++++++++++ Tools/currency-table/.gitignore | 1 + Tools/currency-table/README.md | 17 ++ Tools/currency-table/generate.mjs | 85 ++++++++ Tools/currency-table/package-lock.json | 29 +++ Tools/currency-table/package.json | 12 ++ Tools/currency-table/shopify-currencies.txt | 152 ++++++++++++++ 9 files changed, 491 insertions(+), 185 deletions(-) delete mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs create mode 100644 Tools/currency-table/.gitignore create mode 100644 Tools/currency-table/README.md create mode 100644 Tools/currency-table/generate.mjs create mode 100644 Tools/currency-table/package-lock.json create mode 100644 Tools/currency-table/package.json create mode 100644 Tools/currency-table/shopify-currencies.txt diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index b5f06ee..d896f7f 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -24,7 +24,14 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: 9 - + + - name: Check the currency table matches CLDR + working-directory: tools/currency-table + run: | + npm ci + npm run generate + git diff --exit-code -- ../../Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs + - name: Build run: dotnet build Engine/Quokka.slnx --configuration Release diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs deleted file mode 100644 index c85045e..0000000 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs +++ /dev/null @@ -1,184 +0,0 @@ -// // Copyright 2022 Mindbox Ltd -// // -// // Licensed under the Apache License, Version 2.0 (the "License"); -// // you may not use this file except in compliance with the License. -// // You may obtain a copy of the License at -// // -// // http://www.apache.org/licenses/LICENSE-2.0 -// // -// // Unless required by applicable law or agreed to in writing, software -// // distributed under the License is distributed on an "AS IS" BASIS, -// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// // See the License for the specific language governing permissions and -// // limitations under the License. - -using System; -using System.Collections.Generic; - -namespace Mindbox.Quokka -{ - internal static class CurrencyFormats - { - private static readonly IReadOnlyDictionary formatsByCode = - new Dictionary(StringComparer.OrdinalIgnoreCase) - { - ["AED"] = new("AED", "AED", 2, spaceAfterSymbol: true), - ["AFN"] = new("؋", "AFN", 0), - ["ALL"] = new("ALL", "ALL", 0, spaceAfterSymbol: true), - ["AMD"] = new("֏", "AMD", 2), - ["ANG"] = new("ANG", "ANG", 2, spaceAfterSymbol: true), - ["AOA"] = new("Kz", "AOA", 2, spaceAfterSymbol: true), - ["ARS"] = new("$", "ARS", 2), - ["AUD"] = new("$", "A$", 2), - ["AWG"] = new("AWG", "AWG", 2, spaceAfterSymbol: true), - ["AZN"] = new("₼", "AZN", 2), - ["BAM"] = new("KM", "BAM", 2, spaceAfterSymbol: true), - ["BBD"] = new("$", "BBD", 2), - ["BDT"] = new("৳", "BDT", 2), - ["BGN"] = new("BGN", "BGN", 2, spaceAfterSymbol: true), - ["BHD"] = new("BHD", "BHD", 3, spaceAfterSymbol: true), - ["BIF"] = new("BIF", "BIF", 0, spaceAfterSymbol: true), - ["BMD"] = new("$", "BMD", 2), - ["BND"] = new("$", "BND", 2), - ["BOB"] = new("Bs", "BOB", 2, spaceAfterSymbol: true), - ["BRL"] = new("R$", "R$", 2), - ["BSD"] = new("$", "BSD", 2), - ["BTN"] = new("BTN", "BTN", 2, spaceAfterSymbol: true), - ["BWP"] = new("P", "BWP", 2, spaceAfterSymbol: true), - ["BYN"] = new("BYN", "BYN", 2, spaceAfterSymbol: true), - ["BZD"] = new("$", "BZD", 2), - ["CAD"] = new("$", "CA$", 2), - ["CDF"] = new("CDF", "CDF", 2, spaceAfterSymbol: true), - ["CHF"] = new("CHF", "CHF", 2, spaceAfterSymbol: true), - ["CLP"] = new("$", "CLP", 0), - ["CNY"] = new("¥", "CN¥", 2), - ["COP"] = new("$", "COP", 0), - ["CRC"] = new("₡", "CRC", 2), - ["CVE"] = new("CVE", "CVE", 2, spaceAfterSymbol: true), - ["CZK"] = new("Kč", "CZK", 2, spaceAfterSymbol: true), - ["DJF"] = new("DJF", "DJF", 0, spaceAfterSymbol: true), - ["DKK"] = new("kr", "DKK", 2, spaceAfterSymbol: true), - ["DOP"] = new("$", "DOP", 2), - ["DZD"] = new("DZD", "DZD", 2, spaceAfterSymbol: true), - ["EGP"] = new("E£", "EGP", 2), - ["ERN"] = new("ERN", "ERN", 2, spaceAfterSymbol: true), - ["ETB"] = new("ETB", "ETB", 2, spaceAfterSymbol: true), - ["EUR"] = new("€", "€", 2), - ["FJD"] = new("$", "FJD", 2), - ["FKP"] = new("£", "FKP", 2), - ["GBP"] = new("£", "£", 2), - ["GEL"] = new("₾", "GEL", 2), - ["GHS"] = new("GH₵", "GHS", 2), - ["GIP"] = new("£", "GIP", 2), - ["GMD"] = new("GMD", "GMD", 2, spaceAfterSymbol: true), - ["GNF"] = new("FG", "GNF", 0, spaceAfterSymbol: true), - ["GTQ"] = new("Q", "GTQ", 2, spaceAfterSymbol: true), - ["GYD"] = new("$", "GYD", 2), - ["HKD"] = new("$", "HK$", 2), - ["HNL"] = new("L", "HNL", 2, spaceAfterSymbol: true), - ["HTG"] = new("HTG", "HTG", 2, spaceAfterSymbol: true), - ["HUF"] = new("Ft", "HUF", 0, spaceAfterSymbol: true), - ["IDR"] = new("Rp", "IDR", 0, spaceAfterSymbol: true), - ["ILS"] = new("₪", "₪", 2), - ["INR"] = new("₹", "₹", 2), - ["IQD"] = new("IQD", "IQD", 0, spaceAfterSymbol: true), - ["IRR"] = new("IRR", "IRR", 0, spaceAfterSymbol: true), - ["ISK"] = new("kr", "ISK", 0, spaceAfterSymbol: true), - ["JEP"] = new("JEP", "JEP", 2, spaceAfterSymbol: true), - ["JMD"] = new("$", "JMD", 2), - ["JOD"] = new("JOD", "JOD", 3, spaceAfterSymbol: true), - ["JPY"] = new("¥", "JP¥", 0), - ["KES"] = new("KES", "KES", 2, spaceAfterSymbol: true), - ["KGS"] = new("⃀", "KGS", 2), - ["KHR"] = new("៛", "KHR", 2), - ["KMF"] = new("CF", "KMF", 0, spaceAfterSymbol: true), - ["KRW"] = new("₩", "₩", 0), - ["KWD"] = new("KWD", "KWD", 3, spaceAfterSymbol: true), - ["KYD"] = new("$", "KYD", 2), - ["KZT"] = new("₸", "KZT", 2), - ["LAK"] = new("₭", "LAK", 0), - ["LBP"] = new("L£", "LBP", 0), - ["LKR"] = new("Rs", "LKR", 2, spaceAfterSymbol: true), - ["LRD"] = new("$", "LRD", 2), - ["LSL"] = new("LSL", "LSL", 2, spaceAfterSymbol: true), - ["LYD"] = new("LYD", "LYD", 3, spaceAfterSymbol: true), - ["MAD"] = new("MAD", "MAD", 2, spaceAfterSymbol: true), - ["MDL"] = new("MDL", "MDL", 2, spaceAfterSymbol: true), - ["MGA"] = new("Ar", "MGA", 0, spaceAfterSymbol: true), - ["MKD"] = new("MKD", "MKD", 2, spaceAfterSymbol: true), - ["MMK"] = new("K", "MMK", 0, spaceAfterSymbol: true), - ["MNT"] = new("₮", "MNT", 2), - ["MOP"] = new("MOP", "MOP", 2, spaceAfterSymbol: true), - ["MRU"] = new("MRU", "MRU", 2, spaceAfterSymbol: true), - ["MUR"] = new("Rs", "MUR", 2, spaceAfterSymbol: true), - ["MVR"] = new("MVR", "MVR", 2, spaceAfterSymbol: true), - ["MWK"] = new("MWK", "MWK", 2, spaceAfterSymbol: true), - ["MXN"] = new("$", "MX$", 2), - ["MYR"] = new("RM", "MYR", 2, spaceAfterSymbol: true), - ["MZN"] = new("MZN", "MZN", 2, spaceAfterSymbol: true), - ["NAD"] = new("$", "NAD", 2), - ["NGN"] = new("₦", "NGN", 2), - ["NIO"] = new("C$", "NIO", 2), - ["NOK"] = new("kr", "NOK", 2, spaceAfterSymbol: true), - ["NPR"] = new("Rs", "NPR", 2, spaceAfterSymbol: true), - ["NZD"] = new("$", "NZ$", 2), - ["OMR"] = new("OMR", "OMR", 3, spaceAfterSymbol: true), - ["PAB"] = new("PAB", "PAB", 2, spaceAfterSymbol: true), - ["PEN"] = new("PEN", "PEN", 2, spaceAfterSymbol: true), - ["PGK"] = new("PGK", "PGK", 2, spaceAfterSymbol: true), - ["PHP"] = new("₱", "₱", 2), - ["PKR"] = new("Rs", "PKR", 0, spaceAfterSymbol: true), - ["PLN"] = new("zł", "PLN", 2, spaceAfterSymbol: true), - ["PYG"] = new("₲", "PYG", 0), - ["QAR"] = new("QAR", "QAR", 2, spaceAfterSymbol: true), - ["RON"] = new("lei", "RON", 2, spaceAfterSymbol: true), - ["RSD"] = new("RSD", "RSD", 2, spaceAfterSymbol: true), - ["RUB"] = new("₽", "RUB", 2), - ["RWF"] = new("RF", "RWF", 0, spaceAfterSymbol: true), - ["SAR"] = new("SAR", "SAR", 2, spaceAfterSymbol: true), - ["SBD"] = new("$", "SBD", 2), - ["SCR"] = new("SCR", "SCR", 2, spaceAfterSymbol: true), - ["SDG"] = new("SDG", "SDG", 2, spaceAfterSymbol: true), - ["SEK"] = new("kr", "SEK", 2, spaceAfterSymbol: true), - ["SGD"] = new("$", "SGD", 2), - ["SHP"] = new("£", "SHP", 2), - ["SLL"] = new("SLL", "SLL", 0, spaceAfterSymbol: true), - ["SOS"] = new("SOS", "SOS", 0, spaceAfterSymbol: true), - ["SRD"] = new("$", "SRD", 2), - ["SSP"] = new("£", "SSP", 2), - ["STN"] = new("Db", "STN", 2, spaceAfterSymbol: true), - ["SYP"] = new("£", "SYP", 0), - ["SZL"] = new("SZL", "SZL", 2, spaceAfterSymbol: true), - ["THB"] = new("฿", "THB", 2), - ["TJS"] = new("TJS", "TJS", 2, spaceAfterSymbol: true), - ["TMT"] = new("TMT", "TMT", 2, spaceAfterSymbol: true), - ["TND"] = new("TND", "TND", 3, spaceAfterSymbol: true), - ["TOP"] = new("T$", "TOP", 2), - ["TRY"] = new("₺", "TRY", 2), - ["TTD"] = new("$", "TTD", 2), - ["TWD"] = new("$", "NT$", 2), - ["TZS"] = new("TZS", "TZS", 2, spaceAfterSymbol: true), - ["UAH"] = new("₴", "UAH", 2), - ["UGX"] = new("UGX", "UGX", 0, spaceAfterSymbol: true), - ["USD"] = new("$", "US$", 2), - ["UYU"] = new("$", "UYU", 2), - ["UZS"] = new("UZS", "UZS", 2, spaceAfterSymbol: true), - ["VES"] = new("VES", "VES", 2, spaceAfterSymbol: true), - ["VND"] = new("₫", "₫", 0), - ["VUV"] = new("VUV", "VUV", 0, spaceAfterSymbol: true), - ["WST"] = new("WST", "WST", 2, spaceAfterSymbol: true), - ["XAF"] = new("FCFA", "FCFA", 0, spaceAfterSymbol: true), - ["XCD"] = new("$", "EC$", 2), - ["XOF"] = new("F CFA", "F CFA", 0, spaceAfterSymbol: true), - ["XPF"] = new("CFPF", "CFPF", 0, spaceAfterSymbol: true), - ["YER"] = new("YER", "YER", 0, spaceAfterSymbol: true), - ["ZAR"] = new("R", "ZAR", 2, spaceAfterSymbol: true), - ["ZMW"] = new("ZK", "ZMW", 2, spaceAfterSymbol: true) - }; - - public static CurrencyFormat TryGet(string currencyCode) => - currencyCode != null && formatsByCode.TryGetValue(currencyCode, out var format) - ? format - : null; - } -} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs new file mode 100644 index 0000000..2a076ea --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs @@ -0,0 +1,187 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +// +// Generated by tools/currency-table from CLDR 48.2.0 (en-001). Do not edit by hand. +// + +using System; +using System.Collections.Generic; + +namespace Mindbox.Quokka +{ + internal static class CurrencyFormats + { + private static readonly IReadOnlyDictionary formatsByCode = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["AED"] = new("AED", "AED", 2), + ["AFN"] = new("؋", "AFN", 0), + ["ALL"] = new("ALL", "ALL", 0), + ["AMD"] = new("֏", "AMD", 2), + ["ANG"] = new("ANG", "ANG", 2), + ["AOA"] = new("Kz", "AOA", 2), + ["ARS"] = new("$", "ARS", 2), + ["AUD"] = new("$", "A$", 2), + ["AWG"] = new("AWG", "AWG", 2), + ["AZN"] = new("₼", "AZN", 2), + ["BAM"] = new("KM", "BAM", 2), + ["BBD"] = new("$", "BBD", 2), + ["BDT"] = new("৳", "BDT", 2), + ["BGN"] = new("BGN", "BGN", 2), + ["BHD"] = new("BHD", "BHD", 3), + ["BIF"] = new("BIF", "BIF", 0), + ["BMD"] = new("$", "BMD", 2), + ["BND"] = new("$", "BND", 2), + ["BOB"] = new("Bs", "BOB", 2), + ["BRL"] = new("R$", "R$", 2), + ["BSD"] = new("$", "BSD", 2), + ["BTN"] = new("BTN", "BTN", 2), + ["BWP"] = new("P", "BWP", 2), + ["BYN"] = new("BYN", "BYN", 2), + ["BZD"] = new("$", "BZD", 2), + ["CAD"] = new("$", "CA$", 2), + ["CDF"] = new("CDF", "CDF", 2), + ["CHF"] = new("CHF", "CHF", 2), + ["CLP"] = new("$", "CLP", 0), + ["CNY"] = new("¥", "CN¥", 2), + ["COP"] = new("$", "COP", 0), + ["CRC"] = new("₡", "CRC", 2), + ["CVE"] = new("CVE", "CVE", 2), + ["CZK"] = new("Kč", "CZK", 2), + ["DJF"] = new("DJF", "DJF", 0), + ["DKK"] = new("kr", "DKK", 2), + ["DOP"] = new("$", "DOP", 2), + ["DZD"] = new("DZD", "DZD", 2), + ["EGP"] = new("E£", "EGP", 2), + ["ERN"] = new("ERN", "ERN", 2), + ["ETB"] = new("ETB", "ETB", 2), + ["EUR"] = new("€", "€", 2), + ["FJD"] = new("$", "FJD", 2), + ["FKP"] = new("£", "FKP", 2), + ["GBP"] = new("£", "£", 2), + ["GEL"] = new("₾", "GEL", 2), + ["GHS"] = new("GH₵", "GHS", 2), + ["GIP"] = new("£", "GIP", 2), + ["GMD"] = new("GMD", "GMD", 2), + ["GNF"] = new("FG", "GNF", 0), + ["GTQ"] = new("Q", "GTQ", 2), + ["GYD"] = new("$", "GYD", 2), + ["HKD"] = new("$", "HK$", 2), + ["HNL"] = new("L", "HNL", 2), + ["HTG"] = new("HTG", "HTG", 2), + ["HUF"] = new("Ft", "HUF", 0), + ["IDR"] = new("Rp", "IDR", 0), + ["ILS"] = new("₪", "₪", 2), + ["INR"] = new("₹", "₹", 2), + ["IQD"] = new("IQD", "IQD", 0), + ["IRR"] = new("IRR", "IRR", 0), + ["ISK"] = new("kr", "ISK", 0), + ["JMD"] = new("$", "JMD", 2), + ["JOD"] = new("JOD", "JOD", 3), + ["JPY"] = new("¥", "JP¥", 0), + ["KES"] = new("KES", "KES", 2), + ["KGS"] = new("⃀", "KGS", 2), + ["KHR"] = new("៛", "KHR", 2), + ["KMF"] = new("CF", "KMF", 0), + ["KRW"] = new("₩", "₩", 0), + ["KWD"] = new("KWD", "KWD", 3), + ["KYD"] = new("$", "KYD", 2), + ["KZT"] = new("₸", "KZT", 2), + ["LAK"] = new("₭", "LAK", 0), + ["LBP"] = new("L£", "LBP", 0), + ["LKR"] = new("Rs", "LKR", 2), + ["LRD"] = new("$", "LRD", 2), + ["LSL"] = new("LSL", "LSL", 2), + ["LYD"] = new("LYD", "LYD", 3), + ["MAD"] = new("MAD", "MAD", 2), + ["MDL"] = new("MDL", "MDL", 2), + ["MGA"] = new("Ar", "MGA", 0), + ["MKD"] = new("MKD", "MKD", 2), + ["MMK"] = new("K", "MMK", 0), + ["MNT"] = new("₮", "MNT", 2), + ["MOP"] = new("MOP", "MOP", 2), + ["MRU"] = new("MRU", "MRU", 2), + ["MUR"] = new("Rs", "MUR", 2), + ["MVR"] = new("MVR", "MVR", 2), + ["MWK"] = new("MWK", "MWK", 2), + ["MXN"] = new("$", "MX$", 2), + ["MYR"] = new("RM", "MYR", 2), + ["MZN"] = new("MZN", "MZN", 2), + ["NAD"] = new("$", "NAD", 2), + ["NGN"] = new("₦", "NGN", 2), + ["NIO"] = new("C$", "NIO", 2), + ["NOK"] = new("kr", "NOK", 2), + ["NPR"] = new("Rs", "NPR", 2), + ["NZD"] = new("$", "NZ$", 2), + ["OMR"] = new("OMR", "OMR", 3), + ["PAB"] = new("PAB", "PAB", 2), + ["PEN"] = new("PEN", "PEN", 2), + ["PGK"] = new("PGK", "PGK", 2), + ["PHP"] = new("₱", "₱", 2), + ["PKR"] = new("Rs", "PKR", 0), + ["PLN"] = new("zł", "PLN", 2), + ["PYG"] = new("₲", "PYG", 0), + ["QAR"] = new("QAR", "QAR", 2), + ["RON"] = new("lei", "RON", 2), + ["RSD"] = new("RSD", "RSD", 2), + ["RUB"] = new("₽", "RUB", 2), + ["RWF"] = new("RF", "RWF", 0), + ["SAR"] = new("SAR", "SAR", 2), + ["SBD"] = new("$", "SBD", 2), + ["SCR"] = new("SCR", "SCR", 2), + ["SDG"] = new("SDG", "SDG", 2), + ["SEK"] = new("kr", "SEK", 2), + ["SGD"] = new("$", "SGD", 2), + ["SHP"] = new("£", "SHP", 2), + ["SLL"] = new("SLL", "SLL", 0), + ["SOS"] = new("SOS", "SOS", 0), + ["SRD"] = new("$", "SRD", 2), + ["SSP"] = new("£", "SSP", 2), + ["STN"] = new("Db", "STN", 2), + ["SYP"] = new("£", "SYP", 0), + ["SZL"] = new("SZL", "SZL", 2), + ["THB"] = new("฿", "THB", 2), + ["TJS"] = new("TJS", "TJS", 2), + ["TMT"] = new("TMT", "TMT", 2), + ["TND"] = new("TND", "TND", 3), + ["TOP"] = new("T$", "TOP", 2), + ["TRY"] = new("₺", "TRY", 2), + ["TTD"] = new("$", "TTD", 2), + ["TWD"] = new("$", "NT$", 2), + ["TZS"] = new("TZS", "TZS", 2), + ["UAH"] = new("₴", "UAH", 2), + ["UGX"] = new("UGX", "UGX", 0), + ["USD"] = new("$", "US$", 2), + ["UYU"] = new("$", "UYU", 2), + ["UZS"] = new("UZS", "UZS", 2), + ["VES"] = new("VES", "VES", 2), + ["VND"] = new("₫", "₫", 0), + ["VUV"] = new("VUV", "VUV", 0), + ["WST"] = new("WST", "WST", 2), + ["XAF"] = new("FCFA", "FCFA", 0), + ["XCD"] = new("$", "EC$", 2), + ["XOF"] = new("F CFA", "F CFA", 0), + ["XPF"] = new("CFPF", "CFPF", 0), + ["YER"] = new("YER", "YER", 0), + ["ZAR"] = new("R", "ZAR", 2), + ["ZMW"] = new("ZK", "ZMW", 2) + }; + + public static CurrencyFormat TryGet(string currencyCode) => + currencyCode != null && formatsByCode.TryGetValue(currencyCode, out var format) + ? format + : null; + } +} diff --git a/Tools/currency-table/.gitignore b/Tools/currency-table/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/Tools/currency-table/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/Tools/currency-table/README.md b/Tools/currency-table/README.md new file mode 100644 index 0000000..cf9fae6 --- /dev/null +++ b/Tools/currency-table/README.md @@ -0,0 +1,17 @@ +# Currency table generator + +`Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs` is generated from the +Unicode CLDR data packages pinned in `package.json` — currency symbols from +`cldr-numbers-full`, minor units from `cldr-core`. It is committed so that builds stay +hermetic and every data change shows up as a reviewable diff. + +To refresh after a CLDR release: + + npm ci + npm run generate + +CI runs the same two commands and fails if the committed file differs, so the table +cannot be edited by hand and cannot silently drift from CLDR. + +`shopify-currencies.txt` lists the currency codes Shopify accepts as presentment +currencies; anything outside it falls back to "amount + ISO code" at render time. diff --git a/Tools/currency-table/generate.mjs b/Tools/currency-table/generate.mjs new file mode 100644 index 0000000..679fbb8 --- /dev/null +++ b/Tools/currency-table/generate.mjs @@ -0,0 +1,85 @@ +// Regenerates Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs from the CLDR data +// packages pinned in package.json. Run `npm ci && npm run generate` in this directory. +// The output is committed; CI re-runs this and fails if the committed file differs. + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const LOCALE = 'en-001'; +const OUTPUT = path.resolve(here, '../../Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs'); + +const read = relative => JSON.parse(fs.readFileSync(path.resolve(here, 'node_modules', relative), 'utf8')); + +const symbols = read(`cldr-numbers-full/main/${LOCALE}/currencies.json`).main[LOCALE].numbers.currencies; +const fractions = read('cldr-core/supplemental/currencyData.json').supplemental.currencyData.fractions; +const codes = fs.readFileSync(path.resolve(here, 'shopify-currencies.txt'), 'utf8').trim().split(/\s+/); + +const decimalPlacesOf = code => Number((fractions[code] ?? fractions.DEFAULT)._digits); +const escape = value => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + +const missing = []; +const entries = []; +for (const code of codes) { + const entry = symbols[code]; + if (!entry) { + missing.push(code); + continue; + } + + const narrow = entry['symbol-alt-narrow'] ?? entry.symbol; + const symbol = entry.symbol; + const decimals = decimalPlacesOf(code); + entries.push(`\t\t\t\t["${code}"] = new("${escape(narrow)}", "${escape(symbol)}", ${decimals}),`); +} + +if (entries.length === 0) + throw new Error('CLDR produced no currencies — refusing to write an empty table'); + +entries[entries.length - 1] = entries.at(-1).replace(/,$/, ''); + +const cldrVersion = read('cldr-core/package.json').version; +const source = `// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +// +// Generated by tools/currency-table from CLDR ${cldrVersion} (${LOCALE}). Do not edit by hand. +// + +using System; +using System.Collections.Generic; + +namespace Mindbox.Quokka +{ +\tinternal static class CurrencyFormats +\t{ +\t\tprivate static readonly IReadOnlyDictionary formatsByCode = +\t\t\tnew Dictionary(StringComparer.OrdinalIgnoreCase) +\t\t\t{ +${entries.join('\n')} +\t\t\t}; + +\t\tpublic static CurrencyFormat TryGet(string currencyCode) => +\t\t\tcurrencyCode != null && formatsByCode.TryGetValue(currencyCode, out var format) +\t\t\t\t? format +\t\t\t\t: null; +\t} +} +`; + +fs.writeFileSync(OUTPUT, source); +console.log(`wrote ${entries.length} currencies from CLDR ${cldrVersion}`); +if (missing.length > 0) + console.log(`not present in CLDR: ${missing.join(', ')}`); diff --git a/Tools/currency-table/package-lock.json b/Tools/currency-table/package-lock.json new file mode 100644 index 0000000..7eb195d --- /dev/null +++ b/Tools/currency-table/package-lock.json @@ -0,0 +1,29 @@ +{ + "name": "quokka-currency-table", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "quokka-currency-table", + "dependencies": { + "cldr-core": "48.2.0", + "cldr-numbers-full": "48.2.0" + } + }, + "node_modules/cldr-core": { + "version": "48.2.0", + "resolved": "https://artifactory.mindbox.ru/artifactory/api/npm/npm/cldr-core/-/cldr-core-48.2.0.tgz", + "integrity": "sha512-zfmLothncSwfv2jlevoSrgI2VGgH8SDGHXst6jUEotHA8nq9Igeg9jzdJDFtBso9pgJuG89DK16TzrmZDdo2Bg==", + "license": "Unicode-3.0" + }, + "node_modules/cldr-numbers-full": { + "version": "48.2.0", + "resolved": "https://artifactory.mindbox.ru/artifactory/api/npm/npm/cldr-numbers-full/-/cldr-numbers-full-48.2.0.tgz", + "integrity": "sha512-0EQ+UkVDsyXOlxnWnL1RIENVZFqCnb04D1Yrku0obnOyttbuZz8tHmUd5c5AEvhGS/Gg2zI4fTaCm51LSGITog==", + "license": "Unicode-3.0", + "peerDependencies": { + "cldr-core": "48.2.0" + } + } + } +} diff --git a/Tools/currency-table/package.json b/Tools/currency-table/package.json new file mode 100644 index 0000000..845be20 --- /dev/null +++ b/Tools/currency-table/package.json @@ -0,0 +1,12 @@ +{ + "name": "quokka-currency-table", + "private": true, + "type": "module", + "scripts": { + "generate": "node generate.mjs" + }, + "dependencies": { + "cldr-core": "48.2.0", + "cldr-numbers-full": "48.2.0" + } +} diff --git a/Tools/currency-table/shopify-currencies.txt b/Tools/currency-table/shopify-currencies.txt new file mode 100644 index 0000000..91bdcba --- /dev/null +++ b/Tools/currency-table/shopify-currencies.txt @@ -0,0 +1,152 @@ +AED +AFN +ALL +AMD +ANG +AOA +ARS +AUD +AWG +AZN +BAM +BBD +BDT +BGN +BHD +BIF +BMD +BND +BOB +BRL +BSD +BTN +BWP +BYN +BZD +CAD +CDF +CHF +CLP +CNY +COP +CRC +CVE +CZK +DJF +DKK +DOP +DZD +EGP +ERN +ETB +EUR +FJD +FKP +GBP +GEL +GHS +GIP +GMD +GNF +GTQ +GYD +HKD +HNL +HTG +HUF +IDR +ILS +INR +IQD +IRR +ISK +JEP +JMD +JOD +JPY +KES +KGS +KHR +KMF +KRW +KWD +KYD +KZT +LAK +LBP +LKR +LRD +LSL +LYD +MAD +MDL +MGA +MKD +MMK +MNT +MOP +MRU +MUR +MVR +MWK +MXN +MYR +MZN +NAD +NGN +NIO +NOK +NPR +NZD +OMR +PAB +PEN +PGK +PHP +PKR +PLN +PYG +QAR +RON +RSD +RUB +RWF +SAR +SBD +SCR +SDG +SEK +SGD +SHP +SLL +SOS +SRD +SSP +STN +SYP +SZL +THB +TJS +TMT +TND +TOP +TRY +TTD +TWD +TZS +UAH +UGX +USD +UYU +UZS +VES +VND +VUV +WST +XAF +XCD +XOF +XPF +YER +ZAR +ZMW \ No newline at end of file From 41df63e60828a30c20155c1f487d2fe871e358b6 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Thu, 13 Aug 2026 19:49:11 +0200 Subject: [PATCH 06/14] Let Dependabot raise the CLDR bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generator removed the hand-maintenance of the table itself, but the pinned CLDR version still had to be bumped by someone remembering to. Dependabot now does it monthly, and the pin stays because it is what makes the CI regeneration check deterministic and what keeps a CLDR release — which moves the money shown in every tenant's emails — from arriving inside an unrelated pull request. Co-Authored-By: Claude Fable 5 --- .github/dependabot.yml | 12 ++++++++++++ Tools/currency-table/README.md | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0cac98a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 + +updates: + # CLDR data behind tools/currency-table. A bump changes the generated currency + # table, so it must arrive as its own reviewable pull request rather than + # riding in on an unrelated change. + - package-ecosystem: npm + directory: /tools/currency-table + schedule: + interval: monthly + commit-message: + prefix: "chore(cldr)" diff --git a/Tools/currency-table/README.md b/Tools/currency-table/README.md index cf9fae6..6eabaa1 100644 --- a/Tools/currency-table/README.md +++ b/Tools/currency-table/README.md @@ -5,13 +5,16 @@ Unicode CLDR data packages pinned in `package.json` — currency symbols from `cldr-numbers-full`, minor units from `cldr-core`. It is committed so that builds stay hermetic and every data change shows up as a reviewable diff. -To refresh after a CLDR release: +The CLDR version is pinned so that the CI check below is deterministic and so that a +CLDR release lands as its own reviewable change rather than inside an unrelated pull +request — a data change moves the money in every tenant's emails. Dependabot raises that +bump monthly; regenerate and commit the result: npm ci npm run generate CI runs the same two commands and fails if the committed file differs, so the table -cannot be edited by hand and cannot silently drift from CLDR. +cannot be edited by hand and cannot silently drift from the pinned CLDR. `shopify-currencies.txt` lists the currency codes Shopify accepts as presentment currencies; anything outside it falls back to "amount + ISO code" at render time. From 1f7129d3c69cba733e76e9cd6b2c5b850fc5de2e Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Thu, 13 Aug 2026 19:52:36 +0200 Subject: [PATCH 07/14] Drop the Dependabot config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renovate is the standard here, not Dependabot, and this repository has neither — Renovate was offered in 2020 and the pull request was closed. Turning on a dependency bot is the maintainers' call, not something to slip into a pull request about formatting money. The CLDR bump stays manual for now; the CI check is what keeps the table from drifting. Co-Authored-By: Claude Fable 5 --- .github/dependabot.yml | 12 ------------ Tools/currency-table/README.md | 5 +++-- 2 files changed, 3 insertions(+), 14 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 0cac98a..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: 2 - -updates: - # CLDR data behind tools/currency-table. A bump changes the generated currency - # table, so it must arrive as its own reviewable pull request rather than - # riding in on an unrelated change. - - package-ecosystem: npm - directory: /tools/currency-table - schedule: - interval: monthly - commit-message: - prefix: "chore(cldr)" diff --git a/Tools/currency-table/README.md b/Tools/currency-table/README.md index 6eabaa1..ff535c8 100644 --- a/Tools/currency-table/README.md +++ b/Tools/currency-table/README.md @@ -7,8 +7,9 @@ hermetic and every data change shows up as a reviewable diff. The CLDR version is pinned so that the CI check below is deterministic and so that a CLDR release lands as its own reviewable change rather than inside an unrelated pull -request — a data change moves the money in every tenant's emails. Dependabot raises that -bump monthly; regenerate and commit the result: +request — a data change moves the money in every tenant's emails. This repository has no +dependency bot, so the bump is manual; the CI check below is what stops the table from +drifting unnoticed in the meantime. To take a new CLDR release: npm ci npm run generate From 6040d1068eb8baedad22e6ca6d5c54a3f13a0076 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Fri, 14 Aug 2026 11:18:56 +0200 Subject: [PATCH 08/14] Enable Renovate so the CLDR pin does not rot The generator took the currency table off hand-maintenance, but the pinned CLDR version still needed a human to remember it. Renovate is the standard across the organisation and already has a shared preset here, which sibling packages such as Mindbox.Analyzers and data-linq extend. This repository's onboarding pull request was closed in 2020, but that was not a lasting decision: data-linq closed the same evening's invitation and later opted in through a config file, which is the path Renovate's own bot documented in the closed pull request. Co-Authored-By: Claude Fable 5 --- .github/renovate.json | 5 +++++ Tools/currency-table/README.md | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 .github/renovate.json diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..cace02d --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,5 @@ +{ + "extends":[ + "github>mindbox-cloud/renovate-config" + ] +} diff --git a/Tools/currency-table/README.md b/Tools/currency-table/README.md index ff535c8..024dae5 100644 --- a/Tools/currency-table/README.md +++ b/Tools/currency-table/README.md @@ -7,9 +7,8 @@ hermetic and every data change shows up as a reviewable diff. The CLDR version is pinned so that the CI check below is deterministic and so that a CLDR release lands as its own reviewable change rather than inside an unrelated pull -request — a data change moves the money in every tenant's emails. This repository has no -dependency bot, so the bump is manual; the CI check below is what stops the table from -drifting unnoticed in the meantime. To take a new CLDR release: +request — a data change moves the money in every tenant's emails. Renovate raises that +bump; regenerate and commit the result: npm ci npm run generate From 531f96c9a0b8a2b89f53b6e9b87028a51c1e3c14 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Fri, 14 Aug 2026 11:22:21 +0200 Subject: [PATCH 09/14] Generate every currency CLDR knows, not a curated subset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generator was fed a checked-in list of the currency codes Shopify accepts, which quietly reintroduced the hand-maintenance the generator removed: a currency added by any payment provider would be absent from the table and render without a symbol and with two decimal places it may not have. The list is gone. All 307 currencies CLDR carries are generated, which costs twelve kilobytes of source and covers anything ISO 4217 defines, including currencies that do not exist yet. It also fits the engine better — it renders templates for every integration, and had no business knowing which currencies one of them happens to support. Co-Authored-By: Claude Fable 5 --- .../Standard/Money/CurrencyFormats.g.cs | 158 +++++++++++++++++- Tools/currency-table/README.md | 6 +- Tools/currency-table/generate.mjs | 16 +- Tools/currency-table/shopify-currencies.txt | 152 ----------------- 4 files changed, 164 insertions(+), 168 deletions(-) delete mode 100644 Tools/currency-table/shopify-currencies.txt diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs index 2a076ea..18139c7 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs @@ -26,68 +26,136 @@ internal static class CurrencyFormats private static readonly IReadOnlyDictionary formatsByCode = new Dictionary(StringComparer.OrdinalIgnoreCase) { + ["ADP"] = new("ADP", "ADP", 0), ["AED"] = new("AED", "AED", 2), + ["AFA"] = new("AFA", "AFA", 2), ["AFN"] = new("؋", "AFN", 0), + ["ALK"] = new("ALK", "ALK", 2), ["ALL"] = new("ALL", "ALL", 0), ["AMD"] = new("֏", "AMD", 2), ["ANG"] = new("ANG", "ANG", 2), ["AOA"] = new("Kz", "AOA", 2), + ["AOK"] = new("AOK", "AOK", 2), + ["AON"] = new("AON", "AON", 2), + ["AOR"] = new("AOR", "AOR", 2), + ["ARA"] = new("ARA", "ARA", 2), + ["ARL"] = new("ARL", "ARL", 2), + ["ARM"] = new("ARM", "ARM", 2), + ["ARP"] = new("ARP", "ARP", 2), ["ARS"] = new("$", "ARS", 2), + ["ATS"] = new("ATS", "ATS", 2), ["AUD"] = new("$", "A$", 2), ["AWG"] = new("AWG", "AWG", 2), + ["AZM"] = new("AZM", "AZM", 2), ["AZN"] = new("₼", "AZN", 2), + ["BAD"] = new("BAD", "BAD", 2), ["BAM"] = new("KM", "BAM", 2), + ["BAN"] = new("BAN", "BAN", 2), ["BBD"] = new("$", "BBD", 2), ["BDT"] = new("৳", "BDT", 2), + ["BEC"] = new("BEC", "BEC", 2), + ["BEF"] = new("BEF", "BEF", 2), + ["BEL"] = new("BEL", "BEL", 2), + ["BGL"] = new("BGL", "BGL", 2), + ["BGM"] = new("BGM", "BGM", 2), ["BGN"] = new("BGN", "BGN", 2), + ["BGO"] = new("BGO", "BGO", 2), ["BHD"] = new("BHD", "BHD", 3), ["BIF"] = new("BIF", "BIF", 0), ["BMD"] = new("$", "BMD", 2), ["BND"] = new("$", "BND", 2), ["BOB"] = new("Bs", "BOB", 2), + ["BOL"] = new("BOL", "BOL", 2), + ["BOP"] = new("BOP", "BOP", 2), + ["BOV"] = new("BOV", "BOV", 2), + ["BRB"] = new("BRB", "BRB", 2), + ["BRC"] = new("BRC", "BRC", 2), + ["BRE"] = new("BRE", "BRE", 2), ["BRL"] = new("R$", "R$", 2), + ["BRN"] = new("BRN", "BRN", 2), + ["BRR"] = new("BRR", "BRR", 2), + ["BRZ"] = new("BRZ", "BRZ", 2), ["BSD"] = new("$", "BSD", 2), ["BTN"] = new("BTN", "BTN", 2), + ["BUK"] = new("BUK", "BUK", 2), ["BWP"] = new("P", "BWP", 2), + ["BYB"] = new("BYB", "BYB", 2), ["BYN"] = new("BYN", "BYN", 2), + ["BYR"] = new("BYR", "BYR", 0), ["BZD"] = new("$", "BZD", 2), ["CAD"] = new("$", "CA$", 2), ["CDF"] = new("CDF", "CDF", 2), + ["CHE"] = new("CHE", "CHE", 2), ["CHF"] = new("CHF", "CHF", 2), + ["CHW"] = new("CHW", "CHW", 2), + ["CLE"] = new("CLE", "CLE", 2), + ["CLF"] = new("CLF", "CLF", 4), ["CLP"] = new("$", "CLP", 0), + ["CNH"] = new("CNH", "CNH", 2), + ["CNX"] = new("CNX", "CNX", 2), ["CNY"] = new("¥", "CN¥", 2), ["COP"] = new("$", "COP", 0), + ["COU"] = new("COU", "COU", 2), ["CRC"] = new("₡", "CRC", 2), + ["CSD"] = new("CSD", "CSD", 2), + ["CSK"] = new("CSK", "CSK", 2), + ["CUC"] = new("$", "CUC", 2), + ["CUP"] = new("$", "CUP", 2), ["CVE"] = new("CVE", "CVE", 2), + ["CYP"] = new("CYP", "CYP", 2), ["CZK"] = new("Kč", "CZK", 2), + ["DDM"] = new("DDM", "DDM", 2), + ["DEM"] = new("DEM", "DEM", 2), ["DJF"] = new("DJF", "DJF", 0), ["DKK"] = new("kr", "DKK", 2), ["DOP"] = new("$", "DOP", 2), ["DZD"] = new("DZD", "DZD", 2), + ["ECS"] = new("ECS", "ECS", 2), + ["ECV"] = new("ECV", "ECV", 2), + ["EEK"] = new("EEK", "EEK", 2), ["EGP"] = new("E£", "EGP", 2), ["ERN"] = new("ERN", "ERN", 2), + ["ESA"] = new("ESA", "ESA", 2), + ["ESB"] = new("ESB", "ESB", 2), + ["ESP"] = new("₧", "ESP", 0), ["ETB"] = new("ETB", "ETB", 2), ["EUR"] = new("€", "€", 2), + ["FIM"] = new("FIM", "FIM", 2), ["FJD"] = new("$", "FJD", 2), ["FKP"] = new("£", "FKP", 2), + ["FRF"] = new("FRF", "FRF", 2), ["GBP"] = new("£", "£", 2), + ["GEK"] = new("GEK", "GEK", 2), ["GEL"] = new("₾", "GEL", 2), + ["GHC"] = new("GHC", "GHC", 2), ["GHS"] = new("GH₵", "GHS", 2), ["GIP"] = new("£", "GIP", 2), ["GMD"] = new("GMD", "GMD", 2), ["GNF"] = new("FG", "GNF", 0), + ["GNS"] = new("GNS", "GNS", 2), + ["GQE"] = new("GQE", "GQE", 2), + ["GRD"] = new("GRD", "GRD", 2), ["GTQ"] = new("Q", "GTQ", 2), + ["GWE"] = new("GWE", "GWE", 2), + ["GWP"] = new("GWP", "GWP", 2), ["GYD"] = new("$", "GYD", 2), ["HKD"] = new("$", "HK$", 2), ["HNL"] = new("L", "HNL", 2), + ["HRD"] = new("HRD", "HRD", 2), + ["HRK"] = new("kn", "HRK", 2), ["HTG"] = new("HTG", "HTG", 2), ["HUF"] = new("Ft", "HUF", 0), ["IDR"] = new("Rp", "IDR", 0), + ["IEP"] = new("IEP", "IEP", 2), + ["ILP"] = new("ILP", "ILP", 2), + ["ILR"] = new("ILR", "ILR", 2), ["ILS"] = new("₪", "₪", 2), ["INR"] = new("₹", "₹", 2), ["IQD"] = new("IQD", "IQD", 0), ["IRR"] = new("IRR", "IRR", 0), + ["ISJ"] = new("ISJ", "ISJ", 2), ["ISK"] = new("kr", "ISK", 0), + ["ITL"] = new("ITL", "ITL", 0), ["JMD"] = new("$", "JMD", 2), ["JOD"] = new("JOD", "JOD", 3), ["JPY"] = new("¥", "JP¥", 0), @@ -95,6 +163,9 @@ internal static class CurrencyFormats ["KGS"] = new("⃀", "KGS", 2), ["KHR"] = new("៛", "KHR", 2), ["KMF"] = new("CF", "KMF", 0), + ["KPW"] = new("₩", "KPW", 0), + ["KRH"] = new("KRH", "KRH", 2), + ["KRO"] = new("KRO", "KRO", 2), ["KRW"] = new("₩", "₩", 0), ["KWD"] = new("KWD", "KWD", 3), ["KYD"] = new("$", "KYD", 2), @@ -104,79 +175,164 @@ internal static class CurrencyFormats ["LKR"] = new("Rs", "LKR", 2), ["LRD"] = new("$", "LRD", 2), ["LSL"] = new("LSL", "LSL", 2), + ["LTL"] = new("Lt", "LTL", 2), + ["LTT"] = new("LTT", "LTT", 2), + ["LUC"] = new("LUC", "LUC", 2), + ["LUF"] = new("LUF", "LUF", 0), + ["LUL"] = new("LUL", "LUL", 2), + ["LVL"] = new("Ls", "LVL", 2), + ["LVR"] = new("LVR", "LVR", 2), ["LYD"] = new("LYD", "LYD", 3), ["MAD"] = new("MAD", "MAD", 2), + ["MAF"] = new("MAF", "MAF", 2), + ["MCF"] = new("MCF", "MCF", 2), + ["MDC"] = new("MDC", "MDC", 2), ["MDL"] = new("MDL", "MDL", 2), ["MGA"] = new("Ar", "MGA", 0), + ["MGF"] = new("MGF", "MGF", 0), ["MKD"] = new("MKD", "MKD", 2), + ["MKN"] = new("MKN", "MKN", 2), + ["MLF"] = new("MLF", "MLF", 2), ["MMK"] = new("K", "MMK", 0), ["MNT"] = new("₮", "MNT", 2), ["MOP"] = new("MOP", "MOP", 2), + ["MRO"] = new("MRO", "MRO", 0), ["MRU"] = new("MRU", "MRU", 2), + ["MTL"] = new("MTL", "MTL", 2), + ["MTP"] = new("MTP", "MTP", 2), ["MUR"] = new("Rs", "MUR", 2), + ["MVP"] = new("MVP", "MVP", 2), ["MVR"] = new("MVR", "MVR", 2), ["MWK"] = new("MWK", "MWK", 2), ["MXN"] = new("$", "MX$", 2), + ["MXP"] = new("MXP", "MXP", 2), + ["MXV"] = new("MXV", "MXV", 2), ["MYR"] = new("RM", "MYR", 2), + ["MZE"] = new("MZE", "MZE", 2), + ["MZM"] = new("MZM", "MZM", 2), ["MZN"] = new("MZN", "MZN", 2), ["NAD"] = new("$", "NAD", 2), ["NGN"] = new("₦", "NGN", 2), + ["NIC"] = new("NIC", "NIC", 2), ["NIO"] = new("C$", "NIO", 2), + ["NLG"] = new("NLG", "NLG", 2), ["NOK"] = new("kr", "NOK", 2), ["NPR"] = new("Rs", "NPR", 2), ["NZD"] = new("$", "NZ$", 2), ["OMR"] = new("OMR", "OMR", 3), ["PAB"] = new("PAB", "PAB", 2), + ["PEI"] = new("PEI", "PEI", 2), ["PEN"] = new("PEN", "PEN", 2), + ["PES"] = new("PES", "PES", 2), ["PGK"] = new("PGK", "PGK", 2), ["PHP"] = new("₱", "₱", 2), ["PKR"] = new("Rs", "PKR", 0), ["PLN"] = new("zł", "PLN", 2), + ["PLZ"] = new("PLZ", "PLZ", 2), + ["PTE"] = new("PTE", "PTE", 2), ["PYG"] = new("₲", "PYG", 0), ["QAR"] = new("QAR", "QAR", 2), + ["RHD"] = new("RHD", "RHD", 2), + ["ROL"] = new("ROL", "ROL", 2), ["RON"] = new("lei", "RON", 2), ["RSD"] = new("RSD", "RSD", 2), ["RUB"] = new("₽", "RUB", 2), + ["RUR"] = new("RUR", "RUR", 2), ["RWF"] = new("RF", "RWF", 0), ["SAR"] = new("SAR", "SAR", 2), ["SBD"] = new("$", "SBD", 2), ["SCR"] = new("SCR", "SCR", 2), + ["SDD"] = new("SDD", "SDD", 2), ["SDG"] = new("SDG", "SDG", 2), + ["SDP"] = new("SDP", "SDP", 2), ["SEK"] = new("kr", "SEK", 2), ["SGD"] = new("$", "SGD", 2), ["SHP"] = new("£", "SHP", 2), + ["SIT"] = new("SIT", "SIT", 2), + ["SKK"] = new("SKK", "SKK", 2), + ["SLE"] = new("SLE", "SLE", 2), ["SLL"] = new("SLL", "SLL", 0), ["SOS"] = new("SOS", "SOS", 0), ["SRD"] = new("$", "SRD", 2), + ["SRG"] = new("SRG", "SRG", 2), ["SSP"] = new("£", "SSP", 2), + ["STD"] = new("STD", "STD", 0), ["STN"] = new("Db", "STN", 2), + ["SUR"] = new("SUR", "SUR", 2), + ["SVC"] = new("SVC", "SVC", 2), ["SYP"] = new("£", "SYP", 0), ["SZL"] = new("SZL", "SZL", 2), ["THB"] = new("฿", "THB", 2), + ["TJR"] = new("TJR", "TJR", 2), ["TJS"] = new("TJS", "TJS", 2), + ["TMM"] = new("TMM", "TMM", 0), ["TMT"] = new("TMT", "TMT", 2), ["TND"] = new("TND", "TND", 3), ["TOP"] = new("T$", "TOP", 2), + ["TPE"] = new("TPE", "TPE", 2), + ["TRL"] = new("TRL", "TRL", 0), ["TRY"] = new("₺", "TRY", 2), ["TTD"] = new("$", "TTD", 2), ["TWD"] = new("$", "NT$", 2), ["TZS"] = new("TZS", "TZS", 2), ["UAH"] = new("₴", "UAH", 2), + ["UAK"] = new("UAK", "UAK", 2), + ["UGS"] = new("UGS", "UGS", 2), ["UGX"] = new("UGX", "UGX", 0), ["USD"] = new("$", "US$", 2), + ["USN"] = new("USN", "USN", 2), + ["USS"] = new("USS", "USS", 2), + ["UYI"] = new("UYI", "UYI", 0), + ["UYP"] = new("UYP", "UYP", 2), ["UYU"] = new("$", "UYU", 2), + ["UYW"] = new("UYW", "UYW", 4), ["UZS"] = new("UZS", "UZS", 2), + ["VEB"] = new("VEB", "VEB", 2), + ["VED"] = new("VED", "VED", 2), + ["VEF"] = new("Bs", "VEF", 2), ["VES"] = new("VES", "VES", 2), ["VND"] = new("₫", "₫", 0), + ["VNN"] = new("VNN", "VNN", 2), ["VUV"] = new("VUV", "VUV", 0), ["WST"] = new("WST", "WST", 2), ["XAF"] = new("FCFA", "FCFA", 0), + ["XAG"] = new("XAG", "XAG", 2), + ["XAU"] = new("XAU", "XAU", 2), + ["XBA"] = new("XBA", "XBA", 2), + ["XBB"] = new("XBB", "XBB", 2), + ["XBC"] = new("XBC", "XBC", 2), + ["XBD"] = new("XBD", "XBD", 2), ["XCD"] = new("$", "EC$", 2), + ["XCG"] = new("Cg.", "Cg.", 2), + ["XDR"] = new("XDR", "XDR", 2), + ["XEU"] = new("XEU", "XEU", 2), + ["XFO"] = new("XFO", "XFO", 2), + ["XFU"] = new("XFU", "XFU", 2), ["XOF"] = new("F CFA", "F CFA", 0), + ["XPD"] = new("XPD", "XPD", 2), ["XPF"] = new("CFPF", "CFPF", 0), + ["XPT"] = new("XPT", "XPT", 2), + ["XRE"] = new("XRE", "XRE", 2), + ["XSU"] = new("XSU", "XSU", 2), + ["XTS"] = new("XTS", "XTS", 2), + ["XUA"] = new("XUA", "XUA", 2), + ["XXX"] = new("¤", "¤", 2), + ["YDD"] = new("YDD", "YDD", 2), ["YER"] = new("YER", "YER", 0), + ["YUD"] = new("YUD", "YUD", 2), + ["YUM"] = new("YUM", "YUM", 2), + ["YUN"] = new("YUN", "YUN", 2), + ["YUR"] = new("YUR", "YUR", 2), + ["ZAL"] = new("ZAL", "ZAL", 2), ["ZAR"] = new("R", "ZAR", 2), - ["ZMW"] = new("ZK", "ZMW", 2) + ["ZMK"] = new("ZMK", "ZMK", 0), + ["ZMW"] = new("ZK", "ZMW", 2), + ["ZRN"] = new("ZRN", "ZRN", 2), + ["ZRZ"] = new("ZRZ", "ZRZ", 2), + ["ZWD"] = new("ZWD", "ZWD", 0), + ["ZWG"] = new("ZWG", "ZWG", 2), + ["ZWL"] = new("ZWL", "ZWL", 2), + ["ZWR"] = new("ZWR", "ZWR", 2) }; public static CurrencyFormat TryGet(string currencyCode) => diff --git a/Tools/currency-table/README.md b/Tools/currency-table/README.md index 024dae5..d5ad956 100644 --- a/Tools/currency-table/README.md +++ b/Tools/currency-table/README.md @@ -16,5 +16,7 @@ bump; regenerate and commit the result: CI runs the same two commands and fails if the committed file differs, so the table cannot be edited by hand and cannot silently drift from the pinned CLDR. -`shopify-currencies.txt` lists the currency codes Shopify accepts as presentment -currencies; anything outside it falls back to "amount + ISO code" at render time. +Every currency CLDR carries is generated, not a curated subset — the engine renders +templates for every integration, not just one, and a subset would silently downgrade any +currency a payment provider adds later. A code CLDR does not know falls back to +"amount + ISO code" at render time. diff --git a/Tools/currency-table/generate.mjs b/Tools/currency-table/generate.mjs index 679fbb8..ce63e2b 100644 --- a/Tools/currency-table/generate.mjs +++ b/Tools/currency-table/generate.mjs @@ -14,22 +14,14 @@ const read = relative => JSON.parse(fs.readFileSync(path.resolve(here, 'node_mod const symbols = read(`cldr-numbers-full/main/${LOCALE}/currencies.json`).main[LOCALE].numbers.currencies; const fractions = read('cldr-core/supplemental/currencyData.json').supplemental.currencyData.fractions; -const codes = fs.readFileSync(path.resolve(here, 'shopify-currencies.txt'), 'utf8').trim().split(/\s+/); - const decimalPlacesOf = code => Number((fractions[code] ?? fractions.DEFAULT)._digits); const escape = value => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); -const missing = []; const entries = []; -for (const code of codes) { +for (const code of Object.keys(symbols).sort()) { const entry = symbols[code]; - if (!entry) { - missing.push(code); - continue; - } - - const narrow = entry['symbol-alt-narrow'] ?? entry.symbol; - const symbol = entry.symbol; + const symbol = entry.symbol ?? code; + const narrow = entry['symbol-alt-narrow'] ?? symbol; const decimals = decimalPlacesOf(code); entries.push(`\t\t\t\t["${code}"] = new("${escape(narrow)}", "${escape(symbol)}", ${decimals}),`); } @@ -81,5 +73,3 @@ ${entries.join('\n')} fs.writeFileSync(OUTPUT, source); console.log(`wrote ${entries.length} currencies from CLDR ${cldrVersion}`); -if (missing.length > 0) - console.log(`not present in CLDR: ${missing.join(', ')}`); diff --git a/Tools/currency-table/shopify-currencies.txt b/Tools/currency-table/shopify-currencies.txt deleted file mode 100644 index 91bdcba..0000000 --- a/Tools/currency-table/shopify-currencies.txt +++ /dev/null @@ -1,152 +0,0 @@ -AED -AFN -ALL -AMD -ANG -AOA -ARS -AUD -AWG -AZN -BAM -BBD -BDT -BGN -BHD -BIF -BMD -BND -BOB -BRL -BSD -BTN -BWP -BYN -BZD -CAD -CDF -CHF -CLP -CNY -COP -CRC -CVE -CZK -DJF -DKK -DOP -DZD -EGP -ERN -ETB -EUR -FJD -FKP -GBP -GEL -GHS -GIP -GMD -GNF -GTQ -GYD -HKD -HNL -HTG -HUF -IDR -ILS -INR -IQD -IRR -ISK -JEP -JMD -JOD -JPY -KES -KGS -KHR -KMF -KRW -KWD -KYD -KZT -LAK -LBP -LKR -LRD -LSL -LYD -MAD -MDL -MGA -MKD -MMK -MNT -MOP -MRU -MUR -MVR -MWK -MXN -MYR -MZN -NAD -NGN -NIO -NOK -NPR -NZD -OMR -PAB -PEN -PGK -PHP -PKR -PLN -PYG -QAR -RON -RSD -RUB -RWF -SAR -SBD -SCR -SDG -SEK -SGD -SHP -SLL -SOS -SRD -SSP -STN -SYP -SZL -THB -TJS -TMT -TND -TOP -TRY -TTD -TWD -TZS -UAH -UGX -USD -UYU -UZS -VES -VND -VUV -WST -XAF -XCD -XOF -XPF -YER -ZAR -ZMW \ No newline at end of file From e5a5863d176aad6973616a305fc2c643bad11403 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Fri, 14 Aug 2026 11:25:01 +0200 Subject: [PATCH 10/14] Fix the currency-check path casing The workflow pointed at tools/currency-table while git records the directory as Tools/currency-table, next to the existing Tools/ holding the ANTLR jar. A case-insensitive macOS filesystem hid this locally and the step failed on Linux before anything else could run, which is also why the test report found no results. Co-Authored-By: Claude Fable 5 --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index d896f7f..5cb59d5 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -26,7 +26,7 @@ jobs: dotnet-version: 9 - name: Check the currency table matches CLDR - working-directory: tools/currency-table + working-directory: Tools/currency-table run: | npm ci npm run generate From c50d74911944ab17bf06c2aca4a2b9d0fc352c37 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Fri, 14 Aug 2026 11:27:35 +0200 Subject: [PATCH 11/14] Resolve the CLDR packages from the public npm registry The lockfile was generated on a machine configured against the internal Artifactory mirror, so its resolved URLs pointed there and CI could not authenticate. The tool now pins the public registry and the lockfile is regenerated against it, which is also the right source for an Apache-licensed package built on a public runner. Co-Authored-By: Claude Fable 5 --- Tools/currency-table/.npmrc | 1 + Tools/currency-table/package-lock.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Tools/currency-table/.npmrc diff --git a/Tools/currency-table/.npmrc b/Tools/currency-table/.npmrc new file mode 100644 index 0000000..214c29d --- /dev/null +++ b/Tools/currency-table/.npmrc @@ -0,0 +1 @@ +registry=https://registry.npmjs.org/ diff --git a/Tools/currency-table/package-lock.json b/Tools/currency-table/package-lock.json index 7eb195d..53d8308 100644 --- a/Tools/currency-table/package-lock.json +++ b/Tools/currency-table/package-lock.json @@ -12,13 +12,13 @@ }, "node_modules/cldr-core": { "version": "48.2.0", - "resolved": "https://artifactory.mindbox.ru/artifactory/api/npm/npm/cldr-core/-/cldr-core-48.2.0.tgz", + "resolved": "https://registry.npmjs.org/cldr-core/-/cldr-core-48.2.0.tgz", "integrity": "sha512-zfmLothncSwfv2jlevoSrgI2VGgH8SDGHXst6jUEotHA8nq9Igeg9jzdJDFtBso9pgJuG89DK16TzrmZDdo2Bg==", "license": "Unicode-3.0" }, "node_modules/cldr-numbers-full": { "version": "48.2.0", - "resolved": "https://artifactory.mindbox.ru/artifactory/api/npm/npm/cldr-numbers-full/-/cldr-numbers-full-48.2.0.tgz", + "resolved": "https://registry.npmjs.org/cldr-numbers-full/-/cldr-numbers-full-48.2.0.tgz", "integrity": "sha512-0EQ+UkVDsyXOlxnWnL1RIENVZFqCnb04D1Yrku0obnOyttbuZz8tHmUd5c5AEvhGS/Gg2zI4fTaCm51LSGITog==", "license": "Unicode-3.0", "peerDependencies": { From d7569c49231860d939e29bec94080db6196b88b6 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Fri, 14 Aug 2026 11:30:03 +0200 Subject: [PATCH 12/14] Vendor the two CLDR files instead of installing npm packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Installing the packages pulled forty megabytes of every locale on earth to read one hundred and forty kilobytes, and brought a lockfile, a registry and an authentication failure with it — the lockfile was resolved against the internal Artifactory mirror, which a public runner cannot reach. The two files are now copied verbatim from the CLDR release named in cldr/VERSION, the generator needs nothing but Node, and the build no longer touches the network. Refreshing is two curl commands documented next to the script. The Renovate config goes with it: it was added to bump the CLDR pin, and there is no longer a pin to bump. Whether this repository wants a dependency bot for its other packages is a decision for its maintainers. Co-Authored-By: Claude Fable 5 --- .github/renovate.json | 5 - .github/workflows/pull_request.yml | 6 +- Tools/currency-table/.gitignore | 1 - Tools/currency-table/.npmrc | 1 - Tools/currency-table/README.md | 30 +- Tools/currency-table/cldr/VERSION | 1 + .../cldr/currencies-en-001.json | 1812 ++++++++ Tools/currency-table/cldr/currencyData.json | 3650 +++++++++++++++++ Tools/currency-table/generate.mjs | 12 +- Tools/currency-table/package-lock.json | 29 - Tools/currency-table/package.json | 12 - 11 files changed, 5490 insertions(+), 69 deletions(-) delete mode 100644 .github/renovate.json delete mode 100644 Tools/currency-table/.gitignore delete mode 100644 Tools/currency-table/.npmrc create mode 100644 Tools/currency-table/cldr/VERSION create mode 100644 Tools/currency-table/cldr/currencies-en-001.json create mode 100644 Tools/currency-table/cldr/currencyData.json delete mode 100644 Tools/currency-table/package-lock.json delete mode 100644 Tools/currency-table/package.json diff --git a/.github/renovate.json b/.github/renovate.json deleted file mode 100644 index cace02d..0000000 --- a/.github/renovate.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends":[ - "github>mindbox-cloud/renovate-config" - ] -} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 5cb59d5..20056ea 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -26,11 +26,9 @@ jobs: dotnet-version: 9 - name: Check the currency table matches CLDR - working-directory: Tools/currency-table run: | - npm ci - npm run generate - git diff --exit-code -- ../../Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs + node Tools/currency-table/generate.mjs + git diff --exit-code -- Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs - name: Build run: dotnet build Engine/Quokka.slnx --configuration Release diff --git a/Tools/currency-table/.gitignore b/Tools/currency-table/.gitignore deleted file mode 100644 index c2658d7..0000000 --- a/Tools/currency-table/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ diff --git a/Tools/currency-table/.npmrc b/Tools/currency-table/.npmrc deleted file mode 100644 index 214c29d..0000000 --- a/Tools/currency-table/.npmrc +++ /dev/null @@ -1 +0,0 @@ -registry=https://registry.npmjs.org/ diff --git a/Tools/currency-table/README.md b/Tools/currency-table/README.md index d5ad956..2a9b622 100644 --- a/Tools/currency-table/README.md +++ b/Tools/currency-table/README.md @@ -1,20 +1,28 @@ # Currency table generator `Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs` is generated from the -Unicode CLDR data packages pinned in `package.json` — currency symbols from -`cldr-numbers-full`, minor units from `cldr-core`. It is committed so that builds stay -hermetic and every data change shows up as a reviewable diff. +Unicode CLDR data vendored in `cldr/` — currency symbols from `currencies-en-001.json`, +minor units from `currencyData.json`, both copied verbatim from the release named in +`cldr/VERSION`. -The CLDR version is pinned so that the CI check below is deterministic and so that a -CLDR release lands as its own reviewable change rather than inside an unrelated pull -request — a data change moves the money in every tenant's emails. Renovate raises that -bump; regenerate and commit the result: +Regenerate with Node and nothing else: - npm ci - npm run generate + node generate.mjs -CI runs the same two commands and fails if the committed file differs, so the table -cannot be edited by hand and cannot silently drift from the pinned CLDR. +CI runs the same command and fails if the committed file differs, so the table can be +neither edited by hand nor left behind when the data changes. + +To take a newer CLDR release, replace the two files and the version, then regenerate: + + V=48.2.0 + curl -sfo cldr/currencies-en-001.json https://unpkg.com/cldr-numbers-full@$V/main/en-001/currencies.json + curl -sfo cldr/currencyData.json https://unpkg.com/cldr-core@$V/supplemental/currencyData.json + echo $V > cldr/VERSION + node generate.mjs + +The two files are vendored rather than installed because the packages carrying them are +forty megabytes of every locale on earth, and this needs one locale and one supplemental +table. Vendoring also keeps the build off the network. Every currency CLDR carries is generated, not a curated subset — the engine renders templates for every integration, not just one, and a subset would silently downgrade any diff --git a/Tools/currency-table/cldr/VERSION b/Tools/currency-table/cldr/VERSION new file mode 100644 index 0000000..7ff0d17 --- /dev/null +++ b/Tools/currency-table/cldr/VERSION @@ -0,0 +1 @@ +48.2.0 diff --git a/Tools/currency-table/cldr/currencies-en-001.json b/Tools/currency-table/cldr/currencies-en-001.json new file mode 100644 index 0000000..fd1b895 --- /dev/null +++ b/Tools/currency-table/cldr/currencies-en-001.json @@ -0,0 +1,1812 @@ +{ + "main": { + "en-001": { + "identity": { + "language": "en", + "territory": "001" + }, + "numbers": { + "currencies": { + "ADP": { + "displayName": "Andorran Peseta", + "displayName-count-one": "Andorran peseta", + "displayName-count-other": "Andorran pesetas" + }, + "AED": { + "displayName": "United Arab Emirates Dirham", + "displayName-count-one": "UAE dirham", + "displayName-count-other": "UAE dirhams", + "symbol": "AED" + }, + "AFA": { + "displayName": "Afghan Afghani (1927–2002)", + "displayName-count-one": "Afghan afghani (1927–2002)", + "displayName-count-other": "Afghan afghanis (1927–2002)" + }, + "AFN": { + "displayName": "Afghan Afghani", + "displayName-count-one": "Afghan afghani", + "displayName-count-other": "Afghan afghanis", + "symbol": "AFN", + "symbol-alt-narrow": "؋" + }, + "ALK": { + "displayName": "Albanian Lek (1946–1965)", + "displayName-count-one": "Albanian lek (1946–1965)", + "displayName-count-other": "Albanian lekë (1946–1965)" + }, + "ALL": { + "displayName": "Albanian Lek", + "displayName-count-one": "Albanian lek", + "displayName-count-other": "Albanian lekë", + "symbol": "ALL" + }, + "AMD": { + "displayName": "Armenian Dram", + "displayName-count-one": "Armenian dram", + "displayName-count-other": "Armenian drams", + "symbol": "AMD", + "symbol-alt-narrow": "֏" + }, + "ANG": { + "displayName": "Netherlands Antillean Guilder", + "displayName-count-one": "Netherlands Antillean guilder", + "displayName-count-other": "Netherlands Antillean guilders", + "symbol": "ANG" + }, + "AOA": { + "displayName": "Angolan Kwanza", + "displayName-count-one": "Angolan kwanza", + "displayName-count-other": "Angolan kwanzas", + "symbol": "AOA", + "symbol-alt-narrow": "Kz" + }, + "AOK": { + "displayName": "Angolan Kwanza (1977–1991)", + "displayName-count-one": "Angolan kwanza (1977–1991)", + "displayName-count-other": "Angolan kwanzas (1977–1991)" + }, + "AON": { + "displayName": "Angolan New Kwanza (1990–2000)", + "displayName-count-one": "Angolan new kwanza (1990–2000)", + "displayName-count-other": "Angolan new kwanzas (1990–2000)" + }, + "AOR": { + "displayName": "Angolan Readjusted Kwanza (1995–1999)", + "displayName-count-one": "Angolan readjusted kwanza (1995–1999)", + "displayName-count-other": "Angolan readjusted kwanzas (1995–1999)" + }, + "ARA": { + "displayName": "Argentine Austral", + "displayName-count-one": "Argentine austral", + "displayName-count-other": "Argentine australs" + }, + "ARL": { + "displayName": "Argentine Peso Ley (1970–1983)", + "displayName-count-one": "Argentine peso ley (1970–1983)", + "displayName-count-other": "Argentine pesos ley (1970–1983)" + }, + "ARM": { + "displayName": "Argentine Peso (1881–1970)", + "displayName-count-one": "Argentine peso (1881–1970)", + "displayName-count-other": "Argentine pesos (1881–1970)" + }, + "ARP": { + "displayName": "Argentine Peso (1983–1985)", + "displayName-count-one": "Argentine peso (1983–1985)", + "displayName-count-other": "Argentine pesos (1983–1985)" + }, + "ARS": { + "displayName": "Argentine Peso", + "displayName-count-one": "Argentine peso", + "displayName-count-other": "Argentine pesos", + "symbol": "ARS", + "symbol-alt-narrow": "$" + }, + "ATS": { + "displayName": "Austrian Schilling", + "displayName-count-one": "Austrian schilling", + "displayName-count-other": "Austrian schillings" + }, + "AUD": { + "displayName": "Australian Dollar", + "displayName-count-one": "Australian dollar", + "displayName-count-other": "Australian dollars", + "symbol": "A$", + "symbol-alt-narrow": "$" + }, + "AWG": { + "displayName": "Aruban Florin", + "displayName-count-one": "Aruban florin", + "displayName-count-other": "Aruban florin", + "symbol": "AWG" + }, + "AZM": { + "displayName": "Azerbaijani Manat (1993–2006)", + "displayName-count-one": "Azerbaijani manat (1993–2006)", + "displayName-count-other": "Azerbaijani manats (1993–2006)" + }, + "AZN": { + "displayName": "Azerbaijani Manat", + "displayName-count-one": "Azerbaijani manat", + "displayName-count-other": "Azerbaijani manats", + "symbol": "AZN", + "symbol-alt-narrow": "₼" + }, + "BAD": { + "displayName": "Bosnia-Herzegovina Dinar (1992–1994)", + "displayName-count-one": "Bosnia-Herzegovina dinar (1992–1994)", + "displayName-count-other": "Bosnia-Herzegovina dinars (1992–1994)" + }, + "BAM": { + "displayName": "Bosnia-Herzegovina Convertible Mark", + "displayName-count-one": "Bosnia-Herzegovina convertible mark", + "displayName-count-other": "Bosnia-Herzegovina convertible marks", + "symbol": "BAM", + "symbol-alt-narrow": "KM" + }, + "BAN": { + "displayName": "Bosnia-Herzegovina New Dinar (1994–1997)", + "displayName-count-one": "Bosnia-Herzegovina new dinar (1994–1997)", + "displayName-count-other": "Bosnia-Herzegovina new dinars (1994–1997)" + }, + "BBD": { + "displayName": "Barbadian Dollar", + "displayName-count-one": "Barbadian dollar", + "displayName-count-other": "Barbadian dollars", + "symbol": "BBD", + "symbol-alt-narrow": "$" + }, + "BDT": { + "displayName": "Bangladeshi Taka", + "displayName-count-one": "Bangladeshi taka", + "displayName-count-other": "Bangladeshi takas", + "symbol": "BDT", + "symbol-alt-narrow": "৳" + }, + "BEC": { + "displayName": "Belgian Franc (convertible)", + "displayName-count-one": "Belgian franc (convertible)", + "displayName-count-other": "Belgian francs (convertible)" + }, + "BEF": { + "displayName": "Belgian Franc", + "displayName-count-one": "Belgian franc", + "displayName-count-other": "Belgian francs" + }, + "BEL": { + "displayName": "Belgian Franc (financial)", + "displayName-count-one": "Belgian franc (financial)", + "displayName-count-other": "Belgian francs (financial)" + }, + "BGL": { + "displayName": "Bulgarian Hard Lev", + "displayName-count-one": "Bulgarian hard lev", + "displayName-count-other": "Bulgarian hard leva" + }, + "BGM": { + "displayName": "Bulgarian Socialist Lev", + "displayName-count-one": "Bulgarian socialist lev", + "displayName-count-other": "Bulgarian socialist leva" + }, + "BGN": { + "displayName": "Bulgarian Lev", + "displayName-count-one": "Bulgarian lev", + "displayName-count-other": "Bulgarian leva", + "symbol": "BGN" + }, + "BGO": { + "displayName": "Bulgarian Lev (1879–1952)", + "displayName-count-one": "Bulgarian lev (1879–1952)", + "displayName-count-other": "Bulgarian leva (1879–1952)" + }, + "BHD": { + "displayName": "Bahraini Dinar", + "displayName-count-one": "Bahraini dinar", + "displayName-count-other": "Bahraini dinars", + "symbol": "BHD" + }, + "BIF": { + "displayName": "Burundian Franc", + "displayName-count-one": "Burundian franc", + "displayName-count-other": "Burundian francs", + "symbol": "BIF" + }, + "BMD": { + "displayName": "Bermudian Dollar", + "displayName-count-one": "Bermudian dollar", + "displayName-count-other": "Bermudian dollars", + "symbol": "BMD", + "symbol-alt-narrow": "$" + }, + "BND": { + "displayName": "Brunei Dollar", + "displayName-count-one": "Brunei dollar", + "displayName-count-other": "Brunei dollars", + "symbol": "BND", + "symbol-alt-narrow": "$" + }, + "BOB": { + "displayName": "Bolivian Boliviano", + "displayName-count-one": "Bolivian boliviano", + "displayName-count-other": "Bolivian bolivianos", + "symbol": "BOB", + "symbol-alt-narrow": "Bs" + }, + "BOL": { + "displayName": "Bolivian Boliviano (1863–1963)", + "displayName-count-one": "Bolivian boliviano (1863–1963)", + "displayName-count-other": "Bolivian bolivianos (1863–1963)" + }, + "BOP": { + "displayName": "Bolivian Peso", + "displayName-count-one": "Bolivian peso", + "displayName-count-other": "Bolivian pesos" + }, + "BOV": { + "displayName": "Bolivian Mvdol", + "displayName-count-one": "Bolivian mvdol", + "displayName-count-other": "Bolivian mvdols" + }, + "BRB": { + "displayName": "Brazilian New Cruzeiro (1967–1986)", + "displayName-count-one": "Brazilian new cruzeiro (1967–1986)", + "displayName-count-other": "Brazilian new cruzeiros (1967–1986)" + }, + "BRC": { + "displayName": "Brazilian Cruzado (1986–1989)", + "displayName-count-one": "Brazilian cruzado (1986–1989)", + "displayName-count-other": "Brazilian cruzados (1986–1989)" + }, + "BRE": { + "displayName": "Brazilian Cruzeiro (1990–1993)", + "displayName-count-one": "Brazilian cruzeiro (1990–1993)", + "displayName-count-other": "Brazilian cruzeiros (1990–1993)" + }, + "BRL": { + "displayName": "Brazilian Real", + "displayName-count-one": "Brazilian real", + "displayName-count-other": "Brazilian reals", + "symbol": "R$", + "symbol-alt-narrow": "R$" + }, + "BRN": { + "displayName": "Brazilian New Cruzado (1989–1990)", + "displayName-count-one": "Brazilian new cruzado (1989–1990)", + "displayName-count-other": "Brazilian new cruzados (1989–1990)" + }, + "BRR": { + "displayName": "Brazilian Cruzeiro (1993–1994)", + "displayName-count-one": "Brazilian cruzeiro (1993–1994)", + "displayName-count-other": "Brazilian cruzeiros (1993–1994)" + }, + "BRZ": { + "displayName": "Brazilian Cruzeiro (1942–1967)", + "displayName-count-one": "Brazilian cruzeiro (1942–1967)", + "displayName-count-other": "Brazilian cruzeiros (1942–1967)" + }, + "BSD": { + "displayName": "Bahamian Dollar", + "displayName-count-one": "Bahamian dollar", + "displayName-count-other": "Bahamian dollars", + "symbol": "BSD", + "symbol-alt-narrow": "$" + }, + "BTN": { + "displayName": "Bhutanese Ngultrum", + "displayName-count-one": "Bhutanese ngultrum", + "displayName-count-other": "Bhutanese ngultrums", + "symbol": "BTN" + }, + "BUK": { + "displayName": "Burmese Kyat", + "displayName-count-one": "Burmese kyat", + "displayName-count-other": "Burmese kyats" + }, + "BWP": { + "displayName": "Botswanan Pula", + "displayName-count-one": "Botswanan pula", + "displayName-count-other": "Botswanan pulas", + "symbol": "BWP", + "symbol-alt-narrow": "P" + }, + "BYB": { + "displayName": "Belarusian New Rouble (1994–1999)", + "displayName-count-one": "Belarusian new rouble (1994–1999)", + "displayName-count-other": "Belarusian new roubles (1994–1999)" + }, + "BYN": { + "displayName": "Belarusian Rouble", + "displayName-count-one": "Belarusian rouble", + "displayName-count-other": "Belarusian roubles", + "symbol": "BYN" + }, + "BYR": { + "displayName": "Belarusian Rouble (2000–2016)", + "displayName-count-one": "Belarusian rouble (2000–2016)", + "displayName-count-other": "Belarusian roubles (2000–2016)" + }, + "BZD": { + "displayName": "Belize Dollar", + "displayName-count-one": "Belize dollar", + "displayName-count-other": "Belize dollars", + "symbol": "BZD", + "symbol-alt-narrow": "$" + }, + "CAD": { + "displayName": "Canadian Dollar", + "displayName-count-one": "Canadian dollar", + "displayName-count-other": "Canadian dollars", + "symbol": "CA$", + "symbol-alt-narrow": "$" + }, + "CDF": { + "displayName": "Congolese Franc", + "displayName-count-one": "Congolese franc", + "displayName-count-other": "Congolese francs", + "symbol": "CDF" + }, + "CHE": { + "displayName": "WIR Euro", + "displayName-count-one": "WIR euro", + "displayName-count-other": "WIR euros" + }, + "CHF": { + "displayName": "Swiss Franc", + "displayName-count-one": "Swiss franc", + "displayName-count-other": "Swiss francs", + "symbol": "CHF" + }, + "CHW": { + "displayName": "WIR Franc", + "displayName-count-one": "WIR franc", + "displayName-count-other": "WIR francs" + }, + "CLE": { + "displayName": "Chilean Escudo", + "displayName-count-one": "Chilean escudo", + "displayName-count-other": "Chilean escudos" + }, + "CLF": { + "displayName": "Chilean Unit of Account (UF)", + "displayName-count-one": "Chilean unit of account (UF)", + "displayName-count-other": "Chilean units of account (UF)" + }, + "CLP": { + "displayName": "Chilean Peso", + "displayName-count-one": "Chilean peso", + "displayName-count-other": "Chilean pesos", + "symbol": "CLP", + "symbol-alt-narrow": "$" + }, + "CNH": { + "displayName": "Chinese Yuan (offshore)", + "displayName-count-one": "Chinese yuan (offshore)", + "displayName-count-other": "Chinese yuan (offshore)", + "symbol": "CNH" + }, + "CNX": { + "displayName": "Chinese People’s Bank Dollar", + "displayName-count-one": "Chinese People’s Bank dollar", + "displayName-count-other": "Chinese People’s Bank dollars" + }, + "CNY": { + "displayName": "Chinese Yuan", + "displayName-count-one": "Chinese yuan", + "displayName-count-other": "Chinese yuan", + "symbol": "CN¥", + "symbol-alt-narrow": "¥" + }, + "COP": { + "displayName": "Colombian Peso", + "displayName-count-one": "Colombian peso", + "displayName-count-other": "Colombian pesos", + "symbol": "COP", + "symbol-alt-narrow": "$" + }, + "COU": { + "displayName": "Colombian Real Value Unit", + "displayName-count-one": "Colombian real value unit", + "displayName-count-other": "Colombian real value units" + }, + "CRC": { + "displayName": "Costa Rican Colón", + "displayName-count-one": "Costa Rican colón", + "displayName-count-other": "Costa Rican colóns", + "symbol": "CRC", + "symbol-alt-narrow": "₡" + }, + "CSD": { + "displayName": "Serbian Dinar (2002–2006)", + "displayName-count-one": "Serbian dinar (2002–2006)", + "displayName-count-other": "Serbian dinars (2002–2006)" + }, + "CSK": { + "displayName": "Czechoslovak Hard Koruna", + "displayName-count-one": "Czechoslovak hard koruna", + "displayName-count-other": "Czechoslovak hard korunas" + }, + "CUC": { + "displayName": "Cuban Convertible Peso", + "displayName-count-one": "Cuban convertible peso", + "displayName-count-other": "Cuban convertible pesos", + "symbol": "CUC", + "symbol-alt-narrow": "$" + }, + "CUP": { + "displayName": "Cuban Peso", + "displayName-count-one": "Cuban peso", + "displayName-count-other": "Cuban pesos", + "symbol": "CUP", + "symbol-alt-narrow": "$" + }, + "CVE": { + "displayName": "Cape Verdean Escudo", + "displayName-count-one": "Cape Verdean escudo", + "displayName-count-other": "Cape Verdean escudos", + "symbol": "CVE" + }, + "CYP": { + "displayName": "Cypriot Pound", + "displayName-count-one": "Cypriot pound", + "displayName-count-other": "Cypriot pounds" + }, + "CZK": { + "displayName": "Czech Koruna", + "displayName-count-one": "Czech koruna", + "displayName-count-other": "Czech korunas", + "symbol": "CZK", + "symbol-alt-narrow": "Kč" + }, + "DDM": { + "displayName": "East German Mark", + "displayName-count-one": "East German mark", + "displayName-count-other": "East German marks" + }, + "DEM": { + "displayName": "German Mark", + "displayName-count-one": "German mark", + "displayName-count-other": "German marks" + }, + "DJF": { + "displayName": "Djiboutian Franc", + "displayName-count-one": "Djiboutian franc", + "displayName-count-other": "Djiboutian francs", + "symbol": "DJF" + }, + "DKK": { + "displayName": "Danish Krone", + "displayName-count-one": "Danish krone", + "displayName-count-other": "Danish kroner", + "symbol": "DKK", + "symbol-alt-narrow": "kr" + }, + "DOP": { + "displayName": "Dominican Peso", + "displayName-count-one": "Dominican peso", + "displayName-count-other": "Dominican pesos", + "symbol": "DOP", + "symbol-alt-narrow": "$" + }, + "DZD": { + "displayName": "Algerian Dinar", + "displayName-count-one": "Algerian dinar", + "displayName-count-other": "Algerian dinars", + "symbol": "DZD" + }, + "ECS": { + "displayName": "Ecuadorian Sucre", + "displayName-count-one": "Ecuadorian sucre", + "displayName-count-other": "Ecuadorian sucres" + }, + "ECV": { + "displayName": "Ecuadorian Unit of Constant Value", + "displayName-count-one": "Ecuadorian unit of constant value", + "displayName-count-other": "Ecuadorian units of constant value" + }, + "EEK": { + "displayName": "Estonian Kroon", + "displayName-count-one": "Estonian kroon", + "displayName-count-other": "Estonian kroons" + }, + "EGP": { + "displayName": "Egyptian Pound", + "displayName-count-one": "Egyptian pound", + "displayName-count-other": "Egyptian pounds", + "symbol": "EGP", + "symbol-alt-narrow": "E£" + }, + "ERN": { + "displayName": "Eritrean Nakfa", + "displayName-count-one": "Eritrean nakfa", + "displayName-count-other": "Eritrean nakfas", + "symbol": "ERN" + }, + "ESA": { + "displayName": "Spanish Peseta (A account)", + "displayName-count-one": "Spanish peseta (A account)", + "displayName-count-other": "Spanish pesetas (A account)" + }, + "ESB": { + "displayName": "Spanish Peseta (convertible account)", + "displayName-count-one": "Spanish peseta (convertible account)", + "displayName-count-other": "Spanish pesetas (convertible account)" + }, + "ESP": { + "displayName": "Spanish Peseta", + "displayName-count-one": "Spanish peseta", + "displayName-count-other": "Spanish pesetas", + "symbol-alt-narrow": "₧" + }, + "ETB": { + "displayName": "Ethiopian Birr", + "displayName-count-one": "Ethiopian birr", + "displayName-count-other": "Ethiopian birr", + "symbol": "ETB" + }, + "EUR": { + "displayName": "Euro", + "displayName-count-one": "euro", + "displayName-count-other": "euros", + "symbol": "€", + "symbol-alt-narrow": "€" + }, + "FIM": { + "displayName": "Finnish Markka", + "displayName-count-one": "Finnish markka", + "displayName-count-other": "Finnish markkas" + }, + "FJD": { + "displayName": "Fijian Dollar", + "displayName-count-one": "Fijian dollar", + "displayName-count-other": "Fijian dollars", + "symbol": "FJD", + "symbol-alt-narrow": "$" + }, + "FKP": { + "displayName": "Falkland Islands Pound", + "displayName-count-one": "Falkland Islands pound", + "displayName-count-other": "Falkland Islands pounds", + "symbol": "FKP", + "symbol-alt-narrow": "£" + }, + "FRF": { + "displayName": "French Franc", + "displayName-count-one": "French franc", + "displayName-count-other": "French francs" + }, + "GBP": { + "displayName": "British Pound", + "displayName-count-one": "British pound", + "displayName-count-other": "British pounds", + "symbol": "£", + "symbol-alt-narrow": "£" + }, + "GEK": { + "displayName": "Georgian Kupon Larit", + "displayName-count-one": "Georgian kupon larit", + "displayName-count-other": "Georgian kupon larits" + }, + "GEL": { + "displayName": "Georgian Lari", + "displayName-count-one": "Georgian lari", + "displayName-count-other": "Georgian laris", + "symbol": "GEL", + "symbol-alt-narrow": "₾" + }, + "GHC": { + "displayName": "Ghanaian Cedi (1979–2007)", + "displayName-count-one": "Ghanaian cedi (1979–2007)", + "displayName-count-other": "Ghanaian cedis (1979–2007)" + }, + "GHS": { + "displayName": "Ghanaian Cedi", + "displayName-count-one": "Ghanaian cedi", + "displayName-count-other": "Ghanaian cedis", + "symbol": "GHS", + "symbol-alt-narrow": "GH₵" + }, + "GIP": { + "displayName": "Gibraltar Pound", + "displayName-count-one": "Gibraltar pound", + "displayName-count-other": "Gibraltar pounds", + "symbol": "GIP", + "symbol-alt-narrow": "£" + }, + "GMD": { + "displayName": "Gambian Dalasi", + "displayName-count-one": "Gambian dalasi", + "displayName-count-other": "Gambian dalasis", + "symbol": "GMD" + }, + "GNF": { + "displayName": "Guinean Franc", + "displayName-count-one": "Guinean franc", + "displayName-count-other": "Guinean francs", + "symbol": "GNF", + "symbol-alt-narrow": "FG" + }, + "GNS": { + "displayName": "Guinean Syli", + "displayName-count-one": "Guinean syli", + "displayName-count-other": "Guinean sylis" + }, + "GQE": { + "displayName": "Equatorial Guinean Ekwele", + "displayName-count-one": "Equatorial Guinean ekwele", + "displayName-count-other": "Equatorial Guinean ekwele" + }, + "GRD": { + "displayName": "Greek Drachma", + "displayName-count-one": "Greek drachma", + "displayName-count-other": "Greek drachmas" + }, + "GTQ": { + "displayName": "Guatemalan Quetzal", + "displayName-count-one": "Guatemalan quetzal", + "displayName-count-other": "Guatemalan quetzals", + "symbol": "GTQ", + "symbol-alt-narrow": "Q" + }, + "GWE": { + "displayName": "Portuguese Guinea Escudo", + "displayName-count-one": "Portuguese Guinea escudo", + "displayName-count-other": "Portuguese Guinea escudos" + }, + "GWP": { + "displayName": "Guinea-Bissau Peso", + "displayName-count-one": "Guinea-Bissau peso", + "displayName-count-other": "Guinea-Bissau pesos" + }, + "GYD": { + "displayName": "Guyanaese Dollar", + "displayName-count-one": "Guyanaese dollar", + "displayName-count-other": "Guyanaese dollars", + "symbol": "GYD", + "symbol-alt-narrow": "$" + }, + "HKD": { + "displayName": "Hong Kong Dollar", + "displayName-count-one": "Hong Kong dollar", + "displayName-count-other": "Hong Kong dollars", + "symbol": "HK$", + "symbol-alt-narrow": "$" + }, + "HNL": { + "displayName": "Honduran Lempira", + "displayName-count-one": "Honduran lempira", + "displayName-count-other": "Honduran lempiras", + "symbol": "HNL", + "symbol-alt-narrow": "L" + }, + "HRD": { + "displayName": "Croatian Dinar", + "displayName-count-one": "Croatian dinar", + "displayName-count-other": "Croatian dinars" + }, + "HRK": { + "displayName": "Croatian Kuna", + "displayName-count-one": "Croatian kuna", + "displayName-count-other": "Croatian kunas", + "symbol": "HRK", + "symbol-alt-narrow": "kn" + }, + "HTG": { + "displayName": "Haitian Gourde", + "displayName-count-one": "Haitian gourde", + "displayName-count-other": "Haitian gourdes", + "symbol": "HTG" + }, + "HUF": { + "displayName": "Hungarian Forint", + "displayName-count-one": "Hungarian forint", + "displayName-count-other": "Hungarian forints", + "symbol": "HUF", + "symbol-alt-narrow": "Ft" + }, + "IDR": { + "displayName": "Indonesian Rupiah", + "displayName-count-one": "Indonesian rupiah", + "displayName-count-other": "Indonesian rupiahs", + "symbol": "IDR", + "symbol-alt-narrow": "Rp" + }, + "IEP": { + "displayName": "Irish Pound", + "displayName-count-one": "Irish pound", + "displayName-count-other": "Irish pounds" + }, + "ILP": { + "displayName": "Israeli Pound", + "displayName-count-one": "Israeli pound", + "displayName-count-other": "Israeli pounds" + }, + "ILR": { + "displayName": "Israeli Shekel (1980–1985)", + "displayName-count-one": "Israeli shekel (1980–1985)", + "displayName-count-other": "Israeli shekels (1980–1985)" + }, + "ILS": { + "displayName": "Israeli New Shekel", + "displayName-count-one": "Israeli new shekel", + "displayName-count-other": "Israeli new shekels", + "symbol": "₪", + "symbol-alt-narrow": "₪" + }, + "INR": { + "displayName": "Indian Rupee", + "displayName-count-one": "Indian rupee", + "displayName-count-other": "Indian rupees", + "symbol": "₹", + "symbol-alt-narrow": "₹" + }, + "IQD": { + "displayName": "Iraqi Dinar", + "displayName-count-one": "Iraqi dinar", + "displayName-count-other": "Iraqi dinars", + "symbol": "IQD" + }, + "IRR": { + "displayName": "Iranian Rial", + "displayName-count-one": "Iranian rial", + "displayName-count-other": "Iranian rials", + "symbol": "IRR" + }, + "ISJ": { + "displayName": "Icelandic Króna (1918–1981)", + "displayName-count-one": "Icelandic króna (1918–1981)", + "displayName-count-other": "Icelandic krónur (1918–1981)" + }, + "ISK": { + "displayName": "Icelandic Króna", + "displayName-count-one": "Icelandic króna", + "displayName-count-other": "Icelandic krónur", + "symbol": "ISK", + "symbol-alt-narrow": "kr" + }, + "ITL": { + "displayName": "Italian Lira", + "displayName-count-one": "Italian lira", + "displayName-count-other": "Italian liras" + }, + "JMD": { + "displayName": "Jamaican Dollar", + "displayName-count-one": "Jamaican dollar", + "displayName-count-other": "Jamaican dollars", + "symbol": "JMD", + "symbol-alt-narrow": "$" + }, + "JOD": { + "displayName": "Jordanian Dinar", + "displayName-count-one": "Jordanian dinar", + "displayName-count-other": "Jordanian dinars", + "symbol": "JOD" + }, + "JPY": { + "displayName": "Japanese Yen", + "displayName-count-one": "Japanese yen", + "displayName-count-other": "Japanese yen", + "symbol": "JP¥", + "symbol-alt-narrow": "¥" + }, + "KES": { + "displayName": "Kenyan Shilling", + "displayName-count-one": "Kenyan shilling", + "displayName-count-other": "Kenyan shillings", + "symbol": "KES" + }, + "KGS": { + "displayName": "Kyrgyz Som", + "displayName-count-one": "Kyrgyz som", + "displayName-count-other": "Kyrgyz soms", + "symbol": "KGS", + "symbol-alt-narrow": "⃀" + }, + "KHR": { + "displayName": "Cambodian Riel", + "displayName-count-one": "Cambodian riel", + "displayName-count-other": "Cambodian riels", + "symbol": "KHR", + "symbol-alt-narrow": "៛" + }, + "KMF": { + "displayName": "Comorian Franc", + "displayName-count-one": "Comorian franc", + "displayName-count-other": "Comorian francs", + "symbol": "KMF", + "symbol-alt-narrow": "CF" + }, + "KPW": { + "displayName": "North Korean Won", + "displayName-count-one": "North Korean won", + "displayName-count-other": "North Korean won", + "symbol": "KPW", + "symbol-alt-narrow": "₩" + }, + "KRH": { + "displayName": "South Korean Hwan (1953–1962)", + "displayName-count-one": "South Korean hwan (1953–1962)", + "displayName-count-other": "South Korean hwan (1953–1962)" + }, + "KRO": { + "displayName": "South Korean Won (1945–1953)", + "displayName-count-one": "South Korean won (1945–1953)", + "displayName-count-other": "South Korean won (1945–1953)" + }, + "KRW": { + "displayName": "South Korean Won", + "displayName-count-one": "South Korean won", + "displayName-count-other": "South Korean won", + "symbol": "₩", + "symbol-alt-narrow": "₩" + }, + "KWD": { + "displayName": "Kuwaiti Dinar", + "displayName-count-one": "Kuwaiti dinar", + "displayName-count-other": "Kuwaiti dinars", + "symbol": "KWD" + }, + "KYD": { + "displayName": "Cayman Islands Dollar", + "displayName-count-one": "Cayman Islands dollar", + "displayName-count-other": "Cayman Islands dollars", + "symbol": "KYD", + "symbol-alt-narrow": "$" + }, + "KZT": { + "displayName": "Kazakhstani Tenge", + "displayName-count-one": "Kazakhstani tenge", + "displayName-count-other": "Kazakhstani tenges", + "symbol": "KZT", + "symbol-alt-narrow": "₸" + }, + "LAK": { + "displayName": "Laotian Kip", + "displayName-count-one": "Laotian kip", + "displayName-count-other": "Laotian kips", + "symbol": "LAK", + "symbol-alt-narrow": "₭" + }, + "LBP": { + "displayName": "Lebanese Pound", + "displayName-count-one": "Lebanese pound", + "displayName-count-other": "Lebanese pounds", + "symbol": "LBP", + "symbol-alt-narrow": "L£" + }, + "LKR": { + "displayName": "Sri Lankan Rupee", + "displayName-count-one": "Sri Lankan rupee", + "displayName-count-other": "Sri Lankan rupees", + "symbol": "LKR", + "symbol-alt-narrow": "Rs" + }, + "LRD": { + "displayName": "Liberian Dollar", + "displayName-count-one": "Liberian dollar", + "displayName-count-other": "Liberian dollars", + "symbol": "LRD", + "symbol-alt-narrow": "$" + }, + "LSL": { + "displayName": "Lesotho Loti", + "displayName-count-one": "Lesotho loti", + "displayName-count-other": "Lesotho maloti", + "symbol": "LSL" + }, + "LTL": { + "displayName": "Lithuanian Litas", + "displayName-count-one": "Lithuanian litas", + "displayName-count-other": "Lithuanian litai", + "symbol-alt-narrow": "Lt" + }, + "LTT": { + "displayName": "Lithuanian Talonas", + "displayName-count-one": "Lithuanian talonas", + "displayName-count-other": "Lithuanian talonases" + }, + "LUC": { + "displayName": "Luxembourgian Convertible Franc", + "displayName-count-one": "Luxembourgian convertible franc", + "displayName-count-other": "Luxembourgian convertible francs" + }, + "LUF": { + "displayName": "Luxembourgian Franc", + "displayName-count-one": "Luxembourgian franc", + "displayName-count-other": "Luxembourgian francs" + }, + "LUL": { + "displayName": "Luxembourg Financial Franc", + "displayName-count-one": "Luxembourg financial franc", + "displayName-count-other": "Luxembourg financial francs" + }, + "LVL": { + "displayName": "Latvian Lats", + "displayName-count-one": "Latvian lats", + "displayName-count-other": "Latvian lati", + "symbol-alt-narrow": "Ls" + }, + "LVR": { + "displayName": "Latvian Rouble", + "displayName-count-one": "Latvian rouble", + "displayName-count-other": "Latvian roubles" + }, + "LYD": { + "displayName": "Libyan Dinar", + "displayName-count-one": "Libyan dinar", + "displayName-count-other": "Libyan dinars", + "symbol": "LYD" + }, + "MAD": { + "displayName": "Moroccan Dirham", + "displayName-count-one": "Moroccan dirham", + "displayName-count-other": "Moroccan dirhams", + "symbol": "MAD" + }, + "MAF": { + "displayName": "Moroccan Franc", + "displayName-count-one": "Moroccan franc", + "displayName-count-other": "Moroccan francs" + }, + "MCF": { + "displayName": "Monegasque Franc", + "displayName-count-one": "Monegasque franc", + "displayName-count-other": "Monegasque francs" + }, + "MDC": { + "displayName": "Moldovan Cupon", + "displayName-count-one": "Moldovan cupon", + "displayName-count-other": "Moldovan cupon" + }, + "MDL": { + "displayName": "Moldovan Leu", + "displayName-count-one": "Moldovan leu", + "displayName-count-other": "Moldovan lei", + "symbol": "MDL" + }, + "MGA": { + "displayName": "Malagasy Ariary", + "displayName-count-one": "Malagasy ariary", + "displayName-count-other": "Malagasy ariary", + "symbol": "MGA", + "symbol-alt-narrow": "Ar" + }, + "MGF": { + "displayName": "Malagasy Franc", + "displayName-count-one": "Malagasy franc", + "displayName-count-other": "Malagasy francs" + }, + "MKD": { + "displayName": "Macedonian Denar", + "displayName-count-one": "Macedonian denar", + "displayName-count-other": "Macedonian denari", + "symbol": "MKD" + }, + "MKN": { + "displayName": "Macedonian Denar (1992–1993)", + "displayName-count-one": "Macedonian denar (1992–1993)", + "displayName-count-other": "Macedonian denari (1992–1993)" + }, + "MLF": { + "displayName": "Malian Franc", + "displayName-count-one": "Malian franc", + "displayName-count-other": "Malian francs" + }, + "MMK": { + "displayName": "Myanmar Kyat", + "displayName-count-one": "Myanmar kyat", + "displayName-count-other": "Myanmar kyats", + "symbol": "MMK", + "symbol-alt-narrow": "K" + }, + "MNT": { + "displayName": "Mongolian Tugrik", + "displayName-count-one": "Mongolian tugrik", + "displayName-count-other": "Mongolian tugriks", + "symbol": "MNT", + "symbol-alt-narrow": "₮" + }, + "MOP": { + "displayName": "Macanese Pataca", + "displayName-count-one": "Macanese pataca", + "displayName-count-other": "Macanese patacas", + "symbol": "MOP" + }, + "MRO": { + "displayName": "Mauritanian Ouguiya (1973–2017)", + "displayName-count-one": "Mauritanian ouguiya (1973–2017)", + "displayName-count-other": "Mauritanian ouguiyas (1973–2017)" + }, + "MRU": { + "displayName": "Mauritanian Ouguiya", + "displayName-count-one": "Mauritanian ouguiya", + "displayName-count-other": "Mauritanian ouguiyas", + "symbol": "MRU" + }, + "MTL": { + "displayName": "Maltese Lira", + "displayName-count-one": "Maltese lira", + "displayName-count-other": "Maltese lira" + }, + "MTP": { + "displayName": "Maltese Pound", + "displayName-count-one": "Maltese pound", + "displayName-count-other": "Maltese pounds" + }, + "MUR": { + "displayName": "Mauritian Rupee", + "displayName-count-one": "Mauritian rupee", + "displayName-count-other": "Mauritian rupees", + "symbol": "MUR", + "symbol-alt-narrow": "Rs" + }, + "MVP": { + "displayName": "Maldivian Rupee (1947–1981)", + "displayName-count-one": "Maldivian rupee (1947–1981)", + "displayName-count-other": "Maldivian rupees (1947–1981)" + }, + "MVR": { + "displayName": "Maldivian Rufiyaa", + "displayName-count-one": "Maldivian rufiyaa", + "displayName-count-other": "Maldivian rufiyaa", + "symbol": "MVR" + }, + "MWK": { + "displayName": "Malawian Kwacha", + "displayName-count-one": "Malawian kwacha", + "displayName-count-other": "Malawian kwachas", + "symbol": "MWK" + }, + "MXN": { + "displayName": "Mexican Peso", + "displayName-count-one": "Mexican peso", + "displayName-count-other": "Mexican pesos", + "symbol": "MX$", + "symbol-alt-narrow": "$" + }, + "MXP": { + "displayName": "Mexican Silver Peso (1861–1992)", + "displayName-count-one": "Mexican silver peso (1861–1992)", + "displayName-count-other": "Mexican silver pesos (1861–1992)" + }, + "MXV": { + "displayName": "Mexican Investment Unit", + "displayName-count-one": "Mexican investment unit", + "displayName-count-other": "Mexican investment units" + }, + "MYR": { + "displayName": "Malaysian Ringgit", + "displayName-count-one": "Malaysian ringgit", + "displayName-count-other": "Malaysian ringgits", + "symbol": "MYR", + "symbol-alt-narrow": "RM" + }, + "MZE": { + "displayName": "Mozambican Escudo", + "displayName-count-one": "Mozambican escudo", + "displayName-count-other": "Mozambican escudos" + }, + "MZM": { + "displayName": "Mozambican Metical (1980–2006)", + "displayName-count-one": "Mozambican metical (1980–2006)", + "displayName-count-other": "Mozambican meticals (1980–2006)" + }, + "MZN": { + "displayName": "Mozambican Metical", + "displayName-count-one": "Mozambican metical", + "displayName-count-other": "Mozambican meticals", + "symbol": "MZN" + }, + "NAD": { + "displayName": "Namibian Dollar", + "displayName-count-one": "Namibian dollar", + "displayName-count-other": "Namibian dollars", + "symbol": "NAD", + "symbol-alt-narrow": "$" + }, + "NGN": { + "displayName": "Nigerian Naira", + "displayName-count-one": "Nigerian naira", + "displayName-count-other": "Nigerian nairas", + "symbol": "NGN", + "symbol-alt-narrow": "₦" + }, + "NIC": { + "displayName": "Nicaraguan Córdoba (1988–1991)", + "displayName-count-one": "Nicaraguan córdoba (1988–1991)", + "displayName-count-other": "Nicaraguan córdobas (1988–1991)" + }, + "NIO": { + "displayName": "Nicaraguan Córdoba", + "displayName-count-one": "Nicaraguan córdoba", + "displayName-count-other": "Nicaraguan córdobas", + "symbol": "NIO", + "symbol-alt-narrow": "C$" + }, + "NLG": { + "displayName": "Dutch Guilder", + "displayName-count-one": "Dutch guilder", + "displayName-count-other": "Dutch guilders" + }, + "NOK": { + "displayName": "Norwegian Krone", + "displayName-count-one": "Norwegian krone", + "displayName-count-other": "Norwegian kroner", + "symbol": "NOK", + "symbol-alt-narrow": "kr" + }, + "NPR": { + "displayName": "Nepalese Rupee", + "displayName-count-one": "Nepalese rupee", + "displayName-count-other": "Nepalese rupees", + "symbol": "NPR", + "symbol-alt-narrow": "Rs" + }, + "NZD": { + "displayName": "New Zealand Dollar", + "displayName-count-one": "New Zealand dollar", + "displayName-count-other": "New Zealand dollars", + "symbol": "NZ$", + "symbol-alt-narrow": "$" + }, + "OMR": { + "displayName": "Omani Rial", + "displayName-count-one": "Omani rial", + "displayName-count-other": "Omani rials", + "symbol": "OMR" + }, + "PAB": { + "displayName": "Panamanian Balboa", + "displayName-count-one": "Panamanian balboa", + "displayName-count-other": "Panamanian balboas", + "symbol": "PAB" + }, + "PEI": { + "displayName": "Peruvian Inti", + "displayName-count-one": "Peruvian inti", + "displayName-count-other": "Peruvian intis" + }, + "PEN": { + "displayName": "Peruvian Sol", + "displayName-count-one": "Peruvian sol", + "displayName-count-other": "Peruvian soles", + "symbol": "PEN" + }, + "PES": { + "displayName": "Peruvian Sol (1863–1965)", + "displayName-count-one": "Peruvian sol (1863–1965)", + "displayName-count-other": "Peruvian soles (1863–1965)" + }, + "PGK": { + "displayName": "Papua New Guinean Kina", + "displayName-count-one": "Papua New Guinean kina", + "displayName-count-other": "Papua New Guinean kina", + "symbol": "PGK" + }, + "PHP": { + "displayName": "Philippine Peso", + "displayName-count-one": "Philippine peso", + "displayName-count-other": "Philippine pesos", + "symbol": "₱", + "symbol-alt-narrow": "₱" + }, + "PKR": { + "displayName": "Pakistani Rupee", + "displayName-count-one": "Pakistani rupee", + "displayName-count-other": "Pakistani rupees", + "symbol": "PKR", + "symbol-alt-narrow": "Rs" + }, + "PLN": { + "displayName": "Polish Zloty", + "displayName-count-one": "Polish zloty", + "displayName-count-other": "Polish zlotys", + "symbol": "PLN", + "symbol-alt-narrow": "zł" + }, + "PLZ": { + "displayName": "Polish Zloty (1950–1995)", + "displayName-count-one": "Polish zloty (PLZ)", + "displayName-count-other": "Polish zlotys (PLZ)" + }, + "PTE": { + "displayName": "Portuguese Escudo", + "displayName-count-one": "Portuguese escudo", + "displayName-count-other": "Portuguese escudos" + }, + "PYG": { + "displayName": "Paraguayan Guarani", + "displayName-count-one": "Paraguayan guarani", + "displayName-count-other": "Paraguayan guaranis", + "symbol": "PYG", + "symbol-alt-narrow": "₲" + }, + "QAR": { + "displayName": "Qatari Riyal", + "displayName-count-one": "Qatari riyal", + "displayName-count-other": "Qatari riyals", + "symbol": "QAR" + }, + "RHD": { + "displayName": "Rhodesian Dollar", + "displayName-count-one": "Rhodesian dollar", + "displayName-count-other": "Rhodesian dollars" + }, + "ROL": { + "displayName": "Romanian Leu (1952–2006)", + "displayName-count-one": "Romanian leu (1952–2006)", + "displayName-count-other": "Romanian Lei (1952–2006)" + }, + "RON": { + "displayName": "Romanian Leu", + "displayName-count-one": "Romanian leu", + "displayName-count-other": "Romanian lei", + "symbol": "RON", + "symbol-alt-narrow": "lei" + }, + "RSD": { + "displayName": "Serbian Dinar", + "displayName-count-one": "Serbian dinar", + "displayName-count-other": "Serbian dinars", + "symbol": "RSD" + }, + "RUB": { + "displayName": "Russian Rouble", + "displayName-count-one": "Russian rouble", + "displayName-count-other": "Russian roubles", + "symbol": "RUB", + "symbol-alt-narrow": "₽" + }, + "RUR": { + "displayName": "Russian Rouble (1991–1998)", + "displayName-count-one": "Russian rouble (1991–1998)", + "displayName-count-other": "Russian roubles (1991–1998)" + }, + "RWF": { + "displayName": "Rwandan Franc", + "displayName-count-one": "Rwandan franc", + "displayName-count-other": "Rwandan francs", + "symbol": "RWF", + "symbol-alt-narrow": "RF" + }, + "SAR": { + "displayName": "Saudi Riyal", + "displayName-count-one": "Saudi riyal", + "displayName-count-other": "Saudi riyals", + "symbol": "SAR", + "symbol-alt-variant": "⃁" + }, + "SBD": { + "displayName": "Solomon Islands Dollar", + "displayName-count-one": "Solomon Islands dollar", + "displayName-count-other": "Solomon Islands dollars", + "symbol": "SBD", + "symbol-alt-narrow": "$" + }, + "SCR": { + "displayName": "Seychellois Rupee", + "displayName-count-one": "Seychellois rupee", + "displayName-count-other": "Seychellois rupees", + "symbol": "SCR" + }, + "SDD": { + "displayName": "Sudanese Dinar (1992–2007)", + "displayName-count-one": "Sudanese dinar (1992–2007)", + "displayName-count-other": "Sudanese dinars (1992–2007)" + }, + "SDG": { + "displayName": "Sudanese Pound", + "displayName-count-one": "Sudanese pound", + "displayName-count-other": "Sudanese pounds", + "symbol": "SDG" + }, + "SDP": { + "displayName": "Sudanese Pound (1957–1998)", + "displayName-count-one": "Sudanese pound (1957–1998)", + "displayName-count-other": "Sudanese pounds (1957–1998)" + }, + "SEK": { + "displayName": "Swedish Krona", + "displayName-count-one": "Swedish krona", + "displayName-count-other": "Swedish kronor", + "symbol": "SEK", + "symbol-alt-narrow": "kr" + }, + "SGD": { + "displayName": "Singapore Dollar", + "displayName-count-one": "Singapore dollar", + "displayName-count-other": "Singapore dollars", + "symbol": "SGD", + "symbol-alt-narrow": "$" + }, + "SHP": { + "displayName": "St Helena Pound", + "displayName-count-one": "St Helena pound", + "displayName-count-other": "St Helena pounds", + "symbol": "SHP", + "symbol-alt-narrow": "£" + }, + "SIT": { + "displayName": "Slovenian Tolar", + "displayName-count-one": "Slovenian tolar", + "displayName-count-other": "Slovenian tolars" + }, + "SKK": { + "displayName": "Slovak Koruna", + "displayName-count-one": "Slovak koruna", + "displayName-count-other": "Slovak korunas" + }, + "SLE": { + "displayName": "Sierra Leonean Leone", + "displayName-count-one": "Sierra Leonean leone", + "displayName-count-other": "Sierra Leonean leones", + "symbol": "SLE" + }, + "SLL": { + "displayName": "Sierra Leonean Leone (1964—2022)", + "displayName-count-one": "Sierra Leonean leone (1964—2022)", + "displayName-count-other": "Sierra Leonean leones (1964—2022)", + "symbol": "SLL" + }, + "SOS": { + "displayName": "Somali Shilling", + "displayName-count-one": "Somali shilling", + "displayName-count-other": "Somali shillings", + "symbol": "SOS" + }, + "SRD": { + "displayName": "Surinamese Dollar", + "displayName-count-one": "Surinamese dollar", + "displayName-count-other": "Surinamese dollars", + "symbol": "SRD", + "symbol-alt-narrow": "$" + }, + "SRG": { + "displayName": "Surinamese Guilder", + "displayName-count-one": "Surinamese guilder", + "displayName-count-other": "Surinamese guilders" + }, + "SSP": { + "displayName": "South Sudanese Pound", + "displayName-count-one": "South Sudanese pound", + "displayName-count-other": "South Sudanese pounds", + "symbol": "SSP", + "symbol-alt-narrow": "£" + }, + "STD": { + "displayName": "São Tomé & Príncipe Dobra (1977–2017)", + "displayName-count-one": "São Tomé & Príncipe dobra (1977–2017)", + "displayName-count-other": "São Tomé & Príncipe dobras (1977–2017)" + }, + "STN": { + "displayName": "São Tomé & Príncipe Dobra", + "displayName-count-one": "São Tomé & Príncipe dobra", + "displayName-count-other": "São Tomé & Príncipe dobras", + "symbol": "STN", + "symbol-alt-narrow": "Db" + }, + "SUR": { + "displayName": "Soviet Rouble", + "displayName-count-one": "Soviet rouble", + "displayName-count-other": "Soviet roubles" + }, + "SVC": { + "displayName": "Salvadoran Colón", + "displayName-count-one": "Salvadoran colón", + "displayName-count-other": "Salvadoran colones" + }, + "SYP": { + "displayName": "Syrian Pound", + "displayName-count-one": "Syrian pound", + "displayName-count-other": "Syrian pounds", + "symbol": "SYP", + "symbol-alt-narrow": "£" + }, + "SZL": { + "displayName": "Swazi Lilangeni", + "displayName-count-one": "Swazi lilangeni", + "displayName-count-other": "Swazi emalangeni", + "symbol": "SZL" + }, + "THB": { + "displayName": "Thai Baht", + "displayName-count-one": "Thai baht", + "displayName-count-other": "Thai baht", + "symbol": "THB", + "symbol-alt-narrow": "฿" + }, + "TJR": { + "displayName": "Tajikistani Rouble", + "displayName-count-one": "Tajikistani rouble", + "displayName-count-other": "Tajikistani roubles" + }, + "TJS": { + "displayName": "Tajikistani Somoni", + "displayName-count-one": "Tajikistani somoni", + "displayName-count-other": "Tajikistani somonis", + "symbol": "TJS" + }, + "TMM": { + "displayName": "Turkmenistani Manat (1993–2009)", + "displayName-count-one": "Turkmenistani manat (1993–2009)", + "displayName-count-other": "Turkmenistani manat (1993–2009)" + }, + "TMT": { + "displayName": "Turkmenistani Manat", + "displayName-count-one": "Turkmenistani manat", + "displayName-count-other": "Turkmenistani manat", + "symbol": "TMT" + }, + "TND": { + "displayName": "Tunisian Dinar", + "displayName-count-one": "Tunisian dinar", + "displayName-count-other": "Tunisian dinars", + "symbol": "TND" + }, + "TOP": { + "displayName": "Tongan Paʻanga", + "displayName-count-one": "Tongan paʻanga", + "displayName-count-other": "Tongan paʻanga", + "symbol": "TOP", + "symbol-alt-narrow": "T$" + }, + "TPE": { + "displayName": "Timorese Escudo", + "displayName-count-one": "Timorese escudo", + "displayName-count-other": "Timorese escudos" + }, + "TRL": { + "displayName": "Turkish Lira (1922–2005)", + "displayName-count-one": "Turkish lira (1922–2005)", + "displayName-count-other": "Turkish Lira (1922–2005)" + }, + "TRY": { + "displayName": "Turkish Lira", + "displayName-count-one": "Turkish lira", + "displayName-count-other": "Turkish Lira", + "symbol": "TRY", + "symbol-alt-narrow": "₺", + "symbol-alt-variant": "TL" + }, + "TTD": { + "displayName": "Trinidad & Tobago Dollar", + "displayName-count-one": "Trinidad & Tobago dollar", + "displayName-count-other": "Trinidad & Tobago dollars", + "symbol": "TTD", + "symbol-alt-narrow": "$" + }, + "TWD": { + "displayName": "New Taiwan Dollar", + "displayName-count-one": "New Taiwan dollar", + "displayName-count-other": "New Taiwan dollars", + "symbol": "NT$", + "symbol-alt-narrow": "$" + }, + "TZS": { + "displayName": "Tanzanian Shilling", + "displayName-count-one": "Tanzanian shilling", + "displayName-count-other": "Tanzanian shillings", + "symbol": "TZS" + }, + "UAH": { + "displayName": "Ukrainian Hryvnia", + "displayName-count-one": "Ukrainian hryvnia", + "displayName-count-other": "Ukrainian hryvnias", + "symbol": "UAH", + "symbol-alt-narrow": "₴" + }, + "UAK": { + "displayName": "Ukrainian Karbovanets", + "displayName-count-one": "Ukrainian karbovanets", + "displayName-count-other": "Ukrainian karbovantsiv" + }, + "UGS": { + "displayName": "Ugandan Shilling (1966–1987)", + "displayName-count-one": "Ugandan shilling (1966–1987)", + "displayName-count-other": "Ugandan shillings (1966–1987)" + }, + "UGX": { + "displayName": "Ugandan Shilling", + "displayName-count-one": "Ugandan shilling", + "displayName-count-other": "Ugandan shillings", + "symbol": "UGX" + }, + "USD": { + "displayName": "US Dollar", + "displayName-count-one": "US dollar", + "displayName-count-other": "US dollars", + "symbol": "US$", + "symbol-alt-narrow": "$" + }, + "USN": { + "displayName": "US Dollar (Next day)", + "displayName-count-one": "US dollar (next day)", + "displayName-count-other": "US dollars (next day)" + }, + "USS": { + "displayName": "US Dollar (Same day)", + "displayName-count-one": "US dollar (same day)", + "displayName-count-other": "US dollars (same day)" + }, + "UYI": { + "displayName": "Uruguayan Peso (Indexed Units)", + "displayName-count-one": "Uruguayan peso (indexed units)", + "displayName-count-other": "Uruguayan pesos (indexed units)" + }, + "UYP": { + "displayName": "Uruguayan Peso (1975–1993)", + "displayName-count-one": "Uruguayan peso (1975–1993)", + "displayName-count-other": "Uruguayan pesos (1975–1993)" + }, + "UYU": { + "displayName": "Uruguayan Peso", + "displayName-count-one": "Uruguayan peso", + "displayName-count-other": "Uruguayan pesos", + "symbol": "UYU", + "symbol-alt-narrow": "$" + }, + "UYW": { + "displayName": "Uruguayan Nominal Wage Index Unit", + "displayName-count-one": "Uruguayan nominal wage index unit", + "displayName-count-other": "Uruguayan nominal wage index units" + }, + "UZS": { + "displayName": "Uzbekistani Som", + "displayName-count-one": "Uzbekistani som", + "displayName-count-other": "Uzbekistani som", + "symbol": "UZS" + }, + "VEB": { + "displayName": "Venezuelan Bolívar (1871–2008)", + "displayName-count-one": "Venezuelan bolívar (1871–2008)", + "displayName-count-other": "Venezuelan bolívars (1871–2008)" + }, + "VED": { + "displayName": "Bolívar Soberano", + "displayName-count-one": "Bolívar Soberano", + "displayName-count-other": "Bolívar Soberanos" + }, + "VEF": { + "displayName": "Venezuelan Bolívar (2008–2018)", + "displayName-count-one": "Venezuelan bolívar (2008–2018)", + "displayName-count-other": "Venezuelan bolívars (2008–2018)", + "symbol-alt-narrow": "Bs" + }, + "VES": { + "displayName": "Venezuelan Bolívar", + "displayName-count-one": "Venezuelan bolívar", + "displayName-count-other": "Venezuelan bolívars", + "symbol": "VES" + }, + "VND": { + "displayName": "Vietnamese Dong", + "displayName-count-one": "Vietnamese dong", + "displayName-count-other": "Vietnamese dong", + "symbol": "₫", + "symbol-alt-narrow": "₫" + }, + "VNN": { + "displayName": "Vietnamese Dong (1978–1985)", + "displayName-count-one": "Vietnamese dong (1978–1985)", + "displayName-count-other": "Vietnamese dong (1978–1985)" + }, + "VUV": { + "displayName": "Vanuatu Vatu", + "displayName-count-one": "Vanuatu vatu", + "displayName-count-other": "Vanuatu vatu", + "symbol": "VUV" + }, + "WST": { + "displayName": "Samoan Tala", + "displayName-count-one": "Samoan tala", + "displayName-count-other": "Samoan tala", + "symbol": "WST" + }, + "XAF": { + "displayName": "Central African CFA Franc", + "displayName-count-one": "Central African CFA franc", + "displayName-count-other": "Central African CFA francs", + "symbol": "FCFA" + }, + "XAG": { + "displayName": "Silver", + "displayName-count-one": "troy ounce of silver", + "displayName-count-other": "troy ounces of silver" + }, + "XAU": { + "displayName": "Gold", + "displayName-count-one": "troy ounce of gold", + "displayName-count-other": "troy ounces of gold" + }, + "XBA": { + "displayName": "European Composite Unit", + "displayName-count-one": "European composite unit", + "displayName-count-other": "European composite units" + }, + "XBB": { + "displayName": "European Monetary Unit", + "displayName-count-one": "European monetary unit", + "displayName-count-other": "European monetary units" + }, + "XBC": { + "displayName": "European Unit of Account (XBC)", + "displayName-count-one": "European unit of account (XBC)", + "displayName-count-other": "European units of account (XBC)" + }, + "XBD": { + "displayName": "European Unit of Account (XBD)", + "displayName-count-one": "European unit of account (XBD)", + "displayName-count-other": "European units of account (XBD)" + }, + "XCD": { + "displayName": "East Caribbean Dollar", + "displayName-count-one": "East Caribbean dollar", + "displayName-count-other": "East Caribbean dollars", + "symbol": "EC$", + "symbol-alt-narrow": "$" + }, + "XCG": { + "displayName": "Caribbean guilder", + "displayName-count-one": "Caribbean guilder", + "displayName-count-other": "Caribbean guilders", + "symbol": "Cg." + }, + "XDR": { + "displayName": "Special Drawing Rights", + "displayName-count-one": "special drawing rights", + "displayName-count-other": "special drawing rights" + }, + "XEU": { + "displayName": "European Currency Unit", + "displayName-count-one": "European currency unit", + "displayName-count-other": "European currency units" + }, + "XFO": { + "displayName": "French Gold Franc", + "displayName-count-one": "French gold franc", + "displayName-count-other": "French gold francs" + }, + "XFU": { + "displayName": "French UIC-Franc", + "displayName-count-one": "French UIC-franc", + "displayName-count-other": "French UIC-francs" + }, + "XOF": { + "displayName": "West African CFA Franc", + "displayName-count-one": "West African CFA franc", + "displayName-count-other": "West African CFA francs", + "symbol": "F CFA" + }, + "XPD": { + "displayName": "Palladium", + "displayName-count-one": "troy ounce of palladium", + "displayName-count-other": "troy ounces of palladium" + }, + "XPF": { + "displayName": "CFP Franc", + "displayName-count-one": "CFP franc", + "displayName-count-other": "CFP francs", + "symbol": "CFPF" + }, + "XPT": { + "displayName": "Platinum", + "displayName-count-one": "troy ounce of platinum", + "displayName-count-other": "troy ounces of platinum" + }, + "XRE": { + "displayName": "RINET Funds", + "displayName-count-one": "RINET Funds unit", + "displayName-count-other": "RINET Funds units" + }, + "XSU": { + "displayName": "Sucre", + "displayName-count-one": "Sucre", + "displayName-count-other": "Sucres" + }, + "XTS": { + "displayName": "Testing Currency Code", + "displayName-count-one": "Testing Currency unit", + "displayName-count-other": "Testing Currency units" + }, + "XUA": { + "displayName": "ADB Unit of Account", + "displayName-count-one": "ADB unit of account", + "displayName-count-other": "ADB units of account" + }, + "XXX": { + "displayName": "Unknown Currency", + "displayName-count-one": "(unknown unit of currency)", + "displayName-count-other": "(unknown currency)", + "symbol": "¤" + }, + "YDD": { + "displayName": "Yemeni Dinar", + "displayName-count-one": "Yemeni dinar", + "displayName-count-other": "Yemeni dinars" + }, + "YER": { + "displayName": "Yemeni Rial", + "displayName-count-one": "Yemeni rial", + "displayName-count-other": "Yemeni rials", + "symbol": "YER" + }, + "YUD": { + "displayName": "Yugoslavian Hard Dinar (1966–1990)", + "displayName-count-one": "Yugoslavian hard dinar (1966–1990)", + "displayName-count-other": "Yugoslavian hard dinars (1966–1990)" + }, + "YUM": { + "displayName": "Yugoslavian New Dinar (1994–2002)", + "displayName-count-one": "Yugoslavian new dinar (1994–2002)", + "displayName-count-other": "Yugoslavian new dinars (1994–2002)" + }, + "YUN": { + "displayName": "Yugoslavian Convertible Dinar (1990–1992)", + "displayName-count-one": "Yugoslavian convertible dinar (1990–1992)", + "displayName-count-other": "Yugoslavian convertible dinars (1990–1992)" + }, + "YUR": { + "displayName": "Yugoslavian Reformed Dinar (1992–1993)", + "displayName-count-one": "Yugoslavian reformed dinar (1992–1993)", + "displayName-count-other": "Yugoslavian reformed dinars (1992–1993)" + }, + "ZAL": { + "displayName": "South African Rand (financial)", + "displayName-count-one": "South African rand (financial)", + "displayName-count-other": "South African rands (financial)" + }, + "ZAR": { + "displayName": "South African Rand", + "displayName-count-one": "South African rand", + "displayName-count-other": "South African rand", + "symbol": "ZAR", + "symbol-alt-narrow": "R" + }, + "ZMK": { + "displayName": "Zambian Kwacha (1968–2012)", + "displayName-count-one": "Zambian kwacha (1968–2012)", + "displayName-count-other": "Zambian kwachas (1968–2012)" + }, + "ZMW": { + "displayName": "Zambian Kwacha", + "displayName-count-one": "Zambian kwacha", + "displayName-count-other": "Zambian kwachas", + "symbol": "ZMW", + "symbol-alt-narrow": "ZK" + }, + "ZRN": { + "displayName": "Zairean New Zaire (1993–1998)", + "displayName-count-one": "Zairean new zaire (1993–1998)", + "displayName-count-other": "Zairean new zaires (1993–1998)" + }, + "ZRZ": { + "displayName": "Zairean Zaire (1971–1993)", + "displayName-count-one": "Zairean zaire (1971–1993)", + "displayName-count-other": "Zairean zaires (1971–1993)" + }, + "ZWD": { + "displayName": "Zimbabwean Dollar (1980–2008)", + "displayName-count-one": "Zimbabwean dollar (1980–2008)", + "displayName-count-other": "Zimbabwean dollars (1980–2008)" + }, + "ZWG": { + "displayName": "Zimbabwean Gold", + "displayName-count-one": "Zimbabwean gold", + "displayName-count-other": "Zimbabwean gold", + "symbol": "ZWG" + }, + "ZWL": { + "displayName": "Zimbabwean Dollar (2009–2024)", + "displayName-count-one": "Zimbabwean dollar (2009–2024)", + "displayName-count-other": "Zimbabwean dollars (2009–2024)" + }, + "ZWR": { + "displayName": "Zimbabwean Dollar (2008)", + "displayName-count-one": "Zimbabwean dollar (2008)", + "displayName-count-other": "Zimbabwean dollars (2008)" + } + } + } + } + } +} diff --git a/Tools/currency-table/cldr/currencyData.json b/Tools/currency-table/cldr/currencyData.json new file mode 100644 index 0000000..4a8caf8 --- /dev/null +++ b/Tools/currency-table/cldr/currencyData.json @@ -0,0 +1,3650 @@ +{ + "supplemental": { + "version": { + "_unicodeVersion": "16.0.0", + "_cldrVersion": "48" + }, + "currencyData": { + "fractions": { + "ADP": { + "_rounding": "0", + "_digits": "0" + }, + "AFN": { + "_rounding": "0", + "_digits": "0" + }, + "ALL": { + "_rounding": "0", + "_digits": "0" + }, + "AMD": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "BHD": { + "_rounding": "0", + "_digits": "3" + }, + "BIF": { + "_rounding": "0", + "_digits": "0" + }, + "BYN": { + "_rounding": "0", + "_digits": "2" + }, + "BYR": { + "_rounding": "0", + "_digits": "0" + }, + "CAD": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "5" + }, + "CHF": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "5" + }, + "CLF": { + "_rounding": "0", + "_digits": "4" + }, + "CLP": { + "_rounding": "0", + "_digits": "0" + }, + "COP": { + "_rounding": "0", + "_digits": "0" + }, + "CRC": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "CZK": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "DEFAULT": { + "_rounding": "0", + "_digits": "2" + }, + "DJF": { + "_rounding": "0", + "_digits": "0" + }, + "DKK": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "50" + }, + "ESP": { + "_rounding": "0", + "_digits": "0" + }, + "GNF": { + "_rounding": "0", + "_digits": "0" + }, + "GYD": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "HUF": { + "_rounding": "0", + "_digits": "0", + "_cashRounding": "5" + }, + "IDR": { + "_rounding": "0", + "_digits": "0" + }, + "IQD": { + "_rounding": "0", + "_digits": "0" + }, + "IRR": { + "_rounding": "0", + "_digits": "0" + }, + "ISK": { + "_rounding": "0", + "_digits": "0" + }, + "ITL": { + "_rounding": "0", + "_digits": "0" + }, + "JOD": { + "_rounding": "0", + "_digits": "3" + }, + "JPY": { + "_rounding": "0", + "_digits": "0" + }, + "KMF": { + "_rounding": "0", + "_digits": "0" + }, + "KPW": { + "_rounding": "0", + "_digits": "0" + }, + "KRW": { + "_rounding": "0", + "_digits": "0" + }, + "KWD": { + "_rounding": "0", + "_digits": "3" + }, + "LAK": { + "_rounding": "0", + "_digits": "0" + }, + "LBP": { + "_rounding": "0", + "_digits": "0" + }, + "LUF": { + "_rounding": "0", + "_digits": "0" + }, + "LYD": { + "_rounding": "0", + "_digits": "3" + }, + "MGA": { + "_rounding": "0", + "_digits": "0" + }, + "MGF": { + "_rounding": "0", + "_digits": "0" + }, + "MMK": { + "_rounding": "0", + "_digits": "0" + }, + "MNT": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "MRO": { + "_rounding": "0", + "_digits": "0" + }, + "MUR": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "NOK": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "OMR": { + "_rounding": "0", + "_digits": "3" + }, + "PKR": { + "_rounding": "0", + "_digits": "0" + }, + "PYG": { + "_rounding": "0", + "_digits": "0" + }, + "RSD": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "RWF": { + "_rounding": "0", + "_digits": "0" + }, + "SEK": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "SLE": { + "_rounding": "0", + "_digits": "2" + }, + "SLL": { + "_rounding": "0", + "_digits": "0" + }, + "SOS": { + "_rounding": "0", + "_digits": "0" + }, + "STD": { + "_rounding": "0", + "_digits": "0" + }, + "SYP": { + "_rounding": "0", + "_digits": "0" + }, + "TMM": { + "_rounding": "0", + "_digits": "0" + }, + "TND": { + "_rounding": "0", + "_digits": "3" + }, + "TRL": { + "_rounding": "0", + "_digits": "0" + }, + "TWD": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "TZS": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "UGX": { + "_rounding": "0", + "_digits": "0" + }, + "UYI": { + "_rounding": "0", + "_digits": "0" + }, + "UYW": { + "_rounding": "0", + "_digits": "4" + }, + "UZS": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "VEF": { + "_rounding": "0", + "_digits": "2", + "_cashRounding": "0", + "_cashDigits": "0" + }, + "VND": { + "_rounding": "0", + "_digits": "0" + }, + "VUV": { + "_rounding": "0", + "_digits": "0" + }, + "XAD": { + "_rounding": "0", + "_digits": "2" + }, + "XAF": { + "_rounding": "0", + "_digits": "0" + }, + "XAU": { + "_rounding": "0", + "_digits": "2" + }, + "XOF": { + "_rounding": "0", + "_digits": "0" + }, + "XPF": { + "_rounding": "0", + "_digits": "0" + }, + "YER": { + "_rounding": "0", + "_digits": "0" + }, + "ZMK": { + "_rounding": "0", + "_digits": "0" + }, + "ZWD": { + "_rounding": "0", + "_digits": "0" + } + }, + "region": { + "AC": [ + { + "SHP": { + "_from": "1976-01-01" + } + } + ], + "AD": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "ESP": { + "_from": "1873-01-01", + "_to": "2002-02-28" + } + }, + { + "FRF": { + "_from": "1960-01-01", + "_to": "2002-02-17" + } + }, + { + "ADP": { + "_from": "1936-01-01", + "_to": "2001-12-31" + } + } + ], + "AE": [ + { + "AED": { + "_from": "1973-05-19" + } + } + ], + "AF": [ + { + "AFN": { + "_from": "2002-10-07" + } + }, + { + "AFA": { + "_from": "1927-03-14", + "_to": "2002-12-31" + } + } + ], + "AG": [ + { + "XCD": { + "_from": "1965-10-06" + } + } + ], + "AI": [ + { + "XCD": { + "_from": "1965-10-06" + } + } + ], + "AL": [ + { + "ALL": { + "_from": "1965-08-16" + } + }, + { + "ALK": { + "_from": "1946-11-01", + "_to": "1965-08-16" + } + } + ], + "AM": [ + { + "AMD": { + "_from": "1993-11-22" + } + }, + { + "RUR": { + "_from": "1991-12-25", + "_to": "1993-11-22" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1991-12-25" + } + } + ], + "AO": [ + { + "AOA": { + "_from": "1999-12-13" + } + }, + { + "AOR": { + "_from": "1995-07-01", + "_to": "2000-02-01" + } + }, + { + "AON": { + "_from": "1990-09-25", + "_to": "2000-02-01" + } + }, + { + "AOK": { + "_from": "1977-01-08", + "_to": "1991-03-01" + } + } + ], + "AQ": [ + { + "XXX": { + "_tender": "false" + } + } + ], + "AR": [ + { + "ARS": { + "_from": "1992-01-01" + } + }, + { + "ARA": { + "_from": "1985-06-14", + "_to": "1992-01-01" + } + }, + { + "ARP": { + "_from": "1983-06-01", + "_to": "1985-06-14" + } + }, + { + "ARL": { + "_from": "1970-01-01", + "_to": "1983-06-01" + } + }, + { + "ARM": { + "_from": "1881-11-05", + "_to": "1970-01-01" + } + } + ], + "AS": [ + { + "USD": { + "_from": "1904-07-16" + } + } + ], + "AT": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "ATS": { + "_from": "1947-12-04", + "_to": "2002-02-28" + } + } + ], + "AU": [ + { + "AUD": { + "_from": "1966-02-14" + } + } + ], + "AW": [ + { + "AWG": { + "_from": "1986-01-01" + } + }, + { + "ANG": { + "_from": "1940-05-10", + "_to": "1986-01-01" + } + } + ], + "AX": [ + { + "EUR": { + "_from": "1999-01-01" + } + } + ], + "AZ": [ + { + "AZN": { + "_from": "2006-01-01" + } + }, + { + "AZM": { + "_from": "1993-11-22", + "_to": "2006-12-31" + } + }, + { + "RUR": { + "_from": "1991-12-25", + "_to": "1994-01-01" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1991-12-25" + } + } + ], + "BA": [ + { + "BAM": { + "_from": "1995-01-01" + } + }, + { + "BAN": { + "_from": "1994-08-15", + "_to": "1997-07-01" + } + }, + { + "BAD": { + "_from": "1992-07-01", + "_to": "1994-08-15" + } + }, + { + "YUR": { + "_from": "1992-07-01", + "_to": "1993-10-01" + } + }, + { + "YUN": { + "_from": "1990-01-01", + "_to": "1992-07-01" + } + }, + { + "YUD": { + "_from": "1966-01-01", + "_to": "1990-01-01" + } + } + ], + "BB": [ + { + "BBD": { + "_from": "1973-12-03" + } + }, + { + "XCD": { + "_from": "1965-10-06", + "_to": "1973-12-03" + } + } + ], + "BD": [ + { + "BDT": { + "_from": "1972-01-01" + } + }, + { + "PKR": { + "_from": "1948-04-01", + "_to": "1972-01-01" + } + }, + { + "INR": { + "_from": "1835-08-17", + "_to": "1948-04-01" + } + } + ], + "BE": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "BEF": { + "_from": "1831-02-07", + "_to": "2002-02-28" + } + }, + { + "NLG": { + "_from": "1816-12-15", + "_to": "1831-02-07" + } + }, + { + "BEL": { + "_tender": "false", + "_from": "1970-01-01", + "_to": "1990-03-05" + } + }, + { + "BEC": { + "_tender": "false", + "_from": "1970-01-01", + "_to": "1990-03-05" + } + } + ], + "BF": [ + { + "XOF": { + "_from": "1984-08-04" + } + } + ], + "BG": [ + { + "EUR": { + "_from": "2026-01-01" + } + }, + { + "BGN": { + "_from": "1999-07-05", + "_to": "2026-01-31" + } + }, + { + "BGL": { + "_from": "1962-01-01", + "_to": "1999-07-05" + } + }, + { + "BGM": { + "_from": "1952-05-12", + "_to": "1962-01-01" + } + }, + { + "BGO": { + "_from": "1879-07-08", + "_to": "1952-05-12" + } + } + ], + "BH": [ + { + "BHD": { + "_from": "1965-10-16" + } + } + ], + "BI": [ + { + "BIF": { + "_from": "1964-05-19" + } + } + ], + "BJ": [ + { + "XOF": { + "_from": "1975-11-30" + } + } + ], + "BL": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1960-01-01", + "_to": "2002-02-17" + } + } + ], + "BM": [ + { + "BMD": { + "_from": "1970-02-06" + } + } + ], + "BN": [ + { + "BND": { + "_from": "1967-06-12" + } + }, + { + "MYR": { + "_from": "1963-09-16", + "_to": "1967-06-12" + } + } + ], + "BO": [ + { + "BOB": { + "_from": "1987-01-01" + } + }, + { + "BOP": { + "_from": "1963-01-01", + "_to": "1986-12-31" + } + }, + { + "BOL": { + "_from": "1863-06-23", + "_to": "1963-01-01" + } + }, + { + "BOV": { + "_tender": "false" + } + } + ], + "BQ": [ + { + "USD": { + "_from": "2011-01-01" + } + }, + { + "ANG": { + "_from": "2010-10-10", + "_to": "2011-01-01" + } + } + ], + "BR": [ + { + "BRL": { + "_from": "1994-07-01" + } + }, + { + "BRR": { + "_from": "1993-08-01", + "_to": "1994-07-01" + } + }, + { + "BRE": { + "_from": "1990-03-16", + "_to": "1993-08-01" + } + }, + { + "BRN": { + "_from": "1989-01-15", + "_to": "1990-03-16" + } + }, + { + "BRC": { + "_from": "1986-02-28", + "_to": "1989-01-15" + } + }, + { + "BRB": { + "_from": "1967-02-13", + "_to": "1986-02-28" + } + }, + { + "BRZ": { + "_from": "1942-11-01", + "_to": "1967-02-13" + } + } + ], + "BS": [ + { + "BSD": { + "_from": "1966-05-25" + } + } + ], + "BT": [ + { + "BTN": { + "_from": "1974-04-16" + } + }, + { + "INR": { + "_from": "1907-01-01" + } + } + ], + "BU": [ + { + "BUK": { + "_from": "1952-07-01", + "_to": "1989-06-18" + } + } + ], + "BV": [ + { + "NOK": { + "_from": "1905-06-07" + } + } + ], + "BW": [ + { + "BWP": { + "_from": "1976-08-23" + } + }, + { + "ZAR": { + "_from": "1961-02-14", + "_to": "1976-08-23" + } + } + ], + "BY": [ + { + "BYN": { + "_from": "2016-07-01" + } + }, + { + "BYR": { + "_from": "2000-01-01", + "_to": "2017-01-01" + } + }, + { + "BYB": { + "_from": "1994-08-01", + "_to": "2000-12-31" + } + }, + { + "RUR": { + "_from": "1991-12-25", + "_to": "1994-11-08" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1991-12-25" + } + } + ], + "BZ": [ + { + "BZD": { + "_from": "1974-01-01" + } + } + ], + "CA": [ + { + "CAD": { + "_from": "1858-01-01" + } + } + ], + "CC": [ + { + "AUD": { + "_from": "1966-02-14" + } + } + ], + "CD": [ + { + "CDF": { + "_from": "1998-07-01" + } + }, + { + "ZRN": { + "_from": "1993-11-01", + "_to": "1998-07-01" + } + }, + { + "ZRZ": { + "_from": "1971-10-27", + "_to": "1993-11-01" + } + } + ], + "CF": [ + { + "XAF": { + "_from": "1993-01-01" + } + } + ], + "CG": [ + { + "XAF": { + "_from": "1993-01-01" + } + } + ], + "CH": [ + { + "CHF": { + "_from": "1799-03-17" + } + }, + { + "CHE": { + "_tender": "false" + } + }, + { + "CHW": { + "_tender": "false" + } + } + ], + "CI": [ + { + "XOF": { + "_from": "1958-12-04" + } + } + ], + "CK": [ + { + "NZD": { + "_from": "1967-07-10" + } + } + ], + "CL": [ + { + "CLP": { + "_from": "1975-09-29" + } + }, + { + "CLE": { + "_from": "1960-01-01", + "_to": "1975-09-29" + } + }, + { + "CLF": { + "_tender": "false" + } + } + ], + "CM": [ + { + "XAF": { + "_from": "1973-04-01" + } + } + ], + "CN": [ + { + "CNY": { + "_from": "1953-03-01" + } + }, + { + "CNX": { + "_tender": "false", + "_from": "1979-01-01", + "_to": "1998-12-31" + } + }, + { + "CNH": { + "_tender": "false", + "_from": "2010-07-19" + } + } + ], + "CO": [ + { + "COP": { + "_from": "1905-01-01" + } + }, + { + "COU": { + "_tender": "false" + } + } + ], + "CP": [ + { + "XXX": { + "_tender": "false" + } + } + ], + "CR": [ + { + "CRC": { + "_from": "1896-10-26" + } + } + ], + "CS": [ + { + "CSD": { + "_from": "2002-05-15", + "_to": "2006-06-03" + } + }, + { + "EUR": { + "_from": "2003-02-04", + "_to": "2006-06-03" + } + }, + { + "YUM": { + "_from": "1994-01-24", + "_to": "2002-05-15" + } + } + ], + "CU": [ + { + "CUP": { + "_from": "1859-01-01" + } + }, + { + "CUC": { + "_from": "1994-01-01", + "_to": "2021-06-01" + } + }, + { + "USD": { + "_from": "1899-01-01", + "_to": "1959-01-01" + } + } + ], + "CV": [ + { + "CVE": { + "_from": "1914-01-01" + } + }, + { + "PTE": { + "_from": "1911-05-22", + "_to": "1975-07-05" + } + } + ], + "CW": [ + { + "XCG": { + "_tz": "America/Curacao", + "_from": "2025-03-31" + } + }, + { + "ANG": { + "_from": "2010-10-10", + "_to": "2025-06-30", + "_to-tz": "America/Curacao" + } + } + ], + "CX": [ + { + "AUD": { + "_from": "1966-02-14" + } + } + ], + "CY": [ + { + "EUR": { + "_from": "2008-01-01" + } + }, + { + "CYP": { + "_from": "1914-09-10", + "_to": "2008-01-31" + } + } + ], + "CZ": [ + { + "CZK": { + "_from": "1993-01-01" + } + }, + { + "CSK": { + "_from": "1953-06-01", + "_to": "1993-03-01" + } + } + ], + "DD": [ + { + "DDM": { + "_from": "1948-07-20", + "_to": "1990-10-02" + } + } + ], + "DE": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "DEM": { + "_from": "1948-06-20", + "_to": "2002-02-28" + } + } + ], + "DG": [ + { + "USD": { + "_from": "1965-11-08" + } + } + ], + "DJ": [ + { + "DJF": { + "_from": "1977-06-27" + } + } + ], + "DK": [ + { + "DKK": { + "_from": "1873-05-27" + } + } + ], + "DM": [ + { + "XCD": { + "_from": "1965-10-06" + } + } + ], + "DO": [ + { + "DOP": { + "_from": "1947-10-01" + } + }, + { + "USD": { + "_from": "1905-06-21", + "_to": "1947-10-01" + } + } + ], + "DZ": [ + { + "DZD": { + "_from": "1964-04-01" + } + } + ], + "EA": [ + { + "EUR": { + "_from": "1999-01-01" + } + } + ], + "EC": [ + { + "USD": { + "_from": "2000-10-02" + } + }, + { + "ECS": { + "_from": "1884-04-01", + "_to": "2000-10-02" + } + }, + { + "ECV": { + "_tender": "false", + "_from": "1993-05-23", + "_to": "2000-01-09" + } + } + ], + "EE": [ + { + "EUR": { + "_from": "2011-01-01" + } + }, + { + "EEK": { + "_from": "1992-06-21", + "_to": "2010-12-31" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1992-06-20" + } + } + ], + "EG": [ + { + "EGP": { + "_from": "1885-11-14" + } + } + ], + "EH": [ + { + "MAD": { + "_from": "1976-02-26" + } + } + ], + "ER": [ + { + "ERN": { + "_from": "1997-11-08" + } + }, + { + "ETB": { + "_from": "1993-05-24", + "_to": "1997-11-08" + } + } + ], + "ES": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "ESP": { + "_from": "1868-10-19", + "_to": "2002-02-28" + } + }, + { + "ESA": { + "_tender": "false", + "_from": "1978-01-01", + "_to": "1981-12-31" + } + }, + { + "ESB": { + "_tender": "false", + "_from": "1975-01-01", + "_to": "1994-12-31" + } + } + ], + "ET": [ + { + "ETB": { + "_from": "1976-09-15" + } + } + ], + "EU": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "XEU": { + "_tender": "false", + "_from": "1979-01-01", + "_to": "1998-12-31" + } + } + ], + "FI": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FIM": { + "_from": "1963-01-01", + "_to": "2002-02-28" + } + } + ], + "FJ": [ + { + "FJD": { + "_from": "1969-01-13" + } + } + ], + "FK": [ + { + "FKP": { + "_from": "1901-01-01" + } + } + ], + "FM": [ + { + "USD": { + "_from": "1944-01-01" + } + }, + { + "JPY": { + "_from": "1914-10-03", + "_to": "1944-01-01" + } + } + ], + "FO": [ + { + "DKK": { + "_from": "1948-01-01" + } + } + ], + "FR": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1960-01-01", + "_to": "2002-02-17" + } + } + ], + "GA": [ + { + "XAF": { + "_from": "1993-01-01" + } + } + ], + "GB": [ + { + "GBP": { + "_from": "1694-07-27" + } + } + ], + "GD": [ + { + "XCD": { + "_from": "1967-02-27" + } + } + ], + "GE": [ + { + "GEL": { + "_from": "1995-09-23" + } + }, + { + "GEK": { + "_from": "1993-04-05", + "_to": "1995-09-25" + } + }, + { + "RUR": { + "_from": "1991-12-25", + "_to": "1993-06-11" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1991-12-25" + } + } + ], + "GF": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1960-01-01", + "_to": "2002-02-17" + } + } + ], + "GG": [ + { + "GBP": { + "_from": "1830-01-01" + } + } + ], + "GH": [ + { + "GHS": { + "_from": "2007-07-03" + } + }, + { + "GHC": { + "_from": "1979-03-09", + "_to": "2007-12-31" + } + } + ], + "GI": [ + { + "GIP": { + "_from": "1713-01-01" + } + } + ], + "GL": [ + { + "DKK": { + "_from": "1873-05-27" + } + } + ], + "GM": [ + { + "GMD": { + "_from": "1971-07-01" + } + } + ], + "GN": [ + { + "GNF": { + "_from": "1986-01-06" + } + }, + { + "GNS": { + "_from": "1972-10-02", + "_to": "1986-01-06" + } + } + ], + "GP": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1960-01-01", + "_to": "2002-02-17" + } + } + ], + "GQ": [ + { + "XAF": { + "_from": "1993-01-01" + } + }, + { + "GQE": { + "_from": "1975-07-07", + "_to": "1986-06-01" + } + } + ], + "GR": [ + { + "EUR": { + "_from": "2001-01-01" + } + }, + { + "GRD": { + "_from": "1954-05-01", + "_to": "2002-02-28" + } + } + ], + "GS": [ + { + "GBP": { + "_from": "1908-01-01" + } + } + ], + "GT": [ + { + "GTQ": { + "_from": "1925-05-27" + } + } + ], + "GU": [ + { + "USD": { + "_from": "1944-08-21" + } + } + ], + "GW": [ + { + "XOF": { + "_from": "1997-03-31" + } + }, + { + "GWP": { + "_from": "1976-02-28", + "_to": "1997-03-31" + } + }, + { + "GWE": { + "_from": "1914-01-01", + "_to": "1976-02-28" + } + } + ], + "GY": [ + { + "GYD": { + "_from": "1966-05-26" + } + } + ], + "HK": [ + { + "HKD": { + "_from": "1895-02-02" + } + } + ], + "HM": [ + { + "AUD": { + "_from": "1967-02-16" + } + } + ], + "HN": [ + { + "HNL": { + "_from": "1926-04-03" + } + } + ], + "HR": [ + { + "EUR": { + "_tz": "Europe/Zagreb", + "_from": "2023-01-01" + } + }, + { + "HRK": { + "_from": "1994-05-30", + "_to": "2023-01-14", + "_to-tz": "Europe/Zagreb" + } + }, + { + "HRD": { + "_from": "1991-12-23", + "_to": "1995-01-01" + } + }, + { + "YUN": { + "_from": "1990-01-01", + "_to": "1991-12-23" + } + }, + { + "YUD": { + "_from": "1966-01-01", + "_to": "1990-01-01" + } + } + ], + "HT": [ + { + "HTG": { + "_from": "1872-08-26" + } + }, + { + "USD": { + "_from": "1915-01-01" + } + } + ], + "HU": [ + { + "HUF": { + "_from": "1946-07-23" + } + } + ], + "IC": [ + { + "EUR": { + "_from": "1999-01-01" + } + } + ], + "ID": [ + { + "IDR": { + "_from": "1965-12-13" + } + } + ], + "IE": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "IEP": { + "_from": "1922-01-01", + "_to": "2002-02-09" + } + }, + { + "GBP": { + "_from": "1800-01-01", + "_to": "1922-01-01" + } + } + ], + "IL": [ + { + "ILS": { + "_from": "1985-09-04" + } + }, + { + "ILR": { + "_from": "1980-02-22", + "_to": "1985-09-04" + } + }, + { + "ILP": { + "_from": "1948-08-16", + "_to": "1980-02-22" + } + } + ], + "IM": [ + { + "GBP": { + "_from": "1840-01-03" + } + } + ], + "IN": [ + { + "INR": { + "_from": "1835-08-17" + } + } + ], + "IO": [ + { + "USD": { + "_from": "1965-11-08" + } + } + ], + "IQ": [ + { + "IQD": { + "_from": "1931-04-19" + } + }, + { + "EGP": { + "_from": "1920-11-11", + "_to": "1931-04-19" + } + }, + { + "INR": { + "_from": "1920-11-11", + "_to": "1931-04-19" + } + } + ], + "IR": [ + { + "IRR": { + "_from": "1932-05-13" + } + } + ], + "IS": [ + { + "ISK": { + "_from": "1981-01-01" + } + }, + { + "ISJ": { + "_from": "1918-12-01", + "_to": "1981-01-01" + } + }, + { + "DKK": { + "_from": "1873-05-27", + "_to": "1918-12-01" + } + } + ], + "IT": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "ITL": { + "_from": "1862-08-24", + "_to": "2002-02-28" + } + } + ], + "JE": [ + { + "GBP": { + "_from": "1837-01-01" + } + } + ], + "JM": [ + { + "JMD": { + "_from": "1969-09-08" + } + } + ], + "JO": [ + { + "JOD": { + "_from": "1950-07-01" + } + } + ], + "JP": [ + { + "JPY": { + "_from": "1871-06-01" + } + } + ], + "KE": [ + { + "KES": { + "_from": "1966-09-14" + } + } + ], + "KG": [ + { + "KGS": { + "_from": "1993-05-10" + } + }, + { + "RUR": { + "_from": "1991-12-25", + "_to": "1993-05-10" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1991-12-25" + } + } + ], + "KH": [ + { + "KHR": { + "_from": "1980-03-20" + } + } + ], + "KI": [ + { + "AUD": { + "_from": "1966-02-14" + } + } + ], + "KM": [ + { + "KMF": { + "_from": "1975-07-06" + } + } + ], + "KN": [ + { + "XCD": { + "_from": "1965-10-06" + } + } + ], + "KP": [ + { + "KPW": { + "_from": "1959-04-17" + } + } + ], + "KR": [ + { + "KRW": { + "_from": "1962-06-10" + } + }, + { + "KRH": { + "_from": "1953-02-15", + "_to": "1962-06-10" + } + }, + { + "KRO": { + "_from": "1945-08-15", + "_to": "1953-02-15" + } + } + ], + "KW": [ + { + "KWD": { + "_from": "1961-04-01" + } + } + ], + "KY": [ + { + "KYD": { + "_from": "1971-01-01" + } + }, + { + "JMD": { + "_from": "1969-09-08", + "_to": "1971-01-01" + } + } + ], + "KZ": [ + { + "KZT": { + "_from": "1993-11-05" + } + } + ], + "LA": [ + { + "LAK": { + "_from": "1979-12-10" + } + } + ], + "LB": [ + { + "LBP": { + "_from": "1948-02-02" + } + } + ], + "LC": [ + { + "XCD": { + "_from": "1965-10-06" + } + } + ], + "LI": [ + { + "CHF": { + "_from": "1921-02-01" + } + } + ], + "LK": [ + { + "LKR": { + "_from": "1978-05-22" + } + } + ], + "LR": [ + { + "LRD": { + "_from": "1944-01-01" + } + } + ], + "LS": [ + { + "ZAR": { + "_from": "1961-02-14" + } + }, + { + "LSL": { + "_from": "1980-01-22" + } + } + ], + "LT": [ + { + "EUR": { + "_from": "2015-01-01" + } + }, + { + "LTL": { + "_from": "1993-06-25", + "_to": "2014-12-31" + } + }, + { + "LTT": { + "_from": "1992-10-01", + "_to": "1993-06-25" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1992-10-01" + } + } + ], + "LU": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "LUF": { + "_from": "1944-09-04", + "_to": "2002-02-28" + } + }, + { + "LUC": { + "_tender": "false", + "_from": "1970-01-01", + "_to": "1990-03-05" + } + }, + { + "LUL": { + "_tender": "false", + "_from": "1970-01-01", + "_to": "1990-03-05" + } + } + ], + "LV": [ + { + "EUR": { + "_from": "2014-01-01" + } + }, + { + "LVL": { + "_from": "1993-06-28", + "_to": "2013-12-31" + } + }, + { + "LVR": { + "_from": "1992-05-07", + "_to": "1993-10-17" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1992-07-20" + } + } + ], + "LY": [ + { + "LYD": { + "_from": "1971-09-01" + } + } + ], + "MA": [ + { + "MAD": { + "_from": "1959-10-17" + } + }, + { + "MAF": { + "_from": "1881-01-01", + "_to": "1959-10-17" + } + } + ], + "MC": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1960-01-01", + "_to": "2002-02-17" + } + }, + { + "MCF": { + "_from": "1960-01-01", + "_to": "2002-02-17" + } + } + ], + "MD": [ + { + "MDL": { + "_from": "1993-11-29" + } + }, + { + "MDC": { + "_from": "1992-06-01", + "_to": "1993-11-29" + } + } + ], + "ME": [ + { + "EUR": { + "_from": "2002-01-01" + } + }, + { + "DEM": { + "_from": "1999-10-02", + "_to": "2002-05-15" + } + }, + { + "YUM": { + "_from": "1994-01-24", + "_to": "2002-05-15" + } + } + ], + "MF": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1960-01-01", + "_to": "2002-02-17" + } + } + ], + "MG": [ + { + "MGA": { + "_from": "1983-11-01" + } + }, + { + "MGF": { + "_from": "1963-07-01", + "_to": "2004-12-31" + } + } + ], + "MH": [ + { + "USD": { + "_from": "1944-01-01" + } + } + ], + "MK": [ + { + "MKD": { + "_from": "1993-05-20" + } + }, + { + "MKN": { + "_from": "1992-04-26", + "_to": "1993-05-20" + } + } + ], + "ML": [ + { + "XOF": { + "_from": "1984-06-01" + } + }, + { + "MLF": { + "_from": "1962-07-02", + "_to": "1984-08-31" + } + }, + { + "XOF": { + "_from": "1958-11-24", + "_to": "1962-07-02" + } + } + ], + "MM": [ + { + "MMK": { + "_from": "1989-06-18" + } + }, + { + "BUK": { + "_from": "1952-07-01", + "_to": "1989-06-18" + } + } + ], + "MN": [ + { + "MNT": { + "_from": "1915-03-01" + } + } + ], + "MO": [ + { + "MOP": { + "_from": "1901-01-01" + } + } + ], + "MP": [ + { + "USD": { + "_from": "1944-01-01" + } + } + ], + "MQ": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1960-01-01", + "_to": "2002-02-17" + } + } + ], + "MR": [ + { + "MRU": { + "_from": "2018-01-01" + } + }, + { + "MRO": { + "_from": "1973-06-29", + "_to": "2018-06-30" + } + }, + { + "XOF": { + "_from": "1958-11-28", + "_to": "1973-06-29" + } + } + ], + "MS": [ + { + "XCD": { + "_from": "1967-02-27" + } + } + ], + "MT": [ + { + "EUR": { + "_from": "2008-01-01" + } + }, + { + "MTL": { + "_from": "1968-06-07", + "_to": "2008-01-31" + } + }, + { + "MTP": { + "_from": "1914-08-13", + "_to": "1968-06-07" + } + } + ], + "MU": [ + { + "MUR": { + "_from": "1934-04-01" + } + } + ], + "MV": [ + { + "MVR": { + "_from": "1981-07-01" + } + }, + { + "MVP": { + "_from": "1947-01-01", + "_to": "1981-07-01" + } + } + ], + "MW": [ + { + "MWK": { + "_from": "1971-02-15" + } + } + ], + "MX": [ + { + "MXN": { + "_from": "1993-01-01" + } + }, + { + "MXP": { + "_from": "1822-01-01", + "_to": "1992-12-31" + } + }, + { + "MXV": { + "_tender": "false" + } + } + ], + "MY": [ + { + "MYR": { + "_from": "1963-09-16" + } + } + ], + "MZ": [ + { + "MZN": { + "_from": "2006-07-01" + } + }, + { + "MZM": { + "_from": "1980-06-16", + "_to": "2006-12-31" + } + }, + { + "MZE": { + "_from": "1975-06-25", + "_to": "1980-06-16" + } + } + ], + "NA": [ + { + "NAD": { + "_from": "1993-01-01" + } + }, + { + "ZAR": { + "_from": "1961-02-14" + } + } + ], + "NC": [ + { + "XPF": { + "_from": "1985-01-01" + } + } + ], + "NE": [ + { + "XOF": { + "_from": "1958-12-19" + } + } + ], + "NF": [ + { + "AUD": { + "_from": "1966-02-14" + } + } + ], + "NG": [ + { + "NGN": { + "_from": "1973-01-01" + } + } + ], + "NI": [ + { + "NIO": { + "_from": "1991-04-30" + } + }, + { + "NIC": { + "_from": "1988-02-15", + "_to": "1991-04-30" + } + } + ], + "NL": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "NLG": { + "_from": "1813-01-01", + "_to": "2002-02-28" + } + } + ], + "NO": [ + { + "NOK": { + "_from": "1905-06-07" + } + }, + { + "SEK": { + "_from": "1873-05-27", + "_to": "1905-06-07" + } + } + ], + "NP": [ + { + "NPR": { + "_from": "1933-01-01" + } + }, + { + "INR": { + "_from": "1870-01-01", + "_to": "1966-10-17" + } + } + ], + "NR": [ + { + "AUD": { + "_from": "1966-02-14" + } + } + ], + "NU": [ + { + "NZD": { + "_from": "1967-07-10" + } + } + ], + "NZ": [ + { + "NZD": { + "_from": "1967-07-10" + } + } + ], + "OM": [ + { + "OMR": { + "_from": "1972-11-11" + } + } + ], + "PA": [ + { + "PAB": { + "_from": "1903-11-04" + } + }, + { + "USD": { + "_from": "1903-11-18" + } + } + ], + "PE": [ + { + "PEN": { + "_from": "1991-07-01" + } + }, + { + "PEI": { + "_from": "1985-02-01", + "_to": "1991-07-01" + } + }, + { + "PES": { + "_from": "1863-02-14", + "_to": "1985-02-01" + } + } + ], + "PF": [ + { + "XPF": { + "_from": "1945-12-26" + } + } + ], + "PG": [ + { + "PGK": { + "_from": "1975-09-16" + } + }, + { + "AUD": { + "_from": "1966-02-14", + "_to": "1975-09-16" + } + } + ], + "PH": [ + { + "PHP": { + "_from": "1946-07-04" + } + } + ], + "PK": [ + { + "PKR": { + "_from": "1948-04-01" + } + }, + { + "INR": { + "_from": "1835-08-17", + "_to": "1947-08-15" + } + } + ], + "PL": [ + { + "PLN": { + "_from": "1995-01-01" + } + }, + { + "PLZ": { + "_from": "1950-10-28", + "_to": "1994-12-31" + } + } + ], + "PM": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1972-12-21", + "_to": "2002-02-17" + } + } + ], + "PN": [ + { + "NZD": { + "_from": "1969-01-13" + } + } + ], + "PR": [ + { + "USD": { + "_from": "1898-12-10" + } + }, + { + "ESP": { + "_from": "1800-01-01", + "_to": "1898-12-10" + } + } + ], + "PS": [ + { + "ILS": { + "_from": "1985-09-04" + } + }, + { + "JOD": { + "_from": "1996-02-12" + } + }, + { + "ILP": { + "_from": "1967-06-01", + "_to": "1980-02-22" + } + }, + { + "JOD": { + "_from": "1950-07-01", + "_to": "1967-06-01" + } + } + ], + "PT": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "PTE": { + "_from": "1911-05-22", + "_to": "2002-02-28" + } + } + ], + "PW": [ + { + "USD": { + "_from": "1944-01-01" + } + } + ], + "PY": [ + { + "PYG": { + "_from": "1943-11-01" + } + } + ], + "QA": [ + { + "QAR": { + "_from": "1973-05-19" + } + } + ], + "RE": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1975-01-01", + "_to": "2002-02-17" + } + } + ], + "RO": [ + { + "RON": { + "_from": "2005-07-01" + } + }, + { + "ROL": { + "_from": "1952-01-28", + "_to": "2006-12-31" + } + } + ], + "RS": [ + { + "RSD": { + "_from": "2006-10-25" + } + }, + { + "CSD": { + "_from": "2002-05-15", + "_to": "2006-10-25" + } + }, + { + "YUM": { + "_from": "1994-01-24", + "_to": "2002-05-15" + } + } + ], + "RU": [ + { + "RUB": { + "_from": "1999-01-01" + } + }, + { + "RUR": { + "_from": "1991-12-25", + "_to": "1998-12-31" + } + } + ], + "RW": [ + { + "RWF": { + "_from": "1964-05-19" + } + } + ], + "SA": [ + { + "SAR": { + "_from": "1952-10-22" + } + } + ], + "SB": [ + { + "SBD": { + "_from": "1977-10-24" + } + }, + { + "AUD": { + "_from": "1966-02-14", + "_to": "1978-06-30" + } + } + ], + "SC": [ + { + "SCR": { + "_from": "1903-11-01" + } + } + ], + "SD": [ + { + "SDG": { + "_from": "2007-01-10" + } + }, + { + "SDD": { + "_from": "1992-06-08", + "_to": "2007-06-30" + } + }, + { + "SDP": { + "_from": "1957-04-08", + "_to": "1998-06-01" + } + }, + { + "EGP": { + "_from": "1889-01-19", + "_to": "1958-01-01" + } + }, + { + "GBP": { + "_from": "1889-01-19", + "_to": "1958-01-01" + } + } + ], + "SE": [ + { + "SEK": { + "_from": "1873-05-27" + } + } + ], + "SG": [ + { + "SGD": { + "_from": "1967-06-12" + } + }, + { + "MYR": { + "_from": "1963-09-16", + "_to": "1967-06-12" + } + } + ], + "SH": [ + { + "SHP": { + "_from": "1917-02-15" + } + } + ], + "SI": [ + { + "EUR": { + "_from": "2007-01-01" + } + }, + { + "SIT": { + "_from": "1992-10-07", + "_to": "2007-01-14" + } + } + ], + "SJ": [ + { + "NOK": { + "_from": "1905-06-07" + } + } + ], + "SK": [ + { + "EUR": { + "_from": "2009-01-01" + } + }, + { + "SKK": { + "_from": "1992-12-31", + "_to": "2009-01-01" + } + }, + { + "CSK": { + "_from": "1953-06-01", + "_to": "1992-12-31" + } + } + ], + "SL": [ + { + "SLE": { + "_tz": "Africa/Freetown", + "_from": "2022-07-01" + } + }, + { + "SLL": { + "_from": "1964-08-04", + "_to": "2023-12-31", + "_to-tz": "Africa/Freetown" + } + }, + { + "GBP": { + "_from": "1808-11-30", + "_to": "1966-02-04" + } + } + ], + "SM": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "ITL": { + "_from": "1865-12-23", + "_to": "2001-02-28" + } + } + ], + "SN": [ + { + "XOF": { + "_from": "1959-04-04" + } + } + ], + "SO": [ + { + "SOS": { + "_from": "1960-07-01" + } + } + ], + "SR": [ + { + "SRD": { + "_from": "2004-01-01" + } + }, + { + "SRG": { + "_from": "1940-05-10", + "_to": "2003-12-31" + } + }, + { + "NLG": { + "_from": "1815-11-20", + "_to": "1940-05-10" + } + } + ], + "SS": [ + { + "SSP": { + "_from": "2011-07-18" + } + }, + { + "SDG": { + "_from": "2007-01-10", + "_to": "2011-09-01" + } + } + ], + "ST": [ + { + "STN": { + "_from": "2018-01-01" + } + }, + { + "STD": { + "_from": "1977-09-08", + "_to": "2017-12-31" + } + } + ], + "SU": [ + { + "SUR": { + "_from": "1961-01-01", + "_to": "1991-12-25" + } + } + ], + "SV": [ + { + "USD": { + "_from": "2001-01-01" + } + }, + { + "SVC": { + "_from": "1919-11-11", + "_to": "2001-01-01" + } + } + ], + "SX": [ + { + "XCG": { + "_tz": "America/Lower_Princes", + "_from": "2025-03-31" + } + }, + { + "ANG": { + "_from": "2010-10-10", + "_to": "2025-06-30", + "_to-tz": "America/Lower_Princes" + } + } + ], + "SY": [ + { + "SYP": { + "_from": "1948-01-01" + } + } + ], + "SZ": [ + { + "SZL": { + "_from": "1974-09-06" + } + } + ], + "TA": [ + { + "GBP": { + "_from": "1938-01-12" + } + } + ], + "TC": [ + { + "USD": { + "_from": "1969-09-08" + } + } + ], + "TD": [ + { + "XAF": { + "_from": "1993-01-01" + } + } + ], + "TF": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1959-01-01", + "_to": "2002-02-17" + } + } + ], + "TG": [ + { + "XOF": { + "_from": "1958-11-28" + } + } + ], + "TH": [ + { + "THB": { + "_from": "1928-04-15" + } + } + ], + "TJ": [ + { + "TJS": { + "_from": "2000-10-26" + } + }, + { + "TJR": { + "_from": "1995-05-10", + "_to": "2000-10-25" + } + }, + { + "RUR": { + "_from": "1991-12-25", + "_to": "1995-05-10" + } + } + ], + "TK": [ + { + "NZD": { + "_from": "1967-07-10" + } + } + ], + "TL": [ + { + "USD": { + "_from": "1999-10-20" + } + }, + { + "TPE": { + "_from": "1959-01-02", + "_to": "2002-05-20" + } + }, + { + "IDR": { + "_from": "1975-12-07", + "_to": "2002-05-20" + } + } + ], + "TM": [ + { + "TMT": { + "_from": "2009-01-01" + } + }, + { + "TMM": { + "_from": "1993-11-01", + "_to": "2009-01-01" + } + }, + { + "RUR": { + "_from": "1991-12-25", + "_to": "1993-11-01" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1991-12-25" + } + } + ], + "TN": [ + { + "TND": { + "_from": "1958-11-01" + } + } + ], + "TO": [ + { + "TOP": { + "_from": "1966-02-14" + } + } + ], + "TP": [ + { + "TPE": { + "_from": "1959-01-02", + "_to": "2002-05-20" + } + }, + { + "IDR": { + "_from": "1975-12-07", + "_to": "2002-05-20" + } + } + ], + "TR": [ + { + "TRY": { + "_from": "2005-01-01" + } + }, + { + "TRL": { + "_from": "1922-11-01", + "_to": "2005-12-31" + } + } + ], + "TT": [ + { + "TTD": { + "_from": "1964-01-01" + } + } + ], + "TV": [ + { + "AUD": { + "_from": "1966-02-14" + } + } + ], + "TW": [ + { + "TWD": { + "_from": "1949-06-15" + } + } + ], + "TZ": [ + { + "TZS": { + "_from": "1966-06-14" + } + } + ], + "UA": [ + { + "UAH": { + "_from": "1996-09-02" + } + }, + { + "UAK": { + "_from": "1992-11-13", + "_to": "1993-10-17" + } + }, + { + "RUR": { + "_from": "1991-12-25", + "_to": "1992-11-13" + } + }, + { + "SUR": { + "_from": "1961-01-01", + "_to": "1991-12-25" + } + } + ], + "UG": [ + { + "UGX": { + "_from": "1987-05-15" + } + }, + { + "UGS": { + "_from": "1966-08-15", + "_to": "1987-05-15" + } + } + ], + "UM": [ + { + "USD": { + "_from": "1944-01-01" + } + } + ], + "US": [ + { + "USD": { + "_from": "1792-01-01" + } + }, + { + "USN": { + "_tender": "false" + } + }, + { + "USS": { + "_tender": "false", + "_to": "2014-03-01" + } + } + ], + "UY": [ + { + "UYU": { + "_from": "1993-03-01" + } + }, + { + "UYP": { + "_from": "1975-07-01", + "_to": "1993-03-01" + } + }, + { + "UYI": { + "_tender": "false" + } + }, + { + "UYW": { + "_tender": "false" + } + } + ], + "UZ": [ + { + "UZS": { + "_from": "1994-07-01" + } + } + ], + "VA": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "ITL": { + "_from": "1870-10-19", + "_to": "2002-02-28" + } + } + ], + "VC": [ + { + "XCD": { + "_from": "1965-10-06" + } + } + ], + "VE": [ + { + "VES": { + "_from": "2018-08-20" + } + }, + { + "VEF": { + "_from": "2008-01-01", + "_to": "2018-08-20" + } + }, + { + "VEB": { + "_from": "1871-05-11", + "_to": "2008-06-30" + } + }, + { + "VED": { + "_tender": "false" + } + } + ], + "VG": [ + { + "USD": { + "_from": "1833-01-01" + } + }, + { + "GBP": { + "_from": "1833-01-01", + "_to": "1959-01-01" + } + } + ], + "VI": [ + { + "USD": { + "_from": "1837-01-01" + } + } + ], + "VN": [ + { + "VND": { + "_from": "1985-09-14" + } + }, + { + "VNN": { + "_from": "1978-05-03", + "_to": "1985-09-14" + } + } + ], + "VU": [ + { + "VUV": { + "_from": "1981-01-01" + } + } + ], + "WF": [ + { + "XPF": { + "_from": "1961-07-30" + } + } + ], + "WS": [ + { + "WST": { + "_from": "1967-07-10" + } + } + ], + "XK": [ + { + "EUR": { + "_from": "2002-01-01" + } + }, + { + "DEM": { + "_from": "1999-09-01", + "_to": "2002-03-09" + } + }, + { + "YUM": { + "_from": "1994-01-24", + "_to": "1999-09-30" + } + } + ], + "YD": [ + { + "YDD": { + "_from": "1965-04-01", + "_to": "1996-01-01" + } + } + ], + "YE": [ + { + "YER": { + "_from": "1990-05-22" + } + } + ], + "YT": [ + { + "EUR": { + "_from": "1999-01-01" + } + }, + { + "FRF": { + "_from": "1976-02-23", + "_to": "2002-02-17" + } + }, + { + "KMF": { + "_from": "1975-01-01", + "_to": "1976-02-23" + } + } + ], + "YU": [ + { + "YUM": { + "_from": "1994-01-24", + "_to": "2002-05-15" + } + }, + { + "YUN": { + "_from": "1990-01-01", + "_to": "1992-07-24" + } + }, + { + "YUD": { + "_from": "1966-01-01", + "_to": "1990-01-01" + } + } + ], + "ZA": [ + { + "ZAR": { + "_from": "1961-02-14" + } + }, + { + "ZAL": { + "_tender": "false", + "_from": "1985-09-01", + "_to": "1995-03-13" + } + } + ], + "ZM": [ + { + "ZMW": { + "_from": "2013-01-01" + } + }, + { + "ZMK": { + "_from": "1968-01-16", + "_to": "2013-01-01" + } + } + ], + "ZR": [ + { + "ZRN": { + "_from": "1993-11-01", + "_to": "1998-07-31" + } + }, + { + "ZRZ": { + "_from": "1971-10-27", + "_to": "1993-11-01" + } + } + ], + "ZW": [ + { + "ZWG": { + "_from": "2024-06-25" + } + }, + { + "USD": { + "_from": "2009-04-12" + } + }, + { + "ZWL": { + "_from": "2009-02-02", + "_to": "2024-08-31" + } + }, + { + "ZWR": { + "_from": "2008-08-01", + "_to": "2009-02-02" + } + }, + { + "ZWD": { + "_from": "1980-04-18", + "_to": "2008-08-01" + } + }, + { + "RHD": { + "_from": "1970-02-17", + "_to": "1980-04-18" + } + } + ], + "ZZ": [ + { + "XAD": { + "_tender": "false", + "_from": "2025-05-12" + } + }, + { + "XAG": { + "_tender": "false" + } + }, + { + "XAU": { + "_tender": "false" + } + }, + { + "XBA": { + "_tender": "false" + } + }, + { + "XBB": { + "_tender": "false" + } + }, + { + "XBC": { + "_tender": "false" + } + }, + { + "XBD": { + "_tender": "false" + } + }, + { + "XDR": { + "_tender": "false" + } + }, + { + "XFO": { + "_tender": "false", + "_from": "1930-01-01", + "_to": "2003-04-01" + } + }, + { + "XFU": { + "_tender": "false", + "_to": "2013-11-30" + } + }, + { + "XPD": { + "_tender": "false" + } + }, + { + "XPT": { + "_tender": "false" + } + }, + { + "XRE": { + "_tender": "false", + "_to": "1999-11-30" + } + }, + { + "XSU": { + "_tender": "false" + } + }, + { + "XTS": { + "_tender": "false" + } + }, + { + "XUA": { + "_tender": "false" + } + }, + { + "XXX": { + "_tender": "false" + } + } + ] + } + } + } +} diff --git a/Tools/currency-table/generate.mjs b/Tools/currency-table/generate.mjs index ce63e2b..162b2f0 100644 --- a/Tools/currency-table/generate.mjs +++ b/Tools/currency-table/generate.mjs @@ -1,5 +1,5 @@ -// Regenerates Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs from the CLDR data -// packages pinned in package.json. Run `npm ci && npm run generate` in this directory. +// Regenerates Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs from the CLDR +// data vendored in ./cldr. Run `node generate.mjs`; it needs nothing but Node. // The output is committed; CI re-runs this and fails if the committed file differs. import fs from 'node:fs'; @@ -10,10 +10,10 @@ const here = path.dirname(fileURLToPath(import.meta.url)); const LOCALE = 'en-001'; const OUTPUT = path.resolve(here, '../../Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs'); -const read = relative => JSON.parse(fs.readFileSync(path.resolve(here, 'node_modules', relative), 'utf8')); +const read = name => JSON.parse(fs.readFileSync(path.resolve(here, 'cldr', name), 'utf8')); -const symbols = read(`cldr-numbers-full/main/${LOCALE}/currencies.json`).main[LOCALE].numbers.currencies; -const fractions = read('cldr-core/supplemental/currencyData.json').supplemental.currencyData.fractions; +const symbols = read('currencies-en-001.json').main[LOCALE].numbers.currencies; +const fractions = read('currencyData.json').supplemental.currencyData.fractions; const decimalPlacesOf = code => Number((fractions[code] ?? fractions.DEFAULT)._digits); const escape = value => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); @@ -31,7 +31,7 @@ if (entries.length === 0) entries[entries.length - 1] = entries.at(-1).replace(/,$/, ''); -const cldrVersion = read('cldr-core/package.json').version; +const cldrVersion = fs.readFileSync(path.resolve(here, 'cldr/VERSION'), 'utf8').trim(); const source = `// // Copyright 2022 Mindbox Ltd // // // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Tools/currency-table/package-lock.json b/Tools/currency-table/package-lock.json deleted file mode 100644 index 53d8308..0000000 --- a/Tools/currency-table/package-lock.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "quokka-currency-table", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "quokka-currency-table", - "dependencies": { - "cldr-core": "48.2.0", - "cldr-numbers-full": "48.2.0" - } - }, - "node_modules/cldr-core": { - "version": "48.2.0", - "resolved": "https://registry.npmjs.org/cldr-core/-/cldr-core-48.2.0.tgz", - "integrity": "sha512-zfmLothncSwfv2jlevoSrgI2VGgH8SDGHXst6jUEotHA8nq9Igeg9jzdJDFtBso9pgJuG89DK16TzrmZDdo2Bg==", - "license": "Unicode-3.0" - }, - "node_modules/cldr-numbers-full": { - "version": "48.2.0", - "resolved": "https://registry.npmjs.org/cldr-numbers-full/-/cldr-numbers-full-48.2.0.tgz", - "integrity": "sha512-0EQ+UkVDsyXOlxnWnL1RIENVZFqCnb04D1Yrku0obnOyttbuZz8tHmUd5c5AEvhGS/Gg2zI4fTaCm51LSGITog==", - "license": "Unicode-3.0", - "peerDependencies": { - "cldr-core": "48.2.0" - } - } - } -} diff --git a/Tools/currency-table/package.json b/Tools/currency-table/package.json deleted file mode 100644 index 845be20..0000000 --- a/Tools/currency-table/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "quokka-currency-table", - "private": true, - "type": "module", - "scripts": { - "generate": "node generate.mjs" - }, - "dependencies": { - "cldr-core": "48.2.0", - "cldr-numbers-full": "48.2.0" - } -} From 1d27e01c6f9fc3ccdb794e0face054ca33a4be35 Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Fri, 14 Aug 2026 13:27:14 +0200 Subject: [PATCH 13/14] Parse the display mode once instead of comparing strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mode arrives as a string because the template language has no other way to spell it, and because it may come from the model rather than a literal. That is fine at the boundary; carrying the string into the formatter was not. Every call compared it up to three times, and the set of valid modes was spelled out twice — once in the validator and once in the formatter — which is two places to disagree. It is now parsed at the boundary into an enum, and the validator asks the same parser the formatter uses. Co-Authored-By: Claude Fable 5 --- .../Standard/Money/CurrencyDisplayMode.cs | 63 +++++++++++++++++++ .../Money/FormatMoneyTemplateFunction.cs | 2 +- ...matMoneyWithDisplayModeTemplateFunction.cs | 8 +-- .../Standard/Money/MoneyFormatter.cs | 19 +----- 4 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyDisplayMode.cs diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyDisplayMode.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyDisplayMode.cs new file mode 100644 index 0000000..f8ae804 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyDisplayMode.cs @@ -0,0 +1,63 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +using System; + +namespace Mindbox.Quokka +{ + internal enum CurrencyDisplayMode + { + NarrowSymbol, + Symbol, + Code + } + + internal static class CurrencyDisplayModes + { + public const string NarrowSymbolName = "narrowSymbol"; + public const string SymbolName = "symbol"; + public const string CodeName = "code"; + + public static bool TryParse(string value, out CurrencyDisplayMode mode) + { + var name = value?.Trim(); + if (string.IsNullOrEmpty(name) || Matches(name, NarrowSymbolName)) + { + mode = CurrencyDisplayMode.NarrowSymbol; + return true; + } + + if (Matches(name, SymbolName)) + { + mode = CurrencyDisplayMode.Symbol; + return true; + } + + if (Matches(name, CodeName)) + { + mode = CurrencyDisplayMode.Code; + return true; + } + + mode = CurrencyDisplayMode.NarrowSymbol; + return false; + } + + public static CurrencyDisplayMode ParseOrDefault(string value) => + TryParse(value, out var mode) ? mode : CurrencyDisplayMode.NarrowSymbol; + + private static bool Matches(string value, string name) => + string.Equals(value, name, StringComparison.OrdinalIgnoreCase); + } +} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs index 4bd26d8..aab60a5 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyTemplateFunction.cs @@ -28,7 +28,7 @@ public FormatMoneyTemplateFunction() public override string Invoke(RenderSettings settings, decimal amount, string currencyCode) { - return MoneyFormatter.Format(amount, currencyCode, MoneyFormatter.NarrowSymbolDisplayMode); + return MoneyFormatter.Format(amount, currencyCode, CurrencyDisplayMode.NarrowSymbol); } } } diff --git a/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyWithDisplayModeTemplateFunction.cs b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyWithDisplayModeTemplateFunction.cs index 1f969a8..83023a5 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyWithDisplayModeTemplateFunction.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/FormatMoneyWithDisplayModeTemplateFunction.cs @@ -29,17 +29,17 @@ public FormatMoneyWithDisplayModeTemplateFunction() public override string Invoke(RenderSettings settings, decimal amount, string currencyCode, string displayMode) { - return MoneyFormatter.Format(amount, currencyCode, displayMode); + return MoneyFormatter.Format(amount, currencyCode, CurrencyDisplayModes.ParseOrDefault(displayMode)); } private static ArgumentValueValidationResult ValidateDisplayMode(string displayMode) { - return MoneyFormatter.IsSupportedDisplayMode(displayMode) + return CurrencyDisplayModes.TryParse(displayMode, out _) ? ArgumentValueValidationResult.Valid : new ArgumentValueValidationResult( false, - $"Display mode should be one of: {MoneyFormatter.NarrowSymbolDisplayMode}, " - + $"{MoneyFormatter.SymbolDisplayMode}, {MoneyFormatter.CodeDisplayMode}"); + $"Display mode should be one of: {CurrencyDisplayModes.NarrowSymbolName}, " + + $"{CurrencyDisplayModes.SymbolName}, {CurrencyDisplayModes.CodeName}"); } } } diff --git a/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs b/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs index e0a8bda..d9f46e4 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs @@ -18,20 +18,10 @@ namespace Mindbox.Quokka { internal static class MoneyFormatter { - public const string NarrowSymbolDisplayMode = "narrowSymbol"; - public const string SymbolDisplayMode = "symbol"; - public const string CodeDisplayMode = "code"; - private const string MinusSign = "-"; private const string Space = " "; - public static bool IsSupportedDisplayMode(string displayMode) => - displayMode == null - || IsMode(displayMode, NarrowSymbolDisplayMode) - || IsMode(displayMode, SymbolDisplayMode) - || IsMode(displayMode, CodeDisplayMode); - - public static string Format(decimal amount, string currencyCode, string displayMode) + public static string Format(decimal amount, string currencyCode, CurrencyDisplayMode displayMode) { var code = currencyCode?.Trim(); var format = CurrencyFormats.TryGet(code); @@ -49,16 +39,13 @@ public static string Format(decimal amount, string currencyCode, string displayM ? sign + formattedAmount : sign + formattedAmount + Space + code.ToUpperInvariant(); - if (IsMode(displayMode, CodeDisplayMode)) + if (displayMode == CurrencyDisplayMode.Code) return sign + code.ToUpperInvariant() + Space + formattedAmount; - var symbol = IsMode(displayMode, SymbolDisplayMode) ? format.Symbol : format.NarrowSymbol; + var symbol = displayMode == CurrencyDisplayMode.Symbol ? format.Symbol : format.NarrowSymbol; var separator = format.SpaceAfterSymbol || char.IsLetter(symbol[symbol.Length - 1]) ? Space : string.Empty; return sign + symbol + separator + formattedAmount; } - - private static bool IsMode(string displayMode, string mode) => - string.Equals(displayMode?.Trim(), mode, StringComparison.OrdinalIgnoreCase); } } From 0ce64766ec5505100d0a06cf868ba12335f78b1c Mon Sep 17 00:00:00 2001 From: Dmitry Rubtsov Date: Fri, 14 Aug 2026 15:36:11 +0200 Subject: [PATCH 14/14] Take currency data from NodaMoney instead of a generated table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hand-rolled table was built from CLDR because I believed no library carried the disambiguated symbols the card asks for. That was wrong: I had only read NodaMoney's Symbol property and missed InternationalSymbol sitting next to it, which yields US$, S$, CA$ and NT$ directly. CLDR cannot produce S$ at all — it falls back to the SGD code — so the table failed the card's own acceptance example while the library meets it. Decimals move from CLDR's display digits to ISO 4217 minor units. That gives HUF, IDR, PKR and COP two decimals rather than none, which matches what Shopify shows: its checkout always renders decimal places, and it does not list those currencies as zero-decimal. Arabic-script currencies now render their native RTL symbol where the table substituted the ISO code. Left as is on purpose — a rule guarding them would have to test for RTL script, and the obvious spelling of it ("symbol is not Latin") would also catch the won, baht and rupee. The display mode already lets a template ask for the code instead. Drops the generator, the vendored CLDR payload and the CI drift check. Co-Authored-By: Claude Fable 5 --- .github/workflows/pull_request.yml | 5 - .../Standard/Money/CurrencyFormats.cs | 42 + .../Standard/Money/CurrencyFormats.g.cs | 343 -- .../Standard/Money/MoneyFormatter.cs | 5 +- Engine/Quokka.Core/Mindbox.Quokka.csproj | 1 + .../FormatMoneyTemplateFunctionTests.cs | 28 +- Tools/currency-table/README.md | 30 - Tools/currency-table/cldr/VERSION | 1 - .../cldr/currencies-en-001.json | 1812 -------- Tools/currency-table/cldr/currencyData.json | 3650 ----------------- Tools/currency-table/generate.mjs | 75 - 11 files changed, 63 insertions(+), 5929 deletions(-) create mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs delete mode 100644 Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs delete mode 100644 Tools/currency-table/README.md delete mode 100644 Tools/currency-table/cldr/VERSION delete mode 100644 Tools/currency-table/cldr/currencies-en-001.json delete mode 100644 Tools/currency-table/cldr/currencyData.json delete mode 100644 Tools/currency-table/generate.mjs diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 20056ea..d260780 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -25,11 +25,6 @@ jobs: with: dotnet-version: 9 - - name: Check the currency table matches CLDR - run: | - node Tools/currency-table/generate.mjs - git diff --exit-code -- Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs - - name: Build run: dotnet build Engine/Quokka.slnx --configuration Release diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs new file mode 100644 index 0000000..ef5fed7 --- /dev/null +++ b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.cs @@ -0,0 +1,42 @@ +// // Copyright 2022 Mindbox Ltd +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, software +// // distributed under the License is distributed on an "AS IS" BASIS, +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// // See the License for the specific language governing permissions and +// // limitations under the License. + +using System; +using System.Collections.Concurrent; +using NodaMoney; + +namespace Mindbox.Quokka +{ + internal static class CurrencyFormats + { + private static readonly ConcurrentDictionary knownFormats = + new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + public static CurrencyFormat TryGet(string currencyCode) + { + if (string.IsNullOrEmpty(currencyCode)) + return null; + + if (knownFormats.TryGetValue(currencyCode, out var knownFormat)) + return knownFormat; + + if (!CurrencyInfo.TryFromCode(currencyCode, out var currency)) + return null; + + var format = new CurrencyFormat(currency.Symbol, currency.InternationalSymbol, currency.DecimalDigits); + knownFormats[currencyCode] = format; + return format; + } + } +} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs b/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs deleted file mode 100644 index 18139c7..0000000 --- a/Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs +++ /dev/null @@ -1,343 +0,0 @@ -// // Copyright 2022 Mindbox Ltd -// // -// // Licensed under the Apache License, Version 2.0 (the "License"); -// // you may not use this file except in compliance with the License. -// // You may obtain a copy of the License at -// // -// // http://www.apache.org/licenses/LICENSE-2.0 -// // -// // Unless required by applicable law or agreed to in writing, software -// // distributed under the License is distributed on an "AS IS" BASIS, -// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// // See the License for the specific language governing permissions and -// // limitations under the License. - -// -// Generated by tools/currency-table from CLDR 48.2.0 (en-001). Do not edit by hand. -// - -using System; -using System.Collections.Generic; - -namespace Mindbox.Quokka -{ - internal static class CurrencyFormats - { - private static readonly IReadOnlyDictionary formatsByCode = - new Dictionary(StringComparer.OrdinalIgnoreCase) - { - ["ADP"] = new("ADP", "ADP", 0), - ["AED"] = new("AED", "AED", 2), - ["AFA"] = new("AFA", "AFA", 2), - ["AFN"] = new("؋", "AFN", 0), - ["ALK"] = new("ALK", "ALK", 2), - ["ALL"] = new("ALL", "ALL", 0), - ["AMD"] = new("֏", "AMD", 2), - ["ANG"] = new("ANG", "ANG", 2), - ["AOA"] = new("Kz", "AOA", 2), - ["AOK"] = new("AOK", "AOK", 2), - ["AON"] = new("AON", "AON", 2), - ["AOR"] = new("AOR", "AOR", 2), - ["ARA"] = new("ARA", "ARA", 2), - ["ARL"] = new("ARL", "ARL", 2), - ["ARM"] = new("ARM", "ARM", 2), - ["ARP"] = new("ARP", "ARP", 2), - ["ARS"] = new("$", "ARS", 2), - ["ATS"] = new("ATS", "ATS", 2), - ["AUD"] = new("$", "A$", 2), - ["AWG"] = new("AWG", "AWG", 2), - ["AZM"] = new("AZM", "AZM", 2), - ["AZN"] = new("₼", "AZN", 2), - ["BAD"] = new("BAD", "BAD", 2), - ["BAM"] = new("KM", "BAM", 2), - ["BAN"] = new("BAN", "BAN", 2), - ["BBD"] = new("$", "BBD", 2), - ["BDT"] = new("৳", "BDT", 2), - ["BEC"] = new("BEC", "BEC", 2), - ["BEF"] = new("BEF", "BEF", 2), - ["BEL"] = new("BEL", "BEL", 2), - ["BGL"] = new("BGL", "BGL", 2), - ["BGM"] = new("BGM", "BGM", 2), - ["BGN"] = new("BGN", "BGN", 2), - ["BGO"] = new("BGO", "BGO", 2), - ["BHD"] = new("BHD", "BHD", 3), - ["BIF"] = new("BIF", "BIF", 0), - ["BMD"] = new("$", "BMD", 2), - ["BND"] = new("$", "BND", 2), - ["BOB"] = new("Bs", "BOB", 2), - ["BOL"] = new("BOL", "BOL", 2), - ["BOP"] = new("BOP", "BOP", 2), - ["BOV"] = new("BOV", "BOV", 2), - ["BRB"] = new("BRB", "BRB", 2), - ["BRC"] = new("BRC", "BRC", 2), - ["BRE"] = new("BRE", "BRE", 2), - ["BRL"] = new("R$", "R$", 2), - ["BRN"] = new("BRN", "BRN", 2), - ["BRR"] = new("BRR", "BRR", 2), - ["BRZ"] = new("BRZ", "BRZ", 2), - ["BSD"] = new("$", "BSD", 2), - ["BTN"] = new("BTN", "BTN", 2), - ["BUK"] = new("BUK", "BUK", 2), - ["BWP"] = new("P", "BWP", 2), - ["BYB"] = new("BYB", "BYB", 2), - ["BYN"] = new("BYN", "BYN", 2), - ["BYR"] = new("BYR", "BYR", 0), - ["BZD"] = new("$", "BZD", 2), - ["CAD"] = new("$", "CA$", 2), - ["CDF"] = new("CDF", "CDF", 2), - ["CHE"] = new("CHE", "CHE", 2), - ["CHF"] = new("CHF", "CHF", 2), - ["CHW"] = new("CHW", "CHW", 2), - ["CLE"] = new("CLE", "CLE", 2), - ["CLF"] = new("CLF", "CLF", 4), - ["CLP"] = new("$", "CLP", 0), - ["CNH"] = new("CNH", "CNH", 2), - ["CNX"] = new("CNX", "CNX", 2), - ["CNY"] = new("¥", "CN¥", 2), - ["COP"] = new("$", "COP", 0), - ["COU"] = new("COU", "COU", 2), - ["CRC"] = new("₡", "CRC", 2), - ["CSD"] = new("CSD", "CSD", 2), - ["CSK"] = new("CSK", "CSK", 2), - ["CUC"] = new("$", "CUC", 2), - ["CUP"] = new("$", "CUP", 2), - ["CVE"] = new("CVE", "CVE", 2), - ["CYP"] = new("CYP", "CYP", 2), - ["CZK"] = new("Kč", "CZK", 2), - ["DDM"] = new("DDM", "DDM", 2), - ["DEM"] = new("DEM", "DEM", 2), - ["DJF"] = new("DJF", "DJF", 0), - ["DKK"] = new("kr", "DKK", 2), - ["DOP"] = new("$", "DOP", 2), - ["DZD"] = new("DZD", "DZD", 2), - ["ECS"] = new("ECS", "ECS", 2), - ["ECV"] = new("ECV", "ECV", 2), - ["EEK"] = new("EEK", "EEK", 2), - ["EGP"] = new("E£", "EGP", 2), - ["ERN"] = new("ERN", "ERN", 2), - ["ESA"] = new("ESA", "ESA", 2), - ["ESB"] = new("ESB", "ESB", 2), - ["ESP"] = new("₧", "ESP", 0), - ["ETB"] = new("ETB", "ETB", 2), - ["EUR"] = new("€", "€", 2), - ["FIM"] = new("FIM", "FIM", 2), - ["FJD"] = new("$", "FJD", 2), - ["FKP"] = new("£", "FKP", 2), - ["FRF"] = new("FRF", "FRF", 2), - ["GBP"] = new("£", "£", 2), - ["GEK"] = new("GEK", "GEK", 2), - ["GEL"] = new("₾", "GEL", 2), - ["GHC"] = new("GHC", "GHC", 2), - ["GHS"] = new("GH₵", "GHS", 2), - ["GIP"] = new("£", "GIP", 2), - ["GMD"] = new("GMD", "GMD", 2), - ["GNF"] = new("FG", "GNF", 0), - ["GNS"] = new("GNS", "GNS", 2), - ["GQE"] = new("GQE", "GQE", 2), - ["GRD"] = new("GRD", "GRD", 2), - ["GTQ"] = new("Q", "GTQ", 2), - ["GWE"] = new("GWE", "GWE", 2), - ["GWP"] = new("GWP", "GWP", 2), - ["GYD"] = new("$", "GYD", 2), - ["HKD"] = new("$", "HK$", 2), - ["HNL"] = new("L", "HNL", 2), - ["HRD"] = new("HRD", "HRD", 2), - ["HRK"] = new("kn", "HRK", 2), - ["HTG"] = new("HTG", "HTG", 2), - ["HUF"] = new("Ft", "HUF", 0), - ["IDR"] = new("Rp", "IDR", 0), - ["IEP"] = new("IEP", "IEP", 2), - ["ILP"] = new("ILP", "ILP", 2), - ["ILR"] = new("ILR", "ILR", 2), - ["ILS"] = new("₪", "₪", 2), - ["INR"] = new("₹", "₹", 2), - ["IQD"] = new("IQD", "IQD", 0), - ["IRR"] = new("IRR", "IRR", 0), - ["ISJ"] = new("ISJ", "ISJ", 2), - ["ISK"] = new("kr", "ISK", 0), - ["ITL"] = new("ITL", "ITL", 0), - ["JMD"] = new("$", "JMD", 2), - ["JOD"] = new("JOD", "JOD", 3), - ["JPY"] = new("¥", "JP¥", 0), - ["KES"] = new("KES", "KES", 2), - ["KGS"] = new("⃀", "KGS", 2), - ["KHR"] = new("៛", "KHR", 2), - ["KMF"] = new("CF", "KMF", 0), - ["KPW"] = new("₩", "KPW", 0), - ["KRH"] = new("KRH", "KRH", 2), - ["KRO"] = new("KRO", "KRO", 2), - ["KRW"] = new("₩", "₩", 0), - ["KWD"] = new("KWD", "KWD", 3), - ["KYD"] = new("$", "KYD", 2), - ["KZT"] = new("₸", "KZT", 2), - ["LAK"] = new("₭", "LAK", 0), - ["LBP"] = new("L£", "LBP", 0), - ["LKR"] = new("Rs", "LKR", 2), - ["LRD"] = new("$", "LRD", 2), - ["LSL"] = new("LSL", "LSL", 2), - ["LTL"] = new("Lt", "LTL", 2), - ["LTT"] = new("LTT", "LTT", 2), - ["LUC"] = new("LUC", "LUC", 2), - ["LUF"] = new("LUF", "LUF", 0), - ["LUL"] = new("LUL", "LUL", 2), - ["LVL"] = new("Ls", "LVL", 2), - ["LVR"] = new("LVR", "LVR", 2), - ["LYD"] = new("LYD", "LYD", 3), - ["MAD"] = new("MAD", "MAD", 2), - ["MAF"] = new("MAF", "MAF", 2), - ["MCF"] = new("MCF", "MCF", 2), - ["MDC"] = new("MDC", "MDC", 2), - ["MDL"] = new("MDL", "MDL", 2), - ["MGA"] = new("Ar", "MGA", 0), - ["MGF"] = new("MGF", "MGF", 0), - ["MKD"] = new("MKD", "MKD", 2), - ["MKN"] = new("MKN", "MKN", 2), - ["MLF"] = new("MLF", "MLF", 2), - ["MMK"] = new("K", "MMK", 0), - ["MNT"] = new("₮", "MNT", 2), - ["MOP"] = new("MOP", "MOP", 2), - ["MRO"] = new("MRO", "MRO", 0), - ["MRU"] = new("MRU", "MRU", 2), - ["MTL"] = new("MTL", "MTL", 2), - ["MTP"] = new("MTP", "MTP", 2), - ["MUR"] = new("Rs", "MUR", 2), - ["MVP"] = new("MVP", "MVP", 2), - ["MVR"] = new("MVR", "MVR", 2), - ["MWK"] = new("MWK", "MWK", 2), - ["MXN"] = new("$", "MX$", 2), - ["MXP"] = new("MXP", "MXP", 2), - ["MXV"] = new("MXV", "MXV", 2), - ["MYR"] = new("RM", "MYR", 2), - ["MZE"] = new("MZE", "MZE", 2), - ["MZM"] = new("MZM", "MZM", 2), - ["MZN"] = new("MZN", "MZN", 2), - ["NAD"] = new("$", "NAD", 2), - ["NGN"] = new("₦", "NGN", 2), - ["NIC"] = new("NIC", "NIC", 2), - ["NIO"] = new("C$", "NIO", 2), - ["NLG"] = new("NLG", "NLG", 2), - ["NOK"] = new("kr", "NOK", 2), - ["NPR"] = new("Rs", "NPR", 2), - ["NZD"] = new("$", "NZ$", 2), - ["OMR"] = new("OMR", "OMR", 3), - ["PAB"] = new("PAB", "PAB", 2), - ["PEI"] = new("PEI", "PEI", 2), - ["PEN"] = new("PEN", "PEN", 2), - ["PES"] = new("PES", "PES", 2), - ["PGK"] = new("PGK", "PGK", 2), - ["PHP"] = new("₱", "₱", 2), - ["PKR"] = new("Rs", "PKR", 0), - ["PLN"] = new("zł", "PLN", 2), - ["PLZ"] = new("PLZ", "PLZ", 2), - ["PTE"] = new("PTE", "PTE", 2), - ["PYG"] = new("₲", "PYG", 0), - ["QAR"] = new("QAR", "QAR", 2), - ["RHD"] = new("RHD", "RHD", 2), - ["ROL"] = new("ROL", "ROL", 2), - ["RON"] = new("lei", "RON", 2), - ["RSD"] = new("RSD", "RSD", 2), - ["RUB"] = new("₽", "RUB", 2), - ["RUR"] = new("RUR", "RUR", 2), - ["RWF"] = new("RF", "RWF", 0), - ["SAR"] = new("SAR", "SAR", 2), - ["SBD"] = new("$", "SBD", 2), - ["SCR"] = new("SCR", "SCR", 2), - ["SDD"] = new("SDD", "SDD", 2), - ["SDG"] = new("SDG", "SDG", 2), - ["SDP"] = new("SDP", "SDP", 2), - ["SEK"] = new("kr", "SEK", 2), - ["SGD"] = new("$", "SGD", 2), - ["SHP"] = new("£", "SHP", 2), - ["SIT"] = new("SIT", "SIT", 2), - ["SKK"] = new("SKK", "SKK", 2), - ["SLE"] = new("SLE", "SLE", 2), - ["SLL"] = new("SLL", "SLL", 0), - ["SOS"] = new("SOS", "SOS", 0), - ["SRD"] = new("$", "SRD", 2), - ["SRG"] = new("SRG", "SRG", 2), - ["SSP"] = new("£", "SSP", 2), - ["STD"] = new("STD", "STD", 0), - ["STN"] = new("Db", "STN", 2), - ["SUR"] = new("SUR", "SUR", 2), - ["SVC"] = new("SVC", "SVC", 2), - ["SYP"] = new("£", "SYP", 0), - ["SZL"] = new("SZL", "SZL", 2), - ["THB"] = new("฿", "THB", 2), - ["TJR"] = new("TJR", "TJR", 2), - ["TJS"] = new("TJS", "TJS", 2), - ["TMM"] = new("TMM", "TMM", 0), - ["TMT"] = new("TMT", "TMT", 2), - ["TND"] = new("TND", "TND", 3), - ["TOP"] = new("T$", "TOP", 2), - ["TPE"] = new("TPE", "TPE", 2), - ["TRL"] = new("TRL", "TRL", 0), - ["TRY"] = new("₺", "TRY", 2), - ["TTD"] = new("$", "TTD", 2), - ["TWD"] = new("$", "NT$", 2), - ["TZS"] = new("TZS", "TZS", 2), - ["UAH"] = new("₴", "UAH", 2), - ["UAK"] = new("UAK", "UAK", 2), - ["UGS"] = new("UGS", "UGS", 2), - ["UGX"] = new("UGX", "UGX", 0), - ["USD"] = new("$", "US$", 2), - ["USN"] = new("USN", "USN", 2), - ["USS"] = new("USS", "USS", 2), - ["UYI"] = new("UYI", "UYI", 0), - ["UYP"] = new("UYP", "UYP", 2), - ["UYU"] = new("$", "UYU", 2), - ["UYW"] = new("UYW", "UYW", 4), - ["UZS"] = new("UZS", "UZS", 2), - ["VEB"] = new("VEB", "VEB", 2), - ["VED"] = new("VED", "VED", 2), - ["VEF"] = new("Bs", "VEF", 2), - ["VES"] = new("VES", "VES", 2), - ["VND"] = new("₫", "₫", 0), - ["VNN"] = new("VNN", "VNN", 2), - ["VUV"] = new("VUV", "VUV", 0), - ["WST"] = new("WST", "WST", 2), - ["XAF"] = new("FCFA", "FCFA", 0), - ["XAG"] = new("XAG", "XAG", 2), - ["XAU"] = new("XAU", "XAU", 2), - ["XBA"] = new("XBA", "XBA", 2), - ["XBB"] = new("XBB", "XBB", 2), - ["XBC"] = new("XBC", "XBC", 2), - ["XBD"] = new("XBD", "XBD", 2), - ["XCD"] = new("$", "EC$", 2), - ["XCG"] = new("Cg.", "Cg.", 2), - ["XDR"] = new("XDR", "XDR", 2), - ["XEU"] = new("XEU", "XEU", 2), - ["XFO"] = new("XFO", "XFO", 2), - ["XFU"] = new("XFU", "XFU", 2), - ["XOF"] = new("F CFA", "F CFA", 0), - ["XPD"] = new("XPD", "XPD", 2), - ["XPF"] = new("CFPF", "CFPF", 0), - ["XPT"] = new("XPT", "XPT", 2), - ["XRE"] = new("XRE", "XRE", 2), - ["XSU"] = new("XSU", "XSU", 2), - ["XTS"] = new("XTS", "XTS", 2), - ["XUA"] = new("XUA", "XUA", 2), - ["XXX"] = new("¤", "¤", 2), - ["YDD"] = new("YDD", "YDD", 2), - ["YER"] = new("YER", "YER", 0), - ["YUD"] = new("YUD", "YUD", 2), - ["YUM"] = new("YUM", "YUM", 2), - ["YUN"] = new("YUN", "YUN", 2), - ["YUR"] = new("YUR", "YUR", 2), - ["ZAL"] = new("ZAL", "ZAL", 2), - ["ZAR"] = new("R", "ZAR", 2), - ["ZMK"] = new("ZMK", "ZMK", 0), - ["ZMW"] = new("ZK", "ZMW", 2), - ["ZRN"] = new("ZRN", "ZRN", 2), - ["ZRZ"] = new("ZRZ", "ZRZ", 2), - ["ZWD"] = new("ZWD", "ZWD", 0), - ["ZWG"] = new("ZWG", "ZWG", 2), - ["ZWL"] = new("ZWL", "ZWL", 2), - ["ZWR"] = new("ZWR", "ZWR", 2) - }; - - public static CurrencyFormat TryGet(string currencyCode) => - currencyCode != null && formatsByCode.TryGetValue(currencyCode, out var format) - ? format - : null; - } -} diff --git a/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs b/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs index d9f46e4..17b37f3 100644 --- a/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs +++ b/Engine/Quokka.Core/Functions/Standard/Money/MoneyFormatter.cs @@ -43,7 +43,10 @@ public static string Format(decimal amount, string currencyCode, CurrencyDisplay return sign + code.ToUpperInvariant() + Space + formattedAmount; var symbol = displayMode == CurrencyDisplayMode.Symbol ? format.Symbol : format.NarrowSymbol; - var separator = format.SpaceAfterSymbol || char.IsLetter(symbol[symbol.Length - 1]) ? Space : string.Empty; + var lastCharacter = symbol[symbol.Length - 1]; + var separator = format.SpaceAfterSymbol || char.IsLetter(lastCharacter) || lastCharacter == '.' + ? Space + : string.Empty; return sign + symbol + separator + formattedAmount; } diff --git a/Engine/Quokka.Core/Mindbox.Quokka.csproj b/Engine/Quokka.Core/Mindbox.Quokka.csproj index f0fb783..485ffad 100644 --- a/Engine/Quokka.Core/Mindbox.Quokka.csproj +++ b/Engine/Quokka.Core/Mindbox.Quokka.csproj @@ -18,6 +18,7 @@ + diff --git a/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs index 81af96e..2df6907 100644 --- a/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs +++ b/Engine/Quokka.Tests/Functions/FormatMoneyTemplateFunctionTests.cs @@ -29,11 +29,13 @@ public class FormatMoneyTemplateFunctionTests [DataRow("EUR", "€1,234,567.89")] [DataRow("RUB", "₽1,234,567.89")] [DataRow("INR", "₹1,234,567.89")] - [DataRow("CHF", "CHF 1,234,567.89")] - [DataRow("KWD", "KWD 1,234,567.890")] + [DataRow("CHF", "Fr. 1,234,567.89")] + [DataRow("KWD", "د.ك 1,234,567.890")] [DataRow("NGN", "₦1,234,567.89")] [DataRow("RON", "lei 1,234,567.89")] - [DataRow("PKR", "Rs 1,234,568")] + [DataRow("PKR", "Rs 1,234,567.89")] + [DataRow("HUF", "Ft 1,234,567.89")] + [DataRow("IDR", "Rp 1,234,567.89")] public void FormatMoney_PutsTheSymbolFirstAndGroupsInThousands(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("1234567.89", currencyCode)); @@ -43,8 +45,6 @@ public void FormatMoney_PutsTheSymbolFirstAndGroupsInThousands(string currencyCo [DataRow("JPY", "¥9,072")] [DataRow("KRW", "₩9,072")] [DataRow("VND", "₫9,072")] - [DataRow("HUF", "Ft 9,072")] - [DataRow("IDR", "Rp 9,072")] public void FormatMoney_WithZeroDecimalCurrency_DropsDecimals(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("9072.00", currencyCode)); @@ -53,13 +53,13 @@ public void FormatMoney_WithZeroDecimalCurrency_DropsDecimals(string currencyCod [TestMethod] public void FormatMoney_WithThreeDecimalCurrency_KeepsThreeDecimals() { - Assert.AreEqual("KWD 1,234.560", RenderMoney("1234.56", "KWD")); + Assert.AreEqual("د.ك 1,234.560", RenderMoney("1234.56", "KWD")); } [TestMethod] [DataRow("USD", "-$1,234.56")] [DataRow("RUB", "-₽1,234.56")] - [DataRow("KWD", "-KWD 1,234.560")] + [DataRow("KWD", "-د.ك 1,234.560")] public void FormatMoney_WithNegativeAmount_PutsTheSignFirst(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("-1234.56", currencyCode)); @@ -69,8 +69,10 @@ public void FormatMoney_WithNegativeAmount_PutsTheSignFirst(string currencyCode, [DataRow("USD", "US$1,234.56")] [DataRow("CAD", "CA$1,234.56")] [DataRow("AUD", "A$1,234.56")] - [DataRow("RUB", "RUB 1,234.56")] - [DataRow("THB", "THB 1,234.56")] + [DataRow("SGD", "S$1,234.56")] + [DataRow("HKD", "HK$1,234.56")] + [DataRow("NZD", "NZ$1,234.56")] + [DataRow("TWD", "NT$1,234.56")] public void FormatMoney_WithSymbolDisplayMode_DisambiguatesSharedSymbols(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("1234.56", currencyCode, "symbol")); @@ -79,6 +81,8 @@ public void FormatMoney_WithSymbolDisplayMode_DisambiguatesSharedSymbols(string [TestMethod] [DataRow("EUR", "€1,234.56")] [DataRow("GBP", "£1,234.56")] + [DataRow("RUB", "₽1,234.56")] + [DataRow("THB", "฿1,234.56")] public void FormatMoney_WithSymbolDisplayMode_KeepsUnambiguousSymbols(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("1234.56", currencyCode, "symbol")); @@ -108,8 +112,8 @@ public void FormatMoney_WithUnknownCurrencyCode_FallsBackToAmountAndCode() } [TestMethod] - [DataRow("TND", "TND 1,234.560")] - [DataRow("UGX", "UGX 1,235")] + [DataRow("TND", "د.ت 1,234.560")] + [DataRow("UGX", "USh 1,235")] public void FormatMoney_WithNonTwoDecimalCurrency_UsesItsMinorUnits(string currencyCode, string expected) { Assert.AreEqual(expected, RenderMoney("1234.56", currencyCode)); @@ -202,7 +206,7 @@ public void FormatMoney_SeparatesWithPlainSpaces_SoSmsStaysInTheGsmAlphabet() { var result = RenderMoney("1234.56", "CHF"); - Assert.AreEqual("CHF 1,234.56", result); + Assert.AreEqual("Fr. 1,234.56", result); Assert.IsFalse(result.Contains('\u00A0')); } diff --git a/Tools/currency-table/README.md b/Tools/currency-table/README.md deleted file mode 100644 index 2a9b622..0000000 --- a/Tools/currency-table/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Currency table generator - -`Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs` is generated from the -Unicode CLDR data vendored in `cldr/` — currency symbols from `currencies-en-001.json`, -minor units from `currencyData.json`, both copied verbatim from the release named in -`cldr/VERSION`. - -Regenerate with Node and nothing else: - - node generate.mjs - -CI runs the same command and fails if the committed file differs, so the table can be -neither edited by hand nor left behind when the data changes. - -To take a newer CLDR release, replace the two files and the version, then regenerate: - - V=48.2.0 - curl -sfo cldr/currencies-en-001.json https://unpkg.com/cldr-numbers-full@$V/main/en-001/currencies.json - curl -sfo cldr/currencyData.json https://unpkg.com/cldr-core@$V/supplemental/currencyData.json - echo $V > cldr/VERSION - node generate.mjs - -The two files are vendored rather than installed because the packages carrying them are -forty megabytes of every locale on earth, and this needs one locale and one supplemental -table. Vendoring also keeps the build off the network. - -Every currency CLDR carries is generated, not a curated subset — the engine renders -templates for every integration, not just one, and a subset would silently downgrade any -currency a payment provider adds later. A code CLDR does not know falls back to -"amount + ISO code" at render time. diff --git a/Tools/currency-table/cldr/VERSION b/Tools/currency-table/cldr/VERSION deleted file mode 100644 index 7ff0d17..0000000 --- a/Tools/currency-table/cldr/VERSION +++ /dev/null @@ -1 +0,0 @@ -48.2.0 diff --git a/Tools/currency-table/cldr/currencies-en-001.json b/Tools/currency-table/cldr/currencies-en-001.json deleted file mode 100644 index fd1b895..0000000 --- a/Tools/currency-table/cldr/currencies-en-001.json +++ /dev/null @@ -1,1812 +0,0 @@ -{ - "main": { - "en-001": { - "identity": { - "language": "en", - "territory": "001" - }, - "numbers": { - "currencies": { - "ADP": { - "displayName": "Andorran Peseta", - "displayName-count-one": "Andorran peseta", - "displayName-count-other": "Andorran pesetas" - }, - "AED": { - "displayName": "United Arab Emirates Dirham", - "displayName-count-one": "UAE dirham", - "displayName-count-other": "UAE dirhams", - "symbol": "AED" - }, - "AFA": { - "displayName": "Afghan Afghani (1927–2002)", - "displayName-count-one": "Afghan afghani (1927–2002)", - "displayName-count-other": "Afghan afghanis (1927–2002)" - }, - "AFN": { - "displayName": "Afghan Afghani", - "displayName-count-one": "Afghan afghani", - "displayName-count-other": "Afghan afghanis", - "symbol": "AFN", - "symbol-alt-narrow": "؋" - }, - "ALK": { - "displayName": "Albanian Lek (1946–1965)", - "displayName-count-one": "Albanian lek (1946–1965)", - "displayName-count-other": "Albanian lekë (1946–1965)" - }, - "ALL": { - "displayName": "Albanian Lek", - "displayName-count-one": "Albanian lek", - "displayName-count-other": "Albanian lekë", - "symbol": "ALL" - }, - "AMD": { - "displayName": "Armenian Dram", - "displayName-count-one": "Armenian dram", - "displayName-count-other": "Armenian drams", - "symbol": "AMD", - "symbol-alt-narrow": "֏" - }, - "ANG": { - "displayName": "Netherlands Antillean Guilder", - "displayName-count-one": "Netherlands Antillean guilder", - "displayName-count-other": "Netherlands Antillean guilders", - "symbol": "ANG" - }, - "AOA": { - "displayName": "Angolan Kwanza", - "displayName-count-one": "Angolan kwanza", - "displayName-count-other": "Angolan kwanzas", - "symbol": "AOA", - "symbol-alt-narrow": "Kz" - }, - "AOK": { - "displayName": "Angolan Kwanza (1977–1991)", - "displayName-count-one": "Angolan kwanza (1977–1991)", - "displayName-count-other": "Angolan kwanzas (1977–1991)" - }, - "AON": { - "displayName": "Angolan New Kwanza (1990–2000)", - "displayName-count-one": "Angolan new kwanza (1990–2000)", - "displayName-count-other": "Angolan new kwanzas (1990–2000)" - }, - "AOR": { - "displayName": "Angolan Readjusted Kwanza (1995–1999)", - "displayName-count-one": "Angolan readjusted kwanza (1995–1999)", - "displayName-count-other": "Angolan readjusted kwanzas (1995–1999)" - }, - "ARA": { - "displayName": "Argentine Austral", - "displayName-count-one": "Argentine austral", - "displayName-count-other": "Argentine australs" - }, - "ARL": { - "displayName": "Argentine Peso Ley (1970–1983)", - "displayName-count-one": "Argentine peso ley (1970–1983)", - "displayName-count-other": "Argentine pesos ley (1970–1983)" - }, - "ARM": { - "displayName": "Argentine Peso (1881–1970)", - "displayName-count-one": "Argentine peso (1881–1970)", - "displayName-count-other": "Argentine pesos (1881–1970)" - }, - "ARP": { - "displayName": "Argentine Peso (1983–1985)", - "displayName-count-one": "Argentine peso (1983–1985)", - "displayName-count-other": "Argentine pesos (1983–1985)" - }, - "ARS": { - "displayName": "Argentine Peso", - "displayName-count-one": "Argentine peso", - "displayName-count-other": "Argentine pesos", - "symbol": "ARS", - "symbol-alt-narrow": "$" - }, - "ATS": { - "displayName": "Austrian Schilling", - "displayName-count-one": "Austrian schilling", - "displayName-count-other": "Austrian schillings" - }, - "AUD": { - "displayName": "Australian Dollar", - "displayName-count-one": "Australian dollar", - "displayName-count-other": "Australian dollars", - "symbol": "A$", - "symbol-alt-narrow": "$" - }, - "AWG": { - "displayName": "Aruban Florin", - "displayName-count-one": "Aruban florin", - "displayName-count-other": "Aruban florin", - "symbol": "AWG" - }, - "AZM": { - "displayName": "Azerbaijani Manat (1993–2006)", - "displayName-count-one": "Azerbaijani manat (1993–2006)", - "displayName-count-other": "Azerbaijani manats (1993–2006)" - }, - "AZN": { - "displayName": "Azerbaijani Manat", - "displayName-count-one": "Azerbaijani manat", - "displayName-count-other": "Azerbaijani manats", - "symbol": "AZN", - "symbol-alt-narrow": "₼" - }, - "BAD": { - "displayName": "Bosnia-Herzegovina Dinar (1992–1994)", - "displayName-count-one": "Bosnia-Herzegovina dinar (1992–1994)", - "displayName-count-other": "Bosnia-Herzegovina dinars (1992–1994)" - }, - "BAM": { - "displayName": "Bosnia-Herzegovina Convertible Mark", - "displayName-count-one": "Bosnia-Herzegovina convertible mark", - "displayName-count-other": "Bosnia-Herzegovina convertible marks", - "symbol": "BAM", - "symbol-alt-narrow": "KM" - }, - "BAN": { - "displayName": "Bosnia-Herzegovina New Dinar (1994–1997)", - "displayName-count-one": "Bosnia-Herzegovina new dinar (1994–1997)", - "displayName-count-other": "Bosnia-Herzegovina new dinars (1994–1997)" - }, - "BBD": { - "displayName": "Barbadian Dollar", - "displayName-count-one": "Barbadian dollar", - "displayName-count-other": "Barbadian dollars", - "symbol": "BBD", - "symbol-alt-narrow": "$" - }, - "BDT": { - "displayName": "Bangladeshi Taka", - "displayName-count-one": "Bangladeshi taka", - "displayName-count-other": "Bangladeshi takas", - "symbol": "BDT", - "symbol-alt-narrow": "৳" - }, - "BEC": { - "displayName": "Belgian Franc (convertible)", - "displayName-count-one": "Belgian franc (convertible)", - "displayName-count-other": "Belgian francs (convertible)" - }, - "BEF": { - "displayName": "Belgian Franc", - "displayName-count-one": "Belgian franc", - "displayName-count-other": "Belgian francs" - }, - "BEL": { - "displayName": "Belgian Franc (financial)", - "displayName-count-one": "Belgian franc (financial)", - "displayName-count-other": "Belgian francs (financial)" - }, - "BGL": { - "displayName": "Bulgarian Hard Lev", - "displayName-count-one": "Bulgarian hard lev", - "displayName-count-other": "Bulgarian hard leva" - }, - "BGM": { - "displayName": "Bulgarian Socialist Lev", - "displayName-count-one": "Bulgarian socialist lev", - "displayName-count-other": "Bulgarian socialist leva" - }, - "BGN": { - "displayName": "Bulgarian Lev", - "displayName-count-one": "Bulgarian lev", - "displayName-count-other": "Bulgarian leva", - "symbol": "BGN" - }, - "BGO": { - "displayName": "Bulgarian Lev (1879–1952)", - "displayName-count-one": "Bulgarian lev (1879–1952)", - "displayName-count-other": "Bulgarian leva (1879–1952)" - }, - "BHD": { - "displayName": "Bahraini Dinar", - "displayName-count-one": "Bahraini dinar", - "displayName-count-other": "Bahraini dinars", - "symbol": "BHD" - }, - "BIF": { - "displayName": "Burundian Franc", - "displayName-count-one": "Burundian franc", - "displayName-count-other": "Burundian francs", - "symbol": "BIF" - }, - "BMD": { - "displayName": "Bermudian Dollar", - "displayName-count-one": "Bermudian dollar", - "displayName-count-other": "Bermudian dollars", - "symbol": "BMD", - "symbol-alt-narrow": "$" - }, - "BND": { - "displayName": "Brunei Dollar", - "displayName-count-one": "Brunei dollar", - "displayName-count-other": "Brunei dollars", - "symbol": "BND", - "symbol-alt-narrow": "$" - }, - "BOB": { - "displayName": "Bolivian Boliviano", - "displayName-count-one": "Bolivian boliviano", - "displayName-count-other": "Bolivian bolivianos", - "symbol": "BOB", - "symbol-alt-narrow": "Bs" - }, - "BOL": { - "displayName": "Bolivian Boliviano (1863–1963)", - "displayName-count-one": "Bolivian boliviano (1863–1963)", - "displayName-count-other": "Bolivian bolivianos (1863–1963)" - }, - "BOP": { - "displayName": "Bolivian Peso", - "displayName-count-one": "Bolivian peso", - "displayName-count-other": "Bolivian pesos" - }, - "BOV": { - "displayName": "Bolivian Mvdol", - "displayName-count-one": "Bolivian mvdol", - "displayName-count-other": "Bolivian mvdols" - }, - "BRB": { - "displayName": "Brazilian New Cruzeiro (1967–1986)", - "displayName-count-one": "Brazilian new cruzeiro (1967–1986)", - "displayName-count-other": "Brazilian new cruzeiros (1967–1986)" - }, - "BRC": { - "displayName": "Brazilian Cruzado (1986–1989)", - "displayName-count-one": "Brazilian cruzado (1986–1989)", - "displayName-count-other": "Brazilian cruzados (1986–1989)" - }, - "BRE": { - "displayName": "Brazilian Cruzeiro (1990–1993)", - "displayName-count-one": "Brazilian cruzeiro (1990–1993)", - "displayName-count-other": "Brazilian cruzeiros (1990–1993)" - }, - "BRL": { - "displayName": "Brazilian Real", - "displayName-count-one": "Brazilian real", - "displayName-count-other": "Brazilian reals", - "symbol": "R$", - "symbol-alt-narrow": "R$" - }, - "BRN": { - "displayName": "Brazilian New Cruzado (1989–1990)", - "displayName-count-one": "Brazilian new cruzado (1989–1990)", - "displayName-count-other": "Brazilian new cruzados (1989–1990)" - }, - "BRR": { - "displayName": "Brazilian Cruzeiro (1993–1994)", - "displayName-count-one": "Brazilian cruzeiro (1993–1994)", - "displayName-count-other": "Brazilian cruzeiros (1993–1994)" - }, - "BRZ": { - "displayName": "Brazilian Cruzeiro (1942–1967)", - "displayName-count-one": "Brazilian cruzeiro (1942–1967)", - "displayName-count-other": "Brazilian cruzeiros (1942–1967)" - }, - "BSD": { - "displayName": "Bahamian Dollar", - "displayName-count-one": "Bahamian dollar", - "displayName-count-other": "Bahamian dollars", - "symbol": "BSD", - "symbol-alt-narrow": "$" - }, - "BTN": { - "displayName": "Bhutanese Ngultrum", - "displayName-count-one": "Bhutanese ngultrum", - "displayName-count-other": "Bhutanese ngultrums", - "symbol": "BTN" - }, - "BUK": { - "displayName": "Burmese Kyat", - "displayName-count-one": "Burmese kyat", - "displayName-count-other": "Burmese kyats" - }, - "BWP": { - "displayName": "Botswanan Pula", - "displayName-count-one": "Botswanan pula", - "displayName-count-other": "Botswanan pulas", - "symbol": "BWP", - "symbol-alt-narrow": "P" - }, - "BYB": { - "displayName": "Belarusian New Rouble (1994–1999)", - "displayName-count-one": "Belarusian new rouble (1994–1999)", - "displayName-count-other": "Belarusian new roubles (1994–1999)" - }, - "BYN": { - "displayName": "Belarusian Rouble", - "displayName-count-one": "Belarusian rouble", - "displayName-count-other": "Belarusian roubles", - "symbol": "BYN" - }, - "BYR": { - "displayName": "Belarusian Rouble (2000–2016)", - "displayName-count-one": "Belarusian rouble (2000–2016)", - "displayName-count-other": "Belarusian roubles (2000–2016)" - }, - "BZD": { - "displayName": "Belize Dollar", - "displayName-count-one": "Belize dollar", - "displayName-count-other": "Belize dollars", - "symbol": "BZD", - "symbol-alt-narrow": "$" - }, - "CAD": { - "displayName": "Canadian Dollar", - "displayName-count-one": "Canadian dollar", - "displayName-count-other": "Canadian dollars", - "symbol": "CA$", - "symbol-alt-narrow": "$" - }, - "CDF": { - "displayName": "Congolese Franc", - "displayName-count-one": "Congolese franc", - "displayName-count-other": "Congolese francs", - "symbol": "CDF" - }, - "CHE": { - "displayName": "WIR Euro", - "displayName-count-one": "WIR euro", - "displayName-count-other": "WIR euros" - }, - "CHF": { - "displayName": "Swiss Franc", - "displayName-count-one": "Swiss franc", - "displayName-count-other": "Swiss francs", - "symbol": "CHF" - }, - "CHW": { - "displayName": "WIR Franc", - "displayName-count-one": "WIR franc", - "displayName-count-other": "WIR francs" - }, - "CLE": { - "displayName": "Chilean Escudo", - "displayName-count-one": "Chilean escudo", - "displayName-count-other": "Chilean escudos" - }, - "CLF": { - "displayName": "Chilean Unit of Account (UF)", - "displayName-count-one": "Chilean unit of account (UF)", - "displayName-count-other": "Chilean units of account (UF)" - }, - "CLP": { - "displayName": "Chilean Peso", - "displayName-count-one": "Chilean peso", - "displayName-count-other": "Chilean pesos", - "symbol": "CLP", - "symbol-alt-narrow": "$" - }, - "CNH": { - "displayName": "Chinese Yuan (offshore)", - "displayName-count-one": "Chinese yuan (offshore)", - "displayName-count-other": "Chinese yuan (offshore)", - "symbol": "CNH" - }, - "CNX": { - "displayName": "Chinese People’s Bank Dollar", - "displayName-count-one": "Chinese People’s Bank dollar", - "displayName-count-other": "Chinese People’s Bank dollars" - }, - "CNY": { - "displayName": "Chinese Yuan", - "displayName-count-one": "Chinese yuan", - "displayName-count-other": "Chinese yuan", - "symbol": "CN¥", - "symbol-alt-narrow": "¥" - }, - "COP": { - "displayName": "Colombian Peso", - "displayName-count-one": "Colombian peso", - "displayName-count-other": "Colombian pesos", - "symbol": "COP", - "symbol-alt-narrow": "$" - }, - "COU": { - "displayName": "Colombian Real Value Unit", - "displayName-count-one": "Colombian real value unit", - "displayName-count-other": "Colombian real value units" - }, - "CRC": { - "displayName": "Costa Rican Colón", - "displayName-count-one": "Costa Rican colón", - "displayName-count-other": "Costa Rican colóns", - "symbol": "CRC", - "symbol-alt-narrow": "₡" - }, - "CSD": { - "displayName": "Serbian Dinar (2002–2006)", - "displayName-count-one": "Serbian dinar (2002–2006)", - "displayName-count-other": "Serbian dinars (2002–2006)" - }, - "CSK": { - "displayName": "Czechoslovak Hard Koruna", - "displayName-count-one": "Czechoslovak hard koruna", - "displayName-count-other": "Czechoslovak hard korunas" - }, - "CUC": { - "displayName": "Cuban Convertible Peso", - "displayName-count-one": "Cuban convertible peso", - "displayName-count-other": "Cuban convertible pesos", - "symbol": "CUC", - "symbol-alt-narrow": "$" - }, - "CUP": { - "displayName": "Cuban Peso", - "displayName-count-one": "Cuban peso", - "displayName-count-other": "Cuban pesos", - "symbol": "CUP", - "symbol-alt-narrow": "$" - }, - "CVE": { - "displayName": "Cape Verdean Escudo", - "displayName-count-one": "Cape Verdean escudo", - "displayName-count-other": "Cape Verdean escudos", - "symbol": "CVE" - }, - "CYP": { - "displayName": "Cypriot Pound", - "displayName-count-one": "Cypriot pound", - "displayName-count-other": "Cypriot pounds" - }, - "CZK": { - "displayName": "Czech Koruna", - "displayName-count-one": "Czech koruna", - "displayName-count-other": "Czech korunas", - "symbol": "CZK", - "symbol-alt-narrow": "Kč" - }, - "DDM": { - "displayName": "East German Mark", - "displayName-count-one": "East German mark", - "displayName-count-other": "East German marks" - }, - "DEM": { - "displayName": "German Mark", - "displayName-count-one": "German mark", - "displayName-count-other": "German marks" - }, - "DJF": { - "displayName": "Djiboutian Franc", - "displayName-count-one": "Djiboutian franc", - "displayName-count-other": "Djiboutian francs", - "symbol": "DJF" - }, - "DKK": { - "displayName": "Danish Krone", - "displayName-count-one": "Danish krone", - "displayName-count-other": "Danish kroner", - "symbol": "DKK", - "symbol-alt-narrow": "kr" - }, - "DOP": { - "displayName": "Dominican Peso", - "displayName-count-one": "Dominican peso", - "displayName-count-other": "Dominican pesos", - "symbol": "DOP", - "symbol-alt-narrow": "$" - }, - "DZD": { - "displayName": "Algerian Dinar", - "displayName-count-one": "Algerian dinar", - "displayName-count-other": "Algerian dinars", - "symbol": "DZD" - }, - "ECS": { - "displayName": "Ecuadorian Sucre", - "displayName-count-one": "Ecuadorian sucre", - "displayName-count-other": "Ecuadorian sucres" - }, - "ECV": { - "displayName": "Ecuadorian Unit of Constant Value", - "displayName-count-one": "Ecuadorian unit of constant value", - "displayName-count-other": "Ecuadorian units of constant value" - }, - "EEK": { - "displayName": "Estonian Kroon", - "displayName-count-one": "Estonian kroon", - "displayName-count-other": "Estonian kroons" - }, - "EGP": { - "displayName": "Egyptian Pound", - "displayName-count-one": "Egyptian pound", - "displayName-count-other": "Egyptian pounds", - "symbol": "EGP", - "symbol-alt-narrow": "E£" - }, - "ERN": { - "displayName": "Eritrean Nakfa", - "displayName-count-one": "Eritrean nakfa", - "displayName-count-other": "Eritrean nakfas", - "symbol": "ERN" - }, - "ESA": { - "displayName": "Spanish Peseta (A account)", - "displayName-count-one": "Spanish peseta (A account)", - "displayName-count-other": "Spanish pesetas (A account)" - }, - "ESB": { - "displayName": "Spanish Peseta (convertible account)", - "displayName-count-one": "Spanish peseta (convertible account)", - "displayName-count-other": "Spanish pesetas (convertible account)" - }, - "ESP": { - "displayName": "Spanish Peseta", - "displayName-count-one": "Spanish peseta", - "displayName-count-other": "Spanish pesetas", - "symbol-alt-narrow": "₧" - }, - "ETB": { - "displayName": "Ethiopian Birr", - "displayName-count-one": "Ethiopian birr", - "displayName-count-other": "Ethiopian birr", - "symbol": "ETB" - }, - "EUR": { - "displayName": "Euro", - "displayName-count-one": "euro", - "displayName-count-other": "euros", - "symbol": "€", - "symbol-alt-narrow": "€" - }, - "FIM": { - "displayName": "Finnish Markka", - "displayName-count-one": "Finnish markka", - "displayName-count-other": "Finnish markkas" - }, - "FJD": { - "displayName": "Fijian Dollar", - "displayName-count-one": "Fijian dollar", - "displayName-count-other": "Fijian dollars", - "symbol": "FJD", - "symbol-alt-narrow": "$" - }, - "FKP": { - "displayName": "Falkland Islands Pound", - "displayName-count-one": "Falkland Islands pound", - "displayName-count-other": "Falkland Islands pounds", - "symbol": "FKP", - "symbol-alt-narrow": "£" - }, - "FRF": { - "displayName": "French Franc", - "displayName-count-one": "French franc", - "displayName-count-other": "French francs" - }, - "GBP": { - "displayName": "British Pound", - "displayName-count-one": "British pound", - "displayName-count-other": "British pounds", - "symbol": "£", - "symbol-alt-narrow": "£" - }, - "GEK": { - "displayName": "Georgian Kupon Larit", - "displayName-count-one": "Georgian kupon larit", - "displayName-count-other": "Georgian kupon larits" - }, - "GEL": { - "displayName": "Georgian Lari", - "displayName-count-one": "Georgian lari", - "displayName-count-other": "Georgian laris", - "symbol": "GEL", - "symbol-alt-narrow": "₾" - }, - "GHC": { - "displayName": "Ghanaian Cedi (1979–2007)", - "displayName-count-one": "Ghanaian cedi (1979–2007)", - "displayName-count-other": "Ghanaian cedis (1979–2007)" - }, - "GHS": { - "displayName": "Ghanaian Cedi", - "displayName-count-one": "Ghanaian cedi", - "displayName-count-other": "Ghanaian cedis", - "symbol": "GHS", - "symbol-alt-narrow": "GH₵" - }, - "GIP": { - "displayName": "Gibraltar Pound", - "displayName-count-one": "Gibraltar pound", - "displayName-count-other": "Gibraltar pounds", - "symbol": "GIP", - "symbol-alt-narrow": "£" - }, - "GMD": { - "displayName": "Gambian Dalasi", - "displayName-count-one": "Gambian dalasi", - "displayName-count-other": "Gambian dalasis", - "symbol": "GMD" - }, - "GNF": { - "displayName": "Guinean Franc", - "displayName-count-one": "Guinean franc", - "displayName-count-other": "Guinean francs", - "symbol": "GNF", - "symbol-alt-narrow": "FG" - }, - "GNS": { - "displayName": "Guinean Syli", - "displayName-count-one": "Guinean syli", - "displayName-count-other": "Guinean sylis" - }, - "GQE": { - "displayName": "Equatorial Guinean Ekwele", - "displayName-count-one": "Equatorial Guinean ekwele", - "displayName-count-other": "Equatorial Guinean ekwele" - }, - "GRD": { - "displayName": "Greek Drachma", - "displayName-count-one": "Greek drachma", - "displayName-count-other": "Greek drachmas" - }, - "GTQ": { - "displayName": "Guatemalan Quetzal", - "displayName-count-one": "Guatemalan quetzal", - "displayName-count-other": "Guatemalan quetzals", - "symbol": "GTQ", - "symbol-alt-narrow": "Q" - }, - "GWE": { - "displayName": "Portuguese Guinea Escudo", - "displayName-count-one": "Portuguese Guinea escudo", - "displayName-count-other": "Portuguese Guinea escudos" - }, - "GWP": { - "displayName": "Guinea-Bissau Peso", - "displayName-count-one": "Guinea-Bissau peso", - "displayName-count-other": "Guinea-Bissau pesos" - }, - "GYD": { - "displayName": "Guyanaese Dollar", - "displayName-count-one": "Guyanaese dollar", - "displayName-count-other": "Guyanaese dollars", - "symbol": "GYD", - "symbol-alt-narrow": "$" - }, - "HKD": { - "displayName": "Hong Kong Dollar", - "displayName-count-one": "Hong Kong dollar", - "displayName-count-other": "Hong Kong dollars", - "symbol": "HK$", - "symbol-alt-narrow": "$" - }, - "HNL": { - "displayName": "Honduran Lempira", - "displayName-count-one": "Honduran lempira", - "displayName-count-other": "Honduran lempiras", - "symbol": "HNL", - "symbol-alt-narrow": "L" - }, - "HRD": { - "displayName": "Croatian Dinar", - "displayName-count-one": "Croatian dinar", - "displayName-count-other": "Croatian dinars" - }, - "HRK": { - "displayName": "Croatian Kuna", - "displayName-count-one": "Croatian kuna", - "displayName-count-other": "Croatian kunas", - "symbol": "HRK", - "symbol-alt-narrow": "kn" - }, - "HTG": { - "displayName": "Haitian Gourde", - "displayName-count-one": "Haitian gourde", - "displayName-count-other": "Haitian gourdes", - "symbol": "HTG" - }, - "HUF": { - "displayName": "Hungarian Forint", - "displayName-count-one": "Hungarian forint", - "displayName-count-other": "Hungarian forints", - "symbol": "HUF", - "symbol-alt-narrow": "Ft" - }, - "IDR": { - "displayName": "Indonesian Rupiah", - "displayName-count-one": "Indonesian rupiah", - "displayName-count-other": "Indonesian rupiahs", - "symbol": "IDR", - "symbol-alt-narrow": "Rp" - }, - "IEP": { - "displayName": "Irish Pound", - "displayName-count-one": "Irish pound", - "displayName-count-other": "Irish pounds" - }, - "ILP": { - "displayName": "Israeli Pound", - "displayName-count-one": "Israeli pound", - "displayName-count-other": "Israeli pounds" - }, - "ILR": { - "displayName": "Israeli Shekel (1980–1985)", - "displayName-count-one": "Israeli shekel (1980–1985)", - "displayName-count-other": "Israeli shekels (1980–1985)" - }, - "ILS": { - "displayName": "Israeli New Shekel", - "displayName-count-one": "Israeli new shekel", - "displayName-count-other": "Israeli new shekels", - "symbol": "₪", - "symbol-alt-narrow": "₪" - }, - "INR": { - "displayName": "Indian Rupee", - "displayName-count-one": "Indian rupee", - "displayName-count-other": "Indian rupees", - "symbol": "₹", - "symbol-alt-narrow": "₹" - }, - "IQD": { - "displayName": "Iraqi Dinar", - "displayName-count-one": "Iraqi dinar", - "displayName-count-other": "Iraqi dinars", - "symbol": "IQD" - }, - "IRR": { - "displayName": "Iranian Rial", - "displayName-count-one": "Iranian rial", - "displayName-count-other": "Iranian rials", - "symbol": "IRR" - }, - "ISJ": { - "displayName": "Icelandic Króna (1918–1981)", - "displayName-count-one": "Icelandic króna (1918–1981)", - "displayName-count-other": "Icelandic krónur (1918–1981)" - }, - "ISK": { - "displayName": "Icelandic Króna", - "displayName-count-one": "Icelandic króna", - "displayName-count-other": "Icelandic krónur", - "symbol": "ISK", - "symbol-alt-narrow": "kr" - }, - "ITL": { - "displayName": "Italian Lira", - "displayName-count-one": "Italian lira", - "displayName-count-other": "Italian liras" - }, - "JMD": { - "displayName": "Jamaican Dollar", - "displayName-count-one": "Jamaican dollar", - "displayName-count-other": "Jamaican dollars", - "symbol": "JMD", - "symbol-alt-narrow": "$" - }, - "JOD": { - "displayName": "Jordanian Dinar", - "displayName-count-one": "Jordanian dinar", - "displayName-count-other": "Jordanian dinars", - "symbol": "JOD" - }, - "JPY": { - "displayName": "Japanese Yen", - "displayName-count-one": "Japanese yen", - "displayName-count-other": "Japanese yen", - "symbol": "JP¥", - "symbol-alt-narrow": "¥" - }, - "KES": { - "displayName": "Kenyan Shilling", - "displayName-count-one": "Kenyan shilling", - "displayName-count-other": "Kenyan shillings", - "symbol": "KES" - }, - "KGS": { - "displayName": "Kyrgyz Som", - "displayName-count-one": "Kyrgyz som", - "displayName-count-other": "Kyrgyz soms", - "symbol": "KGS", - "symbol-alt-narrow": "⃀" - }, - "KHR": { - "displayName": "Cambodian Riel", - "displayName-count-one": "Cambodian riel", - "displayName-count-other": "Cambodian riels", - "symbol": "KHR", - "symbol-alt-narrow": "៛" - }, - "KMF": { - "displayName": "Comorian Franc", - "displayName-count-one": "Comorian franc", - "displayName-count-other": "Comorian francs", - "symbol": "KMF", - "symbol-alt-narrow": "CF" - }, - "KPW": { - "displayName": "North Korean Won", - "displayName-count-one": "North Korean won", - "displayName-count-other": "North Korean won", - "symbol": "KPW", - "symbol-alt-narrow": "₩" - }, - "KRH": { - "displayName": "South Korean Hwan (1953–1962)", - "displayName-count-one": "South Korean hwan (1953–1962)", - "displayName-count-other": "South Korean hwan (1953–1962)" - }, - "KRO": { - "displayName": "South Korean Won (1945–1953)", - "displayName-count-one": "South Korean won (1945–1953)", - "displayName-count-other": "South Korean won (1945–1953)" - }, - "KRW": { - "displayName": "South Korean Won", - "displayName-count-one": "South Korean won", - "displayName-count-other": "South Korean won", - "symbol": "₩", - "symbol-alt-narrow": "₩" - }, - "KWD": { - "displayName": "Kuwaiti Dinar", - "displayName-count-one": "Kuwaiti dinar", - "displayName-count-other": "Kuwaiti dinars", - "symbol": "KWD" - }, - "KYD": { - "displayName": "Cayman Islands Dollar", - "displayName-count-one": "Cayman Islands dollar", - "displayName-count-other": "Cayman Islands dollars", - "symbol": "KYD", - "symbol-alt-narrow": "$" - }, - "KZT": { - "displayName": "Kazakhstani Tenge", - "displayName-count-one": "Kazakhstani tenge", - "displayName-count-other": "Kazakhstani tenges", - "symbol": "KZT", - "symbol-alt-narrow": "₸" - }, - "LAK": { - "displayName": "Laotian Kip", - "displayName-count-one": "Laotian kip", - "displayName-count-other": "Laotian kips", - "symbol": "LAK", - "symbol-alt-narrow": "₭" - }, - "LBP": { - "displayName": "Lebanese Pound", - "displayName-count-one": "Lebanese pound", - "displayName-count-other": "Lebanese pounds", - "symbol": "LBP", - "symbol-alt-narrow": "L£" - }, - "LKR": { - "displayName": "Sri Lankan Rupee", - "displayName-count-one": "Sri Lankan rupee", - "displayName-count-other": "Sri Lankan rupees", - "symbol": "LKR", - "symbol-alt-narrow": "Rs" - }, - "LRD": { - "displayName": "Liberian Dollar", - "displayName-count-one": "Liberian dollar", - "displayName-count-other": "Liberian dollars", - "symbol": "LRD", - "symbol-alt-narrow": "$" - }, - "LSL": { - "displayName": "Lesotho Loti", - "displayName-count-one": "Lesotho loti", - "displayName-count-other": "Lesotho maloti", - "symbol": "LSL" - }, - "LTL": { - "displayName": "Lithuanian Litas", - "displayName-count-one": "Lithuanian litas", - "displayName-count-other": "Lithuanian litai", - "symbol-alt-narrow": "Lt" - }, - "LTT": { - "displayName": "Lithuanian Talonas", - "displayName-count-one": "Lithuanian talonas", - "displayName-count-other": "Lithuanian talonases" - }, - "LUC": { - "displayName": "Luxembourgian Convertible Franc", - "displayName-count-one": "Luxembourgian convertible franc", - "displayName-count-other": "Luxembourgian convertible francs" - }, - "LUF": { - "displayName": "Luxembourgian Franc", - "displayName-count-one": "Luxembourgian franc", - "displayName-count-other": "Luxembourgian francs" - }, - "LUL": { - "displayName": "Luxembourg Financial Franc", - "displayName-count-one": "Luxembourg financial franc", - "displayName-count-other": "Luxembourg financial francs" - }, - "LVL": { - "displayName": "Latvian Lats", - "displayName-count-one": "Latvian lats", - "displayName-count-other": "Latvian lati", - "symbol-alt-narrow": "Ls" - }, - "LVR": { - "displayName": "Latvian Rouble", - "displayName-count-one": "Latvian rouble", - "displayName-count-other": "Latvian roubles" - }, - "LYD": { - "displayName": "Libyan Dinar", - "displayName-count-one": "Libyan dinar", - "displayName-count-other": "Libyan dinars", - "symbol": "LYD" - }, - "MAD": { - "displayName": "Moroccan Dirham", - "displayName-count-one": "Moroccan dirham", - "displayName-count-other": "Moroccan dirhams", - "symbol": "MAD" - }, - "MAF": { - "displayName": "Moroccan Franc", - "displayName-count-one": "Moroccan franc", - "displayName-count-other": "Moroccan francs" - }, - "MCF": { - "displayName": "Monegasque Franc", - "displayName-count-one": "Monegasque franc", - "displayName-count-other": "Monegasque francs" - }, - "MDC": { - "displayName": "Moldovan Cupon", - "displayName-count-one": "Moldovan cupon", - "displayName-count-other": "Moldovan cupon" - }, - "MDL": { - "displayName": "Moldovan Leu", - "displayName-count-one": "Moldovan leu", - "displayName-count-other": "Moldovan lei", - "symbol": "MDL" - }, - "MGA": { - "displayName": "Malagasy Ariary", - "displayName-count-one": "Malagasy ariary", - "displayName-count-other": "Malagasy ariary", - "symbol": "MGA", - "symbol-alt-narrow": "Ar" - }, - "MGF": { - "displayName": "Malagasy Franc", - "displayName-count-one": "Malagasy franc", - "displayName-count-other": "Malagasy francs" - }, - "MKD": { - "displayName": "Macedonian Denar", - "displayName-count-one": "Macedonian denar", - "displayName-count-other": "Macedonian denari", - "symbol": "MKD" - }, - "MKN": { - "displayName": "Macedonian Denar (1992–1993)", - "displayName-count-one": "Macedonian denar (1992–1993)", - "displayName-count-other": "Macedonian denari (1992–1993)" - }, - "MLF": { - "displayName": "Malian Franc", - "displayName-count-one": "Malian franc", - "displayName-count-other": "Malian francs" - }, - "MMK": { - "displayName": "Myanmar Kyat", - "displayName-count-one": "Myanmar kyat", - "displayName-count-other": "Myanmar kyats", - "symbol": "MMK", - "symbol-alt-narrow": "K" - }, - "MNT": { - "displayName": "Mongolian Tugrik", - "displayName-count-one": "Mongolian tugrik", - "displayName-count-other": "Mongolian tugriks", - "symbol": "MNT", - "symbol-alt-narrow": "₮" - }, - "MOP": { - "displayName": "Macanese Pataca", - "displayName-count-one": "Macanese pataca", - "displayName-count-other": "Macanese patacas", - "symbol": "MOP" - }, - "MRO": { - "displayName": "Mauritanian Ouguiya (1973–2017)", - "displayName-count-one": "Mauritanian ouguiya (1973–2017)", - "displayName-count-other": "Mauritanian ouguiyas (1973–2017)" - }, - "MRU": { - "displayName": "Mauritanian Ouguiya", - "displayName-count-one": "Mauritanian ouguiya", - "displayName-count-other": "Mauritanian ouguiyas", - "symbol": "MRU" - }, - "MTL": { - "displayName": "Maltese Lira", - "displayName-count-one": "Maltese lira", - "displayName-count-other": "Maltese lira" - }, - "MTP": { - "displayName": "Maltese Pound", - "displayName-count-one": "Maltese pound", - "displayName-count-other": "Maltese pounds" - }, - "MUR": { - "displayName": "Mauritian Rupee", - "displayName-count-one": "Mauritian rupee", - "displayName-count-other": "Mauritian rupees", - "symbol": "MUR", - "symbol-alt-narrow": "Rs" - }, - "MVP": { - "displayName": "Maldivian Rupee (1947–1981)", - "displayName-count-one": "Maldivian rupee (1947–1981)", - "displayName-count-other": "Maldivian rupees (1947–1981)" - }, - "MVR": { - "displayName": "Maldivian Rufiyaa", - "displayName-count-one": "Maldivian rufiyaa", - "displayName-count-other": "Maldivian rufiyaa", - "symbol": "MVR" - }, - "MWK": { - "displayName": "Malawian Kwacha", - "displayName-count-one": "Malawian kwacha", - "displayName-count-other": "Malawian kwachas", - "symbol": "MWK" - }, - "MXN": { - "displayName": "Mexican Peso", - "displayName-count-one": "Mexican peso", - "displayName-count-other": "Mexican pesos", - "symbol": "MX$", - "symbol-alt-narrow": "$" - }, - "MXP": { - "displayName": "Mexican Silver Peso (1861–1992)", - "displayName-count-one": "Mexican silver peso (1861–1992)", - "displayName-count-other": "Mexican silver pesos (1861–1992)" - }, - "MXV": { - "displayName": "Mexican Investment Unit", - "displayName-count-one": "Mexican investment unit", - "displayName-count-other": "Mexican investment units" - }, - "MYR": { - "displayName": "Malaysian Ringgit", - "displayName-count-one": "Malaysian ringgit", - "displayName-count-other": "Malaysian ringgits", - "symbol": "MYR", - "symbol-alt-narrow": "RM" - }, - "MZE": { - "displayName": "Mozambican Escudo", - "displayName-count-one": "Mozambican escudo", - "displayName-count-other": "Mozambican escudos" - }, - "MZM": { - "displayName": "Mozambican Metical (1980–2006)", - "displayName-count-one": "Mozambican metical (1980–2006)", - "displayName-count-other": "Mozambican meticals (1980–2006)" - }, - "MZN": { - "displayName": "Mozambican Metical", - "displayName-count-one": "Mozambican metical", - "displayName-count-other": "Mozambican meticals", - "symbol": "MZN" - }, - "NAD": { - "displayName": "Namibian Dollar", - "displayName-count-one": "Namibian dollar", - "displayName-count-other": "Namibian dollars", - "symbol": "NAD", - "symbol-alt-narrow": "$" - }, - "NGN": { - "displayName": "Nigerian Naira", - "displayName-count-one": "Nigerian naira", - "displayName-count-other": "Nigerian nairas", - "symbol": "NGN", - "symbol-alt-narrow": "₦" - }, - "NIC": { - "displayName": "Nicaraguan Córdoba (1988–1991)", - "displayName-count-one": "Nicaraguan córdoba (1988–1991)", - "displayName-count-other": "Nicaraguan córdobas (1988–1991)" - }, - "NIO": { - "displayName": "Nicaraguan Córdoba", - "displayName-count-one": "Nicaraguan córdoba", - "displayName-count-other": "Nicaraguan córdobas", - "symbol": "NIO", - "symbol-alt-narrow": "C$" - }, - "NLG": { - "displayName": "Dutch Guilder", - "displayName-count-one": "Dutch guilder", - "displayName-count-other": "Dutch guilders" - }, - "NOK": { - "displayName": "Norwegian Krone", - "displayName-count-one": "Norwegian krone", - "displayName-count-other": "Norwegian kroner", - "symbol": "NOK", - "symbol-alt-narrow": "kr" - }, - "NPR": { - "displayName": "Nepalese Rupee", - "displayName-count-one": "Nepalese rupee", - "displayName-count-other": "Nepalese rupees", - "symbol": "NPR", - "symbol-alt-narrow": "Rs" - }, - "NZD": { - "displayName": "New Zealand Dollar", - "displayName-count-one": "New Zealand dollar", - "displayName-count-other": "New Zealand dollars", - "symbol": "NZ$", - "symbol-alt-narrow": "$" - }, - "OMR": { - "displayName": "Omani Rial", - "displayName-count-one": "Omani rial", - "displayName-count-other": "Omani rials", - "symbol": "OMR" - }, - "PAB": { - "displayName": "Panamanian Balboa", - "displayName-count-one": "Panamanian balboa", - "displayName-count-other": "Panamanian balboas", - "symbol": "PAB" - }, - "PEI": { - "displayName": "Peruvian Inti", - "displayName-count-one": "Peruvian inti", - "displayName-count-other": "Peruvian intis" - }, - "PEN": { - "displayName": "Peruvian Sol", - "displayName-count-one": "Peruvian sol", - "displayName-count-other": "Peruvian soles", - "symbol": "PEN" - }, - "PES": { - "displayName": "Peruvian Sol (1863–1965)", - "displayName-count-one": "Peruvian sol (1863–1965)", - "displayName-count-other": "Peruvian soles (1863–1965)" - }, - "PGK": { - "displayName": "Papua New Guinean Kina", - "displayName-count-one": "Papua New Guinean kina", - "displayName-count-other": "Papua New Guinean kina", - "symbol": "PGK" - }, - "PHP": { - "displayName": "Philippine Peso", - "displayName-count-one": "Philippine peso", - "displayName-count-other": "Philippine pesos", - "symbol": "₱", - "symbol-alt-narrow": "₱" - }, - "PKR": { - "displayName": "Pakistani Rupee", - "displayName-count-one": "Pakistani rupee", - "displayName-count-other": "Pakistani rupees", - "symbol": "PKR", - "symbol-alt-narrow": "Rs" - }, - "PLN": { - "displayName": "Polish Zloty", - "displayName-count-one": "Polish zloty", - "displayName-count-other": "Polish zlotys", - "symbol": "PLN", - "symbol-alt-narrow": "zł" - }, - "PLZ": { - "displayName": "Polish Zloty (1950–1995)", - "displayName-count-one": "Polish zloty (PLZ)", - "displayName-count-other": "Polish zlotys (PLZ)" - }, - "PTE": { - "displayName": "Portuguese Escudo", - "displayName-count-one": "Portuguese escudo", - "displayName-count-other": "Portuguese escudos" - }, - "PYG": { - "displayName": "Paraguayan Guarani", - "displayName-count-one": "Paraguayan guarani", - "displayName-count-other": "Paraguayan guaranis", - "symbol": "PYG", - "symbol-alt-narrow": "₲" - }, - "QAR": { - "displayName": "Qatari Riyal", - "displayName-count-one": "Qatari riyal", - "displayName-count-other": "Qatari riyals", - "symbol": "QAR" - }, - "RHD": { - "displayName": "Rhodesian Dollar", - "displayName-count-one": "Rhodesian dollar", - "displayName-count-other": "Rhodesian dollars" - }, - "ROL": { - "displayName": "Romanian Leu (1952–2006)", - "displayName-count-one": "Romanian leu (1952–2006)", - "displayName-count-other": "Romanian Lei (1952–2006)" - }, - "RON": { - "displayName": "Romanian Leu", - "displayName-count-one": "Romanian leu", - "displayName-count-other": "Romanian lei", - "symbol": "RON", - "symbol-alt-narrow": "lei" - }, - "RSD": { - "displayName": "Serbian Dinar", - "displayName-count-one": "Serbian dinar", - "displayName-count-other": "Serbian dinars", - "symbol": "RSD" - }, - "RUB": { - "displayName": "Russian Rouble", - "displayName-count-one": "Russian rouble", - "displayName-count-other": "Russian roubles", - "symbol": "RUB", - "symbol-alt-narrow": "₽" - }, - "RUR": { - "displayName": "Russian Rouble (1991–1998)", - "displayName-count-one": "Russian rouble (1991–1998)", - "displayName-count-other": "Russian roubles (1991–1998)" - }, - "RWF": { - "displayName": "Rwandan Franc", - "displayName-count-one": "Rwandan franc", - "displayName-count-other": "Rwandan francs", - "symbol": "RWF", - "symbol-alt-narrow": "RF" - }, - "SAR": { - "displayName": "Saudi Riyal", - "displayName-count-one": "Saudi riyal", - "displayName-count-other": "Saudi riyals", - "symbol": "SAR", - "symbol-alt-variant": "⃁" - }, - "SBD": { - "displayName": "Solomon Islands Dollar", - "displayName-count-one": "Solomon Islands dollar", - "displayName-count-other": "Solomon Islands dollars", - "symbol": "SBD", - "symbol-alt-narrow": "$" - }, - "SCR": { - "displayName": "Seychellois Rupee", - "displayName-count-one": "Seychellois rupee", - "displayName-count-other": "Seychellois rupees", - "symbol": "SCR" - }, - "SDD": { - "displayName": "Sudanese Dinar (1992–2007)", - "displayName-count-one": "Sudanese dinar (1992–2007)", - "displayName-count-other": "Sudanese dinars (1992–2007)" - }, - "SDG": { - "displayName": "Sudanese Pound", - "displayName-count-one": "Sudanese pound", - "displayName-count-other": "Sudanese pounds", - "symbol": "SDG" - }, - "SDP": { - "displayName": "Sudanese Pound (1957–1998)", - "displayName-count-one": "Sudanese pound (1957–1998)", - "displayName-count-other": "Sudanese pounds (1957–1998)" - }, - "SEK": { - "displayName": "Swedish Krona", - "displayName-count-one": "Swedish krona", - "displayName-count-other": "Swedish kronor", - "symbol": "SEK", - "symbol-alt-narrow": "kr" - }, - "SGD": { - "displayName": "Singapore Dollar", - "displayName-count-one": "Singapore dollar", - "displayName-count-other": "Singapore dollars", - "symbol": "SGD", - "symbol-alt-narrow": "$" - }, - "SHP": { - "displayName": "St Helena Pound", - "displayName-count-one": "St Helena pound", - "displayName-count-other": "St Helena pounds", - "symbol": "SHP", - "symbol-alt-narrow": "£" - }, - "SIT": { - "displayName": "Slovenian Tolar", - "displayName-count-one": "Slovenian tolar", - "displayName-count-other": "Slovenian tolars" - }, - "SKK": { - "displayName": "Slovak Koruna", - "displayName-count-one": "Slovak koruna", - "displayName-count-other": "Slovak korunas" - }, - "SLE": { - "displayName": "Sierra Leonean Leone", - "displayName-count-one": "Sierra Leonean leone", - "displayName-count-other": "Sierra Leonean leones", - "symbol": "SLE" - }, - "SLL": { - "displayName": "Sierra Leonean Leone (1964—2022)", - "displayName-count-one": "Sierra Leonean leone (1964—2022)", - "displayName-count-other": "Sierra Leonean leones (1964—2022)", - "symbol": "SLL" - }, - "SOS": { - "displayName": "Somali Shilling", - "displayName-count-one": "Somali shilling", - "displayName-count-other": "Somali shillings", - "symbol": "SOS" - }, - "SRD": { - "displayName": "Surinamese Dollar", - "displayName-count-one": "Surinamese dollar", - "displayName-count-other": "Surinamese dollars", - "symbol": "SRD", - "symbol-alt-narrow": "$" - }, - "SRG": { - "displayName": "Surinamese Guilder", - "displayName-count-one": "Surinamese guilder", - "displayName-count-other": "Surinamese guilders" - }, - "SSP": { - "displayName": "South Sudanese Pound", - "displayName-count-one": "South Sudanese pound", - "displayName-count-other": "South Sudanese pounds", - "symbol": "SSP", - "symbol-alt-narrow": "£" - }, - "STD": { - "displayName": "São Tomé & Príncipe Dobra (1977–2017)", - "displayName-count-one": "São Tomé & Príncipe dobra (1977–2017)", - "displayName-count-other": "São Tomé & Príncipe dobras (1977–2017)" - }, - "STN": { - "displayName": "São Tomé & Príncipe Dobra", - "displayName-count-one": "São Tomé & Príncipe dobra", - "displayName-count-other": "São Tomé & Príncipe dobras", - "symbol": "STN", - "symbol-alt-narrow": "Db" - }, - "SUR": { - "displayName": "Soviet Rouble", - "displayName-count-one": "Soviet rouble", - "displayName-count-other": "Soviet roubles" - }, - "SVC": { - "displayName": "Salvadoran Colón", - "displayName-count-one": "Salvadoran colón", - "displayName-count-other": "Salvadoran colones" - }, - "SYP": { - "displayName": "Syrian Pound", - "displayName-count-one": "Syrian pound", - "displayName-count-other": "Syrian pounds", - "symbol": "SYP", - "symbol-alt-narrow": "£" - }, - "SZL": { - "displayName": "Swazi Lilangeni", - "displayName-count-one": "Swazi lilangeni", - "displayName-count-other": "Swazi emalangeni", - "symbol": "SZL" - }, - "THB": { - "displayName": "Thai Baht", - "displayName-count-one": "Thai baht", - "displayName-count-other": "Thai baht", - "symbol": "THB", - "symbol-alt-narrow": "฿" - }, - "TJR": { - "displayName": "Tajikistani Rouble", - "displayName-count-one": "Tajikistani rouble", - "displayName-count-other": "Tajikistani roubles" - }, - "TJS": { - "displayName": "Tajikistani Somoni", - "displayName-count-one": "Tajikistani somoni", - "displayName-count-other": "Tajikistani somonis", - "symbol": "TJS" - }, - "TMM": { - "displayName": "Turkmenistani Manat (1993–2009)", - "displayName-count-one": "Turkmenistani manat (1993–2009)", - "displayName-count-other": "Turkmenistani manat (1993–2009)" - }, - "TMT": { - "displayName": "Turkmenistani Manat", - "displayName-count-one": "Turkmenistani manat", - "displayName-count-other": "Turkmenistani manat", - "symbol": "TMT" - }, - "TND": { - "displayName": "Tunisian Dinar", - "displayName-count-one": "Tunisian dinar", - "displayName-count-other": "Tunisian dinars", - "symbol": "TND" - }, - "TOP": { - "displayName": "Tongan Paʻanga", - "displayName-count-one": "Tongan paʻanga", - "displayName-count-other": "Tongan paʻanga", - "symbol": "TOP", - "symbol-alt-narrow": "T$" - }, - "TPE": { - "displayName": "Timorese Escudo", - "displayName-count-one": "Timorese escudo", - "displayName-count-other": "Timorese escudos" - }, - "TRL": { - "displayName": "Turkish Lira (1922–2005)", - "displayName-count-one": "Turkish lira (1922–2005)", - "displayName-count-other": "Turkish Lira (1922–2005)" - }, - "TRY": { - "displayName": "Turkish Lira", - "displayName-count-one": "Turkish lira", - "displayName-count-other": "Turkish Lira", - "symbol": "TRY", - "symbol-alt-narrow": "₺", - "symbol-alt-variant": "TL" - }, - "TTD": { - "displayName": "Trinidad & Tobago Dollar", - "displayName-count-one": "Trinidad & Tobago dollar", - "displayName-count-other": "Trinidad & Tobago dollars", - "symbol": "TTD", - "symbol-alt-narrow": "$" - }, - "TWD": { - "displayName": "New Taiwan Dollar", - "displayName-count-one": "New Taiwan dollar", - "displayName-count-other": "New Taiwan dollars", - "symbol": "NT$", - "symbol-alt-narrow": "$" - }, - "TZS": { - "displayName": "Tanzanian Shilling", - "displayName-count-one": "Tanzanian shilling", - "displayName-count-other": "Tanzanian shillings", - "symbol": "TZS" - }, - "UAH": { - "displayName": "Ukrainian Hryvnia", - "displayName-count-one": "Ukrainian hryvnia", - "displayName-count-other": "Ukrainian hryvnias", - "symbol": "UAH", - "symbol-alt-narrow": "₴" - }, - "UAK": { - "displayName": "Ukrainian Karbovanets", - "displayName-count-one": "Ukrainian karbovanets", - "displayName-count-other": "Ukrainian karbovantsiv" - }, - "UGS": { - "displayName": "Ugandan Shilling (1966–1987)", - "displayName-count-one": "Ugandan shilling (1966–1987)", - "displayName-count-other": "Ugandan shillings (1966–1987)" - }, - "UGX": { - "displayName": "Ugandan Shilling", - "displayName-count-one": "Ugandan shilling", - "displayName-count-other": "Ugandan shillings", - "symbol": "UGX" - }, - "USD": { - "displayName": "US Dollar", - "displayName-count-one": "US dollar", - "displayName-count-other": "US dollars", - "symbol": "US$", - "symbol-alt-narrow": "$" - }, - "USN": { - "displayName": "US Dollar (Next day)", - "displayName-count-one": "US dollar (next day)", - "displayName-count-other": "US dollars (next day)" - }, - "USS": { - "displayName": "US Dollar (Same day)", - "displayName-count-one": "US dollar (same day)", - "displayName-count-other": "US dollars (same day)" - }, - "UYI": { - "displayName": "Uruguayan Peso (Indexed Units)", - "displayName-count-one": "Uruguayan peso (indexed units)", - "displayName-count-other": "Uruguayan pesos (indexed units)" - }, - "UYP": { - "displayName": "Uruguayan Peso (1975–1993)", - "displayName-count-one": "Uruguayan peso (1975–1993)", - "displayName-count-other": "Uruguayan pesos (1975–1993)" - }, - "UYU": { - "displayName": "Uruguayan Peso", - "displayName-count-one": "Uruguayan peso", - "displayName-count-other": "Uruguayan pesos", - "symbol": "UYU", - "symbol-alt-narrow": "$" - }, - "UYW": { - "displayName": "Uruguayan Nominal Wage Index Unit", - "displayName-count-one": "Uruguayan nominal wage index unit", - "displayName-count-other": "Uruguayan nominal wage index units" - }, - "UZS": { - "displayName": "Uzbekistani Som", - "displayName-count-one": "Uzbekistani som", - "displayName-count-other": "Uzbekistani som", - "symbol": "UZS" - }, - "VEB": { - "displayName": "Venezuelan Bolívar (1871–2008)", - "displayName-count-one": "Venezuelan bolívar (1871–2008)", - "displayName-count-other": "Venezuelan bolívars (1871–2008)" - }, - "VED": { - "displayName": "Bolívar Soberano", - "displayName-count-one": "Bolívar Soberano", - "displayName-count-other": "Bolívar Soberanos" - }, - "VEF": { - "displayName": "Venezuelan Bolívar (2008–2018)", - "displayName-count-one": "Venezuelan bolívar (2008–2018)", - "displayName-count-other": "Venezuelan bolívars (2008–2018)", - "symbol-alt-narrow": "Bs" - }, - "VES": { - "displayName": "Venezuelan Bolívar", - "displayName-count-one": "Venezuelan bolívar", - "displayName-count-other": "Venezuelan bolívars", - "symbol": "VES" - }, - "VND": { - "displayName": "Vietnamese Dong", - "displayName-count-one": "Vietnamese dong", - "displayName-count-other": "Vietnamese dong", - "symbol": "₫", - "symbol-alt-narrow": "₫" - }, - "VNN": { - "displayName": "Vietnamese Dong (1978–1985)", - "displayName-count-one": "Vietnamese dong (1978–1985)", - "displayName-count-other": "Vietnamese dong (1978–1985)" - }, - "VUV": { - "displayName": "Vanuatu Vatu", - "displayName-count-one": "Vanuatu vatu", - "displayName-count-other": "Vanuatu vatu", - "symbol": "VUV" - }, - "WST": { - "displayName": "Samoan Tala", - "displayName-count-one": "Samoan tala", - "displayName-count-other": "Samoan tala", - "symbol": "WST" - }, - "XAF": { - "displayName": "Central African CFA Franc", - "displayName-count-one": "Central African CFA franc", - "displayName-count-other": "Central African CFA francs", - "symbol": "FCFA" - }, - "XAG": { - "displayName": "Silver", - "displayName-count-one": "troy ounce of silver", - "displayName-count-other": "troy ounces of silver" - }, - "XAU": { - "displayName": "Gold", - "displayName-count-one": "troy ounce of gold", - "displayName-count-other": "troy ounces of gold" - }, - "XBA": { - "displayName": "European Composite Unit", - "displayName-count-one": "European composite unit", - "displayName-count-other": "European composite units" - }, - "XBB": { - "displayName": "European Monetary Unit", - "displayName-count-one": "European monetary unit", - "displayName-count-other": "European monetary units" - }, - "XBC": { - "displayName": "European Unit of Account (XBC)", - "displayName-count-one": "European unit of account (XBC)", - "displayName-count-other": "European units of account (XBC)" - }, - "XBD": { - "displayName": "European Unit of Account (XBD)", - "displayName-count-one": "European unit of account (XBD)", - "displayName-count-other": "European units of account (XBD)" - }, - "XCD": { - "displayName": "East Caribbean Dollar", - "displayName-count-one": "East Caribbean dollar", - "displayName-count-other": "East Caribbean dollars", - "symbol": "EC$", - "symbol-alt-narrow": "$" - }, - "XCG": { - "displayName": "Caribbean guilder", - "displayName-count-one": "Caribbean guilder", - "displayName-count-other": "Caribbean guilders", - "symbol": "Cg." - }, - "XDR": { - "displayName": "Special Drawing Rights", - "displayName-count-one": "special drawing rights", - "displayName-count-other": "special drawing rights" - }, - "XEU": { - "displayName": "European Currency Unit", - "displayName-count-one": "European currency unit", - "displayName-count-other": "European currency units" - }, - "XFO": { - "displayName": "French Gold Franc", - "displayName-count-one": "French gold franc", - "displayName-count-other": "French gold francs" - }, - "XFU": { - "displayName": "French UIC-Franc", - "displayName-count-one": "French UIC-franc", - "displayName-count-other": "French UIC-francs" - }, - "XOF": { - "displayName": "West African CFA Franc", - "displayName-count-one": "West African CFA franc", - "displayName-count-other": "West African CFA francs", - "symbol": "F CFA" - }, - "XPD": { - "displayName": "Palladium", - "displayName-count-one": "troy ounce of palladium", - "displayName-count-other": "troy ounces of palladium" - }, - "XPF": { - "displayName": "CFP Franc", - "displayName-count-one": "CFP franc", - "displayName-count-other": "CFP francs", - "symbol": "CFPF" - }, - "XPT": { - "displayName": "Platinum", - "displayName-count-one": "troy ounce of platinum", - "displayName-count-other": "troy ounces of platinum" - }, - "XRE": { - "displayName": "RINET Funds", - "displayName-count-one": "RINET Funds unit", - "displayName-count-other": "RINET Funds units" - }, - "XSU": { - "displayName": "Sucre", - "displayName-count-one": "Sucre", - "displayName-count-other": "Sucres" - }, - "XTS": { - "displayName": "Testing Currency Code", - "displayName-count-one": "Testing Currency unit", - "displayName-count-other": "Testing Currency units" - }, - "XUA": { - "displayName": "ADB Unit of Account", - "displayName-count-one": "ADB unit of account", - "displayName-count-other": "ADB units of account" - }, - "XXX": { - "displayName": "Unknown Currency", - "displayName-count-one": "(unknown unit of currency)", - "displayName-count-other": "(unknown currency)", - "symbol": "¤" - }, - "YDD": { - "displayName": "Yemeni Dinar", - "displayName-count-one": "Yemeni dinar", - "displayName-count-other": "Yemeni dinars" - }, - "YER": { - "displayName": "Yemeni Rial", - "displayName-count-one": "Yemeni rial", - "displayName-count-other": "Yemeni rials", - "symbol": "YER" - }, - "YUD": { - "displayName": "Yugoslavian Hard Dinar (1966–1990)", - "displayName-count-one": "Yugoslavian hard dinar (1966–1990)", - "displayName-count-other": "Yugoslavian hard dinars (1966–1990)" - }, - "YUM": { - "displayName": "Yugoslavian New Dinar (1994–2002)", - "displayName-count-one": "Yugoslavian new dinar (1994–2002)", - "displayName-count-other": "Yugoslavian new dinars (1994–2002)" - }, - "YUN": { - "displayName": "Yugoslavian Convertible Dinar (1990–1992)", - "displayName-count-one": "Yugoslavian convertible dinar (1990–1992)", - "displayName-count-other": "Yugoslavian convertible dinars (1990–1992)" - }, - "YUR": { - "displayName": "Yugoslavian Reformed Dinar (1992–1993)", - "displayName-count-one": "Yugoslavian reformed dinar (1992–1993)", - "displayName-count-other": "Yugoslavian reformed dinars (1992–1993)" - }, - "ZAL": { - "displayName": "South African Rand (financial)", - "displayName-count-one": "South African rand (financial)", - "displayName-count-other": "South African rands (financial)" - }, - "ZAR": { - "displayName": "South African Rand", - "displayName-count-one": "South African rand", - "displayName-count-other": "South African rand", - "symbol": "ZAR", - "symbol-alt-narrow": "R" - }, - "ZMK": { - "displayName": "Zambian Kwacha (1968–2012)", - "displayName-count-one": "Zambian kwacha (1968–2012)", - "displayName-count-other": "Zambian kwachas (1968–2012)" - }, - "ZMW": { - "displayName": "Zambian Kwacha", - "displayName-count-one": "Zambian kwacha", - "displayName-count-other": "Zambian kwachas", - "symbol": "ZMW", - "symbol-alt-narrow": "ZK" - }, - "ZRN": { - "displayName": "Zairean New Zaire (1993–1998)", - "displayName-count-one": "Zairean new zaire (1993–1998)", - "displayName-count-other": "Zairean new zaires (1993–1998)" - }, - "ZRZ": { - "displayName": "Zairean Zaire (1971–1993)", - "displayName-count-one": "Zairean zaire (1971–1993)", - "displayName-count-other": "Zairean zaires (1971–1993)" - }, - "ZWD": { - "displayName": "Zimbabwean Dollar (1980–2008)", - "displayName-count-one": "Zimbabwean dollar (1980–2008)", - "displayName-count-other": "Zimbabwean dollars (1980–2008)" - }, - "ZWG": { - "displayName": "Zimbabwean Gold", - "displayName-count-one": "Zimbabwean gold", - "displayName-count-other": "Zimbabwean gold", - "symbol": "ZWG" - }, - "ZWL": { - "displayName": "Zimbabwean Dollar (2009–2024)", - "displayName-count-one": "Zimbabwean dollar (2009–2024)", - "displayName-count-other": "Zimbabwean dollars (2009–2024)" - }, - "ZWR": { - "displayName": "Zimbabwean Dollar (2008)", - "displayName-count-one": "Zimbabwean dollar (2008)", - "displayName-count-other": "Zimbabwean dollars (2008)" - } - } - } - } - } -} diff --git a/Tools/currency-table/cldr/currencyData.json b/Tools/currency-table/cldr/currencyData.json deleted file mode 100644 index 4a8caf8..0000000 --- a/Tools/currency-table/cldr/currencyData.json +++ /dev/null @@ -1,3650 +0,0 @@ -{ - "supplemental": { - "version": { - "_unicodeVersion": "16.0.0", - "_cldrVersion": "48" - }, - "currencyData": { - "fractions": { - "ADP": { - "_rounding": "0", - "_digits": "0" - }, - "AFN": { - "_rounding": "0", - "_digits": "0" - }, - "ALL": { - "_rounding": "0", - "_digits": "0" - }, - "AMD": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "BHD": { - "_rounding": "0", - "_digits": "3" - }, - "BIF": { - "_rounding": "0", - "_digits": "0" - }, - "BYN": { - "_rounding": "0", - "_digits": "2" - }, - "BYR": { - "_rounding": "0", - "_digits": "0" - }, - "CAD": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "5" - }, - "CHF": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "5" - }, - "CLF": { - "_rounding": "0", - "_digits": "4" - }, - "CLP": { - "_rounding": "0", - "_digits": "0" - }, - "COP": { - "_rounding": "0", - "_digits": "0" - }, - "CRC": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "CZK": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "DEFAULT": { - "_rounding": "0", - "_digits": "2" - }, - "DJF": { - "_rounding": "0", - "_digits": "0" - }, - "DKK": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "50" - }, - "ESP": { - "_rounding": "0", - "_digits": "0" - }, - "GNF": { - "_rounding": "0", - "_digits": "0" - }, - "GYD": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "HUF": { - "_rounding": "0", - "_digits": "0", - "_cashRounding": "5" - }, - "IDR": { - "_rounding": "0", - "_digits": "0" - }, - "IQD": { - "_rounding": "0", - "_digits": "0" - }, - "IRR": { - "_rounding": "0", - "_digits": "0" - }, - "ISK": { - "_rounding": "0", - "_digits": "0" - }, - "ITL": { - "_rounding": "0", - "_digits": "0" - }, - "JOD": { - "_rounding": "0", - "_digits": "3" - }, - "JPY": { - "_rounding": "0", - "_digits": "0" - }, - "KMF": { - "_rounding": "0", - "_digits": "0" - }, - "KPW": { - "_rounding": "0", - "_digits": "0" - }, - "KRW": { - "_rounding": "0", - "_digits": "0" - }, - "KWD": { - "_rounding": "0", - "_digits": "3" - }, - "LAK": { - "_rounding": "0", - "_digits": "0" - }, - "LBP": { - "_rounding": "0", - "_digits": "0" - }, - "LUF": { - "_rounding": "0", - "_digits": "0" - }, - "LYD": { - "_rounding": "0", - "_digits": "3" - }, - "MGA": { - "_rounding": "0", - "_digits": "0" - }, - "MGF": { - "_rounding": "0", - "_digits": "0" - }, - "MMK": { - "_rounding": "0", - "_digits": "0" - }, - "MNT": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "MRO": { - "_rounding": "0", - "_digits": "0" - }, - "MUR": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "NOK": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "OMR": { - "_rounding": "0", - "_digits": "3" - }, - "PKR": { - "_rounding": "0", - "_digits": "0" - }, - "PYG": { - "_rounding": "0", - "_digits": "0" - }, - "RSD": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "RWF": { - "_rounding": "0", - "_digits": "0" - }, - "SEK": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "SLE": { - "_rounding": "0", - "_digits": "2" - }, - "SLL": { - "_rounding": "0", - "_digits": "0" - }, - "SOS": { - "_rounding": "0", - "_digits": "0" - }, - "STD": { - "_rounding": "0", - "_digits": "0" - }, - "SYP": { - "_rounding": "0", - "_digits": "0" - }, - "TMM": { - "_rounding": "0", - "_digits": "0" - }, - "TND": { - "_rounding": "0", - "_digits": "3" - }, - "TRL": { - "_rounding": "0", - "_digits": "0" - }, - "TWD": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "TZS": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "UGX": { - "_rounding": "0", - "_digits": "0" - }, - "UYI": { - "_rounding": "0", - "_digits": "0" - }, - "UYW": { - "_rounding": "0", - "_digits": "4" - }, - "UZS": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "VEF": { - "_rounding": "0", - "_digits": "2", - "_cashRounding": "0", - "_cashDigits": "0" - }, - "VND": { - "_rounding": "0", - "_digits": "0" - }, - "VUV": { - "_rounding": "0", - "_digits": "0" - }, - "XAD": { - "_rounding": "0", - "_digits": "2" - }, - "XAF": { - "_rounding": "0", - "_digits": "0" - }, - "XAU": { - "_rounding": "0", - "_digits": "2" - }, - "XOF": { - "_rounding": "0", - "_digits": "0" - }, - "XPF": { - "_rounding": "0", - "_digits": "0" - }, - "YER": { - "_rounding": "0", - "_digits": "0" - }, - "ZMK": { - "_rounding": "0", - "_digits": "0" - }, - "ZWD": { - "_rounding": "0", - "_digits": "0" - } - }, - "region": { - "AC": [ - { - "SHP": { - "_from": "1976-01-01" - } - } - ], - "AD": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "ESP": { - "_from": "1873-01-01", - "_to": "2002-02-28" - } - }, - { - "FRF": { - "_from": "1960-01-01", - "_to": "2002-02-17" - } - }, - { - "ADP": { - "_from": "1936-01-01", - "_to": "2001-12-31" - } - } - ], - "AE": [ - { - "AED": { - "_from": "1973-05-19" - } - } - ], - "AF": [ - { - "AFN": { - "_from": "2002-10-07" - } - }, - { - "AFA": { - "_from": "1927-03-14", - "_to": "2002-12-31" - } - } - ], - "AG": [ - { - "XCD": { - "_from": "1965-10-06" - } - } - ], - "AI": [ - { - "XCD": { - "_from": "1965-10-06" - } - } - ], - "AL": [ - { - "ALL": { - "_from": "1965-08-16" - } - }, - { - "ALK": { - "_from": "1946-11-01", - "_to": "1965-08-16" - } - } - ], - "AM": [ - { - "AMD": { - "_from": "1993-11-22" - } - }, - { - "RUR": { - "_from": "1991-12-25", - "_to": "1993-11-22" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1991-12-25" - } - } - ], - "AO": [ - { - "AOA": { - "_from": "1999-12-13" - } - }, - { - "AOR": { - "_from": "1995-07-01", - "_to": "2000-02-01" - } - }, - { - "AON": { - "_from": "1990-09-25", - "_to": "2000-02-01" - } - }, - { - "AOK": { - "_from": "1977-01-08", - "_to": "1991-03-01" - } - } - ], - "AQ": [ - { - "XXX": { - "_tender": "false" - } - } - ], - "AR": [ - { - "ARS": { - "_from": "1992-01-01" - } - }, - { - "ARA": { - "_from": "1985-06-14", - "_to": "1992-01-01" - } - }, - { - "ARP": { - "_from": "1983-06-01", - "_to": "1985-06-14" - } - }, - { - "ARL": { - "_from": "1970-01-01", - "_to": "1983-06-01" - } - }, - { - "ARM": { - "_from": "1881-11-05", - "_to": "1970-01-01" - } - } - ], - "AS": [ - { - "USD": { - "_from": "1904-07-16" - } - } - ], - "AT": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "ATS": { - "_from": "1947-12-04", - "_to": "2002-02-28" - } - } - ], - "AU": [ - { - "AUD": { - "_from": "1966-02-14" - } - } - ], - "AW": [ - { - "AWG": { - "_from": "1986-01-01" - } - }, - { - "ANG": { - "_from": "1940-05-10", - "_to": "1986-01-01" - } - } - ], - "AX": [ - { - "EUR": { - "_from": "1999-01-01" - } - } - ], - "AZ": [ - { - "AZN": { - "_from": "2006-01-01" - } - }, - { - "AZM": { - "_from": "1993-11-22", - "_to": "2006-12-31" - } - }, - { - "RUR": { - "_from": "1991-12-25", - "_to": "1994-01-01" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1991-12-25" - } - } - ], - "BA": [ - { - "BAM": { - "_from": "1995-01-01" - } - }, - { - "BAN": { - "_from": "1994-08-15", - "_to": "1997-07-01" - } - }, - { - "BAD": { - "_from": "1992-07-01", - "_to": "1994-08-15" - } - }, - { - "YUR": { - "_from": "1992-07-01", - "_to": "1993-10-01" - } - }, - { - "YUN": { - "_from": "1990-01-01", - "_to": "1992-07-01" - } - }, - { - "YUD": { - "_from": "1966-01-01", - "_to": "1990-01-01" - } - } - ], - "BB": [ - { - "BBD": { - "_from": "1973-12-03" - } - }, - { - "XCD": { - "_from": "1965-10-06", - "_to": "1973-12-03" - } - } - ], - "BD": [ - { - "BDT": { - "_from": "1972-01-01" - } - }, - { - "PKR": { - "_from": "1948-04-01", - "_to": "1972-01-01" - } - }, - { - "INR": { - "_from": "1835-08-17", - "_to": "1948-04-01" - } - } - ], - "BE": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "BEF": { - "_from": "1831-02-07", - "_to": "2002-02-28" - } - }, - { - "NLG": { - "_from": "1816-12-15", - "_to": "1831-02-07" - } - }, - { - "BEL": { - "_tender": "false", - "_from": "1970-01-01", - "_to": "1990-03-05" - } - }, - { - "BEC": { - "_tender": "false", - "_from": "1970-01-01", - "_to": "1990-03-05" - } - } - ], - "BF": [ - { - "XOF": { - "_from": "1984-08-04" - } - } - ], - "BG": [ - { - "EUR": { - "_from": "2026-01-01" - } - }, - { - "BGN": { - "_from": "1999-07-05", - "_to": "2026-01-31" - } - }, - { - "BGL": { - "_from": "1962-01-01", - "_to": "1999-07-05" - } - }, - { - "BGM": { - "_from": "1952-05-12", - "_to": "1962-01-01" - } - }, - { - "BGO": { - "_from": "1879-07-08", - "_to": "1952-05-12" - } - } - ], - "BH": [ - { - "BHD": { - "_from": "1965-10-16" - } - } - ], - "BI": [ - { - "BIF": { - "_from": "1964-05-19" - } - } - ], - "BJ": [ - { - "XOF": { - "_from": "1975-11-30" - } - } - ], - "BL": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1960-01-01", - "_to": "2002-02-17" - } - } - ], - "BM": [ - { - "BMD": { - "_from": "1970-02-06" - } - } - ], - "BN": [ - { - "BND": { - "_from": "1967-06-12" - } - }, - { - "MYR": { - "_from": "1963-09-16", - "_to": "1967-06-12" - } - } - ], - "BO": [ - { - "BOB": { - "_from": "1987-01-01" - } - }, - { - "BOP": { - "_from": "1963-01-01", - "_to": "1986-12-31" - } - }, - { - "BOL": { - "_from": "1863-06-23", - "_to": "1963-01-01" - } - }, - { - "BOV": { - "_tender": "false" - } - } - ], - "BQ": [ - { - "USD": { - "_from": "2011-01-01" - } - }, - { - "ANG": { - "_from": "2010-10-10", - "_to": "2011-01-01" - } - } - ], - "BR": [ - { - "BRL": { - "_from": "1994-07-01" - } - }, - { - "BRR": { - "_from": "1993-08-01", - "_to": "1994-07-01" - } - }, - { - "BRE": { - "_from": "1990-03-16", - "_to": "1993-08-01" - } - }, - { - "BRN": { - "_from": "1989-01-15", - "_to": "1990-03-16" - } - }, - { - "BRC": { - "_from": "1986-02-28", - "_to": "1989-01-15" - } - }, - { - "BRB": { - "_from": "1967-02-13", - "_to": "1986-02-28" - } - }, - { - "BRZ": { - "_from": "1942-11-01", - "_to": "1967-02-13" - } - } - ], - "BS": [ - { - "BSD": { - "_from": "1966-05-25" - } - } - ], - "BT": [ - { - "BTN": { - "_from": "1974-04-16" - } - }, - { - "INR": { - "_from": "1907-01-01" - } - } - ], - "BU": [ - { - "BUK": { - "_from": "1952-07-01", - "_to": "1989-06-18" - } - } - ], - "BV": [ - { - "NOK": { - "_from": "1905-06-07" - } - } - ], - "BW": [ - { - "BWP": { - "_from": "1976-08-23" - } - }, - { - "ZAR": { - "_from": "1961-02-14", - "_to": "1976-08-23" - } - } - ], - "BY": [ - { - "BYN": { - "_from": "2016-07-01" - } - }, - { - "BYR": { - "_from": "2000-01-01", - "_to": "2017-01-01" - } - }, - { - "BYB": { - "_from": "1994-08-01", - "_to": "2000-12-31" - } - }, - { - "RUR": { - "_from": "1991-12-25", - "_to": "1994-11-08" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1991-12-25" - } - } - ], - "BZ": [ - { - "BZD": { - "_from": "1974-01-01" - } - } - ], - "CA": [ - { - "CAD": { - "_from": "1858-01-01" - } - } - ], - "CC": [ - { - "AUD": { - "_from": "1966-02-14" - } - } - ], - "CD": [ - { - "CDF": { - "_from": "1998-07-01" - } - }, - { - "ZRN": { - "_from": "1993-11-01", - "_to": "1998-07-01" - } - }, - { - "ZRZ": { - "_from": "1971-10-27", - "_to": "1993-11-01" - } - } - ], - "CF": [ - { - "XAF": { - "_from": "1993-01-01" - } - } - ], - "CG": [ - { - "XAF": { - "_from": "1993-01-01" - } - } - ], - "CH": [ - { - "CHF": { - "_from": "1799-03-17" - } - }, - { - "CHE": { - "_tender": "false" - } - }, - { - "CHW": { - "_tender": "false" - } - } - ], - "CI": [ - { - "XOF": { - "_from": "1958-12-04" - } - } - ], - "CK": [ - { - "NZD": { - "_from": "1967-07-10" - } - } - ], - "CL": [ - { - "CLP": { - "_from": "1975-09-29" - } - }, - { - "CLE": { - "_from": "1960-01-01", - "_to": "1975-09-29" - } - }, - { - "CLF": { - "_tender": "false" - } - } - ], - "CM": [ - { - "XAF": { - "_from": "1973-04-01" - } - } - ], - "CN": [ - { - "CNY": { - "_from": "1953-03-01" - } - }, - { - "CNX": { - "_tender": "false", - "_from": "1979-01-01", - "_to": "1998-12-31" - } - }, - { - "CNH": { - "_tender": "false", - "_from": "2010-07-19" - } - } - ], - "CO": [ - { - "COP": { - "_from": "1905-01-01" - } - }, - { - "COU": { - "_tender": "false" - } - } - ], - "CP": [ - { - "XXX": { - "_tender": "false" - } - } - ], - "CR": [ - { - "CRC": { - "_from": "1896-10-26" - } - } - ], - "CS": [ - { - "CSD": { - "_from": "2002-05-15", - "_to": "2006-06-03" - } - }, - { - "EUR": { - "_from": "2003-02-04", - "_to": "2006-06-03" - } - }, - { - "YUM": { - "_from": "1994-01-24", - "_to": "2002-05-15" - } - } - ], - "CU": [ - { - "CUP": { - "_from": "1859-01-01" - } - }, - { - "CUC": { - "_from": "1994-01-01", - "_to": "2021-06-01" - } - }, - { - "USD": { - "_from": "1899-01-01", - "_to": "1959-01-01" - } - } - ], - "CV": [ - { - "CVE": { - "_from": "1914-01-01" - } - }, - { - "PTE": { - "_from": "1911-05-22", - "_to": "1975-07-05" - } - } - ], - "CW": [ - { - "XCG": { - "_tz": "America/Curacao", - "_from": "2025-03-31" - } - }, - { - "ANG": { - "_from": "2010-10-10", - "_to": "2025-06-30", - "_to-tz": "America/Curacao" - } - } - ], - "CX": [ - { - "AUD": { - "_from": "1966-02-14" - } - } - ], - "CY": [ - { - "EUR": { - "_from": "2008-01-01" - } - }, - { - "CYP": { - "_from": "1914-09-10", - "_to": "2008-01-31" - } - } - ], - "CZ": [ - { - "CZK": { - "_from": "1993-01-01" - } - }, - { - "CSK": { - "_from": "1953-06-01", - "_to": "1993-03-01" - } - } - ], - "DD": [ - { - "DDM": { - "_from": "1948-07-20", - "_to": "1990-10-02" - } - } - ], - "DE": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "DEM": { - "_from": "1948-06-20", - "_to": "2002-02-28" - } - } - ], - "DG": [ - { - "USD": { - "_from": "1965-11-08" - } - } - ], - "DJ": [ - { - "DJF": { - "_from": "1977-06-27" - } - } - ], - "DK": [ - { - "DKK": { - "_from": "1873-05-27" - } - } - ], - "DM": [ - { - "XCD": { - "_from": "1965-10-06" - } - } - ], - "DO": [ - { - "DOP": { - "_from": "1947-10-01" - } - }, - { - "USD": { - "_from": "1905-06-21", - "_to": "1947-10-01" - } - } - ], - "DZ": [ - { - "DZD": { - "_from": "1964-04-01" - } - } - ], - "EA": [ - { - "EUR": { - "_from": "1999-01-01" - } - } - ], - "EC": [ - { - "USD": { - "_from": "2000-10-02" - } - }, - { - "ECS": { - "_from": "1884-04-01", - "_to": "2000-10-02" - } - }, - { - "ECV": { - "_tender": "false", - "_from": "1993-05-23", - "_to": "2000-01-09" - } - } - ], - "EE": [ - { - "EUR": { - "_from": "2011-01-01" - } - }, - { - "EEK": { - "_from": "1992-06-21", - "_to": "2010-12-31" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1992-06-20" - } - } - ], - "EG": [ - { - "EGP": { - "_from": "1885-11-14" - } - } - ], - "EH": [ - { - "MAD": { - "_from": "1976-02-26" - } - } - ], - "ER": [ - { - "ERN": { - "_from": "1997-11-08" - } - }, - { - "ETB": { - "_from": "1993-05-24", - "_to": "1997-11-08" - } - } - ], - "ES": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "ESP": { - "_from": "1868-10-19", - "_to": "2002-02-28" - } - }, - { - "ESA": { - "_tender": "false", - "_from": "1978-01-01", - "_to": "1981-12-31" - } - }, - { - "ESB": { - "_tender": "false", - "_from": "1975-01-01", - "_to": "1994-12-31" - } - } - ], - "ET": [ - { - "ETB": { - "_from": "1976-09-15" - } - } - ], - "EU": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "XEU": { - "_tender": "false", - "_from": "1979-01-01", - "_to": "1998-12-31" - } - } - ], - "FI": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FIM": { - "_from": "1963-01-01", - "_to": "2002-02-28" - } - } - ], - "FJ": [ - { - "FJD": { - "_from": "1969-01-13" - } - } - ], - "FK": [ - { - "FKP": { - "_from": "1901-01-01" - } - } - ], - "FM": [ - { - "USD": { - "_from": "1944-01-01" - } - }, - { - "JPY": { - "_from": "1914-10-03", - "_to": "1944-01-01" - } - } - ], - "FO": [ - { - "DKK": { - "_from": "1948-01-01" - } - } - ], - "FR": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1960-01-01", - "_to": "2002-02-17" - } - } - ], - "GA": [ - { - "XAF": { - "_from": "1993-01-01" - } - } - ], - "GB": [ - { - "GBP": { - "_from": "1694-07-27" - } - } - ], - "GD": [ - { - "XCD": { - "_from": "1967-02-27" - } - } - ], - "GE": [ - { - "GEL": { - "_from": "1995-09-23" - } - }, - { - "GEK": { - "_from": "1993-04-05", - "_to": "1995-09-25" - } - }, - { - "RUR": { - "_from": "1991-12-25", - "_to": "1993-06-11" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1991-12-25" - } - } - ], - "GF": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1960-01-01", - "_to": "2002-02-17" - } - } - ], - "GG": [ - { - "GBP": { - "_from": "1830-01-01" - } - } - ], - "GH": [ - { - "GHS": { - "_from": "2007-07-03" - } - }, - { - "GHC": { - "_from": "1979-03-09", - "_to": "2007-12-31" - } - } - ], - "GI": [ - { - "GIP": { - "_from": "1713-01-01" - } - } - ], - "GL": [ - { - "DKK": { - "_from": "1873-05-27" - } - } - ], - "GM": [ - { - "GMD": { - "_from": "1971-07-01" - } - } - ], - "GN": [ - { - "GNF": { - "_from": "1986-01-06" - } - }, - { - "GNS": { - "_from": "1972-10-02", - "_to": "1986-01-06" - } - } - ], - "GP": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1960-01-01", - "_to": "2002-02-17" - } - } - ], - "GQ": [ - { - "XAF": { - "_from": "1993-01-01" - } - }, - { - "GQE": { - "_from": "1975-07-07", - "_to": "1986-06-01" - } - } - ], - "GR": [ - { - "EUR": { - "_from": "2001-01-01" - } - }, - { - "GRD": { - "_from": "1954-05-01", - "_to": "2002-02-28" - } - } - ], - "GS": [ - { - "GBP": { - "_from": "1908-01-01" - } - } - ], - "GT": [ - { - "GTQ": { - "_from": "1925-05-27" - } - } - ], - "GU": [ - { - "USD": { - "_from": "1944-08-21" - } - } - ], - "GW": [ - { - "XOF": { - "_from": "1997-03-31" - } - }, - { - "GWP": { - "_from": "1976-02-28", - "_to": "1997-03-31" - } - }, - { - "GWE": { - "_from": "1914-01-01", - "_to": "1976-02-28" - } - } - ], - "GY": [ - { - "GYD": { - "_from": "1966-05-26" - } - } - ], - "HK": [ - { - "HKD": { - "_from": "1895-02-02" - } - } - ], - "HM": [ - { - "AUD": { - "_from": "1967-02-16" - } - } - ], - "HN": [ - { - "HNL": { - "_from": "1926-04-03" - } - } - ], - "HR": [ - { - "EUR": { - "_tz": "Europe/Zagreb", - "_from": "2023-01-01" - } - }, - { - "HRK": { - "_from": "1994-05-30", - "_to": "2023-01-14", - "_to-tz": "Europe/Zagreb" - } - }, - { - "HRD": { - "_from": "1991-12-23", - "_to": "1995-01-01" - } - }, - { - "YUN": { - "_from": "1990-01-01", - "_to": "1991-12-23" - } - }, - { - "YUD": { - "_from": "1966-01-01", - "_to": "1990-01-01" - } - } - ], - "HT": [ - { - "HTG": { - "_from": "1872-08-26" - } - }, - { - "USD": { - "_from": "1915-01-01" - } - } - ], - "HU": [ - { - "HUF": { - "_from": "1946-07-23" - } - } - ], - "IC": [ - { - "EUR": { - "_from": "1999-01-01" - } - } - ], - "ID": [ - { - "IDR": { - "_from": "1965-12-13" - } - } - ], - "IE": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "IEP": { - "_from": "1922-01-01", - "_to": "2002-02-09" - } - }, - { - "GBP": { - "_from": "1800-01-01", - "_to": "1922-01-01" - } - } - ], - "IL": [ - { - "ILS": { - "_from": "1985-09-04" - } - }, - { - "ILR": { - "_from": "1980-02-22", - "_to": "1985-09-04" - } - }, - { - "ILP": { - "_from": "1948-08-16", - "_to": "1980-02-22" - } - } - ], - "IM": [ - { - "GBP": { - "_from": "1840-01-03" - } - } - ], - "IN": [ - { - "INR": { - "_from": "1835-08-17" - } - } - ], - "IO": [ - { - "USD": { - "_from": "1965-11-08" - } - } - ], - "IQ": [ - { - "IQD": { - "_from": "1931-04-19" - } - }, - { - "EGP": { - "_from": "1920-11-11", - "_to": "1931-04-19" - } - }, - { - "INR": { - "_from": "1920-11-11", - "_to": "1931-04-19" - } - } - ], - "IR": [ - { - "IRR": { - "_from": "1932-05-13" - } - } - ], - "IS": [ - { - "ISK": { - "_from": "1981-01-01" - } - }, - { - "ISJ": { - "_from": "1918-12-01", - "_to": "1981-01-01" - } - }, - { - "DKK": { - "_from": "1873-05-27", - "_to": "1918-12-01" - } - } - ], - "IT": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "ITL": { - "_from": "1862-08-24", - "_to": "2002-02-28" - } - } - ], - "JE": [ - { - "GBP": { - "_from": "1837-01-01" - } - } - ], - "JM": [ - { - "JMD": { - "_from": "1969-09-08" - } - } - ], - "JO": [ - { - "JOD": { - "_from": "1950-07-01" - } - } - ], - "JP": [ - { - "JPY": { - "_from": "1871-06-01" - } - } - ], - "KE": [ - { - "KES": { - "_from": "1966-09-14" - } - } - ], - "KG": [ - { - "KGS": { - "_from": "1993-05-10" - } - }, - { - "RUR": { - "_from": "1991-12-25", - "_to": "1993-05-10" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1991-12-25" - } - } - ], - "KH": [ - { - "KHR": { - "_from": "1980-03-20" - } - } - ], - "KI": [ - { - "AUD": { - "_from": "1966-02-14" - } - } - ], - "KM": [ - { - "KMF": { - "_from": "1975-07-06" - } - } - ], - "KN": [ - { - "XCD": { - "_from": "1965-10-06" - } - } - ], - "KP": [ - { - "KPW": { - "_from": "1959-04-17" - } - } - ], - "KR": [ - { - "KRW": { - "_from": "1962-06-10" - } - }, - { - "KRH": { - "_from": "1953-02-15", - "_to": "1962-06-10" - } - }, - { - "KRO": { - "_from": "1945-08-15", - "_to": "1953-02-15" - } - } - ], - "KW": [ - { - "KWD": { - "_from": "1961-04-01" - } - } - ], - "KY": [ - { - "KYD": { - "_from": "1971-01-01" - } - }, - { - "JMD": { - "_from": "1969-09-08", - "_to": "1971-01-01" - } - } - ], - "KZ": [ - { - "KZT": { - "_from": "1993-11-05" - } - } - ], - "LA": [ - { - "LAK": { - "_from": "1979-12-10" - } - } - ], - "LB": [ - { - "LBP": { - "_from": "1948-02-02" - } - } - ], - "LC": [ - { - "XCD": { - "_from": "1965-10-06" - } - } - ], - "LI": [ - { - "CHF": { - "_from": "1921-02-01" - } - } - ], - "LK": [ - { - "LKR": { - "_from": "1978-05-22" - } - } - ], - "LR": [ - { - "LRD": { - "_from": "1944-01-01" - } - } - ], - "LS": [ - { - "ZAR": { - "_from": "1961-02-14" - } - }, - { - "LSL": { - "_from": "1980-01-22" - } - } - ], - "LT": [ - { - "EUR": { - "_from": "2015-01-01" - } - }, - { - "LTL": { - "_from": "1993-06-25", - "_to": "2014-12-31" - } - }, - { - "LTT": { - "_from": "1992-10-01", - "_to": "1993-06-25" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1992-10-01" - } - } - ], - "LU": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "LUF": { - "_from": "1944-09-04", - "_to": "2002-02-28" - } - }, - { - "LUC": { - "_tender": "false", - "_from": "1970-01-01", - "_to": "1990-03-05" - } - }, - { - "LUL": { - "_tender": "false", - "_from": "1970-01-01", - "_to": "1990-03-05" - } - } - ], - "LV": [ - { - "EUR": { - "_from": "2014-01-01" - } - }, - { - "LVL": { - "_from": "1993-06-28", - "_to": "2013-12-31" - } - }, - { - "LVR": { - "_from": "1992-05-07", - "_to": "1993-10-17" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1992-07-20" - } - } - ], - "LY": [ - { - "LYD": { - "_from": "1971-09-01" - } - } - ], - "MA": [ - { - "MAD": { - "_from": "1959-10-17" - } - }, - { - "MAF": { - "_from": "1881-01-01", - "_to": "1959-10-17" - } - } - ], - "MC": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1960-01-01", - "_to": "2002-02-17" - } - }, - { - "MCF": { - "_from": "1960-01-01", - "_to": "2002-02-17" - } - } - ], - "MD": [ - { - "MDL": { - "_from": "1993-11-29" - } - }, - { - "MDC": { - "_from": "1992-06-01", - "_to": "1993-11-29" - } - } - ], - "ME": [ - { - "EUR": { - "_from": "2002-01-01" - } - }, - { - "DEM": { - "_from": "1999-10-02", - "_to": "2002-05-15" - } - }, - { - "YUM": { - "_from": "1994-01-24", - "_to": "2002-05-15" - } - } - ], - "MF": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1960-01-01", - "_to": "2002-02-17" - } - } - ], - "MG": [ - { - "MGA": { - "_from": "1983-11-01" - } - }, - { - "MGF": { - "_from": "1963-07-01", - "_to": "2004-12-31" - } - } - ], - "MH": [ - { - "USD": { - "_from": "1944-01-01" - } - } - ], - "MK": [ - { - "MKD": { - "_from": "1993-05-20" - } - }, - { - "MKN": { - "_from": "1992-04-26", - "_to": "1993-05-20" - } - } - ], - "ML": [ - { - "XOF": { - "_from": "1984-06-01" - } - }, - { - "MLF": { - "_from": "1962-07-02", - "_to": "1984-08-31" - } - }, - { - "XOF": { - "_from": "1958-11-24", - "_to": "1962-07-02" - } - } - ], - "MM": [ - { - "MMK": { - "_from": "1989-06-18" - } - }, - { - "BUK": { - "_from": "1952-07-01", - "_to": "1989-06-18" - } - } - ], - "MN": [ - { - "MNT": { - "_from": "1915-03-01" - } - } - ], - "MO": [ - { - "MOP": { - "_from": "1901-01-01" - } - } - ], - "MP": [ - { - "USD": { - "_from": "1944-01-01" - } - } - ], - "MQ": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1960-01-01", - "_to": "2002-02-17" - } - } - ], - "MR": [ - { - "MRU": { - "_from": "2018-01-01" - } - }, - { - "MRO": { - "_from": "1973-06-29", - "_to": "2018-06-30" - } - }, - { - "XOF": { - "_from": "1958-11-28", - "_to": "1973-06-29" - } - } - ], - "MS": [ - { - "XCD": { - "_from": "1967-02-27" - } - } - ], - "MT": [ - { - "EUR": { - "_from": "2008-01-01" - } - }, - { - "MTL": { - "_from": "1968-06-07", - "_to": "2008-01-31" - } - }, - { - "MTP": { - "_from": "1914-08-13", - "_to": "1968-06-07" - } - } - ], - "MU": [ - { - "MUR": { - "_from": "1934-04-01" - } - } - ], - "MV": [ - { - "MVR": { - "_from": "1981-07-01" - } - }, - { - "MVP": { - "_from": "1947-01-01", - "_to": "1981-07-01" - } - } - ], - "MW": [ - { - "MWK": { - "_from": "1971-02-15" - } - } - ], - "MX": [ - { - "MXN": { - "_from": "1993-01-01" - } - }, - { - "MXP": { - "_from": "1822-01-01", - "_to": "1992-12-31" - } - }, - { - "MXV": { - "_tender": "false" - } - } - ], - "MY": [ - { - "MYR": { - "_from": "1963-09-16" - } - } - ], - "MZ": [ - { - "MZN": { - "_from": "2006-07-01" - } - }, - { - "MZM": { - "_from": "1980-06-16", - "_to": "2006-12-31" - } - }, - { - "MZE": { - "_from": "1975-06-25", - "_to": "1980-06-16" - } - } - ], - "NA": [ - { - "NAD": { - "_from": "1993-01-01" - } - }, - { - "ZAR": { - "_from": "1961-02-14" - } - } - ], - "NC": [ - { - "XPF": { - "_from": "1985-01-01" - } - } - ], - "NE": [ - { - "XOF": { - "_from": "1958-12-19" - } - } - ], - "NF": [ - { - "AUD": { - "_from": "1966-02-14" - } - } - ], - "NG": [ - { - "NGN": { - "_from": "1973-01-01" - } - } - ], - "NI": [ - { - "NIO": { - "_from": "1991-04-30" - } - }, - { - "NIC": { - "_from": "1988-02-15", - "_to": "1991-04-30" - } - } - ], - "NL": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "NLG": { - "_from": "1813-01-01", - "_to": "2002-02-28" - } - } - ], - "NO": [ - { - "NOK": { - "_from": "1905-06-07" - } - }, - { - "SEK": { - "_from": "1873-05-27", - "_to": "1905-06-07" - } - } - ], - "NP": [ - { - "NPR": { - "_from": "1933-01-01" - } - }, - { - "INR": { - "_from": "1870-01-01", - "_to": "1966-10-17" - } - } - ], - "NR": [ - { - "AUD": { - "_from": "1966-02-14" - } - } - ], - "NU": [ - { - "NZD": { - "_from": "1967-07-10" - } - } - ], - "NZ": [ - { - "NZD": { - "_from": "1967-07-10" - } - } - ], - "OM": [ - { - "OMR": { - "_from": "1972-11-11" - } - } - ], - "PA": [ - { - "PAB": { - "_from": "1903-11-04" - } - }, - { - "USD": { - "_from": "1903-11-18" - } - } - ], - "PE": [ - { - "PEN": { - "_from": "1991-07-01" - } - }, - { - "PEI": { - "_from": "1985-02-01", - "_to": "1991-07-01" - } - }, - { - "PES": { - "_from": "1863-02-14", - "_to": "1985-02-01" - } - } - ], - "PF": [ - { - "XPF": { - "_from": "1945-12-26" - } - } - ], - "PG": [ - { - "PGK": { - "_from": "1975-09-16" - } - }, - { - "AUD": { - "_from": "1966-02-14", - "_to": "1975-09-16" - } - } - ], - "PH": [ - { - "PHP": { - "_from": "1946-07-04" - } - } - ], - "PK": [ - { - "PKR": { - "_from": "1948-04-01" - } - }, - { - "INR": { - "_from": "1835-08-17", - "_to": "1947-08-15" - } - } - ], - "PL": [ - { - "PLN": { - "_from": "1995-01-01" - } - }, - { - "PLZ": { - "_from": "1950-10-28", - "_to": "1994-12-31" - } - } - ], - "PM": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1972-12-21", - "_to": "2002-02-17" - } - } - ], - "PN": [ - { - "NZD": { - "_from": "1969-01-13" - } - } - ], - "PR": [ - { - "USD": { - "_from": "1898-12-10" - } - }, - { - "ESP": { - "_from": "1800-01-01", - "_to": "1898-12-10" - } - } - ], - "PS": [ - { - "ILS": { - "_from": "1985-09-04" - } - }, - { - "JOD": { - "_from": "1996-02-12" - } - }, - { - "ILP": { - "_from": "1967-06-01", - "_to": "1980-02-22" - } - }, - { - "JOD": { - "_from": "1950-07-01", - "_to": "1967-06-01" - } - } - ], - "PT": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "PTE": { - "_from": "1911-05-22", - "_to": "2002-02-28" - } - } - ], - "PW": [ - { - "USD": { - "_from": "1944-01-01" - } - } - ], - "PY": [ - { - "PYG": { - "_from": "1943-11-01" - } - } - ], - "QA": [ - { - "QAR": { - "_from": "1973-05-19" - } - } - ], - "RE": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1975-01-01", - "_to": "2002-02-17" - } - } - ], - "RO": [ - { - "RON": { - "_from": "2005-07-01" - } - }, - { - "ROL": { - "_from": "1952-01-28", - "_to": "2006-12-31" - } - } - ], - "RS": [ - { - "RSD": { - "_from": "2006-10-25" - } - }, - { - "CSD": { - "_from": "2002-05-15", - "_to": "2006-10-25" - } - }, - { - "YUM": { - "_from": "1994-01-24", - "_to": "2002-05-15" - } - } - ], - "RU": [ - { - "RUB": { - "_from": "1999-01-01" - } - }, - { - "RUR": { - "_from": "1991-12-25", - "_to": "1998-12-31" - } - } - ], - "RW": [ - { - "RWF": { - "_from": "1964-05-19" - } - } - ], - "SA": [ - { - "SAR": { - "_from": "1952-10-22" - } - } - ], - "SB": [ - { - "SBD": { - "_from": "1977-10-24" - } - }, - { - "AUD": { - "_from": "1966-02-14", - "_to": "1978-06-30" - } - } - ], - "SC": [ - { - "SCR": { - "_from": "1903-11-01" - } - } - ], - "SD": [ - { - "SDG": { - "_from": "2007-01-10" - } - }, - { - "SDD": { - "_from": "1992-06-08", - "_to": "2007-06-30" - } - }, - { - "SDP": { - "_from": "1957-04-08", - "_to": "1998-06-01" - } - }, - { - "EGP": { - "_from": "1889-01-19", - "_to": "1958-01-01" - } - }, - { - "GBP": { - "_from": "1889-01-19", - "_to": "1958-01-01" - } - } - ], - "SE": [ - { - "SEK": { - "_from": "1873-05-27" - } - } - ], - "SG": [ - { - "SGD": { - "_from": "1967-06-12" - } - }, - { - "MYR": { - "_from": "1963-09-16", - "_to": "1967-06-12" - } - } - ], - "SH": [ - { - "SHP": { - "_from": "1917-02-15" - } - } - ], - "SI": [ - { - "EUR": { - "_from": "2007-01-01" - } - }, - { - "SIT": { - "_from": "1992-10-07", - "_to": "2007-01-14" - } - } - ], - "SJ": [ - { - "NOK": { - "_from": "1905-06-07" - } - } - ], - "SK": [ - { - "EUR": { - "_from": "2009-01-01" - } - }, - { - "SKK": { - "_from": "1992-12-31", - "_to": "2009-01-01" - } - }, - { - "CSK": { - "_from": "1953-06-01", - "_to": "1992-12-31" - } - } - ], - "SL": [ - { - "SLE": { - "_tz": "Africa/Freetown", - "_from": "2022-07-01" - } - }, - { - "SLL": { - "_from": "1964-08-04", - "_to": "2023-12-31", - "_to-tz": "Africa/Freetown" - } - }, - { - "GBP": { - "_from": "1808-11-30", - "_to": "1966-02-04" - } - } - ], - "SM": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "ITL": { - "_from": "1865-12-23", - "_to": "2001-02-28" - } - } - ], - "SN": [ - { - "XOF": { - "_from": "1959-04-04" - } - } - ], - "SO": [ - { - "SOS": { - "_from": "1960-07-01" - } - } - ], - "SR": [ - { - "SRD": { - "_from": "2004-01-01" - } - }, - { - "SRG": { - "_from": "1940-05-10", - "_to": "2003-12-31" - } - }, - { - "NLG": { - "_from": "1815-11-20", - "_to": "1940-05-10" - } - } - ], - "SS": [ - { - "SSP": { - "_from": "2011-07-18" - } - }, - { - "SDG": { - "_from": "2007-01-10", - "_to": "2011-09-01" - } - } - ], - "ST": [ - { - "STN": { - "_from": "2018-01-01" - } - }, - { - "STD": { - "_from": "1977-09-08", - "_to": "2017-12-31" - } - } - ], - "SU": [ - { - "SUR": { - "_from": "1961-01-01", - "_to": "1991-12-25" - } - } - ], - "SV": [ - { - "USD": { - "_from": "2001-01-01" - } - }, - { - "SVC": { - "_from": "1919-11-11", - "_to": "2001-01-01" - } - } - ], - "SX": [ - { - "XCG": { - "_tz": "America/Lower_Princes", - "_from": "2025-03-31" - } - }, - { - "ANG": { - "_from": "2010-10-10", - "_to": "2025-06-30", - "_to-tz": "America/Lower_Princes" - } - } - ], - "SY": [ - { - "SYP": { - "_from": "1948-01-01" - } - } - ], - "SZ": [ - { - "SZL": { - "_from": "1974-09-06" - } - } - ], - "TA": [ - { - "GBP": { - "_from": "1938-01-12" - } - } - ], - "TC": [ - { - "USD": { - "_from": "1969-09-08" - } - } - ], - "TD": [ - { - "XAF": { - "_from": "1993-01-01" - } - } - ], - "TF": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1959-01-01", - "_to": "2002-02-17" - } - } - ], - "TG": [ - { - "XOF": { - "_from": "1958-11-28" - } - } - ], - "TH": [ - { - "THB": { - "_from": "1928-04-15" - } - } - ], - "TJ": [ - { - "TJS": { - "_from": "2000-10-26" - } - }, - { - "TJR": { - "_from": "1995-05-10", - "_to": "2000-10-25" - } - }, - { - "RUR": { - "_from": "1991-12-25", - "_to": "1995-05-10" - } - } - ], - "TK": [ - { - "NZD": { - "_from": "1967-07-10" - } - } - ], - "TL": [ - { - "USD": { - "_from": "1999-10-20" - } - }, - { - "TPE": { - "_from": "1959-01-02", - "_to": "2002-05-20" - } - }, - { - "IDR": { - "_from": "1975-12-07", - "_to": "2002-05-20" - } - } - ], - "TM": [ - { - "TMT": { - "_from": "2009-01-01" - } - }, - { - "TMM": { - "_from": "1993-11-01", - "_to": "2009-01-01" - } - }, - { - "RUR": { - "_from": "1991-12-25", - "_to": "1993-11-01" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1991-12-25" - } - } - ], - "TN": [ - { - "TND": { - "_from": "1958-11-01" - } - } - ], - "TO": [ - { - "TOP": { - "_from": "1966-02-14" - } - } - ], - "TP": [ - { - "TPE": { - "_from": "1959-01-02", - "_to": "2002-05-20" - } - }, - { - "IDR": { - "_from": "1975-12-07", - "_to": "2002-05-20" - } - } - ], - "TR": [ - { - "TRY": { - "_from": "2005-01-01" - } - }, - { - "TRL": { - "_from": "1922-11-01", - "_to": "2005-12-31" - } - } - ], - "TT": [ - { - "TTD": { - "_from": "1964-01-01" - } - } - ], - "TV": [ - { - "AUD": { - "_from": "1966-02-14" - } - } - ], - "TW": [ - { - "TWD": { - "_from": "1949-06-15" - } - } - ], - "TZ": [ - { - "TZS": { - "_from": "1966-06-14" - } - } - ], - "UA": [ - { - "UAH": { - "_from": "1996-09-02" - } - }, - { - "UAK": { - "_from": "1992-11-13", - "_to": "1993-10-17" - } - }, - { - "RUR": { - "_from": "1991-12-25", - "_to": "1992-11-13" - } - }, - { - "SUR": { - "_from": "1961-01-01", - "_to": "1991-12-25" - } - } - ], - "UG": [ - { - "UGX": { - "_from": "1987-05-15" - } - }, - { - "UGS": { - "_from": "1966-08-15", - "_to": "1987-05-15" - } - } - ], - "UM": [ - { - "USD": { - "_from": "1944-01-01" - } - } - ], - "US": [ - { - "USD": { - "_from": "1792-01-01" - } - }, - { - "USN": { - "_tender": "false" - } - }, - { - "USS": { - "_tender": "false", - "_to": "2014-03-01" - } - } - ], - "UY": [ - { - "UYU": { - "_from": "1993-03-01" - } - }, - { - "UYP": { - "_from": "1975-07-01", - "_to": "1993-03-01" - } - }, - { - "UYI": { - "_tender": "false" - } - }, - { - "UYW": { - "_tender": "false" - } - } - ], - "UZ": [ - { - "UZS": { - "_from": "1994-07-01" - } - } - ], - "VA": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "ITL": { - "_from": "1870-10-19", - "_to": "2002-02-28" - } - } - ], - "VC": [ - { - "XCD": { - "_from": "1965-10-06" - } - } - ], - "VE": [ - { - "VES": { - "_from": "2018-08-20" - } - }, - { - "VEF": { - "_from": "2008-01-01", - "_to": "2018-08-20" - } - }, - { - "VEB": { - "_from": "1871-05-11", - "_to": "2008-06-30" - } - }, - { - "VED": { - "_tender": "false" - } - } - ], - "VG": [ - { - "USD": { - "_from": "1833-01-01" - } - }, - { - "GBP": { - "_from": "1833-01-01", - "_to": "1959-01-01" - } - } - ], - "VI": [ - { - "USD": { - "_from": "1837-01-01" - } - } - ], - "VN": [ - { - "VND": { - "_from": "1985-09-14" - } - }, - { - "VNN": { - "_from": "1978-05-03", - "_to": "1985-09-14" - } - } - ], - "VU": [ - { - "VUV": { - "_from": "1981-01-01" - } - } - ], - "WF": [ - { - "XPF": { - "_from": "1961-07-30" - } - } - ], - "WS": [ - { - "WST": { - "_from": "1967-07-10" - } - } - ], - "XK": [ - { - "EUR": { - "_from": "2002-01-01" - } - }, - { - "DEM": { - "_from": "1999-09-01", - "_to": "2002-03-09" - } - }, - { - "YUM": { - "_from": "1994-01-24", - "_to": "1999-09-30" - } - } - ], - "YD": [ - { - "YDD": { - "_from": "1965-04-01", - "_to": "1996-01-01" - } - } - ], - "YE": [ - { - "YER": { - "_from": "1990-05-22" - } - } - ], - "YT": [ - { - "EUR": { - "_from": "1999-01-01" - } - }, - { - "FRF": { - "_from": "1976-02-23", - "_to": "2002-02-17" - } - }, - { - "KMF": { - "_from": "1975-01-01", - "_to": "1976-02-23" - } - } - ], - "YU": [ - { - "YUM": { - "_from": "1994-01-24", - "_to": "2002-05-15" - } - }, - { - "YUN": { - "_from": "1990-01-01", - "_to": "1992-07-24" - } - }, - { - "YUD": { - "_from": "1966-01-01", - "_to": "1990-01-01" - } - } - ], - "ZA": [ - { - "ZAR": { - "_from": "1961-02-14" - } - }, - { - "ZAL": { - "_tender": "false", - "_from": "1985-09-01", - "_to": "1995-03-13" - } - } - ], - "ZM": [ - { - "ZMW": { - "_from": "2013-01-01" - } - }, - { - "ZMK": { - "_from": "1968-01-16", - "_to": "2013-01-01" - } - } - ], - "ZR": [ - { - "ZRN": { - "_from": "1993-11-01", - "_to": "1998-07-31" - } - }, - { - "ZRZ": { - "_from": "1971-10-27", - "_to": "1993-11-01" - } - } - ], - "ZW": [ - { - "ZWG": { - "_from": "2024-06-25" - } - }, - { - "USD": { - "_from": "2009-04-12" - } - }, - { - "ZWL": { - "_from": "2009-02-02", - "_to": "2024-08-31" - } - }, - { - "ZWR": { - "_from": "2008-08-01", - "_to": "2009-02-02" - } - }, - { - "ZWD": { - "_from": "1980-04-18", - "_to": "2008-08-01" - } - }, - { - "RHD": { - "_from": "1970-02-17", - "_to": "1980-04-18" - } - } - ], - "ZZ": [ - { - "XAD": { - "_tender": "false", - "_from": "2025-05-12" - } - }, - { - "XAG": { - "_tender": "false" - } - }, - { - "XAU": { - "_tender": "false" - } - }, - { - "XBA": { - "_tender": "false" - } - }, - { - "XBB": { - "_tender": "false" - } - }, - { - "XBC": { - "_tender": "false" - } - }, - { - "XBD": { - "_tender": "false" - } - }, - { - "XDR": { - "_tender": "false" - } - }, - { - "XFO": { - "_tender": "false", - "_from": "1930-01-01", - "_to": "2003-04-01" - } - }, - { - "XFU": { - "_tender": "false", - "_to": "2013-11-30" - } - }, - { - "XPD": { - "_tender": "false" - } - }, - { - "XPT": { - "_tender": "false" - } - }, - { - "XRE": { - "_tender": "false", - "_to": "1999-11-30" - } - }, - { - "XSU": { - "_tender": "false" - } - }, - { - "XTS": { - "_tender": "false" - } - }, - { - "XUA": { - "_tender": "false" - } - }, - { - "XXX": { - "_tender": "false" - } - } - ] - } - } - } -} diff --git a/Tools/currency-table/generate.mjs b/Tools/currency-table/generate.mjs deleted file mode 100644 index 162b2f0..0000000 --- a/Tools/currency-table/generate.mjs +++ /dev/null @@ -1,75 +0,0 @@ -// Regenerates Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs from the CLDR -// data vendored in ./cldr. Run `node generate.mjs`; it needs nothing but Node. -// The output is committed; CI re-runs this and fails if the committed file differs. - -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const here = path.dirname(fileURLToPath(import.meta.url)); -const LOCALE = 'en-001'; -const OUTPUT = path.resolve(here, '../../Engine/Quokka.Core/Functions/Standard/Money/CurrencyFormats.g.cs'); - -const read = name => JSON.parse(fs.readFileSync(path.resolve(here, 'cldr', name), 'utf8')); - -const symbols = read('currencies-en-001.json').main[LOCALE].numbers.currencies; -const fractions = read('currencyData.json').supplemental.currencyData.fractions; -const decimalPlacesOf = code => Number((fractions[code] ?? fractions.DEFAULT)._digits); -const escape = value => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); - -const entries = []; -for (const code of Object.keys(symbols).sort()) { - const entry = symbols[code]; - const symbol = entry.symbol ?? code; - const narrow = entry['symbol-alt-narrow'] ?? symbol; - const decimals = decimalPlacesOf(code); - entries.push(`\t\t\t\t["${code}"] = new("${escape(narrow)}", "${escape(symbol)}", ${decimals}),`); -} - -if (entries.length === 0) - throw new Error('CLDR produced no currencies — refusing to write an empty table'); - -entries[entries.length - 1] = entries.at(-1).replace(/,$/, ''); - -const cldrVersion = fs.readFileSync(path.resolve(here, 'cldr/VERSION'), 'utf8').trim(); -const source = `// // Copyright 2022 Mindbox Ltd -// // -// // Licensed under the Apache License, Version 2.0 (the "License"); -// // you may not use this file except in compliance with the License. -// // You may obtain a copy of the License at -// // -// // http://www.apache.org/licenses/LICENSE-2.0 -// // -// // Unless required by applicable law or agreed to in writing, software -// // distributed under the License is distributed on an "AS IS" BASIS, -// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// // See the License for the specific language governing permissions and -// // limitations under the License. - -// -// Generated by tools/currency-table from CLDR ${cldrVersion} (${LOCALE}). Do not edit by hand. -// - -using System; -using System.Collections.Generic; - -namespace Mindbox.Quokka -{ -\tinternal static class CurrencyFormats -\t{ -\t\tprivate static readonly IReadOnlyDictionary formatsByCode = -\t\t\tnew Dictionary(StringComparer.OrdinalIgnoreCase) -\t\t\t{ -${entries.join('\n')} -\t\t\t}; - -\t\tpublic static CurrencyFormat TryGet(string currencyCode) => -\t\t\tcurrencyCode != null && formatsByCode.TryGetValue(currencyCode, out var format) -\t\t\t\t? format -\t\t\t\t: null; -\t} -} -`; - -fs.writeFileSync(OUTPUT, source); -console.log(`wrote ${entries.length} currencies from CLDR ${cldrVersion}`);