Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌐✨i18n for newsletter strings #21433

Merged
merged 49 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
4d60bcb
initial commit iwth date error
cathysarisky Oct 27, 2024
4960f84
almost there!
cathysarisky Oct 27, 2024
3a7c50f
fixed!
cathysarisky Oct 28, 2024
3c3aa27
Merge branch 'main' into newsletter-t
cathysarisky Oct 28, 2024
6ae2844
ah lint
cathysarisky Oct 28, 2024
8834ef6
Merge branch 'newsletter-t' of https://github.com/cathysarisky/Ghost …
cathysarisky Oct 28, 2024
e88c57c
dangit lint
cathysarisky Oct 28, 2024
7f66b14
stray space = failed test :(
cathysarisky Oct 28, 2024
ea627fa
add a test
cathysarisky Oct 28, 2024
1774caa
Merge branch 'main' into newsletter-t
cathysarisky Oct 28, 2024
ea617f4
date i18n
cathysarisky Oct 28, 2024
d1eabc7
Merge branch 'newsletter-t' of https://github.com/cathysarisky/Ghost …
cathysarisky Oct 28, 2024
d095750
belly button lint?
cathysarisky Oct 28, 2024
d8d5de5
issues with imports?
cathysarisky Oct 28, 2024
cb401ed
import issues
cathysarisky Oct 28, 2024
35a639a
build error?
cathysarisky Oct 28, 2024
49aec51
tweak imports again?
cathysarisky Oct 28, 2024
3afc818
hating importing.
cathysarisky Oct 28, 2024
c1100c8
try building, probably fail lint.
cathysarisky Oct 28, 2024
0cf65a0
only one failing test?
cathysarisky Oct 29, 2024
01e5bdb
t fun.
cathysarisky Oct 29, 2024
869ea0b
e2e test fix?
cathysarisky Oct 29, 2024
6240f25
test fussing
cathysarisky Oct 29, 2024
6c0adf3
test wrangling
cathysarisky Oct 29, 2024
2d29b78
better test
cathysarisky Oct 29, 2024
7922bcd
lint is hard
cathysarisky Oct 29, 2024
bde1755
make buttons translatable
cathysarisky Oct 29, 2024
071cf48
preliminary update of snapshots for translatable buttons
cathysarisky Oct 29, 2024
1c51718
argh, package.json...
cathysarisky Oct 29, 2024
abc4d7b
fix dates... again.
cathysarisky Oct 29, 2024
4d35d31
Merge branch 'newsletter-t' of https://github.com/cathysarisky/Ghost …
cathysarisky Oct 29, 2024
d6639fd
added a creation date - my mock is too good, and needs one to make th…
cathysarisky Oct 29, 2024
fbe7250
date localization problem fixed?
cathysarisky Oct 29, 2024
ba30684
and the snap
cathysarisky Oct 29, 2024
156775b
test tweaks
cathysarisky Oct 29, 2024
2fc2c6a
Updated feedback button styling
sanne-san Oct 29, 2024
253608a
Cover last lines in unit test
9larsons Oct 29, 2024
8456170
fix broken test
cathysarisky Oct 29, 2024
3cc0380
more broken tests
cathysarisky Oct 29, 2024
e66ff34
fix snapshots in places I didn't know we had snapshots.
cathysarisky Oct 29, 2024
cc2fc1c
more snapshots.
cathysarisky Oct 29, 2024
a3ff9cc
Merge branch 'main' into newsletter-t
cathysarisky Oct 29, 2024
e970cca
remove unused tpl, improve handling of dynamic strings (sort of)
cathysarisky Oct 30, 2024
124d5df
must... write... better... code
cathysarisky Oct 30, 2024
5d4029e
Merge branch 'main' into newsletter-t
cathysarisky Oct 30, 2024
ba82ea9
i18nmerge fussing
cathysarisky Oct 30, 2024
38cdf3b
Merge branch 'main' into newsletter-t
cathysarisky Oct 31, 2024
3d442a7
trying to make i18n parser play nice
cathysarisky Oct 31, 2024
e124d6e
Merge branch 'newsletter-t' of https://github.com/cathysarisky/Ghost …
cathysarisky Oct 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions ghost/email-service/i18n-setup.js
cathysarisky marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const i18nLib = require('../i18n');
cathysarisky marked this conversation as resolved.
Show resolved Hide resolved
const settingsCache = require('../core/core/shared/settings-cache');
cathysarisky marked this conversation as resolved.
Show resolved Hide resolved

const locale = settingsCache.get('locale') || 'en';

const i18nLanguage = locale;
const i18n = i18nLib(i18nLanguage, 'newsletter');

function t(key, options) {
// same function serves both handlebars (arguments in options.hash) and javascript (arguments in options)
let hash = options?.hash;
return i18n.t(key, hash || options || {});
}

module.exports = {t, locale};
42 changes: 28 additions & 14 deletions ghost/email-service/lib/EmailRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const tpl = require('@tryghost/tpl');
const {EmailAddressParser} = require('@tryghost/email-addresses');
const {registerHelpers} = require('./helpers/register-helpers');
const crypto = require('crypto');
const {t, locale} = require('../i18n-setup');

const messages = {
subscriptionStatus: {
Expand All @@ -24,6 +25,15 @@ const messages = {
}
};

// this is required to trigger the t parser
/*
t('Your subscription has expired.');
t('Your subscription has been canceled and will expire on {date}. You can resume your subscription via your account settings.');
t('Your subscription will renew on {date}.');
t('Your free trial ends on {date}, at which time you will be charged the regular price. You can always cancel before then.');
t('Your subscription will expire on {date}.');
*/

function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, '&')
Expand All @@ -34,7 +44,11 @@ function escapeHtml(unsafe) {
}

function formatDateLong(date, timezone) {
return DateTime.fromJSDate(date).setZone(timezone).setLocale('en-gb').toLocaleString({
let dateLocale = 'en-gb';
if (!locale || locale === 'en') {
dateLocale = 'en-gb';
}
return DateTime.fromJSDate(date).setZone(timezone).setLocale(dateLocale).toLocaleString({
year: 'numeric',
month: 'long',
day: 'numeric'
Expand Down Expand Up @@ -551,7 +565,7 @@ class EmailRenderer {
getMemberStatusText(member) {
if (member.status === 'free') {
// Not really used, but as a backup
return tpl(messages.subscriptionStatus.free);
return t(messages.subscriptionStatus.free);
}

// Do we have an active subscription?
Expand All @@ -564,12 +578,12 @@ class EmailRenderer {

if (!activeSubscription && !member.tiers.length) {
// No subscription?
return tpl(messages.subscriptionStatus.expired);
return t(messages.subscriptionStatus.expired);
}

if (!activeSubscription) {
if (!member.tiers[0]?.expiry_at) {
return tpl(messages.subscriptionStatus.complimentaryInfinite);
return t(messages.subscriptionStatus.complimentaryInfinite);
}
// Create one manually that is expiring
activeSubscription = {
Expand All @@ -584,26 +598,25 @@ class EmailRenderer {
// Translate to a human readable string
if (activeSubscription.trial_end_at && activeSubscription.trial_end_at > new Date() && activeSubscription.status === 'trialing') {
const date = formatDateLong(activeSubscription.trial_end_at, timezone);
return tpl(messages.subscriptionStatus.trial, {date});
return t(messages.subscriptionStatus.trial, {date});
}

const date = formatDateLong(activeSubscription.current_period_end, timezone);
if (activeSubscription.cancel_at_period_end) {
return tpl(messages.subscriptionStatus.canceled, {date});
return t(messages.subscriptionStatus.canceled, {date});
}

return tpl(messages.subscriptionStatus.active, {date});
return t(messages.subscriptionStatus.active, {date});
}

const expires = member.tiers[0]?.expiry_at ?? null;

if (expires) {
const timezone = this.#settingsCache.get('timezone');
const date = formatDateLong(expires, timezone);
return tpl(messages.subscriptionStatus.complimentaryExpires, {date});
return t(messages.subscriptionStatus.complimentaryExpires, {date});
}

return tpl(messages.subscriptionStatus.complimentaryInfinite);
return t(messages.subscriptionStatus.complimentaryInfinite);
}

/**
Expand Down Expand Up @@ -671,15 +684,17 @@ class EmailRenderer {
id: 'status',
getValue: (member) => {
if (member.status === 'comped') {
return 'complimentary';
return t('complimentary');
}
if (this.isMemberTrialing(member)) {
return 'trialing';
return t('trialing');
}
return member.status;
// other possible statuses: t('free'), t('paid') //
return t(member.status);
}
},
{
//TODO i18n
id: 'status_text',
getValue: (member) => {
return this.getMemberStatusText(member);
Expand Down Expand Up @@ -748,7 +763,6 @@ class EmailRenderer {
}
delete replacement.originalId;
}

return replacements;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<td dir="ltr" valign="top" align="center" style="vertical-align: top; font-family: inherit; font-size: 14px; text-align: center; padding: 0 4px 4px; cursor: pointer; width: 33%;" nowrap>
<a href="{{href}}" target="_blank">
<img src={{iconUrl}} border="0" width={{width}} height={{height}} alt="{{buttonText}}">
<p class="feedback-button-mobile-text">{{buttonText}}</p>
<p class="feedback-button-mobile-text">{{t buttonText}}</p>
{{!-- Button text possible values: {{t 'More like this'}} {{t 'Less like this'}} {{t 'Comment'}} --}}
</a>
</td>
22 changes: 11 additions & 11 deletions ghost/email-service/lib/email-templates/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,15 @@
<table class="post-meta-wrapper" role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-bottom: 32px;">
<tr>
<td height="20" class="{{classes.meta}}" style="padding: 0;">
By {{post.authors}} • <span class="post-meta-date">{{post.publishedAt}} </span>
{{{t 'By {authors}' authors=post.authors }}} • <span class="post-meta-date">{{post.publishedAt}} </span>
</td>
<td class="{{classes.meta}} view-online desktop">
<a href="{{post.url}}" class="view-online-link">View in browser</a>
<a href="{{post.url}}" class="view-online-link">{{t 'View in browser'}}</a>
</td>
</tr>
<tr class="{{classes.meta}} view-online-mobile">
<td height="20" class="view-online">
<a href="{{post.url}}" class="view-online-link">View in browser</a>
<a href="{{post.url}}" class="view-online-link">{{t 'View in browser'}}</a>
</td>
</tr>
</table>
Expand Down Expand Up @@ -183,7 +183,7 @@
{{#if latestPosts.length}}
<tr>
<td style="padding: 24px 0; border-bottom: 1px solid #e5eff5;">
<h3 class="latest-posts-header">Keep reading</h3>
<h3 class="latest-posts-header">{{t 'Keep reading'}}</h3>
{{> latestPosts}}
</td>
</tr>
Expand All @@ -192,19 +192,19 @@
{{#if newsletter.showSubscriptionDetails}}
<tr>
<td class="subscription-box">
<h3>Subscription details</h3>
<h3>{{t 'Subscription details'}}</h3>
<p style="margin-bottom: 16px;">
<span>You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {{site.title}}.</span> %%{status_text}%%
<span>{{{t "You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {site}." site=site.title }}}</span> %%{status_text}%%
</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td class="subscription-details">
<p class="%%{name_class}%%">Name: %%{name, "not provided"}%%</p>
<p>Email: <a href="#">%%{email}%%</a></p>
<p>Member since: %%{created_at}%%</p>
<p class="%%{name_class}%%">{{t 'Name'}}: %%{name, "not provided"}%%</p>
<p>{{t 'Email'}}: <a href="#">%%{email}%%</a></p>
<p>{{t 'Member since'}}: %%{created_at}%%</p>
</td>
<td align="right" valign="bottom" class="manage-subscription">
<a href="%%{manage_account_url}%%"> Manage subscription &rarr;</a>
<a href="%%{manage_account_url}%%"> {{t 'Manage subscription'}} &rarr;</a>
</td>
</tr>
</table>
Expand All @@ -219,7 +219,7 @@
<tr><td class="footer">{{{footerContent}}}</td></tr>
{{/if}}
<tr>
<td class="footer">{{site.title}} &copy; {{year}} – <a href="%%{unsubscribe_url}%%">Unsubscribe</a></td>
<td class="footer">{{site.title}} &copy; {{year}} – <a href="%%{unsubscribe_url}%%">{{t 'Unsubscribe'}}</a></td>
</tr>

{{#if showBadge }}
Expand Down
5 changes: 5 additions & 0 deletions ghost/email-service/lib/helpers/register-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,10 @@ module.exports = {
return options.inverse(this);
}
});

handlebars.registerHelper('t', function (key, options) {
let i18n = require('../../i18n-setup');
return i18n.t(key, options);
});
}
};
3 changes: 2 additions & 1 deletion ghost/email-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
},
"files": [
"index.js",
"lib"
"lib",
"i18n-setup.js"
],
"devDependencies": {
"c8": "8.0.1",
Expand Down
12 changes: 11 additions & 1 deletion ghost/i18n/lib/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,17 @@ const SUPPORTED_LOCALES = [

/**
* @param {string} [lng]
* @param {'ghost'|'portal'|'test'|'signup-form'|'comments'|'search'} ns
* @param {'ghost'|'portal'|'test'|'signup-form'|'comments'|'search'|'newsletter'} ns
*/
module.exports = (lng = 'en', ns = 'portal') => {
const i18nextInstance = i18next.createInstance();
let interpolation = {};
if (ns === 'newsletter') {
interpolation = {
prefix: '{',
suffix: '}'
};
}
i18nextInstance.init({
lng,

Expand All @@ -82,6 +89,9 @@ module.exports = (lng = 'en', ns = 'portal') => {
ns: ns,
defaultNS: ns,

// separators
interpolation,

resources: SUPPORTED_LOCALES.reduce((acc, locale) => {
const res = require(`../locales/${locale}/${ns}.json`);

Expand Down
24 changes: 24 additions & 0 deletions ghost/i18n/locales/af/newsletter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"By {authors}": "",
"Comment": "",
"complimentary": "",
"Email": "",
"free": "",
"Keep reading": "",
"Less like this": "",
"Manage subscription": "",
"Member since": "",
"More like this": "",
"Name": "",
"paid": "",
"Subscription details": "",
"trialing": "",
"Unsubscribe": "",
"View in browser": "",
"You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {site}.": "",
"Your free trial ends on {date}, at which time you will be charged the regular price. You can always cancel before then.": "",
"Your subscription has been canceled and will expire on {date}. You can resume your subscription via your account settings.": "",
"Your subscription has expired.": "",
"Your subscription will expire on {date}.": "",
"Your subscription will renew on {date}.": ""
}
24 changes: 24 additions & 0 deletions ghost/i18n/locales/ar/newsletter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"By {authors}": "",
"Comment": "",
"complimentary": "",
"Email": "",
"free": "",
"Keep reading": "",
"Less like this": "",
"Manage subscription": "",
"Member since": "",
"More like this": "",
"Name": "",
"paid": "",
"Subscription details": "",
"trialing": "",
"Unsubscribe": "",
"View in browser": "",
"You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {site}.": "",
"Your free trial ends on {date}, at which time you will be charged the regular price. You can always cancel before then.": "",
"Your subscription has been canceled and will expire on {date}. You can resume your subscription via your account settings.": "",
"Your subscription has expired.": "",
"Your subscription will expire on {date}.": "",
"Your subscription will renew on {date}.": ""
}
24 changes: 24 additions & 0 deletions ghost/i18n/locales/bg/newsletter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"By {authors}": "",
"Comment": "",
"complimentary": "",
"Email": "",
"free": "",
"Keep reading": "",
"Less like this": "",
"Manage subscription": "",
"Member since": "",
"More like this": "",
"Name": "",
"paid": "",
"Subscription details": "",
"trialing": "",
"Unsubscribe": "",
"View in browser": "",
"You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {site}.": "",
"Your free trial ends on {date}, at which time you will be charged the regular price. You can always cancel before then.": "",
"Your subscription has been canceled and will expire on {date}. You can resume your subscription via your account settings.": "",
"Your subscription has expired.": "",
"Your subscription will expire on {date}.": "",
"Your subscription will renew on {date}.": ""
}
24 changes: 24 additions & 0 deletions ghost/i18n/locales/bn/newsletter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"By {authors}": "",
"Comment": "",
"complimentary": "",
"Email": "",
"free": "",
"Keep reading": "",
"Less like this": "",
"Manage subscription": "",
"Member since": "",
"More like this": "",
"Name": "",
"paid": "",
"Subscription details": "",
"trialing": "",
"Unsubscribe": "",
"View in browser": "",
"You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {site}.": "",
"Your free trial ends on {date}, at which time you will be charged the regular price. You can always cancel before then.": "",
"Your subscription has been canceled and will expire on {date}. You can resume your subscription via your account settings.": "",
"Your subscription has expired.": "",
"Your subscription will expire on {date}.": "",
"Your subscription will renew on {date}.": ""
}
24 changes: 24 additions & 0 deletions ghost/i18n/locales/bs/newsletter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"By {authors}": "",
"Comment": "",
"complimentary": "",
"Email": "",
"free": "",
"Keep reading": "",
"Less like this": "",
"Manage subscription": "",
"Member since": "",
"More like this": "",
"Name": "",
"paid": "",
"Subscription details": "",
"trialing": "",
"Unsubscribe": "",
"View in browser": "",
"You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {site}.": "",
"Your free trial ends on {date}, at which time you will be charged the regular price. You can always cancel before then.": "",
"Your subscription has been canceled and will expire on {date}. You can resume your subscription via your account settings.": "",
"Your subscription has expired.": "",
"Your subscription will expire on {date}.": "",
"Your subscription will renew on {date}.": ""
}
Loading
Loading