commit: b7e93739656d225ed0a8854f357746b5291b6857
parent 25e628efe265db583797fe1c10fdcab2f0d9cc9d
Author: Tusooa Zhu <tusooa@kazv.moe>
Date: Mon, 25 Jul 2022 15:38:05 -0400
Add support for multiple interface languages
Diffstat:
5 files changed, 92 insertions(+), 23 deletions(-)
diff --git a/src/components/interface_language_switcher/interface_language_switcher.vue b/src/components/interface_language_switcher/interface_language_switcher.vue
@@ -1,21 +1,39 @@
<template>
- <div>
- <label for="interface-language-switcher">
+ <div class="interface-language-switcher">
+ <label>
{{ promptText }}
</label>
- {{ ' ' }}
- <Select
- id="interface-language-switcher"
- v-model="controlledLanguage"
- >
- <option
- v-for="lang in languages"
- :key="lang.code"
- :value="lang.code"
+ <ol>
+ <li
+ v-for="index of controlledLanguage.keys()"
+ :key="index"
>
- {{ lang.name }}
- </option>
- </Select>
+ <Select
+ class="language-select"
+ :model-value="controlledLanguage[index]"
+ @update:modelValue="val => setLanguageAt(index, val)"
+ >
+ <option
+ v-for="lang in languages"
+ :key="lang.code"
+ :value="lang.code"
+ >
+ {{ lang.name }}
+ </option>
+ </Select>
+ <button
+ v-if="controlledLanguage.length > 1"
+ class="button-default btn"
+ @click="() => removeLanguageAt(index)"
+ >{{ $t('settings.remove_language') }}</button>
+ </li>
+ <li>
+ <button
+ class="button-default btn"
+ @click="addLanguage"
+ >{{ $t('settings.add_language') }}</button>
+ </li>
+ </ol>
</div>
</template>
@@ -34,7 +52,7 @@ export default {
required: true
},
language: {
- type: String,
+ type: [Array, String],
required: true
},
setLanguage: {
@@ -48,7 +66,9 @@ export default {
},
controlledLanguage: {
- get: function () { return this.language },
+ get: function () {
+ return Array.isArray(this.language) ? this.language : [this.language]
+ },
set: function (val) {
this.setLanguage(val)
}
@@ -58,7 +78,30 @@ export default {
methods: {
getLanguageName (code) {
return localeService.getLanguageName(code)
+ },
+ addLanguage () {
+ this.controlledLanguage = [...this.controlledLanguage, '']
+ },
+ setLanguageAt (index, val) {
+ const lang = [...this.controlledLanguage]
+ lang[index] = val
+ this.controlledLanguage = lang
+ },
+ removeLanguageAt (index) {
+ const lang = [...this.controlledLanguage]
+ lang.splice(index, 1)
+ this.controlledLanguage = lang
}
}
}
</script>
+
+<style lang="scss">
+@import '../../_variables.scss';
+
+.interface-language-switcher {
+ .language-select {
+ margin-right: 1em;
+ }
+}
+</style>
diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js
@@ -43,7 +43,7 @@ const ProfileTab = {
bannerPreview: null,
background: null,
backgroundPreview: null,
- emailLanguage: this.$store.state.users.currentUser.language || ''
+ emailLanguage: this.$store.state.users.currentUser.language || ['']
}
},
components: {
@@ -130,7 +130,7 @@ const ProfileTab = {
}
if (this.emailLanguage) {
- params.language = localeService.internalToBackendLocale(this.emailLanguage)
+ params.language = localeService.internalToBackendLocaleMulti(this.emailLanguage)
}
this.$store.state.api.backendInteractor
diff --git a/src/i18n/messages.js b/src/i18n/messages.js
@@ -7,8 +7,11 @@
// sed -i -e "s/'//gm" -e 's/"/\\"/gm' -re 's/^( +)(.+?): ((.+?))?(,?)(\{?)$/\1"\2": "\4"/gm' -e 's/\"\{\"/{/g' -e 's/,"$/",/g' file.json
// There's only problem that apostrophe character ' gets replaced by \\ so you have to fix it manually, sorry.
+import { isEqual } from 'lodash'
import { languages, langCodeToJsonName } from './languages.js'
+const ULTIMATE_FALLBACK_LOCALE = 'en'
+
const hasLanguageFile = (code) => languages.includes(code)
const loadLanguageFile = (code) => {
@@ -25,11 +28,26 @@ const messages = {
en: require('./en.json').default
},
setLanguage: async (i18n, language) => {
- if (hasLanguageFile(language)) {
- const messages = await loadLanguageFile(language)
- i18n.setLocaleMessage(language, messages.default)
+ const languages = (Array.isArray(language) ? language : [language]).filter(k => k)
+
+ if (!languages.includes(ULTIMATE_FALLBACK_LOCALE)) {
+ languages.push(ULTIMATE_FALLBACK_LOCALE)
+ }
+ const [first, ...rest] = languages
+
+ if (first === i18n.locale && isEqual(rest, i18n.fallbackLocale)) {
+ return
}
- i18n.locale = language
+
+ for (const lang of languages) {
+ if (hasLanguageFile(lang)) {
+ const messages = await loadLanguageFile(lang)
+ i18n.setLocaleMessage(lang, messages.default)
+ }
+ }
+
+ i18n.fallbackLocale = rest
+ i18n.locale = first
}
}
diff --git a/src/modules/config.js b/src/modules/config.js
@@ -184,7 +184,10 @@ const config = {
case 'interfaceLanguage':
messages.setLanguage(this.getters.i18n, value)
dispatch('loadUnicodeEmojiData', value)
- Cookies.set(BACKEND_LANGUAGE_COOKIE_NAME, localeService.internalToBackendLocale(value))
+ Cookies.set(
+ BACKEND_LANGUAGE_COOKIE_NAME,
+ localeService.internalToBackendLocaleMulti(value)
+ )
break
case 'thirdColumnMode':
dispatch('setLayoutWidth', undefined)
diff --git a/src/services/locale/locale.service.js b/src/services/locale/locale.service.js
@@ -11,6 +11,10 @@ const specialLanguageCodes = {
const internalToBrowserLocale = code => specialLanguageCodes[code] || code
const internalToBackendLocale = code => internalToBrowserLocale(code).replace('_', '-')
+const internalToBackendLocaleMulti = codes => {
+ const langs = Array.isArray(codes) ? codes : [codes]
+ return langs.map(internalToBackendLocale).join(',')
+}
const getLanguageName = (code) => {
const specialLanguageNames = {
@@ -28,6 +32,7 @@ const languages = _.map(languagesObject.languages, (code) => ({ code, name: getL
const localeService = {
internalToBrowserLocale,
internalToBackendLocale,
+ internalToBackendLocaleMulti,
languages,
getLanguageName
}