mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-26 10:20:29 +00:00
Lazy-load TimeAgo locales (#2094)
1. new translation docs 2. lazy-load TimeAgo locales (used for "x min ago" messages). This 1. reduces size and 2. provides all languages without adding them manually. 3. Remove DayJS locales, they're unused.
This commit is contained in:
parent
74a619ff5b
commit
d1c51f4af8
10 changed files with 67 additions and 38 deletions
|
@ -1,17 +1,9 @@
|
|||
# Translations
|
||||
|
||||
Woodpecker uses [Vue I18n](https://vue-i18n.intlify.dev/) as translation library, thus you can easily translate the web UI into your language. Therefore, copy the file `web/src/assets/locales/en.json` to the same path with your language's code and `.json` as name.
|
||||
Then, translate content of this file, but only the values:
|
||||
To translate the web UI into your language, we have [our own Weblate instance](https://translate.woodpecker-ci.org/). Please register there and translate Woodpecker into your language. **We won't accept PRs changing any language except English.**
|
||||
|
||||
```json
|
||||
{
|
||||
"dont_translate": "Only translate this text"
|
||||
}
|
||||
```
|
||||
<a href="https://translate.woodpecker-ci.org/engage/woodpecker-ci/">
|
||||
<img src="https://translate.woodpecker-ci.org/widgets/woodpecker-ci/-/ui/multi-blue.svg" alt="Translation status" />
|
||||
</a>
|
||||
|
||||
To add support for time formatting, import the language into two files:
|
||||
|
||||
1. `web/src/compositions/useDate.ts`: Just add a line like `import 'dayjs/locale/en';` to the first block of `import` statements and replace `en` with your language's code.
|
||||
2. `web/src/utils/timeAgo.ts`: Add a line like `import en from 'javascript-time-ago/locale/en.json';` to the other `import`-statements and replace both `en`s with your language's code. Then, add the line `TimeAgo.addDefaultLocale(en);` to the other lines of them, and replace `en` with your language's code.
|
||||
|
||||
Then, the web UI should be available in your language. You should open a pull request to our repository to get your changes into the next release.
|
||||
Woodpecker uses [Vue I18n](https://vue-i18n.intlify.dev/) as translation library.
|
||||
|
|
1
web/.gitignore
vendored
1
web/.gitignore
vendored
|
@ -3,3 +3,4 @@ node_modules
|
|||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
src/assets/timeAgoLocales
|
||||
|
|
|
@ -140,11 +140,12 @@ import useApiClient from '~/compositions/useApiClient';
|
|||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import useTimeAgo from '~/compositions/useTimeAgo';
|
||||
import { Agent } from '~/lib/api/types';
|
||||
import timeAgo from '~/utils/timeAgo';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const timeAgo = useTimeAgo();
|
||||
const { t } = useI18n();
|
||||
|
||||
const selectedAgent = ref<Partial<Agent>>();
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<Tooltip>
|
||||
<span>{{ since }}</span>
|
||||
<template #popper
|
||||
><span class="font-bold">{{ $t('created') }}</span> {{ created }}</template
|
||||
><span class="font-bold">{{ $t('repo.pipeline.created') }}</span> {{ created }}</template
|
||||
>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
import 'dayjs/locale/en';
|
||||
import 'dayjs/locale/lv';
|
||||
import 'dayjs/locale/de';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { getUserLanguage } from '~/utils/locale';
|
||||
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(advancedFormat);
|
||||
dayjs.locale(getUserLanguage());
|
||||
|
||||
export function useDate() {
|
||||
function toLocaleString(date: Date) {
|
||||
|
|
|
@ -3,6 +3,8 @@ import { createI18n } from 'vue-i18n';
|
|||
|
||||
import { getUserLanguage } from '~/utils/locale';
|
||||
|
||||
import { loadTimeAgoLocale } from './useTimeAgo';
|
||||
|
||||
const userLanguage = getUserLanguage();
|
||||
const fallbackLocale = 'en';
|
||||
export const i18n = createI18n({
|
||||
|
@ -17,6 +19,8 @@ export const loadLocaleMessages = async (locale: string) => {
|
|||
|
||||
i18n.global.setLocaleMessage(locale, messages);
|
||||
|
||||
loadTimeAgoLocale(locale);
|
||||
|
||||
return nextTick();
|
||||
};
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@ import { useElapsedTime } from '~/compositions/useElapsedTime';
|
|||
import { Pipeline } from '~/lib/api/types';
|
||||
import { prettyDuration } from '~/utils/duration';
|
||||
import { convertEmojis } from '~/utils/emoji';
|
||||
import timeAgo from '~/utils/timeAgo';
|
||||
|
||||
import useTimeAgo from './useTimeAgo';
|
||||
|
||||
const { toLocaleString } = useDate();
|
||||
|
||||
|
@ -27,6 +28,7 @@ export default (pipeline: Ref<Pipeline | undefined>) => {
|
|||
const { time: sinceElapsed } = useElapsedTime(sinceUnderOneHour, sinceRaw);
|
||||
|
||||
const i18n = useI18n();
|
||||
const timeAgo = useTimeAgo();
|
||||
const since = computed(() => {
|
||||
if (sinceRaw.value === 0) {
|
||||
return i18n.t('time.not_started');
|
||||
|
|
17
web/src/compositions/useTimeAgo.ts
Normal file
17
web/src/compositions/useTimeAgo.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import TimeAgo from 'javascript-time-ago';
|
||||
import en from 'javascript-time-ago/locale/en.json';
|
||||
|
||||
import { getUserLanguage } from '~/utils/locale';
|
||||
|
||||
TimeAgo.addDefaultLocale(en);
|
||||
|
||||
const addedLocales = ['en'];
|
||||
|
||||
export default () => new TimeAgo(getUserLanguage());
|
||||
export async function loadTimeAgoLocale(locale: string) {
|
||||
if (!addedLocales.includes(locale)) {
|
||||
const { default: timeAgoLocale } = await import(`~/assets/timeAgoLocales/${locale}.js`);
|
||||
TimeAgo.addLocale(timeAgoLocale);
|
||||
addedLocales.push(locale);
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import TimeAgo from 'javascript-time-ago';
|
||||
import de from 'javascript-time-ago/locale/de.json';
|
||||
import en from 'javascript-time-ago/locale/en.json';
|
||||
import lv from 'javascript-time-ago/locale/lv.json';
|
||||
|
||||
import { getUserLanguage } from '~/utils/locale';
|
||||
|
||||
TimeAgo.addDefaultLocale(en);
|
||||
TimeAgo.addLocale(de);
|
||||
TimeAgo.addLocale(lv);
|
||||
|
||||
const timeAgo = new TimeAgo(getUserLanguage());
|
||||
|
||||
export default timeAgo;
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { readdirSync } from 'fs';
|
||||
import { copyFile, existsSync, mkdirSync, readdirSync } from 'fs';
|
||||
import path from 'path';
|
||||
import IconsResolver from 'unplugin-icons/resolver';
|
||||
import Icons from 'unplugin-icons/vite';
|
||||
|
@ -38,6 +38,39 @@ export default defineConfig({
|
|||
|
||||
const filenames = readdirSync('src/assets/locales/').map((filename) => filename.replace('.json', ''));
|
||||
|
||||
if (!existsSync('src/assets/timeAgoLocales')) {
|
||||
mkdirSync('src/assets/timeAgoLocales');
|
||||
}
|
||||
|
||||
filenames.forEach((name) => {
|
||||
// copy timeAgo language
|
||||
if (name === 'zh-Hans') {
|
||||
// zh-Hans is called zh in javascript-time-ago, so we need to rename this
|
||||
copyFile(
|
||||
'node_modules/javascript-time-ago/locale/zh.json.js',
|
||||
'src/assets/timeAgoLocales/zh-Hans.js',
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
(err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (name !== 'en') {
|
||||
// English is always directly loaded (compiled by Vite) and thus not copied
|
||||
copyFile(
|
||||
`node_modules/javascript-time-ago/locale/${name}.json.js`,
|
||||
`src/assets/timeAgoLocales/${name}.js`,
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
(err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
name: 'vue-i18n-supported-locales',
|
||||
// eslint-disable-next-line consistent-return
|
||||
|
|
Loading…
Reference in a new issue