From 2e6166ece613c7d05ba939ff69bb0f31c01306ce Mon Sep 17 00:00:00 2001 From: David Vargas Dominguez Date: Fri, 19 Dec 2025 13:15:16 -0500 Subject: [PATCH 1/9] fix: fallback to localized currency symbol when list symbol is code-like --- src/libs/CurrencyUtils.ts | 31 +++++++++++ tests/unit/CurrencyUtilsTest.ts | 92 ++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index 04d7c2adcb6c..eaed75c60e98 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -209,6 +209,35 @@ function sanitizeCurrencyCode(currencyCode: string): string { return isValidCurrencyCode(currencyCode) ? currencyCode : CONST.CURRENCY.USD; } +/** + * Checks if a "symbol" is effectively just the ISO currency code (e.g. "CLP"). + */ +function isCurrencyCodeLikeSymbol(symbol?: string, currencyCode?: string): boolean { + if (!symbol || !currencyCode) { + return false; + } + + const normalizedSymbol = symbol.trim().toUpperCase(); + const normalizedCode = currencyCode.trim().toUpperCase(); + + return normalizedSymbol === normalizedCode; +} + +/** + * Returns a preferred currency symbol for display: + * - Uses the symbol from CURRENCY_LIST when it exists and is not code-like. + * - Otherwise falls back to the localized Intl-derived currency value (existing behavior). + */ +function getPreferredCurrencySymbol(currencyCode: string = CONST.CURRENCY.USD): string | undefined { + const symbolFromList = getCurrencySymbol(currencyCode); + + if (symbolFromList && !isCurrencyCodeLikeSymbol(symbolFromList, currencyCode)) { + return symbolFromList; + } + + return getLocalizedCurrencySymbol(currencyCode); +} + function getCurrencyKeyByCountryCode(currencies?: CurrencyList, countryCode?: string): string { if (!currencies || !countryCode) { return CONST.CURRENCY.USD; @@ -237,4 +266,6 @@ export { convertToShortDisplayString, getCurrency, sanitizeCurrencyCode, + isCurrencyCodeLikeSymbol, + getPreferredCurrencySymbol, }; diff --git a/tests/unit/CurrencyUtilsTest.ts b/tests/unit/CurrencyUtilsTest.ts index 7ce37b8a9a06..dfcf80a6ae03 100644 --- a/tests/unit/CurrencyUtilsTest.ts +++ b/tests/unit/CurrencyUtilsTest.ts @@ -2,6 +2,7 @@ import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import IntlStore from '@src/languages/IntlStore'; import * as CurrencyUtils from '@src/libs/CurrencyUtils'; +import type {CurrencyList} from '@src/types/onyx'; import ONYXKEYS from '@src/ONYXKEYS'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // This file can get outdated. In that case, you can follow these steps to update it: @@ -12,7 +13,7 @@ import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // - update currencyList.json import currencyList from './currencyList.json'; -const currencyCodeList = Object.keys(currencyList); +const currencyCodeList = Object.keys(currencyList as CurrencyList); const AVAILABLE_LOCALES = [CONST.LOCALES.EN, CONST.LOCALES.ES]; describe('CurrencyUtils', () => { @@ -189,4 +190,93 @@ describe('CurrencyUtils', () => { IntlStore.load(CONST.LOCALES.ES).then(() => expect(CurrencyUtils.convertToShortDisplayString(amount, currency)).toBe(expectedResult)), ); }); + + describe('isCurrencyCodeLikeSymbol', () => { + test.each([ + ['USD', 'USD', true], + [' usd ', 'USD', true], + ['Usd', 'uSd', true], + ['$', 'USD', false], + ['US$', 'USD', false], + [undefined, 'USD', false], + ['USD', undefined, false], + [undefined, undefined, false], + ])('Returns %s for symbol=%s, currencyCode=%s', (symbol, currencyCode, expected) => { + expect(CurrencyUtils.isCurrencyCodeLikeSymbol(symbol, currencyCode)).toBe(expected); + }); + }); + + describe('getPreferredCurrencySymbol', () => { + test('Uses CURRENCY_LIST.symbol when it exists and is not code-like', () => { + const currencyListTyped = currencyList as CurrencyList; + const currencyWithNonCodeLikeSymbol = currencyCodeList.find((code) => { + const symbol = currencyListTyped?.[code]?.symbol; + return !!symbol && symbol.trim().toUpperCase() !== code.trim().toUpperCase(); + }); + + expect(currencyWithNonCodeLikeSymbol).toBeTruthy(); + + if (!currencyWithNonCodeLikeSymbol) { + throw new Error('Expected a currency with a non-code-like symbol'); + } + + const expectedSymbol = CurrencyUtils.getCurrencySymbol(currencyWithNonCodeLikeSymbol); + expect(expectedSymbol).toBeTruthy(); + + expect(CurrencyUtils.getPreferredCurrencySymbol(currencyWithNonCodeLikeSymbol)).toBe(expectedSymbol); + }); + + test('Falls back to localized value when CURRENCY_LIST.symbol is code-like', async () => { + await IntlStore.load(CONST.LOCALES.EN); + await waitForBatchedUpdates(); + + // Force a known case: make USD symbol "USD" so it becomes code-like + const modifiedCurrencyList = { + ...currencyList, + USD: { + ...currencyList.USD, + symbol: 'USD', + }, + }; + + await Onyx.set(ONYXKEYS.CURRENCY_LIST, modifiedCurrencyList); + await waitForBatchedUpdates(); + + const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD); + const localized = CurrencyUtils.getLocalizedCurrencySymbol(CONST.CURRENCY.USD); + + expect(preferred).toBe(localized); + expect(preferred).not.toBe('USD'); + + // Restore for the rest of the suite + await Onyx.set(ONYXKEYS.CURRENCY_LIST, currencyList); + await waitForBatchedUpdates(); + }); + + test('Falls back to localized value when CURRENCY_LIST.symbol is missing', async () => { + await IntlStore.load(CONST.LOCALES.EN); + await waitForBatchedUpdates(); + + const modifiedCurrencyList = { + ...currencyList, + USD: { + ...currencyList.USD, + symbol: undefined, + }, + }; + + await Onyx.set(ONYXKEYS.CURRENCY_LIST, modifiedCurrencyList); + await waitForBatchedUpdates(); + + const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD); + const localized = CurrencyUtils.getLocalizedCurrencySymbol(CONST.CURRENCY.USD); + + expect(preferred).toBe(localized); + expect(preferred).toBeTruthy(); + + // Restore for the rest of the suite + await Onyx.set(ONYXKEYS.CURRENCY_LIST, currencyList); + await waitForBatchedUpdates(); + }); + }); }); From b8a41ba0157b05ca153a29ec9b1a9ac436780056 Mon Sep 17 00:00:00 2001 From: David Vargas Dominguez Date: Fri, 19 Dec 2025 14:38:36 -0500 Subject: [PATCH 2/9] test: clarify code-like currency symbol checks --- src/libs/CurrencyUtils.ts | 2 +- tests/unit/CurrencyUtilsTest.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index eaed75c60e98..72067598720b 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -210,7 +210,7 @@ function sanitizeCurrencyCode(currencyCode: string): string { } /** - * Checks if a "symbol" is effectively just the ISO currency code (e.g. "CLP"). + * Checks if a "symbol" is effectively just the ISO currency code (e.g. "TZS"). */ function isCurrencyCodeLikeSymbol(symbol?: string, currencyCode?: string): boolean { if (!symbol || !currencyCode) { diff --git a/tests/unit/CurrencyUtilsTest.ts b/tests/unit/CurrencyUtilsTest.ts index dfcf80a6ae03..8ecbc8588e43 100644 --- a/tests/unit/CurrencyUtilsTest.ts +++ b/tests/unit/CurrencyUtilsTest.ts @@ -201,7 +201,7 @@ describe('CurrencyUtils', () => { [undefined, 'USD', false], ['USD', undefined, false], [undefined, undefined, false], - ])('Returns %s for symbol=%s, currencyCode=%s', (symbol, currencyCode, expected) => { + ])('symbol=%s, currencyCode=%s returns %s', (symbol, currencyCode, expected) => { expect(CurrencyUtils.isCurrencyCodeLikeSymbol(symbol, currencyCode)).toBe(expected); }); }); From c27ba136c6b9aa63b1f9f80e9aee2508a5c3706c Mon Sep 17 00:00:00 2001 From: David Vargas Dominguez Date: Sat, 20 Dec 2025 18:45:52 -0500 Subject: [PATCH 3/9] test: cover code-like symbol fallback via getPreferredCurrencySymbol --- src/libs/CurrencyUtils.ts | 1 - tests/unit/CurrencyUtilsTest.ts | 45 ++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index 72067598720b..b700514e3127 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -266,6 +266,5 @@ export { convertToShortDisplayString, getCurrency, sanitizeCurrencyCode, - isCurrencyCodeLikeSymbol, getPreferredCurrencySymbol, }; diff --git a/tests/unit/CurrencyUtilsTest.ts b/tests/unit/CurrencyUtilsTest.ts index 8ecbc8588e43..4203f4058be5 100644 --- a/tests/unit/CurrencyUtilsTest.ts +++ b/tests/unit/CurrencyUtilsTest.ts @@ -191,21 +191,6 @@ describe('CurrencyUtils', () => { ); }); - describe('isCurrencyCodeLikeSymbol', () => { - test.each([ - ['USD', 'USD', true], - [' usd ', 'USD', true], - ['Usd', 'uSd', true], - ['$', 'USD', false], - ['US$', 'USD', false], - [undefined, 'USD', false], - ['USD', undefined, false], - [undefined, undefined, false], - ])('symbol=%s, currencyCode=%s returns %s', (symbol, currencyCode, expected) => { - expect(CurrencyUtils.isCurrencyCodeLikeSymbol(symbol, currencyCode)).toBe(expected); - }); - }); - describe('getPreferredCurrencySymbol', () => { test('Uses CURRENCY_LIST.symbol when it exists and is not code-like', () => { const currencyListTyped = currencyList as CurrencyList; @@ -278,5 +263,35 @@ describe('CurrencyUtils', () => { await Onyx.set(ONYXKEYS.CURRENCY_LIST, currencyList); await waitForBatchedUpdates(); }); + + test.each([ + ['USD'], + [' usd '], + ['Usd'], + ])('Falls back to localized value when CURRENCY_LIST.symbol is code-like (%s)', async (codeLikeSymbol) => { + await IntlStore.load(CONST.LOCALES.EN); + await waitForBatchedUpdates(); + + const modifiedCurrencyList = { + ...currencyList, + USD: { + ...currencyList.USD, + symbol: codeLikeSymbol, + }, + }; + + await Onyx.set(ONYXKEYS.CURRENCY_LIST, modifiedCurrencyList); + await waitForBatchedUpdates(); + + const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD); + const localized = CurrencyUtils.getLocalizedCurrencySymbol(CONST.CURRENCY.USD); + + expect(preferred).toBe(localized); + expect(preferred).not.toBe(codeLikeSymbol); + + await Onyx.set(ONYXKEYS.CURRENCY_LIST, currencyList); + await waitForBatchedUpdates(); + }); + }); }); From d058211b1f548c1794018a9866359363b8ecaa51 Mon Sep 17 00:00:00 2001 From: David Vargas Dominguez Date: Sun, 21 Dec 2025 17:32:58 -0500 Subject: [PATCH 4/9] chore: formatted code with Prettier --- tests/unit/CurrencyUtilsTest.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/unit/CurrencyUtilsTest.ts b/tests/unit/CurrencyUtilsTest.ts index 4203f4058be5..3eef81d7e779 100644 --- a/tests/unit/CurrencyUtilsTest.ts +++ b/tests/unit/CurrencyUtilsTest.ts @@ -2,8 +2,8 @@ import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import IntlStore from '@src/languages/IntlStore'; import * as CurrencyUtils from '@src/libs/CurrencyUtils'; -import type {CurrencyList} from '@src/types/onyx'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {CurrencyList} from '@src/types/onyx'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // This file can get outdated. In that case, you can follow these steps to update it: // - open your browser console and navigate to the Network tab @@ -264,11 +264,7 @@ describe('CurrencyUtils', () => { await waitForBatchedUpdates(); }); - test.each([ - ['USD'], - [' usd '], - ['Usd'], - ])('Falls back to localized value when CURRENCY_LIST.symbol is code-like (%s)', async (codeLikeSymbol) => { + test.each([['USD'], [' usd '], ['Usd']])('Falls back to localized value when CURRENCY_LIST.symbol is code-like (%s)', async (codeLikeSymbol) => { await IntlStore.load(CONST.LOCALES.EN); await waitForBatchedUpdates(); @@ -292,6 +288,5 @@ describe('CurrencyUtils', () => { await Onyx.set(ONYXKEYS.CURRENCY_LIST, currencyList); await waitForBatchedUpdates(); }); - }); }); From 5362fb25f4afc5997988ef2bdf1612ff064fdaf3 Mon Sep 17 00:00:00 2001 From: David Vargas Dominguez Date: Sun, 28 Dec 2025 19:27:12 -0500 Subject: [PATCH 5/9] refactor: updated with localize --- src/components/MoneyRequestAmountInput.tsx | 6 ++---- src/libs/CurrencyUtils.ts | 4 ++-- tests/unit/CurrencyUtilsTest.ts | 6 +++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/MoneyRequestAmountInput.tsx b/src/components/MoneyRequestAmountInput.tsx index 55a8192f985b..32b72ef0e1f8 100644 --- a/src/components/MoneyRequestAmountInput.tsx +++ b/src/components/MoneyRequestAmountInput.tsx @@ -1,8 +1,7 @@ import type {ForwardedRef} from 'react'; import React, {useCallback, useEffect, useRef} from 'react'; import type {BlurEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; -import useLocalize from '@hooks/useLocalize'; -import {convertToFrontendAmountAsString, getCurrencyDecimals, getLocalizedCurrencySymbol} from '@libs/CurrencyUtils'; +import {convertToFrontendAmountAsString, getCurrencyDecimals, getPreferredCurrencySymbol} from '@libs/CurrencyUtils'; import CONST from '@src/CONST'; import NumberWithSymbolForm from './NumberWithSymbolForm'; import type {NumberWithSymbolFormRef} from './NumberWithSymbolForm'; @@ -167,7 +166,6 @@ function MoneyRequestAmountInput({ disabled, ...props }: MoneyRequestAmountInputProps) { - const {preferredLocale} = useLocalize(); const textInput = useRef(null); const numberFormRef = useRef(null); const decimals = getCurrencyDecimals(currency); @@ -230,7 +228,7 @@ function MoneyRequestAmountInput({ } numberFormRef.current = newRef; }} - symbol={getLocalizedCurrencySymbol(preferredLocale, currency) ?? ''} + symbol={getPreferredCurrencySymbol(currency) ?? ''} symbolPosition={CONST.TEXT_INPUT_SYMBOL_POSITION.PREFIX} currency={currency} hideSymbol={hideCurrencySymbol} diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index b700514e3127..d3490691a937 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -47,7 +47,7 @@ function getCurrencyUnit(currency: string = CONST.CURRENCY.USD): number { } /** - * Get localized currency symbol for currency(ISO 4217) Code + * Get localized currency symbol for currency (ISO 4217) Code */ function getLocalizedCurrencySymbol(locale: Locale | undefined, currencyCode: string): string | undefined { const parts = formatToParts(locale, 0, { @@ -235,7 +235,7 @@ function getPreferredCurrencySymbol(currencyCode: string = CONST.CURRENCY.USD): return symbolFromList; } - return getLocalizedCurrencySymbol(currencyCode); + return getLocalizedCurrencySymbol(IntlStore.getCurrentLocale(), currencyCode); } function getCurrencyKeyByCountryCode(currencies?: CurrencyList, countryCode?: string): string { diff --git a/tests/unit/CurrencyUtilsTest.ts b/tests/unit/CurrencyUtilsTest.ts index 3eef81d7e779..a0a9fb0525d2 100644 --- a/tests/unit/CurrencyUtilsTest.ts +++ b/tests/unit/CurrencyUtilsTest.ts @@ -228,7 +228,7 @@ describe('CurrencyUtils', () => { await waitForBatchedUpdates(); const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD); - const localized = CurrencyUtils.getLocalizedCurrencySymbol(CONST.CURRENCY.USD); + const localized = CurrencyUtils.getLocalizedCurrencySymbol(IntlStore.getCurrentLocale(), CONST.CURRENCY.USD); expect(preferred).toBe(localized); expect(preferred).not.toBe('USD'); @@ -254,7 +254,7 @@ describe('CurrencyUtils', () => { await waitForBatchedUpdates(); const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD); - const localized = CurrencyUtils.getLocalizedCurrencySymbol(CONST.CURRENCY.USD); + const localized = CurrencyUtils.getLocalizedCurrencySymbol(IntlStore.getCurrentLocale(), CONST.CURRENCY.USD); expect(preferred).toBe(localized); expect(preferred).toBeTruthy(); @@ -280,7 +280,7 @@ describe('CurrencyUtils', () => { await waitForBatchedUpdates(); const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD); - const localized = CurrencyUtils.getLocalizedCurrencySymbol(CONST.CURRENCY.USD); + const localized = CurrencyUtils.getLocalizedCurrencySymbol(IntlStore.getCurrentLocale(), CONST.CURRENCY.USD); expect(preferred).toBe(localized); expect(preferred).not.toBe(codeLikeSymbol); From 591877431e1f14b3ef10708fcea3c5543ffdd51b Mon Sep 17 00:00:00 2001 From: David Vargas Dominguez Date: Mon, 29 Dec 2025 09:53:56 -0500 Subject: [PATCH 6/9] test: add unit tests for isCurrencyCodeLikeSymbol helper --- src/libs/CurrencyUtils.ts | 1 + tests/unit/CurrencyUtilsTest.ts | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index d3490691a937..2da402ea0063 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -267,4 +267,5 @@ export { getCurrency, sanitizeCurrencyCode, getPreferredCurrencySymbol, + isCurrencyCodeLikeSymbol, }; diff --git a/tests/unit/CurrencyUtilsTest.ts b/tests/unit/CurrencyUtilsTest.ts index a0a9fb0525d2..88c49abf6ef9 100644 --- a/tests/unit/CurrencyUtilsTest.ts +++ b/tests/unit/CurrencyUtilsTest.ts @@ -191,6 +191,41 @@ describe('CurrencyUtils', () => { ); }); + describe('isCurrencyCodeLikeSymbol', () => { + test.each([ + // Exact match + ['USD', 'USD', true], + ['ALL', 'ALL', true], + + // Case-insensitive + ['usd', 'USD', true], + ['Usd', 'uSd', true], + + // Trimming on both sides + [' usd ', 'USD', true], + ['USD', ' usd ', true], + [' usd ', ' UsD ', true], + + // Not equal => false + ['USD', 'PEN', false], + ['US$', 'USD', false], + ['USD$', 'USD', false], + ['$', 'USD', false], + ['S/', 'PEN', false], + + // Missing/empty values => false (note: empty string is falsy) + [undefined, 'USD', false], + ['USD', undefined, false], + ['', 'USD', false], + ['USD', '', false], + [' ', 'USD', false], // whitespace becomes '' after trim, but it's still falsy upfront + ['USD', ' ', false], + ])('symbol=%s, currencyCode=%s => %s', (symbol, currencyCode, expected) => { + expect(CurrencyUtils.isCurrencyCodeLikeSymbol(symbol, currencyCode)).toBe(expected); + }); + }); + + describe('getPreferredCurrencySymbol', () => { test('Uses CURRENCY_LIST.symbol when it exists and is not code-like', () => { const currencyListTyped = currencyList as CurrencyList; From 482cbd8a4ee3501852a619c9f2b1de46242f0a9b Mon Sep 17 00:00:00 2001 From: David Vargas Dominguez Date: Mon, 29 Dec 2025 10:30:29 -0500 Subject: [PATCH 7/9] chore: applied prettier for isCurrencyCodeLikeSymbol test --- tests/unit/CurrencyUtilsTest.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/CurrencyUtilsTest.ts b/tests/unit/CurrencyUtilsTest.ts index 88c49abf6ef9..992581466d27 100644 --- a/tests/unit/CurrencyUtilsTest.ts +++ b/tests/unit/CurrencyUtilsTest.ts @@ -225,7 +225,6 @@ describe('CurrencyUtils', () => { }); }); - describe('getPreferredCurrencySymbol', () => { test('Uses CURRENCY_LIST.symbol when it exists and is not code-like', () => { const currencyListTyped = currencyList as CurrencyList; From cae473dcecadc16aa4ea05f96925670ba776e329 Mon Sep 17 00:00:00 2001 From: David Vargas Dominguez Date: Mon, 29 Dec 2025 13:16:25 -0500 Subject: [PATCH 8/9] refactor: pass preferredLocale to getPreferredCurrencySymbol and update tests --- src/components/MoneyRequestAmountInput.tsx | 4 ++- src/libs/CurrencyUtils.ts | 5 ++-- tests/unit/CurrencyUtilsTest.ts | 29 ++++++++++++++-------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/components/MoneyRequestAmountInput.tsx b/src/components/MoneyRequestAmountInput.tsx index 32b72ef0e1f8..6a0f268ee682 100644 --- a/src/components/MoneyRequestAmountInput.tsx +++ b/src/components/MoneyRequestAmountInput.tsx @@ -1,6 +1,7 @@ import type {ForwardedRef} from 'react'; import React, {useCallback, useEffect, useRef} from 'react'; import type {BlurEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; +import useLocalize from '@hooks/useLocalize'; import {convertToFrontendAmountAsString, getCurrencyDecimals, getPreferredCurrencySymbol} from '@libs/CurrencyUtils'; import CONST from '@src/CONST'; import NumberWithSymbolForm from './NumberWithSymbolForm'; @@ -166,6 +167,7 @@ function MoneyRequestAmountInput({ disabled, ...props }: MoneyRequestAmountInputProps) { + const {preferredLocale} = useLocalize(); const textInput = useRef(null); const numberFormRef = useRef(null); const decimals = getCurrencyDecimals(currency); @@ -228,7 +230,7 @@ function MoneyRequestAmountInput({ } numberFormRef.current = newRef; }} - symbol={getPreferredCurrencySymbol(currency) ?? ''} + symbol={getPreferredCurrencySymbol(currency, preferredLocale) ?? ''} symbolPosition={CONST.TEXT_INPUT_SYMBOL_POSITION.PREFIX} currency={currency} hideSymbol={hideCurrencySymbol} diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index 2da402ea0063..634ba29ab484 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -228,14 +228,15 @@ function isCurrencyCodeLikeSymbol(symbol?: string, currencyCode?: string): boole * - Uses the symbol from CURRENCY_LIST when it exists and is not code-like. * - Otherwise falls back to the localized Intl-derived currency value (existing behavior). */ -function getPreferredCurrencySymbol(currencyCode: string = CONST.CURRENCY.USD): string | undefined { +function getPreferredCurrencySymbol(currencyCode: string = CONST.CURRENCY.USD, preferredLocale?: Locale | undefined): string | undefined { const symbolFromList = getCurrencySymbol(currencyCode); if (symbolFromList && !isCurrencyCodeLikeSymbol(symbolFromList, currencyCode)) { return symbolFromList; } - return getLocalizedCurrencySymbol(IntlStore.getCurrentLocale(), currencyCode); + const locale = preferredLocale ?? IntlStore.getCurrentLocale() + return getLocalizedCurrencySymbol(locale, currencyCode); } function getCurrencyKeyByCountryCode(currencies?: CurrencyList, countryCode?: string): string { diff --git a/tests/unit/CurrencyUtilsTest.ts b/tests/unit/CurrencyUtilsTest.ts index 992581466d27..9d218370b878 100644 --- a/tests/unit/CurrencyUtilsTest.ts +++ b/tests/unit/CurrencyUtilsTest.ts @@ -227,6 +227,7 @@ describe('CurrencyUtils', () => { describe('getPreferredCurrencySymbol', () => { test('Uses CURRENCY_LIST.symbol when it exists and is not code-like', () => { + const preferredLocale = CONST.LOCALES.EN; const currencyListTyped = currencyList as CurrencyList; const currencyWithNonCodeLikeSymbol = currencyCodeList.find((code) => { const symbol = currencyListTyped?.[code]?.symbol; @@ -242,14 +243,16 @@ describe('CurrencyUtils', () => { const expectedSymbol = CurrencyUtils.getCurrencySymbol(currencyWithNonCodeLikeSymbol); expect(expectedSymbol).toBeTruthy(); - expect(CurrencyUtils.getPreferredCurrencySymbol(currencyWithNonCodeLikeSymbol)).toBe(expectedSymbol); + expect(CurrencyUtils.getPreferredCurrencySymbol(currencyWithNonCodeLikeSymbol, preferredLocale)).toBe(expectedSymbol); }); test('Falls back to localized value when CURRENCY_LIST.symbol is code-like', async () => { - await IntlStore.load(CONST.LOCALES.EN); + const preferredLocale = CONST.LOCALES.EN; + + await IntlStore.load(preferredLocale); await waitForBatchedUpdates(); - // Force a known case: make USD symbol "USD" so it becomes code-like + // Force a known case: make the USD symbol "USD" so it becomes code-like const modifiedCurrencyList = { ...currencyList, USD: { @@ -261,8 +264,8 @@ describe('CurrencyUtils', () => { await Onyx.set(ONYXKEYS.CURRENCY_LIST, modifiedCurrencyList); await waitForBatchedUpdates(); - const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD); - const localized = CurrencyUtils.getLocalizedCurrencySymbol(IntlStore.getCurrentLocale(), CONST.CURRENCY.USD); + const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD, preferredLocale); + const localized = CurrencyUtils.getLocalizedCurrencySymbol(preferredLocale, CONST.CURRENCY.USD); expect(preferred).toBe(localized); expect(preferred).not.toBe('USD'); @@ -273,7 +276,9 @@ describe('CurrencyUtils', () => { }); test('Falls back to localized value when CURRENCY_LIST.symbol is missing', async () => { - await IntlStore.load(CONST.LOCALES.EN); + const preferredLocale = CONST.LOCALES.EN; + + await IntlStore.load(preferredLocale); await waitForBatchedUpdates(); const modifiedCurrencyList = { @@ -287,8 +292,8 @@ describe('CurrencyUtils', () => { await Onyx.set(ONYXKEYS.CURRENCY_LIST, modifiedCurrencyList); await waitForBatchedUpdates(); - const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD); - const localized = CurrencyUtils.getLocalizedCurrencySymbol(IntlStore.getCurrentLocale(), CONST.CURRENCY.USD); + const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD, preferredLocale); + const localized = CurrencyUtils.getLocalizedCurrencySymbol(preferredLocale, CONST.CURRENCY.USD); expect(preferred).toBe(localized); expect(preferred).toBeTruthy(); @@ -299,7 +304,9 @@ describe('CurrencyUtils', () => { }); test.each([['USD'], [' usd '], ['Usd']])('Falls back to localized value when CURRENCY_LIST.symbol is code-like (%s)', async (codeLikeSymbol) => { - await IntlStore.load(CONST.LOCALES.EN); + const preferredLocale = CONST.LOCALES.EN; + + await IntlStore.load(preferredLocale); await waitForBatchedUpdates(); const modifiedCurrencyList = { @@ -313,8 +320,8 @@ describe('CurrencyUtils', () => { await Onyx.set(ONYXKEYS.CURRENCY_LIST, modifiedCurrencyList); await waitForBatchedUpdates(); - const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD); - const localized = CurrencyUtils.getLocalizedCurrencySymbol(IntlStore.getCurrentLocale(), CONST.CURRENCY.USD); + const preferred = CurrencyUtils.getPreferredCurrencySymbol(CONST.CURRENCY.USD, preferredLocale); + const localized = CurrencyUtils.getLocalizedCurrencySymbol(preferredLocale, CONST.CURRENCY.USD); expect(preferred).toBe(localized); expect(preferred).not.toBe(codeLikeSymbol); From a9462bf47c3b23782381d1899845766ba8a1a1cd Mon Sep 17 00:00:00 2001 From: David Vargas Dominguez Date: Tue, 30 Dec 2025 09:39:41 -0500 Subject: [PATCH 9/9] chore: executed prettier on currency utils --- src/libs/CurrencyUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index 6dcdf47a3764..3afebd308c14 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -230,7 +230,7 @@ function getPreferredCurrencySymbol(currencyCode: string = CONST.CURRENCY.USD, p return symbolFromList; } - const locale = preferredLocale ?? IntlStore.getCurrentLocale() + const locale = preferredLocale ?? IntlStore.getCurrentLocale(); return getLocalizedCurrencySymbol(locale, currencyCode); }