diff options
Diffstat (limited to 'packages/website/ts')
-rw-r--r-- | packages/website/ts/components/footer.tsx | 180 | ||||
-rw-r--r-- | packages/website/ts/components/top_bar/top_bar.tsx | 67 | ||||
-rw-r--r-- | packages/website/ts/pages/landing/landing.tsx | 181 | ||||
-rw-r--r-- | packages/website/ts/translations/english.ts | 78 | ||||
-rw-r--r-- | packages/website/ts/types.ts | 71 | ||||
-rw-r--r-- | packages/website/ts/utils/translate.ts | 54 |
6 files changed, 412 insertions, 219 deletions
diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx index a0f1a0c96..a5bddc874 100644 --- a/packages/website/ts/components/footer.tsx +++ b/packages/website/ts/components/footer.tsx @@ -1,9 +1,10 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Link } from 'react-router-dom'; -import { WebsitePaths } from 'ts/types'; +import { Deco, Key, 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,86 +16,8 @@ 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', @@ -108,12 +31,90 @@ const titleToIcon: { [title: string]: string } = { Forum: 'discourse.png', }; -export interface FooterProps {} +export interface FooterProps { + translate?: Translate; +} interface FooterState {} export class Footer extends React.Component<FooterProps, FooterState> { + public static defaultProps: Partial<FooterProps> = { + translate: new Translate(), + }; 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', + }, + ], + }; 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 }}> @@ -137,20 +138,20 @@ export class Footer extends React.Component<FooterProps, FooterState> { <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> @@ -195,9 +196,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,7 +205,7 @@ 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> ); } diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index a412007f2..15bfe2a39 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; @@ -36,6 +37,7 @@ interface TopBarProps { docsInfo?: DocsInfo; style?: React.CSSProperties; isNightVersion?: boolean; + translate?: Translate; } interface TopBarState { @@ -79,6 +81,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { shouldFullWidth: false, style: {}, isNightVersion: false, + translate: new Translate(), }; constructor(props: TopBarProps) { super(props); @@ -95,10 +98,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 +115,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 +134,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 : {}; @@ -165,28 +180,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 +248,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 +336,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 +351,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/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index d4c934459..76ea2e1bd 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -5,9 +5,10 @@ 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 { Deco, Key, 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 +37,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 +157,7 @@ const relayerProjects: Project[] = [ export interface LandingProps { location: Location; + translate: Translate; } interface LandingState { @@ -193,11 +166,13 @@ interface LandingState { export class Landing extends React.Component<LandingProps, LandingState> { private _throttledScreenWidthUpdate: () => void; + private _translate: Translate; constructor(props: LandingProps) { super(props); this.state = { screenWidth: utils.getScreenWidth(), }; + this._translate = new Translate(); this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); } public componentDidMount() { @@ -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._translate} /> {this._renderHero()} - {this._renderProjects(relayersAndDappProjects, 'Projects building on 0x', colors.projectsGrey, false)} + {this._renderProjects( + relayersAndDappProjects, + this._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._translate.get(Key.RelayersHeader, Deco.Upper), + colors.heroGrey, + true, + )} {this._renderInfoBoxes()} {this._renderBuildingBlocksSection()} {this._renderUseCases()} {this._renderCallToAction()} - <Footer /> + <Footer translate={this._translate} /> </div> ); } @@ -260,7 +246,7 @@ export class Landing extends React.Component<LandingProps, LandingState> { fontSize: isSmallScreen ? 26 : 34, }} > - Powering decentralized exchange + {this._translate.get(Key.TopHeader, Deco.Cap)} </div> <div className="pt2 h5 sm-mx-auto" @@ -271,8 +257,7 @@ 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._translate.get(Key.TopTagline)} </div> <div className="pt3 clearfix sm-mx-auto" style={{ maxWidth: 342 }}> <div className="lg-pr2 md-pr2 col col-6 sm-center"> @@ -281,7 +266,7 @@ export class Landing extends React.Component<LandingProps, LandingState> { style={{ borderRadius: 6, minWidth: 157.36 }} buttonStyle={{ borderRadius: 6 }} labelStyle={buttonLabelStyle} - label="Build on 0x" + label={this._translate.get(Key.BuildCallToAction, Deco.Cap)} onClick={_.noop} /> </Link> @@ -298,7 +283,7 @@ export class Landing extends React.Component<LandingProps, LandingState> { labelColor="white" backgroundColor={colors.heroGrey} labelStyle={buttonLabelStyle} - label="Join the community" + label={this._translate.get(Key.CommunityCallToAction, Deco.Cap)} onClick={_.noop} /> </a> @@ -346,7 +331,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 +350,13 @@ export class Landing extends React.Component<LandingProps, LandingState> { fontSize: 14, }} > - view the{' '} + {this._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._translate.get(Key.FullListLink)} </Link> </div> </div> @@ -388,30 +372,13 @@ export class Landing extends React.Component<LandingProps, LandingState> { <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="lg-h1 md-h1 sm-h2 sm-center sm-pt3" style={{ fontFamily: 'Roboto Mono' }}> - The world's value is becoming tokenized + {this._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._translate.get(Key.TokenizedSectionDescription, Deco.Cap)} </div> <div className="flex pt1 sm-px3">{this._renderAssetTypes()}</div> </div> @@ -437,8 +404,8 @@ export class Landing extends React.Component<LandingProps, LandingState> { }} > <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>{this._translate.get(Key.OffChainOrderRelay, Deco.Cap)}</div> + <div> {this._translate.get(Key.OonChainSettlement, Deco.Cap)}</div> </div> <div className="pb2 pt2 h5 sm-center sm-px3 sm-mx-auto" @@ -449,9 +416,7 @@ export class Landing extends React.Component<LandingProps, LandingState> { maxWidth: 445, }} > - 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. + {this._translate.get(Key.OffChainOnChainDescription, Deco.Cap)} </div> </div> </div> @@ -485,15 +450,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._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._translate.get(Key.BuildingBlockSectionDescription, Deco.Cap)} </div> <div className="sm-mx-auto sm-center" style={callToActionStyle}> - Learn how in our{' '} + {this._translate.get(Key.DevToolsPrompt, Deco.Cap)}{' '} <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none underline" @@ -507,9 +470,9 @@ export class Landing extends React.Component<LandingProps, LandingState> { className="text-decoration-none underline" style={{ color: colors.beigeWhite, fontFamily: 'Roboto Mono' }} > - smart contract + {this._translate.get(Key.SmartContract)} </Link>{' '} - docs + {this._translate.get(Key.Docs)} </div> </div> {!isSmallScreen && this._renderBlockChipImage()} @@ -537,11 +500,11 @@ export class Landing extends React.Component<LandingProps, LandingState> { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const assetTypes: AssetType[] = [ { - title: 'Currency', + title: this._translate.get(Key.Currency, Deco.Cap), imageUrl: '/images/landing/currency.png', }, { - title: 'Traditional assets', + title: this._translate.get(Key.TraditionalAssets, Deco.Cap), imageUrl: '/images/landing/stocks.png', style: { paddingLeft: isSmallScreen ? 41 : 56, @@ -549,7 +512,7 @@ export class Landing extends React.Component<LandingProps, LandingState> { }, }, { - title: 'Digital goods', + title: this._translate.get(Key.DigitalGoods, Deco.Cap), imageUrl: '/images/landing/digital_goods.png', }, ]; @@ -584,6 +547,26 @@ export class Landing extends React.Component<LandingProps, LandingState> { borderRadius: 5, padding: '10px 24px 24px', }; + const boxContents: BoxContent[] = [ + { + title: this._translate.get(Key.BenefitOneTitle, Deco.Cap), + description: this._translate.get(Key.BenefitOneDescription, Deco.Cap), + imageUrl: '/images/landing/distributed_network.png', + classNames: '', + }, + { + title: this._translate.get(Key.BenefitTwoTitle, Deco.Cap), + description: this._translate.get(Key.BenefitTwoDescription, Deco.Cap), + imageUrl: '/images/landing/liquidity.png', + classNames: 'mx-auto', + }, + { + title: this._translate.get(Key.BenefitThreeTitle, Deco.Cap), + description: this._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 +587,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._translate.get(Key.BenefitsHeader, Deco.Upper)} </div> <div className="mx-auto pb4 sm-mt2 clearfix" style={{ maxWidth: '60em' }}> {boxes} @@ -625,41 +607,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._translate.get(Key.DecentralizedGovernance, Deco.Upper), + description: this._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._translate.get(Key.PredictionMarkets, Deco.Upper), + description: this._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._translate.get(Key.StableTokens, Deco.Upper), + description: this._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._translate.get(Key.DecentralizedLoans, Deco.Upper), + description: this._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 +640,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._translate.get(Key.FundManagement, Deco.Upper), + description: this._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 +652,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,7 +708,7 @@ 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, }; @@ -759,7 +726,7 @@ export class Landing extends React.Component<LandingProps, LandingState> { lineHeight: isSmallScreen ? 1.7 : 3, }} > - Get started on building the decentralized future + {this._translate.get(Key.FinalCallToAction, Deco.Cap)} </div> <div className="col lg-col-4 md-col-4 col-12 sm-center sm-pt2"> <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none"> @@ -769,7 +736,7 @@ export class Landing extends React.Component<LandingProps, LandingState> { labelColor={colors.white} backgroundColor={colors.heroGrey} labelStyle={buttonLabelStyle} - label="Build on 0x" + label={this._translate.get(Key.BuildCallToAction, Deco.Cap)} onClick={_.noop} /> </Link> diff --git a/packages/website/ts/translations/english.ts b/packages/website/ts/translations/english.ts new file mode 100644 index 000000000..ef6e42a55 --- /dev/null +++ b/packages/website/ts/translations/english.ts @@ -0,0 +1,78 @@ +import { Key } from 'ts/types'; + +export const english = { + // Landing Page + [Key.TopHeader]: 'powering decentralized exchange', + [Key.TopTagline]: + '0x is an open, permissionless protocol allowing for ERC20 tokens to be traded on the Ethereum blockchain.', + [Key.BuildCallToAction]: 'build on 0x', + [Key.CommunityCallToAction]: 'join the community', + [Key.ProjectsHeader]: 'projects building on 0x', + [Key.FullListPrompt]: 'view the', + [Key.FullListLink]: 'full list', + [Key.TokenizedSectionHeader]: "the world's value is becoming tokenized", + [Key.TokenizedSectionDescription]: + '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.', + [Key.Currency]: 'currency', + [Key.TraditionalAssets]: 'traditional assets', + [Key.DigitalGoods]: 'digital goods', + [Key.OffChainOrderRelay]: 'off-chain order relay', + [Key.OonChainSettlement]: 'on-chain settlement', + [Key.OffChainOnChainDescription]: + '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.', + [Key.RelayersHeader]: 'relayers building on 0x', + [Key.BenefitsHeader]: 'benefits of 0x', + [Key.BenefitOneTitle]: 'trustless exchange', + [Key.BenefitOneDescription]: + "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.", + [Key.BenefitTwoTitle]: 'shared liquidity', + [Key.BenefitTwoDescription]: + 'by sharing a standard API, relayers can easily aggregate liquidity pools, creating network effects around liquidity that compound as more relayers come online.', + [Key.BenefitThreeTitle]: 'open source', + [Key.BenefitThreeDescription]: + '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.', + [Key.BuildingBlockSectionHeader]: 'a building block for dApps', + [Key.BuildingBlockSectionDescription]: + '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.', + [Key.DevToolsPrompt]: 'learn how in our', + [Key.SmartContract]: 'smart contract', + [Key.Docs]: 'docs', + [Key.DecentralizedGovernance]: 'decentralized governance', + [Key.DecentralizedGovernanceDescription]: + '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.', + [Key.PredictionMarkets]: 'prediction markets', + [Key.PredictionMarketsDescription]: + '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.', + [Key.StableTokens]: 'stable tokens', + [Key.StableTokensDescription]: + '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.', + [Key.DecentralizedLoans]: 'decentralized loans', + [Key.DecentralizedLoansDescription]: + '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.', + [Key.FundManagement]: 'fund management', + [Key.FundManagementDescription]: + '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.', + [Key.FinalCallToAction]: 'get started on building the decentralized future', + // Footer + [Key.Documentation]: 'documentation', + [Key.Community]: 'community', + [Key.Organization]: 'organization', + [Key.About]: 'about', + [Key.Careers]: 'careers', + [Key.Contact]: 'contact', + [Key.Blog]: 'blog', + [Key.Forum]: 'forum', + [Key.Connect]: '0x Connect', + [Key.Whitepaper]: 'whitepaper', + [Key.Wiki]: 'wiki', + [Key.Faq]: 'FAQ', + [Key.SmartContracts]: '0x smart contracts', + [Key.StandardRelayerApi]: 'standard relayer API', + [Key.PortalDApp]: 'portal dApp', + [Key.Website]: 'website', + [Key.Home]: 'home', + [Key.FAQ]: 'FAQ', + [Key.RocketChat]: 'rocket.chat', + // TopBar + [Key.Developers]: 'developers', +}; diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 645c9cc11..8690bcfdd 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -667,4 +667,75 @@ export interface MaterialUIPosition { horizontal: 'left' | 'middle' | 'right'; } +export enum Language { + English = 'EN', +} + +export enum Key { + TopHeader, + TopTagline, + BuildCallToAction, + CommunityCallToAction, + ProjectsHeader, + FullListPrompt, + FullListLink, + TokenizedSectionHeader, + TokenizedSectionDescription, + Currency, + TraditionalAssets, + DigitalGoods, + OffChainOrderRelay, + OonChainSettlement, + OffChainOnChainDescription, + RelayersHeader, + BenefitsHeader, + BenefitOneTitle, + BenefitOneDescription, + BenefitTwoTitle, + BenefitTwoDescription, + BenefitThreeTitle, + BenefitThreeDescription, + BuildingBlockSectionHeader, + BuildingBlockSectionDescription, + DevToolsPrompt, + SmartContract, + Docs, + DecentralizedGovernance, + DecentralizedGovernanceDescription, + PredictionMarkets, + PredictionMarketsDescription, + StableTokens, + StableTokensDescription, + DecentralizedLoans, + DecentralizedLoansDescription, + FundManagement, + FundManagementDescription, + FinalCallToAction, + Documentation, + Community, + Organization, + About, + Careers, + Contact, + Blog, + Forum, + Connect, + Whitepaper, + Wiki, + Faq, + SmartContracts, + StandardRelayerApi, + PortalDApp, + Website, + Developers, + Home, + RocketChat, + FAQ, +} + +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..cddfef088 --- /dev/null +++ b/packages/website/ts/utils/translate.ts @@ -0,0 +1,54 @@ +import * as _ from 'lodash'; +import { english } from 'ts/translations/english'; +import { Deco, Key, Language } from 'ts/types'; + +const languageToTranslations = { + [Language.English]: english, +}; + +interface Translation { + [key: string]: string; +} + +export class Translate { + private _selectedLanguage: Language; + private _translation: Translation; + constructor() { + this.setLanguage(Language.English); + } + 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)) { + 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)}`; + } +} |