mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-30 10:58:23 -05:00
82785435bf
Added tx-import, expects TX_TOKEN environment variable to be set to an API token for Transifex. If no API token exists it’ll just use the default English strings for everything. Removed the localization spot checks because they were checking tha json got built from po files. With Tx-import the files are already json, and may possibly be missing.
236 lines
8.1 KiB
JavaScript
Executable file
236 lines
8.1 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
/*
|
|
Generates javascript translation files for each view from the individual language
|
|
json files downloaded from Transifex for each view. Each view also includes the
|
|
general translations.
|
|
|
|
Expects two inputs, the directory for the input json files, and an output
|
|
to put the generated js files into.
|
|
|
|
The input directory is to have subfolders for each view, with json files named by
|
|
langage code. E.g., localizations/about/fr.json
|
|
|
|
The output files are javascript files that declare objects by locale. Each locale
|
|
has a sub-object with FormattedMessage ids as keys, and translated strings as
|
|
values. If no translation was found for a string, the default english will be the
|
|
value.
|
|
|
|
Output Example:
|
|
'''
|
|
var message = {
|
|
en: {
|
|
'general.inAWorld': 'In a world, where bears are invisible...',
|
|
'general.question': 'Are there bears here?',
|
|
'general.answer': 'I dunno, but there could be...'
|
|
},
|
|
es: {
|
|
'general.inAWorld': 'En un mundo, donde hay osos invisibles',
|
|
'general.question': 'Are there bears here?',
|
|
'general.answer': 'No sé, pero es posible...'
|
|
}
|
|
}
|
|
'''
|
|
*/
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
var merge = require('lodash.merge');
|
|
var async = require('async');
|
|
var languages = require('../languages.json');
|
|
var localizedUrls = require('./lib/localized-urls');
|
|
var routes = require('../src/routes.json');
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Utility function
|
|
// -----------------------------------------------------------------------------
|
|
|
|
var writeIntlFile = function (outputDir, view, translations, callback) {
|
|
var fileString = 'window._messages = ' + JSON.stringify(translations) + ';';
|
|
var fileName = outputDir + '/' + view + '.intl.js';
|
|
fs.writeFile(fileName, fileString, 'utf8', function (err) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback();
|
|
}
|
|
});
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Main script
|
|
// -----------------------------------------------------------------------------
|
|
|
|
var args = process.argv.slice(2);
|
|
|
|
if (!args.length) {
|
|
process.stdout.write('A localizations directory must be specified.\n');
|
|
process.exit(1);
|
|
}
|
|
|
|
var localesDir = path.resolve(__dirname, '../', args.shift());
|
|
|
|
try {
|
|
fs.accessSync(localesDir, fs.F_OK);
|
|
} catch (err) {
|
|
// Doesn't exist - create it.
|
|
process.stdout.write('Fatal error: No localizations directory.\n');
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!args.length) {
|
|
process.stdout.write('A destination directory must be specified.\n');
|
|
process.exit(1);
|
|
}
|
|
var outputDir = path.resolve(__dirname, '../', args[0]);
|
|
try {
|
|
fs.accessSync(outputDir, fs.F_OK);
|
|
} catch (err) {
|
|
// Doesn't exist - create it.
|
|
fs.mkdirSync(outputDir);
|
|
}
|
|
|
|
// get global locale strings first.
|
|
var globalTemplateFile = path.resolve(__dirname, '../src/l10n.json');
|
|
|
|
// generate object with all translations of general strings;
|
|
// every view includes these strings
|
|
var generalLocales = {'en': JSON.parse(fs.readFileSync(globalTemplateFile, 'utf8'))};
|
|
for ( var l in languages ) {
|
|
try {
|
|
var langTranslations = path.resolve(__dirname, '../', localesDir, 'general', l + '.json');
|
|
fs.accessSync(langTranslations);
|
|
generalLocales[l] = JSON.parse(fs.readFileSync(langTranslations, 'utf8'));
|
|
} catch (err) {
|
|
// just use english
|
|
generalLocales[l] = generalLocales['en'];
|
|
}
|
|
}
|
|
|
|
// default strings (en), for all views
|
|
var defaultLocales = {
|
|
//e.g.
|
|
// 'explore: {'explore.recent': 'recent'}
|
|
// 'about': {'about.title': 'about Scratch'}
|
|
// ... etc
|
|
};
|
|
var views = [];
|
|
var localizedAssetUrls = {};
|
|
|
|
// start with english default for all views
|
|
for (var v in routes) {
|
|
if (typeof routes[v].redirect !== 'undefined') {
|
|
continue;
|
|
}
|
|
|
|
views.push(routes[v].name);
|
|
try {
|
|
var subdir = routes[v].view.split('/');
|
|
subdir.pop();
|
|
var l10n = path.resolve(__dirname, '../src/views/' + subdir.join('/') + '/l10n.json');
|
|
var viewIds = JSON.parse(fs.readFileSync(l10n, 'utf8'));
|
|
defaultLocales[routes[v].name] = viewIds;
|
|
} catch (err) {
|
|
if (err.code !== 'ENOENT') {
|
|
throw err;
|
|
}
|
|
|
|
try {
|
|
fs.accessSync(path.resolve(__dirname, '../src/views/' + routes[v].view + '.jsx'));
|
|
} catch (err) {
|
|
// the config for the view is not set up correctly, so throw the error.
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
// get asset url translations
|
|
try {
|
|
subdir = routes[v].view.split('/');
|
|
subdir.pop();
|
|
var l10nStatic = path.resolve(__dirname, '../src/views/' + subdir.join('/') + '/l10n-static.json');
|
|
localizedAssetUrls[routes[v].name] = {};
|
|
|
|
var assetUrls = require(l10nStatic);
|
|
localizedAssetUrls[routes[v].name]['en'] = assetUrls;
|
|
for (var lang in languages) {
|
|
localizedAssetUrls[routes[v].name][lang] = {};
|
|
var langUrls = localizedUrls[lang] || {};
|
|
for (var key in assetUrls) {
|
|
if (langUrls.hasOwnProperty(key)) {
|
|
localizedAssetUrls[routes[v].name][lang][key] = langUrls[key];
|
|
} else {
|
|
localizedAssetUrls[routes[v].name][lang][key] = assetUrls[key];
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
if (err.code !== 'MODULE_NOT_FOUND') {
|
|
throw err;
|
|
}
|
|
|
|
try {
|
|
fs.accessSync(path.resolve(__dirname, '../src/views/' + routes[v].view + '.jsx'));
|
|
} catch (err) {
|
|
// the config for the view is not set up correctly, so throw the error.
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
|
|
var allLangs = Object.keys(languages);
|
|
|
|
async.forEachLimit(views, 5, function (view, cb) {
|
|
//start with localizedAssets, merge the view translations
|
|
var viewLocales = {};
|
|
viewLocales['en'] = merge({}, generalLocales['en'], defaultLocales[view]);
|
|
|
|
if ( defaultLocales.hasOwnProperty(view) ) {
|
|
// merge view specific english strings, first then other languages
|
|
process.stdout.write('Merging translations for ' + view + '\n');
|
|
async.forEach(allLangs, function (isoCode, cb) {
|
|
var translationsFile = path.resolve(__dirname, '../', localesDir, view, isoCode + '.json');
|
|
fs.readFile(translationsFile, 'utf8', function (err, data) {
|
|
if (err) {
|
|
if (err.code === 'ENOENT') {
|
|
if (isoCode !== 'en') {
|
|
process.stdout.write('No translations for ' + view + isoCode + ', using english\n');
|
|
viewLocales[isoCode] = merge({}, generalLocales[isoCode], defaultLocales[view]);
|
|
}
|
|
return cb();
|
|
} else {
|
|
return cb(err);
|
|
}
|
|
}
|
|
try {
|
|
viewLocales[isoCode] = merge({}, generalLocales[isoCode], JSON.parse(data));
|
|
} catch (e) {
|
|
return cb(e);
|
|
}
|
|
cb();
|
|
});
|
|
}, function (err) {
|
|
if (err) process.stdout.write('Error merging translations for view: ' + view + '\n' + err + '\n');
|
|
var viewTranslations = merge({}, viewLocales, localizedAssetUrls[view]);
|
|
writeIntlFile(outputDir, view, viewTranslations, function (err) {
|
|
if (err) {
|
|
process.stdout.write('Failed to save: ' + view + '\n');
|
|
return cb(err);
|
|
}
|
|
});
|
|
});
|
|
|
|
} else {
|
|
var viewTranslations = merge({}, viewLocales, localizedAssetUrls[view]);
|
|
writeIntlFile(outputDir, view, viewTranslations, function (err) {
|
|
if (err) {
|
|
process.stdout.write('Failed to save: ' + view + '\n');
|
|
return cb(err);
|
|
}
|
|
});
|
|
}
|
|
cb();
|
|
}, function (err) {
|
|
if (err) {
|
|
process.stdout.write('Writing intl files completed with errors\n');
|
|
}
|
|
return;
|
|
});
|