From e7a81dc817e87832aa5296ac24168f5127d2328e Mon Sep 17 00:00:00 2001 From: Luke Schoen Date: Tue, 26 Feb 2019 00:06:21 +0100 Subject: [PATCH 01/19] chore: Update to latest React 16.8.3 to requirements of react-i18next --- packages/fether-react/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fether-react/package.json b/packages/fether-react/package.json index e9ea2750a..1a0cd0d12 100644 --- a/packages/fether-react/package.json +++ b/packages/fether-react/package.json @@ -58,9 +58,9 @@ "lodash": "^4.17.10", "mobx": "^5.0.2", "mobx-react": "^5.2.3", - "react": "^16.3.2", + "react": "^16.8.3", "react-blockies": "^1.3.0", - "react-dom": "^16.3.2", + "react-dom": "^16.8.3", "react-final-form": "^3.6.4", "react-final-form-listeners": "^1.0.1", "react-markdown": "^3.3.4", From 27c6e0670ec4768d7549e099923fa341528ca861 Mon Sep 17 00:00:00 2001 From: Luke Schoen Date: Tue, 26 Feb 2019 02:12:17 +0100 Subject: [PATCH 02/19] feat: Scaffold basic translation with English and German --- packages/fether-react/package.json | 3 + .../src/Accounts/AccountsList/AccountsList.js | 17 +++- packages/fether-react/src/i18n/index.js | 87 +++++++++++++++++++ .../fether-react/src/i18n/locales/de.json | 6 ++ .../fether-react/src/i18n/locales/en.json | 6 ++ .../fether-react/src/i18n/locales/index.js | 4 + packages/fether-react/src/utils/withTokens.js | 5 ++ .../fether-react/src/utils/withTranslation.js | 9 ++ yarn.lock | 69 +++++++++++++-- 9 files changed, 196 insertions(+), 10 deletions(-) create mode 100644 packages/fether-react/src/i18n/index.js create mode 100644 packages/fether-react/src/i18n/locales/de.json create mode 100644 packages/fether-react/src/i18n/locales/en.json create mode 100644 packages/fether-react/src/i18n/locales/index.js create mode 100644 packages/fether-react/src/utils/withTranslation.js diff --git a/packages/fether-react/package.json b/packages/fether-react/package.json index 1a0cd0d12..94450dd8a 100644 --- a/packages/fether-react/package.json +++ b/packages/fether-react/package.json @@ -52,6 +52,8 @@ "file-saver": "^2.0.0", "final-form": "^4.8.3", "final-form-calculate": "^1.2.1", + "i18next": "^15.0.4", + "i18next-browser-languagedetector": "^3.0.1", "is-electron": "^2.1.0", "localforage": "^1.7.2", "localforage-observable": "^1.4.0", @@ -63,6 +65,7 @@ "react-dom": "^16.8.3", "react-final-form": "^3.6.4", "react-final-form-listeners": "^1.0.1", + "react-i18next": "^10.2.0", "react-markdown": "^3.3.4", "react-resize-detector": "^3.0.1", "react-router-dom": "^4.2.2", diff --git a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js index 1c72cfec3..4ba3fe187 100644 --- a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js +++ b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js @@ -9,10 +9,16 @@ import { chainId$, withoutLoading } from '@parity/light.js'; import { inject, observer } from 'mobx-react'; import light from '@parity/light.js-react'; +import i18n from '../../i18n'; import RequireHealthOverlay from '../../RequireHealthOverlay'; import Health from '../../Health'; -import Feedback from './Feedback'; import withAccountsInfo from '../../utils/withAccountsInfo'; +// import withTranslation from '../../utils/withTranslation'; + +import Feedback from './Feedback'; + +i18n.changeLanguage('de-DE'); +console.log('i18n.language: ', i18n.language); @withAccountsInfo @inject('createAccountStore', 'parityStore') @@ -39,6 +45,9 @@ class AccountsList extends Component { render () { const { accountsInfo, chainId } = this.props; + // i18n.changeLanguage('en-US'); + // console.log('i18n.language: ', i18n.language); + const accountsList = Object.keys(accountsInfo).filter( key => !accountsInfo[key].chainId || @@ -56,7 +65,7 @@ class AccountsList extends Component { onClick={this.handleCreateAccount} /> } - title={

