diff --git a/src/actions/DiscoveryActions.js b/src/actions/DiscoveryActions.js index 154de83c..c3734327 100644 --- a/src/actions/DiscoveryActions.js +++ b/src/actions/DiscoveryActions.js @@ -1,5 +1,6 @@ /* @flow */ - +import React from 'react'; +import { FormattedMessage } from 'react-intl'; import TrezorConnect, { UI } from 'trezor-connect'; import * as BLOCKCHAIN_ACTION from 'actions/constants/blockchain'; import * as DISCOVERY from 'actions/constants/discovery'; @@ -17,6 +18,8 @@ import type { Account, } from 'flowtype'; import type { Discovery, State } from 'reducers/DiscoveryReducer'; +import l10nMessages from 'components/notifications/Context/actions.messages'; +import l10nCommonMessages from 'views/common.messages'; import * as BlockchainActions from './BlockchainActions'; import * as EthereumDiscoveryActions from './ethereum/DiscoveryActions'; import * as RippleDiscoveryActions from './ripple/DiscoveryActions'; @@ -176,12 +179,12 @@ const begin = (device: TrezorDevice, networkName: string): AsyncAction => async type: NOTIFICATION.ADD, payload: { variant: 'error', - title: 'Discovery error', + title: , message: error.message, cancelable: true, actions: [ { - label: 'Try again', + label: , callback: () => { dispatch(start(device, networkName)); }, @@ -264,12 +267,12 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy type: NOTIFICATION.ADD, payload: { variant: 'error', - title: 'Account discovery error', + title: , message: error.message, cancelable: true, actions: [ { - label: 'Try again', + label: , callback: () => { dispatch(start(device, discoveryProcess.network)); }, diff --git a/src/actions/NotificationActions.js b/src/actions/NotificationActions.js index 35ea0c18..9d9af9c2 100644 --- a/src/actions/NotificationActions.js +++ b/src/actions/NotificationActions.js @@ -2,7 +2,14 @@ import * as React from 'react'; import * as NOTIFICATION from 'actions/constants/notification'; -import type { Action, AsyncAction, GetState, Dispatch, RouterLocationState } from 'flowtype'; +import type { + Action, + AsyncAction, + GetState, + Dispatch, + RouterLocationState, + MessageDescriptor, +} from 'flowtype'; import type { CallbackAction } from 'reducers/NotificationReducer'; export type NotificationAction = @@ -10,7 +17,7 @@ export type NotificationAction = type: typeof NOTIFICATION.ADD, payload: { +variant: string, - +title: React.Node | string, + +title: React.Node | MessageDescriptor | string, +message?: ?(React.Node | string), +cancelable: boolean, actions?: Array, diff --git a/src/actions/ReceiveActions.js b/src/actions/ReceiveActions.js index bbb5d247..7516d195 100644 --- a/src/actions/ReceiveActions.js +++ b/src/actions/ReceiveActions.js @@ -1,5 +1,6 @@ /* @flow */ - +import React from 'react'; +import { FormattedMessage } from 'react-intl'; import TrezorConnect from 'trezor-connect'; import * as RECEIVE from 'actions/constants/receive'; import * as NOTIFICATION from 'actions/constants/notification'; @@ -8,6 +9,8 @@ import { initialState } from 'reducers/ReceiveReducer'; import type { State } from 'reducers/ReceiveReducer'; import type { TrezorDevice, ThunkAction, AsyncAction, Action, GetState, Dispatch } from 'flowtype'; +import l10nMessages from 'components/notifications/Context/actions.messages'; +import l10nCommonMessages from 'views/common.messages'; export type ReceiveAction = | { @@ -112,12 +115,12 @@ export const showAddress = (path: Array): AsyncAction => async ( type: NOTIFICATION.ADD, payload: { variant: 'error', - title: 'Verifying address error', + title: , message: response.payload.error, cancelable: true, actions: [ { - label: 'Try again', + label: , callback: () => { dispatch(showAddress(path)); }, diff --git a/src/actions/SignVerifyActions.js b/src/actions/SignVerifyActions.js index ecfe784b..a0722196 100644 --- a/src/actions/SignVerifyActions.js +++ b/src/actions/SignVerifyActions.js @@ -1,8 +1,11 @@ /* @flow */ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; import TrezorConnect from 'trezor-connect'; import type { GetState, Dispatch, ThunkAction, AsyncAction } from 'flowtype'; import { validateAddress } from 'utils/ethUtils'; import * as NOTIFICATION from 'actions/constants/notification'; +import l10nMessages from 'components/notifications/Context/actions.messages'; import * as SIGN_VERIFY from './constants/signVerify'; export type SignVerifyAction = @@ -65,7 +68,7 @@ const sign = (path: Array, message: string, hex: boolean = false): Async type: NOTIFICATION.ADD, payload: { variant: 'error', - title: 'Sign error', + title: , message: response.payload.error, cancelable: true, }, @@ -110,8 +113,8 @@ const verify = ( type: NOTIFICATION.ADD, payload: { variant: 'success', - title: 'Verify success', - message: 'signature is valid', + title: , + message: , cancelable: true, }, }); @@ -120,7 +123,7 @@ const verify = ( type: NOTIFICATION.ADD, payload: { variant: 'error', - title: 'Verify error', + title: , message: response.payload.error, cancelable: true, }, diff --git a/src/actions/TrezorConnectActions.js b/src/actions/TrezorConnectActions.js index bfa52881..e60931ab 100644 --- a/src/actions/TrezorConnectActions.js +++ b/src/actions/TrezorConnectActions.js @@ -6,6 +6,8 @@ import TrezorConnect, { TRANSPORT_EVENT, BLOCKCHAIN_EVENT, } from 'trezor-connect'; +import React from 'react'; +import { FormattedMessage } from 'react-intl'; import { CONTEXT_NONE } from 'actions/constants/modal'; import urlConstants from 'constants/urls'; import * as CONNECT from 'actions/constants/TrezorConnect'; @@ -35,6 +37,9 @@ import type { TrezorDevice, } from 'flowtype'; +import l10nCommonMessages from 'views/common.messages'; +import l10nMessages from 'components/notifications/Context/actions.messages'; + export type TrezorConnectAction = | { type: typeof CONNECT.INITIALIZATION_ERROR, @@ -264,12 +269,12 @@ export const authorizeDevice = (): AsyncAction => async ( payload: { devicePath: selected.path, variant: 'error', - title: 'Authentication error', + title: , message: response.payload.error, cancelable: false, actions: [ { - label: 'Try again', + label: , callback: () => { dispatch({ type: NOTIFICATION.CLOSE, @@ -351,7 +356,7 @@ export function acquire(): AsyncAction { payload: { type: 'error', variant: 'error', - title: 'Acquire device error', + title: , message: response.payload.error, cancelable: true, // actions: [ diff --git a/src/actions/ethereum/SendFormActions.js b/src/actions/ethereum/SendFormActions.js index 094a9158..92877a12 100644 --- a/src/actions/ethereum/SendFormActions.js +++ b/src/actions/ethereum/SendFormActions.js @@ -1,5 +1,6 @@ /* @flow */ import React from 'react'; +import { FormattedMessage } from 'react-intl'; import { Link } from 'trezor-ui-components'; import TrezorConnect from 'trezor-connect'; import BigNumber from 'bignumber.js'; @@ -23,10 +24,10 @@ import type { TrezorDevice, } from 'flowtype'; import type { State, FeeLevel } from 'reducers/SendFormEthereumReducer'; +import l10nMessages from 'components/notifications/Context/actions.messages'; import * as SessionStorageActions from '../SessionStorageActions'; import { prepareEthereumTx, serializeEthereumTx } from '../TxActions'; import * as BlockchainActions from './BlockchainActions'; - import * as ValidationActions from './SendFormValidationActions'; // list of all actions which has influence on "sendFormEthereum" reducer @@ -717,7 +718,7 @@ export const onSend = (): AsyncAction => async ( type: NOTIFICATION.ADD, payload: { variant: 'error', - title: 'Transaction error', + title: , message: signedTransaction.payload.error, cancelable: true, actions: [], @@ -807,10 +808,10 @@ export const onSend = (): AsyncAction => async ( type: NOTIFICATION.ADD, payload: { variant: 'success', - title: 'Transaction success', + title: , message: ( - See transaction detail + ), cancelable: true, @@ -822,7 +823,7 @@ export const onSend = (): AsyncAction => async ( type: NOTIFICATION.ADD, payload: { variant: 'error', - title: 'Transaction error', + title: , message: error.message || error, cancelable: true, actions: [], diff --git a/src/actions/ethereum/SendFormValidationActions.js b/src/actions/ethereum/SendFormValidationActions.js index c213710d..33aa0ec6 100644 --- a/src/actions/ethereum/SendFormValidationActions.js +++ b/src/actions/ethereum/SendFormValidationActions.js @@ -11,6 +11,8 @@ import * as validators from 'utils/validators'; import type { Dispatch, GetState, PayloadAction } from 'flowtype'; import type { State, FeeLevel } from 'reducers/SendFormEthereumReducer'; +import l10nMessages from 'views/Wallet/views/Account/Send/validation.messages'; +import l10nCommonMessages from 'views/common.messages'; /* * Called from SendFormActions.observe @@ -150,14 +152,14 @@ export const addressValidation = ($state: State): PayloadAction => (): St const { address } = state; if (address.length < 1) { - state.errors.address = 'Address is not set'; + state.errors.address = l10nMessages.TR_ADDRESS_IS_NOT_SET; } else if (!EthereumjsUtil.isValidAddress(address)) { - state.errors.address = 'Address is not valid'; + state.errors.address = l10nMessages.TR_ADDRESS_IS_NOT_VALID; } else if ( validators.hasUppercase(address) && !EthereumjsUtil.isValidChecksumAddress(address) ) { - state.errors.address = 'Address is not a valid checksum'; + state.errors.address = l10nMessages.TR_ADDRESS_CHECKSUM_IS_NOT_VALID; } return state; }; @@ -189,9 +191,13 @@ export const addressLabel = ($state: State): PayloadAction => ( currentNetworkAccount.deviceState ); if (device) { - state.infos.address = `${ - device.instanceLabel - } Account #${currentNetworkAccount.index + 1}`; + state.infos.address = { + ...l10nCommonMessages.TR_DEVICE_LABEL_ACCOUNT_HASH, + values: { + deviceLabel: device.instanceLabel, + number: currentNetworkAccount.index + 1, + }, + }; } } else { // corner-case: the same derivation path is used on different networks @@ -204,11 +210,14 @@ export const addressLabel = ($state: State): PayloadAction => ( const { networks } = getState().localStorage.config; const otherNetwork = networks.find(c => c.shortcut === otherNetworkAccount.network); if (device && otherNetwork) { - state.warnings.address = `Looks like it's ${ - device.instanceLabel - } Account #${otherNetworkAccount.index + 1} address of ${ - otherNetwork.name - } network`; + state.warnings.address = { + ...l10nCommonMessages.TR_LOOKS_LIKE_IT_IS_DEVICE_LABEL, + values: { + deviceLabel: device.instanceLabel, + number: otherNetworkAccount.index + 1, + network: otherNetwork.name, + }, + }; } } } @@ -231,9 +240,9 @@ export const amountValidation = ($state: State): PayloadAction => ( const { amount } = state; if (amount.length < 1) { - state.errors.amount = 'Amount is not set'; + state.errors.amount = l10nMessages.TR_AMOUNT_IS_NOT_SET; } else if (amount.length > 0 && !validators.isNumber(amount)) { - state.errors.amount = 'Amount is not a number'; + state.errors.amount = l10nMessages.TR_AMOUNT_IS_NOT_A_NUMBER; } else { const isToken: boolean = state.currency !== state.networkSymbol; const pendingAmount: BigNumber = getPendingAmount(pending, state.currency, isToken); @@ -248,26 +257,40 @@ export const amountValidation = ($state: State): PayloadAction => ( if (!token) return state; if (!validators.hasDecimals(state.amount, parseInt(token.decimals, 0))) { - state.errors.amount = `Maximum ${token.decimals} decimals allowed`; + state.errors.amount = { + ...l10nMessages.TR_MAXIMUM_DECIMALS_ALLOWED, + values: { decimals: token.decimals }, + }; } else if (new BigNumber(state.total).isGreaterThan(account.balance)) { - state.errors.amount = `Not enough ${state.networkSymbol} to cover transaction fee`; + state.errors.amount = { + ...l10nMessages.TR_NOT_ENOUGH_FUNDS_TO_COVER_TRANSACTION, + values: { + networkSymbol: state.networkSymbol, + }, + }; } else if ( new BigNumber(state.amount).isGreaterThan( new BigNumber(token.balance).minus(pendingAmount) ) ) { - state.errors.amount = 'Not enough funds'; + state.errors.amount = l10nMessages.TR_NOT_ENOUGH_FUNDS; } else if (new BigNumber(state.amount).isLessThanOrEqualTo('0')) { - state.errors.amount = 'Amount is too low'; + // TODO: this is never gonna happen! It will fail in second if condiftion (isNumber validation) + state.errors.amount = l10nMessages.TR_AMOUNT_IS_TOO_LOW; } } else if (!validators.hasDecimals(state.amount, 18)) { - state.errors.amount = 'Maximum 18 decimals allowed'; + state.errors.amount = { + ...l10nMessages.TR_MAXIMUM_DECIMALS_ALLOWED, + values: { + decimals: 18, + }, + }; } else if ( new BigNumber(state.total).isGreaterThan( new BigNumber(account.balance).minus(pendingAmount) ) ) { - state.errors.amount = 'Not enough funds'; + state.errors.amount = l10nMessages.TR_NOT_ENOUGH_FUNDS; } } return state; @@ -288,13 +311,13 @@ export const gasLimitValidation = ($state: State): PayloadAction => ( const { gasLimit } = state; if (gasLimit.length < 1) { - state.errors.gasLimit = 'Gas limit is not set'; + state.errors.gasLimit = l10nMessages.TR_GAS_LIMIT_IS_NOT_SET; } else if (gasLimit.length > 0 && !validators.isNumber(gasLimit)) { - state.errors.gasLimit = 'Gas limit is not a number'; + state.errors.gasLimit = l10nMessages.TR_GAS_LIMIT_IS_NOT_A_NUMBER; } else { const gl: BigNumber = new BigNumber(gasLimit); if (gl.isLessThan(1)) { - state.errors.gasLimit = 'Gas limit is too low'; + state.errors.gasLimit = l10nMessages.TR_GAS_LIMIT_IS_TOO_LOW; } else if ( gl.isLessThan( state.currency !== state.networkSymbol @@ -302,7 +325,7 @@ export const gasLimitValidation = ($state: State): PayloadAction => ( : network.defaultGasLimit ) ) { - state.warnings.gasLimit = 'Gas limit is below recommended'; + state.warnings.gasLimit = l10nMessages.TR_GAS_LIMIT_IS_BELOW_RECOMMENDED; } } return state; @@ -317,15 +340,15 @@ export const gasPriceValidation = ($state: State): PayloadAction => (): S const { gasPrice } = state; if (gasPrice.length < 1) { - state.errors.gasPrice = 'Gas price is not set'; + state.errors.gasPrice = l10nMessages.TR_GAS_PRICE_IS_NOT_SET; } else if (gasPrice.length > 0 && !validators.isNumber(gasPrice)) { - state.errors.gasPrice = 'Gas price is not a number'; + state.errors.gasPrice = l10nMessages.TR_GAS_PRICE_IS_NOT_A_NUMBER; } else { const gp: BigNumber = new BigNumber(gasPrice); if (gp.isGreaterThan(1000)) { - state.warnings.gasPrice = 'Gas price is too high'; + state.warnings.gasPrice = l10nMessages.TR_GAS_PRICE_IS_TOO_HIGH; } else if (gp.isLessThanOrEqualTo('0')) { - state.errors.gasPrice = 'Gas price is too low'; + state.errors.gasPrice = l10nMessages.TR_GAS_PRICE_IS_TOO_LOW; } } return state; @@ -346,28 +369,29 @@ export const nonceValidation = ($state: State): PayloadAction => ( const { nonce } = state; if (nonce.length < 1) { - state.errors.nonce = 'Nonce is not set'; + state.errors.nonce = l10nMessages.TR_NONCE_IS_NOT_SET; } else if (!validators.isAbs(nonce)) { - state.errors.nonce = 'Nonce is not a valid number'; + state.errors.nonce = l10nMessages.TR_NONCE_IS_NOT_A_NUMBER; } else { const n: BigNumber = new BigNumber(nonce); if (n.isLessThan(account.nonce)) { - state.warnings.nonce = 'Nonce is lower than recommended'; + state.warnings.nonce = l10nMessages.TR_NONCE_IS_LOWER_THAN_RECOMMENDED; } else if (n.isGreaterThan(account.nonce)) { - state.warnings.nonce = 'Nonce is greater than recommended'; + state.warnings.nonce = l10nMessages.TR_NONCE_IS_GREATER_THAN_RECOMMENDED; } } return state; }; /* - * Gas price value validation + * Data validation */ export const dataValidation = ($state: State): PayloadAction => (): State => { const state = { ...$state }; if (!state.touched.data || state.data.length === 0) return state; + if (!ethUtils.isHex(state.data)) { - state.errors.data = 'Data is not valid hexadecimal'; + state.errors.data = l10nMessages.TR_DATA_IS_NOT_VALID_HEX; } return state; }; @@ -432,12 +456,13 @@ export const getFeeLevels = ( selected && selected.value === 'Custom' ? { value: 'Custom', + localizedValue: l10nCommonMessages.TR_CUSTOM_FEE, gasPrice: selected.gasPrice, - // label: `${ calculateFee(gasPrice, gasLimit) } ${ symbol }` label: `${calculateFee(selected.gasPrice, gasLimit)} ${symbol}`, } : { value: 'Custom', + localizedValue: l10nCommonMessages.TR_CUSTOM_FEE, gasPrice: low, label: '', }; @@ -445,16 +470,19 @@ export const getFeeLevels = ( return [ { value: 'High', + localizedValue: l10nCommonMessages.TR_HIGH_FEE, gasPrice: high, label: `${calculateFee(high, gasLimit)} ${symbol}`, }, { value: 'Normal', + localizedValue: l10nCommonMessages.TR_NORMAL_FEE, gasPrice: gasPrice.toString(), label: `${calculateFee(price.toFixed(), gasLimit)} ${symbol}`, }, { value: 'Low', + localizedValue: l10nCommonMessages.TR_LOW_FEE, gasPrice: low, label: `${calculateFee(low, gasLimit)} ${symbol}`, }, diff --git a/src/actions/ripple/SendFormActions.js b/src/actions/ripple/SendFormActions.js index 302d0051..fce5ef8b 100644 --- a/src/actions/ripple/SendFormActions.js +++ b/src/actions/ripple/SendFormActions.js @@ -1,4 +1,6 @@ /* @flow */ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; import TrezorConnect from 'trezor-connect'; import * as NOTIFICATION from 'actions/constants/notification'; import * as SEND from 'actions/constants/send'; @@ -19,6 +21,7 @@ import type { TrezorDevice, } from 'flowtype'; import type { State, FeeLevel } from 'reducers/SendFormRippleReducer'; +import l10nMessages from 'components/notifications/Context/actions.messages'; import * as SessionStorageActions from '../SessionStorageActions'; import * as BlockchainActions from './BlockchainActions'; @@ -455,7 +458,7 @@ export const onSend = (): AsyncAction => async ( type: NOTIFICATION.ADD, payload: { variant: 'error', - title: 'Transaction error', + title: , message: signedTransaction.payload.error, cancelable: true, actions: [], @@ -474,7 +477,7 @@ export const onSend = (): AsyncAction => async ( type: NOTIFICATION.ADD, payload: { variant: 'error', - title: 'Transaction error', + title: , message: push.payload.error, cancelable: true, actions: [], @@ -497,7 +500,7 @@ export const onSend = (): AsyncAction => async ( type: NOTIFICATION.ADD, payload: { variant: 'success', - title: 'Transaction success', + title: , message: txid, cancelable: true, actions: [], diff --git a/src/actions/ripple/SendFormValidationActions.js b/src/actions/ripple/SendFormValidationActions.js index d24ff34c..cf400427 100644 --- a/src/actions/ripple/SendFormValidationActions.js +++ b/src/actions/ripple/SendFormValidationActions.js @@ -6,6 +6,8 @@ import { findDevice, getPendingAmount } from 'reducers/utils'; import { toDecimalAmount } from 'utils/formatUtils'; import { toFiatCurrency } from 'utils/fiatConverter'; import * as validators from 'utils/validators'; +import l10nMessages from 'views/Wallet/views/Account/Send/validation.messages'; +import l10nCommonMessages from 'views/common.messages'; import type { Dispatch, @@ -150,11 +152,11 @@ const addressValidation = ($state: State): PayloadAction => ( const { address } = state; if (address.length < 1) { - state.errors.address = 'Address is not set'; + state.errors.address = l10nMessages.TR_ADDRESS_IS_NOT_SET; } else if (!AddressValidator.validate(address, 'XRP')) { - state.errors.address = 'Address is not valid'; + state.errors.address = l10nMessages.TR_ADDRESS_IS_NOT_VALID; } else if (address.toLowerCase() === account.descriptor.toLowerCase()) { - state.errors.address = 'Cannot send to myself'; + state.errors.address = l10nMessages.TR_CANNOT_SEND_TO_MYSELF; } return state; }; @@ -226,9 +228,13 @@ const addressLabel = ($state: State): PayloadAction => ( currentNetworkAccount.deviceState ); if (device) { - state.infos.address = `${ - device.instanceLabel - } Account #${currentNetworkAccount.index + 1}`; + state.infos.address = { + ...l10nCommonMessages.TR_DEVICE_LABEL_ACCOUNT_HASH, + values: { + deviceLabel: device.instanceLabel, + number: currentNetworkAccount.index + 1, + }, + }; } } else { // corner-case: the same derivation path is used on different networks @@ -241,11 +247,14 @@ const addressLabel = ($state: State): PayloadAction => ( const { networks } = getState().localStorage.config; const otherNetwork = networks.find(c => c.shortcut === otherNetworkAccount.network); if (device && otherNetwork) { - state.warnings.address = `Looks like it's ${ - device.instanceLabel - } Account #${otherNetworkAccount.index + 1} address of ${ - otherNetwork.name - } network`; + state.warnings.address = { + ...l10nCommonMessages.TR_LOOKS_LIKE_IT_IS_DEVICE_LABEL, + values: { + deviceLabel: device.instanceLabel, + number: otherNetworkAccount.index + 1, + network: otherNetwork.name, + }, + }; } } } @@ -268,19 +277,22 @@ const amountValidation = ($state: State): PayloadAction => ( const { amount } = state; if (amount.length < 1) { - state.errors.amount = 'Amount is not set'; + state.errors.amount = l10nMessages.TR_AMOUNT_IS_NOT_SET; } else if (amount.length > 0 && !validators.isNumber(amount)) { - state.errors.amount = 'Amount is not a number'; + state.errors.amount = l10nMessages.TR_AMOUNT_IS_NOT_A_NUMBER; } else { const pendingAmount: BigNumber = getPendingAmount(pending, state.networkSymbol); if (!validators.hasDecimals(state.amount, 6)) { - state.errors.amount = 'Maximum 6 decimals allowed'; + state.errors.amount = { + ...l10nMessages.TR_MAXIMUM_DECIMALS_ALLOWED, + values: { decimals: 6 }, + }; } else if ( new BigNumber(state.total).isGreaterThan( new BigNumber(account.balance).minus(pendingAmount) ) ) { - state.errors.amount = 'Not enough funds'; + state.errors.amount = l10nMessages.TR_NOT_ENOUGH_FUNDS; } } @@ -288,15 +300,23 @@ const amountValidation = ($state: State): PayloadAction => ( !state.errors.amount && new BigNumber(account.balance).minus(state.total).lt(account.reserve) ) { - state.errors.amount = `Not enough funds. Reserved amount for this account is ${ - account.reserve - } ${state.networkSymbol}`; + state.errors.amount = { + ...l10nMessages.TR_NOT_ENOUGH_FUNDS_RESERVED_AMOUNT, + values: { + reservedAmount: account.reserve, + networkSymbol: state.networkSymbol, + }, + }; } if (!state.errors.amount && new BigNumber(state.amount).lt(state.minAmount)) { - state.errors.amount = `Amount is too low. Minimum amount for creating a new account is ${ - state.minAmount - } ${state.networkSymbol}`; + state.errors.amount = { + ...l10nMessages.TR_AMOUNT_IS_TOO_LOW_MINIMUM_AMOUNT_FOR_CREATING, + values: { + reservedAmount: state.minAmount, + networkSymbol: state.networkSymbol, + }, + }; } return state; @@ -317,15 +337,15 @@ export const feeValidation = ($state: State): PayloadAction => ( const { fee } = state; if (fee.length < 1) { - state.errors.fee = 'Fee is not set'; + state.errors.fee = l10nMessages.TR_FEE_IS_NOT_SET; } else if (fee.length > 0 && !validators.isAbs(fee)) { - state.errors.fee = 'Fee must be an absolute number'; + state.errors.fee = l10nMessages.TR_FEE_MUST_ME_AN_ABSOLUT_NUMBER; } else { const gl: BigNumber = new BigNumber(fee); if (gl.isLessThan(network.fee.minFee)) { - state.errors.fee = 'Fee is below recommended'; + state.errors.fee = l10nMessages.TR_FEE_IS_BELOW_RECOMMENDED; } else if (gl.isGreaterThan(network.fee.maxFee)) { - state.errors.fee = 'Fee is above recommended'; + state.errors.fee = l10nMessages.TR_FEE_IS_ABOVE_RECOMMENDED; } } return state; @@ -340,11 +360,11 @@ export const destinationTagValidation = ($state: State): PayloadAction => const { destinationTag } = state; if (destinationTag.length > 0 && !validators.isAbs(destinationTag)) { - state.errors.destinationTag = 'Destination tag must be an absolute number'; + state.errors.destinationTag = l10nMessages.TR_DESTINATION_TAG_MUST_BE_AN_ABSOLUTE; } if (parseInt(destinationTag, 10) > U_INT_32) { - state.errors.destinationTag = 'Number is too big'; + state.errors.destinationTag = l10nMessages.TR_DESTINATION_TAG_IS_NOT_VALID; } return state; @@ -386,9 +406,16 @@ export const getFeeLevels = ( const { network } = getState().selectedAccount; if (!network) return []; // flowtype fallback + const l10nFeeMap = { + Low: l10nCommonMessages.TR_LOW_FEE, + Normal: l10nCommonMessages.TR_NORMAL_FEE, + High: l10nCommonMessages.TR_HIGH_FEE, + }; + // map BlockchainFeeLevel to SendFormReducer FeeLevel const levels = feeLevels.map(level => ({ value: level.name, + localizedValue: l10nFeeMap[level.value], fee: level.value, label: `${toDecimalAmount(level.value, network.decimals)} ${network.symbol}`, })); @@ -398,11 +425,13 @@ export const getFeeLevels = ( selected && selected.value === 'Custom' ? { value: 'Custom', + localizedValue: l10nCommonMessages.TR_CUSTOM_FEE, fee: selected.fee, label: `${toDecimalAmount(selected.fee, network.decimals)} ${network.symbol}`, } : { value: 'Custom', + localizedValue: l10nCommonMessages.TR_CUSTOM_FEE, fee: '0', label: '', }; diff --git a/src/components/DeviceHeader/index.js b/src/components/DeviceHeader/index.js index 3f3a7350..ce10788c 100644 --- a/src/components/DeviceHeader/index.js +++ b/src/components/DeviceHeader/index.js @@ -1,9 +1,9 @@ -// TODO: l10n for device status import React from 'react'; import styled, { css } from 'styled-components'; import PropTypes from 'prop-types'; import { getStatusColor, getStatusName, getStatus } from 'utils/device'; import { TrezorImage, colors } from 'trezor-ui-components'; +import { injectIntl } from 'react-intl'; import { FONT_SIZE, FONT_WEIGHT } from 'config/variables'; @@ -102,6 +102,7 @@ const DeviceHeader = ({ isSelected = false, className, testId, + intl, }) => { const status = getStatus(device); return ( @@ -123,7 +124,7 @@ const DeviceHeader = ({ {device.instanceLabel} - {getStatusName(status)} + {getStatusName(status, intl)} {icon && !disabled && isAccessible && icon} @@ -141,6 +142,7 @@ DeviceHeader.propTypes = { onClickWrapper: PropTypes.func.isRequired, className: PropTypes.string, testId: PropTypes.string, + intl: PropTypes.any, }; -export default DeviceHeader; +export default injectIntl(DeviceHeader); diff --git a/src/components/DeviceHeader/index.messages.js b/src/components/DeviceHeader/index.messages.js new file mode 100644 index 00000000..329f1277 --- /dev/null +++ b/src/components/DeviceHeader/index.messages.js @@ -0,0 +1,63 @@ +/* @flow */ +import { defineMessages } from 'react-intl'; +import type { Messages } from 'flowtype'; + +const definedMessages: Messages = defineMessages({ + TR_CONNECTED: { + id: 'TR_CONNECTED', + defaultMessage: 'Connected', + description: 'Device status', + }, + TR_CONNECTED_BOOTLOADER: { + id: 'TR_CONNECTED_BOOTLOADER', + defaultMessage: 'Connected (bootloader mode)', + description: 'Device status', + }, + TR_CONNECTED_NOT_INITIALIZED: { + id: 'TR_CONNECTED_NOT_INITIALIZED', + defaultMessage: 'Connected (not initialized)', + description: 'Device status', + }, + TR_CONNECTED_SEEDLESS: { + id: 'TR_CONNECTED_SEEDLESS', + defaultMessage: 'Connected (seedless mode)', + description: 'Device status', + }, + TR_CONNECTED_UPDATE_REQUIRED: { + id: 'TR_CONNECTED_UPDATE_REQUIRED', + defaultMessage: 'Connected (update required)', + description: 'Device status', + }, + TR_CONNECTED_UPDATE_RECOMMENDED: { + id: 'TR_CONNECTED_UPDATE_RECOMMENDED', + defaultMessage: 'Connected (update recommended)', + description: 'Device status', + }, + TR_USED_IN_ANOTHER_WINDOW: { + id: 'TR_USED_IN_ANOTHER_WINDOW', + defaultMessage: 'Used in other window', + description: 'Device status', + }, + TR_UNAVAILABLE: { + id: 'TR_UNAVAILABLE', + defaultMessage: 'Unavailable', + description: 'Device status', + }, + TR_UNREADABLE: { + id: 'TR_UNREADABLE', + defaultMessage: 'Unreadable', + description: 'Device status', + }, + TR_DISCONNECTED: { + id: 'TR_DISCONNECTED', + defaultMessage: 'Disconnected', + description: 'Device status', + }, + TR_STATUS_UNKNOWN: { + id: 'TR_STATUS_UNKNOWN', + defaultMessage: 'Status unknown', + description: 'Device status', + }, +}); + +export default definedMessages; diff --git a/src/components/Footer/index.messages.js b/src/components/Footer/index.messages.js index f5e1efb8..19542e56 100644 --- a/src/components/Footer/index.messages.js +++ b/src/components/Footer/index.messages.js @@ -1,6 +1,6 @@ /* @flow */ import { defineMessages } from 'react-intl'; -import type { Messages } from 'flowtype/npm/react-intl'; +import type { Messages } from 'flowtype'; const definedMessages: Messages = defineMessages({ TR_TERMS: { diff --git a/src/components/Header/index.messages.js b/src/components/Header/index.messages.js index d9f5f048..1e4adb0a 100644 --- a/src/components/Header/index.messages.js +++ b/src/components/Header/index.messages.js @@ -1,6 +1,6 @@ /* @flow */ import { defineMessages } from 'react-intl'; -import type { Messages } from 'flowtype/npm/react-intl'; +import type { Messages } from 'flowtype'; const definedMessages: Messages = defineMessages({ TR_MENU: { diff --git a/src/components/Log/index.messages.js b/src/components/Log/index.messages.js index 702d677c..11e6711b 100644 --- a/src/components/Log/index.messages.js +++ b/src/components/Log/index.messages.js @@ -1,6 +1,6 @@ /* @flow */ import { defineMessages } from 'react-intl'; -import type { Messages } from 'flowtype/npm/react-intl'; +import type { Messages } from 'flowtype'; const definedMessages: Messages = defineMessages({ TR_ATTENTION_COLON_THE_LOG_CONTAINS: { diff --git a/src/components/modals/QrModal/index.messages.js b/src/components/modals/QrModal/index.messages.js index 2b188323..99582c0a 100644 --- a/src/components/modals/QrModal/index.messages.js +++ b/src/components/modals/QrModal/index.messages.js @@ -1,6 +1,6 @@ /* @flow */ import { defineMessages } from 'react-intl'; -import type { Messages } from 'flowtype/npm/react-intl'; +import type { Messages } from 'flowtype'; const definedMessages: Messages = defineMessages({ TR_SCAN_QR_CODE: { diff --git a/src/components/modals/confirm/Action/index.messages.js b/src/components/modals/confirm/Action/index.messages.js index d98eed09..0d675228 100644 --- a/src/components/modals/confirm/Action/index.messages.js +++ b/src/components/modals/confirm/Action/index.messages.js @@ -1,6 +1,6 @@ /* @flow */ import { defineMessages } from 'react-intl'; -import type { Messages } from 'flowtype/npm/react-intl'; +import type { Messages } from 'flowtype'; const definedMessages: Messages = defineMessages({ TR_CONFIRM_ACTION_ON_YOUR: { diff --git a/src/components/modals/confirm/Address/index.messages.js b/src/components/modals/confirm/Address/index.messages.js index c1d0f4b3..854523ee 100644 --- a/src/components/modals/confirm/Address/index.messages.js +++ b/src/components/modals/confirm/Address/index.messages.js @@ -1,6 +1,6 @@ /* @flow */ import { defineMessages } from 'react-intl'; -import type { Messages } from 'flowtype/npm/react-intl'; +import type { Messages } from 'flowtype'; const definedMessages: Messages = defineMessages({ TR_CONFIRM_ADDRESS_ON_TREZOR: { diff --git a/src/components/modals/confirm/SignTx/index.messages.js b/src/components/modals/confirm/SignTx/index.messages.js index a712178e..8c402546 100644 --- a/src/components/modals/confirm/SignTx/index.messages.js +++ b/src/components/modals/confirm/SignTx/index.messages.js @@ -1,6 +1,6 @@ /* @flow */ import { defineMessages } from 'react-intl'; -import type { Messages } from 'flowtype/npm/react-intl'; +import type { Messages } from 'flowtype'; const definedMessages: Messages = defineMessages({ TR_CONFIRM_TRANSACTION_ON: { diff --git a/src/components/modals/confirm/UnverifiedAddress/index.js b/src/components/modals/confirm/UnverifiedAddress/index.js index 09256c97..790a6406 100644 --- a/src/components/modals/confirm/UnverifiedAddress/index.js +++ b/src/components/modals/confirm/UnverifiedAddress/index.js @@ -145,7 +145,7 @@ class ConfirmUnverifiedAddress extends PureComponent {