2015-07-18 07:07:42 +03:00
|
|
|
/* global Intl */
|
|
|
|
|
|
|
|
var supportedLocales = ['en'],
|
|
|
|
_ = require('lodash'),
|
|
|
|
fs = require('fs'),
|
|
|
|
chalk = require('chalk'),
|
|
|
|
MessageFormat = require('intl-messageformat'),
|
2017-10-03 10:47:48 +03:00
|
|
|
logging = require('./logging'),
|
|
|
|
errors = require('./errors'),
|
2015-07-18 07:07:42 +03:00
|
|
|
|
2017-05-31 18:05:49 +03:00
|
|
|
// TODO: fetch this dynamically based on overall blog settings (`key = "default_locale"`) in the `settings` table
|
2015-07-18 07:07:42 +03:00
|
|
|
currentLocale = 'en',
|
|
|
|
blos,
|
|
|
|
I18n;
|
|
|
|
|
|
|
|
I18n = {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper method to find and compile the given data context with a proper string resource.
|
|
|
|
*
|
|
|
|
* @param {string} path Path with in the JSON language file to desired string (ie: "errors.init.jsNotBuilt")
|
2016-02-22 03:57:22 +03:00
|
|
|
* @param {object} [bindings]
|
2015-07-18 07:07:42 +03:00
|
|
|
* @returns {string}
|
|
|
|
*/
|
2016-02-22 03:57:22 +03:00
|
|
|
t: function t(path, bindings) {
|
2015-07-18 07:07:42 +03:00
|
|
|
var string = I18n.findString(path),
|
|
|
|
msg;
|
|
|
|
|
|
|
|
// If the path returns an array (as in the case with anything that has multiple paragraphs such as emails), then
|
|
|
|
// loop through them and return an array of translated/formatted strings. Otherwise, just return the normal
|
|
|
|
// translated/formatted string.
|
|
|
|
if (_.isArray(string)) {
|
|
|
|
msg = [];
|
|
|
|
string.forEach(function (s) {
|
|
|
|
var m = new MessageFormat(s, currentLocale);
|
|
|
|
|
2016-02-22 03:57:22 +03:00
|
|
|
msg.push(m.format(bindings));
|
2015-07-18 07:07:42 +03:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
msg = new MessageFormat(string, currentLocale);
|
2016-02-22 03:57:22 +03:00
|
|
|
msg = msg.format(bindings);
|
2015-07-18 07:07:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse JSON file for matching locale, returns string giving path.
|
|
|
|
*
|
|
|
|
* @param {string} msgPath Path with in the JSON language file to desired string (ie: "errors.init.jsNotBuilt")
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2017-11-14 16:03:48 +03:00
|
|
|
findString: function findString(msgPath, opts) {
|
|
|
|
var options = _.merge({log: true}, opts || {}),
|
|
|
|
matchingString, path;
|
|
|
|
|
2015-07-18 07:07:42 +03:00
|
|
|
// no path? no string
|
|
|
|
if (_.isEmpty(msgPath) || !_.isString(msgPath)) {
|
|
|
|
chalk.yellow('i18n:t() - received an empty path.');
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2015-11-12 15:29:45 +03:00
|
|
|
if (blos === undefined) {
|
|
|
|
I18n.init();
|
|
|
|
}
|
|
|
|
|
2015-07-18 07:07:42 +03:00
|
|
|
matchingString = blos;
|
|
|
|
|
|
|
|
path = msgPath.split('.');
|
|
|
|
path.forEach(function (key) {
|
|
|
|
// reassign matching object, or set to an empty string if there is no match
|
2017-10-03 10:47:48 +03:00
|
|
|
matchingString = matchingString[key] || {};
|
2015-07-18 07:07:42 +03:00
|
|
|
});
|
2017-10-03 10:47:48 +03:00
|
|
|
if (_.isObject(matchingString) || _.isEqual(matchingString, {})) {
|
2017-11-14 16:03:48 +03:00
|
|
|
if (options.log) {
|
|
|
|
logging.error(new errors.IncorrectUsageError({
|
|
|
|
message: `i18n error: path "${msgPath}" was not found`
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2017-10-03 10:47:48 +03:00
|
|
|
matchingString = blos.errors.errors.anErrorOccurred;
|
2015-07-18 07:07:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return matchingString;
|
|
|
|
},
|
|
|
|
|
Added more database soft limits (#9225)
refs #8143
Sets soft limits for certain db fields:
- `posts`:
- `title`: 255 chars (current hard limit: 2,000 chars)
- `meta_title`: 300 chars (current hard limit: 2,000 chars)
- `meta_description`: 500 chars (current hard limit: 2,000 chars)
- `users`:
- `bio`: 200 chars (current hard limit: 65,535 chars)
- `location`: 150 chars (current hard limit: 65,535 chars)
- `meta_description`: 500 chars (current hard limit: 2,000 chars)
- `meta_title`: 300 chars (current hard limit: 2,000 chars)
- `tags`:
- `description`: 500 chars (current hard limit: 65,535 chars)
- `meta_title`: 300 chars (current hard limit: 2,000 chars)
- `meta_description`: 500 chars (current hard limit: 2,000 chars)
- same error message for isLength validator as for hard limits (more improvements are comming with https://github.com/TryGhost/Ghost/issues/6050)
- added more tests for importer
- added dynamic translation key handling for validators errors (isLength is only supported atm)
2017-11-09 17:22:20 +03:00
|
|
|
doesTranslationKeyExist: function doesTranslationKeyExist(msgPath) {
|
2017-11-14 16:03:48 +03:00
|
|
|
var translation = I18n.findString(msgPath, {log: false});
|
Added more database soft limits (#9225)
refs #8143
Sets soft limits for certain db fields:
- `posts`:
- `title`: 255 chars (current hard limit: 2,000 chars)
- `meta_title`: 300 chars (current hard limit: 2,000 chars)
- `meta_description`: 500 chars (current hard limit: 2,000 chars)
- `users`:
- `bio`: 200 chars (current hard limit: 65,535 chars)
- `location`: 150 chars (current hard limit: 65,535 chars)
- `meta_description`: 500 chars (current hard limit: 2,000 chars)
- `meta_title`: 300 chars (current hard limit: 2,000 chars)
- `tags`:
- `description`: 500 chars (current hard limit: 65,535 chars)
- `meta_title`: 300 chars (current hard limit: 2,000 chars)
- `meta_description`: 500 chars (current hard limit: 2,000 chars)
- same error message for isLength validator as for hard limits (more improvements are comming with https://github.com/TryGhost/Ghost/issues/6050)
- added more tests for importer
- added dynamic translation key handling for validators errors (isLength is only supported atm)
2017-11-09 17:22:20 +03:00
|
|
|
return translation !== blos.errors.errors.anErrorOccurred;
|
|
|
|
},
|
|
|
|
|
2015-07-18 07:07:42 +03:00
|
|
|
/**
|
|
|
|
* Setup i18n support:
|
|
|
|
* - Load proper language file in to memory
|
|
|
|
* - Polyfill node.js if it does not have Intl support or support for a particular locale
|
|
|
|
*/
|
|
|
|
init: function init() {
|
|
|
|
// read file for current locale and keep its content in memory
|
|
|
|
blos = fs.readFileSync(__dirname + '/translations/' + currentLocale + '.json');
|
2016-05-25 10:34:46 +03:00
|
|
|
|
|
|
|
// if translation file is not valid, you will see an error
|
|
|
|
try {
|
|
|
|
blos = JSON.parse(blos);
|
|
|
|
} catch (err) {
|
|
|
|
blos = undefined;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
|
2015-07-18 07:07:42 +03:00
|
|
|
if (global.Intl) {
|
|
|
|
// Determine if the built-in `Intl` has the locale data we need.
|
|
|
|
var hasBuiltInLocaleData,
|
|
|
|
IntlPolyfill;
|
|
|
|
|
|
|
|
hasBuiltInLocaleData = supportedLocales.every(function (locale) {
|
|
|
|
return Intl.NumberFormat.supportedLocalesOf(locale)[0] === locale &&
|
|
|
|
Intl.DateTimeFormat.supportedLocalesOf(locale)[0] === locale;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!hasBuiltInLocaleData) {
|
|
|
|
// `Intl` exists, but it doesn't have the data we need, so load the
|
|
|
|
// polyfill and replace the constructors with need with the polyfill's.
|
|
|
|
IntlPolyfill = require('intl');
|
|
|
|
Intl.NumberFormat = IntlPolyfill.NumberFormat;
|
|
|
|
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// No `Intl`, so use and load the polyfill.
|
|
|
|
global.Intl = require('intl');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = I18n;
|