Accounts

} + title={

{i18n.t('ns1:accounts_list.header')}

} />
@@ -73,7 +82,7 @@ class AccountsList extends Component { address={address} className='-clickable' type={accountsInfo[address].type} - name={accountsInfo[address].name || '(no name)'} + name={accountsInfo[address].name || i18n.t('(no name)')} screen='accounts' shortAddress /> @@ -85,7 +94,7 @@ class AccountsList extends Component { Nothing here yet!

- Click the + icon to add a new account. + {i18n.t('ns1:accounts_list.hint')}

)}
diff --git a/packages/fether-react/src/i18n/index.js b/packages/fether-react/src/i18n/index.js new file mode 100644 index 000000000..d8874606b --- /dev/null +++ b/packages/fether-react/src/i18n/index.js @@ -0,0 +1,87 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +// https://react.i18next.com/getting-started +import i18next from 'i18next'; +// // https://github.com/i18next/i18next-browser-languageDetector +import LanguageDetector from 'i18next-browser-languagedetector'; +// // https://www.npmjs.com/package/i18next-xhr-backend +// import Backend from 'i18next-xhr-backend'; +import { initReactI18next } from 'react-i18next'; + +import { en, de } from './locales'; + +const i18n = i18next; +i18n + // .use(Backend) + .use(LanguageDetector) + .use(initReactI18next) + .init({ + debug: true, + defaultNS: 'ns1', + fallbackLng: ['en-US', 'en', 'de-DE', 'de'], + interpolation: { + escapeValue: false + }, + lng: 'en', + ns: ['ns1'], + // https://react.i18next.com/misc/using-with-icu-format + react: { + wait: true, + bindI18n: 'languageChanged loaded', + bindStore: 'added removed', + nsMode: 'default' + }, + resources: { + en: { + ns1: en + }, + de: { + ns1: de + } + }, + saveMissing: true + }) + .then(() => console.log('i18n: success')) + .catch(error => console.log('i18n: failure', error)); + +// https://www.i18next.com/overview/api#changelanguage +i18n.changeLanguage(navigator.language, (err, t) => { + if (err) { + console.log(`Error loading language ${navigator.language}`, err); + } +}); + +i18next.on('initialized', options => { + console.log('Detected initialisation of i18n'); +}); + +i18next.on('loaded', loaded => { + console.log('Detected success loading resources', loaded); +}); + +i18next.on('failedLoading', (lng, ns, msg) => { + console.log('Detected failure loading resources', lng, ns, msg); +}); + +// saveMissing must be configured to `true` +i18next.on('missingKey', (lngs, namespace, key, res) => { + console.log('Detected missing key: ', lngs, namespace, key, res); +}); + +i18next.store.on('added', (lng, ns) => { + console.log('Detected resources added', lng, ns); +}); + +i18next.store.on('removed', (lng, ns) => { + console.log('Detected resources removed', lng, ns); +}); + +// https://www.i18next.com/overview/api#changelanguage +i18next.on('languageChanged', lng => { + console.log('Detected language change to: ', lng); +}); + +export default i18n; diff --git a/packages/fether-react/src/i18n/locales/de.json b/packages/fether-react/src/i18n/locales/de.json new file mode 100644 index 000000000..7b8b523be --- /dev/null +++ b/packages/fether-react/src/i18n/locales/de.json @@ -0,0 +1,6 @@ +{ + "accounts_list": { + "header": "Konten", + "hint": "Klicken Sie auf das + Symbol, um ein neues Konto hinzuzufügen." + } +} \ No newline at end of file diff --git a/packages/fether-react/src/i18n/locales/en.json b/packages/fether-react/src/i18n/locales/en.json new file mode 100644 index 000000000..252942213 --- /dev/null +++ b/packages/fether-react/src/i18n/locales/en.json @@ -0,0 +1,6 @@ +{ + "accounts_list": { + "header": "Accounts TEST", + "hint": "Click the + icon to add a new account." + } +} \ No newline at end of file diff --git a/packages/fether-react/src/i18n/locales/index.js b/packages/fether-react/src/i18n/locales/index.js new file mode 100644 index 000000000..8e42059f1 --- /dev/null +++ b/packages/fether-react/src/i18n/locales/index.js @@ -0,0 +1,4 @@ +import en from './en.json'; +import de from './de.json'; + +export { en, de }; diff --git a/packages/fether-react/src/utils/withTokens.js b/packages/fether-react/src/utils/withTokens.js index 78ee8d72c..5bead49d6 100644 --- a/packages/fether-react/src/utils/withTokens.js +++ b/packages/fether-react/src/utils/withTokens.js @@ -1,3 +1,8 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + import { chainName$, withoutLoading } from '@parity/light.js'; import { compose, mapPropsStream, withHandlers, withProps } from 'recompose'; import light from '@parity/light.js-react'; diff --git a/packages/fether-react/src/utils/withTranslation.js b/packages/fether-react/src/utils/withTranslation.js new file mode 100644 index 000000000..038b340eb --- /dev/null +++ b/packages/fether-react/src/utils/withTranslation.js @@ -0,0 +1,9 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import { withTranslation } from 'react-i18next'; + +// https://react.i18next.com/getting-started +export default withTranslation(['ns1']); diff --git a/yarn.lock b/yarn.lock index 7cd28facc..36aa938bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -911,6 +911,13 @@ dependencies: regenerator-runtime "^0.12.0" +"@babel/runtime@^7.3.1": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83" + integrity sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g== + dependencies: + regenerator-runtime "^0.12.0" + "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2": version "7.2.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" @@ -7090,6 +7097,13 @@ html-minifier@^3.2.3: relateurl "0.2.x" uglify-js "3.4.x" +html-parse-stringify2@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a" + integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o= + dependencies: + void-elements "^2.0.1" + html-webpack-plugin@4.0.0-alpha.2: version "4.0.0-alpha.2" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-alpha.2.tgz#7745967e389a57a098e26963f328ebe4c19b598d" @@ -7247,6 +7261,18 @@ husky@^1.0.0-rc.13: run-node "^1.0.0" slash "^2.0.0" +i18next-browser-languagedetector@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-3.0.1.tgz#a47c43176e8412c91e808afb7c6eb5367649aa8e" + integrity sha512-WFjPLNPWl62uu07AHY2g+KsC9qz0tyMq+OZEB/H7N58YKL/JLiCz9U709gaR20Mule/Ppn+uyfVx5REJJjn1HA== + +i18next@^15.0.4: + version "15.0.4" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-15.0.4.tgz#17ee253ff2cc67c5003065fef2f4196b2d6d8b04" + integrity sha512-msRzIP/9Vo6wMbbkFRoosVu5X55vARjfhX3H9Z2avRoDAjIBNsXfpfCVF3hbP/2aMQDNEds3NUuDxE4XhcAMnA== + dependencies: + "@babel/runtime" "^7.3.1" + iconv-lite@0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" @@ -12010,15 +12036,15 @@ react-dev-utils@^7.0.1: strip-ansi "4.0.0" text-table "0.2.0" -react-dom@^16.3.2: - version "16.7.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0.tgz#a17b2a7ca89ee7390bc1ed5eb81783c7461748b8" - integrity sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg== +react-dom@^16.8.3: + version "16.8.3" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.3.tgz#ae236029e66210783ac81999d3015dfc475b9c32" + integrity sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.12.0" + scheduler "^0.13.3" react-dropzone@^7.0.1: version "7.0.1" @@ -12045,6 +12071,14 @@ react-final-form@^3.6.4: dependencies: "@babel/runtime" "^7.1.2" +react-i18next@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-10.2.0.tgz#68674945711d4ccfaca52af9e20fe648f84ffad5" + integrity sha512-b+AryBB2QiLCEmk8PaNGMkowEZVSY032crKuQToa+W1lBgrNiWVKstmi0z4bZqqcK9GL4cJr8Mevw4XpPFvpeQ== + dependencies: + "@babel/runtime" "^7.3.1" + html-parse-stringify2 "2.0.1" + react-is@^16.7.0: version "16.7.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.7.0.tgz#c1bd21c64f1f1364c6f70695ec02d69392f41bfa" @@ -12166,7 +12200,7 @@ react-scripts@^2.1.3: optionalDependencies: fsevents "1.2.4" -react@^16.3.2, react@^16.4.0: +react@^16.4.0: version "16.7.0" resolved "https://registry.yarnpkg.com/react/-/react-16.7.0.tgz#b674ec396b0a5715873b350446f7ea0802ab6381" integrity sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A== @@ -12176,6 +12210,16 @@ react@^16.3.2, react@^16.4.0: prop-types "^15.6.2" scheduler "^0.12.0" +react@^16.8.3: + version "16.8.3" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.3.tgz#c6f988a2ce895375de216edcfaedd6b9a76451d9" + integrity sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.3" + read-cmd-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz#2d5d157786a37c055d22077c32c53f8329e91c7b" @@ -12930,6 +12974,14 @@ scheduler@^0.12.0: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@^0.13.3: + version "0.13.3" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.3.tgz#bed3c5850f62ea9c716a4d781f9daeb9b2a58896" + integrity sha512-UxN5QRYWtpR1egNWzJcVLk8jlegxAugswQc984lD3kU7NuobsO37/sRfbpTdBjtnD5TBNFA2Q2oLV5+UmPSmEQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^0.4.3, schema-utils@^0.4.4, schema-utils@^0.4.5: version "0.4.7" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" @@ -14693,6 +14745,11 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + w3c-hr-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" From 12142e2bfcd3a0c75d23a0632267be71a2eafc1c Mon Sep 17 00:00:00 2001 From: Luke Schoen Date: Tue, 26 Feb 2019 19:18:50 +0100 Subject: [PATCH 03/19] feat: Relates to #402. German translation fully working --- .../src/Accounts/AccountsList/AccountsList.js | 20 +- .../AccountsList/Feedback/Feedback.js | 4 +- .../AccountCopyPhrase/AccountCopyPhrase.js | 20 +- .../AccountImportOptions.js | 42 ++-- .../CreateAccount/AccountName/AccountName.js | 16 +- .../AccountPassword/AccountPassword.js | 37 ++- .../AccountRewritePhrase.js | 17 +- .../Accounts/CreateAccount/CreateAccount.js | 11 +- .../src/BackupAccount/BackupAccount.js | 12 +- packages/fether-react/src/Health/Health.js | 19 +- .../fether-react/src/Onboarding/Onboarding.js | 7 +- .../HealthModal/HealthModal.js | 28 +-- .../RequireHealthOverlay.js | 5 +- packages/fether-react/src/Scanner/Scanner.js | 10 +- .../src/Send/ScanSignedTx/ScanSignedTx.js | 17 +- packages/fether-react/src/Send/Sent/Sent.js | 21 +- .../Send/SignedTxSummary/SignedTxSummary.js | 29 ++- .../src/Send/TxForm/TxDetails/TxDetails.js | 24 +- .../fether-react/src/Send/TxForm/TxForm.js | 110 ++++++--- .../src/Send/TxQrCode/TxQrCode.js | 11 +- .../fether-react/src/Send/Unlock/Unlock.js | 23 +- packages/fether-react/src/Tokens/Tokens.js | 8 +- .../TokensList/TokenAddress/TokenAddress.js | 2 + .../src/Tokens/TokensList/TokensList.js | 8 +- .../Whitelist/NewTokenItem/NewTokenItem.js | 5 +- .../fether-react/src/Whitelist/Whitelist.js | 9 +- packages/fether-react/src/i18n/index.js | 3 + .../fether-react/src/i18n/locales/de.json | 225 ++++++++++++++++- .../fether-react/src/i18n/locales/en.json | 227 +++++++++++++++++- packages/fether-react/src/utils/blockscout.js | 6 +- .../fether-react/src/utils/withTranslation.js | 9 - .../fether-ui/src/AccountCard/AccountCard.js | 9 +- .../src/AccountHeader/AccountHeader.js | 13 +- .../fether-ui/src/ClickToCopy/ClickToCopy.js | 9 +- .../fether-ui/src/Form/InputFile/InputFile.js | 5 +- 35 files changed, 796 insertions(+), 225 deletions(-) delete mode 100644 packages/fether-react/src/utils/withTranslation.js diff --git a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js index 4ba3fe187..fe42cd7e5 100644 --- a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js +++ b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js @@ -13,13 +13,8 @@ import i18n from '../../i18n'; import RequireHealthOverlay from '../../RequireHealthOverlay'; import Health from '../../Health'; import withAccountsInfo from '../../utils/withAccountsInfo'; -// import withTranslation from '../../utils/withTranslation'; - import Feedback from './Feedback'; -i18n.changeLanguage('de-DE'); -console.log('i18n.language: ', i18n.language); - @withAccountsInfo @inject('createAccountStore', 'parityStore') @light({ @@ -45,9 +40,6 @@ class AccountsList extends Component { render () { const { accountsInfo, chainId } = this.props; - // i18n.changeLanguage('en-US'); - // console.log('i18n.language: ', i18n.language); - const accountsList = Object.keys(accountsInfo).filter( key => !accountsInfo[key].chainId || @@ -81,20 +73,24 @@ class AccountsList extends Component { ))} ) : (

