Language generation and file structure

* Move language files to strings/ subdir to be consistent with
   react-sdk
 * Only copy static list of languages (to avoid including
   languages that are only a few percent translated)
 * Make copy-res script work with watch mode
 * Other general cleanups like only write each language file once,
   rather than n times.
This commit is contained in:
David Baker 2017-05-26 16:48:21 +01:00
parent e07f9a8bc9
commit 6c3c4fc547
12 changed files with 81 additions and 79 deletions

View File

@ -3,6 +3,23 @@
// copies the resources into the webapp directory. // copies the resources into the webapp directory.
// //
// Languages are listed manually so we can choose when to include
// a translation in the app (because having a translation with only
// 3 strings translated is just frustrating)
// This could readily be automated, but it's nice to explicitly
// control when we languages are available.
const INCLUDE_LANGS = [
//'be' Omitted because no translations in react-sdk
'en_EN',
'da',
'de_DE',
'fr',
'be',
'pt',
'pt_BR',
'ru',
];
// cpx includes globbed parts of the filename in the destination, but excludes // cpx includes globbed parts of the filename in the destination, but excludes
// common parents. Hence, "res/{a,b}/**": the output will be "dest/a/..." and // common parents. Hence, "res/{a,b}/**": the output will be "dest/a/..." and
// "dest/b/...". // "dest/b/...".
@ -14,32 +31,20 @@ const COPY_LIST = [
["node_modules/emojione/assets/svg/*", "webapp/emojione/svg/"], ["node_modules/emojione/assets/svg/*", "webapp/emojione/svg/"],
["node_modules/emojione/assets/png/*", "webapp/emojione/png/"], ["node_modules/emojione/assets/png/*", "webapp/emojione/png/"],
["./config.json", "webapp", { directwatch: 1 }], ["./config.json", "webapp", { directwatch: 1 }],
["src/i18n/", "webapp/i18n/", { languages: 1 }],
["node_modules/matrix-react-sdk/src/i18n/strings/", "webapp/i18n/", { languages: 1 }],
]; ];
INCLUDE_LANGS.forEach(function(l) {
COPY_LIST.push([
l, "webapp/i18n/", { lang: 1 },
]);
});
const parseArgs = require('minimist'); const parseArgs = require('minimist');
const Cpx = require('cpx'); const Cpx = require('cpx');
const chokidar = require('chokidar'); const chokidar = require('chokidar');
const fs = require('fs'); const fs = require('fs');
const rimraf = require('rimraf'); const rimraf = require('rimraf');
// cleanup language files before copying them.
//rimraf("webapp/", function () { console.log('cleanup language files'); });
//From http://stackoverflow.com/a/20525865/4929236
function generateFileArray(dir, files_) {
files_ = files_ || [];
var files = fs.readdirSync(dir);
for (var i in files) {
var name = files[i];
if (name != 'basefile.json') {
files_.push(name);
}
}
return files_;
}
const argv = parseArgs( const argv = parseArgs(
process.argv.slice(2), {} process.argv.slice(2), {}
); );
@ -54,6 +59,15 @@ function errCheck(err) {
} }
} }
// Check if webapp exists
if (!fs.existsSync('webapp')) {
fs.mkdirSync('webapp');
}
// Check if i18n exists
if (!fs.existsSync('webapp/i18n/')) {
fs.mkdirSync('webapp/i18n/');
}
function next(i, err) { function next(i, err) {
errCheck(err); errCheck(err);
@ -67,32 +81,11 @@ function next(i, err) {
const opts = ent[2] || {}; const opts = ent[2] || {};
let cpx = undefined; let cpx = undefined;
if (opts.languages) { if (!opts.lang) {
const sourceFiles = generateFileArray(source);
let Sourcelanguages = {};
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest);
}
sourceFiles.forEach(file => {
const fileContents = fs.readFileSync(source + file).toString();
Sourcelanguages[file] = JSON.parse(fileContents);
});
sourceFiles.forEach(file => {
if (!fs.existsSync(dest + file)) {
let o = Object.assign({}, Sourcelanguages[file]);
fs.writeFileSync(dest + file, JSON.stringify(o, null, 4));
} else {
const fileContents = fs.readFileSync(dest + file).toString();
let o = Object.assign(JSON.parse(fileContents), Sourcelanguages[file]);
fs.writeFileSync(dest + file, JSON.stringify(o, null, 4));
}
});
} else {
cpx = new Cpx.Cpx(source, dest); cpx = new Cpx.Cpx(source, dest);
} }
if (verbose) { if (verbose && cpx) {
cpx.on("copy", (event) => { cpx.on("copy", (event) => {
console.log(`Copied: ${event.srcPath} --> ${event.dstPath}`); console.log(`Copied: ${event.srcPath} --> ${event.dstPath}`);
}); });
@ -115,59 +108,68 @@ function next(i, err) {
.on('change', copy) .on('change', copy)
.on('ready', cb) .on('ready', cb)
.on('error', errCheck); .on('error', errCheck);
} else if (opts.languages) { } else if (opts.lang) {
if (verbose) { const reactSdkFile = 'node_modules/matrix-react-sdk/src/i18n/strings/' + source + '.json';
console.log('don\'t copy language file'); const riotWebFile = 'src/i18n/strings/' + source + '.json';
}
const translations = {};
const makeLang = () => { genLangFile(source, dest) };
[reactSdkFile, riotWebFile].forEach(function(f) {
chokidar.watch(f)
.on('add', makeLang)
.on('change', makeLang)
//.on('ready', cb) We'd have to do this when both files are ready
.on('error', errCheck);
});
next(i + 1, err); next(i + 1, err);
} else { } else {
cpx.on('watch-ready', cb); cpx.on('watch-ready', cb);
cpx.on("watch-error", cb); cpx.on("watch-error", cb);
cpx.watch(); cpx.watch();
} }
} else if (opts.languages) { } else if (opts.lang) {
if (verbose) { genLangFile(source, dest);
console.log('don\'t copy language file');
}
next(i + 1, err); next(i + 1, err);
} else { } else {
cpx.copy(cb); cpx.copy(cb);
} }
} }
// Generate Language List function genLangFile(lang, dest) {
const reactSdkFile = 'node_modules/matrix-react-sdk/src/i18n/strings/' + lang + '.json';
const riotWebFile = 'src/i18n/strings/' + lang + '.json';
const testFolder = 'src/i18n/'; const translations = {};
let languages = {}; [reactSdkFile, riotWebFile].forEach(function(f) {
// Check if webapp exists if (fs.existsSync(f)) {
if (!fs.existsSync('webapp')) { Object.assign(
fs.mkdirSync('webapp'); translations,
} JSON.parse(fs.readFileSync(f).toString())
// Check if i18n exists );
if (!fs.existsSync('webapp/i18n/')) {
fs.mkdirSync('webapp/i18n/');
}
if (!fs.existsSync('webapp/i18n/languages.json')) {
rimraf("webapp/i18n/languages.json", function() { console.log('cleanup languages.json file'); });
}
fs.readdir(testFolder, function(err, files) {
if (err) {
throw err;
} }
files.forEach(function(file) { });
var normalizedLanguage = file.toLowerCase().replace("_", "-").split('.json')[0]; fs.writeFileSync(dest + lang + '.json', JSON.stringify(translations, null, 4));
var languageParts = normalizedLanguage.split('-'); if (verbose) {
if (file != 'basefile.json') { console.log("Generated language file: " + lang);
}
}
function genLangList() {
const languages = {};
INCLUDE_LANGS.forEach(function(lang) {
const normalizedLanguage = lang.toLowerCase().replace("_", "-");
const languageParts = normalizedLanguage.split('-');
if (languageParts.length == 2 && languageParts[0] == languageParts[1]) { if (languageParts.length == 2 && languageParts[0] == languageParts[1]) {
languages[languageParts[0]] = file; languages[languageParts[0]] = lang;
} else { } else {
languages[normalizedLanguage] = file; languages[normalizedLanguage] = lang;
}
} }
}); });
fs.writeFile('webapp/i18n/languages.json', JSON.stringify(languages, null, 4)); fs.writeFile('webapp/i18n/languages.json', JSON.stringify(languages, null, 4));
}) if (verbose) {
console.log("Generated language list");
}
}
genLangList();
next(0); next(0);