Support of styling and i18n

This commit is contained in:
SleepWalker 2016-09-04 12:19:14 +03:00
parent bfcf4971f6
commit f64539d6f3
12 changed files with 131 additions and 60 deletions

View File

@ -188,7 +188,7 @@
"react/jsx-max-props-per-line": ["warn", {"maximum": 3}], "react/jsx-max-props-per-line": ["warn", {"maximum": 3}],
"react/jsx-no-bind": "warn", "react/jsx-no-bind": "warn",
"react/jsx-no-duplicate-props": "warn", "react/jsx-no-duplicate-props": "warn",
"react/jsx-no-literals": "warn", "react/jsx-no-literals": "off",
"react/jsx-no-undef": "warn", "react/jsx-no-undef": "warn",
"react/jsx-pascal-case": "warn", "react/jsx-pascal-case": "warn",
"react/jsx-uses-react": "warn", "react/jsx-uses-react": "warn",
@ -206,7 +206,7 @@
"react/no-unknown-property": "warn", "react/no-unknown-property": "warn",
"react/prefer-es6-class": "warn", "react/prefer-es6-class": "warn",
"react/prop-types": "warn", "react/prop-types": "warn",
"react/react-in-jsx-scope": "warn", "react/react-in-jsx-scope": "off",
"react/self-closing-comp": "warn", "react/self-closing-comp": "warn",
"react/sort-comp": ["warn", {"order": ["lifecycle", "render", "everything-else"]}] "react/sort-comp": ["warn", {"order": ["lifecycle", "render", "everything-else"]}]
} }

View File

@ -19,7 +19,7 @@
"up": "npm update", "up": "npm update",
"test": "karma start ./karma.conf.js", "test": "karma start ./karma.conf.js",
"lint": "eslint ./src", "lint": "eslint ./src",
"i18n": "cd ./scripts && ./node_modules/.bin/babel-node i18n-collect.js", "i18n": "cd ./scripts && ../node_modules/.bin/babel-node i18n-collect.js",
"build": "rm -rf dist/ && NODE_ENV=production webpack --progress --colors", "build": "rm -rf dist/ && NODE_ENV=production webpack --progress --colors",
"render": "" "render": ""
}, },

View File

