Merge pull request #3853 from MetaMask/i3580-InternationalizeCurrency

Internationalize currency
This commit is contained in:
Dan Finlay 2018-04-17 13:46:24 -07:00 committed by GitHub
commit 2ce33a3eee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1247 additions and 1312 deletions

View File

@ -2,7 +2,8 @@
## Current Master
- Improved performance of 3D fox logo
- Correctly format currency conversion for locally selected preferred currency.
- Improved performance of 3D fox logo.
- Fetch token prices based on contract address, not symbol
- Fix bug that prevents setting language locale in settings.

View File

@ -0,0 +1,134 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"name": "Send Account 1"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"name": "Send Account 2"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"name": "Send Account 3"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
"name": "Send Account 4"
}
},
"unapprovedTxs": {},
"currentCurrency": "USD",
"conversionRate": 19855,
"conversionDate": 1489013762,
"noActiveNotices": true,
"frequentRpcList": [],
"network": "3",
"accounts": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"code": "0x",
"balance": "0x47c9d71831c76efe",
"nonce": "0x1b",
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"code": "0x",
"balance": "0x37452b1315889f80",
"nonce": "0xa",
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"code": "0x",
"balance": "0x30c9d71831c76efe",
"nonce": "0x1c",
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
}
},
"addressBook": [
{
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896",
"name": "Address Book Account 1"
}
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
]
}
],
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"currentCurrency": "PHP",
"provider": {
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": [],
"send": {
"gasLimit": null,
"gasPrice": null,
"gasTotal": "0xb451dc41b578",
"tokenBalance": null,
"from": "",
"to": "",
"amount": "0x0",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
},
"currentLocale": "en"
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "accountDetail",
"detailView": null,
"context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"accountDetail": {
"subview": "transactions"
},
"modal": {
"modalState": {},
"previousModalState": {}
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": null
},
"identities": {}
}

20
package-lock.json generated
View File

@ -344,6 +344,11 @@
"negotiator": "0.6.1"
}
},
"accounting": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/accounting/-/accounting-0.4.1.tgz",
"integrity": "sha1-h91BA+/39EYPHhhvXGd+1s9WaIM="
},
"acorn": {
"version": "4.0.13",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
@ -4082,6 +4087,16 @@
"cssom": "0.3.2"
}
},
"currency-formatter": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/currency-formatter/-/currency-formatter-1.4.2.tgz",
"integrity": "sha512-rQ5HB3DenCZwfVPdpVTuVcAORodVO0VoqIbjhdUSuy0sE2b9jBdCaVKbA355NUc2KhPbu5ojHs3WypuEwPLfNg==",
"requires": {
"accounting": "0.4.1",
"locale-currency": "0.0.1",
"object-assign": "4.1.1"
}
},
"currently-unhandled": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@ -13481,6 +13496,11 @@
"object-assign": "4.1.1"
}
},
"locale-currency": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/locale-currency/-/locale-currency-0.0.1.tgz",
"integrity": "sha1-yeFaIv9XW0tLuUekv5KsI2vR/ps="
},
"locate-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",

View File

@ -76,6 +76,7 @@
"classnames": "^2.2.5",
"clone": "^2.1.1",
"copy-to-clipboard": "^3.0.8",
"currency-formatter": "^1.4.2",
"debounce": "^1.0.0",
"debounce-stream": "^2.0.0",
"deep-extend": "^0.5.0",

View File

@ -0,0 +1,28 @@
const reactTriggerChange = require('../../lib/react-trigger-change')
const {
timeout,
queryAsync,
findAsync,
} = require('../../lib/util')
QUnit.module('currency localization')
QUnit.test('renders localized currency', (assert) => {
const done = assert.async()
runCurrencyLocalizationTest(assert).then(done).catch((err) => {
assert.notOk(err, `Error was thrown: ${err.stack}`)
done()
})
})
async function runCurrencyLocalizationTest(assert, done) {
console.log('*** start runCurrencyLocalizationTest')
const selectState = await queryAsync($, 'select')
selectState.val('currency localization')
reactTriggerChange(selectState[0])
await timeout(1000)
const txView = await queryAsync($, '.tx-view')
const heroBalance = await findAsync($(txView), '.hero-balance')
const fiatAmount = await findAsync($(heroBalance), '.fiat-amount')
assert.equal(fiatAmount[0].textContent, '₱102,707.97')
}

View File

