Skip to content

Commit

Permalink
[i18n] Separate .json translation files for themes
Browse files Browse the repository at this point in the history
pull request TryGhost#8437

- for .hbs templates in content/themes, using translation files:
content/themes/mytheme/assets/translations/mytheme_en.json,
mytheme_es.json, mytheme_en-US.json, etc. (replacing "mytheme" with the
active theme, such as casper, etc.)
- during Ghost's initialization, first default internationalization just
for core briefly until settings for language and theme are available;
then full internationalization for core and theme
- files are read into memory just when needed
- included comments with next changes when language will be in settings
- there is a complementary commit for Casper default theme's PR
TryGhost/Casper#306
  • Loading branch information
juan-g authored and kirrg001 committed Jan 9, 2018
1 parent 93250a3 commit 6460c66
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 78 deletions.
10 changes: 8 additions & 2 deletions core/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ function init() {

var ghostServer, parentApp;

// Initialize Internationalization
// Initialize default internationalization, just for core now
// (settings for language and theme not yet available here)
common.i18n.init();
debug('I18n done');
debug('Default i18n done for core');
models.init();
debug('models done');

Expand All @@ -52,6 +53,11 @@ function init() {
return settings.init();
}).then(function () {
debug('Update settings cache done');
// Initialize full internationalization for core and theme
// (settings for language and theme available here)
return i18n.init();
}).then(function () {
debug('Full i18n done for core and theme');
// Initialize the permissions actions and objects
return permissions.init();
}).then(function () {
Expand Down
77 changes: 66 additions & 11 deletions core/server/lib/common/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,20 @@ var supportedLocales = ['en'],
logging = require('./logging'),
errors = require('./errors'),
jp = require('jsonpath'),
path = require('path'),
settingsCache = require('./settings/cache'),

// You can choose the current locale here. E.g.: en = English (default), es = Spanish, etc.
// The corresponding translation file (en.json, es.json...) should be at core/server/translations/
// For now, you can choose the current locale here. E.g.: en = English (default), es = Spanish, en-US = American English, etc.
// The corresponding translation files should be at core/server/translations/en.json, es.json, en-US.json... and
// content/themes/mytheme/assets/translations/mytheme_en.json, mytheme_es.json, mytheme_en-US.json...
// TODO: fetch this dynamically based on overall blog settings (`key = "default_locale"`) in the `settings` table
// (settings not available here during Ghost's initialization but inside i18n functions, see TODO comments below
// and file core/server/index.js)
currentLocale = 'en',
activeTheme,
blosCoreDefault,
blosCore,
blosTheme,
blos,
I18n;

Expand All @@ -30,6 +39,9 @@ I18n = {
var string = I18n.findString(path),
msg;

// TODO: When language will be in settings:
// currentLocale = settingsCache.get('default_lang') || 'en';

// 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.
Expand Down Expand Up @@ -126,15 +138,56 @@ I18n = {
* - 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(path.join(__dirname, '..', '..', 'translations', currentLocale + '.json'));

// if translation file is not valid, you will see an error
try {
blos = JSON.parse(blos);
} catch (err) {
blos = undefined;
throw err;
// TODO: When language will be in settings:
// currentLocale = settingsCache.get('default_lang') || 'en';

activeTheme = settingsCache.get('active_theme') || '';

// read files for current locale and active theme and keep their content in memory
if (activeTheme) {
// Initialize full internationalization for core and theme
// (settings for language and theme available here)
if (blosCore === undefined) {
blosCoreDefault = {};
blosCore = fs.readFileSync(path.join(__dirname, '..', '..', 'translations', currentLocale + '.json'));
}
if (blosTheme === undefined) {
// Compatibility with both old themes and i18n-capable themes.
try {
blosTheme = fs.readFileSync(path.join(__dirname, '..', '..', '..', '..', 'content', 'themes', activeTheme, 'assets', 'translations', activeTheme + '_' + currentLocale + '.json'));
} catch (err) {
blosTheme = undefined;
if (err.code === 'ENOENT') {
console.log('File ' + activeTheme + '_' + currentLocale + '.json not found!');
} else {
throw err;
}
}
}
// if translation files are not valid, you will see an error
try {
if (blosTheme === undefined) {
blos = JSON.parse(blosCore);
} else {
blos = _.merge(JSON.parse(blosCore), JSON.parse(blosTheme));
}
} catch (err) {
blos = undefined;
throw err;
}
} else {
// Initialize default internationalization, just for core now
// (settings for language and theme not yet available here)
if (blosCoreDefault === undefined) {
blosCoreDefault = fs.readFileSync(path.join(__dirname, '..', '..', 'translations', currentLocale + '.json'));
}
// if translation file is not valid, you will see an error
try {
blos = JSON.parse(blosCoreDefault);
} catch (err) {
blos = undefined;
throw err;
}
}

if (global.Intl) {
Expand Down Expand Up @@ -164,6 +217,8 @@ I18n = {
* such as core/server/helpers/date.js and core/server/helpers/t_css.js
*/
locale: function locale() {
// TODO: When language will be in settings:
// currentLocale = settingsCache.get('default_lang') || 'en';
return currentLocale;
}
};
Expand Down
23 changes: 0 additions & 23 deletions core/server/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -611,28 +611,5 @@
"with the email address": "with the email address",
"Your email address": "Your email address",
"You've successfully subscribed to": "You've successfully subscribed to"
},
"casper": {
"A 1-post collection": "A 1-post collection",
"A %-post collection": "A %-post collection",
"by this author": "by this author",
"Close": "Close",
"Get the latest posts delivered right to your inbox": "Get the latest posts delivered right to your inbox",
"Menu": "Menu",
"more posts": "more posts",
"No posts": "No posts",
"on": "on",
"or subscribe": "or subscribe",
"Proudly published with {ghostLink}": "Proudly published with {ghostLink}",
"Read": "Read",
"Scroll Down": "Scroll Down",
"Share this post": "Share this post",
"Subscribe": "Subscribe",
"Subscribe to": "Subscribe to",
"via RSS": "via RSS",
"with Feedly!": "with Feedly!",
"Your email address": "Your email address",
"1 post": "1 post",
"% posts": "% posts"
}
}
23 changes: 0 additions & 23 deletions core/server/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -590,28 +590,5 @@
"with the email address": "con el correo electrónico",
"Your email address": "Tu correo electrónico",
"You've successfully subscribed to": "Te has suscrito con éxito a"
},
"casper": {
"A 1-post collection": "Una colección de 1 artículo",
"A %-post collection": "Una colección de % artículos",
"by this author": "de este autor",
"Close": "Cerrar",
"Get the latest posts delivered right to your inbox": "Recibe los últimos artículos directamente en tu buzón",
"Menu": "Menú",
"more posts": "más artículos",
"No posts": "No hay artículos",
"on": "en",
"or subscribe": "¡O suscríbete",
"Proudly published with {ghostLink}": "Publicado con {ghostLink}",
"Read": "Lee",
"Scroll Down": "Bajar",
"Share this post": "Comparte",
"Subscribe": "Suscríbete",
"Subscribe to": "Suscríbete a",
"via RSS": "por RSS",
"with Feedly!": "con Feedly!",
"Your email address": "Tu correo electrónico",
"1 post": "1 artículo",
"% posts": "% artículos"
}
}
42 changes: 23 additions & 19 deletions core/test/unit/server_helpers/t_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,33 @@ describe('{{t}} helper', function () {
should.exist(rendered);
rendered.string.should.equal(expected);
});
it('returns correct default theme text string', function () {
var expected = 'Proudly published with <a href="https://ghost.org">Ghost</a>',
rendered = helpers.t.call({}, 'casper', 'Proudly published with \{ghostLink\}', {
hash: {
ghostLink: '<a href="https://ghost.org">Ghost</a>'
}
});
should.exist(rendered);
rendered.string.should.equal(expected);
});
// Removed for compatibility with both old themes and i18n-capable themes:
//
// it('returns correct default theme text string', function () {
// var expected = 'Proudly published with <a href="https://ghost.org">Ghost</a>',
// rendered = helpers.t.call({}, 'casper', 'Proudly published with \{ghostLink\}', {
// hash: {
// ghostLink: '<a href="https://ghost.org">Ghost</a>'
// }
// });
// should.exist(rendered);
// rendered.string.should.equal(expected);
// });
} else {
it('returns frontend text string translation', function () {
var rendered = helpers.t.call({}, 'frontend', 'Page', {});
should.exist(rendered);
});
it('returns default theme text string translation', function () {
var rendered = helpers.t.call({}, 'casper', 'Proudly published with \{ghostLink\}', {
hash: {
ghostLink: '<a href="https://ghost.org">Ghost</a>'
}
});
should.exist(rendered);
rendered.string.should.match(/<a href\=\"https\:\/\/ghost\.org\">Ghost<\/a>/);
});
// Removed for compatibility with both old themes and i18n-capable themes:
//
// it('returns default theme text string translation', function () {
// var rendered = helpers.t.call({}, 'casper', 'Proudly published with \{ghostLink\}', {
// hash: {
// ghostLink: '<a href="https://ghost.org">Ghost</a>'
// }
// });
// should.exist(rendered);
// rendered.string.should.match(/<a href\=\"https\:\/\/ghost\.org\">Ghost<\/a>/);
// });
}
});

0 comments on commit 6460c66

Please sign in to comment.