508 lines
19 KiB
TypeScript
508 lines
19 KiB
TypeScript
import { useSettingsStore } from '../stores/settings'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Types
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface LocaleOption {
|
|
code: string
|
|
name: string
|
|
}
|
|
|
|
export interface CurrencyOption {
|
|
code: string
|
|
name: string
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LOCALES — ~140 worldwide locale/region combinations
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const LOCALES: LocaleOption[] = [
|
|
// System default
|
|
{ code: 'system', name: 'System Default' },
|
|
|
|
// English variants
|
|
{ code: 'en-US', name: 'English (United States)' },
|
|
{ code: 'en-GB', name: 'English (United Kingdom)' },
|
|
{ code: 'en-AU', name: 'English (Australia)' },
|
|
{ code: 'en-CA', name: 'English (Canada)' },
|
|
{ code: 'en-NZ', name: 'English (New Zealand)' },
|
|
{ code: 'en-IE', name: 'English (Ireland)' },
|
|
{ code: 'en-IN', name: 'English (India)' },
|
|
{ code: 'en-ZA', name: 'English (South Africa)' },
|
|
{ code: 'en-SG', name: 'English (Singapore)' },
|
|
{ code: 'en-HK', name: 'English (Hong Kong)' },
|
|
{ code: 'en-PH', name: 'English (Philippines)' },
|
|
{ code: 'en-NG', name: 'English (Nigeria)' },
|
|
{ code: 'en-KE', name: 'English (Kenya)' },
|
|
{ code: 'en-GH', name: 'English (Ghana)' },
|
|
{ code: 'en-PK', name: 'English (Pakistan)' },
|
|
|
|
// Spanish variants
|
|
{ code: 'es-ES', name: 'Spanish (Spain)' },
|
|
{ code: 'es-MX', name: 'Spanish (Mexico)' },
|
|
{ code: 'es-AR', name: 'Spanish (Argentina)' },
|
|
{ code: 'es-CO', name: 'Spanish (Colombia)' },
|
|
{ code: 'es-CL', name: 'Spanish (Chile)' },
|
|
{ code: 'es-PE', name: 'Spanish (Peru)' },
|
|
{ code: 'es-VE', name: 'Spanish (Venezuela)' },
|
|
{ code: 'es-EC', name: 'Spanish (Ecuador)' },
|
|
{ code: 'es-GT', name: 'Spanish (Guatemala)' },
|
|
{ code: 'es-CU', name: 'Spanish (Cuba)' },
|
|
{ code: 'es-BO', name: 'Spanish (Bolivia)' },
|
|
{ code: 'es-DO', name: 'Spanish (Dominican Republic)' },
|
|
{ code: 'es-HN', name: 'Spanish (Honduras)' },
|
|
{ code: 'es-PY', name: 'Spanish (Paraguay)' },
|
|
{ code: 'es-SV', name: 'Spanish (El Salvador)' },
|
|
{ code: 'es-NI', name: 'Spanish (Nicaragua)' },
|
|
{ code: 'es-CR', name: 'Spanish (Costa Rica)' },
|
|
{ code: 'es-PA', name: 'Spanish (Panama)' },
|
|
{ code: 'es-UY', name: 'Spanish (Uruguay)' },
|
|
{ code: 'es-PR', name: 'Spanish (Puerto Rico)' },
|
|
|
|
// Portuguese variants
|
|
{ code: 'pt-BR', name: 'Portuguese (Brazil)' },
|
|
{ code: 'pt-PT', name: 'Portuguese (Portugal)' },
|
|
{ code: 'pt-AO', name: 'Portuguese (Angola)' },
|
|
{ code: 'pt-MZ', name: 'Portuguese (Mozambique)' },
|
|
|
|
// French variants
|
|
{ code: 'fr-FR', name: 'French (France)' },
|
|
{ code: 'fr-BE', name: 'French (Belgium)' },
|
|
{ code: 'fr-CA', name: 'French (Canada)' },
|
|
{ code: 'fr-CH', name: 'French (Switzerland)' },
|
|
{ code: 'fr-LU', name: 'French (Luxembourg)' },
|
|
{ code: 'fr-SN', name: 'French (Senegal)' },
|
|
{ code: 'fr-CI', name: "French (C\u00f4te d'Ivoire)" },
|
|
{ code: 'fr-CM', name: 'French (Cameroon)' },
|
|
{ code: 'fr-CD', name: 'French (Congo - Kinshasa)' },
|
|
{ code: 'fr-MA', name: 'French (Morocco)' },
|
|
{ code: 'fr-TN', name: 'French (Tunisia)' },
|
|
{ code: 'fr-DZ', name: 'French (Algeria)' },
|
|
|
|
// German variants
|
|
{ code: 'de-DE', name: 'German (Germany)' },
|
|
{ code: 'de-AT', name: 'German (Austria)' },
|
|
{ code: 'de-CH', name: 'German (Switzerland)' },
|
|
{ code: 'de-LU', name: 'German (Luxembourg)' },
|
|
{ code: 'de-LI', name: 'German (Liechtenstein)' },
|
|
|
|
// Italian variants
|
|
{ code: 'it-IT', name: 'Italian (Italy)' },
|
|
{ code: 'it-CH', name: 'Italian (Switzerland)' },
|
|
|
|
// Dutch variants
|
|
{ code: 'nl-NL', name: 'Dutch (Netherlands)' },
|
|
{ code: 'nl-BE', name: 'Dutch (Belgium)' },
|
|
|
|
// Scandinavian
|
|
{ code: 'sv-SE', name: 'Swedish (Sweden)' },
|
|
{ code: 'sv-FI', name: 'Swedish (Finland)' },
|
|
{ code: 'nb-NO', name: 'Norwegian Bokm\u00e5l (Norway)' },
|
|
{ code: 'nn-NO', name: 'Norwegian Nynorsk (Norway)' },
|
|
{ code: 'da-DK', name: 'Danish (Denmark)' },
|
|
{ code: 'fi-FI', name: 'Finnish (Finland)' },
|
|
{ code: 'is-IS', name: 'Icelandic (Iceland)' },
|
|
|
|
// Baltic
|
|
{ code: 'et-EE', name: 'Estonian (Estonia)' },
|
|
{ code: 'lv-LV', name: 'Latvian (Latvia)' },
|
|
{ code: 'lt-LT', name: 'Lithuanian (Lithuania)' },
|
|
|
|
// Central / Eastern Europe
|
|
{ code: 'pl-PL', name: 'Polish (Poland)' },
|
|
{ code: 'cs-CZ', name: 'Czech (Czech Republic)' },
|
|
{ code: 'sk-SK', name: 'Slovak (Slovakia)' },
|
|
{ code: 'hu-HU', name: 'Hungarian (Hungary)' },
|
|
{ code: 'ro-RO', name: 'Romanian (Romania)' },
|
|
{ code: 'bg-BG', name: 'Bulgarian (Bulgaria)' },
|
|
{ code: 'hr-HR', name: 'Croatian (Croatia)' },
|
|
{ code: 'sr-RS', name: 'Serbian (Serbia)' },
|
|
{ code: 'sl-SI', name: 'Slovenian (Slovenia)' },
|
|
{ code: 'bs-BA', name: 'Bosnian (Bosnia and Herzegovina)' },
|
|
{ code: 'mk-MK', name: 'Macedonian (North Macedonia)' },
|
|
{ code: 'sq-AL', name: 'Albanian (Albania)' },
|
|
|
|
// Eastern Europe / Central Asia
|
|
{ code: 'ru-RU', name: 'Russian (Russia)' },
|
|
{ code: 'uk-UA', name: 'Ukrainian (Ukraine)' },
|
|
{ code: 'be-BY', name: 'Belarusian (Belarus)' },
|
|
{ code: 'ka-GE', name: 'Georgian (Georgia)' },
|
|
{ code: 'hy-AM', name: 'Armenian (Armenia)' },
|
|
{ code: 'az-AZ', name: 'Azerbaijani (Azerbaijan)' },
|
|
{ code: 'kk-KZ', name: 'Kazakh (Kazakhstan)' },
|
|
{ code: 'uz-UZ', name: 'Uzbek (Uzbekistan)' },
|
|
|
|
// Greek / Turkish
|
|
{ code: 'el-GR', name: 'Greek (Greece)' },
|
|
{ code: 'el-CY', name: 'Greek (Cyprus)' },
|
|
{ code: 'tr-TR', name: 'Turkish (Turkey)' },
|
|
|
|
// Arabic variants
|
|
{ code: 'ar-SA', name: 'Arabic (Saudi Arabia)' },
|
|
{ code: 'ar-EG', name: 'Arabic (Egypt)' },
|
|
{ code: 'ar-AE', name: 'Arabic (United Arab Emirates)' },
|
|
{ code: 'ar-MA', name: 'Arabic (Morocco)' },
|
|
{ code: 'ar-DZ', name: 'Arabic (Algeria)' },
|
|
{ code: 'ar-TN', name: 'Arabic (Tunisia)' },
|
|
{ code: 'ar-IQ', name: 'Arabic (Iraq)' },
|
|
{ code: 'ar-JO', name: 'Arabic (Jordan)' },
|
|
{ code: 'ar-LB', name: 'Arabic (Lebanon)' },
|
|
{ code: 'ar-KW', name: 'Arabic (Kuwait)' },
|
|
{ code: 'ar-QA', name: 'Arabic (Qatar)' },
|
|
{ code: 'ar-BH', name: 'Arabic (Bahrain)' },
|
|
{ code: 'ar-OM', name: 'Arabic (Oman)' },
|
|
{ code: 'ar-YE', name: 'Arabic (Yemen)' },
|
|
{ code: 'ar-LY', name: 'Arabic (Libya)' },
|
|
{ code: 'ar-SD', name: 'Arabic (Sudan)' },
|
|
{ code: 'ar-SY', name: 'Arabic (Syria)' },
|
|
{ code: 'ar-PS', name: 'Arabic (Palestine)' },
|
|
|
|
// Hebrew / Persian
|
|
{ code: 'he-IL', name: 'Hebrew (Israel)' },
|
|
{ code: 'fa-IR', name: 'Persian (Iran)' },
|
|
{ code: 'fa-AF', name: 'Persian (Afghanistan)' },
|
|
{ code: 'ps-AF', name: 'Pashto (Afghanistan)' },
|
|
|
|
// South Asian
|
|
{ code: 'hi-IN', name: 'Hindi (India)' },
|
|
{ code: 'bn-BD', name: 'Bengali (Bangladesh)' },
|
|
{ code: 'bn-IN', name: 'Bengali (India)' },
|
|
{ code: 'ta-IN', name: 'Tamil (India)' },
|
|
{ code: 'ta-LK', name: 'Tamil (Sri Lanka)' },
|
|
{ code: 'te-IN', name: 'Telugu (India)' },
|
|
{ code: 'mr-IN', name: 'Marathi (India)' },
|
|
{ code: 'gu-IN', name: 'Gujarati (India)' },
|
|
{ code: 'kn-IN', name: 'Kannada (India)' },
|
|
{ code: 'ml-IN', name: 'Malayalam (India)' },
|
|
{ code: 'pa-IN', name: 'Punjabi (India)' },
|
|
{ code: 'or-IN', name: 'Odia (India)' },
|
|
{ code: 'as-IN', name: 'Assamese (India)' },
|
|
{ code: 'ur-PK', name: 'Urdu (Pakistan)' },
|
|
{ code: 'si-LK', name: 'Sinhala (Sri Lanka)' },
|
|
{ code: 'ne-NP', name: 'Nepali (Nepal)' },
|
|
{ code: 'my-MM', name: 'Burmese (Myanmar)' },
|
|
|
|
// Southeast Asian
|
|
{ code: 'th-TH', name: 'Thai (Thailand)' },
|
|
{ code: 'vi-VN', name: 'Vietnamese (Vietnam)' },
|
|
{ code: 'id-ID', name: 'Indonesian (Indonesia)' },
|
|
{ code: 'ms-MY', name: 'Malay (Malaysia)' },
|
|
{ code: 'ms-SG', name: 'Malay (Singapore)' },
|
|
{ code: 'fil-PH', name: 'Filipino (Philippines)' },
|
|
{ code: 'km-KH', name: 'Khmer (Cambodia)' },
|
|
{ code: 'lo-LA', name: 'Lao (Laos)' },
|
|
|
|
// East Asian
|
|
{ code: 'zh-CN', name: 'Chinese (Simplified, China)' },
|
|
{ code: 'zh-TW', name: 'Chinese (Traditional, Taiwan)' },
|
|
{ code: 'zh-HK', name: 'Chinese (Traditional, Hong Kong)' },
|
|
{ code: 'zh-SG', name: 'Chinese (Simplified, Singapore)' },
|
|
{ code: 'ja-JP', name: 'Japanese (Japan)' },
|
|
{ code: 'ko-KR', name: 'Korean (South Korea)' },
|
|
{ code: 'mn-MN', name: 'Mongolian (Mongolia)' },
|
|
|
|
// African
|
|
{ code: 'sw-KE', name: 'Swahili (Kenya)' },
|
|
{ code: 'sw-TZ', name: 'Swahili (Tanzania)' },
|
|
{ code: 'am-ET', name: 'Amharic (Ethiopia)' },
|
|
{ code: 'af-ZA', name: 'Afrikaans (South Africa)' },
|
|
{ code: 'zu-ZA', name: 'Zulu (South Africa)' },
|
|
{ code: 'xh-ZA', name: 'Xhosa (South Africa)' },
|
|
{ code: 'yo-NG', name: 'Yoruba (Nigeria)' },
|
|
{ code: 'ig-NG', name: 'Igbo (Nigeria)' },
|
|
{ code: 'ha-NG', name: 'Hausa (Nigeria)' },
|
|
{ code: 'rw-RW', name: 'Kinyarwanda (Rwanda)' },
|
|
{ code: 'so-SO', name: 'Somali (Somalia)' },
|
|
{ code: 'mg-MG', name: 'Malagasy (Madagascar)' },
|
|
|
|
// Iberian minority
|
|
{ code: 'ca-ES', name: 'Catalan (Spain)' },
|
|
{ code: 'eu-ES', name: 'Basque (Spain)' },
|
|
{ code: 'gl-ES', name: 'Galician (Spain)' },
|
|
|
|
// Celtic
|
|
{ code: 'cy-GB', name: 'Welsh (United Kingdom)' },
|
|
{ code: 'ga-IE', name: 'Irish (Ireland)' },
|
|
{ code: 'gd-GB', name: 'Scottish Gaelic (United Kingdom)' },
|
|
|
|
// Oceanian
|
|
{ code: 'mi-NZ', name: 'M\u0101ori (New Zealand)' },
|
|
{ code: 'to-TO', name: 'Tongan (Tonga)' },
|
|
{ code: 'sm-WS', name: 'Samoan (Samoa)' },
|
|
{ code: 'fj-FJ', name: 'Fijian (Fiji)' },
|
|
]
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// FALLBACK_CURRENCIES — ~120+ currencies with English names
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const FALLBACK_CURRENCIES: CurrencyOption[] = [
|
|
{ code: 'USD', name: 'US Dollar' },
|
|
{ code: 'EUR', name: 'Euro' },
|
|
{ code: 'GBP', name: 'British Pound' },
|
|
{ code: 'JPY', name: 'Japanese Yen' },
|
|
{ code: 'CHF', name: 'Swiss Franc' },
|
|
{ code: 'CAD', name: 'Canadian Dollar' },
|
|
{ code: 'AUD', name: 'Australian Dollar' },
|
|
{ code: 'NZD', name: 'New Zealand Dollar' },
|
|
{ code: 'CNY', name: 'Chinese Yuan' },
|
|
{ code: 'HKD', name: 'Hong Kong Dollar' },
|
|
{ code: 'SGD', name: 'Singapore Dollar' },
|
|
{ code: 'TWD', name: 'New Taiwan Dollar' },
|
|
{ code: 'KRW', name: 'South Korean Won' },
|
|
{ code: 'INR', name: 'Indian Rupee' },
|
|
{ code: 'IDR', name: 'Indonesian Rupiah' },
|
|
{ code: 'MYR', name: 'Malaysian Ringgit' },
|
|
{ code: 'PHP', name: 'Philippine Peso' },
|
|
{ code: 'THB', name: 'Thai Baht' },
|
|
{ code: 'VND', name: 'Vietnamese Dong' },
|
|
{ code: 'BDT', name: 'Bangladeshi Taka' },
|
|
{ code: 'PKR', name: 'Pakistani Rupee' },
|
|
{ code: 'LKR', name: 'Sri Lankan Rupee' },
|
|
{ code: 'NPR', name: 'Nepalese Rupee' },
|
|
{ code: 'MMK', name: 'Myanmar Kyat' },
|
|
{ code: 'KHR', name: 'Cambodian Riel' },
|
|
{ code: 'LAK', name: 'Lao Kip' },
|
|
{ code: 'MNT', name: 'Mongolian Tugrik' },
|
|
{ code: 'KZT', name: 'Kazakhstani Tenge' },
|
|
{ code: 'UZS', name: 'Uzbekistani Som' },
|
|
{ code: 'AZN', name: 'Azerbaijani Manat' },
|
|
{ code: 'GEL', name: 'Georgian Lari' },
|
|
{ code: 'AMD', name: 'Armenian Dram' },
|
|
{ code: 'BRL', name: 'Brazilian Real' },
|
|
{ code: 'MXN', name: 'Mexican Peso' },
|
|
{ code: 'ARS', name: 'Argentine Peso' },
|
|
{ code: 'COP', name: 'Colombian Peso' },
|
|
{ code: 'CLP', name: 'Chilean Peso' },
|
|
{ code: 'PEN', name: 'Peruvian Sol' },
|
|
{ code: 'VES', name: 'Venezuelan Bol\u00edvar' },
|
|
{ code: 'UYU', name: 'Uruguayan Peso' },
|
|
{ code: 'PYG', name: 'Paraguayan Guarani' },
|
|
{ code: 'BOB', name: 'Bolivian Boliviano' },
|
|
{ code: 'CRC', name: 'Costa Rican Col\u00f3n' },
|
|
{ code: 'PAB', name: 'Panamanian Balboa' },
|
|
{ code: 'DOP', name: 'Dominican Peso' },
|
|
{ code: 'GTQ', name: 'Guatemalan Quetzal' },
|
|
{ code: 'HNL', name: 'Honduran Lempira' },
|
|
{ code: 'NIO', name: 'Nicaraguan C\u00f3rdoba' },
|
|
{ code: 'SVC', name: 'Salvadoran Col\u00f3n' },
|
|
{ code: 'CUP', name: 'Cuban Peso' },
|
|
{ code: 'JMD', name: 'Jamaican Dollar' },
|
|
{ code: 'TTD', name: 'Trinidad and Tobago Dollar' },
|
|
{ code: 'BBD', name: 'Barbadian Dollar' },
|
|
{ code: 'HTG', name: 'Haitian Gourde' },
|
|
{ code: 'SEK', name: 'Swedish Krona' },
|
|
{ code: 'NOK', name: 'Norwegian Krone' },
|
|
{ code: 'DKK', name: 'Danish Krone' },
|
|
{ code: 'ISK', name: 'Icelandic Kr\u00f3na' },
|
|
{ code: 'PLN', name: 'Polish Zloty' },
|
|
{ code: 'CZK', name: 'Czech Koruna' },
|
|
{ code: 'HUF', name: 'Hungarian Forint' },
|
|
{ code: 'RON', name: 'Romanian Leu' },
|
|
{ code: 'BGN', name: 'Bulgarian Lev' },
|
|
{ code: 'HRK', name: 'Croatian Kuna' },
|
|
{ code: 'RSD', name: 'Serbian Dinar' },
|
|
{ code: 'BAM', name: 'Bosnia-Herzegovina Convertible Mark' },
|
|
{ code: 'MKD', name: 'Macedonian Denar' },
|
|
{ code: 'ALL', name: 'Albanian Lek' },
|
|
{ code: 'RUB', name: 'Russian Ruble' },
|
|
{ code: 'UAH', name: 'Ukrainian Hryvnia' },
|
|
{ code: 'BYN', name: 'Belarusian Ruble' },
|
|
{ code: 'MDL', name: 'Moldovan Leu' },
|
|
{ code: 'TRY', name: 'Turkish Lira' },
|
|
{ code: 'ILS', name: 'Israeli New Shekel' },
|
|
{ code: 'SAR', name: 'Saudi Riyal' },
|
|
{ code: 'AED', name: 'United Arab Emirates Dirham' },
|
|
{ code: 'QAR', name: 'Qatari Riyal' },
|
|
{ code: 'KWD', name: 'Kuwaiti Dinar' },
|
|
{ code: 'BHD', name: 'Bahraini Dinar' },
|
|
{ code: 'OMR', name: 'Omani Rial' },
|
|
{ code: 'JOD', name: 'Jordanian Dinar' },
|
|
{ code: 'LBP', name: 'Lebanese Pound' },
|
|
{ code: 'IQD', name: 'Iraqi Dinar' },
|
|
{ code: 'SYP', name: 'Syrian Pound' },
|
|
{ code: 'YER', name: 'Yemeni Rial' },
|
|
{ code: 'IRR', name: 'Iranian Rial' },
|
|
{ code: 'AFN', name: 'Afghan Afghani' },
|
|
{ code: 'EGP', name: 'Egyptian Pound' },
|
|
{ code: 'LYD', name: 'Libyan Dinar' },
|
|
{ code: 'SDG', name: 'Sudanese Pound' },
|
|
{ code: 'MAD', name: 'Moroccan Dirham' },
|
|
{ code: 'TND', name: 'Tunisian Dinar' },
|
|
{ code: 'DZD', name: 'Algerian Dinar' },
|
|
{ code: 'ZAR', name: 'South African Rand' },
|
|
{ code: 'NGN', name: 'Nigerian Naira' },
|
|
{ code: 'KES', name: 'Kenyan Shilling' },
|
|
{ code: 'GHS', name: 'Ghanaian Cedi' },
|
|
{ code: 'TZS', name: 'Tanzanian Shilling' },
|
|
{ code: 'UGX', name: 'Ugandan Shilling' },
|
|
{ code: 'ETB', name: 'Ethiopian Birr' },
|
|
{ code: 'RWF', name: 'Rwandan Franc' },
|
|
{ code: 'SOS', name: 'Somali Shilling' },
|
|
{ code: 'MGA', name: 'Malagasy Ariary' },
|
|
{ code: 'MUR', name: 'Mauritian Rupee' },
|
|
{ code: 'SCR', name: 'Seychellois Rupee' },
|
|
{ code: 'XOF', name: 'West African CFA Franc' },
|
|
{ code: 'XAF', name: 'Central African CFA Franc' },
|
|
{ code: 'CDF', name: 'Congolese Franc' },
|
|
{ code: 'AOA', name: 'Angolan Kwanza' },
|
|
{ code: 'MZN', name: 'Mozambican Metical' },
|
|
{ code: 'ZMW', name: 'Zambian Kwacha' },
|
|
{ code: 'BWP', name: 'Botswanan Pula' },
|
|
{ code: 'NAD', name: 'Namibian Dollar' },
|
|
{ code: 'SZL', name: 'Swazi Lilangeni' },
|
|
{ code: 'LSL', name: 'Lesotho Loti' },
|
|
{ code: 'GMD', name: 'Gambian Dalasi' },
|
|
{ code: 'SLL', name: 'Sierra Leonean Leone' },
|
|
{ code: 'GNF', name: 'Guinean Franc' },
|
|
{ code: 'LRD', name: 'Liberian Dollar' },
|
|
{ code: 'CVE', name: 'Cape Verdean Escudo' },
|
|
{ code: 'XPF', name: 'CFP Franc' },
|
|
{ code: 'FJD', name: 'Fijian Dollar' },
|
|
{ code: 'TOP', name: 'Tongan Pa\u02bbanga' },
|
|
{ code: 'WST', name: 'Samoan Tala' },
|
|
{ code: 'PGK', name: 'Papua New Guinean Kina' },
|
|
{ code: 'SBD', name: 'Solomon Islands Dollar' },
|
|
{ code: 'VUV', name: 'Vanuatu Vatu' },
|
|
{ code: 'BND', name: 'Brunei Dollar' },
|
|
]
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Currency builder (runtime Intl detection with fallback)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
let _currencyCache: CurrencyOption[] | null = null
|
|
|
|
function buildCurrencies(): CurrencyOption[] {
|
|
try {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const codes: string[] = (Intl as any).supportedValuesOf('currency')
|
|
const displayNames = new Intl.DisplayNames(['en'], { type: 'currency' })
|
|
return codes.map((code) => ({
|
|
code,
|
|
name: displayNames.of(code) ?? code,
|
|
}))
|
|
} catch {
|
|
return FALLBACK_CURRENCIES
|
|
}
|
|
}
|
|
|
|
export function getCurrencies(): CurrencyOption[] {
|
|
if (!_currencyCache) {
|
|
_currencyCache = buildCurrencies()
|
|
}
|
|
return _currencyCache
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Locale / currency resolution helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function getLocaleCode(): string {
|
|
const store = useSettingsStore()
|
|
const locale = store.settings.locale
|
|
if (locale && locale !== 'system') {
|
|
return locale
|
|
}
|
|
return navigator.language || 'en-US'
|
|
}
|
|
|
|
export function getCurrencyCode(): string {
|
|
const store = useSettingsStore()
|
|
const currency = store.settings.currency
|
|
if (currency) {
|
|
return currency
|
|
}
|
|
return 'USD'
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Formatting functions
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Short date — e.g. "Jan 5, 2025"
|
|
* Parses the date-part manually to avoid timezone-shift issues.
|
|
*/
|
|
export function formatDate(dateString: string): string {
|
|
const [year, month, day] = dateString.substring(0, 10).split('-').map(Number)
|
|
const date = new Date(year, month - 1, day)
|
|
return new Intl.DateTimeFormat(getLocaleCode(), {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: 'numeric',
|
|
}).format(date)
|
|
}
|
|
|
|
/**
|
|
* Long date — e.g. "Monday, January 5, 2025"
|
|
*/
|
|
export function formatDateLong(dateString: string): string {
|
|
const [year, month, day] = dateString.substring(0, 10).split('-').map(Number)
|
|
const date = new Date(year, month - 1, day)
|
|
return new Intl.DateTimeFormat(getLocaleCode(), {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
}).format(date)
|
|
}
|
|
|
|
/**
|
|
* Short date + time — e.g. "Jan 5, 2025, 3:42 PM"
|
|
*/
|
|
export function formatDateTime(dateString: string): string {
|
|
const date = new Date(dateString)
|
|
return new Intl.DateTimeFormat(getLocaleCode(), {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: 'numeric',
|
|
hour: 'numeric',
|
|
minute: 'numeric',
|
|
}).format(date)
|
|
}
|
|
|
|
/**
|
|
* Full currency format — e.g. "$1,234.56"
|
|
*/
|
|
export function formatCurrency(amount: number): string {
|
|
return new Intl.NumberFormat(getLocaleCode(), {
|
|
style: 'currency',
|
|
currency: getCurrencyCode(),
|
|
}).format(amount)
|
|
}
|
|
|
|
/**
|
|
* Compact currency format (same as formatCurrency, kept for API completeness).
|
|
*/
|
|
export function formatCurrencyCompact(amount: number): string {
|
|
return new Intl.NumberFormat(getLocaleCode(), {
|
|
style: 'currency',
|
|
currency: getCurrencyCode(),
|
|
}).format(amount)
|
|
}
|
|
|
|
/**
|
|
* Number with locale grouping — e.g. "1,234.56"
|
|
*/
|
|
export function formatNumber(amount: number, decimals?: number): string {
|
|
return new Intl.NumberFormat(getLocaleCode(), {
|
|
minimumFractionDigits: decimals,
|
|
maximumFractionDigits: decimals,
|
|
}).format(amount)
|
|
}
|
|
|
|
/**
|
|
* Extract just the currency symbol — e.g. "$", "\u00a3", "\u20ac"
|
|
*/
|
|
export function getCurrencySymbol(): string {
|
|
const parts = new Intl.NumberFormat(getLocaleCode(), {
|
|
style: 'currency',
|
|
currency: getCurrencyCode(),
|
|
}).formatToParts(0)
|
|
const symbolPart = parts.find((p) => p.type === 'currency')
|
|
return symbolPart ? symbolPart.value : getCurrencyCode()
|
|
}
|