@ -91,7 +91,7 @@ async function runSendFlowTest(assert, done) {
)
assert.equal(
sendGasField.find('.currency-display__converted-value')[0].textContent,
'0.24 USD',
'$0.24 USD',
'send gas field should show estimated gas total converted to USD'
)
@ -118,7 +118,7 @@ async function runSendFlowTest(assert, done) {
)
assert.equal(
(await findAsync(sendGasField, '.currency-display__converted-value'))[0].textContent,
'3.60 USD',
'$3.60 USD',
'send gas field should show customized gas total converted to USD'
)
@ -138,9 +138,9 @@ async function runSendFlowTest(assert, done) {
const confirmScreenRows = await queryAsync($, '.confirm-screen-rows')
const confirmScreenGas = confirmScreenRows.find('.currency-display__converted-value')[0]
assert.equal(confirmScreenGas.textContent, '3.60 USD', 'confirm screen should show correct gas')
assert.equal(confirmScreenGas.textContent, '$3.60 USD', 'confirm screen should show correct gas')
const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[2]
assert.equal(confirmScreenTotal.textContent, '2405.36 USD', 'confirm screen should show correct total')
assert.equal(confirmScreenTotal.textContent, '$2,405.36 USD', 'confirm screen should show correct total')
const confirmScreenBackButton = await queryAsync($, '.page-container__back-button')
confirmScreenBackButton[0].click()

View File

@ -0,0 +1,27 @@
const assert = require('assert')
const currencyFormatter = require('currency-formatter')
const infuraConversion = require('../../ui/app/infura-conversion.json')
describe('currencyFormatting', function () {
it('be able to format any infura currency', function (done) {
const number = 10000
infuraConversion.objects.forEach((conversion) => {
const code = conversion.quote.code.toUpperCase()
const result = currencyFormatter.format(number, { code })
switch (code) {
case 'USD':
assert.equal(result, '$10,000.00')
break
case 'JPY':
assert.equal(result, '¥10,000')
break
default:
assert.ok(result, `Currency ${code} formatted as ${result}`)
}
})
done()
})
})

View File

@ -4,6 +4,8 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const TokenBalance = require('./token-balance')
const Identicon = require('./identicon')
const currencyFormatter = require('currency-formatter')
const currencies = require('currency-formatter/currencies')
const { formatBalance, generateBalanceObject } = require('../util')
@ -97,9 +99,17 @@ BalanceComponent.prototype.renderFiatAmount = function (fiatDisplayNumber, fiatS
const shouldNotRenderFiat = fiatDisplayNumber === 'N/A' || Number(fiatDisplayNumber) === 0
if (shouldNotRenderFiat) return null
const upperCaseFiatSuffix = fiatSuffix.toUpperCase()
const display = currencies.find(currency => currency.code === upperCaseFiatSuffix)
? currencyFormatter.format(Number(fiatDisplayNumber), {
code: upperCaseFiatSuffix,
})
: `${fiatPrefix}${fiatDisplayNumber} ${upperCaseFiatSuffix}`
return h('div.fiat-amount', {
style: {},
}, `${fiatPrefix}${fiatDisplayNumber} ${fiatSuffix}`)
}, display)
}
BalanceComponent.prototype.getTokenBalance = function (formattedBalance, shorten) {
@ -117,5 +127,9 @@ BalanceComponent.prototype.getFiatDisplayNumber = function (formattedBalance, co
const splitBalance = formattedBalance.split(' ')
return (Number(splitBalance[0]) * conversionRate).toFixed(2)
const convertedNumber = (Number(splitBalance[0]) * conversionRate)
const wholePart = Math.floor(convertedNumber)
const decimalPart = convertedNumber - wholePart
return wholePart + Number(decimalPart.toPrecision(2))
}

View File

@ -23,6 +23,8 @@ const {
const GasFeeDisplay = require('../send/gas-fee-display-v2')
const SenderToRecipient = require('../sender-to-recipient')
const NetworkDisplay = require('../network-display')
const currencyFormatter = require('currency-formatter')
const currencies = require('currency-formatter/currencies')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
@ -275,6 +277,16 @@ ConfirmSendEther.prototype.getData = function () {
}
}
ConfirmSendEther.prototype.convertToRenderableCurrency = function (value, currencyCode) {
const upperCaseCurrencyCode = currencyCode.toUpperCase()
return currencies.find(currency => currency.code === upperCaseCurrencyCode)
? currencyFormatter.format(Number(value), {
code: upperCaseCurrencyCode,
})
: value
}
ConfirmSendEther.prototype.editTransaction = function (txMeta) {
const { editTransaction, history } = this.props
editTransaction(txMeta)
@ -319,6 +331,9 @@ ConfirmSendEther.prototype.render = function () {
? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
: 'Please review your transaction.'
const convertedAmountInFiat = this.convertToRenderableCurrency(amountInFIAT, currentCurrency)
const convertedTotalInFiat = this.convertToRenderableCurrency(totalInFIAT, currentCurrency)
// This is from the latest master
// It handles some of the errors that we are not currently handling
// Leaving as comments fo reference
@ -365,7 +380,7 @@ ConfirmSendEther.prototype.render = function () {
// `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`,
// ]),
h('h3.flex-center.confirm-screen-send-amount', [`${amountInFIAT}`]),
h('h3.flex-center.confirm-screen-send-amount', [`${convertedAmountInFiat}`]),
h('h3.flex-center.confirm-screen-send-amount-currency', [ currentCurrency.toUpperCase() ]),
h('div.flex-center.confirm-memo-wrapper', [
h('h3.confirm-screen-send-memo', [ memo ? `"${memo}"` : '' ]),
@ -412,7 +427,7 @@ ConfirmSendEther.prototype.render = function () {
]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${totalInFIAT} ${currentCurrency.toUpperCase()}`),
h('div.confirm-screen-row-info', `${convertedTotalInFiat} ${currentCurrency.toUpperCase()}`),
h('div.confirm-screen-row-detail', `${totalInETH} ETH`),
]),

View File

@ -27,6 +27,8 @@ const {
calcTokenAmount,
} = require('../../token-util')
const classnames = require('classnames')
const currencyFormatter = require('currency-formatter')
const currencies = require('currency-formatter/currencies')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
@ -318,10 +320,12 @@ ConfirmSendToken.prototype.renderHeroAmount = function () {
const txParams = txMeta.txParams || {}
const { memo = '' } = txParams
const convertedAmountInFiat = this.convertToRenderableCurrency(fiatAmount, currentCurrency)
return fiatAmount
? (
h('div.confirm-send-token__hero-amount-wrapper', [
h('h3.flex-center.confirm-screen-send-amount', `${fiatAmount}`),
h('h3.flex-center.confirm-screen-send-amount', `${convertedAmountInFiat}`),
h('h3.flex-center.confirm-screen-send-amount-currency', currentCurrency),
h('div.flex-center.confirm-memo-wrapper', [
h('h3.confirm-screen-send-memo', [ memo ? `"${memo}"` : '' ]),
@ -369,6 +373,9 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
const { fiat: fiatAmount, token: tokenAmount } = this.getAmount()
const { fiat: fiatGas, token: tokenGas } = this.getGasFee()
const totalInFIAT = fiatAmount && fiatGas && addCurrencies(fiatAmount, fiatGas)
const convertedTotalInFiat = this.convertToRenderableCurrency(totalInFIAT, currentCurrency)
return fiatAmount && fiatGas
? (
h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
@ -378,7 +385,7 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${addCurrencies(fiatAmount, fiatGas)} ${currentCurrency}`),
h('div.confirm-screen-row-info', `${convertedTotalInFiat} ${currentCurrency}`),
h('div.confirm-screen-row-detail', `${addCurrencies(tokenAmount, tokenGas || '0')} ${symbol}`),
]),
])
@ -413,6 +420,16 @@ ConfirmSendToken.prototype.renderErrorMessage = function (message) {
: null
}
ConfirmSendToken.prototype.convertToRenderableCurrency = function (value, currencyCode) {
const upperCaseCurrencyCode = currencyCode.toUpperCase()
return currencies.find(currency => currency.code === upperCaseCurrencyCode)
? currencyFormatter.format(Number(value), {
code: upperCaseCurrencyCode,
})
: value
}
ConfirmSendToken.prototype.render = function () {
const txMeta = this.gatherTxMeta()
const {

View File

@ -3,6 +3,8 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const CurrencyInput = require('../currency-input')
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
const currencyFormatter = require('currency-formatter')
const currencies = require('currency-formatter/currencies')
module.exports = CurrencyDisplay
@ -53,12 +55,32 @@ CurrencyDisplay.prototype.getValueToRender = function () {
})
}
CurrencyDisplay.prototype.getConvertedValueToRender = function (nonFormattedValue) {
const { primaryCurrency, convertedCurrency, conversionRate } = this.props
let convertedValue = conversionUtil(nonFormattedValue, {
fromNumericBase: 'dec',
fromCurrency: primaryCurrency,
toCurrency: convertedCurrency,
numberOfDecimals: 2,
conversionRate,
})
convertedValue = Number(convertedValue).toFixed(2)
const upperCaseCurrencyCode = convertedCurrency.toUpperCase()
return currencies.find(currency => currency.code === upperCaseCurrencyCode)
? currencyFormatter.format(Number(convertedValue), {
code: upperCaseCurrencyCode,
})
: convertedValue
}
CurrencyDisplay.prototype.render = function () {
const {
className = 'currency-display',
primaryBalanceClassName = 'currency-display__input',
convertedBalanceClassName = 'currency-display__converted-value',
conversionRate,
primaryCurrency,
convertedCurrency,
readOnly = false,
@ -68,14 +90,7 @@ CurrencyDisplay.prototype.render = function () {
const valueToRender = this.getValueToRender()
let convertedValue = conversionUtil(valueToRender, {
fromNumericBase: 'dec',
fromCurrency: primaryCurrency,
toCurrency: convertedCurrency,
numberOfDecimals: 2,
conversionRate,
})
convertedValue = Number(convertedValue).toFixed(2)
const convertedValueToRender = this.getConvertedValueToRender(valueToRender)
return h('div', {
className,
@ -108,7 +123,7 @@ CurrencyDisplay.prototype.render = function () {
h('div', {
className: convertedBalanceClassName,
}, `${convertedValue} ${convertedCurrency.toUpperCase()}`),
}, `${convertedValueToRender} ${convertedCurrency.toUpperCase()}`),
])

2245
yarn.lock

File diff suppressed because it is too large Load Diff