mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 23:24:03 +00:00
Implement i18n for plugins
This commit is contained in:
parent
9abc10e4a8
commit
560e965747
@ -90,12 +90,33 @@ window.onload = function onLoad() {
|
||||
Promise.all([
|
||||
System.import('intl'),
|
||||
System.import('intl/locale-data/jsonp/en.js'),
|
||||
System.import('intl/locale-data/jsonp/en.js'),
|
||||
System.import('intl/locale-data/jsonp/fr.js'),
|
||||
]).then(() => render(translationMessages));
|
||||
} else {
|
||||
render(translationMessages);
|
||||
}
|
||||
};
|
||||
|
||||
// Chunked polyfill for browsers without Intl support
|
||||
if (!window.Intl) {
|
||||
(new Promise((resolve) => {
|
||||
resolve(System.import('intl'));
|
||||
}))
|
||||
.then(() => Promise.all([
|
||||
System.import('intl/locale-data/jsonp/en.js'),
|
||||
System.import('intl/locale-data/jsonp/de.js'),
|
||||
System.import('intl/locale-data/jsonp/en.js'),
|
||||
System.import('intl/locale-data/jsonp/fr.js'),
|
||||
]))
|
||||
.then(() => render(translationMessages))
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
} else {
|
||||
render(translationMessages);
|
||||
}
|
||||
|
||||
// Install ServiceWorker and AppCache in the end since
|
||||
// it's not most important operation and if main code fails,
|
||||
// we do not want it installed
|
||||
@ -120,8 +141,8 @@ const registerPlugin = (plugin) => {
|
||||
const pluginsRoute = _.find(homeRoute.childRoutes, { name: 'plugins' });
|
||||
|
||||
// Create a new prefixed route for each plugin routes
|
||||
if (plugin && plugin.routes && plugin.routes.childRoutes) {
|
||||
plugin.routes.childRoutes.forEach(route => {
|
||||
if (plugin && plugin.routes) {
|
||||
plugin.routes.forEach(route => {
|
||||
pluginsRoute.childRoutes.push({
|
||||
path: `/plugins/${plugin.id}${route.path}`,
|
||||
name: `plugins_${plugin.id}_${route.name}`,
|
||||
@ -131,12 +152,15 @@ const registerPlugin = (plugin) => {
|
||||
}
|
||||
|
||||
// TMP
|
||||
setTimeout(() => {
|
||||
store.dispatch(showNotification('Plugin loaded!', 'success'));
|
||||
store.dispatch(showNotification('Oooooops!', 'warning'));
|
||||
store.dispatch(showNotification('An error occurred!', 'error'));
|
||||
store.dispatch(showNotification('Lorem ipsum dolor sit amet, consectetur adipisicing elit. Corporis earum fugiat inventore iste. Accusantium cumque dolor ducimus esse ex fugiat natus nulla qui ratione ullam vero, voluptas voluptate? Officia, tempora!', 'info'));
|
||||
}, 500);
|
||||
// setTimeout(() => {
|
||||
// store.dispatch(showNotification('Plugin loaded!', 'success'));
|
||||
// store.dispatch(showNotification('Oooooops!', 'warning'));
|
||||
// store.dispatch(showNotification('An error occurred!', 'error'));
|
||||
// store.dispatch(showNotification('Lorem ipsum dolor sit amet, consectetur adipisicing elit. Corporis earum fugiat inventore iste. Accusantium cumque dolor ducimus esse ex fugiat natus nulla qui ratione ullam vero, voluptas voluptate? Officia, tempora!', 'info'));
|
||||
// }, 500);
|
||||
|
||||
// Merge admin translation messages
|
||||
_.merge(translationMessages, plugin.translationMessages);
|
||||
|
||||
store.dispatch(pluginLoaded(plugin));
|
||||
};
|
||||
@ -147,6 +171,9 @@ const displayNotification = (message, status) => {
|
||||
store.dispatch(showNotification(message, status));
|
||||
};
|
||||
|
||||
const port = window.Strapi && window.Strapi.port ? window.Strapi.port : 1337;
|
||||
const apiUrl = window.Strapi && window.Strapi.apiUrl ? window.Strapi.apiUrl : `http://localhost:${port}`;
|
||||
|
||||
window.Strapi = {
|
||||
registerPlugin,
|
||||
notification: {
|
||||
@ -163,6 +190,13 @@ window.Strapi = {
|
||||
displayNotification(message, 'info');
|
||||
},
|
||||
},
|
||||
port,
|
||||
apiUrl,
|
||||
refresh: () => ({
|
||||
translationMessages: (translationMessagesUpdated) => {
|
||||
render(_.merge({}, translationMessages, translationMessagesUpdated));
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
const dispatch = store.dispatch;
|
||||
|
||||
@ -9,12 +9,14 @@ import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import messages from './messages';
|
||||
import styles from './styles.scss';
|
||||
import LocaleToggle from 'containers/LocaleToggle';
|
||||
|
||||
class LeftMenuFooter extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.leftMenuFooter}>
|
||||
<FormattedMessage {...messages.header} /> <a href="http://strapi.io" target="_blank">Strapi</a>
|
||||
<FormattedMessage {...messages.poweredBy} /> <a href="http://strapi.io" target="_blank">Strapi</a>
|
||||
<LocaleToggle />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
export default defineMessages({
|
||||
header: {
|
||||
id: 'app.components.LeftMenuFooter.header',
|
||||
poweredBy: {
|
||||
id: 'app.components.LeftMenuFooter.poweredBy',
|
||||
defaultMessage: 'Proudly powered by ',
|
||||
},
|
||||
});
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
color: $white;
|
||||
|
||||
// TMP
|
||||
background-image: url('assets/images/logo-strapi.png');
|
||||
background-image: url('../../assets/images/logo-strapi.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 12rem auto;
|
||||
|
||||
36
public/app/components/Toggle/index.js
Normal file
36
public/app/components/Toggle/index.js
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
*
|
||||
* LocaleToggle
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
// import { FormattedMessage } from 'react-intl';
|
||||
import styles from './styles.scss';
|
||||
import ToggleOption from '../ToggleOption';
|
||||
|
||||
function Toggle(props) { // eslint-disable-line react/prefer-stateless-function
|
||||
let content = (<option>--</option>);
|
||||
|
||||
// If we have items, render them
|
||||
if (props.values) {
|
||||
content = props.values.map((value) => (
|
||||
<ToggleOption key={value} value={value} message={props.messages[value]} />
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
<select onChange={props.onToggle} className={styles.toggle}>
|
||||
{content}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
Toggle.propTypes = {
|
||||
onToggle: React.PropTypes.func,
|
||||
values: React.PropTypes.array,
|
||||
messages: React.PropTypes.object,
|
||||
};
|
||||
|
||||
export default Toggle;
|
||||
4
public/app/components/Toggle/styles.scss
Normal file
4
public/app/components/Toggle/styles.scss
Normal file
@ -0,0 +1,4 @@
|
||||
.toggle {
|
||||
line-height: 1em;
|
||||
height: 20px;
|
||||
}
|
||||
29
public/app/components/Toggle/tests/index.test.js
Normal file
29
public/app/components/Toggle/tests/index.test.js
Normal file
@ -0,0 +1,29 @@
|
||||
import Toggle from '../index';
|
||||
|
||||
import expect from 'expect';
|
||||
import { shallow } from 'enzyme';
|
||||
import { IntlProvider, defineMessages } from 'react-intl';
|
||||
import React from 'react';
|
||||
|
||||
describe('<Toggle />', () => {
|
||||
it('should contain default text', () => {
|
||||
const defaultEnMessage = 'someContent';
|
||||
const defaultDeMessage = 'someOtherContent';
|
||||
const messages = defineMessages({
|
||||
en: {
|
||||
id: 'app.components.LocaleToggle.en',
|
||||
defaultMessage: defaultEnMessage,
|
||||
},
|
||||
de: {
|
||||
id: 'app.components.LocaleToggle.en',
|
||||
defaultMessage: defaultDeMessage,
|
||||
},
|
||||
});
|
||||
const renderedComponent = shallow(
|
||||
<IntlProvider locale="en">
|
||||
<Toggle values={['en', 'de']} messages={messages} />
|
||||
</IntlProvider>
|
||||
);
|
||||
expect(renderedComponent.contains(<Toggle values={['en', 'de']} messages={messages} />)).toEqual(true);
|
||||
});
|
||||
});
|
||||
22
public/app/components/ToggleOption/index.js
Normal file
22
public/app/components/ToggleOption/index.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
*
|
||||
* ToggleOption
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
|
||||
const ToggleOption = ({ value, message, intl }) => (
|
||||
<option value={value}>
|
||||
{intl.formatMessage(message).toUpperCase()}
|
||||
</option>
|
||||
);
|
||||
|
||||
ToggleOption.propTypes = {
|
||||
value: React.PropTypes.string.isRequired,
|
||||
message: React.PropTypes.object.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(ToggleOption);
|
||||
24
public/app/components/ToggleOption/tests/index.test.js
Normal file
24
public/app/components/ToggleOption/tests/index.test.js
Normal file
@ -0,0 +1,24 @@
|
||||
import ToggleOption from '../index';
|
||||
|
||||
import expect from 'expect';
|
||||
import { shallow } from 'enzyme';
|
||||
import { IntlProvider, defineMessages } from 'react-intl';
|
||||
import React from 'react';
|
||||
|
||||
describe('<ToggleOption />', () => {
|
||||
it('should render default language messages', () => {
|
||||
const defaultEnMessage = 'someContent';
|
||||
const message = defineMessages({
|
||||
enMessage: {
|
||||
id: 'app.components.LocaleToggle.en',
|
||||
defaultMessage: defaultEnMessage,
|
||||
},
|
||||
});
|
||||
const renderedComponent = shallow(
|
||||
<IntlProvider locale="en">
|
||||
<ToggleOption value="en" message={message.enMessage} />
|
||||
</IntlProvider>
|
||||
);
|
||||
expect(renderedComponent.contains(<ToggleOption value="en" message={message.enMessage} />)).toEqual(true);
|
||||
});
|
||||
});
|
||||
@ -30,7 +30,7 @@
|
||||
width: 100%;
|
||||
height: 500%;
|
||||
min-height: 100%;
|
||||
//background: url('assets/images/baseline-18.png');
|
||||
background: url('assets/images/baseline-20.png');
|
||||
//background: url('../../assets/images/baseline-18.png');
|
||||
background: url('../../assets/images/baseline-20.png');
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
43
public/app/containers/LocaleToggle/index.js
Normal file
43
public/app/containers/LocaleToggle/index.js
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
*
|
||||
* LanguageToggle
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectLocale } from '../LanguageProvider/selectors';
|
||||
import { changeLocale } from '../LanguageProvider/actions';
|
||||
import { appLocales } from '../../i18n';
|
||||
import { createSelector } from 'reselect';
|
||||
import styles from './styles.scss';
|
||||
import messages from './messages';
|
||||
import Toggle from 'components/Toggle';
|
||||
|
||||
export class LocaleToggle extends React.Component { // eslint-disable-line
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.localeToggle}>
|
||||
<Toggle values={appLocales} messages={messages} onToggle={this.props.onLocaleToggle} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LocaleToggle.propTypes = {
|
||||
onLocaleToggle: React.PropTypes.func,
|
||||
};
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
selectLocale(),
|
||||
(locale) => ({ locale })
|
||||
);
|
||||
|
||||
export function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
onLocaleToggle: (evt) => dispatch(changeLocale(evt.target.value)),
|
||||
dispatch,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(LocaleToggle);
|
||||
21
public/app/containers/LocaleToggle/messages.js
Normal file
21
public/app/containers/LocaleToggle/messages.js
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* LocaleToggle Messages
|
||||
*
|
||||
* This contains all the text for the LanguageToggle component.
|
||||
*/
|
||||
import { defineMessages } from 'react-intl';
|
||||
import { appLocales } from '../../i18n';
|
||||
|
||||
export function getLocaleMessages(locales) {
|
||||
return locales.reduce((messages, locale) =>
|
||||
Object.assign(messages, {
|
||||
[locale]: {
|
||||
id: `app.components.LocaleToggle.${locale}`,
|
||||
defaultMessage: `${locale}`,
|
||||
},
|
||||
}), {});
|
||||
}
|
||||
|
||||
export default defineMessages(
|
||||
getLocaleMessages(appLocales)
|
||||
);
|
||||
3
public/app/containers/LocaleToggle/styles.scss
Normal file
3
public/app/containers/LocaleToggle/styles.scss
Normal file
@ -0,0 +1,3 @@
|
||||
.localeToggle {
|
||||
float: right;
|
||||
}
|
||||
60
public/app/containers/LocaleToggle/tests/index.test.js
Normal file
60
public/app/containers/LocaleToggle/tests/index.test.js
Normal file
@ -0,0 +1,60 @@
|
||||
import LocaleToggle, { mapDispatchToProps } from '../index';
|
||||
import { changeLocale } from '../../LanguageProvider/actions';
|
||||
import LanguageProvider from '../../LanguageProvider';
|
||||
|
||||
import expect from 'expect';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
import configureStore from '../../../store';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { browserHistory } from 'react-router';
|
||||
import { translationMessages } from '../../../i18n';
|
||||
|
||||
describe('<LocaleToggle />', () => {
|
||||
let store;
|
||||
|
||||
before(() => {
|
||||
store = configureStore({}, browserHistory);
|
||||
});
|
||||
|
||||
it('should render the default language messages', () => {
|
||||
const renderedComponent = shallow(
|
||||
<Provider store={store}>
|
||||
<LanguageProvider messages={translationMessages}>
|
||||
<LocaleToggle />
|
||||
</LanguageProvider>
|
||||
</Provider>
|
||||
);
|
||||
expect(renderedComponent.contains(<LocaleToggle />)).toEqual(true);
|
||||
});
|
||||
|
||||
it('should present the default `en` english language option', () => {
|
||||
const renderedComponent = mount(
|
||||
<Provider store={store}>
|
||||
<LanguageProvider messages={translationMessages}>
|
||||
<LocaleToggle />
|
||||
</LanguageProvider>
|
||||
</Provider>
|
||||
);
|
||||
expect(renderedComponent.contains(<option value="en">en</option>)).toEqual(true);
|
||||
});
|
||||
|
||||
describe('mapDispatchToProps', () => {
|
||||
describe('onLocaleToggle', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = expect.createSpy();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
expect(result.onLocaleToggle).toExist();
|
||||
});
|
||||
|
||||
it('should dispatch changeLocale when called', () => {
|
||||
const dispatch = expect.createSpy();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
const locale = 'de';
|
||||
const evt = { target: { value: locale } };
|
||||
result.onLocaleToggle(evt);
|
||||
expect(dispatch).toHaveBeenCalledWith(changeLocale(locale));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
21
public/app/containers/LocaleToggle/tests/messages.test.js
Normal file
21
public/app/containers/LocaleToggle/tests/messages.test.js
Normal file
@ -0,0 +1,21 @@
|
||||
import assert from 'assert';
|
||||
import { getLocaleMessages } from '../messages';
|
||||
|
||||
describe('getLocaleMessages', () => {
|
||||
it('should create i18n messages for all locales', () => {
|
||||
const expected = {
|
||||
en: {
|
||||
id: 'app.components.LocaleToggle.en',
|
||||
defaultMessage: 'en',
|
||||
},
|
||||
fr: {
|
||||
id: 'app.components.LocaleToggle.fr',
|
||||
defaultMessage: 'fr',
|
||||
},
|
||||
};
|
||||
|
||||
const actual = getLocaleMessages(['en', 'fr']);
|
||||
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
});
|
||||
@ -7,14 +7,18 @@
|
||||
import { addLocaleData } from 'react-intl';
|
||||
|
||||
import enLocaleData from 'react-intl/locale-data/en';
|
||||
import frLocaleData from 'react-intl/locale-data/fr';
|
||||
|
||||
export const appLocales = [
|
||||
'en',
|
||||
'fr',
|
||||
];
|
||||
|
||||
import enTranslationMessages from './translations/en.json';
|
||||
import frTranslationMessages from './translations/fr.json';
|
||||
|
||||
addLocaleData(enLocaleData);
|
||||
addLocaleData(frLocaleData);
|
||||
|
||||
const formatTranslationMessages = (messages) => {
|
||||
const formattedMessages = {};
|
||||
@ -25,6 +29,11 @@ const formatTranslationMessages = (messages) => {
|
||||
return formattedMessages;
|
||||
};
|
||||
|
||||
export const translationMessages = {
|
||||
const translationMessages = {
|
||||
en: formatTranslationMessages(enTranslationMessages),
|
||||
fr: formatTranslationMessages(frTranslationMessages),
|
||||
};
|
||||
|
||||
export {
|
||||
translationMessages,
|
||||
};
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
$gray-dark: #373a3c !default;
|
||||
$gray: #55595c !default;
|
||||
$gray-light: #818a91 !default;
|
||||
$gray-light: #9ca4b9 !default;
|
||||
$gray-lighter: #eceeef !default;
|
||||
$gray-lightest: #f7f7f9 !default;
|
||||
|
||||
|
||||
@ -1,152 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "app.components.LocaleToggle.de",
|
||||
"defaultMessage": "de",
|
||||
"message": ""
|
||||
},
|
||||
{
|
||||
"id": "app.components.LocaleToggle.en",
|
||||
"defaultMessage": "en",
|
||||
"message": ""
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.components.Footer.author.message",
|
||||
"defaultMessage": "Made with love by {author}.",
|
||||
"message": "Mit Liebe gemacht von {author}."
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.components.Footer.license.message",
|
||||
"defaultMessage": "This project is licensed under the MIT license.",
|
||||
"message": "Dieses Projekt wird unter der MIT-Lizenz veröffentlicht."
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.css.header",
|
||||
"defaultMessage": "Features",
|
||||
"message": ""
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.css.message",
|
||||
"defaultMessage": "Next generation CSS",
|
||||
"message": "Die nächste Generation von CSS"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.feedback.header",
|
||||
"defaultMessage": "Instant feedback",
|
||||
"message": "Sofortiges Feedback"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.feedback.message",
|
||||
"defaultMessage": "Enjoy the best DX and code your app at the speed of thought! Your\n saved changes to the CSS and JS are reflected instantaneously\n without refreshing the page. Preserve application state even when\n you update something in the underlying code!",
|
||||
"message": "Genießen Sie die beste Entwicklungserfahrung und programmieren Sie Ihre App so schnell wie noch nie! Ihre Änderungen an dem CSS und JavaScript sind sofort reflektiert, ohne die Seite aktualisieren zu müssen. So bleibt der Anwendungszustand bestehen, auch wenn Sie etwas in dem darunter liegenden Code aktualisieren!"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.header",
|
||||
"defaultMessage": "Features",
|
||||
"message": ""
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.home",
|
||||
"defaultMessage": "Home",
|
||||
"message": ""
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.internationalization.header",
|
||||
"defaultMessage": "Complete i18n Standard Internationalization & Pluralization",
|
||||
"message": "Komplette i18n Standard-Internationalisierung und Pluralisierung"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.internationalization.message",
|
||||
"defaultMessage": "Scalable apps need to support multiple languages, easily add and support multiple languages with `react-intl`.",
|
||||
"message": "Das Internet ist global. Mehrsprachige- und Pluralisierungsunterstützung ist entscheidend für große Web-Anwendungen."
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.javascript.header",
|
||||
"defaultMessage": "Next generation JavaScript",
|
||||
"message": "Die nächste Generation von JavaScript"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.javascript.message",
|
||||
"defaultMessage": "Use template strings, object destructuring, arrow functions, JSX\n syntax and more, today.",
|
||||
"message": "Benutzen Sie ES6 template strings, object destructuring, arrow functions, JSX syntax und mehr, heute."
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.network.header",
|
||||
"defaultMessage": "Offline-first",
|
||||
"message": ""
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.network.message",
|
||||
"defaultMessage": "The next frontier in performant web apps: availability without a\n network connection from the instant your users load the app.",
|
||||
"message": "Die nächste Schwelle für performanten Web-Anwendungen: Verfügbarkeit ohne Netzwerkverbindung wenn Ihre Benutzer die App einmal heruntergeladen haben."
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.routing.header",
|
||||
"defaultMessage": "Industry-standard routing",
|
||||
"message": "Standard Routing"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.routing.message",
|
||||
"defaultMessage": "Write composable CSS that's co-located with your components for\n complete modularity. Unique generated class names keep the\n specificity low while eliminating style clashes. Ship only the\n styles that are on the page for the best performance.",
|
||||
"message": "Schreiben Sie CSS, das am selben Ort wie ihre Komponenten ist. Deterministisch generierte, einzigartige Klassennamen halten die Spezifität niedrig während styling Konflikte vermieden werden. Senden Sie nur das CSS an ihre Benutzer welches dann wirklich sichtbar ist für die schnellste Performance!"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.scaffolding.header",
|
||||
"defaultMessage": "Quick scaffolding",
|
||||
"message": "Schnelles Scaffolding"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.scaffolding.message",
|
||||
"defaultMessage": "Automate the creation of components, containers, routes, selectors\n and sagas - and their tests - right from the CLI!",
|
||||
"message": "Automatisieren Sie die Kreation von Komponenten, Containern, Routen, Selektoren und Sagas – und ihre Tests – direkt von dem Terminal!"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.state_management.header",
|
||||
"defaultMessage": "Predictable state management",
|
||||
"message": "Berechenbare Stateverwaltung"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.FeaturePage.state_management.message",
|
||||
"defaultMessage": "Unidirectional data flow allows for change logging and time travel\n debugging.",
|
||||
"message": "Unidirectional data flow erlaubt uns alle Änderungen ihrer Applikation zu loggen und time travel debugging einzusetzen."
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.HomePage.features.Button",
|
||||
"defaultMessage": "Features",
|
||||
"message": ""
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.HomePage.start_project.header",
|
||||
"defaultMessage": "Start your next react project in seconds",
|
||||
"message": "Beginnen Sie Ihr nächstes React Projekt in Sekunden"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.HomePage.start_project.message",
|
||||
"defaultMessage": "A highly scalable, offline-first foundation with the best DX and a focus on performance and best practices",
|
||||
"message": "Ein skalierendes, offline-first Fundament mit der besten DX und einem Fokus auf Performance und bewährte Methoden"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.HomePage.tryme.atPrefix",
|
||||
"defaultMessage": "@",
|
||||
"message": ""
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.HomePage.tryme.header",
|
||||
"defaultMessage": "Try me!",
|
||||
"message": "Probiere mich!"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.HomePage.tryme.message",
|
||||
"defaultMessage": "Show Github repositories by",
|
||||
"message": "Zeige die Github Repositories von"
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.NotFoundPage.header",
|
||||
"defaultMessage": "Page not found.",
|
||||
"message": "Seite nicht gefunden."
|
||||
},
|
||||
{
|
||||
"id": "boilerplate.containers.NotFoundPage.home",
|
||||
"defaultMessage": "Home",
|
||||
"message": ""
|
||||
}
|
||||
]
|
||||
@ -1 +1,6 @@
|
||||
[]
|
||||
[
|
||||
{
|
||||
"id": "app.components.LeftMenuFooter.poweredBy",
|
||||
"defaultMessage": "Powered by "
|
||||
}
|
||||
]
|
||||
6
public/app/translations/fr.json
Normal file
6
public/app/translations/fr.json
Normal file
@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "app.components.LeftMenuFooter.poweredBy",
|
||||
"defaultMessage": "Propulsé par"
|
||||
}
|
||||
]
|
||||
@ -32,8 +32,8 @@ module.exports = {
|
||||
loaders: [
|
||||
{ test: /\.json$/, loader: 'json-loader' },
|
||||
{ test: /\.css$/, loader: 'null-loader' },
|
||||
{ test: /\.scss$/, loader: 'null-loader' },
|
||||
|
||||
{ test: /\.scss$/, loader: ['style-loader', 'css-loader', 'sass-loader'] },
|
||||
{ test: /\.woff|\.eot|\.ttf/i, loader: 'null-loader' },
|
||||
// sinon.js--aliased for enzyme--expects/requires global vars.
|
||||
// imports-loader allows for global vars to be injected into the module.
|
||||
// See https://github.com/webpack/webpack/issues/304
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user