forked from treehouse/mastodon
Only load Intl data for current language (#3130)
* Only load Intl data for current language * Extract common chunk only from application.js and public.js * Generate locale packs, avoid caching on window objectsignup-info-prompt
parent
73e4468ff3
commit
9d04de1c8d
|
@ -41,34 +41,12 @@ import FavouritedStatuses from '../features/favourited_statuses';
|
||||||
import Blocks from '../features/blocks';
|
import Blocks from '../features/blocks';
|
||||||
import Mutes from '../features/mutes';
|
import Mutes from '../features/mutes';
|
||||||
import Report from '../features/report';
|
import Report from '../features/report';
|
||||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
|
||||||
import ar from 'react-intl/locale-data/ar';
|
|
||||||
import bg from 'react-intl/locale-data/bg';
|
|
||||||
import ca from 'react-intl/locale-data/ca';
|
|
||||||
import de from 'react-intl/locale-data/de';
|
|
||||||
import en from 'react-intl/locale-data/en';
|
|
||||||
import eo from 'react-intl/locale-data/eo';
|
|
||||||
import es from 'react-intl/locale-data/es';
|
|
||||||
import fa from 'react-intl/locale-data/fa';
|
|
||||||
import fi from 'react-intl/locale-data/fi';
|
|
||||||
import fr from 'react-intl/locale-data/fr';
|
|
||||||
import he from 'react-intl/locale-data/he';
|
|
||||||
import hr from 'react-intl/locale-data/hr';
|
|
||||||
import hu from 'react-intl/locale-data/hu';
|
|
||||||
import id from 'react-intl/locale-data/id';
|
|
||||||
import it from 'react-intl/locale-data/it';
|
|
||||||
import ja from 'react-intl/locale-data/ja';
|
|
||||||
import nl from 'react-intl/locale-data/nl';
|
|
||||||
import no from 'react-intl/locale-data/no';
|
|
||||||
import oc from '../locales/locale-data/oc';
|
|
||||||
import pt from 'react-intl/locale-data/pt';
|
|
||||||
import ru from 'react-intl/locale-data/ru';
|
|
||||||
import uk from 'react-intl/locale-data/uk';
|
|
||||||
import zh from 'react-intl/locale-data/zh';
|
|
||||||
import tr from 'react-intl/locale-data/tr';
|
|
||||||
import getMessagesForLocale from '../locales';
|
|
||||||
import { hydrateStore } from '../actions/store';
|
import { hydrateStore } from '../actions/store';
|
||||||
import createStream from '../stream';
|
import createStream from '../stream';
|
||||||
|
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||||
|
import { getLocale } from '../locales';
|
||||||
|
const { localeData, messages } = getLocale();
|
||||||
|
addLocaleData(localeData);
|
||||||
|
|
||||||
const store = configureStore();
|
const store = configureStore();
|
||||||
const initialState = JSON.parse(document.getElementById("initial-state").textContent);
|
const initialState = JSON.parse(document.getElementById("initial-state").textContent);
|
||||||
|
@ -78,33 +56,6 @@ const browserHistory = useRouterHistory(createBrowserHistory)({
|
||||||
basename: '/web',
|
basename: '/web',
|
||||||
});
|
});
|
||||||
|
|
||||||
addLocaleData([
|
|
||||||
...ar,
|
|
||||||
...bg,
|
|
||||||
...ca,
|
|
||||||
...de,
|
|
||||||
...en,
|
|
||||||
...eo,
|
|
||||||
...es,
|
|
||||||
...fa,
|
|
||||||
...fi,
|
|
||||||
...fr,
|
|
||||||
...he,
|
|
||||||
...hr,
|
|
||||||
...hu,
|
|
||||||
...id,
|
|
||||||
...it,
|
|
||||||
...ja,
|
|
||||||
...nl,
|
|
||||||
...no,
|
|
||||||
...oc,
|
|
||||||
...pt,
|
|
||||||
...ru,
|
|
||||||
...uk,
|
|
||||||
...zh,
|
|
||||||
...tr,
|
|
||||||
]);
|
|
||||||
|
|
||||||
class Mastodon extends React.PureComponent {
|
class Mastodon extends React.PureComponent {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -145,7 +96,7 @@ class Mastodon extends React.PureComponent {
|
||||||
store.dispatch(deleteFromTimelines(data.payload));
|
store.dispatch(deleteFromTimelines(data.payload));
|
||||||
break;
|
break;
|
||||||
case 'notification':
|
case 'notification':
|
||||||
store.dispatch(updateNotifications(JSON.parse(data.payload), getMessagesForLocale(locale), locale));
|
store.dispatch(updateNotifications(JSON.parse(data.payload), messages, locale));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -183,7 +134,7 @@ class Mastodon extends React.PureComponent {
|
||||||
const { locale } = this.props;
|
const { locale } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider locale={locale} messages={getMessagesForLocale(locale)}>
|
<IntlProvider locale={locale} messages={messages}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Router history={browserHistory} render={applyRouterMiddleware(useScroll())}>
|
<Router history={browserHistory} render={applyRouterMiddleware(useScroll())}>
|
||||||
<Route path='/' component={UI}>
|
<Route path='/' component={UI}>
|
||||||
|
|
|
@ -1,61 +1,9 @@
|
||||||
import ar from './ar.json';
|
let theLocale;
|
||||||
import en from './en.json';
|
|
||||||
import ca from './ca.json';
|
|
||||||
import de from './de.json';
|
|
||||||
import es from './es.json';
|
|
||||||
import fa from './fa.json';
|
|
||||||
import he from './he.json';
|
|
||||||
import hr from './hr.json';
|
|
||||||
import hu from './hu.json';
|
|
||||||
import io from './io.json';
|
|
||||||
import it from './it.json';
|
|
||||||
import fr from './fr.json';
|
|
||||||
import nl from './nl.json';
|
|
||||||
import no from './no.json';
|
|
||||||
import oc from './oc.json';
|
|
||||||
import pt from './pt.json';
|
|
||||||
import pt_br from './pt-BR.json';
|
|
||||||
import uk from './uk.json';
|
|
||||||
import fi from './fi.json';
|
|
||||||
import eo from './eo.json';
|
|
||||||
import ru from './ru.json';
|
|
||||||
import ja from './ja.json';
|
|
||||||
import zh_hk from './zh-HK.json';
|
|
||||||
import zh_cn from './zh-CN.json';
|
|
||||||
import bg from './bg.json';
|
|
||||||
import id from './id.json';
|
|
||||||
import tr from './tr.json';
|
|
||||||
|
|
||||||
const locales = {
|
export function setLocale(locale) {
|
||||||
ar,
|
theLocale = locale;
|
||||||
en,
|
}
|
||||||
ca,
|
|
||||||
de,
|
|
||||||
es,
|
|
||||||
fa,
|
|
||||||
he,
|
|
||||||
hr,
|
|
||||||
hu,
|
|
||||||
io,
|
|
||||||
it,
|
|
||||||
fr,
|
|
||||||
nl,
|
|
||||||
no,
|
|
||||||
oc,
|
|
||||||
pt,
|
|
||||||
'pt-BR': pt_br,
|
|
||||||
uk,
|
|
||||||
fi,
|
|
||||||
eo,
|
|
||||||
ru,
|
|
||||||
ja,
|
|
||||||
'zh-HK': zh_hk,
|
|
||||||
'zh-CN': zh_cn,
|
|
||||||
bg,
|
|
||||||
id,
|
|
||||||
tr,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function getMessagesForLocale(locale) {
|
export function getLocale() {
|
||||||
return locales[locale];
|
return theLocale;
|
||||||
};
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
= stylesheet_pack_tag 'application', media: 'all'
|
= stylesheet_pack_tag 'application', media: 'all'
|
||||||
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
|
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
|
||||||
|
= javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous'
|
||||||
= csrf_meta_tags
|
= csrf_meta_tags
|
||||||
|
|
||||||
= yield :header_tags
|
= yield :header_tags
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
// To avoid adding a lot of boilerplate, locale packs are
|
||||||
|
// automatically generated here. These are written into the tmp/
|
||||||
|
// directory and then used to generate locale_en.js, locale_fr.js, etc.
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
const mkdirp = require('mkdirp');
|
||||||
|
|
||||||
|
const localesJsonPath = path.join(__dirname, '../../app/javascript/mastodon/locales');
|
||||||
|
const locales = fs.readdirSync(localesJsonPath).filter(filename => {
|
||||||
|
return /\.json$/.test(filename) &&
|
||||||
|
!/defaultMessages/.test(filename) &&
|
||||||
|
!/whitelist/.test(filename);
|
||||||
|
}).map(filename => filename.replace(/\.json$/, ''));
|
||||||
|
|
||||||
|
const outPath = path.join(__dirname, '../../tmp/packs');
|
||||||
|
|
||||||
|
rimraf.sync(outPath);
|
||||||
|
mkdirp.sync(outPath);
|
||||||
|
|
||||||
|
const outPaths = [];
|
||||||
|
|
||||||
|
locales.forEach(locale => {
|
||||||
|
const localePath = path.join(outPath, `locale_${locale}.js`);
|
||||||
|
const baseLocale = locale.split('-')[0]; // e.g. 'zh-TW' -> 'zh'
|
||||||
|
const localeDataPath = [
|
||||||
|
// first try react-intl
|
||||||
|
`../../node_modules/react-intl/locale-data/${baseLocale}.js`,
|
||||||
|
// then check locales/locale-data
|
||||||
|
`../../app/javascript/mastodon/locales/locale-data/${baseLocale}.js`,
|
||||||
|
// fall back to English (this is what react-intl does anyway)
|
||||||
|
`../../node_modules/react-intl/locale-data/en.js`,
|
||||||
|
].filter(filename => fs.existsSync(path.join(outPath, filename)))
|
||||||
|
.map(filename => filename.replace(/..\/..\/node_modules\//, ''))[0];
|
||||||
|
|
||||||
|
const localeContent = `//
|
||||||
|
// locale_${locale}.js
|
||||||
|
// automatically generated by generateLocalePacks.js
|
||||||
|
//
|
||||||
|
import messages from '../../app/javascript/mastodon/locales/${locale}.json';
|
||||||
|
import localeData from ${JSON.stringify(localeDataPath)};
|
||||||
|
import { setLocale } from '../../app/javascript/mastodon/locales';
|
||||||
|
setLocale({messages, localeData});
|
||||||
|
`;
|
||||||
|
fs.writeFileSync(localePath, localeContent, 'utf8');
|
||||||
|
outPaths.push(localePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = outPaths;
|
||||||
|
|
||||||
|
|
|
@ -10,15 +10,20 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||||
const extname = require('path-complete-extname');
|
const extname = require('path-complete-extname');
|
||||||
const { env, paths, publicPath, loadersDir } = require('./configuration.js');
|
const { env, paths, publicPath, loadersDir } = require('./configuration.js');
|
||||||
|
const localePackPaths = require('./generateLocalePacks');
|
||||||
|
|
||||||
const extensionGlob = `**/*{${paths.extensions.join(',')}}*`;
|
const extensionGlob = `**/*{${paths.extensions.join(',')}}*`;
|
||||||
const packPaths = sync(join(paths.source, paths.entry, extensionGlob));
|
const packPaths = sync(join(paths.source, paths.entry, extensionGlob));
|
||||||
|
const entryPacks = [].concat(packPaths).concat(localePackPaths);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: packPaths.reduce(
|
entry: entryPacks.reduce(
|
||||||
(map, entry) => {
|
(map, entry) => {
|
||||||
const localMap = map;
|
const localMap = map;
|
||||||
const namespace = relative(join(paths.source, paths.entry), dirname(entry));
|
let namespace = relative(join(paths.source, paths.entry), dirname(entry));
|
||||||
|
if (namespace === '../../../tmp/packs') {
|
||||||
|
namespace = ''; // generated by generateLocalePacks.js
|
||||||
|
}
|
||||||
localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry);
|
localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry);
|
||||||
return localMap;
|
return localMap;
|
||||||
}, {}
|
}, {}
|
||||||
|
@ -41,7 +46,15 @@ module.exports = {
|
||||||
new ManifestPlugin({ fileName: paths.manifest, publicPath, writeToFileEmit: true }),
|
new ManifestPlugin({ fileName: paths.manifest, publicPath, writeToFileEmit: true }),
|
||||||
new webpack.optimize.CommonsChunkPlugin({
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
name: 'common',
|
name: 'common',
|
||||||
minChunks: 2,
|
minChunks: (module, count) => {
|
||||||
|
if (module.resource && /node_modules\/react-intl/.test(module.resource)) {
|
||||||
|
// skip react-intl because it's useless to put in the common chunk,
|
||||||
|
// e.g. because "shared" modules between zh-TW and zh-CN will never
|
||||||
|
// be loaded together
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return count >= 2;
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
"is-nan": "^1.2.1",
|
"is-nan": "^1.2.1",
|
||||||
"js-yaml": "^3.8.3",
|
"js-yaml": "^3.8.3",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
"node-sass": "^4.5.2",
|
"node-sass": "^4.5.2",
|
||||||
"npmlog": "^4.0.2",
|
"npmlog": "^4.0.2",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
|
@ -91,6 +92,7 @@
|
||||||
"redux-immutable": "^3.1.0",
|
"redux-immutable": "^3.1.0",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
"reselect": "^2.5.4",
|
"reselect": "^2.5.4",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
"sass-loader": "^6.0.3",
|
"sass-loader": "^6.0.3",
|
||||||
"stringz": "^0.1.2",
|
"stringz": "^0.1.2",
|
||||||
"style-loader": "^0.16.1",
|
"style-loader": "^0.16.1",
|
||||||
|
|
28
yarn.lock
28
yarn.lock
|
@ -5407,15 +5407,6 @@ react-redux-loading-bar@2.4.1:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-redux-loading-bar/-/react-redux-loading-bar-2.4.1.tgz#8df64db362f065b5453fbbb7379a5cf62440129a"
|
resolved "https://registry.yarnpkg.com/react-redux-loading-bar/-/react-redux-loading-bar-2.4.1.tgz#8df64db362f065b5453fbbb7379a5cf62440129a"
|
||||||
|
|
||||||
react-redux@^4.4.5:
|
|
||||||
version "4.4.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-4.4.5.tgz#f509a2981be2252d10c629ef7c559347a4aec457"
|
|
||||||
dependencies:
|
|
||||||
hoist-non-react-statics "^1.0.3"
|
|
||||||
invariant "^2.0.0"
|
|
||||||
lodash "^4.2.0"
|
|
||||||
loose-envify "^1.1.0"
|
|
||||||
|
|
||||||
react-redux@^5.0.4:
|
react-redux@^5.0.4:
|
||||||
version "5.0.4"
|
version "5.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.4.tgz#1563babadcfb2672f57f9ceaa439fb16bf85d55b"
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.4.tgz#1563babadcfb2672f57f9ceaa439fb16bf85d55b"
|
||||||
|
@ -5476,12 +5467,6 @@ react-test-renderer@^15.5.4:
|
||||||
fbjs "^0.8.9"
|
fbjs "^0.8.9"
|
||||||
object-assign "^4.1.0"
|
object-assign "^4.1.0"
|
||||||
|
|
||||||
react-themeable@^1.1.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-themeable/-/react-themeable-1.1.0.tgz#7d4466dd9b2b5fa75058727825e9f152ba379a0e"
|
|
||||||
dependencies:
|
|
||||||
object-assign "^3.0.0"
|
|
||||||
|
|
||||||
react-toggle@^2.1.1:
|
react-toggle@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-toggle/-/react-toggle-2.1.1.tgz#80600a64417a1acc8aaa4c1477f7fbdb88b988fb"
|
resolved "https://registry.yarnpkg.com/react-toggle/-/react-toggle-2.1.1.tgz#80600a64417a1acc8aaa4c1477f7fbdb88b988fb"
|
||||||
|
@ -5792,6 +5777,12 @@ rimraf@2, rimraf@^2.2.8, rimraf@~2.5.0, rimraf@~2.5.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.0.5"
|
glob "^7.0.5"
|
||||||
|
|
||||||
|
rimraf@^2.6.1:
|
||||||
|
version "2.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
|
||||||
|
dependencies:
|
||||||
|
glob "^7.0.5"
|
||||||
|
|
||||||
ripemd160@0.2.0:
|
ripemd160@0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce"
|
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce"
|
||||||
|
@ -5843,13 +5834,6 @@ scroll-behavior@^0.8.0:
|
||||||
dom-helpers "^2.4.0"
|
dom-helpers "^2.4.0"
|
||||||
invariant "^2.2.1"
|
invariant "^2.2.1"
|
||||||
|
|
||||||
scss-tokenizer@^0.2.3:
|
|
||||||
version "0.2.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
|
|
||||||
dependencies:
|
|
||||||
js-base64 "^2.1.8"
|
|
||||||
source-map "^0.4.2"
|
|
||||||
|
|
||||||
seed-random@2.2.0:
|
seed-random@2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54"
|
resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54"
|
||||||
|
|
Loading…
Reference in New Issue