|
28 | 28 | * - **The mapping** ({@link currencyForLocale}): the tag's region, or, for a |
29 | 29 | * bare language, the region `Intl.Locale#maximize()` gives it (`zh` → `CN`, |
30 | 30 | * `ja` → `JP`, `en` → `US`), then that region's currency from |
31 | | - * {@link REGION_CURRENCY}, a checked-in CLDR snapshot. A tag with no country |
| 31 | + * `REGION_CURRENCY`, a checked-in CLDR snapshot. Both live in |
| 32 | + * `@objectstack/spec/shared` (`region-currency.ts`), which the console's |
| 33 | + * workspace prompt reads too, so there is one table. A tag with no country |
32 | 34 | * region (`es-419`, `en-150`) or a region with no current currency maps to |
33 | 35 | * nothing. |
34 | 36 | * - **Nothing to derive from ⇒ nothing is written.** There is no fallback to |
|
48 | 50 |
|
49 | 51 | import { tryGetCurrentAuthEndpointContext } from '@better-auth/core/context'; |
50 | 52 | import type { IDataEngine } from '@objectstack/spec/contracts'; |
| 53 | +import { currencyForLocale } from '@objectstack/spec/shared'; |
51 | 54 | import { preferredLocaleFromHeader, SystemObjectName } from '@objectstack/spec/system'; |
52 | 55 | import { acceptLanguageHeader } from './auth-manager.js'; |
53 | 56 |
|
54 | | -/** |
55 | | - * Region (ISO 3166-1 alpha-2 and the CLDR territory codes beside it) → the |
56 | | - * region's current ISO 4217 currency. |
57 | | - * |
58 | | - * ## Provenance: CLDR `currencyData`, checked in, not hand-written |
59 | | - * |
60 | | - * Generated on 2026-10-24 from CLDR 48 `supplemental/currencyData.json` |
61 | | - * (`cldr-core@48.0.0` on npm, sha256 of that file |
62 | | - * `ab007c5bb654a11601d6328f73a9b5b504334c556f3edb760020d6e2cc408472`), the |
63 | | - * same CLDR release as `CURRENCY_FRACTION_DIGITS` in `@objectstack/spec`. |
64 | | - * For each two-letter region the value is the FIRST entry of its |
65 | | - * `currencyData.region` list that has no `_to` date and is not |
66 | | - * `_tender: "false"`. CLDR orders that list so the first current tender is the |
67 | | - * region's primary currency (`PS` → `ILS` ahead of `JOD`). Entries are kept |
68 | | - * only when the code is in the `iso_4217_currency` value domain, the set the |
69 | | - * settings key accepts. Eleven regions have no current tender and are absent: |
70 | | - * `AQ` and `ZZ` (`XXX`), and the retired `BU` `CP` `CS` `DD` `SU` `TP` `YD` |
71 | | - * `YU` `ZR`. |
72 | | - * |
73 | | - * `Intl` has no region → currency lookup, which is why this is a table. To |
74 | | - * refresh it for a later CLDR, regenerate it by the rule above and update this |
75 | | - * provenance block. Do not edit entries by hand. |
76 | | - */ |
77 | | -export const REGION_CURRENCY: Readonly<Record<string, string>> = { |
78 | | - AC: 'SHP', AD: 'EUR', AE: 'AED', AF: 'AFN', AG: 'XCD', AI: 'XCD', AL: 'ALL', AM: 'AMD', |
79 | | - AO: 'AOA', AR: 'ARS', AS: 'USD', AT: 'EUR', AU: 'AUD', AW: 'AWG', AX: 'EUR', AZ: 'AZN', |
80 | | - BA: 'BAM', BB: 'BBD', BD: 'BDT', BE: 'EUR', BF: 'XOF', BG: 'EUR', BH: 'BHD', BI: 'BIF', |
81 | | - BJ: 'XOF', BL: 'EUR', BM: 'BMD', BN: 'BND', BO: 'BOB', BQ: 'USD', BR: 'BRL', BS: 'BSD', |
82 | | - BT: 'BTN', BV: 'NOK', BW: 'BWP', BY: 'BYN', BZ: 'BZD', CA: 'CAD', CC: 'AUD', CD: 'CDF', |
83 | | - CF: 'XAF', CG: 'XAF', CH: 'CHF', CI: 'XOF', CK: 'NZD', CL: 'CLP', CM: 'XAF', CN: 'CNY', |
84 | | - CO: 'COP', CR: 'CRC', CU: 'CUP', CV: 'CVE', CW: 'XCG', CX: 'AUD', CY: 'EUR', CZ: 'CZK', |
85 | | - DE: 'EUR', DG: 'USD', DJ: 'DJF', DK: 'DKK', DM: 'XCD', DO: 'DOP', DZ: 'DZD', EA: 'EUR', |
86 | | - EC: 'USD', EE: 'EUR', EG: 'EGP', EH: 'MAD', ER: 'ERN', ES: 'EUR', ET: 'ETB', EU: 'EUR', |
87 | | - FI: 'EUR', FJ: 'FJD', FK: 'FKP', FM: 'USD', FO: 'DKK', FR: 'EUR', GA: 'XAF', GB: 'GBP', |
88 | | - GD: 'XCD', GE: 'GEL', GF: 'EUR', GG: 'GBP', GH: 'GHS', GI: 'GIP', GL: 'DKK', GM: 'GMD', |
89 | | - GN: 'GNF', GP: 'EUR', GQ: 'XAF', GR: 'EUR', GS: 'GBP', GT: 'GTQ', GU: 'USD', GW: 'XOF', |
90 | | - GY: 'GYD', HK: 'HKD', HM: 'AUD', HN: 'HNL', HR: 'EUR', HT: 'HTG', HU: 'HUF', IC: 'EUR', |
91 | | - ID: 'IDR', IE: 'EUR', IL: 'ILS', IM: 'GBP', IN: 'INR', IO: 'USD', IQ: 'IQD', IR: 'IRR', |
92 | | - IS: 'ISK', IT: 'EUR', JE: 'GBP', JM: 'JMD', JO: 'JOD', JP: 'JPY', KE: 'KES', KG: 'KGS', |
93 | | - KH: 'KHR', KI: 'AUD', KM: 'KMF', KN: 'XCD', KP: 'KPW', KR: 'KRW', KW: 'KWD', KY: 'KYD', |
94 | | - KZ: 'KZT', LA: 'LAK', LB: 'LBP', LC: 'XCD', LI: 'CHF', LK: 'LKR', LR: 'LRD', LS: 'ZAR', |
95 | | - LT: 'EUR', LU: 'EUR', LV: 'EUR', LY: 'LYD', MA: 'MAD', MC: 'EUR', MD: 'MDL', ME: 'EUR', |
96 | | - MF: 'EUR', MG: 'MGA', MH: 'USD', MK: 'MKD', ML: 'XOF', MM: 'MMK', MN: 'MNT', MO: 'MOP', |
97 | | - MP: 'USD', MQ: 'EUR', MR: 'MRU', MS: 'XCD', MT: 'EUR', MU: 'MUR', MV: 'MVR', MW: 'MWK', |
98 | | - MX: 'MXN', MY: 'MYR', MZ: 'MZN', NA: 'NAD', NC: 'XPF', NE: 'XOF', NF: 'AUD', NG: 'NGN', |
99 | | - NI: 'NIO', NL: 'EUR', NO: 'NOK', NP: 'NPR', NR: 'AUD', NU: 'NZD', NZ: 'NZD', OM: 'OMR', |
100 | | - PA: 'PAB', PE: 'PEN', PF: 'XPF', PG: 'PGK', PH: 'PHP', PK: 'PKR', PL: 'PLN', PM: 'EUR', |
101 | | - PN: 'NZD', PR: 'USD', PS: 'ILS', PT: 'EUR', PW: 'USD', PY: 'PYG', QA: 'QAR', RE: 'EUR', |
102 | | - RO: 'RON', RS: 'RSD', RU: 'RUB', RW: 'RWF', SA: 'SAR', SB: 'SBD', SC: 'SCR', SD: 'SDG', |
103 | | - SE: 'SEK', SG: 'SGD', SH: 'SHP', SI: 'EUR', SJ: 'NOK', SK: 'EUR', SL: 'SLE', SM: 'EUR', |
104 | | - SN: 'XOF', SO: 'SOS', SR: 'SRD', SS: 'SSP', ST: 'STN', SV: 'USD', SX: 'XCG', SY: 'SYP', |
105 | | - SZ: 'SZL', TA: 'GBP', TC: 'USD', TD: 'XAF', TF: 'EUR', TG: 'XOF', TH: 'THB', TJ: 'TJS', |
106 | | - TK: 'NZD', TL: 'USD', TM: 'TMT', TN: 'TND', TO: 'TOP', TR: 'TRY', TT: 'TTD', TV: 'AUD', |
107 | | - TW: 'TWD', TZ: 'TZS', UA: 'UAH', UG: 'UGX', UM: 'USD', US: 'USD', UY: 'UYU', UZ: 'UZS', |
108 | | - VA: 'EUR', VC: 'XCD', VE: 'VES', VG: 'USD', VI: 'USD', VN: 'VND', VU: 'VUV', WF: 'XPF', |
109 | | - WS: 'WST', XK: 'EUR', YE: 'YER', YT: 'EUR', ZA: 'ZAR', ZM: 'ZMW', ZW: 'ZWG', |
110 | | -}; |
111 | | - |
112 | | -/** |
113 | | - * The ISO 4217 currency of a BCP 47 tag's region, or `undefined`. |
114 | | - * |
115 | | - * The region is the tag's own (`zh-CN` → `CN`), or for a tag without one the |
116 | | - * likely region `Intl.Locale#maximize()` gives (`zh` → `CN`, `ja` → `JP`, |
117 | | - * `zh-Hant` → `TW`). A malformed tag, a tag whose region is not a country |
118 | | - * (`es-419`, `en-150`), and a region with no current currency all answer |
119 | | - * `undefined`: no guess is made. |
120 | | - */ |
121 | | -export function currencyForLocale(tag: unknown): string | undefined { |
122 | | - if (typeof tag !== 'string') return undefined; |
123 | | - const value = tag.trim(); |
124 | | - if (!value) return undefined; |
125 | | - let region: string | undefined; |
126 | | - try { |
127 | | - region = new Intl.Locale(value).maximize().region; |
128 | | - } catch { |
129 | | - return undefined; |
130 | | - } |
131 | | - if (!region || !Object.prototype.hasOwnProperty.call(REGION_CURRENCY, region)) return undefined; |
132 | | - return REGION_CURRENCY[region]; |
133 | | -} |
134 | | - |
135 | 57 | /** |
136 | 58 | * The host's locale as a BCP 47 tag, read from the POSIX environment, or |
137 | 59 | * `undefined`. |
|
0 commit comments