mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
refactor: upgrade to i18n9
This commit is contained in:
parent
dd9ae21472
commit
81707bb11a
8 changed files with 97 additions and 57 deletions
|
@ -21,7 +21,7 @@
|
||||||
"fork-awesome": "^1.2.0",
|
"fork-awesome": "^1.2.0",
|
||||||
"simple-evaluate": "^1.4.6",
|
"simple-evaluate": "^1.4.6",
|
||||||
"vue": "3.3.4",
|
"vue": "3.3.4",
|
||||||
"vue-i18n": "^8.28.2",
|
"vue-i18n": "^9.10.1",
|
||||||
"vue-router": "^4.3.0",
|
"vue-router": "^4.3.0",
|
||||||
"vue-showdown": "^2.4.1",
|
"vue-showdown": "^2.4.1",
|
||||||
"vuex": "^4.1.0"
|
"vuex": "^4.1.0"
|
||||||
|
|
|
@ -10,7 +10,7 @@ class APIError extends Error {
|
||||||
super(
|
super(
|
||||||
error
|
error
|
||||||
? error.replaceAll('\n', '<br>')
|
? error.replaceAll('\n', '<br>')
|
||||||
: i18n.t('error_server_unexpected'),
|
: i18n.global.t('error_server_unexpected'),
|
||||||
)
|
)
|
||||||
const urlObj = new URL(url)
|
const urlObj = new URL(url)
|
||||||
this.name = 'APIError'
|
this.name = 'APIError'
|
||||||
|
@ -39,7 +39,9 @@ class APIErrorLog extends APIError {
|
||||||
// 0 — (means "the connexion has been closed" apparently)
|
// 0 — (means "the connexion has been closed" apparently)
|
||||||
class APIConnexionError extends APIError {
|
class APIConnexionError extends APIError {
|
||||||
constructor(method, response) {
|
constructor(method, response) {
|
||||||
super(method, response, { error: i18n.t('error_connection_interrupted') })
|
super(method, response, {
|
||||||
|
error: i18n.global.t('error_connection_interrupted'),
|
||||||
|
})
|
||||||
this.name = 'APIConnexionError'
|
this.name = 'APIConnexionError'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +59,7 @@ class APIBadRequestError extends APIError {
|
||||||
// 401 — Unauthorized
|
// 401 — Unauthorized
|
||||||
class APIUnauthorizedError extends APIError {
|
class APIUnauthorizedError extends APIError {
|
||||||
constructor(method, response, errorData) {
|
constructor(method, response, errorData) {
|
||||||
super(method, response, { error: i18n.t('unauthorized') })
|
super(method, response, { error: i18n.global.t('unauthorized') })
|
||||||
this.name = 'APIUnauthorizedError'
|
this.name = 'APIUnauthorizedError'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +67,7 @@ class APIUnauthorizedError extends APIError {
|
||||||
// 404 — Not Found
|
// 404 — Not Found
|
||||||
class APINotFoundError extends APIError {
|
class APINotFoundError extends APIError {
|
||||||
constructor(method, response, errorData) {
|
constructor(method, response, errorData) {
|
||||||
errorData.error = i18n.t('api_not_found')
|
errorData.error = i18n.global.t('api_not_found')
|
||||||
super(method, response, errorData)
|
super(method, response, errorData)
|
||||||
this.name = 'APINotFoundError'
|
this.name = 'APINotFoundError'
|
||||||
}
|
}
|
||||||
|
@ -83,7 +85,7 @@ class APIInternalError extends APIError {
|
||||||
// 502 — Bad gateway (means API is down)
|
// 502 — Bad gateway (means API is down)
|
||||||
class APINotRespondingError extends APIError {
|
class APINotRespondingError extends APIError {
|
||||||
constructor(method, response) {
|
constructor(method, response) {
|
||||||
super(method, response, { error: i18n.t('api_not_responding') })
|
super(method, response, { error: i18n.global.t('api_not_responding') })
|
||||||
this.name = 'APINotRespondingError'
|
this.name = 'APINotRespondingError'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,7 +152,7 @@ export function formatYunoHostArgument(arg) {
|
||||||
props: defaultProps.concat(['type', 'autocomplete', 'trim']),
|
props: defaultProps.concat(['type', 'autocomplete', 'trim']),
|
||||||
callback: function () {
|
callback: function () {
|
||||||
if (!arg.help) {
|
if (!arg.help) {
|
||||||
arg.help = i18n.t('good_practices_about_admin_password')
|
arg.help = i18n.global.t('good_practices_about_admin_password')
|
||||||
}
|
}
|
||||||
arg.example = '••••••••••••'
|
arg.example = '••••••••••••'
|
||||||
validation.passwordLenght = validators.minLength(8)
|
validation.passwordLenght = validators.minLength(8)
|
||||||
|
@ -180,7 +180,7 @@ export function formatYunoHostArgument(arg) {
|
||||||
if (arg.type !== 'select') {
|
if (arg.type !== 'select') {
|
||||||
field.props.link = {
|
field.props.link = {
|
||||||
name: arg.type + '-list',
|
name: arg.type + '-list',
|
||||||
text: i18n.t(`manage_${arg.type}s`),
|
text: i18n.global.t(`manage_${arg.type}s`),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -337,7 +337,7 @@ export function formatYunoHostArgument(arg) {
|
||||||
if (arg.helpLink) {
|
if (arg.helpLink) {
|
||||||
field.props.link = {
|
field.props.link = {
|
||||||
href: arg.helpLink.href,
|
href: arg.helpLink.href,
|
||||||
text: i18n.t(arg.helpLink.text),
|
text: i18n.global.t(arg.helpLink.text),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { nextTick } from 'vue'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import i18n from '@/i18n'
|
import i18n from '@/i18n'
|
||||||
import supportedLocales from './supportedLocales'
|
import supportedLocales from './supportedLocales'
|
||||||
|
|
||||||
let dateFnsLocale
|
export let dateFnsLocale
|
||||||
const loadedLanguages = []
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first two supported locales that can be found in the `localStorage` or
|
* Returns the first two supported locales that can be found in the `localStorage` or
|
||||||
|
@ -34,26 +34,54 @@ function getDefaultLocales() {
|
||||||
return defaultLocales
|
return defaultLocales
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDocumentLocale(locale) {
|
export async function setI18nLocale(locale) {
|
||||||
document.documentElement.lang = locale
|
if (!i18n.global.availableLocales.includes(locale)) {
|
||||||
|
await loadLocaleMessages(locale)
|
||||||
|
// also query/set the date-fns locale object for time translation
|
||||||
|
await loadDateFnsLocale(locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preload 'en' locales as it is the hard fallback
|
||||||
|
if (locale !== 'en' && !i18n.global.availableLocales.includes('en')) {
|
||||||
|
loadLocaleMessages('en')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i18n.mode === 'legacy') {
|
||||||
|
i18n.global.locale = locale
|
||||||
|
} else {
|
||||||
|
i18n.global.locale.value = locale
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('html').setAttribute('lang', locale)
|
||||||
// FIXME can't currently change document direction easily since bootstrap still doesn't handle rtl.
|
// FIXME can't currently change document direction easily since bootstrap still doesn't handle rtl.
|
||||||
// document.dir = locale === 'ar' ? 'rtl' : 'ltr'
|
// document.dir = locale === 'ar' ? 'rtl' : 'ltr'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setI18nFallbackLocale(locale) {
|
||||||
|
if (!i18n.global.availableLocales.includes(locale)) {
|
||||||
|
await loadLocaleMessages(locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i18n.mode === 'legacy') {
|
||||||
|
i18n.global.fallbackLocale = [locale, 'en']
|
||||||
|
} else {
|
||||||
|
i18n.global.fallbackLocale.value = [locale, 'en']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a translation file and adds its content to the i18n plugin `messages`.
|
* Loads a translation file and adds its content to the i18n plugin `messages`.
|
||||||
*
|
*
|
||||||
* @return {Promise<string>} Promise that resolve the given locale string
|
* @return {Promise<string>} Promise that resolve the given locale string
|
||||||
*/
|
*/
|
||||||
function loadLocaleMessages(locale) {
|
export async function loadLocaleMessages(locale) {
|
||||||
if (loadedLanguages.includes(locale)) {
|
// load locale messages with dynamic import
|
||||||
return Promise.resolve(locale)
|
const messages = await import(`./locales/${locale}.json`)
|
||||||
}
|
|
||||||
return import(`@/i18n/locales/${locale}.json`).then((messages) => {
|
// set locale and locale message
|
||||||
i18n.setLocaleMessage(locale, messages.default)
|
i18n.global.setLocaleMessage(locale, messages)
|
||||||
loadedLanguages.push(locale)
|
|
||||||
return locale
|
return nextTick()
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,19 +99,10 @@ async function loadDateFnsLocale(locale) {
|
||||||
/**
|
/**
|
||||||
* Initialize all locales
|
* Initialize all locales
|
||||||
*/
|
*/
|
||||||
function initDefaultLocales() {
|
export async function initDefaultLocales() {
|
||||||
// Get defined locales from `localStorage` or `navigator`
|
// Get defined locales from `localStorage` or `navigator`
|
||||||
const [locale, fallbackLocale] = getDefaultLocales()
|
const [locale, fallbackLocale] = getDefaultLocales()
|
||||||
|
|
||||||
store.dispatch('UPDATE_LOCALE', locale)
|
await store.dispatch('UPDATE_LOCALE', locale)
|
||||||
store.dispatch('UPDATE_FALLBACKLOCALE', fallbackLocale || 'en')
|
await store.dispatch('UPDATE_FALLBACKLOCALE', fallbackLocale || 'en')
|
||||||
return loadLocaleMessages('en')
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
initDefaultLocales,
|
|
||||||
updateDocumentLocale,
|
|
||||||
loadLocaleMessages,
|
|
||||||
loadDateFnsLocale,
|
|
||||||
dateFnsLocale,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
* @module i18n
|
* @module i18n
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Vue from 'vue'
|
import { createI18n } from 'vue-i18n'
|
||||||
import VueI18n from 'vue-i18n'
|
|
||||||
|
|
||||||
// Plugin Initialization
|
export default createI18n({
|
||||||
Vue.use(VueI18n)
|
// FIXME
|
||||||
|
legacy: true,
|
||||||
export default new VueI18n({})
|
})
|
||||||
|
|
|
@ -199,7 +199,7 @@ export default {
|
||||||
? humanKey
|
? humanKey
|
||||||
: { key: humanKey }
|
: { key: humanKey }
|
||||||
const humanRoute = key
|
const humanRoute = key
|
||||||
? i18n.t('human_routes.' + key, args)
|
? i18n.global.t('human_routes.' + key, args)
|
||||||
: `[${method}] /${uri}`
|
: `[${method}] /${uri}`
|
||||||
|
|
||||||
let request = {
|
let request = {
|
||||||
|
@ -368,9 +368,9 @@ export default {
|
||||||
// if a traduction key string has been given and we also need to pass
|
// if a traduction key string has been given and we also need to pass
|
||||||
// the route param as a variable.
|
// the route param as a variable.
|
||||||
if (trad && param) {
|
if (trad && param) {
|
||||||
text = i18n.t(trad, { [param]: to.params[param] })
|
text = i18n.global.t(trad, { [param]: to.params[param] })
|
||||||
} else if (trad) {
|
} else if (trad) {
|
||||||
text = i18n.t(trad)
|
text = i18n.global.t(trad)
|
||||||
} else {
|
} else {
|
||||||
text = to.params[param]
|
text = to.params[param]
|
||||||
}
|
}
|
||||||
|
@ -395,7 +395,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display a simplified breadcrumb as the document title.
|
// Display a simplified breadcrumb as the document title.
|
||||||
document.title = `${getTitle(breadcrumb)} | ${i18n.t('yunohost_admin')}`
|
document.title = `${getTitle(breadcrumb)} | ${i18n.global.t('yunohost_admin')}`
|
||||||
},
|
},
|
||||||
|
|
||||||
UPDATE_TRANSITION_NAME({ state, commit }, { to, from }) {
|
UPDATE_TRANSITION_NAME({ state, commit }, { to, from }) {
|
||||||
|
|
|
@ -3,12 +3,7 @@
|
||||||
* @module store/settings
|
* @module store/settings
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import i18n from '@/i18n'
|
import { setI18nLocale, setI18nFallbackLocale } from '@/i18n/helpers'
|
||||||
import {
|
|
||||||
loadLocaleMessages,
|
|
||||||
updateDocumentLocale,
|
|
||||||
loadDateFnsLocale,
|
|
||||||
} from '@/i18n/helpers'
|
|
||||||
import supportedLocales from '@/i18n/supportedLocales'
|
import supportedLocales from '@/i18n/supportedLocales'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -62,19 +57,14 @@ export default {
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
UPDATE_LOCALE({ commit }, locale) {
|
UPDATE_LOCALE({ commit }, locale) {
|
||||||
loadLocaleMessages(locale).then(() => {
|
return setI18nLocale(locale).then(() => {
|
||||||
updateDocumentLocale(locale)
|
|
||||||
commit('SET_LOCALE', locale)
|
commit('SET_LOCALE', locale)
|
||||||
i18n.locale = locale
|
|
||||||
})
|
})
|
||||||
// also query the date-fns locale object for filters
|
|
||||||
loadDateFnsLocale(locale)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
UPDATE_FALLBACKLOCALE({ commit }, locale) {
|
UPDATE_FALLBACKLOCALE({ commit }, locale) {
|
||||||
loadLocaleMessages(locale).then(() => {
|
return setI18nFallbackLocale(locale).then(() => {
|
||||||
commit('SET_FALLBACKLOCALE', locale)
|
commit('SET_FALLBACKLOCALE', locale)
|
||||||
i18n.fallbackLocale = [locale, 'en']
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,14 @@ import { defineConfig, loadEnv } from 'vite'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import createVuePlugin from '@vitejs/plugin-vue'
|
import createVuePlugin from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
import supportedLocales from './src/i18n/supportedLocales'
|
||||||
|
|
||||||
|
const supportedDatefnsLocales = Object.entries(supportedLocales).map(
|
||||||
|
([locale, { dateFnsLocale }]) => {
|
||||||
|
return dateFnsLocale || locale
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
export default defineConfig(({ command, mode }) => {
|
export default defineConfig(({ command, mode }) => {
|
||||||
// Load env file based on `mode` in the current working directory.
|
// Load env file based on `mode` in the current working directory.
|
||||||
// Set the third parameter to '' to load all env regardless of the `VITE_` prefix.
|
// Set the third parameter to '' to load all env regardless of the `VITE_` prefix.
|
||||||
|
@ -53,6 +61,28 @@ export default defineConfig(({ command, mode }) => {
|
||||||
if (!id.includes('node_modules') && id.includes('api/')) {
|
if (!id.includes('node_modules') && id.includes('api/')) {
|
||||||
return 'core'
|
return 'core'
|
||||||
}
|
}
|
||||||
|
// Translations
|
||||||
|
if (id.includes('locales')) {
|
||||||
|
const match = /.*\/i18n\/locales\/([\w-]+)\.json/.exec(id)
|
||||||
|
return `locales/${match[1]}/translations`
|
||||||
|
}
|
||||||
|
// Split date-fns locales
|
||||||
|
if (id.includes('date-fns')) {
|
||||||
|
const match = /.*\/date-fns\/esm\/locale\/([\w-]+)\/.*\.js/.exec(
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
if (match) {
|
||||||
|
if (supportedDatefnsLocales.includes(match[1])) {
|
||||||
|
return `locales/${match[1]}/date-fns`
|
||||||
|
} else {
|
||||||
|
// FIXME: currently difficult to cherry pick only needed locales,
|
||||||
|
// hopefully this chunk should not be fetched.
|
||||||
|
return 'locales/not-used'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 'date-fns'
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue