aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.circleci/config.yml54
-rw-r--r--CHANGELOG.md23
-rw-r--r--README.md4
-rw-r--r--app/_locales/de/messages.json873
-rw-r--r--app/_locales/en/messages.json27
-rw-r--r--app/_locales/es/messages.json106
-rw-r--r--app/_locales/hn/messages.json819
-rw-r--r--app/_locales/index.json19
-rw-r--r--app/_locales/it/messages.json2
-rw-r--r--app/_locales/ja/messages.json76
-rw-r--r--app/_locales/pt/messages.json2
-rw-r--r--app/_locales/ru/messages.json819
-rw-r--r--app/_locales/sl/messages.json819
-rw-r--r--app/_locales/th/messages.json819
-rw-r--r--app/_locales/zh_TW/messages.json903
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/background.js22
-rw-r--r--app/scripts/controllers/preferences.js5
-rw-r--r--app/scripts/lib/extractEthjsErrorMessage.js27
-rw-r--r--app/scripts/lib/get-first-preferred-lang-code.js16
-rw-r--r--app/scripts/lib/notification-manager.js11
-rw-r--r--app/scripts/lib/reportFailedTxToSentry.js26
-rw-r--r--app/scripts/lib/setupRaven.js37
-rw-r--r--app/scripts/lib/tx-state-manager.js7
-rw-r--r--app/scripts/metamask-controller.js86
-rw-r--r--app/scripts/migrations/022.js39
-rw-r--r--app/scripts/migrations/023.js50
-rw-r--r--app/scripts/migrations/index.js2
-rw-r--r--app/scripts/popup.js125
-rw-r--r--app/scripts/vendor/raven.min.js3
-rw-r--r--development/genStates.js5
-rw-r--r--development/states/add-token.json3
-rw-r--r--development/states/confirm-new-ui.json3
-rw-r--r--development/states/confirm-sig-requests.json3
-rw-r--r--development/states/first-time.json3
-rw-r--r--development/states/send-edit.json3
-rw-r--r--development/states/send-new-ui.json3
-rw-r--r--gulpfile.js12
-rw-r--r--mascara/src/app/first-time/index.css9
-rw-r--r--mascara/src/app/first-time/index.js9
-rw-r--r--old-ui/app/config.js5
-rw-r--r--package-lock.json309
-rw-r--r--package.json27
-rw-r--r--test/base.conf.js2
-rw-r--r--test/e2e/func.js18
-rw-r--r--test/e2e/metamask.spec.js122
-rw-r--r--test/integration/lib/add-token.js39
-rw-r--r--test/integration/lib/confirm-sig-requests.js6
-rw-r--r--test/integration/lib/mascara-first-time.js6
-rw-r--r--test/integration/lib/send-new-ui.js8
-rw-r--r--test/unit/migrations/022-test.js32
-rw-r--r--test/unit/migrations/023-test.js99
-rw-r--r--test/unit/tx-state-manager-test.js6
-rw-r--r--ui/app/accounts/import/index.js38
-rw-r--r--ui/app/accounts/import/json.js25
-rw-r--r--ui/app/accounts/import/private-key.js13
-rw-r--r--ui/app/accounts/import/seed.js7
-rw-r--r--ui/app/accounts/new-account/create-form.js18
-rw-r--r--ui/app/accounts/new-account/index.js9
-rw-r--r--ui/app/actions.js46
-rw-r--r--ui/app/add-token.js220
-rw-r--r--ui/app/app.js33
-rw-r--r--ui/app/components/account-dropdowns.js18
-rw-r--r--ui/app/components/account-export.js17
-rw-r--r--ui/app/components/account-menu/index.js17
-rw-r--r--ui/app/components/balance-component.js2
-rw-r--r--ui/app/components/bn-as-decimal-input.js12
-rw-r--r--ui/app/components/buy-button-subview.js17
-rw-r--r--ui/app/components/coinbase-form.js7
-rw-r--r--ui/app/components/copyButton.js6
-rw-r--r--ui/app/components/copyable.js6
-rw-r--r--ui/app/components/customize-gas-modal/index.js31
-rw-r--r--ui/app/components/dropdowns/components/account-dropdowns.js23
-rw-r--r--ui/app/components/dropdowns/network-dropdown.js30
-rw-r--r--ui/app/components/dropdowns/token-menu-dropdown.js6
-rw-r--r--ui/app/components/ens-input.js50
-rw-r--r--ui/app/components/hex-as-decimal-input.js12
-rw-r--r--ui/app/components/identicon.js2
-rw-r--r--ui/app/components/modals/account-details-modal.js11
-rw-r--r--ui/app/components/modals/account-modal-container.js5
-rw-r--r--ui/app/components/modals/buy-options-modal.js17
-rw-r--r--ui/app/components/modals/deposit-ether-modal.js53
-rw-r--r--ui/app/components/modals/edit-account-name-modal.js7
-rw-r--r--ui/app/components/modals/export-private-key-modal.js17
-rw-r--r--ui/app/components/modals/hide-token-confirmation-modal.js11
-rw-r--r--ui/app/components/modals/modal.js9
-rw-r--r--ui/app/components/modals/new-account-modal.js18
-rw-r--r--ui/app/components/modals/notification-modal.js7
-rw-r--r--ui/app/components/modals/notification-modals/confirm-reset-account.js2
-rw-r--r--ui/app/components/modals/shapeshift-deposit-tx-modal.js2
-rw-r--r--ui/app/components/network-display.js6
-rw-r--r--ui/app/components/network.js28
-rw-r--r--ui/app/components/notice.js6
-rw-r--r--ui/app/components/pending-msg-details.js6
-rw-r--r--ui/app/components/pending-msg.js14
-rw-r--r--ui/app/components/pending-tx/confirm-deploy-contract.js32
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js23
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js44
-rw-r--r--ui/app/components/pending-tx/index.js20
-rw-r--r--ui/app/components/qr-code.js2
-rw-r--r--ui/app/components/send/account-list-item.js2
-rw-r--r--ui/app/components/send/gas-fee-display-v2.js8
-rw-r--r--ui/app/components/send/gas-tooltip.js6
-rw-r--r--ui/app/components/send/send-v2-container.js6
-rw-r--r--ui/app/components/send/to-autocomplete.js6
-rw-r--r--ui/app/components/sender-to-recipient.js7
-rw-r--r--ui/app/components/shapeshift-form.js25
-rw-r--r--ui/app/components/shift-list-item.js17
-rw-r--r--ui/app/components/signature-request.js27
-rw-r--r--ui/app/components/token-balance.js2
-rw-r--r--ui/app/components/token-cell.js2
-rw-r--r--ui/app/components/token-list.js9
-rw-r--r--ui/app/components/tx-list-item.js21
-rw-r--r--ui/app/components/tx-list.js11
-rw-r--r--ui/app/components/tx-view.js15
-rw-r--r--ui/app/components/wallet-view.js13
-rw-r--r--ui/app/conf-tx.js2
-rw-r--r--ui/app/css/itcss/components/add-token.scss189
-rw-r--r--ui/app/css/itcss/components/buttons.scss84
-rw-r--r--ui/app/css/itcss/components/hero-balance.scss3
-rw-r--r--ui/app/css/itcss/components/modal.scss13
-rw-r--r--ui/app/css/itcss/components/new-account.scss27
-rw-r--r--ui/app/css/itcss/components/request-signature.scss35
-rw-r--r--ui/app/css/itcss/components/send.scss8
-rw-r--r--ui/app/css/itcss/components/settings.scss38
-rw-r--r--ui/app/css/itcss/components/token-list.scss9
-rw-r--r--ui/app/css/itcss/generic/index.scss2
-rw-r--r--ui/app/css/itcss/settings/variables.scss3
-rw-r--r--ui/app/first-time/init-menu.js21
-rw-r--r--ui/app/keychains/hd/create-vault-complete.js2
-rw-r--r--ui/app/keychains/hd/recover-seed/confirmation.js9
-rw-r--r--ui/app/keychains/hd/restore-vault.js27
-rw-r--r--ui/app/metamask-connect.js27
-rw-r--r--ui/app/new-keychain.js2
-rw-r--r--ui/app/reducers.js7
-rw-r--r--ui/app/reducers/locale.js17
-rw-r--r--ui/app/reducers/metamask.js10
-rw-r--r--ui/app/select-app.js2
-rw-r--r--ui/app/selectors.js3
-rw-r--r--ui/app/send-v2.js55
-rw-r--r--ui/app/settings.js127
-rw-r--r--ui/app/unlock.js11
-rw-r--r--ui/i18n-helper.js45
-rw-r--r--ui/i18n.js33
-rw-r--r--ui/index.js20
-rw-r--r--yarn.lock273
146 files changed, 7691 insertions, 1150 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index c14909783..75819fc6e 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -6,12 +6,19 @@ workflows:
jobs:
- prep-deps-npm
- prep-deps-firefox
+ - prep-build:
+ requires:
+ - prep-deps-npm
- prep-scss:
requires:
- prep-deps-npm
- test-lint:
requires:
- prep-deps-npm
+ - test-e2e:
+ requires:
+ - prep-build
+ - prep-deps-npm
- test-unit:
requires:
- prep-deps-npm
@@ -33,6 +40,15 @@ workflows:
- prep-deps-npm
- prep-deps-firefox
- prep-scss
+ - all-tests-pass:
+ requires:
+ - test-lint
+ - test-unit
+ - test-e2e
+ - test-integration-mascara-chrome
+ - test-integration-mascara-firefox
+ - test-integration-flat-chrome
+ - test-integration-flat-firefox
jobs:
prep-deps-npm:
@@ -65,6 +81,23 @@ jobs:
paths:
- firefox
+ prep-build:
+ docker:
+ - image: circleci/node:8-browsers
+ steps:
+ - checkout
+ - restore_cache:
+ key: dependency-cache-{{ checksum "package-lock.json" }}
+ - run:
+ name: build:dist
+ command: npm run dist
+ - run:
+ name: build:debug
+ command: find dist/ -type f -exec md5sum {} \; | sort -k 2
+ - save_cache:
+ key: build-cache-{{ .Revision }}
+ paths:
+ - dist
prep-scss:
docker:
@@ -96,6 +129,19 @@ jobs:
name: Test
command: npm run lint
+ test-e2e:
+ docker:
+ - image: circleci/node:8-browsers
+ steps:
+ - checkout
+ - restore_cache:
+ key: dependency-cache-{{ checksum "package-lock.json" }}
+ - restore_cache:
+ key: build-cache-{{ .Revision }}
+ - run:
+ name: Test
+ command: npm run test:e2e
+
test-unit:
docker:
- image: circleci/node:8-browsers
@@ -200,3 +246,11 @@ jobs:
- run:
name: test:integration:mascara
command: npm run test:mascara
+
+ all-tests-pass:
+ docker:
+ - image: circleci/node:8-browsers
+ steps:
+ - run:
+ name: All Tests Passed
+ command: echo 'weew - everything passed!'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75ba7670f..06cf51c14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,11 +2,28 @@
## Current Master
-- MetaMask will no longer allow nonces to be specified by the dapp
-- Add ability for internationalization.
+- Fix bug where the "Reset account" feature would not clear the network cache.
+
+## 4.4.0 Mon Mar 26 2018
+
+- Internationalization: Taiwanese, Thai, Slovenian
+- Fixes bug where MetaMask would not open once its storage grew too large.
+- Updates design of new-ui Add Token screen
+- New-ui can send to ens addresses
+- Update new-ui button styles
+- Signed-type-data notification handles long messages
+- Popup extension in new-ui uses new on-boarding designs
+- Buy ether step of new-ui on-boarding uses new buy ether modal designs
+
+## 4.3.0 Wed Mar 21 2018
+
+- (beta) Add internationalization support! Includes translations for 13 (!!) new languages: French, Spanish, Italian, German, Dutch, Portuguese, Japanese, Korean, Vietnamese, Mandarin, Hindi, Tagalog, and Russian! Select "Try Beta" in the menu to take them for a spin. Read more about the community effort [here](https://medium.com/gitcoin/metamask-internationalizes-via-gitcoin-bf1390c0301c)
+- No longer uses nonces specified by the dapp
- Will now throw an error if the `to` field in txParams is not valid.
- Will strip null values from the `to` field.
-- Fix flashing to Log in screen after logging in or restoring from seed phrase.
+- (beta) No longer shows token confirmation screen when performing a non-send
+- (beta) Fixes bug where tx data was nullified when repricing a tx
+- Fix flashing Login screen after logging in or restoring from seed phrase.
- Increase tap areas for menu buttons on mobile
- Change all fonts in new-ui onboarding to Roboto, size 400
- Add a welcome screen to new-ui onboarding flow
diff --git a/README.md b/README.md
index 278357c23..2e68991b5 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
# MetaMask Browser Extension
-[![Build Status](https://circleci.com/gh/MetaMask/metamask-extension.svg?style=shield&circle-token=a1ddcf3cd38e29267f254c9c59d556d513e3a1fd)](https://circleci.com/gh/MetaMask/metamask-extension) [![Coverage Status](https://coveralls.io/repos/github/MetaMask/metamask-extension/badge.svg?branch=master)](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [![Greenkeeper badge](https://badges.greenkeeper.io/MetaMask/metamask-extension.svg)](https://greenkeeper.io/) [![Stories in Ready](https://badge.waffle.io/MetaMask/metamask-extension.png?label=in%20progress&title=waffle.io)](http://waffle.io/MetaMask/metamask-extension)
+[![Build Status](https://circleci.com/gh/MetaMask/metamask-extension.svg?style=shield&circle-token=a1ddcf3cd38e29267f254c9c59d556d513e3a1fd)](https://circleci.com/gh/MetaMask/metamask-extension) [![Coverage Status](https://coveralls.io/repos/github/MetaMask/metamask-extension/badge.svg?branch=master)](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [![Greenkeeper badge](https://badges.greenkeeper.io/MetaMask/metamask-extension.svg)](https://greenkeeper.io/) [![Stories in Ready](https://badge.waffle.io/MetaMask/metamask-extension.png?label=in%20progress&title=waffle.io)](https://waffle.io/MetaMask/metamask-extension)
## Support
@@ -76,5 +76,3 @@ To write tests that will be run in the browser using QUnit, add your test files
- [How to generate a visualization of this repository's development](./docs/development-visualization.md)
[1]: http://www.nomnoml.com/#view/%5B%3Cactor%3Euser%5D%0A%0A%5Bmetamask-ui%7C%0A%20%20%20%5Btools%7C%0A%20%20%20%20%20react%0A%20%20%20%20%20redux%0A%20%20%20%20%20thunk%0A%20%20%20%20%20ethUtils%0A%20%20%20%20%20jazzicon%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20account-detail%0A%20%20%20%20%20accounts%0A%20%20%20%20%20locked-screen%0A%20%20%20%20%20restore-vault%0A%20%20%20%20%20identicon%0A%20%20%20%20%20config%0A%20%20%20%20%20info%0A%20%20%20%5D%0A%20%20%20%5Breducers%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20metamask%0A%20%20%20%20%20identities%0A%20%20%20%5D%0A%20%20%20%5Bactions%7C%0A%20%20%20%20%20%5BaccountManager%5D%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%5D%3A-%3E%5Bactions%5D%0A%20%20%20%5Bactions%5D%3A-%3E%5Breducers%5D%0A%20%20%20%5Breducers%5D%3A-%3E%5Bcomponents%5D%0A%5D%0A%0A%5Bweb%20dapp%7C%0A%20%20%5Bui%20code%5D%0A%20%20%5Bweb3%5D%0A%20%20%5Bmetamask-inpage%5D%0A%20%20%0A%20%20%5B%3Cactor%3Eui%20developer%5D%0A%20%20%5Bui%20developer%5D-%3E%5Bui%20code%5D%0A%20%20%5Bui%20code%5D%3C-%3E%5Bweb3%5D%0A%20%20%5Bweb3%5D%3C-%3E%5Bmetamask-inpage%5D%0A%5D%0A%0A%5Bmetamask-background%7C%0A%20%20%5Bprovider-engine%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bid%20store%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%3E%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%3C-%3E%5Bid%20store%5D%0A%20%20%5Bconfig%20manager%7C%0A%20%20%20%20%5Brpc%20configuration%5D%0A%20%20%20%20%5Bencrypted%20keys%5D%0A%20%20%20%20%5Bwallet%20nicknames%5D%0A%20%20%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%5Bconfig%20manager%5D%0A%20%20%5Bid%20store%5D%3C-%3E%5Bconfig%20manager%5D%0A%5D%0A%0A%5Buser%5D%3C-%3E%5Bmetamask-ui%5D%0A%0A%5Buser%5D%3C%3A--%3A%3E%5Bweb%20dapp%5D%0A%0A%5Bmetamask-contentscript%7C%0A%20%20%5Bplugin%20restart%20detector%5D%0A%20%20%5Brpc%20passthrough%5D%0A%5D%0A%0A%5Brpc%20%7C%0A%20%20%5Bethereum%20blockchain%20%7C%0A%20%20%20%20%5Bcontracts%5D%0A%20%20%20%20%5Baccounts%5D%0A%20%20%5D%0A%5D%0A%0A%5Bweb%20dapp%5D%3C%3A--%3A%3E%5Bmetamask-contentscript%5D%0A%5Bmetamask-contentscript%5D%3C-%3E%5Bmetamask-background%5D%0A%5Bmetamask-background%5D%3C-%3E%5Bmetamask-ui%5D%0A%5Bmetamask-background%5D%3C-%3E%5Brpc%5D%0A
-
-
diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json
new file mode 100644
index 000000000..0bdce516c
--- /dev/null
+++ b/app/_locales/de/messages.json
@@ -0,0 +1,873 @@
+{
+ "accept": {
+ "message": "Annehmen"
+ },
+ "account": {
+ "message": "Account"
+ },
+ "accountDetails": {
+ "message": "Accountdetails"
+ },
+ "accountName": {
+ "message": "Accountname"
+ },
+ "address": {
+ "message": "Adresse"
+ },
+ "addToken": {
+ "message": "Token hinzufügen"
+ },
+ "amount": {
+ "message": "Betrag"
+ },
+ "amountPlusGas": {
+ "message": "Betrag + Gas"
+ },
+ "appDescription": {
+ "message": "Ethereum Browsererweiterung",
+ "description": "Die Beschreibung der Erweiterung"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "Der Name der Erweiterung"
+ },
+ "attemptingConnect": {
+ "message": "Versuch mit der Blockchain zu verbinden."
+ },
+ "available": {
+ "message": "Verfügbar"
+ },
+ "back": {
+ "message": "Zurück"
+ },
+ "balance": {
+ "message": "Guthaben:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Guthaben unzureichend für den aktuellen gesamten Gasbetrag"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "Muss größer oder gleich $1 und kleiner oder gleich $2 sein.",
+ "description": "Helfer für die Eingabe von hex als dezimal"
+ },
+ "blockiesIdenticon": {
+ "message": "Blockies Identicon verwenden"
+ },
+ "borrowDharma": {
+ "message": "Mit Dharma ausleihen (Beta)"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask wurde in Kalifornien entwickelt und gebaut."
+ },
+ "buy": {
+ "message": "Kaufen"
+ },
+ "buyCoinbase": {
+ "message": "Auf Coinbase kaufen"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase ist die weltweit bekannteste Möglichkeit bitcoin, ethereum und litecoin zu kaufen und verkaufen."
+ },
+ "ok": {
+ "message": "Ok"
+ },
+ "cancel": {
+ "message": "Abbrechen"
+ },
+ "classicInterface": {
+ "message": "Klassische Oberfläche verwenden"
+ },
+ "clickCopy": {
+ "message": "Klicken um zu kopieren"
+ },
+ "confirm": {
+ "message": "Bestätigen"
+ },
+ "confirmed": {
+ "message": "Bestätigt"
+ },
+ "confirmContract": {
+ "message": "Smart Contract bestätigen"
+ },
+ "confirmPassword": {
+ "message": "Passwort bestätigen"
+ },
+ "confirmTransaction": {
+ "message": "Transaktion bestätigen"
+ },
+ "continue": {
+ "message": "Weiter"
+ },
+ "continueToCoinbase": {
+ "message": "Zu Coinbase fortfahren"
+ },
+ "contractDeployment": {
+ "message": "Smart Contract ausführen"
+ },
+ "conversionProgress": {
+ "message": "Umtausch in Arbeit"
+ },
+ "copiedButton": {
+ "message": "Kopiert"
+ },
+ "copiedClipboard": {
+ "message": "In die Zwischenablage kopiert"
+ },
+ "copiedExclamation": {
+ "message": "Kopiert!"
+ },
+ "copiedSafe": {
+ "message": "Ich habe es an einen sicheren Ort kopiert"
+ },
+ "copy": {
+ "message": "Kopieren"
+ },
+ "copyToClipboard": {
+ "message": "In die Zwischenablage kopieren"
+ },
+ "copyButton": {
+ "message": " Kopieren "
+ },
+ "copyPrivateKey": {
+ "message": "Das ist Ihr Private Key (klicken um zu kopieren)"
+ },
+ "create": {
+ "message": "Erstellen"
+ },
+ "createAccount": {
+ "message": "Account erstellen"
+ },
+ "createDen": {
+ "message": "Erstellen"
+ },
+ "crypto": {
+ "message": "Krypto",
+ "description": "Börsentyp (Kryptowährungen)"
+ },
+ "currentConversion": {
+ "message": "Aktueller Umtausch"
+ },
+ "currentNetwork": {
+ "message": "Aktuelles Netzwerk"
+ },
+ "customGas": {
+ "message": "Gas anpassen"
+ },
+ "customize": {
+ "message": "Anpassen"
+ },
+ "customRPC": {
+ "message": "Spezieller RPC"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "Die Dezimalangabe muss mindestens 0 und nicht höher als 36 sein."
+ },
+ "decimal": {
+ "message": "Dezimalangabe der Präzision"
+ },
+ "defaultNetwork": {
+ "message": "Das Standardnetzwerk für Ether Transaktionen ist das Main Net."
+ },
+ "denExplainer": {
+ "message": "Dein DEN ist dein passwortverschlüsselter Speicher innerhalb von MetaMask."
+ },
+ "deposit": {
+ "message": "Einzahlen"
+ },
+ "depositBTC": {
+ "message": "Zahle dein BTC in die unten stehende Adresse ein:"
+ },
+ "depositCoin": {
+ "message": "Zahle deine $1 in die unten stehende Adresse ein",
+ "description": "Teilt dem Benutzer mit welchen Token er beim Einzahlen mit Shapeshift ausgewählt hat"
+ },
+ "depositEth": {
+ "message": "Eth einzahlen"
+ },
+ "depositEther": {
+ "message": "Ether einzahlen"
+ },
+ "depositFiat": {
+ "message": "Fiat einzahlen"
+ },
+ "depositFromAccount": {
+ "message": "Von einem anderen Account einzahlen"
+ },
+ "depositShapeShift": {
+ "message": "Mit ShapeShift einzahlen"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Wenn du andere Kryptowährungen besitzt, kannst du diese direkt mit Hilfe deiner MetaMask Wallet handeln und einzahlen. Du benötigst keinen Account."
+ },
+ "details": {
+ "message": "Details"
+ },
+ "directDeposit": {
+ "message": "Sofortige Einzahlung"
+ },
+ "directDepositEther": {
+ "message": "Sofort Ether einzahlen"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Wenn du bereits Ether besitzt, ist die sofortige Einzahlung die schnellste Methode Ether in deine neue Wallet zu bekommen."
+ },
+ "done": {
+ "message": "Fertig"
+ },
+ "downloadStatelogs": {
+ "message": "Statelogs herunterladen"
+ },
+ "dropped": {
+ "message": "Abgewählt"
+ },
+ "edit": {
+ "message": "Editieren"
+ },
+ "editAccountName": {
+ "message": "Namen des Accounts editieren"
+ },
+ "emailUs": {
+ "message": "Schreib uns eine Mail!"
+ },
+ "encryptNewDen": {
+ "message": "Verschlüssele deine neue DEN"
+ },
+ "enterPassword": {
+ "message": "Passwort eingeben"
+ },
+ "enterPasswordConfirm": {
+ "message": "Gib dein neues Passwort zur Bestätigung ein"
+ },
+ "passwordNotLongEnough": {
+ "message": "Passwort ist nicht lang genug"
+ },
+ "passwordsDontMatch": {
+ "message": "Passwörter stimmen nicht überein"
+ },
+ "etherscanView": {
+ "message": "Account auf Etherscan anschauen"
+ },
+ "exchangeRate": {
+ "message": "Wechselrate"
+ },
+ "exportPrivateKey": {
+ "message": "Private Key exportieren"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Der Export von Private Keys verläuft auf eigene Verantwortung."
+ },
+ "failed": {
+ "message": "Fehlgeschlagen"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Börsentyp"
+ },
+ "fileImportFail": {
+ "message": "Dateiimport fehlgeschlagen? Bitte hier klicken!",
+ "description": "Hilft dem Benutzer sein Benutzerkonto durch eine JSON Datei zu importieren"
+ },
+ "followTwitter": {
+ "message": "Folge uns auf Twitter"
+ },
+ "from": {
+ "message": "von"
+ },
+ "fromToSame": {
+ "message": "Ziel- und Ursprungsadresse dürfen nicht identisch sein"
+ },
+ "fromShapeShift": {
+ "message": "Von ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Kleiner Hinweis bzgl. der Gaskosten"
+ },
+ "gasFee": {
+ "message": "Gasgebühren"
+ },
+ "gasLimit": {
+ "message": "Gaslimit"
+ },
+ "gasLimitCalculation": {
+ "message": "Wir berechnen das empfohlene Gaslimit basierend auf der Erfolgsrate des Netzwerks."
+ },
+ "gasLimitRequired": {
+ "message": "Gaslimit benötigt"
+ },
+ "gasLimitTooLow": {
+ "message": "Gaslimit muss mindestens 21000 betragen"
+ },
+ "generatingSeed": {
+ "message": "Seed generieren..."
+ },
+ "gasPrice": {
+ "message": "Gaspreis (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Wir berechnen den empfohlenen Gaspreis basierend auf der Erfolgsrate des Netzwerks."
+ },
+ "gasPriceRequired": {
+ "message": "Gaspreis benötigt"
+ },
+ "getEther": {
+ "message": "Ether holen"
+ },
+ "getEtherFromFaucet": {
+ "message": "Ether für $1 vom Faucet holen",
+ "description": "Zeigt den Netzwerknamen für den Ether Faucet an"
+ },
+ "greaterThanMin": {
+ "message": "Muss größer oder gleich $1 sein.",
+ "description": "Helfer für die Eingabe von hex als dezimal"
+ },
+ "here": {
+ "message": "hier",
+ "description": "z.B. für klick hier für mehr Informationen (in Zusammenhang mit troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "Hier ist eine Liste!!!!"
+ },
+ "hide": {
+ "message": "Ausblenden"
+ },
+ "hideToken": {
+ "message": "Token ausblenden"
+ },
+ "hideTokenPrompt": {
+ "message": "Token ausblenden?"
+ },
+ "howToDeposit": {
+ "message": "Wie möchtest du Ether einzahlen?"
+ },
+ "holdEther": {
+ "message": "Es erlaubt dir ether & Token zu halten und dient dir als Verbindung zu dezentralisierten Applikationen."
+ },
+ "import": {
+ "message": "Import",
+ "description": "Button um den Account aus einer ausgewählten Datei zu importieren"
+ },
+ "importAccount": {
+ "message": "Account importieren"
+ },
+ "importAccountMsg": {
+ "message":" Importierte Accounts werden nicht mit der Seed Wörterfolge deines ursprünglichen MetaMask Accounts verknüpft. Erfahre mehr über importierte Accounts."
+ },
+ "importAnAccount": {
+ "message": "Einen Account importieren"
+ },
+ "importDen": {
+ "message": "Vorhandenes DEN importieren"
+ },
+ "imported": {
+ "message": "Importiert",
+ "description": "Status der angezeigt wird wenn ein Benutzerkonto vollständig in das Schlüsselbund geladen wurde."
+ },
+ "infoHelp": {
+ "message": "Info & Hilfe"
+ },
+ "insufficientFunds": {
+ "message": "Nicht genügend Guthaben."
+ },
+ "insufficientTokens": {
+ "message": "Nicht genügend Token."
+ },
+ "invalidAddress": {
+ "message": "Ungültige Adresse"
+ },
+ "invalidAddressRecipient": {
+ "message": "Empfängeradresse ist unzulässig"
+ },
+ "invalidGasParams": {
+ "message": "Ungültige Gasparameter"
+ },
+ "invalidInput": {
+ "message": "Ungültige Eingabe."
+ },
+ "invalidRequest": {
+ "message": "Ungültige Abfrage"
+ },
+ "invalidRPC": {
+ "message": "Ungültige RPC URI"
+ },
+ "jsonFail": {
+ "message": "Irgendetwas ist schief gelaufen. Bitte überprüfe ob deine JSON Datei korrekt formatiert ist."
+ },
+ "jsonFile": {
+ "message": "JSON Datei",
+ "description": "Dateiformat für das Importieren eines Accounts"
+ },
+ "kovan": {
+ "message": "Kovan Testnetzwerk"
+ },
+ "knowledgeDataBase": {
+ "message": "Schau in unsere Wissensdatenbank"
+ },
+ "max": {
+ "message": "Max"
+ },
+ "lessThanMax": {
+ "message": "Muss kleiner oder gleich $1 sein.",
+ "description": "Helfer für die Eingabe von hex als dezimal"
+ },
+ "likeToAddTokens": {
+ "message": "Möchtest du diese Token hinzufügen?"
+ },
+ "links": {
+ "message": "Links"
+ },
+ "limit": {
+ "message": "Limit"
+ },
+ "loading": {
+ "message": "Laden..."
+ },
+ "loadingTokens": {
+ "message": "Token laden..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "Login"
+ },
+ "logout": {
+ "message": "Ausloggen"
+ },
+ "loose": {
+ "message": "Frei"
+ },
+ "loweCaseWords": {
+ "message": "Die Wörter der Seed Wörterfolgen sind alle kleingeschrieben"
+ },
+ "mainnet": {
+ "message": "Ethereum Hauptnetzwerk (Main Net)"
+ },
+ "message": {
+ "message": "Nachricht"
+ },
+ "metamaskDescription": {
+ "message": "MetaMask ist ein sicherer Identitätssafe für Ethereum."
+ },
+ "min": {
+ "message": "Minimum"
+ },
+ "myAccounts": {
+ "message": "Meine Accounts"
+ },
+ "mustSelectOne": {
+ "message": "Du musst mindestens 1 Token auswählen."
+ },
+ "needEtherInWallet": {
+ "message": "Um dezentralisierte Applikationen mit MetaMask verwenden zu können, benötigst du Ether in deiner Wallet."
+ },
+ "needImportFile": {
+ "message": "Für den Import musst du eine Datei auswählen.",
+ "description": "Benutzer importiert ein Benutzerkonto und muss eine Datei hinzufügen um fortzufahren"
+ },
+ "needImportPassword": {
+ "message": "Für die ausgewählte Datei muss ein Passwort eingegeben werden.",
+ "description": "Passwort und Datei sind notwendig um einen Account zu importieren"
+ },
+ "negativeETH": {
+ "message": "Negative ETH Beträge können nicht versendet werden."
+ },
+ "networks": {
+ "message": "Netzwerke"
+ },
+ "newAccount": {
+ "message": "Neuer Account"
+ },
+ "newAccountNumberName": {
+ "message": "Account $1",
+ "description": "Standardname für einen weiteren Account der angelegt wird, wenn create account screen geklickt wird"
+ },
+ "newContract": {
+ "message": "Neuer Smart Contract"
+ },
+ "newPassword": {
+ "message": "Neues Passwort (min. 8 Zeichen)"
+ },
+ "newRecipient": {
+ "message": "Neuer Empfänger"
+ },
+ "newRPC": {
+ "message": "Neue RPC URL"
+ },
+ "next": {
+ "message": "Weiter"
+ },
+ "noAddressForName": {
+ "message": "Für den angegebene Namen wurde keine Adresse eingegeben."
+ },
+ "noDeposits": {
+ "message": "Keine Einzahlung erhalten"
+ },
+ "noTransactionHistory": {
+ "message": "Keine Transaktionshistorie."
+ },
+ "noTransactions": {
+ "message": "Keine Transaktionen"
+ },
+ "notStarted": {
+ "message": "Nicht gestartet"
+ },
+ "oldUI": {
+ "message": "Alte Oberfläche"
+ },
+ "oldUIMessage": {
+ "message": "Du bist zur alten Oberfläche zurückgewechselt. Du kannst mit Hilfe der Option im oberen rechten Dropdown Menü zur neuen Oberfläche zurückwechseln."
+ },
+ "or": {
+ "message": "oder",
+ "description": "Wahl zwischen erstellen oder importieren eines Accounts."
+ },
+ "passwordCorrect": {
+ "message": "Bitte überzeuge dich davon, dass dein Passwort korrekt ist."
+ },
+ "passwordMismatch": {
+ "message": "Passwörter stimmen nicht überein",
+ "description": "Im Passwort erstellen Prozess stimmen beide Passwörter nicht miteinander überein"
+ },
+ "passwordShort": {
+ "message": "Passwort ist nicht lang genug",
+ "description": "Im Passwort erstellen Prozess ist das eingegebene Passwort nicht lang genug um sicher zu sein"
+ },
+ "pastePrivateKey": {
+ "message": "Füge deine Private Key Zeichenfolge hier ein:",
+ "description": "Für den Import eine Accounts mit Hilfe eines Private Keys"
+ },
+ "pasteSeed": {
+ "message": "Füge deine Seed Wörterfolge hier ein!"
+ },
+ "personalAddressDetected": {
+ "message": "Personalisierte Adresse identifiziert. Bitte füge die Token Contract Adresse ein."
+ },
+ "pleaseReviewTransaction": {
+ "message": "Bitte überprüfe deine Transaktion."
+ },
+ "privacyMsg": {
+ "message": "Datenschutzrichtlinie"
+ },
+ "privateKey": {
+ "message": "Private Key",
+ "description": "Wähle diesen Dateityp um damit einen Account zu importieren"
+ },
+ "privateKeyWarning": {
+ "message": "Warnung: Niemals jemanden deinen Private Key mitteilen. Jeder der im Besitz deines Private Keys ist, kann jegliches Guthaben deines Accounts stehlen."
+ },
+ "privateNetwork": {
+ "message": "Privates Netzwerk"
+ },
+ "qrCode": {
+ "message": "QR Code anzeigen"
+ },
+ "readdToken": {
+ "message": "Du kannst diesen Token zukünftig wieder hinzufügen indem du in den Menüpunkt \"Token hinzufügen\" in den Einstellungen deines Accounts gehst."
+ },
+ "readMore": {
+ "message": "Hier mehr erfahren."
+ },
+ "readMore2": {
+ "message": "Mehr erfahren."
+ },
+ "receive": {
+ "message": "Erhalten"
+ },
+ "recipientAddress": {
+ "message": "Empfängeradresse"
+ },
+ "refundAddress": {
+ "message": "Rückerstattungsadresse"
+ },
+ "rejected": {
+ "message": "Abgelehnt"
+ },
+ "resetAccount": {
+ "message": "Account zurücksetzten"
+ },
+ "restoreFromSeed": {
+ "message": "Mit Hilfe der Seed Wörterfolge wiederherstellen."
+ },
+ "restoreVault": {
+ "message": "Vault wiederherstellen"
+ },
+ "required": {
+ "message": "Benötigt"
+ },
+ "retryWithMoreGas": {
+ "message": "Versuche es hier mit einem höheren Gaspreis noch einmal"
+ },
+ "walletSeed": {
+ "message": "Wallet Seed"
+ },
+ "revealSeedWords": {
+ "message": "Seed Wörterfolge anzeigen"
+ },
+ "revealSeedWordsWarning": {
+ "message": "Bitte niemals deine Seed Wörterfolge an einem öffentlichen Ort kenntlich machen. Mit diesen Wörtern können alle deine Accounts gestohlen werden."
+ },
+ "revert": {
+ "message": "Zurück gehen"
+ },
+ "rinkeby": {
+ "message": "Rinkeby Testnetzwerk"
+ },
+ "ropsten": {
+ "message": "Ropsten Testnetzwerk"
+ },
+ "currentRpc": {
+ "message": "Aktueller RPC"
+ },
+ "connectingToMainnet": {
+ "message": "Verbinde zum Ethereum Hauptnetzwerk (Main Net)"
+ },
+ "connectingToRopsten": {
+ "message": " Verbinde zum Ropsten Testnetzwerk"
+ },
+ "connectingToKovan": {
+ "message": " Verbinde zum Kovan Testnetzwerk"
+ },
+ "connectingToRinkeby": {
+ "message": " Verbinde zum Rinkeby Testnetzwerk"
+ },
+ "connectingToUnknown": {
+ "message": "Verbinde zu einem unbekanntem Netzwerk"
+ },
+ "sampleAccountName": {
+ "message": "Z.B. mein neuer Account",
+ "description": "Dem Benutzer helfen das Konzept des Hinzufügens eines menschlich lesbaren Namen für den Account hinzuzufügen"
+ },
+ "save": {
+ "message": "Speichern"
+ },
+ "saveAsFile": {
+ "message": "Als Datei speichern",
+ "description": "Prozess des Exportieren eines Accounts"
+ },
+ "saveSeedAsFile": {
+ "message": "Seed Wörterfolge als Datei speichern"
+ },
+ "search": {
+ "message": "Suche"
+ },
+ "secretPhrase": {
+ "message": "Gib die 12 Wörter deiner geheimem Wörterfolge ein um deinen Vault wiederherzustellen."
+ },
+ "newPassword8Chars": {
+ "message": "Neues Passwort (min. 8 Zeichen)"
+ },
+ "seedPhraseReq": {
+ "message": "Seed Wörterfolgen bestehen aus 12 Wörtern"
+ },
+ "select": {
+ "message": "Auswählen"
+ },
+ "selectCurrency": {
+ "message": "Währung auswählen"
+ },
+ "selectService": {
+ "message": "Service auswählen"
+ },
+ "selectType": {
+ "message": "Typ auswählen"
+ },
+ "send": {
+ "message": "Senden"
+ },
+ "sendETH": {
+ "message": "ETH senden"
+ },
+ "sendTokens": {
+ "message": "Token senden"
+ },
+ "onlySendToEtherAddress": {
+ "message": "ETH nur zu einer Ethereum Adresse senden."
+ },
+ "sendTokensAnywhere": {
+ "message": "Token zu einer beliebigen Person mit einem Ethereumaccount senden"
+ },
+ "settings": {
+ "message": "Einstellungen"
+ },
+ "info": {
+ "message": "Info"
+ },
+ "shapeshiftBuy": {
+ "message": "Mit Shapeshift kaufen"
+ },
+ "showPrivateKeys": {
+ "message": "Private Keys anzeigen"
+ },
+ "showQRCode": {
+ "message": "QR Code anzeigen"
+ },
+ "sign": {
+ "message": "Unterschreiben"
+ },
+ "signed": {
+ "message": "Unterschrieben"
+ },
+ "signMessage": {
+ "message": "Nachricht unterschreiben"
+ },
+ "signNotice": {
+ "message": "Das Unterschreiben dieser Nachricht kann gefährliche Nebeneffekte haben. Bitte nur Nachrichten von Webseiten unterschreiben denen du deinen vollständigen Account anvertrauen würdest. Diese gefährliche Variante wird in zukünftigen Versionen entfernt werden."
+ },
+ "sigRequest": {
+ "message": "Unterschriftsanfrage"
+ },
+ "sigRequested": {
+ "message": "Unterschrift angefragt"
+ },
+ "spaceBetween": {
+ "message": "Es darf nur ein Leerzeichen zwischen den Wörtern sein"
+ },
+ "status": {
+ "message": "Status"
+ },
+ "stateLogs": {
+ "message": "Statelogs"
+ },
+ "stateLogsDescription": {
+ "message": "Statelogs zeigen die Public Adresse und die gesendeten Transaktionen deines Accounts."
+ },
+ "stateLogError": {
+ "message": "Fehler beim Abfragen der Statelogs."
+ },
+ "submit": {
+ "message": "Einreichen"
+ },
+ "submitted": {
+ "message": "Eingereicht"
+ },
+ "supportCenter": {
+ "message": "Gehe zu unserem Support Center"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "Das Symbol muss zwischen 0 und 10 Zeichen haben."
+ },
+ "takesTooLong": {
+ "message": "Dauert es zu lang?"
+ },
+ "terms": {
+ "message": "Nutzungsbedingungen"
+ },
+ "testFaucet": {
+ "message": "Testfaucet"
+ },
+ "to": {
+ "message": "An:"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 an ETH via ShapeShift",
+ "description": "Das System wird den Einzahlungstyp im Beginn der Nachricht eintragen"
+ },
+ "tokenAddress": {
+ "message": "Tokenadresse"
+ },
+ "tokenAlreadyAdded": {
+ "message": "Der Token wurde bereits hinzugefügt."
+ },
+ "tokenBalance": {
+ "message": "Dein Tokenguthaben beträgt:"
+ },
+ "tokenSelection": {
+ "message": "Suche nach Token oder wähle aus einer Liste der beliebtesten Token aus."
+ },
+ "tokenSymbol": {
+ "message": "Tokensymbol"
+ },
+ "tokenWarning1": {
+ "message": "Behalte die Token die du mit deinem MetaMask Account gekauft hast im Auge. Wenn du Token mit einem anderen Account gekauft hast, werden diese hier nicht angezeigt."
+ },
+ "total": {
+ "message": "Gesamt"
+ },
+ "transactions": {
+ "message": "Transaktionen"
+ },
+ "transactionMemo": {
+ "message": "Transaktionsmemo (optional)"
+ },
+ "transactionNumber": {
+ "message": "Transaktionsnummer"
+ },
+ "transfers": {
+ "message": "Transfers"
+ },
+ "troubleTokenBalances": {
+ "message": "Wir haben Schwierigkeiten dein Tokenguthaben zu laden. Du kannst es hier anzeigen lassen",
+ "description": "Gefolgt von einem Link (hier) um die Tokenguthaben anzuzeigen"
+ },
+ "twelveWords": {
+ "message": "Diese 12 Wörter stellen die einzige Möglichkeit dar deinen MetaMask Account wiederherzustellen. Speichere sie daher an einem sicheren und geheimen Ort."
+ },
+ "typePassword": {
+ "message": "Passwort eingeben"
+ },
+ "uiWelcome": {
+ "message": "Willkommen zur neuen Oberfläche (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Du verwendest nun die neue Metamask Oberfläche. Schau dich um, teste die neuen Features wie z.B. das Senden von Token und lass es uns wissen falls du irgendwelche Probleme hast."
+ },
+ "unapproved": {
+ "message": "Nicht genehmigt"
+ },
+ "unavailable": {
+ "message": "Nicht verfügbar"
+ },
+ "unknown": {
+ "message": "Unbekannt"
+ },
+ "unknownNetwork": {
+ "message": "Unbekanntes privates Netzwerk"
+ },
+ "uriErrorMsg": {
+ "message": "URIs benötigen die korrekten HTTP/HTTPS Präfixe."
+ },
+ "unknownNetworkId": {
+ "message": "Unbekannte Netzwerk ID"
+ },
+ "usaOnly": {
+ "message": "Nur USA ",
+ "description": "Diese Börse ist nur für Einwohner der USA verfügbar"
+ },
+ "usedByClients": {
+ "message": "Verwendet von einer Reihe verschiedenen Kunden"
+ },
+ "useOldUI": {
+ "message": "Alte Oberfläche verwenden"
+ },
+ "validFileImport": {
+ "message": "Du musst eine gültige Datei für den Import auswählen."
+ },
+ "vaultCreated": {
+ "message": "Vault erstellt"
+ },
+ "viewAccount": {
+ "message": " Account einsehen"
+ },
+ "visitWebSite": {
+ "message": "Gehe zu unsere Webseite"
+ },
+ "warning": {
+ "message": "Warnung"
+ },
+ "welcomeBeta": {
+ "message": "Willkommen zu MetaMask Beta"
+ },
+ "whatsThis": {
+ "message": "Was ist das?"
+ },
+ "yourSigRequested": {
+ "message": "Deine Unterschrift wird angefordert"
+ },
+ "youSign": {
+ "message": "Du unterschreibst"
+ }
+}
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index c64b7248b..3e469cf44 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -171,6 +171,9 @@
"customGas": {
"message": "Customize Gas"
},
+ "customToken": {
+ "message": "Custom Token"
+ },
"customize": {
"message": "Customize"
},
@@ -182,7 +185,7 @@
},
"decimal": {
"message": "Decimals of Precision"
- },
+ },
"defaultNetwork": {
"message": "The default network for Ether transactions is Main Net."
},
@@ -232,7 +235,7 @@
"done": {
"message": "Done"
},
- "downloadStatelogs": {
+ "downloadStateLogs": {
"message": "Download State Logs"
},
"dropped": {
@@ -415,6 +418,9 @@
"message": "JSON File",
"description": "format for importing an account"
},
+ "keepTrackTokens": {
+ "message": "Keep track of the tokens you’ve bought with your MetaMask account."
+ },
"kovan": {
"message": "Kovan Test Network"
},
@@ -424,6 +430,9 @@
"max": {
"message": "Max"
},
+ "learnMore": {
+ "message": "Learn more."
+ },
"lessThanMax": {
"message": "must be less than or equal to $1.",
"description": "helper for inputting hex as decimal input"
@@ -564,6 +573,9 @@
"pleaseReviewTransaction": {
"message": "Please review your transaction."
},
+ "popularTokens": {
+ "message": "Popular Tokens"
+ },
"privacyMsg": {
"message": "Privacy Policy"
},
@@ -659,6 +671,12 @@
"save": {
"message": "Save"
},
+ "reprice_title": {
+ "message": "Reprice Transaction"
+ },
+ "reprice_subtitle": {
+ "message": "Increase your gas price to attempt to overwrite and speed up your transaction"
+ },
"saveAsFile": {
"message": "Save as File",
"description": "Account export process"
@@ -702,6 +720,9 @@
"onlySendToEtherAddress": {
"message": "Only send ETH to an Ethereum address."
},
+ "searchTokens": {
+ "message": "Search Tokens"
+ },
"sendTokensAnywhere": {
"message": "Send Tokens to anyone with an Ethereum account"
},
@@ -869,7 +890,7 @@
},
"visitWebSite": {
"message": "Visit our web site"
- },
+ },
"warning": {
"message": "Warning"
},
diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json
index aa2701c2c..fa28b09da 100644
--- a/app/_locales/es/messages.json
+++ b/app/_locales/es/messages.json
@@ -75,7 +75,7 @@
"message": "Pedir prestado con Dharma (Beta)"
},
"builtInCalifornia": {
- "message": "Metamask fue diseñado y construido en California "
+ "message": "Metamask fue diseñado y construido en California"
},
"buy": {
"message": "Comprar"
@@ -84,13 +84,13 @@
"message": "Comprar en Coinbase"
},
"buyCoinbaseExplainer": {
- "message": "Coinbase es la forma global más popular para comprar y vender bitcoin, ethereum y litecoin"
+ "message": "Coinbase es la plataforma global más popular para comprar y vender Bitcoin, Ethereum y Litecoin"
},
"cancel": {
"message": "Cancelar"
},
"classicInterface": {
- "message": "Usar interfaz clásica "
+ "message": "Usar interfaz clásica"
},
"clickCopy": {
"message": "Click para copiar"
@@ -144,7 +144,7 @@
"message": "Copiado al portapapeles"
},
"copiedExclamation": {
- "message": "Copiado!"
+ "message": "¡Copiado!"
},
"copiedSafe": {
"message": "Ya lo guardé en un lugar seguro"
@@ -156,7 +156,7 @@
"message": " Copiar "
},
"copyPrivateKey": {
- "message": "Esta es tu llave privada (Click para copiar)"
+ "message": "Ésta es tu llave privada (haz click para copiar)"
},
"copyToClipboard": {
"message": "Copiar al portapapeles"
@@ -165,17 +165,17 @@
"message": "Crear"
},
"createAccount": {
- "message": "Crear Cuenta"
+ "message": "Crear cuenta"
},
"createDen": {
"message": "Crear"
},
"crypto": {
"message": "Crypto",
- "description": "Tipo de Cambio (criptomonedas)"
+ "description": "Tipo de cambio (criptomonedas)"
},
"currentConversion": {
- "message": "Conversión Actual"
+ "message": "Conversión actual"
},
"currentNetwork": {
"message": "Red actual"
@@ -184,10 +184,10 @@
"message": "RPC actual"
},
"customGas": {
- "message": "Personalizar Gas"
+ "message": "Personalizar gas"
},
"customRPC": {
- "message": "RPC Personalizado"
+ "message": "RPC personalizado"
},
"customize": {
"message": "Personalizar"
@@ -202,7 +202,7 @@
"message": "La red por defecto para las transacciones de Ether es MainNet (red principal)"
},
"denExplainer": {
- "message": "Tu DEN es tu contraseña encriptada guardada dentro de MetaMask"
+ "message": "El DEN es tu contraseña encriptada almacenada dentro de MetaMask"
},
"deposit": {
"message": "Depositar"
@@ -221,7 +221,7 @@
"message": "Depositar Ether"
},
"depositFiat": {
- "message": "Depositar con Fiat (divisa nacional)"
+ "message": "Depositar con fiat (divisa nacional)"
},
"depositFromAccount": {
"message": "Depositar con otra cuenta"
@@ -230,7 +230,7 @@
"message": "Depositar con ShapeShift"
},
"depositShapeShiftExplainer": {
- "message": "Si tu tienes otras criptomonedas, puedes intercambiar y depositar Ether directamente en tu billetera de MetaMask. No necesitas tener una cuenta"
+ "message": "Si posees otras criptomonedas, puedes intercambiar y depositar Ether directamente en tu billetera de MetaMask. No necesitas tener una cuenta."
},
"details": {
"message": "Detalles"
@@ -242,7 +242,7 @@
"message": "Depositar Ether directamente"
},
"directDepositEtherExplainer": {
- "message": "Si tu tienes algo de Ether, la forma rápida para tener Ether en tu nueva billetera es depositando directamente"
+ "message": "Si posees Ether, la forma más rápida de transferirlo a tu nueva billetera es depositándolo directamente"
},
"done": {
"message": "Completo"
@@ -260,7 +260,7 @@
"message": "Editar el nombre de la cuenta"
},
"emailUs": {
- "message": "Envíanos un correo!"
+ "message": "¡Envíanos un correo!"
},
"encryptNewDen": {
"message": "Encriptar tu nuevo DEN"
@@ -291,7 +291,7 @@
"description": "Tipo de cambio"
},
"fileImportFail": {
- "message": "No funciona importar el archivo? Haz Click Aquí!",
+ "message": "¿La importación no funcionó? ¡Haz click aquí!",
"description": "Ayuda al usuario a importar su cuenta desde un archivo JSON"
},
"followTwitter": {
@@ -320,7 +320,7 @@
"message": "Calculamos el límite de gas sugerido en función de las tasas de éxito de la red"
},
"gasLimitRequired": {
- "message": "Límite de Gas requerido"
+ "message": "Límite de gas requerido"
},
"gasLimitTooLow": {
"message": "El límite de gas debe ser de al menos 21000"
@@ -353,22 +353,22 @@
"description": "como en -haz click aquí- para más información"
},
"hereList": {
- "message": "Aquí está una lista!!!"
+ "message": "¡¡¡Aquí está una lista!!!"
},
"hide": {
"message": "Ocultar"
},
"hideToken": {
- "message": "Ocultar Token"
+ "message": "Ocultar token"
},
"hideTokenPrompt": {
- "message": "Ocultar Token?"
+ "message": "¿Ocultar token?"
},
"holdEther": {
"message": "Te permite mantener tus ether y tokens, así como puente para aplicaciones descentralizadas"
},
"howToDeposit": {
- "message": "Cómo te gustaria depositar Ether?"
+ "message": "¿Cómo te gustaria depositar Ether?"
},
"import": {
"message": "Importar",
@@ -388,13 +388,13 @@
"description": "Estado que muestra que una cuenta ha sido completamente cargada en el llavero"
},
"importAccountMsg": {
- "message": "Cuentas importadas no serán asociadas con tu cuenta original creada con tu MetaMask. Aprende más acerca de importar cuentas."
+ "message": "Las cuentas importadas no serán asociadas con tu cuenta original creada con tu MetaMask. Aprende más acerca de importar cuentas."
},
"info": {
"message": "Información"
},
"infoHelp": {
- "message": "Informacion y Ayuda"
+ "message": "Informacion y ayuda"
},
"insufficientFunds": {
"message": "Fondos insuficientes"
@@ -421,7 +421,7 @@
"message": "Petición inválida"
},
"jsonFail": {
- "message": "Algo malo pasó. Asegurate que tu JSON tiene el formato correcto"
+ "message": "Algo falló. Asegúrate que tu JSON tiene el formato correcto"
},
"jsonFile": {
"message": "Archivo JSON",
@@ -477,7 +477,7 @@
"message": "Mensaje"
},
"metamaskDescription": {
- "message": "Metamask es una identidad segura en Ethereum"
+ "message": "MetaMask es una identidad segura en Ethereum"
},
"min": {
"message": "Mínimo"
@@ -489,7 +489,7 @@
"message": "Mis cuentas"
},
"needEtherInWallet": {
- "message": "Para interactuar con una aplicación descentralizada usando MetaMask, vas a necesitar tener Ether en tu billetera"
+ "message": "Para interactuar con una aplicación descentralizada usando MetaMask, necesitas tener Ether en tu billetera"
},
"needImportFile": {
"message": "Debes seleccionar un archivo para importar",
@@ -565,17 +565,17 @@
"message": "Asegurate que tu contraseña es correcta"
},
"passwordMismatch": {
- "message": "Contraseña no coincide",
+ "message": "La contraseña no coincide",
"description": "En el proceso de creación de contraseña, los dos campos de contraseña no coincidieron"
},
"passwordNotLongEnough": {
"message": "La contraseña no es lo suficientemente larga"
},
"passwordsDontMatch": {
- "message": "Contraseñas no coinciden"
+ "message": "Las contraseñas no coinciden"
},
"passwordShort": {
- "message": "Contraseña no es lo suficientemente larga",
+ "message": "La contraseña no es lo suficientemente larga",
"description": "En el proceso de creación de contraseña, esta no es lo suficientemente larga para ser segura"
},
"pastePrivateKey": {
@@ -583,13 +583,13 @@
"description": "Para importar una cuenta desde una llave privada"
},
"pasteSeed": {
- "message": "Pega tu frase semilla aquí!"
+ "message": "¡Pega tu frase semilla aquí!"
},
"personalAddressDetected": {
"message": "Dirección personal detectada. Ingresa la dirección del contrato del token"
},
"pleaseReviewTransaction": {
- "message": "Por favor revisa tu transaccion"
+ "message": "Por favor, revisa tu transaccion"
},
"privacyMsg": {
"message": "Política de privacidad"
@@ -602,7 +602,7 @@
"message": "Advertencia: NUNCA reveles esta clave. Cualquier persona con tus claves privadas puede robar los activos retenidos en tu cuenta"
},
"privateNetwork": {
- "message": "Red Privada"
+ "message": "Red privada"
},
"qrCode": {
"message": "Mostrar codigo QR"
@@ -614,7 +614,7 @@
"message": "Leer más"
},
"readdToken": {
- "message": "Puede volver a agregar este token en el futuro yendo a 'Agregar token' en el menú de opciones de tu cuenta"
+ "message": "Puedes volver a agregar este token en el futuro pinchando sobre 'Agregar token' en el menú de opciones de tu cuenta"
},
"receive": {
"message": "Recibir"
@@ -641,13 +641,13 @@
"message": "Restaurar Bóveda"
},
"retryWithMoreGas": {
- "message": "Vuelva a intentar con un precio de Gas más alto aquí"
+ "message": "Vuelva a intentar con un precio de gas más alto aquí"
},
"revealSeedWords": {
"message": "Revelar palabras de semilla"
},
"revealSeedWordsWarning": {
- "message": "No recuperes tu semilla en un lugar publico! Esas palabras pueden ser usadas para robarte todas tus cuentas"
+ "message": "¡No recuperes tu semilla en un lugar pública! Esas palabras pueden ser usadas para robarte todas tus cuentas"
},
"revert": {
"message": "Revertir"
@@ -659,7 +659,7 @@
"message": "Red privada Ropsten"
},
"sampleAccountName": {
- "message": "Ej. Mi nueva cuenta",
+ "message": "P.ej. Mi nueva cuenta",
"description": "Ayuda al usuario a entender el concepto de agregar un nombre, leíble por humanos, a su cuenta"
},
"save": {
@@ -700,10 +700,10 @@
"message": "Enviar Ether"
},
"sendTokens": {
- "message": "Enviar Tokens"
+ "message": "Enviar tokens"
},
"sendTokensAnywhere": {
- "message": "Enviar Tokens a cualquiera con una cuenta de Ethereum"
+ "message": "Enviar tokens a cualquiera con una cuenta de Ethereum"
},
"settings": {
"message": "Configuración"
@@ -730,10 +730,10 @@
"message": "Firmado"
},
"signMessage": {
- "message": "Firmar Mensaje"
+ "message": "Firmar mensaje"
},
"signNotice": {
- "message": "Firmar este mensaje puede tener\n efectos secundarios peligrosos. Firma sólo\nmensajes desde sitios a los cuales tú estés dispuesto a confiar completamente tu cuenta.\nEste método peligroso va a ser \nremovido en una version futura."
+ "message": "Firmar este mensaje puede tener\n efectos secundarios peligrosos. Firma sólo\nmensajes desde sitios a los que estés plenamente dispuesto a confiar tu cuenta.\nEste método peligroso va a ser \neliminado en una version futura."
},
"spaceBetween": {
"message": "Sólo puede haber un espacio entre las palabras"
@@ -742,7 +742,7 @@
"message": "Logs de estado"
},
"stateLogsDescription": {
- "message": "Los Logs de estado contienen tus direcciones de cuentas públicas y transacciones envíadas"
+ "message": "Los logs de estado contienen tus direcciones de cuentas públicas y transacciones envíadas"
},
"stateLogError": {
"message": "Error en la recogida de logs de estado"
@@ -763,10 +763,10 @@
"message": "Símbolo debe ser entre 0 y 10 caracteres"
},
"takesTooLong": {
- "message": "¿Está tomando demasiado?"
+ "message": "¿Está tardando demasiado?"
},
"terms": {
- "message": "Terminos de Uso"
+ "message": "Términos de uso"
},
"testFaucet": {
"message": "Probar Faucet"
@@ -782,7 +782,7 @@
"message": "Dirección del token"
},
"tokenAlreadyAdded": {
- "message": "El token esta actualmente agregado"
+ "message": "El token está actualmente agregado"
},
"tokenBalance": {
"message": "Tu balance de tokens es:"
@@ -794,13 +794,13 @@
"message": "Símbolo del token"
},
"tokenWarning1": {
- "message": "Manten un registro de los tokens que has comprado con tu cuenta de MetaMask. Si compraste tokens usando una cuenta diferente, esos tokens no aparecerán aquí."
+ "message": "Mantén un registro de los tokens que has comprado con tu cuenta de MetaMask. Si compraste tokens usando una cuenta diferente, esos tokens no aparecerán aquí."
},
"total": {
"message": "Total"
},
"transactionMemo": {
- "message": "Memo de transaccion (opcional)"
+ "message": "Memo de transacción (opcional)"
},
"transactionNumber": {
"message": "Número de transacción"
@@ -816,7 +816,7 @@
"description": "Seguidos por un enlace (aquí) para ver los saldos de token"
},
"twelveWords": {
- "message": "Estas 12 palabras son la única forma de restablecer tus cuentas de MetaMask. \nGuardalas en un lugar seguro y secreto."
+ "message": "Estas 12 palabras son la única forma de restablecer tus cuentas de MetaMask. \nGuárdalas en un lugar seguro y secreto."
},
"typePassword": {
"message": "Escribe tu contraseña"
@@ -825,13 +825,13 @@
"message": "Bienvenido a la nueva UI (Beta)"
},
"uiWelcomeMessage": {
- "message": "Estás usando la nueva UI de MetaMask. Echa un vistazo alrededor, prueba las nuevas características, tales como mandar tokens, y déjanos saber si tienes algún problema"
+ "message": "Estás usando la nueva UI de MetaMask. Echa un vistazo alrededor, prueba las nuevas características, tales como mandar tokens, y háznos saber si tienes algún problema"
},
"unavailable": {
"message": "No disponible"
},
"unapproved": {
- "message": "No Aprobado"
+ "message": "No aprobado"
},
"unknown": {
"message": "Desconocido (a)"
@@ -840,14 +840,14 @@
"message": "Red privada desconocida"
},
"unknownNetworkId": {
- "message": "ID (identidad) de Red desconocida"
+ "message": "ID (identidad) de red desconocida"
},
"uriErrorMsg": {
- "message": "URI necesita el prefijo HTTP/HTTPS apropiado"
+ "message": "URI necesita el prefijo HTTP/HTTPS apropiado"
},
"usaOnly": {
"message": "Sólo USA (Estados Unidos)",
- "description": "El uso de este exchange (casa de cambio) está limitado a las personas dentro de los Estados Unidos de America"
+ "description": "El uso de este exchange (casa de cambio) está limitado a las personas dentro de los Estados Unidos de América"
},
"useOldUI": {
"message": "Usar UI antigua"
@@ -877,7 +877,7 @@
"message": "Bienvenido a Metamask Beta"
},
"whatsThis": {
- "message": "Qué es esto?"
+ "message": "¿Qué es esto?"
},
"youSign": {
"message": "Usted está firmando"
diff --git a/app/_locales/hn/messages.json b/app/_locales/hn/messages.json
new file mode 100644
index 000000000..3703faa13
--- /dev/null
+++ b/app/_locales/hn/messages.json
@@ -0,0 +1,819 @@
+{
+ "accept": {
+ "message": "स्वीकार करें"
+ },
+ "account": {
+ "message": "खाता"
+ },
+ "accountDetails": {
+ "message": "खाता विवरण"
+ },
+ "accountName": {
+ "message": "खाता का नाम"
+ },
+ "address": {
+ "message": "खाते का पता"
+ },
+ "addCustomToken": {
+ "message": "कस्टम टोकन जोड़ें"
+ },
+ "addToken": {
+ "message": "टोकन जोड़ें"
+ },
+ "addTokens": {
+ "message": "टोकनो को जोड़ें"
+ },
+ "amount": {
+ "message": "राशि"
+ },
+ "amountPlusGas": {
+ "message": "राशि + गैस"
+ },
+ "appDescription": {
+ "message": "एथरेम ब्राउज़र एक्सटेंशन",
+ "description": "आवेदन का विवरण"
+ },
+ "appName": {
+ "message": "मेटामास्क/MetaMask",
+ "description": "एप्लिकेशन का नाम"
+ },
+ "attemptingConnect": {
+ "message": "ब्लॉकचैन से कनेक्ट करने का प्रयास करना होगा।सब्र करे।"
+ },
+ "attributions": {
+ "message": "एट्रिब्यूशन"
+ },
+ "available": {
+ "message": "एट्रिब्यूशन उपलब्ध ह्ने"
+ },
+ "back": {
+ "message": "वापस"
+ },
+ "balance": {
+ "message": "उपलब्ध बैलेंस।"
+ },
+ "balances": {
+ "message": "ापके उपलब्ध बैलेंस"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "वर्तमान गैस कुल के लिए अपर्याप्त शेष"
+ },
+ "beta": {
+ "message": "BETA/बीटा"
+ },
+ "betweenMinAndMax": {
+ "message": "$1 के बराबर या ज्यदा या, $2 के बराबर या कम होना चाहिए।",
+ "description": "हेक्स इनपुट के लिए दशमलव इनपुट के रूप में सहायक"
+ },
+ "blockiesIdenticon": {
+ "message": "ब्लॉकीज पहचान का उपयोग करें"
+ },
+ "borrowDharma": {
+ "message": "धर्मा (बीटा) से / के साथ उधार लें"
+ },
+ "builtInCalifornia": {
+ "message": "मेटामास्क कैलिफ़ोर्निया में डिज़ाइन और बनाया गया है।"
+ },
+ "buy": {
+ "message": "खरीदें"
+ },
+ "buyCoinbase": {
+ "message": "कॉनबेस पर खरीदें"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "बिल्टकोइन, एथरेम और लाइटकोइन खरीदने और बेचने के लिए दुनिया का सबसे लोकप्रिय तरीका Coinbase है।"
+ },
+ "cancel": {
+ "message": "रद्द करें"
+ },
+ "classicInterface": {
+ "message": "क्लासिक इंटरफ़ेस का उपयोग क"
+ },
+ "clickCopy": {
+ "message": "कॉपी करने के लिए क्लिक करें"
+ },
+ "confirm": {
+ "message": "पुष्टि करें"
+ },
+ "confirmContract": {
+ "message": "अनुबंध की पुष्टि करें"
+ },
+ "confirmPassword": {
+ "message": "पासवर्ड की पुष्टि करें"
+ },
+ "confirmTransaction": {
+ "message": "लेनदेन की पुष्टि करें"
+ },
+ "continue": {
+ "message": "जारी रखें"
+ },
+ "continueToCoinbase": {
+ "message": "कॉ्ोनबेस को ब्हेजना जारी रखें"
+ },
+ "contractDeployment": {
+ "message": "अनुबंध परिनियोजन व तैनाती"
+ },
+ "conversionProgress": {
+ "message": "रूपांतरण प्रगति में हे।"
+ },
+ "copiedButton": {
+ "message": "कॉपी किया गया"
+ },
+ "copiedClipboard": {
+ "message": "क्लिपबोर्ड पर कॉपी किया गया"
+ },
+ "copiedExclamation": {
+ "message": "कॉपी कर दिया गया!"
+ },
+ "copiedSafe": {
+ "message": "मैंने इसे कहीं सुरक्षित कॉपी कर दिया है"
+ },
+ "copy": {
+ "message": "कॉपी / प्रतिलिपि कर्रे"
+ },
+ "copyToClipboard": {
+ "message": "क्लिपबोर्ड पर कॉपी करें"
+ },
+ "copyButton": {
+ "message": " कॉपी / प्रतिलिपि कर्रे [बत्तन] "
+ },
+ "copyPrivateKey": {
+ "message": "यह आपकी निजी कुंजी है (कॉपी करने के लिए क्लिक करें)।"
+ },
+ "create": {
+ "message": "बनाएं"
+ },
+ "createAccount": {
+ "message": "खाता बनाएं"
+ },
+ "createDen": {
+ "message": "डेन बनाएं"
+ },
+ "crypto": {
+ "message": "क्रिप्टो",
+ "description": "एक्सचेंज टाइप (क्रिप्टोक्यूचरस)"
+ },
+ "currentConversion": {
+ "message": "वर्तमान रूपांतरण"
+ },
+ "currentNetwork": {
+ "message": "वर्तमान नेटवर्क"
+ },
+ "customGas": {
+ "message": "अनुकूलित करें गैस"
+ },
+ "customize": {
+ "message": "अनुकूलित करें"
+ },
+ "customRPC": {
+ "message": "कस्टम RPC"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "दशमलव कम से कम 0 होनी चाहिए, और 36 से अधिक नहीं होनी चाहिए।"
+ },
+ "decimal": {
+ "message": "दशमलव परिशुद्धता"
+ },
+ "defaultNetwork": {
+ "message": "ईथर लेनदेन के लिए डिफ़ॉल्ट नेटवर्क मुख्य नेट है।"
+ },
+ "denExplainer": {
+ "message": "आपका डेन मेटामास्क के भीतर आपका पासवर्ड-एन्क्रिप्टेड स्टोरेज है।"
+ },
+ "deposit": {
+ "message": "जमा"
+ },
+ "depositBTC": {
+ "message": "नीचे दिए गए पते पर अपना बीटीसी जमा करें:"
+ },
+ "depositCoin": {
+ "message": "नीचे दिए गए पते पर अपना $1 जमा करें",
+ "description": "उपयोगकर्ता को बताता है कि उन्होंने सिक्का के साथ जमा करने के लिए किस सिक्का का चयन किया है"
+ },
+ "depositEth": {
+ "message": "Eth जमाआर्थ"
+ },
+ "depositEther": {
+ "message": "जमा - Ether"
+ },
+ "depositFiat": {
+ "message": "फिएट के साथ जमा हो"
+ },
+ "depositFromAccount": {
+ "message": "दूसरे खाते से जमा करें"
+ },
+ "depositShapeShift": {
+ "message": "शेपशिप के साथ जमा करें"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "यदि आप अन्य क्रिप्टोकाउंटरज रखते हैं, तो आप सीधे मेटामास्क वॉलेट में ईथर को व्यापार और जमा कर सकते हैं। कोई खाता आवश्यक नहीं है।"
+ },
+ "details": {
+ "message": "संदेश विवरण"
+ },
+ "directDeposit": {
+ "message": "प्रत्यक्ष जमा"
+ },
+ "directDepositEther": {
+ "message": "सीधे ईथर जमा करें"
+ },
+ "directDepositEtherExplainer": {
+ "message": "यदि आपके पास पहले से कुछ ईथर है, तो सीधे जमा द्वारा अपने नए बटुए में ईथर प्राप्त करने का तेज़ तरीका है।"
+ },
+ "done": {
+ "message": "संपन्न"
+ },
+ "downloadStatelogs": {
+ "message": "राज्य लॉग डाउनलोड करें"
+ },
+ "edit": {
+ "message": "संपादित करें"
+ },
+ "editAccountName": {
+ "message": "खाता नाम संपादित करें"
+ },
+ "emailUs": {
+ "message": "हमें ईमेल करें!"
+ },
+ "encryptNewDen": {
+ "message": "अपना नया डेन एन्क्रिप्ट करें"
+ },
+ "enterPassword": {
+ "message": "पासवर्ड दर्ज करें"
+ },
+ "enterPasswordConfirm": {
+ "message": "पुष्टि करने के लिए अपना पासवर्ड दर्ज करें"
+ },
+ "etherscanView": {
+ "message": "ईथरस्कैन पर खाता देखें"
+ },
+ "exchangeRate": {
+ "message": "विनिमय दरै"
+ },
+ "exportPrivateKey": {
+ "message": "निजी कुंजी निर्यात करें"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "अपने जोखिम पर निजी कुंजी निर्यात करें।"
+ },
+ "failed": {
+ "message": "विफल"
+ },
+ "fiat": {
+ "message": "FIAT एक्सचेंज टाइप",
+ "description": "एक्सचेंज FIAT टाइप"
+ },
+ "fileImportFail": {
+ "message": "फ़ाइल आयात काम नहीं कर रहा है? यहां क्लिक करें!",
+ "description": "यूजर को अपने खाते को जे.एस.ौ.एन फ़ाइल से आयात करने में मदद करता है"
+ },
+ "followTwitter": {
+ "message": "हमें ट्विटर पर अनुसरण करें"
+ },
+ "from": {
+ "message": "की तरफ से - संदेश"
+ },
+ "fromToSame": {
+ "message": "से और पता करने के लिए समान नहीं हो सकता"
+ },
+ "fromShapeShift": {
+ "message": "सेशशशफ्ट का"
+ },
+ "gas": {
+ "message": "गैस की लागत",
+ "description": "गैस की लागत का संक्षिप्त संकेत"
+ },
+ "gasFee": {
+ "message": "गैस शुल्क"
+ },
+ "gasLimit": {
+ "message": "गैस सीमा"
+ },
+ "gasLimitCalculation": {
+ "message": "हम नेटवर्क की सफलता दर के आधार पर सुझाई गई गैस सीमा की गणना करते हैं।"
+ },
+ "gasLimitRequired": {
+ "message": "गैस सीमा आवश्यक"
+ },
+ "gasLimitTooLow": {
+ "message": "२१००० - गैस की सीमा कम से कम 21000 होनी चाहिए"
+ },
+ "generatingSeed": {
+ "message": "सृजन बीज उत्पन्न ... उत्पन्न ...उत्पन्न..."
+ },
+ "gasPrice": {
+ "message": "गैस मूल्य (जीडब्ल्यूईआई),(GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "हम नेटवर्क की सफलता दर के आधार पर सुझाए गए गैस की कीमतों की गणना करते हैं।"
+ },
+ "gasPriceRequired": {
+ "message": "गैस की कीमत आवश्यक है"
+ },
+ "getEther": {
+ "message": "ईथर प्राप्त करें"
+ },
+ "getEtherFromFaucet": {
+ "message": "$1 के लिए एक नल से ईथर प्राप्त करें",
+ "description": "ईथर नल के लिए नेटवर्क नाम प्रदर्शित करता है"
+ },
+ "greaterThanMin": {
+ "message": "$1 के बराबर या बराबर होना चाहिए।",
+ "description": "हेक्स इनपुट के लिए दशमलव इनपुट के रूप में सहायक"
+ },
+ "here": {
+ "message": "यहां",
+ "description": "अधिक जानकारी के लिए यहां क्लिक करें- (परेशानी के साथ जाता है टोकनबैलेंस) (troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "यहां एक सूची है !!!!"
+ },
+ "hide": {
+ "message": "छुपाएं"
+ },
+ "hideToken": {
+ "message": "टोकन छिपाएं"
+ },
+ "hideTokenPrompt": {
+ "message": "टोकन छिपाएंn?"
+ },
+ "howToDeposit": {
+ "message": "आप ईथर जमा कैसे करना चाहेंगे?"
+ },
+ "holdEther": {
+ "message": "यह आपको आकाश और टोकन रखने की अनुमति देता है, और विकेंद्रीकृत अनुप्रयोगों के लिए आपके पुल के रूप में कार्य करता है।"
+ },
+ "import": {
+ "message": "आयात",
+ "description": "एक चयनित फ़ाइल से एक खाता आयात करने के लिए बटन "
+ },
+ "importAccount": {
+ "message": "खाता आयात"
+ },
+ "importAccountMsg": {
+ "message": "आयात किए गए खाते आपके मूल रूप से बनाए गए मेटामास्क अकाउंट सीडफ्रेज से संबद्ध नहीं होंगे। आयात किए गए खातों के बारे में और जानें"
+ },
+ "importAnAccount": {
+ "message": "खाता आयात करैॉ"
+ },
+ "importDen": {
+ "message": "मौजूदा डेन आयात करें - DEN"
+ },
+ "imported": {
+ "message": "आयातित",
+ "description": "यह स्थिति दिखाती है कि कोई खाता पूरी तरह से कीरिंग में लोड हो चुका है"
+ },
+ "infoHelp": {
+ "message": "जानकारी और सहायता"
+ },
+ "insufficientFunds": {
+ "message": "अपर्याप्त धन"
+ },
+ "insufficientTokens": {
+ "message": "अपर्याप्त टोकन।"
+ },
+ "invalidAddress": {
+ "message": "अमान्य पता"
+ },
+ "invalidAddressRecipient": {
+ "message": "प्राप्तकर्ता का पता अमान्य है"
+ },
+ "invalidGasParams": {
+ "message": "अमान्य गैस पैरामीटर"
+ },
+ "invalidInput": {
+ "message": "अमान्य इनपुट।"
+ },
+ "invalidRequest": {
+ "message": "अमान्य अनुरोध"
+ },
+ "invalidRPC": {
+ "message": "अमान्य RPC कै URI"
+ },
+ "jsonFail": {
+ "message": "कुछ गलत हो गया। कृपया सुनिश्चित करें कि आपकी JSON फ़ाइल ठीक से फ़ॉर्मेट की गई है।"
+ },
+ "jsonFile": {
+ "message": "JSON फ़ाइल",
+ "description": "एक खाता आयात करने के लिए प्रारूप"
+ },
+ "kovan": {
+ "message": "कोवान टेस्ट नेटवर्क"
+ },
+ "knowledgeDataBase": {
+ "message": "हमारे नॉलेज बेस पर जाएं"
+ },
+ "lessThanMax": {
+ "message": "$1 से कम या बराबर होना चाहिए।",
+ "description": "हेक्स इनपुट के लिए दशमलव इनपुट के रूप में सहायक"
+ },
+ "likeToAddTokens": {
+ "message": "क्या आप इन टोकनों को जोड़ना चाहते हैं?"
+ },
+ "limit": {
+ "message": "सीमा"
+ },
+ "loading": {
+ "message": "लोड हो रहा है ....."
+ },
+ "loadingTokens": {
+ "message": "टोकन लोड हो रहा है ....."
+ },
+ "localhost": {
+ "message": "स्थानीयहोस्ट 8545"
+ },
+ "login": {
+ "message": "लॉग इन करें"
+ },
+ "logout": {
+ "message": "लॉग आउट करें"
+ },
+ "loose": {
+ "message": "ढीला"
+ },
+ "loweCaseWords": {
+ "message": "बीज शब्द में केवल लोअरकेस वर्ण होते हैं"
+ },
+ "mainnet": {
+ "message": "मुख्य ईथरम नेटवर्क"
+ },
+ "message": {
+ "message": "संदेश"
+ },
+ "metamaskDescription": {
+ "message": "मेटामास्क एथर्मम के लिए एक सुरक्षित पहचान वॉल्ट है।"
+ },
+ "min": {
+ "message": "न्यूनतम"
+ },
+ "myAccounts": {
+ "message": "मेरे खाते"
+ },
+ "mustSelectOne": {
+ "message": "कम से कम 1 टोकन का चयन करना आवश्यक है।"
+ },
+ "needEtherInWallet": {
+ "message": "मेटामास्क का उपयोग करने वाले विकेन्द्रीकृत अनुप्रयोगों के साथ बातचीत करने के लिए, आपको अपने वॉलेट में ईथर की आवश्यकता होगी।"
+ },
+ "needImportFile": {
+ "message": "आयात करने के लिए आपको एक फ़ाइल का चयन करना होगा।",
+ "description": "प्रयोक्ता महत्वपूर्ण खाता है और उसे जारी रखने के लिए एक फ़ाइल जोड़ने की आवश्यकता है"
+ },
+ "needImportPassword": {
+ "message": "चयनित फ़ाइल के लिए आपको एक पासवर्ड दर्ज करना होगा।",
+ "description": "पासवर्ड और फाइल को एक खाते आयात करने के लिए आवश्यक है"
+ },
+ "negativeETH": {
+ "message": "ईटीएच की नकारात्मक मात्रा नहीं भेज सकते हैं।."
+ },
+ "networks": {
+ "message": "नेटवर्क"
+ },
+ "newAccount": {
+ "message": "नया खाता"
+ },
+ "newAccountNumberName": {
+ "message": "नया खाता $1",
+ "description": "खाते का खाता बनाने पर अगले खाते का डिफ़ॉल्ट नाम"
+ },
+ "newContract": {
+ "message": "नया अनुबंध"
+ },
+ "newPassword": {
+ "message": "नया पासवर्ड (न्यूनतम 8 वर्ण)"
+ },
+ "newRecipient": {
+ "message": "नया प्राप्तकर्ता"
+ },
+ "newRPC": {
+ "message": "नया RPC URL"
+ },
+ "next": {
+ "message": "अगला"
+ },
+ "noAddressForName": {
+ "message": "इस नाम के लिए कोई पता सेट नहीं किया गया है।"
+ },
+ "noDeposits": {
+ "message": "कोई जमा प्राप्त नहीं हुई"
+ },
+ "noTransactionHistory": {
+ "message": "कोई लेनदेन इतिहास नहीं ..."
+ },
+ "noTransactions": {
+ "message": "कोई लेन-देन नहीं"
+ },
+ "notStarted": {
+ "message": "प्रारंभ नहीं किया गया"
+ },
+ "oldUI": {
+ "message": "पुराना UI"
+ },
+ "oldUIMessage": {
+ "message": "आप पुराने UI पर वापस आ गए हैं। आप ऊपर दाईं ओर ड्रॉपडाउन मेनू में विकल्प के माध्यम से नए UI पर वापस स्विच कर सकते हैं।"
+ },
+ "or": {
+ "message": "या",
+ "description": "एक नया खाता बनाने या आयात करने के बीच की पसंद"
+ },
+ "passwordCorrect": {
+ "message": "कृपया सुनिश्चित करें कि आपका पासवर्ड सही है।"
+ },
+ "passwordMismatch": {
+ "message": "पासवर्ड मेल नहीं खाते",
+ "description": "पासवर्ड निर्माण प्रक्रिया में, दो नए पासवर्ड फ़ील्ड मेल नहीं खाते"
+ },
+ "passwordShort": {
+ "message": "पासवर्ड पर्याप्त लंबा नहीं",
+ "description": "पासवर्ड बनाने की प्रक्रिया में, पासवर्ड सुरक्षित होने के लिए पर्याप्त नहीं है"
+ },
+ "pastePrivateKey": {
+ "message": "यहां अपनी निजी कुंजी स्ट्रिंग चिपकाएं:",
+ "description": "किसी निजी कुंजी से किसी खाते को आयात करने के लिए"
+ },
+ "pasteSeed": {
+ "message": "यहां अपने बीज वाक्यांश पेस्ट करें!"
+ },
+ "personalAddressDetected": {
+ "message": "व्यक्तिगत पता मिला। टोकन अनुबंध का पता इनपुट।"
+ },
+ "pleaseReviewTransaction": {
+ "message": "कृपया अपने लेनदेन की समीक्षा करें।"
+ },
+ "privacyMsg": {
+ "message": "गोपनीयता नीति"
+ },
+ "privateKey": {
+ "message": "निजी कुंजी",
+ "description": "खाता आयात करने के लिए उपयोग करने के लिए इस प्रकार की फ़ाइल का चयन करें"
+ },
+ "privateKeyWarning": {
+ "message": "चेतावनी: कभी भी इस कुंजी का खुलासा न करें। आपकी निजी कुंजी वाले कोई भी आपके खाते में रखी किसी भी संपत्ति को चुरा सकता है।"
+ },
+ "privateNetwork": {
+ "message": "निजी नेटवर्क"
+ },
+ "qrCode": {
+ "message": "QR कोड दिखाएं"
+ },
+ "readdToken": {
+ "message": "आप अपने खाता विकल्प मेनू में .टोकन जोड़ें. पर जाकर भविष्य में इस टोकन को वापस जोड़ सकते हैं।"
+ },
+ "readMore": {
+ "message": "यहां और पढ़ें।"
+ },
+ "readMore2": {
+ "message": "हां - और पढ़ें।"
+ },
+ "receive": {
+ "message": "प्राप्त करें"
+ },
+ "recipientAddress": {
+ "message": "प्राप्तकर्ता पता"
+ },
+ "refundAddress": {
+ "message": "आपका रिफंड पता"
+ },
+ "rejected": {
+ "message": "अस्वीकृत"
+ },
+ "resetAccount": {
+ "message": "खाता रीसेट करें"
+ },
+ "restoreFromSeed": {
+ "message": "बीज वाक्यांश से पुनर्स्थापित करें"
+ },
+ "required": {
+ "message": "आवश्यक"
+ },
+ "retryWithMoreGas": {
+ "message": "यहां एक उच्च गैस कीमत के साथ पुन: प्रयास करें"
+ },
+ "revealSeedWords": {
+ "message": "बीज शब्द प्रकट करें"
+ },
+ "revealSeedWordsWarning": {
+ "message": "किसी सार्वजनिक स्थान पर अपने बीज के शब्द ठीक नहीं करें! ये शब्द आपके सभी खातों को चोरी करने के लिए उपयोग किए जा सकते हैं।"
+ },
+ "revert": {
+ "message": "वापस"
+ },
+ "rinkeby": {
+ "message": "रिचीव टेस्ट नेटवर्क"
+ },
+ "ropsten": {
+ "message": "रॉप्स्टेन टेस्ट नेटवर्क"
+ },
+ "sampleAccountName": {
+ "message": "उदाहरण के लिए मेरा नया खाता",
+ "description": "उपयोगकर्ता को अपने खाते में मानव-पठनीय नाम जोड़ने की अवधारणा को समझें."
+ },
+ "save": {
+ "message": "सहेजें"
+ },
+ "saveAsFile": {
+ "message": "फ़ाइल के रूप में सहेजें",
+ "description": "खाता निर्यात प्रक्रिया"
+ },
+ "saveSeedAsFile": {
+ "message": "सेड वर्ड्स - फाईल्स सेव करें"
+ },
+ "search": {
+ "message": "खोज करें"
+ },
+ "secretPhrase": {
+ "message": "अपनी गुप्त बारह शब्द वाक्यांश यहाँ अपनी तिजोरी को पुनर्स्थापित करने के लिए दर्ज करें।"
+ },
+ "seedPhraseReq": {
+ "message": "बीज वाक्यांश 12 शब्द लंबा हैं"
+ },
+ "select": {
+ "message": "चुनें"
+ },
+ "selectCurrency": {
+ "message": "मुद्रा चुनें"
+ },
+ "selectService": {
+ "message": "सेवा चुनें"
+ },
+ "selectType": {
+ "message": "प्रकार चुनें"
+ },
+ "send": {
+ "message": "भेजें"
+ },
+ "sendETH": {
+ "message": "भेजें ETH"
+ },
+ "sendTokens": {
+ "message": "भेजें टोकन"
+ },
+ "sendTokensAnywhere": {
+ "message": "इटोरम खाते वाले किसी को भी टोकन भेजें"
+ },
+ "settings": {
+ "message": "सेटिंग्स"
+ },
+ "shapeshiftBuy": {
+ "message": "शेपेशिस्ट के साथ खरीदें"
+ },
+ "showPrivateKeys": {
+ "message": "निजी कुंजी दिखाएँ"
+ },
+ "showQRCode": {
+ "message": "QR कोड दिखाएं"
+ },
+ "sign": {
+ "message": "हस्ताक्षर"
+ },
+ "signMessage": {
+ "message": "हस्ताक्षर संदेश"
+ },
+ "signNotice": {
+ "message": "इस संदेश पर हस्ताक्षर करने से \n साइड इफेक्ट हो सकते हैं। \n केवल अपने पूरे खाते के साथ पूरी तरह से भरोसेमंद \n साइटों से संदेश पर हस्ताक्षर करें। \n यह खतरनाक विधि भविष्य के संस्करण में निकाल दी जाएगी।"
+ },
+ "sigRequest": {
+ "message": "हस्ताक्षर अनुरोध"
+ },
+ "sigRequested": {
+ "message": "हस्ताक्षर अनुरोधित"
+ },
+ "spaceBetween": {
+ "message": "केवल शब्दों के बीच एक स्थान हो सकता है"
+ },
+ "status": {
+ "message": "स्थिति"
+ },
+ "stateLogs": {
+ "message": "स्थिति संदेश"
+ },
+ "stateLogsDescription": {
+ "message": "स्थिति संदेश में आपका सार्वजनिक खाता, पतों और भेजे गए लेनदेन, होते हैं।"
+ },
+ "submit": {
+ "message": "सबमिट करें"
+ },
+ "supportCenter": {
+ "message": "हमारे सहायता केंद्र पर जाएं"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "प्रतीक 0 और 10 अक्षरों के बीच होना चाहिए"
+ },
+ "takesTooLong": {
+ "message": "बहुत समय ले रहा है?"
+ },
+ "terms": {
+ "message": "उपयोग की शर्तें"
+ },
+ "testFaucet": {
+ "message": "टेस्ट नलि"
+ },
+ "to": {
+ "message": "के लिए"
+ },
+ "toETHviaShapeShift": {
+ "message": "शैपशैफ्ट द्वारा $1 से ETH में जमा",
+ "description": "संदेश की शुरुआत में जमा प्रकार भर जाएगा"
+ },
+ "tokenAddress": {
+ "message": "टोकन पता"
+ },
+ "tokenAlreadyAdded": {
+ "message": "टोकन पहले ही जोड़ा जा चुका है।"
+ },
+ "tokenBalance": {
+ "message": "आपका टोकन बैलेंस है:"
+ },
+ "tokenSelection": {
+ "message": "टोकन के लिए खोजें या हमारी लोकप्रिय टॉकेन्स की सूची से चुनें।"
+ },
+ "tokenSymbol": {
+ "message": "टोकन प्रतीक"
+ },
+ "tokenWarning1": {
+ "message": "अपने मेटामास्क अकाउंट से खरीदे गए टोकनों का नज़र रखें। अगर आप किसी अलग खाते का उपयोग कर टोकन खरीदे हैं, तो ये टोकन यहां दिखाई नहीं देंगे।"
+ },
+ "total": {
+ "message": "कुल"
+ },
+ "transactions": {
+ "message": "लेनदेन"
+ },
+ "transactionMemo": {
+ "message": "लेनदेन मेमो (वैकल्पिक)"
+ },
+ "transactionNumber": {
+ "message": "लेनदेन संख्या"
+ },
+ "transfers": {
+ "message": "स्थानांतरण"
+ },
+ "troubleTokenBalances": {
+ "message": "मुसीबत... आपके टोकन शेष राशि को लोड करने में हमें परेशानी हुई थी। आप उन्हें देख सकते हैं",
+ "description": "टोकन शेष देखने के लिए एक लिंक ... (यहां)"
+ },
+ "twelveWords": {
+ "message": "ये 12 शब्द आपके मेटामास्क खातों को पुनर्स्थापित करने का एकमात्र तरीका है। \n उन्हें कहीं सुरक्षित और गुप्त रूप से सहेजें।"
+ },
+ "typePassword": {
+ "message": "अपना पासवर्ड टाइप करें"
+ },
+ "uiWelcome": {
+ "message": "नया यूआई (बीटा) में आपका स्वागत है"
+ },
+ "uiWelcomeMessage": {
+ "message": "आप अब नए मेटामास्क UI का उपयोग कर रहे हैं। चारों ओर एक नज़र डालें, टोकन भेजने की तरह नई सुविधाएं देखें, और हमें बताएं कि आपके पास कोई समस्या है।"
+ },
+ "unavailable": {
+ "message": "अनुपलब्ध"
+ },
+ "unknown": {
+ "message": "अज्ञात नेटवर्क"
+ },
+ "unknownNetwork": {
+ "message": "अज्ञात निजी नेटवर्क"
+ },
+ "unknownNetworkId": {
+ "message": "अज्ञात नेटवर्क आईडी.य़"
+ },
+ "uriErrorMsg": {
+ "message": "URI-यूआरआई को उपयुक्त HTTP / HTTPS उपसर्ग की आवश्यकता होती है।"
+ },
+ "usaOnly": {
+ "message": "केवल यूएसए - USA",
+ "description": "इस एक्सचेंज का उपयोग करना संयुक्त राज्य अमेरिका के अंदर ही सीमित है"
+ },
+ "usedByClients": {
+ "message": "विभिन्न क्लाइंट्स द्वारा उपयोग किया जाता है"
+ },
+ "useOldUI": {
+ "message": "पुराने UI का उपयोग करें"
+ },
+ "validFileImport": {
+ "message": "आयात करने के लिए आपको एक वैध फ़ाइल चुननी होगी।"
+ },
+ "vaultCreated": {
+ "message": "वॉल्ट बनाया गया"
+ },
+ "viewAccount": {
+ "message": "खाता देखें"
+ },
+ "visitWebSite": {
+ "message": "हमारी वेब साइट पर जाएं"
+ },
+ "warning": {
+ "message": "चेतावनी"
+ },
+ "welcomeBeta": {
+ "message": "मेटामास्क बीटा में आपका स्वागत है"
+ },
+ "whatsThis": {
+ "message": "यह क्या है?"
+ },
+ "yourSigRequested": {
+ "message": "आपका हस्ताक्षर अनुरोध किया जा रहा है"
+ },
+ "youSign": {
+ "message": "आप हस्ताक्षर कर रहे हैं"
+ }
+}
diff --git a/app/_locales/index.json b/app/_locales/index.json
new file mode 100644
index 000000000..c085deb72
--- /dev/null
+++ b/app/_locales/index.json
@@ -0,0 +1,19 @@
+[
+ { "code": "de", "name": "German" },
+ { "code": "en", "name": "English" },
+ { "code": "es", "name": "Spanish" },
+ { "code": "fr", "name": "French" },
+ { "code": "hn", "name": "Hindi" },
+ { "code": "it", "name": "Italian" },
+ { "code": "ja", "name": "Japanese" },
+ { "code": "ko", "name": "Korean" },
+ { "code": "nl", "name": "Dutch" },
+ { "code": "ph", "name": "Tagalog" },
+ { "code": "pt", "name": "Portuguese" },
+ { "code": "ru", "name": "Russian" },
+ { "code": "sl", "name": "Slovenian" },
+ { "code": "th", "name": "Thai" },
+ { "code": "vi", "name": "Vietnamese" },
+ { "code": "zh_CN", "name": "Mandarin" },
+ { "code": "zh_TW", "name": "Taiwanese" }
+]
diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json
index f54ef98ca..ef3ed4025 100644
--- a/app/_locales/it/messages.json
+++ b/app/_locales/it/messages.json
@@ -223,7 +223,7 @@
"done": {
"message": "Finito"
},
- "downloadStatelogs": {
+ "downloadStateLogs": {
"message": "Scarica i log di Stato"
},
"edit": {
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index bab8d3b95..d9762a3e9 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -14,6 +14,9 @@
"address": {
"message": "アドレス"
},
+ "addCustomToken": {
+ "message": "カスタムトークンを追加"
+ },
"addToken": {
"message": "トークンを追加"
},
@@ -63,11 +66,14 @@
"message": "Coinbaseで購入"
},
"buyCoinbaseExplainer": {
- "message": "Coinbaseは、世界で最もポピュラーなBitcoin、Ethereum、そしてLitecoinの取引所です。"
+ "message": "Coinbaseは、世界的なBitcoin、Ethereum、そしてLitecoinの取引所です。"
},
"cancel": {
"message": "キャンセル"
},
+ "classicInterface": {
+ "message": "旧インタフェイスを使用"
+ },
"clickCopy": {
"message": "クリックしてコピー"
},
@@ -126,6 +132,9 @@
"message": "暗号通貨",
"description": "Exchange type (cryptocurrencies)"
},
+ "currentConversion": {
+ "message": "基軸通貨"
+ },
"customGas": {
"message": "ガスのカスタマイズ"
},
@@ -135,14 +144,17 @@
"customRPC": {
"message": "カスタムRPC"
},
+ "decimal": {
+ "message": "小数点桁数"
+ },
"defaultNetwork": {
- "message": "Etherトランザクションのデフォルトのネットワークはメインネットです。"
+ "message": "デフォルトのEther送受信ネットワークはメインネットです。"
},
"denExplainer": {
"message": "DENとは、あなたのパスワードが暗号化されたMetaMask内のストレージです。"
},
"deposit": {
- "message": "デポジット"
+ "message": "受取り"
},
"depositBTC": {
"message": "あなたのBTCを次のアドレスへデポジット:"
@@ -161,13 +173,13 @@
"message": "法定通貨でデポジット"
},
"depositFromAccount": {
- "message": "別のアカウントからデポジット"
+ "message": "別のアカウントから入金"
},
"depositShapeShift": {
- "message": "ShapeShiftでデポジット"
+ "message": "ShapeShiftで入金"
},
"depositShapeShiftExplainer": {
- "message": "あなたが他の暗号通貨を持っているなら、Etherにトレードしてダイレクトにメタマスクウォレットへのデポジットが可能です。アカウント作成は不要。"
+ "message": "他の暗号通貨をEtherと交換してMetaMaskのウォレットへ入金できます。アカウント作成は不要です。"
},
"details": {
"message": "詳細"
@@ -176,10 +188,10 @@
"message": "ダイレクトデポジット"
},
"directDepositEther": {
- "message": "Etherをダイレクトデポジット"
+ "message": "Etherを直接受け取り"
},
"directDepositEtherExplainer": {
- "message": "あなたがEtherをすでにお持ちなら、ダイレクトデポジットは新しいウォレットにEtherを入手する最も迅速な方法です。"
+ "message": "Etherをすでにお持ちなら、MetaMaskの新しいウォレットにEtherを送信することができます。"
},
"done": {
"message": "完了"
@@ -197,7 +209,7 @@
"message": "パスワードを入力"
},
"etherscanView": {
- "message": "Etherscanでアカウントを見る"
+ "message": "Etherscanでアカウントを参照"
},
"exchangeRate": {
"message": "交換レート"
@@ -257,7 +269,7 @@
"message": "Etherをゲット"
},
"getEtherFromFaucet": {
- "message": "フォーセットで $1のEtherをゲット",
+ "message": "フォーセットで $1のEtherを得ることができます。",
"description": "Displays network name for Ether faucet"
},
"greaterThanMin": {
@@ -281,12 +293,15 @@
"message": "どのようにEtherをデポジットしますか?"
},
"import": {
- "message": "インポート",
+ "message": "追加",
"description": "Button to import an account from a selected file"
},
"importAccount": {
"message": "アカウントのインポート"
},
+ "importAccountMsg": {
+ "message":"追加したアカウントはMetaMaskのアカウントシードフレーズとは関連付けられません。インポートしたアカウントについての詳細は"
+ },
"importAnAccount": {
"message": "アカウントをインポート"
},
@@ -298,7 +313,10 @@
"description": "status showing that an account has been fully loaded into the keyring"
},
"infoHelp": {
- "message": "インフォメーションとヘルプ"
+ "message": "情報とヘルプ"
+ },
+ "insufficientFunds": {
+ "message": "残高不足"
},
"invalidAddress": {
"message": "アドレスが無効です。"
@@ -354,7 +372,7 @@
"message": "マイアカウント"
},
"needEtherInWallet": {
- "message": "MetaMaskを使って分散型アプリケーションと対話するためには、あなたのウォレットにEtherが必要になります。"
+ "message": "MetaMaskを使って分散型アプリケーションを使用するためには、このウォレットにEtherが必要です。"
},
"needImportFile": {
"message": "インポートするファイルを選択してください。",
@@ -383,6 +401,9 @@
"newRecipient": {
"message": "新規受取人"
},
+ "newRPC": {
+ "message": "新しいRPCのURLを追加"
+ },
"next": {
"message": "次へ"
},
@@ -460,12 +481,21 @@
"rejected": {
"message": "拒否されました"
},
+ "resetAccount": {
+ "message": "アカウントをリセット"
+ },
+ "restoreFromSeed": {
+ "message": "パスフレーズから復元する"
+ },
"required": {
"message": "必要です。"
},
"retryWithMoreGas": {
"message": "より高いガスプライスで再度試して下さい。"
},
+ "revealSeedWords": {
+ "message": "パスフレーズを表示"
+ },
"revert": {
"message": "元に戻す"
},
@@ -495,8 +525,11 @@
"sendTokens": {
"message": "トークンを送る"
},
+ "onlySendToEtherAddress": {
+ "message": "ETHはイーサリウムアカウントのみに送信できます。"
+ },
"sendTokensAnywhere": {
- "message": "イーサリアムのアカウントを持っている人にトークンを送る"
+ "message": "イーサリアムアカウントを持っている人にトークンを送る"
},
"settings": {
"message": "設定"
@@ -544,9 +577,21 @@
"message": "ShapeShiftで $1をETHにする",
"description": "system will fill in deposit type in start of message"
},
+ "tokenAddress": {
+ "message": "トークンアドレス"
+ },
"tokenBalance": {
"message": "あなたのトークン残高:"
},
+ "tokenSelection": {
+ "message": "トークンを検索、またはリストから選択してください。"
+ },
+ "tokenSymbol": {
+ "message": "トークンシンボル"
+ },
+ "tokenWarning1": {
+ "message": "MetaMaskのアカウントで取得したアカウントのみ追加できます。他のアカウントを使用して取得したトークンは、カスタムトークンを使用してください。"
+ },
"total": {
"message": "合計"
},
@@ -591,6 +636,9 @@
"usedByClients": {
"message": "様々なクライアントによって使用されています。"
},
+ "useOldUI": {
+ "message": "旧UIに切り替え"
+ },
"viewAccount": {
"message": "アカウントを見る"
},
diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json
index c9eb178f9..e770392d0 100644
--- a/app/_locales/pt/messages.json
+++ b/app/_locales/pt/messages.json
@@ -223,7 +223,7 @@
"done": {
"message": "Finalizado"
},
- "downloadStatelogs": {
+ "downloadStateLogs": {
"message": "Descarregar Registos de Estado"
},
"edit": {
diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json
new file mode 100644
index 000000000..e3a1935f5
--- /dev/null
+++ b/app/_locales/ru/messages.json
@@ -0,0 +1,819 @@
+{
+ "accept": {
+ "message": "Принять"
+ },
+ "account": {
+ "message": "Аккаунт"
+ },
+ "accountDetails": {
+ "message": "Детали Аккаунта"
+ },
+ "accountName": {
+ "message": "Имя Пользователя"
+ },
+ "address": {
+ "message": "Адрес"
+ },
+ "addCustomToken": {
+ "message": "Добавить пользовательский токен"
+ },
+ "addToken": {
+ "message": "Добавить токен"
+ },
+ "addTokens": {
+ "message": "Добавить Токены"
+ },
+ "amount": {
+ "message": "Количество"
+ },
+ "amountPlusGas": {
+ "message": "Количество + газ"
+ },
+ "appDescription": {
+ "message": "Расширение браузера для Ethereum",
+ "description": "The description of the application"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "The name of the application"
+ },
+ "attemptingConnect": {
+ "message": "Попытка подключиться к блокчейн сети."
+ },
+ "attributions": {
+ "message": "Опознания"
+ },
+ "available": {
+ "message": "Доступный"
+ },
+ "back": {
+ "message": "Назад"
+ },
+ "balance": {
+ "message": "Баланс:"
+ },
+ "balances": {
+ "message": "Ваши балансы"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Недостаточный баланс для текущего объема газа"
+ },
+ "beta": {
+ "message": "БЕТА"
+ },
+ "betweenMinAndMax": {
+ "message": "должно быть больше или равно $1 и меньше или равно $2.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "blockiesIdenticon": {
+ "message": "Использовать Blockies Identicon"
+ },
+ "borrowDharma": {
+ "message": "Заимствовать с Dharma (бета)"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask спроектирован и построен в Калифорнии."
+ },
+ "buy": {
+ "message": "Купить"
+ },
+ "buyCoinbase": {
+ "message": "Купить на Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase - самый популярный в мире способ купить и продать биткойн, ethereum и litecoin."
+ },
+ "cancel": {
+ "message": "Отмена"
+ },
+ "classicInterface": {
+ "message": "Использовать классический интерфейс"
+ },
+ "clickCopy": {
+ "message": "Нажмите, чтобы скопировать"
+ },
+ "confirm": {
+ "message": "Подтвердить"
+ },
+ "confirmContract": {
+ "message": "Подтвердить Контракт"
+ },
+ "confirmPassword": {
+ "message": "Подтвердите Пароль"
+ },
+ "confirmTransaction": {
+ "message": "Подтвердить Транзакцию"
+ },
+ "continue": {
+ "message": "Продолжить"
+ },
+ "continueToCoinbase": {
+ "message": "Продолжить в Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Развертывание контракта"
+ },
+ "conversionProgress": {
+ "message": "Выполняется конверсия"
+ },
+ "copiedButton": {
+ "message": "Скопировано"
+ },
+ "copiedClipboard": {
+ "message": "Скопировано в буфер обмена"
+ },
+ "copiedExclamation": {
+ "message": "Скопировано!"
+ },
+ "copiedSafe": {
+ "message": "Я скопировал его где-то в безопасности"
+ },
+ "copy": {
+ "message": "Скопировать"
+ },
+ "copyToClipboard": {
+ "message": "Скопировать в буфер обмена"
+ },
+ "copyButton": {
+ "message": " Скопировать "
+ },
+ "copyPrivateKey": {
+ "message": "Это ваш личный ключ (нажмите, чтобы скопировать)"
+ },
+ "create": {
+ "message": "Создать"
+ },
+ "createAccount": {
+ "message": "Регистрация"
+ },
+ "createDen": {
+ "message": "Создать"
+ },
+ "crypto": {
+ "message": "Крипто",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "currentConversion": {
+ "message": "Текущая конверсия"
+ },
+ "currentNetwork": {
+ "message": "Текущая сеть"
+ },
+ "customGas": {
+ "message": "Настроить Газ"
+ },
+ "customize": {
+ "message": "Настроить"
+ },
+ "customRPC": {
+ "message": "Пользовательский RPC"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "Десятичные числа должны быть не менее 0, и не более 36."
+ },
+ "decimal": {
+ "message": "Десятичные значения точности"
+ },
+ "defaultNetwork": {
+ "message": "Сеть по умолчанию для транзакций Ether - это Main Net."
+ },
+ "denExplainer": {
+ "message": "Ваш DEN - это ваше зашифрованное паролем хранилище в MetaMask."
+ },
+ "deposit": {
+ "message": "Депозит"
+ },
+ "depositBTC": {
+ "message": "Депозит BTC по адресу:"
+ },
+ "depositCoin": {
+ "message": "Депозит $1 по указанному ниже адресу",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "Депозит Eth"
+ },
+ "depositEther": {
+ "message": "Депозит Эфир"
+ },
+ "depositFiat": {
+ "message": "Депозит с деньгами"
+ },
+ "depositFromAccount": {
+ "message": "Депозит с другого счета"
+ },
+ "depositShapeShift": {
+ "message": "Депозит с ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Если у вас есть другие крипторесурсы, вы можете торговать и вносить Эфир непосредственно в кошелек MetaMask. Нет необходимости в аккаунте."
+ },
+ "details": {
+ "message": "Детали"
+ },
+ "directDeposit": {
+ "message": "Прямой Депозит"
+ },
+ "directDepositEther": {
+ "message": "Прямой Депозит Эфира"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Если у вас уже есть Эфир, самый быстрый способ получить Эфир в вашем новом кошельке это прямым депозитом."
+ },
+ "done": {
+ "message": "Готово"
+ },
+ "downloadStatelogs": {
+ "message": "Загрузить логи статус"
+ },
+ "edit": {
+ "message": "Редактировать"
+ },
+ "editAccountName": {
+ "message": "Изменить Имя Аккаунта"
+ },
+ "emailUs": {
+ "message": "Свяжитесь с нами по электронной почте!"
+ },
+ "encryptNewDen": {
+ "message": "Шифруйте новый DEN"
+ },
+ "enterPassword": {
+ "message": "Введите пароль"
+ },
+ "enterPasswordConfirm": {
+ "message": "Введите свой пароль для подтверждения"
+ },
+ "etherscanView": {
+ "message": "Просмотреть аккаунт на Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Обменный Курс"
+ },
+ "exportPrivateKey": {
+ "message": "Экспорт закрытого ключа"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Экспорт секретных ключей на свой страх и риск."
+ },
+ "failed": {
+ "message": "Не смогли"
+ },
+ "fiat": {
+ "message": "Бумажные деньги",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "Ошибка импорта файлов? Кликните сюда!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "followTwitter": {
+ "message": "Следуйте за нами на Twitter"
+ },
+ "from": {
+ "message": "Из"
+ },
+ "fromToSame": {
+ "message": "От и до адреса не могут быть одинаковым"
+ },
+ "fromShapeShift": {
+ "message": "Из ShapeShift"
+ },
+ "gas": {
+ "message": "Газ",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "Плата за Газ"
+ },
+ "gasLimit": {
+ "message": "Газовый Предел"
+ },
+ "gasLimitCalculation": {
+ "message": "Мы рассчитываем предполагаемый предел газа на основе коэффициентов успешности сети."
+ },
+ "gasLimitRequired": {
+ "message": "Требуется ограничение на Газ"
+ },
+ "gasLimitTooLow": {
+ "message": "Предел газа должен быть не менее 21000"
+ },
+ "generatingSeed": {
+ "message": "Создание Семян ..."
+ },
+ "gasPrice": {
+ "message": "Цена на Газ (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Мы вычисляем предлагаемые цены на газ на основе коэффициентов успеха сети."
+ },
+ "gasPriceRequired": {
+ "message": "Требуется цена на Газ"
+ },
+ "getEther": {
+ "message": "Получить Эфир"
+ },
+ "getEtherFromFaucet": {
+ "message": "Получите Эфир из крана $1",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "должно быть больше или равно $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "здесь",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "Вот список !!!!"
+ },
+ "hide": {
+ "message": "Спрятать"
+ },
+ "hideToken": {
+ "message": "Скрыть токен"
+ },
+ "hideTokenPrompt": {
+ "message": "Скрыть токен?"
+ },
+ "howToDeposit": {
+ "message": "Как бы вы хотели поместить Эфир?"
+ },
+ "holdEther": {
+ "message": "Это позволяет вам использовать эфир и токены и служит мостом для децентрализованных приложений."
+ },
+ "import": {
+ "message": "Импортировать",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "Импорт Аккаунта"
+ },
+ "importAccountMsg": {
+ "message": " Импортированные аккаунты не будут связаны с вашей первоначально созданным аккаунтом MetaMask. Подробнее о импортированных аккаунтах "
+ },
+ "importAnAccount": {
+ "message": "Импортировать аккаунт"
+ },
+ "importDen": {
+ "message": "Импорт существующих DEN"
+ },
+ "imported": {
+ "message": "Импортирован",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "Информация и Помощь"
+ },
+ "insufficientFunds": {
+ "message": "Недостаточно средств."
+ },
+ "insufficientTokens": {
+ "message": "Недостаточно токенов."
+ },
+ "invalidAddress": {
+ "message": "Недействительный адрес"
+ },
+ "invalidAddressRecipient": {
+ "message": "Недопустимый адрес получателя."
+ },
+ "invalidGasParams": {
+ "message": "Недопустимые параметры Газа"
+ },
+ "invalidInput": {
+ "message": "Неправильный ввод."
+ },
+ "invalidRequest": {
+ "message": "Неверный Запрос"
+ },
+ "invalidRPC": {
+ "message": "Недопустимый URI RPC"
+ },
+ "jsonFail": {
+ "message": "Что-то пошло не так. Убедитесь, что ваш файл JSON правильно отформатирован."
+ },
+ "jsonFile": {
+ "message": "Файл JSON",
+ "description": "format for importing an account"
+ },
+ "kovan": {
+ "message": "Kovan тестовая сеть"
+ },
+ "knowledgeDataBase": {
+ "message": "Посетите нашу базу знаний"
+ },
+ "lessThanMax": {
+ "message": "должно быть меньше или равно $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "likeToAddTokens": {
+ "message": "Вы хотите добавить эти токены?"
+ },
+ "limit": {
+ "message": "Предел"
+ },
+ "loading": {
+ "message": "Загрузка..."
+ },
+ "loadingTokens": {
+ "message": "Загрузка токенов ..."
+ },
+ "localhost": {
+ "message": "Локальный адрес 8545"
+ },
+ "login": {
+ "message": "Авторизоваться"
+ },
+ "logout": {
+ "message": "Выйти"
+ },
+ "loose": {
+ "message": "Рыхлый"
+ },
+ "loweCaseWords": {
+ "message": "семенные слова имеют только символы нижнего регистра"
+ },
+ "mainnet": {
+ "message": "Основная сеть Ethereum"
+ },
+ "message": {
+ "message": "Сообщение"
+ },
+ "metamaskDescription": {
+ "message": "MetaMask - это безопасное хранилище для Ethereum."
+ },
+ "min": {
+ "message": "Минимум"
+ },
+ "myAccounts": {
+ "message": "Мои Аккаунты"
+ },
+ "mustSelectOne": {
+ "message": "Необходимо выбрать не менее 1 токена."
+ },
+ "needEtherInWallet": {
+ "message": "Чтобы взаимодействовать с децентрализованными приложениями с помощью MetaMask, вам понадобится Эфир в вашем кошельке."
+ },
+ "needImportFile": {
+ "message": "Вы должны выбрать файл для импорта.",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "Вы должны ввести пароль для выбранного файла.",
+ "description": "Password and file needed to import an account"
+ },
+ "negativeETH": {
+ "message": "Невозможно отправить отрицательные количества ETH."
+ },
+ "networks": {
+ "message": "Сети"
+ },
+ "newAccount": {
+ "message": "Новый Аккаунт"
+ },
+ "newAccountNumberName": {
+ "message": "Аккаунт $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "Новый Контракт"
+ },
+ "newPassword": {
+ "message": "Новый пароль (мин. 8 символов)"
+ },
+ "newRecipient": {
+ "message": "Новый Получатель"
+ },
+ "newRPC": {
+ "message": "Новый URL-адрес RPC"
+ },
+ "next": {
+ "message": "Далее"
+ },
+ "noAddressForName": {
+ "message": "Для этого имени не задан адрес."
+ },
+ "noDeposits": {
+ "message": "Не было получено никаких депозитов"
+ },
+ "noTransactionHistory": {
+ "message": "Нет истории транзакций."
+ },
+ "noTransactions": {
+ "message": "Нет Транзакций"
+ },
+ "notStarted": {
+ "message": "Не Начался"
+ },
+ "oldUI": {
+ "message": "Старый Интерфейс"
+ },
+ "oldUIMessage": {
+ "message": "Вы вернулись к старому интерфейсу. Вы можете вернуться к новому с помощью опции в раскрывающемся меню в правом верхнем углу."
+ },
+ "or": {
+ "message": "или",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordCorrect": {
+ "message": "Убедитесь, что ваш пароль правильный."
+ },
+ "passwordMismatch": {
+ "message": "пароли не совпадают",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "пароль недостаточно длинный",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "Вставьте свою личную строку:",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "Вставьте здесь свою семенную фразу!"
+ },
+ "personalAddressDetected": {
+ "message": "Персональный адрес обнаружен. Введите адрес контракта токена."
+ },
+ "pleaseReviewTransaction": {
+ "message": "Проверьте транзакцию."
+ },
+ "privacyMsg": {
+ "message": "Политика Конфиденциальности"
+ },
+ "privateKey": {
+ "message": "Закрытый ключ",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": "Предупреждение: никогда не раскрывайте этот ключ. Любой, у кого есть ваши личные ключи, может украсть любые активы, хранящиеся в вашем аккаунте."
+ },
+ "privateNetwork": {
+ "message": "Частная сеть"
+ },
+ "qrCode": {
+ "message": "Показать QR-код"
+ },
+ "readdToken": {
+ "message": "Вы можете добавить этот токен в будущем, перейдя в “Добавить токен” в меню параметров вашего аккаунта."
+ },
+ "readMore": {
+ "message": "Подробнее читайте здесь."
+ },
+ "readMore2": {
+ "message": "Прочитайте больше."
+ },
+ "receive": {
+ "message": "Получить"
+ },
+ "recipientAddress": {
+ "message": "Адрес Получателя"
+ },
+ "refundAddress": {
+ "message": "Ваш Адрес Возврата"
+ },
+ "rejected": {
+ "message": "Отклонено"
+ },
+ "resetAccount": {
+ "message": "Сбросить аккаунт"
+ },
+ "restoreFromSeed": {
+ "message": "Восстановить от семенной фразы"
+ },
+ "required": {
+ "message": "Необходимо"
+ },
+ "retryWithMoreGas": {
+ "message": "Повторите попытку с более высокой ценой на газ здесь"
+ },
+ "revealSeedWords": {
+ "message": "Раскрыть семенные слова"
+ },
+ "revealSeedWordsWarning": {
+ "message": "Не восстанавливайте семенные слова в общественном месте! Эти слова могут использоваться для кражи всех ваших аккаунтах."
+ },
+ "revert": {
+ "message": "Откат"
+ },
+ "rinkeby": {
+ "message": "Rinkeby тестовая сеть"
+ },
+ "ropsten": {
+ "message": "Ropsten тестовая сеть"
+ },
+ "sampleAccountName": {
+ "message": "Например, Мой новый аккаунт",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "Сохранить"
+ },
+ "saveAsFile": {
+ "message": "Сохранить как Файл",
+ "description": "Account export process"
+ },
+ "saveSeedAsFile": {
+ "message": "Сохранить Семенные Слова Как Файл"
+ },
+ "search": {
+ "message": "Поиск"
+ },
+ "secretPhrase": {
+ "message": "Введите свою секретную двенадцатисловную фразу здесь, чтобы восстановить хранилище."
+ },
+ "seedPhraseReq": {
+ "message": "семенные фразы длиной 12 слов"
+ },
+ "select": {
+ "message": "Выбрать"
+ },
+ "selectCurrency": {
+ "message": "Выберите Валюту"
+ },
+ "selectService": {
+ "message": "Выберите Сервис"
+ },
+ "selectType": {
+ "message": "Выберите Тип"
+ },
+ "send": {
+ "message": "Послать"
+ },
+ "sendETH": {
+ "message": "Отправить ETH"
+ },
+ "sendTokens": {
+ "message": "Отправить Токены"
+ },
+ "sendTokensAnywhere": {
+ "message": "Отправить Токены кому-либо с аккаунтом Ethereum"
+ },
+ "settings": {
+ "message": "Настройки"
+ },
+ "shapeshiftBuy": {
+ "message": "Купить с помощью Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Показать приватные ключи"
+ },
+ "showQRCode": {
+ "message": "Показать QR-код"
+ },
+ "sign": {
+ "message": "Знак"
+ },
+ "signMessage": {
+ "message": "Нодписать сообщение"
+ },
+ "signNotice": {
+ "message": "Подписание этого сообщения может иметь \nопасные побочные эффекты. Только подписывайте сообщения \nс сайтов, которым вы полностью доверяете своим аккаунтом. Этот опасный метод будет удален в будущей версии."
+ },
+ "sigRequest": {
+ "message": "Запрос на подпись"
+ },
+ "sigRequested": {
+ "message": "Подпись Запрошена"
+ },
+ "spaceBetween": {
+ "message": "между словами может быть только пробел"
+ },
+ "status": {
+ "message": "Статус"
+ },
+ "stateLogs": {
+ "message": "Логи Статуса"
+ },
+ "stateLogsDescription": {
+ "message": "Логи статуса содержат ваши общедоступные адреса и отправленные транзакции."
+ },
+ "submit": {
+ "message": "Отправить"
+ },
+ "supportCenter": {
+ "message": "Посетите наш Центр поддержки"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "Символ должен быть от 0 до 10 символов."
+ },
+ "takesTooLong": {
+ "message": "Занимает слишком долго?"
+ },
+ "terms": {
+ "message": "Условия Эксплуатации"
+ },
+ "testFaucet": {
+ "message": "Тестовый Кран"
+ },
+ "to": {
+ "message": "К"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 в ETH через ShapeShift",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenAddress": {
+ "message": "Адрес Токена"
+ },
+ "tokenAlreadyAdded": {
+ "message": "Токен уже добавлен."
+ },
+ "tokenBalance": {
+ "message": "Баланс Вашых Tокенов:"
+ },
+ "tokenSelection": {
+ "message": "Поиск токенов или выбор из нашего списка популярных токенов."
+ },
+ "tokenSymbol": {
+ "message": "Символ Токена"
+ },
+ "tokenWarning1": {
+ "message": "Следите за токенами, которые вы купили с помощью аккаунта MetaMask. Если вы купили токены, используя другой аккаунт, эти токены здесь не появятся."
+ },
+ "total": {
+ "message": "Всего"
+ },
+ "transactions": {
+ "message": "транзакции"
+ },
+ "transactionMemo": {
+ "message": "Транзакционная записка (необязательно)"
+ },
+ "transactionNumber": {
+ "message": "Номер Транзакции"
+ },
+ "transfers": {
+ "message": "Переводы"
+ },
+ "troubleTokenBalances": {
+ "message": "У нас были проблемы с загрузкой ваших токенов. Вы можете просмотреть их ",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "twelveWords": {
+ "message": "Эти 12 слов - единственный способ восстановить ваши учетные записи MetaMask.\nСохраните их где-нибудь в безопасности и в тайне."
+ },
+ "typePassword": {
+ "message": "Введите Пароль"
+ },
+ "uiWelcome": {
+ "message": "Добро пожаловать в новый интерфейс (бета-версия)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Теперь вы используете новый интерфейс Metamask. Осмотритесь, попробуйте новые функции, такие как отправку токенов, и сообщите нам, есть ли у вас какие-либо проблемы."
+ },
+ "unavailable": {
+ "message": "Недоступен"
+ },
+ "unknown": {
+ "message": "Неизвестный"
+ },
+ "unknownNetwork": {
+ "message": "Неизвестная частная сеть"
+ },
+ "unknownNetworkId": {
+ "message": "Неизвестный идентификатор сети"
+ },
+ "uriErrorMsg": {
+ "message": "Для URI требуется соответствующий префикс HTTP / HTTPS."
+ },
+ "usaOnly": {
+ "message": "Только США",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "Используется различными клиентами"
+ },
+ "useOldUI": {
+ "message": "Использовать старый интерфейс"
+ },
+ "validFileImport": {
+ "message": "Вы должны выбрать действительный файл для импорта."
+ },
+ "vaultCreated": {
+ "message": "Создано хранилище"
+ },
+ "viewAccount": {
+ "message": "Посмотреть аккаунт"
+ },
+ "visitWebSite": {
+ "message": "Посетите наш сайт"
+ },
+ "warning": {
+ "message": "Предупреждение"
+ },
+ "welcomeBeta": {
+ "message": "Добро пожаловать в MetaMask Beta"
+ },
+ "whatsThis": {
+ "message": "Что это?"
+ },
+ "yourSigRequested": {
+ "message": "Ваша подпись запрашивается"
+ },
+ "youSign": {
+ "message": "Вы подписываете"
+ }
+}
diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json
new file mode 100644
index 000000000..0532f11b2
--- /dev/null
+++ b/app/_locales/sl/messages.json
@@ -0,0 +1,819 @@
+{
+ "accept": {
+ "message": "Sprejmi"
+ },
+ "account": {
+ "message": "Račun"
+ },
+ "accountDetails": {
+ "message": "Podrobnosti računa"
+ },
+ "accountName": {
+ "message": "Ime računa"
+ },
+ "address": {
+ "message": "Naslov"
+ },
+ "addCustomToken": {
+ "message": "Dodaj žeton po meri"
+ },
+ "addToken": {
+ "message": "Dodaj žeton"
+ },
+ "addTokens": {
+ "message": "Dodaj žetone"
+ },
+ "amount": {
+ "message": "Znesek"
+ },
+ "amountPlusGas": {
+ "message": "Znesek + Gas"
+ },
+ "appDescription": {
+ "message": "Denarnica za Ethereum v brskalniku",
+ "description": "The description of the application"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "The name of the application"
+ },
+ "attemptingConnect": {
+ "message": "Povezovanje z verigo blokov ..."
+ },
+ "attributions": {
+ "message": "Dodelitve"
+ },
+ "available": {
+ "message": "Na voljo"
+ },
+ "back": {
+ "message": "Nazaj"
+ },
+ "balance": {
+ "message": "Znesek:"
+ },
+ "balances": {
+ "message": "Vaš znesek"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Napačen znesek za skupno gas vrednost"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "mora biti večji ali enak $1 in manjši ali enak $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "blockiesIdenticon": {
+ "message": "Uporabite Blockies Identicon"
+ },
+ "borrowDharma": {
+ "message": "Izposoja z Dharma (Beta)"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask je ustvarjen v Kaliforniji."
+ },
+ "buy": {
+ "message": "Kupi"
+ },
+ "buyCoinbase": {
+ "message": "Kupi na Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase je najpopularnejši načun za kupovanje in prodajo bitcoinov, ethereuma, in litecoina."
+ },
+ "cancel": {
+ "message": "Prekliči"
+ },
+ "classicInterface": {
+ "message": "Uporabi navaden način"
+ },
+ "clickCopy": {
+ "message": "Kliknite za kopiranje"
+ },
+ "confirm": {
+ "message": "Potrdi"
+ },
+ "confirmContract": {
+ "message": "Potrdi pogodbo"
+ },
+ "confirmPassword": {
+ "message": "Potrdi geslo"
+ },
+ "confirmTransaction": {
+ "message": "Potrdi transakcijo"
+ },
+ "continue": {
+ "message": "Nadaljuj"
+ },
+ "continueToCoinbase": {
+ "message": "Nadaljuj na Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Ustvarjanje pogodbe"
+ },
+ "conversionProgress": {
+ "message": "Poteka pretvorba"
+ },
+ "copiedButton": {
+ "message": "Kopirano"
+ },
+ "copiedClipboard": {
+ "message": "Kopirano v odložišče"
+ },
+ "copiedExclamation": {
+ "message": "Kopirano!"
+ },
+ "copiedSafe": {
+ "message": "Prilepil sem ga na varno!"
+ },
+ "copy": {
+ "message": "Kopiraj"
+ },
+ "copyToClipboard": {
+ "message": "Kopiraj v odložišče"
+ },
+ "copyButton": {
+ "message": " Kopiraj "
+ },
+ "copyPrivateKey": {
+ "message": "To je vaš zesebni ključ (kliknite za kopiranje)"
+ },
+ "create": {
+ "message": "Ustvari"
+ },
+ "createAccount": {
+ "message": "Ustvari račun"
+ },
+ "createDen": {
+ "message": "Ustvari"
+ },
+ "crypto": {
+ "message": "Kripto",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "currentConversion": {
+ "message": "Trenutna cena"
+ },
+ "currentNetwork": {
+ "message": "Trenutno omrežje"
+ },
+ "customGas": {
+ "message": "Prilagodi gas"
+ },
+ "customize": {
+ "message": "Prilagodi"
+ },
+ "customRPC": {
+ "message": "Poljuben RPC"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "Decimalk mora biti vsaj 0, in ne več kot 36."
+ },
+ "decimal": {
+ "message": "Decimalke natančnosti"
+ },
+ "defaultNetwork": {
+ "message": "Privzeto omrežje za transakcije je Main Net."
+ },
+ "denExplainer": {
+ "message": "DEN je vaša šifrirana shramba v MetaMasku."
+ },
+ "deposit": {
+ "message": "Vplačilo"
+ },
+ "depositBTC": {
+ "message": "Vplačajte vaš BTC na spodnji naslov:"
+ },
+ "depositCoin": {
+ "message": "Vplačajte $1 na spodnji naslov",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "Vplačilo ETH"
+ },
+ "depositEther": {
+ "message": "Vplačilo ethera"
+ },
+ "depositFiat": {
+ "message": "Vplačilo s klasičnimi valutami"
+ },
+ "depositFromAccount": {
+ "message": "Vplačilo iz drugega računa"
+ },
+ "depositShapeShift": {
+ "message": "Vplačilo z ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Če imate druge kriptovalute, lahko vpačate ether neposredno v MetaMask. Brez računov."
+ },
+ "details": {
+ "message": "Podrobnosti"
+ },
+ "directDeposit": {
+ "message": "Direktno vplačilo"
+ },
+ "directDepositEther": {
+ "message": "Direktno vplačilo ehera"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Če že imate ether, ga lahko najhitreje dobite v MetaMask z direktnim vplačilom."
+ },
+ "done": {
+ "message": "Končano"
+ },
+ "downloadStatelogs": {
+ "message": "Prenesi state dnevnike"
+ },
+ "edit": {
+ "message": "Uredi"
+ },
+ "editAccountName": {
+ "message": "Uredi ime računa"
+ },
+ "emailUs": {
+ "message": "Pišite nam!"
+ },
+ "encryptNewDen": {
+ "message": "Šifrirajte DEN"
+ },
+ "enterPassword": {
+ "message": "Vpišite geslo"
+ },
+ "enterPasswordConfirm": {
+ "message": "Potrdite geslo"
+ },
+ "etherscanView": {
+ "message": "Poglejte račun na Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Menjalni tečaj"
+ },
+ "exportPrivateKey": {
+ "message": "Izvozi zasebni ključ"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Izvažanje zasebnih ključev je na lastno odgovornost."
+ },
+ "failed": {
+ "message": "Ni uspelo"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "Uvoz z datoteko ni uspel? Kliknite tukaj!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "followTwitter": {
+ "message": "Sledite nam na Twitterju"
+ },
+ "from": {
+ "message": "Od"
+ },
+ "fromToSame": {
+ "message": "From and To address cannot be the same"
+ },
+ "fromShapeShift": {
+ "message": "Od ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "Gas fee"
+ },
+ "gasLimit": {
+ "message": "Gas limit"
+ },
+ "gasLimitCalculation": {
+ "message": "Priporočen gas limit je izračunan glede na omrežje."
+ },
+ "gasLimitRequired": {
+ "message": "Gas limit je zahtevan"
+ },
+ "gasLimitTooLow": {
+ "message": "Gas limit mora biti najmanj 21000"
+ },
+ "generatingSeed": {
+ "message": "Ustvarjenje seed ..."
+ },
+ "gasPrice": {
+ "message": "Gas price (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Priporočen gas price je izračunan glede na omrežje"
+ },
+ "gasPriceRequired": {
+ "message": "Gas price je zahtevan"
+ },
+ "getEther": {
+ "message": "Pridobite ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Pridobite ether iz fauceta za $1",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "mora biti višji ali enak $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "tukaj",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "Tukaj je seznam!!!"
+ },
+ "hide": {
+ "message": "Skrij"
+ },
+ "hideToken": {
+ "message": "Skrij žeton"
+ },
+ "hideTokenPrompt": {
+ "message": "Skrijem žeton?"
+ },
+ "howToDeposit": {
+ "message": "Kako želite vplačati ether?"
+ },
+ "holdEther": {
+ "message": "Omogoča vam, da imate eter in žetone in služi kot most za decentralizirane aplikacije."
+ },
+ "import": {
+ "message": "Uvozi",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "Uvozi račun"
+ },
+ "importAccountMsg": {
+ "message":" Uvoženi računi ne bodo povezani s prvotnim seedphaseom. Preberite več o uvoženih računih "
+ },
+ "importAnAccount": {
+ "message": "Uvozi račun"
+ },
+ "importDen": {
+ "message": "Uvozi DEN"
+ },
+ "imported": {
+ "message": "Uvoženo",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "Info & Pomoč"
+ },
+ "insufficientFunds": {
+ "message": "Nezadostna sredstva."
+ },
+ "insufficientTokens": {
+ "message": "Nezadostni žetoni."
+ },
+ "invalidAddress": {
+ "message": "Nepravilen naslov"
+ },
+ "invalidAddressRecipient": {
+ "message": "Prejemnikov naslov je neveljaven"
+ },
+ "invalidGasParams": {
+ "message": "Nepravilno nastavljen gas"
+ },
+ "invalidInput": {
+ "message": "Napačen vnos."
+ },
+ "invalidRequest": {
+ "message": "Napačna zahteva"
+ },
+ "invalidRPC": {
+ "message": "Napačen RPC URI"
+ },
+ "jsonFail": {
+ "message": "Nekaj je bilo narobe. Prepričajte se, da je JSON datoteka pravilno oblikovana."
+ },
+ "jsonFile": {
+ "message": "JSON datoteka",
+ "description": "format for importing an account"
+ },
+ "kovan": {
+ "message": "Testno omrežje Kovan"
+ },
+ "knowledgeDataBase": {
+ "message": "Obiščite našo pomoč"
+ },
+ "lessThanMax": {
+ "message": "mora biti večji ali enak $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "likeToAddTokens": {
+ "message": "Želite dodati te žetone?"
+ },
+ "limit": {
+ "message": "Omejitev"
+ },
+ "loading": {
+ "message": "Nalaganje ..."
+ },
+ "loadingTokens": {
+ "message": "Nalaganje žetonov ..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "Prijava"
+ },
+ "logout": {
+ "message": "Odjava"
+ },
+ "loose": {
+ "message": "Loose"
+ },
+ "loweCaseWords": {
+ "message": "seed words imajo lahko le male črke"
+ },
+ "mainnet": {
+ "message": "Glavno omrežje"
+ },
+ "message": {
+ "message": "Sporočilo"
+ },
+ "metamaskDescription": {
+ "message": "MetaMask je varen identitetni sklad za Ethereum."
+ },
+ "min": {
+ "message": "Najmanj"
+ },
+ "myAccounts": {
+ "message": "Moji računi"
+ },
+ "mustSelectOne": {
+ "message": "Izbran mora biti vsaj 1 žeton."
+ },
+ "needEtherInWallet": {
+ "message": "Za interakcijo z decentraliziranimi aplikacijami, ki uporabljajo MetaMask, boste v svoji denarnici potrebovali eter."
+ },
+ "needImportFile": {
+ "message": "Za uvoz morate izbrati datoteko.",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "Za izbrano datoteko morate vnesti geslo.",
+ "description": "Password and file needed to import an account"
+ },
+ "negativeETH": {
+ "message": "Ni mogoče poslati negativne vsote ETH."
+ },
+ "networks": {
+ "message": "Omrežja"
+ },
+ "newAccount": {
+ "message": "Nov račun"
+ },
+ "newAccountNumberName": {
+ "message": "Račun $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "Nova pogodba"
+ },
+ "newPassword": {
+ "message": "Novo geslo (min. 8. črk)"
+ },
+ "newRecipient": {
+ "message": "Nov prejemnik"
+ },
+ "newRPC": {
+ "message": "Nov RPC URL"
+ },
+ "next": {
+ "message": "Naprej"
+ },
+ "noAddressForName": {
+ "message": "Za to ime ni bil nastavljen noben naslov."
+ },
+ "noDeposits": {
+ "message": "Ni prejetih vplačil"
+ },
+ "noTransactionHistory": {
+ "message": "Ni zgodovine transakcij."
+ },
+ "noTransactions": {
+ "message": "Ni transakcij"
+ },
+ "notStarted": {
+ "message": "Ni se začelo"
+ },
+ "oldUI": {
+ "message": "Starejši uporabniški vmesnik"
+ },
+ "oldUIMessage": {
+ "message": "Vrnili ste se v starejši uporabniški vmesnik. V novega se lahko vrnete z možnostjo v spustnem meniju v zgornjem desnem kotu."
+ },
+ "or": {
+ "message": "ali",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordCorrect": {
+ "message": "Prepričajte se, da je geslo pravilno."
+ },
+ "passwordMismatch": {
+ "message": "gesli se ne ujemata",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "geslo ni dovolj dolgo",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "Tukaj prilepite zasebni ključ:",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "Tukaj prilepite seed phrase!"
+ },
+ "personalAddressDetected": {
+ "message": "Osebni naslov je zaznan. Vnesite naslov žetona."
+ },
+ "pleaseReviewTransaction": {
+ "message": "Preglejte transakcijo."
+ },
+ "privacyMsg": {
+ "message": "Politika zasebnosti"
+ },
+ "privateKey": {
+ "message": "Zasebni ključ",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": "Opozorilo: Nikoli ne razkrijte tega ključa. Vsakdo s svojimi zasebnimi ključi lahko ukrade vse premoženje v računu."
+ },
+ "privateNetwork": {
+ "message": "Zasebno omrežje"
+ },
+ "qrCode": {
+ "message": "Prikaži QR kodo"
+ },
+ "readdToken": {
+ "message": "Ta žeton lahko dodate tudi v prihodnosti, tako da odprete možnost »Dodaj žeton« v meniju z računi."
+ },
+ "readMore": {
+ "message": "Preberite več."
+ },
+ "readMore2": {
+ "message": "Preberite več."
+ },
+ "receive": {
+ "message": "Prejmite"
+ },
+ "recipientAddress": {
+ "message": "Prejemnikov naslov"
+ },
+ "refundAddress": {
+ "message": "Vaš naslov za vračilo"
+ },
+ "rejected": {
+ "message": "Zavrnjeno"
+ },
+ "resetAccount": {
+ "message": "Ponastavi račun"
+ },
+ "restoreFromSeed": {
+ "message": "Obnovi iz seed phrase"
+ },
+ "required": {
+ "message": "Zahtevano"
+ },
+ "retryWithMoreGas": {
+ "message": "Poskusi z višjim gas price"
+ },
+ "revealSeedWords": {
+ "message": "Prikaži seed words"
+ },
+ "revealSeedWordsWarning": {
+ "message": "Ne obnovite seed words na javnem mestu! Te besede se lahko uporabijo za krajo vseh vaših računov."
+ },
+ "revert": {
+ "message": "Povrni"
+ },
+ "rinkeby": {
+ "message": "Testno omrežje Rinkeby"
+ },
+ "ropsten": {
+ "message": "Testno omrežje Ropsten"
+ },
+ "sampleAccountName": {
+ "message": "npr. Moj nov račun",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "Shrani"
+ },
+ "saveAsFile": {
+ "message": "Shrani kot datoteko",
+ "description": "Account export process"
+ },
+ "saveSeedAsFile": {
+ "message": "Shrani seed words kot datoteko"
+ },
+ "search": {
+ "message": "Iskanje"
+ },
+ "secretPhrase": {
+ "message": "Tukaj vnesite svoje seed words, da obnovite svoje račune."
+ },
+ "seedPhraseReq": {
+ "message": "seed phrases so dolgi 12 besed"
+ },
+ "select": {
+ "message": "Izberi"
+ },
+ "selectCurrency": {
+ "message": "Izberi valuto"
+ },
+ "selectService": {
+ "message": "Izberi storitev"
+ },
+ "selectType": {
+ "message": "Izberi vrsto"
+ },
+ "send": {
+ "message": "Pošlji"
+ },
+ "sendETH": {
+ "message": "Pošlji ETH"
+ },
+ "sendTokens": {
+ "message": "Pošlji žetone"
+ },
+ "sendTokensAnywhere": {
+ "message": "Pošljite žetone vsem, ki imajo Ethereum račun"
+ },
+ "settings": {
+ "message": "Nastavitve"
+ },
+ "shapeshiftBuy": {
+ "message": "Kupite z Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Prikaži zasebne ključe"
+ },
+ "showQRCode": {
+ "message": "Prikaži QR kodo"
+ },
+ "sign": {
+ "message": "Podpiši"
+ },
+ "signMessage": {
+ "message": "Podpiši sporočilo"
+ },
+ "signNotice": {
+ "message": "To podpisovanje lahko povzroči \nnevarne stranske učinke. Podpisujte samo sporočila \nstrani, ki jim zaupate s svojim celotnim računom.\n Ta nevarna funkcija bo odstranjena v prihodnji različici. "
+ },
+ "sigRequest": {
+ "message": "Zahteva za podpis"
+ },
+ "sigRequested": {
+ "message": "Podpis je zahtevan"
+ },
+ "spaceBetween": {
+ "message": "med besedami je lahko samo presledek"
+ },
+ "status": {
+ "message": "Status"
+ },
+ "stateLogs": {
+ "message": "State dnevniki"
+ },
+ "stateLogsDescription": {
+ "message": "State dnevniki vsebujejo naslove vašega računa in poslane transakcije.."
+ },
+ "submit": {
+ "message": "Potrdi"
+ },
+ "supportCenter": {
+ "message": "Obiščite našo podporo"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "Simbol mora biti dolg od 0 do 10 znakov."
+ },
+ "takesTooLong": {
+ "message": "Traja predolgo?"
+ },
+ "terms": {
+ "message": "Pogoji uporabe"
+ },
+ "testFaucet": {
+ "message": "Testni faucet"
+ },
+ "to": {
+ "message": "Za"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 v ETH prek ShapeShift",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenAddress": {
+ "message": "Naslov žetona"
+ },
+ "tokenAlreadyAdded": {
+ "message": "Žeton je že bil dodan."
+ },
+ "tokenBalance": {
+ "message": "Vaš znesek žetona:"
+ },
+ "tokenSelection": {
+ "message": "Poiščite žetone ali jih izberite z našega seznama priljubljenih žetonov."
+ },
+ "tokenSymbol": {
+ "message": "Simbol žetona"
+ },
+ "tokenWarning1": {
+ "message": "Spremljajte žetone, ki ste jih kupili s svojim MetaMask računom. Če ste kupili žetone z drugačnim računom, ti žetoni ne bodo prikazani tukaj."
+ },
+ "total": {
+ "message": "Skupno"
+ },
+ "transactions": {
+ "message": "transakcije"
+ },
+ "transactionMemo": {
+ "message": "Opis transakcije (ni zahtevano)"
+ },
+ "transactionNumber": {
+ "message": "Številka transakcije"
+ },
+ "transfers": {
+ "message": "Prenosi"
+ },
+ "troubleTokenBalances": {
+ "message": "Imeli smo težave pri nalaganju vaših žetonov. Ogledate si jih lahko ",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "twelveWords": {
+ "message": "Edini način za obnovitev MetaMask računa, je teh 12 besed.\nShranite jih na varno in skrivno mesto."
+ },
+ "typePassword": {
+ "message": "Vpišite vaše geslo"
+ },
+ "uiWelcome": {
+ "message": "Dobrodošli v novem uporabniškem vmesniku (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Zdaj uporabljate novi MetaMask uporabniški vmesnik. Razglejte se, preizkusite nove funkcije, kot so pošiljanje žetonov, in nas obvestite, če imate kakšne težave."
+ },
+ "unavailable": {
+ "message": "Ni na voljo"
+ },
+ "unknown": {
+ "message": "Neznano"
+ },
+ "unknownNetwork": {
+ "message": "Neznano zasebno omrežje"
+ },
+ "unknownNetworkId": {
+ "message": "Neznan ID omrežja"
+ },
+ "uriErrorMsg": {
+ "message": "URI-ji zahtevajo ustrezno HTTP/HTTPS predpono."
+ },
+ "usaOnly": {
+ "message": "Samo za ZDA",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "Uporablja jih več različnih odjemalcev"
+ },
+ "useOldUI": {
+ "message": "Uporabi star uporabniški vmesnik"
+ },
+ "validFileImport": {
+ "message": "Za uvoz morate izbrati pravilno datoteko."
+ },
+ "vaultCreated": {
+ "message": "Račun je ustvarjen"
+ },
+ "viewAccount": {
+ "message": "Poglej račun"
+ },
+ "visitWebSite": {
+ "message": "Obiščite našo spletno stran"
+ },
+ "warning": {
+ "message": "Opozorilo"
+ },
+ "welcomeBeta": {
+ "message": "Dobrodošli v MetaMask Beta"
+ },
+ "whatsThis": {
+ "message": "Kaj je to?"
+ },
+ "yourSigRequested": {
+ "message": "Vaš podpis je bil zahtevan"
+ },
+ "youSign": {
+ "message": "Podpisani ste"
+ }
+}
diff --git a/app/_locales/th/messages.json b/app/_locales/th/messages.json
new file mode 100644
index 000000000..887714f3f
--- /dev/null
+++ b/app/_locales/th/messages.json
@@ -0,0 +1,819 @@
+{
+ "accept": {
+ "message": "ยอมรับ"
+ },
+ "account": {
+ "message": "บัญชี"
+ },
+ "accountDetails": {
+ "message": "รายละเอียดบัญชี"
+ },
+ "accountName": {
+ "message": "ชื่อบัญชี"
+ },
+ "address": {
+ "message": "แอดเดรส"
+ },
+ "addCustomToken": {
+ "message": "เพิ่มโทเค็นด้วยตัวเอง"
+ },
+ "addToken": {
+ "message": "เพิ่มโทเค็น"
+ },
+ "addTokens": {
+ "message": "เพิ่มหลายโทเค็น"
+ },
+ "amount": {
+ "message": "จำนวน"
+ },
+ "amountPlusGas": {
+ "message": "จำนวน + แก๊ส"
+ },
+ "appDescription": {
+ "message": "ส่วนขยายเบราว์เซอร์สำหรับอีเธอเรียม",
+ "description": "The description of the application"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "The name of the application"
+ },
+ "attemptingConnect": {
+ "message": "กำลังเชื่อมต่อกับบล็อกเชน"
+ },
+ "attributions": {
+ "message": "อ้างถึง"
+ },
+ "available": {
+ "message": "ว่าง"
+ },
+ "back": {
+ "message": "กลับ"
+ },
+ "balance": {
+ "message": "ยอดคงเหลือ:"
+ },
+ "balances": {
+ "message": "ยอดคงเหลือของคุณ"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "ยอดคงเหลือไม่พอสำหรับจ่ายค่าแก๊สทั้งหมด"
+ },
+ "beta": {
+ "message": "เบต้า"
+ },
+ "betweenMinAndMax": {
+ "message": "ต้องมากกว่าหรือเท่ากับ $1 และน้อยกว่าหรือเท่ากับ $2",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "blockiesIdenticon": {
+ "message": "ใช้งาน Blockies Identicon"
+ },
+ "borrowDharma": {
+ "message": "ยืมด้วย Dharma (เบต้า)"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask ออกแบบและพัฒนาที่แคลิฟอร์เนีย"
+ },
+ "buy": {
+ "message": "ซื้อ"
+ },
+ "buyCoinbase": {
+ "message": "ซื้อด้วย Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase เป็นแหล่งซื้อขายบิตคอยน์ไลท์คอยน์และอีเธอเรียมที่ได้รับความนิยมสูงสุดในโลก"
+ },
+ "cancel": {
+ "message": "ยกเลิก"
+ },
+ "classicInterface": {
+ "message": "ใช้หน้าตาแบบเก่า"
+ },
+ "clickCopy": {
+ "message": "กดเพื่อคัดลอก"
+ },
+ "confirm": {
+ "message": "ยืนยัน"
+ },
+ "confirmContract": {
+ "message": "ยืนยันสัญญา"
+ },
+ "confirmPassword": {
+ "message": "ยืนยันรหัสผ่าน"
+ },
+ "confirmTransaction": {
+ "message": "ยืนยันการทำรายการธุรกรรม"
+ },
+ "continue": {
+ "message": "ทำต่อไป"
+ },
+ "continueToCoinbase": {
+ "message": "ไปที่ Coinbase"
+ },
+ "contractDeployment": {
+ "message": "การติดตั้งสัญญา"
+ },
+ "conversionProgress": {
+ "message": "กำลังดำเนินการแปลงหน่วย"
+ },
+ "copiedButton": {
+ "message": "คัดลอกแล้ว"
+ },
+ "copiedClipboard": {
+ "message": "คัดลอกไปที่คลิบบอร์ดแล้ว"
+ },
+ "copiedExclamation": {
+ "message": "คัดลอกแล้ว!"
+ },
+ "copiedSafe": {
+ "message": "ฉันได้คัดลอกเก็บไว้ในที่ปลอดภัยเรียบร้อยแล้ว"
+ },
+ "copy": {
+ "message": "คัดลอก"
+ },
+ "copyToClipboard": {
+ "message": "คัดลอกไปคลิปบอร์ด"
+ },
+ "copyButton": {
+ "message": " คัดลอก "
+ },
+ "copyPrivateKey": {
+ "message": "นี่คือคีย์ส่วนตัวของคุณ(กดเพื่อคัดลอก)"
+ },
+ "create": {
+ "message": "สร้าง"
+ },
+ "createAccount": {
+ "message": "สร้างบัญชี"
+ },
+ "createDen": {
+ "message": "สร้าง"
+ },
+ "crypto": {
+ "message": "คริปโต",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "currentConversion": {
+ "message": "อัตราแลกเปลี่ยนปัจจุบัน"
+ },
+ "currentNetwork": {
+ "message": "เครือข่ายปัจจุบัน"
+ },
+ "customGas": {
+ "message": "กำหนดค่าแก็สเอง"
+ },
+ "customize": {
+ "message": "กำหนดค่าเอง"
+ },
+ "customRPC": {
+ "message": "กำหนดค่า RPC เอง"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "จำนวนต้องมากกว่า 0 และไม่เกิน 36"
+ },
+ "decimal": {
+ "message": "ตำแหน่งของทศนิยม"
+ },
+ "defaultNetwork": {
+ "message": "ค่าเริ่มต้นของเครือข่ายสำหรับทำรายการธุรกรรมอีเธอร์คือ Main Net"
+ },
+ "denExplainer": {
+ "message": "DEN ของคุณคือตัวเก็บข้อมูลที่เข้ารหัสไว้ด้วยรหัสผ่านของคุณภายใน MetaMask "
+ },
+ "deposit": {
+ "message": "ฝาก"
+ },
+ "depositBTC": {
+ "message": "ฝากบิตคอยน์ของคุณไปที่แอดเดรสด้านล่างนี้:"
+ },
+ "depositCoin": {
+ "message": "ฝาก $1 ของคุณไปที่แอดเดรสด้านล่างนี้:",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "การฝากอีเธอร์"
+ },
+ "depositEther": {
+ "message": "การฝากอีเธอร์"
+ },
+ "depositFiat": {
+ "message": "ฝากด้วยเงินตรา"
+ },
+ "depositFromAccount": {
+ "message": "ฝากจากบัญชีอื่น"
+ },
+ "depositShapeShift": {
+ "message": "ฝากด้วย ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "ถ้ามีเงินสกุลอื่นอยู่ก็สามารถแลกเงินและฝากเป็นอีเธอร์ได้โดยตรงเข้ากระเป๋า MetaMask ได้เลยไม่ต้องสมัครบัญชี"
+ },
+ "details": {
+ "message": "รายละเอียด"
+ },
+ "directDeposit": {
+ "message": "ฝากตรง"
+ },
+ "directDepositEther": {
+ "message": "ฝากอีเธอร์โดยตรง"
+ },
+ "directDepositEtherExplainer": {
+ "message": "ถ้าคุณมีอีเธอร์อยู่แล้ววิธีการที่เร็วที่สุดในการเอาเงินเข้ากระเป๋าใหม่ก็คือการโอนตรงๆ"
+ },
+ "done": {
+ "message": "เสร็จสิ้น"
+ },
+ "downloadStatelogs": {
+ "message": "ดาวน์โหลดล็อกสถานะ"
+ },
+ "edit": {
+ "message": "แก้ไข"
+ },
+ "editAccountName": {
+ "message": "แก้ไขชื่อบัญชี"
+ },
+ "emailUs": {
+ "message": "อีเมลหาเรา!"
+ },
+ "encryptNewDen": {
+ "message": "เข้ารหัส DEN ของคุณ"
+ },
+ "enterPassword": {
+ "message": "ใส่รหัสผ่าน"
+ },
+ "enterPasswordConfirm": {
+ "message": "ใส่รหัสผ่านอีกครั้งเพื่อยืนยัน"
+ },
+ "etherscanView": {
+ "message": "ดูบัญชีบน Etherscan"
+ },
+ "exchangeRate": {
+ "message": "อัตราแลกเปลี่ยน"
+ },
+ "exportPrivateKey": {
+ "message": "ส่งออกคีย์ส่วนตัว"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "ส่งออกคีย์ส่วนตัวโดยคุณรับความเสี่ยงเอง"
+ },
+ "failed": {
+ "message": "ล้มเหลว"
+ },
+ "fiat": {
+ "message": "เงินตรา",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "นำเข้าไฟล์ไม่สำเหร็จ กดที่นี่!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "followTwitter": {
+ "message": "ติดตามเราบนทวิตเตอร์"
+ },
+ "from": {
+ "message": "จาก"
+ },
+ "fromToSame": {
+ "message": "แอดเดรสที่ส่งกับที่รับจะต้องไม่ไช่อันเดียวกัน"
+ },
+ "fromShapeShift": {
+ "message": "จาก ShapeShift"
+ },
+ "gas": {
+ "message": "แก็ส",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "ค่าแก๊ส"
+ },
+ "gasLimit": {
+ "message": "วงเงินแก็ส"
+ },
+ "gasLimitCalculation": {
+ "message": "เราแนะนำวงเงินแก็สตามความสำเร็จบนเครือข่าย"
+ },
+ "gasLimitRequired": {
+ "message": "ต้องกำหนดวงเงินแก็ส"
+ },
+ "gasLimitTooLow": {
+ "message": "วงเงินแก็สต้องอย่างน้อย 21000"
+ },
+ "generatingSeed": {
+ "message": "กำลังสร้างชีด..."
+ },
+ "gasPrice": {
+ "message": "ราคาแก๊ส (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "เราแนะนำราคาแก็สตามความสำเร็จบนเครือข่าย"
+ },
+ "gasPriceRequired": {
+ "message": "ต้องมีราคาแก๊ส"
+ },
+ "getEther": {
+ "message": "รับอีเธอร์"
+ },
+ "getEtherFromFaucet": {
+ "message": "รับอีเธอร์ที่ปล่อยจาก $1",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "ต้องมากกว่าหรือเท่ากับ $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "ที่นี่",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "รายการอยู่ที่นี่!!!!"
+ },
+ "hide": {
+ "message": "ซ่อน"
+ },
+ "hideToken": {
+ "message": "ซ่อนโทเค็น"
+ },
+ "hideTokenPrompt": {
+ "message": "ซ่อนโทเค็นหรือไม่?"
+ },
+ "howToDeposit": {
+ "message": "คุณต้องการฝากอีเธอร์อย่างไร?"
+ },
+ "holdEther": {
+ "message": "ช่วยคุณถืออีเทอร์และโทเค็นและทำหน้าที่เป็นสะพานเชื่อมต่อกับแอพพลิเคชันแบบกระจาย"
+ },
+ "import": {
+ "message": "นำเข้า",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "นำเข้าบัญชี"
+ },
+ "importAccountMsg": {
+ "message":"บัญชีที่นำเข้าจะไม่ถูกรวมกับบัญชีที่สร้างด้วยคำเเริ่มต้นบนเมต้ามาร์สในตอนแรก เรียนรู้เพิ่มเติมเกี่ยวกับบัญชีที่นำเข้า"
+ },
+ "importAnAccount": {
+ "message": "นำเข้าบัญชี"
+ },
+ "importDen": {
+ "message": "นำเข้า DEN ที่มีอยู่แล้ว"
+ },
+ "imported": {
+ "message": "นำเข้าเรียบร้อย",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "ข้อมูลและความช่วยเหลือ"
+ },
+ "insufficientFunds": {
+ "message": "เงินทุนไม่เพียงพอ"
+ },
+ "insufficientTokens": {
+ "message": "โทเค็นไม่เพียงพอ"
+ },
+ "invalidAddress": {
+ "message": "แอดแดรสไม่ถูกต้อง"
+ },
+ "invalidAddressRecipient": {
+ "message": "แอดแดรสผู้รับไม่ถูกต้อง"
+ },
+ "invalidGasParams": {
+ "message": "ตั้งค่าแก๊สไม่ถูกต้อง"
+ },
+ "invalidInput": {
+ "message": "อินพุทไม่ถูกต้อง"
+ },
+ "invalidRequest": {
+ "message": "คำร้องขอไม่ถูกต้อง"
+ },
+ "invalidRPC": {
+ "message": "RPC URI ไม่ถูกต้อง"
+ },
+ "jsonFail": {
+ "message": "เกิดบางอย่างผิดพลาด โปรดตรวจสอบว่าไฟล์ JSON ของคุณมีรูปแบบที่ถูกต้อง."
+ },
+ "jsonFile": {
+ "message": "ไฟล์ JSON",
+ "description": "format for importing an account"
+ },
+ "kovan": {
+ "message": "เครือข่ายทดสอบ Kovan"
+ },
+ "knowledgeDataBase": {
+ "message": "ไปที่คลังความรู้ของเรา"
+ },
+ "lessThanMax": {
+ "message": "ต้องน้อยกว่าหรือเท่ากับ $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "likeToAddTokens": {
+ "message": "คุณต้องการเพิ่มโทเค็นเหล่านี้หรือไม่?"
+ },
+ "limit": {
+ "message": "ข้อจำกัด"
+ },
+ "loading": {
+ "message": "กำลังโหลด..."
+ },
+ "loadingTokens": {
+ "message": "กำลังโหลดโทเค็น..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "เข้าสู่ระบบ"
+ },
+ "logout": {
+ "message": "ออกจากระบบ"
+ },
+ "loose": {
+ "message": "อิสระ"
+ },
+ "loweCaseWords": {
+ "message": "กลุ่มคำชีดมีเพียงตัวพิมพ์เล็กเท่านั้น"
+ },
+ "mainnet": {
+ "message": "เครือข่าย Main Net"
+ },
+ "message": {
+ "message": "ข้อความ"
+ },
+ "metamaskDescription": {
+ "message": "MetaMask คือที่เก็บตัวตนนิรภัยสำหรับอีเธอเรียม"
+ },
+ "min": {
+ "message": "ขั้นต่ำ"
+ },
+ "myAccounts": {
+ "message": "บัญชีของฉัน"
+ },
+ "mustSelectOne": {
+ "message": "ต้องเลือกอย่างน้อย 1 โทเค็น"
+ },
+ "needEtherInWallet": {
+ "message": "คุณจะต้องมีอีเธอร์ในกระเป๋าเงินของคุณในการใช้งานกับแอพพลิเคชันแบบกระจายด้วย MetaMask"
+ },
+ "needImportFile": {
+ "message": "คุณต้องเลือกไฟล์ที่จะนำเข้า",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "คุณต้องป้อนรหัสผ่านสำหรับไฟล์ที่เลือก",
+ "description": "Password and file needed to import an account"
+ },
+ "negativeETH": {
+ "message": "ไม่สามารถส่งอีเธอร์เป็นจำนวนติดลบได้"
+ },
+ "networks": {
+ "message": "เครือข่าย"
+ },
+ "newAccount": {
+ "message": "บัญชีใหม่"
+ },
+ "newAccountNumberName": {
+ "message": "บัญชี $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "สร้างสัญญาใหม่"
+ },
+ "newPassword": {
+ "message": "รหัสผ่านใหม่(ขั้นต่ำ 8 ตัวอักษร)"
+ },
+ "newRecipient": {
+ "message": "ผู้รับใหม่"
+ },
+ "newRPC": {
+ "message": "RPC URL ใหม่"
+ },
+ "next": {
+ "message": "ถัดไป"
+ },
+ "noAddressForName": {
+ "message": "ยังไม่มีแอดแดรสไหนตั้งในชื่อนี้"
+ },
+ "noDeposits": {
+ "message": "ไม่มีเงินฝากเข้ามา"
+ },
+ "noTransactionHistory": {
+ "message": "ไม่มีรายการธุรกรรมในอดีต"
+ },
+ "noTransactions": {
+ "message": "ยังไม่มีรายการธุรกรรม"
+ },
+ "notStarted": {
+ "message": "ยังไม่เริ่ม"
+ },
+ "oldUI": {
+ "message": "หน้าตาแบบเก่า"
+ },
+ "oldUIMessage": {
+ "message": "คุณได้เปลี่ยนเป็นหน้าตาแบบเก่าแล้ว คุณสามารถเปลี่ยนเป็นหน้าตาแบบใหม่ได้โดยไปที่ตัวเลือกตรงเมนูมุมขวาบน"
+ },
+ "or": {
+ "message": "หรือ",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordCorrect": {
+ "message": "โปรดตรวจสอบว่ารหัสผ่านของคุณถูกต้อง"
+ },
+ "passwordMismatch": {
+ "message": "รหัสผ่านไม่ตรงกัน",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "รหัสผ่านไม่ยาวพอ",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "วางคีย์ส่วนตัวของคุณที่นี่:",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "วางคำชีดของคุณที่นี่!"
+ },
+ "personalAddressDetected": {
+ "message": "ตรวจพบแอดแดรสส่วนตัวแล้ว ใส่แอดแดรสสัญญาโทเค็น"
+ },
+ "pleaseReviewTransaction": {
+ "message": "โปรดตรวจสอบธุรกรรมของคุณ"
+ },
+ "privacyMsg": {
+ "message": "นโยบายสความเป็นส่วนตัว"
+ },
+ "privateKey": {
+ "message": "คีย์ส่วนตัว",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": "คำเตือน: ห้ามเปิดเผยคีย์นี้ ทุกคนที่มีคีย์ส่วนตัวสามารถขโมยข้อมูลใด ๆ ที่เก็บไว้ในบัญชีของคุณได้"
+ },
+ "privateNetwork": {
+ "message": "เครือข่ายส่วนตัว"
+ },
+ "qrCode": {
+ "message": "แสดง QR Code"
+ },
+ "readdToken": {
+ "message": "คุณสามารถเพิ่มโทเค็นนี้ในอนาคตได้โดยไปที่ “เพิ่มโทเค็น” ในเมนูตัวเลือกบัญชีของคุณ"
+ },
+ "readMore": {
+ "message": "อ่านเพิ่มเติมที่นี่"
+ },
+ "readMore2": {
+ "message": "อ่านเพิ่มเติม"
+ },
+ "receive": {
+ "message": "รับ"
+ },
+ "recipientAddress": {
+ "message": "แอดแดรสผู้รับ"
+ },
+ "refundAddress": {
+ "message": "แอดแดรสสำหรับการคืนเงินของคุณ"
+ },
+ "rejected": {
+ "message": "ถูกปฏิเสธ"
+ },
+ "resetAccount": {
+ "message": "รีเซ็ตบัญชี"
+ },
+ "restoreFromSeed": {
+ "message": "กู้คืนจากกลุ่มคำชีด"
+ },
+ "required": {
+ "message": "จำเป็น"
+ },
+ "retryWithMoreGas": {
+ "message": "ลองใหม่ด้วยราคาแก๊สที่สูงกว่านี้ที่นี่"
+ },
+ "revealSeedWords": {
+ "message": "เปิดเผยกลุ่มคำชีด"
+ },
+ "revealSeedWordsWarning": {
+ "message": "อย่าเปิดเผยคำกลุ่มคำชีดของคุณในที่สาธารณะ! คำเหล่านี้สามารถใช้เพื่อขโมยบัญชีทั้งหมดของคุณ"
+ },
+ "revert": {
+ "message": "ย้อนกลับ"
+ },
+ "rinkeby": {
+ "message": "เครือข่ายทดสอบ Rinkeby"
+ },
+ "ropsten": {
+ "message": "เครือข่ายทดสอบ Ropsten"
+ },
+ "sampleAccountName": {
+ "message": "เช่น บัญชีเฮงเฮงของฉัน",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "บันทึก"
+ },
+ "saveAsFile": {
+ "message": "บันทึกเป็นไฟล์",
+ "description": "Account export process"
+ },
+ "saveSeedAsFile": {
+ "message": "บันทึกกลุ่มคำชีดเป็นไฟล์"
+ },
+ "search": {
+ "message": "ค้นหา"
+ },
+ "secretPhrase": {
+ "message": "ป้อนกลุ่มคำสิบสองคำเพื่อกู้คืนตู้เซฟของคุณ"
+ },
+ "seedPhraseReq": {
+ "message": "กลุ่มคำชีดมีความยาว 12 คำ"
+ },
+ "select": {
+ "message": "เลือก"
+ },
+ "selectCurrency": {
+ "message": "เลือกสกุลเงิน"
+ },
+ "selectService": {
+ "message": "เลือกบริการ"
+ },
+ "selectType": {
+ "message": "เลือกประเภท"
+ },
+ "send": {
+ "message": "ส่ง"
+ },
+ "sendETH": {
+ "message": "ส่งอีเธอร์"
+ },
+ "sendTokens": {
+ "message": "ส่งโทเค็น"
+ },
+ "sendTokensAnywhere": {
+ "message": "ส่งโทเค็นไปให้ทุกคนที่มีบัญชีอีเธอเรียม"
+ },
+ "settings": {
+ "message": "การตั้งค่า"
+ },
+ "shapeshiftBuy": {
+ "message": "ซื้อด้วย Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "แสดงคีย์ส่วนตัว"
+ },
+ "showQRCode": {
+ "message": "แสดง QR Code"
+ },
+ "sign": {
+ "message": "เซ็นชื่อ"
+ },
+ "signMessage": {
+ "message": "เซ็นชื่อในข้อความ"
+ },
+ "signNotice": {
+ "message": "การเซ็นชื่อในข้อความนี้อาจจะเป็นอันตรายได้ \nเซ็นชื่อเฉพาะข้อความจากแหล่งที่คุณไว้วางใจได้จริง ๆ เท่านั้น \nวิธีที่อันตรายนี้จะถูกลบออกในอนาคต"
+ },
+ "sigRequest": {
+ "message": "ขอลายเซ็น"
+ },
+ "sigRequested": {
+ "message": "ขอลายเซ็นแล้ว"
+ },
+ "spaceBetween": {
+ "message": "มีช่องว่างได้เพียงตัวเดียวระหว่างคำเท่านั้น"
+ },
+ "status": {
+ "message": "สถานะ"
+ },
+ "stateLogs": {
+ "message": "บันทึกของสถานะ"
+ },
+ "stateLogsDescription": {
+ "message": "บันทึกของสถานะประกอบด้วยแอดแดรสสาธารณะและธุรกรรมที่ส่ง"
+ },
+ "submit": {
+ "message": "ตกลง"
+ },
+ "supportCenter": {
+ "message": "ไปที่ศูนย์สนับสนุนของเรา"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "สัญลักษณ์ต้องมีความยาวตั้งแต่ 0 ถึง 10 อักขระ"
+ },
+ "takesTooLong": {
+ "message": "ใช้เวลานานเกินไปใช่หรือไม่?"
+ },
+ "terms": {
+ "message": "ข้อตกลงในการใช้งาน"
+ },
+ "testFaucet": {
+ "message": "ตัวแจกจ่ายเพื่อการทดสอบ"
+ },
+ "to": {
+ "message": "ถึง"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 เป็นอีเธอร์โดย ShapeShift",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenAddress": {
+ "message": "แอดแดรสโทเค็น"
+ },
+ "tokenAlreadyAdded": {
+ "message": "โทเคนได้ถูกเพิ่มไปแล้ว"
+ },
+ "tokenBalance": {
+ "message": "ยอดโทเค็นคงเหลือของคุณคือ:"
+ },
+ "tokenSelection": {
+ "message": "ค้นหาโทเค็นหรือเลือกจากรายการโทเค็นยอดนิยมของเรา"
+ },
+ "tokenSymbol": {
+ "message": "สัญลักษณ์ประจำตัว"
+ },
+ "tokenWarning1": {
+ "message": "ติดตามโทเค็นที่คุณซื้อด้วยบัญชี MetaMask ของคุณ หากคุณซื้อโทเค็นโดยใช้บัญชีอื่นโทเค็นเหล่านั้นจะไม่ปรากฏที่นี่"
+ },
+ "total": {
+ "message": "รวม"
+ },
+ "transactions": {
+ "message": "ธุรกรรม"
+ },
+ "transactionMemo": {
+ "message": "บันทึกช่วยจำของการทำธุรกรรม (ไม่บังคับ)"
+ },
+ "transactionNumber": {
+ "message": "หมายเลขธุรกรรม"
+ },
+ "transfers": {
+ "message": "โอน"
+ },
+ "troubleTokenBalances": {
+ "message": "เรามีปัญหาในการโหลดยอดโทเค็นคงเหลือของคุณ คุณสามารถดูได้ที่นี่",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "twelveWords": {
+ "message": "กลุ่มคำ 12 คำเหล่านี้เป็นวิธีเดียวที่จะกู้คืนบัญชี MetaMask ของคุณ \n กรุณาเก็บไว้ในที่ปลอดภัยและเก็บเป็นความลับ"
+ },
+ "typePassword": {
+ "message": "พิมพ์รหัสผ่านของคุณ"
+ },
+ "uiWelcome": {
+ "message": "ยินดีต้อนรับสู่หน้าตาใหม่ (เบต้า)"
+ },
+ "uiWelcomeMessage": {
+ "message": "ขณะนี้คุณใช้งาน Metamask หน้าตาใหม่แล้ว ลองใช้ความสามรถใหม่ ๆ เช่นการส่งโทเค็นและหากพบปัญหากรุณาแจ้งให้เราทราบ"
+ },
+ "unavailable": {
+ "message": "ใช้งานไม่ได้"
+ },
+ "unknown": {
+ "message": "ไม่รู้จัก"
+ },
+ "unknownNetwork": {
+ "message": "ไม่รู้จักเครือข่ายส่วนตัว"
+ },
+ "unknownNetworkId": {
+ "message": "ไม่รู้จักหมายเลขเครือข่าย"
+ },
+ "uriErrorMsg": {
+ "message": "URI ต้องมีคำนำหน้าเป็น HTTP หรือ HTTPS"
+ },
+ "usaOnly": {
+ "message": "ในสหรัฐอเมริกาเท่านั้น",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "ถูกใช้งานโดยหลายไคลเอนท์"
+ },
+ "useOldUI": {
+ "message": "ใช้หน้าตาเก่า"
+ },
+ "validFileImport": {
+ "message": "คุณต้องเลือกไฟล์ที่ถูกต้องเพื่อนำเข้า"
+ },
+ "vaultCreated": {
+ "message": "สร้างตู้เซฟแล้ว"
+ },
+ "viewAccount": {
+ "message": "ดูบัญชี"
+ },
+ "visitWebSite": {
+ "message": "เยี่ยมชมเว็บไซต์ของเรา"
+ },
+ "warning": {
+ "message": "คำเตือน"
+ },
+ "welcomeBeta": {
+ "message": "ยินดีต้อนรับสู่ MetaMask เบต้า"
+ },
+ "whatsThis": {
+ "message": "นี่คืออะไร?"
+ },
+ "yourSigRequested": {
+ "message": "ลายเซ็นของคุณกำลังได้รับการร้องขอ"
+ },
+ "youSign": {
+ "message": "คุณกำลังเซ็นชื่อ"
+ }
+}
diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json
new file mode 100644
index 000000000..e39793430
--- /dev/null
+++ b/app/_locales/zh_TW/messages.json
@@ -0,0 +1,903 @@
+{
+ "accept": {
+ "message": "接受"
+ },
+ "account": {
+ "message": "帳戶"
+ },
+ "accountDetails": {
+ "message": "帳戶詳情"
+ },
+ "accountName": {
+ "message": "帳戶名稱"
+ },
+ "address": {
+ "message": "帳戶地址"
+ },
+ "addCustomToken": {
+ "message": "加入自訂代幣"
+ },
+ "addToken": {
+ "message": "加入代幣"
+ },
+ "addTokens": {
+ "message": "加入多筆代幣"
+ },
+ "amount": {
+ "message": "數額"
+ },
+ "amountPlusGas": {
+ "message": "數額 + Gas"
+ },
+ "appDescription": {
+ "message": "乙太坊瀏覽器擴充插件",
+ "description": "The description of the application"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "The name of the application"
+ },
+ "approved": {
+ "message": "已同意"
+ },
+ "attemptingConnect": {
+ "message": "正在嘗試連接區塊鏈。"
+ },
+ "attributions": {
+ "message": "來源"
+ },
+ "available": {
+ "message": "可使用"
+ },
+ "back": {
+ "message": "上一頁"
+ },
+ "balance": {
+ "message": "餘額:"
+ },
+ "balances": {
+ "message": "你的餘額:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "當前餘額不足以支付 Gas"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "必須大於等於 $1 並且小於等於 $2 。",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "blockiesIdenticon": {
+ "message": "使用 Blockies Identicon"
+ },
+ "borrowDharma": {
+ "message": "透過 Dharma (Beta) 借用"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask 是在加州設計製造."
+ },
+ "buy": {
+ "message": "購買"
+ },
+ "buyCoinbase": {
+ "message": "在 Coinbase 上購買"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase 是世界上最流行的買賣比特幣,以太幣和萊特幣的交易所。"
+ },
+ "ok": {
+ "message": "Ok"
+ },
+ "cancel": {
+ "message": "取消"
+ },
+ "classicInterface": {
+ "message": "使用舊版界面"
+ },
+ "clickCopy": {
+ "message": "點擊複製"
+ },
+ "confirm": {
+ "message": "確認"
+ },
+ "confirmed": {
+ "message": "已確認"
+ },
+ "confirmContract": {
+ "message": "確認合約"
+ },
+ "confirmPassword": {
+ "message": "確認密碼"
+ },
+ "confirmTransaction": {
+ "message": "確認交易"
+ },
+ "continue": {
+ "message": "繼續"
+ },
+ "continueToCoinbase": {
+ "message": "繼續前往 Coinbase"
+ },
+ "contractDeployment": {
+ "message": "合約部署"
+ },
+ "conversionProgress": {
+ "message": "正在取得匯率"
+ },
+ "copiedButton": {
+ "message": "已複製"
+ },
+ "copiedClipboard": {
+ "message": "已複製到剪貼簿"
+ },
+ "copiedExclamation": {
+ "message": "已複製!"
+ },
+ "copiedSafe": {
+ "message": "我已經複製到某個安全的地方了"
+ },
+ "copy": {
+ "message": "複製"
+ },
+ "copyToClipboard": {
+ "message": "複製到剪貼簿"
+ },
+ "copyButton": {
+ "message": " 複製 "
+ },
+ "copyPrivateKey": {
+ "message": "這是你的私鑰(點擊複製)"
+ },
+ "create": {
+ "message": "建立"
+ },
+ "createAccount": {
+ "message": "建立帳戶"
+ },
+ "createDen": {
+ "message": "建立"
+ },
+ "crypto": {
+ "message": "加密",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "currentConversion": {
+ "message": "當前匯率"
+ },
+ "currentNetwork": {
+ "message": "當前網路"
+ },
+ "customGas": {
+ "message": "自訂 Gas"
+ },
+ "customToken": {
+ "message": "自訂代幣"
+ },
+ "customize": {
+ "message": "自訂"
+ },
+ "customRPC": {
+ "message": "自訂 RPC"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "小數點後位數至少為0, 最多為36."
+ },
+ "decimal": {
+ "message": "小數點精度"
+ },
+ "defaultNetwork": {
+ "message": "預設 Ether 交易網路為主網路(Main Net)。"
+ },
+ "denExplainer": {
+ "message": "你的 DEN 是在你的 MetaMask 中的加密密碼儲存庫。"
+ },
+ "deposit": {
+ "message": "存入"
+ },
+ "depositBTC": {
+ "message": "將你的 BTC 存入到下面的地址:"
+ },
+ "depositCoin": {
+ "message": "將你的 $1 存入到下面的地址",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "存入 Eth"
+ },
+ "depositEther": {
+ "message": "存入 Ether"
+ },
+ "depositFiat": {
+ "message": "從法定貨幣存入"
+ },
+ "depositFromAccount": {
+ "message": "從其他帳戶存入"
+ },
+ "depositShapeShift": {
+ "message": "從 ShapeShift 存入"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "如果你擁有其他加密貨幣,你可以直接交易並存入 Ether 到你的 MetaMask 錢包。不需要開帳戶。"
+ },
+ "details": {
+ "message": "詳情"
+ },
+ "directDeposit": {
+ "message": "直接存入"
+ },
+ "directDepositEther": {
+ "message": "直接存入 Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "如果你已經擁有了一些 Ether,使用直接存入功能是讓你的新錢包最快取得 Ether 的方式。"
+ },
+ "done": {
+ "message": "完成"
+ },
+ "downloadStatelogs": {
+ "message": "下載狀態紀錄"
+ },
+ "dropped": {
+ "message": "丟棄"
+ },
+ "edit": {
+ "message": "編輯"
+ },
+ "editAccountName": {
+ "message": "編輯帳戶名稱"
+ },
+ "emailUs": {
+ "message": "寄 Email 給我們!"
+ },
+ "encryptNewDen": {
+ "message": "加密你的新 DEN"
+ },
+ "enterPassword": {
+ "message": "請輸入密碼"
+ },
+ "enterPasswordConfirm": {
+ "message": "請再次輸入密碼確認"
+ },
+ "passwordNotLongEnough": {
+ "message": "您所輸入的密碼長度不足"
+ },
+ "passwordsDontMatch": {
+ "message": "您所輸入的密碼不一致"
+ },
+ "etherscanView": {
+ "message": "在 Etherscan 上查看帳戶"
+ },
+ "exchangeRate": {
+ "message": "匯率"
+ },
+ "exportPrivateKey": {
+ "message": "導出私鑰"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "您需要自行負擔導出私鑰產生的風險"
+ },
+ "failed": {
+ "message": "失败"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "檔案導入失敗?點擊這裡!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "followTwitter": {
+ "message": "追蹤 Twitter"
+ },
+ "from": {
+ "message": "來源地址"
+ },
+ "fromToSame": {
+ "message": "來源和目的地址不能一樣"
+ },
+ "fromShapeShift": {
+ "message": "來自 ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "Gas 費用"
+ },
+ "gasLimit": {
+ "message": "Gas 上限"
+ },
+ "gasLimitCalculation": {
+ "message": "我們根據網路成功率算出建議的 Gas 上限。"
+ },
+ "gasLimitRequired": {
+ "message": "必需填寫 Gas 上限"
+ },
+ "gasLimitTooLow": {
+ "message": "Gas 上限至少為 21000"
+ },
+ "generatingSeed": {
+ "message": "產生助憶詞中..."
+ },
+ "gasPrice": {
+ "message": "Gas 價格 (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "我們根據網路成功率算出建議的 Gas 價格"
+ },
+ "gasPriceRequired": {
+ "message": "必需填寫 Gas 價格"
+ },
+ "getEther": {
+ "message": "取得 Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "從水管取得$1 Ether",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "必須要大於等於 $1。",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "這裡",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "Here's a list!!!!"
+ },
+ "hide": {
+ "message": "隱藏"
+ },
+ "hideToken": {
+ "message": "隱藏代幣"
+ },
+ "hideTokenPrompt": {
+ "message": "隱藏代幣?"
+ },
+ "howToDeposit": {
+ "message": "你想怎麼存入 Ether?"
+ },
+ "holdEther": {
+ "message": "Metamask 讓您能保存 ether 和代幣, 並成為您接觸分散式應用程式的途徑."
+ },
+ "import": {
+ "message": "導入",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "導入帳戶"
+ },
+ "importAccountMsg": {
+ "message":" 匯入的帳戶與您原有 MetaMask 帳戶的助憶詞並無關聯. 請查看與導入帳戶相關的資料 "
+ },
+ "importAnAccount": {
+ "message": "導入一個帳戶"
+ },
+ "importDen": {
+ "message": "導入現成的 DEN"
+ },
+ "imported": {
+ "message": "已導入私鑰",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "說明 & 資訊"
+ },
+ "insufficientFunds": {
+ "message": "資金不足."
+ },
+ "insufficientTokens": {
+ "message": "代幣不足."
+ },
+ "invalidAddress": {
+ "message": "錯誤的地址"
+ },
+ "invalidAddressRecipient": {
+ "message": "接收地址錯誤"
+ },
+ "invalidGasParams": {
+ "message": "Gas 參數錯誤"
+ },
+ "invalidInput": {
+ "message": "輸入錯誤。"
+ },
+ "invalidRequest": {
+ "message": "無效的請求"
+ },
+ "invalidRPC": {
+ "message": "無效的 RPC URI"
+ },
+ "jsonFail": {
+ "message": "有東西出錯了. 請確認你的 JSON 檔案格式正確。"
+ },
+ "jsonFile": {
+ "message": "JSON 檔案",
+ "description": "format for importing an account"
+ },
+ "keepTrackTokens": {
+ "message": "持續追蹤您 MetaMask 帳戶中的代幣。"
+ },
+ "kovan": {
+ "message": "Kovan 測試網路"
+ },
+ "knowledgeDataBase": {
+ "message": "查看我們的知識庫"
+ },
+ "max": {
+ "message": "最大值"
+ },
+ "learnMore": {
+ "message": "了解更多。"
+ },
+ "lessThanMax": {
+ "message": "必須小於等於 $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "likeToAddTokens": {
+ "message": "您確定要加入這些代幣嗎?"
+ },
+ "links": {
+ "message": "連結"
+ },
+ "limit": {
+ "message": "上限"
+ },
+ "loading": {
+ "message": "載入..."
+ },
+ "loadingTokens": {
+ "message": "載入代幣..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "登入"
+ },
+ "logout": {
+ "message": "登出"
+ },
+ "loose": {
+ "message": "非 MetaMask 帳號"
+ },
+ "loweCaseWords": {
+ "message": "助憶詞僅包含小寫字元"
+ },
+ "mainnet": {
+ "message": "乙太坊 主網路"
+ },
+ "message": {
+ "message": "訊息"
+ },
+ "metamaskDescription": {
+ "message": "MetaMask 是Ethereum的安全身份識別金庫."
+ },
+ "min": {
+ "message": "最小"
+ },
+ "myAccounts": {
+ "message": "我的帳戶"
+ },
+ "mustSelectOne": {
+ "message": "必須選擇至少 1 代幣."
+ },
+ "needEtherInWallet": {
+ "message": "要使用 MetaMask 存取 DAPP 時,您的錢包中需要有 Ether。"
+ },
+ "needImportFile": {
+ "message": "您必須選擇一個檔案來導入。",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "您必須為選擇好的檔案輸入密碼。",
+ "description": "Password and file needed to import an account"
+ },
+ "negativeETH": {
+ "message": "不能送出負值的 ETH。"
+ },
+ "networks": {
+ "message": "網路"
+ },
+ "newAccount": {
+ "message": "新帳戶"
+ },
+ "newAccountNumberName": {
+ "message": "帳戶 $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "新合約"
+ },
+ "newPassword": {
+ "message": "新密碼(至少8個字)"
+ },
+ "newRecipient": {
+ "message": "新收款人"
+ },
+ "newRPC": {
+ "message": "New RPC URL"
+ },
+ "next": {
+ "message": "下一頁"
+ },
+ "noAddressForName": {
+ "message": "此 ENS 尚未指定地址。"
+ },
+ "noDeposits": {
+ "message": "尚未有存款"
+ },
+ "noTransactionHistory": {
+ "message": "尚未有交易紀錄。"
+ },
+ "noTransactions": {
+ "message": "尚未有交易"
+ },
+ "notStarted": {
+ "message": "尚未開始"
+ },
+ "oldUI": {
+ "message": "舊版界面"
+ },
+ "oldUIMessage": {
+ "message": "你已經切換到舊版界面。可以通過右上方下拉選單中的選項切換回新的使用者界面。"
+ },
+ "or": {
+ "message": "或",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordCorrect": {
+ "message": "請確認您的密碼是正確的。"
+ },
+ "passwordMismatch": {
+ "message": "密碼不一致",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "密碼不夠長",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "請貼上你的私鑰串:",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "請貼上你的助憶詞!"
+ },
+ "personalAddressDetected": {
+ "message": "已偵測到個人地址. 請輸入代幣合約地址."
+ },
+ "pleaseReviewTransaction": {
+ "message": "請檢查你的交易。"
+ },
+ "popularTokens": {
+ "message": "常見的代幣"
+ },
+ "privacyMsg": {
+ "message": "隱私政策"
+ },
+ "privateKey": {
+ "message": "私鑰",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": "注意:永遠不要公開這個私鑰。任何取得這把私鑰的人都可以竊取這個帳號中的任何資產。"
+ },
+ "privateNetwork": {
+ "message": "私有網路"
+ },
+ "qrCode": {
+ "message": "顯示 QR Code"
+ },
+ "readdToken": {
+ "message": "之後還可以透過帳戶選單中的“加入代幣”來加入此代幣。"
+ },
+ "readMore": {
+ "message": "了解更多。"
+ },
+ "readMore2": {
+ "message": "了解更多。"
+ },
+ "receive": {
+ "message": "接收"
+ },
+ "recipientAddress": {
+ "message": "接收地址"
+ },
+ "refundAddress": {
+ "message": "你的退款地址"
+ },
+ "rejected": {
+ "message": "拒絕"
+ },
+ "resetAccount": {
+ "message": "重置帳戶"
+ },
+ "restoreFromSeed": {
+ "message": "透過助憶詞重置"
+ },
+ "restoreVault": {
+ "message": "重置金庫"
+ },
+ "required": {
+ "message": "必填"
+ },
+ "retryWithMoreGas": {
+ "message": "改用更高的 Gas 價格重試"
+ },
+ "walletSeed": {
+ "message": "錢包助憶詞"
+ },
+ "revealSeedWords": {
+ "message": "顯示助憶詞"
+ },
+ "revealSeedWordsWarning": {
+ "message": "別在公共場合回復你的助憶詞!這些詞可被用來竊取你的帳戶."
+ },
+ "revert": {
+ "message": "還原"
+ },
+ "rinkeby": {
+ "message": "Rinkeby 測試網路"
+ },
+ "ropsten": {
+ "message": "Ropsten 測試網路"
+ },
+ "currentRpc": {
+ "message": "當前的 RPC"
+ },
+ "connectingToMainnet": {
+ "message": "連線到主 Ethereum 網路"
+ },
+ "connectingToRopsten": {
+ "message": "連線到 Ropsten 測試網路"
+ },
+ "connectingToKovan": {
+ "message": "連線到 Kovan 測試網路"
+ },
+ "connectingToRinkeby": {
+ "message": "連線到 Rinkeby 測試網路"
+ },
+ "connectingToUnknown": {
+ "message": "連線到未知網路"
+ },
+ "sampleAccountName": {
+ "message": "例如:我的新帳戶",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "儲存"
+ },
+ "saveAsFile": {
+ "message": "儲存檔案",
+ "description": "Account export process"
+ },
+ "saveSeedAsFile": {
+ "message": "將助憶詞儲存成檔案"
+ },
+ "search": {
+ "message": "搜尋"
+ },
+ "secretPhrase": {
+ "message": "在此輸入你的12個祕密助憶詞以回復金庫."
+ },
+ "newPassword8Chars": {
+ "message": "新密碼 (至少 8 個字元)"
+ },
+ "seedPhraseReq": {
+ "message": "助憶詞為 12 個詞語"
+ },
+ "select": {
+ "message": "選擇"
+ },
+ "selectCurrency": {
+ "message": "選擇幣別"
+ },
+ "selectService": {
+ "message": "選擇服務"
+ },
+ "selectType": {
+ "message": "選擇類型"
+ },
+ "send": {
+ "message": "發送"
+ },
+ "sendETH": {
+ "message": "發送 ETH"
+ },
+ "sendTokens": {
+ "message": "發送代幣"
+ },
+ "onlySendToEtherAddress": {
+ "message": "只發送 ETH 到乙太坊地址."
+ },
+ "searchTokens": {
+ "message": "搜尋代幣"
+ },
+ "sendTokensAnywhere": {
+ "message": "發送代幣給擁有乙太坊帳戶的任何人"
+ },
+ "settings": {
+ "message": "設定"
+ },
+ "info": {
+ "message": "資訊"
+ },
+ "shapeshiftBuy": {
+ "message": "從 Shapeshift 購買"
+ },
+ "showPrivateKeys": {
+ "message": "顯示私鑰"
+ },
+ "showQRCode": {
+ "message": "顯示 QR Code"
+ },
+ "sign": {
+ "message": "簽署"
+ },
+ "signed": {
+ "message": "已簽署"
+ },
+ "signMessage": {
+ "message": "簽署訊息"
+ },
+ "signNotice": {
+ "message": "簽署此訊息可能會產生危險地副作用。 \n只從你完全信任的網站上簽署。這種危險的方法;將在未來的版本中被移除。"
+ },
+ "sigRequest": {
+ "message": "請求簽署"
+ },
+ "sigRequested": {
+ "message": "已請求簽署"
+ },
+ "spaceBetween": {
+ "message": "there can only be a space between words"
+ },
+ "status": {
+ "message": "狀態"
+ },
+ "stateLogs": {
+ "message": "狀態紀錄"
+ },
+ "stateLogsDescription": {
+ "message": "狀態紀錄包含你的公開帳戶地址和已傳送的交易資訊."
+ },
+ "stateLogError": {
+ "message": "在取得狀態紀錄時發生錯誤."
+ },
+ "submit": {
+ "message": "送出"
+ },
+ "submitted": {
+ "message": "已送出"
+ },
+ "supportCenter": {
+ "message": "造訪我們的協助中心"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "代號必須介於 0 到 10 字元間."
+ },
+ "takesTooLong": {
+ "message": "花費太長時間?"
+ },
+ "terms": {
+ "message": "使用條款"
+ },
+ "testFaucet": {
+ "message": "測試水管"
+ },
+ "to": {
+ "message": "目的帳號"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 ETH 透過 ShapeShift",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenAddress": {
+ "message": "代幣地址"
+ },
+ "tokenAlreadyAdded": {
+ "message": "已加入過此代幣。"
+ },
+ "tokenBalance": {
+ "message": "代幣餘額:"
+ },
+ "tokenSelection": {
+ "message": "搜尋代幣或是從常見代幣列表中選擇。"
+ },
+ "tokenSymbol": {
+ "message": "代幣代號"
+ },
+ "tokenWarning1": {
+ "message": "使用 MetaMask 帳戶追蹤你已購得的代幣。如果你使用不同的帳戶保存購得的代幣,那些代幣就不會出現在這裡。"
+ },
+ "total": {
+ "message": "總量"
+ },
+ "transactions": {
+ "message": "交易紀錄"
+ },
+ "transactionMemo": {
+ "message": "交易備註(選填)"
+ },
+ "transactionNumber": {
+ "message": "交易號碼"
+ },
+ "transfers": {
+ "message": "交易"
+ },
+ "troubleTokenBalances": {
+ "message": "無法取得代幣餘額。您k可以到這裡查看 ",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "twelveWords": {
+ "message": "這 12 個單詞是唯一回復你的 MetaMask 帳號的方法。\n將它們儲存到那些安全且隱密的地方吧。"
+ },
+ "typePassword": {
+ "message": "請輸入密碼"
+ },
+ "uiWelcome": {
+ "message": "歡迎使用新版界面 (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "你現在正在使用新版 MetaMask 界面。試試諸如發送代幣等新功能吧,有任何問題請告知我們。"
+ },
+ "unapproved": {
+ "message": "未同意"
+ },
+ "unavailable": {
+ "message": "不可用"
+ },
+ "unknown": {
+ "message": "未知"
+ },
+ "unknownNetwork": {
+ "message": "未知私有網路"
+ },
+ "unknownNetworkId": {
+ "message": "未知網路 ID"
+ },
+ "uriErrorMsg": {
+ "message": "URIs 需要加入適當的 HTTP/HTTPS 前綴."
+ },
+ "usaOnly": {
+ "message": "僅限美國",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "可用於各種不同的客戶端"
+ },
+ "useOldUI": {
+ "message": "使用舊版界面"
+ },
+ "validFileImport": {
+ "message": "您必須選擇一個合法的檔案來導入."
+ },
+ "vaultCreated": {
+ "message": "已建立金庫"
+ },
+ "viewAccount": {
+ "message": "查看帳戶"
+ },
+ "visitWebSite": {
+ "message": "造訪我們的網站"
+ },
+ "warning": {
+ "message": "警告"
+ },
+ "welcomeBeta": {
+ "message": "歡迎到 MetaMask Beta"
+ },
+ "whatsThis": {
+ "message": "這是什麼?"
+ },
+ "yourSigRequested": {
+ "message": "正在請求你的簽署"
+ },
+ "youSign": {
+ "message": "正在簽署"
+ }
+}
diff --git a/app/manifest.json b/app/manifest.json
index 6fcf6cd7c..a20f9b976 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
- "version": "4.2.0",
+ "version": "4.4.0",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",
diff --git a/app/scripts/background.js b/app/scripts/background.js
index ef5513ec7..7782fc41e 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -19,7 +19,7 @@ const setupRaven = require('./lib/setupRaven')
const reportFailedTxToSentry = require('./lib/reportFailedTxToSentry')
const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
const EdgeEncryptor = require('./edge-encryptor')
-
+const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code')
const STORAGE_KEY = 'metamask-config'
const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
@@ -42,6 +42,7 @@ const isIE = !!document.documentMode
const isEdge = !isIE && !!window.StyleMedia
let popupIsOpen = false
+let notificationIsOpen = false
let openMetamaskTabsIDs = {}
// state persistence
@@ -57,7 +58,8 @@ setupMetamaskMeshMetrics()
async function initialize () {
const initState = await loadStateFromPersistence()
- await setupController(initState)
+ const initLangCode = await getFirstPreferredLangCode()
+ await setupController(initState, initLangCode)
log.debug('MetaMask initialization complete.')
}
@@ -83,17 +85,16 @@ async function loadStateFromPersistence () {
// write to disk
if (localStore.isSupported) localStore.set(versionedData)
- diskStore.putState(versionedData)
// return just the data
return versionedData.data
}
-function setupController (initState) {
+function setupController (initState, initLangCode) {
//
// MetaMask Controller
//
-
+
const controller = new MetamaskController({
// User confirmation callbacks:
showUnconfirmedMessage: triggerUi,
@@ -101,6 +102,8 @@ function setupController (initState) {
showUnapprovedTx: triggerUi,
// initial state
initState,
+ // initial locale code
+ initLangCode,
// platform specific api
platform,
encryptor: isEdge ? new EdgeEncryptor() : undefined,
@@ -120,7 +123,6 @@ function setupController (initState) {
debounce(1000),
storeTransform(versionifyData),
storeTransform(syncDataWithExtension),
- asStream(diskStore),
(error) => {
log.error('pump hit error', error)
}
@@ -165,6 +167,11 @@ function setupController (initState) {
}
})
}
+ if (remotePort.name === 'notification') {
+ endOfStream(portStream, () => {
+ notificationIsOpen = false
+ })
+ }
} else {
// communication with page
const originDomain = urlUtil.parse(remotePort.sender.url).hostname
@@ -207,7 +214,8 @@ function setupController (initState) {
function triggerUi () {
extension.tabs.query({ active: true }, (tabs) => {
const currentlyActiveMetamaskTab = tabs.find(tab => openMetamaskTabsIDs[tab.id])
- if (!popupIsOpen && !currentlyActiveMetamaskTab) notificationManager.showPopup()
+ if (!popupIsOpen && !currentlyActiveMetamaskTab && !notificationIsOpen) notificationManager.showPopup()
+ notificationIsOpen = true
})
}
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index 39d15fd83..b4819d951 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -11,6 +11,7 @@ class PreferencesController {
tokens: [],
useBlockie: false,
featureFlags: {},
+ currentLocale: opts.initLangCode,
}, opts.initState)
this.store = new ObservableStore(initState)
}
@@ -24,6 +25,10 @@ class PreferencesController {
return this.store.getState().useBlockie
}
+ setCurrentLocale (key) {
+ this.store.updateState({ currentLocale: key })
+ }
+
setSelectedAddress (_address) {
return new Promise((resolve, reject) => {
const address = normalizeAddress(_address)
diff --git a/app/scripts/lib/extractEthjsErrorMessage.js b/app/scripts/lib/extractEthjsErrorMessage.js
new file mode 100644
index 000000000..bac541735
--- /dev/null
+++ b/app/scripts/lib/extractEthjsErrorMessage.js
@@ -0,0 +1,27 @@
+const ethJsRpcSlug = 'Error: [ethjs-rpc] rpc error with payload '
+const errorLabelPrefix = 'Error: '
+
+module.exports = extractEthjsErrorMessage
+
+
+//
+// ethjs-rpc provides overly verbose error messages
+// if we detect this type of message, we extract the important part
+// Below is an example input and output
+//
+// Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced
+//
+// Transaction Failed: replacement transaction underpriced
+//
+
+
+function extractEthjsErrorMessage(errorMessage) {
+ const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug)
+ if (isEthjsRpcError) {
+ const payloadAndError = errorMessage.slice(ethJsRpcSlug.length)
+ const originalError = payloadAndError.slice(payloadAndError.indexOf(errorLabelPrefix) + errorLabelPrefix.length)
+ return originalError
+ } else {
+ return errorMessage
+ }
+}
diff --git a/app/scripts/lib/get-first-preferred-lang-code.js b/app/scripts/lib/get-first-preferred-lang-code.js
new file mode 100644
index 000000000..28612f763
--- /dev/null
+++ b/app/scripts/lib/get-first-preferred-lang-code.js
@@ -0,0 +1,16 @@
+const extension = require('extensionizer')
+const promisify = require('pify')
+const allLocales = require('../../_locales/index.json')
+
+const existingLocaleCodes = allLocales.map(locale => locale.code)
+
+async function getFirstPreferredLangCode () {
+ const userPreferredLocaleCodes = await promisify(
+ extension.i18n.getAcceptLanguages,
+ { errorFirst: false }
+ )()
+ const firstPreferredLangCode = userPreferredLocaleCodes.find(code => existingLocaleCodes.includes(code))
+ return firstPreferredLangCode || 'en'
+}
+
+module.exports = getFirstPreferredLangCode
diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js
index adaf60c65..1fcb7cf69 100644
--- a/app/scripts/lib/notification-manager.js
+++ b/app/scripts/lib/notification-manager.js
@@ -13,11 +13,12 @@ class NotificationManager {
this._getPopup((err, popup) => {
if (err) throw err
+ // Bring focus to chrome popup
if (popup) {
- // bring focus to existing popup
+ // bring focus to existing chrome popup
extension.windows.update(popup.id, { focused: true })
} else {
- // create new popup
+ // create new notification popup
extension.windows.create({
url: 'notification.html',
type: 'popup',
@@ -29,6 +30,7 @@ class NotificationManager {
}
closePopup () {
+ // closes notification popup
this._getPopup((err, popup) => {
if (err) throw err
if (!popup) return
@@ -60,9 +62,8 @@ class NotificationManager {
_getPopupIn (windows) {
return windows ? windows.find((win) => {
- return (win && win.type === 'popup' &&
- win.height === height &&
- win.width === width)
+ // Returns notification popup
+ return (win && win.type === 'popup')
}) : null
}
diff --git a/app/scripts/lib/reportFailedTxToSentry.js b/app/scripts/lib/reportFailedTxToSentry.js
index ee73f6845..e09f4f1f8 100644
--- a/app/scripts/lib/reportFailedTxToSentry.js
+++ b/app/scripts/lib/reportFailedTxToSentry.js
@@ -1,5 +1,4 @@
-const ethJsRpcSlug = 'Error: [ethjs-rpc] rpc error with payload '
-const errorLabelPrefix = 'Error: '
+const extractEthjsErrorMessage = require('./extractEthjsErrorMessage')
module.exports = reportFailedTxToSentry
@@ -9,30 +8,9 @@ module.exports = reportFailedTxToSentry
//
function reportFailedTxToSentry({ raven, txMeta }) {
- const errorMessage = extractErrorMessage(txMeta.err.message)
+ const errorMessage = 'Transaction Failed: ' + extractEthjsErrorMessage(txMeta.err.message)
raven.captureMessage(errorMessage, {
// "extra" key is required by Sentry
extra: txMeta,
})
}
-
-//
-// ethjs-rpc provides overly verbose error messages
-// if we detect this type of message, we extract the important part
-// Below is an example input and output
-//
-// Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced
-//
-// Transaction Failed: replacement transaction underpriced
-//
-
-function extractErrorMessage(errorMessage) {
- const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug)
- if (isEthjsRpcError) {
- const payloadAndError = errorMessage.slice(ethJsRpcSlug.length)
- const originalError = payloadAndError.slice(payloadAndError.indexOf(errorLabelPrefix) + errorLabelPrefix.length)
- return `Transaction Failed: ${originalError}`
- } else {
- return `Transaction Failed: ${errorMessage}`
- }
-}
diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js
index 42e48cb90..a869588d0 100644
--- a/app/scripts/lib/setupRaven.js
+++ b/app/scripts/lib/setupRaven.js
@@ -1,5 +1,6 @@
-const Raven = require('../vendor/raven.min.js')
+const Raven = require('raven-js')
const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
+const extractEthjsErrorMessage = require('./extractEthjsErrorMessage')
const PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
@@ -18,9 +19,39 @@ function setupRaven(opts) {
ravenTarget = PROD
}
- Raven.config(ravenTarget, {
+ const client = Raven.config(ravenTarget, {
release,
- }).install()
+ transport: function(opts) {
+ const report = opts.data
+ // simplify ethjs error messages
+ report.exception.values.forEach(item => {
+ item.value = extractEthjsErrorMessage(item.value)
+ })
+ // modify report urls
+ rewriteReportUrls(report)
+ // make request normally
+ client._makeRequest(opts)
+ },
+ })
+ client.install()
return Raven
}
+
+function rewriteReportUrls(report) {
+ // update request url
+ report.request.url = toMetamaskUrl(report.request.url)
+ // update exception stack trace
+ report.exception.values.forEach(item => {
+ item.stacktrace.frames.forEach(frame => {
+ frame.filename = toMetamaskUrl(frame.filename)
+ })
+ })
+}
+
+function toMetamaskUrl(origUrl) {
+ const filePath = origUrl.split(location.origin)[1]
+ if (!filePath) return origUrl
+ const metamaskUrl = `metamask${filePath}`
+ return metamaskUrl
+}
diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js
index ad07c813f..ab344ae9b 100644
--- a/app/scripts/lib/tx-state-manager.js
+++ b/app/scripts/lib/tx-state-manager.js
@@ -38,11 +38,6 @@ module.exports = class TransactionStateManager extends EventEmitter {
}, opts)
}
- // Returns the number of txs for the current network.
- getTxCount () {
- return this.getTxList().length
- }
-
getTxList () {
const network = this.getNetwork()
const fullTxList = this.getFullTxList()
@@ -88,7 +83,7 @@ module.exports = class TransactionStateManager extends EventEmitter {
txMeta.history.push(snapshot)
const transactions = this.getFullTxList()
- const txCount = this.getTxCount()
+ const txCount = transactions.length
const txHistoryLimit = this.txHistoryLimit
// checks if the length of the tx history is
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 18d71874a..4422a5cf3 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -49,7 +49,7 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* @constructor
- * @param {Object} opts
+ * @param {Object} opts
*/
constructor (opts) {
super()
@@ -57,7 +57,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.defaultMaxListeners = 20
this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200)
-
+
this.opts = opts
const initState = opts.initState || {}
this.recordFirstTimeInfo(initState)
@@ -82,6 +82,7 @@ module.exports = class MetamaskController extends EventEmitter {
// preferences controller
this.preferencesController = new PreferencesController({
initState: initState.PreferencesController,
+ initLangCode: opts.initLangCode,
})
// currency controller
@@ -296,8 +297,8 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* The metamask-state of the various controllers, made available to the UI
- *
- * @returns {Object} status
+ *
+ * @returns {Object} status
*/
getState () {
const wallet = this.configManager.getWallet()
@@ -335,8 +336,8 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* Returns an api-object which is consumed by the UI
- *
- * @returns {Object}
+ *
+ * @returns {Object}
*/
getApi () {
const keyringController = this.keyringController
@@ -351,6 +352,7 @@ module.exports = class MetamaskController extends EventEmitter {
getState: (cb) => cb(null, this.getState()),
setCurrentCurrency: this.setCurrentCurrency.bind(this),
setUseBlockie: this.setUseBlockie.bind(this),
+ setCurrentLocale: this.setCurrentLocale.bind(this),
markAccountsFound: this.markAccountsFound.bind(this),
markPasswordForgotten: this.markPasswordForgotten.bind(this),
unMarkPasswordForgotten: this.unMarkPasswordForgotten.bind(this),
@@ -365,7 +367,7 @@ module.exports = class MetamaskController extends EventEmitter {
placeSeedWords: this.placeSeedWords.bind(this),
verifySeedPhrase: nodeify(this.verifySeedPhrase, this),
clearSeedWordCache: this.clearSeedWordCache.bind(this),
- resetAccount: this.resetAccount.bind(this),
+ resetAccount: nodeify(this.resetAccount, this),
importAccountWithStrategy: this.importAccountWithStrategy.bind(this),
// vault management
@@ -426,14 +428,14 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* Creates a new Vault(?) and create a new keychain(?)
- *
+ *
* A vault is ...
- *
+ *
* A keychain is ...
- *
+ *
*
* @param {} password
- *
+ *
* @returns {} vault
*/
async createNewVaultAndKeychain (password) {
@@ -479,9 +481,9 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* Retrieves the first Identiy from the passed Vault and selects the related address
- *
+ *
* An Identity is ...
- *
+ *
* @param {} vault
*/
selectFirstIdentity (vault) {
@@ -495,8 +497,8 @@ module.exports = class MetamaskController extends EventEmitter {
//
/**
- * Adds a new account to ...
- *
+ * Adds a new account to ...
+ *
* @returns {} keyState
*/
async addNewAccount () {
@@ -522,10 +524,10 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* Adds the current vault's seed words to the UI's state tree.
- *
+ *
* Used when creating a first vault, to allow confirmation.
* Also used when revealing the seed words in the confirmation view.
- */
+ */
placeSeedWords (cb) {
this.verifySeedPhrase()
@@ -540,7 +542,7 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* Verifies the validity of the current vault's seed phrase.
- *
+ *
* Validity: seed phrase restores the accounts belonging to the current vault.
*
* Called when the first account is created and on unlocking the vault.
@@ -571,27 +573,32 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* Remove the primary account seed phrase from the UI's state tree.
- *
+ *
* The seed phrase remains available in the background process.
- *
+ *
*/
clearSeedWordCache (cb) {
this.configManager.setSeedWords(null)
cb(null, this.preferencesController.getSelectedAddress())
}
-
+
/**
* ?
*/
- resetAccount (cb) {
+ async resetAccount (cb) {
const selectedAddress = this.preferencesController.getSelectedAddress()
this.txController.wipeTransactions(selectedAddress)
- cb(null, selectedAddress)
+
+ const networkController = this.networkController
+ const oldType = networkController.getProviderConfig().type
+ await networkController.setProviderType(oldType, true)
+
+ return selectedAddress
}
/**
* Imports an account ... ?
- *
+ *
* @param {} strategy
* @param {} args
* @param {} cb
@@ -634,9 +641,9 @@ module.exports = class MetamaskController extends EventEmitter {
}
// Prefixed Style Message Signing Methods:
-
+
/**
- *
+ *
* @param {} msgParams
* @param {} cb
*/
@@ -655,7 +662,7 @@ module.exports = class MetamaskController extends EventEmitter {
}
})
}
-
+
/**
* @param {} msgParams
*/
@@ -676,7 +683,7 @@ module.exports = class MetamaskController extends EventEmitter {
return this.getState()
})
}
-
+
/**
* @param {} msgParams
*/
@@ -697,13 +704,13 @@ module.exports = class MetamaskController extends EventEmitter {
return this.getState()
})
}
-
+
// ---------------------------------------------------------------------------
// Account Restauration
/**
* ?
- *
+ *
* @param {} migratorOutput
*/
restoreOldVaultAccounts (migratorOutput) {
@@ -714,7 +721,7 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* ?
- *
+ *
* @param {} migratorOutput
*/
restoreOldLostAccounts (migratorOutput) {
@@ -728,9 +735,9 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* Import (lost) Accounts
- *
+ *
* @param {Object} {lostAccounts} @Array accounts <{ address, privateKey }>
- *
+ *
* Uses the array's private keys to create a new Simple Key Pair keychain
* and add it to the keyring controller.
*/
@@ -823,7 +830,7 @@ module.exports = class MetamaskController extends EventEmitter {
if (cb && typeof cb === 'function') {
cb(null, this.getState())
}
- }
+ }
cancelPersonalMessage (msgId, cb) {
const messageManager = this.personalMessageManager
@@ -978,7 +985,7 @@ module.exports = class MetamaskController extends EventEmitter {
const percentileNum = percentile(50, lowestPrices)
const percentileNumBn = new BN(percentileNum)
return '0x' + percentileNumBn.mul(GWEI_BN).toString(16)
- }
+ }
//=============================================================================
// CONFIG
@@ -1029,6 +1036,15 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ setCurrentLocale (key, cb) {
+ try {
+ this.preferencesController.setCurrentLocale(key)
+ cb(null)
+ } catch (err) {
+ cb(err)
+ }
+ }
+
recordFirstTimeInfo (initState) {
if (!('firstTimeInfo' in initState)) {
initState.firstTimeInfo = {
diff --git a/app/scripts/migrations/022.js b/app/scripts/migrations/022.js
new file mode 100644
index 000000000..c3c0d53ef
--- /dev/null
+++ b/app/scripts/migrations/022.js
@@ -0,0 +1,39 @@
+
+const version = 22
+
+/*
+
+This migration adds submittedTime to the txMeta if it is not their
+
+*/
+
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = state
+ const transactions = newState.TransactionController.transactions
+
+ newState.TransactionController.transactions = transactions.map((txMeta) => {
+ if (txMeta.status !== 'submitted' || txMeta.submittedTime) return txMeta
+ txMeta.submittedTime = (new Date()).getTime()
+ return txMeta
+ })
+ return newState
+}
diff --git a/app/scripts/migrations/023.js b/app/scripts/migrations/023.js
new file mode 100644
index 000000000..bce0a5bea
--- /dev/null
+++ b/app/scripts/migrations/023.js
@@ -0,0 +1,50 @@
+
+const version = 23
+
+/*
+
+This migration removes transactions that are no longer usefull down to 40 total
+
+*/
+
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = state
+ const transactions = newState.TransactionController.transactions
+
+ if (transactions.length <= 40) return newState
+
+ let reverseTxList = transactions.reverse()
+ let stripping = true
+ while (reverseTxList.length > 40 && stripping) {
+ let txIndex = reverseTxList.findIndex((txMeta) => {
+ return (txMeta.status === 'failed' ||
+ txMeta.status === 'rejected' ||
+ txMeta.status === 'confirmed' ||
+ txMeta.status === 'dropped')
+ })
+ if (txIndex < 0) stripping = false
+ else reverseTxList.splice(txIndex, 1)
+ }
+
+ newState.TransactionController.transactions = reverseTxList.reverse()
+ return newState
+}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
index a0cf5f4d4..811e06b6b 100644
--- a/app/scripts/migrations/index.js
+++ b/app/scripts/migrations/index.js
@@ -32,4 +32,6 @@ module.exports = [
require('./019'),
require('./020'),
require('./021'),
+ require('./022'),
+ require('./023'),
]
diff --git a/app/scripts/popup.js b/app/scripts/popup.js
index 11d50ee87..13c7ac5ec 100644
--- a/app/scripts/popup.js
+++ b/app/scripts/popup.js
@@ -10,68 +10,75 @@ const NotificationManager = require('./lib/notification-manager')
const notificationManager = new NotificationManager()
const setupRaven = require('./lib/setupRaven')
-// create platform global
-global.platform = new ExtensionPlatform()
-
-// setup sentry error reporting
-const release = global.platform.getVersion()
-setupRaven({ release })
-
-// inject css
-// const css = MetaMaskUiCss()
-// injectCss(css)
-
-// identify window type (popup, notification)
-const windowType = isPopupOrNotification()
-global.METAMASK_UI_TYPE = windowType
-closePopupIfOpen(windowType)
-
-// setup stream to background
-const extensionPort = extension.runtime.connect({ name: windowType })
-const connectionStream = new PortStream(extensionPort)
-
-// start ui
-const container = document.getElementById('app-content')
-startPopup({ container, connectionStream }, (err, store) => {
- if (err) return displayCriticalError(err)
-
- // Code commented out until we begin auto adding users to NewUI
- // const { isMascara, identities = {}, featureFlags = {} } = store.getState().metamask
- // const firstTime = Object.keys(identities).length === 0
- const { isMascara, featureFlags = {} } = store.getState().metamask
- let betaUIState = featureFlags.betaUI
-
- // Code commented out until we begin auto adding users to NewUI
- // const useBetaCss = isMascara || firstTime || betaUIState
- const useBetaCss = isMascara || betaUIState
-
- let css = useBetaCss ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
- let deleteInjectedCss = injectCss(css)
- let newBetaUIState
-
- store.subscribe(() => {
- const state = store.getState()
- newBetaUIState = state.metamask.featureFlags.betaUI
- if (newBetaUIState !== betaUIState) {
- deleteInjectedCss()
- betaUIState = newBetaUIState
- css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
- deleteInjectedCss = injectCss(css)
- }
- if (state.appState.shouldClose) notificationManager.closePopup()
+start().catch(log.error)
+
+async function start() {
+
+ // create platform global
+ global.platform = new ExtensionPlatform()
+
+ // setup sentry error reporting
+ const release = global.platform.getVersion()
+ setupRaven({ release })
+
+ // inject css
+ // const css = MetaMaskUiCss()
+ // injectCss(css)
+
+ // identify window type (popup, notification)
+ const windowType = isPopupOrNotification()
+ global.METAMASK_UI_TYPE = windowType
+ closePopupIfOpen(windowType)
+
+ // setup stream to background
+ const extensionPort = extension.runtime.connect({ name: windowType })
+ const connectionStream = new PortStream(extensionPort)
+
+ // start ui
+ const container = document.getElementById('app-content')
+ startPopup({ container, connectionStream }, (err, store) => {
+ if (err) return displayCriticalError(err)
+
+ // Code commented out until we begin auto adding users to NewUI
+ // const { isMascara, identities = {}, featureFlags = {} } = store.getState().metamask
+ // const firstTime = Object.keys(identities).length === 0
+ const { isMascara, featureFlags = {} } = store.getState().metamask
+ let betaUIState = featureFlags.betaUI
+
+ // Code commented out until we begin auto adding users to NewUI
+ // const useBetaCss = isMascara || firstTime || betaUIState
+ const useBetaCss = isMascara || betaUIState
+
+ let css = useBetaCss ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
+ let deleteInjectedCss = injectCss(css)
+ let newBetaUIState
+
+ store.subscribe(() => {
+ const state = store.getState()
+ newBetaUIState = state.metamask.featureFlags.betaUI
+ if (newBetaUIState !== betaUIState) {
+ deleteInjectedCss()
+ betaUIState = newBetaUIState
+ css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
+ deleteInjectedCss = injectCss(css)
+ }
+ if (state.appState.shouldClose) notificationManager.closePopup()
+ })
})
-})
-function closePopupIfOpen (windowType) {
- if (windowType !== 'notification') {
- notificationManager.closePopup()
+ function closePopupIfOpen (windowType) {
+ if (windowType !== 'notification') {
+ // should close only chrome popup
+ notificationManager.closePopup()
+ }
+ }
+
+ function displayCriticalError (err) {
+ container.innerHTML = '<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>'
+ container.style.height = '80px'
+ log.error(err.stack)
+ throw err
}
-}
-function displayCriticalError (err) {
- container.innerHTML = '<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>'
- container.style.height = '80px'
- log.error(err.stack)
- throw err
}
diff --git a/app/scripts/vendor/raven.min.js b/app/scripts/vendor/raven.min.js
deleted file mode 100644
index b439aeae6..000000000
--- a/app/scripts/vendor/raven.min.js
+++ /dev/null
@@ -1,3 +0,0 @@
-/*! Raven.js 3.22.1 (7584197) | github.com/getsentry/raven-js */
-!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.Raven=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){function d(a){this.name="RavenConfigError",this.message=a}d.prototype=new Error,d.prototype.constructor=d,b.exports=d},{}],2:[function(a,b,c){var d=function(a,b,c){var d=a[b],e=a;if(b in a){var f="warn"===b?"warning":b;a[b]=function(){var a=[].slice.call(arguments),g=""+a.join(" "),h={level:f,logger:"console",extra:{arguments:a}};"assert"===b?a[0]===!1&&(g="Assertion failed: "+(a.slice(1).join(" ")||"console.assert"),h.extra.arguments=a.slice(1),c&&c(g,h)):c&&c(g,h),d&&Function.prototype.apply.call(d,e,a)}}};b.exports={wrapMethod:d}},{}],3:[function(a,b,c){(function(c){function d(){return+new Date}function e(a,b){return o(b)?function(c){return b(c,a)}:b}function f(){this.a=!("object"!=typeof JSON||!JSON.stringify),this.b=!n(K),this.c=!n(L),this.d=null,this.e=null,this.f=null,this.g=null,this.h=null,this.i=null,this.j={},this.k={release:J.SENTRY_RELEASE&&J.SENTRY_RELEASE.id,logger:"javascript",ignoreErrors:[],ignoreUrls:[],whitelistUrls:[],includePaths:[],headers:null,collectWindowErrors:!0,maxMessageLength:0,maxUrlLength:250,stackTraceLimit:50,autoBreadcrumbs:!0,instrument:!0,sampleRate:1},this.l={method:"POST",keepalive:!0,referrerPolicy:"origin"},this.m=0,this.n=!1,this.o=Error.stackTraceLimit,this.p=J.console||{},this.q={},this.r=[],this.s=d(),this.t=[],this.u=[],this.v=null,this.w=J.location,this.x=this.w&&this.w.href,this.y();for(var a in this.p)this.q[a]=this.p[a]}var g=a(6),h=a(7),i=a(1),j=a(5),k=j.isError,l=j.isObject,m=j.isErrorEvent,n=j.isUndefined,o=j.isFunction,p=j.isString,q=j.isArray,r=j.isEmptyObject,s=j.each,t=j.objectMerge,u=j.truncate,v=j.objectFrozen,w=j.hasKey,x=j.joinRegExp,y=j.urlencode,z=j.uuid4,A=j.htmlTreeAsString,B=j.isSameException,C=j.isSameStacktrace,D=j.parseUrl,E=j.fill,F=j.supportsFetch,G=a(2).wrapMethod,H="source protocol user pass host port path".split(" "),I=/^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/,J="undefined"!=typeof window?window:"undefined"!=typeof c?c:"undefined"!=typeof self?self:{},K=J.document,L=J.navigator;f.prototype={VERSION:"3.22.1",debug:!1,TraceKit:g,config:function(a,b){var c=this;if(c.g)return this.z("error","Error: Raven has already been configured"),c;if(!a)return c;var d=c.k;b&&s(b,function(a,b){"tags"===a||"extra"===a||"user"===a?c.j[a]=b:d[a]=b}),c.setDSN(a),d.ignoreErrors.push(/^Script error\.?$/),d.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/),d.ignoreErrors=x(d.ignoreErrors),d.ignoreUrls=!!d.ignoreUrls.length&&x(d.ignoreUrls),d.whitelistUrls=!!d.whitelistUrls.length&&x(d.whitelistUrls),d.includePaths=x(d.includePaths),d.maxBreadcrumbs=Math.max(0,Math.min(d.maxBreadcrumbs||100,100));var e={xhr:!0,console:!0,dom:!0,location:!0,sentry:!0},f=d.autoBreadcrumbs;"[object Object]"==={}.toString.call(f)?f=t(e,f):f!==!1&&(f=e),d.autoBreadcrumbs=f;var h={tryCatch:!0},i=d.instrument;return"[object Object]"==={}.toString.call(i)?i=t(h,i):i!==!1&&(i=h),d.instrument=i,g.collectWindowErrors=!!d.collectWindowErrors,c},install:function(){var a=this;return a.isSetup()&&!a.n&&(g.report.subscribe(function(){a.A.apply(a,arguments)}),a.B(),a.k.instrument&&a.k.instrument.tryCatch&&a.C(),a.k.autoBreadcrumbs&&a.D(),a.E(),a.n=!0),Error.stackTraceLimit=a.k.stackTraceLimit,this},setDSN:function(a){var b=this,c=b.F(a),d=c.path.lastIndexOf("/"),e=c.path.substr(1,d);b.G=a,b.h=c.user,b.H=c.pass&&c.pass.substr(1),b.i=c.path.substr(d+1),b.g=b.I(c),b.J=b.g+"/"+e+"api/"+b.i+"/store/",this.y()},context:function(a,b,c){return o(a)&&(c=b||[],b=a,a=void 0),this.wrap(a,b).apply(this,c)},wrap:function(a,b,c){function d(){var d=[],f=arguments.length,g=!a||a&&a.deep!==!1;for(c&&o(c)&&c.apply(this,arguments);f--;)d[f]=g?e.wrap(a,arguments[f]):arguments[f];try{return b.apply(this,d)}catch(h){throw e.K(),e.captureException(h,a),h}}var e=this;if(n(b)&&!o(a))return a;if(o(a)&&(b=a,a=void 0),!o(b))return b;try{if(b.L)return b;if(b.M)return b.M}catch(f){return b}for(var g in b)w(b,g)&&(d[g]=b[g]);return d.prototype=b.prototype,b.M=d,d.L=!0,d.N=b,d},uninstall:function(){return g.report.uninstall(),this.O(),this.P(),Error.stackTraceLimit=this.o,this.n=!1,this},captureException:function(a,b){var c=!k(a),d=!m(a),e=m(a)&&!a.error;if(c&&d||e)return this.captureMessage(a,t({trimHeadFrames:1,stacktrace:!0},b));m(a)&&(a=a.error),this.d=a;try{var f=g.computeStackTrace(a);this.Q(f,b)}catch(h){if(a!==h)throw h}return this},captureMessage:function(a,b){if(!this.k.ignoreErrors.test||!this.k.ignoreErrors.test(a)){b=b||{};var c,d=t({message:a+""},b);try{throw new Error(a)}catch(e){c=e}c.name=null;var f=g.computeStackTrace(c),h=q(f.stack)&&f.stack[1],i=h&&h.url||"";if((!this.k.ignoreUrls.test||!this.k.ignoreUrls.test(i))&&(!this.k.whitelistUrls.test||this.k.whitelistUrls.test(i))){if(this.k.stacktrace||b&&b.stacktrace){b=t({fingerprint:a,trimHeadFrames:(b.trimHeadFrames||0)+1},b);var j=this.R(f,b);d.stacktrace={frames:j.reverse()}}return this.S(d),this}}},captureBreadcrumb:function(a){var b=t({timestamp:d()/1e3},a);if(o(this.k.breadcrumbCallback)){var c=this.k.breadcrumbCallback(b);if(l(c)&&!r(c))b=c;else if(c===!1)return this}return this.u.push(b),this.u.length>this.k.maxBreadcrumbs&&this.u.shift(),this},addPlugin:function(a){var b=[].slice.call(arguments,1);return this.r.push([a,b]),this.n&&this.E(),this},setUserContext:function(a){return this.j.user=a,this},setExtraContext:function(a){return this.T("extra",a),this},setTagsContext:function(a){return this.T("tags",a),this},clearContext:function(){return this.j={},this},getContext:function(){return JSON.parse(h(this.j))},setEnvironment:function(a){return this.k.environment=a,this},setRelease:function(a){return this.k.release=a,this},setDataCallback:function(a){var b=this.k.dataCallback;return this.k.dataCallback=e(b,a),this},setBreadcrumbCallback:function(a){var b=this.k.breadcrumbCallback;return this.k.breadcrumbCallback=e(b,a),this},setShouldSendCallback:function(a){var b=this.k.shouldSendCallback;return this.k.shouldSendCallback=e(b,a),this},setTransport:function(a){return this.k.transport=a,this},lastException:function(){return this.d},lastEventId:function(){return this.f},isSetup:function(){return!!this.a&&(!!this.g||(this.ravenNotConfiguredError||(this.ravenNotConfiguredError=!0,this.z("error","Error: Raven has not been configured.")),!1))},afterLoad:function(){var a=J.RavenConfig;a&&this.config(a.dsn,a.config).install()},showReportDialog:function(a){if(K){a=a||{};var b=a.eventId||this.lastEventId();if(!b)throw new i("Missing eventId");var c=a.dsn||this.G;if(!c)throw new i("Missing DSN");var d=encodeURIComponent,e="";e+="?eventId="+d(b),e+="&dsn="+d(c);var f=a.user||this.j.user;f&&(f.name&&(e+="&name="+d(f.name)),f.email&&(e+="&email="+d(f.email)));var g=this.I(this.F(c)),h=K.createElement("script");h.async=!0,h.src=g+"/api/embed/error-page/"+e,(K.head||K.body).appendChild(h)}},K:function(){var a=this;this.m+=1,setTimeout(function(){a.m-=1})},U:function(a,b){var c,d;if(this.b){b=b||{},a="raven"+a.substr(0,1).toUpperCase()+a.substr(1),K.createEvent?(c=K.createEvent("HTMLEvents"),c.initEvent(a,!0,!0)):(c=K.createEventObject(),c.eventType=a);for(d in b)w(b,d)&&(c[d]=b[d]);if(K.createEvent)K.dispatchEvent(c);else try{K.fireEvent("on"+c.eventType.toLowerCase(),c)}catch(e){}}},V:function(a){var b=this;return function(c){if(b.W=null,b.v!==c){b.v=c;var d;try{d=A(c.target)}catch(e){d="<unknown>"}b.captureBreadcrumb({category:"ui."+a,message:d})}}},X:function(){var a=this,b=1e3;return function(c){var d;try{d=c.target}catch(e){return}var f=d&&d.tagName;if(f&&("INPUT"===f||"TEXTAREA"===f||d.isContentEditable)){var g=a.W;g||a.V("input")(c),clearTimeout(g),a.W=setTimeout(function(){a.W=null},b)}}},Y:function(a,b){var c=D(this.w.href),d=D(b),e=D(a);this.x=b,c.protocol===d.protocol&&c.host===d.host&&(b=d.relative),c.protocol===e.protocol&&c.host===e.host&&(a=e.relative),this.captureBreadcrumb({category:"navigation",data:{to:b,from:a}})},B:function(){var a=this;a.Z=Function.prototype.toString,Function.prototype.toString=function(){return"function"==typeof this&&this.L?a.Z.apply(this.N,arguments):a.Z.apply(this,arguments)}},O:function(){this.Z&&(Function.prototype.toString=this.Z)},C:function(){function a(a){return function(b,d){for(var e=new Array(arguments.length),f=0;f<e.length;++f)e[f]=arguments[f];var g=e[0];return o(g)&&(e[0]=c.wrap(g)),a.apply?a.apply(this,e):a(e[0],e[1])}}function b(a){var b=J[a]&&J[a].prototype;b&&b.hasOwnProperty&&b.hasOwnProperty("addEventListener")&&(E(b,"addEventListener",function(b){return function(d,f,g,h){try{f&&f.handleEvent&&(f.handleEvent=c.wrap(f.handleEvent))}catch(i){}var j,k,l;return e&&e.dom&&("EventTarget"===a||"Node"===a)&&(k=c.V("click"),l=c.X(),j=function(a){if(a){var b;try{b=a.type}catch(c){return}return"click"===b?k(a):"keypress"===b?l(a):void 0}}),b.call(this,d,c.wrap(f,void 0,j),g,h)}},d),E(b,"removeEventListener",function(a){return function(b,c,d,e){try{c=c&&(c.M?c.M:c)}catch(f){}return a.call(this,b,c,d,e)}},d))}var c=this,d=c.t,e=this.k.autoBreadcrumbs;E(J,"setTimeout",a,d),E(J,"setInterval",a,d),J.requestAnimationFrame&&E(J,"requestAnimationFrame",function(a){return function(b){return a(c.wrap(b))}},d);for(var f=["EventTarget","Window","Node","ApplicationCache","AudioTrackList","ChannelMergerNode","CryptoOperation","EventSource","FileReader","HTMLUnknownElement","IDBDatabase","IDBRequest","IDBTransaction","KeyOperation","MediaController","MessagePort","ModalWindow","Notification","SVGElementInstance","Screen","TextTrack","TextTrackCue","TextTrackList","WebSocket","WebSocketWorker","Worker","XMLHttpRequest","XMLHttpRequestEventTarget","XMLHttpRequestUpload"],g=0;g<f.length;g++)b(f[g])},D:function(){function a(a,c){a in c&&o(c[a])&&E(c,a,function(a){return b.wrap(a)})}var b=this,c=this.k.autoBreadcrumbs,d=b.t;if(c.xhr&&"XMLHttpRequest"in J){var e=XMLHttpRequest.prototype;E(e,"open",function(a){return function(c,d){return p(d)&&d.indexOf(b.h)===-1&&(this.$={method:c,url:d,status_code:null}),a.apply(this,arguments)}},d),E(e,"send",function(c){return function(){function d(){if(e.$&&4===e.readyState){try{e.$.status_code=e.status}catch(a){}b.captureBreadcrumb({type:"http",category:"xhr",data:e.$})}}for(var e=this,f=["onload","onerror","onprogress"],g=0;g<f.length;g++)a(f[g],e);return"onreadystatechange"in e&&o(e.onreadystatechange)?E(e,"onreadystatechange",function(a){return b.wrap(a,void 0,d)}):e.onreadystatechange=d,c.apply(this,arguments)}},d)}c.xhr&&F()&&E(J,"fetch",function(a){return function(){for(var c=new Array(arguments.length),d=0;d<c.length;++d)c[d]=arguments[d];var e,f=c[0],g="GET";if("string"==typeof f?e=f:"Request"in J&&f instanceof J.Request?(e=f.url,f.method&&(g=f.method)):e=""+f,e.indexOf(b.h)!==-1)return a.apply(this,c);c[1]&&c[1].method&&(g=c[1].method);var h={method:g,url:e,status_code:null};return a.apply(this,c).then(function(a){return h.status_code=a.status,b.captureBreadcrumb({type:"http",category:"fetch",data:h}),a})}},d),c.dom&&this.b&&(K.addEventListener?(K.addEventListener("click",b.V("click"),!1),K.addEventListener("keypress",b.X(),!1)):(K.attachEvent("onclick",b.V("click")),K.attachEvent("onkeypress",b.X())));var f=J.chrome,g=f&&f.app&&f.app.runtime,h=!g&&J.history&&history.pushState&&history.replaceState;if(c.location&&h){var i=J.onpopstate;J.onpopstate=function(){var a=b.w.href;if(b.Y(b.x,a),i)return i.apply(this,arguments)};var j=function(a){return function(){var c=arguments.length>2?arguments[2]:void 0;return c&&b.Y(b.x,c+""),a.apply(this,arguments)}};E(history,"pushState",j,d),E(history,"replaceState",j,d)}if(c.console&&"console"in J&&console.log){var k=function(a,c){b.captureBreadcrumb({message:a,level:c.level,category:"console"})};s(["debug","info","warn","error","log"],function(a,b){G(console,b,k)})}},P:function(){for(var a;this.t.length;){a=this.t.shift();var b=a[0],c=a[1],d=a[2];b[c]=d}},E:function(){var a=this;s(this.r,function(b,c){var d=c[0],e=c[1];d.apply(a,[a].concat(e))})},F:function(a){var b=I.exec(a),c={},d=7;try{for(;d--;)c[H[d]]=b[d]||""}catch(e){throw new i("Invalid DSN: "+a)}if(c.pass&&!this.k.allowSecretKey)throw new i("Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key");return c},I:function(a){var b="//"+a.host+(a.port?":"+a.port:"");return a.protocol&&(b=a.protocol+":"+b),b},A:function(){this.m||this.Q.apply(this,arguments)},Q:function(a,b){var c=this.R(a,b);this.U("handle",{stackInfo:a,options:b}),this._(a.name,a.message,a.url,a.lineno,c,b)},R:function(a,b){var c=this,d=[];if(a.stack&&a.stack.length&&(s(a.stack,function(b,e){var f=c.aa(e,a.url);f&&d.push(f)}),b&&b.trimHeadFrames))for(var e=0;e<b.trimHeadFrames&&e<d.length;e++)d[e].in_app=!1;return d=d.slice(0,this.k.stackTraceLimit)},aa:function(a,b){var c={filename:a.url,lineno:a.line,colno:a.column,"function":a.func||"?"};return a.url||(c.filename=b),c.in_app=!(this.k.includePaths.test&&!this.k.includePaths.test(c.filename)||/(Raven|TraceKit)\./.test(c["function"])||/raven\.(min\.)?js$/.test(c.filename)),c},_:function(a,b,c,d,e,f){var g=(a?a+": ":"")+(b||"");if(!this.k.ignoreErrors.test||!this.k.ignoreErrors.test(b)&&!this.k.ignoreErrors.test(g)){var h;if(e&&e.length?(c=e[0].filename||c,e.reverse(),h={frames:e}):c&&(h={frames:[{filename:c,lineno:d,in_app:!0}]}),(!this.k.ignoreUrls.test||!this.k.ignoreUrls.test(c))&&(!this.k.whitelistUrls.test||this.k.whitelistUrls.test(c))){var i=t({exception:{values:[{type:a,value:b,stacktrace:h}]},culprit:c},f);this.S(i)}}},ba:function(a){var b=this.k.maxMessageLength;if(a.message&&(a.message=u(a.message,b)),a.exception){var c=a.exception.values[0];c.value=u(c.value,b)}var d=a.request;return d&&(d.url&&(d.url=u(d.url,this.k.maxUrlLength)),d.Referer&&(d.Referer=u(d.Referer,this.k.maxUrlLength))),a.breadcrumbs&&a.breadcrumbs.values&&this.ca(a.breadcrumbs),a},ca:function(a){for(var b,c,d,e=["to","from","url"],f=0;f<a.values.length;++f)if(c=a.values[f],c.hasOwnProperty("data")&&l(c.data)&&!v(c.data)){d=t({},c.data);for(var g=0;g<e.length;++g)b=e[g],d.hasOwnProperty(b)&&d[b]&&(d[b]=u(d[b],this.k.maxUrlLength));a.values[f].data=d}},da:function(){if(this.c||this.b){var a={};return this.c&&L.userAgent&&(a.headers={"User-Agent":navigator.userAgent}),J.location&&J.location.href&&(a.url=J.location.href),this.b&&K.referrer&&(a.headers||(a.headers={}),a.headers.Referer=K.referrer),a}},y:function(){this.ea=0,this.fa=null},ga:function(){return this.ea&&d()-this.fa<this.ea},ha:function(a){var b=this.e;return!(!b||a.message!==b.message||a.culprit!==b.culprit)&&(a.stacktrace||b.stacktrace?C(a.stacktrace,b.stacktrace):!a.exception&&!b.exception||B(a.exception,b.exception))},ia:function(a){if(!this.ga()){var b=a.status;if(400===b||401===b||429===b){var c;try{c=F()?a.headers.get("Retry-After"):a.getResponseHeader("Retry-After"),c=1e3*parseInt(c,10)}catch(e){}this.ea=c?c:2*this.ea||1e3,this.fa=d()}}},S:function(a){var b=this.k,c={project:this.i,logger:b.logger,platform:"javascript"},e=this.da();if(e&&(c.request=e),a.trimHeadFrames&&delete a.trimHeadFrames,a=t(c,a),a.tags=t(t({},this.j.tags),a.tags),a.extra=t(t({},this.j.extra),a.extra),a.extra["session:duration"]=d()-this.s,this.u&&this.u.length>0&&(a.breadcrumbs={values:[].slice.call(this.u,0)}),this.j.user&&(a.user=this.j.user),b.environment&&(a.environment=b.environment),b.release&&(a.release=b.release),b.serverName&&(a.server_name=b.serverName),Object.keys(a).forEach(function(b){(null==a[b]||""===a[b]||r(a[b]))&&delete a[b]}),o(b.dataCallback)&&(a=b.dataCallback(a)||a),a&&!r(a)&&(!o(b.shouldSendCallback)||b.shouldSendCallback(a)))return this.ga()?void this.z("warn","Raven dropped error due to backoff: ",a):void("number"==typeof b.sampleRate?Math.random()<b.sampleRate&&this.ja(a):this.ja(a))},ka:function(){return z()},ja:function(a,b){var c=this,d=this.k;if(this.isSetup()){if(a=this.ba(a),!this.k.allowDuplicates&&this.ha(a))return void this.z("warn","Raven dropped repeat event: ",a);this.f=a.event_id||(a.event_id=this.ka()),this.e=a,this.z("debug","Raven about to send:",a);var e={sentry_version:"7",sentry_client:"raven-js/"+this.VERSION,sentry_key:this.h};this.H&&(e.sentry_secret=this.H);var f=a.exception&&a.exception.values[0];this.k.autoBreadcrumbs&&this.k.autoBreadcrumbs.sentry&&this.captureBreadcrumb({category:"sentry",message:f?(f.type?f.type+": ":"")+f.value:a.message,event_id:a.event_id,level:a.level||"error"});var g=this.J;(d.transport||this.la).call(this,{url:g,auth:e,data:a,options:d,onSuccess:function(){c.y(),c.U("success",{data:a,src:g}),b&&b()},onError:function(d){c.z("error","Raven transport failed to send: ",d),d.request&&c.ia(d.request),c.U("failure",{data:a,src:g}),d=d||new Error("Raven send failed (no additional details provided)"),b&&b(d)}})}},la:function(a){var b=a.url+"?"+y(a.auth),c=null,d={};if(a.options.headers&&(c=this.ma(a.options.headers)),a.options.fetchParameters&&(d=this.ma(a.options.fetchParameters)),F()){d.body=h(a.data);var e=t({},this.l),f=t(e,d);return c&&(f.headers=c),J.fetch(b,f).then(function(b){if(b.ok)a.onSuccess&&a.onSuccess();else{var c=new Error("Sentry error code: "+b.status);c.request=b,a.onError&&a.onError(c)}})["catch"](function(){a.onError&&a.onError(new Error("Sentry error code: network unavailable"))})}var g=J.XMLHttpRequest&&new J.XMLHttpRequest;if(g){var i="withCredentials"in g||"undefined"!=typeof XDomainRequest;i&&("withCredentials"in g?g.onreadystatechange=function(){if(4===g.readyState)if(200===g.status)a.onSuccess&&a.onSuccess();else if(a.onError){var b=new Error("Sentry error code: "+g.status);b.request=g,a.onError(b)}}:(g=new XDomainRequest,b=b.replace(/^https?:/,""),a.onSuccess&&(g.onload=a.onSuccess),a.onError&&(g.onerror=function(){var b=new Error("Sentry error code: XDomainRequest");b.request=g,a.onError(b)})),g.open("POST",b),c&&s(c,function(a,b){g.setRequestHeader(a,b)}),g.send(h(a.data)))}},ma:function(a){var b={};for(var c in a)if(a.hasOwnProperty(c)){var d=a[c];b[c]="function"==typeof d?d():d}return b},z:function(a){this.q[a]&&this.debug&&Function.prototype.apply.call(this.q[a],this.p,[].slice.call(arguments,1))},T:function(a,b){n(b)?delete this.j[a]:this.j[a]=t(this.j[a]||{},b)}},f.prototype.setUser=f.prototype.setUserContext,f.prototype.setReleaseContext=f.prototype.setRelease,b.exports=f}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{1:1,2:2,5:5,6:6,7:7}],4:[function(a,b,c){(function(c){var d=a(3),e="undefined"!=typeof window?window:"undefined"!=typeof c?c:"undefined"!=typeof self?self:{},f=e.Raven,g=new d;g.noConflict=function(){return e.Raven=f,g},g.afterLoad(),b.exports=g}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{3:3}],5:[function(a,b,c){(function(a){function c(a){return"object"==typeof a&&null!==a}function d(a){switch({}.toString.call(a)){case"[object Error]":return!0;case"[object Exception]":return!0;case"[object DOMException]":return!0;default:return a instanceof Error}}function e(a){return l()&&"[object ErrorEvent]"==={}.toString.call(a)}function f(a){return void 0===a}function g(a){return"function"==typeof a}function h(a){return"[object Object]"===Object.prototype.toString.call(a)}function i(a){return"[object String]"===Object.prototype.toString.call(a)}function j(a){return"[object Array]"===Object.prototype.toString.call(a)}function k(a){if(!h(a))return!1;for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}function l(){try{return new ErrorEvent(""),!0}catch(a){return!1}}function m(){if(!("fetch"in E))return!1;try{return new Headers,new Request(""),new Response,!0}catch(a){return!1}}function n(a){function b(b,c){var d=a(b)||b;return c?c(d)||d:d}return b}function o(a,b){var c,d;if(f(a.length))for(c in a)s(a,c)&&b.call(null,c,a[c]);else if(d=a.length)for(c=0;c<d;c++)b.call(null,c,a[c])}function p(a,b){return b?(o(b,function(b,c){a[b]=c}),a):a}function q(a){return!!Object.isFrozen&&Object.isFrozen(a)}function r(a,b){return!b||a.length<=b?a:a.substr(0,b)+"…"}function s(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function t(a){for(var b,c=[],d=0,e=a.length;d<e;d++)b=a[d],i(b)?c.push(b.replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1")):b&&b.source&&c.push(b.source);return new RegExp(c.join("|"),"i")}function u(a){var b=[];return o(a,function(a,c){b.push(encodeURIComponent(a)+"="+encodeURIComponent(c))}),b.join("&")}function v(a){if("string"!=typeof a)return{};var b=a.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/),c=b[6]||"",d=b[8]||"";return{protocol:b[2],host:b[4],path:b[5],relative:b[5]+c+d}}function w(){var a=E.crypto||E.msCrypto;if(!f(a)&&a.getRandomValues){var b=new Uint16Array(8);a.getRandomValues(b),b[3]=4095&b[3]|16384,b[4]=16383&b[4]|32768;var c=function(a){for(var b=a.toString(16);b.length<4;)b="0"+b;return b};return c(b[0])+c(b[1])+c(b[2])+c(b[3])+c(b[4])+c(b[5])+c(b[6])+c(b[7])}return"xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=16*Math.random()|0,c="x"===a?b:3&b|8;return c.toString(16)})}function x(a){for(var b,c=5,d=80,e=[],f=0,g=0,h=" > ",i=h.length;a&&f++<c&&(b=y(a),!("html"===b||f>1&&g+e.length*i+b.length>=d));)e.push(b),g+=b.length,a=a.parentNode;return e.reverse().join(h)}function y(a){var b,c,d,e,f,g=[];if(!a||!a.tagName)return"";if(g.push(a.tagName.toLowerCase()),a.id&&g.push("#"+a.id),b=a.className,b&&i(b))for(c=b.split(/\s+/),f=0;f<c.length;f++)g.push("."+c[f]);var h=["type","name","title","alt"];for(f=0;f<h.length;f++)d=h[f],e=a.getAttribute(d),e&&g.push("["+d+'="'+e+'"]');return g.join("")}function z(a,b){return!!(!!a^!!b)}function A(a,b){return f(a)&&f(b)}function B(a,b){return!z(a,b)&&(a=a.values[0],b=b.values[0],a.type===b.type&&a.value===b.value&&(!A(a.stacktrace,b.stacktrace)&&C(a.stacktrace,b.stacktrace)))}function C(a,b){if(z(a,b))return!1;var c=a.frames,d=b.frames;if(c.length!==d.length)return!1;for(var e,f,g=0;g<c.length;g++)if(e=c[g],f=d[g],e.filename!==f.filename||e.lineno!==f.lineno||e.colno!==f.colno||e["function"]!==f["function"])return!1;return!0}function D(a,b,c,d){var e=a[b];a[b]=c(e),a[b].L=!0,a[b].N=e,d&&d.push([a,b,e])}var E="undefined"!=typeof window?window:"undefined"!=typeof a?a:"undefined"!=typeof self?self:{};b.exports={isObject:c,isError:d,isErrorEvent:e,isUndefined:f,isFunction:g,isPlainObject:h,isString:i,isArray:j,isEmptyObject:k,supportsErrorEvent:l,supportsFetch:m,wrappedCallback:n,each:o,objectMerge:p,truncate:r,objectFrozen:q,hasKey:s,joinRegExp:t,urlencode:u,uuid4:w,htmlTreeAsString:x,htmlElementAsString:y,isSameException:B,isSameStacktrace:C,parseUrl:v,fill:D}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],6:[function(a,b,c){(function(c){function d(){return"undefined"==typeof document||null==document.location?"":document.location.href}var e=a(5),f={collectWindowErrors:!0,debug:!1},g="undefined"!=typeof window?window:"undefined"!=typeof c?c:"undefined"!=typeof self?self:{},h=[].slice,i="?",j=/^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/;f.report=function(){function a(a){m(),s.push(a)}function b(a){for(var b=s.length-1;b>=0;--b)s[b]===a&&s.splice(b,1)}function c(){n(),s=[]}function k(a,b){var c=null;if(!b||f.collectWindowErrors){for(var d in s)if(s.hasOwnProperty(d))try{s[d].apply(null,[a].concat(h.call(arguments,2)))}catch(e){c=e}if(c)throw c}}function l(a,b,c,g,h){var l=null,m=e.isErrorEvent(h)?h.error:h,n=e.isErrorEvent(a)?a.message:a;if(v)f.computeStackTrace.augmentStackTraceWithInitialElement(v,b,c,n),o();else if(m&&e.isError(m))l=f.computeStackTrace(m),k(l,!0);else{var p,r={url:b,line:c,column:g},s=void 0;if("[object String]"==={}.toString.call(n)){var p=n.match(j);p&&(s=p[1],n=p[2])}r.func=i,l={name:s,message:n,url:d(),stack:[r]},k(l,!0)}return!!q&&q.apply(this,arguments)}function m(){r||(q=g.onerror,g.onerror=l,r=!0)}function n(){r&&(g.onerror=q,r=!1,q=void 0)}function o(){var a=v,b=t;t=null,v=null,u=null,k.apply(null,[a,!1].concat(b))}function p(a,b){var c=h.call(arguments,1);if(v){if(u===a)return;o()}var d=f.computeStackTrace(a);if(v=d,u=a,t=c,setTimeout(function(){u===a&&o()},d.incomplete?2e3:0),b!==!1)throw a}var q,r,s=[],t=null,u=null,v=null;return p.subscribe=a,p.unsubscribe=b,p.uninstall=c,p}(),f.computeStackTrace=function(){function a(a){if("undefined"!=typeof a.stack&&a.stack){for(var b,c,e,f=/^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,g=/^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i,h=/^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx(?:-web)|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,j=/(\S+) line (\d+)(?: > eval line \d+)* > eval/i,k=/\((\S*)(?::(\d+))(?::(\d+))\)/,l=a.stack.split("\n"),m=[],n=(/^(.*) is undefined$/.exec(a.message),0),o=l.length;n<o;++n){if(c=f.exec(l[n])){var p=c[2]&&0===c[2].indexOf("native"),q=c[2]&&0===c[2].indexOf("eval");q&&(b=k.exec(c[2]))&&(c[2]=b[1],c[3]=b[2],c[4]=b[3]),e={url:p?null:c[2],func:c[1]||i,args:p?[c[2]]:[],line:c[3]?+c[3]:null,column:c[4]?+c[4]:null}}else if(c=h.exec(l[n]))e={url:c[2],func:c[1]||i,args:[],line:+c[3],column:c[4]?+c[4]:null};else{if(!(c=g.exec(l[n])))continue;var q=c[3]&&c[3].indexOf(" > eval")>-1;q&&(b=j.exec(c[3]))?(c[3]=b[1],c[4]=b[2],c[5]=null):0!==n||c[5]||"undefined"==typeof a.columnNumber||(m[0].column=a.columnNumber+1),e={url:c[3],func:c[1]||i,args:c[2]?c[2].split(","):[],line:c[4]?+c[4]:null,column:c[5]?+c[5]:null}}!e.func&&e.line&&(e.func=i),m.push(e)}return m.length?{name:a.name,message:a.message,url:d(),stack:m}:null}}function b(a,b,c,d){var e={url:b,line:c};if(e.url&&e.line){if(a.incomplete=!1,e.func||(e.func=i),a.stack.length>0&&a.stack[0].url===e.url){if(a.stack[0].line===e.line)return!1;if(!a.stack[0].line&&a.stack[0].func===e.func)return a.stack[0].line=e.line,!1}return a.stack.unshift(e),a.partial=!0,!0}return a.incomplete=!0,!1}function c(a,g){for(var h,j,k=/function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,l=[],m={},n=!1,o=c.caller;o&&!n;o=o.caller)if(o!==e&&o!==f.report){if(j={url:null,func:i,line:null,column:null},o.name?j.func=o.name:(h=k.exec(o.toString()))&&(j.func=h[1]),"undefined"==typeof j.func)try{j.func=h.input.substring(0,h.input.indexOf("{"))}catch(p){}m[""+o]?n=!0:m[""+o]=!0,l.push(j)}g&&l.splice(0,g);var q={name:a.name,message:a.message,url:d(),stack:l};return b(q,a.sourceURL||a.fileName,a.line||a.lineNumber,a.message||a.description),q}function e(b,e){var g=null;e=null==e?0:+e;try{if(g=a(b))return g}catch(h){if(f.debug)throw h}try{if(g=c(b,e+1))return g}catch(h){if(f.debug)throw h}return{name:b.name,message:b.message,url:d()}}return e.augmentStackTraceWithInitialElement=b,e.computeStackTraceFromStackProp=a,e}(),b.exports=f}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{5:5}],7:[function(a,b,c){function d(a,b){for(var c=0;c<a.length;++c)if(a[c]===b)return c;return-1}function e(a,b,c,d){return JSON.stringify(a,g(b,d),c)}function f(a){var b={stack:a.stack,message:a.message,name:a.name};for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b}function g(a,b){var c=[],e=[];return null==b&&(b=function(a,b){return c[0]===b?"[Circular ~]":"[Circular ~."+e.slice(0,d(c,b)).join(".")+"]"}),function(g,h){if(c.length>0){var i=d(c,this);~i?c.splice(i+1):c.push(this),~i?e.splice(i,1/0,g):e.push(g),~d(c,h)&&(h=b.call(this,g,h))}else c.push(h);return null==a?h instanceof Error?f(h):h:a.call(this,g,h)}}c=b.exports=e,c.getSerialize=g},{}]},{},[4])(4)});
-//# sourceMappingURL=raven.min.js.map \ No newline at end of file
diff --git a/development/genStates.js b/development/genStates.js
index bc274c757..0ac1059be 100644
--- a/development/genStates.js
+++ b/development/genStates.js
@@ -1,6 +1,8 @@
const fs = require('fs')
const path = require('path')
const promisify = require('pify')
+const enLocaleMessages = require('../app/_locales/en/messages.json')
+
start().catch(console.error)
@@ -12,6 +14,9 @@ async function start () {
const stateFilePath = path.join(__dirname, 'states', stateFileName)
const stateFileContent = await promisify(fs.readFile)(stateFilePath, 'utf8')
const state = JSON.parse(stateFileContent)
+
+ state.localeMessages = { en: enLocaleMessages, current: {} }
+
const stateName = stateFileName.split('.')[0].replace(/-/g, ' ', 'g')
states[stateName] = state
}))
diff --git a/development/states/add-token.json b/development/states/add-token.json
index e78393b7f..9c0f16372 100644
--- a/development/states/add-token.json
+++ b/development/states/add-token.json
@@ -106,7 +106,8 @@
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
- }
+ },
+ "currentLocale": "en"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/confirm-new-ui.json b/development/states/confirm-new-ui.json
index 6981781a9..ae3098ecb 100644
--- a/development/states/confirm-new-ui.json
+++ b/development/states/confirm-new-ui.json
@@ -128,7 +128,8 @@
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
- }
+ },
+ "currentLocale": "en"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/confirm-sig-requests.json b/development/states/confirm-sig-requests.json
index 0a691e948..b51003d11 100644
--- a/development/states/confirm-sig-requests.json
+++ b/development/states/confirm-sig-requests.json
@@ -149,7 +149,8 @@
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
- }
+ },
+ "currentLocale": "en"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/first-time.json b/development/states/first-time.json
index 4f5352992..fe9188b80 100644
--- a/development/states/first-time.json
+++ b/development/states/first-time.json
@@ -36,7 +36,8 @@
},
"shapeShiftTxList": [],
"lostAccounts": [],
- "tokens": []
+ "tokens": [],
+ "currentLocale": "en"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/send-edit.json b/development/states/send-edit.json
index 6981781a9..ae3098ecb 100644
--- a/development/states/send-edit.json
+++ b/development/states/send-edit.json
@@ -128,7 +128,8 @@
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
- }
+ },
+ "currentLocale": "en"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/send-new-ui.json b/development/states/send-new-ui.json
index a0a2c66e4..1297a9139 100644
--- a/development/states/send-new-ui.json
+++ b/development/states/send-new-ui.json
@@ -107,7 +107,8 @@
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
- }
+ },
+ "currentLocale": "en"
},
"appState": {
"menuOpen": false,
diff --git a/gulpfile.js b/gulpfile.js
index dbbb1e4ff..f57ea6206 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -339,7 +339,7 @@ function generateBundler(opts, performBundle) {
const browserifyOpts = assign({}, watchify.args, {
entries: ['./app/scripts/'+opts.filename],
plugin: 'browserify-derequire',
- debug: debug,
+ debug: true,
fullPaths: debug,
})
@@ -405,17 +405,13 @@ function bundleTask(opts) {
.pipe(buffer())
// sourcemaps
// loads map from browserify file
- .pipe(gulpif(debug, sourcemaps.init({ loadMaps: true })))
+ .pipe(sourcemaps.init({ loadMaps: true }))
// Minification
.pipe(gulpif(opts.isBuild, uglify({
- mangle: { reserved: [ 'MetamaskInpageProvider' ] },
- })))
- // Transpile to ES5
- .pipe(gulpif(opts.isBuild, babel({
- presets: ['env']
+ mangle: { reserved: [ 'MetamaskInpageProvider' ] },
})))
// writes .map file
- .pipe(gulpif(debug, sourcemaps.write('./')))
+ .pipe(sourcemaps.write(debug ? './' : '../../sourcemaps'))
// write completed bundles
.pipe(gulp.dest('./dist/firefox/scripts'))
.pipe(gulp.dest('./dist/chrome/scripts'))
diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css
index d7b146e51..5f8bbd4be 100644
--- a/mascara/src/app/first-time/index.css
+++ b/mascara/src/app/first-time/index.css
@@ -122,6 +122,10 @@
width: calc(100vw - 80px);
}
+ .unique-image {
+ width: auto;
+ }
+
.create-password__title,
.unique-image__title,
.tou__title,
@@ -221,10 +225,6 @@
max-width: 46rem;
}
-.backup-phrase {
- width: 100%;
-}
-
.create-password__title,
.unique-image__title,
.tou__title,
@@ -674,7 +674,6 @@ button.backup-phrase__confirm-seed-option:hover {
}
.buy-ether__action-content-wrapper {
- width: 360px;
display: flex;
flex-flow: column nowrap;
}
diff --git a/mascara/src/app/first-time/index.js b/mascara/src/app/first-time/index.js
index da2f6bab9..0cc3b4b0e 100644
--- a/mascara/src/app/first-time/index.js
+++ b/mascara/src/app/first-time/index.js
@@ -10,6 +10,7 @@ import ImportSeedPhraseScreen from './import-seed-phrase-screen'
import {
onboardingBuyEthView,
unMarkPasswordForgotten,
+ showModal,
} from '../../../../ui/app/actions'
class FirstTimeFlow extends Component {
@@ -19,7 +20,6 @@ class FirstTimeFlow extends Component {
seedWords: PropTypes.string,
address: PropTypes.string,
noActiveNotices: PropTypes.bool,
- goToBuyEtherView: PropTypes.func.isRequired,
};
static defaultProps = {
@@ -80,7 +80,7 @@ class FirstTimeFlow extends Component {
renderScreen () {
const {SCREEN_TYPE} = FirstTimeFlow
const {
- goToBuyEtherView,
+ openBuyEtherModal,
address,
restoreCreatePasswordScreen,
forgottenPassword,
@@ -131,7 +131,7 @@ class FirstTimeFlow extends Component {
case SCREEN_TYPE.BACK_UP_PHRASE:
return (
<BackupPhraseScreen
- next={() => goToBuyEtherView(address)}
+ next={() => openBuyEtherModal()}
/>
)
default:
@@ -167,7 +167,6 @@ export default connect(
}),
dispatch => ({
leaveImportSeedScreenState: () => dispatch(unMarkPasswordForgotten()),
- goToBuyEtherView: address => dispatch(onboardingBuyEthView(address)),
+ openBuyEtherModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER'})),
})
)(FirstTimeFlow)
-
diff --git a/old-ui/app/config.js b/old-ui/app/config.js
index 9e07cf348..7da3694ac 100644
--- a/old-ui/app/config.js
+++ b/old-ui/app/config.js
@@ -168,7 +168,6 @@ ConfigScreen.prototype.render = function () {
h('a', {
href: 'http://metamask.helpscoutdocs.com/article/36-resetting-an-account',
target: '_blank',
- onClick (event) { this.navigateTo(event.target.href) },
}, 'Read more.'),
]),
h('br'),
@@ -260,7 +259,3 @@ function currentProviderDisplay (metamaskState) {
h('span', value),
])
}
-
-ConfigScreen.prototype.navigateTo = function (url) {
- global.platform.openWindow({ url })
-}
diff --git a/package-lock.json b/package-lock.json
index d1c488b09..015828549 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -182,6 +182,48 @@
"through2": "2.0.3"
}
},
+ "@sentry/cli": {
+ "version": "1.30.3",
+ "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.30.3.tgz",
+ "integrity": "sha1-AtD3eBwe5eG+WkMSoyX76LGzcjE=",
+ "dev": true,
+ "requires": {
+ "https-proxy-agent": "2.2.0",
+ "node-fetch": "1.7.3",
+ "progress": "2.0.0",
+ "proxy-from-env": "1.0.0"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz",
+ "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "5.0.0"
+ }
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.0.tgz",
+ "integrity": "sha512-uUWcfXHvy/dwfM9bqa6AozvAjS32dZSTUYd/4SEpYKRg6LEcPLshksnQYRudM9AyNvUARMfAg5TLjUDyX/K4vA==",
+ "dev": true,
+ "requires": {
+ "agent-base": "4.2.0",
+ "debug": "3.1.0"
+ }
+ }
+ }
+ },
"@types/node": {
"version": "8.5.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.5.tgz",
@@ -2952,6 +2994,19 @@
"readdirp": "2.1.0"
}
},
+ "chromedriver": {
+ "version": "2.36.0",
+ "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.36.0.tgz",
+ "integrity": "sha512-Lq2HrigCJ4RVdIdCmchenv1rVrejNSJ7EUCQojycQo12ww3FedQx4nb+GgTdqMhjbOMTqq5+ziaiZlrEN2z1gQ==",
+ "dev": true,
+ "requires": {
+ "del": "3.0.0",
+ "extract-zip": "1.6.6",
+ "kew": "0.7.0",
+ "mkdirp": "0.5.1",
+ "request": "2.83.0"
+ }
+ },
"cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
@@ -4932,6 +4987,21 @@
"event-emitter": "0.3.5"
}
},
+ "es6-promise": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz",
+ "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==",
+ "dev": true
+ },
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+ "dev": true,
+ "requires": {
+ "es6-promise": "4.2.4"
+ }
+ },
"es6-set": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
@@ -5282,6 +5352,11 @@
"json-rpc-error": "2.0.0",
"promise-to-callback": "1.0.0"
}
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
}
}
},
@@ -5595,6 +5670,13 @@
"ethjs-util": "0.1.4",
"pify": "2.3.0",
"tape": "4.8.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ }
}
},
"ethjs-format": {
@@ -6325,6 +6407,35 @@
"is-extglob": "1.0.0"
}
},
+ "extract-zip": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz",
+ "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=",
+ "dev": true,
+ "requires": {
+ "concat-stream": "1.6.0",
+ "debug": "2.6.9",
+ "mkdirp": "0.5.0",
+ "yauzl": "2.4.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
+ "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ }
+ }
+ },
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
@@ -6433,6 +6544,15 @@
}
}
},
+ "fd-slicer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
+ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
+ "dev": true,
+ "requires": {
+ "pend": "1.2.0"
+ }
+ },
"fetch-ponyfill": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz",
@@ -6945,6 +7065,13 @@
"pify": "2.3.0",
"pinkie-promise": "2.0.1",
"rimraf": "2.6.2"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ }
}
}
}
@@ -8249,6 +8376,13 @@
"object-assign": "4.1.1",
"pify": "2.3.0",
"pinkie-promise": "2.0.1"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ }
}
},
"globjoin": {
@@ -11102,6 +11236,53 @@
"array-includes": "3.0.3"
}
},
+ "jszip": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz",
+ "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==",
+ "dev": true,
+ "requires": {
+ "core-js": "2.3.0",
+ "es6-promise": "3.0.2",
+ "lie": "3.1.1",
+ "pako": "1.0.6",
+ "readable-stream": "2.0.6"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz",
+ "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=",
+ "dev": true
+ },
+ "es6-promise": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz",
+ "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+ "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "1.0.7",
+ "string_decoder": "0.10.31",
+ "util-deprecate": "1.0.2"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ }
+ }
+ },
"just-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz",
@@ -11397,6 +11578,12 @@
"sha3": "1.2.0"
}
},
+ "kew": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
+ "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=",
+ "dev": true
+ },
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -11708,6 +11895,23 @@
"integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=",
"dev": true
},
+ "lie": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
+ "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
+ "dev": true,
+ "requires": {
+ "immediate": "3.0.6"
+ },
+ "dependencies": {
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
+ "dev": true
+ }
+ }
+ },
"liftoff": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
@@ -11739,6 +11943,13 @@
"pify": "2.3.0",
"pinkie-promise": "2.0.1",
"strip-bom": "2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ }
}
},
"loader-runner": {
@@ -16283,6 +16494,13 @@
"graceful-fs": "4.1.11",
"pify": "2.3.0",
"pinkie-promise": "2.0.1"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ }
}
},
"pathval": {
@@ -16312,6 +16530,12 @@
"sha.js": "2.4.9"
}
},
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
+ "dev": true
+ },
"percentile": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/percentile/-/percentile-1.2.0.tgz",
@@ -16323,9 +16547,9 @@
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
},
"ping-pong-stream": {
"version": "1.0.0",
@@ -16914,6 +17138,12 @@
}
}
},
+ "proxy-from-env": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
+ "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=",
+ "dev": true
+ },
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
@@ -17237,6 +17467,11 @@
"eve-raphael": "0.5.0"
}
},
+ "raven-js": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.24.0.tgz",
+ "integrity": "sha512-+/ygcWib8PXAE7Xq53j1tYxCgkzFyp9z05LYAKp2PA9KwO4Ek74q1tkGwZyPWI/FoXOgas6jNtQ7O3tdPif6uA=="
+ },
"raw-body": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
@@ -18263,6 +18498,29 @@
"safe-buffer": "5.1.1"
}
},
+ "selenium-webdriver": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
+ "dev": true,
+ "requires": {
+ "jszip": "3.1.5",
+ "rimraf": "2.6.2",
+ "tmp": "0.0.30",
+ "xml2js": "0.4.19"
+ },
+ "dependencies": {
+ "tmp": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
+ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "1.0.2"
+ }
+ }
+ }
+ },
"semaphore": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz",
@@ -19568,6 +19826,14 @@
"object-assign": "4.1.1",
"pify": "2.3.0",
"pinkie-promise": "2.0.1"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
}
},
"has-flag": {
@@ -19832,6 +20098,12 @@
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
"postcss": {
"version": "5.2.18",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
@@ -21436,6 +21708,12 @@
"integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=",
"dev": true
},
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
"replace-ext": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz",
@@ -22175,6 +22453,22 @@
"integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=",
"dev": true
},
+ "xml2js": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
+ "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
+ "dev": true,
+ "requires": {
+ "sax": "1.2.4",
+ "xmlbuilder": "9.0.7"
+ }
+ },
+ "xmlbuilder": {
+ "version": "9.0.7",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
+ "dev": true
+ },
"xmldom": {
"version": "0.1.27",
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
@@ -22242,6 +22536,15 @@
"camelcase": "3.0.0"
}
},
+ "yauzl": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
+ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
+ "dev": true,
+ "requires": {
+ "fd-slicer": "1.0.1"
+ }
+ },
"yazl": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/yazl/-/yazl-2.4.3.tgz",
diff --git a/package.json b/package.json
index 25bc27c19..2b24cc348 100644
--- a/package.json
+++ b/package.json
@@ -10,26 +10,35 @@
"mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
"mascara": "gulp build && cross-env METAMASK_DEBUG=true node ./mascara/example/server",
- "dist": "npm run dist:clear && npm install && gulp dist && npm run test:es5",
+ "dist": "npm run dist:clear && npm install && gulp dist",
"dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect",
- "test": "npm run lint && npm run test:coverage && npm run test:integration",
- "test:es5": "es-check es5 ./dist/**/*.js",
+ "test": "npm run test:unit && npm run test:integration && npm run lint",
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
"test:integration:build": "gulp build:scss",
+ "test:e2e": "METAMASK_ENV=test mocha test/e2e/metamask.spec --recursive",
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
"test:coveralls-upload": "if [ $COVERALLS_REPO_TOKEN ]; then nyc report --reporter=text-lcov | coveralls; fi",
"test:flat": "npm run test:flat:build && karma start test/flat.conf.js",
- "test:flat:build": "npm run test:flat:build:ui && npm run test:flat:build:tests",
+ "test:flat:build": "npm run test:flat:build:ui && npm run test:flat:build:tests && npm run test:flat:build:locales",
"test:flat:build:tests": "node test/integration/index.js",
"test:flat:build:states": "node development/genStates.js",
+ "test:flat:build:locales": "mkdirp dist/chrome && cp -R app/_locales dist/chrome/_locales",
"test:flat:build:ui": "npm run test:flat:build:states && browserify ./development/mock-dev.js -o ./development/bundle.js",
"test:mascara": "npm run test:mascara:build && karma start test/mascara.conf.js",
- "test:mascara:build": "mkdirp dist/mascara && npm run test:mascara:build:ui && npm run test:mascara:build:background && npm run test:mascara:build:tests",
+ "test:mascara:build": "mkdirp dist/mascara && npm run test:mascara:build:ui && npm run test:mascara:build:background && npm run test:mascara:build:tests && npm run test:mascara:build:locales",
"test:mascara:build:ui": "browserify mascara/test/test-ui.js -o dist/mascara/ui.js",
+ "test:mascara:build:locales": "mkdirp dist/chrome && cp -R app/_locales dist/chrome/_locales",
"test:mascara:build:background": "browserify mascara/src/background.js -o dist/mascara/background.js",
"test:mascara:build:tests": "browserify test/integration/lib/first-time.js -o dist/mascara/tests.js",
+ "sentry": "export RELEASE=`cat app/manifest.json| jq -r .version` && npm run sentry:release && npm run sentry:upload",
+ "sentry:release": "npm run sentry:release:new && npm run sentry:release:clean",
+ "sentry:release:new": "sentry-cli releases --org 'metamask' --project 'metamask' new $RELEASE",
+ "sentry:release:clean": "sentry-cli releases --org 'metamask' --project 'metamask' files $RELEASE delete --all",
+ "sentry:upload": "npm run sentry:upload:source && npm run sentry:upload:maps",
+ "sentry:upload:source": "for FILEPATH in ./dist/chrome/scripts/*.js; do [ -e $FILEPATH ] || continue; export FILE=`basename $FILEPATH` && echo uploading $FILE && sentry-cli releases --org 'metamask' --project 'metamask' files $RELEASE upload $FILEPATH metamask/scripts/$FILE; done;",
+ "sentry:upload:maps": "sentry-cli releases --org 'metamask' --project 'metamask' files $RELEASE upload-sourcemaps ./dist/sourcemaps/ --url-prefix 'sourcemaps' --rewrite",
"lint": "gulp lint",
"lint:fix": "gulp lint:fix",
"disc": "gulp disc --debug",
@@ -142,9 +151,11 @@
"promise-filter": "^1.1.0",
"promise-to-callback": "^1.0.0",
"pump": "^3.0.0",
+ "pify": "^3.0.0",
"pumpify": "^1.3.4",
"qrcode-npm": "0.0.3",
"ramda": "^0.24.1",
+ "raven-js": "^3.24.0",
"react": "^15.6.2",
"react-addons-css-transition-group": "^15.6.0",
"react-dom": "^15.6.2",
@@ -180,6 +191,7 @@
"xtend": "^4.0.1"
},
"devDependencies": {
+ "@sentry/cli": "^1.30.3",
"babel-core": "^6.24.1",
"babel-eslint": "^8.0.0",
"babel-plugin-transform-async-to-generator": "^6.24.1",
@@ -193,6 +205,7 @@
"brfs": "^1.4.3",
"browserify": "^16.1.1",
"chai": "^4.1.0",
+ "chromedriver": "^2.34.1",
"compression": "^1.7.1",
"coveralls": "^3.0.0",
"cross-env": "^5.1.4",
@@ -201,7 +214,6 @@
"envify": "^4.0.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-15": "^1.0.5",
- "es-check": "^2.0.2",
"eslint-plugin-chai": "0.0.1",
"eslint-plugin-mocha": "^4.9.0",
"eslint-plugin-react": "^7.4.0",
@@ -223,7 +235,7 @@
"gulp-watch": "^5.0.0",
"gulp-zip": "^4.0.0",
"isomorphic-fetch": "^2.2.1",
- "jsdom": "^11.1.0",
+ "jsdom": "^11.2.0",
"jsdom-global": "^3.0.2",
"jshint-stylish": "~2.2.1",
"karma": "^2.0.0",
@@ -246,6 +258,7 @@
"react-addons-test-utils": "^15.5.1",
"react-test-renderer": "^15.6.2",
"react-testutils-additions": "^15.2.0",
+ "selenium-webdriver": "^3.5.0",
"redux-test-utils": "^0.2.2",
"sinon": "^5.0.0",
"stylelint-config-standard": "^18.2.0",
diff --git a/test/base.conf.js b/test/base.conf.js
index adb5357e8..e2e9d44ba 100644
--- a/test/base.conf.js
+++ b/test/base.conf.js
@@ -19,11 +19,13 @@ module.exports = function(config) {
'test/integration/jquery-3.1.0.min.js',
{ pattern: 'dist/chrome/images/**/*.*', watched: false, included: false, served: true },
{ pattern: 'dist/chrome/fonts/**/*.*', watched: false, included: false, served: true },
+ { pattern: 'dist/chrome/_locales/**/*.*', watched: false, included: false, served: true },
],
proxies: {
'/images/': '/base/dist/chrome/images/',
'/fonts/': '/base/dist/chrome/fonts/',
+ '/_locales/': '/base/dist/chrome/_locales/',
},
// test results reporter to use
diff --git a/test/e2e/func.js b/test/e2e/func.js
new file mode 100644
index 000000000..733225565
--- /dev/null
+++ b/test/e2e/func.js
@@ -0,0 +1,18 @@
+require('chromedriver')
+const webdriver = require('selenium-webdriver')
+
+exports.delay = function delay (time) {
+ return new Promise(resolve => setTimeout(resolve, time))
+}
+
+
+exports.buildWebDriver = function buildWebDriver (extPath) {
+ return new webdriver.Builder()
+ .withCapabilities({
+ chromeOptions: {
+ args: [`load-extension=${extPath}`],
+ },
+ })
+ .forBrowser('chrome')
+ .build()
+}
diff --git a/test/e2e/metamask.spec.js b/test/e2e/metamask.spec.js
new file mode 100644
index 000000000..c73ba2b41
--- /dev/null
+++ b/test/e2e/metamask.spec.js
@@ -0,0 +1,122 @@
+const path = require('path')
+const assert = require('assert')
+const webdriver = require('selenium-webdriver')
+const By = webdriver.By
+const { delay, buildWebDriver } = require('./func')
+
+describe('Metamask popup page', function () {
+ let driver
+ this.seedPhase
+ this.accountAddress
+ this.timeout(0)
+
+ before(async function () {
+ const extPath = path.resolve('dist/chrome')
+ driver = buildWebDriver(extPath)
+ await driver.get('chrome://extensions-frame')
+ const elems = await driver.findElements(By.className('extension-list-item-wrapper'))
+ const extensionId = await elems[1].getAttribute('id')
+ await driver.get(`chrome-extension://${extensionId}/popup.html`)
+ await delay(500)
+ })
+
+ after(async function () {
+ await driver.quit()
+ })
+
+ describe('#onboarding', () => {
+ it('should open Metamask.io', async function () {
+ const tabs = await driver.getAllWindowHandles()
+ await driver.switchTo().window(tabs[0])
+ await delay(300)
+ })
+
+ it('should match title', async () => {
+ const title = await driver.getTitle()
+ assert.equal(title, 'MetaMask Plugin', 'title matches MetaMask Plugin')
+ })
+
+ it('should show privacy notice', async () => {
+ const privacy = await driver.findElement(By.className(
+ 'terms-header'
+ )).getText()
+ assert.equal(privacy, 'PRIVACY NOTICE', 'shows privacy notice')
+ driver.findElement(By.css(
+ 'button'
+ )).click()
+ })
+
+ it('should show terms of use', async () => {
+ await delay(300)
+ const terms = await driver.findElement(By.className(
+ 'terms-header'
+ )).getText()
+ assert.equal(terms, 'TERMS OF USE', 'shows terms of use')
+ })
+
+ it('should be unable to continue without scolling throught the terms of use', async () => {
+ const button = await driver.findElement(By.css(
+ 'button'
+ )).isEnabled()
+ assert.equal(button, false, 'disabled continue button')
+ const element = driver.findElement(By.linkText(
+ 'Attributions'
+ ))
+ await driver.executeScript('arguments[0].scrollIntoView(true)', element)
+ })
+
+ it('should be able to continue when scrolled to the bottom of terms of use', async () => {
+ const button = await driver.findElement(By.css('button'))
+ const buttonEnabled = await button.isEnabled()
+ await delay(500)
+ assert.equal(buttonEnabled, true, 'enabled continue button')
+ await button.click()
+ })
+
+ it('should accept password with length of eight', async () => {
+ await delay(300)
+ const passwordBox = await driver.findElement(By.id('password-box'))
+ const passwordBoxConfirm = await driver.findElement(By.id('password-box-confirm'))
+ const button = driver.findElement(By.css('button'))
+
+ passwordBox.sendKeys('123456789')
+ passwordBoxConfirm.sendKeys('123456789')
+ await delay(500)
+ await button.click()
+ })
+
+ it('should show value was created and seed phrase', async () => {
+ await delay(700)
+ this.seedPhase = await driver.findElement(By.className('twelve-word-phrase')).getText()
+ const continueAfterSeedPhrase = await driver.findElement(By.css('button'))
+ await continueAfterSeedPhrase.click()
+ })
+
+ it('should show lock account', async () => {
+ await delay(300)
+ await driver.findElement(By.className('sandwich-expando')).click()
+ await delay(500)
+ await driver.findElement(By.xpath('//*[@id="app-content"]/div/div[3]/span/div/li[2]')).click()
+ })
+
+ it('should accept account password after lock', async () => {
+ await delay(500)
+ await driver.findElement(By.id('password-box')).sendKeys('123456789')
+ await driver.findElement(By.css('button')).click()
+ await delay(500)
+ })
+
+ it('should show QR code', async () => {
+ await delay(300)
+ await driver.findElement(By.className('fa-ellipsis-h')).click()
+ await driver.findElement(By.xpath('//*[@id="app-content"]/div/div[4]/div/div/div[1]/flex-column/div[1]/div/span/i/div/div/li[2]')).click()
+ await delay(300)
+ })
+
+ it('should show the account address', async () => {
+ this.accountAddress = await driver.findElement(By.className('ellip-address')).getText()
+ await driver.findElement(By.className('fa-arrow-left')).click()
+ await delay(500)
+ })
+ })
+})
diff --git a/test/integration/lib/add-token.js b/test/integration/lib/add-token.js
index 42ed28dca..cc04beb21 100644
--- a/test/integration/lib/add-token.js
+++ b/test/integration/lib/add-token.js
@@ -26,7 +26,7 @@ async function runAddTokenFlowTest (assert, done) {
assert.ok($('.token-list-item').length === 0, 'no tokens added')
// Go to Add Token screen
- let addTokenButton = await queryAsync($, 'button.btn-clear.wallet-view__add-token-button')
+ let addTokenButton = await queryAsync($, 'button.btn-primary.wallet-view__add-token-button')
assert.ok(addTokenButton[0], 'add token button present')
addTokenButton[0].click()
@@ -34,26 +34,26 @@ async function runAddTokenFlowTest (assert, done) {
let addTokenWrapper = await queryAsync($, '.add-token__wrapper')
assert.ok(addTokenWrapper[0], 'add token wrapper renders')
- let addTokenTitle = await queryAsync($, '.add-token__title')
- assert.equal(addTokenTitle[0].textContent, 'Add Token', 'add token title is correct')
+ let addTokenTitle = await queryAsync($, '.add-token__header__title')
+ assert.equal(addTokenTitle[0].textContent, 'Add Tokens', 'add token title is correct')
// Cancel Add Token
- const cancelAddTokenButton = await queryAsync($, 'button.btn-cancel.add-token__button')
+ const cancelAddTokenButton = await queryAsync($, 'button.btn-secondary--lg.add-token__cancel-button')
assert.ok(cancelAddTokenButton[0], 'cancel add token button present')
cancelAddTokenButton.click()
assert.ok($('.wallet-view')[0], 'cancelled and returned to account detail wallet view')
// Return to Add Token Screen
- addTokenButton = await queryAsync($, 'button.btn-clear.wallet-view__add-token-button')
+ addTokenButton = await queryAsync($, 'button.btn-primary.wallet-view__add-token-button')
assert.ok(addTokenButton[0], 'add token button present')
addTokenButton[0].click()
// Verify Add Token Screen
addTokenWrapper = await queryAsync($, '.add-token__wrapper')
- addTokenTitle = await queryAsync($, '.add-token__title')
+ addTokenTitle = await queryAsync($, '.add-token__header__title')
assert.ok(addTokenWrapper[0], 'add token wrapper renders')
- assert.equal(addTokenTitle[0].textContent, 'Add Token', 'add token title is correct')
+ assert.equal(addTokenTitle[0].textContent, 'Add Tokens', 'add token title is correct')
// Search for token
const searchInput = await queryAsync($, 'input.add-token__input')
@@ -68,7 +68,7 @@ async function runAddTokenFlowTest (assert, done) {
tokenWrapper[0].click()
// Click Next button
- let nextButton = await queryAsync($, 'button.btn-clear.add-token__button')
+ let nextButton = await queryAsync($, 'button.btn-primary--lg')
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
nextButton[0].click()
@@ -78,8 +78,8 @@ async function runAddTokenFlowTest (assert, done) {
'Would you like to add these tokens?',
'confirm add token rendered'
)
- assert.ok($('button.btn-clear.add-token__button')[0], 'confirm add token button found')
- $('button.btn-clear.add-token__button')[0].click()
+ assert.ok($('button.btn-primary--lg')[0], 'confirm add token button found')
+ $('button.btn-primary--lg')[0].click()
// Verify added token image
let heroBalance = await queryAsync($, '.hero-balance')
@@ -87,13 +87,15 @@ async function runAddTokenFlowTest (assert, done) {
assert.ok(tokenImageUrl.indexOf(heroBalance.find('img').attr('src')) > -1, 'token added')
// Return to Add Token Screen
- addTokenButton = await queryAsync($, 'button.btn-clear.wallet-view__add-token-button')
+ addTokenButton = await queryAsync($, 'button.btn-primary.wallet-view__add-token-button')
assert.ok(addTokenButton[0], 'add token button present')
addTokenButton[0].click()
- const addCustom = await queryAsync($, '.add-token__add-custom')
- assert.ok(addCustom[0], 'add custom token button present')
- addCustom[0].click()
+ const addTokenTabs = await queryAsync($, '.add-token__header__tabs__tab')
+ assert.equal(addTokenTabs.length, 2, 'expected number of tabs')
+ assert.equal(addTokenTabs[1].textContent, 'Custom Token', 'Custom Token tab present')
+ assert.ok(addTokenTabs[1], 'add custom token tab present')
+ addTokenTabs[1].click()
// Input token contract address
const customInput = await queryAsync($, 'input.add-token__add-custom-input')
@@ -101,14 +103,15 @@ async function runAddTokenFlowTest (assert, done) {
reactTriggerChange(customInput[0])
// Click Next button
- nextButton = await queryAsync($, 'button.btn-clear.add-token__button')
+ nextButton = await queryAsync($, 'button.btn-primary--lg')
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
nextButton[0].click()
// Verify symbol length error since contract address won't return symbol
const errorMessage = await queryAsync($, '.add-token__add-custom-error-message')
assert.ok(errorMessage[0], 'error rendered')
- $('button.btn-cancel.add-token__button')[0].click()
+
+ $('button.btn-secondary--lg')[0].click()
// // Confirm Add token
// assert.equal(
@@ -116,8 +119,8 @@ async function runAddTokenFlowTest (assert, done) {
// 'Would you like to add these tokens?',
// 'confirm add token rendered'
// )
- // assert.ok($('button.btn-clear.add-token__button')[0], 'confirm add token button found')
- // $('button.btn-clear.add-token__button')[0].click()
+ // assert.ok($('button.btn-primary--lg')[0], 'confirm add token button found')
+ // $('button.btn-primary--lg')[0].click()
// // Verify added token image
// heroBalance = await queryAsync($, '.hero-balance')
diff --git a/test/integration/lib/confirm-sig-requests.js b/test/integration/lib/confirm-sig-requests.js
index 9737a2283..f1116d1a6 100644
--- a/test/integration/lib/confirm-sig-requests.js
+++ b/test/integration/lib/confirm-sig-requests.js
@@ -27,7 +27,7 @@ async function runConfirmSigRequestsTest(assert, done) {
let confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
assert.ok(confirmSigRowValue[0].textContent.match(/^\#\sTerms\sof\sUse/))
- let confirmSigSignButton = await queryAsync($, '.request-signature__footer__sign-button')
+ let confirmSigSignButton = await queryAsync($, 'button.btn-primary--lg')
confirmSigSignButton[0].click()
confirmSigHeadline = await queryAsync($, '.request-signature__headline')
@@ -39,7 +39,7 @@ async function runConfirmSigRequestsTest(assert, done) {
confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
assert.equal(confirmSigRowValue[0].textContent, '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0')
- confirmSigSignButton = await queryAsync($, '.request-signature__footer__sign-button')
+ confirmSigSignButton = await queryAsync($, 'button.btn-primary--lg')
confirmSigSignButton[0].click()
confirmSigHeadline = await queryAsync($, '.request-signature__headline')
@@ -49,7 +49,7 @@ async function runConfirmSigRequestsTest(assert, done) {
assert.equal(confirmSigRowValue[0].textContent, 'Hi, Alice!')
assert.equal(confirmSigRowValue[1].textContent, '1337')
- confirmSigSignButton = await queryAsync($, '.request-signature__footer__sign-button')
+ confirmSigSignButton = await queryAsync($, 'button.btn-primary--lg')
confirmSigSignButton[0].click()
const txView = await queryAsync($, '.tx-view')
diff --git a/test/integration/lib/mascara-first-time.js b/test/integration/lib/mascara-first-time.js
index bcbc94ff6..564852585 100644
--- a/test/integration/lib/mascara-first-time.js
+++ b/test/integration/lib/mascara-first-time.js
@@ -57,9 +57,9 @@ async function runFirstTimeUsageTest (assert, done) {
;(await findAsync(app, '.first-time-flow__button')).click()
// Deposit Ether Screen
- const buyEthTitle = (await findAsync(app, '.buy-ether__title'))[0]
- assert.equal(buyEthTitle.textContent, 'Deposit Ether', 'deposit ether screen')
- ;(await findAsync(app, '.buy-ether__do-it-later')).click()
+ const depositEthTitle = (await findAsync(app, '.page-container__title'))[0]
+ assert.equal(depositEthTitle.textContent, 'Deposit Ether', 'deposit ether screen')
+ ;(await findAsync(app, '.page-container__header-close')).click()
const menu = (await findAsync(app, '.account-menu__icon'))[0]
menu.click()
diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js
index 3fc7c79f8..163f3658c 100644
--- a/test/integration/lib/send-new-ui.js
+++ b/test/integration/lib/send-new-ui.js
@@ -21,13 +21,15 @@ global.ethQuery = {
sendTransaction: () => {},
}
+global.ethereumProvider = {}
+
async function runSendFlowTest(assert, done) {
console.log('*** start runSendFlowTest')
const selectState = await queryAsync($, 'select')
selectState.val('send new ui')
reactTriggerChange(selectState[0])
- const sendScreenButton = await queryAsync($, 'button.btn-clear.hero-balance-button')
+ const sendScreenButton = await queryAsync($, 'button.btn-primary.hero-balance-button')
assert.ok(sendScreenButton[1], 'send screen button present')
sendScreenButton[1].click()
@@ -120,7 +122,7 @@ async function runSendFlowTest(assert, done) {
'send gas field should show customized gas total converted to USD'
)
- const sendButton = await queryAsync($, 'button.btn-clear.page-container__footer-button')
+ const sendButton = await queryAsync($, 'button.btn-primary--lg.page-container__footer-button')
assert.equal(sendButton[0].textContent, 'Next', 'next button rendered')
sendButton[0].click()
await timeout()
@@ -160,7 +162,7 @@ async function runSendFlowTest(assert, done) {
sendAmountFieldInputInEdit.val('1.0')
reactTriggerChange(sendAmountFieldInputInEdit[0])
- const sendButtonInEdit = await queryAsync($, '.btn-clear.page-container__footer-button')
+ const sendButtonInEdit = await queryAsync($, '.btn-primary--lg.page-container__footer-button')
assert.equal(sendButtonInEdit[0].textContent, 'Next', 'next button in edit rendered')
sendButtonInEdit[0].click()
diff --git a/test/unit/migrations/022-test.js b/test/unit/migrations/022-test.js
new file mode 100644
index 000000000..1333d929d
--- /dev/null
+++ b/test/unit/migrations/022-test.js
@@ -0,0 +1,32 @@
+const assert = require('assert')
+const migration22 = require('../../../app/scripts/migrations/022')
+const properTime = (new Date()).getTime()
+const storage = {
+ "meta": {},
+ "data": {
+ "TransactionController": {
+ "transactions": [
+ { "status": "submitted" },
+ { "status": "submitted", "submittedTime": properTime },
+ {"status": "confirmed"},
+ ]
+ },
+ },
+}
+
+describe('storage is migrated successfully where transactions that are submitted have submittedTimes', () => {
+ it('should add submittedTime key on the txMeta if appropriate', (done) => {
+ migration22.migrate(storage)
+ .then((migratedData) => {
+ const [txMeta1, txMeta2, txMeta3] = migratedData.data.TransactionController.transactions
+ assert.equal(migratedData.meta.version, 22)
+ // should have written a submitted time
+ assert(txMeta1.submittedTime)
+ // should not have written a submitted time because it already has one
+ assert.equal(txMeta2.submittedTime, properTime)
+ // should not have written a submitted time
+ assert(!txMeta3.submittedTime)
+ done()
+ }).catch(done)
+ })
+})
diff --git a/test/unit/migrations/023-test.js b/test/unit/migrations/023-test.js
new file mode 100644
index 000000000..be432d9fa
--- /dev/null
+++ b/test/unit/migrations/023-test.js
@@ -0,0 +1,99 @@
+const assert = require('assert')
+const migration23 = require('../../../app/scripts/migrations/023')
+const properTime = (new Date()).getTime()
+const storage = {
+ "meta": {},
+ "data": {
+ "TransactionController": {
+ "transactions": [
+ ]
+ },
+ },
+}
+
+const transactions = []
+const transactions40 = []
+const transactions20 = []
+
+const txStates = [
+ 'unapproved',
+ 'approved',
+ 'signed',
+ 'submitted',
+ 'confirmed',
+ 'rejected',
+ 'failed',
+ 'dropped',
+]
+
+const deletableTxStates = [
+ 'confirmed',
+ 'rejected',
+ 'failed',
+ 'dropped',
+]
+
+let nonDeletableCount = 0
+
+let status
+while (transactions.length <= 100) {
+ status = txStates[Math.floor(Math.random() * Math.floor(txStates.length - 1))]
+ if (!deletableTxStates.find((s) => s === status)) nonDeletableCount++
+ transactions.push({status})
+}
+
+while (transactions40.length < 40) {
+ status = txStates[Math.floor(Math.random() * Math.floor(txStates.length - 1))]
+ transactions40.push({status})
+}
+
+while (transactions20.length < 20) {
+ status = txStates[Math.floor(Math.random() * Math.floor(txStates.length - 1))]
+ transactions20.push({status})
+}
+
+
+
+storage.data.TransactionController.transactions = transactions
+
+describe('storage is migrated successfully and the proper transactions are remove from state', () => {
+ it('should remove transactions that are unneeded', (done) => {
+ migration23.migrate(storage)
+ .then((migratedData) => {
+ let leftoverNonDeletableTxCount = 0
+ const migratedTransactions = migratedData.data.TransactionController.transactions
+ migratedTransactions.forEach((tx) => {
+ if (!deletableTxStates.find((s) => s === tx.status)) {
+ leftoverNonDeletableTxCount++
+ }
+ })
+ assert.equal(leftoverNonDeletableTxCount, nonDeletableCount, 'migration shouldnt delete transactions we want to keep')
+ assert((migratedTransactions.length >= 40), `should be equal or greater to 40 if they are non deletable states got ${migratedTransactions.length} transactions`)
+ done()
+ }).catch(done)
+ })
+
+ it('should not remove any transactions because 40 is the expectable limit', (done) => {
+ storage.meta.version = 22
+ storage.data.TransactionController.transactions = transactions40
+ migration23.migrate(storage)
+ .then((migratedData) => {
+ const migratedTransactions = migratedData.data.TransactionController.transactions
+
+ assert.equal(migratedTransactions.length, 40, 'migration shouldnt delete when at limit')
+ done()
+ }).catch(done)
+ })
+
+ it('should not remove any transactions because 20 txs is under the expectable limit', (done) => {
+ storage.meta.version = 22
+ storage.data.TransactionController.transactions = transactions20
+ migration23.migrate(storage)
+ .then((migratedData) => {
+ const migratedTransactions = migratedData.data.TransactionController.transactions
+ assert.equal(migratedTransactions.length, 20, 'migration shouldnt delete when under limit')
+ done()
+ }).catch(done)
+ })
+
+})
diff --git a/test/unit/tx-state-manager-test.js b/test/unit/tx-state-manager-test.js
index 220bf501f..a5ac13664 100644
--- a/test/unit/tx-state-manager-test.js
+++ b/test/unit/tx-state-manager-test.js
@@ -240,12 +240,12 @@ describe('TransactionStateManager', function () {
})
describe('#wipeTransactions', function () {
-
+
const specificAddress = '0xaa'
const otherAddress = '0xbb'
it('should remove only the transactions from a specific address', function () {
-
+
const txMetas = [
{ id: 0, status: 'unapproved', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: currentNetworkId },
{ id: 1, status: 'confirmed', txParams: { from: otherAddress, to: specificAddress }, metamaskNetworkId: currentNetworkId },
@@ -268,7 +268,7 @@ describe('TransactionStateManager', function () {
{ id: 1, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId },
{ id: 2, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId },
]
-
+
txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop))
txStateManager.wipeTransactions(specificAddress)
diff --git a/ui/app/accounts/import/index.js b/ui/app/accounts/import/index.js
index fc9031a65..b09b50ef7 100644
--- a/ui/app/accounts/import/index.js
+++ b/ui/app/accounts/import/index.js
@@ -1,43 +1,38 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const t = require('../../../i18n')
+const connect = require('../../metamask-connect')
import Select from 'react-select'
// Subviews
const JsonImportView = require('./json.js')
const PrivateKeyImportView = require('./private-key.js')
-const menuItems = [
- t('privateKey'),
- t('jsonFile'),
-]
-module.exports = connect(mapStateToProps)(AccountImportSubview)
-
-function mapStateToProps (state) {
- return {
- menuItems,
- }
-}
+module.exports = connect()(AccountImportSubview)
inherits(AccountImportSubview, Component)
function AccountImportSubview () {
Component.call(this)
}
+AccountImportSubview.prototype.getMenuItemTexts = function () {
+ return [
+ this.props.t('privateKey'),
+ this.props.t('jsonFile'),
+ ]
+}
+
AccountImportSubview.prototype.render = function () {
- const props = this.props
const state = this.state || {}
- const { menuItems } = props
+ const menuItems = this.getMenuItemTexts()
const { type } = state
return (
h('div.new-account-import-form', [
h('.new-account-import-disclaimer', [
- h('span', t('importAccountMsg')),
+ h('span', this.props.t('importAccountMsg')),
h('span', {
style: {
cursor: 'pointer',
@@ -48,12 +43,12 @@ AccountImportSubview.prototype.render = function () {
url: 'https://metamask.helpscoutdocs.com/article/17-what-are-loose-accounts',
})
},
- }, t('here')),
+ }, this.props.t('here')),
]),
h('div.new-account-import-form__select-section', [
- h('div.new-account-import-form__select-label', t('selectType')),
+ h('div.new-account-import-form__select-label', this.props.t('selectType')),
h(Select, {
className: 'new-account-import-form__select',
@@ -79,16 +74,15 @@ AccountImportSubview.prototype.render = function () {
}
AccountImportSubview.prototype.renderImportView = function () {
- const props = this.props
const state = this.state || {}
const { type } = state
- const { menuItems } = props
+ const menuItems = this.getMenuItemTexts()
const current = type || menuItems[0]
switch (current) {
- case t('privateKey'):
+ case this.props.t('privateKey'):
return h(PrivateKeyImportView)
- case t('jsonFile'):
+ case this.props.t('jsonFile'):
return h(JsonImportView)
default:
return h(JsonImportView)
diff --git a/ui/app/accounts/import/json.js b/ui/app/accounts/import/json.js
index fa25168f1..02b64858d 100644
--- a/ui/app/accounts/import/json.js
+++ b/ui/app/accounts/import/json.js
@@ -1,10 +1,10 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const FileInput = require('react-simple-file-input').default
-const t = require('../../../i18n')
+
const HELP_LINK = 'https://support.metamask.io/kb/article/7-importing-accounts'
@@ -24,11 +24,11 @@ class JsonImportSubview extends Component {
return (
h('div.new-account-import-form__json', [
- h('p', t('usedByClients')),
+ h('p', this.props.t('usedByClients')),
h('a.warning', {
href: HELP_LINK,
target: '_blank',
- }, t('fileImportFail')),
+ }, this.props.t('fileImportFail')),
h(FileInput, {
readAs: 'text',
@@ -43,23 +43,23 @@ class JsonImportSubview extends Component {
h('input.new-account-import-form__input-password', {
type: 'password',
- placeholder: t('enterPassword'),
+ placeholder: this.props.t('enterPassword'),
id: 'json-password-box',
onKeyPress: this.createKeyringOnEnter.bind(this),
}),
h('div.new-account-create-form__buttons', {}, [
- h('button.new-account-create-form__button-cancel', {
+ h('button.btn-secondary.new-account-create-form__button', {
onClick: () => this.props.goHome(),
}, [
- t('cancel'),
+ this.props.t('cancel'),
]),
- h('button.new-account-create-form__button-create', {
+ h('button.btn-primary.new-account-create-form__button', {
onClick: () => this.createNewKeychain(),
}, [
- t('import'),
+ this.props.t('import'),
]),
]),
@@ -84,14 +84,14 @@ class JsonImportSubview extends Component {
const state = this.state
if (!state) {
- const message = t('validFileImport')
+ const message = this.props.t('validFileImport')
return this.props.displayWarning(message)
}
const { fileContents } = state
if (!fileContents) {
- const message = t('needImportFile')
+ const message = this.props.t('needImportFile')
return this.props.displayWarning(message)
}
@@ -99,7 +99,7 @@ class JsonImportSubview extends Component {
const password = passwordInput.value
if (!password) {
- const message = t('needImportPassword')
+ const message = this.props.t('needImportPassword')
return this.props.displayWarning(message)
}
@@ -112,6 +112,7 @@ JsonImportSubview.propTypes = {
goHome: PropTypes.func,
displayWarning: PropTypes.func,
importNewJsonAccount: PropTypes.func,
+ t: PropTypes.func,
}
const mapStateToProps = state => {
diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js
index bc9e9384e..bdf581b01 100644
--- a/ui/app/accounts/import/private-key.js
+++ b/ui/app/accounts/import/private-key.js
@@ -1,9 +1,8 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
-const t = require('../../../i18n')
module.exports = connect(mapStateToProps, mapDispatchToProps)(PrivateKeyImportView)
@@ -34,7 +33,7 @@ PrivateKeyImportView.prototype.render = function () {
return (
h('div.new-account-import-form__private-key', [
- h('span.new-account-create-form__instruction', t('pastePrivateKey')),
+ h('span.new-account-create-form__instruction', this.props.t('pastePrivateKey')),
h('div.new-account-import-form__private-key-password-container', [
@@ -48,16 +47,16 @@ PrivateKeyImportView.prototype.render = function () {
h('div.new-account-import-form__buttons', {}, [
- h('button.new-account-create-form__button-cancel.allcaps', {
+ h('button.btn-secondary--lg.new-account-create-form__button', {
onClick: () => goHome(),
}, [
- t('cancel'),
+ this.props.t('cancel'),
]),
- h('button.new-account-create-form__button-create.allcaps', {
+ h('button.btn-primary--lg.new-account-create-form__button', {
onClick: () => this.createNewKeychain(),
}, [
- t('import'),
+ this.props.t('import'),
]),
]),
diff --git a/ui/app/accounts/import/seed.js b/ui/app/accounts/import/seed.js
index 9ffc669a2..5479a6be9 100644
--- a/ui/app/accounts/import/seed.js
+++ b/ui/app/accounts/import/seed.js
@@ -1,8 +1,7 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const t = require('../../../i18n')
+const connect = require('../../metamask-connect')
module.exports = connect(mapStateToProps)(SeedImportSubview)
@@ -21,10 +20,10 @@ SeedImportSubview.prototype.render = function () {
style: {
},
}, [
- t('pasteSeed'),
+ this.props.t('pasteSeed'),
h('textarea'),
h('br'),
- h('button', t('submit')),
+ h('button', this.props.t('submit')),
])
)
}
diff --git a/ui/app/accounts/new-account/create-form.js b/ui/app/accounts/new-account/create-form.js
index 8ef842a2a..69fbf1e35 100644
--- a/ui/app/accounts/new-account/create-form.js
+++ b/ui/app/accounts/new-account/create-form.js
@@ -1,9 +1,8 @@
const { Component } = require('react')
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const { connect } = require('react-redux')
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
-const t = require('../../../i18n')
class NewAccountCreateForm extends Component {
constructor (props) {
@@ -14,18 +13,18 @@ class NewAccountCreateForm extends Component {
this.state = {
newAccountName: '',
- defaultAccountName: t('newAccountNumberName', [newAccountNumber]),
+ defaultAccountName: this.props.t('newAccountNumberName', [newAccountNumber]),
}
}
render () {
const { newAccountName, defaultAccountName } = this.state
-
+
return h('div.new-account-create-form', [
h('div.new-account-create-form__input-label', {}, [
- t('accountName'),
+ this.props.t('accountName'),
]),
h('div.new-account-create-form__input-wrapper', {}, [
@@ -38,16 +37,16 @@ class NewAccountCreateForm extends Component {
h('div.new-account-create-form__buttons', {}, [
- h('button.new-account-create-form__button-cancel.allcaps', {
+ h('button.btn-secondary--lg.new-account-create-form__button', {
onClick: () => this.props.goHome(),
}, [
- t('cancel'),
+ this.props.t('cancel'),
]),
- h('button.new-account-create-form__button-create.allcaps', {
+ h('button.btn-primary--lg.new-account-create-form__button', {
onClick: () => this.props.createAccount(newAccountName || defaultAccountName),
}, [
- t('create'),
+ this.props.t('create'),
]),
]),
@@ -62,6 +61,7 @@ NewAccountCreateForm.propTypes = {
createAccount: PropTypes.func,
goHome: PropTypes.func,
numberOfExistingAccounts: PropTypes.number,
+ t: PropTypes.func,
}
const mapStateToProps = state => {
diff --git a/ui/app/accounts/new-account/index.js b/ui/app/accounts/new-account/index.js
index 854568c77..584016974 100644
--- a/ui/app/accounts/new-account/index.js
+++ b/ui/app/accounts/new-account/index.js
@@ -1,9 +1,8 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
-const t = require('../../../i18n')
const { getCurrentViewContext } = require('../../selectors')
const classnames = require('classnames')
@@ -46,7 +45,7 @@ AccountDetailsModal.prototype.render = function () {
h('div.new-account__header', [
- h('div.new-account__title', t('newAccount')),
+ h('div.new-account__title', this.props.t('newAccount')),
h('div.new-account__tabs', [
@@ -56,7 +55,7 @@ AccountDetailsModal.prototype.render = function () {
'new-account__tabs__unselected cursor-pointer': displayedForm !== 'CREATE',
}),
onClick: () => displayForm('CREATE'),
- }, t('createDen')),
+ }, this.props.t('createDen')),
h('div.new-account__tabs__tab', {
className: classnames('new-account__tabs__tab', {
@@ -64,7 +63,7 @@ AccountDetailsModal.prototype.render = function () {
'new-account__tabs__unselected cursor-pointer': displayedForm !== 'IMPORT',
}),
onClick: () => displayForm('IMPORT'),
- }, t('import')),
+ }, this.props.t('import')),
]),
diff --git a/ui/app/actions.js b/ui/app/actions.js
index bc7ee3d07..58240054d 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -2,6 +2,7 @@ const abi = require('human-standard-token-abi')
const getBuyEthUrl = require('../../app/scripts/lib/buy-eth-url')
const { getTokenAddressFromTokenObject } = require('./util')
const ethUtil = require('ethereumjs-util')
+const { fetchLocale } = require('../i18n-helper')
var actions = {
_setBackgroundConnection: _setBackgroundConnection,
@@ -23,7 +24,7 @@ var actions = {
NETWORK_DROPDOWN_CLOSE: 'UI_NETWORK_DROPDOWN_CLOSE',
showNetworkDropdown: showNetworkDropdown,
hideNetworkDropdown: hideNetworkDropdown,
- // menu state
+ // menu state/
getNetworkStatus: 'getNetworkStatus',
// transition state
TRANSITION_FORWARD: 'TRANSITION_FORWARD',
@@ -254,6 +255,13 @@ var actions = {
SET_USE_BLOCKIE: 'SET_USE_BLOCKIE',
setUseBlockie,
+ // locale
+ SET_CURRENT_LOCALE: 'SET_CURRENT_LOCALE',
+ SET_LOCALE_MESSAGES: 'SET_LOCALE_MESSAGES',
+ setCurrentLocale,
+ updateCurrentLocale,
+ setLocaleMessages,
+ //
// Feature Flags
setFeatureFlag,
updateFeatureFlags,
@@ -694,10 +702,10 @@ function updateSendFrom (from) {
}
}
-function updateSendTo (to) {
+function updateSendTo (to, nickname = '') {
return {
type: actions.UPDATE_SEND_TO,
- value: to,
+ value: { to, nickname },
}
}
@@ -1792,6 +1800,38 @@ function setUseBlockie (val) {
}
}
+function updateCurrentLocale (key) {
+ return (dispatch) => {
+ dispatch(actions.showLoadingIndication())
+ fetchLocale(key)
+ .then((localeMessages) => {
+ log.debug(`background.setCurrentLocale`)
+ background.setCurrentLocale(key, (err) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) {
+ return dispatch(actions.displayWarning(err.message))
+ }
+ dispatch(actions.setCurrentLocale(key))
+ dispatch(actions.setLocaleMessages(localeMessages))
+ })
+ })
+ }
+}
+
+function setCurrentLocale (key) {
+ return {
+ type: actions.SET_CURRENT_LOCALE,
+ value: key,
+ }
+}
+
+function setLocaleMessages (localeMessages) {
+ return {
+ type: actions.SET_LOCALE_MESSAGES,
+ value: localeMessages,
+ }
+}
+
function setNetworkEndpoints (networkEndpointType) {
return dispatch => {
log.debug('background.setNetworkEndpoints')
diff --git a/ui/app/add-token.js b/ui/app/add-token.js
index b3a5bdc20..472f3b736 100644
--- a/ui/app/add-token.js
+++ b/ui/app/add-token.js
@@ -2,7 +2,7 @@ const inherits = require('util').inherits
const Component = require('react').Component
const classnames = require('classnames')
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
+const connect = require('./metamask-connect')
const R = require('ramda')
const Fuse = require('fuse.js')
const contractMap = require('eth-contract-metadata')
@@ -26,7 +26,6 @@ const fuse = new Fuse(contractList, {
const actions = require('./actions')
const ethUtil = require('ethereumjs-util')
const { tokenInfoGetter } = require('./token-util')
-const t = require('../i18n')
const emptyAddr = '0x0000000000000000000000000000000000000000'
@@ -55,10 +54,10 @@ function AddTokenScreen () {
customSymbol: '',
customDecimals: '',
searchQuery: '',
- isCollapsed: true,
selectedTokens: {},
errors: {},
autoFilled: false,
+ displayedTab: 'SEARCH',
}
this.tokenAddressDidChange = this.tokenAddressDidChange.bind(this)
this.tokenSymbolDidChange = this.tokenSymbolDidChange.bind(this)
@@ -140,28 +139,31 @@ AddTokenScreen.prototype.validate = function () {
if (customAddress) {
const validAddress = ethUtil.isValidAddress(customAddress)
if (!validAddress) {
- errors.customAddress = t('invalidAddress')
+ errors.customAddress = this.props.t('invalidAddress')
}
- const validDecimals = customDecimals !== null && customDecimals >= 0 && customDecimals < 36
+ const validDecimals = customDecimals !== null
+ && customDecimals !== ''
+ && customDecimals >= 0
+ && customDecimals < 36
if (!validDecimals) {
- errors.customDecimals = t('decimalsMustZerotoTen')
+ errors.customDecimals = this.props.t('decimalsMustZerotoTen')
}
const symbolLen = customSymbol.trim().length
const validSymbol = symbolLen > 0 && symbolLen < 10
if (!validSymbol) {
- errors.customSymbol = t('symbolBetweenZeroTen')
+ errors.customSymbol = this.props.t('symbolBetweenZeroTen')
}
const ownAddress = identitiesList.includes(standardAddress)
if (ownAddress) {
- errors.customAddress = t('personalAddressDetected')
+ errors.customAddress = this.props.t('personalAddressDetected')
}
const tokenAlreadyAdded = this.checkExistingAddresses(customAddress)
if (tokenAlreadyAdded) {
- errors.customAddress = t('tokenAlreadyAdded')
+ errors.customAddress = this.props.t('tokenAlreadyAdded')
}
} else if (
Object.entries(selectedTokens)
@@ -169,7 +171,7 @@ AddTokenScreen.prototype.validate = function () {
isEmpty && !isSelected
), true)
) {
- errors.tokenSelector = t('mustSelectOne')
+ errors.tokenSelector = this.props.t('mustSelectOne')
}
return {
@@ -192,14 +194,14 @@ AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address)
AddTokenScreen.prototype.renderCustomForm = function () {
const { autoFilled, customAddress, customSymbol, customDecimals, errors } = this.state
- return !this.state.isCollapsed && (
+ return (
h('div.add-token__add-custom-form', [
h('div', {
className: classnames('add-token__add-custom-field', {
'add-token__add-custom-field--error': errors.customAddress,
}),
}, [
- h('div.add-token__add-custom-label', t('tokenAddress')),
+ h('div.add-token__add-custom-label', this.props.t('tokenAddress')),
h('input.add-token__add-custom-input', {
type: 'text',
onChange: this.tokenAddressDidChange,
@@ -212,7 +214,7 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customSymbol,
}),
}, [
- h('div.add-token__add-custom-label', t('tokenSymbol')),
+ h('div.add-token__add-custom-label', this.props.t('tokenSymbol')),
h('input.add-token__add-custom-input', {
type: 'text',
onChange: this.tokenSymbolDidChange,
@@ -226,7 +228,7 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customDecimals,
}),
}, [
- h('div.add-token__add-custom-label', t('decimal')),
+ h('div.add-token__add-custom-label', this.props.t('decimal')),
h('input.add-token__add-custom-input', {
type: 'number',
onChange: this.tokenDecimalsDidChange,
@@ -247,33 +249,36 @@ AddTokenScreen.prototype.renderTokenList = function () {
})
const results = [...addressSearchResult, ...fuseSearchResult]
- return Array(6).fill(undefined)
- .map((_, i) => {
- const { logo, symbol, name, address } = results[i] || {}
- const tokenAlreadyAdded = this.checkExistingAddresses(address)
- return Boolean(logo || symbol || name) && (
- h('div.add-token__token-wrapper', {
- className: classnames({
- 'add-token__token-wrapper--selected': selectedTokens[address],
- 'add-token__token-wrapper--disabled': tokenAlreadyAdded,
- }),
- onClick: () => !tokenAlreadyAdded && this.toggleToken(address, results[i]),
- }, [
- h('div.add-token__token-icon', {
- style: {
- backgroundImage: logo && `url(images/contract/${logo})`,
- },
- }),
- h('div.add-token__token-data', [
- h('div.add-token__token-symbol', symbol),
- h('div.add-token__token-name', name),
- ]),
- // tokenAlreadyAdded && (
- // h('div.add-token__token-message', 'Already added')
- // ),
- ])
- )
- })
+ return h('div', [
+ results.length > 0 && h('div.add-token__token-icons-title', this.props.t('popularTokens')),
+ h('div.add-token__token-icons-container', Array(6).fill(undefined)
+ .map((_, i) => {
+ const { logo, symbol, name, address } = results[i] || {}
+ const tokenAlreadyAdded = this.checkExistingAddresses(address)
+ return Boolean(logo || symbol || name) && (
+ h('div.add-token__token-wrapper', {
+ className: classnames({
+ 'add-token__token-wrapper--selected': selectedTokens[address],
+ 'add-token__token-wrapper--disabled': tokenAlreadyAdded,
+ }),
+ onClick: () => !tokenAlreadyAdded && this.toggleToken(address, results[i]),
+ }, [
+ h('div.add-token__token-icon', {
+ style: {
+ backgroundImage: logo && `url(images/contract/${logo})`,
+ },
+ }),
+ h('div.add-token__token-data', [
+ h('div.add-token__token-symbol', symbol),
+ h('div.add-token__token-name', name),
+ ]),
+ // tokenAlreadyAdded && (
+ // h('div.add-token__token-message', 'Already added')
+ // ),
+ ])
+ )
+ })),
+ ])
}
AddTokenScreen.prototype.renderConfirmation = function () {
@@ -300,11 +305,10 @@ AddTokenScreen.prototype.renderConfirmation = function () {
h('div.add-token', [
h('div.add-token__wrapper', [
h('div.add-token__title-container.add-token__confirmation-title', [
- h('div.add-token__title', t('addToken')),
- h('div.add-token__description', t('likeToAddTokens')),
+ h('div.add-token__description', this.props.t('likeToAddTokens')),
]),
h('div.add-token__content-container.add-token__confirmation-content', [
- h('div.add-token__description.add-token__confirmation-description', t('balances')),
+ h('div.add-token__description.add-token__confirmation-description', this.props.t('balances')),
h('div.add-token__confirmation-token-list',
Object.entries(tokens)
.map(([ address, token ]) => (
@@ -321,63 +325,97 @@ AddTokenScreen.prototype.renderConfirmation = function () {
]),
]),
h('div.add-token__buttons', [
- h('button.btn-cancel.add-token__button', {
+ h('button.btn-secondary--lg.add-token__cancel-button', {
onClick: () => this.setState({ isShowingConfirmation: false }),
- }, t('back')),
- h('button.btn-clear.add-token__button', {
+ }, this.props.t('back')),
+ h('button.btn-primary--lg', {
onClick: () => addTokens(tokens).then(goHome),
- }, t('addTokens')),
+ }, this.props.t('addTokens')),
]),
])
)
}
-AddTokenScreen.prototype.render = function () {
- const { isCollapsed, errors, isShowingConfirmation } = this.state
- const { goHome } = this.props
+AddTokenScreen.prototype.displayTab = function (selectedTab) {
+ this.setState({ displayedTab: selectedTab })
+}
- return isShowingConfirmation
- ? this.renderConfirmation()
- : (
- h('div.add-token', [
- h('div.add-token__wrapper', [
- h('div.add-token__title-container', [
- h('div.add-token__title', t('addToken')),
- h('div.add-token__description', t('tokenWarning1')),
- h('div.add-token__description', t('tokenSelection')),
- ]),
- h('div.add-token__content-container', [
- h('div.add-token__input-container', [
- h('input.add-token__input', {
- type: 'text',
- placeholder: t('search'),
- onChange: e => this.setState({ searchQuery: e.target.value }),
- }),
- h('div.add-token__search-input-error-message', errors.tokenSelector),
- ]),
- h(
- 'div.add-token__token-icons-container',
- this.renderTokenList(),
- ),
+AddTokenScreen.prototype.renderTabs = function () {
+ const { displayedTab, errors } = this.state
+
+ return displayedTab === 'CUSTOM_TOKEN'
+ ? this.renderCustomForm()
+ : h('div', [
+ h('div.add-token__wrapper', [
+ h('div.add-token__content-container', [
+ h('div.add-token__info-box', [
+ h('div.add-token__info-box__close'),
+ h('div.add-token__info-box__title', this.props.t('whatsThis')),
+ h('div.add-token__info-box__copy', this.props.t('keepTrackTokens')),
+ h('div.add-token__info-box__copy--blue', this.props.t('learnMore')),
]),
- h('div.add-token__footers', [
- h('div.add-token__add-custom', {
- onClick: () => this.setState({ isCollapsed: !isCollapsed }),
- }, [
- t('addCustomToken'),
- h(`i.fa.fa-angle-${isCollapsed ? 'down' : 'up'}`),
- ]),
- this.renderCustomForm(),
+ h('div.add-token__input-container', [
+ h('input.add-token__input', {
+ type: 'text',
+ placeholder: this.props.t('searchTokens'),
+ onChange: e => this.setState({ searchQuery: e.target.value }),
+ }),
+ h('div.add-token__search-input-error-message', errors.tokenSelector),
]),
+ this.renderTokenList(),
]),
- h('div.add-token__buttons', [
- h('button.btn-cancel.add-token__button', {
- onClick: goHome,
- }, t('cancel')),
- h('button.btn-clear.add-token__button', {
- onClick: this.onNext,
- }, t('next')),
+ ]),
+ ])
+}
+
+AddTokenScreen.prototype.render = function () {
+ const {
+ isShowingConfirmation,
+ displayedTab,
+ } = this.state
+ const { goHome } = this.props
+
+ return h('div.add-token', [
+ h('div.add-token__header', [
+ h('div.add-token__header__cancel', {
+ onClick: () => goHome(),
+ }, [
+ h('i.fa.fa-angle-left.fa-lg'),
+ h('span', this.props.t('cancel')),
]),
- ])
- )
+ h('div.add-token__header__title', this.props.t('addTokens')),
+ !isShowingConfirmation && h('div.add-token__header__tabs', [
+
+ h('div.add-token__header__tabs__tab', {
+ className: classnames('add-token__header__tabs__tab', {
+ 'add-token__header__tabs__selected': displayedTab === 'SEARCH',
+ 'add-token__header__tabs__unselected cursor-pointer': displayedTab !== 'SEARCH',
+ }),
+ onClick: () => this.displayTab('SEARCH'),
+ }, this.props.t('search')),
+
+ h('div.add-token__header__tabs__tab', {
+ className: classnames('add-token__header__tabs__tab', {
+ 'add-token__header__tabs__selected': displayedTab === 'CUSTOM_TOKEN',
+ 'add-token__header__tabs__unselected cursor-pointer': displayedTab !== 'CUSTOM_TOKEN',
+ }),
+ onClick: () => this.displayTab('CUSTOM_TOKEN'),
+ }, this.props.t('customToken')),
+
+ ]),
+ ]),
+//
+ isShowingConfirmation
+ ? this.renderConfirmation()
+ : this.renderTabs(),
+
+ !isShowingConfirmation && h('div.add-token__buttons', [
+ h('button.btn-secondary--lg.add-token__cancel-button', {
+ onClick: goHome,
+ }, this.props.t('cancel')),
+ h('button.btn-primary--lg.add-token__confirm-button', {
+ onClick: this.onNext,
+ }, this.props.t('next')),
+ ]),
+ ])
}
diff --git a/ui/app/app.js b/ui/app/app.js
index 6d9296131..d23238bab 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -1,10 +1,9 @@
const inherits = require('util').inherits
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('./metamask-connect')
const h = require('react-hyperscript')
const actions = require('./actions')
const classnames = require('classnames')
-const t = require('../i18n')
// mascara
const MascaraFirstTime = require('../../mascara/src/app/first-time').default
@@ -294,8 +293,8 @@ App.prototype.renderAppBar = function () {
// metamask name
h('.flex-row', [
- h('h1', t('appName')),
- h('div.beta-label', t('beta')),
+ h('h1', this.props.t('appName')),
+ h('div.beta-label', this.props.t('beta')),
]),
]),
@@ -386,7 +385,7 @@ App.prototype.renderPrimary = function () {
isUnlocked,
} = props
const isMascaraOnboarding = isMascara && isOnboarding
- const isBetaUIOnboarding = betaUI && isOnboarding && !props.isPopup && !isRevealingSeedWords
+ const isBetaUIOnboarding = betaUI && isOnboarding
if (!welcomeScreenSeen && betaUI && !isInitialized && !isUnlocked) {
return h(WelcomeScreen)
@@ -397,7 +396,7 @@ App.prototype.renderPrimary = function () {
}
// notices
- if (!props.noActiveNotices) {
+ if (!props.noActiveNotices && !betaUI) {
log.debug('rendering notice screen for unread notices.')
return h(NoticeScreen, {
notice: props.lastUnreadNotice,
@@ -418,7 +417,7 @@ App.prototype.renderPrimary = function () {
return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
} else if (!props.isInitialized && !props.isUnlocked && !isRevealingSeedWords) {
log.debug('rendering menu screen')
- return props.isPopup
+ return !betaUI
? h(OldUIInitializeMenuScreen, {key: 'menuScreenInit'})
: h(InitializeMenuScreen, {key: 'menuScreenInit'})
}
@@ -557,15 +556,15 @@ App.prototype.getConnectingLabel = function () {
let name
if (providerName === 'mainnet') {
- name = t('connectingToMainnet')
+ name = this.props.t('connectingToMainnet')
} else if (providerName === 'ropsten') {
- name = t('connectingToRopsten')
+ name = this.props.t('connectingToRopsten')
} else if (providerName === 'kovan') {
- name = t('connectingToRopsten')
+ name = this.props.t('connectingToRopsten')
} else if (providerName === 'rinkeby') {
- name = t('connectingToRinkeby')
+ name = this.props.t('connectingToRinkeby')
} else {
- name = t('connectingToUnknown')
+ name = this.props.t('connectingToUnknown')
}
return name
@@ -578,15 +577,15 @@ App.prototype.getNetworkName = function () {
let name
if (providerName === 'mainnet') {
- name = t('mainnet')
+ name = this.props.t('mainnet')
} else if (providerName === 'ropsten') {
- name = t('ropsten')
+ name = this.props.t('ropsten')
} else if (providerName === 'kovan') {
- name = t('kovan')
+ name = this.props.t('kovan')
} else if (providerName === 'rinkeby') {
- name = t('rinkeby')
+ name = this.props.t('rinkeby')
} else {
- name = t('unknownNetwork')
+ name = this.props.t('unknownNetwork')
}
return name
diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js
index 1612d7b6a..84678fee6 100644
--- a/ui/app/components/account-dropdowns.js
+++ b/ui/app/components/account-dropdowns.js
@@ -3,13 +3,12 @@ const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const actions = require('../actions')
const genAccountLink = require('etherscan-link').createAccountLink
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const Dropdown = require('./dropdown').Dropdown
const DropdownMenuItem = require('./dropdown').DropdownMenuItem
const Identicon = require('./identicon')
const ethUtil = require('ethereumjs-util')
const copyToClipboard = require('copy-to-clipboard')
-const t = require('../../i18n')
class AccountDropdowns extends Component {
constructor (props) {
@@ -80,7 +79,7 @@ class AccountDropdowns extends Component {
try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
- return isLoose ? h('.keyring-label.allcaps', t('loose')) : null
+ return isLoose ? h('.keyring-label.allcaps', this.props.t('loose')) : null
} catch (e) { return }
}
@@ -130,7 +129,7 @@ class AccountDropdowns extends Component {
diameter: 32,
},
),
- h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, t('createAccount')),
+ h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, this.props.t('createAccount')),
],
),
h(
@@ -155,7 +154,7 @@ class AccountDropdowns extends Component {
fontSize: '24px',
marginBottom: '5px',
},
- }, t('importAccount')),
+ }, this.props.t('importAccount')),
]
),
]
@@ -193,7 +192,7 @@ class AccountDropdowns extends Component {
global.platform.openWindow({ url })
},
},
- t('etherscanView'),
+ this.props.t('etherscanView'),
),
h(
DropdownMenuItem,
@@ -205,7 +204,7 @@ class AccountDropdowns extends Component {
actions.showQrView(selected, identity ? identity.name : '')
},
},
- t('showQRCode'),
+ this.props.t('showQRCode'),
),
h(
DropdownMenuItem,
@@ -217,7 +216,7 @@ class AccountDropdowns extends Component {
copyToClipboard(checkSumAddress)
},
},
- t('copyAddress'),
+ this.props.t('copyAddress'),
),
h(
DropdownMenuItem,
@@ -227,7 +226,7 @@ class AccountDropdowns extends Component {
actions.requestAccountExport()
},
},
- t('exportPrivateKey'),
+ this.props.t('exportPrivateKey'),
),
]
)
@@ -301,6 +300,7 @@ AccountDropdowns.propTypes = {
style: PropTypes.object,
enableAccountOptions: PropTypes.bool,
enableAccountsSelector: PropTypes.bool,
+ t: PropTypes.func,
}
const mapDispatchToProps = (dispatch) => {
diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js
index 5637bc8d0..8889f88a7 100644
--- a/ui/app/components/account-export.js
+++ b/ui/app/components/account-export.js
@@ -5,8 +5,7 @@ const exportAsFile = require('../util').exportAsFile
const copyToClipboard = require('copy-to-clipboard')
const actions = require('../actions')
const ethUtil = require('ethereumjs-util')
-const connect = require('react-redux').connect
-const t = require('../../i18n')
+const connect = require('../metamask-connect')
module.exports = connect(mapStateToProps)(ExportAccountView)
@@ -36,7 +35,7 @@ ExportAccountView.prototype.render = function () {
if (notExporting) return h('div')
if (exportRequested) {
- const warning = t('exportPrivateKeyWarning')
+ const warning = this.props.t('exportPrivateKeyWarning')
return (
h('div', {
style: {
@@ -54,7 +53,7 @@ ExportAccountView.prototype.render = function () {
h('p.error', warning),
h('input#exportAccount.sizing-input', {
type: 'password',
- placeholder: t('confirmPassword').toLowerCase(),
+ placeholder: this.props.t('confirmPassword').toLowerCase(),
onKeyPress: this.onExportKeyPress.bind(this),
style: {
position: 'relative',
@@ -75,10 +74,10 @@ ExportAccountView.prototype.render = function () {
style: {
marginRight: '10px',
},
- }, t('submit')),
+ }, this.props.t('submit')),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
- }, t('cancel')),
+ }, this.props.t('cancel')),
]),
(this.props.warning) && (
h('span.error', {
@@ -99,7 +98,7 @@ ExportAccountView.prototype.render = function () {
margin: '0 20px',
},
}, [
- h('label', t('copyPrivateKey') + ':'),
+ h('label', this.props.t('copyPrivateKey') + ':'),
h('p.error.cursor-pointer', {
style: {
textOverflow: 'ellipsis',
@@ -113,13 +112,13 @@ ExportAccountView.prototype.render = function () {
}, plainKey),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
- }, t('done')),
+ }, this.props.t('done')),
h('button', {
style: {
marginLeft: '10px',
},
onClick: () => exportAsFile(`MetaMask ${nickname} Private Key`, plainKey),
- }, t('saveAsFile')),
+ }, this.props.t('saveAsFile')),
])
}
}
diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js
index e838e8916..a9120f9db 100644
--- a/ui/app/components/account-menu/index.js
+++ b/ui/app/components/account-menu/index.js
@@ -1,12 +1,11 @@
const inherits = require('util').inherits
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const h = require('react-hyperscript')
const actions = require('../../actions')
const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu')
const Identicon = require('../identicon')
const { formatBalance } = require('../../util')
-const t = require('../../../i18n')
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountMenu)
@@ -71,10 +70,10 @@ AccountMenu.prototype.render = function () {
h(Item, {
className: 'account-menu__header',
}, [
- t('myAccounts'),
+ this.props.t('myAccounts'),
h('button.account-menu__logout-button', {
onClick: lockMetamask,
- }, t('logout')),
+ }, this.props.t('logout')),
]),
h(Divider),
h('div.account-menu__accounts', this.renderAccounts()),
@@ -82,23 +81,23 @@ AccountMenu.prototype.render = function () {
h(Item, {
onClick: () => showNewAccountPage('CREATE'),
icon: h('img.account-menu__item-icon', { src: 'images/plus-btn-white.svg' }),
- text: t('createAccount'),
+ text: this.props.t('createAccount'),
}),
h(Item, {
onClick: () => showNewAccountPage('IMPORT'),
icon: h('img.account-menu__item-icon', { src: 'images/import-account.svg' }),
- text: t('importAccount'),
+ text: this.props.t('importAccount'),
}),
h(Divider),
h(Item, {
onClick: showInfoPage,
icon: h('img', { src: 'images/mm-info-icon.svg' }),
- text: t('infoHelp'),
+ text: this.props.t('infoHelp'),
}),
h(Item, {
onClick: showConfigPage,
icon: h('img.account-menu__item-icon', { src: 'images/settings.svg' }),
- text: t('settings'),
+ text: this.props.t('settings'),
}),
])
}
@@ -156,6 +155,6 @@ AccountMenu.prototype.indicateIfLoose = function (keyring) {
try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
- return isLoose ? h('.keyring-label.allcaps', t('imported')) : null
+ return isLoose ? h('.keyring-label.allcaps', this.props.t('imported')) : null
} catch (e) { return }
}
diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js
index d591ab455..f6292e358 100644
--- a/ui/app/components/balance-component.js
+++ b/ui/app/components/balance-component.js
@@ -1,5 +1,5 @@
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const TokenBalance = require('./token-balance')
diff --git a/ui/app/components/bn-as-decimal-input.js b/ui/app/components/bn-as-decimal-input.js
index 70701b039..0ace2b840 100644
--- a/ui/app/components/bn-as-decimal-input.js
+++ b/ui/app/components/bn-as-decimal-input.js
@@ -4,9 +4,9 @@ const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const extend = require('xtend')
-const t = require('../../i18n')
+const connect = require('../metamask-connect')
-module.exports = BnAsDecimalInput
+module.exports = connect()(BnAsDecimalInput)
inherits(BnAsDecimalInput, Component)
function BnAsDecimalInput () {
@@ -137,13 +137,13 @@ BnAsDecimalInput.prototype.constructWarning = function () {
let message = name ? name + ' ' : ''
if (min && max) {
- message += t('betweenMinAndMax', [`${newMin} ${suffix}`, `${newMax} ${suffix}`])
+ message += this.props.t('betweenMinAndMax', [`${newMin} ${suffix}`, `${newMax} ${suffix}`])
} else if (min) {
- message += t('greaterThanMin', [`${newMin} ${suffix}`])
+ message += this.props.t('greaterThanMin', [`${newMin} ${suffix}`])
} else if (max) {
- message += t('lessThanMax', [`${newMax} ${suffix}`])
+ message += this.props.t('lessThanMax', [`${newMax} ${suffix}`])
} else {
- message += t('invalidInput')
+ message += this.props.t('invalidInput')
}
return message
diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js
index 1e277a94b..eafa2af91 100644
--- a/ui/app/components/buy-button-subview.js
+++ b/ui/app/components/buy-button-subview.js
@@ -1,7 +1,7 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const actions = require('../actions')
const CoinbaseForm = require('./coinbase-form')
const ShapeshiftForm = require('./shapeshift-form')
@@ -9,7 +9,6 @@ const Loading = require('./loading')
const AccountPanel = require('./account-panel')
const RadioList = require('./custom-radio-list')
const networkNames = require('../../../app/scripts/config.js').networkNames
-const t = require('../../i18n')
module.exports = connect(mapStateToProps)(BuyButtonSubview)
@@ -77,7 +76,7 @@ BuyButtonSubview.prototype.headerSubview = function () {
paddingTop: '4px',
paddingBottom: '4px',
},
- }, t('depositEth')),
+ }, this.props.t('depositEth')),
]),
// loading indication
@@ -119,7 +118,7 @@ BuyButtonSubview.prototype.headerSubview = function () {
paddingTop: '4px',
paddingBottom: '4px',
},
- }, t('selectService')),
+ }, this.props.t('selectService')),
]),
])
@@ -144,7 +143,7 @@ BuyButtonSubview.prototype.primarySubview = function () {
case '4':
case '42':
const networkName = networkNames[network]
- const label = `${networkName} ${t('testFaucet')}`
+ const label = `${networkName} ${this.props.t('testFaucet')}`
return (
h('div.flex-column', {
style: {
@@ -165,14 +164,14 @@ BuyButtonSubview.prototype.primarySubview = function () {
style: {
marginTop: '15px',
},
- }, t('borrowDharma'))
+ }, this.props.t('borrowDharma'))
) : null,
])
)
default:
return (
- h('h2.error', t('unknownNetworkId'))
+ h('h2.error', this.props.t('unknownNetworkId'))
)
}
@@ -204,8 +203,8 @@ BuyButtonSubview.prototype.mainnetSubview = function () {
'ShapeShift',
],
subtext: {
- 'Coinbase': `${t('crypto')}/${t('fiat')} (${t('usaOnly')})`,
- 'ShapeShift': t('crypto'),
+ 'Coinbase': `${this.props.t('crypto')}/${this.props.t('fiat')} (${this.props.t('usaOnly')})`,
+ 'ShapeShift': this.props.t('crypto'),
},
onClick: this.radioHandler.bind(this),
}),
diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js
index e442b43d5..0f980fbd5 100644
--- a/ui/app/components/coinbase-form.js
+++ b/ui/app/components/coinbase-form.js
@@ -1,9 +1,8 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const actions = require('../actions')
-const t = require('../../i18n')
module.exports = connect(mapStateToProps)(CoinbaseForm)
@@ -38,11 +37,11 @@ CoinbaseForm.prototype.render = function () {
}, [
h('button.btn-green', {
onClick: this.toCoinbase.bind(this),
- }, t('continueToCoinbase')),
+ }, this.props.t('continueToCoinbase')),
h('button.btn-red', {
onClick: () => props.dispatch(actions.goHome()),
- }, t('cancel')),
+ }, this.props.t('cancel')),
]),
])
}
diff --git a/ui/app/components/copyButton.js b/ui/app/components/copyButton.js
index 355f78d45..ea1c43d54 100644
--- a/ui/app/components/copyButton.js
+++ b/ui/app/components/copyButton.js
@@ -2,11 +2,11 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const copyToClipboard = require('copy-to-clipboard')
-const t = require('../../i18n')
+const connect = require('../metamask-connect')
const Tooltip = require('./tooltip')
-module.exports = CopyButton
+module.exports = connect()(CopyButton)
inherits(CopyButton, Component)
function CopyButton () {
@@ -23,7 +23,7 @@ CopyButton.prototype.render = function () {
const value = props.value
const copied = state.copied
- const message = copied ? t('copiedButton') : props.title || t('copyButton')
+ const message = copied ? this.props.t('copiedButton') : props.title || this.props.t('copyButton')
return h('.copy-button', {
style: {
diff --git a/ui/app/components/copyable.js b/ui/app/components/copyable.js
index fca7d3863..28def9adb 100644
--- a/ui/app/components/copyable.js
+++ b/ui/app/components/copyable.js
@@ -4,9 +4,9 @@ const inherits = require('util').inherits
const Tooltip = require('./tooltip')
const copyToClipboard = require('copy-to-clipboard')
-const t = require('../../i18n')
+const connect = require('../metamask-connect')
-module.exports = Copyable
+module.exports = connect()(Copyable)
inherits(Copyable, Component)
function Copyable () {
@@ -23,7 +23,7 @@ Copyable.prototype.render = function () {
const { copied } = state
return h(Tooltip, {
- title: copied ? t('copiedExclamation') : t('copy'),
+ title: copied ? this.props.t('copiedExclamation') : this.props.t('copy'),
position: 'bottom',
}, h('span', {
style: {
diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js
index d8384c19d..8234f8d19 100644
--- a/ui/app/components/customize-gas-modal/index.js
+++ b/ui/app/components/customize-gas-modal/index.js
@@ -1,9 +1,8 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
-const t = require('../../../i18n')
const GasModalCard = require('./gas-modal-card')
const ethUtil = require('ethereumjs-util')
@@ -150,7 +149,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
})
if (!balanceIsSufficient) {
- error = t('balanceIsInsufficientGas')
+ error = this.props.t('balanceIsInsufficientGas')
}
const gasLimitTooLow = gasLimit && conversionGreaterThan(
@@ -166,7 +165,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
)
if (gasLimitTooLow) {
- error = t('gasLimitTooLow')
+ error = this.props.t('gasLimitTooLow')
}
this.setState({ error })
@@ -259,7 +258,7 @@ CustomizeGasModal.prototype.render = function () {
}, [
h('div.send-v2__customize-gas__header', {}, [
- h('div.send-v2__customize-gas__title', t('customGas')),
+ h('div.send-v2__customize-gas__title', this.props.t('customGas')),
h('div.send-v2__customize-gas__close', {
onClick: hideModal,
@@ -275,8 +274,8 @@ CustomizeGasModal.prototype.render = function () {
// max: 1000,
step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10),
onChange: value => this.convertAndSetGasPrice(value),
- title: t('gasPrice'),
- copy: t('gasPriceCalculation'),
+ title: this.props.t('gasPrice'),
+ copy: this.props.t('gasPriceCalculation'),
}),
h(GasModalCard, {
@@ -285,8 +284,8 @@ CustomizeGasModal.prototype.render = function () {
// max: 100000,
step: 1,
onChange: value => this.convertAndSetGasLimit(value),
- title: t('gasLimit'),
- copy: t('gasLimitCalculation'),
+ title: this.props.t('gasLimit'),
+ copy: this.props.t('gasLimitCalculation'),
}),
]),
@@ -299,16 +298,20 @@ CustomizeGasModal.prototype.render = function () {
h('div.send-v2__customize-gas__revert', {
onClick: () => this.revert(),
- }, [t('revert')]),
+ }, [this.props.t('revert')]),
h('div.send-v2__customize-gas__buttons', [
- h('div.send-v2__customize-gas__cancel.allcaps', {
+ h('button.btn-secondary.send-v2__customize-gas__cancel', {
onClick: this.props.hideModal,
- }, [t('cancel')]),
+ style: {
+ marginRight: '10px',
+ },
+ }, [this.props.t('cancel')]),
- h(`div.send-v2__customize-gas__save${error ? '__error' : ''}.allcaps`, {
+ h('button.btn-primary.send-v2__customize-gas__save', {
onClick: () => !error && this.save(newGasPrice, gasLimit, gasTotal),
- }, [t('save')]),
+ className: error && 'btn-primary--disabled',
+ }, [this.props.t('save')]),
]),
]),
diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js
index e5359c1d6..5e7c0d554 100644
--- a/ui/app/components/dropdowns/components/account-dropdowns.js
+++ b/ui/app/components/dropdowns/components/account-dropdowns.js
@@ -3,14 +3,14 @@ const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const actions = require('../../../actions')
const genAccountLink = require('../../../../lib/account-link.js')
-const connect = require('react-redux').connect
+const connect = require('../../../metamask-connect')
const Dropdown = require('./dropdown').Dropdown
const DropdownMenuItem = require('./dropdown').DropdownMenuItem
const Identicon = require('../../identicon')
const ethUtil = require('ethereumjs-util')
const copyToClipboard = require('copy-to-clipboard')
const { formatBalance } = require('../../../util')
-const t = require('../../../../i18n')
+
class AccountDropdowns extends Component {
constructor (props) {
@@ -130,7 +130,7 @@ class AccountDropdowns extends Component {
actions.showEditAccountModal(identity)
},
}, [
- t('edit'),
+ this.props.t('edit'),
]),
]),
@@ -144,7 +144,7 @@ class AccountDropdowns extends Component {
try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
- return isLoose ? h('.keyring-label.allcaps', t('loose')) : null
+ return isLoose ? h('.keyring-label.allcaps', this.props.t('loose')) : null
} catch (e) { return }
}
@@ -202,7 +202,7 @@ class AccountDropdowns extends Component {
fontSize: '16px',
lineHeight: '23px',
},
- }, t('createAccount')),
+ }, this.props.t('createAccount')),
],
),
h(
@@ -236,7 +236,7 @@ class AccountDropdowns extends Component {
fontSize: '16px',
lineHeight: '23px',
},
- }, t('importAccount')),
+ }, this.props.t('importAccount')),
]
),
]
@@ -287,7 +287,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- t('accountDetails'),
+ this.props.t('accountDetails'),
),
h(
DropdownMenuItem,
@@ -303,7 +303,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- t('etherscanView'),
+ this.props.t('etherscanView'),
),
h(
DropdownMenuItem,
@@ -319,7 +319,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- t('copyAddress'),
+ this.props.t('copyAddress'),
),
h(
DropdownMenuItem,
@@ -331,7 +331,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- t('exportPrivateKey'),
+ this.props.t('exportPrivateKey'),
),
h(
DropdownMenuItem,
@@ -346,7 +346,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- t('addToken'),
+ this.props.t('addToken'),
),
]
@@ -425,6 +425,7 @@ AccountDropdowns.propTypes = {
enableAccountsSelector: PropTypes.bool,
enableAccountOption: PropTypes.bool,
enableAccountOptions: PropTypes.bool,
+ t: PropTypes.func,
}
const mapDispatchToProps = (dispatch) => {
diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js
index 5afe730c1..aac7a9ee5 100644
--- a/ui/app/components/dropdowns/network-dropdown.js
+++ b/ui/app/components/dropdowns/network-dropdown.js
@@ -1,14 +1,14 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const Dropdown = require('./components/dropdown').Dropdown
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
const NetworkDropdownIcon = require('./components/network-dropdown-icon')
-const t = require('../../../i18n')
const R = require('ramda')
+
// classes from nodes of the toggle element.
const notToggleElementClassnames = [
'menu-icon',
@@ -94,13 +94,13 @@ NetworkDropdown.prototype.render = function () {
}, [
h('div.network-dropdown-header', {}, [
- h('div.network-dropdown-title', {}, t('networks')),
+ h('div.network-dropdown-title', {}, this.props.t('networks')),
h('div.network-dropdown-divider'),
h('div.network-dropdown-content',
{},
- t('defaultNetwork')
+ this.props.t('defaultNetwork')
),
]),
@@ -122,7 +122,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: providerType === 'mainnet' ? '#ffffff' : '#9b9b9b',
},
- }, t('mainnet')),
+ }, this.props.t('mainnet')),
]
),
@@ -144,7 +144,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: providerType === 'ropsten' ? '#ffffff' : '#9b9b9b',
},
- }, t('ropsten')),
+ }, this.props.t('ropsten')),
]
),
@@ -166,7 +166,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: providerType === 'kovan' ? '#ffffff' : '#9b9b9b',
},
- }, t('kovan')),
+ }, this.props.t('kovan')),
]
),
@@ -188,7 +188,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: providerType === 'rinkeby' ? '#ffffff' : '#9b9b9b',
},
- }, t('rinkeby')),
+ }, this.props.t('rinkeby')),
]
),
@@ -210,7 +210,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: activeNetwork === 'http://localhost:8545' ? '#ffffff' : '#9b9b9b',
},
- }, t('localhost')),
+ }, this.props.t('localhost')),
]
),
@@ -234,7 +234,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: activeNetwork === 'custom' ? '#ffffff' : '#9b9b9b',
},
- }, t('customRPC')),
+ }, this.props.t('customRPC')),
]
),
@@ -249,15 +249,15 @@ NetworkDropdown.prototype.getNetworkName = function () {
let name
if (providerName === 'mainnet') {
- name = t('mainnet')
+ name = this.props.t('mainnet')
} else if (providerName === 'ropsten') {
- name = t('ropsten')
+ name = this.props.t('ropsten')
} else if (providerName === 'kovan') {
- name = t('kovan')
+ name = this.props.t('kovan')
} else if (providerName === 'rinkeby') {
- name = t('rinkeby')
+ name = this.props.t('rinkeby')
} else {
- name = t('unknownNetwork')
+ name = this.props.t('unknownNetwork')
}
return name
diff --git a/ui/app/components/dropdowns/token-menu-dropdown.js b/ui/app/components/dropdowns/token-menu-dropdown.js
index a4f93b505..630e1f99d 100644
--- a/ui/app/components/dropdowns/token-menu-dropdown.js
+++ b/ui/app/components/dropdowns/token-menu-dropdown.js
@@ -1,9 +1,9 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
-const t = require('../../../i18n')
+
module.exports = connect(null, mapDispatchToProps)(TokenMenuDropdown)
@@ -44,7 +44,7 @@ TokenMenuDropdown.prototype.render = function () {
showHideTokenConfirmationModal(this.props.token)
this.props.onClose()
},
- }, t('hideToken')),
+ }, this.props.t('hideToken')),
]),
]),
diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js
index add67ea35..922f24d40 100644
--- a/ui/app/components/ens-input.js
+++ b/ui/app/components/ens-input.js
@@ -8,10 +8,10 @@ const ENS = require('ethjs-ens')
const networkMap = require('ethjs-ens/lib/network-map.json')
const ensRE = /.+\..+$/
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
-const t = require('../../i18n')
+const connect = require('../metamask-connect')
+const ToAutoComplete = require('./send/to-autocomplete')
-
-module.exports = EnsInput
+module.exports = connect()(EnsInput)
inherits(EnsInput, Component)
function EnsInput () {
@@ -22,12 +22,14 @@ EnsInput.prototype.render = function () {
const props = this.props
const opts = extend(props, {
list: 'addresses',
- onChange: () => {
+ onChange: (recipient) => {
const network = this.props.network
const networkHasEnsSupport = getNetworkEnsSupport(network)
+
if (!networkHasEnsSupport) return
- const recipient = document.querySelector('input[name="address"]').value
+ props.onChange(recipient)
+
if (recipient.match(ensRE) === null) {
return this.setState({
loadingEns: false,
@@ -39,34 +41,13 @@ EnsInput.prototype.render = function () {
this.setState({
loadingEns: true,
})
- this.checkName()
+ this.checkName(recipient)
},
})
return h('div', {
- style: { width: '100%' },
+ style: { width: '100%', position: 'relative' },
}, [
- h('input.large-input.send-screen-input', opts),
- // The address book functionality.
- h('datalist#addresses',
- [
- // Corresponds to the addresses owned.
- Object.keys(props.identities).map((key) => {
- const identity = props.identities[key]
- return h('option', {
- value: identity.address,
- label: identity.name,
- key: identity.address,
- })
- }),
- // Corresponds to previously sent-to addresses.
- props.addressBook.map((identity) => {
- return h('option', {
- value: identity.address,
- label: identity.name,
- key: identity.address,
- })
- }),
- ]),
+ h(ToAutoComplete, { ...opts }),
this.ensIcon(),
])
}
@@ -83,20 +64,19 @@ EnsInput.prototype.componentDidMount = function () {
}
}
-EnsInput.prototype.lookupEnsName = function () {
- const recipient = document.querySelector('input[name="address"]').value
+EnsInput.prototype.lookupEnsName = function (recipient) {
const { ensResolution } = this.state
log.info(`ENS attempting to resolve name: ${recipient}`)
this.ens.lookup(recipient.trim())
.then((address) => {
- if (address === ZERO_ADDRESS) throw new Error(t('noAddressForName'))
+ if (address === ZERO_ADDRESS) throw new Error(this.props.t('noAddressForName'))
if (address !== ensResolution) {
this.setState({
loadingEns: false,
ensResolution: address,
nickname: recipient.trim(),
- hoverText: address + '\n' + t('clickCopy'),
+ hoverText: address + '\n' + this.props.t('clickCopy'),
ensFailure: false,
})
}
@@ -130,8 +110,8 @@ EnsInput.prototype.ensIcon = function (recipient) {
title: hoverText,
style: {
position: 'absolute',
- padding: '9px',
- transform: 'translatex(-40px)',
+ top: '16px',
+ left: '-25px',
},
}, this.ensIconContents(recipient))
}
diff --git a/ui/app/components/hex-as-decimal-input.js b/ui/app/components/hex-as-decimal-input.js
index a43d44f89..be7ba4c9e 100644
--- a/ui/app/components/hex-as-decimal-input.js
+++ b/ui/app/components/hex-as-decimal-input.js
@@ -4,9 +4,9 @@ const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const extend = require('xtend')
-const t = require('../../i18n')
+const connect = require('../metamask-connect')
-module.exports = HexAsDecimalInput
+module.exports = connect()(HexAsDecimalInput)
inherits(HexAsDecimalInput, Component)
function HexAsDecimalInput () {
@@ -127,13 +127,13 @@ HexAsDecimalInput.prototype.constructWarning = function () {
let message = name ? name + ' ' : ''
if (min && max) {
- message += t('betweenMinAndMax', [min, max])
+ message += this.props.t('betweenMinAndMax', [min, max])
} else if (min) {
- message += t('greaterThanMin', [min])
+ message += this.props.t('greaterThanMin', [min])
} else if (max) {
- message += t('lessThanMax', [max])
+ message += this.props.t('lessThanMax', [max])
} else {
- message += t('invalidInput')
+ message += this.props.t('invalidInput')
}
return message
diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js
index b803b7ceb..6b2a1b428 100644
--- a/ui/app/components/identicon.js
+++ b/ui/app/components/identicon.js
@@ -1,7 +1,7 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const isNode = require('detect-node')
const findDOMNode = require('react-dom').findDOMNode
const jazzicon = require('jazzicon')
diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js
index 75f989e86..c43e3e3a5 100644
--- a/ui/app/components/modals/account-details-modal.js
+++ b/ui/app/components/modals/account-details-modal.js
@@ -1,14 +1,13 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const AccountModalContainer = require('./account-modal-container')
const { getSelectedIdentity } = require('../../selectors')
const genAccountLink = require('../../../lib/account-link.js')
const QrView = require('../qr-code')
const EditableLabel = require('../editable-label')
-const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -63,14 +62,14 @@ AccountDetailsModal.prototype.render = function () {
h('div.account-modal-divider'),
- h('button.btn-clear.account-modal__button', {
+ h('button.btn-primary.account-modal__button', {
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
- }, t('etherscanView')),
+ }, this.props.t('etherscanView')),
// Holding on redesign for Export Private Key functionality
- h('button.btn-clear.account-modal__button', {
+ h('button.btn-primary.account-modal__button', {
onClick: () => showExportPrivateKeyModal(),
- }, t('exportPrivateKey')),
+ }, this.props.t('exportPrivateKey')),
])
}
diff --git a/ui/app/components/modals/account-modal-container.js b/ui/app/components/modals/account-modal-container.js
index 08540aa76..70efe16cb 100644
--- a/ui/app/components/modals/account-modal-container.js
+++ b/ui/app/components/modals/account-modal-container.js
@@ -1,11 +1,10 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const { getSelectedIdentity } = require('../../selectors')
const Identicon = require('../identicon')
-const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -60,7 +59,7 @@ AccountModalContainer.prototype.render = function () {
h('i.fa.fa-angle-left.fa-lg'),
- h('span.account-modal-back__text', ' ' + t('back')),
+ h('span.account-modal-back__text', ' ' + this.props.t('back')),
]),
diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js
index 7eb73c3a6..c0ee3632e 100644
--- a/ui/app/components/modals/buy-options-modal.js
+++ b/ui/app/components/modals/buy-options-modal.js
@@ -1,10 +1,9 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const networkNames = require('../../../../app/scripts/config.js').networkNames
-const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -57,15 +56,15 @@ BuyOptions.prototype.render = function () {
}, [
h('div.buy-modal-content-title', {
style: {},
- }, t('transfers')),
- h('div', {}, t('howToDeposit')),
+ }, this.props.t('transfers')),
+ h('div', {}, this.props.t('howToDeposit')),
]),
h('div.buy-modal-content-options.flex-column.flex-center', {}, [
isTestNetwork
- ? this.renderModalContentOption(networkName, t('testFaucet'), () => toFaucet(network))
- : this.renderModalContentOption('Coinbase', t('depositFiat'), () => toCoinbase(address)),
+ ? this.renderModalContentOption(networkName, this.props.t('testFaucet'), () => toFaucet(network))
+ : this.renderModalContentOption('Coinbase', this.props.t('depositFiat'), () => toCoinbase(address)),
// h('div.buy-modal-content-option', {}, [
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
@@ -73,8 +72,8 @@ BuyOptions.prototype.render = function () {
// ]),,
this.renderModalContentOption(
- t('directDeposit'),
- t('depositFromAccount'),
+ this.props.t('directDeposit'),
+ this.props.t('depositFromAccount'),
() => this.goToAccountDetailsModal()
),
@@ -85,7 +84,7 @@ BuyOptions.prototype.render = function () {
background: 'white',
},
onClick: () => { this.props.hideModal() },
- }, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, t('cancel'))),
+ }, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, this.props.t('cancel'))),
]),
])
}
diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js
index b642513d7..e38899d04 100644
--- a/ui/app/components/modals/deposit-ether-modal.js
+++ b/ui/app/components/modals/deposit-ether-modal.js
@@ -1,22 +1,18 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const networkNames = require('../../../../app/scripts/config.js').networkNames
const ShapeshiftForm = require('../shapeshift-form')
-const t = require('../../../i18n')
-
-const DIRECT_DEPOSIT_ROW_TITLE = t('directDepositEther')
-const DIRECT_DEPOSIT_ROW_TEXT = t('directDepositEtherExplainer')
-const COINBASE_ROW_TITLE = t('buyCoinbase')
-const COINBASE_ROW_TEXT = t('buyCoinbaseExplainer')
-const SHAPESHIFT_ROW_TITLE = t('depositShapeShift')
-const SHAPESHIFT_ROW_TEXT = t('depositShapeShiftExplainer')
-const FAUCET_ROW_TITLE = t('testFaucet')
-const facuetRowText = (networkName) => {
- return t('getEtherFromFaucet', [networkName])
-}
+
+let DIRECT_DEPOSIT_ROW_TITLE
+let DIRECT_DEPOSIT_ROW_TEXT
+let COINBASE_ROW_TITLE
+let COINBASE_ROW_TEXT
+let SHAPESHIFT_ROW_TITLE
+let SHAPESHIFT_ROW_TEXT
+let FAUCET_ROW_TITLE
function mapStateToProps (state) {
return {
@@ -44,9 +40,18 @@ function mapDispatchToProps (dispatch) {
}
inherits(DepositEtherModal, Component)
-function DepositEtherModal () {
+function DepositEtherModal (props) {
Component.call(this)
+ // need to set after i18n locale has loaded
+ DIRECT_DEPOSIT_ROW_TITLE = props.t('directDepositEther')
+ DIRECT_DEPOSIT_ROW_TEXT = props.t('directDepositEtherExplainer')
+ COINBASE_ROW_TITLE = props.t('buyCoinbase')
+ COINBASE_ROW_TEXT = props.t('buyCoinbaseExplainer')
+ SHAPESHIFT_ROW_TITLE = props.t('depositShapeShift')
+ SHAPESHIFT_ROW_TEXT = props.t('depositShapeShiftExplainer')
+ FAUCET_ROW_TITLE = props.t('testFaucet')
+
this.state = {
buyingWithShapeshift: false,
}
@@ -54,6 +59,10 @@ function DepositEtherModal () {
module.exports = connect(mapStateToProps, mapDispatchToProps)(DepositEtherModal)
+DepositEtherModal.prototype.facuetRowText = function (networkName) {
+ return this.props.t('getEtherFromFaucet', [networkName])
+}
+
DepositEtherModal.prototype.renderRow = function ({
logo,
title,
@@ -94,7 +103,7 @@ DepositEtherModal.prototype.renderRow = function ({
]),
!hideButton && h('div.deposit-ether-modal__buy-row__button', [
- h('button.deposit-ether-modal__deposit-button', {
+ h('button.btn-primary--lg.deposit-ether-modal__deposit-button', {
onClick: onButtonClick,
}, [buttonLabel]),
]),
@@ -113,10 +122,10 @@ DepositEtherModal.prototype.render = function () {
h('div.page-container__header', [
- h('div.page-container__title', [t('depositEther')]),
+ h('div.page-container__title', [this.props.t('depositEther')]),
h('div.page-container__subtitle', [
- t('needEtherInWallet'),
+ this.props.t('needEtherInWallet'),
]),
h('div.page-container__header-close', {
@@ -139,7 +148,7 @@ DepositEtherModal.prototype.render = function () {
}),
title: DIRECT_DEPOSIT_ROW_TITLE,
text: DIRECT_DEPOSIT_ROW_TEXT,
- buttonLabel: t('viewAccount'),
+ buttonLabel: this.props.t('viewAccount'),
onButtonClick: () => this.goToAccountDetailsModal(),
hide: buyingWithShapeshift,
}),
@@ -147,8 +156,8 @@ DepositEtherModal.prototype.render = function () {
this.renderRow({
logo: h('i.fa.fa-tint.fa-2x'),
title: FAUCET_ROW_TITLE,
- text: facuetRowText(networkName),
- buttonLabel: t('getEther'),
+ text: this.facuetRowText(networkName),
+ buttonLabel: this.props.t('getEther'),
onButtonClick: () => toFaucet(network),
hide: !isTestNetwork || buyingWithShapeshift,
}),
@@ -162,7 +171,7 @@ DepositEtherModal.prototype.render = function () {
}),
title: COINBASE_ROW_TITLE,
text: COINBASE_ROW_TEXT,
- buttonLabel: t('continueToCoinbase'),
+ buttonLabel: this.props.t('continueToCoinbase'),
onButtonClick: () => toCoinbase(address),
hide: isTestNetwork || buyingWithShapeshift,
}),
@@ -175,7 +184,7 @@ DepositEtherModal.prototype.render = function () {
}),
title: SHAPESHIFT_ROW_TITLE,
text: SHAPESHIFT_ROW_TEXT,
- buttonLabel: t('shapeshiftBuy'),
+ buttonLabel: this.props.t('shapeshiftBuy'),
onButtonClick: () => this.setState({ buyingWithShapeshift: true }),
hide: isTestNetwork,
hideButton: buyingWithShapeshift,
diff --git a/ui/app/components/modals/edit-account-name-modal.js b/ui/app/components/modals/edit-account-name-modal.js
index 6efa8d476..4f5bc001a 100644
--- a/ui/app/components/modals/edit-account-name-modal.js
+++ b/ui/app/components/modals/edit-account-name-modal.js
@@ -1,10 +1,9 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const { getSelectedAccount } = require('../../selectors')
-const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -51,7 +50,7 @@ EditAccountNameModal.prototype.render = function () {
]),
h('div.edit-account-name-modal-title', {
- }, [t('editAccountName')]),
+ }, [this.props.t('editAccountName')]),
h('input.edit-account-name-modal-input', {
placeholder: identity.name,
@@ -70,7 +69,7 @@ EditAccountNameModal.prototype.render = function () {
},
disabled: this.state.inputText.length === 0,
}, [
- t('save'),
+ this.props.t('save'),
]),
]),
diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js
index 017177cfd..d5a1ba7b8 100644
--- a/ui/app/components/modals/export-private-key-modal.js
+++ b/ui/app/components/modals/export-private-key-modal.js
@@ -1,13 +1,12 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const ethUtil = require('ethereumjs-util')
const actions = require('../../actions')
const AccountModalContainer = require('./account-modal-container')
const { getSelectedIdentity } = require('../../selectors')
const ReadOnlyInput = require('../readonly-input')
-const t = require('../../../i18n')
const copyToClipboard = require('copy-to-clipboard')
function mapStateToProps (state) {
@@ -49,8 +48,8 @@ ExportPrivateKeyModal.prototype.exportAccountAndGetPrivateKey = function (passwo
ExportPrivateKeyModal.prototype.renderPasswordLabel = function (privateKey) {
return h('span.private-key-password-label', privateKey
- ? t('copyPrivateKey')
- : t('typePassword')
+ ? this.props.t('copyPrivateKey')
+ : this.props.t('typePassword')
)
}
@@ -81,14 +80,14 @@ ExportPrivateKeyModal.prototype.renderButton = function (className, onClick, lab
ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, address, hideModal) {
return h('div.export-private-key-buttons', {}, [
!privateKey && this.renderButton(
- 'btn-cancel export-private-key__button export-private-key__button--cancel',
+ 'btn-secondary--lg export-private-key__button export-private-key__button--cancel',
() => hideModal(),
'Cancel'
),
(privateKey
- ? this.renderButton('btn-clear export-private-key__button', () => hideModal(), t('done'))
- : this.renderButton('btn-clear export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), t('confirm'))
+ ? this.renderButton('btn-primary--lg export-private-key__button', () => hideModal(), this.props.t('done'))
+ : this.renderButton('btn-primary--lg export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), this.props.t('confirm'))
),
])
@@ -121,7 +120,7 @@ ExportPrivateKeyModal.prototype.render = function () {
h('div.account-modal-divider'),
- h('span.modal-body-title', t('showPrivateKeys')),
+ h('span.modal-body-title', this.props.t('showPrivateKeys')),
h('div.private-key-password', {}, [
this.renderPasswordLabel(privateKey),
@@ -131,7 +130,7 @@ ExportPrivateKeyModal.prototype.render = function () {
!warning ? null : h('span.private-key-password-error', warning),
]),
- h('div.private-key-password-warning', t('privateKeyWarning')),
+ h('div.private-key-password-warning', this.props.t('privateKeyWarning')),
this.renderButtons(privateKey, this.state.password, address, hideModal),
diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js
index 33d8062c6..5207b4c95 100644
--- a/ui/app/components/modals/hide-token-confirmation-modal.js
+++ b/ui/app/components/modals/hide-token-confirmation-modal.js
@@ -1,10 +1,9 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const Identicon = require('../identicon')
-const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -42,7 +41,7 @@ HideTokenConfirmationModal.prototype.render = function () {
h('div.hide-token-confirmation__container', {
}, [
h('div.hide-token-confirmation__title', {}, [
- t('hideTokenPrompt'),
+ this.props.t('hideTokenPrompt'),
]),
h(Identicon, {
@@ -55,19 +54,19 @@ HideTokenConfirmationModal.prototype.render = function () {
h('div.hide-token-confirmation__symbol', {}, symbol),
h('div.hide-token-confirmation__copy', {}, [
- t('readdToken'),
+ this.props.t('readdToken'),
]),
h('div.hide-token-confirmation__buttons', {}, [
h('button.btn-cancel.hide-token-confirmation__button.allcaps', {
onClick: () => hideModal(),
}, [
- t('cancel'),
+ this.props.t('cancel'),
]),
h('button.btn-clear.hide-token-confirmation__button.allcaps', {
onClick: () => hideToken(address),
}, [
- t('hide'),
+ this.props.t('hide'),
]),
]),
]),
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index 501b83430..9250cc77e 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -6,7 +6,6 @@ const FadeModal = require('boron').FadeModal
const actions = require('../../actions')
const isMobileView = require('../../../lib/is-mobile-view')
const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-notification')
-const t = require('../../../i18n')
// Modal Components
const BuyOptions = require('./buy-options-modal')
@@ -174,8 +173,8 @@ const MODALS = {
BETA_UI_NOTIFICATION_MODAL: {
contents: [
h(NotifcationModal, {
- header: t('uiWelcome'),
- message: t('uiWelcomeMessage'),
+ header: 'uiWelcome',
+ message: 'uiWelcomeMessage',
}),
],
mobileModalStyle: {
@@ -191,8 +190,8 @@ const MODALS = {
OLD_UI_NOTIFICATION_MODAL: {
contents: [
h(NotifcationModal, {
- header: t('oldUI'),
- message: t('oldUIMessage'),
+ header: 'oldUI',
+ message: 'oldUIMessage',
}),
],
mobileModalStyle: {
diff --git a/ui/app/components/modals/new-account-modal.js b/ui/app/components/modals/new-account-modal.js
index 298b76af4..372b65251 100644
--- a/ui/app/components/modals/new-account-modal.js
+++ b/ui/app/components/modals/new-account-modal.js
@@ -1,9 +1,8 @@
const { Component } = require('react')
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const { connect } = require('react-redux')
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
-const t = require('../../../i18n')
class NewAccountModal extends Component {
constructor (props) {
@@ -12,7 +11,7 @@ class NewAccountModal extends Component {
const newAccountNumber = numberOfExistingAccounts + 1
this.state = {
- newAccountName: `${t('account')} ${newAccountNumber}`,
+ newAccountName: `${props.t('account')} ${newAccountNumber}`,
}
}
@@ -23,7 +22,7 @@ class NewAccountModal extends Component {
h('div.new-account-modal-wrapper', {
}, [
h('div.new-account-modal-header', {}, [
- t('newAccount'),
+ this.props.t('newAccount'),
]),
h('div.modal-close-x', {
@@ -31,19 +30,19 @@ class NewAccountModal extends Component {
}),
h('div.new-account-modal-content', {}, [
- t('accountName'),
+ this.props.t('accountName'),
]),
h('div.new-account-input-wrapper', {}, [
h('input.new-account-input', {
value: this.state.newAccountName,
- placeholder: t('sampleAccountName'),
+ placeholder: this.props.t('sampleAccountName'),
onChange: event => this.setState({ newAccountName: event.target.value }),
}, []),
]),
h('div.new-account-modal-content.after-input', {}, [
- t('or'),
+ this.props.t('or'),
]),
h('div.new-account-modal-content.after-input.pointer', {
@@ -51,13 +50,13 @@ class NewAccountModal extends Component {
this.props.hideModal()
this.props.showImportPage()
},
- }, t('importAnAccount')),
+ }, this.props.t('importAnAccount')),
h('div.new-account-modal-content.button.allcaps', {}, [
h('button.btn-clear', {
onClick: () => this.props.createAccount(newAccountName),
}, [
- t('save'),
+ this.props.t('save'),
]),
]),
]),
@@ -70,6 +69,7 @@ NewAccountModal.propTypes = {
showImportPage: PropTypes.func,
createAccount: PropTypes.func,
numberOfExistingAccounts: PropTypes.number,
+ t: PropTypes.func,
}
const mapStateToProps = state => {
diff --git a/ui/app/components/modals/notification-modal.js b/ui/app/components/modals/notification-modal.js
index 621a974d0..5d6dca177 100644
--- a/ui/app/components/modals/notification-modal.js
+++ b/ui/app/components/modals/notification-modal.js
@@ -1,7 +1,7 @@
const { Component } = require('react')
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const { connect } = require('react-redux')
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
class NotificationModal extends Component {
@@ -22,12 +22,12 @@ class NotificationModal extends Component {
}, [
h('div.notification-modal__header', {}, [
- header,
+ this.props.t(header),
]),
h('div.notification-modal__message-wrapper', {}, [
h('div.notification-modal__message', {}, [
- message,
+ this.props.t(message),
]),
]),
@@ -62,6 +62,7 @@ NotificationModal.propTypes = {
showCancelButton: PropTypes.bool,
showConfirmButton: PropTypes.bool,
onConfirm: PropTypes.func,
+ t: PropTypes.func,
}
const mapDispatchToProps = dispatch => {
diff --git a/ui/app/components/modals/notification-modals/confirm-reset-account.js b/ui/app/components/modals/notification-modals/confirm-reset-account.js
index e1bc91b24..94ee997ab 100644
--- a/ui/app/components/modals/notification-modals/confirm-reset-account.js
+++ b/ui/app/components/modals/notification-modals/confirm-reset-account.js
@@ -1,7 +1,7 @@
const { Component } = require('react')
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const { connect } = require('react-redux')
+const connect = require('../../../metamask-connect')
const actions = require('../../../actions')
const NotifcationModal = require('../notification-modal')
diff --git a/ui/app/components/modals/shapeshift-deposit-tx-modal.js b/ui/app/components/modals/shapeshift-deposit-tx-modal.js
index 24af5a0de..28dcb1902 100644
--- a/ui/app/components/modals/shapeshift-deposit-tx-modal.js
+++ b/ui/app/components/modals/shapeshift-deposit-tx-modal.js
@@ -1,7 +1,7 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const QrView = require('../qr-code')
const AccountModalContainer = require('./account-modal-container')
diff --git a/ui/app/components/network-display.js b/ui/app/components/network-display.js
index 9dc31b5c7..111a82be1 100644
--- a/ui/app/components/network-display.js
+++ b/ui/app/components/network-display.js
@@ -1,9 +1,8 @@
const { Component } = require('react')
const h = require('react-hyperscript')
const PropTypes = require('prop-types')
-const { connect } = require('react-redux')
+const connect = require('../metamask-connect')
const NetworkDropdownIcon = require('./dropdowns/components/network-dropdown-icon')
-const t = require('../../i18n')
const networkToColorHash = {
1: '#038789',
@@ -31,7 +30,7 @@ class NetworkDisplay extends Component {
const { provider: { type } } = this.props
return h('.network-display__container', [
this.renderNetworkIcon(),
- h('.network-name', t(type)),
+ h('.network-name', this.props.t(type)),
])
}
}
@@ -39,6 +38,7 @@ class NetworkDisplay extends Component {
NetworkDisplay.propTypes = {
network: PropTypes.string,
provider: PropTypes.object,
+ t: PropTypes.func,
}
const mapStateToProps = ({ metamask: { network, provider } }) => {
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index f3df2242a..10961390e 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -1,11 +1,11 @@
const Component = require('react').Component
const h = require('react-hyperscript')
+const connect = require('../metamask-connect')
const classnames = require('classnames')
const inherits = require('util').inherits
const NetworkDropdownIcon = require('./dropdowns/components/network-dropdown-icon')
-const t = require('../../i18n')
-module.exports = Network
+module.exports = connect()(Network)
inherits(Network, Component)
@@ -34,7 +34,7 @@ Network.prototype.render = function () {
onClick: (event) => this.props.onClick(event),
}, [
h('img', {
- title: t('attemptingConnect'),
+ title: props.t('attemptingConnect'),
style: {
width: '27px',
},
@@ -42,22 +42,22 @@ Network.prototype.render = function () {
}),
])
} else if (providerName === 'mainnet') {
- hoverText = t('mainnet')
+ hoverText = props.t('mainnet')
iconName = 'ethereum-network'
} else if (providerName === 'ropsten') {
- hoverText = t('ropsten')
+ hoverText = props.t('ropsten')
iconName = 'ropsten-test-network'
} else if (parseInt(networkNumber) === 3) {
- hoverText = t('ropsten')
+ hoverText = props.t('ropsten')
iconName = 'ropsten-test-network'
} else if (providerName === 'kovan') {
- hoverText = t('kovan')
+ hoverText = props.t('kovan')
iconName = 'kovan-test-network'
} else if (providerName === 'rinkeby') {
- hoverText = t('rinkeby')
+ hoverText = props.t('rinkeby')
iconName = 'rinkeby-test-network'
} else {
- hoverText = t('unknownNetwork')
+ hoverText = props.t('unknownNetwork')
iconName = 'unknown-private-network'
}
@@ -85,7 +85,7 @@ Network.prototype.render = function () {
backgroundColor: '#038789', // $blue-lagoon
nonSelectBackgroundColor: '#15afb2',
}),
- h('.network-name', t('mainnet')),
+ h('.network-name', props.t('mainnet')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'ropsten-test-network':
@@ -94,7 +94,7 @@ Network.prototype.render = function () {
backgroundColor: '#e91550', // $crimson
nonSelectBackgroundColor: '#ec2c50',
}),
- h('.network-name', t('ropsten')),
+ h('.network-name', props.t('ropsten')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'kovan-test-network':
@@ -103,7 +103,7 @@ Network.prototype.render = function () {
backgroundColor: '#690496', // $purple
nonSelectBackgroundColor: '#b039f3',
}),
- h('.network-name', t('kovan')),
+ h('.network-name', props.t('kovan')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'rinkeby-test-network':
@@ -112,7 +112,7 @@ Network.prototype.render = function () {
backgroundColor: '#ebb33f', // $tulip-tree
nonSelectBackgroundColor: '#ecb23e',
}),
- h('.network-name', t('rinkeby')),
+ h('.network-name', props.t('rinkeby')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
default:
@@ -124,7 +124,7 @@ Network.prototype.render = function () {
},
}),
- h('.network-name', t('privateNetwork')),
+ h('.network-name', props.t('privateNetwork')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
}
diff --git a/ui/app/components/notice.js b/ui/app/components/notice.js
index 8b0ce1e8b..a999ffd88 100644
--- a/ui/app/components/notice.js
+++ b/ui/app/components/notice.js
@@ -4,9 +4,9 @@ const h = require('react-hyperscript')
const ReactMarkdown = require('react-markdown')
const linker = require('extension-link-enabler')
const findDOMNode = require('react-dom').findDOMNode
-const t = require('../../i18n')
+const connect = require('../metamask-connect')
-module.exports = Notice
+module.exports = connect()(Notice)
inherits(Notice, Component)
function Notice () {
@@ -111,7 +111,7 @@ Notice.prototype.render = function () {
style: {
marginTop: '18px',
},
- }, t('accept')),
+ }, this.props.t('accept')),
])
)
}
diff --git a/ui/app/components/pending-msg-details.js b/ui/app/components/pending-msg-details.js
index 87e66855d..ddec8470d 100644
--- a/ui/app/components/pending-msg-details.js
+++ b/ui/app/components/pending-msg-details.js
@@ -1,11 +1,11 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const t = require('../../i18n')
+const connect = require('../metamask-connect')
const AccountPanel = require('./account-panel')
-module.exports = PendingMsgDetails
+module.exports = connect()(PendingMsgDetails)
inherits(PendingMsgDetails, Component)
function PendingMsgDetails () {
@@ -40,7 +40,7 @@ PendingMsgDetails.prototype.render = function () {
// message data
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [
h('.flex-column.flex-space-between', [
- h('label.font-small.allcaps', t('message')),
+ h('label.font-small.allcaps', this.props.t('message')),
h('span.font-small', msgParams.data),
]),
]),
diff --git a/ui/app/components/pending-msg.js b/ui/app/components/pending-msg.js
index dc406b955..56e646a1c 100644
--- a/ui/app/components/pending-msg.js
+++ b/ui/app/components/pending-msg.js
@@ -2,9 +2,9 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const PendingTxDetails = require('./pending-msg-details')
-const t = require('../../i18n')
+const connect = require('../metamask-connect')
-module.exports = PendingMsg
+module.exports = connect()(PendingMsg)
inherits(PendingMsg, Component)
function PendingMsg () {
@@ -30,14 +30,14 @@ PendingMsg.prototype.render = function () {
fontWeight: 'bold',
textAlign: 'center',
},
- }, t('signMessage')),
+ }, this.props.t('signMessage')),
h('.error', {
style: {
margin: '10px',
},
}, [
- t('signNotice'),
+ this.props.t('signNotice'),
h('a', {
href: 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527',
style: { color: 'rgb(247, 134, 28)' },
@@ -46,7 +46,7 @@ PendingMsg.prototype.render = function () {
const url = 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527'
global.platform.openWindow({ url })
},
- }, t('readMore')),
+ }, this.props.t('readMore')),
]),
// message details
@@ -56,10 +56,10 @@ PendingMsg.prototype.render = function () {
h('.flex-row.flex-space-around', [
h('button', {
onClick: state.cancelMessage,
- }, t('cancel')),
+ }, this.props.t('cancel')),
h('button', {
onClick: state.signMessage,
- }, t('sign')),
+ }, this.props.t('sign')),
]),
])
diff --git a/ui/app/components/pending-tx/confirm-deploy-contract.js b/ui/app/components/pending-tx/confirm-deploy-contract.js
index b75f3a964..f41fa51e6 100644
--- a/ui/app/components/pending-tx/confirm-deploy-contract.js
+++ b/ui/app/components/pending-tx/confirm-deploy-contract.js
@@ -1,5 +1,5 @@
const { Component } = require('react')
-const { connect } = require('react-redux')
+const connect = require('../../metamask-connect')
const h = require('react-hyperscript')
const PropTypes = require('prop-types')
const actions = require('../../actions')
@@ -8,7 +8,6 @@ const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
const { conversionUtil } = require('../../conversion-util')
-const t = require('../../../i18n')
const SenderToRecipient = require('../sender-to-recipient')
const NetworkDisplay = require('../network-display')
@@ -33,7 +32,7 @@ class ConfirmDeployContract extends Component {
if (valid && this.verifyGasParams()) {
this.props.sendTransaction(txMeta, event)
} else {
- this.props.displayWarning('invalidGasParams')
+ this.props.displayWarning(this.props.t('invalidGasParams'))
this.setState({ submitting: false })
}
}
@@ -178,7 +177,7 @@ class ConfirmDeployContract extends Component {
return (
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ this.props.t('gasFee') ]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency.toUpperCase()}`),
@@ -217,8 +216,8 @@ class ConfirmDeployContract extends Component {
return (
h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ t('total') + ' ' ]),
- h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
+ h('span.confirm-screen-label', [ this.props.t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ this.props.t('amountPlusGas') ]),
]),
h('div.confirm-screen-section-column', [
@@ -248,11 +247,11 @@ class ConfirmDeployContract extends Component {
h('.page-container__header-row', [
h('span.page-container__back-button', {
onClick: () => backToAccountDetail(selectedAddress),
- }, t('back')),
+ }, this.props.t('back')),
window.METAMASK_UI_TYPE === 'notification' && h(NetworkDisplay),
]),
- h('.page-container__title', t('confirmContract')),
- h('.page-container__subtitle', t('pleaseReviewTransaction')),
+ h('.page-container__title', this.props.t('confirmContract')),
+ h('.page-container__subtitle', this.props.t('pleaseReviewTransaction')),
]),
// Main Send token Card
h('.page-container__content', [
@@ -275,7 +274,7 @@ class ConfirmDeployContract extends Component {
h('div.confirm-screen-rows', [
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ this.props.t('from') ]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', fromName),
h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
@@ -283,9 +282,9 @@ class ConfirmDeployContract extends Component {
]),
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ this.props.t('to') ]),
h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', t('newContract')),
+ h('div.confirm-screen-row-info', this.props.t('newContract')),
]),
]),
@@ -303,12 +302,12 @@ class ConfirmDeployContract extends Component {
// Cancel Button
h('button.btn-cancel.page-container__footer-button.allcaps', {
onClick: event => this.cancel(event, txMeta),
- }, t('cancel')),
+ }, this.props.t('cancel')),
// Accept Button
h('button.btn-confirm.page-container__footer-button.allcaps', {
onClick: event => this.onSubmit(event),
- }, t('confirm')),
+ }, this.props.t('confirm')),
]),
]),
])
@@ -325,6 +324,7 @@ ConfirmDeployContract.propTypes = {
conversionRate: PropTypes.number,
currentCurrency: PropTypes.string,
selectedAddress: PropTypes.string,
+ t: PropTypes.func,
}
const mapStateToProps = state => {
@@ -347,8 +347,8 @@ const mapDispatchToProps = dispatch => {
return {
backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
- displayWarning: warning => actions.displayWarning(t(warning)),
+ displayWarning: warning => actions.displayWarning(warning),
}
}
-module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract)
+module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract) \ No newline at end of file
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index d1ce25cbf..255f0e8a2 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -1,5 +1,5 @@
const Component = require('react').Component
-const { connect } = require('react-redux')
+const connect = require('../../metamask-connect')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const actions = require('../../actions')
@@ -13,7 +13,6 @@ const {
multiplyCurrencies,
} = require('../../conversion-util')
const GasFeeDisplay = require('../send/gas-fee-display-v2')
-const t = require('../../../i18n')
const SenderToRecipient = require('../sender-to-recipient')
const NetworkDisplay = require('../network-display')
@@ -197,7 +196,7 @@ ConfirmSendEther.prototype.getData = function () {
},
to: {
address: txParams.to,
- name: identities[txParams.to] ? identities[txParams.to].name : t('newRecipient'),
+ name: identities[txParams.to] ? identities[txParams.to].name : this.props.t('newRecipient'),
},
memo: txParams.memo || '',
gasFeeInFIAT,
@@ -298,7 +297,7 @@ ConfirmSendEther.prototype.render = function () {
h('div.confirm-screen-rows', [
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ this.props.t('from') ]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', fromName),
h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
@@ -306,7 +305,7 @@ ConfirmSendEther.prototype.render = function () {
]),
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ this.props.t('to') ]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', toName),
h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`),
@@ -314,7 +313,7 @@ ConfirmSendEther.prototype.render = function () {
]),
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ this.props.t('gasFee') ]),
h('div.confirm-screen-section-column', [
h(GasFeeDisplay, {
gasTotal: gasTotal || gasFeeInHex,
@@ -327,8 +326,8 @@ ConfirmSendEther.prototype.render = function () {
h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ t('total') + ' ' ]),
- h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
+ h('span.confirm-screen-label', [ this.props.t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ this.props.t('amountPlusGas') ]),
]),
h('div.confirm-screen-section-column', [
@@ -429,10 +428,10 @@ ConfirmSendEther.prototype.render = function () {
clearSend()
this.cancel(event, txMeta)
},
- }, t('cancel')),
+ }, this.props.t('cancel')),
// Accept Button
- h('button.btn-confirm.page-container__footer-button.allcaps', [t('confirm')]),
+ h('button.btn-confirm.page-container__footer-button.allcaps', [this.props.t('confirm')]),
]),
]),
])
@@ -448,7 +447,7 @@ ConfirmSendEther.prototype.onSubmit = function (event) {
if (valid && this.verifyGasParams()) {
this.props.sendTransaction(txMeta, event)
} else {
- this.props.dispatch(actions.displayWarning(t('invalidGasParams')))
+ this.props.dispatch(actions.displayWarning(this.props.t('invalidGasParams')))
this.setState({ submitting: false })
}
}
@@ -523,4 +522,4 @@ ConfirmSendEther.prototype.bnMultiplyByFraction = function (targetBN, numerator,
const numBN = new BN(numerator)
const denomBN = new BN(denominator)
return targetBN.mul(numBN).div(denomBN)
-}
+} \ No newline at end of file
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index ccd87c0a4..3e66705ae 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -1,12 +1,11 @@
const Component = require('react').Component
-const { connect } = require('react-redux')
+const connect = require('../../metamask-connect')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const tokenAbi = require('human-standard-token-abi')
const abiDecoder = require('abi-decoder')
abiDecoder.addABI(tokenAbi)
const actions = require('../../actions')
-const t = require('../../../i18n')
const clone = require('clone')
const Identicon = require('../identicon')
const GasFeeDisplay = require('../send/gas-fee-display-v2.js')
@@ -87,6 +86,7 @@ function mapDispatchToProps (dispatch, ownProps) {
amount: tokenAmountInHex,
errors: { to: null, amount: null },
editingTransactionId: id,
+ token: ownProps.token,
}))
dispatch(actions.showSendTokenPage())
},
@@ -101,7 +101,7 @@ function mapDispatchToProps (dispatch, ownProps) {
fromNumericBase: 'dec',
toNumericBase: 'hex',
})
-
+
let forceGasMin
if (lastGasPrice) {
forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
@@ -168,7 +168,7 @@ ConfirmSendToken.prototype.getAmount = function () {
? +(sendTokenAmount * tokenExchangeRate * conversionRate).toFixed(2)
: null,
token: typeof value === 'undefined'
- ? t('unknown')
+ ? this.props.t('unknown')
: +sendTokenAmount.toFixed(decimals),
}
@@ -240,7 +240,7 @@ ConfirmSendToken.prototype.getData = function () {
},
to: {
address: value,
- name: identities[value] ? identities[value].name : t('newRecipient'),
+ name: identities[value] ? identities[value].name : this.props.t('newRecipient'),
},
memo: txParams.memo || '',
}
@@ -286,7 +286,7 @@ ConfirmSendToken.prototype.renderGasFee = function () {
return (
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ this.props.t('gasFee') ]),
h('div.confirm-screen-section-column', [
h(GasFeeDisplay, {
gasTotal: gasTotal || gasFeeInHex,
@@ -308,8 +308,8 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
? (
h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ t('total') + ' ' ]),
- h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
+ h('span.confirm-screen-label', [ this.props.t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ this.props.t('amountPlusGas') ]),
]),
h('div.confirm-screen-section-column', [
@@ -321,13 +321,13 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
: (
h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ t('total') + ' ' ]),
- h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
+ h('span.confirm-screen-label', [ this.props.t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ this.props.t('amountPlusGas') ]),
]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${tokenAmount} ${symbol}`),
- h('div.confirm-screen-row-detail', `+ ${fiatGas} ${currentCurrency} ${t('gas')}`),
+ h('div.confirm-screen-row-detail', `+ ${fiatGas} ${currentCurrency} ${this.props.t('gas')}`),
]),
])
)
@@ -349,10 +349,11 @@ ConfirmSendToken.prototype.render = function () {
this.inputs = []
- const title = txMeta.lastGasPrice ? 'Reprice Transaction' : t('confirm')
- const subtitle = txMeta.lastGasPrice
- ? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
- : t('pleaseReviewTransaction')
+ const isTxReprice = Boolean(txMeta.lastGasPrice)
+ const title = isTxReprice ? this.props.t('reprice_title') : this.props.t('confirm')
+ const subtitle = isTxReprice
+ ? this.props.t('reprice_subtitle')
+ : this.props.t('pleaseReviewTransaction')
return (
h('div.confirm-screen-container.confirm-send-token', [
@@ -361,7 +362,7 @@ ConfirmSendToken.prototype.render = function () {
h('div.page-container__header', [
!txMeta.lastGasPrice && h('button.confirm-screen-back-button', {
onClick: () => editTransaction(txMeta),
- }, t('edit')),
+ }, this.props.t('edit')),
h('div.page-container__title', title),
h('div.page-container__subtitle', subtitle),
]),
@@ -405,7 +406,7 @@ ConfirmSendToken.prototype.render = function () {
h('div.confirm-screen-rows', [
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ this.props.t('from') ]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', fromName),
h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
@@ -413,7 +414,7 @@ ConfirmSendToken.prototype.render = function () {
]),
toAddress && h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ this.props.t('to') ]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', toName),
h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`),
@@ -427,6 +428,7 @@ ConfirmSendToken.prototype.render = function () {
]),
]),
+
h('form#pending-tx-form', {
onSubmit: this.onSubmit,
}, [
@@ -434,10 +436,10 @@ ConfirmSendToken.prototype.render = function () {
// Cancel Button
h('button.btn-cancel.page-container__footer-button.allcaps', {
onClick: (event) => this.cancel(event, txMeta),
- }, t('cancel')),
+ }, this.props.t('cancel')),
// Accept Button
- h('button.btn-confirm.page-container__footer-button.allcaps', [t('confirm')]),
+ h('button.btn-confirm.page-container__footer-button.allcaps', [this.props.t('confirm')]),
]),
]),
]),
@@ -454,7 +456,7 @@ ConfirmSendToken.prototype.onSubmit = function (event) {
if (valid && this.verifyGasParams()) {
this.props.sendTransaction(txMeta, event)
} else {
- this.props.dispatch(actions.displayWarning(t('invalidGasParams')))
+ this.props.dispatch(actions.displayWarning(this.props.t('invalidGasParams')))
this.setState({ submitting: false })
}
}
diff --git a/ui/app/components/pending-tx/index.js b/ui/app/components/pending-tx/index.js
index f4f6afb8f..0a8813b03 100644
--- a/ui/app/components/pending-tx/index.js
+++ b/ui/app/components/pending-tx/index.js
@@ -1,5 +1,5 @@
const Component = require('react').Component
-const { connect } = require('react-redux')
+const connect = require('../../metamask-connect')
const h = require('react-hyperscript')
const clone = require('clone')
const abi = require('human-standard-token-abi')
@@ -64,13 +64,20 @@ PendingTx.prototype.componentWillMount = async function () {
})
}
- try {
+ // inspect tx data for supported special confirmation screens
+ let isTokenTransaction = false
+ if (txParams.data) {
+ const tokenData = abiDecoder.decodeMethod(txParams.data)
+ const { name: tokenMethodName } = tokenData || {}
+ isTokenTransaction = (tokenMethodName === 'transfer')
+ }
+
+ if (isTokenTransaction) {
const token = util.getContractAtAddress(txParams.to)
const results = await Promise.all([
token.symbol(),
token.decimals(),
])
-
const [ symbol, decimals ] = results
if (symbol[0] && decimals[0]) {
@@ -83,11 +90,14 @@ PendingTx.prototype.componentWillMount = async function () {
})
} else {
this.setState({
- transactionType: TX_TYPES.SEND_ETHER,
+ transactionType: TX_TYPES.SEND_TOKEN,
+ tokenAddress: txParams.to,
+ tokenSymbol: null,
+ tokenDecimals: null,
isFetching: false,
})
}
- } catch (e) {
+ } else {
this.setState({
transactionType: TX_TYPES.SEND_ETHER,
isFetching: false,
diff --git a/ui/app/components/qr-code.js b/ui/app/components/qr-code.js
index 83885539c..89504eca3 100644
--- a/ui/app/components/qr-code.js
+++ b/ui/app/components/qr-code.js
@@ -2,7 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const qrCode = require('qrcode-npm').qrcode
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const isHexPrefixed = require('ethereumjs-util').isHexPrefixed
const ReadOnlyInput = require('./readonly-input')
diff --git a/ui/app/components/send/account-list-item.js b/ui/app/components/send/account-list-item.js
index 1ad3f69c1..749339694 100644
--- a/ui/app/components/send/account-list-item.js
+++ b/ui/app/components/send/account-list-item.js
@@ -1,7 +1,7 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const Identicon = require('../identicon')
const CurrencyDisplay = require('./currency-display')
const { conversionRateSelector, getCurrentCurrency } = require('../../selectors')
diff --git a/ui/app/components/send/gas-fee-display-v2.js b/ui/app/components/send/gas-fee-display-v2.js
index f6af13454..76ed1b5d4 100644
--- a/ui/app/components/send/gas-fee-display-v2.js
+++ b/ui/app/components/send/gas-fee-display-v2.js
@@ -2,9 +2,9 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const CurrencyDisplay = require('./currency-display')
-const t = require('../../../i18n')
+const connect = require('../../metamask-connect')
-module.exports = GasFeeDisplay
+module.exports = connect()(GasFeeDisplay)
inherits(GasFeeDisplay, Component)
function GasFeeDisplay () {
@@ -33,8 +33,8 @@ GasFeeDisplay.prototype.render = function () {
readOnly: true,
})
: gasLoadingError
- ? h('div..currency-display.currency-display--message', 'Set with the gas price customizer.')
- : h('div.currency-display', t('loading')),
+ ? h('div.currency-display.currency-display--message', this.props.t('setGasPrice'))
+ : h('div.currency-display', this.props.t('loading')),
h('button.sliders-icon-container', {
onClick,
diff --git a/ui/app/components/send/gas-tooltip.js b/ui/app/components/send/gas-tooltip.js
index d925d3ed8..607394c8b 100644
--- a/ui/app/components/send/gas-tooltip.js
+++ b/ui/app/components/send/gas-tooltip.js
@@ -2,9 +2,9 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const InputNumber = require('../input-number.js')
-const t = require('../../../i18n')
+const connect = require('../../metamask-connect')
-module.exports = GasTooltip
+module.exports = connect()(GasTooltip)
inherits(GasTooltip, Component)
function GasTooltip () {
@@ -82,7 +82,7 @@ GasTooltip.prototype.render = function () {
'marginTop': '81px',
},
}, [
- h('span.gas-tooltip-label', {}, [t('gasLimit')]),
+ h('span.gas-tooltip-label', {}, [this.props.t('gasLimit')]),
h('i.fa.fa-info-circle'),
]),
h(InputNumber, {
diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js
index d1319b6dc..5b903a96e 100644
--- a/ui/app/components/send/send-v2-container.js
+++ b/ui/app/components/send/send-v2-container.js
@@ -1,4 +1,4 @@
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const actions = require('../../actions')
const abi = require('ethereumjs-abi')
const SendEther = require('../../send-v2')
@@ -69,13 +69,13 @@ function mapDispatchToProps (dispatch) {
updateAndApproveTx: txParams => dispatch(actions.updateAndApproveTx(txParams)),
updateTx: txData => dispatch(actions.updateTransaction(txData)),
setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)),
- addToAddressBook: address => dispatch(actions.addToAddressBook(address)),
+ addToAddressBook: (address, nickname) => dispatch(actions.addToAddressBook(address, nickname)),
updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)),
updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)),
updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)),
updateSendTokenBalance: tokenBalance => dispatch(actions.updateSendTokenBalance(tokenBalance)),
updateSendFrom: newFrom => dispatch(actions.updateSendFrom(newFrom)),
- updateSendTo: newTo => dispatch(actions.updateSendTo(newTo)),
+ updateSendTo: (newTo, nickname) => dispatch(actions.updateSendTo(newTo, nickname)),
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)),
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js
index 72074229e..4c54701cb 100644
--- a/ui/app/components/send/to-autocomplete.js
+++ b/ui/app/components/send/to-autocomplete.js
@@ -2,9 +2,9 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const AccountListItem = require('./account-list-item')
-const t = require('../../../i18n')
+const connect = require('../../metamask-connect')
-module.exports = ToAutoComplete
+module.exports = connect()(ToAutoComplete)
inherits(ToAutoComplete, Component)
function ToAutoComplete () {
@@ -93,7 +93,7 @@ ToAutoComplete.prototype.render = function () {
return h('div.send-v2__to-autocomplete', {}, [
h('input.send-v2__to-autocomplete__input', {
- placeholder: t('recipientAddress'),
+ placeholder: this.props.t('recipientAddress'),
className: inError ? `send-v2__error-border` : '',
value: to,
onChange: event => onChange(event.target.value),
diff --git a/ui/app/components/sender-to-recipient.js b/ui/app/components/sender-to-recipient.js
index f35c353ad..4c3881668 100644
--- a/ui/app/components/sender-to-recipient.js
+++ b/ui/app/components/sender-to-recipient.js
@@ -1,7 +1,7 @@
const { Component } = require('react')
const h = require('react-hyperscript')
+const connect = require('../metamask-connect')
const PropTypes = require('prop-types')
-const t = require('../../i18n')
const Identicon = require('./identicon')
class SenderToRecipient extends Component {
@@ -21,7 +21,7 @@ class SenderToRecipient extends Component {
this.renderRecipientIcon(),
h(
'.sender-to-recipient__name.sender-to-recipient__recipient-name',
- recipientName || t('newContract')
+ recipientName || this.props.t('newContract')
),
])
)
@@ -61,6 +61,7 @@ SenderToRecipient.propTypes = {
senderAddress: PropTypes.string,
recipientName: PropTypes.string,
recipientAddress: PropTypes.string,
+ t: PropTypes.func,
}
-module.exports = SenderToRecipient
+module.exports = connect()(SenderToRecipient)
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index 3f8c17932..5f58527fe 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -1,13 +1,12 @@
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const classnames = require('classnames')
const { qrcode } = require('qrcode-npm')
const { shapeShiftSubview, pairUpdate, buyWithShapeShift } = require('../actions')
const { isValidAddress } = require('../util')
const SimpleDropdown = require('./dropdowns/simple-dropdown')
-const t = require('../../i18n')
function mapStateToProps (state) {
const {
@@ -94,7 +93,7 @@ ShapeshiftForm.prototype.onBuyWithShapeShift = function () {
}))
.catch(() => this.setState({
showQrCode: false,
- errorMessage: t('invalidRequest'),
+ errorMessage: this.props.t('invalidRequest'),
isLoading: false,
}))
}
@@ -126,10 +125,10 @@ ShapeshiftForm.prototype.renderMarketInfo = function () {
return h('div.shapeshift-form__metadata', {}, [
- this.renderMetadata(t('status'), limit ? t('available') : t('unavailable')),
- this.renderMetadata(t('limit'), limit),
- this.renderMetadata(t('exchangeRate'), rate),
- this.renderMetadata(t('min'), minimum),
+ this.renderMetadata(this.props.t('status'), limit ? this.props.t('available') : this.props.t('unavailable')),
+ this.renderMetadata(this.props.t('limit'), limit),
+ this.renderMetadata(this.props.t('exchangeRate'), rate),
+ this.renderMetadata(this.props.t('min'), minimum),
])
}
@@ -143,7 +142,7 @@ ShapeshiftForm.prototype.renderQrCode = function () {
return h('div.shapeshift-form', {}, [
h('div.shapeshift-form__deposit-instruction', [
- t('depositCoin', [depositCoin.toUpperCase()]),
+ this.props.t('depositCoin', [depositCoin.toUpperCase()]),
]),
h('div', depositAddress),
@@ -180,7 +179,7 @@ ShapeshiftForm.prototype.render = function () {
h('div.shapeshift-form__selector', [
- h('div.shapeshift-form__selector-label', t('deposit')),
+ h('div.shapeshift-form__selector-label', this.props.t('deposit')),
h(SimpleDropdown, {
selectedOption: this.state.depositCoin,
@@ -200,7 +199,7 @@ ShapeshiftForm.prototype.render = function () {
h('div.shapeshift-form__selector', [
h('div.shapeshift-form__selector-label', [
- t('receive'),
+ this.props.t('receive'),
]),
h('div.shapeshift-form__selector-input', ['ETH']),
@@ -218,7 +217,7 @@ ShapeshiftForm.prototype.render = function () {
}, [
h('div.shapeshift-form__address-input-label', [
- t('refundAddress'),
+ this.props.t('refundAddress'),
]),
h('input.shapeshift-form__address-input', {
@@ -236,11 +235,11 @@ ShapeshiftForm.prototype.render = function () {
]),
- !depositAddress && h('button.shapeshift-form__shapeshift-buy-btn', {
+ !depositAddress && h('button.btn-primary--lg.shapeshift-form__shapeshift-buy-btn', {
className: btnClass,
disabled: !token,
onClick: () => this.onBuyWithShapeShift(),
- }, [t('buy')]),
+ }, [this.props.t('buy')]),
])
}
diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js
index fddbc6821..d810eddc9 100644
--- a/ui/app/components/shift-list-item.js
+++ b/ui/app/components/shift-list-item.js
@@ -1,12 +1,11 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const vreme = new (require('vreme'))()
const explorerLink = require('etherscan-link').createExplorerLink
const actions = require('../actions')
const addressSummary = require('../util').addressSummary
-const t = require('../../i18n')
const CopyButton = require('./copyButton')
const EthBalance = require('./eth-balance')
@@ -76,7 +75,7 @@ ShiftListItem.prototype.renderUtilComponents = function () {
value: this.props.depositAddress,
}),
h(Tooltip, {
- title: t('qrCode'),
+ title: this.props.t('qrCode'),
}, [
h('i.fa.fa-qrcode.pointer.pop-hover', {
onClick: () => props.dispatch(actions.reshowQrCode(props.depositAddress, props.depositType)),
@@ -136,8 +135,8 @@ ShiftListItem.prototype.renderInfo = function () {
color: '#ABA9AA',
width: '100%',
},
- }, t('toETHviaShapeShift', [props.depositType])),
- h('div', t('noDeposits')),
+ }, this.props.t('toETHviaShapeShift', [props.depositType])),
+ h('div', this.props.t('noDeposits')),
h('div', {
style: {
fontSize: 'x-small',
@@ -159,8 +158,8 @@ ShiftListItem.prototype.renderInfo = function () {
color: '#ABA9AA',
width: '100%',
},
- }, t('toETHviaShapeShift', [props.depositType])),
- h('div', t('conversionProgress')),
+ }, this.props.t('toETHviaShapeShift', [props.depositType])),
+ h('div', this.props.t('conversionProgress')),
h('div', {
style: {
fontSize: 'x-small',
@@ -185,7 +184,7 @@ ShiftListItem.prototype.renderInfo = function () {
color: '#ABA9AA',
width: '100%',
},
- }, t('fromShapeShift')),
+ }, this.props.t('fromShapeShift')),
h('div', formatDate(props.time)),
h('div', {
style: {
@@ -197,7 +196,7 @@ ShiftListItem.prototype.renderInfo = function () {
])
case 'failed':
- return h('span.error', '(' + t('failed') + ')')
+ return h('span.error', '(' + this.props.t('failed') + ')')
default:
return ''
}
diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js
index 7bf34e7b6..b76c1e60f 100644
--- a/ui/app/components/signature-request.js
+++ b/ui/app/components/signature-request.js
@@ -2,14 +2,13 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Identicon = require('./identicon')
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const ethUtil = require('ethereumjs-util')
const classnames = require('classnames')
const AccountDropdownMini = require('./dropdowns/account-dropdown-mini')
const actions = require('../actions')
-const t = require('../../i18n')
const { conversionUtil } = require('../conversion-util')
const {
@@ -55,7 +54,7 @@ SignatureRequest.prototype.renderHeader = function () {
h('div.request-signature__header-background'),
- h('div.request-signature__header__text', t('sigRequest')),
+ h('div.request-signature__header__text', this.props.t('sigRequest')),
h('div.request-signature__header__tip-container', [
h('div.request-signature__header__tip'),
@@ -76,7 +75,7 @@ SignatureRequest.prototype.renderAccountDropdown = function () {
return h('div.request-signature__account', [
- h('div.request-signature__account-text', [t('account') + ':']),
+ h('div.request-signature__account-text', [this.props.t('account') + ':']),
h(AccountDropdownMini, {
selectedAccount,
@@ -103,7 +102,7 @@ SignatureRequest.prototype.renderBalance = function () {
return h('div.request-signature__balance', [
- h('div.request-signature__balance-text', [t('balance')]),
+ h('div.request-signature__balance-text', [this.props.t('balance')]),
h('div.request-signature__balance-value', `${balanceInEther} ETH`),
@@ -137,7 +136,7 @@ SignatureRequest.prototype.renderRequestInfo = function () {
return h('div.request-signature__request-info', [
h('div.request-signature__headline', [
- t('yourSigRequested'),
+ this.props.t('yourSigRequested'),
]),
])
@@ -155,18 +154,18 @@ SignatureRequest.prototype.msgHexToText = function (hex) {
SignatureRequest.prototype.renderBody = function () {
let rows
- let notice = t('youSign') + ':'
+ let notice = this.props.t('youSign') + ':'
const { txData } = this.props
const { type, msgParams: { data } } = txData
if (type === 'personal_sign') {
- rows = [{ name: t('message'), value: this.msgHexToText(data) }]
+ rows = [{ name: this.props.t('message'), value: this.msgHexToText(data) }]
} else if (type === 'eth_signTypedData') {
rows = data
} else if (type === 'eth_sign') {
- rows = [{ name: t('message'), value: data }]
- notice = t('signNotice')
+ rows = [{ name: this.props.t('message'), value: data }]
+ notice = this.props.t('signNotice')
}
return h('div.request-signature__body', {}, [
@@ -223,12 +222,12 @@ SignatureRequest.prototype.renderFooter = function () {
}
return h('div.request-signature__footer', [
- h('button.request-signature__footer__cancel-button', {
+ h('button.btn-secondary--lg.request-signature__footer__cancel-button', {
onClick: cancel,
- }, t('cancel')),
- h('button.request-signature__footer__sign-button', {
+ }, this.props.t('cancel')),
+ h('button.btn-primary--lg', {
onClick: sign,
- }, t('sign')),
+ }, this.props.t('sign')),
])
}
diff --git a/ui/app/components/token-balance.js b/ui/app/components/token-balance.js
index 2f71c0687..7d7744fe6 100644
--- a/ui/app/components/token-balance.js
+++ b/ui/app/components/token-balance.js
@@ -2,7 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker')
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const selectors = require('../selectors')
function mapStateToProps (state) {
diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js
index 0332fde88..a24781af1 100644
--- a/ui/app/components/token-cell.js
+++ b/ui/app/components/token-cell.js
@@ -1,7 +1,7 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const Identicon = require('./identicon')
const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
const selectors = require('../selectors')
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
index 01529aeda..ae22f3702 100644
--- a/ui/app/components/token-list.js
+++ b/ui/app/components/token-list.js
@@ -3,9 +3,8 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker')
const TokenCell = require('./token-cell.js')
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const selectors = require('../selectors')
-const t = require('../../i18n')
function mapStateToProps (state) {
return {
@@ -43,7 +42,7 @@ TokenList.prototype.render = function () {
const { tokens, isLoading, error } = state
if (isLoading) {
- return this.message(t('loadingTokens'))
+ return this.message(this.props.t('loadingTokens'))
}
if (error) {
@@ -53,7 +52,7 @@ TokenList.prototype.render = function () {
padding: '80px',
},
}, [
- t('troubleTokenBalances'),
+ this.props.t('troubleTokenBalances'),
h('span.hotFix', {
style: {
color: 'rgba(247, 134, 28, 1)',
@@ -64,7 +63,7 @@ TokenList.prototype.render = function () {
url: `https://ethplorer.io/address/${userAddress}`,
})
},
- }, t('here')),
+ }, this.props.t('here')),
])
}
diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js
index d104eda88..5e88d38d3 100644
--- a/ui/app/components/tx-list-item.js
+++ b/ui/app/components/tx-list-item.js
@@ -1,6 +1,6 @@
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const inherits = require('util').inherits
const classnames = require('classnames')
const abi = require('human-standard-token-abi')
@@ -14,7 +14,6 @@ const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
const { calcTokenAmount } = require('../token-util')
const { getCurrentCurrency } = require('../selectors')
-const t = require('../../i18n')
module.exports = connect(mapStateToProps, mapDispatchToProps)(TxListItem)
@@ -75,7 +74,7 @@ TxListItem.prototype.getAddressText = function () {
default:
return address
? `${address.slice(0, 10)}...${address.slice(-4)}`
- : t('contractDeployment')
+ : this.props.t('contractDeployment')
}
}
@@ -307,21 +306,21 @@ TxListItem.prototype.txStatusIndicator = function () {
let name
if (transactionStatus === 'unapproved') {
- name = t('unapproved')
+ name = this.props.t('unapproved')
} else if (transactionStatus === 'rejected') {
- name = t('rejected')
+ name = this.props.t('rejected')
} else if (transactionStatus === 'approved') {
- name = t('approved')
+ name = this.props.t('approved')
} else if (transactionStatus === 'signed') {
- name = t('signed')
+ name = this.props.t('signed')
} else if (transactionStatus === 'submitted') {
- name = t('submitted')
+ name = this.props.t('submitted')
} else if (transactionStatus === 'confirmed') {
- name = t('confirmed')
+ name = this.props.t('confirmed')
} else if (transactionStatus === 'failed') {
- name = t('failed')
+ name = this.props.t('failed')
} else if (transactionStatus === 'dropped') {
- name = t('dropped')
+ name = this.props.t('dropped')
}
return name
}
diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js
index 037c7de8c..5f09d887e 100644
--- a/ui/app/components/tx-list.js
+++ b/ui/app/components/tx-list.js
@@ -1,5 +1,5 @@
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
@@ -10,7 +10,6 @@ const { formatDate } = require('../util')
const { showConfTxPage } = require('../actions')
const classnames = require('classnames')
const { tokenInfoGetter } = require('../token-util')
-const t = require('../../i18n')
module.exports = connect(mapStateToProps, mapDispatchToProps)(TxList)
@@ -40,7 +39,7 @@ TxList.prototype.render = function () {
return h('div.flex-column', [
h('div.flex-row.tx-list-header-wrapper', [
h('div.flex-row.tx-list-header', [
- h('div', t('transactions')),
+ h('div', this.props.t('transactions')),
]),
]),
h('div.flex-column.tx-list-container', {}, [
@@ -57,7 +56,7 @@ TxList.prototype.renderTransaction = function () {
: [h(
'div.tx-list-item.tx-list-item--empty',
{ key: 'tx-list-none' },
- [ t('noTransactions') ],
+ [ this.props.t('noTransactions') ],
)]
}
@@ -110,8 +109,8 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
const isUnapproved = transactionStatus === 'unapproved'
if (isUnapproved) {
- opts.onClick = () => showConfTxPage({id: transactionId})
- opts.transactionStatus = t('Not Started')
+ opts.onClick = () => showConfTxPage({ id: transactionId })
+ opts.transactionStatus = this.props.t('notStarted')
} else if (transactionHash) {
opts.onClick = () => this.view(transactionHash, transactionNetworkId)
}
diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js
index 96d776270..8b0cc890a 100644
--- a/ui/app/components/tx-view.js
+++ b/ui/app/components/tx-view.js
@@ -1,11 +1,10 @@
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const h = require('react-hyperscript')
const ethUtil = require('ethereumjs-util')
const inherits = require('util').inherits
const actions = require('../actions')
const selectors = require('../selectors')
-const t = require('../../i18n')
const BalanceComponent = require('./balance-component')
const TxList = require('./tx-list')
@@ -69,25 +68,25 @@ TxView.prototype.renderButtons = function () {
return !selectedToken
? (
h('div.flex-row.flex-center.hero-balance-buttons', [
- h('button.btn-clear.hero-balance-button.allcaps', {
+ h('button.btn-primary.hero-balance-button', {
onClick: () => showModal({
name: 'DEPOSIT_ETHER',
}),
- }, t('deposit')),
+ }, this.props.t('deposit')),
- h('button.btn-clear.hero-balance-button.allcaps', {
+ h('button.btn-primary.hero-balance-button', {
style: {
marginLeft: '0.8em',
},
onClick: showSendPage,
- }, t('send')),
+ }, this.props.t('send')),
])
)
: (
h('div.flex-row.flex-center.hero-balance-buttons', [
- h('button.btn-clear.hero-balance-button', {
+ h('button.btn-primary.hero-balance-button', {
onClick: showSendTokenPage,
- }, t('send')),
+ }, this.props.t('send')),
])
)
}
diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js
index 18452205c..5f1ed7b60 100644
--- a/ui/app/components/wallet-view.js
+++ b/ui/app/components/wallet-view.js
@@ -1,5 +1,5 @@
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const classnames = require('classnames')
@@ -11,7 +11,6 @@ const actions = require('../actions')
const BalanceComponent = require('./balance-component')
const TokenList = require('./token-list')
const selectors = require('../selectors')
-const t = require('../../i18n')
module.exports = connect(mapStateToProps, mapDispatchToProps)(WalletView)
@@ -117,7 +116,7 @@ WalletView.prototype.render = function () {
onClick: hideSidebar,
}),
- h('div.wallet-view__keyring-label.allcaps', isLoose ? t('imported') : ''),
+ h('div.wallet-view__keyring-label.allcaps', isLoose ? this.props.t('imported') : ''),
h('div.flex-column.flex-center.wallet-view__name-container', {
style: { margin: '0 auto' },
@@ -134,13 +133,13 @@ WalletView.prototype.render = function () {
selectedIdentity.name,
]),
- h('button.btn-clear.wallet-view__details-button.allcaps', t('details')),
+ h('button.btn-clear.wallet-view__details-button.allcaps', this.props.t('details')),
]),
]),
h(Tooltip, {
position: 'bottom',
- title: this.state.hasCopied ? t('copiedExclamation') : t('copyToClipboard'),
+ title: this.state.hasCopied ? this.props.t('copiedExclamation') : this.props.t('copyToClipboard'),
wrapperClassName: 'wallet-view__tooltip',
}, [
h('button.wallet-view__address', {
@@ -168,12 +167,12 @@ WalletView.prototype.render = function () {
h(TokenList),
- h('button.btn-clear.wallet-view__add-token-button', {
+ h('button.btn-primary.wallet-view__add-token-button', {
onClick: () => {
showAddTokenPage()
hideSidebar()
},
- }, t('addToken')),
+ }, this.props.t('addToken')),
])
}
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index 1070436c3..03848f490 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -1,7 +1,7 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
+const connect = require('./metamask-connect')
const actions = require('./actions')
const txHelper = require('../lib/tx-helper')
diff --git a/ui/app/css/itcss/components/add-token.scss b/ui/app/css/itcss/components/add-token.scss
index 13020f62f..f5c1de67c 100644
--- a/ui/app/css/itcss/components/add-token.scss
+++ b/ui/app/css/itcss/components/add-token.scss
@@ -1,37 +1,118 @@
.add-token {
width: 498px;
+ max-height: 805px;
display: flex;
flex-flow: column nowrap;
- align-items: center;
position: relative;
z-index: 12;
- font-family: 'DIN Next Light';
+ font-family: 'Roboto';
+ background: white;
+ border-radius: 8px;
&__wrapper {
background-color: $white;
- box-shadow: 0 2px 4px 0 rgba($black, .08);
display: flex;
flex-flow: column nowrap;
align-items: center;
flex: 0 0 auto;
}
- &__title-container {
+ &__header {
display: flex;
flex-flow: column nowrap;
- align-items: center;
- padding: 30px 60px 12px;
- border-bottom: 1px solid $gallery;
+ padding: 16px 16px 0px;
+ border-bottom: 1px solid $geyser;
flex: 0 0 auto;
+
+ &__cancel {
+ color: $dodger-blue;
+ display: flex;
+ align-items: center;
+
+ span {
+ font-family: Roboto;
+ font-size: 16px;
+ line-height: 21px;
+ margin-left: 8px;
+ }
+ }
+
+ &__title {
+ color: $tundora;
+ font-size: 32px;
+ font-weight: 500;
+ margin-top: 4px;
+ }
+
+ &__tabs {
+ margin-left: 22px;
+ display: flex;
+
+ &__tab {
+ height: 54px;
+ padding: 15px 10px;
+ color: $dusty-gray;
+ font-family: Roboto;
+ font-size: 18px;
+ line-height: 24px;
+ text-align: center;
+ }
+
+ &__tab:first-of-type {
+ margin-right: 20px;
+ }
+
+ &__unselected:hover {
+ color: $black;
+ border-bottom: none;
+ }
+
+ &__selected {
+ color: $curious-blue;
+ border-bottom: 3px solid $curious-blue;
+ }
+ }
}
- &__title {
- color: $scorpion;
- font-size: 20px;
- line-height: 26px;
- text-align: center;
- font-weight: 600;
- margin-bottom: 12px;
+ &__info-box {
+ height: 96px;
+ margin: 20px 24px 0px;
+ border-radius: 4px;
+ background-color: $alabaster;
+ position: relative;
+ padding-left: 18px;
+ display: flex;
+ flex-flow: column;
+
+ &__close::after {
+ content: '\00D7';
+ font-size: 29px;
+ font-weight: 200;
+ color: $dusty-gray;
+ position: absolute;
+ right: 17px;
+ cursor: pointer;
+ }
+
+ &__title {
+ color: $mid-gray;
+ font-family: Roboto;
+ font-size: 14px;
+ margin-top: 15px;
+ margin-bottom: 9px;
+ }
+
+ &__copy,
+ &__copy--blue {
+ color: $mid-gray;
+ font-family: Roboto;
+ font-size: 12px;
+ line-height: 18px;
+ }
+
+ &__copy--blue {
+ color: $curious-blue;
+ }
}
&__description {
@@ -48,19 +129,17 @@
&__content-container {
width: 100%;
- border-bottom: 1px solid $gallery;
}
&__input-container {
- padding: 11px 0;
- width: 263px;
- margin: 0 auto;
+ display: flex;
position: relative;
}
&__search-input-error-message {
position: absolute;
bottom: -10px;
+ left: 22px;
font-size: 12px;
width: 100%;
text-overflow: ellipsis;
@@ -69,16 +148,24 @@
color: $red;
}
- &__input {
- width: 100%;
- border: 2px solid $gallery;
+ &__input,
+ &__add-custom-input {
+ height: 54px;
+ padding: 21px 6px;
+ border: 1px solid $geyser;
border-radius: 4px;
- padding: 5px 15px;
- font-size: 14px;
- line-height: 19px;
+ margin: 22px 24px;
+ position: relative;
+ flex: 1 0 auto;
+ color: $scorpion;
+ font-family: Roboto;
+ font-size: 16px;
&::placeholder {
- color: $silver;
+ color: $scorpion;
+ font-family: Roboto;
+ font-size: 16px;
+ line-height: 21px;
}
}
@@ -115,13 +202,14 @@
&__add-custom-form {
display: flex;
flex-flow: column nowrap;
- margin: 8px 0 51px;
+ margin: 40px 0 30px;
}
&__add-custom-field {
- width: 290px;
- margin: 0 auto;
position: relative;
+ display: flex;
+ flex-flow: column;
+ flex: 1 0 auto;
&--error {
.add-token__add-custom-input {
@@ -132,7 +220,8 @@
&__add-custom-error-message {
position: absolute;
- bottom: -21px;
+ bottom: 1px;
+ left: 22px;
font-size: 12px;
width: 100%;
text-overflow: ellipsis;
@@ -144,39 +233,50 @@
&__add-custom-label {
font-size: 16px;
line-height: 21px;
- margin-bottom: 8px;
+ margin-left: 22px;
+ color: $scorpion;
}
&__add-custom-input {
- width: 100%;
- border: 1px solid $silver;
- padding: 5px 15px;
- font-size: 14px;
- line-height: 19px;
+ margin-top: 6px;
+ font-size: 16px;
&::placeholder {
color: $silver;
+ font-size: 16px;
}
}
&__add-custom-field + &__add-custom-field {
- margin-top: 21px;
+ margin-top: 6px;
}
&__buttons {
display: flex;
flex-flow: row nowrap;
- margin: 30px 0 51px;
flex: 0 0 auto;
align-items: center;
justify-content: center;
+ padding-bottom: 30px;
+ padding-top: 20px;
}
- &__button {
- flex: 1 0 141px;
+ &__confirm-button,
+ &__cancel-button {
margin: 0 12px;
- padding: 10px 22px;
+ padding: 10px 13px;
height: 54px;
+ width: 133px;
+ margin-right: 1.2rem;
+ }
+
+ &__token-icons-title {
+ color: #5B5D67;
+ font-family: Roboto;
+ font-size: 18px;
+ line-height: 24px;
+ margin-left: 24px;
+ margin-top: 8px;
}
&__token-icons-container {
@@ -191,7 +291,7 @@
flex: 0 0 42.5%;
align-items: center;
padding: 12px;
- margin: 2.5%;
+ margin: 0% 2.5% 1.5%;
box-sizing: border-box;
border-radius: 10px;
cursor: pointer;
@@ -305,13 +405,14 @@
top: 0;
width: 100%;
overflow: hidden;
- height: 100%;
+ flex: 1 0 auto;
&__wrapper {
box-shadow: none !important;
flex: 1 1 auto;
width: 100%;
- overflow-y: auto;
+ overflow-y: scroll;
+ height: 400px;
}
&__footers {
@@ -334,7 +435,7 @@
}
&__buttons {
- padding: 12px 0;
+ padding: 1rem;
margin: 0;
border-top: 1px solid $gallery;
width: 100%;
diff --git a/ui/app/css/itcss/components/buttons.scss b/ui/app/css/itcss/components/buttons.scss
index 8df8829f2..04e1ed96e 100644
--- a/ui/app/css/itcss/components/buttons.scss
+++ b/ui/app/css/itcss/components/buttons.scss
@@ -2,6 +2,76 @@
Buttons
*/
+.btn-primary,
+.btn-primary--lg,
+.btn-secondary,
+.btn-secondary--lg {
+ background: $white;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ box-sizing: border-box;
+ border-radius: 4px;
+ font-size: 14px;
+ font-weight: 500;
+ transition: border-color .3s ease;
+ padding: 0 20px;
+ min-width: 140px;
+ text-transform: uppercase;
+}
+
+.btn-primary,
+.btn-primary--lg {
+ color: $curious-blue;
+ border: 2px solid $spindle;
+
+ &:active {
+ background: $zumthor;
+ border-color: $curious-blue;
+ }
+
+ &:hover {
+ border-color: $curious-blue;
+ }
+
+ &--disabled,
+ &[disabled] {
+ cursor: auto;
+ opacity: .5;
+ pointer-events: none;
+ }
+}
+
+.btn-secondary,
+.btn-secondary--lg {
+ color: $scorpion;
+ border: 2px solid $dusty-gray;
+
+ &:active {
+ background: $gallery;
+ border-color: $dusty-gray;
+ }
+
+ &:hover {
+ border-color: $scorpion;
+ }
+
+ &--disabled,
+ &[disabled] {
+ cursor: auto;
+ opacity: .5;
+ pointer-events: none;
+ }
+}
+
+.btn-primary, .btn-secondary {
+ height: 44px;
+}
+
+.btn-primary--lg, .btn-secondary--lg {
+ height: 54px;
+}
+
.btn-green {
background-color: #02c9b1; // TODO: reusable color in colors.css
}
@@ -130,20 +200,6 @@ button.btn-thin {
font-size: 13px;
}
-.btn-secondary {
- border: 1px solid #979797;
- border-radius: 2px;
- background-color: $white;
- font-size: 16px;
- line-height: 24px;
- padding: 16px 42px;
-
- &[disabled] {
- background-color: $white !important;
- opacity: .5;
- }
-}
-
.btn-tertiary {
border: 1px solid transparent;
border-radius: 2px;
diff --git a/ui/app/css/itcss/components/hero-balance.scss b/ui/app/css/itcss/components/hero-balance.scss
index a3f051361..69cde8a0f 100644
--- a/ui/app/css/itcss/components/hero-balance.scss
+++ b/ui/app/css/itcss/components/hero-balance.scss
@@ -103,10 +103,11 @@
}
.hero-balance-button {
+ min-width: initial;
width: 6rem;
@media #{$sub-mid-size-breakpoint-range} {
- padding: 0.4rem;
+ padding: .4rem;
width: 4rem;
display: flex;
flex: 1;
diff --git a/ui/app/css/itcss/components/modal.scss b/ui/app/css/itcss/components/modal.scss
index a8d5e8dc2..9ae3ea7fa 100644
--- a/ui/app/css/itcss/components/modal.scss
+++ b/ui/app/css/itcss/components/modal.scss
@@ -261,7 +261,7 @@
.account-modal__button {
margin-top: 17px;
padding: 10px 22px;
- width: 235px;
+ width: 286px;
}
}
@@ -341,9 +341,8 @@
.export-private-key__button {
margin-top: 17px;
- padding: 10px 22px;
width: 141px;
- height: 54px;
+ min-width: initial;
}
.export-private-key__button--cancel {
@@ -765,15 +764,7 @@
}
&__deposit-button, .shapeshift-form__shapeshift-buy-btn {
- height: 54px;
width: 257px;
- border: 1px solid $curious-blue;
- border-radius: 4px;
- display: flex;
- justify-content: center;
- font-size: 16px;
- color: $curious-blue;
- background-color: $white;
}
.shapeshift-form-wrapper {
diff --git a/ui/app/css/itcss/components/new-account.scss b/ui/app/css/itcss/components/new-account.scss
index c2cefe4ad..aa7fed956 100644
--- a/ui/app/css/itcss/components/new-account.scss
+++ b/ui/app/css/itcss/components/new-account.scss
@@ -192,29 +192,8 @@
justify-content: space-between;
}
- &__button-cancel,
- &__button-create {
- height: 55px;
+ &__button {
width: 150px;
- border-radius: 2px;
- background-color: #FFFFFF;
- }
-
- &__button-cancel {
- border: 1px solid $dusty-gray;
- color: $dusty-gray;
- font-family: Roboto;
- font-size: 16px;
- line-height: 21px;
- text-align: center;
+ min-width: initial;
}
-
- &__button-create {
- border: 1px solid $curious-blue;
- color: $curious-blue;
- font-family: Roboto;
- font-size: 16px;
- line-height: 21px;
- text-align: center;
- }
-} \ No newline at end of file
+}
diff --git a/ui/app/css/itcss/components/request-signature.scss b/ui/app/css/itcss/components/request-signature.scss
index d81099bfa..083481b8f 100644
--- a/ui/app/css/itcss/components/request-signature.scss
+++ b/ui/app/css/itcss/components/request-signature.scss
@@ -162,6 +162,7 @@
&__row {
display: flex;
flex-flow: column;
+ flex: 1 0 auto;
}
&__row-title {
@@ -190,41 +191,19 @@
width: 100%;
display: flex;
align-items: center;
- justify-content: space-evenly;
+ justify-content: center;
font-size: 22px;
position: relative;
flex: 0 0 auto;
border-top: 1px solid $geyser;
+ padding: 1.6rem;
- &__cancel-button,
- &__sign-button {
- display: flex;
- align-items: center;
- justify-content: center;
- flex: 1 0 auto;
- font-family: Roboto;
- font-size: 16px;
- font-weight: 300;
- height: 55px;
- line-height: 32px;
- cursor: pointer;
- border-radius: 2px;
- box-shadow: none;
- max-width: 162px;
- margin: 12px;
+ button {
+ width: 165px;
}
&__cancel-button {
- background: none;
- border: 1px solid $dusty-gray;
- margin-right: 6px;
- }
-
- &__sign-button {
- background-color: $caribbean-green;
- border-width: 0;
- color: $white;
- margin-left: 6px;
+ margin-right: 1.2rem;
}
}
-} \ No newline at end of file
+}
diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss
index 263b362ca..89d2be891 100644
--- a/ui/app/css/itcss/components/send.scss
+++ b/ui/app/css/itcss/components/send.scss
@@ -782,7 +782,6 @@
&__buttons {
display: flex;
justify-content: space-between;
- width: 181.75px;
margin-right: 21.25px;
}
@@ -800,13 +799,8 @@
}
&__cancel, &__save, &__save__error {
- height: 34.64px;
width: 85.74px;
- border: 1px solid $dusty-gray;
- border-radius: 2px;
- font-family: 'DIN OT';
- font-size: 12px;
- color: $dusty-gray;
+ min-width: initial;
}
&__save__error {
diff --git a/ui/app/css/itcss/components/settings.scss b/ui/app/css/itcss/components/settings.scss
index d60ebd934..dcc9b98d5 100644
--- a/ui/app/css/itcss/components/settings.scss
+++ b/ui/app/css/itcss/components/settings.scss
@@ -130,24 +130,32 @@
cursor: pointer;
}
-.settings__clear-button {
- font-size: 16px;
- border: 1px solid $curious-blue;
- color: $curious-blue;
- border-radius: 2px;
- padding: 18px;
- background-color: $white;
- text-transform: uppercase;
-}
-
-.settings__clear-button--red {
- border: 1px solid $monzo;
+.settings__button--red {
+ border-color: lighten($monzo, 10%);
color: $monzo;
+
+ &:active {
+ background: lighten($monzo, 55%);
+ border-color: $monzo;
+ }
+
+ &:hover {
+ border-color: $monzo;
+ }
}
-.settings__clear-button--orange {
- border: 1px solid rgba(247, 134, 28, 1);
- color: rgba(247, 134, 28, 1);
+.settings__button--orange {
+ border-color: lighten($ecstasy, 20%);
+ color: $ecstasy;
+
+ &:active {
+ background: lighten($ecstasy, 40%);
+ border-color: $ecstasy;
+ }
+
+ &:hover {
+ border-color: $ecstasy;
+ }
}
.settings__info-logo-wrapper {
diff --git a/ui/app/css/itcss/components/token-list.scss b/ui/app/css/itcss/components/token-list.scss
index 9dc4f1055..e8de317e3 100644
--- a/ui/app/css/itcss/components/token-list.scss
+++ b/ui/app/css/itcss/components/token-list.scss
@@ -10,9 +10,14 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
transition: linear 200ms;
background-color: rgba($wallet-balance-bg, 0);
position: relative;
+ flex: 1;
+ min-width: 0;
&__token-balance {
font-size: 1.5rem;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
@media #{$wallet-balance-breakpoint-range} {
font-size: 95%;
@@ -51,7 +56,8 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
&__balance-ellipsis {
display: flex;
align-items: center;
- width: 100%;
+ min-width: 0;
+ flex: 1;
}
&__ellipsis {
@@ -61,6 +67,7 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
&__balance-wrapper {
flex: 1 1 auto;
+ min-width: 0;
}
}
diff --git a/ui/app/css/itcss/generic/index.scss b/ui/app/css/itcss/generic/index.scss
index 08e639d74..92321394b 100644
--- a/ui/app/css/itcss/generic/index.scss
+++ b/ui/app/css/itcss/generic/index.scss
@@ -132,7 +132,7 @@ input.large-input {
height: 55px;
font-size: 1rem;
text-transform: uppercase;
- margin-right: 1rem;
+ margin-right: 1.2rem;
border-radius: 2px;
&:last-of-type {
diff --git a/ui/app/css/itcss/settings/variables.scss b/ui/app/css/itcss/settings/variables.scss
index 640fd95b8..51548306f 100644
--- a/ui/app/css/itcss/settings/variables.scss
+++ b/ui/app/css/itcss/settings/variables.scss
@@ -51,6 +51,9 @@ $java: #29b6af;
$wild-strawberry: #ff4a8d;
$cornflower-blue: #7057ff;
$saffron: #f6c343;
+$dodger-blue: #3099f2;
+$zumthor: #edf7ff;
+$ecstasy: #f7861c;
/*
Z-Indicies
diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js
index 370fdd5b7..c0c2d3ed0 100644
--- a/ui/app/first-time/init-menu.js
+++ b/ui/app/first-time/init-menu.js
@@ -1,12 +1,11 @@
const inherits = require('util').inherits
const EventEmitter = require('events').EventEmitter
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('../metamask-connect')
const h = require('react-hyperscript')
const Mascot = require('../components/mascot')
const actions = require('../actions')
const Tooltip = require('../components/tooltip')
-const t = require('../../i18n')
const getCaretCoordinates = require('textarea-caret')
const environmentType = require('../../../app/scripts/lib/environment-type')
const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums
@@ -60,7 +59,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
color: '#7F8082',
marginBottom: 10,
},
- }, t('appName')),
+ }, this.props.t('appName')),
h('div', [
@@ -70,10 +69,10 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
color: '#7F8082',
display: 'inline',
},
- }, t('encryptNewDen')),
+ }, this.props.t('encryptNewDen')),
h(Tooltip, {
- title: t('denExplainer'),
+ title: this.props.t('denExplainer'),
}, [
h('i.fa.fa-question-circle.pointer', {
style: {
@@ -93,7 +92,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
- placeholder: t('newPassword'),
+ placeholder: this.props.t('newPassword'),
onInput: this.inputChanged.bind(this),
style: {
width: 260,
@@ -105,7 +104,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box-confirm',
- placeholder: t('confirmPassword'),
+ placeholder: this.props.t('confirmPassword'),
onKeyPress: this.createVaultOnEnter.bind(this),
onInput: this.inputChanged.bind(this),
style: {
@@ -120,7 +119,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
style: {
margin: 12,
},
- }, t('createDen')),
+ }, this.props.t('createDen')),
h('.flex-row.flex-center.flex-grow', [
h('p.pointer', {
@@ -130,7 +129,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
color: 'rgb(247, 134, 28)',
textDecoration: 'underline',
},
- }, t('importDen')),
+ }, this.props.t('importDen')),
]),
h('.flex-row.flex-center.flex-grow', [
@@ -179,12 +178,12 @@ InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () {
var passwordConfirm = passwordConfirmBox.value
if (password.length < 8) {
- this.warning = t('passwordShort')
+ this.warning = this.props.t('passwordShort')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
if (password !== passwordConfirm) {
- this.warning = t('passwordMismatch')
+ this.warning = this.props.t('passwordMismatch')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
diff --git a/ui/app/keychains/hd/create-vault-complete.js b/ui/app/keychains/hd/create-vault-complete.js
index 5ab5d4c33..9d99eeb0d 100644
--- a/ui/app/keychains/hd/create-vault-complete.js
+++ b/ui/app/keychains/hd/create-vault-complete.js
@@ -1,6 +1,6 @@
const inherits = require('util').inherits
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const h = require('react-hyperscript')
const actions = require('../../actions')
const exportAsFile = require('../../util').exportAsFile
diff --git a/ui/app/keychains/hd/recover-seed/confirmation.js b/ui/app/keychains/hd/recover-seed/confirmation.js
index bc5339549..f97ac66fe 100644
--- a/ui/app/keychains/hd/recover-seed/confirmation.js
+++ b/ui/app/keychains/hd/recover-seed/confirmation.js
@@ -1,10 +1,9 @@
const inherits = require('util').inherits
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('../../../metamask-connect')
const h = require('react-hyperscript')
const actions = require('../../../actions')
-const t = require('../../../../i18n')
module.exports = connect(mapStateToProps)(RevealSeedConfirmation)
@@ -50,13 +49,13 @@ RevealSeedConfirmation.prototype.render = function () {
},
}, [
- h('h4', t('revealSeedWordsWarning')),
+ h('h4', this.props.t('revealSeedWordsWarning')),
// confirmation
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
- placeholder: t('enterPasswordConfirm'),
+ placeholder: this.props.t('enterPasswordConfirm'),
onKeyPress: this.checkConfirmation.bind(this),
style: {
width: 260,
@@ -92,7 +91,7 @@ RevealSeedConfirmation.prototype.render = function () {
),
props.inProgress && (
- h('span.in-progress-notification', t('generatingSeed'))
+ h('span.in-progress-notification', this.props.t('generatingSeed'))
),
]),
])
diff --git a/ui/app/keychains/hd/restore-vault.js b/ui/app/keychains/hd/restore-vault.js
index 5e4e004cf..f47a2641a 100644
--- a/ui/app/keychains/hd/restore-vault.js
+++ b/ui/app/keychains/hd/restore-vault.js
@@ -1,8 +1,7 @@
const inherits = require('util').inherits
const PersistentForm = require('../../../lib/persistent-form')
-const connect = require('react-redux').connect
+const connect = require('../../metamask-connect')
const h = require('react-hyperscript')
-const t = require('../../../i18n')
const actions = require('../../actions')
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
@@ -37,23 +36,23 @@ RestoreVaultScreen.prototype.render = function () {
padding: 6,
},
}, [
- t('restoreVault'),
+ this.props.t('restoreVault'),
]),
// wallet seed entry
- h('h3', t('walletSeed')),
+ h('h3', this.props.t('walletSeed')),
h('textarea.twelve-word-phrase.letter-spacey', {
dataset: {
persistentFormId: 'wallet-seed',
},
- placeholder: t('secretPhrase'),
+ placeholder: this.props.t('secretPhrase'),
}),
// password
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
- placeholder: t('newPassword8Chars'),
+ placeholder: this.props.t('newPassword8Chars'),
dataset: {
persistentFormId: 'password',
},
@@ -67,7 +66,7 @@ RestoreVaultScreen.prototype.render = function () {
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box-confirm',
- placeholder: t('confirmPassword'),
+ placeholder: this.props.t('confirmPassword'),
onKeyPress: this.createOnEnter.bind(this),
dataset: {
persistentFormId: 'password-confirmation',
@@ -97,7 +96,7 @@ RestoreVaultScreen.prototype.render = function () {
style: {
textTransform: 'uppercase',
},
- }, t('cancel')),
+ }, this.props.t('cancel')),
// submit
h('button.primary', {
@@ -105,7 +104,7 @@ RestoreVaultScreen.prototype.render = function () {
style: {
textTransform: 'uppercase',
},
- }, t('ok')),
+ }, this.props.t('ok')),
]),
])
)
@@ -136,13 +135,13 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
var passwordConfirmBox = document.getElementById('password-box-confirm')
var passwordConfirm = passwordConfirmBox.value
if (password.length < 8) {
- this.warning = t('passwordNotLongEnough')
+ this.warning = this.props.t('passwordNotLongEnough')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
if (password !== passwordConfirm) {
- this.warning = t('passwordsDontMatch')
+ this.warning = this.props.t('passwordsDontMatch')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
@@ -152,18 +151,18 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
// true if the string has more than a space between words.
if (seed.split(' ').length > 1) {
- this.warning = t('spaceBetween')
+ this.warning = this.props.t('spaceBetween')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
// true if seed contains a character that is not between a-z or a space
if (!seed.match(/^[a-z ]+$/)) {
- this.warning = t('loweCaseWords')
+ this.warning = this.props.t('loweCaseWords')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
if (seed.split(' ').length !== 12) {
- this.warning = t('seedPhraseReq')
+ this.warning = this.props.t('seedPhraseReq')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
diff --git a/ui/app/metamask-connect.js b/ui/app/metamask-connect.js
new file mode 100644
index 000000000..8da594635
--- /dev/null
+++ b/ui/app/metamask-connect.js
@@ -0,0 +1,27 @@
+const connect = require('react-redux').connect
+const t = require('../i18n-helper').getMessage
+
+const metamaskConnect = (mapStateToProps, mapDispatchToProps) => {
+ return connect(
+ _higherOrderMapStateToProps(mapStateToProps),
+ mapDispatchToProps
+ )
+}
+
+const _higherOrderMapStateToProps = (mapStateToProps) => {
+ let _t
+ let currentLocale
+ return (state, ownProps = {}) => {
+ const stateProps = mapStateToProps
+ ? mapStateToProps(state, ownProps)
+ : ownProps
+ if (currentLocale !== state.metamask.currentLocale) {
+ currentLocale = state.metamask.currentLocale
+ _t = t.bind(null, state.localeMessages)
+ }
+ stateProps.t = _t
+ return stateProps
+ }
+}
+
+module.exports = metamaskConnect \ No newline at end of file
diff --git a/ui/app/new-keychain.js b/ui/app/new-keychain.js
index cc9633166..6337faa08 100644
--- a/ui/app/new-keychain.js
+++ b/ui/app/new-keychain.js
@@ -1,7 +1,7 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
+const connect = require('./metamask-connect')
module.exports = connect(mapStateToProps)(NewKeychain)
diff --git a/ui/app/reducers.js b/ui/app/reducers.js
index 70b7e71dc..f155b2bf3 100644
--- a/ui/app/reducers.js
+++ b/ui/app/reducers.js
@@ -7,6 +7,7 @@ const copyToClipboard = require('copy-to-clipboard')
const reduceIdentities = require('./reducers/identities')
const reduceMetamask = require('./reducers/metamask')
const reduceApp = require('./reducers/app')
+const reduceLocale = require('./reducers/locale')
window.METAMASK_CACHED_LOG_STATE = null
@@ -38,6 +39,12 @@ function rootReducer (state, action) {
state.appState = reduceApp(state, action)
+ //
+ // LocaleMessages
+ //
+
+ state.localeMessages = reduceLocale(state, action)
+
window.METAMASK_CACHED_LOG_STATE = state
return state
}
diff --git a/ui/app/reducers/locale.js b/ui/app/reducers/locale.js
new file mode 100644
index 000000000..bdd97acb4
--- /dev/null
+++ b/ui/app/reducers/locale.js
@@ -0,0 +1,17 @@
+const extend = require('xtend')
+const actions = require('../actions')
+
+module.exports = reduceMetamask
+
+function reduceMetamask (state, action) {
+ const localeMessagesState = extend({}, state.localeMessages)
+
+ switch (action.type) {
+ case actions.SET_LOCALE_MESSAGES:
+ return extend(localeMessagesState, {
+ current: action.value,
+ })
+ default:
+ return localeMessagesState
+ }
+}
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index e6e02d057..6d0a5bb10 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -39,6 +39,7 @@ function reduceMetamask (state, action) {
maxModeOn: false,
editingTransactionId: null,
forceGasMin: null,
+ toNickname: '',
},
coinOptions: {},
useBlockie: false,
@@ -46,6 +47,7 @@ function reduceMetamask (state, action) {
networkEndpointType: OLD_UI_NETWORK_TYPE,
isRevealingSeedWords: false,
welcomeScreenSeen: false,
+ currentLocale: '',
}, state.metamask)
switch (action.type) {
@@ -238,7 +240,8 @@ function reduceMetamask (state, action) {
return extend(metamaskState, {
send: {
...metamaskState.send,
- to: action.value,
+ to: action.value.to,
+ toNickname: action.value.nickname,
},
})
@@ -355,6 +358,11 @@ function reduceMetamask (state, action) {
welcomeScreenSeen: true,
})
+ case action.SET_CURRENT_LOCALE:
+ return extend(metamaskState, {
+ currentLocale: action.value,
+ })
+
default:
return metamaskState
diff --git a/ui/app/select-app.js b/ui/app/select-app.js
index 193c98353..fca3decce 100644
--- a/ui/app/select-app.js
+++ b/ui/app/select-app.js
@@ -1,6 +1,6 @@
const inherits = require('util').inherits
const Component = require('react').Component
-const connect = require('react-redux').connect
+const connect = require('./metamask-connect')
const h = require('react-hyperscript')
const App = require('./app')
const OldApp = require('../../old-ui/app/app')
diff --git a/ui/app/selectors.js b/ui/app/selectors.js
index d37c26f7e..2bdc39004 100644
--- a/ui/app/selectors.js
+++ b/ui/app/selectors.js
@@ -56,8 +56,9 @@ function getSelectedToken (state) {
const tokens = state.metamask.tokens || []
const selectedTokenAddress = state.metamask.selectedTokenAddress
const selectedToken = tokens.filter(({ address }) => address === selectedTokenAddress)[0]
+ const sendToken = state.metamask.send.token
- return selectedToken || null
+ return selectedToken || sendToken || null
}
function getSelectedTokenExchangeRate (state) {
diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js
index de71ce94c..52b8bb273 100644
--- a/ui/app/send-v2.js
+++ b/ui/app/send-v2.js
@@ -1,13 +1,12 @@
const { inherits } = require('util')
const PersistentForm = require('../lib/persistent-form')
const h = require('react-hyperscript')
-const t = require('../i18n')
const ethAbi = require('ethereumjs-abi')
const ethUtil = require('ethereumjs-util')
const FromDropdown = require('./components/send/from-dropdown')
-const ToAutoComplete = require('./components/send/to-autocomplete')
+const EnsInput = require('./components/ens-input')
const CurrencyDisplay = require('./components/send/currency-display')
const MemoTextArea = require('./components/send/memo-textarea')
const GasFeeDisplay = require('./components/send/gas-fee-display-v2')
@@ -189,9 +188,9 @@ SendTransactionScreen.prototype.renderHeader = function () {
return h('div.page-container__header', [
- h('div.page-container__title', selectedToken ? t('sendTokens') : t('sendETH')),
+ h('div.page-container__title', selectedToken ? this.props.t('sendTokens') : this.props.t('sendETH')),
- h('div.page-container__subtitle', t('onlySendToEtherAddress')),
+ h('div.page-container__subtitle', this.props.t('onlySendToEtherAddress')),
h('div.page-container__header-close', {
onClick: () => {
@@ -253,7 +252,7 @@ SendTransactionScreen.prototype.renderFromRow = function () {
])
}
-SendTransactionScreen.prototype.handleToChange = function (to) {
+SendTransactionScreen.prototype.handleToChange = function (to, nickname = '') {
const {
updateSendTo,
updateSendErrors,
@@ -262,19 +261,19 @@ SendTransactionScreen.prototype.handleToChange = function (to) {
let toError = null
if (!to) {
- toError = t('required')
+ toError = this.props.t('required')
} else if (!isValidAddress(to)) {
- toError = t('invalidAddressRecipient')
+ toError = this.props.t('invalidAddressRecipient')
} else if (to === from) {
- toError = t('fromToSame')
+ toError = this.props.t('fromToSame')
}
- updateSendTo(to)
+ updateSendTo(to, nickname)
updateSendErrors({ to: toError })
}
SendTransactionScreen.prototype.renderToRow = function () {
- const { toAccounts, errors, to } = this.props
+ const { toAccounts, errors, to, network } = this.props
const { toDropdownOpen } = this.state
@@ -282,14 +281,17 @@ SendTransactionScreen.prototype.renderToRow = function () {
h('div.send-v2__form-label', [
- t('to'),
+ this.props.t('to'),
- this.renderErrorMessage(t('to')),
+ this.renderErrorMessage(this.props.t('to')),
]),
h('div.send-v2__form-field', [
- h(ToAutoComplete, {
+ h(EnsInput, {
+ name: 'address',
+ placeholder: 'Recipient Address',
+ network,
to,
accounts: Object.entries(toAccounts).map(([key, account]) => account),
dropdownOpen: toDropdownOpen,
@@ -382,11 +384,11 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
)
if (conversionRate && !sufficientBalance) {
- amountError = t('insufficientFunds')
+ amountError = this.props.t('insufficientFunds')
} else if (verifyTokenBalance && !sufficientTokens) {
- amountError = t('insufficientTokens')
+ amountError = this.props.t('insufficientTokens')
} else if (amountLessThanZero) {
- amountError = t('negativeETH')
+ amountError = this.props.t('negativeETH')
}
updateSendErrors({ amount: amountError })
@@ -416,7 +418,7 @@ SendTransactionScreen.prototype.renderAmountRow = function () {
setMaxModeTo(true)
this.setAmountToMax()
},
- }, [ !maxModeOn ? t('max') : '' ]),
+ }, [ !maxModeOn ? this.props.t('max') : '' ]),
]),
h('div.send-v2__form-field', [
@@ -510,16 +512,16 @@ SendTransactionScreen.prototype.renderFooter = function () {
const noErrors = !amountError && toError === null
return h('div.page-container__footer', [
- h('button.btn-cancel.page-container__footer-button', {
+ h('button.btn-secondary--lg.page-container__footer-button', {
onClick: () => {
clearSend()
goHome()
},
- }, t('cancel')),
- h('button.btn-clear.page-container__footer-button', {
+ }, this.props.t('cancel')),
+ h('button.btn-primary--lg.page-container__footer-button', {
disabled: !noErrors || !gasTotal || missingTokenBalance,
onClick: event => this.onSubmit(event),
- }, t('next')),
+ }, this.props.t('next')),
])
}
@@ -538,11 +540,11 @@ SendTransactionScreen.prototype.render = function () {
)
}
-SendTransactionScreen.prototype.addToAddressBookIfNew = function (newAddress) {
+SendTransactionScreen.prototype.addToAddressBookIfNew = function (newAddress, nickname = '') {
const { toAccounts, addToAddressBook } = this.props
if (!toAccounts.find(({ address }) => newAddress === address)) {
// TODO: nickname, i.e. addToAddressBook(recipient, nickname)
- addToAddressBook(newAddress)
+ addToAddressBook(newAddress, nickname)
}
}
@@ -594,7 +596,7 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
event.preventDefault()
const {
from: {address: from},
- to,
+ to: _to,
amount,
gasLimit: gas,
gasPrice,
@@ -603,6 +605,7 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
updateTx,
selectedToken,
editingTransactionId,
+ toNickname,
errors: { amount: amountError, to: toError },
} = this.props
@@ -612,7 +615,9 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
return
}
- this.addToAddressBookIfNew(to)
+ const to = ethUtil.addHexPrefix(_to)
+
+ this.addToAddressBookIfNew(to, toNickname)
if (editingTransactionId) {
const editedTx = this.getEditedTx()
diff --git a/ui/app/settings.js b/ui/app/settings.js
index 105cbb40b..8764dcafe 100644
--- a/ui/app/settings.js
+++ b/ui/app/settings.js
@@ -1,7 +1,7 @@
const { Component } = require('react')
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const { connect } = require('react-redux')
+const connect = require('./metamask-connect')
const actions = require('./actions')
const infuraCurrencies = require('./infura-conversion.json')
const validUrl = require('valid-url')
@@ -9,8 +9,8 @@ const { exportAsFile } = require('./util')
const TabBar = require('./components/tab-bar')
const SimpleDropdown = require('./components/dropdowns/simple-dropdown')
const ToggleButton = require('react-toggle-button')
+const locales = require('../../app/_locales/index.json')
const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
-const t = require('../i18n')
const getInfuraCurrencyOptions = () => {
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
@@ -26,6 +26,16 @@ const getInfuraCurrencyOptions = () => {
})
}
+const getLocaleOptions = () => {
+ return locales.map((locale) => {
+ return {
+ displayValue: `${locale.name}`,
+ key: locale.code,
+ value: locale.code,
+ }
+ })
+}
+
class Settings extends Component {
constructor (props) {
super(props)
@@ -45,8 +55,8 @@ class Settings extends Component {
return h('div.settings__tabs', [
h(TabBar, {
tabs: [
- { content: t('settings'), key: 'settings' },
- { content: t('info'), key: 'info' },
+ { content: this.props.t('settings'), key: 'settings' },
+ { content: this.props.t('info'), key: 'info' },
],
defaultTab: activeTab,
tabSelected: key => this.setState({ activeTab: key }),
@@ -59,7 +69,7 @@ class Settings extends Component {
return h('div.settings__content-row', [
h('div.settings__content-item', [
- h('span', t('blockiesIdenticon')),
+ h('span', this.props.t('blockiesIdenticon')),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
@@ -79,13 +89,13 @@ class Settings extends Component {
return h('div.settings__content-row', [
h('div.settings__content-item', [
- h('span', t('currentConversion')),
+ h('span', this.props.t('currentConversion')),
h('span.settings__content-description', `Updated ${Date(conversionDate)}`),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h(SimpleDropdown, {
- placeholder: t('selectCurrency'),
+ placeholder: this.props.t('selectCurrency'),
options: getInfuraCurrencyOptions(),
selectedOption: currentCurrency,
onSelect: newCurrency => setCurrentCurrency(newCurrency),
@@ -95,6 +105,30 @@ class Settings extends Component {
])
}
+ renderCurrentLocale () {
+ const { updateCurrentLocale, currentLocale } = this.props
+ const currentLocaleMeta = locales.find(locale => locale.code === currentLocale)
+
+ return h('div.settings__content-row', [
+ h('div.settings__content-item', [
+ h('span', 'Current Language'),
+ h('span.settings__content-description', `${currentLocaleMeta.name}`),
+ ]),
+ h('div.settings__content-item', [
+ h('div.settings__content-item-col', [
+ h(SimpleDropdown, {
+ placeholder: 'Select Locale',
+ options: getLocaleOptions(),
+ selectedOption: currentLocale,
+ onSelect: async (newLocale) => {
+ updateCurrentLocale(newLocale)
+ },
+ }),
+ ]),
+ ]),
+ ])
+ }
+
renderCurrentProvider () {
const { metamask: { provider = {} } } = this.props
let title, value, color
@@ -102,31 +136,31 @@ class Settings extends Component {
switch (provider.type) {
case 'mainnet':
- title = t('currentNetwork')
- value = t('mainnet')
+ title = this.props.t('currentNetwork')
+ value = this.props.t('mainnet')
color = '#038789'
break
case 'ropsten':
- title = t('currentNetwork')
- value = t('ropsten')
+ title = this.props.t('currentNetwork')
+ value = this.props.t('ropsten')
color = '#e91550'
break
case 'kovan':
- title = t('currentNetwork')
- value = t('kovan')
+ title = this.props.t('currentNetwork')
+ value = this.props.t('kovan')
color = '#690496'
break
case 'rinkeby':
- title = t('currentNetwork')
- value = t('rinkeby')
+ title = this.props.t('currentNetwork')
+ value = this.props.t('rinkeby')
color = '#ebb33f'
break
default:
- title = t('currentRpc')
+ title = this.props.t('currentRpc')
value = provider.rpcTarget
}
@@ -147,12 +181,12 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
h('div.settings__content-item', [
- h('span', t('newRPC')),
+ h('span', this.props.t('newRPC')),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h('input.settings__input', {
- placeholder: t('newRPC'),
+ placeholder: this.props.t('newRPC'),
onChange: event => this.setState({ newRpc: event.target.value }),
onKeyPress: event => {
if (event.key === 'Enter') {
@@ -165,7 +199,7 @@ class Settings extends Component {
event.preventDefault()
this.validateRpc(this.state.newRpc)
},
- }, t('save')),
+ }, this.props.t('save')),
]),
]),
])
@@ -181,9 +215,9 @@ class Settings extends Component {
const appendedRpc = `http://${newRpc}`
if (validUrl.isWebUri(appendedRpc)) {
- displayWarning(t('uriErrorMsg'))
+ displayWarning(this.props.t('uriErrorMsg'))
} else {
- displayWarning(t('invalidRPC'))
+ displayWarning(this.props.t('invalidRPC'))
}
}
}
@@ -192,25 +226,25 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
h('div.settings__content-item', [
- h('div', t('stateLogs')),
+ h('div', this.props.t('stateLogs')),
h(
'div.settings__content-description',
- t('stateLogsDescription')
+ this.props.t('stateLogsDescription')
),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.settings__clear-button', {
+ h('button.btn-primary--lg.settings__button', {
onClick (event) {
window.logStateString((err, result) => {
if (err) {
- this.state.dispatch(actions.displayWarning(t('stateLogError')))
+ this.state.dispatch(actions.displayWarning(this.props.t('stateLogError')))
} else {
exportAsFile('MetaMask State Logs.json', result)
}
})
},
- }, t('downloadStateLogs')),
+ }, this.props.t('downloadStateLogs')),
]),
]),
])
@@ -222,15 +256,15 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
- h('div.settings__content-item', t('revealSeedWords')),
+ h('div.settings__content-item', this.props.t('revealSeedWords')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.settings__clear-button.settings__clear-button--red', {
+ h('button.btn-primary--lg.settings__button--red', {
onClick (event) {
event.preventDefault()
revealSeedConfirmation()
},
- }, t('revealSeedWords')),
+ }, this.props.t('revealSeedWords')),
]),
]),
])
@@ -242,15 +276,15 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
- h('div.settings__content-item', t('useOldUI')),
+ h('div.settings__content-item', this.props.t('useOldUI')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.settings__clear-button.settings__clear-button--orange', {
+ h('button.btn-primary--lg.settings__button--orange', {
onClick (event) {
event.preventDefault()
setFeatureFlagToBeta()
},
- }, t('useOldUI')),
+ }, this.props.t('useOldUI')),
]),
]),
])
@@ -261,15 +295,15 @@ class Settings extends Component {
const { showResetAccountConfirmationModal } = this.props
return h('div.settings__content-row', [
- h('div.settings__content-item', t('resetAccount')),
+ h('div.settings__content-item', this.props.t('resetAccount')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.settings__clear-button.settings__clear-button--orange', {
+ h('button.btn-primary--lg.settings__button--orange', {
onClick (event) {
event.preventDefault()
showResetAccountConfirmationModal()
},
- }, t('resetAccount')),
+ }, this.props.t('resetAccount')),
]),
]),
])
@@ -282,6 +316,7 @@ class Settings extends Component {
h('div.settings__content', [
warning && h('div.settings__error', warning),
this.renderCurrentConversion(),
+ this.renderCurrentLocale(),
// this.renderCurrentProvider(),
this.renderNewRpcUrl(),
this.renderStateLogs(),
@@ -304,13 +339,13 @@ class Settings extends Component {
renderInfoLinks () {
return (
h('div.settings__content-item.settings__content-item--without-height', [
- h('div.settings__info-link-header', t('links')),
+ h('div.settings__info-link-header', this.props.t('links')),
h('div.settings__info-link-item', [
h('a', {
href: 'https://metamask.io/privacy.html',
target: '_blank',
}, [
- h('span.settings__info-link', t('privacyMsg')),
+ h('span.settings__info-link', this.props.t('privacyMsg')),
]),
]),
h('div.settings__info-link-item', [
@@ -318,7 +353,7 @@ class Settings extends Component {
href: 'https://metamask.io/terms.html',
target: '_blank',
}, [
- h('span.settings__info-link', t('terms')),
+ h('span.settings__info-link', this.props.t('terms')),
]),
]),
h('div.settings__info-link-item', [
@@ -326,7 +361,7 @@ class Settings extends Component {
href: 'https://metamask.io/attributions.html',
target: '_blank',
}, [
- h('span.settings__info-link', t('attributions')),
+ h('span.settings__info-link', this.props.t('attributions')),
]),
]),
h('hr.settings__info-separator'),
@@ -335,7 +370,7 @@ class Settings extends Component {
href: 'https://support.metamask.io',
target: '_blank',
}, [
- h('span.settings__info-link', t('supportCenter')),
+ h('span.settings__info-link', this.props.t('supportCenter')),
]),
]),
h('div.settings__info-link-item', [
@@ -343,7 +378,7 @@ class Settings extends Component {
href: 'https://metamask.io/',
target: '_blank',
}, [
- h('span.settings__info-link', t('visitWebSite')),
+ h('span.settings__info-link', this.props.t('visitWebSite')),
]),
]),
h('div.settings__info-link-item', [
@@ -351,7 +386,7 @@ class Settings extends Component {
target: '_blank',
href: 'mailto:help@metamask.io?subject=Feedback',
}, [
- h('span.settings__info-link', t('emailUs')),
+ h('span.settings__info-link', this.props.t('emailUs')),
]),
]),
])
@@ -373,7 +408,7 @@ class Settings extends Component {
h('div.settings__info-item', [
h(
'div.settings__info-about',
- t('builtInCalifornia')
+ this.props.t('builtInCalifornia')
),
]),
]),
@@ -417,6 +452,9 @@ Settings.propTypes = {
warning: PropTypes.string,
goHome: PropTypes.func,
isMascara: PropTypes.bool,
+ updateCurrentLocale: PropTypes.func,
+ currentLocale: PropTypes.string,
+ t: PropTypes.func,
}
const mapStateToProps = state => {
@@ -424,6 +462,7 @@ const mapStateToProps = state => {
metamask: state.metamask,
warning: state.appState.warning,
isMascara: state.metamask.isMascara,
+ currentLocale: state.metamask.currentLocale,
}
}
@@ -435,6 +474,7 @@ const mapDispatchToProps = dispatch => {
displayWarning: warning => dispatch(actions.displayWarning(warning)),
revealSeedConfirmation: () => dispatch(actions.revealSeedConfirmation()),
setUseBlockie: value => dispatch(actions.setUseBlockie(value)),
+ updateCurrentLocale: key => dispatch(actions.updateCurrentLocale(key)),
setFeatureFlagToBeta: () => {
return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
.then(() => dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
@@ -446,4 +486,3 @@ const mapDispatchToProps = dispatch => {
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(Settings)
-
diff --git a/ui/app/unlock.js b/ui/app/unlock.js
index 322808619..e8e1ba051 100644
--- a/ui/app/unlock.js
+++ b/ui/app/unlock.js
@@ -1,11 +1,10 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
+const connect = require('./metamask-connect')
const actions = require('./actions')
const getCaretCoordinates = require('textarea-caret')
const EventEmitter = require('events').EventEmitter
-const t = require('../i18n')
const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
const environmentType = require('../../app/scripts/lib/environment-type')
@@ -41,7 +40,7 @@ UnlockScreen.prototype.render = function () {
textTransform: 'uppercase',
color: '#7F8082',
},
- }, t('appName')),
+ }, this.props.t('appName')),
h('input.large-input', {
type: 'password',
@@ -67,7 +66,7 @@ UnlockScreen.prototype.render = function () {
style: {
margin: 10,
},
- }, t('login')),
+ }, this.props.t('login')),
h('p.pointer', {
onClick: () => {
@@ -81,7 +80,7 @@ UnlockScreen.prototype.render = function () {
color: 'rgb(247, 134, 28)',
textDecoration: 'underline',
},
- }, t('restoreFromSeed')),
+ }, this.props.t('restoreFromSeed')),
h('p.pointer', {
onClick: () => {
@@ -94,7 +93,7 @@ UnlockScreen.prototype.render = function () {
textDecoration: 'underline',
marginTop: '32px',
},
- }, t('classicInterface')),
+ }, this.props.t('classicInterface')),
])
)
}
diff --git a/ui/i18n-helper.js b/ui/i18n-helper.js
new file mode 100644
index 000000000..3ce24ddfb
--- /dev/null
+++ b/ui/i18n-helper.js
@@ -0,0 +1,45 @@
+// cross-browser connection to extension i18n API
+const log = require('loglevel')
+
+const getMessage = (locale, key, substitutions) => {
+ // check locale is loaded
+ if (!locale) {
+ // throw new Error('Translator - has not loaded a locale yet.')
+ return ''
+ }
+ // check entry is present
+ const { current, en } = locale
+ const entry = current[key] || en[key]
+ if (!entry) {
+ log.error(`Translator - Unable to find value for "${key}"`)
+ // throw new Error(`Translator - Unable to find value for "${key}"`)
+ }
+ let phrase = entry.message
+ // perform substitutions
+ if (substitutions && substitutions.length) {
+ phrase = phrase.replace(/\$1/g, substitutions[0])
+ if (substitutions.length > 1) {
+ phrase = phrase.replace(/\$2/g, substitutions[1])
+ }
+ }
+ return phrase
+}
+
+function fetchLocale (localeName) {
+ return new Promise((resolve, reject) => {
+ return fetch(`/_locales/${localeName}/messages.json`)
+ .then(response => response.json())
+ .then(
+ locale => resolve(locale),
+ error => {
+ log.error(`failed to fetch ${localeName} locale because of ${error}`)
+ resolve({})
+ }
+ )
+ })
+}
+
+module.exports = {
+ getMessage,
+ fetchLocale,
+}
diff --git a/ui/i18n.js b/ui/i18n.js
deleted file mode 100644
index abfece426..000000000
--- a/ui/i18n.js
+++ /dev/null
@@ -1,33 +0,0 @@
-
-// cross-browser connection to extension i18n API
-
-const chrome = chrome || null
-const browser = browser || null
-const extension = require('extensionizer')
-var log = require('loglevel')
-window.log = log
-let getMessage
-
-if (extension.i18n && extension.i18n.getMessage) {
- getMessage = extension.i18n.getMessage
-} else {
- // fallback function
- log.warn('browser.i18n API not available, calling back to english.')
- const msg = require('../app/_locales/en/messages.json')
- getMessage = function (key, substitutions) {
- if (!msg[key]) {
- log.error(key)
- throw new Error(key)
- }
- let phrase = msg[key].message
- if (substitutions && substitutions.length) {
- phrase = phrase.replace(/\$1/g, substitutions[0])
- if (substitutions.length > 1) {
- phrase = phrase.replace(/\$2/g, substitutions[1])
- }
- }
- return phrase
- }
-}
-
-module.exports = getMessage
diff --git a/ui/index.js b/ui/index.js
index fdb2f23e0..1e0e9f1cc 100644
--- a/ui/index.js
+++ b/ui/index.js
@@ -4,6 +4,7 @@ const Root = require('./app/root')
const actions = require('./app/actions')
const configureStore = require('./app/store')
const txHelper = require('./lib/tx-helper')
+const { fetchLocale } = require('./i18n-helper')
const { OLD_UI_NETWORK_TYPE, BETA_UI_NETWORK_TYPE } = require('../app/scripts/config').enums
global.log = require('loglevel')
@@ -18,14 +19,22 @@ function launchMetamaskUi (opts, cb) {
// check if we are unlocked first
accountManager.getState(function (err, metamaskState) {
if (err) return cb(err)
- const store = startApp(metamaskState, accountManager, opts)
- cb(null, store)
+ startApp(metamaskState, accountManager, opts)
+ .then((store) => {
+ cb(null, store)
+ })
})
}
-function startApp (metamaskState, accountManager, opts) {
+async function startApp (metamaskState, accountManager, opts) {
// parse opts
if (!metamaskState.featureFlags) metamaskState.featureFlags = {}
+
+ const currentLocaleMessages = metamaskState.currentLocale
+ ? await fetchLocale(metamaskState.currentLocale)
+ : {}
+ const enLocaleMessages = await fetchLocale('en')
+
const store = configureStore({
// metamaskState represents the cross-tab state
@@ -34,6 +43,11 @@ function startApp (metamaskState, accountManager, opts) {
// appState represents the current tab's popup state
appState: {},
+ localeMessages: {
+ current: currentLocaleMessages,
+ en: enLocaleMessages,
+ },
+
// Which blockchain we are using:
networkVersion: opts.networkVersion,
})
diff --git a/yarn.lock b/yarn.lock
index 7512e10db..35365248a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -72,6 +72,15 @@
normalize-path "^2.0.1"
through2 "^2.0.3"
+"@sentry/cli@^1.30.3":
+ version "1.30.3"
+ resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.30.3.tgz#02d0f7781c1ee5e1be5a4312a325fbe8b1b37231"
+ dependencies:
+ https-proxy-agent "^2.1.1"
+ node-fetch "^1.7.3"
+ progress "2.0.0"
+ proxy-from-env "^1.0.0"
+
"@types/node@*":
version "8.5.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.5.2.tgz#83b8103fa9a2c2e83d78f701a9aa7c9539739aa5"
@@ -201,6 +210,12 @@ agent-base@2:
extend "~3.0.0"
semver "~5.0.1"
+agent-base@^4.1.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce"
+ dependencies:
+ es6-promisify "^5.0.0"
+
ajv-keywords@^1.1.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
@@ -253,6 +268,10 @@ ansi-colors@^1.0.1:
dependencies:
ansi-wrap "^0.1.0"
+ansi-escapes@^1.1.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+
ansi-escapes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92"
@@ -299,6 +318,10 @@ ansi-wrap@0.1.0, ansi-wrap@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
+ansi@^0.3.0, ansi@~0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21"
+
ansicolors@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979"
@@ -1611,7 +1634,7 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
-bluebird@^3.0.5, bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.4.6, bluebird@^3.5.0:
+bluebird@^3.0.5, bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.4.6, bluebird@^3.4.7, bluebird@^3.5.0:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
@@ -2021,6 +2044,10 @@ buffer-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe"
+buffer-from@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
+
buffer-more-ints@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz#26b3885d10fa13db7fc01aae3aab870199e0124c"
@@ -2155,6 +2182,22 @@ caniuse-lite@^1.0.30000810, caniuse-lite@^1.0.30000813:
version "1.0.30000814"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000814.tgz#73eb6925ac2e54d495218f1ea0007da3940e488b"
+caporal@^0.10.0:
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/caporal/-/caporal-0.10.0.tgz#2ddfbe5ede26d2bd356f042f564ff57803cdabc9"
+ dependencies:
+ bluebird "^3.4.7"
+ chalk "^1.1.3"
+ cli-table2 "^0.2.0"
+ fast-levenshtein "^2.0.6"
+ lodash.camelcase "^4.3.0"
+ lodash.kebabcase "^4.1.1"
+ lodash.merge "^4.6.0"
+ micromist "^1.0.1"
+ prettyjson "^1.2.1"
+ tabtab "^2.2.2"
+ winston "^2.3.1"
+
caseless@~0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
@@ -2353,12 +2396,27 @@ classnames@^2.2.4, classnames@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
+cli-cursor@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
+ dependencies:
+ restore-cursor "^1.0.1"
+
cli-cursor@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
dependencies:
restore-cursor "^2.0.0"
+cli-table2@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/cli-table2/-/cli-table2-0.2.0.tgz#2d1ef7f218a0e786e214540562d4bd177fe32d97"
+ dependencies:
+ lodash "^3.10.1"
+ string-width "^1.0.1"
+ optionalDependencies:
+ colors "^1.1.2"
+
cli-width@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
@@ -2635,6 +2693,15 @@ concat-stream@^1.4.3, concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@
readable-stream "^2.2.2"
typedarray "^0.0.6"
+concat-stream@^1.4.7:
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+ dependencies:
+ buffer-from "^1.0.0"
+ inherits "^2.0.3"
+ readable-stream "^2.2.2"
+ typedarray "^0.0.6"
+
concat-stream@~1.5.0, concat-stream@~1.5.1:
version "1.5.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
@@ -3677,6 +3744,14 @@ es-abstract@^1.5.0, es-abstract@^1.6.1, es-abstract@^1.7.0:
is-callable "^1.1.3"
is-regex "^1.0.4"
+es-check@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/es-check/-/es-check-2.0.3.tgz#04eafde54f5bfcf0881b1ae35c4a5205a9041ee2"
+ dependencies:
+ acorn "^5.1.2"
+ caporal "^0.10.0"
+ glob "^7.1.2"
+
es-to-primitive@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
@@ -3711,6 +3786,16 @@ es6-map@^0.1.3:
es6-symbol "~3.1.1"
event-emitter "~0.3.5"
+es6-promise@^4.0.3:
+ version "4.2.4"
+ resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
+
+es6-promisify@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
+ dependencies:
+ es6-promise "^4.0.3"
+
es6-set@~0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
@@ -4462,6 +4547,10 @@ exists-stat@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/exists-stat/-/exists-stat-1.0.0.tgz#0660e3525a2e89d9e446129440c272edfa24b529"
+exit-hook@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
+
expand-braces@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea"
@@ -4579,6 +4668,14 @@ extensionizer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/extensionizer/-/extensionizer-1.0.0.tgz#01c209bbea6d9c0acba77129c3aa4a9a98fc3538"
+external-editor@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-1.1.1.tgz#12d7b0db850f7ff7e7081baf4005700060c4600b"
+ dependencies:
+ extend "^3.0.0"
+ spawn-sync "^1.0.15"
+ tmp "^0.0.29"
+
external-editor@^2.0.4:
version "2.1.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48"
@@ -4696,6 +4793,13 @@ fetch-ponyfill@^4.0.0:
dependencies:
node-fetch "~1.7.1"
+figures@^1.3.5:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
+ dependencies:
+ escape-string-regexp "^1.0.5"
+ object-assign "^4.1.0"
+
figures@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
@@ -5068,6 +5172,16 @@ gather-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/gather-stream/-/gather-stream-1.0.0.tgz#b33994af457a8115700d410f317733cbe7a0904b"
+gauge@~1.2.5:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93"
+ dependencies:
+ ansi "^0.3.0"
+ has-unicode "^2.0.0"
+ lodash.pad "^4.1.0"
+ lodash.padend "^4.1.0"
+ lodash.padstart "^4.1.0"
+
gauge@~2.7.3:
version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@@ -5919,6 +6033,13 @@ https-proxy-agent@1:
debug "2"
extend "3"
+https-proxy-agent@^2.1.1:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.0.tgz#7fbba856be8cd677986f42ebd3664f6317257887"
+ dependencies:
+ agent-base "^4.1.0"
+ debug "^3.1.0"
+
human-standard-token-abi@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/human-standard-token-abi/-/human-standard-token-abi-1.0.2.tgz#207d7846796ee5bb85fdd336e769cb38045b2ae0"
@@ -6046,6 +6167,25 @@ inline-source-map@~0.6.0:
dependencies:
source-map "~0.5.3"
+inquirer@^1.0.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.3.tgz#4dec6f32f37ef7bb0b2ed3f1d1a5c3f545074918"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^2.0.0"
+ external-editor "^1.1.0"
+ figures "^1.3.5"
+ lodash "^4.3.0"
+ mute-stream "0.0.6"
+ pinkie-promise "^2.0.0"
+ run-async "^2.2.0"
+ rx "^4.1.0"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
inquirer@^3.0.6:
version "3.3.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
@@ -7173,6 +7313,10 @@ lodash.assignin@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
+lodash.camelcase@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+
lodash.castarray@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115"
@@ -7191,6 +7335,10 @@ lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+lodash.difference@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
+
lodash.escape@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698"
@@ -7228,6 +7376,10 @@ lodash.isarray@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
+lodash.kebabcase@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
+
lodash.keys@^3.0.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
@@ -7244,10 +7396,26 @@ lodash.memoize@~3.0.3:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
+lodash.merge@^4.6.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
+
lodash.mergewith@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
+lodash.pad@^4.1.0:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70"
+
+lodash.padend@^4.1.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e"
+
+lodash.padstart@^4.1.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b"
+
lodash.restparam@^3.0.0:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
@@ -7281,10 +7449,18 @@ lodash.templatesettings@^3.0.0:
lodash._reinterpolate "^3.0.0"
lodash.escape "^3.0.0"
+lodash.uniq@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+
lodash.uniqby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302"
+lodash@^3.10.1:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+
lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.2, lodash@~4.17.4:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -7655,6 +7831,12 @@ micromatch@^3.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
+micromist@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/micromist/-/micromist-1.0.2.tgz#41f84949a04c30cdc60a394d0cb06aaa08b86364"
+ dependencies:
+ lodash.camelcase "^4.3.0"
+
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@@ -7883,6 +8065,10 @@ mute-stdout@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.0.tgz#5b32ea07eb43c9ded6130434cf926f46b2a7fd4d"
+mute-stream@0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db"
+
mute-stream@0.0.7, mute-stream@~0.0.4:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
@@ -7984,7 +8170,7 @@ nock@^9.0.14:
qs "^6.5.1"
semver "^5.3.0"
-node-fetch@^1.0.1, node-fetch@~1.7.1:
+node-fetch@^1.0.1, node-fetch@^1.7.3, node-fetch@~1.7.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
dependencies:
@@ -8203,6 +8389,14 @@ npm-run-path@^2.0.0:
gauge "~2.7.3"
set-blocking "~2.0.0"
+npmlog@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-2.0.4.tgz#98b52530f2514ca90d09ec5b22c8846722375692"
+ dependencies:
+ ansi "~0.3.1"
+ are-we-there-yet "~1.1.2"
+ gauge "~1.2.5"
+
nth-check@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4"
@@ -8437,6 +8631,10 @@ onecolor@^3.0.4:
version "3.0.5"
resolved "https://registry.yarnpkg.com/onecolor/-/onecolor-3.0.5.tgz#36eff32201379efdf1180fb445e51a8e2425f9f6"
+onetime@^1.0.0:
+ version "1.1.0"
+ resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
+
onetime@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
@@ -8505,7 +8703,11 @@ os-locale@^2.0.0:
lcid "^1.0.0"
mem "^1.1.0"
-os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2:
+os-shim@^0.1.2:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917"
+
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@@ -8967,6 +9169,13 @@ pretty-hrtime@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
+prettyjson@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289"
+ dependencies:
+ colors "^1.1.2"
+ minimist "^1.2.0"
+
printf@^0.2.3:
version "0.2.5"
resolved "https://registry.yarnpkg.com/printf/-/printf-0.2.5.tgz#c438ca2ca33e3927671db4ab69c0e52f936a4f0f"
@@ -8991,7 +9200,7 @@ process@~0.5.1:
version "0.5.2"
resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
-progress@^2.0.0:
+progress@2.0.0, progress@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
@@ -9067,6 +9276,10 @@ proxy-agent@~2.0.0:
pac-proxy-agent "1"
socks-proxy-agent "2"
+proxy-from-env@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
+
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
@@ -9245,6 +9458,10 @@ raphael@^2.2.0:
dependencies:
eve-raphael "0.5.0"
+raven-js@^3.24.0:
+ version "3.24.0"
+ resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.24.0.tgz#59464d8bc4b3812ae87a282e9bb98ecad5b4b047"
+
raw-body@2, raw-body@2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
@@ -9967,6 +10184,13 @@ response-stream@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/response-stream/-/response-stream-0.0.0.tgz#da4b17cc7684c98c962beb4d95f668c8dcad09d5"
+restore-cursor@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
+ dependencies:
+ exit-hook "^1.0.0"
+ onetime "^1.0.0"
+
restore-cursor@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@@ -10038,6 +10262,10 @@ rx-lite@*, rx-lite@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
+rx@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
+
safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
@@ -10538,6 +10766,13 @@ spawn-args@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/spawn-args/-/spawn-args-0.2.0.tgz#fb7d0bd1d70fd4316bd9e3dec389e65f9d6361bb"
+spawn-sync@^1.0.15:
+ version "1.0.15"
+ resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476"
+ dependencies:
+ concat-stream "^1.4.7"
+ os-shim "^0.1.2"
+
spawn-wrap@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.2.tgz#cff58e73a8224617b6561abdc32586ea0c82248c"
@@ -11034,6 +11269,19 @@ table@^4.0.1:
slice-ansi "1.0.0"
string-width "^2.1.1"
+tabtab@^2.2.2:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/tabtab/-/tabtab-2.2.2.tgz#7a047f143b010b4cbd31f857e82961512cbf4e14"
+ dependencies:
+ debug "^2.2.0"
+ inquirer "^1.0.2"
+ lodash.difference "^4.5.0"
+ lodash.uniq "^4.5.0"
+ minimist "^1.2.0"
+ mkdirp "^0.5.1"
+ npmlog "^2.0.3"
+ object-assign "^4.1.0"
+
tap-parser@^5.1.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/tap-parser/-/tap-parser-5.4.0.tgz#6907e89725d7b7fa6ae41ee2c464c3db43188aec"
@@ -11252,6 +11500,12 @@ tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33:
dependencies:
os-tmpdir "~1.0.2"
+tmp@^0.0.29:
+ version "0.0.29"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0"
+ dependencies:
+ os-tmpdir "~1.0.1"
+
to-absolute-glob@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b"
@@ -12055,6 +12309,17 @@ winston@2.1.x:
pkginfo "0.3.x"
stack-trace "0.0.x"
+winston@^2.3.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.1.tgz#a3a9265105564263c6785b4583b8c8aca26fded6"
+ dependencies:
+ async "~1.0.0"
+ colors "1.0.x"
+ cycle "1.0.x"
+ eyes "0.1.x"
+ isstream "0.1.x"
+ stack-trace "0.0.x"
+
wordwrap@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"