From e8e0b28eda543c566dae3286a3e6cf4da8ff6b8e Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Thu, 24 Sep 2026 16:16:04 +0000 Subject: [PATCH 01/16] CLDR-19681 Add granular deep-link anchors to TR35 currency formatting specification --- docs/ldml/tr35-modifications.md | 2 ++ docs/ldml/tr35-numbers.anchors.json | 33 +++++++++++++++++++++++ docs/ldml/tr35-numbers.md | 42 ++++++++++++++++++++++++++--- tools/scripts/tr-archive/archive.js | 2 +- 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/docs/ldml/tr35-modifications.md b/docs/ldml/tr35-modifications.md index 522a6c0606a..df9b6a13ae6 100644 --- a/docs/ldml/tr35-modifications.md +++ b/docs/ldml/tr35-modifications.md @@ -82,6 +82,8 @@ This is a partial document, describing only the changes to the LDML since the pr * [`Currencies`](tr35-numbers.md#Currencies) Revised the `numberFormat` description for currencies. +* [`Currency Formats`](tr35-numbers.md#Currency_Formats), [`Currencies`](tr35-numbers.md#Currencies), [`Supplemental Currency Data`](tr35-numbers.md#Supplemental_Currency_Data) Added granular subsection headings and deep-link anchors for currency formatting rules, pattern variants (`alt="alphaNextToNumber"`, `alt="noCurrency"`, `currencyPatternAppendISO`), currency placeholders (`¤`..`¤¤¤¤¤`), `unitPattern` display name formatting, `currencySpacing`, and supplemental currency fractions and region mappings. + * [`Plural rules syntax`](tr35-numbers.md#plural-rules-syntax) Made it clear that plural rules are evaluated in semantic order (`zero`, then `one`,…). diff --git a/docs/ldml/tr35-numbers.anchors.json b/docs/ldml/tr35-numbers.anchors.json index e2bc167e740..c6a3095d149 100644 --- a/docs/ldml/tr35-numbers.anchors.json +++ b/docs/ldml/tr35-numbers.anchors.json @@ -4,14 +4,39 @@ "cardinal", "Collapsing_Number_Ranges", "collapsing-number-ranges", + "combining-currency-symbols-and-iso-codes-currencypatternappendiso", + "Compact_Currency_Formatting", "Compact_Number_Formats", + "compact-currency-alphaNextToNumber", + "compact-currency-formatting", "compact-number-formats", "Contents", "contents-of-part-3-numbers", "currencies", "Currencies", + "Currency_Decimal_and_Grouping_Symbols", "Currency_Formats", + "Currency_Placeholder_Placement", + "Currency_Symbols_Decimal_Group", + "currency-alphaNextToNumber", + "currency-boundary-spacing-currencyspacing", + "currency-custom-decimal-group", + "currency-decimal-and-grouping-symbols", + "currency-format-types", "currency-formats", + "currency-fraction-digits-and-rounding-fractions", + "currency-noCurrency", + "currency-pattern-append-iso", + "currency-placeholder-iso", + "currency-placeholder-name", + "currency-placeholder-narrow", + "currency-placeholder-placement", + "currency-placeholder-symbol", + "currency-spacing", + "Currency-Specific_Decimal_and_Grouping_Overrides", + "currency-specific-decimal-and-grouping-overrides", + "currency-symbol-placeholders", + "currency-unit-pattern-formatting", "Default_Numbering_System", "default-numbering-system", "defaultNumberingSystem", @@ -24,6 +49,7 @@ "explicit-plus-signs", "formatting", "Formatting", + "formatting-currency-display-names-unitpattern", "Language_Plural_Rules", "language-plural-rules", "limitations", @@ -86,6 +112,7 @@ "RBNF_Remove_Ruleset_Rule", "RBNF_Syntax", "RBNF_Syntax_Rule_Set", + "regional-legal-tender-currency-mappings-region", "relations", "Relations", "Relations_Examples", @@ -114,12 +141,16 @@ "special-pattern-characters", "spelloutrules", "SpelloutRules", + "Standard_and_Accounting_Currency_Format_Types", + "standard-and-accounting-currency-format-types", "status", "Status", "summary", "Summary", "Supplemental_Currency_Data", "supplemental-currency-data", + "supplemental-currency-fractions", + "supplemental-currency-regions", "Syntax_of_rbnfRules", "syntax-of-rbnfrules", "table-number-pattern-character-definitions", @@ -131,6 +162,8 @@ "table-relations-examples", "table-sample-patterns-and-results", "table-significant-digits-examples", + "the-altalphanexttonumber-pattern-variant", + "the-altnocurrency-pattern-variant", "Using_cardinals", "Using_Cardinals", "using-cardinals" diff --git a/docs/ldml/tr35-numbers.md b/docs/ldml/tr35-numbers.md index 3b1fbdf5169..1e3706d2b31 100644 --- a/docs/ldml/tr35-numbers.md +++ b/docs/ldml/tr35-numbers.md @@ -52,9 +52,15 @@ The LDML specification is divided into the following parts: * [Default Numbering System](#defaultNumberingSystem) * [Other Numbering Systems](#otherNumberingSystems) * [Number Symbols](#Number_Symbols) + * [Currency Decimal and Grouping Symbols](#Currency_Symbols_Decimal_Group) * [Number Formats](#Number_Formats) * [Compact Number Formats](#Compact_Number_Formats) + * [Compact Currency Formatting](#Compact_Currency_Formatting) * [Currency Formats](#Currency_Formats) + * [Standard and Accounting Currency Format Types](#currency-format-types) + * [The `alt="alphaNextToNumber"` Pattern Variant](#currency-alphaNextToNumber) + * [The `alt="noCurrency"` Pattern Variant](#currency-noCurrency) + * [Combining Currency Symbols and ISO Codes (`currencyPatternAppendISO`)](#currency-pattern-append-iso) * [Miscellaneous Patterns](#Miscellaneous_Patterns) * [Minimal Pairs](#Minimal_Pairs) * [Number Format Patterns](#Number_Format_Patterns) @@ -62,7 +68,8 @@ The LDML specification is divided into the following parts: * Table: [Number Pattern Examples](#Number_Pattern_Examples) * [Special Pattern Characters](#Special_Pattern_Characters) * Table: [Number Pattern Character Definitions](#Number_Pattern_Character_Definitions) - * Table: [Sample Patterns and Results](#Sample_Patterns_and_Results) + * [Currency Placeholder Placement](#Currency_Placeholder_Placement) + * Table: [Sample Patterns and Results](#Sample_Patterns_and_Results) * [Explicit Plus Signs](#Explicit_Plus) * [Formatting](#Formatting) * [Scientific Notation](#sci) @@ -73,7 +80,12 @@ The LDML specification is divided into the following parts: * [Quoting Rules](#Quoting_Rules) * [Rational Numbers](#rational-numbers) * [Currencies](#Currencies) + * [Formatting Currency Display Names (`unitPattern`)](#currency-unit-pattern-formatting) + * [Currency Boundary Spacing (`currencySpacing`)](#currency-spacing) + * [Currency-Specific Decimal and Grouping Overrides](#currency-custom-decimal-group) * [Supplemental Currency Data](#Supplemental_Currency_Data) + * [Currency Fraction Digits and Rounding (`fractions`)](#supplemental-currency-fractions) + * [Regional Legal Tender Currency Mappings (`region`)](#supplemental-currency-regions) * [Language Plural Rules](#Language_Plural_Rules) * [Explicit 0 and 1 rules](#Explicit_0_1_rules) * [Plural rules syntax](#Plural_rules_syntax) @@ -256,6 +268,8 @@ The available number symbols are as follows: > The NaN sign. Corresponds to the IEEE NaN bit pattern. +##### Currency Decimal and Grouping Symbols + **currencyDecimal** > Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator; otherwise, the regular decimal separator is used. @@ -429,6 +443,8 @@ A pattern `type` attribute is used for _compact number formats_, such as the fol Formats can be supplied for numbers (as above) or for currencies or other units. They can also be used with ranges of numbers, resulting in formatting strings like “$10K” or “$3–7M”. +##### Compact Currency Formatting + To format a number N, use the following steps: Notes: @@ -443,7 +459,7 @@ For example, each of the following are are _letter grapheme clusters_: \, \¤00K`, use `¤#,##0.00`. -4. If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value. +4. If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value. If ¤ is immediately to the left of a 0 and the currency string ends with a _letter grapheme cluster_ (eg, "$CA"), or to the right and the currency starts with a letter (eg, "CA$"), then switch to the `alt=alphaNextToNumber` pattern, if there is one. @@ -506,6 +522,8 @@ The following additional elements were intended to allow proper placement of the ``` +##### Standard and Accounting Currency Format Types + In addition to a standard currency format, in which negative currency amounts might typically be displayed as something like “-$3.27”, locales may provide an "accounting" form, in which for "en_US" the same example would appear as “($3.27)”. The locale keyword "cf" can be used to select the standard or accounting form, see [Unicode Currency Format Identifier](tr35.md#UnicodeCurrencyFormatIdentifier). ```xml @@ -536,10 +554,16 @@ In addition to a standard currency format, in which negative currency amounts mi ``` +##### The `alt="alphaNextToNumber"` Pattern Variant + The `alt="alphaNextToNumber"` pattern, if available, should be used instead of the standard pattern when the currency symbol character closest to the numeric value has Unicode General Category L (letter). The `alt="alphaNextToNumber"` pattern is typically provided when the standard currency pattern does not have a space between currency symbol and numeric value; the alphaNextToNumber variant adds a non-breaking space if appropriate for the locale. +##### The `alt="noCurrency"` Pattern Variant + The `alt="noCurrency"` pattern can be used when a currency-style format is desired but without the currency symbol. This sort of display may be used when formatting a large column of values all in the same currency, for example. For compact currency formats (``), the compact decimal format (``) should be used if no `alt="noCurrency"` pattern is present (so the `alt="noCurrency"` pattern is typically not needed for compact currency formats). +##### Combining Currency Symbols and ISO Codes (`currencyPatternAppendISO`) + ```xml {0} ¤¤ ``` @@ -682,7 +706,7 @@ Invalid sequences of special characters (such as “¤¤¤¤¤¤” in current C | % | Prefix or suffix | percentSign | Multiply by 100 and show as percentage | | ‰ (U+2030) | Prefix or suffix | perMille | Multiply by 1000 and show as per mille (aka “basis points”) | | ; | Subpattern boundary | _syntax_ | Separates positive and negative subpatterns. When there is no explicit negative subpattern, an implicit negative subpattern is formed from the positive pattern with a prefixed - (ASCII U+002D HYPHEN-MINUS). | -| ¤ (U+00A4) | Prefix or suffix | _currency symbol/name from currency specified in API_ | Any sequence is replaced by the localized currency symbol for the currency being formatted, as in the table below. If present in a pattern, the monetary decimal separator and grouping separators (if available) are used instead of the numeric ones. If data is unavailable for a given sequence in a given locale, the display may fall back to ¤ or ¤¤. See also the formatting for currency display names, steps 2 and 4 in [Currencies](#Currencies).
No.Replacement / Example
¤Standard currency symbol
_C$12.00_
¤¤ISO currency symbol (constant)
_CAD 12.00_
¤¤¤Appropriate currency display name for the currency, based on the plural rules in effect for the locale
_5.00 Canadian dollars_
¤¤¤¤¤Narrow currency symbol. The same symbols may be used for multiple currencies. Thus the symbol may be ambiguous, and should only be where the context is clear.
_$12.00_
_others__Invalid in current CLDR. Reserved for future specification_
| +| ¤ (U+00A4) | Prefix or suffix | _currency symbol/name from currency specified in API_ | Any sequence is replaced by the localized currency symbol for the currency being formatted, as in the table below. If present in a pattern, the monetary decimal separator and grouping separators (if available) are used instead of the numeric ones. If data is unavailable for a given sequence in a given locale, the display may fall back to ¤ or ¤¤. See also the formatting for currency display names, steps 2 and 4 in [Currencies](#Currencies).
No.Replacement / Example
¤Standard currency symbol
_C$12.00_
¤¤ISO currency symbol (constant)
_CAD 12.00_
¤¤¤Appropriate currency display name for the currency, based on the plural rules in effect for the locale
_5.00 Canadian dollars_
¤¤¤¤¤Narrow currency symbol. The same symbols may be used for multiple currencies. Thus the symbol may be ambiguous, and should only be where the context is clear.
_$12.00_
_others__Invalid in current CLDR. Reserved for future specification_
| | * | Prefix or suffix boundary | _padding character specified in API_ | Pad escape, precedes pad character | | ' | Prefix or suffix | _syntax-only_ | Used to quote special characters in a prefix or suffix, for example, `"'#'#"` formats 123 to `"#123"`. To create a single quote itself, use two in a row: `"# o''clock"`. | @@ -694,6 +718,8 @@ If there is an explicit negative subpattern, it serves only to specify the negat > **Note:** The thousands separator and decimal separator in patterns are always ASCII ',' and '.'. They are substituted by the code with the correct local values according to other fields in CLDR. The same is true of the - (ASCII minus sign) and other special characters listed above. +##### Currency Placeholder Placement + A currency decimal pattern normally contains a currency symbol placeholder (¤, ¤¤, ¤¤¤, or ¤¤¤¤¤). The currency symbol placeholder may occur before the first digit, after the last digit symbol, or where the decimal symbol would otherwise be placed (for formats such as "12€50", as in "12€50 pour une omelette"). | Placement | Examples | @@ -940,6 +966,8 @@ In environments where the rendering system and font can't be trusted to handle U In formatting currencies, the currency number format is used with the appropriate symbol from ``, according to the currency code. The `` list can contain codes that are no longer in current use, such as PTE. The `choice` attribute has been deprecated. +### Formatting Currency Display Names (`unitPattern`) + The `count` attribute distinguishes the different plural forms, such as in the following: ```xml @@ -985,6 +1013,8 @@ For example, if the currency is ZWD and the number is 1234, then the latter maps --- +### Currency Boundary Spacing (`currencySpacing`) + When a currency symbol is substitited into a pattern, some spacing adjustments or other adjustments may be necessary depending on the nature of the symbol. In CLDR 42 and later, the preferred way to handle this is via the `alt="alphaNextToNumber"` variant of the `currencyFormat` `pattern`, as described in _[Section 2.4.2: Currency Formats](#Currency_Formats)_. In earlier versions of CLDR this was handled via the `currencySpacing` element as described below. This element is still present in CLDR 42 and its use is described below for implementations that may not yet support the `alt="alphaNextToNumber"` variant of the `currencyFormat` `pattern`. ```xml @@ -1010,6 +1040,8 @@ For more information on the matching used in the `currencyMatch` and `surroundin --- +### Currency-Specific Decimal and Grouping Overrides + Currencies can also contain optional grouping, decimal data, and pattern elements. This data is inherited from the `` in the same locale data (if not present in the chain up to root), so only the _differing_ data will be present. See the main document _[Multiple Inheritance](tr35.md#Multiple_Inheritance)_. > **Note:** _Currency values should **never** be interchanged without a known currency code. You never want the number 3.5 interpreted as $3.50 by one user and €3.50 by another._ Locale data contains localization information for currencies, not a currency value for a country. A currency amount logically consists of a numeric value, plus an accompanying currency code (or equivalent). The currency code may be implicit in a protocol, such as where USD is implicit. But if the raw numeric value is transmitted without any context, then it has no definitive interpretation. @@ -1076,6 +1108,8 @@ Each `currencyData` element contains one `fractions` element followed by one or ``` +#### Currency Fraction Digits and Rounding (`fractions`) + The `fractions` element contains any number of `info` elements, with the following attributes: * **iso4217:** the ISO 4217 code for the currency in question. If a particular currency does not occur in the fractions list, then it is given the defaults listed for the next two attributes. @@ -1095,6 +1129,8 @@ For example, the following line should cause the value 2.006 to be displayed as “2.01”, not “2.00”. +#### Regional Legal Tender Currency Mappings (`region`) + Each `region` element contains one attribute: * **iso3166:** the ISO 3166 code for the region in question. The special value _XXX_ can be used to indicate that the region has no valid currency or that the circumstances are unknown (usually used in conjunction with _before_, as described below). diff --git a/tools/scripts/tr-archive/archive.js b/tools/scripts/tr-archive/archive.js index 56ba05b1a2c..1175f715d32 100644 --- a/tools/scripts/tr-archive/archive.js +++ b/tools/scripts/tr-archive/archive.js @@ -210,7 +210,7 @@ async function renderit(infile, info) { body.appendChild( getScript({ // This invokes anchor.js - code: `anchors.add('${ELEMENTS.join(", ")}');`, + code: `anchors.add('${ELEMENTS.concat(["li[id]", "tr[id]", "p[id]", "dt[id]"]).join(", ")}');`, }) ); body.appendChild(document.createTextNode("\n")); From c6b8e7d97c898d2cc4fdbb7a5aa91285b2644e34 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Thu, 24 Sep 2026 16:48:12 +0000 Subject: [PATCH 02/16] CLDR-19814 Reference sub-ticket CLDR-19814 in tr35-modifications.md --- docs/ldml/tr35-modifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ldml/tr35-modifications.md b/docs/ldml/tr35-modifications.md index df9b6a13ae6..7e54a66ae11 100644 --- a/docs/ldml/tr35-modifications.md +++ b/docs/ldml/tr35-modifications.md @@ -83,7 +83,7 @@ This is a partial document, describing only the changes to the LDML since the pr * [`Currencies`](tr35-numbers.md#Currencies) Revised the `numberFormat` description for currencies. * [`Currency Formats`](tr35-numbers.md#Currency_Formats), [`Currencies`](tr35-numbers.md#Currencies), [`Supplemental Currency Data`](tr35-numbers.md#Supplemental_Currency_Data) Added granular subsection headings and deep-link anchors for currency formatting rules, pattern variants (`alt="alphaNextToNumber"`, `alt="noCurrency"`, `currencyPatternAppendISO`), currency placeholders (`¤`..`¤¤¤¤¤`), `unitPattern` display name formatting, `currencySpacing`, and supplemental currency fractions and region mappings. - + * [`Plural rules syntax`](tr35-numbers.md#plural-rules-syntax) Made it clear that plural rules are evaluated in semantic order (`zero`, then `one`,…). From c5f8c254129457c83d82ff95097271a9800e012a Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Thu, 24 Sep 2026 17:39:25 +0000 Subject: [PATCH 03/16] CLDR-19814 Use h4 headings for direct children of h3 sections in tr35-numbers.md --- docs/ldml/tr35-numbers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ldml/tr35-numbers.md b/docs/ldml/tr35-numbers.md index 1e3706d2b31..5db6c0754f3 100644 --- a/docs/ldml/tr35-numbers.md +++ b/docs/ldml/tr35-numbers.md @@ -268,7 +268,7 @@ The available number symbols are as follows: > The NaN sign. Corresponds to the IEEE NaN bit pattern. -##### Currency Decimal and Grouping Symbols +#### Currency Decimal and Grouping Symbols **currencyDecimal** @@ -718,7 +718,7 @@ If there is an explicit negative subpattern, it serves only to specify the negat > **Note:** The thousands separator and decimal separator in patterns are always ASCII ',' and '.'. They are substituted by the code with the correct local values according to other fields in CLDR. The same is true of the - (ASCII minus sign) and other special characters listed above. -##### Currency Placeholder Placement +#### Currency Placeholder Placement A currency decimal pattern normally contains a currency symbol placeholder (¤, ¤¤, ¤¤¤, or ¤¤¤¤¤). The currency symbol placeholder may occur before the first digit, after the last digit symbol, or where the decimal symbol would otherwise be placed (for formats such as "12€50", as in "12€50 pour une omelette"). From f18139dfa2c0f4156b319674166fee567a5b3868 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Thu, 24 Sep 2026 22:41:38 +0000 Subject: [PATCH 04/16] CLDR-19814 Fix scope of currency subsection headings in tr35-numbers.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace the "Currency Decimal and Grouping Symbols" and "Currency Placeholder Placement" headings with inline anchors (same names), so timeSeparator, the numberSystem text, the sample patterns table, grouping conventions and minimumGroupingDigits are no longer nested under currency headings. - Drop the "Compact Currency Formatting" heading: the steps are the general compact number algorithm. The currency-specific step keeps its compact-currency-alphaNextToNumber anchor. - Add a "Currency Codes and Currency Amounts" heading so the general currency-code notes are not under "Currency-Specific Decimal and Grouping Overrides". - Rename "Regional Legal Tender Currency Mappings" to "Regional Currency Mappings": region also lists tender="false" codes. - Link the currency placeholder row to the new unitPattern section, and put the placeholder ids on the first table cell rather than the row. - Revert the anchors.add selector change in archive.js: the extra link icon made the ¤¤¤¤¤ cell wrap. The ids still work as link targets. - Regenerate tr35-numbers.anchors.json. --- docs/ldml/tr35-numbers.anchors.json | 10 ++++------ docs/ldml/tr35-numbers.md | 24 +++++++++--------------- tools/scripts/tr-archive/archive.js | 2 +- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/docs/ldml/tr35-numbers.anchors.json b/docs/ldml/tr35-numbers.anchors.json index c6a3095d149..b66d42c0fb9 100644 --- a/docs/ldml/tr35-numbers.anchors.json +++ b/docs/ldml/tr35-numbers.anchors.json @@ -5,23 +5,22 @@ "Collapsing_Number_Ranges", "collapsing-number-ranges", "combining-currency-symbols-and-iso-codes-currencypatternappendiso", - "Compact_Currency_Formatting", "Compact_Number_Formats", "compact-currency-alphaNextToNumber", - "compact-currency-formatting", "compact-number-formats", "Contents", "contents-of-part-3-numbers", "currencies", "Currencies", - "Currency_Decimal_and_Grouping_Symbols", + "Currency_Codes_and_Currency_Amounts", "Currency_Formats", "Currency_Placeholder_Placement", "Currency_Symbols_Decimal_Group", "currency-alphaNextToNumber", "currency-boundary-spacing-currencyspacing", + "currency-codes-and-amounts", + "currency-codes-and-currency-amounts", "currency-custom-decimal-group", - "currency-decimal-and-grouping-symbols", "currency-format-types", "currency-formats", "currency-fraction-digits-and-rounding-fractions", @@ -30,7 +29,6 @@ "currency-placeholder-iso", "currency-placeholder-name", "currency-placeholder-narrow", - "currency-placeholder-placement", "currency-placeholder-symbol", "currency-spacing", "Currency-Specific_Decimal_and_Grouping_Overrides", @@ -112,7 +110,7 @@ "RBNF_Remove_Ruleset_Rule", "RBNF_Syntax", "RBNF_Syntax_Rule_Set", - "regional-legal-tender-currency-mappings-region", + "regional-currency-mappings-region", "relations", "Relations", "Relations_Examples", diff --git a/docs/ldml/tr35-numbers.md b/docs/ldml/tr35-numbers.md index 5db6c0754f3..9035df49931 100644 --- a/docs/ldml/tr35-numbers.md +++ b/docs/ldml/tr35-numbers.md @@ -52,10 +52,8 @@ The LDML specification is divided into the following parts: * [Default Numbering System](#defaultNumberingSystem) * [Other Numbering Systems](#otherNumberingSystems) * [Number Symbols](#Number_Symbols) - * [Currency Decimal and Grouping Symbols](#Currency_Symbols_Decimal_Group) * [Number Formats](#Number_Formats) * [Compact Number Formats](#Compact_Number_Formats) - * [Compact Currency Formatting](#Compact_Currency_Formatting) * [Currency Formats](#Currency_Formats) * [Standard and Accounting Currency Format Types](#currency-format-types) * [The `alt="alphaNextToNumber"` Pattern Variant](#currency-alphaNextToNumber) @@ -68,8 +66,7 @@ The LDML specification is divided into the following parts: * Table: [Number Pattern Examples](#Number_Pattern_Examples) * [Special Pattern Characters](#Special_Pattern_Characters) * Table: [Number Pattern Character Definitions](#Number_Pattern_Character_Definitions) - * [Currency Placeholder Placement](#Currency_Placeholder_Placement) - * Table: [Sample Patterns and Results](#Sample_Patterns_and_Results) + * Table: [Sample Patterns and Results](#Sample_Patterns_and_Results) * [Explicit Plus Signs](#Explicit_Plus) * [Formatting](#Formatting) * [Scientific Notation](#sci) @@ -83,9 +80,10 @@ The LDML specification is divided into the following parts: * [Formatting Currency Display Names (`unitPattern`)](#currency-unit-pattern-formatting) * [Currency Boundary Spacing (`currencySpacing`)](#currency-spacing) * [Currency-Specific Decimal and Grouping Overrides](#currency-custom-decimal-group) + * [Currency Codes and Currency Amounts](#currency-codes-and-amounts) * [Supplemental Currency Data](#Supplemental_Currency_Data) * [Currency Fraction Digits and Rounding (`fractions`)](#supplemental-currency-fractions) - * [Regional Legal Tender Currency Mappings (`region`)](#supplemental-currency-regions) + * [Regional Currency Mappings (`region`)](#supplemental-currency-regions) * [Language Plural Rules](#Language_Plural_Rules) * [Explicit 0 and 1 rules](#Explicit_0_1_rules) * [Plural rules syntax](#Plural_rules_syntax) @@ -268,9 +266,7 @@ The available number symbols are as follows: > The NaN sign. Corresponds to the IEEE NaN bit pattern. -#### Currency Decimal and Grouping Symbols - -**currencyDecimal** +**currencyDecimal** > Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator; otherwise, the regular decimal separator is used. @@ -443,8 +439,6 @@ A pattern `type` attribute is used for _compact number formats_, such as the fol Formats can be supplied for numbers (as above) or for currencies or other units. They can also be used with ranges of numbers, resulting in formatting strings like “$10K” or “$3–7M”. -##### Compact Currency Formatting - To format a number N, use the following steps: Notes: @@ -706,7 +700,7 @@ Invalid sequences of special characters (such as “¤¤¤¤¤¤” in current C | % | Prefix or suffix | percentSign | Multiply by 100 and show as percentage | | ‰ (U+2030) | Prefix or suffix | perMille | Multiply by 1000 and show as per mille (aka “basis points”) | | ; | Subpattern boundary | _syntax_ | Separates positive and negative subpatterns. When there is no explicit negative subpattern, an implicit negative subpattern is formed from the positive pattern with a prefixed - (ASCII U+002D HYPHEN-MINUS). | -| ¤ (U+00A4) | Prefix or suffix | _currency symbol/name from currency specified in API_ | Any sequence is replaced by the localized currency symbol for the currency being formatted, as in the table below. If present in a pattern, the monetary decimal separator and grouping separators (if available) are used instead of the numeric ones. If data is unavailable for a given sequence in a given locale, the display may fall back to ¤ or ¤¤. See also the formatting for currency display names, steps 2 and 4 in [Currencies](#Currencies).
No.Replacement / Example
¤Standard currency symbol
_C$12.00_
¤¤ISO currency symbol (constant)
_CAD 12.00_
¤¤¤Appropriate currency display name for the currency, based on the plural rules in effect for the locale
_5.00 Canadian dollars_
¤¤¤¤¤Narrow currency symbol. The same symbols may be used for multiple currencies. Thus the symbol may be ambiguous, and should only be where the context is clear.
_$12.00_
_others__Invalid in current CLDR. Reserved for future specification_
| +| ¤ (U+00A4) | Prefix or suffix | _currency symbol/name from currency specified in API_ | Any sequence is replaced by the localized currency symbol for the currency being formatted, as in the table below. If present in a pattern, the monetary decimal separator and grouping separators (if available) are used instead of the numeric ones. If data is unavailable for a given sequence in a given locale, the display may fall back to ¤ or ¤¤. See also the formatting for currency display names, steps 2 and 4 in [Formatting Currency Display Names](#currency-unit-pattern-formatting).
No.Replacement / Example
¤Standard currency symbol
_C$12.00_
¤¤ISO currency symbol (constant)
_CAD 12.00_
¤¤¤Appropriate currency display name for the currency, based on the plural rules in effect for the locale
_5.00 Canadian dollars_
¤¤¤¤¤Narrow currency symbol. The same symbols may be used for multiple currencies. Thus the symbol may be ambiguous, and should only be where the context is clear.
_$12.00_
_others__Invalid in current CLDR. Reserved for future specification_
| | * | Prefix or suffix boundary | _padding character specified in API_ | Pad escape, precedes pad character | | ' | Prefix or suffix | _syntax-only_ | Used to quote special characters in a prefix or suffix, for example, `"'#'#"` formats 123 to `"#123"`. To create a single quote itself, use two in a row: `"# o''clock"`. | @@ -718,9 +712,7 @@ If there is an explicit negative subpattern, it serves only to specify the negat > **Note:** The thousands separator and decimal separator in patterns are always ASCII ',' and '.'. They are substituted by the code with the correct local values according to other fields in CLDR. The same is true of the - (ASCII minus sign) and other special characters listed above. -#### Currency Placeholder Placement - -A currency decimal pattern normally contains a currency symbol placeholder (¤, ¤¤, ¤¤¤, or ¤¤¤¤¤). The currency symbol placeholder may occur before the first digit, after the last digit symbol, or where the decimal symbol would otherwise be placed (for formats such as "12€50", as in "12€50 pour une omelette"). +A currency decimal pattern normally contains a currency symbol placeholder (¤, ¤¤, ¤¤¤, or ¤¤¤¤¤). The currency symbol placeholder may occur before the first digit, after the last digit symbol, or where the decimal symbol would otherwise be placed (for formats such as "12€50", as in "12€50 pour une omelette"). | Placement | Examples | |-----------|----------------------------------------------------------------------------------| @@ -1044,6 +1036,8 @@ For more information on the matching used in the `currencyMatch` and `surroundin Currencies can also contain optional grouping, decimal data, and pattern elements. This data is inherited from the `` in the same locale data (if not present in the chain up to root), so only the _differing_ data will be present. See the main document _[Multiple Inheritance](tr35.md#Multiple_Inheritance)_. +### Currency Codes and Currency Amounts + > **Note:** _Currency values should **never** be interchanged without a known currency code. You never want the number 3.5 interpreted as $3.50 by one user and €3.50 by another._ Locale data contains localization information for currencies, not a currency value for a country. A currency amount logically consists of a numeric value, plus an accompanying currency code (or equivalent). The currency code may be implicit in a protocol, such as where USD is implicit. But if the raw numeric value is transmitted without any context, then it has no definitive interpretation. Notice that the currency code is completely independent of the end-user's language or locale. For example, BGN is the code for Bulgarian Lev. A currency amount of would be localized for a Bulgarian user into "1 234,56 лв." (using Cyrillic letters). For an English user it would be localized into the string "BGN 1,234.56". The end-user's language is needed for doing this last localization step; but that language is completely orthogonal to the currency code needed in the data. After all, the same English user could be working with dozens of currencies. Notice also that the currency code is also independent of whether currency values are inter-converted, which requires more interesting financial processing: the rate of conversion may depend on a variety of factors. @@ -1129,7 +1123,7 @@ For example, the following line should cause the value 2.006 to be displayed as “2.01”, not “2.00”. -#### Regional Legal Tender Currency Mappings (`region`) +#### Regional Currency Mappings (`region`) Each `region` element contains one attribute: diff --git a/tools/scripts/tr-archive/archive.js b/tools/scripts/tr-archive/archive.js index 1175f715d32..56ba05b1a2c 100644 --- a/tools/scripts/tr-archive/archive.js +++ b/tools/scripts/tr-archive/archive.js @@ -210,7 +210,7 @@ async function renderit(infile, info) { body.appendChild( getScript({ // This invokes anchor.js - code: `anchors.add('${ELEMENTS.concat(["li[id]", "tr[id]", "p[id]", "dt[id]"]).join(", ")}');`, + code: `anchors.add('${ELEMENTS.join(", ")}');`, }) ); body.appendChild(document.createTextNode("\n")); From 7b32bcab05bff7d05b43a92197d048f3881a83f6 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 16:51:32 +0000 Subject: [PATCH 05/16] CLDR-19814 Use plain currency subsection headings and drop explicit anchors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review on #6169: - Restore tr35-numbers.anchors.json; it is generated automatically. - Use plain headings for the 10 new currency subsections, relying on the implicit heading anchors instead of wrappers. - Remove the inline anchors on currencyDecimal, compact step 4, the ¤ row of the pattern character table, and the currency placeholder placement paragraph, plus the ids on the ¤ sub-table cells. - Point the table of contents and the ¤ row link at the implicit anchors (the table of contents matches fix-tocs.js output). --- docs/ldml/tr35-modifications.md | 2 +- docs/ldml/tr35-numbers.anchors.json | 31 ------------------- docs/ldml/tr35-numbers.md | 48 ++++++++++++++--------------- 3 files changed, 25 insertions(+), 56 deletions(-) diff --git a/docs/ldml/tr35-modifications.md b/docs/ldml/tr35-modifications.md index 7e54a66ae11..48114d92ac0 100644 --- a/docs/ldml/tr35-modifications.md +++ b/docs/ldml/tr35-modifications.md @@ -82,7 +82,7 @@ This is a partial document, describing only the changes to the LDML since the pr * [`Currencies`](tr35-numbers.md#Currencies) Revised the `numberFormat` description for currencies. -* [`Currency Formats`](tr35-numbers.md#Currency_Formats), [`Currencies`](tr35-numbers.md#Currencies), [`Supplemental Currency Data`](tr35-numbers.md#Supplemental_Currency_Data) Added granular subsection headings and deep-link anchors for currency formatting rules, pattern variants (`alt="alphaNextToNumber"`, `alt="noCurrency"`, `currencyPatternAppendISO`), currency placeholders (`¤`..`¤¤¤¤¤`), `unitPattern` display name formatting, `currencySpacing`, and supplemental currency fractions and region mappings. +* [`Currency Formats`](tr35-numbers.md#Currency_Formats), [`Currencies`](tr35-numbers.md#Currencies), [`Supplemental Currency Data`](tr35-numbers.md#Supplemental_Currency_Data) Added subsection headings for currency formatting rules, pattern variants (`alt="alphaNextToNumber"`, `alt="noCurrency"`, `currencyPatternAppendISO`), currency placeholders (`¤`..`¤¤¤¤¤`), `unitPattern` display name formatting, `currencySpacing`, and supplemental currency fractions and region mappings. * [`Plural rules syntax`](tr35-numbers.md#plural-rules-syntax) Made it clear that plural rules are evaluated in semantic order (`zero`, then `one`,…). diff --git a/docs/ldml/tr35-numbers.anchors.json b/docs/ldml/tr35-numbers.anchors.json index b66d42c0fb9..e2bc167e740 100644 --- a/docs/ldml/tr35-numbers.anchors.json +++ b/docs/ldml/tr35-numbers.anchors.json @@ -4,37 +4,14 @@ "cardinal", "Collapsing_Number_Ranges", "collapsing-number-ranges", - "combining-currency-symbols-and-iso-codes-currencypatternappendiso", "Compact_Number_Formats", - "compact-currency-alphaNextToNumber", "compact-number-formats", "Contents", "contents-of-part-3-numbers", "currencies", "Currencies", - "Currency_Codes_and_Currency_Amounts", "Currency_Formats", - "Currency_Placeholder_Placement", - "Currency_Symbols_Decimal_Group", - "currency-alphaNextToNumber", - "currency-boundary-spacing-currencyspacing", - "currency-codes-and-amounts", - "currency-codes-and-currency-amounts", - "currency-custom-decimal-group", - "currency-format-types", "currency-formats", - "currency-fraction-digits-and-rounding-fractions", - "currency-noCurrency", - "currency-pattern-append-iso", - "currency-placeholder-iso", - "currency-placeholder-name", - "currency-placeholder-narrow", - "currency-placeholder-symbol", - "currency-spacing", - "Currency-Specific_Decimal_and_Grouping_Overrides", - "currency-specific-decimal-and-grouping-overrides", - "currency-symbol-placeholders", - "currency-unit-pattern-formatting", "Default_Numbering_System", "default-numbering-system", "defaultNumberingSystem", @@ -47,7 +24,6 @@ "explicit-plus-signs", "formatting", "Formatting", - "formatting-currency-display-names-unitpattern", "Language_Plural_Rules", "language-plural-rules", "limitations", @@ -110,7 +86,6 @@ "RBNF_Remove_Ruleset_Rule", "RBNF_Syntax", "RBNF_Syntax_Rule_Set", - "regional-currency-mappings-region", "relations", "Relations", "Relations_Examples", @@ -139,16 +114,12 @@ "special-pattern-characters", "spelloutrules", "SpelloutRules", - "Standard_and_Accounting_Currency_Format_Types", - "standard-and-accounting-currency-format-types", "status", "Status", "summary", "Summary", "Supplemental_Currency_Data", "supplemental-currency-data", - "supplemental-currency-fractions", - "supplemental-currency-regions", "Syntax_of_rbnfRules", "syntax-of-rbnfrules", "table-number-pattern-character-definitions", @@ -160,8 +131,6 @@ "table-relations-examples", "table-sample-patterns-and-results", "table-significant-digits-examples", - "the-altalphanexttonumber-pattern-variant", - "the-altnocurrency-pattern-variant", "Using_cardinals", "Using_Cardinals", "using-cardinals" diff --git a/docs/ldml/tr35-numbers.md b/docs/ldml/tr35-numbers.md index 9035df49931..3cf9ac083ec 100644 --- a/docs/ldml/tr35-numbers.md +++ b/docs/ldml/tr35-numbers.md @@ -55,10 +55,10 @@ The LDML specification is divided into the following parts: * [Number Formats](#Number_Formats) * [Compact Number Formats](#Compact_Number_Formats) * [Currency Formats](#Currency_Formats) - * [Standard and Accounting Currency Format Types](#currency-format-types) - * [The `alt="alphaNextToNumber"` Pattern Variant](#currency-alphaNextToNumber) - * [The `alt="noCurrency"` Pattern Variant](#currency-noCurrency) - * [Combining Currency Symbols and ISO Codes (`currencyPatternAppendISO`)](#currency-pattern-append-iso) + * [Standard and Accounting Currency Format Types](#standard-and-accounting-currency-format-types) + * [The `alt="alphaNextToNumber"` Pattern Variant](#the-altalphanexttonumber-pattern-variant) + * [The `alt="noCurrency"` Pattern Variant](#the-altnocurrency-pattern-variant) + * [Combining Currency Symbols and ISO Codes (`currencyPatternAppendISO`)](#combining-currency-symbols-and-iso-codes-currencypatternappendiso) * [Miscellaneous Patterns](#Miscellaneous_Patterns) * [Minimal Pairs](#Minimal_Pairs) * [Number Format Patterns](#Number_Format_Patterns) @@ -77,13 +77,13 @@ The LDML specification is divided into the following parts: * [Quoting Rules](#Quoting_Rules) * [Rational Numbers](#rational-numbers) * [Currencies](#Currencies) - * [Formatting Currency Display Names (`unitPattern`)](#currency-unit-pattern-formatting) - * [Currency Boundary Spacing (`currencySpacing`)](#currency-spacing) - * [Currency-Specific Decimal and Grouping Overrides](#currency-custom-decimal-group) - * [Currency Codes and Currency Amounts](#currency-codes-and-amounts) + * [Formatting Currency Display Names (`unitPattern`)](#formatting-currency-display-names-unitpattern) + * [Currency Boundary Spacing (`currencySpacing`)](#currency-boundary-spacing-currencyspacing) + * [Currency-Specific Decimal and Grouping Overrides](#currency-specific-decimal-and-grouping-overrides) + * [Currency Codes and Currency Amounts](#currency-codes-and-currency-amounts) * [Supplemental Currency Data](#Supplemental_Currency_Data) - * [Currency Fraction Digits and Rounding (`fractions`)](#supplemental-currency-fractions) - * [Regional Currency Mappings (`region`)](#supplemental-currency-regions) + * [Currency Fraction Digits and Rounding (`fractions`)](#currency-fraction-digits-and-rounding-fractions) + * [Regional Currency Mappings (`region`)](#regional-currency-mappings-region) * [Language Plural Rules](#Language_Plural_Rules) * [Explicit 0 and 1 rules](#Explicit_0_1_rules) * [Plural rules syntax](#Plural_rules_syntax) @@ -266,7 +266,7 @@ The available number symbols are as follows: > The NaN sign. Corresponds to the IEEE NaN bit pattern. -**currencyDecimal** +**currencyDecimal** > Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator; otherwise, the regular decimal separator is used. @@ -453,7 +453,7 @@ For example, each of the following are are _letter grapheme clusters_: \, \¤00K`, use `¤#,##0.00`. -4. If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value. +4. If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value. If ¤ is immediately to the left of a 0 and the currency string ends with a _letter grapheme cluster_ (eg, "$CA"), or to the right and the currency starts with a letter (eg, "CA$"), then switch to the `alt=alphaNextToNumber` pattern, if there is one. @@ -516,7 +516,7 @@ The following additional elements were intended to allow proper placement of the ``` -##### Standard and Accounting Currency Format Types +##### Standard and Accounting Currency Format Types In addition to a standard currency format, in which negative currency amounts might typically be displayed as something like “-$3.27”, locales may provide an "accounting" form, in which for "en_US" the same example would appear as “($3.27)”. The locale keyword "cf" can be used to select the standard or accounting form, see [Unicode Currency Format Identifier](tr35.md#UnicodeCurrencyFormatIdentifier). @@ -548,15 +548,15 @@ In addition to a standard currency format, in which negative currency amounts mi ``` -##### The `alt="alphaNextToNumber"` Pattern Variant +##### The `alt="alphaNextToNumber"` Pattern Variant The `alt="alphaNextToNumber"` pattern, if available, should be used instead of the standard pattern when the currency symbol character closest to the numeric value has Unicode General Category L (letter). The `alt="alphaNextToNumber"` pattern is typically provided when the standard currency pattern does not have a space between currency symbol and numeric value; the alphaNextToNumber variant adds a non-breaking space if appropriate for the locale. -##### The `alt="noCurrency"` Pattern Variant +##### The `alt="noCurrency"` Pattern Variant The `alt="noCurrency"` pattern can be used when a currency-style format is desired but without the currency symbol. This sort of display may be used when formatting a large column of values all in the same currency, for example. For compact currency formats (``), the compact decimal format (``) should be used if no `alt="noCurrency"` pattern is present (so the `alt="noCurrency"` pattern is typically not needed for compact currency formats). -##### Combining Currency Symbols and ISO Codes (`currencyPatternAppendISO`) +##### Combining Currency Symbols and ISO Codes (`currencyPatternAppendISO`) ```xml {0} ¤¤ @@ -700,7 +700,7 @@ Invalid sequences of special characters (such as “¤¤¤¤¤¤” in current C | % | Prefix or suffix | percentSign | Multiply by 100 and show as percentage | | ‰ (U+2030) | Prefix or suffix | perMille | Multiply by 1000 and show as per mille (aka “basis points”) | | ; | Subpattern boundary | _syntax_ | Separates positive and negative subpatterns. When there is no explicit negative subpattern, an implicit negative subpattern is formed from the positive pattern with a prefixed - (ASCII U+002D HYPHEN-MINUS). | -| ¤ (U+00A4) | Prefix or suffix | _currency symbol/name from currency specified in API_ | Any sequence is replaced by the localized currency symbol for the currency being formatted, as in the table below. If present in a pattern, the monetary decimal separator and grouping separators (if available) are used instead of the numeric ones. If data is unavailable for a given sequence in a given locale, the display may fall back to ¤ or ¤¤. See also the formatting for currency display names, steps 2 and 4 in [Formatting Currency Display Names](#currency-unit-pattern-formatting).
No.Replacement / Example
¤Standard currency symbol
_C$12.00_
¤¤ISO currency symbol (constant)
_CAD 12.00_
¤¤¤Appropriate currency display name for the currency, based on the plural rules in effect for the locale
_5.00 Canadian dollars_
¤¤¤¤¤Narrow currency symbol. The same symbols may be used for multiple currencies. Thus the symbol may be ambiguous, and should only be where the context is clear.
_$12.00_
_others__Invalid in current CLDR. Reserved for future specification_
| +| ¤ (U+00A4) | Prefix or suffix | _currency symbol/name from currency specified in API_ | Any sequence is replaced by the localized currency symbol for the currency being formatted, as in the table below. If present in a pattern, the monetary decimal separator and grouping separators (if available) are used instead of the numeric ones. If data is unavailable for a given sequence in a given locale, the display may fall back to ¤ or ¤¤. See also the formatting for currency display names, steps 2 and 4 in [Formatting Currency Display Names](#formatting-currency-display-names-unitpattern).
No.Replacement / Example
¤Standard currency symbol
_C$12.00_
¤¤ISO currency symbol (constant)
_CAD 12.00_
¤¤¤Appropriate currency display name for the currency, based on the plural rules in effect for the locale
_5.00 Canadian dollars_
¤¤¤¤¤Narrow currency symbol. The same symbols may be used for multiple currencies. Thus the symbol may be ambiguous, and should only be where the context is clear.
_$12.00_
_others__Invalid in current CLDR. Reserved for future specification_
| | * | Prefix or suffix boundary | _padding character specified in API_ | Pad escape, precedes pad character | | ' | Prefix or suffix | _syntax-only_ | Used to quote special characters in a prefix or suffix, for example, `"'#'#"` formats 123 to `"#123"`. To create a single quote itself, use two in a row: `"# o''clock"`. | @@ -712,7 +712,7 @@ If there is an explicit negative subpattern, it serves only to specify the negat > **Note:** The thousands separator and decimal separator in patterns are always ASCII ',' and '.'. They are substituted by the code with the correct local values according to other fields in CLDR. The same is true of the - (ASCII minus sign) and other special characters listed above. -A currency decimal pattern normally contains a currency symbol placeholder (¤, ¤¤, ¤¤¤, or ¤¤¤¤¤). The currency symbol placeholder may occur before the first digit, after the last digit symbol, or where the decimal symbol would otherwise be placed (for formats such as "12€50", as in "12€50 pour une omelette"). +A currency decimal pattern normally contains a currency symbol placeholder (¤, ¤¤, ¤¤¤, or ¤¤¤¤¤). The currency symbol placeholder may occur before the first digit, after the last digit symbol, or where the decimal symbol would otherwise be placed (for formats such as "12€50", as in "12€50 pour une omelette"). | Placement | Examples | |-----------|----------------------------------------------------------------------------------| @@ -958,7 +958,7 @@ In environments where the rendering system and font can't be trusted to handle U In formatting currencies, the currency number format is used with the appropriate symbol from ``, according to the currency code. The `` list can contain codes that are no longer in current use, such as PTE. The `choice` attribute has been deprecated. -### Formatting Currency Display Names (`unitPattern`) +### Formatting Currency Display Names (`unitPattern`) The `count` attribute distinguishes the different plural forms, such as in the following: @@ -1005,7 +1005,7 @@ For example, if the currency is ZWD and the number is 1234, then the latter maps --- -### Currency Boundary Spacing (`currencySpacing`) +### Currency Boundary Spacing (`currencySpacing`) When a currency symbol is substitited into a pattern, some spacing adjustments or other adjustments may be necessary depending on the nature of the symbol. In CLDR 42 and later, the preferred way to handle this is via the `alt="alphaNextToNumber"` variant of the `currencyFormat` `pattern`, as described in _[Section 2.4.2: Currency Formats](#Currency_Formats)_. In earlier versions of CLDR this was handled via the `currencySpacing` element as described below. This element is still present in CLDR 42 and its use is described below for implementations that may not yet support the `alt="alphaNextToNumber"` variant of the `currencyFormat` `pattern`. @@ -1032,11 +1032,11 @@ For more information on the matching used in the `currencyMatch` and `surroundin --- -### Currency-Specific Decimal and Grouping Overrides +### Currency-Specific Decimal and Grouping Overrides Currencies can also contain optional grouping, decimal data, and pattern elements. This data is inherited from the `` in the same locale data (if not present in the chain up to root), so only the _differing_ data will be present. See the main document _[Multiple Inheritance](tr35.md#Multiple_Inheritance)_. -### Currency Codes and Currency Amounts +### Currency Codes and Currency Amounts > **Note:** _Currency values should **never** be interchanged without a known currency code. You never want the number 3.5 interpreted as $3.50 by one user and €3.50 by another._ Locale data contains localization information for currencies, not a currency value for a country. A currency amount logically consists of a numeric value, plus an accompanying currency code (or equivalent). The currency code may be implicit in a protocol, such as where USD is implicit. But if the raw numeric value is transmitted without any context, then it has no definitive interpretation. @@ -1102,7 +1102,7 @@ Each `currencyData` element contains one `fractions` element followed by one or ``` -#### Currency Fraction Digits and Rounding (`fractions`) +#### Currency Fraction Digits and Rounding (`fractions`) The `fractions` element contains any number of `info` elements, with the following attributes: @@ -1123,7 +1123,7 @@ For example, the following line should cause the value 2.006 to be displayed as “2.01”, not “2.00”. -#### Regional Currency Mappings (`region`) +#### Regional Currency Mappings (`region`) Each `region` element contains one attribute: From 030589f8d5341a41adecad582b79adfe616236c9 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 17:19:11 +0000 Subject: [PATCH 06/16] =?UTF-8?q?CLDR-19814=20Revert=20the=20link=20change?= =?UTF-8?q?=20in=20the=20=C2=A4=20row=20of=20the=20pattern=20character=20t?= =?UTF-8?q?able?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the ¤ (U+00A4) row identical to main so that this PR only adds subsection headings. The row links to #Currencies as before. --- docs/ldml/tr35-numbers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ldml/tr35-numbers.md b/docs/ldml/tr35-numbers.md index 3cf9ac083ec..613669be09e 100644 --- a/docs/ldml/tr35-numbers.md +++ b/docs/ldml/tr35-numbers.md @@ -700,7 +700,7 @@ Invalid sequences of special characters (such as “¤¤¤¤¤¤” in current C | % | Prefix or suffix | percentSign | Multiply by 100 and show as percentage | | ‰ (U+2030) | Prefix or suffix | perMille | Multiply by 1000 and show as per mille (aka “basis points”) | | ; | Subpattern boundary | _syntax_ | Separates positive and negative subpatterns. When there is no explicit negative subpattern, an implicit negative subpattern is formed from the positive pattern with a prefixed - (ASCII U+002D HYPHEN-MINUS). | -| ¤ (U+00A4) | Prefix or suffix | _currency symbol/name from currency specified in API_ | Any sequence is replaced by the localized currency symbol for the currency being formatted, as in the table below. If present in a pattern, the monetary decimal separator and grouping separators (if available) are used instead of the numeric ones. If data is unavailable for a given sequence in a given locale, the display may fall back to ¤ or ¤¤. See also the formatting for currency display names, steps 2 and 4 in [Formatting Currency Display Names](#formatting-currency-display-names-unitpattern).
No.Replacement / Example
¤Standard currency symbol
_C$12.00_
¤¤ISO currency symbol (constant)
_CAD 12.00_
¤¤¤Appropriate currency display name for the currency, based on the plural rules in effect for the locale
_5.00 Canadian dollars_
¤¤¤¤¤Narrow currency symbol. The same symbols may be used for multiple currencies. Thus the symbol may be ambiguous, and should only be where the context is clear.
_$12.00_
_others__Invalid in current CLDR. Reserved for future specification_
| +| ¤ (U+00A4) | Prefix or suffix | _currency symbol/name from currency specified in API_ | Any sequence is replaced by the localized currency symbol for the currency being formatted, as in the table below. If present in a pattern, the monetary decimal separator and grouping separators (if available) are used instead of the numeric ones. If data is unavailable for a given sequence in a given locale, the display may fall back to ¤ or ¤¤. See also the formatting for currency display names, steps 2 and 4 in [Currencies](#Currencies).
No.Replacement / Example
¤Standard currency symbol
_C$12.00_
¤¤ISO currency symbol (constant)
_CAD 12.00_
¤¤¤Appropriate currency display name for the currency, based on the plural rules in effect for the locale
_5.00 Canadian dollars_
¤¤¤¤¤Narrow currency symbol. The same symbols may be used for multiple currencies. Thus the symbol may be ambiguous, and should only be where the context is clear.
_$12.00_
_others__Invalid in current CLDR. Reserved for future specification_
| | * | Prefix or suffix boundary | _padding character specified in API_ | Pad escape, precedes pad character | | ' | Prefix or suffix | _syntax-only_ | Used to quote special characters in a prefix or suffix, for example, `"'#'#"` formats 123 to `"#123"`. To create a single quote itself, use two in a row: `"# o''clock"`. | From 2997bffaf08aa85dafd7fe56e1a27d1a0a1f25cf Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 17:50:04 +0000 Subject: [PATCH 07/16] CLDR-19814 Use subheadings for the Number Symbols entries Turn the bold entries under Number Symbols (decimal, group, ..., currencyDecimal, currencyGroup, timeSeparator, including the two deprecated ones) into #### subheadings, so each symbol gets a generated anchor such as #currencydecimal instead of needing an explicit . Regenerate the table of contents with fix-tocs.js and note the change in tr35-modifications.md. --- docs/ldml/tr35-modifications.md | 1 + docs/ldml/tr35-numbers.md | 51 ++++++++++++++++++++++----------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/docs/ldml/tr35-modifications.md b/docs/ldml/tr35-modifications.md index 48114d92ac0..2b33f4ba0d2 100644 --- a/docs/ldml/tr35-modifications.md +++ b/docs/ldml/tr35-modifications.md @@ -82,6 +82,7 @@ This is a partial document, describing only the changes to the LDML since the pr * [`Currencies`](tr35-numbers.md#Currencies) Revised the `numberFormat` description for currencies. +* [`Number Symbols`](tr35-numbers.md#Number_Symbols) Added a subsection heading for each number symbol. * [`Currency Formats`](tr35-numbers.md#Currency_Formats), [`Currencies`](tr35-numbers.md#Currencies), [`Supplemental Currency Data`](tr35-numbers.md#Supplemental_Currency_Data) Added subsection headings for currency formatting rules, pattern variants (`alt="alphaNextToNumber"`, `alt="noCurrency"`, `currencyPatternAppendISO`), currency placeholders (`¤`..`¤¤¤¤¤`), `unitPattern` display name formatting, `currencySpacing`, and supplemental currency fractions and region mappings. * [`Plural rules syntax`](tr35-numbers.md#plural-rules-syntax) Made it clear that plural rules are evaluated in semantic order (`zero`, then `one`,…). diff --git a/docs/ldml/tr35-numbers.md b/docs/ldml/tr35-numbers.md index 613669be09e..cd764e9fbad 100644 --- a/docs/ldml/tr35-numbers.md +++ b/docs/ldml/tr35-numbers.md @@ -52,6 +52,23 @@ The LDML specification is divided into the following parts: * [Default Numbering System](#defaultNumberingSystem) * [Other Numbering Systems](#otherNumberingSystems) * [Number Symbols](#Number_Symbols) + * [decimal](#decimal) + * [group](#group) + * [list](#list) + * [percentSign](#percentsign) + * [~~nativeZeroDigit~~](#nativezerodigit) + * [~~patternDigit~~](#patterndigit) + * [minusSign](#minussign) + * [plusSign](#plussign) + * [approximatelySign](#approximatelysign) + * [exponential](#exponential) + * [superscriptingExponent](#superscriptingexponent) + * [perMille](#permille) + * [infinity](#infinity) + * [nan - Not a number](#nan---not-a-number) + * [currencyDecimal](#currencydecimal) + * [currencyGroup](#currencygroup) + * [timeSeparator](#timeseparator) * [Number Formats](#Number_Formats) * [Compact Number Formats](#Compact_Number_Formats) * [Currency Formats](#Currency_Formats) @@ -210,71 +227,71 @@ Number symbols define the localized symbols that are commonly used when formatti The available number symbols are as follows: -**decimal** +#### decimal > separates the integer and fractional part of the number. -**group** +#### group > separates clusters of integer digits to make large numbers more legible; commonly used for thousands (grouping size 3, e.g. "100,000,000") or in some locales, ten-thousands (grouping size 4, e.g. "1,0000,0000"). There may be two different grouping sizes: The _primary grouping size_ used for the least significant integer group, and the _secondary grouping size_ used for more significant groups; these are not the same in all locales (e.g. "12,34,56,789"). If a pattern contains multiple grouping separators, the interval between the last one and the end of the integer defines the primary grouping size, and the interval between the last two defines the secondary grouping size. All others are ignored, so "#,##,###,####" == "###,###,####" == "##,#,###,####". -**list** +#### list > symbol used to separate numbers in a list intended to represent structured data such as an array; must be different from the **decimal** value. This list separator is for “non-linguistic” usage as opposed to the listPatterns for “linguistic” lists (e.g. “Bob, Carol, and Ted”) described in Part 2, _[List Patterns](tr35-general.md#ListPatterns)_. -**percentSign** +#### percentSign > symbol used to indicate a percentage (1/100th) amount. (If present, the value is also multiplied by 100 before formatting. That way 1.23 → 123%) -~~**nativeZeroDigit**~~ +#### ~~nativeZeroDigit~~ > Deprecated - do not use. -~~**patternDigit**~~ +#### ~~patternDigit~~ > Deprecated. This was formerly used to provide the localized pattern character corresponding to '#', but localization of the pattern characters themselves has been deprecated for some time (determining the locale-specific _replacements_ for pattern characters is of course not deprecated and is part of normal number formatting). -**minusSign** +#### minusSign > Symbol used to denote negative value. -**plusSign** +#### plusSign > Symbol used to denote positive value. It can be used to produce modified patterns, so that 3.12 is formatted as "+3.12", for example. The standard number patterns (except for type="accounting") will contain the minusSign, explicitly or implicitly. In the explicit pattern, the value of the plusSign can be substituted for the value of the minusSign to produce a pattern that has an explicit plus sign. -**approximatelySign** +#### approximatelySign > Symbol used to denote a value that is approximate but not exact. The symbol is substituted in place of the minusSign using the same semantics as plusSign substitution. -**exponential** +#### exponential > Symbol separating the mantissa and exponent values. -**superscriptingExponent** +#### superscriptingExponent > (Programmers are used to the fallback exponent style “1.23E4”, but that should not be shown to end-users. Instead, the exponential notation superscriptingExponent should be used to show a format like “1.23 × 104”. ) The superscripting can use markup, such as `4` in HTML, or for the special case of Latin digits, use the superscript characters: U+207B ( ⁻ ), U+2070 ( ⁰ ), U+00B9 ( ¹ ), U+00B2 ( ² ), U+00B3 ( ³ ), U+2074 ( ⁴ ) .. U+2079 ( ⁹ ). -**perMille** +#### perMille > symbol used to indicate a per-mille (1/1000th) amount. (If present, the value is also multiplied by 1000 before formatting. That way 1.23 → 1230 [1/000]) -**infinity** +#### infinity > The infinity sign. Corresponds to the IEEE infinity bit pattern. -**nan - Not a number** +#### nan - Not a number > The NaN sign. Corresponds to the IEEE NaN bit pattern. -**currencyDecimal** +#### currencyDecimal > Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator; otherwise, the regular decimal separator is used. -**currencyGroup** +#### currencyGroup > Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator; otherwise, the regular group separator is used. -**timeSeparator** +#### timeSeparator > This replaces any use of the timeSeparator pattern character in a date-time format pattern (no timeSeparator pattern character is currently defined, see note below). This allows the same time format to be used for multiple number systems when the time separator depends on the number system. For example, the time format for Arabic should be COLON when using the Latin numbering system (0, 1, 2, …), but when the Arabic numbering system is used (٠‎ - ١‎ - ٢‎ …), the traditional time separator in older print styles was often ARABIC COMMA. > From 9122c749db081f6cdfb7130ec7c752f22e407f61 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 18:54:12 +0000 Subject: [PATCH 08/16] CLDR-19466 Add TR35 #Currency_Symbols_Decimal_Group test coverage verification doc Adds Section 1 (#Currency_Symbols_Decimal_Group: currencyDecimal and currencyGroup) to common/testData/currency/tr35_currency_test_coverage.md, mapping each normative sentence to required (dimension = value) combinations and identifying the missing fr_CH and de_AT CORE_LOCALES entries. --- .../currency/tr35_currency_test_coverage.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 common/testData/currency/tr35_currency_test_coverage.md diff --git a/common/testData/currency/tr35_currency_test_coverage.md b/common/testData/currency/tr35_currency_test_coverage.md new file mode 100644 index 00000000000..0c6887ac37d --- /dev/null +++ b/common/testData/currency/tr35_currency_test_coverage.md @@ -0,0 +1,60 @@ +# UTS #35 Currency Formatting Specification — Test Data Coverage Verification + +This document provides a reproducible, sentence-by-sentence verification of test data coverage for the currency formatting specification in **UTS #35: Unicode LDML, Part 3: Numbers (`docs/ldml/tr35-numbers.md`)**. + +For each deep-linked currency section in UTS #35: +1. We link directly to the normative specification anchor in `tr35-numbers.html`. +2. We quote the exact normative snippet from `docs/ldml/tr35-numbers.md`. +3. We break down every normative sentence/clause in the snippet into the exact **`(dimension = value)` combinations** required to exercise that behavior. +4. We compare the required `(dimension = value)` combinations against `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) to verify whether each combination is already covered in `CORE` or needs to be added. + +--- + +## Test Data Dimensions Reference + +| Dimension | Description | Allowed / Representative Values | +| :--- | :--- | :--- | +| **`locale`** | CLDR locale identifier | `CORE_LOCALES` (`ar`, `ar_EG`, `bn`, `de`, `de_CH`, `en`, `fy`, `ja`, `pt_PT`, `ru`) + extended modern locales | +| **`currency`** | ISO 4217 3-letter currency code | `CORE_CURRENCIES` (`USD`, `EUR`, `JPY`, `RUB`, `EGP`) + extended modern currencies | +| **`currency_format_length`** | `` width | `""` (standard plain decimal) or `"short"` (compact short) | +| **`currency_format_type`** | `` style | `"standard"` or `"accounting"` | +| **`currency_display`** | Currency symbol/unit representation | `"symbol"`, `"symbolNarrow"`, `"code"`, `"name"`, `"noCurrency"` | +| **`input`** | Numeric currency amount | `CORE_NUMBERS` (`0.0`, `1.2`, `0.00831765`, `1234565.0`, `-1230.05`) + extended numbers | + +--- + +## Section 1: Monetary Decimal and Grouping Symbols (`#Currency_Symbols_Decimal_Group`) + +* **TR35 Specification Link**: [`tr35-numbers.html#Currency_Symbols_Decimal_Group`](https://www.unicode.org/reports/tr35/tr35-numbers.html#Currency_Symbols_Decimal_Group) (UTS #35 Part 3, Section 2.3: *Number Symbols*) + +### 1.1 Verbatim Specification Snippet (`docs/ldml/tr35-numbers.md`) + +> **currencyDecimal** +> +> > Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator; otherwise, the regular decimal separator is used. +> +> **currencyGroup** +> +> > Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator; otherwise, the regular group separator is used. + +--- + +### 1.2 Sentence-by-Sentence `(Dimension / Value)` Coverage Breakdown + +| # | Verbatim Sentence / Normative Clause | Required `(Dimension = Value)` Combinations to Cover Clause | CLDR Data Evidence & Expected Behavior | +| :---: | :--- | :--- | :--- | +| **S1.1** | **`currencyDecimal`** — *"Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator;"* | • `locale = "fr_CH"` *(defines `.` vs. `,` inherited from `fr`)*
• `currency = "EUR"` or `"USD"` *(currency with fraction digits $> 0$)*
• `currency_format_length = ""`
• `currency_display = "symbol" \| "symbolNarrow" \| "code"`
• `input = 1.2` or `-1230.05` *(non-zero fractional part)* | In all of CLDR `common/main/*.xml`, **`fr_CH`** is the **only** locale that defines ``. Standard decimal formatting in `fr_CH` uses `,` (`-1 230,05` / `"-1\u202F230,05"`), whereas currency formatting uses `.` (`-1 230.05 €` / `"-1\u202F230.05\u00A0€"`). | +| **S1.2** | **`currencyDecimal`** — *"...otherwise, the regular decimal separator is used."* | • `locale = "en"` *(uses regular `.`)*
• `locale = "de"` *(uses regular `,`)*
• `locale = "ar_EG"` *(uses regular `٫` `\u066B`)*
• `currency = "USD" \| "EUR" \| "EGP"`
• `currency_format_length = ""`
• `input = 1.2`, `-1230.05` | None of `en`, `de`, or `ar_EG` specifies ``, so currency formatting falls back to the regular `` symbol (`.` in `en`, `,` in `de`, `٫` in `ar_EG`). | +| **S1.3** | **`currencyGroup`** — *"Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator;"* | • `locale = "de_AT"` *(defines `.` vs. ` ` `\u00A0`)*
• `currency = "EUR"` or `"USD"`
• `currency_format_length = ""`
• `currency_display = "symbol" \| "symbolNarrow" \| "code"`
• `input = -1230.05` or `1234565.0` *(magnitude $\ge 10^3$ to trigger grouping)* | In all of CLDR `common/main/*.xml`, **`de_AT`** is the **only** locale that defines ``. Standard decimal formatting in `de_AT` uses NBSP ` ` (`1 234 565,00` / `"1\u00A0234\u00A0565,00"`), whereas currency formatting uses `.` (`€ 1.234.565,00` / `"€\u00A01.234.565,00"`). | +| **S1.4** | **`currencyGroup`** — *"...otherwise, the regular group separator is used."* | • `locale = "en"` *(regular `,`)*
• `locale = "de"` *(regular `.`)*
• `locale = "de_CH"` *(regular `'`)*
• `locale = "bn"` *(regular `,` with Indian `#,##,##0.00` secondary grouping)*
• `currency_format_length = ""`
• `input = -1230.05`, `1234565.0` | None of `en`, `de`, `de_CH`, or `bn` specifies ``, so currency formatting falls back to the regular `` separator (`,`, `.`, `'`). | + +--- + +### 1.3 Comparison Against `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) + +| Clause | Required `(Dimension = Value)` Combination | Present in PR #5808 (`CORE`)? | Generator Audit & Action Required | +| :---: | :--- | :---: | :--- | +| **S1.1** | `locale = "fr_CH"` × `currency = "EUR" \| "USD"` × `input = 1.2, -1230.05` | **Missing** | `fr_CH` is not in `CORE_LOCALES`, and `StandardCodes` (`Level.MODERN`) only includes base locale `fr` in `currencies_modern_locales.tsv`. **Action**: Add `"fr_CH"` to `Dimensions.CORE_LOCALES`. | +| **S1.2** | `locale = "en" \| "de" \| "ar_EG"` × `currency = "USD" \| "EUR" \| "EGP"` × `input = 1.2, -1230.05` | **Covered** | Already present in `Dimensions.CORE_LOCALES` × `CORE_CURRENCIES` × `CORE_NUMBERS` (`currencies.tsv`). | +| **S1.3** | `locale = "de_AT"` × `currency = "EUR" \| "USD"` × `input = -1230.05, 1234565.0` | **Missing** | `de_AT` is not in `CORE_LOCALES`, and `StandardCodes` (`Level.MODERN`) only includes base locale `de` in `currencies_modern_locales.tsv`. **Action**: Add `"de_AT"` to `Dimensions.CORE_LOCALES`. | +| **S1.4** | `locale = "en" \| "de" \| "de_CH" \| "bn"` × `input = -1230.05, 1234565.0` | **Covered** | Already present in `Dimensions.CORE_LOCALES` × `CORE_CURRENCIES` × `CORE_NUMBERS` (`currencies.tsv`). | From 82977fea39956e4a06ffc93e8d2a44572e7a9e37 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 18:54:12 +0000 Subject: [PATCH 09/16] CLDR-19466 Apply review fixes to #Currency_Symbols_Decimal_Group coverage section - Correct the fr_CH evidence (fr_CH has its own group ' since CLDR-13986) and the de_AT plain-decimal example. - Require a currency with fraction digits > 0 instead of an input with a fractional part. - Cover all 12 generator styles through style classes A-D, and add a compact input (12345.0) for de_AT. - Mark display-name and noCurrency cases as spec unclear (CLDR-19825), and parsing as out of scope. - Add pinned inputs, a coverage status legend, witness rows, repo-relative spec links, and a summary of generator changes. --- .../currency/tr35_currency_test_coverage.md | 113 +++++++++++++++--- 1 file changed, 96 insertions(+), 17 deletions(-) diff --git a/common/testData/currency/tr35_currency_test_coverage.md b/common/testData/currency/tr35_currency_test_coverage.md index 0c6887ac37d..149380a9e29 100644 --- a/common/testData/currency/tr35_currency_test_coverage.md +++ b/common/testData/currency/tr35_currency_test_coverage.md @@ -1,12 +1,37 @@ # UTS #35 Currency Formatting Specification — Test Data Coverage Verification -This document provides a reproducible, sentence-by-sentence verification of test data coverage for the currency formatting specification in **UTS #35: Unicode LDML, Part 3: Numbers (`docs/ldml/tr35-numbers.md`)**. +This document provides a reproducible, sentence-by-sentence verification of test data coverage for the currency formatting specification in **UTS #35: Unicode LDML, Part 3: Numbers** ([`docs/ldml/tr35-numbers.md`](../../../docs/ldml/tr35-numbers.md)). For each deep-linked currency section in UTS #35: -1. We link directly to the normative specification anchor in `tr35-numbers.html`. -2. We quote the exact normative snippet from `docs/ldml/tr35-numbers.md`. -3. We break down every normative sentence/clause in the snippet into the exact **`(dimension = value)` combinations** required to exercise that behavior. -4. We compare the required `(dimension = value)` combinations against `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) to verify whether each combination is already covered in `CORE` or needs to be added. +1. We link directly to the specification anchor in [`docs/ldml/tr35-numbers.md`](../../../docs/ldml/tr35-numbers.md), with its line range at the pinned commit. +2. We quote the exact normative snippet. +3. We break down every normative sentence/clause in the snippet into the exact **`(dimension = value)` combinations** required to exercise that behavior, with the expected result derived from the CLDR data. +4. We compare the required `(dimension = value)` combinations against the test data generated by `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)), citing a witness row from the TSV files or recording what is missing. + +### Pinned Inputs + +| Input | Version | +| :--- | :--- | +| Specification text (`docs/ldml/tr35-numbers.md`) | `f18139dfa2` (PR [#6169](https://github.com/unicode-org/cldr/pull/6169)) | +| CLDR data (`common/main`, `common/supplemental`) | `f18139dfa2` | +| Generator and TSV files (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) | `604c2bb2ac` | +| ICU4J that produced the TSV `expected` values | `79.0.1-20260318.053439-2` | + +Expected values in the breakdown tables are derived from the CLDR data and the specification, and cross-checked with ICU4J `78.3`. + +> [!NOTE] +> PR #5808 is being rebased onto the ICU4J version pinned on `main` (`79.0.1-20260723.162400-4`), and its TSV files will be regenerated. The pinned inputs and witness rows will be updated then. + +### Coverage Status + +| Status | Meaning | +| :--- | :--- | +| ✅ **Covered** | A witness row exists in the TSV files, and its `expected` value matches the specification | +| 🟡 **Missing: dimension** | No row exists; the generator needs a new dimension value (for example, a locale) | +| 🟡 **Missing: input** | No row exists; the generator needs a new input number | +| 🔴 **Non-conformant** | A row exists, but its `expected` value contradicts the specification | +| ❓ **Spec unclear** | The specification does not determine the expected value; see the linked ticket | +| ⚪ **Out of scope** | Not testable with formatting test data | --- @@ -16,16 +41,28 @@ For each deep-linked currency section in UTS #35: | :--- | :--- | :--- | | **`locale`** | CLDR locale identifier | `CORE_LOCALES` (`ar`, `ar_EG`, `bn`, `de`, `de_CH`, `en`, `fy`, `ja`, `pt_PT`, `ru`) + extended modern locales | | **`currency`** | ISO 4217 3-letter currency code | `CORE_CURRENCIES` (`USD`, `EUR`, `JPY`, `RUB`, `EGP`) + extended modern currencies | -| **`currency_format_length`** | `` width | `""` (standard plain decimal) or `"short"` (compact short) | +| **`currency_format_length`** | `` type | `""` (default, non-compact) or `"short"` (compact short) | | **`currency_format_type`** | `` style | `"standard"` or `"accounting"` | -| **`currency_display`** | Currency symbol/unit representation | `"symbol"`, `"symbolNarrow"`, `"code"`, `"name"`, `"noCurrency"` | +| **`currency_display`** | How the currency is shown | `"symbol"`, `"symbolNarrow"`, `"code"`, `"name"`, `"noCurrency"` (generated with ICU4J `UnitWidth.HIDDEN`) | | **`input`** | Numeric currency amount | `CORE_NUMBERS` (`0.0`, `1.2`, `0.00831765`, `1234565.0`, `-1230.05`) + extended numbers | +### Style Classes + +The generator produces 12 styles (valid combinations of `currency_format_length`, `currency_format_type`, and `currency_display`). The specification selects patterns differently for 4 classes of styles: + +| Class | `currency_format_length` | `currency_format_type` | `currency_display` | Pattern used | +| :---: | :--- | :--- | :--- | :--- | +| **A** | `""` | `standard`, `accounting` | `symbol`, `symbolNarrow`, `code` | `currencyFormat` pattern containing `¤` | +| **B** | `short` | `standard` | `symbol`, `symbolNarrow`, `code` | compact `currencyFormat` pattern containing `¤` | +| **C** | `""` | `standard` | `name` | `decimalFormat` pattern + `unitPattern` | +| **D** | `""` | `standard`, `accounting` | `noCurrency` | `currencyFormat` `alt="noCurrency"` pattern (no `¤`) | + --- ## Section 1: Monetary Decimal and Grouping Symbols (`#Currency_Symbols_Decimal_Group`) -* **TR35 Specification Link**: [`tr35-numbers.html#Currency_Symbols_Decimal_Group`](https://www.unicode.org/reports/tr35/tr35-numbers.html#Currency_Symbols_Decimal_Group) (UTS #35 Part 3, Section 2.3: *Number Symbols*) +* **TR35 Specification Link**: [`tr35-numbers.md#Currency_Symbols_Decimal_Group`](../../../docs/ldml/tr35-numbers.md#Currency_Symbols_Decimal_Group) (UTS #35 Part 3, Section 2.3: *Number Symbols*; L269–L275 at `f18139dfa2`) +* **Related specification text**: L695–L697 (pattern characters `.` and `,`), L703 ([`¤` row](../../../docs/ldml/tr35-numbers.md#currency-symbol-placeholders)), and L998 ([Formatting Currency Display Names](../../../docs/ldml/tr35-numbers.md#currency-unit-pattern-formatting), step 5) ### 1.1 Verbatim Specification Snippet (`docs/ldml/tr35-numbers.md`) @@ -41,20 +78,62 @@ For each deep-linked currency section in UTS #35: ### 1.2 Sentence-by-Sentence `(Dimension / Value)` Coverage Breakdown +**CLDR data.** `git grep -n -E "]" -- common/main` finds exactly two locales. Neither has child locales or `parentLocales` entries. + +| Locale | Monetary separator | Regular separator | Other relevant data | +| :--- | :--- | :--- | :--- | +| `fr_CH` | `.` | `,` (inherited from `fr`) | Own `'` (U+0027, added by CLDR-13986) | +| `de_AT` | `.` | ` ` / `"\u00A0"` | `,` (inherited from `de`). With approved data only, `group` is inherited as `.` from `de`, and the override cannot be observed. | + +"Currency with digits > 0" means a currency whose `` fraction digits are greater than 0 (for example `EUR` or `USD`, but not `JPY`). The decimal separator appears for such currencies even when the input has no fractional part (`0.0` → `0.00 $US` in `fr_CH`). + | # | Verbatim Sentence / Normative Clause | Required `(Dimension = Value)` Combinations to Cover Clause | CLDR Data Evidence & Expected Behavior | | :---: | :--- | :--- | :--- | -| **S1.1** | **`currencyDecimal`** — *"Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator;"* | • `locale = "fr_CH"` *(defines `.` vs. `,` inherited from `fr`)*
• `currency = "EUR"` or `"USD"` *(currency with fraction digits $> 0$)*
• `currency_format_length = ""`
• `currency_display = "symbol" \| "symbolNarrow" \| "code"`
• `input = 1.2` or `-1230.05` *(non-zero fractional part)* | In all of CLDR `common/main/*.xml`, **`fr_CH`** is the **only** locale that defines ``. Standard decimal formatting in `fr_CH` uses `,` (`-1 230,05` / `"-1\u202F230,05"`), whereas currency formatting uses `.` (`-1 230.05 €` / `"-1\u202F230.05\u00A0€"`). | -| **S1.2** | **`currencyDecimal`** — *"...otherwise, the regular decimal separator is used."* | • `locale = "en"` *(uses regular `.`)*
• `locale = "de"` *(uses regular `,`)*
• `locale = "ar_EG"` *(uses regular `٫` `\u066B`)*
• `currency = "USD" \| "EUR" \| "EGP"`
• `currency_format_length = ""`
• `input = 1.2`, `-1230.05` | None of `en`, `de`, or `ar_EG` specifies ``, so currency formatting falls back to the regular `` symbol (`.` in `en`, `,` in `de`, `٫` in `ar_EG`). | -| **S1.3** | **`currencyGroup`** — *"Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator;"* | • `locale = "de_AT"` *(defines `.` vs. ` ` `\u00A0`)*
• `currency = "EUR"` or `"USD"`
• `currency_format_length = ""`
• `currency_display = "symbol" \| "symbolNarrow" \| "code"`
• `input = -1230.05` or `1234565.0` *(magnitude $\ge 10^3$ to trigger grouping)* | In all of CLDR `common/main/*.xml`, **`de_AT`** is the **only** locale that defines ``. Standard decimal formatting in `de_AT` uses NBSP ` ` (`1 234 565,00` / `"1\u00A0234\u00A0565,00"`), whereas currency formatting uses `.` (`€ 1.234.565,00` / `"€\u00A01.234.565,00"`). | -| **S1.4** | **`currencyGroup`** — *"...otherwise, the regular group separator is used."* | • `locale = "en"` *(regular `,`)*
• `locale = "de"` *(regular `.`)*
• `locale = "de_CH"` *(regular `'`)*
• `locale = "bn"` *(regular `,` with Indian `#,##,##0.00` secondary grouping)*
• `currency_format_length = ""`
• `input = -1230.05`, `1234565.0` | None of `en`, `de`, `de_CH`, or `bn` specifies ``, so currency formatting falls back to the regular `` separator (`,`, `.`, `'`). | +| **S1.1** | **`currencyDecimal`** — *"Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator;"* | • `locale = "fr_CH"`
• `currency` with digits > 0 (e.g. `"EUR"`)
• **S1.1a**: class A, any `input`
• **S1.1b**: class B, `input = 1234565.0`
• **S1.1c**: class C, any `input`
• **S1.1d**: class D, any `input` | Plain decimal in `fr_CH`: `-1'230,05`. Currency (EUR):
• A (−1230.05): `-1'230.05 €` / `"-1'230.05\u00A0€"`
• B (1234565.0): `1.2 M €` / `"1.2\u00A0M\u00A0€"`
• C (−1230.05): `-1'230.05 euros` (L998 step 5 requires `currencyDecimal`)
• D (−1230.05): `-1'230.05` or `-1'230,05` (spec unclear) | +| **S1.2** | **`currencyDecimal`** — *"...otherwise, the regular decimal separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"ar_EG"`)
• `currency` with digits > 0
• class A, any `input` | None of `en`, `de`, or `ar_EG` specifies ``, so currency formatting uses the regular `` symbol (`.` in `en`, `,` in `de`, `٫` U+066B in `ar_EG`). | +| **S1.3** | **`currencyGroup`** — *"Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator;"* | • `locale = "de_AT"`
• **S1.3a**: class A, `input` ≥ 1000 (e.g. `-1230.05`)
• **S1.3b**: class B, 10⁴ ≤ `input` < 10⁶ (e.g. `12345.0`)
• **S1.3c**: class C, `input` ≥ 1000
• **S1.3d**: class D, `input` ≥ 1000 | Plain decimal in `de_AT`: `-1 230,05` / `"-1\u00A0230,05"`. Currency (EUR):
• A (−1230.05): `-€ 1.230,05` / `"-€\u00A01.230,05"`
• B (12345.0): `€ 12.345` / `"€\u00A012.345"`. ICU4J groups only numbers with 5 or more integer digits in compact notation (`-€ 1230` / `"-€\u00A01230"` for −1230.05).
• C (−1230.05): `-1.230,05 Euro` or `-1 230,05 Euro` (spec unclear)
• D (−1230.05): `-1.230,05` or `-1 230,05` (spec unclear) | +| **S1.4** | **`currencyGroup`** — *"...otherwise, the regular group separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"de_CH"`, `"bn"`)
• class A, `input` ≥ 1000 | None of `en`, `de`, `de_CH`, or `bn` specifies ``, so currency formatting uses the regular `` separator (`,` in `en`, `.` in `de`, `'` in `de_CH`, `,` with Indian `#,##,##0.00` grouping in `bn`). | +| **S1.5** | **`currencyDecimal`** and **`currencyGroup`** — *"...for currency formatting/parsing..."* (the parsing half) | — | Not testable: the TSV files contain formatting data only. | --- ### 1.3 Comparison Against `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) -| Clause | Required `(Dimension = Value)` Combination | Present in PR #5808 (`CORE`)? | Generator Audit & Action Required | +| Clause | Required `(Dimension = Value)` Combination | Status | Witness Row / Action Required | | :---: | :--- | :---: | :--- | -| **S1.1** | `locale = "fr_CH"` × `currency = "EUR" \| "USD"` × `input = 1.2, -1230.05` | **Missing** | `fr_CH` is not in `CORE_LOCALES`, and `StandardCodes` (`Level.MODERN`) only includes base locale `fr` in `currencies_modern_locales.tsv`. **Action**: Add `"fr_CH"` to `Dimensions.CORE_LOCALES`. | -| **S1.2** | `locale = "en" \| "de" \| "ar_EG"` × `currency = "USD" \| "EUR" \| "EGP"` × `input = 1.2, -1230.05` | **Covered** | Already present in `Dimensions.CORE_LOCALES` × `CORE_CURRENCIES` × `CORE_NUMBERS` (`currencies.tsv`). | -| **S1.3** | `locale = "de_AT"` × `currency = "EUR" \| "USD"` × `input = -1230.05, 1234565.0` | **Missing** | `de_AT` is not in `CORE_LOCALES`, and `StandardCodes` (`Level.MODERN`) only includes base locale `de` in `currencies_modern_locales.tsv`. **Action**: Add `"de_AT"` to `Dimensions.CORE_LOCALES`. | -| **S1.4** | `locale = "en" \| "de" \| "de_CH" \| "bn"` × `input = -1230.05, 1234565.0` | **Covered** | Already present in `Dimensions.CORE_LOCALES` × `CORE_CURRENCIES` × `CORE_NUMBERS` (`currencies.tsv`). | +| **S1.1a–c** | `locale = "fr_CH"` × currency with digits > 0 × classes A, B, C | 🟡 **Missing: dimension** | None of the 10 TSV files has `fr_CH` rows. `fr_CH` is not in `CORE_LOCALES`, and the modern-locale suite only includes `fr` and `fr_CA`. **Action**: add `"fr_CH"` to the generator's locales. | +| **S1.1d** | `locale = "fr_CH"` × class D | ❓ **Spec unclear** | [CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825) | +| **S1.2** | `locale` without `` × currency with digits > 0 × class A | ✅ **Covered** | W1.2 rows below. | +| **S1.3a** | `locale = "de_AT"` × class A × `input` ≥ 1000 | 🟡 **Missing: dimension** | None of the 10 TSV files has `de_AT` rows. **Action**: add `"de_AT"` to the generator's locales. | +| **S1.3b** | `locale = "de_AT"` × class B × 10⁴ ≤ `input` < 10⁶ | 🟡 **Missing: dimension, input** | No value in `CORE_NUMBERS` is in this range. **Action**: add `"de_AT"` and an input such as `12345.0` (only needed with `de_AT` × `short`). | +| **S1.3c–d** | `locale = "de_AT"` × classes C, D | ❓ **Spec unclear** | [CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825) | +| **S1.4** | `locale` without `` × class A × `input` ≥ 1000 | ✅ **Covered** | W1.4 rows below. | +| **S1.5** | Parsing | ⚪ **Out of scope** | — | + +**Witness rows** from `currencies.tsv` at `604c2bb2ac` (all with `currency_format_length = ""`, `currency_format_type = "standard"`, `currency_display = "symbol"`): + +| Row | `locale` | `currency` | `input` | `expected` | Escaped | +| :---: | :--- | :--- | ---: | :--- | :--- | +| W1.2 | `en` | `USD` | `-1230.05` | `-$1,230.05` | `"-$1,230.05"` | +| W1.2 | `de` | `EUR` | `-1230.05` | `-1.230,05 €` | `"-1.230,05\u00A0€"` | +| W1.2 | `ar_EG` | `EGP` | `-1230.05` | `-١٬٢٣٠٫٠٥ ج.م.`¹ | `"\u061C-\u200F١٬٢٣٠٫٠٥\u00A0ج.م.\u200F"` | +| W1.4 | `de_CH` | `EUR` | `1234565.0` | `EUR 1'234'565.00` | `"EUR\u00A01'234'565.00"` | +| W1.4 | `bn` | `USD` | `1234565.0` | `১২,৩৪,৫৬৫.০০ US$` | `"১২,৩৪,৫৬৫.০০\u00A0US$"` | + +¹ The bidi marks U+061C and U+200F appear only in the escaped form. + +### 1.4 Notes + +* **Spec unclear** ([CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825)): L271/L275 apply the monetary separators to all currency formatting, L703 applies them only when `¤` is in the pattern, and L998 step 5 mentions only `currencyDecimal`. ICU4J applies both overrides in all 12 styles. +* **`noCurrency` in PR #5808**: ICU4J `UnitWidth.HIDDEN` removes `¤` from the standard pattern but keeps the space next to it (for example `-1.230,05 ` / `"-1.230,05\u00A0"` for `de`), instead of using the `alt="noCurrency"` pattern. Section 5 (`#currency-noCurrency`) covers this. +* **Compact grouping**: TR35 L454 (a compact pattern of `"0"` falls back to the non-compact pattern) and L752 (`minimumGroupingDigits` is 1 for `de`) suggest `-€ 1.230` for `de_AT` −1230.05, while ICU4J prints `-€ 1230`. Section 2 (compact currency formatting) revisits this. + +--- + +## Summary: Required Generator Changes + +| Change | Needed by | +| :--- | :--- | +| Add `"fr_CH"` to the generator's locales (+300 rows if added to `CORE_LOCALES`) | S1.1a–c | +| Add `"de_AT"` to the generator's locales (+300 rows if added to `CORE_LOCALES`) | S1.3a–b | +| Add an input with 10⁴ ≤ `input` < 10⁶, such as `12345.0` (only needed with `de_AT` × `short`) | S1.3b | From 5f84d9f523c65a4d8f33bb099f65f02b01c6b479 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 18:54:12 +0000 Subject: [PATCH 10/16] CLDR-19466 Update #5808 pinned inputs after rebase onto main PR #5808 is rebased onto main and now pins ICU4J 79.0.1-20260723.162400-4. Regenerating its TSV files with that version produces byte-identical files, so the witness rows are unchanged. - Pin the generator and TSV files to 5e1d4b0ee3 and ICU4J to 79.0.1-20260723.162400-4. - Cross-check the expected values with the same ICU4J version. - Remove the note about the pending rebase. --- .../testData/currency/tr35_currency_test_coverage.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/common/testData/currency/tr35_currency_test_coverage.md b/common/testData/currency/tr35_currency_test_coverage.md index 149380a9e29..5bfcc899805 100644 --- a/common/testData/currency/tr35_currency_test_coverage.md +++ b/common/testData/currency/tr35_currency_test_coverage.md @@ -14,13 +14,10 @@ For each deep-linked currency section in UTS #35: | :--- | :--- | | Specification text (`docs/ldml/tr35-numbers.md`) | `f18139dfa2` (PR [#6169](https://github.com/unicode-org/cldr/pull/6169)) | | CLDR data (`common/main`, `common/supplemental`) | `f18139dfa2` | -| Generator and TSV files (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) | `604c2bb2ac` | -| ICU4J that produced the TSV `expected` values | `79.0.1-20260318.053439-2` | +| Generator and TSV files (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) | `5e1d4b0ee3` | +| ICU4J that produced the TSV `expected` values | `79.0.1-20260723.162400-4` (pinned in `tools/pom.xml` at `5e1d4b0ee3`) | -Expected values in the breakdown tables are derived from the CLDR data and the specification, and cross-checked with ICU4J `78.3`. - -> [!NOTE] -> PR #5808 is being rebased onto the ICU4J version pinned on `main` (`79.0.1-20260723.162400-4`), and its TSV files will be regenerated. The pinned inputs and witness rows will be updated then. +Expected values in the breakdown tables are derived from the CLDR data and the specification, and cross-checked with the same ICU4J version. ### Coverage Status @@ -110,7 +107,7 @@ The generator produces 12 styles (valid combinations of `currency_format_length` | **S1.4** | `locale` without `` × class A × `input` ≥ 1000 | ✅ **Covered** | W1.4 rows below. | | **S1.5** | Parsing | ⚪ **Out of scope** | — | -**Witness rows** from `currencies.tsv` at `604c2bb2ac` (all with `currency_format_length = ""`, `currency_format_type = "standard"`, `currency_display = "symbol"`): +**Witness rows** from `currencies.tsv` at `5e1d4b0ee3` (all with `currency_format_length = ""`, `currency_format_type = "standard"`, `currency_display = "symbol"`): | Row | `locale` | `currency` | `input` | `expected` | Escaped | | :---: | :--- | :--- | ---: | :--- | :--- | From a515c87186fcab875e4c5412f7a1327c6fc66212 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 18:54:12 +0000 Subject: [PATCH 11/16] CLDR-19466 Check Section 1 coverage against the generator's CORE and extended dimensions - Coverage statuses now depend on whether each (dimension = value) is in the generator's CORE values, only in its extended values, or in neither. Covered no longer depends on the expected values, and Non-conformant is removed. - Replace the dimensions reference with the current CORE and extended values of GenerateCurrencyFormatTestData.java, and write the clauses with the dimensions themselves instead of style classes A-D. - Cite generator values instead of TSV witness rows in the comparison. - Recommend adding one of fr_CH or de_AT to CORE_LOCALES. --- .../currency/tr35_currency_test_coverage.md | 89 ++++++++----------- 1 file changed, 37 insertions(+), 52 deletions(-) diff --git a/common/testData/currency/tr35_currency_test_coverage.md b/common/testData/currency/tr35_currency_test_coverage.md index 5bfcc899805..ec495835f89 100644 --- a/common/testData/currency/tr35_currency_test_coverage.md +++ b/common/testData/currency/tr35_currency_test_coverage.md @@ -1,12 +1,12 @@ # UTS #35 Currency Formatting Specification — Test Data Coverage Verification -This document provides a reproducible, sentence-by-sentence verification of test data coverage for the currency formatting specification in **UTS #35: Unicode LDML, Part 3: Numbers** ([`docs/ldml/tr35-numbers.md`](../../../docs/ldml/tr35-numbers.md)). +This document traces each sentence of the currency formatting specification in **UTS #35: Unicode LDML, Part 3: Numbers** ([`docs/ldml/tr35-numbers.md`](../../../docs/ldml/tr35-numbers.md)) to the test data dimension values that exercise it. For each deep-linked currency section in UTS #35: 1. We link directly to the specification anchor in [`docs/ldml/tr35-numbers.md`](../../../docs/ldml/tr35-numbers.md), with its line range at the pinned commit. 2. We quote the exact normative snippet. 3. We break down every normative sentence/clause in the snippet into the exact **`(dimension = value)` combinations** required to exercise that behavior, with the expected result derived from the CLDR data. -4. We compare the required `(dimension = value)` combinations against the test data generated by `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)), citing a witness row from the TSV files or recording what is missing. +4. We check those combinations against the dimensions of `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)): first whether the generator has each dimension, then whether each `(dimension = value)` is one of the CORE values of that dimension, and if not, whether it is one of its extended values. ### Pinned Inputs @@ -23,36 +23,33 @@ Expected values in the breakdown tables are derived from the CLDR data and the s | Status | Meaning | | :--- | :--- | -| ✅ **Covered** | A witness row exists in the TSV files, and its `expected` value matches the specification | -| 🟡 **Missing: dimension** | No row exists; the generator needs a new dimension value (for example, a locale) | -| 🟡 **Missing: input** | No row exists; the generator needs a new input number | -| 🔴 **Non-conformant** | A row exists, but its `expected` value contradicts the specification | +| ✅ **Covered** | At least one combination of CORE dimension values represents the clause | +| 🟡 **Missing: *dimension* in CORE** | The needed value is only among the extended values of the dimension, so it is not combined with the other CORE values | +| 🟡 **Missing: *dimension*** | The needed value is in neither the CORE nor the extended values of the dimension | +| 🟡 **Missing: new dimension** | The generator does not have the dimension; the dimensions table below marks it **Needs to be added** | | ❓ **Spec unclear** | The specification does not determine the expected value; see the linked ticket | | ⚪ **Out of scope** | Not testable with formatting test data | +*dimension* is the name of a dimension, for example **Missing: `input` in CORE** or **Missing: `locale`**. This document checks coverage only: whether the `expected` values in the TSV files match the specification is out of scope. + --- -## Test Data Dimensions Reference +## Current Test Data Dimensions in `GenerateCurrencyFormatTestData.java` -| Dimension | Description | Allowed / Representative Values | -| :--- | :--- | :--- | -| **`locale`** | CLDR locale identifier | `CORE_LOCALES` (`ar`, `ar_EG`, `bn`, `de`, `de_CH`, `en`, `fy`, `ja`, `pt_PT`, `ru`) + extended modern locales | -| **`currency`** | ISO 4217 3-letter currency code | `CORE_CURRENCIES` (`USD`, `EUR`, `JPY`, `RUB`, `EGP`) + extended modern currencies | -| **`currency_format_length`** | `` type | `""` (default, non-compact) or `"short"` (compact short) | -| **`currency_format_type`** | `` style | `"standard"` or `"accounting"` | -| **`currency_display`** | How the currency is shown | `"symbol"`, `"symbolNarrow"`, `"code"`, `"name"`, `"noCurrency"` (generated with ICU4J `UnitWidth.HIDDEN`) | -| **`input`** | Numeric currency amount | `CORE_NUMBERS` (`0.0`, `1.2`, `0.00831765`, `1234565.0`, `-1230.05`) + extended numbers | +Values at `5e1d4b0ee3`. `currencies.tsv` combines all CORE values with each other. Extended values are combined with fewer values: each extended locale with its own currencies (plus one more) and `TINY_NUMBERS` (`1.2`, `-1230.05`); each extended currency with `TINY_LOCALES` (`en`, `ar`, `de`), one or two related locales, and `TINY_NUMBERS`; and the extended numbers only with `TINY_LOCALES` and `TINY_CURRENCIES` (`USD`, `EUR`). -### Style Classes +| Dimension | Description | CORE values | Extended values | +| :--- | :--- | :--- | :--- | +| **`locale`** | CLDR locale identifier | `CORE_LOCALES`: `ar`, `ar_EG`, `bn`, `de`, `de_CH`, `en`, `fy`, `ja`, `pt_PT`, `ru` | The other locales that CLDR targets at `modern` coverage (`getExtendedModernLocales()`; `currencies_modern_locales.tsv`) | +| **`currency`** | ISO 4217 currency code | `CORE_CURRENCIES`: `USD`, `EUR`, `JPY`, `RUB`, `EGP` | The other currencies that are legal tender today (`getExtendedModernCurrencies()`; `currencies_*_modern_currencies.tsv`) | +| **`currency_format_length`** | `` type | `""` (non-compact), `"short"` (compact) | — | +| **`currency_format_type`** | `` type | `"standard"`, `"accounting"` | — | +| **`currency_display`** | How the currency is shown | `"symbol"`, `"symbolNarrow"`, `"code"`, `"name"`, `"noCurrency"` (generated with ICU4J `UnitWidth.HIDDEN`) | — | +| **`input`** | Numeric currency amount | `CORE_NUMBERS`: `0.0`, `1.2`, `0.00831765`, `1234565.0`, `-1230.05` | 10ⁱ, 1.5 × 10ⁱ, and 5 × 10ⁱ for −6 ≤ i ≤ 12; `12`, `123`, `1234.56`, `1234567`, `0.000123`, `0.5`, `2.5`, `3.5`, `0.125`, `0.135`, `999.9`, `999999.9`; the negatives of all positive values, including the CORE ones; and `-0.0` (`getExtendedNumbers()` minus `CORE_NUMBERS`; `currencies_*_extended_numbers.tsv`) | -The generator produces 12 styles (valid combinations of `currency_format_length`, `currency_format_type`, and `currency_display`). The specification selects patterns differently for 4 classes of styles: +The generator produces 12 of the 20 combinations of `currency_format_length`, `currency_format_type`, and `currency_display`: it skips `"short"` with `"accounting"`, `"name"`, or `"noCurrency"`, and `"accounting"` with `"name"`. `"noCurrency"` is not combined with extended values. In the tables below, *any `¤` display* means `currency_display` = `"symbol"`, `"symbolNarrow"`, or `"code"`. -| Class | `currency_format_length` | `currency_format_type` | `currency_display` | Pattern used | -| :---: | :--- | :--- | :--- | :--- | -| **A** | `""` | `standard`, `accounting` | `symbol`, `symbolNarrow`, `code` | `currencyFormat` pattern containing `¤` | -| **B** | `short` | `standard` | `symbol`, `symbolNarrow`, `code` | compact `currencyFormat` pattern containing `¤` | -| **C** | `""` | `standard` | `name` | `decimalFormat` pattern + `unitPattern` | -| **D** | `""` | `standard`, `accounting` | `noCurrency` | `currencyFormat` `alt="noCurrency"` pattern (no `¤`) | +A section that needs a dimension the generator does not have adds it to this table, marked **Needs to be added**. --- @@ -86,44 +83,31 @@ The generator produces 12 styles (valid combinations of `currency_format_length` | # | Verbatim Sentence / Normative Clause | Required `(Dimension = Value)` Combinations to Cover Clause | CLDR Data Evidence & Expected Behavior | | :---: | :--- | :--- | :--- | -| **S1.1** | **`currencyDecimal`** — *"Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator;"* | • `locale = "fr_CH"`
• `currency` with digits > 0 (e.g. `"EUR"`)
• **S1.1a**: class A, any `input`
• **S1.1b**: class B, `input = 1234565.0`
• **S1.1c**: class C, any `input`
• **S1.1d**: class D, any `input` | Plain decimal in `fr_CH`: `-1'230,05`. Currency (EUR):
• A (−1230.05): `-1'230.05 €` / `"-1'230.05\u00A0€"`
• B (1234565.0): `1.2 M €` / `"1.2\u00A0M\u00A0€"`
• C (−1230.05): `-1'230.05 euros` (L998 step 5 requires `currencyDecimal`)
• D (−1230.05): `-1'230.05` or `-1'230,05` (spec unclear) | -| **S1.2** | **`currencyDecimal`** — *"...otherwise, the regular decimal separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"ar_EG"`)
• `currency` with digits > 0
• class A, any `input` | None of `en`, `de`, or `ar_EG` specifies ``, so currency formatting uses the regular `` symbol (`.` in `en`, `,` in `de`, `٫` U+066B in `ar_EG`). | -| **S1.3** | **`currencyGroup`** — *"Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator;"* | • `locale = "de_AT"`
• **S1.3a**: class A, `input` ≥ 1000 (e.g. `-1230.05`)
• **S1.3b**: class B, 10⁴ ≤ `input` < 10⁶ (e.g. `12345.0`)
• **S1.3c**: class C, `input` ≥ 1000
• **S1.3d**: class D, `input` ≥ 1000 | Plain decimal in `de_AT`: `-1 230,05` / `"-1\u00A0230,05"`. Currency (EUR):
• A (−1230.05): `-€ 1.230,05` / `"-€\u00A01.230,05"`
• B (12345.0): `€ 12.345` / `"€\u00A012.345"`. ICU4J groups only numbers with 5 or more integer digits in compact notation (`-€ 1230` / `"-€\u00A01230"` for −1230.05).
• C (−1230.05): `-1.230,05 Euro` or `-1 230,05 Euro` (spec unclear)
• D (−1230.05): `-1.230,05` or `-1 230,05` (spec unclear) | -| **S1.4** | **`currencyGroup`** — *"...otherwise, the regular group separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"de_CH"`, `"bn"`)
• class A, `input` ≥ 1000 | None of `en`, `de`, `de_CH`, or `bn` specifies ``, so currency formatting uses the regular `` separator (`,` in `en`, `.` in `de`, `'` in `de_CH`, `,` with Indian `#,##,##0.00` grouping in `bn`). | +| **S1.1** | **`currencyDecimal`** — *"Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator;"* | • `locale = "fr_CH"`
• `currency` with digits > 0 (e.g. `"EUR"`)
• **S1.1a**: `currency_format_length = ""`, any `¤` display, any `input`
• **S1.1b**: `currency_format_length = "short"`, `input = 1234565.0`
• **S1.1c**: `currency_display = "name"`, any `input`
• **S1.1d**: `currency_display = "noCurrency"`, any `input` | Plain decimal in `fr_CH`: `-1'230,05`. Currency (EUR):
• S1.1a (−1230.05): `-1'230.05 €` / `"-1'230.05\u00A0€"`
• S1.1b (1234565.0): `1.2 M €` / `"1.2\u00A0M\u00A0€"`
• S1.1c (−1230.05): `-1'230.05 euros` (L998 step 5 requires `currencyDecimal`)
• S1.1d (−1230.05): `-1'230.05` or `-1'230,05` (spec unclear) | +| **S1.2** | **`currencyDecimal`** — *"...otherwise, the regular decimal separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"ar_EG"`)
• `currency` with digits > 0
• `currency_format_length = ""`, any `¤` display, any `input` | None of `en`, `de`, or `ar_EG` specifies ``, so currency formatting uses the regular `` symbol (`.` in `en`, `,` in `de`, `٫` U+066B in `ar_EG`). | +| **S1.3** | **`currencyGroup`** — *"Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator;"* | • `locale = "de_AT"`
• **S1.3a**: `currency_format_length = ""`, any `¤` display, `input` of magnitude ≥ 1000 (e.g. `-1230.05`)
• **S1.3b**: `currency_format_length = "short"`, `input` with 10⁴ ≤ magnitude < 10⁶ (e.g. `15000.0`)
• **S1.3c**: `currency_display = "name"`, `input` of magnitude ≥ 1000
• **S1.3d**: `currency_display = "noCurrency"`, `input` of magnitude ≥ 1000 | Plain decimal in `de_AT`: `-1 230,05` / `"-1\u00A0230,05"`. Currency (EUR):
• S1.3a (−1230.05): `-€ 1.230,05` / `"-€\u00A01.230,05"`
• S1.3b (15000.0): `€ 15.000` / `"€\u00A015.000"` (see the S1.3b note below)
• S1.3c (−1230.05): `-1.230,05 Euro` or `-1 230,05 Euro` (spec unclear)
• S1.3d (−1230.05): `-1.230,05` or `-1 230,05` (spec unclear) | +| **S1.4** | **`currencyGroup`** — *"...otherwise, the regular group separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"de_CH"`, `"bn"`)
• `currency_format_length = ""`, any `¤` display, `input` of magnitude ≥ 1000 | None of `en`, `de`, `de_CH`, or `bn` specifies ``, so currency formatting uses the regular `` separator (`,` in `en`, `.` in `de`, `'` in `de_CH`, `,` with Indian `#,##,##0.00` grouping in `bn`). | | **S1.5** | **`currencyDecimal`** and **`currencyGroup`** — *"...for currency formatting/parsing..."* (the parsing half) | — | Not testable: the TSV files contain formatting data only. | --- ### 1.3 Comparison Against `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) -| Clause | Required `(Dimension = Value)` Combination | Status | Witness Row / Action Required | +| Clause | Required `(Dimension = Value)` Combination | Status | Generator Evidence / Action Required | | :---: | :--- | :---: | :--- | -| **S1.1a–c** | `locale = "fr_CH"` × currency with digits > 0 × classes A, B, C | 🟡 **Missing: dimension** | None of the 10 TSV files has `fr_CH` rows. `fr_CH` is not in `CORE_LOCALES`, and the modern-locale suite only includes `fr` and `fr_CA`. **Action**: add `"fr_CH"` to the generator's locales. | -| **S1.1d** | `locale = "fr_CH"` × class D | ❓ **Spec unclear** | [CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825) | -| **S1.2** | `locale` without `` × currency with digits > 0 × class A | ✅ **Covered** | W1.2 rows below. | -| **S1.3a** | `locale = "de_AT"` × class A × `input` ≥ 1000 | 🟡 **Missing: dimension** | None of the 10 TSV files has `de_AT` rows. **Action**: add `"de_AT"` to the generator's locales. | -| **S1.3b** | `locale = "de_AT"` × class B × 10⁴ ≤ `input` < 10⁶ | 🟡 **Missing: dimension, input** | No value in `CORE_NUMBERS` is in this range. **Action**: add `"de_AT"` and an input such as `12345.0` (only needed with `de_AT` × `short`). | -| **S1.3c–d** | `locale = "de_AT"` × classes C, D | ❓ **Spec unclear** | [CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825) | -| **S1.4** | `locale` without `` × class A × `input` ≥ 1000 | ✅ **Covered** | W1.4 rows below. | +| **S1.1a–c** | `locale = "fr_CH"` × `currency` with digits > 0 × the S1.1a–c values of `currency_format_length`, `currency_display`, and `input` | 🟡 **Missing: `locale`** | `fr_CH` is in neither `CORE_LOCALES` nor the extended locales: none of the 10 TSV files has `fr_CH` rows, and the modern-locale suite has only `fr` and `fr_CA`. The other values are CORE values (`EUR` and `USD` in `CORE_CURRENCIES`, `1234565.0` in `CORE_NUMBERS`). **Action**: add `"fr_CH"` to `CORE_LOCALES` (see the Summary). | +| **S1.1d** | `locale = "fr_CH"` × `currency_display = "noCurrency"` | ❓ **Spec unclear** | [CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825) | +| **S1.2** | `locale` without `` × `currency` with digits > 0 × `currency_format_length = ""` × any `¤` display | ✅ **Covered** | CORE values: `en`, `de`, and `ar_EG` in `CORE_LOCALES`; `USD`, `EUR`, and `EGP` in `CORE_CURRENCIES`. | +| **S1.3a** | `locale = "de_AT"` × `currency_format_length = ""` × any `¤` display × `input` of magnitude ≥ 1000 | 🟡 **Missing: `locale`** | `de_AT` is in neither `CORE_LOCALES` nor the extended locales (no TSV file has `de_AT` rows). `-1230.05` and `1234565.0` are in `CORE_NUMBERS`. **Action**: add `"de_AT"` to `CORE_LOCALES` only if it is chosen instead of `fr_CH` (see the Summary). | +| **S1.3b** | `locale = "de_AT"` × `currency_format_length = "short"` × `input` with 10⁴ ≤ magnitude < 10⁶ | 🟡 **Missing: `locale`**
🟡 **Missing: `input` in CORE** | No `CORE_NUMBERS` value is in this range. The extended numbers have `10000.0`, `15000.0`, `50000.0`, `100000.0`, `150000.0`, `500000.0`, and `999999.9` (and their negatives), but only with `TINY_LOCALES` and `TINY_CURRENCIES`. **Action**: only with `de_AT`, add one of them (e.g. `15000.0`) to `CORE_NUMBERS`. | +| **S1.3c–d** | `locale = "de_AT"` × `currency_display = "name"` or `"noCurrency"` | ❓ **Spec unclear** | [CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825) | +| **S1.4** | `locale` without `` × `currency_format_length = ""` × any `¤` display × `input` of magnitude ≥ 1000 | ✅ **Covered** | CORE values: `en`, `de`, `de_CH`, and `bn` in `CORE_LOCALES`; `-1230.05` and `1234565.0` in `CORE_NUMBERS`. | | **S1.5** | Parsing | ⚪ **Out of scope** | — | -**Witness rows** from `currencies.tsv` at `5e1d4b0ee3` (all with `currency_format_length = ""`, `currency_format_type = "standard"`, `currency_display = "symbol"`): - -| Row | `locale` | `currency` | `input` | `expected` | Escaped | -| :---: | :--- | :--- | ---: | :--- | :--- | -| W1.2 | `en` | `USD` | `-1230.05` | `-$1,230.05` | `"-$1,230.05"` | -| W1.2 | `de` | `EUR` | `-1230.05` | `-1.230,05 €` | `"-1.230,05\u00A0€"` | -| W1.2 | `ar_EG` | `EGP` | `-1230.05` | `-١٬٢٣٠٫٠٥ ج.م.`¹ | `"\u061C-\u200F١٬٢٣٠٫٠٥\u00A0ج.م.\u200F"` | -| W1.4 | `de_CH` | `EUR` | `1234565.0` | `EUR 1'234'565.00` | `"EUR\u00A01'234'565.00"` | -| W1.4 | `bn` | `USD` | `1234565.0` | `১২,৩৪,৫৬৫.০০ US$` | `"১২,৩৪,৫৬৫.০০\u00A0US$"` | - -¹ The bidi marks U+061C and U+200F appear only in the escaped form. - ### 1.4 Notes * **Spec unclear** ([CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825)): L271/L275 apply the monetary separators to all currency formatting, L703 applies them only when `¤` is in the pattern, and L998 step 5 mentions only `currencyDecimal`. ICU4J applies both overrides in all 12 styles. -* **`noCurrency` in PR #5808**: ICU4J `UnitWidth.HIDDEN` removes `¤` from the standard pattern but keeps the space next to it (for example `-1.230,05 ` / `"-1.230,05\u00A0"` for `de`), instead of using the `alt="noCurrency"` pattern. Section 5 (`#currency-noCurrency`) covers this. -* **Compact grouping**: TR35 L454 (a compact pattern of `"0"` falls back to the non-compact pattern) and L752 (`minimumGroupingDigits` is 1 for `de`) suggest `-€ 1.230` for `de_AT` −1230.05, while ICU4J prints `-€ 1230`. Section 2 (compact currency formatting) revisits this. +* **S1.3b input range**: `de_AT` uses the `short` pattern `0` (inherited from `de`) for amounts below 10⁶, so they are formatted with the regular grouping. From 10⁴ on, both TR35 and ICU4J show a group separator (`€ 15.000` / `"€\u00A015.000"`). For 4-digit amounts they differ: TR35 L454 and L752 give a grouped value for −1230.05, while ICU4J prints `-€ 1230` / `"-€\u00A01230"`. Section 2 (compact currency formatting) covers that case. --- @@ -131,6 +115,7 @@ The generator produces 12 styles (valid combinations of `currency_format_length` | Change | Needed by | | :--- | :--- | -| Add `"fr_CH"` to the generator's locales (+300 rows if added to `CORE_LOCALES`) | S1.1a–c | -| Add `"de_AT"` to the generator's locales (+300 rows if added to `CORE_LOCALES`) | S1.3a–b | -| Add an input with 10⁴ ≤ `input` < 10⁶, such as `12345.0` (only needed with `de_AT` × `short`) | S1.3b | +| Add one locale with a monetary separator to `CORE_LOCALES` (+300 rows): `"fr_CH"` (recommended: its `currencyDecimal` shows in S1.1a–c with the current `CORE_NUMBERS`) or `"de_AT"` | S1.1a–c (`fr_CH`) or S1.3a–b (`de_AT`) | +| Only with `"de_AT"`: add an input with 10⁴ ≤ magnitude < 10⁶ to `CORE_NUMBERS`, such as `15000.0` from the extended numbers (+660 rows) | S1.3b | + +One such locale is enough to exercise the monetary separator override. `en` and the other CORE locales already cover the "otherwise" clauses (S1.2, S1.4). The clauses of the locale that is not added stay 🟡 **Missing: `locale`**. From 626b22501dfff8545951e7c21ba8b02852f58568 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 18:54:12 +0000 Subject: [PATCH 12/16] CLDR-19466 Let the CORE input -1230.05 represent the compact currencyGroup clause - TR35 L454 and L485 format de_AT short amounts below 10^6 with the non-compact pattern and the regular grouping, so -1230.05 (CORE_NUMBERS) already shows currencyGroup. The 10^4 lower bound for S1.3b came from ICU4J's output, which the coverage check does not use. - Merge S1.3a and S1.3b into one Missing: locale row, and drop the extra input that de_AT needed in the Summary. - Base the fr_CH recommendation on approved data: de_AT's own group separator is draft="contributed". --- .../testData/currency/tr35_currency_test_coverage.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/common/testData/currency/tr35_currency_test_coverage.md b/common/testData/currency/tr35_currency_test_coverage.md index ec495835f89..17cd8938708 100644 --- a/common/testData/currency/tr35_currency_test_coverage.md +++ b/common/testData/currency/tr35_currency_test_coverage.md @@ -85,7 +85,7 @@ A section that needs a dimension the generator does not have adds it to this tab | :---: | :--- | :--- | :--- | | **S1.1** | **`currencyDecimal`** — *"Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator;"* | • `locale = "fr_CH"`
• `currency` with digits > 0 (e.g. `"EUR"`)
• **S1.1a**: `currency_format_length = ""`, any `¤` display, any `input`
• **S1.1b**: `currency_format_length = "short"`, `input = 1234565.0`
• **S1.1c**: `currency_display = "name"`, any `input`
• **S1.1d**: `currency_display = "noCurrency"`, any `input` | Plain decimal in `fr_CH`: `-1'230,05`. Currency (EUR):
• S1.1a (−1230.05): `-1'230.05 €` / `"-1'230.05\u00A0€"`
• S1.1b (1234565.0): `1.2 M €` / `"1.2\u00A0M\u00A0€"`
• S1.1c (−1230.05): `-1'230.05 euros` (L998 step 5 requires `currencyDecimal`)
• S1.1d (−1230.05): `-1'230.05` or `-1'230,05` (spec unclear) | | **S1.2** | **`currencyDecimal`** — *"...otherwise, the regular decimal separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"ar_EG"`)
• `currency` with digits > 0
• `currency_format_length = ""`, any `¤` display, any `input` | None of `en`, `de`, or `ar_EG` specifies ``, so currency formatting uses the regular `` symbol (`.` in `en`, `,` in `de`, `٫` U+066B in `ar_EG`). | -| **S1.3** | **`currencyGroup`** — *"Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator;"* | • `locale = "de_AT"`
• **S1.3a**: `currency_format_length = ""`, any `¤` display, `input` of magnitude ≥ 1000 (e.g. `-1230.05`)
• **S1.3b**: `currency_format_length = "short"`, `input` with 10⁴ ≤ magnitude < 10⁶ (e.g. `15000.0`)
• **S1.3c**: `currency_display = "name"`, `input` of magnitude ≥ 1000
• **S1.3d**: `currency_display = "noCurrency"`, `input` of magnitude ≥ 1000 | Plain decimal in `de_AT`: `-1 230,05` / `"-1\u00A0230,05"`. Currency (EUR):
• S1.3a (−1230.05): `-€ 1.230,05` / `"-€\u00A01.230,05"`
• S1.3b (15000.0): `€ 15.000` / `"€\u00A015.000"` (see the S1.3b note below)
• S1.3c (−1230.05): `-1.230,05 Euro` or `-1 230,05 Euro` (spec unclear)
• S1.3d (−1230.05): `-1.230,05` or `-1 230,05` (spec unclear) | +| **S1.3** | **`currencyGroup`** — *"Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator;"* | • `locale = "de_AT"`
• **S1.3a**: `currency_format_length = ""`, any `¤` display, `input` of magnitude ≥ 1000 (e.g. `-1230.05`)
• **S1.3b**: `currency_format_length = "short"`, `input` with 1000 ≤ magnitude < 10⁶ (e.g. `-1230.05`)
• **S1.3c**: `currency_display = "name"`, `input` of magnitude ≥ 1000
• **S1.3d**: `currency_display = "noCurrency"`, `input` of magnitude ≥ 1000 | Plain decimal in `de_AT`: `-1 230,05` / `"-1\u00A0230,05"`. Currency (EUR):
• S1.3a (−1230.05): `-€ 1.230,05` / `"-€\u00A01.230,05"`
• S1.3b (−1230.05): `-€ 1.230` / `"-€\u00A01.230"` or `-€ 1.200` / `"-€\u00A01.200"` (see the S1.3b note below)
• S1.3c (−1230.05): `-1.230,05 Euro` or `-1 230,05 Euro` (spec unclear)
• S1.3d (−1230.05): `-1.230,05` or `-1 230,05` (spec unclear) | | **S1.4** | **`currencyGroup`** — *"...otherwise, the regular group separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"de_CH"`, `"bn"`)
• `currency_format_length = ""`, any `¤` display, `input` of magnitude ≥ 1000 | None of `en`, `de`, `de_CH`, or `bn` specifies ``, so currency formatting uses the regular `` separator (`,` in `en`, `.` in `de`, `'` in `de_CH`, `,` with Indian `#,##,##0.00` grouping in `bn`). | | **S1.5** | **`currencyDecimal`** and **`currencyGroup`** — *"...for currency formatting/parsing..."* (the parsing half) | — | Not testable: the TSV files contain formatting data only. | @@ -98,8 +98,7 @@ A section that needs a dimension the generator does not have adds it to this tab | **S1.1a–c** | `locale = "fr_CH"` × `currency` with digits > 0 × the S1.1a–c values of `currency_format_length`, `currency_display`, and `input` | 🟡 **Missing: `locale`** | `fr_CH` is in neither `CORE_LOCALES` nor the extended locales: none of the 10 TSV files has `fr_CH` rows, and the modern-locale suite has only `fr` and `fr_CA`. The other values are CORE values (`EUR` and `USD` in `CORE_CURRENCIES`, `1234565.0` in `CORE_NUMBERS`). **Action**: add `"fr_CH"` to `CORE_LOCALES` (see the Summary). | | **S1.1d** | `locale = "fr_CH"` × `currency_display = "noCurrency"` | ❓ **Spec unclear** | [CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825) | | **S1.2** | `locale` without `` × `currency` with digits > 0 × `currency_format_length = ""` × any `¤` display | ✅ **Covered** | CORE values: `en`, `de`, and `ar_EG` in `CORE_LOCALES`; `USD`, `EUR`, and `EGP` in `CORE_CURRENCIES`. | -| **S1.3a** | `locale = "de_AT"` × `currency_format_length = ""` × any `¤` display × `input` of magnitude ≥ 1000 | 🟡 **Missing: `locale`** | `de_AT` is in neither `CORE_LOCALES` nor the extended locales (no TSV file has `de_AT` rows). `-1230.05` and `1234565.0` are in `CORE_NUMBERS`. **Action**: add `"de_AT"` to `CORE_LOCALES` only if it is chosen instead of `fr_CH` (see the Summary). | -| **S1.3b** | `locale = "de_AT"` × `currency_format_length = "short"` × `input` with 10⁴ ≤ magnitude < 10⁶ | 🟡 **Missing: `locale`**
🟡 **Missing: `input` in CORE** | No `CORE_NUMBERS` value is in this range. The extended numbers have `10000.0`, `15000.0`, `50000.0`, `100000.0`, `150000.0`, `500000.0`, and `999999.9` (and their negatives), but only with `TINY_LOCALES` and `TINY_CURRENCIES`. **Action**: only with `de_AT`, add one of them (e.g. `15000.0`) to `CORE_NUMBERS`. | +| **S1.3a–b** | `locale = "de_AT"` × the S1.3a–b values of `currency_format_length`, `currency_display`, and `input` | 🟡 **Missing: `locale`** | `de_AT` is in neither `CORE_LOCALES` nor the extended locales (no TSV file has `de_AT` rows). The inputs are CORE values (`-1230.05` and `1234565.0` in `CORE_NUMBERS`). **Action**: add `"de_AT"` to `CORE_LOCALES` only if it is chosen instead of `fr_CH` (see the Summary). | | **S1.3c–d** | `locale = "de_AT"` × `currency_display = "name"` or `"noCurrency"` | ❓ **Spec unclear** | [CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825) | | **S1.4** | `locale` without `` × `currency_format_length = ""` × any `¤` display × `input` of magnitude ≥ 1000 | ✅ **Covered** | CORE values: `en`, `de`, `de_CH`, and `bn` in `CORE_LOCALES`; `-1230.05` and `1234565.0` in `CORE_NUMBERS`. | | **S1.5** | Parsing | ⚪ **Out of scope** | — | @@ -107,7 +106,7 @@ A section that needs a dimension the generator does not have adds it to this tab ### 1.4 Notes * **Spec unclear** ([CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825)): L271/L275 apply the monetary separators to all currency formatting, L703 applies them only when `¤` is in the pattern, and L998 step 5 mentions only `currencyDecimal`. ICU4J applies both overrides in all 12 styles. -* **S1.3b input range**: `de_AT` uses the `short` pattern `0` (inherited from `de`) for amounts below 10⁶, so they are formatted with the regular grouping. From 10⁴ on, both TR35 and ICU4J show a group separator (`€ 15.000` / `"€\u00A015.000"`). For 4-digit amounts they differ: TR35 L454 and L752 give a grouped value for −1230.05, while ICU4J prints `-€ 1230` / `"-€\u00A01230"`. Section 2 (compact currency formatting) covers that case. +* **S1.3b**: `de_AT` uses the `short` pattern `0` (inherited from `de`) for amounts below 10⁶. TR35 L454 and L485 format them with the non-compact pattern and "the normal formatting for the locale (such as the grouping separators)", so `currencyGroup` shows. L485 leaves the number of digits open ("typically to 2 or 3 digits"): 1.200 or 1.230 for −1230.05, grouped either way. --- @@ -115,7 +114,6 @@ A section that needs a dimension the generator does not have adds it to this tab | Change | Needed by | | :--- | :--- | -| Add one locale with a monetary separator to `CORE_LOCALES` (+300 rows): `"fr_CH"` (recommended: its `currencyDecimal` shows in S1.1a–c with the current `CORE_NUMBERS`) or `"de_AT"` | S1.1a–c (`fr_CH`) or S1.3a–b (`de_AT`) | -| Only with `"de_AT"`: add an input with 10⁴ ≤ magnitude < 10⁶ to `CORE_NUMBERS`, such as `15000.0` from the extended numbers (+660 rows) | S1.3b | +| Add one locale with a monetary separator to `CORE_LOCALES` (+300 rows): `"fr_CH"` (recommended: unlike the `de_AT` override, it also shows with approved data only; see 1.2) or `"de_AT"` | S1.1a–c (`fr_CH`) or S1.3a–b (`de_AT`) | One such locale is enough to exercise the monetary separator override. `en` and the other CORE locales already cover the "otherwise" clauses (S1.2, S1.4). The clauses of the locale that is not added stay 🟡 **Missing: `locale`**. From f278d080c3294aeae8b66eb52e4bac28a389b99a Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 18:54:13 +0000 Subject: [PATCH 13/16] CLDR-19466 Update Section 1 for the plain TR35 headings of #6169 #6169 now uses plain headings with generated anchors and adds 17 lines to the table of contents of tr35-numbers.md. In the text added by this PR: - Link to `#currencydecimal` (Number Symbols is now a list of subheadings), and update the other links to the new anchors. - Quote the specification text as it is at 2997bffaf0 (without inline anchors). - Shift the tr35-numbers.md line numbers by 17 and pin them to 2997bffaf0. --- .../currency/tr35_currency_test_coverage.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/common/testData/currency/tr35_currency_test_coverage.md b/common/testData/currency/tr35_currency_test_coverage.md index 17cd8938708..a2131c50e64 100644 --- a/common/testData/currency/tr35_currency_test_coverage.md +++ b/common/testData/currency/tr35_currency_test_coverage.md @@ -12,8 +12,8 @@ For each deep-linked currency section in UTS #35: | Input | Version | | :--- | :--- | -| Specification text (`docs/ldml/tr35-numbers.md`) | `f18139dfa2` (PR [#6169](https://github.com/unicode-org/cldr/pull/6169)) | -| CLDR data (`common/main`, `common/supplemental`) | `f18139dfa2` | +| Specification text (`docs/ldml/tr35-numbers.md`) | `2997bffaf0` (PR [#6169](https://github.com/unicode-org/cldr/pull/6169)) | +| CLDR data (`common/main`, `common/supplemental`) | `2997bffaf0` | | Generator and TSV files (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) | `5e1d4b0ee3` | | ICU4J that produced the TSV `expected` values | `79.0.1-20260723.162400-4` (pinned in `tools/pom.xml` at `5e1d4b0ee3`) | @@ -53,18 +53,18 @@ A section that needs a dimension the generator does not have adds it to this tab --- -## Section 1: Monetary Decimal and Grouping Symbols (`#Currency_Symbols_Decimal_Group`) +## Section 1: Monetary Decimal and Grouping Symbols (`#currencydecimal`, `#currencygroup`) -* **TR35 Specification Link**: [`tr35-numbers.md#Currency_Symbols_Decimal_Group`](../../../docs/ldml/tr35-numbers.md#Currency_Symbols_Decimal_Group) (UTS #35 Part 3, Section 2.3: *Number Symbols*; L269–L275 at `f18139dfa2`) -* **Related specification text**: L695–L697 (pattern characters `.` and `,`), L703 ([`¤` row](../../../docs/ldml/tr35-numbers.md#currency-symbol-placeholders)), and L998 ([Formatting Currency Display Names](../../../docs/ldml/tr35-numbers.md#currency-unit-pattern-formatting), step 5) +* **TR35 Specification Link**: [`tr35-numbers.md#currencydecimal`](../../../docs/ldml/tr35-numbers.md#currencydecimal) (UTS #35 Part 3, Section 2.3: *Number Symbols*; L286–L292 at `2997bffaf0`) +* **Related specification text**: L712–L714 (pattern characters `.` and `,`), L720 ([`¤` row](../../../docs/ldml/tr35-numbers.md#Number_Pattern_Character_Definitions)), and L1015 ([Formatting Currency Display Names](../../../docs/ldml/tr35-numbers.md#formatting-currency-display-names-unitpattern), step 5) ### 1.1 Verbatim Specification Snippet (`docs/ldml/tr35-numbers.md`) -> **currencyDecimal** +> #### currencyDecimal > > > Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator; otherwise, the regular decimal separator is used. > -> **currencyGroup** +> #### currencyGroup > > > Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator; otherwise, the regular group separator is used. @@ -83,7 +83,7 @@ A section that needs a dimension the generator does not have adds it to this tab | # | Verbatim Sentence / Normative Clause | Required `(Dimension = Value)` Combinations to Cover Clause | CLDR Data Evidence & Expected Behavior | | :---: | :--- | :--- | :--- | -| **S1.1** | **`currencyDecimal`** — *"Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator;"* | • `locale = "fr_CH"`
• `currency` with digits > 0 (e.g. `"EUR"`)
• **S1.1a**: `currency_format_length = ""`, any `¤` display, any `input`
• **S1.1b**: `currency_format_length = "short"`, `input = 1234565.0`
• **S1.1c**: `currency_display = "name"`, any `input`
• **S1.1d**: `currency_display = "noCurrency"`, any `input` | Plain decimal in `fr_CH`: `-1'230,05`. Currency (EUR):
• S1.1a (−1230.05): `-1'230.05 €` / `"-1'230.05\u00A0€"`
• S1.1b (1234565.0): `1.2 M €` / `"1.2\u00A0M\u00A0€"`
• S1.1c (−1230.05): `-1'230.05 euros` (L998 step 5 requires `currencyDecimal`)
• S1.1d (−1230.05): `-1'230.05` or `-1'230,05` (spec unclear) | +| **S1.1** | **`currencyDecimal`** — *"Optional. If specified, then for currency formatting/parsing this is used as the decimal separator instead of using the regular decimal separator;"* | • `locale = "fr_CH"`
• `currency` with digits > 0 (e.g. `"EUR"`)
• **S1.1a**: `currency_format_length = ""`, any `¤` display, any `input`
• **S1.1b**: `currency_format_length = "short"`, `input = 1234565.0`
• **S1.1c**: `currency_display = "name"`, any `input`
• **S1.1d**: `currency_display = "noCurrency"`, any `input` | Plain decimal in `fr_CH`: `-1'230,05`. Currency (EUR):
• S1.1a (−1230.05): `-1'230.05 €` / `"-1'230.05\u00A0€"`
• S1.1b (1234565.0): `1.2 M €` / `"1.2\u00A0M\u00A0€"`
• S1.1c (−1230.05): `-1'230.05 euros` (L1015 step 5 requires `currencyDecimal`)
• S1.1d (−1230.05): `-1'230.05` or `-1'230,05` (spec unclear) | | **S1.2** | **`currencyDecimal`** — *"...otherwise, the regular decimal separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"ar_EG"`)
• `currency` with digits > 0
• `currency_format_length = ""`, any `¤` display, any `input` | None of `en`, `de`, or `ar_EG` specifies ``, so currency formatting uses the regular `` symbol (`.` in `en`, `,` in `de`, `٫` U+066B in `ar_EG`). | | **S1.3** | **`currencyGroup`** — *"Optional. If specified, then for currency formatting/parsing this is used as the group separator instead of using the regular group separator;"* | • `locale = "de_AT"`
• **S1.3a**: `currency_format_length = ""`, any `¤` display, `input` of magnitude ≥ 1000 (e.g. `-1230.05`)
• **S1.3b**: `currency_format_length = "short"`, `input` with 1000 ≤ magnitude < 10⁶ (e.g. `-1230.05`)
• **S1.3c**: `currency_display = "name"`, `input` of magnitude ≥ 1000
• **S1.3d**: `currency_display = "noCurrency"`, `input` of magnitude ≥ 1000 | Plain decimal in `de_AT`: `-1 230,05` / `"-1\u00A0230,05"`. Currency (EUR):
• S1.3a (−1230.05): `-€ 1.230,05` / `"-€\u00A01.230,05"`
• S1.3b (−1230.05): `-€ 1.230` / `"-€\u00A01.230"` or `-€ 1.200` / `"-€\u00A01.200"` (see the S1.3b note below)
• S1.3c (−1230.05): `-1.230,05 Euro` or `-1 230,05 Euro` (spec unclear)
• S1.3d (−1230.05): `-1.230,05` or `-1 230,05` (spec unclear) | | **S1.4** | **`currencyGroup`** — *"...otherwise, the regular group separator is used."* | • `locale` without `` (e.g. `"en"`, `"de"`, `"de_CH"`, `"bn"`)
• `currency_format_length = ""`, any `¤` display, `input` of magnitude ≥ 1000 | None of `en`, `de`, `de_CH`, or `bn` specifies ``, so currency formatting uses the regular `` separator (`,` in `en`, `.` in `de`, `'` in `de_CH`, `,` with Indian `#,##,##0.00` grouping in `bn`). | @@ -105,8 +105,8 @@ A section that needs a dimension the generator does not have adds it to this tab ### 1.4 Notes -* **Spec unclear** ([CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825)): L271/L275 apply the monetary separators to all currency formatting, L703 applies them only when `¤` is in the pattern, and L998 step 5 mentions only `currencyDecimal`. ICU4J applies both overrides in all 12 styles. -* **S1.3b**: `de_AT` uses the `short` pattern `0` (inherited from `de`) for amounts below 10⁶. TR35 L454 and L485 format them with the non-compact pattern and "the normal formatting for the locale (such as the grouping separators)", so `currencyGroup` shows. L485 leaves the number of digits open ("typically to 2 or 3 digits"): 1.200 or 1.230 for −1230.05, grouped either way. +* **Spec unclear** ([CLDR-19825](https://unicode-org.atlassian.net/browse/CLDR-19825)): L288/L292 apply the monetary separators to all currency formatting, L720 applies them only when `¤` is in the pattern, and L1015 step 5 mentions only `currencyDecimal`. ICU4J applies both overrides in all 12 styles. +* **S1.3b**: `de_AT` uses the `short` pattern `0` (inherited from `de`) for amounts below 10⁶. TR35 L471 and L502 format them with the non-compact pattern and "the normal formatting for the locale (such as the grouping separators)", so `currencyGroup` shows. L502 leaves the number of digits open ("typically to 2 or 3 digits"): 1.200 or 1.230 for −1230.05, grouped either way. --- From cb55c4150a5e8d02cc81ea2990dfa5a02a0d7f81 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 18:54:14 +0000 Subject: [PATCH 14/16] CLDR-19466 Add TR35 #compact-currency-alphaNextToNumber test coverage section --- .../currency/tr35_currency_test_coverage.md | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/common/testData/currency/tr35_currency_test_coverage.md b/common/testData/currency/tr35_currency_test_coverage.md index a2131c50e64..59b0e1490c7 100644 --- a/common/testData/currency/tr35_currency_test_coverage.md +++ b/common/testData/currency/tr35_currency_test_coverage.md @@ -110,10 +110,109 @@ A section that needs a dimension the generator does not have adds it to this tab --- +## Section 2: Compact Currency `alt="alphaNextToNumber"` Selection (`#compact-currency-alphaNextToNumber`) + +* **TR35 Specification Link**: [`tr35-numbers.md#compact-currency-alphaNextToNumber`](../../../docs/ldml/tr35-numbers.md#compact-currency-alphaNextToNumber) (UTS #35 Part 3, Section 2.4.1: *Compact Number Formats*, step 4; L456–L461 at `f18139dfa2`) +* **Related specification text**: L445–L446 (definition of *letter grapheme cluster*), L448 (the examples use the currency symbol "$CA"), L454–L455 (step 3: a pattern of "0" uses non-compact formatting), L481–L489 (the "0" pattern), L553 ([non-compact `alt="alphaNextToNumber"` rule](../../../docs/ldml/tr35-numbers.md#currency-alphaNextToNumber)), and L1010 ([`currencySpacing`](../../../docs/ldml/tr35-numbers.md#currency-spacing), kept for implementations that do not support `alt="alphaNextToNumber"`) + +### 2.1 Verbatim Specification Snippet (`docs/ldml/tr35-numbers.md`) + +> 4. If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value. +> If ¤ is immediately to the left of a 0 and the currency string ends with a _letter grapheme cluster_ (eg, "$CA"), +> or to the right and the currency starts with a letter (eg, "CA$"), +> then switch to the `alt=alphaNextToNumber` pattern, if there is one. +> * P = `¤ 000K` // with the currency symbol "CA$" +> * V = "¤ 000K" + +--- + +### 2.2 Sentence-by-Sentence `(Dimension / Value)` Coverage Breakdown + +**CLDR data.** Short `currencyFormat` patterns (`latn`, `type="standard"`, `count="other"`), resolved through inheritance: + +| Locale | Pattern (`type`) | `alt="alphaNextToNumber"` | Position of `¤` | +| :--- | :--- | :--- | :--- | +| `en` | `¤0K` (1000), `¤0M` (1000000) | `¤ 0K` / `"¤\u00A00K"`, `¤ 0M` / `"¤\u00A00M"` | Left of `0` | +| `ja` | `0` (1000), `¤000万` (1000000) | 1000000: `¤ 000万` / `"¤\u00A0000万"` | Left of `0` (1000000) | +| `zh` | `0` (1000) | `¤ 0K` / `"¤\u00A00K"` | — (the pattern is `0`) | +| `kn` | `¤0ಸಾ` (1000) | `¤ 0ಸಾ` / `"¤\u00A00ಸಾ"` | Left of `0` | +| `id` | `¤0 rb` / `"¤0\u00A0rb"` (1000) | none | Left of `0` | +| `af` | `¤0 k` / `"¤0\u00A0k"` (1000) | none | Left of `0` | +| `bn` | `00 লা¤` / `"00\u00A0লা¤"` (1000000) | `00 লা ¤` / `"00\u00A0লা\u00A0¤"` | After the abbreviation `লা` | +| `km` | `0ពាន់¤` (1000) | `0ពាន់ ¤` / `"0ពាន់\u00A0¤"` | After the abbreviation `ពាន់` | +| `si` | `¤ද0` (1000) | `¤ ද0` / `"¤\u00A0ද0"` | Before the abbreviation `ද` | +| `de` | `0` (1000) | — | — (the pattern is `0`; non-compact `#,##0.00 ¤` / `"#,##0.00\u00A0¤"`) | + +`root` has no compact `alt="alphaNextToNumber"` patterns, so `id` and `af`, whose `type="1000"` alt is the inheritance marker `↑↑↑`, have none. A scan of `common/main` (all numbering systems, skipping `draft="provisional"` and `draft="unconfirmed"` values) finds: +* no compact currency pattern with `¤` immediately to the right of a `0`; +* four locales whose compact `alt` patterns put `¤` next to an abbreviation instead of a `0`: `bn`, `kab`, and `km` (after it) and `si` (before it). + +Currency strings used below: `en` USD `$`, RUB `RUB`, EGP `symbolNarrow` `E£`, CAD `CA$`, XCG `Cg.` (from `root`); `kn` RON `symbolNarrow` `ಲೀ` (U+0CB2 KANNADA LETTER LA, U+0CC0 KANNADA VOWEL SIGN II, General Category Mc); `id` IDR `Rp`; `af` ZAR `R`; `bn` USD `US$`. For `currency_display = "code"`, the ISO code takes the place of `¤`. + +| # | Verbatim Sentence / Normative Clause | Required `(Dimension = Value)` Combinations to Cover Clause | CLDR Data Evidence & Expected Behavior | +| :---: | :--- | :--- | :--- | +| **S2.1** | *"If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value."* | • **S2.1a**: class B where P is a compact pattern (e.g. `locale = "en"`, `input = 1234565.0`)
• **S2.1b**: class B where P is `"0"` but has an `alt` (`locale = "zh"`, `input = -1230.05`)
• **S2.1c**: compact decimal format (P is not a currency format) | • S2.1a: `en` P = `¤0M`; step 4 applies (S2.2–S2.4).
• S2.1b: step 3 (L454) comes first and skips step 4, so the `zh` alt `¤ 0K` / `"¤\u00A00K"` is never used. CNY `code` uses the non-compact pattern `¤#,##0.00`, which the non-compact rule (L553) switches to its alt `¤ #,##0.00` / `"¤\u00A0#,##0.00"`. The result has no `K`, unlike `-CNY 1.2K` / `"-CNY\u00A01.2K"`.
• S2.1c: step 4 does not apply. | +| **S2.2** | *"If ¤ is immediately to the left of a 0 and the currency string ends with a letter grapheme cluster (eg, "$CA"), [...] then switch to the `alt=alphaNextToNumber` pattern"* | `locale` with `¤` left of `0` (`en`, `ja`, `kn`), class B, P not `"0"`, and a currency string that ends with:
• **S2.2a**: a letter (`code`; `en` RUB `symbol`)
• **S2.2b**: a currency sign (`$`, `E£`, `CA$`)
• **S2.2c**: another non-letter (`en` XCG `Cg.`)
• **S2.2d**: a letter and a combining mark (`kn` RON `symbolNarrow` `ಲೀ`) | • S2.2a: switch. USD `code` 1234565.0: `en` → `USD 1.2M` / `"USD\u00A01.2M"`, `ja` → `USD 123万` / `"USD\u00A0123万"`. `en` RUB `symbol` → `RUB 1.2M` / `"RUB\u00A01.2M"`.
• S2.2b: no switch. `en` → `$1.2M`, `E£1.2M`, `-CA$1.2K`.
• S2.2c: no switch. `en` XCG −1230.05 → `-Cg.1.2K`.
• S2.2d: switch, because `ಲೀ` is a letter grapheme cluster (L445). `kn` RON −1230.05 → `-ಲೀ 1.2ಸಾ` / `"-ಲೀ\u00A01.2ಸಾ"`. | +| **S2.3** | *"...or to the right and the currency starts with a letter (eg, "CA$"),"* | • **S2.3a**: `¤` right of `0` × a currency string that starts with a letter
• **S2.3b**: `¤` next to a compact abbreviation, in a locale with an `alt` (`bn`, `km`, `si`) | • S2.3a: no CLDR data.
• S2.3b: the text only checks for a `0`, so no switch: `bn` USD `symbol` 1234565.0 → `১২ লাUS$` / `"১২\u00A0লাUS$"`; `km` BDT `code` −1230.05 → `-1.2ពាន់BDT`; `si` KWD `code` −1230.05 → `-KWDද1.2`. The alt patterns would give `১২ লা US$` / `"১২\u00A0লা\u00A0US$"`, `-1.2ពាន់ BDT` / `"-1.2ពាន់\u00A0BDT"`, and `-KWD ද1.2` / `"-KWD\u00A0ද1.2"`. | +| **S2.4** | *"...if there is one."* | • **S2.4a**: the locale has an `alt` (S2.2a)
• **S2.4b**: the locale has no `alt` (`id`, `af`) × a currency string that ends with a letter | • S2.4b: no switch. `id` IDR `symbol` −1230.05 → `-Rp1,2 rb` / `"-Rp1,2\u00A0rb"`; `af` ZAR `symbol` −1230.05 → `-R1,2 k` / `"-R1,2\u00A0k"`. | +| **S2.5** | Related text (L485): *"...then the normal number format pattern for that sort of object is supplied [...] with the normal formatting for the locale (such as the grouping separators)."* | • class B where P is `"0"` × a 4-digit `input` × a locale whose `minimumGroupingDigits` is 1 (`de`) | `de` (`minimumGroupingDigits` 1, inherited from `root`) EUR `symbol`: 1500.0 → `1.500 €` / `"1.500\u00A0€"`; 5000.0 → `5.000 €` / `"5.000\u00A0€"`. | + +--- + +### 2.3 Comparison Against `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) + +| Clause | Required `(Dimension = Value)` Combination | Status | Witness Row / Action Required | +| :---: | :--- | :---: | :--- | +| **S2.1a** | Class B × compact P | ✅ **Covered** | W2.2a rows below. | +| **S2.1b** | `locale = "zh"` × class B × `input = -1230.05` | ✅ **Covered** | W2.1b uses the non-compact pattern. Its missing grouping separator is S2.5. | +| **S2.1c** | Compact decimal format | ⚪ **Out of scope** | The generator produces currency formats only. | +| **S2.2a** | `¤` left of `0` × currency string ending with a letter | ✅ **Covered** | W2.2a rows below. | +| **S2.2b** | `¤` left of `0` × currency string ending with a currency sign | ✅ **Covered** | W2.2b rows below. | +| **S2.2c** | `locale = "en"` × `currency = "XCG"` (`Cg.`) × class B | 🔴 **Non-conformant** | W2.2c has a no-break space; step 4 gives `-Cg.1.2K`. The TSV follows the legacy `currencySpacing` rule (see 2.4). **Action**: see the Summary. | +| **S2.2d** | `locale = "kn"` × `currency = "RON"` × `symbolNarrow` × class B | 🟡 **Missing: dimension** | The generator pairs `kn` only with `INR`, `KHR`, `SBD`, and `TWD`, and no other locale with `¤` left of `0` has such a symbol (apart from `lo` GEL `alt="variant"`, which the generator does not use). **Action**: add this combination. | +| **S2.3a** | `¤` right of `0` | ⚪ **Out of scope** | No CLDR data. | +| **S2.3b** | `locale` = `bn`, `km`, `si` × class B | ❓ **Spec unclear** | W2.3b rows follow the text, which never selects these locales' `alt` patterns (see 2.4). | +| **S2.4a** | Locale with an `alt` | ✅ **Covered** | W2.2a rows below. | +| **S2.4b** | `locale` = `id`, `af` × currency string ending with a letter × class B | 🔴 **Non-conformant** | W2.4b rows have a no-break space after the currency string; step 4 gives `-Rp1,2 rb` / `"-Rp1,2\u00A0rb"` and `-R1,2 k` / `"-R1,2\u00A0k"`. The TSV follows the legacy `currencySpacing` rule (see 2.4). **Action**: see the Summary. | +| **S2.5** | `locale = "de"` × class B × `input` = `1500.0`, `5000.0` | 🔴 **Non-conformant** | W2.5 rows have no grouping separator; L485 gives `1.500 €` / `"1.500\u00A0€"`. ICU4J groups only numbers with 5 or more integer digits in compact notation. **Action**: decide whether L485 or the test data changes. | + +**Witness rows** at `5e1d4b0ee3` (all with `currency_format_length = "short"` and `currency_format_type = "standard"`): + +| Row | File | `locale` | `currency` | `currency_display` | `input` | `expected` | Escaped | +| :---: | :--- | :--- | :--- | :--- | ---: | :--- | :--- | +| W2.1b | `currencies_modern_locales.tsv` | `zh` | `CNY` | `code` | `-1230.05` | `-CNY 1230` | `"-CNY\u00A01230"` | +| W2.2a | `currencies.tsv` | `en` | `USD` | `code` | `1234565.0` | `USD 1.2M` | `"USD\u00A01.2M"` | +| W2.2a | `currencies.tsv` | `en` | `RUB` | `symbol` | `1234565.0` | `RUB 1.2M` | `"RUB\u00A01.2M"` | +| W2.2a | `currencies.tsv` | `ja` | `USD` | `code` | `1234565.0` | `USD 123万` | `"USD\u00A0123万"` | +| W2.2b | `currencies.tsv` | `en` | `USD` | `symbol` | `1234565.0` | `$1.2M` | `"$1.2M"` | +| W2.2b | `currencies.tsv` | `en` | `EGP` | `symbolNarrow` | `1234565.0` | `E£1.2M` | `"E£1.2M"` | +| W2.2b | `currencies_symbol_modern_currencies.tsv` | `en` | `CAD` | `symbol` | `-1230.05` | `-CA$1.2K` | `"-CA$1.2K"` | +| W2.2c | `currencies_symbol_modern_currencies.tsv` | `en` | `XCG` | `symbol` | `-1230.05` | `-Cg. 1.2K` | `"-Cg.\u00A01.2K"` | +| W2.3b | `currencies.tsv` | `bn` | `USD` | `symbol` | `1234565.0` | `১২ লাUS$` | `"১২\u00A0লাUS$"` | +| W2.3b | `currencies_code_modern_currencies.tsv` | `km` | `BDT` | `code` | `-1230.05` | `-1.2ពាន់BDT` | `"-1.2ពាន់BDT"` | +| W2.3b | `currencies_code_modern_currencies.tsv` | `si` | `KWD` | `code` | `-1230.05` | `-KWDද1.2` | `"-KWDද1.2"` | +| W2.4b | `currencies_modern_locales.tsv` | `id` | `IDR` | `symbol` | `-1230.05` | `-Rp 1,2 rb` | `"-Rp\u00A01,2\u00A0rb"` | +| W2.4b | `currencies_modern_locales.tsv` | `af` | `ZAR` | `symbol` | `-1230.05` | `-R 1,2 k` | `"-R\u00A01,2\u00A0k"` | +| W2.5 | `currencies_symbol_extended_numbers.tsv` | `de` | `EUR` | `symbol` | `1500.0` | `1500 €` | `"1500\u00A0€"` | +| W2.5 | `currencies_symbol_extended_numbers.tsv` | `de` | `EUR` | `symbol` | `5000.0` | `5000 €` | `"5000\u00A0€"` | + +### 2.4 Notes + +* **Legacy `currencySpacing` in the TSV files** (S2.2c, S2.4b): L1010 keeps `currencySpacing` for "implementations that may not yet support the `alt="alphaNextToNumber"` variant". The pinned ICU4J is one of them: its data has the `alphaNextToNumber` patterns (resource key `currencyFormat%alphaNextToNumber`), but no class in the jar references them. The TSV values follow the `root` `currencySpacing` rule instead: insert U+00A0 between the currency string and a digit when the character of the string next to the digit is in `[[:^S:]&[:^Z:]]`. Of the 1,284 `short` rows whose CLDR pattern has `¤` immediately left of a `0` (inputs of magnitude 1000 or more), 1,253 agree with step 4. The other 31 are 4 `XCG` rows (S2.2c), 15 `af` and `id` rows (S2.4b), and 12 `ha` rows (data vintage, below). +* **Data vintage**: the data in the pinned ICU4J predates the Survey Tool import in CLDR-19616 (`03725842ae`, 2026-07-28). For 4 of the 106 locales with `short` rows (`af`, `ha`, `mn`, `ta`), ICU's compact patterns are the ones from before that import, so they differ from `f18139dfa2`. For `ha`, this changes the step 4 result: CLDR now has `¤0K` with no `alt`, so CNY `code` −1230.05 → `-CNY1.2K`, but the TSV has `-CNY 1.2K` / `"-CNY\u00A01.2K"` from ICU's older `¤ 0K` / `"¤\u00A00K"`. This section does not use these rows as witnesses. +* **Spec unclear: abbreviations next to `¤`** (S2.3b): step 4 only checks for a `0` next to `¤`, so the compact `alt` patterns of `bn`, `kab`, `km`, and `si` are never selected, although their only change is a no-break space between `¤` and the abbreviation. The compact and non-compact rules also test different things: step 4 checks for a letter grapheme cluster (L445), while L553 checks the General Category of the character closest to the number. For `kn` RON `ಲೀ`, that character is U+0CC0 (Mc), so step 4 switches and the non-compact rule does not. Section 4 covers the non-compact rule. +* **Editorial issues in the snippet**: L460 says "CA$", but with `¤000K` the symbol "CA$" does not switch (W2.2b: `-CA$1.2K`); L448 and L477 use "$CA". L468 says "N = 123.456" where it means N′. L446 repeats "are". +* **Compact grouping** (S2.5, deferred from Section 1): the same ungrouped 4-digit values appear in the CORE rows for −1230.05 (`ja` USD `-$1230`, `de` EUR `-1230 €` / `"-1230\u00A0€"`, `de_CH` EUR `EUR-1230`) and in W2.1b. For −1230.05, the digits also depend on the precision of the "0" case, which L485 leaves open ("typically to 2 or 3 digits", so `1,200` or `1,230`); W2.5 therefore uses 1500.0 and 5000.0. Section 1's `de_AT` example (`-€ 1230` / `"-€\u00A01230"`) is the same case. +* **`zh`**: its `type="1000"` pattern is `0` but still has an `alt` (`¤ 0K` / `"¤\u00A00K"`), which step 3 makes unreachable (S2.1b). [CLDR-19632](https://unicode-org.atlassian.net/browse/CLDR-19632) audits compact `alphaNextToNumber` data. + +--- + ## Summary: Required Generator Changes | Change | Needed by | | :--- | :--- | | Add one locale with a monetary separator to `CORE_LOCALES` (+300 rows): `"fr_CH"` (recommended: unlike the `de_AT` override, it also shows with approved data only; see 1.2) or `"de_AT"` | S1.1a–c (`fr_CH`) or S1.3a–b (`de_AT`) | +| Add `kn` × `RON` × `symbolNarrow` × `short` (a currency symbol that ends with a combining mark, with `¤` left of `0`) | S2.2d | +| Produce the 19 `short` rows where step 4 and the legacy `currencySpacing` rule differ (`en` and `en_GB` × `XCG`, `af`, `id`) with step 4 instead of ICU4J's output | S2.2c, S2.4b | One such locale is enough to exercise the monetary separator override. `en` and the other CORE locales already cover the "otherwise" clauses (S1.2, S1.4). The clauses of the locale that is not added stay 🟡 **Missing: `locale`**. From 476280653b8ab2f7356f76e722923e256abaa3af Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 18:54:14 +0000 Subject: [PATCH 15/16] CLDR-19466 Check Section 2 coverage against the generator's CORE and extended dimensions - Apply the Section 1 method: write the clauses with currency_format_length instead of class B, and base each status on whether the needed values are CORE values, only extended values, or neither. - S2.1b (zh), S2.2c (XCG), S2.2d (kn and RON), and S2.4b (id) need values that are only extended values, so they are Missing: in CORE. S2.3b (bn) and S2.5 (de with -1230.05) are covered by CORE values. - Remove the Non-conformant statuses, the TSV witness rows, and the notes about the TSV values (legacy currencySpacing, ICU4J data vintage). Checking the expected values is a separate project. - Use CORE currencies in the zh and id examples, and list the CORE additions with their row counts in the Summary. --- .../currency/tr35_currency_test_coverage.md | 77 ++++++++----------- 1 file changed, 30 insertions(+), 47 deletions(-) diff --git a/common/testData/currency/tr35_currency_test_coverage.md b/common/testData/currency/tr35_currency_test_coverage.md index 59b0e1490c7..1abc74ab4fc 100644 --- a/common/testData/currency/tr35_currency_test_coverage.md +++ b/common/testData/currency/tr35_currency_test_coverage.md @@ -136,73 +136,52 @@ A section that needs a dimension the generator does not have adds it to this tab | `ja` | `0` (1000), `¤000万` (1000000) | 1000000: `¤ 000万` / `"¤\u00A0000万"` | Left of `0` (1000000) | | `zh` | `0` (1000) | `¤ 0K` / `"¤\u00A00K"` | — (the pattern is `0`) | | `kn` | `¤0ಸಾ` (1000) | `¤ 0ಸಾ` / `"¤\u00A00ಸಾ"` | Left of `0` | -| `id` | `¤0 rb` / `"¤0\u00A0rb"` (1000) | none | Left of `0` | +| `id` | `¤0 rb` / `"¤0\u00A0rb"` (1000), `¤0 jt` / `"¤0\u00A0jt"` (1000000) | none | Left of `0` | | `af` | `¤0 k` / `"¤0\u00A0k"` (1000) | none | Left of `0` | -| `bn` | `00 লা¤` / `"00\u00A0লা¤"` (1000000) | `00 লা ¤` / `"00\u00A0লা\u00A0¤"` | After the abbreviation `লা` | +| `ha` | `¤0K` (1000), `¤0M` (1000000) | none | Left of `0` | +| `bn` | `0 হা¤` / `"0\u00A0হা¤"` (1000), `00 লা¤` / `"00\u00A0লা¤"` (1000000) | `0 হা ¤` / `"0\u00A0হা\u00A0¤"`, `00 লা ¤` / `"00\u00A0লা\u00A0¤"` | After the abbreviation (`হা`, `লা`) | | `km` | `0ពាន់¤` (1000) | `0ពាន់ ¤` / `"0ពាន់\u00A0¤"` | After the abbreviation `ពាន់` | | `si` | `¤ද0` (1000) | `¤ ද0` / `"¤\u00A0ද0"` | Before the abbreviation `ද` | | `de` | `0` (1000) | — | — (the pattern is `0`; non-compact `#,##0.00 ¤` / `"#,##0.00\u00A0¤"`) | -`root` has no compact `alt="alphaNextToNumber"` patterns, so `id` and `af`, whose `type="1000"` alt is the inheritance marker `↑↑↑`, have none. A scan of `common/main` (all numbering systems, skipping `draft="provisional"` and `draft="unconfirmed"` values) finds: +`root` has no compact `alt="alphaNextToNumber"` patterns, so `id`, `af`, and `ha`, whose `type="1000"` alt is the inheritance marker `↑↑↑`, have none. A scan of `common/main` (all numbering systems, skipping `draft="provisional"` and `draft="unconfirmed"` values) finds: * no compact currency pattern with `¤` immediately to the right of a `0`; * four locales whose compact `alt` patterns put `¤` next to an abbreviation instead of a `0`: `bn`, `kab`, and `km` (after it) and `si` (before it). -Currency strings used below: `en` USD `$`, RUB `RUB`, EGP `symbolNarrow` `E£`, CAD `CA$`, XCG `Cg.` (from `root`); `kn` RON `symbolNarrow` `ಲೀ` (U+0CB2 KANNADA LETTER LA, U+0CC0 KANNADA VOWEL SIGN II, General Category Mc); `id` IDR `Rp`; `af` ZAR `R`; `bn` USD `US$`. For `currency_display = "code"`, the ISO code takes the place of `¤`. +Currency strings used below: `en` USD `$`, RUB `RUB`, EGP `symbolNarrow` `E£`, CAD `CA$`, XCG `Cg.`, and CHF `symbolNarrow` `Fr.` (the last two from `root`); `kn` RON `symbolNarrow` `ಲೀ` (U+0CB2 KANNADA LETTER LA, U+0CC0 KANNADA VOWEL SIGN II, General Category Mc); `af` ZAR `R`; `bn` USD `US$`. For `currency_display = "code"`, the ISO code takes the place of `¤`. | # | Verbatim Sentence / Normative Clause | Required `(Dimension = Value)` Combinations to Cover Clause | CLDR Data Evidence & Expected Behavior | | :---: | :--- | :--- | :--- | -| **S2.1** | *"If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value."* | • **S2.1a**: class B where P is a compact pattern (e.g. `locale = "en"`, `input = 1234565.0`)
• **S2.1b**: class B where P is `"0"` but has an `alt` (`locale = "zh"`, `input = -1230.05`)
• **S2.1c**: compact decimal format (P is not a currency format) | • S2.1a: `en` P = `¤0M`; step 4 applies (S2.2–S2.4).
• S2.1b: step 3 (L454) comes first and skips step 4, so the `zh` alt `¤ 0K` / `"¤\u00A00K"` is never used. CNY `code` uses the non-compact pattern `¤#,##0.00`, which the non-compact rule (L553) switches to its alt `¤ #,##0.00` / `"¤\u00A0#,##0.00"`. The result has no `K`, unlike `-CNY 1.2K` / `"-CNY\u00A01.2K"`.
• S2.1c: step 4 does not apply. | -| **S2.2** | *"If ¤ is immediately to the left of a 0 and the currency string ends with a letter grapheme cluster (eg, "$CA"), [...] then switch to the `alt=alphaNextToNumber` pattern"* | `locale` with `¤` left of `0` (`en`, `ja`, `kn`), class B, P not `"0"`, and a currency string that ends with:
• **S2.2a**: a letter (`code`; `en` RUB `symbol`)
• **S2.2b**: a currency sign (`$`, `E£`, `CA$`)
• **S2.2c**: another non-letter (`en` XCG `Cg.`)
• **S2.2d**: a letter and a combining mark (`kn` RON `symbolNarrow` `ಲೀ`) | • S2.2a: switch. USD `code` 1234565.0: `en` → `USD 1.2M` / `"USD\u00A01.2M"`, `ja` → `USD 123万` / `"USD\u00A0123万"`. `en` RUB `symbol` → `RUB 1.2M` / `"RUB\u00A01.2M"`.
• S2.2b: no switch. `en` → `$1.2M`, `E£1.2M`, `-CA$1.2K`.
• S2.2c: no switch. `en` XCG −1230.05 → `-Cg.1.2K`.
• S2.2d: switch, because `ಲೀ` is a letter grapheme cluster (L445). `kn` RON −1230.05 → `-ಲೀ 1.2ಸಾ` / `"-ಲೀ\u00A01.2ಸಾ"`. | +| **S2.1** | *"If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value."* | • **S2.1a**: `currency_format_length = "short"` where P is a compact pattern (e.g. `locale = "en"`, `input = 1234565.0`)
• **S2.1b**: `currency_format_length = "short"` where P is `"0"` but has an `alt` (`locale = "zh"`, `input = -1230.05`)
• **S2.1c**: compact decimal format (P is not a currency format) | • S2.1a: `en` P = `¤0M`; step 4 applies (S2.2–S2.4).
• S2.1b: step 3 (L454) comes first and skips step 4, so the `zh` alt `¤ 0K` / `"¤\u00A00K"` is never used. USD `code` uses the non-compact pattern `¤#,##0.00`, which the non-compact rule (L553) switches to its alt `¤ #,##0.00` / `"¤\u00A0#,##0.00"` (from `root`). The result has no `K`, unlike `-USD 1.2K` / `"-USD\u00A01.2K"`.
• S2.1c: step 4 does not apply. | +| **S2.2** | *"If ¤ is immediately to the left of a 0 and the currency string ends with a letter grapheme cluster (eg, "$CA"), [...] then switch to the `alt=alphaNextToNumber` pattern"* | `locale` with `¤` left of `0` (`en`, `ja`, `kn`), `currency_format_length = "short"`, P not `"0"`, and a currency string that ends with:
• **S2.2a**: a letter (`code`; `en` RUB `symbol`)
• **S2.2b**: a currency sign (`$`, `E£`, `CA$`)
• **S2.2c**: another non-letter (`en` XCG `Cg.`, CHF `symbolNarrow` `Fr.`)
• **S2.2d**: a letter and a combining mark (`kn` RON `symbolNarrow` `ಲೀ`) | • S2.2a: switch. USD `code` 1234565.0: `en` → `USD 1.2M` / `"USD\u00A01.2M"`, `ja` → `USD 123万` / `"USD\u00A0123万"`. `en` RUB `symbol` → `RUB 1.2M` / `"RUB\u00A01.2M"`.
• S2.2b: no switch. `en` → `$1.2M`, `E£1.2M`, `-CA$1.2K`.
• S2.2c: no switch. `en` −1230.05: XCG → `-Cg.1.2K`, CHF `symbolNarrow` → `-Fr.1.2K`.
• S2.2d: switch, because `ಲೀ` is a letter grapheme cluster (L445). `kn` RON −1230.05 → `-ಲೀ 1.2ಸಾ` / `"-ಲೀ\u00A01.2ಸಾ"`. | | **S2.3** | *"...or to the right and the currency starts with a letter (eg, "CA$"),"* | • **S2.3a**: `¤` right of `0` × a currency string that starts with a letter
• **S2.3b**: `¤` next to a compact abbreviation, in a locale with an `alt` (`bn`, `km`, `si`) | • S2.3a: no CLDR data.
• S2.3b: the text only checks for a `0`, so no switch: `bn` USD `symbol` 1234565.0 → `১২ লাUS$` / `"১২\u00A0লাUS$"`; `km` BDT `code` −1230.05 → `-1.2ពាន់BDT`; `si` KWD `code` −1230.05 → `-KWDද1.2`. The alt patterns would give `১২ লা US$` / `"১২\u00A0লা\u00A0US$"`, `-1.2ពាន់ BDT` / `"-1.2ពាន់\u00A0BDT"`, and `-KWD ද1.2` / `"-KWD\u00A0ද1.2"`. | -| **S2.4** | *"...if there is one."* | • **S2.4a**: the locale has an `alt` (S2.2a)
• **S2.4b**: the locale has no `alt` (`id`, `af`) × a currency string that ends with a letter | • S2.4b: no switch. `id` IDR `symbol` −1230.05 → `-Rp1,2 rb` / `"-Rp1,2\u00A0rb"`; `af` ZAR `symbol` −1230.05 → `-R1,2 k` / `"-R1,2\u00A0k"`. | -| **S2.5** | Related text (L485): *"...then the normal number format pattern for that sort of object is supplied [...] with the normal formatting for the locale (such as the grouping separators)."* | • class B where P is `"0"` × a 4-digit `input` × a locale whose `minimumGroupingDigits` is 1 (`de`) | `de` (`minimumGroupingDigits` 1, inherited from `root`) EUR `symbol`: 1500.0 → `1.500 €` / `"1.500\u00A0€"`; 5000.0 → `5.000 €` / `"5.000\u00A0€"`. | +| **S2.4** | *"...if there is one."* | • **S2.4a**: the locale has an `alt` (S2.2a)
• **S2.4b**: the locale has no `alt` (`id`, `af`, `ha`) × a currency string that ends with a letter | • S2.4b: no switch. `id` USD `code` −1230.05 → `-USD1,2 rb` / `"-USD1,2\u00A0rb"`; `af` ZAR `symbol` −1230.05 → `-R1,2 k` / `"-R1,2\u00A0k"`. | +| **S2.5** | Related text (L485): *"...then the normal number format pattern for that sort of object is supplied [...] with the normal formatting for the locale (such as the grouping separators)."* | • `currency_format_length = "short"` where P is `"0"` × `input` of magnitude ≥ 1000 × a locale whose `minimumGroupingDigits` is 1 (`locale = "de"`, `input = -1230.05`) | `de` (`minimumGroupingDigits` 1, inherited from `root`) EUR `symbol` −1230.05 → `-1.200 €` / `"-1.200\u00A0€"` or `-1.230 €` / `"-1.230\u00A0€"`: L485 sets the significant digits "typically to 2 or 3 digits", and both results are grouped. | --- ### 2.3 Comparison Against `GenerateCurrencyFormatTestData.java` (PR [#5808](https://github.com/unicode-org/cldr/pull/5808)) -| Clause | Required `(Dimension = Value)` Combination | Status | Witness Row / Action Required | +| Clause | Required `(Dimension = Value)` Combination | Status | Generator Evidence / Action Required | | :---: | :--- | :---: | :--- | -| **S2.1a** | Class B × compact P | ✅ **Covered** | W2.2a rows below. | -| **S2.1b** | `locale = "zh"` × class B × `input = -1230.05` | ✅ **Covered** | W2.1b uses the non-compact pattern. Its missing grouping separator is S2.5. | +| **S2.1a** | `locale` and `input` where P is a compact pattern × `currency_format_length = "short"` | ✅ **Covered** | CORE values: `en` in `CORE_LOCALES` with `-1230.05` and `1234565.0` in `CORE_NUMBERS` (P = `¤0K` and `¤0M`), and `ja` with `1234565.0` (P = `¤000万`). | +| **S2.1b** | `locale` and `input` where P is `"0"` but has an `alt` × `currency_format_length = "short"` | 🟡 **Missing: `locale` in CORE** | No CORE locale has such a P: `de`, `de_CH`, and `ja` have `"0"` for `type="1000"`, but no `alt`. `zh` is an extended locale value (`currencies_modern_locales.tsv`). **Action**: add `"zh"` to `CORE_LOCALES` (see the Summary). | | **S2.1c** | Compact decimal format | ⚪ **Out of scope** | The generator produces currency formats only. | -| **S2.2a** | `¤` left of `0` × currency string ending with a letter | ✅ **Covered** | W2.2a rows below. | -| **S2.2b** | `¤` left of `0` × currency string ending with a currency sign | ✅ **Covered** | W2.2b rows below. | -| **S2.2c** | `locale = "en"` × `currency = "XCG"` (`Cg.`) × class B | 🔴 **Non-conformant** | W2.2c has a no-break space; step 4 gives `-Cg.1.2K`. The TSV follows the legacy `currencySpacing` rule (see 2.4). **Action**: see the Summary. | -| **S2.2d** | `locale = "kn"` × `currency = "RON"` × `symbolNarrow` × class B | 🟡 **Missing: dimension** | The generator pairs `kn` only with `INR`, `KHR`, `SBD`, and `TWD`, and no other locale with `¤` left of `0` has such a symbol (apart from `lo` GEL `alt="variant"`, which the generator does not use). **Action**: add this combination. | +| **S2.2a** | `locale` with `¤` left of `0` × `currency_format_length = "short"` × a currency string that ends with a letter | ✅ **Covered** | CORE values: `en` and `ja` in `CORE_LOCALES`; `currency_display = "code"`, or `"symbol"` with `RUB` or `EGP` from `CORE_CURRENCIES`; `1234565.0` in `CORE_NUMBERS`. | +| **S2.2b** | `locale` with `¤` left of `0` × `currency_format_length = "short"` × a currency string that ends with a currency sign | ✅ **Covered** | CORE values: `en` and `ja`; `"symbol"` with `USD`, `EUR`, or `JPY`, or `"symbolNarrow"` with `EGP` (`E£`); `1234565.0`. | +| **S2.2c** | `locale` with `¤` left of `0` × `currency_format_length = "short"` × a currency string that ends with another non-letter | 🟡 **Missing: `currency` in CORE** | No CORE currency has such a string in `en` or `ja`. `XCG` (`Cg.` in `en`) is an extended currency value (`currencies_symbol_modern_currencies.tsv`, `currencies_narrow_modern_currencies.tsv`). `CHF` (`symbolNarrow` `Fr.`, added by [CLDR-19297](https://unicode-org.atlassian.net/browse/CLDR-19297)) would also work, but the pinned ICU4J does not have that symbol yet. **Action**: add `"XCG"` to `CORE_CURRENCIES` (see the Summary). | +| **S2.2d** | `locale = "kn"` × `currency = "RON"` × `currency_display = "symbolNarrow"` × `currency_format_length = "short"` | 🟡 **Missing: `locale` in CORE**
🟡 **Missing: `currency` in CORE** | `kn` and `RON` are extended values, but the generator never combines them: it pairs `kn` only with `INR`, `KHR`, `SBD`, and `TWD`, and `RON` only with `am`, `ar`, `cs`, `de`, `en`, and `ro`. No other locale with `¤` left of `0` has a currency string that ends with a combining mark (apart from `lo` GEL `alt="variant"`, which the generator does not use). **Action**: add `"kn"` to `CORE_LOCALES` and `"RON"` to `CORE_CURRENCIES` (see the Summary). | | **S2.3a** | `¤` right of `0` | ⚪ **Out of scope** | No CLDR data. | -| **S2.3b** | `locale` = `bn`, `km`, `si` × class B | ❓ **Spec unclear** | W2.3b rows follow the text, which never selects these locales' `alt` patterns (see 2.4). | -| **S2.4a** | Locale with an `alt` | ✅ **Covered** | W2.2a rows below. | -| **S2.4b** | `locale` = `id`, `af` × currency string ending with a letter × class B | 🔴 **Non-conformant** | W2.4b rows have a no-break space after the currency string; step 4 gives `-Rp1,2 rb` / `"-Rp1,2\u00A0rb"` and `-R1,2 k` / `"-R1,2\u00A0k"`. The TSV follows the legacy `currencySpacing` rule (see 2.4). **Action**: see the Summary. | -| **S2.5** | `locale = "de"` × class B × `input` = `1500.0`, `5000.0` | 🔴 **Non-conformant** | W2.5 rows have no grouping separator; L485 gives `1.500 €` / `"1.500\u00A0€"`. ICU4J groups only numbers with 5 or more integer digits in compact notation. **Action**: decide whether L485 or the test data changes. | - -**Witness rows** at `5e1d4b0ee3` (all with `currency_format_length = "short"` and `currency_format_type = "standard"`): - -| Row | File | `locale` | `currency` | `currency_display` | `input` | `expected` | Escaped | -| :---: | :--- | :--- | :--- | :--- | ---: | :--- | :--- | -| W2.1b | `currencies_modern_locales.tsv` | `zh` | `CNY` | `code` | `-1230.05` | `-CNY 1230` | `"-CNY\u00A01230"` | -| W2.2a | `currencies.tsv` | `en` | `USD` | `code` | `1234565.0` | `USD 1.2M` | `"USD\u00A01.2M"` | -| W2.2a | `currencies.tsv` | `en` | `RUB` | `symbol` | `1234565.0` | `RUB 1.2M` | `"RUB\u00A01.2M"` | -| W2.2a | `currencies.tsv` | `ja` | `USD` | `code` | `1234565.0` | `USD 123万` | `"USD\u00A0123万"` | -| W2.2b | `currencies.tsv` | `en` | `USD` | `symbol` | `1234565.0` | `$1.2M` | `"$1.2M"` | -| W2.2b | `currencies.tsv` | `en` | `EGP` | `symbolNarrow` | `1234565.0` | `E£1.2M` | `"E£1.2M"` | -| W2.2b | `currencies_symbol_modern_currencies.tsv` | `en` | `CAD` | `symbol` | `-1230.05` | `-CA$1.2K` | `"-CA$1.2K"` | -| W2.2c | `currencies_symbol_modern_currencies.tsv` | `en` | `XCG` | `symbol` | `-1230.05` | `-Cg. 1.2K` | `"-Cg.\u00A01.2K"` | -| W2.3b | `currencies.tsv` | `bn` | `USD` | `symbol` | `1234565.0` | `১২ লাUS$` | `"১২\u00A0লাUS$"` | -| W2.3b | `currencies_code_modern_currencies.tsv` | `km` | `BDT` | `code` | `-1230.05` | `-1.2ពាន់BDT` | `"-1.2ពាន់BDT"` | -| W2.3b | `currencies_code_modern_currencies.tsv` | `si` | `KWD` | `code` | `-1230.05` | `-KWDද1.2` | `"-KWDද1.2"` | -| W2.4b | `currencies_modern_locales.tsv` | `id` | `IDR` | `symbol` | `-1230.05` | `-Rp 1,2 rb` | `"-Rp\u00A01,2\u00A0rb"` | -| W2.4b | `currencies_modern_locales.tsv` | `af` | `ZAR` | `symbol` | `-1230.05` | `-R 1,2 k` | `"-R\u00A01,2\u00A0k"` | -| W2.5 | `currencies_symbol_extended_numbers.tsv` | `de` | `EUR` | `symbol` | `1500.0` | `1500 €` | `"1500\u00A0€"` | -| W2.5 | `currencies_symbol_extended_numbers.tsv` | `de` | `EUR` | `symbol` | `5000.0` | `5000 €` | `"5000\u00A0€"` | +| **S2.3b** | `locale` with `¤` next to a compact abbreviation and an `alt` × `currency_format_length = "short"` | ✅ **Covered** | CORE values: `bn` in `CORE_LOCALES`, for both `-1230.05` and `1234565.0` (see the table in 2.2). Step 4 never selects these `alt` patterns (see 2.4). | +| **S2.4a** | `locale` with an `alt` | ✅ **Covered** | CORE values: `en` and `ja` (S2.2a). | +| **S2.4b** | `locale` with `¤` left of `0` and no `alt` × `currency_format_length = "short"` × a currency string that ends with a letter | 🟡 **Missing: `locale` in CORE** | No CORE locale has such a P. `id`, `af`, and `ha` are extended locale values (`currencies_modern_locales.tsv`); `cop`, `ee`, `om`, and `quc` are in neither. **Action**: add `"id"` to `CORE_LOCALES` (see the Summary). | +| **S2.5** | `locale` and `input` of magnitude ≥ 1000 where P is `"0"` × `currency_format_length = "short"` | ✅ **Covered** | CORE values: `de` and `de_CH` in `CORE_LOCALES` (`"0"` for `type="1000"`, `minimumGroupingDigits` 1) with `-1230.05` in `CORE_NUMBERS`. | ### 2.4 Notes -* **Legacy `currencySpacing` in the TSV files** (S2.2c, S2.4b): L1010 keeps `currencySpacing` for "implementations that may not yet support the `alt="alphaNextToNumber"` variant". The pinned ICU4J is one of them: its data has the `alphaNextToNumber` patterns (resource key `currencyFormat%alphaNextToNumber`), but no class in the jar references them. The TSV values follow the `root` `currencySpacing` rule instead: insert U+00A0 between the currency string and a digit when the character of the string next to the digit is in `[[:^S:]&[:^Z:]]`. Of the 1,284 `short` rows whose CLDR pattern has `¤` immediately left of a `0` (inputs of magnitude 1000 or more), 1,253 agree with step 4. The other 31 are 4 `XCG` rows (S2.2c), 15 `af` and `id` rows (S2.4b), and 12 `ha` rows (data vintage, below). -* **Data vintage**: the data in the pinned ICU4J predates the Survey Tool import in CLDR-19616 (`03725842ae`, 2026-07-28). For 4 of the 106 locales with `short` rows (`af`, `ha`, `mn`, `ta`), ICU's compact patterns are the ones from before that import, so they differ from `f18139dfa2`. For `ha`, this changes the step 4 result: CLDR now has `¤0K` with no `alt`, so CNY `code` −1230.05 → `-CNY1.2K`, but the TSV has `-CNY 1.2K` / `"-CNY\u00A01.2K"` from ICU's older `¤ 0K` / `"¤\u00A00K"`. This section does not use these rows as witnesses. -* **Spec unclear: abbreviations next to `¤`** (S2.3b): step 4 only checks for a `0` next to `¤`, so the compact `alt` patterns of `bn`, `kab`, `km`, and `si` are never selected, although their only change is a no-break space between `¤` and the abbreviation. The compact and non-compact rules also test different things: step 4 checks for a letter grapheme cluster (L445), while L553 checks the General Category of the character closest to the number. For `kn` RON `ಲೀ`, that character is U+0CC0 (Mc), so step 4 switches and the non-compact rule does not. Section 4 covers the non-compact rule. -* **Editorial issues in the snippet**: L460 says "CA$", but with `¤000K` the symbol "CA$" does not switch (W2.2b: `-CA$1.2K`); L448 and L477 use "$CA". L468 says "N = 123.456" where it means N′. L446 repeats "are". -* **Compact grouping** (S2.5, deferred from Section 1): the same ungrouped 4-digit values appear in the CORE rows for −1230.05 (`ja` USD `-$1230`, `de` EUR `-1230 €` / `"-1230\u00A0€"`, `de_CH` EUR `EUR-1230`) and in W2.1b. For −1230.05, the digits also depend on the precision of the "0" case, which L485 leaves open ("typically to 2 or 3 digits", so `1,200` or `1,230`); W2.5 therefore uses 1500.0 and 5000.0. Section 1's `de_AT` example (`-€ 1230` / `"-€\u00A01230"`) is the same case. +* **Abbreviations next to `¤`** (S2.3b): step 4 only checks for a `0` next to `¤`, so the compact `alt` patterns of `bn`, `kab`, `km`, and `si` are never selected, although their only change is a no-break space between `¤` and the abbreviation. The specification does not say whether this is intended. +* **Letter test** (S2.2d): step 4 checks for a letter grapheme cluster (L445), while the non-compact rule (L553) checks the General Category of the character closest to the number. For `kn` RON `ಲೀ`, that character is U+0CC0 (Mc), so step 4 switches and the non-compact rule does not. Section 4 covers the non-compact rule. +* **Editorial issues in the snippet**: L460 says "CA$", but with `¤000K` the symbol "CA$" does not switch (`en` CAD: `-CA$1.2K`); L448 and L477 use "$CA". L468 says "N = 123.456" where it means N′. L446 repeats "are". * **`zh`**: its `type="1000"` pattern is `0` but still has an `alt` (`¤ 0K` / `"¤\u00A00K"`), which step 3 makes unreachable (S2.1b). [CLDR-19632](https://unicode-org.atlassian.net/browse/CLDR-19632) audits compact `alphaNextToNumber` data. --- @@ -212,7 +191,11 @@ Currency strings used below: `en` USD `$`, RUB `RUB`, EGP `symbolNarrow` `E£`, | Change | Needed by | | :--- | :--- | | Add one locale with a monetary separator to `CORE_LOCALES` (+300 rows): `"fr_CH"` (recommended: unlike the `de_AT` override, it also shows with approved data only; see 1.2) or `"de_AT"` | S1.1a–c (`fr_CH`) or S1.3a–b (`de_AT`) | -| Add `kn` × `RON` × `symbolNarrow` × `short` (a currency symbol that ends with a combining mark, with `¤` left of `0`) | S2.2d | -| Produce the 19 `short` rows where step 4 and the legacy `currencySpacing` rule differ (`en` and `en_GB` × `XCG`, `af`, `id`) with step 4 instead of ICU4J's output | S2.2c, S2.4b | +| Add `"zh"` to `CORE_LOCALES` (+300 rows) | S2.1b | +| Add `"XCG"` to `CORE_CURRENCIES` (+600 rows); `"CHF"` also works once ICU4J has its `symbolNarrow` `Fr.` | S2.2c | +| Add `"kn"` to `CORE_LOCALES` and `"RON"` to `CORE_CURRENCIES` (+960 rows) | S2.2d | +| Add `"id"` to `CORE_LOCALES` (+300 rows); `af` and `ha` also qualify, but the pinned ICU4J has older compact patterns for them | S2.4b | + +For Section 1, one locale with a monetary separator is enough to exercise the override. `en` and the other CORE locales already cover the "otherwise" clauses (S1.2, S1.4). The clauses of the locale that is not added stay 🟡 **Missing: `locale`**. -One such locale is enough to exercise the monetary separator override. `en` and the other CORE locales already cover the "otherwise" clauses (S1.2, S1.4). The clauses of the locale that is not added stay 🟡 **Missing: `locale`**. +The row counts are for `currencies.tsv` and for each change alone. The file now has 10 locales × 5 currencies × 12 combinations of the format and display dimensions × 5 inputs = 3,000 rows; with all the changes above, it would have 14 × 7 × 12 × 5 = 5,880. From 7b7a05a56b6c007fc49a87ebb24613ba16937816 Mon Sep 17 00:00:00 2001 From: Younies Mahmoud Date: Tue, 29 Sep 2026 18:54:16 +0000 Subject: [PATCH 16/16] CLDR-19466 Update Section 2 for the plain TR35 headings of #6169 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #6169 now uses plain headings with generated anchors and adds 17 lines to the table of contents of tr35-numbers.md. In the text added by this PR: - Link to `#Compact_Number_Formats` (the compact step no longer has its own anchor), and update the other links to the new anchors. - Quote the specification text as it is at 2997bffaf0 (without inline anchors). - Shift the tr35-numbers.md line numbers by 17 and pin them to 2997bffaf0. - Restore the U+00A0 NO-BREAK SPACE of the spec in the quoted `¤ 000K` example. --- .../currency/tr35_currency_test_coverage.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/common/testData/currency/tr35_currency_test_coverage.md b/common/testData/currency/tr35_currency_test_coverage.md index 1abc74ab4fc..3c6e830d83e 100644 --- a/common/testData/currency/tr35_currency_test_coverage.md +++ b/common/testData/currency/tr35_currency_test_coverage.md @@ -110,18 +110,18 @@ A section that needs a dimension the generator does not have adds it to this tab --- -## Section 2: Compact Currency `alt="alphaNextToNumber"` Selection (`#compact-currency-alphaNextToNumber`) +## Section 2: Compact Currency `alt="alphaNextToNumber"` Selection (`#Compact_Number_Formats`, step 4) -* **TR35 Specification Link**: [`tr35-numbers.md#compact-currency-alphaNextToNumber`](../../../docs/ldml/tr35-numbers.md#compact-currency-alphaNextToNumber) (UTS #35 Part 3, Section 2.4.1: *Compact Number Formats*, step 4; L456–L461 at `f18139dfa2`) -* **Related specification text**: L445–L446 (definition of *letter grapheme cluster*), L448 (the examples use the currency symbol "$CA"), L454–L455 (step 3: a pattern of "0" uses non-compact formatting), L481–L489 (the "0" pattern), L553 ([non-compact `alt="alphaNextToNumber"` rule](../../../docs/ldml/tr35-numbers.md#currency-alphaNextToNumber)), and L1010 ([`currencySpacing`](../../../docs/ldml/tr35-numbers.md#currency-spacing), kept for implementations that do not support `alt="alphaNextToNumber"`) +* **TR35 Specification Link**: [`tr35-numbers.md#Compact_Number_Formats`](../../../docs/ldml/tr35-numbers.md#Compact_Number_Formats) (UTS #35 Part 3, Section 2.4.1: *Compact Number Formats*, step 4; L473–L478 at `2997bffaf0`) +* **Related specification text**: L462–L463 (definition of *letter grapheme cluster*), L465 (the examples use the currency symbol "$CA"), L471–L472 (step 3: a pattern of "0" uses non-compact formatting), L498–L506 (the "0" pattern), L570 ([non-compact `alt="alphaNextToNumber"` rule](../../../docs/ldml/tr35-numbers.md#the-altalphanexttonumber-pattern-variant)), and L1027 ([`currencySpacing`](../../../docs/ldml/tr35-numbers.md#currency-boundary-spacing-currencyspacing), kept for implementations that do not support `alt="alphaNextToNumber"`) ### 2.1 Verbatim Specification Snippet (`docs/ldml/tr35-numbers.md`) -> 4. If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value. +> 4. If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value. > If ¤ is immediately to the left of a 0 and the currency string ends with a _letter grapheme cluster_ (eg, "$CA"), > or to the right and the currency starts with a letter (eg, "CA$"), > then switch to the `alt=alphaNextToNumber` pattern, if there is one. -> * P = `¤ 000K` // with the currency symbol "CA$" +> * P = `¤ 000K` // with the currency symbol "CA$" > * V = "¤ 000K" --- @@ -152,11 +152,11 @@ Currency strings used below: `en` USD `$`, RUB `RUB`, EGP `symbolNarrow` `E£`, | # | Verbatim Sentence / Normative Clause | Required `(Dimension = Value)` Combinations to Cover Clause | CLDR Data Evidence & Expected Behavior | | :---: | :--- | :--- | :--- | -| **S2.1** | *"If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value."* | • **S2.1a**: `currency_format_length = "short"` where P is a compact pattern (e.g. `locale = "en"`, `input = 1234565.0`)
• **S2.1b**: `currency_format_length = "short"` where P is `"0"` but has an `alt` (`locale = "zh"`, `input = -1230.05`)
• **S2.1c**: compact decimal format (P is not a currency format) | • S2.1a: `en` P = `¤0M`; step 4 applies (S2.2–S2.4).
• S2.1b: step 3 (L454) comes first and skips step 4, so the `zh` alt `¤ 0K` / `"¤\u00A00K"` is never used. USD `code` uses the non-compact pattern `¤#,##0.00`, which the non-compact rule (L553) switches to its alt `¤ #,##0.00` / `"¤\u00A0#,##0.00"` (from `root`). The result has no `K`, unlike `-USD 1.2K` / `"-USD\u00A01.2K"`.
• S2.1c: step 4 does not apply. | -| **S2.2** | *"If ¤ is immediately to the left of a 0 and the currency string ends with a letter grapheme cluster (eg, "$CA"), [...] then switch to the `alt=alphaNextToNumber` pattern"* | `locale` with `¤` left of `0` (`en`, `ja`, `kn`), `currency_format_length = "short"`, P not `"0"`, and a currency string that ends with:
• **S2.2a**: a letter (`code`; `en` RUB `symbol`)
• **S2.2b**: a currency sign (`$`, `E£`, `CA$`)
• **S2.2c**: another non-letter (`en` XCG `Cg.`, CHF `symbolNarrow` `Fr.`)
• **S2.2d**: a letter and a combining mark (`kn` RON `symbolNarrow` `ಲೀ`) | • S2.2a: switch. USD `code` 1234565.0: `en` → `USD 1.2M` / `"USD\u00A01.2M"`, `ja` → `USD 123万` / `"USD\u00A0123万"`. `en` RUB `symbol` → `RUB 1.2M` / `"RUB\u00A01.2M"`.
• S2.2b: no switch. `en` → `$1.2M`, `E£1.2M`, `-CA$1.2K`.
• S2.2c: no switch. `en` −1230.05: XCG → `-Cg.1.2K`, CHF `symbolNarrow` → `-Fr.1.2K`.
• S2.2d: switch, because `ಲೀ` is a letter grapheme cluster (L445). `kn` RON −1230.05 → `-ಲೀ 1.2ಸಾ` / `"-ಲೀ\u00A01.2ಸಾ"`. | +| **S2.1** | *"If P is a currency format, look at the currency symbol string, and the position of the currency symbol ¤ in the pattern element value."* | • **S2.1a**: `currency_format_length = "short"` where P is a compact pattern (e.g. `locale = "en"`, `input = 1234565.0`)
• **S2.1b**: `currency_format_length = "short"` where P is `"0"` but has an `alt` (`locale = "zh"`, `input = -1230.05`)
• **S2.1c**: compact decimal format (P is not a currency format) | • S2.1a: `en` P = `¤0M`; step 4 applies (S2.2–S2.4).
• S2.1b: step 3 (L471) comes first and skips step 4, so the `zh` alt `¤ 0K` / `"¤\u00A00K"` is never used. USD `code` uses the non-compact pattern `¤#,##0.00`, which the non-compact rule (L570) switches to its alt `¤ #,##0.00` / `"¤\u00A0#,##0.00"` (from `root`). The result has no `K`, unlike `-USD 1.2K` / `"-USD\u00A01.2K"`.
• S2.1c: step 4 does not apply. | +| **S2.2** | *"If ¤ is immediately to the left of a 0 and the currency string ends with a letter grapheme cluster (eg, "$CA"), [...] then switch to the `alt=alphaNextToNumber` pattern"* | `locale` with `¤` left of `0` (`en`, `ja`, `kn`), `currency_format_length = "short"`, P not `"0"`, and a currency string that ends with:
• **S2.2a**: a letter (`code`; `en` RUB `symbol`)
• **S2.2b**: a currency sign (`$`, `E£`, `CA$`)
• **S2.2c**: another non-letter (`en` XCG `Cg.`, CHF `symbolNarrow` `Fr.`)
• **S2.2d**: a letter and a combining mark (`kn` RON `symbolNarrow` `ಲೀ`) | • S2.2a: switch. USD `code` 1234565.0: `en` → `USD 1.2M` / `"USD\u00A01.2M"`, `ja` → `USD 123万` / `"USD\u00A0123万"`. `en` RUB `symbol` → `RUB 1.2M` / `"RUB\u00A01.2M"`.
• S2.2b: no switch. `en` → `$1.2M`, `E£1.2M`, `-CA$1.2K`.
• S2.2c: no switch. `en` −1230.05: XCG → `-Cg.1.2K`, CHF `symbolNarrow` → `-Fr.1.2K`.
• S2.2d: switch, because `ಲೀ` is a letter grapheme cluster (L462). `kn` RON −1230.05 → `-ಲೀ 1.2ಸಾ` / `"-ಲೀ\u00A01.2ಸಾ"`. | | **S2.3** | *"...or to the right and the currency starts with a letter (eg, "CA$"),"* | • **S2.3a**: `¤` right of `0` × a currency string that starts with a letter
• **S2.3b**: `¤` next to a compact abbreviation, in a locale with an `alt` (`bn`, `km`, `si`) | • S2.3a: no CLDR data.
• S2.3b: the text only checks for a `0`, so no switch: `bn` USD `symbol` 1234565.0 → `১২ লাUS$` / `"১২\u00A0লাUS$"`; `km` BDT `code` −1230.05 → `-1.2ពាន់BDT`; `si` KWD `code` −1230.05 → `-KWDද1.2`. The alt patterns would give `১২ লা US$` / `"১২\u00A0লা\u00A0US$"`, `-1.2ពាន់ BDT` / `"-1.2ពាន់\u00A0BDT"`, and `-KWD ද1.2` / `"-KWD\u00A0ද1.2"`. | | **S2.4** | *"...if there is one."* | • **S2.4a**: the locale has an `alt` (S2.2a)
• **S2.4b**: the locale has no `alt` (`id`, `af`, `ha`) × a currency string that ends with a letter | • S2.4b: no switch. `id` USD `code` −1230.05 → `-USD1,2 rb` / `"-USD1,2\u00A0rb"`; `af` ZAR `symbol` −1230.05 → `-R1,2 k` / `"-R1,2\u00A0k"`. | -| **S2.5** | Related text (L485): *"...then the normal number format pattern for that sort of object is supplied [...] with the normal formatting for the locale (such as the grouping separators)."* | • `currency_format_length = "short"` where P is `"0"` × `input` of magnitude ≥ 1000 × a locale whose `minimumGroupingDigits` is 1 (`locale = "de"`, `input = -1230.05`) | `de` (`minimumGroupingDigits` 1, inherited from `root`) EUR `symbol` −1230.05 → `-1.200 €` / `"-1.200\u00A0€"` or `-1.230 €` / `"-1.230\u00A0€"`: L485 sets the significant digits "typically to 2 or 3 digits", and both results are grouped. | +| **S2.5** | Related text (L502): *"...then the normal number format pattern for that sort of object is supplied [...] with the normal formatting for the locale (such as the grouping separators)."* | • `currency_format_length = "short"` where P is `"0"` × `input` of magnitude ≥ 1000 × a locale whose `minimumGroupingDigits` is 1 (`locale = "de"`, `input = -1230.05`) | `de` (`minimumGroupingDigits` 1, inherited from `root`) EUR `symbol` −1230.05 → `-1.200 €` / `"-1.200\u00A0€"` or `-1.230 €` / `"-1.230\u00A0€"`: L502 sets the significant digits "typically to 2 or 3 digits", and both results are grouped. | --- @@ -180,8 +180,8 @@ Currency strings used below: `en` USD `$`, RUB `RUB`, EGP `symbolNarrow` `E£`, ### 2.4 Notes * **Abbreviations next to `¤`** (S2.3b): step 4 only checks for a `0` next to `¤`, so the compact `alt` patterns of `bn`, `kab`, `km`, and `si` are never selected, although their only change is a no-break space between `¤` and the abbreviation. The specification does not say whether this is intended. -* **Letter test** (S2.2d): step 4 checks for a letter grapheme cluster (L445), while the non-compact rule (L553) checks the General Category of the character closest to the number. For `kn` RON `ಲೀ`, that character is U+0CC0 (Mc), so step 4 switches and the non-compact rule does not. Section 4 covers the non-compact rule. -* **Editorial issues in the snippet**: L460 says "CA$", but with `¤000K` the symbol "CA$" does not switch (`en` CAD: `-CA$1.2K`); L448 and L477 use "$CA". L468 says "N = 123.456" where it means N′. L446 repeats "are". +* **Letter test** (S2.2d): step 4 checks for a letter grapheme cluster (L462), while the non-compact rule (L570) checks the General Category of the character closest to the number. For `kn` RON `ಲೀ`, that character is U+0CC0 (Mc), so step 4 switches and the non-compact rule does not. Section 4 covers the non-compact rule. +* **Editorial issues in the snippet**: L477 says "CA$", but with `¤000K` the symbol "CA$" does not switch (`en` CAD: `-CA$1.2K`); L465 and L494 use "$CA". L485 says "N = 123.456" where it means N′. L463 repeats "are". * **`zh`**: its `type="1000"` pattern is `0` but still has an `alt` (`¤ 0K` / `"¤\u00A00K"`), which step 3 makes unreachable (S2.1b). [CLDR-19632](https://unicode-org.atlassian.net/browse/CLDR-19632) audits compact `alphaNextToNumber` data. ---