apostrophe
Version:
The Apostrophe Content Management System.
132 lines (123 loc) • 5.22 kB
JavaScript
// This module makes an instance of the [i18n](https://npmjs.org/package/i18n) npm module available
// as `apos.i18n`. Apostrophe also makes this available in Nunjucks templates via the
// usual `__ns('apostrophe', )` helper function. Any options passed to this module are passed on to `i18n`.
//
// By default i18n locale files are generated in the `locales` subdirectory of the project.
//
// ## Options
//
// `localesDir`: if specified, the locale `.json` files are stored here, otherwise they
// are stored in the `locales` subdirectory of the project root.
//
// `namespaces`: if set to `true`, the translation key is prefixed like this
// so that translation teams can tell the difference between Apostrophe's
// UI phrases and your own phrases:
//
// "apostrophe<:>Phrase Here"
//
// The separator was chosen to be unlikely to be part of your actual text,
// but you can change the separator with the `namespaceOperator` option.
//
// ## Namespacing your own i18n calls
//
// You can optionally namespace your own i18n calls by invoking
// `__ns('namespace', 'phrase')` rather than `__('phrase')`,
// `__ns_n` rather than `__n`, etc.
//
// Currently the namespaced wrapper calls only support being invoked with a key as the
// second argument. Other variations are not supported with namespaces.
//
// *If you are using the `objectNotation` option to i18n, do not use your
// objectNotation separator character in a namespace name.*
var _ = require('@sailshq/lodash');
var i18n = require('i18n');
module.exports = {
construct: function(self, options) {
var i18nOptions = self.options || {};
_.defaults(i18nOptions, {
locales: [ 'en' ],
cookie: self.apos.shortName + '.locale',
directory: self.options.localesDir || (self.apos.rootDir + '/locales')
});
var superConfigure = i18n.configure;
i18n.configure = function(i18nOptions) {
if (!self.options.namespaceSeparator) {
if (i18nOptions.objectNotation) {
// With object notation a : in our separator breaks i18n
self.namespaceSeparator = '<@>';
} else {
// For bc with previous release when not using object notation
self.namespaceSeparator = '<:>';
}
self.objectNotation = i18nOptions.objectNotation;
} else {
self.namespaceSeparator = self.options.namespaceSeparator;
}
return superConfigure.call(i18n, i18nOptions);
};
i18n.configure(i18nOptions);
const operators = {
'__ns': '__',
'__ns_n': '__n',
'__ns_mf': '__mf',
'__ns_l': '__l',
'__ns_h': '__h'
};
_.each(operators, function(to, from) {
i18n[from] = function(namespace, key /* etc */) {
var prefixed;
var prefixLength;
var prefix;
var objectNotationChar;
if (self.options.namespaces && ((typeof key) === 'string')) {
prefix = namespace + self.namespaceSeparator;
prefixed = prefix + key;
arguments[1] = prefixed;
}
if (self.objectNotation) {
// We do not use object notation in the standard modules (apostrohe
// namespace), but use of . or : in the key will be treated as such,
// causing errors and misdisplay of the UI. Work around this by
// substituting for ":" (the key/default separator for object notation)
// and "." (or the configured objectNotation character)
if (namespace === 'apostrophe') {
// Replace : with U+FE55 SMALL COLON
key = key.replace(/:/g, '﹕');
objectNotationChar = (self.objectNotation !== true) ? self.objectNotation : '.';
// Replace . or chosen separator with U+FE52 SMALL PERIOD
key = key.replace(new RegExp(self.apos.utils.regExpQuote(objectNotationChar), 'g'), '﹒');
}
}
// `this` will typically be req or res at this point and that's important because
// i18n looks at it to figure out what the locale is, so it is correct to pass it
// to apply
let result = i18n[to].apply(this, Array.prototype.slice.call(arguments, 1));
if (self.options.namespaces && prefix && ((typeof result) === 'string')) {
prefixLength = prefix.length;
if (result.substring(0, prefixLength) === prefix) {
// No translation available, so it came straight through. Undo the prefixing
result = result.substring(prefixLength);
}
}
if (self.objectNotation && (namespace === 'apostrophe')) {
return result.replace(/﹕/g, ':').replace(/﹒/g, objectNotationChar);
} else {
return result;
}
};
});
// Make the i18n instance available globally in Apostrophe
self.apos.i18n = i18n;
self.namespacesMiddleware = function(req, res, next) {
// Implementation is above, we just need to bind it
const operators = [ '__ns', '__ns_n', '__ns_mf', '__ns_l', '__ns_h' ];
const targets = [ req, res ];
_.each(targets, function(target) {
_.each(operators, function(operator) {
target[operator] = i18n[operator];
});
});
return next();
};
}
};