From d89abaa15edf5990887e1c6341ca547fc13c97d8 Mon Sep 17 00:00:00 2001 From: Maurice Williams Date: Sat, 18 Jul 2015 00:07:42 -0400 Subject: [PATCH] foundation work for server-side i18n support: * ref #5345 and #3801 * creating helper methods for dealing with i18n initialization, polyfill and string+compilation * initial structure for server-side JSON language file --- core/server/i18n.js | 111 +++++++++++++++++++++++++++++++ core/server/index.js | 4 ++ core/server/translations/en.json | 14 ++++ package.json | 2 + 4 files changed, 131 insertions(+) create mode 100644 core/server/i18n.js create mode 100644 core/server/translations/en.json diff --git a/core/server/i18n.js b/core/server/i18n.js new file mode 100644 index 000000000000..eae299cf35ab --- /dev/null +++ b/core/server/i18n.js @@ -0,0 +1,111 @@ +/* global Intl */ + +var supportedLocales = ['en'], + _ = require('lodash'), + fs = require('fs'), + chalk = require('chalk'), + MessageFormat = require('intl-messageformat'), + errors = require('./errors'), + + // TODO: fetch this dynamically based on overall blog settings (`key = "defaultLang"` in the `settings` table + 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") + * @param {json} context + * @returns {string} + */ + t: function t(path, context) { + 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); + + msg.push(m.format(context)); + }); + } else { + msg = new MessageFormat(string, currentLocale); + msg = msg.format(context); + } + + 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} + */ + findString: function findString(msgPath) { + var matchingString, path; + + // no path? no string + if (_.isEmpty(msgPath) || !_.isString(msgPath)) { + chalk.yellow('i18n:t() - received an empty path.'); + return ''; + } + + matchingString = blos; + + path = msgPath.split('.'); + path.forEach(function (key) { + // reassign matching object, or set to an empty string if there is no match + matchingString = matchingString[key] || null; + }); + + if (_.isNull(matchingString)) { + errors.logError('Unable to find matching path [' + msgPath + '] in locale file.'); + matchingString = 'i18n error: path "' + msgPath + '" was not found.'; + } + + return matchingString; + }, + + /** + * 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'); + blos = JSON.parse(blos); + + 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; diff --git a/core/server/index.js b/core/server/index.js index 760e18be7646..796b3706013a 100644 --- a/core/server/index.js +++ b/core/server/index.js @@ -9,6 +9,7 @@ var express = require('express'), uuid = require('node-uuid'), _ = require('lodash'), Promise = require('bluebird'), + i18n = require('./i18n'), api = require('./api'), config = require('./config'), @@ -186,6 +187,9 @@ function init(options) { }).then(function () { var adminHbs = hbs.create(); + // Initialize Internationalization + i18n.init(); + // Output necessary notifications on init initNotifications(); // ##Configuration diff --git a/core/server/translations/en.json b/core/server/translations/en.json new file mode 100644 index 000000000000..80ec37b3ffe9 --- /dev/null +++ b/core/server/translations/en.json @@ -0,0 +1,14 @@ +{ + "common": { + + }, + "errors": { + + }, + "warnings": { + + }, + "notices": { + + } +} diff --git a/package.json b/package.json index 192fb0173c70..582199d04d2f 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "fs-extra": "0.18.4", "glob": "4.3.2", "html-to-text": "1.3.0", + "intl": "1.0.0", + "intl-messageformat": "1.1.0", "knex": "0.7.3", "lodash": "3.10.0", "moment": "2.10.3",