Add support for multiple interface languages

This commit is contained in:
Tusooa Zhu 2022-07-25 15:38:05 -04:00 committed by tusooa
parent 25e628efe2
commit b7e9373965
No known key found for this signature in database
GPG Key ID: 7B467EDE43A08224
5 changed files with 92 additions and 23 deletions

View File

@ -1,12 +1,17 @@
<template> <template>
<div> <div class="interface-language-switcher">
<label for="interface-language-switcher"> <label>
{{ promptText }} {{ promptText }}
</label> </label>
{{ ' ' }} <ol>
<li
v-for="index of controlledLanguage.keys()"
:key="index"
>
<Select <Select
id="interface-language-switcher" class="language-select"
v-model="controlledLanguage" :model-value="controlledLanguage[index]"
@update:modelValue="val => setLanguageAt(index, val)"
> >
<option <option
v-for="lang in languages" v-for="lang in languages"
@ -16,6 +21,19 @@
{{ lang.name }} {{ lang.name }}
</option> </option>
</Select> </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> </div>
</template> </template>
@ -34,7 +52,7 @@ export default {
required: true required: true
}, },
language: { language: {
type: String, type: [Array, String],
required: true required: true
}, },
setLanguage: { setLanguage: {
@ -48,7 +66,9 @@ export default {
}, },
controlledLanguage: { controlledLanguage: {
get: function () { return this.language }, get: function () {
return Array.isArray(this.language) ? this.language : [this.language]
},
set: function (val) { set: function (val) {
this.setLanguage(val) this.setLanguage(val)
} }
@ -58,7 +78,30 @@ export default {
methods: { methods: {
getLanguageName (code) { getLanguageName (code) {
return localeService.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> </script>
<style lang="scss">
@import '../../_variables.scss';
.interface-language-switcher {
.language-select {
margin-right: 1em;
}
}
</style>

View File

@ -43,7 +43,7 @@ const ProfileTab = {
bannerPreview: null, bannerPreview: null,
background: null, background: null,
backgroundPreview: null, backgroundPreview: null,
emailLanguage: this.$store.state.users.currentUser.language || '' emailLanguage: this.$store.state.users.currentUser.language || ['']
} }
}, },
components: { components: {
@ -130,7 +130,7 @@ const ProfileTab = {
} }
if (this.emailLanguage) { if (this.emailLanguage) {
params.language = localeService.internalToBackendLocale(this.emailLanguage) params.language = localeService.internalToBackendLocaleMulti(this.emailLanguage)
} }
this.$store.state.api.backendInteractor this.$store.state.api.backendInteractor

View File

@ -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 // 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. // 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' import { languages, langCodeToJsonName } from './languages.js'
const ULTIMATE_FALLBACK_LOCALE = 'en'
const hasLanguageFile = (code) => languages.includes(code) const hasLanguageFile = (code) => languages.includes(code)
const loadLanguageFile = (code) => { const loadLanguageFile = (code) => {
@ -25,11 +28,26 @@ const messages = {
en: require('./en.json').default en: require('./en.json').default
}, },
setLanguage: async (i18n, language) => { setLanguage: async (i18n, language) => {
if (hasLanguageFile(language)) { const languages = (Array.isArray(language) ? language : [language]).filter(k => k)
const messages = await loadLanguageFile(language)
i18n.setLocaleMessage(language, messages.default) if (!languages.includes(ULTIMATE_FALLBACK_LOCALE)) {
languages.push(ULTIMATE_FALLBACK_LOCALE)
} }
i18n.locale = language const [first, ...rest] = languages
if (first === i18n.locale && isEqual(rest, i18n.fallbackLocale)) {
return
}
for (const lang of languages) {
if (hasLanguageFile(lang)) {
const messages = await loadLanguageFile(lang)
i18n.setLocaleMessage(lang, messages.default)
}
}
i18n.fallbackLocale = rest
i18n.locale = first
} }
} }

View File

@ -184,7 +184,10 @@ const config = {
case 'interfaceLanguage': case 'interfaceLanguage':
messages.setLanguage(this.getters.i18n, value) messages.setLanguage(this.getters.i18n, value)
dispatch('loadUnicodeEmojiData', value) dispatch('loadUnicodeEmojiData', value)
Cookies.set(BACKEND_LANGUAGE_COOKIE_NAME, localeService.internalToBackendLocale(value)) Cookies.set(
BACKEND_LANGUAGE_COOKIE_NAME,
localeService.internalToBackendLocaleMulti(value)
)
break break
case 'thirdColumnMode': case 'thirdColumnMode':
dispatch('setLayoutWidth', undefined) dispatch('setLayoutWidth', undefined)

View File

@ -11,6 +11,10 @@ const specialLanguageCodes = {
const internalToBrowserLocale = code => specialLanguageCodes[code] || code const internalToBrowserLocale = code => specialLanguageCodes[code] || code
const internalToBackendLocale = code => internalToBrowserLocale(code).replace('_', '-') 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 getLanguageName = (code) => {
const specialLanguageNames = { const specialLanguageNames = {
@ -28,6 +32,7 @@ const languages = _.map(languagesObject.languages, (code) => ({ code, name: getL
const localeService = { const localeService = {
internalToBrowserLocale, internalToBrowserLocale,
internalToBackendLocale, internalToBackendLocale,
internalToBackendLocaleMulti,
languages, languages,
getLanguageName getLanguageName
} }