mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-23 08:56:29 +00:00
Lazy load locales (#1362)
Closes #1345 `index.js` size reduction: `538.71 KiB` -> `382.74 KiB` Also sets correct html `lang` attribute.
This commit is contained in:
parent
7c9644c887
commit
72df167d2d
6 changed files with 81 additions and 39 deletions
|
@ -16,8 +16,8 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
|
@ -26,30 +26,26 @@ import PipelineFeedSidebar from '~/components/pipeline-feed/PipelineFeedSidebar.
|
|||
import useApiClient from '~/compositions/useApiClient';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
const route = useRoute();
|
||||
const apiClient = useApiClient();
|
||||
const { notify } = useNotifications();
|
||||
const i18n = useI18n();
|
||||
|
||||
components: {
|
||||
Navbar,
|
||||
PipelineFeedSidebar,
|
||||
},
|
||||
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const i18n = useI18n();
|
||||
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
apiClient.setErrorHandler((err) => {
|
||||
notifications.notify({ title: err.message || i18n.t('unknown_error'), type: 'error' });
|
||||
});
|
||||
|
||||
const blank = computed(() => route.meta.blank);
|
||||
|
||||
return { blank };
|
||||
},
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
apiClient.setErrorHandler((err) => {
|
||||
notify({ title: err.message || i18n.t('unknown_error'), type: 'error' });
|
||||
});
|
||||
|
||||
const blank = computed(() => route.meta.blank);
|
||||
|
||||
const { locale } = useI18n();
|
||||
watch(
|
||||
locale,
|
||||
() => {
|
||||
document.documentElement.setAttribute('lang', locale.value);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -1,14 +1,31 @@
|
|||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import messages from '@intlify/vite-plugin-vue-i18n/messages';
|
||||
import { nextTick } from 'vue';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
import { getUserLanguage } from '~/utils/locale';
|
||||
|
||||
const userLanguage = getUserLanguage();
|
||||
const fallbackLocale = 'en';
|
||||
export const i18n = createI18n({
|
||||
locale: getUserLanguage(),
|
||||
locale: userLanguage,
|
||||
legacy: false,
|
||||
globalInjection: true,
|
||||
fallbackLocale: 'en',
|
||||
messages,
|
||||
fallbackLocale,
|
||||
});
|
||||
|
||||
export const loadLocaleMessages = async (locale: string) => {
|
||||
const { default: messages } = await import(`~/assets/locales/${locale}.json`);
|
||||
|
||||
i18n.global.setLocaleMessage(locale, messages);
|
||||
|
||||
return nextTick();
|
||||
};
|
||||
|
||||
export const setI18nLanguage = async (lang: string): Promise<void> => {
|
||||
if (!i18n.global.availableLocales.includes(lang)) {
|
||||
await loadLocaleMessages(lang);
|
||||
}
|
||||
i18n.global.locale.value = lang;
|
||||
};
|
||||
|
||||
loadLocaleMessages(fallbackLocale);
|
||||
loadLocaleMessages(userLanguage);
|
||||
|
|
|
@ -77,7 +77,7 @@ export default defineComponent({
|
|||
const { doSubmit: activateRepo, isLoading: isActivatingRepo } = useAsyncAction(async (repo: Repo) => {
|
||||
repoToActivate.value = repo;
|
||||
await apiClient.activateRepo(repo.owner, repo.name);
|
||||
notifications.notify({ title: i18n.t('repo.enabled.success'), type: 'success' });
|
||||
notifications.notify({ title: i18n.t('repo.enable.success'), type: 'success' });
|
||||
repoToActivate.value = undefined;
|
||||
await router.push({ name: 'repo', params: { repoName: repo.name, repoOwner: repo.owner } });
|
||||
});
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
import { useLocalStorage } from '@vueuse/core';
|
||||
import dayjs from 'dayjs';
|
||||
import TimeAgo from 'javascript-time-ago';
|
||||
import { SUPPORTED_LOCALES } from 'virtual:vue-i18n-supported-locales';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
@ -42,8 +43,9 @@ import Button from '~/components/atomic/Button.vue';
|
|||
import SelectField from '~/components/form/SelectField.vue';
|
||||
import Scaffold from '~/components/layout/scaffold/Scaffold.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { setI18nLanguage } from '~/compositions/useI18n';
|
||||
|
||||
const { t, availableLocales, locale } = useI18n();
|
||||
const { t, locale } = useI18n();
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const token = ref<string | undefined>();
|
||||
|
@ -70,17 +72,17 @@ const usageWithCli = `# ${t('user.shell_setup_before')}\nwoodpecker info`;
|
|||
const cliDownload = 'https://github.com/woodpecker-ci/woodpecker/releases';
|
||||
|
||||
const localeOptions = computed(() =>
|
||||
availableLocales.map((availableLocale) => ({
|
||||
value: availableLocale,
|
||||
text: new Intl.DisplayNames(availableLocale, { type: 'language' }).of(availableLocale) || availableLocale,
|
||||
SUPPORTED_LOCALES.map((supportedLocale) => ({
|
||||
value: supportedLocale,
|
||||
text: new Intl.DisplayNames(supportedLocale, { type: 'language' }).of(supportedLocale) || supportedLocale,
|
||||
})),
|
||||
);
|
||||
|
||||
const storedLocale = useLocalStorage('woodpecker:locale', locale.value);
|
||||
const selectedLocale = computed<string>({
|
||||
set(_selectedLocale) {
|
||||
async set(_selectedLocale) {
|
||||
await setI18nLanguage(_selectedLocale);
|
||||
storedLocale.value = _selectedLocale;
|
||||
locale.value = _selectedLocale;
|
||||
dayjs.locale(_selectedLocale);
|
||||
TimeAgo.setDefaultLocale(_selectedLocale);
|
||||
},
|
||||
|
|
4
web/src/vite-env.d.ts
vendored
4
web/src/vite-env.d.ts
vendored
|
@ -1 +1,5 @@
|
|||
// / <reference types="vite/client" />
|
||||
|
||||
declare module 'virtual:vue-i18n-supported-locales' {
|
||||
export const SUPPORTED_LOCALES: string[];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import vueI18n from '@intlify/vite-plugin-vue-i18n';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { readdirSync } from 'fs';
|
||||
import path from 'path';
|
||||
import IconsResolver from 'unplugin-icons/resolver';
|
||||
import Icons from 'unplugin-icons/vite';
|
||||
|
@ -30,11 +31,33 @@ export default defineConfig({
|
|||
vueI18n({
|
||||
include: path.resolve(__dirname, 'src/assets/locales/**'),
|
||||
}),
|
||||
(() => {
|
||||
const virtualModuleId = 'virtual:vue-i18n-supported-locales';
|
||||
const resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
||||
|
||||
const filenames = readdirSync('src/assets/locales/').map((filename) => filename.replace('.json', ''));
|
||||
|
||||
return {
|
||||
name: 'vue-i18n-supported-locales',
|
||||
// eslint-disable-next-line consistent-return
|
||||
resolveId(id) {
|
||||
if (id === virtualModuleId) {
|
||||
return resolvedVirtualModuleId;
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line consistent-return
|
||||
load(id) {
|
||||
if (id === resolvedVirtualModuleId) {
|
||||
return `export const SUPPORTED_LOCALES = ${JSON.stringify(filenames)}`;
|
||||
}
|
||||
},
|
||||
};
|
||||
})(),
|
||||
WindiCSS(),
|
||||
Icons(),
|
||||
Icons({}),
|
||||
svgLoader(),
|
||||
Components({
|
||||
resolvers: IconsResolver(),
|
||||
resolvers: [IconsResolver()],
|
||||
}),
|
||||
woodpeckerInfoPlugin(),
|
||||
prismjs({
|
||||
|
|
Loading…
Reference in a new issue