Skip to content

Commit 76ea239

Browse files
committed
Merge pull request #725 from taimir/i18n-get-msg-improved
Improved localization through google getMsg()
2 parents d2c14c7 + f398a41 commit 76ea239

21 files changed

+641
-85
lines changed

bower.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
},
2020
"roboto-fontface": {
2121
"main": "css/roboto-fontface.css"
22+
},
23+
"google-closure-library": {
24+
"main": ["closure/goog/base.js", "closure/goog/deps.js"]
2225
}
2326
},
2427
"devDependencies": {

build/build.js

Lines changed: 150 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ import gulpHtmlmin from 'gulp-htmlmin';
2222
import gulpUglify from 'gulp-uglify';
2323
import gulpIf from 'gulp-if';
2424
import gulpUseref from 'gulp-useref';
25-
import gulpRev from 'gulp-rev';
26-
import gulpRevReplace from 'gulp-rev-replace';
27-
import uglifySaveLicense from 'uglify-save-license';
25+
import GulpRevAll from 'gulp-rev-all';
26+
import mergeStream from 'merge-stream';
2827
import path from 'path';
28+
import uglifySaveLicense from 'uglify-save-license';
2929

3030
import conf from './conf';
3131
import {multiDest} from './multidest';
@@ -41,19 +41,67 @@ gulp.task('build', ['backend:prod', 'build-frontend']);
4141
gulp.task('build:cross', ['backend:prod:cross', 'build-frontend:cross']);
4242

4343
/**
44-
* Builds production version of the frontend application for the current architecture.
44+
* Builds production version of the frontend application for the default architecture.
45+
*/
46+
gulp.task(
47+
'build-frontend', ['localize', 'locales-for-backend'], function() { return doRevision(); });
48+
49+
/**
50+
* Builds production version of the frontend application for all supported architectures.
51+
*/
52+
gulp.task('build-frontend:cross', ['localize:cross', 'locales-for-backend:cross'], function() {
53+
return doRevision();
54+
});
55+
56+
/**
57+
* Localizes all pre-created frontend copies for the default arch, so that they are ready to serve.
58+
*/
59+
gulp.task('localize', ['frontend-copies'], function() {
60+
return localize([path.join(conf.paths.distPre, conf.arch.default, 'public')]);
61+
});
62+
63+
/**
64+
* Localizes all pre-created frontend copies in all cross-arch directories, so that they are ready
65+
* to serve.
66+
*/
67+
gulp.task('localize:cross', ['frontend-copies:cross'], function() {
68+
return localize(conf.arch.list.map((arch) => path.join(conf.paths.distPre, arch, 'public')));
69+
});
70+
71+
/**
72+
* Copies the locales configuration to the default arch directory.
73+
* This configuration file is then used by the backend to localize dashboard.
74+
*/
75+
gulp.task('locales-for-backend', ['clean-dist'], function() {
76+
return localesForBackend([conf.paths.dist]);
77+
});
78+
79+
/**
80+
* Copies the locales configuration to each arch directory.
81+
* This configuration file is then used by the backend to localize dashboard.
82+
*/
83+
gulp.task('locales-for-backend:cross', ['clean-dist'], function() {
84+
return localesForBackend(conf.paths.distCross);
85+
});
86+
87+
/**
88+
* Builds production version of the frontend application for the default architecture
89+
* (one copy per locale) and plcaes it under .tmp/dist , preparing it for localization and revision.
4590
*/
46-
gulp.task('build-frontend', ['fonts', 'icons', 'assets', 'index:prod', 'clean-dist'], function() {
47-
return buildFrontend(conf.paths.distPublic);
91+
gulp.task('frontend-copies', ['fonts', 'icons', 'assets', 'index:prod', 'clean-dist'], function() {
92+
return createFrontendCopies([path.join(conf.paths.distPre, conf.arch.default, 'public')]);
4893
});
4994

5095
/**
51-
* Builds production version of the frontend application for all architecures.
96+
* Builds production versions of the frontend application for all architecures
97+
* (one copy per locale) and places them under .tmp, preparing them for localization and revision.
5298
*/
5399
gulp.task(
54-
'build-frontend:cross',
55-
['fonts:cross', 'icons:cross', 'assets:cross', 'index:prod', 'clean-dist'],
56-
function() { return buildFrontend(conf.paths.distPublicCross); });
100+
'frontend-copies:cross',
101+
['fonts:cross', 'icons:cross', 'assets:cross', 'index:prod', 'clean-dist'], function() {
102+
return createFrontendCopies(
103+
conf.arch.list.map((arch) => path.join(conf.paths.distPre, arch, 'public')));
104+
});
57105

58106
/**
59107
* Copies assets to the dist directory for current architecture.
@@ -69,24 +117,22 @@ gulp.task(
69117
/**
70118
* Copies icons to the dist directory for current architecture.
71119
*/
72-
gulp.task('icons', ['clean-dist'], function() { return icons(conf.paths.iconsDistPublic); });
120+
gulp.task('icons', ['clean-dist'], function() { return icons([conf.paths.distPublic]); });
73121

74122
/**
75123
* Copies icons to the dist directory for all architectures.
76124
*/
77-
gulp.task(
78-
'icons:cross', ['clean-dist'], function() { return icons(conf.paths.iconsDistPublicCross); });
125+
gulp.task('icons:cross', ['clean-dist'], function() { return icons(conf.paths.distPublicCross); });
79126

80127
/**
81128
* Copies fonts to the dist directory for current architecture.
82129
*/
83-
gulp.task('fonts', ['clean-dist'], function() { return fonts(conf.paths.fontsDistPublic); });
130+
gulp.task('fonts', ['clean-dist'], function() { return fonts([conf.paths.distPublic]); });
84131

85132
/**
86133
* Copies fonts to the dist directory for all architectures.
87134
*/
88-
gulp.task(
89-
'fonts:cross', ['clean-dist'], function() { return fonts(conf.paths.fontsDistPublicCross); });
135+
gulp.task('fonts:cross', ['clean-dist'], function() { return fonts(conf.paths.distPublicCross); });
90136

91137
/**
92138
* Cleans all build artifacts.
@@ -98,20 +144,27 @@ gulp.task('clean', ['clean-dist'], function() {
98144
/**
99145
* Cleans all build artifacts in the dist/ folder.
100146
*/
101-
gulp.task('clean-dist', function() { return del([conf.paths.distRoot]); });
147+
gulp.task('clean-dist', function() { return del([conf.paths.distRoot, conf.paths.distPre]); });
102148

103149
/**
104-
* Builds production version of the frontend application.
150+
* Builds production version of the frontend application and copies it to all
151+
* the specified outputDirs, creating one copy per (outputDir x locale) tuple.
105152
*
106153
* Following steps are done here:
107154
* 1. Vendor CSS and JS files are concatenated and minified.
108155
* 2. index.html is minified.
109-
* 3. CSS and JS assets are suffixed with version hash.
110-
* 4. Everything is saved in the dist directory.
111-
* @param {string|!Array<string>} outputDirs
156+
* 3. Everything is saved in the .tmp/dist directory, ready to be localized and revisioned.
157+
*
158+
* @param {!Array<string>} outputDirs
112159
* @return {stream}
113160
*/
114-
function buildFrontend(outputDirs) {
161+
function createFrontendCopies(outputDirs) {
162+
// create an output for each locale
163+
let localizedOutputDirs = outputDirs.reduce((localizedDirs, outputDir) => {
164+
return localizedDirs.concat(
165+
conf.translations.map((translation) => { return path.join(outputDir, translation.key); }));
166+
}, []);
167+
115168
let searchPath = [
116169
// To resolve local paths.
117170
path.relative(conf.paths.base, conf.paths.prodTmp),
@@ -123,44 +176,109 @@ function buildFrontend(outputDirs) {
123176
.pipe(gulpUseref({searchPath: searchPath}))
124177
.pipe(gulpIf('**/vendor.css', gulpMinifyCss()))
125178
.pipe(gulpIf('**/vendor.js', gulpUglify({preserveComments: uglifySaveLicense})))
126-
.pipe(gulpIf(['**/*.js', '**/*.css'], gulpRev()))
127-
.pipe(gulpUseref({searchPath: searchPath}))
128-
.pipe(gulpRevReplace())
129179
.pipe(gulpIf('*.html', gulpHtmlmin({
130180
removeComments: true,
131181
collapseWhitespace: true,
132182
conservativeCollapse: true,
133183
})))
134-
.pipe(multiDest(outputDirs));
184+
.pipe(multiDest(localizedOutputDirs));
135185
}
136186

137187
/**
138-
* @param {string|!Array<string>} outputDirs
188+
* Creates revisions of all .js anc .css files at once (for production).
189+
* Replaces the occurances of those files in index.html with their new names.
190+
* index.html does not get renamed in the process.
191+
* The processed files are then moved to the dist directory.
192+
* @return {stream}
193+
*/
194+
function doRevision() {
195+
// Do not update references other than in index.html. Do not rev index.html itself.
196+
let revAll =
197+
new GulpRevAll({dontRenameFile: ['index.html'], dontSearchFile: [/^(?!.*index\.html$).*$/]});
198+
return gulp.src([path.join(conf.paths.distPre, '**'), '!**/assets/**/*'])
199+
.pipe(revAll.revision())
200+
.pipe(gulp.dest(conf.paths.distRoot));
201+
}
202+
203+
/**
204+
* Copies the localized app.js files for each supported language in outputDir/<locale>/static
205+
* for each of the specified output dirs.
206+
* @param {!Array<string>} outputDirs - list of all arch directories
207+
* @return {stream}
208+
*/
209+
function localize(outputDirs) {
210+
let streams = conf.translations.map((translation) => {
211+
let localizedOutputDirs =
212+
outputDirs.map((outputDir) => { return path.join(outputDir, translation.key, 'static'); });
213+
return gulp.src(path.join(conf.paths.i18nProd, translation.key, '*.js'))
214+
.pipe(multiDest(localizedOutputDirs));
215+
});
216+
217+
return mergeStream.apply(null, streams);
218+
}
219+
220+
/**
221+
* Copies the locales configuration file at the base of each arch directory, next to
222+
* all of the localized subdirs. This file is meant to be used by the backend binary
223+
* to compare against and determine the right locale to serve at runtime.
224+
* @param {!Array<string>} outputDirs - list of all arch directories
225+
* @return {stream}
226+
*/
227+
function localesForBackend(outputDirs) {
228+
return gulp.src(path.join(conf.paths.base, 'i18n', '*.json')).pipe(multiDest(outputDirs));
229+
}
230+
231+
/**
232+
* Copies the assets files to all dist directories per arch and locale.
233+
* @param {!Array<string>} outputDirs
139234
* @return {stream}
140235
*/
141236
function assets(outputDirs) {
237+
let localizedOutputDirs = createLocalizedOutputs(outputDirs);
142238
return gulp.src(path.join(conf.paths.assets, '/**/*'), {base: conf.paths.app})
143-
.pipe(multiDest(outputDirs));
239+
.pipe(multiDest(localizedOutputDirs));
144240
}
145241

146242
/**
147-
* @param {string|!Array<string>} outputDirs
243+
* Copies the icons files to all dist directories per arch and locale.
244+
* @param {!Array<string>} outputDirs
148245
* @return {stream}
149246
*/
150247
function icons(outputDirs) {
248+
let localizedOutputDirs = createLocalizedOutputs(outputDirs, 'static');
151249
return gulp
152250
.src(
153251
path.join(conf.paths.materialIcons, '/**/*.+(woff2|woff|eot|ttf)'),
154252
{base: conf.paths.materialIcons})
155-
.pipe(multiDest(outputDirs));
253+
.pipe(multiDest(localizedOutputDirs));
156254
}
157255

158256
/**
159-
* @param {string|!Array<string>} outputDirs
257+
* Copies the font files to all dist directories per arch and locale.
258+
* @param {!Array<string>} outputDirs
160259
* @return {stream}
161260
*/
162261
function fonts(outputDirs) {
262+
let localizedOutputDirs = createLocalizedOutputs(outputDirs, 'fonts');
163263
return gulp
164264
.src(path.join(conf.paths.robotoFonts, '/**/*.+(woff2)'), {base: conf.paths.robotoFonts})
165-
.pipe(multiDest(outputDirs));
265+
.pipe(multiDest(localizedOutputDirs));
266+
}
267+
268+
/**
269+
* Returns one subdirectory path for each supported locale inside all of the specified
270+
* outputDirs. Optionally, a subdirectory structure can be passed to append after each locale path.
271+
* @param {!Array<string>} outputDirs
272+
* @param {undefined|string} opt_subdir - an optional sub directory inside each locale directory.
273+
* @return {!Array<string>} localized output directories
274+
*/
275+
function createLocalizedOutputs(outputDirs, opt_subdir) {
276+
return outputDirs.reduce((localizedDirs, outputDir) => {
277+
return localizedDirs.concat(conf.translations.map((translation) => {
278+
if (opt_subdir) {
279+
return path.join(outputDir, translation.key, opt_subdir);
280+
}
281+
return path.join(outputDir, translation.key);
282+
}));
283+
}, []);
166284
}

build/conf.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
*/
1818
import path from 'path';
1919

20+
/**
21+
* Load the i18n and l10n configuration. Used when dashboard is built in production.
22+
*/
23+
let localization = require('../i18n/locale_conf.json');
24+
2025
/**
2126
* Base path for all other paths.
2227
*/
@@ -154,6 +159,13 @@ export default {
154159
!!process.env.TRAVIS && process.env.TRAVIS_PULL_REQUEST === 'false',
155160
},
156161

162+
/**
163+
* Configuration for i18n & l10n.
164+
*/
165+
translations: localization.translations.map((translation) => {
166+
return {path: path.join(basePath, 'i18n', translation.file), key: translation.key};
167+
}),
168+
157169
/**
158170
* Absolute paths to known directories, e.g., to source directory.
159171
*/
@@ -172,21 +184,17 @@ export default {
172184
deploySrc: path.join(basePath, 'src/deploy'),
173185
dist: path.join(basePath, 'dist', arch.default),
174186
distCross: arch.list.map((arch) => path.join(basePath, 'dist', arch)),
187+
distPre: path.join(basePath, '.tmp/dist'),
175188
distPublic: path.join(basePath, 'dist', arch.default, 'public'),
176189
distPublicCross: arch.list.map((arch) => path.join(basePath, 'dist', arch, 'public')),
177190
distRoot: path.join(basePath, 'dist'),
178191
externs: path.join(basePath, 'src/app/externs'),
179-
fontsDistPublic: path.join(basePath, 'dist', arch.default, 'public/fonts'),
180-
fontsDistPublicCross:
181-
arch.list.map((arch) => path.join(basePath, 'dist', arch, 'public/fonts')),
182192
frontendSrc: path.join(basePath, 'src/app/frontend'),
183193
frontendTest: path.join(basePath, 'src/test/frontend'),
184194
goTools: path.join(basePath, '.tools/go'),
185195
goWorkspace: path.join(basePath, '.go_workspace'),
186196
hyperkube: path.join(basePath, 'build/hyperkube.sh'),
187-
iconsDistPublic: path.join(basePath, 'dist', arch.default, 'public/static'),
188-
iconsDistPublicCross:
189-
arch.list.map((arch) => path.join(basePath, 'dist', arch, 'public/static')),
197+
i18nProd: path.join(basePath, '.tmp/i18n'),
190198
integrationTest: path.join(basePath, 'src/test/integration'),
191199
karmaConf: path.join(basePath, 'build/karma.conf.js'),
192200
materialIcons: path.join(basePath, 'bower_components/material-design-icons/iconfont'),
@@ -198,5 +206,6 @@ export default {
198206
serve: path.join(basePath, '.tmp/serve'),
199207
src: path.join(basePath, 'src'),
200208
tmp: path.join(basePath, '.tmp'),
209+
xtbgenerator: path.join(basePath, '.tools/xtbgenerator/bin/XtbGenerator.jar'),
201210
},
202211
};

0 commit comments

Comments
 (0)