Translate collections/fields live (#5081)

Fixes #5078
This commit is contained in:
Rijk van Zanten
2021-04-15 11:27:52 -04:00
committed by GitHub
parent 07b85b7eb7
commit 8af2cbadfa
7 changed files with 101 additions and 58 deletions

View File

@@ -13,7 +13,8 @@ import {
} from '@/stores';
import { register as registerModules, unregister as unregisterModules } from '@/modules/register';
import { setLanguage, Language } from '@/lang';
import { Language } from '@/lang';
import { setLanguage } from '@/lang/set-language';
type GenericStore = {
id: string;

View File

@@ -7,19 +7,8 @@ import availableLanguages from './available-languages.yaml';
import enUSBase from './translations/en-US.yaml';
import dateFormats from './date-formats.yaml';
import { getModules } from '@/modules';
import { getLayouts } from '@/layouts';
import { getInterfaces } from '@/interfaces';
import { getDisplays } from '@/displays';
import { translate } from '@/utils/translate-object-values';
Vue.use(VueI18n);
const { modules, modulesRaw } = getModules();
const { layouts, layoutsRaw } = getLayouts();
const { interfaces, interfacesRaw } = getInterfaces();
const { displays, displaysRaw } = getDisplays();
export const i18n = new VueI18n({
locale: 'en-US',
fallbackLocale: 'en-US',
@@ -34,29 +23,6 @@ export type Language = keyof typeof availableLanguages;
export const loadedLanguages: Language[] = ['en-US'];
export async function setLanguage(lang: Language): Promise<boolean> {
if (Object.keys(availableLanguages).includes(lang) === false) {
return false;
}
if (loadedLanguages.includes(lang) === false) {
const translations = await import(`@/lang/translations/${lang}.yaml`).catch((err) => console.warn(err));
i18n.mergeLocaleMessage(lang, translations);
loadedLanguages.push(lang);
}
i18n.locale = lang;
(document.querySelector('html') as HTMLElement).setAttribute('lang', lang);
modules.value = translate(modulesRaw.value);
layouts.value = translate(layoutsRaw.value);
interfaces.value = translate(interfacesRaw.value);
displays.value = translate(displaysRaw.value);
return true;
}
export default i18n;
export function translateAPIError(error: RequestError | string) {

View File

@@ -0,0 +1,44 @@
import availableLanguages from './available-languages.yaml';
import { i18n, Language, loadedLanguages } from './index';
import { getModules } from '@/modules';
import { getLayouts } from '@/layouts';
import { getInterfaces } from '@/interfaces';
import { getDisplays } from '@/displays';
import { translate } from '@/utils/translate-object-values';
import { useCollectionsStore, useFieldsStore } from '@/stores';
const { modules, modulesRaw } = getModules();
const { layouts, layoutsRaw } = getLayouts();
const { interfaces, interfacesRaw } = getInterfaces();
const { displays, displaysRaw } = getDisplays();
export async function setLanguage(lang: Language): Promise<boolean> {
const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
if (Object.keys(availableLanguages).includes(lang) === false) {
return false;
}
if (loadedLanguages.includes(lang) === false) {
const translations = await import(`@/lang/translations/${lang}.yaml`).catch((err) => console.warn(err));
i18n.mergeLocaleMessage(lang, translations);
loadedLanguages.push(lang);
}
i18n.locale = lang;
(document.querySelector('html') as HTMLElement).setAttribute('lang', lang);
modules.value = translate(modulesRaw.value);
layouts.value = translate(layoutsRaw.value);
interfaces.value = translate(interfacesRaw.value);
displays.value = translate(displaysRaw.value);
collectionsStore.translateCollections();
fieldsStore.translateFields();
return true;
}

View File

@@ -58,17 +58,15 @@ import { registerDisplays } from './displays/register';
import App from './app.vue';
async function init() {
const app = new Vue({
render: (h) => h(App),
router,
i18n,
});
await Promise.all([registerInterfaces(), registerDisplays(), registerLayouts(), loadModules()]);
Vue.config.productionTip = false;
app.$mount('#app');
new Vue({
render: (h) => h(App),
router,
i18n,
}).$mount('#app');
console.timeEnd('🕓 Application Loaded');

View File

@@ -113,10 +113,17 @@
</template>
<template v-else-if="isNew === false && item">
<div class="name type-label">
{{ userName(item) }}<span v-if="item.title" class="title">, {{ item.title }}</span>
{{ userName(item) }}
<span v-if="item.title" class="title">, {{ item.title }}</span>
</div>
<div class="email">
<v-icon name="alternate_email" small outline />
{{ item.email }}
</div>
<div class="location" v-if="item.location">
<v-icon name="place" small outline />
{{ item.location }}
</div>
<div class="email"><v-icon name="alternate_email" small outline /> {{ item.email }}</div>
<div class="location" v-if="item.location"><v-icon name="place" small outline /> {{ item.location }}</div>
<v-chip :class="item.status" small v-if="roleName">{{ roleName }}</v-chip>
</template>
</div>
@@ -169,7 +176,8 @@
import { defineComponent, computed, toRefs, ref, watch } from '@vue/composition-api';
import UsersNavigation from '../components/navigation.vue';
import { i18n, setLanguage } from '@/lang';
import { i18n } from '@/lang';
import { setLanguage } from '@/lang/set-language';
import router from '@/router';
import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-detail';
import CommentsSidebarDetail from '@/views/private/components/comments-sidebar-detail';
@@ -526,16 +534,16 @@ export default defineComponent({
height: 112px;
margin-bottom: var(--form-vertical-gap);
padding: 20px;
border-radius: calc(var(--border-radius) + 4px);
background-color: var(--background-normal);
border-radius: calc(var(--border-radius) + 4px);
.avatar {
--v-icon-color: var(--foreground-subdued);
display: flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
justify-content: center;
width: 84px;
height: 84px;
margin-right: 16px;

View File

@@ -32,8 +32,8 @@ export const useCollectionsStore = createStore({
const collections: CollectionRaw[] = response.data.data;
this.state.collections = collections.map((collection: CollectionRaw) => {
let name: string | VueI18n.TranslateResult;
const icon = collection.meta?.icon || 'label';
const name = formatTitle(collection.collection);
if (collection.meta && notEmpty(collection.meta.translations)) {
for (let i = 0; i < collection.meta.translations.length; i++) {
@@ -45,7 +45,22 @@ export const useCollectionsStore = createStore({
},
});
}
}
return {
...collection,
name,
icon,
};
});
this.translateCollections();
},
translateCollections() {
this.state.collections = this.state.collections.map((collection: CollectionRaw) => {
let name: string | VueI18n.TranslateResult;
if (i18n.te(`collection_names.${collection.collection}`)) {
name = i18n.t(`collection_names.${collection.collection}`);
} else {
name = formatTitle(collection.collection);
@@ -54,7 +69,6 @@ export const useCollectionsStore = createStore({
return {
...collection,
name,
icon,
};
});
},

View File

@@ -66,16 +66,16 @@ export const useFieldsStore = createStore({
*/
this.state.fields = [...fields.map(this.parseField), fakeFilesField];
this.translateFields();
},
async dehydrate() {
this.reset();
},
parseField(field: FieldRaw): Field {
let name: string | VueI18n.TranslateResult;
const name = formatTitle(field.field);
if (i18n.te(`fields.${field.collection}.${field.field}`)) {
name = i18n.t(`fields.${field.collection}.${field.field}`);
} else if (field.meta && notEmpty(field.meta.translations) && field.meta.translations.length > 0) {
if (field.meta && notEmpty(field.meta.translations) && field.meta.translations.length > 0) {
for (let i = 0; i < field.meta.translations.length; i++) {
const { language, translation } = field.meta.translations[i];
@@ -87,10 +87,6 @@ export const useFieldsStore = createStore({
},
});
}
name = i18n.t(`fields.${field.collection}.${field.field}`);
} else {
name = formatTitle(field.field);
}
return {
@@ -98,6 +94,22 @@ export const useFieldsStore = createStore({
name,
};
},
translateFields() {
this.state.fields = this.state.fields.map((field) => {
let name: string | VueI18n.TranslateResult;
if (i18n.te(`fields.${field.collection}.${field.field}`)) {
name = i18n.t(`fields.${field.collection}.${field.field}`);
} else {
name = formatTitle(field.field);
}
return {
...field,
name,
};
});
},
async createField(collectionKey: string, newField: Field) {
const stateClone = [...this.state.fields];