Implement i18n

This commit is contained in:
Pierre Burgy 2016-10-12 12:07:26 +02:00
parent ed4586ee7a
commit 6de2503b9c
39 changed files with 506 additions and 76 deletions

View File

@ -18,6 +18,7 @@ const store = configureStore(initialState, browserHistory);
// Set up the router, wrapping all Routes in the App component // Set up the router, wrapping all Routes in the App component
import App from 'containers/App'; import App from 'containers/App';
import createRoutes from './routes'; import createRoutes from './routes';
import { translationMessages } from './i18n';
// Plugin identifier based on the package.json `name` value // Plugin identifier based on the package.json `name` value
const pluginId = require('../package.json').name.replace(/^strapi-/i, ''); const pluginId = require('../package.json').name.replace(/^strapi-/i, '');
@ -33,6 +34,22 @@ if (window.Strapi) {
}, },
mainComponent: App, mainComponent: App,
routes: createRoutes(store), routes: createRoutes(store),
translationMessages,
});
}
// Hot reloadable translation json files
if (module.hot) {
// modules.hot.accept does not accept dynamic dependencies,
// have to be constants at compile-time
module.hot.accept('./i18n', () => {
if (window.Strapi) {
System.import('./i18n')
.then(result => {
const translationMessagesUpdated = result.translationMessages;
window.Strapi.refresh(pluginId).translationMessages(translationMessagesUpdated);
});
}
}); });
} }

View File

