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
import App from 'containers/App';
import createRoutes from './routes';
import { translationMessages } from './i18n';
// Plugin identifier based on the package.json `name` value
const pluginId = require('../package.json').name.replace(/^strapi-/i, '');
@ -33,6 +34,22 @@ if (window.Strapi) {
},
mainComponent: App,
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
links = [{
label: 'General',
value: 'general',
to: '',
}, {
label: 'Languages',
value: 'languages',
to: 'languages',
}, {
label: 'Databases',
value: 'databases',
to: 'databases',
}, {
label: 'Security',
value: 'security',
to: 'security',
}, {
label: 'Server',
value: 'server',
to: 'server',
}, {
label: 'Advanced',
value: 'advanced',
to: 'advanced',
}];

View File

@ -1,19 +1,27 @@
/**
*
* LeftMenuLink
*
*/
*
* LeftMenuLink
*
*/
import React from 'react';
import { Link } from 'react-router';
import appMessages from 'containers/App/messages';
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss';
console.log('styles', styles);
class LeftMenuLink extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
const messageKey = `${this.props.link.value}SectionTitle`;
return (
<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>
</Link>
</li>

View File

@ -5,6 +5,8 @@
*/
import React from 'react';
import messages from './messages';
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss';
@ -17,7 +19,7 @@ class PluginHeaderActions extends React.Component { // eslint-disable-line react
className={`${styles.pluginHeaderActionsButton} btn btn-secondary`}
onClick={this.props.onCancel}
>
Cancel
<FormattedMessage {...messages.cancelLabel} />
</button>
<button
type="submit"
@ -25,7 +27,7 @@ class PluginHeaderActions extends React.Component { // eslint-disable-line react
disabled={this.props.loading}
onClick={this.props.onFormSubmit}
>
Save
<FormattedMessage {...messages.saveLabel} />
</button>
</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 { FormattedMessage } from 'react-intl';
import messages from './messages';
import styles from './styles.scss';
class PluginHeaderTitle extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div className={styles.pluginHeaderTitle}>
<h1 className={styles.pluginHeaderTitleName}>Settings Manager</h1>
<p className={styles.pluginHeaderTitleDescription}>Easily update your settings</p>
<h1 className={styles.pluginHeaderTitleName}>
<FormattedMessage {...messages.title} />
</h1>
<p className={styles.pluginHeaderTitleDescription}>
<FormattedMessage {...messages.description} />
</p>
</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 RightContentTitle from 'components/RightContentTitle';
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';
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() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.advancedPage}>
<Helmet
title="Settings Manager - Advanced"
meta={[
{ name: 'description', content: 'Configure your Advanced settings.' },
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader></PluginHeader>
<PluginHeader />
<Container>
<RightContentTitle title="Advanced" description="Configure your advanced settings."></RightContentTitle>
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle>
<RightContentTitle
title={formatMessage(appMessages.advancedSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</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 RightContentTitle from 'components/RightContentTitle';
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';
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() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.databasesPage}>
<Helmet
title="Settings Manager - Databases"
meta={[
{ name: 'description', content: 'Configure your Databases settings.' },
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader></PluginHeader>
<PluginHeader />
<Container>
<RightContentTitle title="Databases" description="Configure your databases settings."></RightContentTitle>
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle>
<RightContentTitle
title={formatMessage(appMessages.databasesSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</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';
export default defineMessages({
header: {
id: 'app.components.DatabasesPage.header',
defaultMessage: 'This is the Databases page of the Settings Manager plugin!',
rightSectionDescription: {
id: 'settings-manager.components.DatabasesPage.rightSectionDescription',
defaultMessage: 'Configure your databases settings',
},
});

View File

@ -31,6 +31,9 @@ import {
updateGeneralSettings,
cancelGeneralSettings,
} 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';
@ -41,21 +44,31 @@ export class HomePage extends React.Component {
}
render() {
const { formatMessage } = this.props.intl;
return (
<div>
<div className="container">
<PluginHeader {...this.props}></PluginHeader>
<Container>
<RightContentTitle title="General" description="Configure your general settings."></RightContentTitle>
<RightContentSectionTitle title="Application" description="The general settings of your Strapi application."></RightContentSectionTitle>
<RightContentTitle
title={formatMessage(appMessages.generalSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(messages.rightContentSectionTitle)}
description={formatMessage(messages.rightContentSectionDescription)}
/>
<form onSubmit={this.props.onFormSubmit}>
<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">
<input
className="form-control"
type="text"
placeholder="My Application"
placeholder={formatMessage(messages.namePlaceholder)}
id="applicationName"
value={this.props.name || ''}
onChange={this.props.onChangeName}
@ -64,12 +77,14 @@ export class HomePage extends React.Component {
</div>
</div>
<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">
<input
className="form-control"
type="text"
placeholder="A Strapi application"
placeholder={formatMessage(messages.descriptionPlaceholder)}
id="applicationDescription"
value={this.props.description || ''}
onChange={this.props.onChangeDescription}
@ -77,12 +92,14 @@ export class HomePage extends React.Component {
</div>
</div>
<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">
<input
className="form-control"
type="text"
placeholder="0.0.1"
placeholder={formatMessage(messages.versionPlaceholder)}
id="applicationVersion"
value={this.props.version || ''}
onChange={this.props.onChangeVersion}
@ -108,6 +125,7 @@ HomePage.propTypes = {
React.PropTypes.object,
React.PropTypes.bool,
]),
intl: intlShape.isRequired,
loading: React.PropTypes.bool,
name: React.PropTypes.oneOfType([
React.PropTypes.string,
@ -123,7 +141,6 @@ HomePage.propTypes = {
React.PropTypes.string,
React.PropTypes.bool,
]),
};
export function mapDispatchToProps(dispatch) {
@ -153,4 +170,4 @@ const mapStateToProps = createStructuredSelector({
});
// 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 RightContentTitle from 'components/RightContentTitle';
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';
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() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.languagesPage}>
<Helmet
title="Settings Manager - Languages"
meta={[
{ name: 'description', content: 'Configure your Languages settings.' },
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader></PluginHeader>
<PluginHeader />
<Container>
<RightContentTitle title="Languages" description="Configure your languages settings."></RightContentTitle>
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle>
<RightContentTitle
title={formatMessage(appMessages.languagesSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</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 RightContentTitle from 'components/RightContentTitle';
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';
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() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.securityPage}>
<Helmet
title="Settings Manager - Security"
meta={[
{ name: 'description', content: 'Configure your security settings.' },
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader></PluginHeader>
<PluginHeader />
<Container>
<RightContentTitle title="Security" description="Configure your security settings."></RightContentTitle>
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle>
<RightContentTitle
title={formatMessage(appMessages.securitySectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</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 RightContentTitle from 'components/RightContentTitle';
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';
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() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.serverPage}>
<Helmet
title="Settings Manager - Server"
meta={[
{ name: 'description', content: 'Configure your Server settings.' },
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader></PluginHeader>
<PluginHeader />
<Container>
<RightContentTitle title="Server" description="Configure your server settings."></RightContentTitle>
<RightContentSectionTitle title="Coming soon" description=""></RightContentSectionTitle>
<RightContentTitle
title={formatMessage(appMessages.serverSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</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.
*
*/
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 = [
'en',
];
// export const appLocales = [
// 'en',
// 'fr',
//
// ];
import enTranslationMessages from './translations/en.json';
import frTranslationMessages from './translations/fr.json';
addLocaleData(enLocaleData);
// addLocaleData(enLocaleData);
// addLocaleData(frLocaleData);
const formatTranslationMessages = (messages) => {
const formattedMessages = {};
@ -27,4 +32,5 @@ const formatTranslationMessages = (messages) => {
export const translationMessages = {
en: formatTranslationMessages(enTranslationMessages),
fr: formatTranslationMessages(frTranslationMessages),
};

View File

@ -1,2 +1,2 @@
// 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';
{{/if}}
{{#if wantCSS}}
import styles from './styles.scss';
import styles from './styles.css';
{{/if}}
class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages';
{{/if}}
{{#if wantCSS}}
import styles from './styles.scss';
import styles from './styles.css';
{{/if}}
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 containerGenerator = require('./container/index.js');
const routeGenerator = require('./route/index.js');
const languageGenerator = require('./language/index.js');
module.exports = (plop) => {
plop.setGenerator('component', componentGenerator);
plop.setGenerator('container', containerGenerator);
plop.setGenerator('route', routeGenerator);
plop.setGenerator('language', languageGenerator);
plop.addHelper('directory', (comp) => {
try {
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:prod": "cross-env NODE_ENV=production node server",
"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:js": "npm run lint:eslint -- . ",
"lint:css": "stylelint ./app/**/*.css",
"lint:scss": "stylelint ./app/**/*.scss",
"lint:staged": "lint-staged",
"pretest": "npm run test:clean && npm run lint",
"test:clean": "rimraf ./coverage",
@ -44,7 +44,7 @@
},
"lint-staged": {
"*.js": "lint:eslint",
"*.css": "stylelint"
"*.scss": "stylelint"
},
"pre-commit": "lint:staged",
"babel": {