logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git
commit: a9716701be26c696ee1b908a1787b34880175ffa
parent a3c971526a04aee8a8f42ce3e39d0195a442335d
Author: HJ <30-hj@users.noreply.git.pleroma.social>
Date:   Wed, 25 Jan 2023 23:47:29 +0000

Merge branch 'from/develop/tusooa/multi-iface-lang' into 'develop'

Multiple interface languages support

See merge request pleroma/pleroma-fe!1568

Diffstat:

Msrc/components/interface_language_switcher/interface_language_switcher.vue80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/components/settings_modal/tabs/profile_tab.js4++--
Msrc/i18n/en.json4++++
Msrc/i18n/messages.js26++++++++++++++++++++++----
Msrc/modules/config.js5++++-
Msrc/modules/instance.js9+++++++--
Msrc/services/locale/locale.service.js5+++++
7 files changed, 108 insertions(+), 25 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,44 @@ <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" + <ul class="setting-list"> + <li + v-for="index of controlledLanguage.keys()" + :key="index" > - {{ lang.name }} - </option> - </Select> + <label> + {{ index === 0 ? $t('settings.primary_language') : $tc('settings.fallback_language', index, { index }) }} + <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> + </label> + <button + v-if="controlledLanguage.length > 1 && index !== 0" + 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> + </ul> </div> </template> @@ -34,7 +57,7 @@ export default { required: true }, language: { - type: String, + type: [Array, String], required: true }, setLanguage: { @@ -48,7 +71,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 +83,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/en.json b/src/i18n/en.json @@ -335,6 +335,10 @@ "select_all": "Select all" }, "settings": { + "add_language": "Add fallback language", + "remove_language": "Remove", + "primary_language": "Primary language:", + "fallback_language": "Fallback language {index}:", "app_name": "App name", "expert_mode": "Show advanced", "save": "Save changes", 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/modules/instance.js b/src/modules/instance.js @@ -286,8 +286,13 @@ const instance = { langList .map(async lang => { if (!state.unicodeEmojiAnnotations[lang]) { - const annotations = await loadAnnotations(lang) - commit('setUnicodeEmojiAnnotations', { lang, annotations }) + try { + const annotations = await loadAnnotations(lang) + commit('setUnicodeEmojiAnnotations', { lang, annotations }) + } catch (e) { + console.warn(`Error loading unicode emoji annotations for ${lang}: `, e) + // ignore + } } })) }, 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 }