i18n support

This commit is contained in:
maggie-5miles 2018-12-29 13:36:49 +08:00 committed by Hayden Adams
parent e761cd6f8a
commit 520b9a58d6
18 changed files with 469 additions and 151 deletions

@ -10,6 +10,9 @@
"d3": "^4.13.0",
"deep-equal": "^1.0.1",
"fuse": "^0.4.0",
"i18next": "^13.0.1",
"i18next-browser-languagedetector": "^2.2.4",
"i18next-xhr-backend": "^1.5.1",
"jazzicon": "^1.5.0",
"node-sass": "^4.9.3",
"npm": "^6.0.0",
@ -19,6 +22,7 @@
"react-dom": "^16.2.0",
"react-ga": "^2.5.3",
"react-helmet": "^5.2.0",
"react-i18next": "^8.4.0",
"react-redux": "^5.0.7",
"react-responsive": "^5.0.0",
"react-router-dom": "^4.3.1",

72
public/locales/en.json Normal file

@ -0,0 +1,72 @@
{
"noWallet": "No Ethereum wallet found",
"wrongNetwork": "You are on the wrong network",
"switchNetwork": "Please switch to {{ correctNetwork }}",
"installWeb3MobileBrowser": "Please visit us from a web3-enabled mobile browser such as Trust Wallet or Coinbase Wallet.",
"installMetamask": "Please visit us after installing Metamask on Chrome or Brave.",
"disconnected": "Disconnected",
"swap": "Swap",
"send": "Send",
"pool": "Pool",
"betaWarning": "This project is in beta. Use at your own risk.",
"input": "Input",
"output": "Output",
"estimated": "estimated",
"balance": "Balance: {{ balanceInput }}",
"unlock": "Unlock",
"pending": "Pending",
"selectToken": "Select a token",
"searchOrPaste": "Search Token or Paste Address",
"noExchange": "No Exchange Found",
"exchangeRate": "Exchange Rate",
"enterValueCont": "Enter a {{ missingCurrencyValue }} value to continue.",
"selectTokenCont": "Select a token to continue.",
"noLiquidity": "No liquidity.",
"unlockTokenCont": "Please unlock token to continue.",
"transactionDetails": "Transaction Details",
"youAreSelling": "You are selling",
"orTransFail": "or the transaction will fail.",
"youWillReceive": "You will receive at least",
"youAreBuying": "You are buying",
"itWillCost": "It will cost at most",
"insufficientBalance": "Insufficient Balance",
"inputNotValid": "Not a valid input value",
"differentToken": "Must be different token.",
"noRecipient": "Enter a wallet address to send to.",
"invalidRecipient": "Please enter a valid wallet address recipient.",
"recipientAddress": "Recipient Address",
"youAreSending": "You are sending",
"willReceive": "will receive at least",
"to": "to",
"addLiquidity": "Add Liquidity",
"deposit": "Deposit",
"currentPoolSize": "Current Pool Size",
"yourPoolShare": "Your Pool Share",
"noZero": "Amount cannot be zero.",
"mustBeETH": "One of the input must be ETH.",
"enterCurrencyOrLabelCont": "Enter a {{ inputCurrency }} or {{ label }} value to continue.",
"youAreAdding": "You are adding between",
"and": "and",
"intoPool": "into the liquidity pool.",
"outPool": "into the liquidity pool.",
"youWillMint": "You will mint",
"liquidityTokens": "liquidity tokens.",
"totalSupplyIs": "Current total supply of liquidity tokens is",
"tokenWorth": "At current exchange rate, each pool token is worth",
"firstLiquidity": "You are the first person to add liquidity!",
"initialExchangeRate": "The initial exchange rate will be set based on your deposits. Please make sure that your ETH and {{ label }} deposits have the same fiat value.",
"removeLiquidity": "Remove Liquidity",
"poolTokens": "Pool Tokens",
"enterLabelCont": "Enter a {{ label }} value to continue.",
"youAreRemoving": "You are removing between",
"youWillRemove": "You will remove",
"createExchange": "Create Exchange",
"invalidTokenAddress": "Not a valid token address",
"exchangeExists": "{{ label }} Exchange already exists!",
"invalidSymbol": "Invalid symbol",
"invalidDecimals": "Invalid decimals",
"tokenAddress": "Token Address",
"label": "Label",
"decimals": "Decimals",
"enterTokenCont": "Enter a token address to continue"
}

72
public/locales/zh-CN.json Normal file

@ -0,0 +1,72 @@
{
"noWallet": "未发现以太钱包",
"wrongNetwork": "网络错误",
"switchNetwork": "请切换到 {{ correctNetwork }}",
"installWeb3MobileBrowser": "请从支持web3的移动端浏览器如 Trust Wallet 或 Coinbase Wallet 访问。",
"installMetamask": "请从安装了 Metamask 插件的 Chrome 或 Brave 访问。",
"disconnected": "未连接",
"swap": "交换",
"send": "发送",
"pool": "资金池",
"betaWarning": "项目尚处于beta阶段。使用需自行承担风险。",
"input": "输入",
"output": "输出",
"estimated": "估计",
"balance": "余额: {{ balanceInput }}",
"unlock": "解锁",
"pending": "处理中",
"selectToken": "选择代币",
"searchOrPaste": "搜索代币或粘贴地址",
"noExchange": "未找到交易所",
"exchangeRate": "兑换率",
"enterValueCont": "输入{{ missingCurrencyValue }}值继续。",
"selectTokenCont": "选取代币继续。",
"noLiquidity": "没有流动金。",
"unlockTokenCont": "请解锁代币继续。",
"transactionDetails": "交易明细",
"youAreSelling": "你正在出售",
"orTransFail": "或交易失败。",
"youWillReceive": "你将收到至少",
"youAreBuying": "你正在购买",
"itWillCost": "它将花费至少",
"insufficientBalance": "余额不足",
"inputNotValid": "无效的输入值",
"differentToken": "必须是不同的代币。",
"noRecipient": "输入接收钱包地址。",
"invalidRecipient": "请输入有效的接收钱包地址。",
"recipientAddress": "接收地址",
"youAreSending": "你正在发送",
"willReceive": "将收到至少",
"to": "到",
"addLiquidity": "添加流动金",
"deposit": "存入",
"currentPoolSize": "当前资金池大小",
"yourPoolShare": "你的资金池份额",
"noZero": "金额不能为零。",
"mustBeETH": "输入中必须有一个是 ETH。",
"enterCurrencyOrLabelCont": "输入 {{ inputCurrency }} 或 {{ label }} 值继续。",
"youAreAdding": "你将添加",
"and": "和",
"intoPool": "入流动资金池。",
"outPool": "出流动资金池。",
"youWillMint": "你将铸造",
"liquidityTokens": "流动代币。",
"totalSupplyIs": "当前流动代币的总量是",
"tokenWorth": "当前兑换率下,每个资金池代币价值",
"firstLiquidity": "你是第一个添加流动金的人!",
"initialExchangeRate": "初始兑换率将由你的存入情况决定。请确保你存入的 ETH 和 {{ label }} 具有相同的法币价值。",
"removeLiquidity": "删除流动金",
"poolTokens": "资金池代币",
"enterLabelCont": "输入 {{ label }} 值继续。",
"youAreRemoving": "你正在移除",
"youWillRemove": "你将移除",
"createExchange": "创建交易所",
"invalidTokenAddress": "代币地址无效",
"exchangeExists": "{{ label }} 交易所已存在!",
"invalidSymbol": "代币符号无效",
"invalidDecimals": "小数位数无效",
"tokenAddress": "代币地址",
"label": "代币符号",
"decimals": "小数位数",
"enterTokenCont": "输入代币地址继续"
}

