Skip to content

Instantly share code, notes, and snippets.

@valscion
Created February 21, 2016 11:33
Show Gist options
  • Save valscion/3bca60229a1923c1a948 to your computer and use it in GitHub Desktop.
Save valscion/3bca60229a1923c1a948 to your computer and use it in GitHub Desktop.
Venuu I18n Webpack integration
var ConstDependency = require('webpack/lib/dependencies/ConstDependency');
var NullFactory = require('webpack/lib/NullFactory');
var _ = require('lodash');
function I18nRuntimePlugin(options) {
options = options || {};
this.functionNames = options.functionNames || ['I18n.t', 'I18n.translate'];
this.translationsPlaceholder = options.translationsPlaceholder || 'I18N_RUNTIME_TRANSLATIONS';
this.fullTranslations = options.fullTranslations || {};
}
module.exports = I18nRuntimePlugin;
/**
* Return a single translation from the fullTranslations object using the
* normalized keypath string ("a.string.to.translate")
*/
function resolveTranslation(keypath, locale, fullTranslations) {
return keypath.split('.').reduce(function(previous, current) {
if (previous) {
return previous[current];
}
}, fullTranslations[locale]);
}
/**
* Add a translation to the given object, looking it up from the
* fullTranslations object using the given keypath
*/
function addTranslation(key, usedTranslationsForAllLocales, fullTranslations) {
Object.keys(usedTranslationsForAllLocales).forEach(function (locale) {
var usedTranslations = usedTranslationsForAllLocales[locale];
// Find the last possible translation object which can be modified in-place
// to set the new translations into.
var objToMergeTo = key.split('.').slice(0, -1).reduce(function(previous, current) {
previous[current] = previous[current] || {};
return previous[current];
}, usedTranslations);
// Find the translation to merge into the existing translations
var objToMergeFrom = {};
objToMergeFrom[key.split('.').slice(-1)[0]] = resolveTranslation(key, locale, fullTranslations);
_.merge(objToMergeTo, objToMergeFrom);
});
}
I18nRuntimePlugin.prototype.apply = function(compiler) {
var replacementDependency;
var keys = [];
var fullTranslations = this.fullTranslations;
var usedTranslations = {};
// Initialize all locales to empty objects
Object.keys(fullTranslations).forEach(function (locale) { usedTranslations[locale] = {}; });
compiler.plugin('compilation', function(compilation) {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
});
/*
* Obtain a reference to the placeholder location.
* We will decide the content later on.
*/
compiler.parser.plugin('expression ' + this.translationsPlaceholder, function(expr) {
replacementDependency = new ConstDependency(null, expr.range);
replacementDependency.loc = expr.loc;
this.state.current.addDependency(replacementDependency);
return true;
});
/*
* Collects the first argument of each call to `I18n.t` into the `keys` array.
*/
var collectKey = function(functionExpression) {
if (functionExpression.arguments.length < 1) return;
var key = this.evaluateExpression(functionExpression.arguments[0]);
if (!key.isString()) return;
key = key.string;
if (keys.indexOf(key) >= 0) return;
keys.push(key);
// Add the translation to the usedTranslations object
addTranslation(key, usedTranslations, fullTranslations);
// We don't know when the last call will occur, so we change the
// replacement on every call.
replacementDependency.expression = JSON.stringify(usedTranslations);
return;
};
this.functionNames.forEach(function(functionName) {
compiler.parser.plugin('call ' + functionName, collectKey);
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment