Skip to content

Commit

Permalink
Merge branch 'fix-i18n'
Browse files Browse the repository at this point in the history
  • Loading branch information
knadh committed Mar 2, 2022
2 parents 174a48f + c4f1bed commit 8d6e475
Show file tree
Hide file tree
Showing 21 changed files with 199 additions and 57 deletions.
60 changes: 41 additions & 19 deletions frontend/src/main.js
Expand Up @@ -15,11 +15,25 @@ const i18n = new VueI18n();
Vue.use(Buefy, {});
Vue.config.productionTip = false;

// Globals.
Vue.prototype.$utils = new Utils(i18n);
Vue.prototype.$api = api;

new Vue({
// Setup the router.
router.beforeEach((to, from, next) => {
if (to.matched.length === 0) {
next('/404');
} else {
next();
}
});

router.afterEach((to) => {
Vue.nextTick(() => {
const t = to.meta.title && i18n.te(to.meta.title) ? `${i18n.tc(to.meta.title, 0)} /` : '';
document.title = `${t} listmonk`;
});
});


const v = new Vue({
router,
store,
i18n,
Expand All @@ -29,20 +43,28 @@ new Vue({
isLoaded: false,
},

methods: {
loadConfig() {
api.getServerConfig().then((data) => {
api.getLang(data.lang).then((lang) => {
i18n.locale = data.lang;
i18n.setLocaleMessage(i18n.locale, lang);
this.isLoaded = true;
});
});
},
mounted() {
v.isLoaded = true;
},
});

created() {
this.loadConfig();
api.getSettings();
},
}).$mount('#app');

// Load server side config and language before mounting the app.
api.getServerConfig().then((data) => {
api.getLang(data.lang).then((lang) => {
i18n.locale = data.lang;
i18n.setLocaleMessage(i18n.locale, lang);

Vue.prototype.$utils = new Utils(i18n);
Vue.prototype.$api = api;

// Set the page title after i18n has loaded.
const to = router.history.current;
const t = to.meta.title ? `${i18n.tc(to.meta.title, 0)} /` : '';
document.title = `${t} listmonk`;

v.$mount('#app');
});
});

api.getSettings();
46 changes: 16 additions & 30 deletions frontend/src/router/index.js
Expand Up @@ -14,97 +14,97 @@ const routes = [
{
path: '/',
name: 'dashboard',
meta: { title: 'Dashboard' },
meta: { title: '' },
component: () => import(/* webpackChunkName: "main" */ '../views/Dashboard.vue'),
},
{
path: '/lists',
name: 'lists',
meta: { title: 'Lists', group: 'lists' },
meta: { title: 'globals.terms.lists', group: 'lists' },
component: () => import(/* webpackChunkName: "main" */ '../views/Lists.vue'),
},
{
path: '/lists/forms',
name: 'forms',
meta: { title: 'Forms', group: 'lists' },
meta: { title: 'forms.title', group: 'lists' },
component: () => import(/* webpackChunkName: "main" */ '../views/Forms.vue'),
},
{
path: '/lists/:id',
name: 'lists',
meta: { title: 'Lists', group: 'lists' },
meta: { title: 'globals.terms.lists', group: 'lists' },
component: () => import(/* webpackChunkName: "main" */ '../views/Lists.vue'),
},
{
path: '/subscribers',
name: 'subscribers',
meta: { title: 'Subscribers', group: 'subscribers' },
meta: { title: 'globals.terms.subscribers', group: 'subscribers' },
component: () => import(/* webpackChunkName: "main" */ '../views/Subscribers.vue'),
},
{
path: '/subscribers/import',
name: 'import',
meta: { title: 'Import subscribers', group: 'subscribers' },
meta: { title: 'import.title', group: 'subscribers' },
component: () => import(/* webpackChunkName: "main" */ '../views/Import.vue'),
},
{
path: '/subscribers/bounces',
name: 'bounces',
meta: { title: 'Bounces', group: 'subscribers' },
meta: { title: 'globals.terms.bounces', group: 'subscribers' },
component: () => import(/* webpackChunkName: "main" */ '../views/Bounces.vue'),
},
{
path: '/subscribers/lists/:listID',
name: 'subscribers_list',
meta: { title: 'Subscribers', group: 'subscribers' },
meta: { title: 'globals.terms.subscribers', group: 'subscribers' },
component: () => import(/* webpackChunkName: "main" */ '../views/Subscribers.vue'),
},
{
path: '/subscribers/:id',
name: 'subscriber',
meta: { title: 'Subscribers', group: 'subscribers' },
meta: { title: 'globals.terms.subscribers', group: 'subscribers' },
component: () => import(/* webpackChunkName: "main" */ '../views/Subscribers.vue'),
},
{
path: '/campaigns',
name: 'campaigns',
meta: { title: 'Campaigns', group: 'campaigns' },
meta: { title: 'globals.terms.campaigns', group: 'campaigns' },
component: () => import(/* webpackChunkName: "main" */ '../views/Campaigns.vue'),
},
{
path: '/campaigns/media',
name: 'media',
meta: { title: 'Media', group: 'campaigns' },
meta: { title: 'globals.terms.media', group: 'campaigns' },
component: () => import(/* webpackChunkName: "main" */ '../views/Media.vue'),
},
{
path: '/campaigns/templates',
name: 'templates',
meta: { title: 'Templates', group: 'campaigns' },
meta: { title: 'globals.terms.templates', group: 'campaigns' },
component: () => import(/* webpackChunkName: "main" */ '../views/Templates.vue'),
},
{
path: '/campaigns/analytics',
name: 'campaignAnalytics',
meta: { title: 'Campaign analytics', group: 'campaigns' },
meta: { title: 'analytics.title', group: 'campaigns' },
component: () => import(/* webpackChunkName: "main" */ '../views/CampaignAnalytics.vue'),
},
{
path: '/campaigns/:id',
name: 'campaign',
meta: { title: 'Campaign', group: 'campaigns' },
meta: { title: 'globals.terms.campaign', group: 'campaigns' },
component: () => import(/* webpackChunkName: "main" */ '../views/Campaign.vue'),
},
{
path: '/settings',
name: 'settings',
meta: { title: 'Settings', group: 'settings' },
meta: { title: 'globals.terms.settings', group: 'settings' },
component: () => import(/* webpackChunkName: "main" */ '../views/Settings.vue'),
},
{
path: '/settings/logs',
name: 'logs',
meta: { title: 'Logs', group: 'settings' },
meta: { title: 'logs.title', group: 'settings' },
component: () => import(/* webpackChunkName: "main" */ '../views/Logs.vue'),
},
];
Expand All @@ -122,18 +122,4 @@ const router = new VueRouter({
},
});

router.beforeEach((to, from, next) => {
if (to.matched.length === 0) {
next('/404');
} else {
next();
}
});

router.afterEach((to) => {
Vue.nextTick(() => {
document.title = `${to.meta.title} / listmonk`;
});
});

export default router;
22 changes: 22 additions & 0 deletions frontend/src/utils.js
Expand Up @@ -4,7 +4,9 @@ import {
} from 'buefy';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import updateLocale from 'dayjs/plugin/updateLocale';

dayjs.extend(updateLocale);
dayjs.extend(relativeTime);

const reEmail = /(.+?)@(.+?)/ig;
Expand All @@ -25,6 +27,26 @@ export default class Utils {
constructor(i18n) {
this.i18n = i18n;
this.intlNumFormat = new Intl.NumberFormat();

if (i18n) {
dayjs.updateLocale('en', {
relativeTime: {
future: '%s',
past: '%s',
s: `${i18n.tc('globals.terms.second', 2)}`,
m: `1 ${i18n.tc('globals.terms.minute', 1)}`,
mm: `%d ${i18n.tc('globals.terms.minute', 2)}`,
h: `1 ${i18n.tc('globals.terms.hour', 1)}`,
hh: `%d ${i18n.tc('globals.terms.hour', 2)}`,
d: `1 ${i18n.tc('globals.terms.day', 1)}`,
dd: `%d ${i18n.tc('globals.terms.day', 2)}`,
M: `1 ${i18n.tc('globals.terms.month', 1)}`,
MM: `%d ${i18n.tc('globals.terms.month', 2)}`,
y: `${i18n.tc('globals.terms.year', 1)}`,
yy: `%d ${i18n.tc('globals.terms.year', 2)}`,
},
});
}
}

// Parses an ISO timestamp to a simpler form.
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/views/Dashboard.vue
Expand Up @@ -218,7 +218,7 @@ export default Vue.extend({
// Pull the charts.
this.$api.getDashboardCharts().then((data) => {
this.isChartsLoading = false;
this.renderChart(this.$t('dashboard.linkClicks'), data.campaignViews, this.$refs['chart-views']);
this.renderChart(this.$t('dashboard.campaignViews'), data.campaignViews, this.$refs['chart-views']);
this.renderChart(this.$t('dashboard.linkClicks'), data.linkClicks, this.$refs['chart-clicks']);
});
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/views/Lists.vue
Expand Up @@ -98,7 +98,7 @@
header-class="cy-subscribers" width="10%">
<div class="fields stats">
<p v-for="(count, status) in props.row.subscriberStatuses" :key="status">
<label>{{ $t(`subscribers.status.${status}`) }}</label>
<label>{{ $tc(`subscribers.status.${status}`, count) }}</label>
<span :class="status">{{ $utils.formatNumber(count) }}</span>
</p>
</div>
Expand Down
7 changes: 7 additions & 0 deletions i18n/cs-cz.json
Expand Up @@ -140,6 +140,7 @@
"globals.days.4": "Čt",
"globals.days.5": "",
"globals.days.6": "So",
"globals.days.7": "Sat",
"globals.fields.createdAt": "Vytvořeno",
"globals.fields.id": "ID",
"globals.fields.name": "Jméno",
Expand Down Expand Up @@ -186,18 +187,24 @@
"globals.terms.campaign": "Kampaň | Kampaně",
"globals.terms.campaigns": "Kampaně",
"globals.terms.dashboard": "Řídicí panel",
"globals.terms.day": "Day | Days",
"globals.terms.hour": "Hour | Hours",
"globals.terms.list": "Seznam | Seznamy",
"globals.terms.lists": "Seznamy",
"globals.terms.media": "Médium | Média",
"globals.terms.messenger": "Kurýr | Kurýři",
"globals.terms.messengers": "Kurýři",
"globals.terms.minute": "Minute | Minutes",
"globals.terms.month": "Month | Months",
"globals.terms.second": "Second | Seconds",
"globals.terms.settings": "Nastavení",
"globals.terms.subscriber": "Odběratel | Odběratelé",
"globals.terms.subscribers": "Odběratelé",
"globals.terms.tag": "Značka | Značky",
"globals.terms.tags": "Značky",
"globals.terms.template": "Šablona | Šablony",
"globals.terms.templates": "Šablony",
"globals.terms.year": "Year | Years",
"import.alreadyRunning": "Import již běží. Počkejte na jeho dokončení nebo jej zastavte před dalším pokusem.",
"import.blocklist": "Seznam blokovaných",
"import.csvDelim": "Oddělovač CSV",
Expand Down
7 changes: 7 additions & 0 deletions i18n/de.json
Expand Up @@ -140,6 +140,7 @@
"globals.days.4": "Do",
"globals.days.5": "Fr",
"globals.days.6": "Sa",
"globals.days.7": "Sat",
"globals.fields.createdAt": "Erstellt",
"globals.fields.id": "ID",
"globals.fields.name": "Name",
Expand Down Expand Up @@ -186,18 +187,24 @@
"globals.terms.campaign": "Kampagne | Kampagnen",
"globals.terms.campaigns": "Kampagnen",
"globals.terms.dashboard": "Überblick",
"globals.terms.day": "Day | Days",
"globals.terms.hour": "Hour | Hours",
"globals.terms.list": "Liste | Listen",
"globals.terms.lists": "Listen",
"globals.terms.media": "Medien | Medien",
"globals.terms.messenger": "Messenger | Messenger",
"globals.terms.messengers": "Messenger",
"globals.terms.minute": "Minute | Minutes",
"globals.terms.month": "Month | Months",
"globals.terms.second": "Second | Seconds",
"globals.terms.settings": "Einstellungen",
"globals.terms.subscriber": "Abonnent | Abonnenten",
"globals.terms.subscribers": "Abonnenten",
"globals.terms.tag": "Tag | Tags",
"globals.terms.tags": "Tags",
"globals.terms.template": "Vorlage | Vorlagen",
"globals.terms.templates": "Vorlagen",
"globals.terms.year": "Year | Years",
"import.alreadyRunning": "Bitte warte bis der aktuelle Importvorgang beendet wurde.",
"import.blocklist": "Sperrliste",
"import.csvDelim": "CSV-Trennzeichen",
Expand Down
19 changes: 13 additions & 6 deletions i18n/en.json
Expand Up @@ -134,12 +134,13 @@
"globals.buttons.save": "Save",
"globals.buttons.saveChanges": "Save changes",
"globals.days.0": "Sun",
"globals.days.1": "Mon",
"globals.days.2": "Tue",
"globals.days.3": "Wed",
"globals.days.4": "Thu",
"globals.days.5": "Fri",
"globals.days.6": "Sat",
"globals.days.1": "Sun",
"globals.days.2": "Mon",
"globals.days.3": "Tue",
"globals.days.4": "Wed",
"globals.days.5": "Thu",
"globals.days.6": "Fri",
"globals.days.7": "Sat",
"globals.fields.createdAt": "Created",
"globals.fields.id": "ID",
"globals.fields.name": "Name",
Expand Down Expand Up @@ -186,18 +187,24 @@
"globals.terms.campaign": "Campaign | Campaigns",
"globals.terms.campaigns": "Campaigns",
"globals.terms.dashboard": "Dashboard",
"globals.terms.day": "Day | Days",
"globals.terms.hour": "Hour | Hours",
"globals.terms.list": "List | Lists",
"globals.terms.lists": "Lists",
"globals.terms.media": "Media | Media",
"globals.terms.messenger": "Messenger | Messengers",
"globals.terms.messengers": "Messengers",
"globals.terms.minute": "Minute | Minutes",
"globals.terms.month": "Month | Months",
"globals.terms.second": "Second | Seconds",
"globals.terms.settings": "Settings",
"globals.terms.subscriber": "Subscriber | Subscribers",
"globals.terms.subscribers": "Subscribers",
"globals.terms.tag": "Tag | Tags",
"globals.terms.tags": "Tags",
"globals.terms.template": "Template | Templates",
"globals.terms.templates": "Templates",
"globals.terms.year": "Year | Years",
"import.alreadyRunning": "An import is already running. Wait for it to finish or stop it before trying again.",
"import.blocklist": "Blocklist",
"import.csvDelim": "CSV delimiter",
Expand Down

0 comments on commit 8d6e475

Please sign in to comment.