- Nothing here yet! + {i18n.t('ns1:accounts_list.hint.none')}

- {i18n.t('ns1:accounts_list.hint')} + {i18n.t('ns1:accounts_list.hint.exist')}

)} diff --git a/packages/fether-react/src/Accounts/AccountsList/Feedback/Feedback.js b/packages/fether-react/src/Accounts/AccountsList/Feedback/Feedback.js index 198e6a961..535f613ee 100644 --- a/packages/fether-react/src/Accounts/AccountsList/Feedback/Feedback.js +++ b/packages/fether-react/src/Accounts/AccountsList/Feedback/Feedback.js @@ -5,6 +5,8 @@ import React from 'react'; +import i18n from '../../../i18n'; + export const Feedback = ({ accountsListLength }) => ( ( rel='noopener noreferrer' target='_blank' > - Feedback + {i18n.t('ns1:feedback:title')} ); diff --git a/packages/fether-react/src/Accounts/CreateAccount/AccountCopyPhrase/AccountCopyPhrase.js b/packages/fether-react/src/Accounts/CreateAccount/AccountCopyPhrase/AccountCopyPhrase.js index 6b13d7e09..c45934ddd 100644 --- a/packages/fether-react/src/Accounts/CreateAccount/AccountCopyPhrase/AccountCopyPhrase.js +++ b/packages/fether-react/src/Accounts/CreateAccount/AccountCopyPhrase/AccountCopyPhrase.js @@ -7,6 +7,7 @@ import React, { Component } from 'react'; import { AccountCard } from 'fether-ui'; import { inject, observer } from 'mobx-react'; +import i18n from '../../../i18n'; import RequireHealthOverlay from '../../../RequireHealthOverlay'; @inject('createAccountStore') @@ -39,21 +40,15 @@ class AccountCopyPhrase extends Component { drawers={[
-

Please write your secret phrase on a piece of paper:

+

{i18n.t('ns1:account.create.copy_phrase.msg1')}

{bip39Phrase}
- Keep it secure and secret. + {i18n.t('ns1:account.create.copy_phrase.msg2')}
    -
  • - If you lose your secret phrase, your wallet cannot be - recovered. -
  • -
  • - If someone gets hold of your secret phrase, they will be - able to drain your account. -
  • +
  • {i18n.t('ns1:account.create.copy_phrase.msg3')}
  • +
  • {i18n.t('ns1:account.create.copy_phrase.msg4')}
@@ -64,15 +59,16 @@ class AccountCopyPhrase extends Component { onClick={history.goBack} type='button' > - Back + {i18n.t('ns1:navigation.back')} )}
]} + i18n={i18n} /> ); diff --git a/packages/fether-react/src/Accounts/CreateAccount/AccountImportOptions/AccountImportOptions.js b/packages/fether-react/src/Accounts/CreateAccount/AccountImportOptions/AccountImportOptions.js index baac796cf..3013cd736 100644 --- a/packages/fether-react/src/Accounts/CreateAccount/AccountImportOptions/AccountImportOptions.js +++ b/packages/fether-react/src/Accounts/CreateAccount/AccountImportOptions/AccountImportOptions.js @@ -10,6 +10,7 @@ import { inject, observer } from 'mobx-react'; import RequireHealthOverlay from '../../../RequireHealthOverlay'; import Scanner from '../../../Scanner'; import withAccountsInfo from '../../../utils/withAccountsInfo'; +import i18n from '../../../i18n'; @withAccountsInfo @inject('createAccountStore') @@ -55,8 +56,7 @@ class AccountImportOptions extends Component { } catch (error) { this.setState({ isLoading: false, - error: - 'The passphrase was not recognized. Please verify that you entered your passphrase correctly.' + error: i18n.t('ns1:account.import.phrase.error_msg_submit_phrase') }); console.error(error); } @@ -81,8 +81,7 @@ class AccountImportOptions extends Component { } catch (error) { this.setState({ isLoading: false, - error: - 'Invalid file. Please check this is your actual Parity backup JSON keyfile and try again.' + error: i18n.t('ns1:account.import.error_msg_change_json_file') }); console.error(error); } @@ -94,7 +93,9 @@ class AccountImportOptions extends Component { } = this.props; if (!address || !chainIdString) { - this.setState({ error: 'Invalid QR code.' }); + this.setState({ + error: i18n.t('ns1:account.import.signer.error_msg_signer_imported') + }); return; } @@ -128,7 +129,9 @@ class AccountImportOptions extends Component { if (isExistingAddress) { this.setState({ isLoading: false, - error: `Account ${addressShort(addressForImport)} already listed` + error: i18n.t('ns1:account.import.error_msg_existing_address', { + address: addressShort(addressForImport) + }) }); } @@ -147,10 +150,11 @@ class AccountImportOptions extends Component {
-

Recover from JSON Keyfile

+

{i18n.t('ns1:account.import.json.label_msg_recover_json')}

@@ -163,19 +167,25 @@ class AccountImportOptions extends Component {
-

Recover from Parity Signer

+

+ {i18n.t('ns1:account.import.signer.label_msg_recover_signer')} +

{importingFromSigner ? ( ) : ( )}
@@ -187,11 +197,13 @@ class AccountImportOptions extends Component {
-

Recover from Seed Phrase

+

+ {i18n.t('ns1:account.import.phrase.label_msg_recover_phrase')} +

{currentStep > 1 && ( )} @@ -236,7 +248,7 @@ class AccountImportOptions extends Component { disabled={(!json && !phrase) || isLoading} onClick={this.handleSubmitPhrase} > - Next + {i18n.t('ns1:navigation.next')} ); }; diff --git a/packages/fether-react/src/Accounts/CreateAccount/AccountName/AccountName.js b/packages/fether-react/src/Accounts/CreateAccount/AccountName/AccountName.js index 401bb98e6..242374e9b 100644 --- a/packages/fether-react/src/Accounts/CreateAccount/AccountName/AccountName.js +++ b/packages/fether-react/src/Accounts/CreateAccount/AccountName/AccountName.js @@ -8,6 +8,7 @@ import { AccountCard, Card, Form as FetherForm } from 'fether-ui'; import Blockies from 'react-blockies'; import { inject, observer } from 'mobx-react'; +import i18n from '../../../i18n'; import RequireHealthOverlay from '../../../RequireHealthOverlay'; import loading from '../../../assets/img/icons/loading.svg'; @@ -77,7 +78,8 @@ class AccountName extends Component { address={address} type={noPrivateKey ? 'signer' : 'node'} drawers={[this.renderDrawer()]} - name={name || '(no name)'} + name={name || i18n.t('ns1:account.existing.no_name')} + i18n={i18n} /> ); }; @@ -103,7 +105,7 @@ class AccountName extends Component {
@@ -123,11 +125,11 @@ class AccountName extends Component { return (
-

Please give this account a name:

+

{i18n.t('ns1:account.create.label_name_msg')}

- Back + {i18n.t('ns1:navigation.back')} )} {name && address ? ( - + ) : ( )} diff --git a/packages/fether-react/src/Accounts/CreateAccount/AccountPassword/AccountPassword.js b/packages/fether-react/src/Accounts/CreateAccount/AccountPassword/AccountPassword.js index 2db613b0c..c1f1599bf 100644 --- a/packages/fether-react/src/Accounts/CreateAccount/AccountPassword/AccountPassword.js +++ b/packages/fether-react/src/Accounts/CreateAccount/AccountPassword/AccountPassword.js @@ -7,6 +7,7 @@ import React, { Component } from 'react'; import { AccountCard, Form as FetherForm } from 'fether-ui'; import { inject, observer } from 'mobx-react'; +import i18n from '../../../i18n'; import RequireHealthOverlay from '../../../RequireHealthOverlay'; @inject('createAccountStore') @@ -35,7 +36,9 @@ class AccountPassword extends Component { if (!createAccountStore.jsonString && confirm !== password) { this.setState({ - error: 'Password confirmation does not match.' + error: `${i18n.t( + 'ns1:account.password.create.error_msg_password_confirmation_no_match' + )}` }); return; } @@ -79,14 +82,16 @@ class AccountPassword extends Component {

{' '} {jsonString - ? 'Unlock your account to decrypt your JSON keystore file: ' - : 'Secure your account with a password:'} + ? i18n.t( + 'ns1:account.password.import.label_msg_unlock_json' + ) + : i18n.t('ns1:account.password.create.label_msg_password')}

- {error && error + ' Please check your password and try again.'} + {error && + error + + ' ' + + i18n.t( + 'ns1:account.password.common.error_msg_password_incorrect' + )}

]} + i18n={i18n} /> ); diff --git a/packages/fether-react/src/Accounts/CreateAccount/AccountRewritePhrase/AccountRewritePhrase.js b/packages/fether-react/src/Accounts/CreateAccount/AccountRewritePhrase/AccountRewritePhrase.js index bd4f84531..3a8bbb5a9 100644 --- a/packages/fether-react/src/Accounts/CreateAccount/AccountRewritePhrase/AccountRewritePhrase.js +++ b/packages/fether-react/src/Accounts/CreateAccount/AccountRewritePhrase/AccountRewritePhrase.js @@ -7,6 +7,7 @@ import React, { Component } from 'react'; import { AccountCard, Card, Form as FetherForm } from 'fether-ui'; import { inject, observer } from 'mobx-react'; +import i18n from '../../../i18n'; import RequireHealthOverlay from '../../../RequireHealthOverlay'; import AccountImportOptions from '../AccountImportOptions'; @@ -55,8 +56,7 @@ class AccountRewritePhrase extends Component { ) : (

- Type your secret phrase to confirm that you wrote it down - correctly: + {i18n.t('ns1:account.phrase_rewrite.label_msg_rewrite_phrase')}

)}
@@ -64,7 +64,7 @@ class AccountRewritePhrase extends Component { - Back + {i18n.t('ns1:navigation.back')} )} {this.renderButton()} @@ -92,8 +92,11 @@ class AccountRewritePhrase extends Component { ) : ( )} @@ -111,7 +114,7 @@ class AccountRewritePhrase extends Component { if (!isImport) { return ( ); } @@ -119,7 +122,7 @@ class AccountRewritePhrase extends Component { // If we are importing an existing account, the button goes to the next step return ( ); }; diff --git a/packages/fether-react/src/Accounts/CreateAccount/CreateAccount.js b/packages/fether-react/src/Accounts/CreateAccount/CreateAccount.js index 63bba51a3..f1c890a6f 100644 --- a/packages/fether-react/src/Accounts/CreateAccount/CreateAccount.js +++ b/packages/fether-react/src/Accounts/CreateAccount/CreateAccount.js @@ -14,6 +14,7 @@ import AccountRewritePhrase from './AccountRewritePhrase'; import AccountName from './AccountName'; import AccountPassword from './AccountPassword'; import Health from '../../Health'; +import i18n from '../../i18n'; import withAccountsInfo from '../../utils/withAccountsInfo'; @inject('createAccountStore') @@ -75,7 +76,11 @@ class CreateAccount extends Component { ) } title={ -

{isImport ? 'Import account' : 'Create a new account'}

+

+ {isImport + ? i18n.t('ns1:account.import.title') + : i18n.t('ns1:account.create.title')} +

} /> @@ -100,12 +105,12 @@ class CreateAccount extends Component {
{isImport ? null : (

- Already have an account? + {i18n.t('ns1:account.import.question')}

)} diff --git a/packages/fether-react/src/BackupAccount/BackupAccount.js b/packages/fether-react/src/BackupAccount/BackupAccount.js index e74634c17..a6eab8d48 100644 --- a/packages/fether-react/src/BackupAccount/BackupAccount.js +++ b/packages/fether-react/src/BackupAccount/BackupAccount.js @@ -8,6 +8,7 @@ import { AccountHeader, Card, Form as FetherForm } from 'fether-ui'; import { observer } from 'mobx-react'; import { Link, withRouter } from 'react-router-dom'; +import i18n from '../i18n'; import RequireHealthOverlay from '../RequireHealthOverlay'; import backupAccount from '../utils/backupAccount'; import withAccount from '../utils/withAccount'; @@ -68,11 +69,12 @@ class BackupAccount extends Component { - Back + {i18n.t('ns1:navigation.back')} } /> @@ -80,11 +82,11 @@ class BackupAccount extends Component {
-

Unlock your account to encrypt the JSON keystore file:

+

{i18n.t('ns1:account.backup.label_msg_password_unlock')}

- Back + {i18n.t('ns1:navigation.back')} diff --git a/packages/fether-react/src/Health/Health.js b/packages/fether-react/src/Health/Health.js index 9ba42c033..0d26c6908 100644 --- a/packages/fether-react/src/Health/Health.js +++ b/packages/fether-react/src/Health/Health.js @@ -8,6 +8,7 @@ import { branch } from 'recompose'; import { chainName$, withoutLoading } from '@parity/light.js'; import light from '@parity/light.js-react'; import withHealth from '../utils/withHealth'; +import i18n from '../i18n'; @withHealth @branch( @@ -58,23 +59,23 @@ class Health extends Component { } = this.props; if (status.downloading) { - return `Downloading Parity Ethereum (${ + return `${i18n.t('ns1:health.status.title.downloading')} (${ payload.downloading.syncPercentage }%)`; } else if (status.launching) { - return 'Launching the node...'; + return i18n.t('ns1:health.status.title.launching'); } else if (!status.nodeConnected && !status.internet) { - return 'No internet. No node connected'; + return i18n.t('ns1:health.status.title.no_internet_no_node_connected'); } else if (!status.nodeConnected && status.internet) { - return 'Connecting to node...'; + return i18n.t('ns1:health.status.title.internet_no_node_connected'); } else if (status.nodeConnected && !status.internet) { - return 'No internet. Connected to node'; + return i18n.t('ns1:health.status.title.no_internet_node_connected'); } else if (!status.clockSync) { - return 'Clock of host not in sync'; + return i18n.t('ns1:health.status.title.no_clock_sync'); } else if (!status.peers) { - return 'Connecting to peers...'; + return i18n.t('ns1:health.status.title.no_peers'); } else if (status.syncing) { - return `Syncing...${ + return `${i18n.t('ns1:health.status.title.syncing')} ${ payload && payload.syncing && payload.syncing.syncPercentage && @@ -83,7 +84,7 @@ class Health extends Component { : '' } ${chainName}`; } else if (status.good) { - return `Synced ${chainName}`; + return `${i18n.t('ns1:health.status.title.synced')} ${chainName}`; } else { return JSON.stringify(payload) || ''; // Just in case payload is an object } diff --git a/packages/fether-react/src/Onboarding/Onboarding.js b/packages/fether-react/src/Onboarding/Onboarding.js index a31150b24..dfb1652a5 100644 --- a/packages/fether-react/src/Onboarding/Onboarding.js +++ b/packages/fether-react/src/Onboarding/Onboarding.js @@ -8,6 +8,7 @@ import { Form as FetherForm, Header } from 'fether-ui'; import { inject, observer } from 'mobx-react'; import ReactMarkdown from 'react-markdown'; +import i18n from '../i18n'; import Health from '../Health'; import termsAndConditions from './termsAndConditions.md'; @@ -50,7 +51,7 @@ class Onboarding extends Component { render () { return ( -
Terms of Use} /> +
{i18n.t('ns1:onboarding.header')}} />
@@ -60,7 +61,7 @@ class Onboarding extends Component { className='terms-and-conditions' renderers={reactMarkdownOptions} source={this.state.markdown} - label='Please read carefully' + label={i18n.t('ns1:onboarding.instructions')} />
@@ -72,7 +73,7 @@ class Onboarding extends Component {
diff --git a/packages/fether-react/src/RequireHealthOverlay/HealthModal/HealthModal.js b/packages/fether-react/src/RequireHealthOverlay/HealthModal/HealthModal.js index be72519d8..06d3b2485 100644 --- a/packages/fether-react/src/RequireHealthOverlay/HealthModal/HealthModal.js +++ b/packages/fether-react/src/RequireHealthOverlay/HealthModal/HealthModal.js @@ -10,6 +10,7 @@ import { chainName$, withoutLoading } from '@parity/light.js'; import light from '@parity/light.js-react'; import { Modal } from 'fether-ui'; +import i18n from '../../i18n'; import withHealth from '../../utils/withHealth'; import loading from '../../assets/img/icons/loading.svg'; @@ -56,21 +57,21 @@ class HealthModal extends Component { } = this.props; if (status.downloading) { - return 'Downloading Parity Ethereum...'; + return i18n.t('ns1:health.status.title.downloading'); } else if (status.launching) { - return 'Launching the node...'; + return i18n.t('ns1:health.status.title.launching'); } else if (!status.nodeConnected && !status.internet) { - return 'No internet. No node connected'; + return i18n.t('ns1:health.status.title.no_internet_no_node_connected'); } else if (!status.nodeConnected && status.internet) { - return 'Connecting to node...'; + return i18n.t('ns1:health.status.title.internet_no_node_connected'); } else if (status.nodeConnected && !status.internet) { - return 'No internet. Connected to node'; + return i18n.t('ns1:health.status.title.no_internet_node_connected'); } else if (!status.clockSync) { - return 'Clock of host not in sync'; + return i18n.t('ns1:health.status.title.no_clock_sync'); } else if (!status.peers) { - return 'Connecting to peers...'; + return i18n.t('ns1:health.status.title.no_peers'); } else if (status.syncing) { - return `Syncing...`; + return i18n.t('ns1:health.status.title.syncing'); } else { return ''; } @@ -83,18 +84,17 @@ class HealthModal extends Component { } = this.props; if (!status.internet) { - return 'Please connect to the Internet'; + return i18n.t('ns1:health.status.description.no_internet'); } else if (status.downloading) { - return `Downloading Parity Ethereum... (${ + return `${i18n.t('ns1:health.status.description.downloading')} (${ payload.downloading.syncPercentage }%)`; } else if (!status.clockSync) { - return `Mac: System Preferences -> Date & Time -> Uncheck and recheck "Set date and time automatically" - Windows: Control Panel -> "Clock, Language, and Region" -> "Date and Time" -> Uncheck and recheck "Set date and time automatically"`; + return i18n.t('ns1:health.status.description.no_clock_sync'); } else if (!status.peers) { - return 'Searching for peers'; + return i18n.t('ns1:health.status.description.no_peers'); } else if (status.syncing) { - return `Syncing...${ + return `${i18n.t('ns1:health.status.description.syncing')} ${ payload && payload.syncing && payload.syncing.syncPercentage && diff --git a/packages/fether-react/src/RequireHealthOverlay/RequireHealthOverlay.js b/packages/fether-react/src/RequireHealthOverlay/RequireHealthOverlay.js index 7e8f12b31..624597e0c 100644 --- a/packages/fether-react/src/RequireHealthOverlay/RequireHealthOverlay.js +++ b/packages/fether-react/src/RequireHealthOverlay/RequireHealthOverlay.js @@ -6,6 +6,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import i18n from '../i18n'; import withHealth from '../utils/withHealth'; import { HealthModal } from './HealthModal'; @@ -21,9 +22,7 @@ function statusMatches (status, require) { case 'sync': return status.good; default: - throw new Error( - `Status '${status}' must be one of 'node-internet|node|sync'.` - ); + throw new Error(i18n.t('ns1:health.error_status_invalid', { status })); } } diff --git a/packages/fether-react/src/Scanner/Scanner.js b/packages/fether-react/src/Scanner/Scanner.js index 0c246f297..c11538979 100644 --- a/packages/fether-react/src/Scanner/Scanner.js +++ b/packages/fether-react/src/Scanner/Scanner.js @@ -6,6 +6,7 @@ import React from 'react'; import QrSigner from '@parity/qr-signer'; +import i18n from '../i18n'; import loading from '../assets/img/icons/loading.svg'; export default class Scanner extends React.PureComponent { @@ -46,18 +47,17 @@ export default class Scanner extends React.PureComponent { switch (e.name) { case 'NotAllowedError': case 'SecurityError': - errorMessage = 'Access to the webcam was refused.'; + errorMessage = i18n.t('ns1:scanner.error_security'); break; case 'NotFoundError': case 'OverconstrainedError': - errorMessage = 'No webcam found on the device.'; + errorMessage = i18n.t('ns1:scanner.error_overconstrained'); break; case 'NotReadableError': - errorMessage = - 'Webcam hardware error. Try restarting your computer'; + errorMessage = i18n.t('ns1:scanner.error_not_readable'); break; default: - errorMessage = 'Unknown error.'; + errorMessage = i18n.t('ns1:scanner.error_unknown'); } this.setState({ webcamError: errorMessage, diff --git a/packages/fether-react/src/Send/ScanSignedTx/ScanSignedTx.js b/packages/fether-react/src/Send/ScanSignedTx/ScanSignedTx.js index f917f8eb5..658b036b8 100644 --- a/packages/fether-react/src/Send/ScanSignedTx/ScanSignedTx.js +++ b/packages/fether-react/src/Send/ScanSignedTx/ScanSignedTx.js @@ -10,6 +10,7 @@ import { Link, Redirect } from 'react-router-dom'; import Scanner from '../../Scanner'; import { withProps } from 'recompose'; +import i18n from '../../i18n'; import RequireHealthOverlay from '../../RequireHealthOverlay'; import withAccount from '../../utils/withAccount.js'; import withTokens from '../../utils/withTokens'; @@ -41,7 +42,7 @@ class ScanSignedTx extends Component { ); } catch (e) { this.setState({ - error: "The QR code doesn't seem to be a valid transaction." + error: i18n.t('ns1:tx.scan_signed.error_qr_tx_invalid') }); } }; @@ -64,10 +65,16 @@ class ScanSignedTx extends Component {
- Close + {i18n.t('ns1:navigation.close')} } - title={token &&

Send {token.name}

} + title={ + token && ( +

+ {i18n.t('ns1:tx.header_send_prefix', { token: token.name })} +

+ ) + } /> @@ -76,7 +83,7 @@ class ScanSignedTx extends Component { {error &&

{error}

} @@ -87,7 +94,7 @@ class ScanSignedTx extends Component { onClick={history.goBack} type='button' > - Back + {i18n.t('ns1:navigation.back')}
diff --git a/packages/fether-react/src/Send/Sent/Sent.js b/packages/fether-react/src/Send/Sent/Sent.js index d41828632..9ea07f787 100644 --- a/packages/fether-react/src/Send/Sent/Sent.js +++ b/packages/fether-react/src/Send/Sent/Sent.js @@ -11,6 +11,7 @@ import light from '@parity/light.js-react'; import { withProps } from 'recompose'; import { Modal } from 'fether-ui'; +import i18n from '../../i18n'; import RequireHealthOverlay from '../../RequireHealthOverlay'; import check from '../../assets/img/icons/check.svg'; import loading from '../../assets/img/icons/loading.svg'; @@ -81,11 +82,13 @@ class Sent extends Component { } if (confirmations > 0) { - return `Waiting ${confirmations}/${MIN_CONFIRMATIONS} confirmations`; + return i18n.t('ns1:tx.sent.waiting_confirmations_receiving', { + progress: `${confirmations}/${MIN_CONFIRMATIONS}` + }); } if (txStatus.confirmed) { - return 'Waiting for confirmations...'; + return i18n.t('ns1:tx.sent.waiting_confirmed'); } if (txStatus.failed) { @@ -120,17 +123,17 @@ class Sent extends Component { return ( {confirmations >= MIN_CONFIRMATIONS - ? 'Transaction confirmed' - : 'Submitted'} + ? i18n.t('ns1:tx.sent.confirmed') + : i18n.t('ns1:tx.sent.submitted')} ); } if (txStatus.failed) { - return 'Error'; + return i18n.t('ns1:tx.sent.error'); } - return 'Sending your transaction...'; + return i18n.t('ns1:tx.header_sending'); }; renderGoHomepage = () => { @@ -149,7 +152,7 @@ class Sent extends Component { disabled={confirmations < MIN_CONFIRMATIONS} onClick={this.handleGoToHomepage} > - Go back + {i18n.t('ns1:navigation.go_back')} ); @@ -173,7 +176,9 @@ class Sent extends Component { target='_blank' rel='noopener noreferrer' > - + ); } diff --git a/packages/fether-react/src/Send/SignedTxSummary/SignedTxSummary.js b/packages/fether-react/src/Send/SignedTxSummary/SignedTxSummary.js index 59a4eef56..b27b8ba37 100644 --- a/packages/fether-react/src/Send/SignedTxSummary/SignedTxSummary.js +++ b/packages/fether-react/src/Send/SignedTxSummary/SignedTxSummary.js @@ -10,6 +10,7 @@ import { inject, observer } from 'mobx-react'; import { Link } from 'react-router-dom'; import { withProps } from 'recompose'; +import i18n from '../../i18n'; import RequireHealthOverlay from '../../RequireHealthOverlay'; import TokenAddress from '../../Tokens/TokensList/TokenAddress'; import withAccount from '../../utils/withAccount'; @@ -54,10 +55,16 @@ class SignedTxSummary extends Component {
- Close + {i18n.t('ns1:navigation.close')} } - title={token &&

Send {token.name}

} + title={ + token && ( +

+ {i18n.t('ns1:tx.header_send_prefix', { token: token.name })} +

+ ) + } /> @@ -82,7 +89,7 @@ class SignedTxSummary extends Component { as='textarea' className='form_field_value' disabled - label='To' + label={i18n.t('ns1:tx.form.field.to')} name='to' render={FetherForm.Field} /> @@ -90,22 +97,30 @@ class SignedTxSummary extends Component { {values.to === values.from && ( -

WARNING:

+

+ {i18n.t( + 'ns1:tx.form.warning.title_same_sender_receiver' + )} +

- The sender and receiver addresses are the same. + {i18n.t( + 'ns1:tx.form.warning.body_same_sender_receiver' + )}

)} )} diff --git a/packages/fether-react/src/Send/TxForm/TxDetails/TxDetails.js b/packages/fether-react/src/Send/TxForm/TxDetails/TxDetails.js index 0c4a36237..1e8acb41a 100644 --- a/packages/fether-react/src/Send/TxForm/TxDetails/TxDetails.js +++ b/packages/fether-react/src/Send/TxForm/TxDetails/TxDetails.js @@ -7,6 +7,8 @@ import React, { Component } from 'react'; import BigNumber from 'bignumber.js'; import { fromWei, toWei } from '@parity/api/lib/util/wei'; +import i18n from '../../../i18n'; + class TxDetails extends Component { renderDetails = () => { const { estimatedTxFee, token, values } = this.props; @@ -19,7 +21,7 @@ class TxDetails extends Component { ) { // Keep line break so message is centered return ` -Missing input fields...`; +${i18n.t('ns1:tx.form.details.missing_fields')}`; } return `${this.renderCalculation()} @@ -41,7 +43,7 @@ ${this.renderTotalAmount()}`; .toFixed(0) .toString(); - return `Gas Limit: ${gasLimitBn}`; + return i18n.t('ns1:tx.form.details.gas_limit', { gas_limit: gasLimitBn }); }; renderFee = () => { @@ -51,9 +53,11 @@ ${this.renderTotalAmount()}`; return; } - return `Fee: ${fromWei(estimatedTxFee, 'ether') + const fee = `${fromWei(estimatedTxFee, 'ether') .toFixed(9) - .toString()} ETH (gas limit * gas price)`; + .toString()}`; + + return i18n.t('ns1:tx.form.details.fee', { fee }); }; renderTotalAmount = () => { @@ -63,12 +67,16 @@ ${this.renderTotalAmount()}`; return; } - return `Total Amount: ${fromWei( + const totalAmount = `${fromWei( estimatedTxFee.plus( token.address === 'ETH' ? toWei(values.amount.toString()) : 0 ), 'ether' - ).toString()} ETH`; + ).toString()}`; + + return i18n.t('ns1:tx.form.details.total_amount', { + total_amount: totalAmount + }); }; render () { @@ -78,7 +86,9 @@ ${this.renderTotalAmount()}`;