Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added i18n #105

Open
wants to merge 3 commits into
base: canary
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added client/assets/i18next.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 9 additions & 4 deletions client/components/App/AppComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,27 @@ import React from 'react';
import PropTypes from 'prop-types';
import 'normalize.css/normalize.css';
import 'react-mdl/extra/css/material.cyan-red.min.css';
import { translate } from 'react-i18next';
import Navbar from '../Navbar/NavbarComponent';
import Footer from '../Footer/FooterContainer';
import styles from './App.scss';
import yeoman from '../../assets/yeoman.png';

export default class App extends React.Component {
class App extends React.Component {
static propTypes = {
children: PropTypes.object.isRequired,
viewer: PropTypes.object.isRequired
viewer: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};

render() {
const { t } = this.props;
return (
<div className={styles.root}>
<Navbar />
<div className={styles.greeting}>
<h1 className={styles.sawasdee}>Sawasdee, Sawasdee!</h1>
<p>Always a pleasure scaffolding your apps</p>
<h1 className={styles.sawasdee}>{t('sawasdee')}</h1>
<p>{t('pleasure')}</p>
<img src={yeoman} alt='yeoman' />
</div>
<div className={styles.content}>
Expand All @@ -31,3 +34,5 @@ export default class App extends React.Component {
);
}
}

export default translate()(App);
11 changes: 8 additions & 3 deletions client/components/Feature/AddFeatureComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import Dropdown from 'react-dropdown';
import { Grid, Cell, Button } from 'react-mdl';
import { translate } from 'react-i18next';
import Page from '../Page/PageComponent';
import AddFeatureMutation from './AddFeatureMutation';

Expand All @@ -19,10 +20,11 @@ const inputData = {
css: { name: 'css', url: 'https://www.w3.org/Style/CSS/Overview.en.html', description: 'Cascading Style Sheets (CSS) is a simple mechanism for adding style to Web documents.' }
};

export default class AddFeature extends React.Component {
class AddFeature extends React.Component {
static propTypes = {
viewer: PropTypes.object.isRequired,
relay: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};

state = {
Expand All @@ -48,17 +50,20 @@ export default class AddFeature extends React.Component {
}

render() {
const { t } = this.props;
return (
<Page heading='Add a Feature'>
<Page heading={t('addFeature')}>
<Grid>
<Cell col={9}>
<Dropdown options={options} onChange={this.onSelect.bind(this)} value={this.state.form.dropdown} />
</Cell>
<Cell col={3} style={{ textAlign: 'center' }}>
<Button raised accent onClick={this.addFeature.bind(this)}>Add Feature</Button>
<Button raised accent onClick={this.addFeature.bind(this)}>{t('addFeature')}</Button>
</Cell>
</Grid>
</Page>
);
}
}

export default translate()(AddFeature);
9 changes: 7 additions & 2 deletions client/components/Feature/FeatureComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Grid, Cell, Card, CardTitle, CardText, CardActions, Button } from 'react-mdl';
import { translate } from 'react-i18next';
import Page from '../Page/PageComponent';
import styles from './Feature.scss';
import AddFeature from './AddFeatureComponent';

export default class Feature extends React.Component {
class Feature extends React.Component {
static propTypes = {
viewer: PropTypes.object.isRequired,
relay: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};

render() {
const { t } = this.props;
return (
<div>
<Page heading='Integrated with'>
<Page heading={t('integratedWith')}>
<Grid>
{this.props.viewer.features.edges.map((edge) => {
const imageUrl = require(`../../assets/${edge.node.name.toLowerCase()}.png`);
Expand All @@ -41,3 +44,5 @@ export default class Feature extends React.Component {
);
}
}

export default translate()(Feature);
9 changes: 7 additions & 2 deletions client/components/Footer/FooterComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Footer as MDLFooter, FooterSection } from 'react-mdl';
import { translate } from 'react-i18next';
import styles from './Footer.scss';

export default class Footer extends React.Component {
class Footer extends React.Component {
static propTypes = {
viewer: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};

render() {
const { t } = this.props;
return (
<MDLFooter className={styles.root} size='mini'>
<FooterSection type='middle'>
<span>Handcrafted with ♥ by <a href={this.props.viewer.website}> @{this.props.viewer.username}</a></span>
<span>{t('handcrafted')} <a href={this.props.viewer.website}> @{this.props.viewer.username}</a></span>
</FooterSection>
</MDLFooter>
);
}
}

export default translate()(Footer);
19 changes: 14 additions & 5 deletions client/components/Login/LoginComponent.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
// @flow
/* eslint-disable jsx-a11y/href-no-hash */
import React from 'react';
import PropTypes from 'prop-types';
import { Grid, Cell, Textfield, Button, Checkbox } from 'react-mdl';
import { translate } from 'react-i18next';
import Page from '../Page/PageComponent';

export default class Login extends React.Component {
class Login extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired
};

render() {
const { t } = this.props;
return (
<Page heading='Login'>
<div style={{ width: '70%', margin: 'auto' }}>
<Grid>
<form style={{ margin: 'auto' }}>
<Cell col={12}>
<Textfield onChange={() => {}} label='Username' />
<Textfield onChange={() => {}} label={t('username')} />
</Cell>
<Cell col={12}>
<Textfield onChange={() => {}} label='Password' type='password' />
<Textfield onChange={() => {}} label={t('password')} type='password' />
</Cell>
<Cell col={12}>
<Checkbox label='Remember me' ripple style={{ textAlign: 'right' }} />
<Checkbox label={t('rememberMe')} ripple style={{ textAlign: 'right' }} />
</Cell>
<Cell col={12} style={{ textAlign: 'right' }}>
<a href='#'>Forgot password</a>
<a href='#'>{t('forgotPassword')}</a>
<Button primary>Login</Button>
</Cell>
</form>
Expand All @@ -31,3 +38,5 @@ export default class Login extends React.Component {
);
}
}

export default translate()(Login);
15 changes: 12 additions & 3 deletions client/components/Navbar/NavbarComponent.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
// @flow
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import { Layout, Header, Navigation, Drawer } from 'react-mdl';
import { translate } from 'react-i18next';
import styles from './Navbar.scss';

export default class Navbar extends React.Component {
class Navbar extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired
};

render() {
const title = 'Relay Fullstack';
const { t } = this.props;
return (
<Layout className={styles.root}>
<Header title={<Link to='/'>{title}</Link>} scroll>
<Navigation>
<Link to='/signup'>Sign up</Link>
<Link to='/signup'>{t('signUp')}</Link>
<Link to='/login'>Login</Link>
</Navigation>
</Header>
<Drawer title={<Link to='/' style={{ fontSize: '1.5em' }}>{title}</Link>} className='mdl-layout--small-screen-only'>
<Navigation>
<Link to='/signup'>Sign up</Link>
<Link to='/signup'>{t('signUp')}</Link>
<Link to='/login'>Login</Link>
</Navigation>
</Drawer>
</Layout>
);
}
}

export default translate()(Navbar);
20 changes: 15 additions & 5 deletions client/components/Signup/SignupComponent.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
// @flow
import React from 'react';
import PropTypes from 'prop-types';
import { Grid, Cell, Textfield, Button } from 'react-mdl';
import { translate } from 'react-i18next';
import Page from '../Page/PageComponent';

export default class Signup extends React.Component {
class Signup extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired
};

render() {
const { t } = this.props;
return (
<Page heading='Signup'>
<Page heading={t('signUp')}>
<div style={{ width: '70%', margin: 'auto' }}>
<Grid>
<form style={{ margin: 'auto' }}>
<Cell col={12}>
<Textfield onChange={() => {}} label='Username' />
<Textfield onChange={() => {}} label={t('username')} />
</Cell>
<Cell col={12}>
<Textfield onChange={() => {}} label='Password' type='password' />
<Textfield onChange={() => {}} label={t('password')} type='password' />
</Cell>
<Cell col={12} style={{ textAlign: 'right' }}>
<Button primary>Sign up</Button>
<Button primary>{t('signUp')}</Button>
</Cell>
</form>
</Grid>
Expand All @@ -26,3 +33,6 @@ export default class Signup extends React.Component {
);
}
}

export default translate()(Signup);

49 changes: 49 additions & 0 deletions client/i18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import i18next from 'i18next';

i18next.init({
// we init with resources
resources: {
en: {
translations: {
sawasdee: 'Sawasdee, Sawasdee!',
pleasure: 'Always a pleasure scaffolding your apps',
handcrafted: 'Handcrafted with ♥ by',
signUp: 'Sign Up',
addFeature: 'Add Feature',
forgotPassword: 'Forgot password',
rememberMe: 'Remember me',
password: 'Password',
username: 'Username',
integratedWith: 'Integrated with',
}
},
ptBr: {
translations: {
sawasdee: 'Sawasdee, Sawasdee!',
pleasure: 'Sempre um prazer fazer o scaffold dos seus apps',
handcrafted: 'Feito à mão com ♥ por',
signUp: 'Registrar',
addFeature: 'Adicionar Funcionalidade',
forgotPassword: 'Esqueci a senha',
rememberMe: 'Lembrar de mim',
password: 'Senha',
username: 'Usuário',
integratedWith: 'Integrado com'
}
}
},
fallbackLng: 'en',

// have a common namespace used around the full app
ns: ['translations'],
defaultNS: 'translations',

keySeparator: false, // we use content as keys

interpolation: {
escapeValue: false, // not needed for react!!
formatSeparator: ','
}
});

export default i18next;
12 changes: 12 additions & 0 deletions client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { translate } from 'react-i18next';
import '../node_modules/react-mdl/extra/material';
import Root from './root';
import i18next from './i18n';

const rootNode = document.createElement('div');

translate.setI18n(i18next);
translate.setDefaults({
wait: false,
withRef: false,
bindI18n: 'languageChanged loaded',
bindStore: 'added removed',
nsMode: 'default',
translateFuncName: 't'
});

if (document.body) {
document.body.appendChild(rootNode);
}
Expand Down
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading