From 0f1c3a16584a8316e1b55b0edf7b10d15af84656 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 8 Jun 2019 21:00:12 -0400 Subject: [PATCH] feat: translations (wip) --- client/components/admin.vue | 2 +- client/components/admin/admin-auth.vue | 43 ++++++++++++----------- client/components/admin/admin-theme.vue | 30 ++++++++-------- client/components/admin/admin-users.vue | 2 +- client/components/common/nav-header.vue | 36 +++++++++---------- client/components/editor.vue | 4 +-- client/themes/default/components/page.vue | 14 ++++---- server/app/data.yml | 1 + server/jobs/fetch-graph-locale.js | 1 + server/jobs/sync-graph-locales.js | 1 + server/jobs/sync-graph-updates.js | 2 +- 11 files changed, 71 insertions(+), 65 deletions(-) diff --git a/client/components/admin.vue b/client/components/admin.vue index 297c1bc9..40c9bcfc 100644 --- a/client/components/admin.vue +++ b/client/components/admin.vue @@ -3,7 +3,7 @@ nav-header(hide-search) template(slot='mid') v-spacer - .subheading.grey--text Administration Area + .subheading.grey--text {{$t('admin:adminArea')}} v-spacer v-navigation-drawer.pb-0.admin-sidebar(v-model='adminDrawerShown', app, fixed, clipped, :right='$vuetify.rtl', permanent) vue-scroll(:ops='scrollStyle') diff --git a/client/components/admin/admin-auth.vue b/client/components/admin/admin-auth.vue index 830854d4..0e7d84dc 100644 --- a/client/components/admin/admin-auth.vue +++ b/client/components/admin/admin-auth.vue @@ -80,8 +80,8 @@ i18next.body-2(path='admin:auth.strategyState', tag='div', v-else) v-chip(color='red', small, dark, label, place='state') {{$t('admin:auth.strategyStateInactive')}} v-divider.mt-3 - v-subheader.pl-0 Strategy Configuration - .body-1.ml-3(v-if='!strategy.config || strategy.config.length < 1'): em This strategy has no configuration options you can modify. + v-subheader.pl-0 {{$t('admin:auth.strategyConfiguration')}} + .body-1.ml-3(v-if='!strategy.config || strategy.config.length < 1'): em {{$t('admin:auth.strategyNoConfiguration')}} template(v-else, v-for='cfg in strategy.config') v-select( v-if='cfg.value.type === "string" && cfg.value.enum' @@ -119,13 +119,13 @@ :class='cfg.value.hint ? "mb-2" : ""' ) v-divider.mt-3 - v-subheader.pl-0 Registration + v-subheader.pl-0 {{$t('admin:auth.registration')}} .pr-3 v-switch.ml-3( v-model='strategy.selfRegistration' - label='Allow self-registration' + :label='$t(`admin:auth.selfRegistration`)' color='primary' - hint='Allow any user successfully authorized by the strategy to access the wiki.' + :hint='$t(`admin:auth.selfRegistrationHint`)' persistent-hint ) v-switch.ml-3( @@ -138,12 +138,12 @@ persistent-hint ) v-combobox.ml-3.mt-3( - label='Limit to specific email domains' + :label='$t(`admin:auth.domainsWhitelist`)' v-model='strategy.domainWhitelist' prepend-icon='mail_outline' outline :disabled='!strategy.selfRegistration' - hint='A list of domains authorized to register. The user email address domain must match one of these to gain access.' + :hint='$t(`admin:auth.domainsWhitelistHint`)' persistent-hint small-chips deletable-chips @@ -157,10 +157,10 @@ :items='groups' item-text='name' item-value='id' - label='Assign to group' + :label='$t(`admin:auth.autoEnrollGroups`)' v-model='strategy.autoEnrollGroups' prepend-icon='people' - hint='Automatically assign new users to these groups.' + :hint='$t(`admin:auth.autoEnrollGroupsHint`)' small-chips persistent-hint deletable-chips @@ -170,36 +170,39 @@ ) template(v-if='strategy.useForm') v-divider.mt-3 - v-subheader.pl-0 Security + v-subheader.pl-0 {{$t('admin:auth.security')}} v-switch.ml-3( v-model='strategy.recaptcha' :disabled='true' - label='Force all users to use Two-Factor Authentication (2FA)' + :label='$t(`admin:auth.force2fa`)' color='primary' - hint='Users will be required to setup 2FA the first time they login and cannot be disabled by the user.' + :hint='$t(`admin:auth.force2faHint`)' persistent-hint ) v-card.mt-3.wiki-form.animated.fadeInUp.wait-p4s v-toolbar(color='primary', dense, flat, dark) - .subheading Configuration Reference + .subheading {{$t('admin:auth.configReference')}} v-card-text - .body-1 Some strategies may require some configuration values to be set on your provider. These are provided for reference only and may not be needed by the current strategy. - v-alert.mt-3.radius-7(v-if='host.length < 8', color='red', outline, :value='true', icon='warning') You must set a valid #[strong Site URL] first! Click on #[strong General] in the left sidebar. + .body-1 {{$t('admin:auth.configReferenceSubtitle')}} + v-alert.mt-3.radius-7(v-if='host.length < 8', color='red', outline, :value='true', icon='warning') + i18next(path='admin:auth.siteUrlNotSetup', tag='span') + strong(place='siteUrl') {{$t('admin:general.siteUrl')}} + strong(place='general') {{$t('admin:general.title')}} .pa-3.mt-3.radius-7.grey(v-else, :class='$vuetify.dark ? `darken-3-d5` : `lighten-3`') - .body-2 Allowed Web Origins + .body-2 {{$t('admin:auth.allowedWebOrigins')}} .body-1 {{host}} v-divider.my-3 - .body-2 Callback URL / Redirect URI + .body-2 {{$t('admin:auth.callbackUrl')}} .body-1 {{host}}/login/{{strategy.key}}/callback v-divider.my-3 - .body-2 Login URL + .body-2 {{$t('admin:auth.loginUrl')}} .body-1 {{host}}/login v-divider.my-3 - .body-2 Logout URL + .body-2 {{$t('admin:auth.logoutUrl')}} .body-1 {{host}} v-divider.my-3 - .body-2 Token Endpoint Authentication Method + .body-2 {{$t('admin:auth.tokenEndpointAuthMethod')}} .body-1 HTTP-POST diff --git a/client/components/admin/admin-theme.vue b/client/components/admin/admin-theme.vue index 478878de..ee3eadca 100644 --- a/client/components/admin/admin-theme.vue +++ b/client/components/admin/admin-theme.vue @@ -5,8 +5,8 @@ .admin-header img.animated.fadeInUp(src='/svg/icon-paint-palette.svg', alt='Theme', style='width: 80px;') .admin-header-title - .headline.primary--text.animated.fadeInLeft Theme - .subheading.grey--text.animated.fadeInLeft.wait-p2s Modify the look & feel of your wiki + .headline.primary--text.animated.fadeInLeft {{$t('admin:theme.title')}} + .subheading.grey--text.animated.fadeInLeft.wait-p2s {{$t('admin:theme.subtitle')}} v-spacer v-btn.animated.fadeInRight(color='success', depressed, @click='save', large, :loading='loading') v-icon(left) check @@ -17,7 +17,7 @@ v-card.wiki-form.animated.fadeInUp v-toolbar(color='primary', dark, dense, flat) v-toolbar-title - .subheading Theme + .subheading {{$t('admin:theme.title')}} v-card-text v-select( :items='themes' @@ -25,9 +25,9 @@ background-color='grey lighten-2' prepend-icon='palette' v-model='config.theme' - label='Site Theme' + :label='$t(`admin:theme.siteTheme`)' persistent-hint - hint='Themes affect how content pages are displayed. Other site sections (such as the editor or admin area) are not affected.' + :hint='$t(`admin:theme.siteThemeHint`)' ) template(slot='item', slot-scope='data') v-list-tile-avatar @@ -38,51 +38,51 @@ v-divider.mt-3 v-switch( v-model='darkMode' - label='Dark Mode' + :label='$t(`admin:theme.darkMode`)' color='primary' persistent-hint - hint='Not recommended for accessibility. May not be supported by all themes.' + :hint='$t(`admin:theme.darkModeHint`)' ) v-card.wiki-form.mt-3.animated.fadeInUp.wait-p2s v-toolbar(color='primary', dark, dense, flat) v-toolbar-title - .subheading Code Injection + .subheading {{$t(`admin:theme.codeInjection`)}} v-card-text v-textarea( v-model='config.injectCSS' - label='CSS Override' + :label='$t(`admin:theme.cssOverride`)' outline background-color='grey lighten-1' color='primary' persistent-hint - hint='CSS code to inject after system default CSS. Consider using custom themes if you have a large amount of css code. Injecting too much CSS code will result in poor page load performance! CSS will automatically be minified.' + :hint='$t(`admin:theme.cssOverrideHint`)' auto-grow ) v-textarea.mt-2( v-model='config.injectHead' - label='Head HTML Injection' + :label='$t(`admin:theme.headHtmlInjection`)' outline background-color='grey lighten-1' color='primary' persistent-hint - hint='HTML code to be injected just before the closing head tag. Usually for script tags.' + :hint='$t(`admin:theme.headHtmlInjectionHint`)' auto-grow ) v-textarea.mt-2( v-model='config.injectBody' - label='Body HTML Injection' + :label='$t(`admin:theme.bodyHtmlInjection`)' outline background-color='grey lighten-1' color='primary' persistent-hint - hint='HTML code to be injected just before the closing body tag.' + :hint='$t(`admin:theme.bodyHtmlInjectionHint`)' auto-grow ) v-flex(lg6 xs12) v-card.animated.fadeInUp.wait-p2s v-toolbar(color='teal', dark, dense, flat) v-toolbar-title - .subheading Download Themes + .subheading {{$t('admin:theme.downloadThemes')}} v-spacer v-chip(label, color='white', small).teal--text coming soon v-card-text.caption -- Coming soon -- diff --git a/client/components/admin/admin-users.vue b/client/components/admin/admin-users.vue index 91b93dd6..37f20fed 100644 --- a/client/components/admin/admin-users.vue +++ b/client/components/admin/admin-users.vue @@ -10,7 +10,7 @@ v-spacer v-btn.animated.fadeInDown.wait-p2s(outline, color='grey', large, @click='refresh') v-icon refresh - v-btn.animated.fadeInDown(color='primary', large, depressed, @click='createUser', disabled) + v-btn.animated.fadeInDown(color='primary', large, depressed, @click='createUser') v-icon(left) add span New User v-card.wiki-form.mt-3.animated.fadeInUp diff --git a/client/components/common/nav-header.vue b/client/components/common/nav-header.vue index 45d9b5d8..fd4ce781 100644 --- a/client/components/common/nav-header.vue +++ b/client/components/common/nav-header.vue @@ -25,36 +25,36 @@ v-list(dense, :light='!$vuetify.dark', :dark='$vuetify.dark', :class='$vuetify.dark ? `grey darken-4` : ``').py-0 v-list-tile(avatar, href='/') v-list-tile-avatar: v-icon(color='blue') home - v-list-tile-content Home + v-list-tile-content {{$t('common:header.home')}} v-list-tile(avatar, @click='pageNew') v-list-tile-avatar: v-icon(color='green') add_box - v-list-tile-content New Page + v-list-tile-content {{$t('common:header.newPage')}} template(v-if='path && path.length') v-divider.my-0 - v-subheader Current Page + v-subheader {{$t('common:header.currentPage')}} v-list-tile(avatar, @click='pageView', v-if='mode !== `view`') v-list-tile-avatar: v-icon(color='indigo') subject - v-list-tile-content View + v-list-tile-content {{$t('common:header.view')}} v-list-tile(avatar, @click='pageEdit', v-if='mode !== `edit`') v-list-tile-avatar: v-icon(color='indigo') edit - v-list-tile-content Edit + v-list-tile-content {{$t('common:header.edit')}} v-list-tile(avatar, @click='pageHistory', v-if='mode !== `history`') v-list-tile-avatar: v-icon(color='indigo') history - v-list-tile-content History + v-list-tile-content {{$t('common:header.history')}} v-list-tile(avatar, @click='pageSource', v-if='mode !== `source`') v-list-tile-avatar: v-icon(color='indigo') code - v-list-tile-content View Source + v-list-tile-content {{$t('common:header.viewSource')}} v-list-tile(avatar, @click='pageMove') v-list-tile-avatar: v-icon(color='grey lighten-2') forward - v-list-tile-content.grey--text.text--ligten-2 Move / Rename + v-list-tile-content.grey--text.text--ligten-2 {{$t('common:header.move')}} v-list-tile(avatar, @click='pageDelete') v-list-tile-avatar: v-icon(color='red darken-2') delete - v-list-tile-content Delete + v-list-tile-content {{$t('common:header.delete')}} v-divider.my-0 - v-subheader Assets + v-subheader {{$t('common:header.assets')}} v-list-tile(avatar, @click='assets') v-list-tile-avatar: v-icon(color='grey lighten-2') burst_mode - v-list-tile-content.grey--text.text--ligten-2 Images & Files + v-list-tile-content.grey--text.text--ligten-2 {{$t('common:header.imagesFiles')}} v-toolbar-title(:class='{ "ml-2": $vuetify.breakpoint.mdAndUp, "ml-0": $vuetify.breakpoint.smAndDown }') span.subheading {{title}} v-flex(md4, v-if='$vuetify.breakpoint.mdAndUp') @@ -66,7 +66,7 @@ v-if='searchIsShown && $vuetify.breakpoint.mdAndUp', v-model='search', color='white', - label='Search...', + :label='$t(`common:header.search`)', single-line, solo flat @@ -151,14 +151,14 @@ v-tooltip(bottom, v-if='isAuthenticated && isAdmin') v-btn.btn-animate-rotate(icon, href='/a', slot='activator') v-icon(color='grey') settings - span Admin + span {{$t('common:header.admin')}} v-menu(v-if='isAuthenticated', offset-y, min-width='300', left, transition='slide-y-transition') v-tooltip(bottom, slot='activator') v-btn(icon, slot='activator', outline, color='blue') v-icon(v-if='picture.kind === `initials`', color='grey') account_circle v-avatar(v-else-if='picture.kind === `image`', :size='29') v-img(:src='picture.url') - span Account + span {{$t('common:header.account')}} v-list.py-0 v-list-tile.py-3.grey(avatar, :class='$vuetify.dark ? `darken-4-l5` : `lighten-5`') v-list-tile-avatar @@ -172,20 +172,20 @@ v-divider.my-0 v-list-tile(href='/w', disabled) v-list-tile-action: v-icon(color='blue') web - v-list-tile-title My Wiki + v-list-tile-title {{$t('common:header.myWiki')}} v-divider.my-0 v-list-tile(href='/p', disabled) v-list-tile-action: v-icon(color='blue') person - v-list-tile-title Profile + v-list-tile-title {{$t('common:header.profile')}} v-divider.my-0 v-list-tile(@click='logout') v-list-tile-action: v-icon(color='red') exit_to_app - v-list-tile-title Logout + v-list-tile-title {{$t('common:header.logout')}} v-tooltip(v-else, left) v-btn(icon, slot='activator', outline, color='grey darken-3', href='/login') v-icon(color='grey') account_circle - span Login + span {{$t('common:header.login')}} page-selector(mode='create', v-model='newPageModal', :open-handler='pageNewCreate') page-delete(v-model='deletePageModal', v-if='path && path.length') diff --git a/client/components/editor.vue b/client/components/editor.vue index 1167d1e5..943bc6f3 100644 --- a/client/components/editor.vue +++ b/client/components/editor.vue @@ -21,7 +21,7 @@ :class='{ "is-icon": $vuetify.breakpoint.mdAndDown, "mx-0": !welcomeMode, "ml-0": welcomeMode }' ) v-icon(color='blue', :left='$vuetify.breakpoint.lgAndUp') sort_by_alpha - span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('editor:page') }} + span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('common:actions.page') }} v-btn.animated.fadeInDown.wait-p2s( v-if='!welcomeMode' flat @@ -30,7 +30,7 @@ @click.native.stop='exit' ) v-icon(color='red', :left='$vuetify.breakpoint.lgAndUp') close - span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('editor:close') }} + span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('common:actions.close') }} v-content component(:is='currentEditor', :save='save') editor-modal-properties(v-model='dialogProps') diff --git a/client/themes/default/components/page.vue b/client/themes/default/components/page.vue index a04724a9..61494d83 100644 --- a/client/themes/default/components/page.vue +++ b/client/themes/default/components/page.vue @@ -45,7 +45,7 @@ v-btn.ma-0(v-else, :href='props.item.path', small, flat) {{props.item.name}} template(v-if='!isPublished') v-spacer - .caption.red--text Unpublished + .caption.red--text {{$t('common:page.unpublished')}} status-indicator.ml-3(negative, pulse) v-divider v-layout(row) @@ -61,18 +61,18 @@ v-flex(lg3, xl2, fill-height, v-if='$vuetify.breakpoint.lgAndUp') v-toolbar(:color='darkMode ? `grey darken-4-l3` : `grey lighten-4`', flat, :height='90') div - .caption.grey--text.text--lighten-1 Last edited by + .caption.grey--text.text--lighten-1 {{$t('common:page.lastEditedBy')}} .body-2.grey--text(:class='darkMode ? `` : `text--darken-3`') {{ authorName }} .caption.grey--text.text--darken-1 {{ updatedAt | moment('calendar') }} v-spacer v-tooltip(left) v-btn.btn-animate-edit(icon, slot='activator', :href='"/e/" + path') v-icon(color='grey') edit - span Edit Page + span {{$t('common:page.editPage')}} v-divider template(v-if='toc.length') v-list.grey.pb-3(dense, :class='darkMode ? `darken-3-d3` : `lighten-3`') - v-subheader.pl-4(:class='darkMode ? `indigo--text text--lighten-3` : `primary--text`') Table of Contents + v-subheader.pl-4(:class='darkMode ? `indigo--text text--lighten-3` : `primary--text`') {{$t('common:page.toc')}} template(v-for='(tocItem, tocIdx) in toc') v-list-tile(@click='$vuetify.goTo(tocItem.anchor, scrollOpts)') v-icon(color='grey') arrow_right @@ -109,13 +109,13 @@ v-spacer v-tooltip(bottom) v-btn(icon, slot='activator'): v-icon(color='grey') bookmark - span Bookmark + span {{$t('common:page.bookmark')}} v-tooltip(bottom) v-btn(icon, slot='activator'): v-icon(color='grey') share - span Share + span {{$t('common:page.share')}} v-tooltip(bottom) v-btn(icon, slot='activator'): v-icon(color='grey') print - span Print Format + span {{$t('common:page.printFormat')}} v-spacer nav-footer notify diff --git a/server/app/data.yml b/server/app/data.yml index 7f7db3ed..c4c0ea38 100644 --- a/server/app/data.yml +++ b/server/app/data.yml @@ -43,6 +43,7 @@ defaults: ldapdebug: false sqllog: false # System defaults + channel: BETA setup: false paths: content: ./content diff --git a/server/jobs/fetch-graph-locale.js b/server/jobs/fetch-graph-locale.js index f4b7c230..3328cbf7 100644 --- a/server/jobs/fetch-graph-locale.js +++ b/server/jobs/fetch-graph-locale.js @@ -28,6 +28,7 @@ module.exports = async (localeCode) => { let lcObj = {} _.forEach(strings, row => { if (_.includes(row.key, '::')) { return } + if (_.isEmpty(row.value)) { row.value = row.key } _.set(lcObj, row.key.replace(':', '.'), row.value) }) diff --git a/server/jobs/sync-graph-locales.js b/server/jobs/sync-graph-locales.js index f5b0984f..9a95f876 100644 --- a/server/jobs/sync-graph-locales.js +++ b/server/jobs/sync-graph-locales.js @@ -51,6 +51,7 @@ module.exports = async () => { let lcObj = {} _.forEach(strings, row => { if (_.includes(row.key, '::')) { return } + if (_.isEmpty(row.value)) { row.value = row.key } _.set(lcObj, row.key.replace(':', '.'), row.value) }) diff --git a/server/jobs/sync-graph-updates.js b/server/jobs/sync-graph-updates.js index c4cd21b7..79b6cc1f 100644 --- a/server/jobs/sync-graph-updates.js +++ b/server/jobs/sync-graph-updates.js @@ -24,7 +24,7 @@ module.exports = async () => { } }`, variables: { - channel: 'BETA', // TODO + channel: WIKI.config.channel, version: WIKI.version } })