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 object
This commit is contained in:
		
				
					committed by
					
						 Eugen Rochko
						Eugen Rochko
					
				
			
			
				
	
			
			
			
						parent
						
							73e4468ff3
						
					
				
				
					commit
					9d04de1c8d
				
			| @@ -41,34 +41,12 @@ import FavouritedStatuses from '../features/favourited_statuses'; | ||||
| import Blocks from '../features/blocks'; | ||||
| import Mutes from '../features/mutes'; | ||||
| 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 createStream from '../stream'; | ||||
| import { IntlProvider, addLocaleData } from 'react-intl'; | ||||
| import { getLocale } from '../locales'; | ||||
| const { localeData, messages } = getLocale(); | ||||
| addLocaleData(localeData); | ||||
|  | ||||
| const store = configureStore(); | ||||
| const initialState = JSON.parse(document.getElementById("initial-state").textContent); | ||||
| @@ -78,33 +56,6 @@ const browserHistory = useRouterHistory(createBrowserHistory)({ | ||||
|   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 { | ||||
|  | ||||
|   componentDidMount() { | ||||
| @@ -145,7 +96,7 @@ class Mastodon extends React.PureComponent { | ||||
|           store.dispatch(deleteFromTimelines(data.payload)); | ||||
|           break; | ||||
|         case 'notification': | ||||
|           store.dispatch(updateNotifications(JSON.parse(data.payload), getMessagesForLocale(locale), locale)); | ||||
|           store.dispatch(updateNotifications(JSON.parse(data.payload), messages, locale)); | ||||
|           break; | ||||
|         } | ||||
|       }, | ||||
| @@ -183,7 +134,7 @@ class Mastodon extends React.PureComponent { | ||||
|     const { locale } = this.props; | ||||
|  | ||||
|     return ( | ||||
|       <IntlProvider locale={locale} messages={getMessagesForLocale(locale)}> | ||||
|       <IntlProvider locale={locale} messages={messages}> | ||||
|         <Provider store={store}> | ||||
|           <Router history={browserHistory} render={applyRouterMiddleware(useScroll())}> | ||||
|             <Route path='/' component={UI}> | ||||
|   | ||||
| @@ -1,61 +1,9 @@ | ||||
| import ar from './ar.json'; | ||||
| 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'; | ||||
| let theLocale; | ||||
|  | ||||
| const locales = { | ||||
|   ar, | ||||
|   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 function setLocale(locale) { | ||||
|   theLocale = locale; | ||||
| } | ||||
|  | ||||
| export default function getMessagesForLocale(locale) { | ||||
|   return locales[locale]; | ||||
| }; | ||||
| export function getLocale() { | ||||
|   return theLocale; | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
|  | ||||
|     = stylesheet_pack_tag 'application', media: 'all' | ||||
|     = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' | ||||
|     = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' | ||||
|     = csrf_meta_tags | ||||
|  | ||||
|     = yield :header_tags | ||||
|   | ||||
							
								
								
									
										52
									
								
								config/webpack/generateLocalePacks.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								config/webpack/generateLocalePacks.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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 extname = require('path-complete-extname'); | ||||
| const { env, paths, publicPath, loadersDir } = require('./configuration.js'); | ||||
| const localePackPaths = require('./generateLocalePacks'); | ||||
|  | ||||
| const extensionGlob = `**/*{${paths.extensions.join(',')}}*`; | ||||
| const packPaths = sync(join(paths.source, paths.entry, extensionGlob)); | ||||
| const entryPacks = [].concat(packPaths).concat(localePackPaths); | ||||
|  | ||||
| module.exports = { | ||||
|   entry: packPaths.reduce( | ||||
|   entry: entryPacks.reduce( | ||||
|     (map, entry) => { | ||||
|       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); | ||||
|       return localMap; | ||||
|     }, {} | ||||
| @@ -41,7 +46,15 @@ module.exports = { | ||||
|     new ManifestPlugin({ fileName: paths.manifest, publicPath, writeToFileEmit: true }), | ||||
|     new webpack.optimize.CommonsChunkPlugin({ | ||||
|       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", | ||||
|     "js-yaml": "^3.8.3", | ||||
|     "lodash": "^4.17.4", | ||||
|     "mkdirp": "^0.5.1", | ||||
|     "node-sass": "^4.5.2", | ||||
|     "npmlog": "^4.0.2", | ||||
|     "object-assign": "^4.1.1", | ||||
| @@ -91,6 +92,7 @@ | ||||
|     "redux-immutable": "^3.1.0", | ||||
|     "redux-thunk": "^2.2.0", | ||||
|     "reselect": "^2.5.4", | ||||
|     "rimraf": "^2.6.1", | ||||
|     "sass-loader": "^6.0.3", | ||||
|     "stringz": "^0.1.2", | ||||
|     "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" | ||||
|   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: | ||||
|   version "5.0.4" | ||||
|   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" | ||||
|     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: | ||||
|   version "2.1.1" | ||||
|   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: | ||||
|     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: | ||||
|   version "0.2.0" | ||||
|   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" | ||||
|     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: | ||||
|   version "2.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user