@ -20,6 +20,7 @@ class AddressInputPanel extends Component {
render() {
const {
t,
title,
onChange,
value,
@ -34,7 +35,7 @@ class AddressInputPanel extends Component {
<div className="address-input-panel__input-container">
<div className="currency-input-panel__label-row">
<div className="currency-input-panel__label-container">
<span className="currency-input-panel__label">{title || 'Recipient Address'}</span>
<span className="currency-input-panel__label">{title || t("recipientAddress")}</span>
</div>
</div>
<div className="currency-input-panel__input-row">

@ -32,10 +32,29 @@ class ContextualInfo extends Component {
}
return (
<div className="contextual-info__details">
{this.props.renderTransactionDetails()}
</div>
)
<Modal key="modal" onClose={() => this.setState({ showDetailModal: false })}>
<CSSTransitionGroup
transitionName="summary-modal"
transitionAppear={true}
transitionLeave={true}
transitionAppearTimeout={200}
transitionLeaveTimeout={200}
transitionEnterTimeout={200}
>
<div className={c('contextual-info__summary-modal', modalClass)}>
<div
key="open-details"
className="contextual-info__open-details-container contextual-info__modal-button"
onClick={() => this.setState({showDetailModal: false})}
>
<span>{this.props.openModalText}</span>
<img src={DropupBlue} />
</div>
{this.props.renderTransactionDetails()}
</div>
</CSSTransitionGroup>
</Modal>
);
}
render() {

@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import { CSSTransitionGroup } from "react-transition-group";
import classnames from 'classnames';
import { withRouter } from 'react-router-dom';
import { withNamespaces } from 'react-i18next';
import Fuse from '../../helpers/fuse';
import Modal from '../Modal';
import TokenLogo from '../TokenLogo';
@ -105,6 +106,7 @@ class CurrencyInputPanel extends Component {
const tokens = this.createTokenList();
const { loadingExchange, searchQuery } = this.state;
const {
t,
selectedTokens,
disableTokenSelect,
web3,
@ -160,7 +162,7 @@ class CurrencyInputPanel extends Component {
const { label } = selectors().getBalance(account, searchQuery);
return [
<div key="token-modal-no-exchange" className="token-modal__token-row token-modal__token-row--no-exchange">
<div>No Exchange Found</div>
<div>{t("noExchange")}</div>
</div>,
<div
key="token-modal-create-exchange"
@ -178,7 +180,7 @@ class CurrencyInputPanel extends Component {
if (!results.length) {
return (
<div className="token-modal__token-row token-modal__token-row--no-exchange">
<div>No Exchange Found</div>
<div>{t("noExchange")}</div>
</div>
)
}
@ -222,7 +224,7 @@ class CurrencyInputPanel extends Component {
<div className="token-modal__search-container">
<input
type="text"
placeholder="Search Token or Paste Address"
placeholder={this.props.t("searchOrPaste")}
className="token-modal__search-input"
onChange={e => {
this.setState({ searchQuery: e.target.value });
@ -241,6 +243,7 @@ class CurrencyInputPanel extends Component {
renderUnlockButton() {
const {
t,
selectors,
selectedTokenAddress,
account,
@ -276,7 +279,7 @@ class CurrencyInputPanel extends Component {
className='currency-input-panel__sub-currency-select currency-input-panel__sub-currency-select--pending'
>
<div className="loader" />
Pending
{t("pending")}
</button>
);
}
@ -296,13 +299,14 @@ class CurrencyInputPanel extends Component {
});
}}
>
Unlock
{t("unlock")}
</button>
);
}
renderInput() {
const {
t,
errorMessage,
value,
onValueChange,
@ -358,7 +362,7 @@ class CurrencyInputPanel extends Component {
)
: null
}
{ TOKEN_ADDRESS_TO_LABEL[selectedTokenAddress] || 'Select a token' }
{ TOKEN_ADDRESS_TO_LABEL[selectedTokenAddress] || t("selectToken") }
<span className="currency-input-panel__dropdown-icon" />
</button>
</div>
@ -416,5 +420,5 @@ export default withRouter(
addPendingTx: opts => dispatch(addPendingTx(opts)),
addApprovalTx: opts => dispatch(addApprovalTx(opts)),
}),
)(CurrencyInputPanel)
)(withNamespaces()(CurrencyInputPanel))
);

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import classnames from 'classnames';
import UAParser from 'ua-parser-js';
import { withNamespaces } from 'react-i18next';
import Logo from '../Logo';
import CoinbaseWalletLogo from '../../assets/images/coinbase-wallet-logo.png';
import TrustLogo from '../../assets/images/trust-wallet-logo.svg';
@ -77,6 +78,7 @@ function isMobile() {
class BlockingWarning extends Component {
render () {
const {
t,
isConnected,
initialized,
networkId,
@ -90,21 +92,21 @@ class BlockingWarning extends Component {
if (wrongNetwork && initialized) {
content = [
<div key="warning-title">You are on the wrong network</div>,
<div key="warning-title">{t("wrongNetwork")}</div>,
<div key="warning-desc" className="header__dialog__description">
{`Please switch to ${correctNetwork}`}
{t("switchNetwork", {correctNetwork})}
</div>,
];
}
if (!isConnected && initialized) {
content = [
<div key="warning-title">No Ethereum wallet found</div>,
<div key="warning-title">{t("noWallet")}</div>,
<div key="warning-desc" className="header__dialog__description">
{
isMobile()
? 'Please visit us from a web3-enabled mobile browser such as Trust Wallet or Coinbase Wallet.'
: 'Please visit us after installing Metamask on Chrome or Brave.'
? t("installWeb3MobileBrowser")
: t("installMetamask")
}
</div>,
<div key="warning-logos" className="header__download">
@ -171,4 +173,4 @@ export default connect(
web3: state.web3connect.web3,
networkId: state.web3connect.networkId,
}),
)(Header);
)(withNamespaces()(Header));

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withNamespaces } from 'react-i18next';
import { dismissBetaMessage } from '../../ducks/app';
import {Tab, Tabs} from "../Tab";
@ -38,18 +39,18 @@ class NavigationTabs extends Component {
}
render() {
const { showBetaMessage, className, dismissBetaMessage } = this.props;
const { t, showBetaMessage, className, dismissBetaMessage } = this.props;
return (
<div>
<Tabs className={className}>
{ this.renderTab('Swap', '/swap', /swap/) }
{ this.renderTab('Send', '/send', /send/) }
{ this.renderTab('Pool', '/add-liquidity', /add-liquidity|remove-liquidity|create-exchange/) }
{ this.renderTab(t("swap"), '/swap', /swap/) }
{ this.renderTab(t("send"), '/send', /send/) }
{ this.renderTab(t("pool"), '/add-liquidity', /add-liquidity|remove-liquidity|create-exchange/) }
</Tabs>
{
showBetaMessage && (
<div className="beta-message" onClick={dismissBetaMessage}>
💀 This project is in beta. Use at your own risk.
💀 {t("betaWarning")}
</div>
)
}
@ -66,5 +67,5 @@ export default withRouter(
dispatch => ({
dismissBetaMessage: () => dispatch(dismissBetaMessage()),
}),
)(NavigationTabs)
)(withNamespaces()(NavigationTabs))
);

@ -5,6 +5,7 @@ import classnames from 'classnames';
import Web3 from 'web3';
import Jazzicon from 'jazzicon';
import { CSSTransitionGroup } from "react-transition-group";
import { withNamespaces } from 'react-i18next';
import './web3-status.scss';
import Modal from '../Modal';
@ -35,7 +36,7 @@ class Web3Status extends Component {
{transaction}
</div>
<div className="pending-modal__pending-indicator">
<div className="loader" /> Pending
<div className="loader" /> {this.props.t("pending")}
</div>
</div>
);
@ -69,7 +70,7 @@ class Web3Status extends Component {
}
render() {
const { address, pending, confirmed } = this.props;
const { t, address, pending, confirmed } = this.props;
const hasPendingTransactions = !!pending.length;
const hasConfirmedTransactions = !!confirmed.length;
@ -83,7 +84,7 @@ class Web3Status extends Component {
onClick={this.handleClick}
>
<div className="web3-status__text">
{ hasPendingTransactions ? getPendingText(pending) : getText(address) }
{hasPendingTransactions ? getPendingText(pending, t("pending")) : getText(address, t("disconnected")) }
</div>
<div
className="web3-status__identicon"
@ -108,18 +109,18 @@ class Web3Status extends Component {
function getPendingText(pendingTransactions) {
function getPendingText(pendingTransactions, pendingLabel) {
return (
<div className="web3-status__pending-container">
<div className="loader" />
<span key="text">{pendingTransactions.length} Pending</span>
<span key="text">{pendingTransactions.length} {pendingLabel}</span>
</div>
);
}
function getText(text) {
function getText(text, disconnectedText) {
if (!text || text.length < 42 || !Web3.utils.isHexStrict(text)) {
return 'Disconnected';
return disconnectedText;
}
const address = Web3.utils.toChecksumAddress(text);
@ -145,4 +146,4 @@ export default connect(
confirmed: state.web3connect.transactions.confirmed,
};
}
)(Web3Status);
)(withNamespaces()(Web3Status));

32
src/i18n.js Normal file

@ -0,0 +1,32 @@
import i18n from "i18next";
import Backend from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { reactI18nextModule } from "react-i18next";
const resources = {
loadPath: `./locales/{{lng}}.json`
}
i18n
// load translation using xhr -> see /public/locales
// learn more: https://github.com/i18next/i18next-xhr-backend
.use(Backend)
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
.use(LanguageDetector)
// pass the i18n instance to react-i18next.
.use(reactI18nextModule)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
backend: resources,
fallbackLng: "en",
keySeparator: false,
interpolation: {
escapeValue: false
}
});
export default i18n;

@ -2,6 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import ReactGA from 'react-ga';
import './i18n';
import App from './pages/App';
import store from './store';

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import classnames from "classnames";
import { withNamespaces } from 'react-i18next';
import CurrencyInputPanel from '../../components/CurrencyInputPanel';
import OversizedPanel from '../../components/OversizedPanel';
import ContextualInfo from '../../components/ContextualInfo';
@ -51,10 +52,11 @@ class AddLiquidity extends Component {
};
shouldComponentUpdate(nextProps, nextState) {
const { isConnected, account, exchangeAddresses, balances, web3 } = this.props;
const { t, isConnected, account, exchangeAddresses, balances, web3 } = this.props;
const { inputValue, outputValue, inputCurrency, outputCurrency, lastEditedField } = this.state;
return isConnected !== nextProps.isConnected ||
t != nextProps.t ||
account !== nextProps.account ||
exchangeAddresses !== nextProps.exchangeAddresses ||
web3 !== nextProps.web3 ||
@ -111,7 +113,7 @@ class AddLiquidity extends Component {
};
getBalance(currency) {
const { selectors, account } = this.props;
const { t, selectors, account } = this.props;
if (!currency) {
return '';
@ -122,7 +124,8 @@ class AddLiquidity extends Component {
return '';
}
return `Balance: ${value.dividedBy(10 ** decimals).toFixed(4)}`;
const balanceInput = value.dividedBy(10 ** decimals).toFixed(4);
return t("balance", { balanceInput });
}
isUnapproved() {
@ -271,7 +274,7 @@ class AddLiquidity extends Component {
}
validate() {
const { selectors, account } = this.props;
const { t, selectors, account } = this.props;
const {
inputValue, outputValue,
inputCurrency, outputCurrency,
@ -291,11 +294,11 @@ class AddLiquidity extends Component {
const { value: tokenValue, decimals } = selectors().getBalance(account, outputCurrency);
if (ethValue.isLessThan(BN(inputValue * 10 ** 18))) {
inputError = 'Insufficient Balance';
inputError = t("insufficientBalance");
}
if (tokenValue.isLessThan(BN(outputValue * 10 ** decimals))) {
outputError = 'Insufficient Balance';
outputError = t("insufficientBalance");
}
return {
@ -306,18 +309,19 @@ class AddLiquidity extends Component {
}
renderInfo() {
const t = this.props.t;
const blank = (
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">Exchange Rate</span>
<span className="pool__exchange-rate">{t("exchangeRate")}</span>
<span> - </span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">Current Pool Size</span>
<span className="swap__exchange-rate">{t("currentPoolSize")}</span>
<span> - </span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">Your Pool Share</span>
<span className="swap__exchange-rate">{t("yourPoolShare")}</span>
<span> - </span>
</div>
</div>
@ -353,16 +357,16 @@ class AddLiquidity extends Component {
return (
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">Exchange Rate</span>
<span className="pool__exchange-rate">{t("exchangeRate")}</span>
<span>{`1 ETH = ${rateText} ${label}`}</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">Current Pool Size</span>
<span className="swap__exchange-rate">{t("currentPoolSize")}</span>
<span>{` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} + ${tokenValue.dividedBy(10 ** decimals).toFixed(2)} ${label}`}</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">
Your Pool Share ({ownership.multipliedBy(100).toFixed(2)}%)
{t("yourPoolShare")} ({ownership.multipliedBy(100).toFixed(2)}%)
</span>
<span>{`${ownedEth.toFixed(2)} ETH + ${ownedToken.toFixed(2)} ${label}`}</span>
</div>
@ -377,16 +381,16 @@ class AddLiquidity extends Component {
return (
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">Exchange Rate</span>
<span className="pool__exchange-rate">{t("exchangeRate")}</span>
<span>{`1 ETH = ${tokenValue.multipliedBy(10 ** (18 - decimals)).dividedBy(ethValue).toFixed(4)} ${label}`}</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">Current Pool Size</span>
<span className="swap__exchange-rate">{t("currentPoolSize")}</span>
<span>{` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} + ${tokenValue.dividedBy(10 ** decimals).toFixed(2)} ${label}`}</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">
Your Pool Share ({ownership.multipliedBy(100).toFixed(2)}%)
{t("yourPoolShare")} ({ownership.multipliedBy(100).toFixed(2)}%)
</span>
<span>{`${ownedEth.toFixed(2)} ETH + ${ownedToken.toFixed(2)} ${label}`}</span>
</div>
@ -395,7 +399,7 @@ class AddLiquidity extends Component {
}
renderSummary(inputError, outputError) {
const { selectors, exchangeAddresses: { fromToken } } = this.props;
const { t, selectors, exchangeAddresses: { fromToken } } = this.props;
const {
inputValue,
outputValue,
@ -412,22 +416,23 @@ class AddLiquidity extends Component {
contextualInfo = inputError || outputError;
isError = true;
} else if (!inputCurrency || !outputCurrency) {
contextualInfo = 'Select a token to continue.';
contextualInfo = t("selectTokenCont");
} else if (inputCurrency === outputCurrency) {
contextualInfo = 'Must be different token.';
contextualInfo = t("differentToken");
} else if (![inputCurrency, outputCurrency].includes('ETH')) {
contextualInfo = 'One of the input must be ETH.';
contextualInfo = t("mustBeETH");
} else if (inputIsZero || outputIsZero) {
contextualInfo = 'Amount cannot be zero.';
contextualInfo = t("noZero");
} else if (this.isUnapproved()) {
contextualInfo = 'Please unlock token to continue.';
contextualInfo = t("unlockTokenCont");
} else if (!inputValue || !outputValue) {
contextualInfo = `Enter a ${inputCurrency} or ${label} value to continue.`;
contextualInfo = t("enterCurrencyOrLabelCont", {inputCurrency, label});
}
return (
<ContextualInfo
key="context-info"
openModalText={t("transactionDetails")}
contextualInfo={contextualInfo}
isError={isError}
renderTransactionDetails={this.renderTransactionDetails}
@ -436,7 +441,7 @@ class AddLiquidity extends Component {
}
renderTransactionDetails = () => {
const { selectors, exchangeAddresses: { fromToken }, account } = this.props;
const { t, selectors, exchangeAddresses: { fromToken }, account } = this.props;
const {
inputValue,
outputValue,
@ -474,16 +479,17 @@ class AddLiquidity extends Component {
return (
<div>
<div className="pool__summary-item">You are adding between {b(`${+BN(inputValue).toFixed(7)} ETH`)} and {b(`${+minOutput.toFixed(7)} - ${+maxOutput.toFixed(7)} ${label}`)} into the liquidity pool.</div>
<div className="pool__summary-item">You will mint {b(+liquidityMinted.toFixed(7))} liquidity tokens.</div>
<div className="pool__summary-item">Current total supply of liquidity tokens is {b(+adjTotalSupply.toFixed(7))}</div>
<div className="pool__summary-item">At current exchange rate, each pool token is worth {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH and {b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label}</div>
<div className="pool__summary-modal__item">{t("youAreAdding")} {b(`${+BN(inputValue).toFixed(7)} ETH`)} {t("and")} {b(`${+minOutput.toFixed(7)} - ${+maxOutput.toFixed(7)} ${label}`)} {t("intoPool")}</div>
<div className="pool__summary-modal__item">{t("youWillMint")} {b(+liquidityMinted.toFixed(7))} {t("liquidityTokens")}</div>
<div className="pool__summary-modal__item">{t("totalSupplyIs")} {b(+adjTotalSupply.toFixed(7))}</div>
<div className="pool__summary-modal__item">{t("tokenWorth")} {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH {t("and")} {b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label}</div>
</div>
);
}
render() {
const {
t,
isConnected,
exchangeAddresses: { fromToken },
selectors,
@ -516,18 +522,18 @@ class AddLiquidity extends Component {
? (
<div className="pool__new-exchange-warning">
<div className="pool__new-exchange-warning-text">
🚰 You are the first person to add liquidity!
🚰 {t("firstLiquidity")}
</div>
<div className="pool__new-exchange-warning-text">
{`The initial exchange rate will be set based on your deposits. Please make sure that your ETH and ${label} deposits have the same fiat value.`}
{ t("initialExchangeRate", { label }) }
</div>
</div>
)
: null
}
<ModeSelector title="Add Liquidity" />
<ModeSelector title={t("addLiquidity")}/>
<CurrencyInputPanel
title="Deposit"
title={t("deposit")}
extraText={this.getBalance(inputCurrency)}
onValueChange={this.onInputChange}
selectedTokenAddress="ETH"
@ -541,8 +547,8 @@ class AddLiquidity extends Component {
</div>
</OversizedPanel>
<CurrencyInputPanel
title="Deposit"
description={this.isNewExchange() ? '(estimated)' : ''}
title={t("deposit")}
description={this.isNewExchange() ? `(${t("estimated")})` : ''}
extraText={this.getBalance(outputCurrency)}
selectedTokenAddress={outputCurrency}
onCurrencySelected={currency => {
@ -568,7 +574,7 @@ class AddLiquidity extends Component {
disabled={!isValid}
onClick={this.onAddLiquidity}
>
Add Liquidity
{t("addLiquidity")}
</button>
</div>
</div>
@ -588,7 +594,7 @@ export default connect(
selectors: () => dispatch(selectors()),
addPendingTx: id => dispatch(addPendingTx(id)),
})
)(AddLiquidity);
)(withNamespaces()(AddLiquidity));
function b(text) {
return <span className="swap__highlight-text">{text}</span>

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { withNamespaces } from 'react-i18next';
import {selectors, addPendingTx} from "../../ducks/web3connect";
import classnames from "classnames";
import NavigationTabs from "../../components/NavigationTabs";
@ -39,6 +40,7 @@ class CreateExchange extends Component {
validate() {
const { tokenAddress } = this.state;
const {
t,
web3,
account,
selectors,
@ -59,7 +61,7 @@ class CreateExchange extends Component {
if (web3 && web3.utils && !web3.utils.isAddress(tokenAddress)) {
return {
isValid: false,
errorMessage: 'Not a valid token address',
errorMessage: t("invalidTokenAddress"),
};
}
@ -74,15 +76,15 @@ class CreateExchange extends Component {
}
});
} else {
errorMessage = `${label} Exchange already exists!`;
errorMessage = t("exchangeExists", { label });
}
if (!label) {
errorMessage = 'Invalid symbol';
errorMessage = t("invalidSymbol");
}
if (!decimals) {
errorMessage = 'Invalid decimals';
errorMessage = t("invalidDecimals");
}
return {
@ -141,7 +143,7 @@ class CreateExchange extends Component {
if (!tokenAddress) {
return (
<div className="create-exchange__summary-panel">
<div className="create-exchange__summary-text">Enter a token address to continue</div>
<div className="create-exchange__summary-text">{this.props.t("enterTokenCont")}</div>
</div>
)
}
@ -159,7 +161,7 @@ class CreateExchange extends Component {
render() {
const { tokenAddress } = this.state;
const { isConnected, account, selectors, web3 } = this.props;
const { t, isConnected, account, selectors, web3 } = this.props;
const { isValid, errorMessage } = this.validate();
let label, decimals;
@ -181,9 +183,9 @@ class CreateExchange extends Component {
'header--inactive': !isConnected,
})}
/>
<ModeSelector title="Create Exchange" />
<ModeSelector title={t("createExchange")} />
<AddressInputPanel
title="Token Address"
title={t("tokenAddress")}
value={tokenAddress}
onChange={this.onChange}
errorMessage={errorMessage}
@ -191,11 +193,11 @@ class CreateExchange extends Component {
<OversizedPanel hideBottom>
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">Label</span>
<span className="pool__exchange-rate">{t("label")}</span>
<span>{label || ' - '}</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">Decimals</span>
<span className="swap__exchange-rate">{t("decimals")}</span>
<span>{decimals || ' - '}</span>
</div>
</div>
@ -209,7 +211,7 @@ class CreateExchange extends Component {
disabled={!isValid}
onClick={this.onCreateExchange}
>
Create Exchange
{t("createExchange")}
</button>
</div>
</div>
@ -232,5 +234,5 @@ export default withRouter(
addExchange: opts => dispatch(addExchange(opts)),
addPendingTx: id => dispatch(addPendingTx(id)),
})
)(CreateExchange)
)(withNamespaces()(CreateExchange))
);

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { withNamespaces } from 'react-i18next';
import OversizedPanel from "../../components/OversizedPanel";
import Dropdown from "../../assets/images/dropdown-blue.svg";
import Modal from "../../components/Modal";
@ -55,19 +56,19 @@ class ModeSelector extends Component {
className="pool-modal__item"
onClick={() => this.changeView(ADD)}
>
{ADD}
{this.props.t("addLiquidity")}
</div>
<div
className="pool-modal__item"
onClick={() => this.changeView(REMOVE)}
>
{REMOVE}
{this.props.t("removeLiquidity")}
</div>
<div
className="pool-modal__item"
onClick={() => this.changeView(CREATE)}
>
{CREATE}
{this.props.t("createExchange")}
</div>
</div>
</CSSTransitionGroup>
@ -93,4 +94,4 @@ class ModeSelector extends Component {
}
}
export default withRouter(ModeSelector);
export default withRouter(withNamespaces()(ModeSelector));

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import classnames from "classnames";
import { connect } from 'react-redux';
import { BigNumber as BN } from 'bignumber.js';
import { withNamespaces } from 'react-i18next';
import NavigationTabs from "../../components/NavigationTabs";
import ModeSelector from "./ModeSelector";
import CurrencyInputPanel from "../../components/CurrencyInputPanel";
@ -40,7 +41,7 @@ class RemoveLiquidity extends Component {
validate() {
const { tokenAddress, value } = this.state;
const { account, selectors, exchangeAddresses: { fromToken }, web3 } = this.props;
const { t, account, selectors, exchangeAddresses: { fromToken }, web3 } = this.props;
const exchangeAddress = fromToken[tokenAddress];
if (!web3 || !exchangeAddress || !account || !value) {
@ -54,7 +55,7 @@ class RemoveLiquidity extends Component {
const { value: liquidityBalance, decimals: liquidityDecimals } = getBalance(account, exchangeAddress);
if (liquidityBalance.isLessThan(BN(value).multipliedBy(10 ** liquidityDecimals))) {
return { isValid: false, errorMessage: 'Insufficient balance' };
return { isValid: false, errorMessage: t("insufficientBalance") };
}
return {
@ -159,7 +160,7 @@ class RemoveLiquidity extends Component {
};
renderSummary(errorMessage) {
const { selectors, exchangeAddresses: { fromToken } } = this.props;
const { t, selectors, exchangeAddresses: { fromToken } } = this.props;
const {
value: input,
tokenAddress,
@ -172,17 +173,18 @@ class RemoveLiquidity extends Component {
contextualInfo = errorMessage;
isError = true;
} else if (!tokenAddress) {
contextualInfo = 'Select a token to continue.';
contextualInfo = t("selectTokenCont");
} else if (inputIsZero) {
contextualInfo = 'Amount cannot be zero.';
contextualInfo = t("noZero");
} else if (!input) {
const { label } = selectors().getTokenBalance(tokenAddress, fromToken[tokenAddress]);
contextualInfo = `Enter a ${label} value to continue.`;
contextualInfo = t("enterLabelCont", { label });
}
return (
<ContextualInfo
key="context-info"
openModalText={t("transactionDetails")}
contextualInfo={contextualInfo}
isError={isError}
renderTransactionDetails={this.renderTransactionDetails}
@ -193,6 +195,7 @@ class RemoveLiquidity extends Component {
renderTransactionDetails = () => {
const { tokenAddress, value: input, totalSupply } = this.state;
const {
t,
exchangeAddresses: { fromToken },
web3,
selectors,
@ -228,16 +231,24 @@ class RemoveLiquidity extends Component {
return (
<div>
<<<<<<< HEAD
<div className="pool__summary-item">You are removing between {b(`${+BN(ethWithdrawn).toFixed(7)} ETH`)} and {b(`${+minTokenWithdrawn} - ${+maxTokenWithdrawn} ${label}`)} into the liquidity pool.</div>
<div className="pool__summary-item">You will remove {b(+input)} liquidity tokens.</div>
<div className="pool__summary-item">Current total supply of liquidity tokens is {b(+adjTotalSupply.toFixed(7))}</div>
<div className="pool__summary-item">At current exchange rate, each pool token is worth {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH and {b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label}</div>
=======
<div className="pool__summary-modal__item">{t("youAreRemoving")} {b(`${+BN(ethWithdrawn).toFixed(7)} ETH`)} {t("and")} {b(`${+minTokenWithdrawn} - ${+maxTokenWithdrawn} ${label}`)} {t("outPool")}</div>
<div className="pool__summary-modal__item">{t("youWillRemove")} {b(+input)} {t("liquidityTokens")}</div>
<div className="pool__summary-modal__item">{t("totalSupplyIs")} {b(+adjTotalSupply.toFixed(7))}</div>
<div className="pool__summary-modal__item">{t("tokenWorth")} {b(+ethReserve.dividedBy(totalSupply).toFixed(7))} ETH {t("and")} {b(+tokenReserve.dividedBy(totalSupply).toFixed(7))} {label}</div>
>>>>>>> 30eade0... i18n support
</div>
);
}
renderOutput() {
const {
t,
exchangeAddresses: { fromToken },
account,
web3,
@ -250,8 +261,8 @@ class RemoveLiquidity extends Component {
const blank = [
<CurrencyInputPanel
key="remove-liquidity-input"
title="Output"
description="(estimated)"
title={t("output")}
description={`(${t("estimated")})`}
renderInput={() => (
<div className="remove-liquidity__output"></div>
)}
@ -261,15 +272,15 @@ class RemoveLiquidity extends Component {
<OversizedPanel key="remove-liquidity-input-under" hideBottom>
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">Exchange Rate</span>
<span className="pool__exchange-rate">{t("exchangeRate")}</span>
<span> - </span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">Current Pool Size</span>
<span className="swap__exchange-rate">{t("currentPoolSize")}</span>
<span> - </span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">Your Pool Share</span>
<span className="swap__exchange-rate">{t("yourPoolShare")}</span>
<span> - </span>
</div>
</div>
@ -299,8 +310,8 @@ class RemoveLiquidity extends Component {
return [
<CurrencyInputPanel
title="Output"
description="(estimated)"
title={t("output")}
description={`(${t("estimated")})`}
key="remove-liquidity-input"
renderInput={() => input
? (
@ -322,18 +333,18 @@ class RemoveLiquidity extends Component {
<OversizedPanel key="remove-liquidity-input-under" hideBottom>
<div className="pool__summary-panel">
<div className="pool__exchange-rate-wrapper">
<span className="pool__exchange-rate">Exchange Rate</span>
<span className="pool__exchange-rate">{t("exchangeRate")}</span>
<span>
{`1 ETH = ${exchangeRate.toFixed(4)} ${label}`}
</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">Current Pool Size</span>
<span className="swap__exchange-rate">{t("currentPoolSize")}</span>
<span>{`${ethReserve.dividedBy(10 ** 18).toFixed(2)} ETH + ${tokenReserve.dividedBy(10 ** tokenDecimals).toFixed(2)} ${label}`}</span>
</div>
<div className="pool__exchange-rate-wrapper">
<span className="swap__exchange-rate">
Your Pool Share ({ownership.multipliedBy(100).toFixed(2)}%)
{t("yourPoolShare")} ({ownership.multipliedBy(100).toFixed(2)}%)
</span>
<span>{`${ownedEth.toFixed(2)} ETH + ${ownedToken.toFixed(2)} ${label}`}</span>
</div>
@ -343,7 +354,7 @@ class RemoveLiquidity extends Component {
}
render() {
const { isConnected } = this.props;
const { t, isConnected } = this.props;
const { tokenAddress, value } = this.state;
const { isValid, errorMessage } = this.validate();
@ -359,9 +370,9 @@ class RemoveLiquidity extends Component {
'header--inactive': !isConnected,
})}
/>
<ModeSelector title="Remove Liquidity" />
<ModeSelector title={t("removeLiquidity")} />
<CurrencyInputPanel
title="Pool Tokens"
title={t("poolTokens")}
extraText={this.getBalance(tokenAddress)}
onValueChange={this.onInputChange}
value={value}
@ -386,7 +397,7 @@ class RemoveLiquidity extends Component {
disabled={!isValid}
onClick={this.onRemoveLiquidity}
>
Remove Liquidity
{t("removeLiquidity")}
</button>
</div>
</div>
@ -406,7 +417,7 @@ export default connect(
selectors: () => dispatch(selectors()),
addPendingTx: id => dispatch(addPendingTx(id)),
})
)(RemoveLiquidity);
)(withNamespaces()(RemoveLiquidity));
function b(text) {
return <span className="swap__highlight-text">{text}</span>

@ -3,6 +3,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import {BigNumber as BN} from "bignumber.js";
import { withNamespaces } from 'react-i18next';
import { selectors, addPendingTx } from '../../ducks/web3connect';
import Header from '../../components/Header';
import NavigationTabs from '../../components/NavigationTabs';
@ -87,11 +88,11 @@ class Send extends Component {
const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency);
if (inputBalance.isLessThan(BN(inputValue * 10 ** inputDecimals))) {
inputError = 'Insufficient Balance';
inputError = this.props.t("insufficientBalance");
}
if (inputValue === 'N/A') {
inputError = 'Not a valid input value';
inputError = this.props.t("inputNotValid");
}
return {
@ -535,7 +536,7 @@ class Send extends Component {
outputCurrency,
recipient,
} = this.state;
const { web3 } = this.props;
const { t, web3 } = this.props;
const { selectors, account } = this.props;
const { label: inputLabel } = selectors().getBalance(account, inputCurrency);
@ -551,24 +552,25 @@ class Send extends Component {
contextualInfo = inputError || outputError;
isError = true;
} else if (!inputCurrency || !outputCurrency) {
contextualInfo = 'Select a token to continue.';
contextualInfo = t("selectTokenCont");
} else if (inputCurrency === outputCurrency) {
contextualInfo = 'Must be different token.';
contextualInfo = t("differentToken");
} else if (!inputValue || !outputValue) {
const missingCurrencyValue = !inputValue ? inputLabel : outputLabel;
contextualInfo = `Enter a ${missingCurrencyValue} value to continue.`;
contextualInfo = t("enterValueCont", {missingCurrencyValue});
} else if (inputIsZero || outputIsZero) {
contextualInfo = 'No liquidity.';
contextualInfo = t("noLiquidity");
} else if (this.isUnapproved()) {
contextualInfo = 'Please unlock token to continue.';
contextualInfo = t("unlockTokenCont");
} else if (!recipient) {
contextualInfo = 'Enter a wallet address to send to.';
contextualInfo = t("noRecipient");
} else if (!validRecipientAddress) {
contextualInfo = 'Please enter a valid wallet address recipient.';
contextualInfo = t("invalidRecipient");
}
return (
<ContextualInfo
openModalText={t("transactionDetails")}
contextualInfo={contextualInfo}
isError={isError}
renderTransactionDetails={this.renderTransactionDetails}
@ -586,7 +588,7 @@ class Send extends Component {
inputAmountB,
lastEditedField,
} = this.state;
const { selectors, account } = this.props;
const { t, selectors, account } = this.props;
ReactGA.event({
category: 'TransactionDetail',
@ -641,10 +643,10 @@ class Send extends Component {
return (
<div>
<div>
You are selling {b(`${+inputValue} ${inputLabel}`)}.
{t("youAreSending")} {b(`${+inputValue} ${inputLabel}`)}.
</div>
<div className="send__last-summary-text">
{recipientText} will receive at least {b(`${+minOutput} ${outputLabel}`)} or the transaction will fail.
{recipientText} {t("willReceive")} {b(`${+minOutput} ${outputLabel}`)} {t("orTransFail")}
</div>
</div>
);
@ -652,12 +654,12 @@ class Send extends Component {
return (
<div>
<div>
You are sending {b(`${+outputValue} ${outputLabel}`)} to {recipientText}.
{t("youAreSending")} {b(`${+outputValue} ${outputLabel}`)} {t("to")} {recipientText}.
{/*You are selling between {b(`${+inputValue} ${inputLabel}`)} to {b(`${+maxInput} ${inputLabel}`)}.*/}
</div>
<div className="send__last-summary-text">
{/*{b(`${recipient.slice(0, 6)}...${recipient.slice(-4)}`)} will receive {b(`${+outputValue} ${outputLabel}`)}.*/}
It will cost at most {b(`${+maxInput} ${inputLabel}`)} or the transaction will fail.
{t("itWillCost")} {b(`${+maxInput} ${inputLabel}`)} {t("orTransFail")}
</div>
</div>
);
@ -665,7 +667,7 @@ class Send extends Component {
}
renderExchangeRate() {
const { account, selectors } = this.props;
const { t, account, selectors } = this.props;
const { exchangeRate, inputCurrency, outputCurrency } = this.state;
const { label: inputLabel } = selectors().getBalance(account, inputCurrency);
const { label: outputLabel } = selectors().getBalance(account, outputCurrency);
@ -674,7 +676,7 @@ class Send extends Component {
return (
<OversizedPanel hideBottom>
<div className="swap__exchange-rate-wrapper">
<span className="swap__exchange-rate">Exchange Rate</span>
<span className="swap__exchange-rate">{t("exchangeRate")}</span>
<span> - </span>
</div>
</OversizedPanel>
@ -684,7 +686,7 @@ class Send extends Component {
return (
<OversizedPanel hideBottom>
<div className="swap__exchange-rate-wrapper">
<span className="swap__exchange-rate">Exchange Rate</span>
<span className="swap__exchange-rate">{t("exchangeRate")}</span>
<span>
{`1 ${inputLabel} = ${exchangeRate.toFixed(7)} ${outputLabel}`}
</span>
@ -697,12 +699,12 @@ class Send extends Component {
if (!currency || decimals === 0) {
return '';
}
return `Balance: ${balance.dividedBy(BN(10 ** decimals)).toFixed(4)}`
const balanceInput = balance.dividedBy(BN(10 ** decimals)).toFixed(4)
return this.props.t("balance", { balanceInput })
}
render() {
const { selectors, account } = this.props;
const { t, selectors, account } = this.props;
const {
lastEditedField,
inputCurrency,
@ -711,7 +713,7 @@ class Send extends Component {
outputValue,
recipient,
} = this.state;
const estimatedText = '(estimated)';
const estimatedText = `(${t("estimated")})`;
const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency);
const { value: outputBalance, decimals: outputDecimals } = selectors().getBalance(account, outputCurrency);
@ -733,7 +735,7 @@ class Send extends Component {
})}
/>
<CurrencyInputPanel
title="Input"
title={t("input")}
description={lastEditedField === OUTPUT ? estimatedText : ''}
extraText={this.renderBalance(inputCurrency, inputBalance, inputDecimals)}
onCurrencySelected={inputCurrency => this.setState({ inputCurrency }, this.recalcForm)}
@ -749,7 +751,7 @@ class Send extends Component {
</div>
</OversizedPanel>
<CurrencyInputPanel
title="Output"
title={t("output")}
description={lastEditedField === INPUT ? estimatedText : ''}
extraText={this.renderBalance(outputCurrency, outputBalance, outputDecimals)}
onCurrencySelected={outputCurrency => this.setState({ outputCurrency }, this.recalcForm)}
@ -766,6 +768,7 @@ class Send extends Component {
</div>
</OversizedPanel>
<AddressInputPanel
t={this.props.t}
value={recipient}
onChange={address => this.setState({recipient: address})}
/>
@ -779,7 +782,7 @@ class Send extends Component {
disabled={!isValid}
onClick={this.onSend}
>
Send
{t("send")}
</button>
</div>
</div>
@ -800,7 +803,7 @@ export default connect(
selectors: () => dispatch(selectors()),
addPendingTx: id => dispatch(addPendingTx(id)),
}),
)(Send);
)(withNamespaces()(Send));
const b = text => <span className="swap__highlight-text">{text}</span>;

@ -5,6 +5,7 @@ import classnames from 'classnames';
import {BigNumber as BN} from "bignumber.js";
import MediaQuery from 'react-responsive';
import ReactGA from 'react-ga';
import { withNamespaces } from 'react-i18next';
import { selectors, addPendingTx } from '../../ducks/web3connect';
import Header from '../../components/Header';
import NavigationTabs from '../../components/NavigationTabs';
@ -84,11 +85,11 @@ class Swap extends Component {
const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency);
if (inputBalance.isLessThan(BN(inputValue * 10 ** inputDecimals))) {
inputError = 'Insufficient Balance';
inputError = this.props.t("insufficientBalance");
}
if (inputValue === 'N/A') {
inputError = 'Not a valid input value';
inputError = this.props.t("inputNotValid");
}
return {
@ -525,6 +526,7 @@ class Swap extends Component {
outputValue,
outputCurrency,
} = this.state;
const t = this.props.t;
const inputIsZero = BN(inputValue).isZero();
const outputIsZero = BN(outputValue).isZero();
@ -532,11 +534,11 @@ class Swap extends Component {
let isError = false;
if (!inputCurrency || !outputCurrency) {
contextualInfo = 'Select a token to continue.';
contextualInfo = t("selectTokenCont");
}
if (!inputValue || !outputValue) {
contextualInfo = 'Enter a value to continue.';
contextualInfo = t("enterValueCont");
}
if (inputError || outputError) {
@ -545,15 +547,16 @@ class Swap extends Component {
}
if (inputIsZero || outputIsZero) {
contextualInfo = 'No liquidity.';
contextualInfo = t("noLiquidity");
}
if (this.isUnapproved()) {
contextualInfo = 'Please unlock token to continue.';
contextualInfo = t("unlockTokenCont");
}
return (
<ContextualInfo
openModalText={t("transactionDetails")}
contextualInfo={contextualInfo}
isError={isError}
renderTransactionDetails={this.renderTransactionDetails}
@ -569,7 +572,7 @@ class Swap extends Component {
outputCurrency,
lastEditedField,
} = this.state;
const { selectors, account } = this.props;
const { t, selectors, account } = this.props;
ReactGA.event({
category: 'TransactionDetail',
@ -623,10 +626,10 @@ class Swap extends Component {
return (
<div>
<div>
You are selling {b(`${+inputValue} ${inputLabel}`)}.
{t("youAreSelling")} {b(`${+inputValue} ${inputLabel}`)} {t("orTransFail")}
</div>
<div className="send__last-summary-text">
You will receive at least {b(`${+minOutput} ${outputLabel}`)} or the transaction will fail.
{t("youWillReceive")} {b(`${+minOutput} ${outputLabel}`)} {t("orTransFail")}
</div>
</div>
);
@ -634,10 +637,10 @@ class Swap extends Component {
return (
<div>
<div>
You are buying {b(`${+outputValue} ${outputLabel}`)}.
{t("youAreBuying")} {b(`${+outputValue} ${outputLabel}`)}.
</div>
<div className="send__last-summary-text">
It will cost at most {b(`${+maxInput} ${inputLabel}`)} or the transaction will fail.
{t("itWillCost")} {b(`${+maxInput} ${inputLabel}`)} {t("orTransFail")}
</div>
</div>
);
@ -645,7 +648,7 @@ class Swap extends Component {
}
renderExchangeRate() {
const { account, selectors } = this.props;
const { t, account, selectors } = this.props;
const { exchangeRate, inputCurrency, outputCurrency } = this.state;
const { label: inputLabel } = selectors().getBalance(account, inputCurrency);
const { label: outputLabel } = selectors().getBalance(account, outputCurrency);
@ -654,7 +657,7 @@ class Swap extends Component {
return (
<OversizedPanel hideBottom>
<div className="swap__exchange-rate-wrapper">
<span className="swap__exchange-rate">Exchange Rate</span>
<span className="swap__exchange-rate">{t("exchangeRate")}</span>
<span> - </span>
</div>
</OversizedPanel>
@ -664,7 +667,7 @@ class Swap extends Component {
return (
<OversizedPanel hideBottom>
<div className="swap__exchange-rate-wrapper">
<span className="swap__exchange-rate">Exchange Rate</span>
<span className="swap__exchange-rate">{t("exchangeRate")}</span>
<span>
{`1 ${inputLabel} = ${exchangeRate.toFixed(7)} ${outputLabel}`}
</span>
@ -678,11 +681,12 @@ class Swap extends Component {
return '';
}
return `Balance: ${balance.dividedBy(BN(10 ** decimals)).toFixed(4)}`
const balanceInput = balance.dividedBy(BN(10 ** decimals)).toFixed(4)
return this.props.t("balance", { balanceInput })
}
render() {
const { selectors, account } = this.props;
const { t, selectors, account } = this.props;
const {
lastEditedField,
inputCurrency,
@ -690,7 +694,7 @@ class Swap extends Component {
inputValue,
outputValue,
} = this.state;
const estimatedText = '(estimated)';
const estimatedText = `(${t("estimated")})`;
const { value: inputBalance, decimals: inputDecimals } = selectors().getBalance(account, inputCurrency);
const { value: outputBalance, decimals: outputDecimals } = selectors().getBalance(account, outputCurrency);
@ -715,7 +719,7 @@ class Swap extends Component {
})}
/>
<CurrencyInputPanel
title="Input"
title={t("input")}
description={lastEditedField === OUTPUT ? estimatedText : ''}
extraText={this.renderBalance(inputCurrency, inputBalance, inputDecimals)}
onCurrencySelected={inputCurrency => this.setState({ inputCurrency }, this.recalcForm)}
@ -731,7 +735,7 @@ class Swap extends Component {
</div>
</OversizedPanel>
<CurrencyInputPanel
title="Output"
title={t("output")}
description={lastEditedField === INPUT ? estimatedText : ''}
extraText={this.renderBalance(outputCurrency, outputBalance, outputDecimals)}
onCurrencySelected={outputCurrency => this.setState({ outputCurrency }, this.recalcForm)}
@ -752,7 +756,7 @@ class Swap extends Component {
disabled={!isValid}
onClick={this.onSwap}
>
Swap
{t("swap")}
</button>
</div>
</div>
@ -773,7 +777,7 @@ export default connect(
selectors: () => dispatch(selectors()),
addPendingTx: id => dispatch(addPendingTx(id)),
}),
)(Swap);
)(withNamespaces()(Swap));
const b = text => <span className="swap__highlight-text">{text}</span>;

@ -751,6 +751,13 @@
dependencies:
regenerator-runtime "^0.12.0"
"@babel/runtime@^7.1.2":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.2.0.tgz#b03e42eeddf5898e00646e4c840fa07ba8dcad7f"
integrity sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==
dependencies:
regenerator-runtime "^0.12.0"
"@babel/template@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "http://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
@ -2994,6 +3001,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
create-react-context@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3"
integrity sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==
dependencies:
fbjs "^0.8.0"
gud "^1.0.0"
cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@ -4835,6 +4850,19 @@ fb-watchman@^2.0.0:
dependencies:
bser "^2.0.0"
fbjs@^0.8.0:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
fbjs@^0.8.16:
version "0.8.16"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
@ -5522,6 +5550,11 @@ growly@^1.3.0:
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
gud@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
gzip-size@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80"
@ -5767,6 +5800,13 @@ hoek@4.x.x:
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
integrity sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==
hoist-non-react-statics@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.0.1.tgz#fba3e7df0210eb9447757ca1a7cb607162f0a364"
integrity sha512-1kXwPsOi0OGQIZNVMPvgWJ9tSnGMiMfJdihqEzrPEXlHOBh9AAHXX/QYmAJTXztnz/K+PQ8ryCb4eGaN6HlGbQ==
dependencies:
react-is "^16.3.2"
hoist-non-react-statics@^2.5.0:
version "2.5.5"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
@ -5853,6 +5893,13 @@ html-minifier@^3.2.3:
relateurl "0.2.x"
uglify-js "3.3.x"
html-parse-stringify2@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a"
integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=
dependencies:
void-elements "^2.0.1"
html-webpack-plugin@4.0.0-alpha.2:
version "4.0.0-alpha.2"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-alpha.2.tgz#7745967e389a57a098e26963f328ebe4c19b598d"
@ -5966,6 +6013,21 @@ hyphenate-style-name@^1.0.0:
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b"
integrity sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=
i18next-browser-languagedetector@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-2.2.4.tgz#b02412d7ab15d7d74e1b1317d67d8a244b219ee3"
integrity sha512-wPbtH18FdOuB245I8Bhma5/XSDdN/HpYlX+wga1eMy+slhaFQSnrWX6fp+aYSL2eEuj0RlfHeEVz6Fo/lxAj6A==
i18next-xhr-backend@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/i18next-xhr-backend/-/i18next-xhr-backend-1.5.1.tgz#50282610780c6a696d880dfa7f4ac1d01e8c3ad5"
integrity sha512-9OLdC/9YxDvTFcgsH5t2BHCODHEotHCa6h7Ly0EUlUC7Y2GS09UeoHOGj3gWKQ3HCqXz8NlH4gOrK3NNc9vPuw==
i18next@^13.0.1:
version "13.0.1"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-13.0.1.tgz#aac758333e01a712710a81447bf033e6a0dbb71c"
integrity sha512-V9hnqoP7N7aHncb1FYvHcAgKiDTmVpNuIr70QGFTyyNUVlQwz2O393fM0x7JIm0/ZYsoKjZdwtlGKYO4aYJ79Q==
iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -10374,6 +10436,16 @@ react-helmet@^5.2.0:
prop-types "^15.5.4"
react-side-effect "^1.1.0"
react-i18next@^8.4.0:
version "8.4.0"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-8.4.0.tgz#e9f0c5b9938b155eaa6051360614719c08cae72f"
integrity sha512-zIO/bc1L0UUGdaws2y40cTiSQHuQud5e9SSodYM6MTzhJTI3iayxCCdgvotblOLM4taOD77Ct2/fUbheQIhyeg==
dependencies:
"@babel/runtime" "^7.1.2"
create-react-context "0.2.3"
hoist-non-react-statics "3.0.1"
html-parse-stringify2 "2.0.1"
react-input-autosize@^2.1.2:
version "2.2.1"
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.1.tgz#ec428fa15b1592994fb5f9aa15bb1eb6baf420f8"
@ -10381,6 +10453,11 @@ react-input-autosize@^2.1.2:
dependencies:
prop-types "^15.5.8"
react-is@^16.3.2:
version "16.7.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.7.0.tgz#c1bd21c64f1f1364c6f70695ec02d69392f41bfa"
integrity sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g==
react-motion@^0.5.0:
version "0.5.2"
resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316"
@ -12964,6 +13041,11 @@ vm-browserify@0.0.4:
dependencies:
indexof "0.0.1"
void-elements@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
w3c-hr-time@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045"