From f64539d6f305fe4bd66e99be0642d335326c43b3 Mon Sep 17 00:00:00 2001 From: SleepWalker Date: Sun, 4 Sep 2016 12:19:14 +0300 Subject: [PATCH] Support of styling and i18n --- .eslintrc.json | 4 +- package.json | 2 +- scripts/i18n-collect.js | 71 +++++++++++++------------- src/App.jsx | 51 ++++++++++++++++++ src/emails/register/Register.jsx | 15 +++++- src/emails/register/messages.intl.json | 3 ++ src/emails/register/styles.js | 8 +++ src/i18n/be.json | 3 ++ src/i18n/en.json | 3 ++ src/i18n/ru.json | 3 ++ src/i18n/uk.json | 3 ++ src/index.js | 25 ++------- 12 files changed, 131 insertions(+), 60 deletions(-) create mode 100644 src/App.jsx create mode 100644 src/emails/register/messages.intl.json create mode 100644 src/emails/register/styles.js create mode 100644 src/i18n/be.json create mode 100644 src/i18n/en.json create mode 100644 src/i18n/ru.json create mode 100644 src/i18n/uk.json diff --git a/.eslintrc.json b/.eslintrc.json index e92b6b9..411df8f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -188,7 +188,7 @@ "react/jsx-max-props-per-line": ["warn", {"maximum": 3}], "react/jsx-no-bind": "warn", "react/jsx-no-duplicate-props": "warn", - "react/jsx-no-literals": "warn", + "react/jsx-no-literals": "off", "react/jsx-no-undef": "warn", "react/jsx-pascal-case": "warn", "react/jsx-uses-react": "warn", @@ -206,7 +206,7 @@ "react/no-unknown-property": "warn", "react/prefer-es6-class": "warn", "react/prop-types": "warn", - "react/react-in-jsx-scope": "warn", + "react/react-in-jsx-scope": "off", "react/self-closing-comp": "warn", "react/sort-comp": ["warn", {"order": ["lifecycle", "render", "everything-else"]}] } diff --git a/package.json b/package.json index 755dc0e..dff6a5c 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "up": "npm update", "test": "karma start ./karma.conf.js", "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", "render": "" }, diff --git a/scripts/i18n-collect.js b/scripts/i18n-collect.js index 10c2e44..6a4657c 100644 --- a/scripts/i18n-collect.js +++ b/scripts/i18n-collect.js @@ -52,38 +52,35 @@ let keysToAdd = []; let keysToRemove = []; const keysToRename = []; const isNotMarked = (value) => value.slice(0, 2) !== '--'; -try { - const prevMessages = JSON.parse(fs.readFileSync(defaultMessagesPath, 'utf8')); - const prevMessagesMap = Object.entries(prevMessages).reduce((acc, [key, value]) => { - if (acc[value]) { - acc[value].push(key); - } else { - acc[value] = [key]; - } - return acc; - }, {}); - keysToAdd = Object.keys(collectedMessages).filter((key) => !prevMessages[key]); - keysToRemove = Object.keys(prevMessages).filter((key) => !collectedMessages[key]).filter(isNotMarked); - keysToUpdate = Object.entries(prevMessages).reduce((acc, [key, message]) => - acc.concat(collectedMessages[key] && collectedMessages[key] !== message ? key : []) - , []); +const prevMessages = readJSON(defaultMessagesPath); +const prevMessagesMap = Object.entries(prevMessages).reduce((acc, [key, value]) => { + if (acc[value]) { + acc[value].push(key); + } else { + acc[value] = [key]; + } - // detect keys to rename, mutating keysToAdd and keysToRemove - [].concat(keysToAdd).forEach((toKey) => { - const keys = prevMessagesMap[collectedMessages[toKey]] || []; - const fromKey = keys.find((fromKey) => keysToRemove.indexOf(fromKey) > -1); + return acc; +}, {}); +keysToAdd = Object.keys(collectedMessages).filter((key) => !prevMessages[key]); +keysToRemove = Object.keys(prevMessages).filter((key) => !collectedMessages[key]).filter(isNotMarked); +keysToUpdate = Object.entries(prevMessages).reduce((acc, [key, message]) => + acc.concat(collectedMessages[key] && collectedMessages[key] !== message ? key : []) +, []); - if (fromKey) { - keysToRename.push([fromKey, toKey]); +// detect keys to rename, mutating keysToAdd and keysToRemove +[].concat(keysToAdd).forEach((toKey) => { + const keys = prevMessagesMap[collectedMessages[toKey]] || []; + const fromKey = keys.find((fromKey) => keysToRemove.indexOf(fromKey) > -1); - keysToRemove.splice(keysToRemove.indexOf(fromKey), 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 (fromKey) { + keysToRename.push([fromKey, toKey]); + + keysToRemove.splice(keysToRemove.indexOf(fromKey), 1); + keysToAdd.splice(keysToAdd.indexOf(toKey), 1); + } +}); if (!keysToAdd.length && !keysToRemove.length && !keysToUpdate.length && !keysToRename.length) { return console.log(chalk.green('Everything is up to date!')); @@ -142,13 +139,7 @@ function buildLocales() { SUPPORTED_LANGS.map((lang) => { const destPath = `${LANG_DIR}/${lang}.json`; - - 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); - } + const newMessages = readJSON(destPath); keysToRename.forEach(([fromKey, toKey]) => { newMessages[toKey] = newMessages[fromKey]; @@ -181,3 +172,13 @@ function buildLocales() { 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 {}; +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..043b61b --- /dev/null +++ b/src/App.jsx @@ -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 ( + + {debug + ? ( +
+ Hello world + +
+ ) : ( + + ) + } +
+ ); +} + +App.propTypes = { + type: PropTypes.string.isRequired, + payload: PropTypes.shape({ + locale: PropTypes.string + }), + debug: PropTypes.bool +}; diff --git a/src/emails/register/Register.jsx b/src/emails/register/Register.jsx index b8b4e54..719fbd6 100644 --- a/src/emails/register/Register.jsx +++ b/src/emails/register/Register.jsx @@ -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}) { return ( -
- You Have Been Registered, {username}! +
+
); } + +Register.propTypes = { + username: PropTypes.string +}; diff --git a/src/emails/register/messages.intl.json b/src/emails/register/messages.intl.json new file mode 100644 index 0000000..4b1baf4 --- /dev/null +++ b/src/emails/register/messages.intl.json @@ -0,0 +1,3 @@ +{ + "you_registered_as": "You have been registered as {username}" +} diff --git a/src/emails/register/styles.js b/src/emails/register/styles.js new file mode 100644 index 0000000..ba98cc4 --- /dev/null +++ b/src/emails/register/styles.js @@ -0,0 +1,8 @@ +export default { + container: { + padding: '10px', + margin: '10px', + background: '#f7f7f7', + border: '1px #ddd solid', + } +}; diff --git a/src/i18n/be.json b/src/i18n/be.json new file mode 100644 index 0000000..0ae7e45 --- /dev/null +++ b/src/i18n/be.json @@ -0,0 +1,3 @@ +{ + "emails.register.you_registered_as": "Вы былі зарэгістраваныя як {username}" +} diff --git a/src/i18n/en.json b/src/i18n/en.json new file mode 100644 index 0000000..51292cf --- /dev/null +++ b/src/i18n/en.json @@ -0,0 +1,3 @@ +{ + "emails.register.you_registered_as": "You have been registered as {username}" +} diff --git a/src/i18n/ru.json b/src/i18n/ru.json new file mode 100644 index 0000000..a3586da --- /dev/null +++ b/src/i18n/ru.json @@ -0,0 +1,3 @@ +{ + "emails.register.you_registered_as": "Вы были зарегистрированы как {username}" +} diff --git a/src/i18n/uk.json b/src/i18n/uk.json new file mode 100644 index 0000000..a4589ad --- /dev/null +++ b/src/i18n/uk.json @@ -0,0 +1,3 @@ +{ + "emails.register.you_registered_as": "Ви були зареєстровані як {username}" +} diff --git a/src/index.js b/src/index.js index 4fe5ecc..41cb848 100644 --- a/src/index.js +++ b/src/index.js @@ -1,35 +1,20 @@ import 'babel-polyfill'; -import React from 'react'; import ReactDOM from 'react-dom'; import ReactDOMServer from 'react-dom/server'; -import { IntlProvider } from 'react-intl'; - -import Register from 'emails/register'; +import App from 'App'; const isCli = typeof window === 'undefined'; -const App = ({type, payload = {}}) => ( - - {isCli - ? ( - - ) : ( -
- Hello world - -
- ) - } -
-); - if (isCli) { module.exports = { default: (props) => ReactDOMServer.renderToStaticMarkup() }; } else { - ReactDOM.render(, document.getElementById('app')); + ReactDOM.render( + , + document.getElementById('app') + ); }