Add i18n translation (#85)

* Fix redux store configuration

* Setup i18n

* Add welcome page french and english translation

* Rename languages files

* Add optimized translation

* Add Ethereum network error message translation

* Add top navbar translation

* Add constants translation

* Add move domain translation

* Add translation to add domain

* Add name lookup translation

* Add display box translation

* Add edit options translation

* Add register sub domain translation

* Add release domain translation

* Add setup ens translation

* Add update controller translation

* Add test token translation

* Add erc20 token translation

* Add ens sub management translation

* Add admin mode translation

* Add terms translation
This commit is contained in:
Pierrick TURELIER 2018-12-03 19:37:03 +01:00 committed by Ricardo Guilherme Schmidt
parent 6dc98d1db9
commit 8d4d00396e
27 changed files with 661 additions and 311 deletions

View File

@ -4,6 +4,7 @@
"react"
],
"rules": {
"react/jsx-filename-extension": 0,
"react/prop-types": 0
}
}

View File

@ -1,3 +1,4 @@
import lang from 'i18n-js';
import React, { Fragment } from 'react';
import { Tabs, Tab } from 'react-bootstrap';
import TopNavbar from './topnavbar';
@ -11,16 +12,16 @@ const AdminMode = () => (
<Fragment>
<TopNavbar />
<Tabs defaultActiveKey={1} id="uncontrolled-tab-example">
<Tab eventKey={1} title="TestToken">
<Tab eventKey={1} title={lang.t('admin.tab.test_token')}>
<TestTokenUI />
</Tab>
<Tab eventKey={2} title="ERC20Token">
<Tab eventKey={2} title={lang.t('admin.tab.erc20_token')}>
<ERC20TokenUI />
</Tab>
<Tab eventKey={3} title="ENS Management">
<Tab eventKey={3} title={lang.t('admin.tab.ens_management')}>
<ENSSubManagement />
</Tab>
<Tab eventKey={4} title="Name Lookup">
<Tab eventKey={4} title={lang.t('admin.tab.name_lookup')}>
<NameLookup />
</Tab>
</Tabs>

View File

@ -1,6 +1,7 @@
import lang from 'i18n-js';
import React from 'react';
import Typography from '@material-ui/core/Typography';
import styled from "styled-components";
import styled from 'styled-components';
const DisplayLabel = styled.div`
font-size: 14px;
@ -29,7 +30,9 @@ const BlueBox = styled.div`
color: #4360df;
`;
const DisplayBox = ({ displayType, text, onClick, showBlueBox }) => (
const DisplayBox = ({
displayType, text, onClick, showBlueBox,
}) => (
<div>
<DisplayLabel>
{displayType}
@ -37,14 +40,12 @@ const DisplayBox = ({ displayType, text, onClick, showBlueBox }) => (
<DisplayBoxDiv showBlueBox={showBlueBox} onClick={onClick}>
<InnerDisplayBox>
{
showBlueBox && onClick ?
<BlueBox>
Grant access
</BlueBox>
showBlueBox && onClick ?
<BlueBox>{lang.t('action.grant_access')}</BlueBox>
:
<Typography type='body1'>
{text}
</Typography>
<Typography type="body1">
{text}
</Typography>
}
</InnerDisplayBox>
</DisplayBoxDiv>

View File

@ -1,3 +1,4 @@
import lang from 'i18n-js';
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
@ -47,13 +48,13 @@ class SimpleDialog extends React.Component {
<ListItemIcon>
<EditIcon />
</ListItemIcon>
<ListItemText primary="Edit Contact Code" />
<ListItemText primary={lang.t('action.edit_contact_code')} />
</ListItem>
{canBeReleased && <ListItem button onClick={() => this.handleListItemClick('release')}>
<ListItemIcon>
<DeleteOutline />
</ListItemIcon>
<ListItemText primary="Release Name" />
<ListItemText primary={lang.t('action.release_name')} />
</ListItem>}
</List>
</Dialog>
@ -69,4 +70,3 @@ SimpleDialog.propTypes = {
const SimpleDialogWrapped = withStyles(styles)(SimpleDialog);
export default SimpleDialogWrapped;

View File

@ -1,3 +1,4 @@
import lang from 'i18n-js';
import React from 'react';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
@ -7,37 +8,36 @@ import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Slide from '@material-ui/core/Slide';
import classNames from "classnames";
import classNames from 'classnames';
function Transition(props) {
return <Slide direction="up" {...props} />;
}
const styles = theme => ({
dialog: {
textAlign: 'center',
},
actions: {
background: 'rgba(255, 255, 255, 0.8)',
margin: 0,
borderTop: 'solid 1px #ccc',
},
button: {
margin: '0',
fontSize: '17px',
color: '#007AFF',
width: '50%',
borderRight: 'solid 1px #ccc',
borderRadius: 0,
padding: '15px',
}
const styles = () => ({
dialog: {
textAlign: 'center',
},
actions: {
background: 'rgba(255, 255, 255, 0.8)',
margin: 0,
borderTop: 'solid 1px #ccc',
},
button: {
margin: '0',
fontSize: '17px',
color: '#007AFF',
width: '50%',
borderRight: 'solid 1px #ccc',
borderRadius: 0,
padding: '15px',
},
});
const ReleaseDomainAlert = ({ classes, open, handleClose }) => (
<div>
<Dialog
className={classNames(classes.dialog)}
className={classNames(classes.dialog)}
open={open}
TransitionComponent={Transition}
keepMounted
@ -46,19 +46,19 @@ const ReleaseDomainAlert = ({ classes, open, handleClose }) => (
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle id="alert-dialog-slide-title">
{"Release domain?"}
{lang.t('domain.release.alert.title')}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-slide-description">
Your SNT deposit will be returned and name will be available to other users.
{lang.t('domain.release.alert.text')}
</DialogContentText>
</DialogContent>
<DialogActions className={classNames(classes.actions)}>
<Button onClick={() => handleClose(null)} className={classNames(classes.button)} color="primary">
<strong>Cancel</strong>
<strong>{lang.t('action.cancel')}</strong>
</Button>
<Button onClick={() => handleClose(true)} className={classNames(classes.button)} color="primary">
Yes
{lang.t('action.yes')}
</Button>
</DialogActions>
</Dialog>
@ -66,4 +66,4 @@ const ReleaseDomainAlert = ({ classes, open, handleClose }) => (
);
export default withStyles(styles)(ReleaseDomainAlert);
export default withStyles(styles)(ReleaseDomainAlert);

View File

@ -1,3 +1,4 @@
import lang from 'i18n-js';
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import web3 from 'web3';
import ENSRegistry from 'Embark/contracts/ENSRegistry';
@ -36,7 +37,7 @@ const InnerForm = ({
id="registryName"
name="registryName"
type="text"
label="Registry Name"
label={lang.t('registry.name.label')}
onChange={handleChange}
onBlur={handleBlur}
value={values.registryName}
@ -46,13 +47,13 @@ const InnerForm = ({
id="registryPrice"
name="registryPrice"
type="number"
label="Registry Price"
placeholder="(Optional) Registry will be free if left blank"
label={lang.t('registry.price.label')}
placeholder={lang.t('registry.price.placeholder')}
onChange={handleChange}
onBlur={handleBlur}
value={values.registryPrice}
/>
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button>
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? lang.t('action.submit') : lang.t('action.submitting_to_blockchain')}</Button>
</form>
)
@ -61,8 +62,8 @@ const AddRegistry = withFormik({
async validate(values) {
const { registryName } = values;
const errors = {};
if (!registryName) errors.registryName = 'Required';
if (registryName && !await getAndIsOwner(registryName)) errors.registryName = 'This registry is not owned by registry';
if (!registryName) errors.registryName = lang.t('registry.error.name.required');
if (registryName && !await getAndIsOwner(registryName)) errors.registryName = lang.t('registry.error.name.owned');
if (Object.keys(errors).length) throw errors;
},
async handleSubmit(values, { setSubmitting }) {

View File

@ -1,2 +1,4 @@
export const YOUR_CONTACT_CODE = 'Your contact code';
export const YOUR_WALLET_ADDRESS = 'Your wallet address';
import lang from 'i18n-js';
export const YOUR_CONTACT_CODE = lang.t('constants.contact_code');
export const YOUR_WALLET_ADDRESS = lang.t('constants.wallet_address');

View File

@ -1,23 +0,0 @@
export default {
release: {
title: {
sub: 'Done!',
body: 'The released username will be available to other users'
},
subheading: 'Follow the progress in the Transaction History section of your wallet.'
},
registered: {
title: {
sub: 'Nice!',
body: 'The name is yours once the transaction is complete'
},
subheading: 'Follow the progress in the Transaction History section of your wallet.'
},
edit: {
title: {
sub: 'Done!',
body: 'Your changes will be saved when the transaction is complete'
},
subheading: 'Follow the progress in the Transaction History section of your wallet.'
}
}

View File

@ -1,3 +1,4 @@
import lang from 'i18n-js';
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import web3 from 'web3';
import React from 'react';
@ -18,13 +19,13 @@ const InnerForm = ({
id="newAddress"
name="newAddress"
type="text"
label="New Controller Address"
label={lang.t('domain.new_address.label')}
onChange={handleChange}
onBlur={handleBlur}
value={values.newAddress}
error={errors.newAddress}
/>
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button>
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? lang.t('action.submit') : lang.t('action.submitting_to_blockchain')}</Button>
</form>
)
@ -34,7 +35,7 @@ const MoveDomain = withFormik({
const { utils: { isAddress } } = web3;
const { newAddress } = values;
const errors = {};
if (!isAddress(newAddress)) errors.newAddress = 'Please enter a valid address'
if (!isAddress(newAddress)) errors.newAddress = lang.t('error.valid_address')
if (Object.keys(errors).length) throw errors;
},
async handleSubmit(values, { setSubmitting }) {

View File

@ -1,3 +1,4 @@
import lang from 'i18n-js';
import React, { Fragment, PureComponent } from 'react';
import web3 from 'web3';
import EmbarkJS from 'Embark/EmbarkJS';
@ -27,7 +28,6 @@ import Warning from '../../ui/components/Warning';
const { getPrice, getExpirationTime, getCreationTime, release } = UsernameRegistrar.methods;
import NotInterested from '@material-ui/icons/NotInterested';
import Face from '@material-ui/icons/Face';
import Copy from './copy';
import IDNANormalizer from 'idna-normalize';
import { nullAddress, getResolver } from './utils/domain';
import { YOUR_CONTACT_CODE } from './constants';
@ -208,11 +208,11 @@ const RegisterInfoCard = ({ formattedDomain, domainPrice, registryOwnsDomain })
const TransactionComplete = ({ type, setStatus }) => (
<div style={{ textAlign: 'center', margin: '40% 15 10' }}>
<Typography variant="title" style={{ marginBottom: '1rem' }}>
{Copy[type]['title']['sub']}<br/>
{Copy[type]['title']['body']}
{lang.t(`copy.${type}.title.sub`)}<br/>
{lang.t(`copy.${type}.title.body`)}
</Typography>
<Typography variant="subheading" style={{ color: '#939BA1' }}>
{Copy[type]['subheading']}
{lang.t('copy.subheading')}
</Typography>
<MobileButton text="Main Page" style={{ marginTop: '12rem' }} onClick={() => { setStatus(null) } } />
</div>

View File

@ -1,3 +1,4 @@
import lang from 'i18n-js';
import web3 from "Embark/web3"
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import TestToken from 'Embark/contracts/TestToken';
@ -63,7 +64,7 @@ class InnerForm extends React.Component {
id="subDomain"
name="subDomain"
type="text"
label="Sub Domain"
label={lang.t('sub_domain.label')}
onChange={handleChange}
onBlur={handleBlur}
value={values.subDomain}
@ -74,7 +75,7 @@ class InnerForm extends React.Component {
id="domainName"
name="domainName"
type="text"
label="Domain Name"
label={lang.t('domain.name.label')}
onChange={handleChange}
onBlur={handleBlur}
value={values.domainName}
@ -97,7 +98,7 @@ class InnerForm extends React.Component {
<FieldGroup
id="price"
name="price"
label="Domain Price"
label={lang.t('domain.price.label')}
disabled
value={values.price ? `${formatPrice(values.price)} SNT` : ''}/>}
<Hidden mdDown>
@ -105,7 +106,7 @@ class InnerForm extends React.Component {
id="statusAddress"
name="statusAddress"
type="text"
label="Status messenger address domain resolves to"
label={lang.t('domain.status_address.label')}
onChange={handleChange}
onBlur={handleBlur}
value={values.statusAddress}
@ -116,15 +117,20 @@ class InnerForm extends React.Component {
id="address"
name="address"
type="text"
label="Ethereum address domain resolves to"
label={lang.t('domain.ethereum_address.label')}
onChange={handleChange}
onBlur={handleBlur}
value={values.address}
error={errors.address}
button={<Button mode="strong"
style={{padding: '5px 15px 5px 15px', marginTop: '5px'}}
onClick={() => setFieldValue('address', web3.eth.defaultAccount)}>Use My Primary
Address</Button>}
button={
<Button
mode="strong"
style={{padding: '5px 15px 5px 15px', marginTop: '5px'}}
onClick={() => setFieldValue('address', web3.eth.defaultAccount)}
>
{lang.t('action.use_my_primary_address')}
</Button>
}
/>
{!isSubmitting ? <Button wide mode="strong" type="submit"
disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button> :
@ -143,22 +149,22 @@ class InnerForm extends React.Component {
onClick={() => requestStatusContactCode()}/>
</Fragment> :
<Fragment>
<Field label="Your Wallet Address">
<Field label={YOUR_WALLET_ADDRESS}>
<MobileSearch
name="address"
style={{ marginTop: '10px' }}
placeholder="Your wallet address"
placeholder={YOUR_WALLET_ADDRESS}
value={values.address}
onChange={handleChange}
onClick={() => setFieldValue('address', '')}
required
wide />
</Field>
<Field label="Your contact code">
<Field label={YOUR_CONTACT_CODE}>
<MobileSearch
name="statusAddress"
style={{ marginTop: '10px' }}
placeholder="Enter Your Status Messenger Address Here"
placeholder={lang.t('domain.status_address.placeholder')}
value={values.statusAddress}
onChange={handleChange}
onClick={() => setFieldValue('statusAddress', '')}
@ -172,7 +178,7 @@ class InnerForm extends React.Component {
this.onRegisterClick(this.props);
}}>
<div>
{`${editAccount ? 'Update' : 'Register'}`}
{`${editAccount ? lang.t('action.update') : lang.t('action.register')}`}
</div>
</ArrowButton>
:
@ -203,8 +209,8 @@ const RegisterSubDomain = withFormik({
const errors = {};
const { address } = values;
const { subDomain } = props || values;
if (address && !web3.utils.isAddress(address)) errors.address = 'Not a valid address';
if (!subDomain) errors.subDomain = 'Required';
if (address && !web3.utils.isAddress(address)) errors.address = lang.t('domain.ethereum_address.error');
if (!subDomain) errors.subDomain = lang.t('sub_domain.error.required');
return errors;
},
async handleSubmit(values, { setSubmitting, props }) {

View File

@ -1,10 +1,11 @@
import lang from 'i18n-js';
import React, { Fragment } from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap';
import { Button } from 'react-bootstrap';
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import web3Utils from 'web3-utils'
import { hash } from 'eth-ens-namehash'
const zeroBytes32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
const zeroBytes32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
const getUserAddress = contract => contract._provider.publicConfigStore._state.selectedAddress;
const dispatchSetup = (ENSRegistry) => {
const { methods: { setSubnodeOwner } } = ENSRegistry;
@ -21,8 +22,13 @@ const dispatchSetup = (ENSRegistry) => {
}
const SetupEns = ({ ENSRegistry }) => (
<Fragment>
<Button bsStyle="primary" onClick={() => dispatchSetup(ENSRegistry)}>ADD INITIAL NODES TO ENS</Button>
<Button
bsStyle="primary"
onClick={() => dispatchSetup(ENSRegistry)}
>
{lang.t('action.setup_ens')}
</Button>
</Fragment>
)
);
export default SetupEns;

View File

@ -1,7 +1,8 @@
import lang from 'i18n-js';
import styled from 'styled-components';
import React from 'react';
import Dialog from '@material-ui/core/Dialog';
import {ArrowButton} from '../../ui/components';
import styled from "styled-components";
import { ArrowButton } from '../../ui/components';
const TermsContainer = styled.div`
word-wrap: break-word;
@ -36,31 +37,34 @@ const ListContainer = styled.ul`
const Terms = ({ open, onSubmit }) => (
<Dialog fullScreen open={open}>
<TermsContainer>
<InfoHeading className="ens-terms__title">Terms of name registration</InfoHeading>
<InfoHeading className="ens-terms__title">{lang.t('terms.title')}</InfoHeading>
<TermsDescription>
<ListContainer>
<li>Funds are deposited for 1 year. Your SNT will be locked, but not spent.</li>
<li>After 1 year, you can release the name and get your deposit back. The name is yours until you release it.</li>
<li>Names are created as a subdomain of <i>stateofus.eth</i>. They are property of Status and may be subject to new terms.</li>
<li>If the <i>stateofus.eth</i> contract terms changee.g. Status makes contract upgradesyou have the right to get your deposit back, even for names held less than 1 year.</li>
<li> Names may not:
<li>{lang.t('terms.funds_deposit')}</li>
<li>{lang.t('terms.funds_release')}</li>
<li>{lang.t('terms.names_creation', { stateofus: <i>stateofus.eth</i> })}</li>
<li>{lang.t('terms.contract', { stateofus: <i>stateofus.eth</i> })}</li>
<li>{lang.t('terms.rule.title')}
<ol type="1">
<li>contain less than 4 characters;</li>
<li>n non-alphanumeric characters;</li>
<li>contain uppercase letters;</li>
<li>appear on this <a href="https://github.com/status-im/ens-usernames/blob/master/config/ens-usernames/reservedNames.js">reserved list</a></li>
<li>mimic an Ethereum address (start with <code>Ox</code> and contain only hexadecimal characters in the first 12 digits)</li>
<li>{lang.t('terms.rule.one')}</li>
<li>{lang.t('terms.rule.two')}</li>
<li>{lang.t('terms.rule.three')}</li>
<li>{lang.t('terms.rule.four', { reserved_list_link: <a href="https://github.com/status-im/ens-usernames/blob/master/config/ens-usernames/reservedNames.js">{lang.t('terms.reserved_list')}</a> })}</li>
<li>{lang.t('terms.rule.five', { eth_address: <code>Ox</code> })}</li>
</ol>
</li>
<li>Registering an illegal name via the registry contract will result in the loss of your SNT deposit and removal of the name.</li>
<li>Contact codes and wallet addresses associated with your name are publicly available information.</li>
<li>{lang.t('terms.illegal_name')}</li>
<li>{lang.t('terms.contact')}</li>
</ListContainer>
</TermsDescription>
<div style={{display: 'flex', flexDirection: 'row-reverse', marginBottom: '16px', marginRight: '8px'}}>
<div
style={{
display: 'flex', flexDirection: 'row-reverse', marginBottom: '16px', marginRight: '8px',
}}
>
<ArrowButton type="submit" onClick={onSubmit}>
<div>Send SNT</div>
<div>{lang.t('action.send_snt')}</div>
</ArrowButton>
</div>

View File

@ -1,3 +1,4 @@
import lang from 'i18n-js';
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import web3 from 'web3';
import React from 'react';
@ -18,23 +19,29 @@ const InnerForm = ({
id="newAddress"
name="newAddress"
type="text"
label="New Controller Address"
label={lang.t('domain.new_address.label')}
onChange={handleChange}
onBlur={handleBlur}
value={values.newAddress}
error={errors.newAddress}
/>
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button>
<Button
bsStyle="primary"
type="submit"
disabled={isSubmitting || !!Object.keys(errors).length}
>
{!isSubmitting ? lang.t('action.submit') : lang.t('action.submitting_to_blockchain')}
</Button>
</form>
)
);
const UpdateController = withFormik({
mapPropsToValues: props => ({ newAddress: '' }),
mapPropsToValues: () => ({ newAddress: '' }),
async validate(values) {
const { utils: { isAddress } } = web3;
const { newAddress } = values;
const errors = {};
if (!isAddress(newAddress)) errors.newAddress = 'Please enter a valid address'
if (!isAddress(newAddress)) errors.newAddress = lang.t('error.valid_address');
if (Object.keys(errors).length) throw errors;
},
async handleSubmit(values, { setSubmitting }) {
@ -49,8 +56,8 @@ const UpdateController = withFormik({
.catch((err) => {
setSubmitting(false);
console.log(err);
})
}
});
},
})(InnerForm);
export default UpdateController;

View File

@ -1,33 +1,36 @@
import lang from 'i18n-js';
import React from 'react';
import {StyledButton} from '../../ui/components';
import { Link } from 'react-router-dom';
import { StyledButton } from '../../ui/components';
import StatusCards from '../../ui/icons/svg/intro_name.svg';
import {Link} from "react-router-dom";
import './welcome.css';
const WelcomeContent = () => (
<div>
<div className="ens-welcome__guide"><span>How it works</span></div>
<div className="ens-welcome__guide"><span>{lang.t('welcome.how')}</span></div>
<ol className="ens-welcome__list">
<li className="item">
<div className="title">Simplify your ETH address</div>
<div className="text">Your complex wallet address (0x...) becomes an easy to read, remember & share URL: <span
className="ens-welcome__highlight">myname.stateofus.eth</span></div>
</li>
<li className="item">
<div className="title">10 SNT to register</div>
<div className="text">Register once to keep the name forever. After 1 year, you can release the name and get
your SNT back.
<div className="title">{lang.t('welcome.step.one.title')}</div>
<div className="text">
{lang.t('welcome.step.one.subtitle')}{' '}
<span className="ens-welcome__highlight">{lang.t('welcome.eth_address_example')}</span>
</div>
</li>
<li className="item">
<div className="title">Connect & get paid</div>
<div className="text">Share your name to chat on Status or receive ETH and tokens.</div>
<div className="title">{lang.t('welcome.step.two.title')}</div>
<div className="text">{lang.t('welcome.step.two.subtitle')}
</div>
</li>
<li className="item">
<div className="title">{lang.t('welcome.step.three.title')}</div>
<div className="text">{lang.t('welcome.step.three.subtitle')}</div>
</li>
</ol>
<div className="text-light">
<small>Powered by Ethereum Name Services</small>
<small>{lang.t('welcome.disclaimer')}</small>
</div>
</div>
);
@ -35,15 +38,13 @@ const WelcomeContent = () => (
const Welcome = () => (
<div className="ens-welcome">
<img className="ens-welcome__img" src={StatusCards} />
<h2 className="ens-welcome__title">
ENS names transform those crazy-long addresses into unique usernames
</h2>
<h2 className="ens-welcome__title">{lang.t('welcome.title')}</h2>
<Link to="/search">
<StyledButton>
<div>Let's Go</div>
<div>{lang.t('welcome.cta')}</div>
</StyledButton>
</Link>
<WelcomeContent/>
<WelcomeContent />
</div>
);

View File

@ -1,9 +1,8 @@
import EmbarkJS from 'Embark/EmbarkJS';
import lang from 'i18n-js';
import ENSRegistry from 'Embark/contracts/ENSRegistry';
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import TestToken from 'Embark/contracts/TestToken';
import React, { Fragment } from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap';
import AddDomain from './ens/addDomain';
import MoveDomain from './ens/moveDomain';
import RegisterSubDomain from './ens/registerSubDomain';
@ -11,34 +10,27 @@ import TokenPermissions from './standard/TokenPermission';
import SetupENS from './ens/setupENS';
import UpdateController from './ens/updateController';
const FieldGroup = ({ id, label, help, ...props }) => (
<FormGroup controlId={id}>
<ControlLabel>{label}</ControlLabel>
<FormControl {...props} />
{help && <HelpBlock>{help}</HelpBlock>}
</FormGroup>
)
const ENSSubManagement = props => (
const ENSSubManagement = () => (
<Fragment>
<h2 style={{ textAlign: 'center' }}>Subdomain Management</h2>
<h3>Change Registry Controller</h3>
<h2 style={{ textAlign: 'center' }}>{lang.t('sub_domain.management.title')}</h2>
<h3>{lang.t('sub_domain.management.change_registry')}</h3>
<UpdateController />
<h3>Activate Registry/Update Registry Price</h3>
<h3>{lang.t('sub_domain.management.activate_registry')}</h3>
<AddDomain />
<h3>Move Domain To Another Registry</h3>
<h3>{lang.t('sub_domain.management.move_domain')}</h3>
<MoveDomain />
<hr/>
<h3>Register Sub-Domain</h3>
<hr />
<h3>{lang.t('sub_domain.management.register_sub_domain')}</h3>
<RegisterSubDomain />
<hr/>
<hr />
<TokenPermissions
symbol='SNT'
symbol="SNT"
spender={UsernameRegistrar._address}
methods={TestToken.methods} />
<hr/>
methods={TestToken.methods}
/>
<hr />
<SetupENS ENSRegistry={ENSRegistry} />
</Fragment>
)
);
export default ENSSubManagement;

View File

@ -1,128 +1,128 @@
import lang from 'i18n-js';
import EmbarkJS from 'Embark/EmbarkJS';
import ERC20Token from 'Embark/contracts/ERC20Token';
import React from 'react';
import { connect } from 'react-redux';
import { Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap';
import { getCurrentAccount, accountsIsLoading } from '../reducers/accounts';
class ERC20TokenUI extends React.Component {
constructor(props) {
super(props);
ERC20Token.options.address = props.address;
this.state = {
balanceOf: 0,
transferTo: '',
transferAmount: 0,
accountB: web3.eth.defaultAccount,
};
}
constructor(props) {
super(props);
ERC20Token.options.address = props.address;
this.state = {
balanceOf: 0,
transferTo: "",
transferAmount: 0,
accountBalance: 0,
accountB: web3.eth.defaultAccount,
}
}
update_transferTo(e){
this.setState({transferTo: e.target.value});
}
update_transferTo(e) {
this.setState({ transferTo: e.target.value });
}
update_transferAmount(e){
this.setState({transferAmount: e.target.value});
}
update_transferAmount(e) {
this.setState({ transferAmount: e.target.value });
}
transfer(e){
var to = this.state.transferTo;
var amount = this.state.transferAmount;
var tx = ERC20Token.methods.transfer(to, amount).send({from: web3.eth.defaultAccount});
this._addToLog(ERC20Token.options.address+".transfer(" + to + ", "+amount+")");
}
transfer(e){
var to = this.state.transferTo;
var amount = this.state.transferAmount;
var tx = ERC20Token.methods.transfer(to, amount).send({from: web3.eth.defaultAccount});
this._addToLog(ERC20Token.options.address+".transfer(" + to + ", "+amount+")");
}
approve(e){
var to = this.state.transferTo;
var amount = this.state.transferAmount;
var tx = ERC20Token.methods.approve(to, amount).send({from: web3.eth.defaultAccount});
this._addToLog(ERC20Token.options.address+".approve(" + to + ", "+amount+")");
}
approve(e){
var to = this.state.transferTo;
var amount = this.state.transferAmount;
var tx = ERC20Token.methods.approve(to, amount).send({from: web3.eth.defaultAccount});
this._addToLog(ERC20Token.options.address+".approve(" + to + ", "+amount+")");
}
balanceOf(e){
e.preventDefault();
var who = e.target.value;
if (EmbarkJS.isNewWeb3()) {
ERC20Token.methods.balanceOf(who).call()
.then(_value => this.setState({balanceOf: _value}))
} else {
ERC20Token.balanceOf(who)
.then(_value => this.x({balanceOf: _value}));
}
this._addToLog(ERC20Token.options.address+".balanceOf(" + who + ")");
}
getDefaultAccountBalance(){
if (EmbarkJS.isNewWeb3()) {
ERC20Token.methods.balanceOf(web3.eth.defaultAccount).call()
.then(_value => this.setState({accountBalance: _value}))
} else {
ERC20Token.balanceOf(web3.eth.defaultAccount)
.then(_value => this.x({valueGet: _value}))
}
this._addToLog(ERC20Token.options.address + ".balanceOf(" + web3.eth.defaultAccount + ")");
balanceOf(e){
e.preventDefault();
var who = e.target.value;
if (EmbarkJS.isNewWeb3()) {
ERC20Token.methods.balanceOf(who).call()
.then(_value => this.setState({balanceOf: _value}))
} else {
ERC20Token.balanceOf(who)
.then(_value => this.x({balanceOf: _value}));
}
this._addToLog(ERC20Token.options.address+".balanceOf(" + who + ")");
}
_addToLog(txt){
console.log(txt);
getDefaultAccountBalance(){
if (EmbarkJS.isNewWeb3()) {
ERC20Token.methods.balanceOf(web3.eth.defaultAccount).call()
.then(_value => this.setState({accountBalance: _value}))
} else {
ERC20Token.balanceOf(web3.eth.defaultAccount)
.then(_value => this.x({valueGet: _value}))
}
this._addToLog(ERC20Token.options.address + ".balanceOf(" + web3.eth.defaultAccount + ")");
}
_addToLog(txt){
console.log(txt);
}
render() {
const { account, isLoading } = this.props;
return (
<React.Fragment>
<h3> Read your account token balance </h3>
<h3>{lang.t('account.read_your_token_balance')}</h3>
<Form inline>
<FormGroup>
{!isLoading && <HelpBlock>Your test token balance is <span className="accountBalance">{account.SNTBalance}</span></HelpBlock>}
{!isLoading && <HelpBlock>{lang.t('account.your_test_token_balance')} <span className="accountBalance">{account.SNTBalance}</span></HelpBlock>}
</FormGroup>
</Form>
<h3> Read account token balance</h3>
<h3>{lang.t('account.read_token_balance')}</h3>
<Form inline>
<FormGroup>
<label>
Of:
{lang.t('account.of')}{':'}
<FormControl
type="text"
defaultValue={this.state.accountB}
onChange={(e) => this.balanceOf(e)} />
onChange={e => this.balanceOf(e)}
/>
</label>
<label>
<HelpBlock><span className="balanceOf">{this.state.balanceOf}</span></HelpBlock>
</label>
</FormGroup>
</Form>
<h3> Transfer/Approve token balance</h3>
<h3>{lang.t('account.transfer_approve_token_balance')}</h3>
<Form inline>
<FormGroup>
<label>
To:
{lang.t('account.to')}{':'}
<FormControl
type="text"
defaultValue={this.state.transferTo}
onChange={(e) => this.update_transferTo(e) } />
onChange={e => this.update_transferTo(e)}
/>
</label>
<label>
Amount:
{lang.t('account.amount')}{':'}
<FormControl
type="text"
defaultValue={this.state.transferAmount}
onChange={(e) => this.update_transferAmount(e) } />
onChange={e => this.update_transferAmount(e)}
/>
</label>
<Button bsStyle="primary" onClick={(e) => this.transfer(e)}>Transfer</Button>
<Button bsStyle="primary" onClick={(e) => this.approve(e)}>Approve</Button>
<Button bsStyle="primary" onClick={e => this.transfer(e)}>{lang.t('action.transfer')}</Button>
<Button bsStyle="primary" onClick={e => this.approve(e)}>{lang.t('action.approve')}</Button>
</FormGroup>
</Form>
</React.Fragment>
);
}
}
}
const mapStateToProps = state => ({
account: getCurrentAccount(state),

View File

@ -1,3 +1,4 @@
import lang from 'i18n-js';
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
@ -32,7 +33,7 @@ function ButtonAppBar(props) {
<AppBar position="static">
<Toolbar>
<Typography variant="title" color="inherit" className={classes.flex}>
This site is optimized for Status. Get the App to enable all features and get the best experience.
{lang.t('optimized')}
</Typography>
<IconButton color="inherit" aria-label="iPhone" href="https://status.im/success.html" target="_blank">
<IPhone />

View File

@ -1,9 +1,13 @@
import lang from 'i18n-js';
import React, { Fragment } from 'react';
import Warning from '../../ui/components/Warning';
const Web3Render = ({ ready, children, network }) => (
<Fragment>
{ready ? <Fragment>{children}</Fragment> : <Warning>Please connect to Ethereum {network}<br />to continue.</Warning>}
{ready
? <Fragment>{children}</Fragment>
: <Warning>{lang.t('error.connect', { network })}</Warning>
}
</Fragment>
);

View File

@ -1,60 +1,62 @@
import lang from 'i18n-js';
import EmbarkJS from 'Embark/EmbarkJS';
import TestToken from 'Embark/contracts/TestToken';
import React from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap';
import ERC20TokenUI from './erc20token';
import { Form, FormGroup, FormControl, Button } from 'react-bootstrap';
import { connect } from 'react-redux';
import web3 from 'web3';
import ERC20TokenUI from './erc20token';
import { actions as accountActions } from '../reducers/accounts';
class TestTokenUI extends React.Component {
constructor(props) {
super(props);
this.state = {
amountToMint: 100,
}
}
handleMintAmountChange(e){
this.setState({amountToMint: e.target.value});
}
mint(e){
const { addToBalance } = this.props;
e.preventDefault();
var value = parseInt(this.state.amountToMint, 10);
if (EmbarkJS.isNewWeb3()) {
TestToken.methods.mint(value).send({from: web3.eth.defaultAccount})
.then(r => { addToBalance(value) });
} else {
TestToken.mint(value).send({from: web3.eth.defaultAccount})
.then(r => { addToBalance(value) });
}
console.log(TestToken.options.address +".mint("+value+").send({from: " + web3.eth.defaultAccount + "})");
}
render(){
return (<React.Fragment>
<h3> Mint Test Token</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.amountToMint}
onChange={(e) => this.handleMintAmountChange(e)} />
<Button bsStyle="primary" onClick={(e) => this.mint(e)}>Mint</Button>
</FormGroup>
</Form>
<ERC20TokenUI address={ TestToken.options.address } />
</React.Fragment>
);
}
constructor(props) {
super(props);
this.state = {
amountToMint: 100,
};
}
handleMintAmountChange(e) {
this.setState({ amountToMint: e.target.value });
}
mint(e) {
const { addToBalance } = this.props;
e.preventDefault();
const value = parseInt(this.state.amountToMint, 10);
if (EmbarkJS.isNewWeb3()) {
TestToken.methods.mint(value).send({ from: web3.eth.defaultAccount })
.then(() => { addToBalance(value); });
} else {
TestToken.mint(value).send({ from: web3.eth.defaultAccount })
.then(() => { addToBalance(value); });
}
console.log(TestToken.options.address +".mint("+value+").send({from: " + web3.eth.defaultAccount + "})");
}
render() {
return (
<React.Fragment>
<h3>{lang.t('action.mint_test_token')}</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.amountToMint}
onChange={e => this.handleMintAmountChange(e)}
/>
<Button bsStyle="primary" onClick={e => this.mint(e)}>{lang.t('action.mint')}</Button>
</FormGroup>
</Form>
<ERC20TokenUI address={TestToken.options.address} />
</React.Fragment>
);
}
}
const mapDispatchToProps = dispatch => ({
addToBalance(amount) {
dispatch(accountActions.addToSntTokenBalance(amount));

View File

@ -1,33 +1,29 @@
import EmbarkJS from 'Embark/EmbarkJS';
import lang from 'i18n-js';
import React from 'react';
import { Navbar, NavItem, Nav, MenuItem , NavDropdown} from 'react-bootstrap';
import AccountList from './accountList';
import { Navbar } from 'react-bootstrap';
import AccountList from './accountList';
class TopNavbar extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
constructor(props) {
super(props);
this.state = {
}
}
render(){
return (
render() {
return (
<React.Fragment>
<Navbar>
<Navbar>
<Navbar.Header>
<Navbar.Brand>
<a href="#home">Status.im Demo</a>
<a href="#home">{lang.t('navbar.brand')}</a>
</Navbar.Brand>
</Navbar.Header>
<AccountList classNameNavDropdown="pull-right" />
</Navbar>
</React.Fragment>
);
}
);
}
}
export default TopNavbar;
export default TopNavbar;

View File

@ -1,11 +1,20 @@
import lang from 'i18n-js';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import store from './store/configureStore';
import App from './dapp';
import init from './store/init'
import init from './store/init';
import translations from './languages';
import './dapp.css';
// Init i18n translation
lang.defaultLocale = 'en';
lang.locale = navigator.language;
lang.fallbacks = true;
lang.translations = translations;
// Init Redux store
init();
render(

163
app/languages/en.json Normal file
View File

@ -0,0 +1,163 @@
{
"account": {
"amount": "Amount",
"from": "From",
"of": "Of",
"read_token_balance": "Read account token balance",
"read_your_token_balance": "Read your account token balance",
"to": "To",
"transfer_approve_token_balance": "Transfer/Approve token balance",
"your_test_token_balance": "Your test token balance is"
},
"action" : {
"approve": "Approve",
"cancel": "Cancel",
"edit_contact_code": "Edit Contact Code",
"grant_access": "Grant access",
"mint_test_token": "Mint Test Token",
"mint": "Mint",
"register": "Register",
"release_name": "Release Name",
"send_snt": "Send SNT",
"setup_ens": "ADD INITIAL NODES TO ENS",
"submit": "Submit",
"submitting_to_blockchain": "Submitting to the Blockchain - (this may take awhile)",
"transfer": "Transfer",
"update": "Update",
"use_my_primary_address": "Use My Primary Address",
"yes": "Yes"
},
"admin": {
"tab": {
"ens_management": "ENS Management",
"erc20_token": "ERC20Token",
"name_lookup": "Name Lookup",
"test_token": "TestToken"
}
},
"constants" : {
"contact_code": "Your contact code",
"wallet_address": "Your wallet address"
},
"copy": {
"release": {
"title": {
"sub": "Done!",
"body": "The released username will be available to other users"
}
},
"registered": {
"title": {
"sub": "Nice!",
"body": "The name is yours once the transaction is complete"
}
},
"edit": {
"title": {
"sub": "Done!",
"body": "Your changes will be saved when the transaction is complete"
}
},
"subheading": "Follow the progress in the Transaction History section of your wallet."
},
"domain": {
"ethereum_address": {
"error": "Not a valid address",
"label": "Ethereum address domain resolves to"
},
"name": {
"label": "Domain Name"
},
"new_address": {
"label": "New Controller Address"
},
"price": {
"label": "Domain Price"
},
"release": {
"alert": {
"text": "Your SNT deposit will be returned and name will be available to other users.",
"title": "Release domain?"
}
},
"status_address": {
"label": "Status messenger address domain resolves to",
"placeholder": "Enter Your Status Messenger Address Here"
}
},
"error": {
"connect": "Please connect to Ethereum %{network} to continue.",
"valid_address": "Please enter a valid address"
},
"navbar":{
"brand": "Status.im Demo"
},
"optimized": "This site is optimized for Status. Get the App to enable all features and get the best experience.",
"registry": {
"error": {
"name": {
"owned": "This registry is not owned by registry",
"required": "Required"
}
},
"name": {
"label": "Registry Name"
},
"price": {
"label": "Registry Price",
"placeholder": "(Optional) Registry will be free if left blank"
}
},
"sub_domain": {
"error": {
"required": "Required"
},
"label": "Sub Domain",
"management": {
"activate_registry": "Activate Registry/Update Registry Price",
"change_registry": "Change Registry Controller",
"move_domain": "Move Domain To Another Registry",
"register_sub_domain": "Register Sub-Domain",
"title": "Subdomain Management"
}
},
"terms": {
"title": "Terms of name registration",
"funds_deposit": "Funds are deposited for 1 year. Your SNT will be locked, but not spent.",
"funds_release": "After 1 year, you can release the name and get your deposit back. The name is yours until you release it.",
"names_creation": "Names are created as a subdomain of %{stateofus}. They are property of Status and may be subject to new terms.",
"contract": "If the %{stateofus} contract terms change—e.g. Status makes contract upgrades—you have the right to get your deposit back, even for names held less than 1 year.",
"reserved_list": "reserved list",
"rule": {
"title": "Names may not:",
"one": "contain less than 4 characters;",
"two": "n non-alphanumeric characters;",
"three": "contain uppercase letters;",
"four": "appear on this %{reserved_list_link}",
"five": "mimic an Ethereum address (start with %{eth_address} and contain only hexadecimal characters in the first 12 digits)"
},
"illegal_name": "Registering an illegal name via the registry contract will result in the loss of your SNT deposit and removal of the name.",
"contact": "Contact codes and wallet addresses associated with your name are publicly available information."
},
"welcome": {
"cta": "Let's Go",
"disclaimer": "Powered by Ethereum Name Services",
"eth_address_example": "myname.stateofus.eth",
"how": "How it works",
"step": {
"one": {
"title": "Simplify your ETH address",
"subtitle": "Your complex wallet address (0x...) becomes an easy to read, remember & share URL:"
},
"two": {
"title": "10 SNT to register",
"subtitle": "Register once to keep the name forever. After 1 year, you can release the name and get your SNT back."
},
"three": {
"title": "Connect & get paid",
"subtitle": "Share your name to chat on Status or receive ETH and tokens."
}
},
"title": "ENS names transform those crazy-long addresses into unique usernames"
}
}

163
app/languages/fr.json Normal file
View File

@ -0,0 +1,163 @@
{
"account": {
"amount": "Montant",
"from": "De",
"of": "De",
"read_token_balance": "Lire le solde de tokens du compte",
"read_your_token_balance": "Lisez le solde de tokens de votre compte",
"to": "Vers",
"transfer_approve_token_balance": "Transférer/Approuver le solde de tokens",
"your_test_token_balance": "Votre solde de tokens de test est"
},
"action" : {
"approve": "Approuver",
"cancel": "Annuler",
"edit_contact_code": "Editez le code de contact",
"grant_access": "Donnez l'accès",
"mint_test_token": "Mintez Jeton de Test",
"mint": "Minter",
"register": "Enregistrer",
"release_name": "Libérez le nom",
"send_snt": "Envoyer SNT",
"setup_ens": "AJOUTEZ LES NODES INITIAUX À ENS",
"submit": "Envoyer",
"submitting_to_blockchain": "En cours d'envoie à la Blockchain - (ceci peut prendre quelque temps)",
"transfer": "Transférer",
"update": "Mettre à jour",
"use_my_primary_address": "Utiliser mon adresse principale",
"yes": "Oui"
},
"admin": {
"tab": {
"ens_management": "Gestion ENS",
"erc20_token": "Token ERC20",
"name_lookup": "Recherche de nom",
"test_token": "Token de test"
}
},
"constants" : {
"contact_code": "Votre code de contact",
"wallet_address": "Votre adresse de portefeuille"
},
"copy": {
"release": {
"title": {
"sub": "Fait!",
"body": "Le nom d'utilisateur publié sera disponible pour les autres utilisateurs"
}
},
"registered": {
"title": {
"sub": "Sympa!",
"body": "Le nom est le votre une fois que la transaction est complétée"
}
},
"edit": {
"title": {
"sub": "Fait!",
"body": "Your changes will be saved when the transaction is complete"
}
},
"subheading": "Suivez le progrès dans la rubrique Historique de Transactions de votre portefeuille."
},
"domain": {
"ethereum_address": {
"error": "Adresse invalide",
"label": "Adresse Ethereum du domaine résout vers"
},
"name": {
"label": "Nom du domaine"
},
"new_address": {
"label": "Nouvelle adresse du contrôleur"
},
"price": {
"label": "Prix du domaine"
},
"release": {
"alert": {
"text": "Votre dépot de SNT vous sera retourné et le nom sera disponible pour d'autres utilisateurs.",
"title": "Libérez le domaine ?"
}
},
"status_address": {
"label": "Adresse Status messenger du domaine résout vers",
"placeholder": "Entrez votre adresse Status Messenger ici"
}
},
"error": {
"connect": "Veuillez vous connecter au réseau Ethereum %{network} pour continuer.",
"valid_address": "Veuillez entrer une adresse valide"
},
"navbar":{
"brand": "Démo Status.im"
},
"optimized": "Ce site est optimisé pour Status. Téléchargez l'application pour activer toutes les fonctionnalitées et profiter d'une meilleure expérience.",
"registry": {
"error": {
"name": {
"owned": "Cet enregistrement n'est pas possédé par un enregistrement",
"required": "Requis"
}
},
"name": {
"label": "Nom de l'enregistrement"
},
"price": {
"label": "Prix de l'enregistrement",
"placeholder": "(Optionnel) Si laissé vide, l'enregistrement sera gratuit"
}
},
"sub_domain": {
"error": {
"required": "Requis"
},
"label": "Sous-Domaine",
"management": {
"activate_registry": "Activer le registre/Mettre à jour le prix du registre",
"change_registry": "Changer le contrôleur du registre",
"move_domain": "Déplacer le domaine vers un autre registre",
"register_sub_domain": "Enregistrer le sous domaine",
"title": "Gestion du sous-domaine"
}
},
"terms": {
"title": "Conditions d'enregistrement du nom",
"funds_deposit": "Les fonds sont déposés pour une durée d'un an. Vos SNT sont bloqués mais non dépensés.",
"funds_release": "Après une année, vous pouvez libérer votre nom et ainsi récupérer votre dépôt. Le nom vous appartient tant que vous ne l'avez pas libéré.",
"names_creation": "Les noms sont créés en tant que sous-domaines de %{stateofus}. Ils sont la propriété de Status et peuvent êtres soumis à de nouvelles conditions.",
"contract": "Si les conditions du contrat %{stateofus} changent—e.g. Status met à jour le contrat—vous avez le droit de récupérer votre dépôt you, ainsi que vos noms de domaines que vous avez gardé moins d'un an.",
"reserved_list": "liste réservée",
"rule": {
"title": "Les noms ne peuvent pas:",
"one": "contenir moins de 4 caractères;",
"two": "n caractères non-alphanumérique;",
"three": "contenir des lettres majuscules;",
"four": "apparaitre sur cette %{reserved_list_link}",
"five": "copier une adresse Ethereum (commençant par %{eth_address} et contenant seulement des caractères hexadécimaux parmis les 12 premiers caractères)"
},
"illegal_name": "Enregistrer un nom illégal via le contrat du registre entraine la perte de votre dépôt de SNT et la supression du nom.",
"contact": "Les codes de contact ainsi que l'adresse du portefeuille associé à votre nom sont des informations publiquement disponibles."
},
"welcome": {
"cta": "Allons-y",
"disclaimer": "Propulsé par Ethereum Name Services",
"eth_address_example": "myname.stateofus.eth",
"how": "Comment ça marche",
"step": {
"one": {
"title": "Simplifiez votre adresse ETH",
"subtitle": "Votre complexe adresse de portefeuille (0x...) devient facile à lire, à mémoriser et à partager l'URL:"
},
"two": {
"title": "10 SNT pour l'enregister",
"subtitle": "Enregistrez le nom pour toujours. Après une année, vous pouvez libérer le nom et recevoir vos SNT en retour."
},
"three": {
"title": "Connectez-vous et recevez des paiements",
"subtitle": "Partagez votre nom sur le chat de Status ou recevez des ETH ainsi que des tokens."
}
},
"title": "Les noms ENS transforment vos très longues adresses en d'unique noms d'utilisateur"
}
}

9
app/languages/index.js Normal file
View File

@ -0,0 +1,9 @@
import en from './en.json';
import fr from './fr.json';
const translations = {
en,
fr,
};
export default translations;

View File

@ -1,11 +1,13 @@
import { createStore, applyMiddleware } from 'redux';
import { applyMiddleware, compose, createStore } from 'redux';
import rootReducer from '../reducers/rootReducer';
import thunk from 'redux-thunk';
const store = createStore(
rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
applyMiddleware(thunk)
compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f,
),
);
export default store;

View File

@ -25,6 +25,7 @@
"elliptic": "^6.4.1",
"eth-ens-namehash": "^2.0.8",
"formik": "^0.11.11",
"i18n-js": "^3.1.0",
"idna-normalize": "^1.0.0",
"install": "^0.11.0",
"npm": "^6.1.0",