diff --git a/app/src/hydrate.ts b/app/src/hydrate.ts index 2ab2316860..4381f5a920 100644 --- a/app/src/hydrate.ts +++ b/app/src/hydrate.ts @@ -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; diff --git a/app/src/lang/index.ts b/app/src/lang/index.ts index 3320b52c25..acd6737367 100644 --- a/app/src/lang/index.ts +++ b/app/src/lang/index.ts @@ -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 { - 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) { diff --git a/app/src/lang/set-language.ts b/app/src/lang/set-language.ts new file mode 100644 index 0000000000..831ebc8f68 --- /dev/null +++ b/app/src/lang/set-language.ts @@ -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 { + 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; +} diff --git a/app/src/main.ts b/app/src/main.ts index 42ccd8261d..7d2f49b008 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -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'); diff --git a/app/src/modules/users/routes/item.vue b/app/src/modules/users/routes/item.vue index 703a1edf3e..086feac335 100644 --- a/app/src/modules/users/routes/item.vue +++ b/app/src/modules/users/routes/item.vue @@ -113,10 +113,17 @@ @@ -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; diff --git a/app/src/stores/collections.ts b/app/src/stores/collections.ts index 92b96f68c3..a60c19b978 100644 --- a/app/src/stores/collections.ts +++ b/app/src/stores/collections.ts @@ -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, }; }); }, diff --git a/app/src/stores/fields.ts b/app/src/stores/fields.ts index 2a71e91841..3ba3de9825 100644 --- a/app/src/stores/fields.ts +++ b/app/src/stores/fields.ts @@ -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];