@ -52,9 +52,9 @@ let keysToAdd = [];
let keysToRemove = []; let keysToRemove = [];
const keysToRename = []; const keysToRename = [];
const isNotMarked = (value) => value.slice(0, 2) !== '--'; const isNotMarked = (value) => value.slice(0, 2) !== '--';
try {
const prevMessages = JSON.parse(fs.readFileSync(defaultMessagesPath, 'utf8')); const prevMessages = readJSON(defaultMessagesPath);
const prevMessagesMap = Object.entries(prevMessages).reduce((acc, [key, value]) => { const prevMessagesMap = Object.entries(prevMessages).reduce((acc, [key, value]) => {
if (acc[value]) { if (acc[value]) {
acc[value].push(key); acc[value].push(key);
} else { } else {
@ -62,15 +62,15 @@ try {
} }
return acc; return acc;
}, {}); }, {});
keysToAdd = Object.keys(collectedMessages).filter((key) => !prevMessages[key]); keysToAdd = Object.keys(collectedMessages).filter((key) => !prevMessages[key]);
keysToRemove = Object.keys(prevMessages).filter((key) => !collectedMessages[key]).filter(isNotMarked); keysToRemove = Object.keys(prevMessages).filter((key) => !collectedMessages[key]).filter(isNotMarked);
keysToUpdate = Object.entries(prevMessages).reduce((acc, [key, message]) => keysToUpdate = Object.entries(prevMessages).reduce((acc, [key, message]) =>
acc.concat(collectedMessages[key] && collectedMessages[key] !== message ? key : []) acc.concat(collectedMessages[key] && collectedMessages[key] !== message ? key : [])
, []); , []);
// detect keys to rename, mutating keysToAdd and keysToRemove // detect keys to rename, mutating keysToAdd and keysToRemove
[].concat(keysToAdd).forEach((toKey) => { [].concat(keysToAdd).forEach((toKey) => {
const keys = prevMessagesMap[collectedMessages[toKey]] || []; const keys = prevMessagesMap[collectedMessages[toKey]] || [];
const fromKey = keys.find((fromKey) => keysToRemove.indexOf(fromKey) > -1); const fromKey = keys.find((fromKey) => keysToRemove.indexOf(fromKey) > -1);
@ -80,10 +80,7 @@ try {
keysToRemove.splice(keysToRemove.indexOf(fromKey), 1); keysToRemove.splice(keysToRemove.indexOf(fromKey), 1);
keysToAdd.splice(keysToAdd.indexOf(toKey), 1); keysToAdd.splice(keysToAdd.indexOf(toKey), 1);
} }
}); });
} catch (err) {
console.log(chalk.yellow(`Can not read ${defaultMessagesPath}. The new file will be created.`), err);
}
if (!keysToAdd.length && !keysToRemove.length && !keysToUpdate.length && !keysToRename.length) { if (!keysToAdd.length && !keysToRemove.length && !keysToUpdate.length && !keysToRename.length) {
return console.log(chalk.green('Everything is up to date!')); return console.log(chalk.green('Everything is up to date!'));
@ -142,13 +139,7 @@ function buildLocales() {
SUPPORTED_LANGS.map((lang) => { SUPPORTED_LANGS.map((lang) => {
const destPath = `${LANG_DIR}/${lang}.json`; const destPath = `${LANG_DIR}/${lang}.json`;
const newMessages = readJSON(destPath);
let newMessages = {};
try {
newMessages = JSON.parse(fs.readFileSync(destPath, 'utf8'));
} catch (err) {
console.log(chalk.yellow(`Can not read ${destPath}. The new file will be created.`), err);
}
keysToRename.forEach(([fromKey, toKey]) => { keysToRename.forEach(([fromKey, toKey]) => {
newMessages[toKey] = newMessages[fromKey]; newMessages[toKey] = newMessages[fromKey];
@ -181,3 +172,13 @@ function buildLocales() {
fs.writeFileSync(destPath, JSON.stringify(sortedNewMessages, null, 4) + '\n'); fs.writeFileSync(destPath, JSON.stringify(sortedNewMessages, null, 4) + '\n');
}); });
} }
function readJSON(destPath) {
try {
return JSON.parse(fs.readFileSync(destPath, 'utf8'));
} catch (err) {
console.log(chalk.yellow(`Can not read ${destPath}. The new file will be created.`), `(${err.message})`);
}
return {};
}

51
src/App.jsx Normal file
View File

@ -0,0 +1,51 @@
import { PropTypes } from 'react';
import { IntlProvider, addLocaleData } from 'react-intl';
import enLocaleData from 'react-intl/locale-data/en';
import ruLocaleData from 'react-intl/locale-data/ru';
import beLocaleData from 'react-intl/locale-data/be';
import ukLocaleData from 'react-intl/locale-data/uk';
// till we have not so many locales, we can require their data at once
addLocaleData(enLocaleData);
addLocaleData(ruLocaleData);
addLocaleData(beLocaleData);
addLocaleData(ukLocaleData);
const SUPPORTED_LANGUAGES = ['ru', 'en', 'be', 'uk'];
const DEFAULT_LANGUAGE = 'en';
export default function App({type, payload = {}, debug = false}) {
let {locale} = payload;
if (!locale || SUPPORTED_LANGUAGES.indexOf(locale) === -1) {
locale = DEFAULT_LANGUAGE;
}
const messages = require(`i18n/${locale}.json`);
const Email = require(`emails/${type}/index`).default;
return (
<IntlProvider locale={locale} messages={messages}>
{debug
? (
<div>
Hello world
<Email {...payload} />
</div>
) : (
<Email {...payload} />
)
}
</IntlProvider>
);
}
App.propTypes = {
type: PropTypes.string.isRequired,
payload: PropTypes.shape({
locale: PropTypes.string
}),
debug: PropTypes.bool
};

View File

@ -1,7 +1,18 @@
import { PropTypes } from 'react';
import { FormattedMessage as Message } from 'react-intl';
import styles from './styles';
import messages from './messages.intl.json';
export default function Register({username}) { export default function Register({username}) {
return ( return (
<div> <div style={styles.container}>
You Have Been Registered, {username}! <Message {...messages.you_registered_as} values={{username}} />
</div> </div>
); );
} }
Register.propTypes = {
username: PropTypes.string
};

View File

@ -0,0 +1,3 @@
{
"you_registered_as": "You have been registered as {username}"
}

View File

@ -0,0 +1,8 @@
export default {
container: {
padding: '10px',
margin: '10px',
background: '#f7f7f7',
border: '1px #ddd solid',
}
};

3
src/i18n/be.json Normal file
View File

@ -0,0 +1,3 @@
{
"emails.register.you_registered_as": "Вы былі зарэгістраваныя як {username}"
}

3
src/i18n/en.json Normal file
View File

@ -0,0 +1,3 @@
{
"emails.register.you_registered_as": "You have been registered as {username}"
}

3
src/i18n/ru.json Normal file
View File

@ -0,0 +1,3 @@
{
"emails.register.you_registered_as": "Вы были зарегистрированы как {username}"
}

3
src/i18n/uk.json Normal file
View File

@ -0,0 +1,3 @@
{
"emails.register.you_registered_as": "Ви були зареєстровані як {username}"
}

View File

@ -1,35 +1,20 @@
import 'babel-polyfill'; import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import ReactDOMServer from 'react-dom/server'; import ReactDOMServer from 'react-dom/server';
import { IntlProvider } from 'react-intl'; import App from 'App';
import Register from 'emails/register';
const isCli = typeof window === 'undefined'; const isCli = typeof window === 'undefined';
const App = ({type, payload = {}}) => (
<IntlProvider locale="en" messages={{}}>
{isCli
? (
<Register {...payload} />
) : (
<div>
Hello world
<Register {...payload} />
</div>
)
}
</IntlProvider>
);
if (isCli) { if (isCli) {
module.exports = { module.exports = {
default: (props) => default: (props) =>
ReactDOMServer.renderToStaticMarkup(<App {...props} />) ReactDOMServer.renderToStaticMarkup(<App {...props} />)
}; };
} else { } else {
ReactDOM.render(<App />, document.getElementById('app')); ReactDOM.render(
<App type="register" payload={{locale: 'ru'}} debug />,
document.getElementById('app')
);
} }