Ghost/core/frontend/services/theme-engine/i18n/i18n.js
Hannah Wolfe ba53de1add Refactored i18n into a class + index
- preparation for using DI instead of requires, so we can move this out of Ghost
- have done this for both the main i18n and theme i18n file
- refactored the constructor
2021-05-05 15:13:23 +01:00

97 lines
3.3 KiB
JavaScript

const errors = require('@tryghost/errors');
const i18n = require('../../../../shared/i18n');
const logging = require('../../../../shared/logging');
const settingsCache = require('../../../../server/services/settings/cache');
const config = require('../../../../shared/config');
const jp = require('jsonpath');
const isNil = require('lodash/isNil');
class ThemeI18n extends i18n.I18n {
constructor(options = {}) {
super(options);
}
/**
* Setup i18n support for themes:
* - Load correct language file into memory
*
* @param {String} activeTheme - name of the currently loaded theme
*/
init(activeTheme) {
// This function is called during theme initialization, and when switching language or theme.
const currentLocale = this._loadLocale();
// Reading file for current locale and active theme and keeping its content in memory
if (activeTheme) {
// Reading translation file for theme .hbs templates.
// Compatibility with both old themes and i18n-capable themes.
// Preventing missing files.
this._strings = this._tryGetLocale(activeTheme, currentLocale);
if (!this._strings && currentLocale !== this.defaultLocale()) {
logging.warn(`Falling back to locales/${this.defaultLocale()}.json.`);
this._strings = this._tryGetLocale(activeTheme, this.defaultLocale());
}
}
if (isNil(this._strings)) {
// even if empty, themeStrings must be an object for jp.value
this._strings = {};
}
this._initializeIntl();
}
/**
* Attempt to load a local file and parse the contents
*
* @param {String} activeTheme
* @param {String} locale
*/
_tryGetLocale(activeTheme, locale) {
try {
return this._readStringsFile(config.getContentPath('themes'), activeTheme, 'locales', `${locale}.json`);
} catch (err) {
if (err.code === 'ENOENT') {
if (locale !== this.defaultLocale()) {
logging.warn(`Theme's file locales/${locale}.json not found.`);
}
} else if (err instanceof SyntaxError) {
logging.error(new errors.IncorrectUsageError({
err,
message: `Unable to parse locales/${locale}.json. Please check that it is valid JSON.`
}));
} else {
throw err;
}
}
}
/**
* Load the current locale out of the settings cache
*/
_loadLocale() {
this._locale = settingsCache.get('lang');
return this._locale;
}
/**
* Do the lookup with JSON path
*
* @param {String} msgPath
*/
_getCandidateString(msgPath) {
// Both jsonpath's dot-notation and bracket-notation start with '$'
// E.g.: $.store.book.title or $['store']['book']['title']
// The {{t}} translation helper passes the default English text
// The full Unicode jsonpath with '$' is built here
// jp.stringify and jp.value are jsonpath methods
// Info: https://www.npmjs.com/package/jsonpath
let path = jp.stringify(['$', msgPath]);
return jp.value(this._strings, path) || msgPath;
}
}
module.exports = ThemeI18n;