aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts
diff options
context:
space:
mode:
authorJacob Evans <dekz@dekz.net>2018-02-27 10:12:11 +0800
committerJacob Evans <jacob@dekz.net>2018-02-27 10:47:09 +0800
commit6961169f89c4cd8480aca9c720cd8299aec9bbc0 (patch)
tree51b9a6a2f0dbaee50a9758a7983fe201d5898354 /packages/website/ts
parent3d66feb89f8e4cfcb0b5251a96d02ef31f3ae34b (diff)
parent709fa9e02ec21cee9fc145b4a578742c8dd190aa (diff)
downloaddexon-0x-contracts-6961169f89c4cd8480aca9c720cd8299aec9bbc0.tar.gz
dexon-0x-contracts-6961169f89c4cd8480aca9c720cd8299aec9bbc0.tar.zst
dexon-0x-contracts-6961169f89c4cd8480aca9c720cd8299aec9bbc0.zip
Merge branch 'development' into feature/0x.js/lowercase-addresses
Diffstat (limited to 'packages/website/ts')
-rw-r--r--packages/website/ts/components/footer.tsx236
-rw-r--r--packages/website/ts/components/portal.tsx5
-rw-r--r--packages/website/ts/components/top_bar/top_bar.tsx68
-rw-r--r--packages/website/ts/components/top_bar/top_bar_menu_item.tsx2
-rw-r--r--packages/website/ts/containers/about.tsx26
-rw-r--r--packages/website/ts/containers/connect_documentation.tsx3
-rw-r--r--packages/website/ts/containers/faq.tsx26
-rw-r--r--packages/website/ts/containers/landing.tsx28
-rw-r--r--packages/website/ts/containers/not_found.tsx28
-rw-r--r--packages/website/ts/containers/portal.tsx3
-rw-r--r--packages/website/ts/containers/smart_contracts_documentation.tsx3
-rw-r--r--packages/website/ts/containers/wiki.tsx26
-rw-r--r--packages/website/ts/containers/zero_ex_js_documentation.tsx3
-rw-r--r--packages/website/ts/index.tsx10
-rw-r--r--packages/website/ts/pages/about/about.tsx7
-rw-r--r--packages/website/ts/pages/documentation/documentation.tsx3
-rw-r--r--packages/website/ts/pages/faq/faq.tsx8
-rw-r--r--packages/website/ts/pages/landing/landing.tsx293
-rw-r--r--packages/website/ts/pages/not_found.tsx8
-rw-r--r--packages/website/ts/pages/wiki/wiki.tsx5
-rw-r--r--packages/website/ts/redux/dispatcher.ts7
-rw-r--r--packages/website/ts/redux/reducer.ts15
-rw-r--r--packages/website/ts/types.ts76
-rw-r--r--packages/website/ts/utils/translate.ts83
24 files changed, 684 insertions, 288 deletions
diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx
index a0f1a0c96..810460cac 100644
--- a/packages/website/ts/components/footer.tsx
+++ b/packages/website/ts/components/footer.tsx
@@ -1,9 +1,13 @@
import * as _ from 'lodash';
+import DropDownMenu from 'material-ui/DropDownMenu';
+import MenuItem from 'material-ui/MenuItem';
import * as React from 'react';
import { Link } from 'react-router-dom';
-import { WebsitePaths } from 'ts/types';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { Deco, Key, Language, WebsitePaths } from 'ts/types';
import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
interface MenuItemsBySection {
[sectionName: string]: FooterMenuItem[];
@@ -15,105 +19,114 @@ interface FooterMenuItem {
isExternal?: boolean;
}
-enum Sections {
- Documentation = 'Documentation',
- Community = 'Community',
- Organization = 'Organization',
-}
-
const ICON_DIMENSION = 16;
-const menuItemsBySection: MenuItemsBySection = {
- Documentation: [
- {
- title: '0x.js',
- path: WebsitePaths.ZeroExJs,
- },
- {
- title: '0x Smart Contracts',
- path: WebsitePaths.SmartContracts,
- },
- {
- title: '0x Connect',
- path: WebsitePaths.Connect,
- },
- {
- title: 'Whitepaper',
- path: WebsitePaths.Whitepaper,
- isExternal: true,
- },
- {
- title: 'Wiki',
- path: WebsitePaths.Wiki,
- },
- {
- title: 'FAQ',
- path: WebsitePaths.FAQ,
- },
- ],
- Community: [
- {
- title: 'Rocket.chat',
- isExternal: true,
- path: constants.URL_ZEROEX_CHAT,
- },
- {
- title: 'Blog',
- isExternal: true,
- path: constants.URL_BLOG,
- },
- {
- title: 'Twitter',
- isExternal: true,
- path: constants.URL_TWITTER,
- },
- {
- title: 'Reddit',
- isExternal: true,
- path: constants.URL_REDDIT,
- },
- {
- title: 'Forum',
- isExternal: true,
- path: constants.URL_DISCOURSE_FORUM,
- },
- ],
- Organization: [
- {
- title: 'About',
- isExternal: false,
- path: WebsitePaths.About,
- },
- {
- title: 'Careers',
- isExternal: true,
- path: constants.URL_ANGELLIST,
- },
- {
- title: 'Contact',
- isExternal: true,
- path: 'mailto:team@0xproject.com',
- },
- ],
-};
+
const linkStyle = {
color: colors.white,
cursor: 'pointer',
};
-const titleToIcon: { [title: string]: string } = {
- 'Rocket.chat': 'rocketchat.png',
- Blog: 'medium.png',
- Twitter: 'twitter.png',
- Reddit: 'reddit.png',
- Forum: 'discourse.png',
+const languageToMenuTitle = {
+ [Language.English]: 'English',
+ [Language.Russian]: 'Русский',
+ [Language.Spanish]: 'Español',
+ [Language.Korean]: '한국어',
+ [Language.Chinese]: '中文',
};
-export interface FooterProps {}
+export interface FooterProps {
+ translate: Translate;
+ dispatcher: Dispatcher;
+}
-interface FooterState {}
+interface FooterState {
+ selectedLanguage: Language;
+}
export class Footer extends React.Component<FooterProps, FooterState> {
+ constructor(props: FooterProps) {
+ super();
+ this.state = {
+ selectedLanguage: props.translate.getLanguage(),
+ };
+ }
public render() {
+ const menuItemsBySection: MenuItemsBySection = {
+ [Key.Documentation]: [
+ {
+ title: '0x.js',
+ path: WebsitePaths.ZeroExJs,
+ },
+ {
+ title: this.props.translate.get(Key.SmartContracts, Deco.Cap),
+ path: WebsitePaths.SmartContracts,
+ },
+ {
+ title: this.props.translate.get(Key.Connect, Deco.Cap),
+ path: WebsitePaths.Connect,
+ },
+ {
+ title: this.props.translate.get(Key.Whitepaper, Deco.Cap),
+ path: WebsitePaths.Whitepaper,
+ isExternal: true,
+ },
+ {
+ title: this.props.translate.get(Key.Wiki, Deco.Cap),
+ path: WebsitePaths.Wiki,
+ },
+ {
+ title: this.props.translate.get(Key.Faq, Deco.Cap),
+ path: WebsitePaths.FAQ,
+ },
+ ],
+ [Key.Community]: [
+ {
+ title: this.props.translate.get(Key.RocketChat, Deco.Cap),
+ isExternal: true,
+ path: constants.URL_ZEROEX_CHAT,
+ },
+ {
+ title: this.props.translate.get(Key.Blog, Deco.Cap),
+ isExternal: true,
+ path: constants.URL_BLOG,
+ },
+ {
+ title: 'Twitter',
+ isExternal: true,
+ path: constants.URL_TWITTER,
+ },
+ {
+ title: 'Reddit',
+ isExternal: true,
+ path: constants.URL_REDDIT,
+ },
+ {
+ title: this.props.translate.get(Key.Forum, Deco.Cap),
+ isExternal: true,
+ path: constants.URL_DISCOURSE_FORUM,
+ },
+ ],
+ [Key.Organization]: [
+ {
+ title: this.props.translate.get(Key.About, Deco.Cap),
+ isExternal: false,
+ path: WebsitePaths.About,
+ },
+ {
+ title: this.props.translate.get(Key.Careers, Deco.Cap),
+ isExternal: true,
+ path: constants.URL_ANGELLIST,
+ },
+ {
+ title: this.props.translate.get(Key.Contact, Deco.Cap),
+ isExternal: true,
+ path: 'mailto:team@0xproject.com',
+ },
+ ],
+ };
+ const languageMenuItems = _.map(languageToMenuTitle, (menuTitle: string, language: Language) => {
+ return <MenuItem key={menuTitle} value={language} primaryText={menuTitle} />;
+ });
return (
<div className="relative pb4 pt2" style={{ backgroundColor: colors.darkerGrey }}>
<div className="mx-auto max-width-4 md-px2 lg-px0 py4 clearfix" style={{ color: colors.white }}>
@@ -132,25 +145,34 @@ export class Footer extends React.Component<FooterProps, FooterState> {
>
© ZeroEx, Intl.
</div>
+ <div className="pt4 center">
+ <DropDownMenu
+ labelStyle={{ color: colors.white }}
+ value={this.state.selectedLanguage}
+ onChange={this._updateLanguage.bind(this)}
+ >
+ {languageMenuItems}
+ </DropDownMenu>
+ </div>
</div>
</div>
<div className="col lg-col-8 md-col-8 col-12 lg-pl4 md-pl4">
<div className="col lg-col-4 md-col-4 col-12">
<div className="lg-right md-right sm-center">
- {this._renderHeader(Sections.Documentation)}
- {_.map(menuItemsBySection[Sections.Documentation], this._renderMenuItem.bind(this))}
+ {this._renderHeader(Key.Documentation)}
+ {_.map(menuItemsBySection[Key.Documentation], this._renderMenuItem.bind(this))}
</div>
</div>
<div className="col lg-col-4 md-col-4 col-12 lg-pr2 md-pr2">
<div className="lg-right md-right sm-center">
- {this._renderHeader(Sections.Community)}
- {_.map(menuItemsBySection[Sections.Community], this._renderMenuItem.bind(this))}
+ {this._renderHeader(Key.Community)}
+ {_.map(menuItemsBySection[Key.Community], this._renderMenuItem.bind(this))}
</div>
</div>
<div className="col lg-col-4 md-col-4 col-12">
<div className="lg-right md-right sm-center">
- {this._renderHeader(Sections.Organization)}
- {_.map(menuItemsBySection[Sections.Organization], this._renderMenuItem.bind(this))}
+ {this._renderHeader(Key.Organization)}
+ {_.map(menuItemsBySection[Key.Organization], this._renderMenuItem.bind(this))}
</div>
</div>
</div>
@@ -166,17 +188,22 @@ export class Footer extends React.Component<FooterProps, FooterState> {
);
}
private _renderMenuItem(item: FooterMenuItem) {
+ const titleToIcon: { [title: string]: string } = {
+ [this.props.translate.get(Key.RocketChat, Deco.Cap)]: 'rocketchat.png',
+ [this.props.translate.get(Key.Blog, Deco.Cap)]: 'medium.png',
+ Twitter: 'twitter.png',
+ Reddit: 'reddit.png',
+ [this.props.translate.get(Key.Forum, Deco.Cap)]: 'discourse.png',
+ };
const iconIfExists = titleToIcon[item.title];
return (
<div key={item.title} className="sm-center" style={{ fontSize: 13, paddingTop: 25 }}>
{item.isExternal ? (
<a className="text-decoration-none" style={linkStyle} target="_blank" href={item.path}>
{!_.isUndefined(iconIfExists) ? (
- <div className="sm-mx-auto" style={{ width: 65 }}>
- <div className="flex">
- <div className="pr1">{this._renderIcon(iconIfExists)}</div>
- <div>{item.title}</div>
- </div>
+ <div className="inline-block">
+ <div className="pr1 table-cell">{this._renderIcon(iconIfExists)}</div>
+ <div className="table-cell">{item.title}</div>
</div>
) : (
item.title
@@ -195,9 +222,8 @@ export class Footer extends React.Component<FooterProps, FooterState> {
</div>
);
}
- private _renderHeader(title: string) {
+ private _renderHeader(key: Key) {
const headerStyle = {
- textTransform: 'uppercase',
color: colors.grey400,
letterSpacing: 2,
fontFamily: 'Roboto Mono',
@@ -205,8 +231,14 @@ export class Footer extends React.Component<FooterProps, FooterState> {
};
return (
<div className="lg-pb2 md-pb2 sm-pt4" style={headerStyle}>
- {title}
+ {this.props.translate.get(key, Deco.Upper)}
</div>
);
}
+ private _updateLanguage(e: any, index: number, value: Language) {
+ this.setState({
+ selectedLanguage: value,
+ });
+ this.props.dispatcher.updateSelectedLanguage(value);
+ }
}
diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx
index 0409f28c0..4871997ac 100644
--- a/packages/website/ts/components/portal.tsx
+++ b/packages/website/ts/components/portal.tsx
@@ -27,6 +27,7 @@ import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAdd
import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
import { utils } from 'ts/utils/utils';
const THROTTLE_TIMEOUT = 100;
@@ -52,6 +53,7 @@ export interface PortalAllProps {
location: Location;
flashMessage?: string | React.ReactNode;
lastForceTokenStateRefetch: number;
+ translate: Translate;
}
interface PortalAllState {
@@ -166,6 +168,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
blockchainIsLoaded={this.props.blockchainIsLoaded}
location={this.props.location}
blockchain={this._blockchain}
+ translate={this.props.translate}
/>
<div id="portal" className="mx-auto max-width-4" style={{ width: '100%' }}>
<Paper className="mb3 mt2">
@@ -259,7 +262,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
/>
)}
</div>
- <Footer />;
+ <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
</div>
);
}
diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx
index a412007f2..2723c2218 100644
--- a/packages/website/ts/components/top_bar/top_bar.tsx
+++ b/packages/website/ts/components/top_bar/top_bar.tsx
@@ -14,9 +14,10 @@ import { Identicon } from 'ts/components/ui/identicon';
import { DocsInfo } from 'ts/pages/documentation/docs_info';
import { NestedSidebarMenu } from 'ts/pages/shared/nested_sidebar_menu';
import { Dispatcher } from 'ts/redux/dispatcher';
-import { DocsMenu, MenuSubsectionsBySection, ProviderType, Styles, WebsitePaths } from 'ts/types';
+import { Deco, DocsMenu, Key, MenuSubsectionsBySection, ProviderType, Styles, WebsitePaths } from 'ts/types';
import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
interface TopBarProps {
userAddress?: string;
@@ -28,6 +29,7 @@ interface TopBarProps {
dispatcher?: Dispatcher;
blockchainIsLoaded: boolean;
location: Location;
+ translate: Translate;
docsVersion?: string;
availableDocVersions?: string[];
menu?: DocsMenu;
@@ -95,10 +97,16 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
<MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="0x.js" />
</Link>,
<Link key="subMenuItem-smartContracts" to={WebsitePaths.SmartContracts} className="text-decoration-none">
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="Smart Contracts" />
+ <MenuItem
+ style={{ fontSize: styles.menuItem.fontSize }}
+ primaryText={this.props.translate.get(Key.SmartContract, Deco.CapWords)}
+ />
</Link>,
<Link key="subMenuItem-0xconnect" to={WebsitePaths.Connect} className="text-decoration-none">
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="0x Connect" />
+ <MenuItem
+ style={{ fontSize: styles.menuItem.fontSize }}
+ primaryText={this.props.translate.get(Key.Connect, Deco.CapWords)}
+ />
</Link>,
<a
key="subMenuItem-standard-relayer-api"
@@ -106,7 +114,10 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
className="text-decoration-none"
href={constants.URL_STANDARD_RELAYER_API_GITHUB}
>
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="Standard Relayer API" />
+ <MenuItem
+ style={{ fontSize: styles.menuItem.fontSize }}
+ primaryText={this.props.translate.get(Key.StandardRelayerApi, Deco.CapWords)}
+ />
</a>,
<a
key="subMenuItem-github"
@@ -122,7 +133,10 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
className="text-decoration-none"
href={`${WebsitePaths.Whitepaper}`}
>
- <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="Whitepaper" />
+ <MenuItem
+ style={{ fontSize: styles.menuItem.fontSize }}
+ primaryText={this.props.translate.get(Key.Whitepaper, Deco.CapWords)}
+ />
</a>,
];
const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {};
@@ -137,7 +151,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
};
const hoverActiveNode = (
<div className="flex relative" style={{ color: menuIconStyle.color }}>
- <div style={{ paddingRight: 10 }}>Developers</div>
+ <div style={{ paddingRight: 10 }}>{this.props.translate.get(Key.Developers, Deco.Cap)}</div>
<div className="absolute" style={{ paddingLeft: 3, right: 3, top: -2 }}>
<i className="zmdi zmdi-caret-right" style={{ fontSize: 22 }} />
</div>
@@ -165,28 +179,28 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
style={styles.menuItem}
/>
<TopBarMenuItem
- title="Wiki"
+ title={this.props.translate.get(Key.Wiki, Deco.Cap)}
path={`${WebsitePaths.Wiki}`}
style={styles.menuItem}
isNightVersion={isNightVersion}
isExternal={false}
/>
<TopBarMenuItem
- title="Blog"
+ title={this.props.translate.get(Key.Blog, Deco.Cap)}
path={constants.URL_BLOG}
style={styles.menuItem}
isNightVersion={isNightVersion}
isExternal={true}
/>
<TopBarMenuItem
- title="About"
+ title={this.props.translate.get(Key.About, Deco.Cap)}
path={`${WebsitePaths.About}`}
style={styles.menuItem}
isNightVersion={isNightVersion}
isExternal={false}
/>
<TopBarMenuItem
- title="Portal DApp"
+ title={this.props.translate.get(Key.PortalDApp, Deco.CapWords)}
path={`${WebsitePaths.Portal}`}
isPrimary={true}
style={styles.menuItem}
@@ -233,46 +247,54 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
{this._renderDocsMenu()}
{this._renderWiki()}
<div className="pl1 py1 mt3" style={{ backgroundColor: colors.lightGrey }}>
- Website
+ {this.props.translate.get(Key.Website, Deco.Cap)}
</div>
<Link to={WebsitePaths.Home} className="text-decoration-none">
- <MenuItem className="py2">Home</MenuItem>
+ <MenuItem className="py2">{this.props.translate.get(Key.Home, Deco.Cap)}</MenuItem>
</Link>
<Link to={`${WebsitePaths.Wiki}`} className="text-decoration-none">
- <MenuItem className="py2">Wiki</MenuItem>
+ <MenuItem className="py2">{this.props.translate.get(Key.Wiki, Deco.Cap)}</MenuItem>
</Link>
{!this._isViewing0xjsDocs() && (
<Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <MenuItem className="py2">0x.js Docs</MenuItem>
+ <MenuItem className="py2">0x.js {this.props.translate.get(Key.Docs, Deco.Cap)}</MenuItem>
</Link>
)}
{!this._isViewingConnectDocs() && (
<Link to={WebsitePaths.Connect} className="text-decoration-none">
- <MenuItem className="py2">0x Connect Docs</MenuItem>
+ <MenuItem className="py2">
+ {this.props.translate.get(Key.Connect, Deco.Cap)}{' '}
+ {this.props.translate.get(Key.Docs, Deco.Cap)}
+ </MenuItem>
</Link>
)}
{!this._isViewingSmartContractsDocs() && (
<Link to={WebsitePaths.SmartContracts} className="text-decoration-none">
- <MenuItem className="py2">Smart Contract Docs</MenuItem>
+ <MenuItem className="py2">
+ {this.props.translate.get(Key.SmartContract, Deco.Cap)}{' '}
+ {this.props.translate.get(Key.Docs, Deco.Cap)}
+ </MenuItem>
</Link>
)}
{!this._isViewingPortal() && (
<Link to={`${WebsitePaths.Portal}`} className="text-decoration-none">
- <MenuItem className="py2">Portal DApp</MenuItem>
+ <MenuItem className="py2">
+ {this.props.translate.get(Key.PortalDApp, Deco.CapWords)}
+ </MenuItem>
</Link>
)}
<a className="text-decoration-none" target="_blank" href={`${WebsitePaths.Whitepaper}`}>
- <MenuItem className="py2">Whitepaper</MenuItem>
+ <MenuItem className="py2">{this.props.translate.get(Key.Whitepaper, Deco.Cap)}</MenuItem>
</a>
<Link to={`${WebsitePaths.About}`} className="text-decoration-none">
- <MenuItem className="py2">About</MenuItem>
+ <MenuItem className="py2">{this.props.translate.get(Key.About, Deco.Cap)}</MenuItem>
</Link>
<a className="text-decoration-none" target="_blank" href={constants.URL_BLOG}>
- <MenuItem className="py2">Blog</MenuItem>
+ <MenuItem className="py2">{this.props.translate.get(Key.Blog, Deco.Cap)}</MenuItem>
</a>
<Link to={`${WebsitePaths.FAQ}`} className="text-decoration-none">
<MenuItem className="py2" onTouchTap={this._onMenuButtonClick.bind(this)}>
- FAQ
+ {this.props.translate.get(Key.Faq, Deco.Cap)}
</MenuItem>
</Link>
</div>
@@ -313,7 +335,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
<NestedSidebarMenu
topLevelMenu={this.props.menuSubsectionsBySection}
menuSubsectionsBySection={this.props.menuSubsectionsBySection}
- title="Wiki"
+ title={this.props.translate.get(Key.Wiki, Deco.Cap)}
shouldDisplaySectionHeaders={false}
onMenuItemClick={this._onMenuButtonClick.bind(this)}
/>
@@ -328,7 +350,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
return (
<div className="lg-hide md-hide">
<div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}>
- Portal DApp
+ {this.props.translate.get(Key.PortalDApp, Deco.CapWords)}
</div>
<PortalMenu menuItemStyle={{ color: 'black' }} onClick={this._onMenuButtonClick.bind(this)} />
</div>
diff --git a/packages/website/ts/components/top_bar/top_bar_menu_item.tsx b/packages/website/ts/components/top_bar/top_bar_menu_item.tsx
index 983050abc..e70381456 100644
--- a/packages/website/ts/components/top_bar/top_bar_menu_item.tsx
+++ b/packages/website/ts/components/top_bar/top_bar_menu_item.tsx
@@ -34,7 +34,7 @@ export class TopBarMenuItem extends React.Component<TopBarMenuItemProps, TopBarM
marginTop: 15,
paddingLeft: 9,
paddingRight: 9,
- width: 77,
+ minWidth: 77,
}
: {};
const menuItemColor = this.props.isNightVersion ? 'white' : this.props.style.color;
diff --git a/packages/website/ts/containers/about.tsx b/packages/website/ts/containers/about.tsx
new file mode 100644
index 000000000..ce8fd3afb
--- /dev/null
+++ b/packages/website/ts/containers/about.tsx
@@ -0,0 +1,26 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { About as AboutComponent, AboutProps } from 'ts/pages/about/about';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { State } from 'ts/redux/reducer';
+import { Translate } from 'ts/utils/translate';
+
+interface ConnectedState {
+ translate: Translate;
+}
+
+interface ConnectedDispatch {
+ dispatcher: Dispatcher;
+}
+
+const mapStateToProps = (state: State, ownProps: AboutProps): ConnectedState => ({
+ translate: state.translate,
+});
+
+const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
+ dispatcher: new Dispatcher(dispatch),
+});
+
+export const About: React.ComponentClass<AboutProps> = connect(mapStateToProps, mapDispatchToProps)(AboutComponent);
diff --git a/packages/website/ts/containers/connect_documentation.tsx b/packages/website/ts/containers/connect_documentation.tsx
index 79eafa431..5c5d26b44 100644
--- a/packages/website/ts/containers/connect_documentation.tsx
+++ b/packages/website/ts/containers/connect_documentation.tsx
@@ -9,6 +9,7 @@ import { State } from 'ts/redux/reducer';
import { DocsInfoConfig, Environments, WebsitePaths } from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
import { typeDocUtils } from 'ts/utils/typedoc_utils';
/* tslint:disable:no-var-requires */
@@ -84,6 +85,7 @@ interface ConnectedState {
docsVersion: string;
availableDocVersions: string[];
docsInfo: DocsInfo;
+ translate: Translate;
}
interface ConnectedDispatch {
@@ -93,6 +95,7 @@ interface ConnectedDispatch {
const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({
docsVersion: state.docsVersion,
availableDocVersions: state.availableDocVersions,
+ translate: state.translate,
docsInfo,
});
diff --git a/packages/website/ts/containers/faq.tsx b/packages/website/ts/containers/faq.tsx
new file mode 100644
index 000000000..b539e33c9
--- /dev/null
+++ b/packages/website/ts/containers/faq.tsx
@@ -0,0 +1,26 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { FAQ as FAQComponent, FAQProps } from 'ts/pages/faq/faq';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { State } from 'ts/redux/reducer';
+import { Translate } from 'ts/utils/translate';
+
+interface ConnectedState {
+ translate: Translate;
+}
+
+interface ConnectedDispatch {
+ dispatcher: Dispatcher;
+}
+
+const mapStateToProps = (state: State, ownProps: FAQProps): ConnectedState => ({
+ translate: state.translate,
+});
+
+const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
+ dispatcher: new Dispatcher(dispatch),
+});
+
+export const FAQ: React.ComponentClass<FAQProps> = connect(mapStateToProps, mapDispatchToProps)(FAQComponent);
diff --git a/packages/website/ts/containers/landing.tsx b/packages/website/ts/containers/landing.tsx
new file mode 100644
index 000000000..a620bb12e
--- /dev/null
+++ b/packages/website/ts/containers/landing.tsx
@@ -0,0 +1,28 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { Landing as LandingComponent, LandingProps } from 'ts/pages/landing/landing';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { State } from 'ts/redux/reducer';
+import { Translate } from 'ts/utils/translate';
+
+interface ConnectedState {
+ translate: Translate;
+}
+
+interface ConnectedDispatch {
+ dispatcher: Dispatcher;
+}
+
+const mapStateToProps = (state: State, ownProps: LandingProps): ConnectedState => ({
+ translate: state.translate,
+});
+
+const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
+ dispatcher: new Dispatcher(dispatch),
+});
+
+export const Landing: React.ComponentClass<LandingProps> = connect(mapStateToProps, mapDispatchToProps)(
+ LandingComponent,
+);
diff --git a/packages/website/ts/containers/not_found.tsx b/packages/website/ts/containers/not_found.tsx
new file mode 100644
index 000000000..dd151e2c8
--- /dev/null
+++ b/packages/website/ts/containers/not_found.tsx
@@ -0,0 +1,28 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { NotFound as NotFoundComponent, NotFoundProps } from 'ts/pages/not_found';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { State } from 'ts/redux/reducer';
+import { Translate } from 'ts/utils/translate';
+
+interface ConnectedState {
+ translate: Translate;
+}
+
+interface ConnectedDispatch {
+ dispatcher: Dispatcher;
+}
+
+const mapStateToProps = (state: State, ownProps: NotFoundProps): ConnectedState => ({
+ translate: state.translate,
+});
+
+const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
+ dispatcher: new Dispatcher(dispatch),
+});
+
+export const NotFound: React.ComponentClass<NotFoundProps> = connect(mapStateToProps, mapDispatchToProps)(
+ NotFoundComponent,
+);
diff --git a/packages/website/ts/containers/portal.tsx b/packages/website/ts/containers/portal.tsx
index bcca0d70f..befa16bdb 100644
--- a/packages/website/ts/containers/portal.tsx
+++ b/packages/website/ts/containers/portal.tsx
@@ -8,6 +8,7 @@ import { Dispatcher } from 'ts/redux/dispatcher';
import { State } from 'ts/redux/reducer';
import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, Side, TokenByAddress } from 'ts/types';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
interface ConnectedState {
blockchainErr: BlockchainErrs;
@@ -26,6 +27,7 @@ interface ConnectedState {
userAddress: string;
userSuppliedOrderCache: Order;
flashMessage?: string | React.ReactNode;
+ translate: Translate;
}
interface ConnectedDispatch {
@@ -73,6 +75,7 @@ const mapStateToProps = (state: State, ownProps: PortalComponentAllProps): Conne
userEtherBalance: state.userEtherBalance,
userSuppliedOrderCache: state.userSuppliedOrderCache,
flashMessage: state.flashMessage,
+ translate: state.translate,
};
};
diff --git a/packages/website/ts/containers/smart_contracts_documentation.tsx b/packages/website/ts/containers/smart_contracts_documentation.tsx
index 8be33b546..c4731f929 100644
--- a/packages/website/ts/containers/smart_contracts_documentation.tsx
+++ b/packages/website/ts/containers/smart_contracts_documentation.tsx
@@ -8,6 +8,7 @@ import { Dispatcher } from 'ts/redux/dispatcher';
import { State } from 'ts/redux/reducer';
import { DocsInfoConfig, SmartContractDocSections as Sections, WebsitePaths } from 'ts/types';
import { doxityUtils } from 'ts/utils/doxity_utils';
+import { Translate } from 'ts/utils/translate';
/* tslint:disable:no-var-requires */
const IntroMarkdown = require('md/docs/smart_contracts/introduction');
@@ -40,6 +41,7 @@ const docsInfo = new DocsInfo(docsInfoConfig);
interface ConnectedState {
docsVersion: string;
availableDocVersions: string[];
+ translate: Translate;
}
interface ConnectedDispatch {
@@ -50,6 +52,7 @@ interface ConnectedDispatch {
const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({
docsVersion: state.docsVersion,
availableDocVersions: state.availableDocVersions,
+ translate: state.translate,
});
const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
diff --git a/packages/website/ts/containers/wiki.tsx b/packages/website/ts/containers/wiki.tsx
new file mode 100644
index 000000000..2cb87d0a1
--- /dev/null
+++ b/packages/website/ts/containers/wiki.tsx
@@ -0,0 +1,26 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { Wiki as WikiComponent, WikiProps } from 'ts/pages/wiki/wiki';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { State } from 'ts/redux/reducer';
+import { Translate } from 'ts/utils/translate';
+
+interface ConnectedState {
+ translate: Translate;
+}
+
+interface ConnectedDispatch {
+ dispatcher: Dispatcher;
+}
+
+const mapStateToProps = (state: State, ownProps: WikiProps): ConnectedState => ({
+ translate: state.translate,
+});
+
+const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
+ dispatcher: new Dispatcher(dispatch),
+});
+
+export const Wiki: React.ComponentClass<WikiProps> = connect(mapStateToProps, mapDispatchToProps)(WikiComponent);
diff --git a/packages/website/ts/containers/zero_ex_js_documentation.tsx b/packages/website/ts/containers/zero_ex_js_documentation.tsx
index eee2c7cc8..a32a87f7f 100644
--- a/packages/website/ts/containers/zero_ex_js_documentation.tsx
+++ b/packages/website/ts/containers/zero_ex_js_documentation.tsx
@@ -9,6 +9,7 @@ import { State } from 'ts/redux/reducer';
import { DocsInfoConfig, Environments, WebsitePaths } from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
import { typeDocUtils } from 'ts/utils/typedoc_utils';
/* tslint:disable:no-var-requires */
@@ -154,6 +155,7 @@ interface ConnectedState {
docsVersion: string;
availableDocVersions: string[];
docsInfo: DocsInfo;
+ translate: Translate;
}
interface ConnectedDispatch {
@@ -164,6 +166,7 @@ const mapStateToProps = (state: State, ownProps: DocumentationAllProps): Connect
docsVersion: state.docsVersion,
availableDocVersions: state.availableDocVersions,
docsInfo,
+ translate: state.translate,
});
const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx
index bc4e0b472..c0539c6d0 100644
--- a/packages/website/ts/index.tsx
+++ b/packages/website/ts/index.tsx
@@ -7,14 +7,14 @@ import { Provider } from 'react-redux';
import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-dom';
import * as injectTapEventPlugin from 'react-tap-event-plugin';
import { createStore, Store as ReduxStore } from 'redux';
+import { About } from 'ts/containers/about';
+import { FAQ } from 'ts/containers/faq';
+import { Landing } from 'ts/containers/landing';
+import { NotFound } from 'ts/containers/not_found';
+import { Wiki } from 'ts/containers/wiki';
import { createLazyComponent } from 'ts/lazy_component';
import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage';
import { tradeHistoryStorage } from 'ts/local_storage/trade_history_storage';
-import { About } from 'ts/pages/about/about';
-import { FAQ } from 'ts/pages/faq/faq';
-import { Landing } from 'ts/pages/landing/landing';
-import { NotFound } from 'ts/pages/not_found';
-import { Wiki } from 'ts/pages/wiki/wiki';
import { reducer, State } from 'ts/redux/reducer';
import { WebsitePaths } from 'ts/types';
import { muiTheme } from 'ts/utils/mui_theme';
diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx
index 0889e79f3..b99dc34ab 100644
--- a/packages/website/ts/pages/about/about.tsx
+++ b/packages/website/ts/pages/about/about.tsx
@@ -4,9 +4,11 @@ import * as DocumentTitle from 'react-document-title';
import { Footer } from 'ts/components/footer';
import { TopBar } from 'ts/components/top_bar/top_bar';
import { Profile } from 'ts/pages/about/profile';
+import { Dispatcher } from 'ts/redux/dispatcher';
import { ProfileInfo, Styles } from 'ts/types';
import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
import { utils } from 'ts/utils/utils';
const teamRow1: ProfileInfo[] = [
@@ -143,6 +145,8 @@ const advisors: ProfileInfo[] = [
export interface AboutProps {
source: string;
location: Location;
+ translate: Translate;
+ dispatcher: Dispatcher;
}
interface AboutState {}
@@ -174,6 +178,7 @@ export class About extends React.Component<AboutProps, AboutState> {
blockchainIsLoaded={false}
location={this.props.location}
style={{ backgroundColor: colors.lightestGrey }}
+ translate={this.props.translate}
/>
<div id="about" className="mx-auto max-width-4 py4" style={{ color: colors.grey800 }}>
<div className="mx-auto pb4 sm-px3" style={{ maxWidth: 435 }}>
@@ -230,7 +235,7 @@ export class About extends React.Component<AboutProps, AboutState> {
</div>
</div>
</div>
- <Footer />
+ <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
</div>
);
}
diff --git a/packages/website/ts/pages/documentation/documentation.tsx b/packages/website/ts/pages/documentation/documentation.tsx
index da3728a60..285471166 100644
--- a/packages/website/ts/pages/documentation/documentation.tsx
+++ b/packages/website/ts/pages/documentation/documentation.tsx
@@ -35,6 +35,7 @@ import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
import { docUtils } from 'ts/utils/doc_utils';
+import { Translate } from 'ts/utils/translate';
import { utils } from 'ts/utils/utils';
const TOP_BAR_HEIGHT = 60;
@@ -54,6 +55,7 @@ export interface DocumentationAllProps {
docsVersion: string;
availableDocVersions: string[];
docsInfo: DocsInfo;
+ translate: Translate;
}
interface DocumentationState {
@@ -114,6 +116,7 @@ export class Documentation extends React.Component<DocumentationAllProps, Docume
menu={this.props.docsInfo.getMenu(this.props.docsVersion)}
menuSubsectionsBySection={menuSubsectionsBySection}
docsInfo={this.props.docsInfo}
+ translate={this.props.translate}
/>
{_.isUndefined(this.state.docAgnosticFormat) ? (
<div className="col col-12" style={styles.mainContainers}>
diff --git a/packages/website/ts/pages/faq/faq.tsx b/packages/website/ts/pages/faq/faq.tsx
index 0a7eecc2d..34175abdc 100644
--- a/packages/website/ts/pages/faq/faq.tsx
+++ b/packages/website/ts/pages/faq/faq.tsx
@@ -4,14 +4,18 @@ import * as DocumentTitle from 'react-document-title';
import { Footer } from 'ts/components/footer';
import { TopBar } from 'ts/components/top_bar/top_bar';
import { Question } from 'ts/pages/faq/question';
+import { Dispatcher } from 'ts/redux/dispatcher';
import { FAQQuestion, FAQSection, Styles, WebsitePaths } from 'ts/types';
import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
export interface FAQProps {
source: string;
location: Location;
+ translate: Translate;
+ dispatcher: Dispatcher;
}
interface FAQState {}
@@ -407,14 +411,14 @@ export class FAQ extends React.Component<FAQProps, FAQState> {
return (
<div>
<DocumentTitle title="0x FAQ" />
- <TopBar blockchainIsLoaded={false} location={this.props.location} />
+ <TopBar blockchainIsLoaded={false} location={this.props.location} translate={this.props.translate} />
<div id="faq" className="mx-auto max-width-4 pt4" style={{ color: colors.grey800 }}>
<h1 className="center" style={{ ...styles.thin }}>
0x FAQ
</h1>
<div className="sm-px2 md-px2 lg-px0 pb4">{this._renderSections()}</div>
</div>
- <Footer />
+ <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
</div>
);
}
diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx
index d4c934459..044f0b41f 100644
--- a/packages/website/ts/pages/landing/landing.tsx
+++ b/packages/website/ts/pages/landing/landing.tsx
@@ -5,9 +5,11 @@ import DocumentTitle = require('react-document-title');
import { Link } from 'react-router-dom';
import { Footer } from 'ts/components/footer';
import { TopBar } from 'ts/components/top_bar/top_bar';
-import { ScreenWidths, WebsitePaths } from 'ts/types';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { Deco, Key, Language, ScreenWidths, WebsitePaths } from 'ts/types';
import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
import { utils } from 'ts/utils/utils';
interface BoxContent {
@@ -36,35 +38,6 @@ interface Project {
const THROTTLE_TIMEOUT = 100;
-const boxContents: BoxContent[] = [
- {
- title: 'Trustless exchange',
- description:
- "Built on Ethereum's distributed network with no centralized \
- point of failure and no down time, each trade is settled atomically \
- and without counterparty risk.",
- imageUrl: '/images/landing/distributed_network.png',
- classNames: '',
- },
- {
- title: 'Shared liquidity',
- description:
- 'By sharing a standard API, relayers can easily aggregate liquidity pools, \
- creating network effects around liquidity that compound as more relayers come online.',
- imageUrl: '/images/landing/liquidity.png',
- classNames: 'mx-auto',
- },
- {
- title: 'Open source',
- description:
- '0x is open source, permissionless and free to use. Trade directly with a known \
- counterparty for free or pay a relayer some ZRX tokens to access their liquidity \
- pool.',
- imageUrl: '/images/landing/open_source.png',
- classNames: 'right',
- },
-];
-
const relayersAndDappProjects: Project[] = [
{
logoFileName: 'ethfinex.png',
@@ -185,6 +158,8 @@ const relayerProjects: Project[] = [
export interface LandingProps {
location: Location;
+ translate: Translate;
+ dispatcher: Dispatcher;
}
interface LandingState {
@@ -216,17 +191,28 @@ export class Landing extends React.Component<LandingProps, LandingState> {
location={this.props.location}
isNightVersion={true}
style={{ backgroundColor: colors.heroGrey, position: 'relative' }}
+ translate={this.props.translate}
/>
{this._renderHero()}
- {this._renderProjects(relayersAndDappProjects, 'Projects building on 0x', colors.projectsGrey, false)}
+ {this._renderProjects(
+ relayersAndDappProjects,
+ this.props.translate.get(Key.ProjectsHeader, Deco.Upper),
+ colors.projectsGrey,
+ false,
+ )}
{this._renderTokenizationSection()}
{this._renderProtocolSection()}
- {this._renderProjects(relayerProjects, 'Relayers building on 0x', colors.heroGrey, true)}
+ {this._renderProjects(
+ relayerProjects,
+ this.props.translate.get(Key.RelayersHeader, Deco.Upper),
+ colors.heroGrey,
+ true,
+ )}
{this._renderInfoBoxes()}
{this._renderBuildingBlocksSection()}
{this._renderUseCases()}
{this._renderCallToAction()}
- <Footer />
+ <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
</div>
);
}
@@ -243,7 +229,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
lineHeight: '33px',
height: 38,
};
- const left = 'col lg-col-7 md-col-7 col-12 lg-pt4 md-pt4 sm-pt0 mt1 lg-pl4 md-pl4 sm-pl0 sm-px3 sm-center';
+ const left = 'col lg-col-7 md-col-7 col-12 lg-pl4 md-pl4 sm-pl0 sm-px3 sm-center';
return (
<div className="clearfix py4" style={{ backgroundColor: colors.heroGrey }}>
<div className="mx-auto max-width-4 clearfix">
@@ -251,8 +237,14 @@ export class Landing extends React.Component<LandingProps, LandingState> {
<div className="col lg-col-5 md-col-5 col-12 sm-center">
<img src="/images/landing/hero_chip_image.png" height={isSmallScreen ? 300 : 395} />
</div>
- <div className={left} style={{ color: colors.white }}>
- <div style={{ paddingLeft: isSmallScreen ? 0 : 12 }}>
+ <div className={left} style={{ color: colors.white, height: 390, lineHeight: '390px' }}>
+ <div
+ className="inline-block lg-align-middle md-align-middle sm-align-top"
+ style={{
+ paddingLeft: isSmallScreen ? 0 : 12,
+ lineHeight: '36px',
+ }}
+ >
<div
className="sm-pb2"
style={{
@@ -260,7 +252,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
fontSize: isSmallScreen ? 26 : 34,
}}
>
- Powering decentralized exchange
+ {this.props.translate.get(Key.TopHeader, Deco.Cap)}
</div>
<div
className="pt2 h5 sm-mx-auto"
@@ -271,17 +263,16 @@ export class Landing extends React.Component<LandingProps, LandingState> {
fontWeight: 300,
}}
>
- 0x is an open, permissionless protocol allowing for ERC20 tokens to be traded on the
- Ethereum blockchain.
+ {this.props.translate.get(Key.TopTagline)}
</div>
- <div className="pt3 clearfix sm-mx-auto" style={{ maxWidth: 342 }}>
+ <div className="pt3 clearfix sm-mx-auto" style={{ maxWidth: 389 }}>
<div className="lg-pr2 md-pr2 col col-6 sm-center">
<Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
<RaisedButton
style={{ borderRadius: 6, minWidth: 157.36 }}
buttonStyle={{ borderRadius: 6 }}
labelStyle={buttonLabelStyle}
- label="Build on 0x"
+ label={this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
onClick={_.noop}
/>
</Link>
@@ -298,7 +289,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
labelColor="white"
backgroundColor={colors.heroGrey}
labelStyle={buttonLabelStyle}
- label="Join the community"
+ label={this.props.translate.get(Key.CommunityCallToAction, Deco.Cap)}
onClick={_.noop}
/>
</a>
@@ -313,7 +304,6 @@ export class Landing extends React.Component<LandingProps, LandingState> {
}
private _renderProjects(projects: Project[], title: string, backgroundColor: string, isTitleCenter: boolean) {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
- const isMediumScreen = this.state.screenWidth === ScreenWidths.Md;
const projectList = _.map(projects, (project: Project, i: number) => {
const isRelayersOnly = projects.length === 12;
let colWidth: number;
@@ -346,7 +336,6 @@ export class Landing extends React.Component<LandingProps, LandingState> {
const titleStyle: React.CSSProperties = {
fontFamily: 'Roboto Mono',
color: colors.grey,
- textTransform: 'uppercase',
fontWeight: 300,
letterSpacing: 3,
};
@@ -366,13 +355,13 @@ export class Landing extends React.Component<LandingProps, LandingState> {
fontSize: 14,
}}
>
- view the{' '}
+ {this.props.translate.get(Key.FullListPrompt)}{' '}
<Link
to={`${WebsitePaths.Wiki}#List-of-Projects-Using-0x-Protocol`}
className="text-decoration-none underline"
style={{ color: colors.landingLinkGrey }}
>
- full list
+ {this.props.translate.get(Key.FullListLink)}
</Link>
</div>
</div>
@@ -385,33 +374,22 @@ export class Landing extends React.Component<LandingProps, LandingState> {
<div className="clearfix lg-py4 md-py4 sm-pb4 sm-pt2" style={{ backgroundColor: colors.grey100 }}>
<div className="mx-auto max-width-4 py4 clearfix">
{isSmallScreen && this._renderTokenCloud()}
- <div className="col lg-col-6 md-col-6 col-12" style={{ color: colors.darkestGrey }}>
- <div className="mx-auto" style={{ maxWidth: 385, paddingTop: 7 }}>
+ <div
+ className="col lg-col-6 md-col-6 col-12 center"
+ style={{ color: colors.darkestGrey, height: 364, lineHeight: '364px' }}
+ >
+ <div
+ className="mx-auto inline-block lg-align-middle md-align-middle sm-align-top"
+ style={{ maxWidth: 385, lineHeight: '44px', textAlign: 'left' }}
+ >
<div className="lg-h1 md-h1 sm-h2 sm-center sm-pt3" style={{ fontFamily: 'Roboto Mono' }}>
- The world's value is becoming tokenized
+ {this.props.translate.get(Key.TokenizedSectionHeader, Deco.Cap)}
</div>
<div
className="pb2 lg-pt2 md-pt2 sm-pt3 sm-px3 h5 sm-center"
- style={{ fontFamily: 'Roboto Mono', lineHeight: 1.7 }}
+ style={{ fontFamily: 'Roboto Mono', lineHeight: 1.7, maxWidth: 370 }}
>
- {isSmallScreen ? (
- <span>
- The Ethereum blockchain is an open, borderless financial system that represents
- a wide variety of assets as cryptographic tokens. In the future, most digital
- assets and goods will be tokenized.
- </span>
- ) : (
- <div>
- <div>
- The Ethereum blockchain is an open, borderless financial system that
- represents
- </div>
- <div>
- a wide variety of assets as cryptographic tokens. In the future, most
- digital assets and goods will be tokenized.
- </div>
- </div>
- )}
+ {this.props.translate.get(Key.TokenizedSectionDescription, Deco.Cap)}
</div>
<div className="flex pt1 sm-px3">{this._renderAssetTypes()}</div>
</div>
@@ -430,28 +408,36 @@ export class Landing extends React.Component<LandingProps, LandingState> {
<img src="/images/landing/relayer_diagram.png" height={isSmallScreen ? 326 : 426} />
</div>
<div
- className="col lg-col-6 md-col-6 col-12 lg-pr3 md-pr3 sm-mx-auto lg-pt4 md-pt4 lg-mt3 md-mt3"
+ className="col lg-col-6 md-col-6 col-12 lg-pr3 md-pr3 sm-mx-auto"
style={{
color: colors.beigeWhite,
maxWidth: isSmallScreen ? 'none' : 445,
+ height: 430,
+ lineHeight: '430px',
}}
>
- <div className="lg-h1 md-h1 sm-h2 pb1 sm-pt3 sm-center" style={{ fontFamily: 'Roboto Mono' }}>
- <div>Off-chain order relay</div>
- <div>On-chain settlement</div>
- </div>
<div
- className="pb2 pt2 h5 sm-center sm-px3 sm-mx-auto"
- style={{
- fontFamily: 'Roboto Mono',
- lineHeight: 1.7,
- fontWeight: 300,
- maxWidth: 445,
- }}
+ className="inline-block lg-align-middle md-align-middle sm-align-top"
+ style={{ lineHeight: '43px' }}
>
- In 0x protocol, orders are transported off-chain, massively reducing gas costs and
- eliminating blockchain bloat. Relayers help broadcast orders and collect a fee each time
- they facilitate a trade. Anyone can build a relayer.
+ <div
+ className="lg-h1 md-h1 sm-h2 pb1 sm-pt3 sm-center"
+ style={{ fontFamily: 'Roboto Mono' }}
+ >
+ <div>{this.props.translate.get(Key.OffChainOrderRelay, Deco.Cap)}</div>
+ <div> {this.props.translate.get(Key.OonChainSettlement, Deco.Cap)}</div>
+ </div>
+ <div
+ className="pb2 pt2 h5 sm-center sm-px3 sm-mx-auto"
+ style={{
+ fontFamily: 'Roboto Mono',
+ lineHeight: 1.7,
+ fontWeight: 300,
+ maxWidth: 445,
+ }}
+ >
+ {this.props.translate.get(Key.OffChainOnChainDescription, Deco.Cap)}
+ </div>
</div>
</div>
</div>
@@ -485,15 +471,13 @@ export class Landing extends React.Component<LandingProps, LandingState> {
className="pb1 lg-pt4 md-pt4 sm-pt3 lg-h1 md-h1 sm-h2 sm-px3 sm-center"
style={{ fontFamily: 'Roboto Mono' }}
>
- A building block for dApps
+ {this.props.translate.get(Key.BuildingBlockSectionHeader, Deco.Cap)}
</div>
<div className="pb3 pt2 sm-mx-auto sm-center" style={descriptionStyle}>
- 0x protocol is a pluggable building block for dApps that require exchange functionality.
- Join the many developers that are already using 0x in their web applications and smart
- contracts.
+ {this.props.translate.get(Key.BuildingBlockSectionDescription, Deco.Cap)}
</div>
<div className="sm-mx-auto sm-center" style={callToActionStyle}>
- Learn how in our{' '}
+ {this.props.translate.get(Key.DevToolsPrompt, Deco.Cap)}{' '}
<Link
to={WebsitePaths.ZeroExJs}
className="text-decoration-none underline"
@@ -501,15 +485,15 @@ export class Landing extends React.Component<LandingProps, LandingState> {
>
0x.js
</Link>{' '}
- and{' '}
+ {this.props.translate.get(Key.And)}{' '}
<Link
to={WebsitePaths.SmartContracts}
className="text-decoration-none underline"
style={{ color: colors.beigeWhite, fontFamily: 'Roboto Mono' }}
>
- smart contract
+ {this.props.translate.get(Key.SmartContract)}
</Link>{' '}
- docs
+ {this.props.translate.get(Key.Docs)}
</div>
</div>
{!isSmallScreen && this._renderBlockChipImage()}
@@ -537,11 +521,11 @@ export class Landing extends React.Component<LandingProps, LandingState> {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
const assetTypes: AssetType[] = [
{
- title: 'Currency',
+ title: this.props.translate.get(Key.Currency, Deco.Cap),
imageUrl: '/images/landing/currency.png',
},
{
- title: 'Traditional assets',
+ title: this.props.translate.get(Key.TraditionalAssets, Deco.Cap),
imageUrl: '/images/landing/stocks.png',
style: {
paddingLeft: isSmallScreen ? 41 : 56,
@@ -549,7 +533,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
},
},
{
- title: 'Digital goods',
+ title: this.props.translate.get(Key.DigitalGoods, Deco.Cap),
imageUrl: '/images/landing/digital_goods.png',
},
];
@@ -566,6 +550,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
fontSize: 13.5,
fontWeight: 400,
color: colors.darkestGrey,
+ lineHeight: 1.4,
}}
>
{assetType.title}
@@ -578,12 +563,32 @@ export class Landing extends React.Component<LandingProps, LandingState> {
private _renderInfoBoxes() {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
const boxStyle: React.CSSProperties = {
- maxWidth: 252,
- height: 386,
+ maxWidth: 253,
+ height: 402,
backgroundColor: colors.grey50,
borderRadius: 5,
padding: '10px 24px 24px',
};
+ const boxContents: BoxContent[] = [
+ {
+ title: this.props.translate.get(Key.BenefitOneTitle, Deco.Cap),
+ description: this.props.translate.get(Key.BenefitOneDescription, Deco.Cap),
+ imageUrl: '/images/landing/distributed_network.png',
+ classNames: '',
+ },
+ {
+ title: this.props.translate.get(Key.BenefitTwoTitle, Deco.Cap),
+ description: this.props.translate.get(Key.BenefitTwoDescription, Deco.Cap),
+ imageUrl: '/images/landing/liquidity.png',
+ classNames: 'mx-auto',
+ },
+ {
+ title: this.props.translate.get(Key.BenefitThreeTitle, Deco.Cap),
+ description: this.props.translate.get(Key.BenefitThreeDescription, Deco.Cap),
+ imageUrl: '/images/landing/open_source.png',
+ classNames: 'right',
+ },
+ ];
const boxes = _.map(boxContents, (boxContent: BoxContent) => {
return (
<div key={`box-${boxContent.title}`} className="col lg-col-4 md-col-4 col-12 sm-pb4">
@@ -604,14 +609,13 @@ export class Landing extends React.Component<LandingProps, LandingState> {
const titleStyle: React.CSSProperties = {
fontFamily: 'Roboto Mono',
color: colors.grey,
- textTransform: 'uppercase',
fontWeight: 300,
letterSpacing: 3,
};
return (
<div className="clearfix" style={{ backgroundColor: colors.heroGrey }}>
<div className="center pb3 pt4" style={titleStyle}>
- Benefits of 0x
+ {this.props.translate.get(Key.BenefitsHeader, Deco.Upper)}
</div>
<div className="mx-auto pb4 sm-mt2 clearfix" style={{ maxWidth: '60em' }}>
{boxes}
@@ -625,41 +629,29 @@ export class Landing extends React.Component<LandingProps, LandingState> {
const useCases: UseCase[] = [
{
imageUrl: '/images/landing/governance_icon.png',
- type: 'Decentralized governance',
- description:
- 'Decentralized organizations use tokens to represent ownership and \
- guide their governance logic. 0x allows decentralized organizations \
- to seamlessly and safely trade ownership for startup capital.',
+ type: this.props.translate.get(Key.DecentralizedGovernance, Deco.Upper),
+ description: this.props.translate.get(Key.DecentralizedGovernanceDescription, Deco.Cap),
projectIconUrls: ['/images/landing/aragon.png'],
classNames: 'lg-px2 md-px2',
},
{
imageUrl: '/images/landing/prediction_market_icon.png',
- type: 'Prediction markets',
- description:
- 'Decentralized prediction market platforms generate sets of tokens that \
- represent a financial stake in the outcomes of real-world events. 0x allows \
- these tokens to be instantly tradable.',
+ type: this.props.translate.get(Key.PredictionMarkets, Deco.Upper),
+ description: this.props.translate.get(Key.PredictionMarketsDescription, Deco.Cap),
projectIconUrls: ['/images/landing/augur.png'],
classNames: 'lg-px2 md-px2',
},
{
imageUrl: '/images/landing/stable_tokens_icon.png',
- type: 'Stable tokens',
- description:
- 'Novel economic constructs such as stable coins require efficient, liquid \
- markets to succeed. 0x will facilitate the underlying economic mechanisms \
- that allow these tokens to remain stable.',
+ type: this.props.translate.get(Key.StableTokens, Deco.Upper),
+ description: this.props.translate.get(Key.StableTokensDescription, Deco.Cap),
projectIconUrls: ['/images/landing/maker.png'],
classNames: 'lg-px2 md-px2',
},
{
imageUrl: '/images/landing/loans_icon.png',
- type: 'Decentralized loans',
- description:
- 'Efficient lending requires liquid markets where investors can buy and re-sell loans. \
- 0x enables an ecosystem of lenders to self-organize and efficiently determine \
- market prices for all outstanding loans.',
+ type: this.props.translate.get(Key.DecentralizedLoans, Deco.Upper),
+ description: this.props.translate.get(Key.DecentralizedLoansDescription, Deco.Cap),
projectIconUrls: ['/images/landing/dharma.png', '/images/landing/lendroid.png'],
classNames: 'lg-pr2 md-pr2 lg-col-6 md-col-6',
style: {
@@ -670,11 +662,8 @@ export class Landing extends React.Component<LandingProps, LandingState> {
},
{
imageUrl: '/images/landing/fund_management_icon.png',
- type: 'Fund management',
- description:
- 'Decentralized fund management limits fund managers to investing in pre-agreed \
- upon asset classes. Embedding 0x into fund management smart contracts enables \
- them to enforce these security constraints.',
+ type: this.props.translate.get(Key.FundManagement, Deco.Upper),
+ description: this.props.translate.get(Key.FundManagementDescription, Deco.Cap),
projectIconUrls: ['/images/landing/melonport.png'],
classNames: 'lg-pl2 md-pl2 lg-col-6 md-col-6',
style: { width: 291, marginTop: !isSmallScreen ? 38 : 0 },
@@ -685,7 +674,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
const style = _.isUndefined(useCase.style) || isSmallScreen ? {} : useCase.style;
const useCaseBoxStyle = {
color: colors.grey,
- border: '1px solid #565656',
+ border: `1px solid ${colors.grey750}`,
borderRadius: 4,
maxWidth: isSmallScreen ? 375 : 'none',
...style,
@@ -741,38 +730,39 @@ export class Landing extends React.Component<LandingProps, LandingState> {
};
const lightButtonStyle: React.CSSProperties = {
borderRadius: 6,
- border: '1px solid #a0a0a0',
+ border: `1px solid ${colors.grey500}`,
lineHeight: '33px',
height: 49,
};
const callToActionClassNames =
- 'col lg-col-8 md-col-8 col-12 lg-pr3 md-pr3 \
- lg-right-align md-right-align sm-center sm-px3 h4';
+ 'lg-pr3 md-pr3 lg-right-align md-right-align sm-center sm-px3 h4 lg-table-cell md-table-cell';
return (
<div className="clearfix pb4" style={{ backgroundColor: colors.heroGrey }}>
- <div className="mx-auto max-width-4 pb4 mb3 clearfix">
- <div
- className={callToActionClassNames}
- style={{
- fontFamily: 'Roboto Mono',
- color: colors.white,
- lineHeight: isSmallScreen ? 1.7 : 3,
- }}
- >
- Get started on building the decentralized future
- </div>
- <div className="col lg-col-4 md-col-4 col-12 sm-center sm-pt2">
- <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 150 }}
- buttonStyle={lightButtonStyle}
- labelColor={colors.white}
- backgroundColor={colors.heroGrey}
- labelStyle={buttonLabelStyle}
- label="Build on 0x"
- onClick={_.noop}
- />
- </Link>
+ <div className="mx-auto max-width-4 pb4 mb3 clearfix center">
+ <div className="center inline-block" style={{ textAlign: 'left' }}>
+ <div
+ className={callToActionClassNames}
+ style={{
+ fontFamily: 'Roboto Mono',
+ color: colors.white,
+ lineHeight: isSmallScreen ? 1.7 : 3,
+ }}
+ >
+ {this.props.translate.get(Key.FinalCallToAction, Deco.Cap)}
+ </div>
+ <div className="sm-center sm-pt2 lg-table-cell md-table-cell">
+ <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
+ <RaisedButton
+ style={{ borderRadius: 6, minWidth: 150 }}
+ buttonStyle={lightButtonStyle}
+ labelColor={colors.white}
+ backgroundColor={colors.heroGrey}
+ labelStyle={buttonLabelStyle}
+ label={this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
+ onClick={_.noop}
+ />
+ </Link>
+ </div>
</div>
</div>
</div>
@@ -786,4 +776,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
});
}
}
+ private _onLanguageSelected(language: Language) {
+ this.props.dispatcher.updateSelectedLanguage(language);
+ }
} // tslint:disable:max-file-line-count
diff --git a/packages/website/ts/pages/not_found.tsx b/packages/website/ts/pages/not_found.tsx
index 0a6ec071c..ad37f6242 100644
--- a/packages/website/ts/pages/not_found.tsx
+++ b/packages/website/ts/pages/not_found.tsx
@@ -2,10 +2,14 @@ import * as _ from 'lodash';
import * as React from 'react';
import { Footer } from 'ts/components/footer';
import { TopBar } from 'ts/components/top_bar/top_bar';
+import { Dispatcher } from 'ts/redux/dispatcher';
import { Styles } from 'ts/types';
+import { Translate } from 'ts/utils/translate';
export interface NotFoundProps {
location: Location;
+ translate: Translate;
+ dispatcher: Dispatcher;
}
interface NotFoundState {}
@@ -20,7 +24,7 @@ export class NotFound extends React.Component<NotFoundProps, NotFoundState> {
public render() {
return (
<div>
- <TopBar blockchainIsLoaded={false} location={this.props.location} />
+ <TopBar blockchainIsLoaded={false} location={this.props.location} translate={this.props.translate} />
<div className="mx-auto max-width-4 py4">
<div className="center py4">
<div className="py4">
@@ -35,7 +39,7 @@ export class NotFound extends React.Component<NotFoundProps, NotFoundState> {
</div>
</div>
</div>
- <Footer />
+ <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
</div>
);
}
diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx
index bbbda6eee..56c3be0fe 100644
--- a/packages/website/ts/pages/wiki/wiki.tsx
+++ b/packages/website/ts/pages/wiki/wiki.tsx
@@ -8,10 +8,12 @@ import { TopBar } from 'ts/components/top_bar/top_bar';
import { MarkdownSection } from 'ts/pages/shared/markdown_section';
import { NestedSidebarMenu } from 'ts/pages/shared/nested_sidebar_menu';
import { SectionHeader } from 'ts/pages/shared/section_header';
+import { Dispatcher } from 'ts/redux/dispatcher';
import { Article, ArticlesBySection, HeaderSizes, Styles, WebsitePaths } from 'ts/types';
import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
import { utils } from 'ts/utils/utils';
const TOP_BAR_HEIGHT = 60;
@@ -20,6 +22,8 @@ const WIKI_NOT_READY_BACKOUT_TIMEOUT_MS = 5000;
export interface WikiProps {
source: string;
location: Location;
+ dispatcher: Dispatcher;
+ translate: Translate;
}
interface WikiState {
@@ -79,6 +83,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> {
blockchainIsLoaded={false}
location={this.props.location}
menuSubsectionsBySection={menuSubsectionsBySection}
+ translate={this.props.translate}
/>
{_.isUndefined(this.state.articlesBySection) ? (
<div className="col col-12" style={mainContainersStyle}>
diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts
index 925f2aa29..5c40ded2c 100644
--- a/packages/website/ts/redux/dispatcher.ts
+++ b/packages/website/ts/redux/dispatcher.ts
@@ -6,6 +6,7 @@ import {
ActionTypes,
AssetToken,
BlockchainErrs,
+ Language,
Order,
ProviderType,
ScreenWidths,
@@ -211,4 +212,10 @@ export class Dispatcher {
data: injectedProviderName,
});
}
+ public updateSelectedLanguage(language: Language) {
+ this._dispatch({
+ type: ActionTypes.UpdateSelectedLanguage,
+ data: language,
+ });
+ }
}
diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts
index c2a21dc07..1f489db85 100644
--- a/packages/website/ts/redux/reducer.ts
+++ b/packages/website/ts/redux/reducer.ts
@@ -13,6 +13,7 @@ import {
SideToAssetToken,
TokenByAddress,
} from 'ts/types';
+import { Translate } from 'ts/utils/translate';
import { utils } from 'ts/utils/utils';
// Instead of defaulting the docs version to an empty string, we pre-populate it with
@@ -49,6 +50,7 @@ export interface State {
flashMessage: string | React.ReactNode;
providerType: ProviderType;
injectedProviderName: string;
+ translate: Translate;
}
const INITIAL_STATE: State = {
@@ -86,13 +88,17 @@ const INITIAL_STATE: State = {
flashMessage: undefined,
providerType: ProviderType.Injected,
injectedProviderName: '',
+ translate: new Translate(),
};
export function reducer(state: State = INITIAL_STATE, action: Action) {
switch (action.type) {
// Portal
case ActionTypes.ResetState:
- return INITIAL_STATE;
+ return {
+ ...INITIAL_STATE,
+ translate: state.translate,
+ };
case ActionTypes.UpdateOrderSalt: {
return {
@@ -101,6 +107,13 @@ export function reducer(state: State = INITIAL_STATE, action: Action) {
};
}
+ case ActionTypes.UpdateSelectedLanguage: {
+ return {
+ ...state,
+ translate: new Translate(action.data),
+ };
+ }
+
case ActionTypes.UpdateNodeVersion: {
return {
...state,
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts
index 645c9cc11..f6413eec5 100644
--- a/packages/website/ts/types.ts
+++ b/packages/website/ts/types.ts
@@ -140,6 +140,7 @@ export enum ActionTypes {
HideFlashMessage = 'HIDE_FLASH_MESSAGE',
UpdateProviderType = 'UPDATE_PROVIDER_TYPE',
UpdateInjectedProviderName = 'UPDATE_INJECTED_PROVIDER_NAME',
+ UpdateSelectedLanguage = 'UPDATE_SELECTED_LANGUAGE',
}
export interface Action {
@@ -667,4 +668,79 @@ export interface MaterialUIPosition {
horizontal: 'left' | 'middle' | 'right';
}
+export enum Language {
+ English = 'EN',
+ Spanish = 'ES',
+ Chinese = 'ZH',
+ Korean = 'KO',
+ Russian = 'RU',
+}
+
+export enum Key {
+ TopHeader = 'TOP_HEADER',
+ TopTagline = 'TOP_TAGLINE',
+ BuildCallToAction = 'BUILD_CALL_TO_ACTION',
+ CommunityCallToAction = 'COMMUNITY_CALL_TO_ACTION',
+ ProjectsHeader = 'PROJECTS_HEADER',
+ FullListPrompt = 'FULL_LIST_PROMPT',
+ FullListLink = 'FULL_LIST_LINK',
+ TokenizedSectionHeader = 'TOKENIZED_SECTION_HEADER',
+ TokenizedSectionDescription = 'TOKENIZED_SECTION_DESCRIPTION',
+ Currency = 'CURRENCY',
+ TraditionalAssets = 'TRADITIONAL_ASSETS',
+ DigitalGoods = 'DIGITAL_GOODS',
+ OffChainOrderRelay = 'OFFCHAIN_ORDER_RELAY',
+ OonChainSettlement = 'OONCHAIN_SETTLEMENT',
+ OffChainOnChainDescription = 'OFFCHAIN_ONCHAIN_DESCRIPTION',
+ RelayersHeader = 'RELAYERS_HEADER',
+ BenefitsHeader = 'BENEFITS_HEADER',
+ BenefitOneTitle = 'BENEFIT_ONE_TITLE',
+ BenefitOneDescription = 'BENEFIT_ONE_DESCRIPTION',
+ BenefitTwoTitle = 'BENEFIT_TWO_TITLE',
+ BenefitTwoDescription = 'BENEFIT_TWO_DESCRIPTION',
+ BenefitThreeTitle = 'BENEFIT_THREE_TITLE',
+ BenefitThreeDescription = 'BENEFIT_THREE_DESCRIPTION',
+ BuildingBlockSectionHeader = 'BUILDING_BLOCK_SECTION_HEADER',
+ BuildingBlockSectionDescription = 'BUILDING_BLOCK_SECTION_DESCRIPTION',
+ DevToolsPrompt = 'DEV_TOOLS_PROMPT',
+ SmartContract = 'SMART_CONTRACT',
+ Docs = 'DOCS',
+ DecentralizedGovernance = 'DECENTRALIZED_GOVERNANCE',
+ DecentralizedGovernanceDescription = 'DECENTRALIZED_GOVERNANCE_DESCRIPTION',
+ PredictionMarkets = 'PREDICTION_MARKETS',
+ PredictionMarketsDescription = 'PREDICTION_MARKETS_DESCRIPTION',
+ StableTokens = 'STABLE_TOKENS',
+ StableTokensDescription = 'STABLE_TOKENS_DESCRIPTION',
+ DecentralizedLoans = 'DECENTRALIZED_LOANS',
+ DecentralizedLoansDescription = 'DECENTRALIZED_LOANS_DESCRIPTION',
+ FundManagement = 'FUND_MANAGEMENT',
+ FundManagementDescription = 'FUND_MANAGEMENT_DESCRIPTION',
+ FinalCallToAction = 'FINAL_CALL_TO_ACTION',
+ Documentation = 'DOCUMENTATION',
+ Community = 'COMMUNITY',
+ Organization = 'ORGANIZATION',
+ About = 'ABOUT',
+ Careers = 'CAREERS',
+ Contact = 'CONTACT',
+ Blog = 'BLOG',
+ Forum = 'FORUM',
+ Connect = 'CONNECT',
+ Whitepaper = 'WHITEPAPER',
+ Wiki = 'WIKI',
+ And = 'AND',
+ Faq = 'FAQ',
+ SmartContracts = 'SMART_CONTRACTS',
+ StandardRelayerApi = 'STANDARD_RELAYER_API',
+ PortalDApp = 'PORTAL_DAPP',
+ Website = 'WEBSITE',
+ Developers = 'DEVELOPERS',
+ Home = 'HOME',
+ RocketChat = 'ROCKETCHAT',
+}
+
+export enum Deco {
+ Cap,
+ CapWords,
+ Upper,
+}
// tslint:disable:max-file-line-count
diff --git a/packages/website/ts/utils/translate.ts b/packages/website/ts/utils/translate.ts
new file mode 100644
index 000000000..5148e48ad
--- /dev/null
+++ b/packages/website/ts/utils/translate.ts
@@ -0,0 +1,83 @@
+import * as _ from 'lodash';
+import { Deco, Key, Language } from 'ts/types';
+
+import * as chinese from '../../translations/chinese.json';
+import * as english from '../../translations/english.json';
+import * as korean from '../../translations/korean.json';
+import * as russian from '../../translations/russian.json';
+import * as spanish from '../../translations/spanish.json';
+
+const languageToTranslations = {
+ [Language.English]: english,
+ [Language.Spanish]: spanish,
+ [Language.Chinese]: chinese,
+ [Language.Korean]: korean,
+ [Language.Russian]: russian,
+};
+
+const languagesWithoutCaps = [Language.Chinese, Language.Korean];
+
+interface Translation {
+ [key: string]: string;
+}
+
+export class Translate {
+ private _selectedLanguage: Language;
+ private _translation: Translation;
+ constructor(desiredLanguage?: Language) {
+ if (!_.isUndefined(desiredLanguage)) {
+ this.setLanguage(desiredLanguage);
+ return;
+ }
+ const browserLanguage = (window.navigator as any).userLanguage || window.navigator.language || 'en-US';
+ let language = Language.English;
+ if (_.includes(browserLanguage, 'es-')) {
+ language = Language.Spanish;
+ } else if (_.includes(browserLanguage, 'zh-')) {
+ language = Language.Chinese;
+ } else if (_.includes(browserLanguage, 'ko-')) {
+ language = Language.Korean;
+ } else if (_.includes(browserLanguage, 'ru-')) {
+ language = Language.Russian;
+ }
+ this.setLanguage(language);
+ }
+ public getLanguage() {
+ return this._selectedLanguage;
+ }
+ public setLanguage(language: Language) {
+ const isLanguageSupported = !_.isUndefined(languageToTranslations[language]);
+ if (!isLanguageSupported) {
+ throw new Error(`${language} not supported`);
+ }
+ this._selectedLanguage = language;
+ this._translation = languageToTranslations[language];
+ }
+ public get(key: Key, decoration?: Deco) {
+ let text = this._translation[key];
+ if (!_.isUndefined(decoration) && !_.includes(languagesWithoutCaps, this._selectedLanguage)) {
+ switch (decoration) {
+ case Deco.Cap:
+ text = this._capitalize(text);
+ break;
+
+ case Deco.Upper:
+ text = text.toUpperCase();
+ break;
+
+ case Deco.CapWords:
+ const words = text.split(' ');
+ const capitalizedWords = _.map(words, w => this._capitalize(w));
+ text = capitalizedWords.join(' ');
+ break;
+
+ default:
+ throw new Error(`Unrecognized decoration: ${decoration}`);
+ }
+ }
+ return text;
+ }
+ private _capitalize(text: string) {
+ return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
+ }
+}