@ -11,27 +11,21 @@ import styles from './styles.scss';
class LeftMenu extends React.Component { // eslint-disable-line react/prefer-stateless-function class LeftMenu extends React.Component { // eslint-disable-line react/prefer-stateless-function
links = [{ links = [{
label: 'General',
value: 'general', value: 'general',
to: '', to: '',
}, { }, {
label: 'Languages',
value: 'languages', value: 'languages',
to: 'languages', to: 'languages',
}, { }, {
label: 'Databases',
value: 'databases', value: 'databases',
to: 'databases', to: 'databases',
}, { }, {
label: 'Security',
value: 'security', value: 'security',
to: 'security', to: 'security',
}, { }, {
label: 'Server',
value: 'server', value: 'server',
to: 'server', to: 'server',
}, { }, {
label: 'Advanced',
value: 'advanced', value: 'advanced',
to: 'advanced', to: 'advanced',
}]; }];

View File

@ -6,14 +6,22 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router';
import appMessages from 'containers/App/messages';
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss'; import styles from './styles.scss';
console.log('styles', styles);
class LeftMenuLink extends React.Component { // eslint-disable-line react/prefer-stateless-function class LeftMenuLink extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() { render() {
const messageKey = `${this.props.link.value}SectionTitle`;
return ( return (
<li className={styles.leftMenuLink}> <li className={styles.leftMenuLink}>
<Link className={styles.leftMenuLinkDestination} activeClassName={styles.leftMenuLinkDestinationActive} to={`/plugins/settings-manager/${this.props.link.to}`}>{this.props.link.label} <Link
className={styles.leftMenuLinkDestination}
activeClassName={styles.leftMenuLinkDestinationActive}
to={`/plugins/settings-manager/${this.props.link.to}`}
>
<FormattedMessage {...appMessages[messageKey]} />
<i className={`ion ion-arrow-right-c ${styles.leftMenuLinkIcon}`}></i> <i className={`ion ion-arrow-right-c ${styles.leftMenuLinkIcon}`}></i>
</Link> </Link>
</li> </li>

View File

@ -5,6 +5,8 @@
*/ */
import React from 'react'; import React from 'react';
import messages from './messages';
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss'; import styles from './styles.scss';
@ -17,7 +19,7 @@ class PluginHeaderActions extends React.Component { // eslint-disable-line react
className={`${styles.pluginHeaderActionsButton} btn btn-secondary`} className={`${styles.pluginHeaderActionsButton} btn btn-secondary`}
onClick={this.props.onCancel} onClick={this.props.onCancel}
> >
Cancel <FormattedMessage {...messages.cancelLabel} />
</button> </button>
<button <button
type="submit" type="submit"
@ -25,7 +27,7 @@ class PluginHeaderActions extends React.Component { // eslint-disable-line react
disabled={this.props.loading} disabled={this.props.loading}
onClick={this.props.onFormSubmit} onClick={this.props.onFormSubmit}
> >
Save <FormattedMessage {...messages.saveLabel} />
</button> </button>
</div> </div>
); );

View File

@ -0,0 +1,17 @@
/*
* PluginHeaderActions Messages
*
* This contains all the text for the PluginHeaderActions component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
cancelLabel: {
id: 'settings-manager.components.PluginHeaderActions.cancelLabel',
defaultMessage: 'Cancel',
},
saveLabel: {
id: 'settings-manager.components.PluginHeaderActions.saveLabel',
defaultMessage: 'Save',
},
});

View File

@ -5,15 +5,21 @@
*/ */
import React from 'react'; import React from 'react';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import styles from './styles.scss'; import styles from './styles.scss';
class PluginHeaderTitle extends React.Component { // eslint-disable-line react/prefer-stateless-function class PluginHeaderTitle extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() { render() {
return ( return (
<div className={styles.pluginHeaderTitle}> <div className={styles.pluginHeaderTitle}>
<h1 className={styles.pluginHeaderTitleName}>Settings Manager</h1> <h1 className={styles.pluginHeaderTitleName}>
<p className={styles.pluginHeaderTitleDescription}>Easily update your settings</p> <FormattedMessage {...messages.title} />
</h1>
<p className={styles.pluginHeaderTitleDescription}>
<FormattedMessage {...messages.description} />
</p>
</div> </div>
); );
} }

View File

@ -0,0 +1,17 @@
/*
* PluginHeaderTitle Messages
*
* This contains all the text for the PluginHeaderTitle component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
title: {
id: 'settings-manager.components.PluginHeaderTitle.title',
defaultMessage: 'Settings Manager',
},
description: {
id: 'settings-manager.components.PluginHeaderTitle.description',
defaultMessage: 'Easily configure your settings',
},
});

View File

@ -10,26 +10,44 @@ import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container'; import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle'; import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle'; import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages';
import messages from './messages';
import styles from './styles.scss'; import styles from './styles.scss';
export default class AdvancedPage extends React.Component { // eslint-disable-line react/prefer-stateless-function export class AdvancedPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() { render() {
const { formatMessage } = this.props.intl;
return ( return (
<div className={styles.advancedPage}> <div className={styles.advancedPage}>
<Helmet <Helmet
title="Settings Manager - Advanced" title="Settings Manager - Advanced"
meta={[ meta={[
{ name: 'description', content: 'Configure your Advanced settings.' }, { name: 'description', content: formatMessage(messages.rightSectionDescription) },
]} ]}
/> />
<div className="container"> <div className="container">
<PluginHeader></PluginHeader> <PluginHeader />
<Container> <Container>
<RightContentTitle title="Advanced" description="Configure your advanced settings."></RightContentTitle> <RightContentTitle
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle> title={formatMessage(appMessages.advancedSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container> </Container>
</div> </div>
</div> </div>
); );
} }
} }
AdvancedPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(AdvancedPage);

View File

@ -0,0 +1,13 @@
/*
* LanguagesPage Messages
*
* This contains all the text for the LanguagesPage component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
rightSectionDescription: {
id: 'settings-manager.components.AdvancedPage.rightSectionDescription',
defaultMessage: 'Configure your advanced settings',
},
});

View File

@ -0,0 +1,37 @@
/*
* App Messages
*
* This contains all the text use across the plugin.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
generalSectionTitle: {
id: 'settings-manager.components.App.generalSectionTitle',
defaultMessage: 'General',
},
languagesSectionTitle: {
id: 'settings-manager.components.App.languagesSectionTitle',
defaultMessage: 'Languages',
},
databasesSectionTitle: {
id: 'settings-manager.components.App.databasesSectionTitle',
defaultMessage: 'Databases',
},
securitySectionTitle: {
id: 'settings-manager.components.App.securitySectionTitle',
defaultMessage: 'Security',
},
serverSectionTitle: {
id: 'settings-manager.components.App.serverSectionTitle',
defaultMessage: 'Server',
},
advancedSectionTitle: {
id: 'settings-manager.components.App.advancedSectionTitle',
defaultMessage: 'Advanced',
},
comingSoon: {
id: 'settings-manager.components.App.comingSoon',
defaultMessage: 'Coming soon',
},
});

View File

@ -8,28 +8,45 @@ import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container'; import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle'; import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle'; import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages';
import messages from './messages';
import styles from './styles.scss'; import styles from './styles.scss';
export default class DatabasesPage extends React.Component { // eslint-disable-line react/prefer-stateless-function export class DatabasesPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() { render() {
const { formatMessage } = this.props.intl;
return ( return (
<div className={styles.databasesPage}> <div className={styles.databasesPage}>
<Helmet <Helmet
title="Settings Manager - Databases" title="Settings Manager - Databases"
meta={[ meta={[
{ name: 'description', content: 'Configure your Databases settings.' }, { name: 'description', content: formatMessage(messages.rightSectionDescription) },
]} ]}
/> />
<div className="container"> <div className="container">
<PluginHeader></PluginHeader> <PluginHeader />
<Container> <Container>
<RightContentTitle title="Databases" description="Configure your databases settings."></RightContentTitle> <RightContentTitle
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle> title={formatMessage(appMessages.databasesSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container> </Container>
</div> </div>
</div> </div>
); );
} }
} }
DatabasesPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(DatabasesPage);

View File

@ -1,13 +1,13 @@
/* /*
* DatabasesPage Messages * LanguagesPage Messages
* *
* This contains all the text for the DatabasesPage component. * This contains all the text for the LanguagesPage component.
*/ */
import { defineMessages } from 'react-intl'; import { defineMessages } from 'react-intl';
export default defineMessages({ export default defineMessages({
header: { rightSectionDescription: {
id: 'app.components.DatabasesPage.header', id: 'settings-manager.components.DatabasesPage.rightSectionDescription',
defaultMessage: 'This is the Databases page of the Settings Manager plugin!', defaultMessage: 'Configure your databases settings',
}, },
}); });

View File

@ -31,6 +31,9 @@ import {
updateGeneralSettings, updateGeneralSettings,
cancelGeneralSettings, cancelGeneralSettings,
} from 'containers/HomePage/actions'; } from 'containers/HomePage/actions';
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
import appMessages from 'containers/App/messages';
import messages from './messages';
import styles from './styles.scss'; import styles from './styles.scss';
@ -41,21 +44,31 @@ export class HomePage extends React.Component {
} }
render() { render() {
const { formatMessage } = this.props.intl;
return ( return (
<div> <div>
<div className="container"> <div className="container">
<PluginHeader {...this.props}></PluginHeader> <PluginHeader {...this.props}></PluginHeader>
<Container> <Container>
<RightContentTitle title="General" description="Configure your general settings."></RightContentTitle> <RightContentTitle
<RightContentSectionTitle title="Application" description="The general settings of your Strapi application."></RightContentSectionTitle> title={formatMessage(appMessages.generalSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(messages.rightContentSectionTitle)}
description={formatMessage(messages.rightContentSectionDescription)}
/>
<form onSubmit={this.props.onFormSubmit}> <form onSubmit={this.props.onFormSubmit}>
<div className={`form-group row ${styles.homePageRightContentFormGroup}`}> <div className={`form-group row ${styles.homePageRightContentFormGroup}`}>
<label htmlFor="applicationName" className="col-xs-7 col-form-label">Name</label> <label htmlFor="applicationName" className="col-xs-7 col-form-label">
<FormattedMessage {...messages.nameLabel} />
</label>
<div className="col-xs-5"> <div className="col-xs-5">
<input <input
className="form-control" className="form-control"
type="text" type="text"
placeholder="My Application" placeholder={formatMessage(messages.namePlaceholder)}
id="applicationName" id="applicationName"
value={this.props.name || ''} value={this.props.name || ''}
onChange={this.props.onChangeName} onChange={this.props.onChangeName}
@ -64,12 +77,14 @@ export class HomePage extends React.Component {
</div> </div>
</div> </div>
<div className={`form-group row ${styles.homePageRightContentFormGroup}`}> <div className={`form-group row ${styles.homePageRightContentFormGroup}`}>
<label htmlFor="applicationDescription" className="col-xs-7 col-form-label">Description</label> <label htmlFor="applicationDescription" className="col-xs-7 col-form-label">
<FormattedMessage {...messages.descriptionLabel} />
</label>
<div className="col-xs-5"> <div className="col-xs-5">
<input <input
className="form-control" className="form-control"
type="text" type="text"
placeholder="A Strapi application" placeholder={formatMessage(messages.descriptionPlaceholder)}
id="applicationDescription" id="applicationDescription"
value={this.props.description || ''} value={this.props.description || ''}
onChange={this.props.onChangeDescription} onChange={this.props.onChangeDescription}
@ -77,12 +92,14 @@ export class HomePage extends React.Component {
</div> </div>
</div> </div>
<div className={`form-group row ${styles.homePageRightContentFormGroup}`}> <div className={`form-group row ${styles.homePageRightContentFormGroup}`}>
<label htmlFor="applicationVersion" className="col-xs-7 col-form-label">Version</label> <label htmlFor="applicationVersion" className="col-xs-7 col-form-label">
<FormattedMessage {...messages.versionLabel} />
</label>
<div className="col-xs-5"> <div className="col-xs-5">
<input <input
className="form-control" className="form-control"
type="text" type="text"
placeholder="0.0.1" placeholder={formatMessage(messages.versionPlaceholder)}
id="applicationVersion" id="applicationVersion"
value={this.props.version || ''} value={this.props.version || ''}
onChange={this.props.onChangeVersion} onChange={this.props.onChangeVersion}
@ -108,6 +125,7 @@ HomePage.propTypes = {
React.PropTypes.object, React.PropTypes.object,
React.PropTypes.bool, React.PropTypes.bool,
]), ]),
intl: intlShape.isRequired,
loading: React.PropTypes.bool, loading: React.PropTypes.bool,
name: React.PropTypes.oneOfType([ name: React.PropTypes.oneOfType([
React.PropTypes.string, React.PropTypes.string,
@ -123,7 +141,6 @@ HomePage.propTypes = {
React.PropTypes.string, React.PropTypes.string,
React.PropTypes.bool, React.PropTypes.bool,
]), ]),
}; };
export function mapDispatchToProps(dispatch) { export function mapDispatchToProps(dispatch) {
@ -153,4 +170,4 @@ const mapStateToProps = createStructuredSelector({
}); });
// Wrap the component to inject dispatch and state into it // Wrap the component to inject dispatch and state into it
export default connect(mapStateToProps, mapDispatchToProps)(HomePage); export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(HomePage));

View File

@ -0,0 +1,49 @@
/*
* HomePage Messages
*
* This contains all the text for the HomePage component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
rightSectionDescription: {
id: 'settings-manager.components.HomePage.rightSectionDescription',
defaultMessage: 'Configure your general settings',
},
rightContentSectionTitle: {
id: 'settings-manager.components.HomePage.rightContentSectionTitle',
defaultMessage: 'Application',
},
rightContentSectionDescription: {
id: 'settings-manager.components.HomePage.rightContentSectionDescription',
defaultMessage: 'The general settings of your Strapi application.',
},
nameLabel: {
id: 'settings-manager.components.HomePage.nameLabel',
defaultMessage: 'Name',
},
namePlaceholder: {
id: 'settings-manager.components.HomePage.namePlaceholder',
defaultMessage: 'My Application',
},
descriptionLabel: {
id: 'settings-manager.components.HomePage.descriptionLabel',
defaultMessage: 'Description',
},
descriptionPlaceholder: {
id: 'settings-manager.components.HomePage.descriptionPlaceholder',
defaultMessage: 'A Strapi application',
},
versionLabel: {
id: 'settings-manager.components.HomePage.versionLabel',
defaultMessage: 'Version',
},
versionPlaceholder: {
id: 'settings-manager.components.HomePage.versionPlaceholder',
defaultMessage: '0.0.1',
},
submit: {
id: 'settings-manager.components.HomePage.submit',
defaultMessage: 'Submit',
},
});

View File

@ -8,27 +8,45 @@ import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container'; import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle'; import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle'; import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages';
import messages from './messages';
import styles from './styles.scss'; import styles from './styles.scss';
export default class LanguagesPage extends React.Component { // eslint-disable-line react/prefer-stateless-function export class LanguagesPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() { render() {
const { formatMessage } = this.props.intl;
return ( return (
<div className={styles.languagesPage}> <div className={styles.languagesPage}>
<Helmet <Helmet
title="Settings Manager - Languages" title="Settings Manager - Languages"
meta={[ meta={[
{ name: 'description', content: 'Configure your Languages settings.' }, { name: 'description', content: formatMessage(messages.rightSectionDescription) },
]} ]}
/> />
<div className="container"> <div className="container">
<PluginHeader></PluginHeader> <PluginHeader />
<Container> <Container>
<RightContentTitle title="Languages" description="Configure your languages settings."></RightContentTitle> <RightContentTitle
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle> title={formatMessage(appMessages.languagesSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container> </Container>
</div> </div>
</div> </div>
); );
} }
} }
LanguagesPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(LanguagesPage);

View File

@ -0,0 +1,13 @@
/*
* LanguagesPage Messages
*
* This contains all the text for the LanguagesPage component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
rightSectionDescription: {
id: 'settings-manager.components.LanguagesPage.rightSectionDescription',
defaultMessage: 'Configure your languages settings',
},
});

View File

@ -8,27 +8,44 @@ import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container'; import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle'; import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle'; import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages';
import messages from './messages';
import styles from './styles.scss'; import styles from './styles.scss';
export default class SecurityPage extends React.Component { // eslint-disable-line react/prefer-stateless-function export class SecurityPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() { render() {
const { formatMessage } = this.props.intl;
return ( return (
<div className={styles.securityPage}> <div className={styles.securityPage}>
<Helmet <Helmet
title="Settings Manager - Security" title="Settings Manager - Security"
meta={[ meta={[
{ name: 'description', content: 'Configure your security settings.' }, { name: 'description', content: formatMessage(messages.rightSectionDescription) },
]} ]}
/> />
<div className="container"> <div className="container">
<PluginHeader></PluginHeader> <PluginHeader />
<Container> <Container>
<RightContentTitle title="Security" description="Configure your security settings."></RightContentTitle> <RightContentTitle
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle> title={formatMessage(appMessages.securitySectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container> </Container>
</div> </div>
</div> </div>
); );
} }
} }
SecurityPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(SecurityPage);

View File

@ -0,0 +1,13 @@
/*
* LanguagesPage Messages
*
* This contains all the text for the LanguagesPage component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
rightSectionDescription: {
id: 'settings-manager.components.SecurityPage.rightSectionDescription',
defaultMessage: 'Configure your security settings',
},
});

View File

@ -8,27 +8,44 @@ import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container'; import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle'; import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle'; import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages';
import messages from './messages';
import styles from './styles.scss'; import styles from './styles.scss';
export default class ServerPage extends React.Component { // eslint-disable-line react/prefer-stateless-function export class ServerPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() { render() {
const { formatMessage } = this.props.intl;
return ( return (
<div className={styles.serverPage}> <div className={styles.serverPage}>
<Helmet <Helmet
title="Settings Manager - Server" title="Settings Manager - Server"
meta={[ meta={[
{ name: 'description', content: 'Configure your Server settings.' }, { name: 'description', content: formatMessage(messages.rightSectionDescription) },
]} ]}
/> />
<div className="container"> <div className="container">
<PluginHeader></PluginHeader> <PluginHeader />
<Container> <Container>
<RightContentTitle title="Server" description="Configure your server settings."></RightContentTitle> <RightContentTitle
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle> title={formatMessage(appMessages.serverSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container> </Container>
</div> </div>
</div> </div>
); );
} }
} }
ServerPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(ServerPage);

View File

@ -0,0 +1,13 @@
/*
* LanguagesPage Messages
*
* This contains all the text for the LanguagesPage component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
rightSectionDescription: {
id: 'settings-manager.components.ServerPage.rightSectionDescription',
defaultMessage: 'Configure your server settings',
},
});

View File

@ -4,17 +4,22 @@
* This will setup the i18n language files and locale data for your plugin. * This will setup the i18n language files and locale data for your plugin.
* *
*/ */
import { addLocaleData } from 'react-intl'; // import { addLocaleData } from 'react-intl';
import enLocaleData from 'react-intl/locale-data/en'; // import enLocaleData from 'react-intl/locale-data/en';
// import frLocaleData from 'react-intl/locale-data/fr';
export const appLocales = [ // export const appLocales = [
'en', // 'en',
]; // 'fr',
//
// ];
import enTranslationMessages from './translations/en.json'; import enTranslationMessages from './translations/en.json';
import frTranslationMessages from './translations/fr.json';
addLocaleData(enLocaleData); // addLocaleData(enLocaleData);
// addLocaleData(frLocaleData);
const formatTranslationMessages = (messages) => { const formatTranslationMessages = (messages) => {
const formattedMessages = {}; const formattedMessages = {};
@ -27,4 +32,5 @@ const formatTranslationMessages = (messages) => {
export const translationMessages = { export const translationMessages = {
en: formatTranslationMessages(enTranslationMessages), en: formatTranslationMessages(enTranslationMessages),
fr: formatTranslationMessages(frTranslationMessages),
}; };

View File

@ -1,2 +1,2 @@
// Import variables // Import variables
@import "variables/variables"; @import 'variables/variables';

View File

@ -1 +1,16 @@
[] [
{
"id": "app.components.DatabasesPage.header",
"defaultMessage": "This is the Databases page of the Settings Manager plugin!",
"message": ""
},
{
"id": "app.components.NotFoundPage.pageNotFound",
"defaultMessage": "Page not found.",
"message": ""
},
{
"id": "settings-manager.components.HomePage.header",
"defaultMessage": "This is HomePage components !"
}
]

View File

@ -0,0 +1,16 @@
[
{
"id": "app.components.DatabasesPage.header",
"defaultMessage": "This is the Databases page of the Settings Manager plugin!",
"message": ""
},
{
"id": "app.components.NotFoundPage.pageNotFound",
"defaultMessage": "Page not found.",
"message": ""
},
{
"id": "settings-manager.components.HomePage.header",
"defaultMessage": "Ceci est la page d'accueil!"
}
]

View File

@ -11,7 +11,7 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages'; import messages from './messages';
{{/if}} {{/if}}
{{#if wantCSS}} {{#if wantCSS}}
import styles from './styles.scss'; import styles from './styles.css';
{{/if}} {{/if}}
class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function

View File

@ -49,12 +49,12 @@ module.exports = {
abortOnFail: true, abortOnFail: true,
}]; }];
// If they want a CSS file, add styles.scss // If they want a CSS file, add styles.css
if (data.wantCSS) { if (data.wantCSS) {
actions.push({ actions.push({
type: 'add', type: 'add',
path: '../../app/components/{{properCase name}}/styles.scss', path: '../../app/components/{{properCase name}}/styles.css',
templateFile: './component/styles.scss.hbs', templateFile: './component/styles.css.hbs',
abortOnFail: true, abortOnFail: true,
}); });
} }

View File

@ -12,7 +12,7 @@ import messages from './messages';
{{/if}} {{/if}}
{{#if wantCSS}} {{#if wantCSS}}
import styles from './styles.scss'; import styles from './styles.css';
{{/if}} {{/if}}
function {{ properCase name }}() { function {{ properCase name }}() {

View File

@ -58,12 +58,12 @@ module.exports = {
abortOnFail: true, abortOnFail: true,
}]; }];
// If they want a CSS file, add styles.scss // If they want a CSS file, add styles.css
if (data.wantCSS) { if (data.wantCSS) {
actions.push({ actions.push({
type: 'add', type: 'add',
path: '../../app/containers/{{properCase name}}/styles.scss', path: '../../app/containers/{{properCase name}}/styles.css',
templateFile: './container/styles.scss.hbs', templateFile: './container/styles.css.hbs',
abortOnFail: true, abortOnFail: true,
}); });
} }

View File

@ -17,7 +17,7 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages'; import messages from './messages';
{{/if}} {{/if}}
{{#if wantCSS}} {{#if wantCSS}}
import styles from './styles.scss'; import styles from './styles.css';
{{/if}} {{/if}}
export class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function export class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function

View File

@ -8,11 +8,13 @@ const fs = require('fs');
const componentGenerator = require('./component/index.js'); const componentGenerator = require('./component/index.js');
const containerGenerator = require('./container/index.js'); const containerGenerator = require('./container/index.js');
const routeGenerator = require('./route/index.js'); const routeGenerator = require('./route/index.js');
const languageGenerator = require('./language/index.js');
module.exports = (plop) => { module.exports = (plop) => {
plop.setGenerator('component', componentGenerator); plop.setGenerator('component', componentGenerator);
plop.setGenerator('container', containerGenerator); plop.setGenerator('container', containerGenerator);
plop.setGenerator('route', routeGenerator); plop.setGenerator('route', routeGenerator);
plop.setGenerator('language', languageGenerator);
plop.addHelper('directory', (comp) => { plop.addHelper('directory', (comp) => {
try { try {
fs.accessSync(`app/containers/${comp}`, fs.F_OK); fs.accessSync(`app/containers/${comp}`, fs.F_OK);

View File

@ -0,0 +1 @@
$1addLocaleData({{language}}LocaleData);

View File

@ -0,0 +1,2 @@
$1
'{{language}}',

View File

@ -0,0 +1 @@
$1 {{language}}: formatTranslationMessages({{language}}TranslationMessages),

View File

@ -0,0 +1,80 @@
/**
* Language Generator
*/
const exec = require('child_process').exec;
module.exports = {
description: 'Add a langauge',
prompts: [{
type: 'input',
name: 'language',
message: 'What is the language you want to add i18n support for (e.g. "fr", "de")?',
default: 'fr',
validate: value => {
if ((/.+/).test(value) && value.length === 2) {
return true;
}
return '2 character language specifier is required';
},
}],
actions: () => {
const actions = [];
actions.push({
type: 'modify',
path: '../../app/i18n.js',
pattern: /('react-intl\/locale-data\/[a-z]+';\n)(?!.*'react-intl\/locale-data\/[a-z]+';)/g,
templateFile: './language/intl-locale-data.hbs',
});
actions.push({
type: 'modify',
path: '../../app/i18n.js',
pattern: /([\n\s'[a-z]+',)(?!.*[\n\s'[a-z]+',)/g,
templateFile: './language/app-locale.hbs',
});
actions.push({
type: 'modify',
path: '../../app/i18n.js',
pattern: /(from\s'.\/translations\/[a-z]+.json';\n)(?!.*from\s'.\/translations\/[a-z]+.json';)/g,
templateFile: './language/translation-messages.hbs',
});
actions.push({
type: 'modify',
path: '../../app/i18n.js',
pattern: /(addLocaleData\([a-z]+LocaleData\);\n)(?!.*addLocaleData\([a-z]+LocaleData\);)/g,
templateFile: './language/add-locale-data.hbs',
});
actions.push({
type: 'modify',
path: '../../app/i18n.js',
pattern: /([a-z]+:\sformatTranslationMessages\([a-z]+TranslationMessages\),\n)(?!.*[a-z]+:\sformatTranslationMessages\([a-z]+TranslationMessages\),)/g,
templateFile: './language/format-translation-messages.hbs',
});
actions.push({
type: 'add',
path: '../../app/translations/{{language}}.json',
templateFile: './language/translations-json.hbs',
abortOnFail: true,
});
actions.push({
type: 'modify',
path: '../../app/app.js',
pattern: /(System\.import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),\n)(?!.*System\.import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),)/g,
templateFile: './language/polyfill-intl-locale.hbs',
});
actions.push(
() => {
const cmd = 'npm run extract-intl';
exec(cmd, (err, result, stderr) => {
if (err || stderr) {
throw err || stderr;
}
process.stdout.write(result);
});
}
);
return actions;
},
};

View File

@ -0,0 +1 @@
$1import {{language}}LocaleData from 'react-intl/locale-data/{{language}}';

View File

@ -0,0 +1 @@
$1 System.import('intl/locale-data/jsonp/{{language}}.js'),

View File

@ -0,0 +1 @@
$1import {{language}}TranslationMessages from './translations/{{language}}.json';

View File

@ -0,0 +1 @@
[]

View File

@ -28,10 +28,10 @@
"start:production": "npm run build && npm run start:prod", "start:production": "npm run build && npm run start:prod",
"start:prod": "cross-env NODE_ENV=production node server", "start:prod": "cross-env NODE_ENV=production node server",
"generate": "plop --plopfile internals/generators/index.js", "generate": "plop --plopfile internals/generators/index.js",
"lint": "npm run lint:js && npm run lint:css", "lint": "npm run lint:js && npm run lint:scss",
"lint:eslint": "eslint --ignore-path .gitignore --ignore-pattern internals/scripts", "lint:eslint": "eslint --ignore-path .gitignore --ignore-pattern internals/scripts",
"lint:js": "npm run lint:eslint -- . ", "lint:js": "npm run lint:eslint -- . ",
"lint:css": "stylelint ./app/**/*.css", "lint:scss": "stylelint ./app/**/*.scss",
"lint:staged": "lint-staged", "lint:staged": "lint-staged",
"pretest": "npm run test:clean && npm run lint", "pretest": "npm run test:clean && npm run lint",
"test:clean": "rimraf ./coverage", "test:clean": "rimraf ./coverage",
@ -44,7 +44,7 @@
}, },
"lint-staged": { "lint-staged": {
"*.js": "lint:eslint", "*.js": "lint:eslint",
"*.css": "stylelint" "*.scss": "stylelint"
}, },
"pre-commit": "lint:staged", "pre-commit": "lint:staged",
"babel": { "babel": {