forked from AkkomaGang/akkoma-fe
Make interface language configurable from settings
The locale can now be configured in settings and is stored in Vuex. The changes are applied immidiately after selection. The list of languages is taken from the messages file, which contains all the available locales (and a new value, `interfaceLanguage`, to control the translation of this option in the options menu) Closes #36
This commit is contained in:
parent
30a6b7be5b
commit
c1e4bfa90f
9 changed files with 64 additions and 5 deletions
|
@ -63,6 +63,7 @@
|
||||||
"html-webpack-plugin": "^2.8.1",
|
"html-webpack-plugin": "^2.8.1",
|
||||||
"http-proxy-middleware": "^0.17.2",
|
"http-proxy-middleware": "^0.17.2",
|
||||||
"inject-loader": "^2.0.1",
|
"inject-loader": "^2.0.1",
|
||||||
|
"iso-639-1": "^2.0.3",
|
||||||
"isparta-loader": "^2.0.0",
|
"isparta-loader": "^2.0.0",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"karma": "^1.3.0",
|
"karma": "^1.3.0",
|
||||||
|
|
|
@ -20,6 +20,10 @@ export default {
|
||||||
data: () => ({
|
data: () => ({
|
||||||
mobileActivePanel: 'timeline'
|
mobileActivePanel: 'timeline'
|
||||||
}),
|
}),
|
||||||
|
created () {
|
||||||
|
// Load the locale from the storage
|
||||||
|
this.$i18n.locale = this.$store.state.config.interfaceLanguage
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
currentUser () { return this.$store.state.users.currentUser },
|
currentUser () { return this.$store.state.users.currentUser },
|
||||||
background () {
|
background () {
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label for="interface-language-switcher" class='select'>
|
||||||
|
<select id="interface-language-switcher" v-model="language">
|
||||||
|
<option v-for="(langCode, i) in languageCodes" :value="langCode">
|
||||||
|
{{ languageNames[i] }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<i class="icon-down-open"/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import languagesObject from '../../i18n/messages'
|
||||||
|
import ISO6391 from 'iso-639-1'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
languageCodes () {
|
||||||
|
return Object.keys(languagesObject)
|
||||||
|
},
|
||||||
|
|
||||||
|
languageNames () {
|
||||||
|
return _.map(this.languageCodes, ISO6391.getName)
|
||||||
|
},
|
||||||
|
|
||||||
|
language: {
|
||||||
|
get: function () { return this.$store.state.config.interfaceLanguage },
|
||||||
|
set: function (val) {
|
||||||
|
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||||
|
this.$i18n.locale = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,5 +1,6 @@
|
||||||
/* eslint-env browser */
|
/* eslint-env browser */
|
||||||
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
||||||
|
import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue'
|
||||||
import { filter, trim } from 'lodash'
|
import { filter, trim } from 'lodash'
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
|
@ -28,7 +29,8 @@ const settings = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
StyleSwitcher
|
StyleSwitcher,
|
||||||
|
InterfaceLanguageSwitcher
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user () {
|
user () {
|
||||||
|
|
|
@ -84,6 +84,10 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('settings.interfaceLanguage') }}</h2>
|
||||||
|
<interface-language-switcher />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -350,7 +350,8 @@ const en = {
|
||||||
default_vis: 'Default visibility scope',
|
default_vis: 'Default visibility scope',
|
||||||
profile_tab: 'Profile',
|
profile_tab: 'Profile',
|
||||||
security_tab: 'Security',
|
security_tab: 'Security',
|
||||||
data_import_export_tab: 'Data Import / Export'
|
data_import_export_tab: 'Data Import / Export',
|
||||||
|
interfaceLanguage: 'Interface language'
|
||||||
},
|
},
|
||||||
notifications: {
|
notifications: {
|
||||||
notifications: 'Notifications',
|
notifications: 'Notifications',
|
||||||
|
@ -1689,7 +1690,8 @@ const ru = {
|
||||||
profile_tab: 'Профиль',
|
profile_tab: 'Профиль',
|
||||||
security_tab: 'Безопасность',
|
security_tab: 'Безопасность',
|
||||||
data_import_export_tab: 'Импорт / Экспорт данных',
|
data_import_export_tab: 'Импорт / Экспорт данных',
|
||||||
collapse_subject: 'Сворачивать посты с темой'
|
collapse_subject: 'Сворачивать посты с темой',
|
||||||
|
interfaceLanguage: 'Язык интерфейса'
|
||||||
},
|
},
|
||||||
notifications: {
|
notifications: {
|
||||||
notifications: 'Уведомления',
|
notifications: 'Уведомления',
|
||||||
|
|
|
@ -60,6 +60,7 @@ const persistedStateOptions = {
|
||||||
'config.loopVideoSilentOnly',
|
'config.loopVideoSilentOnly',
|
||||||
'config.pauseOnUnfocused',
|
'config.pauseOnUnfocused',
|
||||||
'config.stopGifs',
|
'config.stopGifs',
|
||||||
|
'config.interfaceLanguage',
|
||||||
'users.lastLoginName',
|
'users.lastLoginName',
|
||||||
'statuses.notifications.maxSavedId'
|
'statuses.notifications.maxSavedId'
|
||||||
]
|
]
|
||||||
|
@ -79,6 +80,7 @@ const store = new Vuex.Store({
|
||||||
})
|
})
|
||||||
|
|
||||||
const i18n = new VueI18n({
|
const i18n = new VueI18n({
|
||||||
|
// By default, use the browser locale, we will update it if neccessary
|
||||||
locale: currentLocale,
|
locale: currentLocale,
|
||||||
fallbackLocale: 'en',
|
fallbackLocale: 'en',
|
||||||
messages
|
messages
|
||||||
|
@ -201,4 +203,3 @@ window.fetch('/nodeinfo/2.0.json')
|
||||||
store.dispatch('setOption', { name: 'suggestionsEnabled', value: suggestions.enabled })
|
store.dispatch('setOption', { name: 'suggestionsEnabled', value: suggestions.enabled })
|
||||||
store.dispatch('setOption', { name: 'suggestionsWeb', value: suggestions.web })
|
store.dispatch('setOption', { name: 'suggestionsWeb', value: suggestions.web })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { set, delete as del } from 'vue'
|
import { set, delete as del } from 'vue'
|
||||||
import StyleSetter from '../services/style_setter/style_setter.js'
|
import StyleSetter from '../services/style_setter/style_setter.js'
|
||||||
|
|
||||||
|
const browserLocale = (window.navigator.language || 'en').split('-')[0]
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
name: 'Pleroma FE',
|
name: 'Pleroma FE',
|
||||||
colors: {},
|
colors: {},
|
||||||
|
@ -17,7 +19,8 @@ const defaultState = {
|
||||||
stopGifs: false,
|
stopGifs: false,
|
||||||
replyVisibility: 'all',
|
replyVisibility: 'all',
|
||||||
muteWords: [],
|
muteWords: [],
|
||||||
highlight: {}
|
highlight: {},
|
||||||
|
interfaceLanguage: browserLocale
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
|
|
|
@ -3081,6 +3081,10 @@ isexe@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||||
|
|
||||||
|
iso-639-1@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/iso-639-1/-/iso-639-1-2.0.3.tgz#72dd3448ac5629c271628c5ac566369428d6ccd0"
|
||||||
|
|
||||||
isobject@^2.0.0:
|
isobject@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
|
||||||
|
|
Loading…
Reference in a new issue