Skip to content

[Fix] Localize currency symbol #8299

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7890252
fix: localize currency symbol
mdneyazahmad Mar 23, 2022
3fa9bc3
refactor: move common function to hoc component
mdneyazahmad Mar 28, 2022
fad813c
refactor: rename variable names
mdneyazahmad Mar 28, 2022
0d56d96
refactor: ltr symbol component
mdneyazahmad Mar 28, 2022
1f50122
feat: add currency symbol utils
mdneyazahmad Mar 31, 2022
df003d6
refactor: currency utils functions
mdneyazahmad Mar 31, 2022
3cd30fa
test: add CurrencySymbolUtilsTest
mdneyazahmad Apr 1, 2022
155dc02
test: replace require to import
mdneyazahmad Apr 1, 2022
3e57e26
test: fix lint error
mdneyazahmad Apr 1, 2022
6a0e400
test: fix lint error
mdneyazahmad Apr 1, 2022
84bcdeb
refactor: CurrencySymbolUtils
mdneyazahmad Apr 1, 2022
696b7d5
test: refactor names
mdneyazahmad Apr 1, 2022
3ef60e8
docs: update comments
mdneyazahmad Apr 1, 2022
5992dfc
docs: update comment
mdneyazahmad Apr 1, 2022
b6d518c
merge main branch
mdneyazahmad May 1, 2022
21d3bc0
merge main branch
mdneyazahmad May 9, 2022
671f2d2
add comment for currencyList.json
mdneyazahmad May 10, 2022
7c4aa7e
refactor: IOUAmountPage
mdneyazahmad May 10, 2022
291782f
update comment in tests/unit/CurrencySymbolUtilsTest.js
mdneyazahmad May 12, 2022
1841022
refactor: TextInputWithCurrencySymbol
mdneyazahmad May 12, 2022
6147523
refactor: move inline function to class method
mdneyazahmad May 12, 2022
8e9764f
refactor: IOUAmountPage
mdneyazahmad May 12, 2022
46148a3
docs: add jsdocs
mdneyazahmad May 12, 2022
f85bbde
style: format code
mdneyazahmad May 17, 2022
04882ce
fix merge conflict
mdneyazahmad May 17, 2022
355e65d
refactor: remove unused code
mdneyazahmad May 17, 2022
fc91394
Merge branch 'fix/7915-localize-currency-symbol' of github.com:mdneya…
mdneyazahmad May 17, 2022
c25bf64
style: fix eslint error
mdneyazahmad May 17, 2022
133a62e
chore: update comment
mdneyazahmad May 17, 2022
f018a24
test: fix typo
mdneyazahmad Jun 2, 2022
c93b4cb
test: update comment
mdneyazahmad Jun 2, 2022
d87f569
fix: merge conflict
mdneyazahmad Jun 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/components/withLocalize.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {createContext, forwardRef} from 'react';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import getComponentDisplayName from '../libs/getComponentDisplayName';
import ONYXKEYS from '../ONYXKEYS';
import * as Localize from '../libs/Localize';
Expand All @@ -19,6 +20,9 @@ const withLocalizePropTypes = {
/** Formats number formatted according to locale and options */
numberFormat: PropTypes.func.isRequired,

/** Formats number to parts according to locale and options */
formatToParts: PropTypes.func.isRequired,

/** Converts a timestamp into a localized string representation that's relative to current moment in time */
timestampToRelative: PropTypes.func.isRequired,

Expand All @@ -34,6 +38,9 @@ const withLocalizePropTypes = {
/** Gets the standard digit corresponding to a locale digit */
fromLocaleDigit: PropTypes.func.isRequired,

/** Get localized currency symbol for SO4217 Code */
toLocalizedCurrencySymbol: PropTypes.func.isRequired,

/** Gets the locale digit corresponding to a standard digit */
toLocaleDigit: PropTypes.func.isRequired,
};
Expand All @@ -59,12 +66,14 @@ class LocaleContextProvider extends React.Component {
return {
translate: this.translate.bind(this),
numberFormat: this.numberFormat.bind(this),
formatToParts: this.formatToParts.bind(this),
timestampToRelative: this.timestampToRelative.bind(this),
timestampToDateTime: this.timestampToDateTime.bind(this),
fromLocalPhone: this.fromLocalPhone.bind(this),
toLocalPhone: this.toLocalPhone.bind(this),
fromLocaleDigit: this.fromLocaleDigit.bind(this),
toLocaleDigit: this.toLocaleDigit.bind(this),
toLocalizedCurrencySymbol: this.toLocalizedCurrencySymbol.bind(this),
preferredLocale: this.props.preferredLocale,
};
}
Expand All @@ -87,6 +96,15 @@ class LocaleContextProvider extends React.Component {
return NumberFormatUtils.format(this.props.preferredLocale, number, options);
}

/**
* @param {Number} number
* @param {Intl.NumberFormatOptions} options
* @returns {Array}
*/
formatToParts(number, options) {
return NumberFormatUtils.formatToParts(this.props.preferredLocale, number, options);
}

/**
* @param {Number} timestamp
* @returns {String}
Expand Down Expand Up @@ -140,6 +158,19 @@ class LocaleContextProvider extends React.Component {
return LocaleDigitUtils.fromLocaleDigit(this.props.preferredLocale, localeDigit);
}

/**
* @param {String} currencyCode
* @return {String}
*/
toLocalizedCurrencySymbol(currencyCode) {
const parts = NumberFormatUtils.formatToParts(this.props.preferredLocale, 0, {
style: 'currency',
currency: currencyCode,
});
const currencySymbol = _.find(parts, part => part.type === 'currency').value;
return currencySymbol;
}

render() {
return (
<LocaleContext.Provider value={this.getContextValue()}>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/iou/IOUCurrencySelection.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class IOUCurrencySelection extends Component {
getCurrencyOptions() {
const currencyListKeys = _.keys(this.props.currencyList);
const currencyOptions = _.map(currencyListKeys, currencyCode => ({
text: `${currencyCode} - ${this.props.currencyList[currencyCode].symbol}`,
text: `${currencyCode} - ${this.props.toLocalizedCurrencySymbol(currencyCode)}`,
searchText: `${currencyCode} ${this.props.currencyList[currencyCode].symbol}`,
currencyCode,
}));
Expand Down
97 changes: 64 additions & 33 deletions src/pages/iou/steps/IOUAmountPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,6 @@ const propTypes = {
/** Callback to inform parent modal of success */
onStepComplete: PropTypes.func.isRequired,

/** The currency list constant object from Onyx */
currencyList: PropTypes.objectOf(PropTypes.shape({
/** Symbol for the currency */
symbol: PropTypes.string,

/** Name of the currency */
name: PropTypes.string,

/** ISO4217 Code for the currency */
ISO4217: PropTypes.string,
})).isRequired,

/** Previously selected amount to show if the user comes back to this screen */
selectedAmount: PropTypes.string.isRequired,

Expand Down Expand Up @@ -75,6 +63,7 @@ class IOUAmountPage extends React.Component {
this.updateAmount = this.updateAmount.bind(this);
this.stripCommaFromAmount = this.stripCommaFromAmount.bind(this);
this.focusTextInput = this.focusTextInput.bind(this);
this.isCurrencySymbolToLeft = this.isCurrencySymbolToLeft.bind(this);

this.state = {
amount: props.selectedAmount,
Expand All @@ -93,6 +82,24 @@ class IOUAmountPage extends React.Component {
this.focusTextInput();
}

/**
* Is currency symbol to left
* @param {String} currencyCode
* @return {Boolean}
*/
isCurrencySymbolToLeft(currencyCode) {
const parts = this.props.formatToParts(0, {
style: 'currency',
currency: currencyCode,
});

// The first element of parts will be type: currency for all currency
// Where it starts with symbol and the other will have it at last
// If it is not the first, it must be at last
const isLeft = parts[0].type === 'currency';
return isLeft;
}

/**
* Focus text input
*/
Expand Down Expand Up @@ -206,7 +213,9 @@ class IOUAmountPage extends React.Component {
}

render() {
const currencySymbol = this.props.toLocalizedCurrencySymbol(this.props.iou.selectedCurrencyCode);
const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit);
const isCurrencySymbolToLeft = this.isCurrencySymbolToLeft(this.props.iou.selectedCurrencyCode);
return (
<>
<View style={[
Expand All @@ -217,26 +226,49 @@ class IOUAmountPage extends React.Component {
styles.justifyContentCenter,
]}
>
<TouchableOpacity onPress={() => Navigation.navigate(this.props.hasMultipleParticipants
? ROUTES.getIouBillCurrencyRoute(this.props.reportID)
: ROUTES.getIouRequestCurrencyRoute(this.props.reportID))}
>
<Text style={styles.iouAmountText}>
{lodashGet(this.props.currencyList, [this.props.iou.selectedCurrencyCode, 'symbol'])}
</Text>
</TouchableOpacity>
<TextInput
disableKeyboard
autoGrow
hideFocusedState
inputStyle={[styles.iouAmountTextInput, styles.p0, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
textInputContainerStyles={[styles.borderNone, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
onChangeText={this.updateAmount}
ref={el => this.textInput = el}
value={formattedAmount}
placeholder={this.props.numberFormat(0)}
keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
/>
{isCurrencySymbolToLeft ? (
<>
<TouchableOpacity onPress={() => Navigation.navigate(this.props.hasMultipleParticipants
? ROUTES.getIouBillCurrencyRoute(this.props.reportID)
: ROUTES.getIouRequestCurrencyRoute(this.props.reportID))}
>
<Text style={styles.iouAmountText}>{currencySymbol}</Text>
</TouchableOpacity>
<TextInput
disableKeyboard
autoGrow
hideFocusedState
inputStyle={[styles.iouAmountTextInput, styles.p0, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
textInputContainerStyles={[styles.borderNone, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
onChangeText={this.updateAmount}
ref={el => this.textInput = el}
value={formattedAmount}
placeholder={this.props.numberFormat(0)}
keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
/>
</>
) : (
<>
<TextInput
disableKeyboard
autoGrow
hideFocusedState
inputStyle={[styles.iouAmountTextInput, styles.p0, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
textInputContainerStyles={[styles.borderNone, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
onChangeText={this.updateAmount}
ref={el => this.textInput = el}
value={formattedAmount}
placeholder={this.props.numberFormat(0)}
keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
/>
<TouchableOpacity onPress={() => Navigation.navigate(this.props.hasMultipleParticipants
? ROUTES.getIouBillCurrencyRoute(this.props.reportID)
: ROUTES.getIouRequestCurrencyRoute(this.props.reportID))}
>
<Text style={styles.iouAmountText}>{currencySymbol}</Text>
</TouchableOpacity>
</>
)}
</View>
<View style={[styles.w100, styles.justifyContentEnd]}>
{canUseTouchScreen()
Expand Down Expand Up @@ -266,7 +298,6 @@ IOUAmountPage.defaultProps = defaultProps;
export default compose(
withLocalize,
withOnyx({
currencyList: {key: ONYXKEYS.CURRENCY_LIST},
iou: {key: ONYXKEYS.IOU},
}),
)(IOUAmountPage);