feat: allow disabling translation for specific languages (#1371)
parent
e197a1dbe9
commit
b48b7f4c16
|
@ -0,0 +1,63 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import ISO6391 from 'iso-639-1'
|
||||||
|
|
||||||
|
const supportedTranslationLanguages = ISO6391.getLanguages([...supportedTranslationCodes])
|
||||||
|
const userSettings = useUserSettings()
|
||||||
|
|
||||||
|
const language = ref<string | null>(null)
|
||||||
|
|
||||||
|
const availableOptions = computed(() => {
|
||||||
|
return Object.values(supportedTranslationLanguages).filter((value) => {
|
||||||
|
return !userSettings.value.disabledTranslationLanguages.includes(value.code)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
function addDisabledTranslation() {
|
||||||
|
if (language.value) {
|
||||||
|
const uniqueValues = new Set(userSettings.value.disabledTranslationLanguages)
|
||||||
|
uniqueValues.add(language.value)
|
||||||
|
userSettings.value.disabledTranslationLanguages = [...uniqueValues]
|
||||||
|
language.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function removeDisabledTranslation(code: string) {
|
||||||
|
const uniqueValues = new Set(userSettings.value.disabledTranslationLanguages)
|
||||||
|
uniqueValues.delete(code)
|
||||||
|
userSettings.value.disabledTranslationLanguages = [...uniqueValues]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<CommonCheckbox v-model="userSettings.preferences.hideTranslation" :label="$t('settings.preferences.hide_translation')" />
|
||||||
|
<div v-if="!userSettings.preferences.hideTranslation" class="mt-1 ms-2">
|
||||||
|
<p class=" mb-2">
|
||||||
|
{{ $t('settings.language.translations.hide_specific') }}
|
||||||
|
</p>
|
||||||
|
<div class="ms-4">
|
||||||
|
<ul>
|
||||||
|
<li v-for="langCode in userSettings.disabledTranslationLanguages" :key="langCode" class="flex items-center">
|
||||||
|
<div>{{ ISO6391.getNativeName(langCode) }}</div>
|
||||||
|
<button class="btn-text" type="button" :title="$t('settings.language.translations.remove')" @click.prevent="removeDisabledTranslation(langCode)">
|
||||||
|
<span class="block i-ri:close-line" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="flex items-center mt-2">
|
||||||
|
<select v-model="language" class="select-settings">
|
||||||
|
<option disabled selected :value="null">
|
||||||
|
{{ $t('settings.language.translations.choose_language') }}
|
||||||
|
</option>
|
||||||
|
<option v-for="availableOption in availableOptions" :key="availableOption.code" :value="availableOption.code">
|
||||||
|
{{ availableOption.nativeName }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<button class="btn-text" @click="addDisabledTranslation">
|
||||||
|
{{ $t('settings.language.translations.add') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -12,7 +12,7 @@ const {
|
||||||
} = useTranslation(status, getLanguageCode())
|
} = useTranslation(status, getLanguageCode())
|
||||||
const preferenceHideTranslation = usePreferences('hideTranslation')
|
const preferenceHideTranslation = usePreferences('hideTranslation')
|
||||||
|
|
||||||
const showButton = computed(() => !preferenceHideTranslation.value && isTranslationEnabled && status.language !== getLanguageCode())
|
const showButton = computed(() => !preferenceHideTranslation.value && isTranslationEnabled)
|
||||||
|
|
||||||
let translating = $ref(false)
|
let translating = $ref(false)
|
||||||
const toggleTranslation = async () => {
|
const toggleTranslation = async () => {
|
||||||
|
|
|
@ -8,6 +8,40 @@ export interface TranslationResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @see https://github.com/LibreTranslate/LibreTranslate/tree/main/libretranslate/locales
|
||||||
|
export const supportedTranslationCodes = [
|
||||||
|
'ar',
|
||||||
|
'az',
|
||||||
|
'cs',
|
||||||
|
'da',
|
||||||
|
'de',
|
||||||
|
'el',
|
||||||
|
'en',
|
||||||
|
'eo',
|
||||||
|
'es',
|
||||||
|
'fa',
|
||||||
|
'fi',
|
||||||
|
'fr',
|
||||||
|
'ga',
|
||||||
|
'he',
|
||||||
|
'hi',
|
||||||
|
'hu',
|
||||||
|
'id',
|
||||||
|
'it',
|
||||||
|
'ja',
|
||||||
|
'ko',
|
||||||
|
'nl',
|
||||||
|
'pl',
|
||||||
|
'pt',
|
||||||
|
'ru',
|
||||||
|
'sk',
|
||||||
|
'sv',
|
||||||
|
'tr',
|
||||||
|
'uk',
|
||||||
|
'vi',
|
||||||
|
'zh',
|
||||||
|
] as const
|
||||||
|
|
||||||
export const getLanguageCode = () => {
|
export const getLanguageCode = () => {
|
||||||
let code = 'en'
|
let code = 'en'
|
||||||
const getCode = (code: string) => code.replace(/-.*$/, '')
|
const getCode = (code: string) => code.replace(/-.*$/, '')
|
||||||
|
@ -63,9 +97,16 @@ export function useTranslation(status: mastodon.v1.Status | mastodon.v1.StatusEd
|
||||||
translations.set(status, reactive({ visible: false, text: '', success: false, error: '' }))
|
translations.set(status, reactive({ visible: false, text: '', success: false, error: '' }))
|
||||||
|
|
||||||
const translation = translations.get(status)!
|
const translation = translations.get(status)!
|
||||||
|
const userSettings = useUserSettings()
|
||||||
|
|
||||||
|
const shouldTranslate = 'language' in status && status.language && status.language !== to
|
||||||
|
&& supportedTranslationCodes.includes(to as any)
|
||||||
|
&& supportedTranslationCodes.includes(status.language as any)
|
||||||
|
&& !userSettings.value.disabledTranslationLanguages.includes(status.language)
|
||||||
|
const enabled = /*! !useRuntimeConfig().public.translateApi && */ shouldTranslate
|
||||||
|
|
||||||
async function toggle() {
|
async function toggle() {
|
||||||
if (!('language' in status))
|
if (!shouldTranslate)
|
||||||
return
|
return
|
||||||
|
|
||||||
if (!translation.text) {
|
if (!translation.text) {
|
||||||
|
@ -79,7 +120,7 @@ export function useTranslation(status: mastodon.v1.Status | mastodon.v1.StatusEd
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enabled: !!useRuntimeConfig().public.translateApi,
|
enabled,
|
||||||
toggle,
|
toggle,
|
||||||
translation,
|
translation,
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ export interface UserSettings {
|
||||||
colorMode?: ColorMode
|
colorMode?: ColorMode
|
||||||
fontSize: FontSize
|
fontSize: FontSize
|
||||||
language: string
|
language: string
|
||||||
|
disabledTranslationLanguages: string[]
|
||||||
zenMode: boolean
|
zenMode: boolean
|
||||||
themeColors?: ThemeColors
|
themeColors?: ThemeColors
|
||||||
}
|
}
|
||||||
|
@ -56,6 +57,7 @@ export function getDefaultUserSettings(locales: string[]): UserSettings {
|
||||||
return {
|
return {
|
||||||
language: getDefaultLanguage(locales),
|
language: getDefaultLanguage(locales),
|
||||||
fontSize: DEFAULT_FONT_SIZE,
|
fontSize: DEFAULT_FONT_SIZE,
|
||||||
|
disabledTranslationLanguages: [],
|
||||||
zenMode: false,
|
zenMode: false,
|
||||||
preferences: {},
|
preferences: {},
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,7 +270,14 @@
|
||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"display_language": "Anzeigesprache",
|
"display_language": "Anzeigesprache",
|
||||||
"label": "Sprache"
|
"label": "Sprache",
|
||||||
|
"translations": {
|
||||||
|
"add": "Hinzufügen",
|
||||||
|
"choose_language": "Sprache wählen",
|
||||||
|
"heading": "Übersetzungen",
|
||||||
|
"hide_specific": "Bestimmte Übersetzungen ausblenden",
|
||||||
|
"remove": "Entfernen"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"label": "Benachrichtigungen",
|
"label": "Benachrichtigungen",
|
||||||
|
@ -328,7 +335,7 @@
|
||||||
"hide_boost_count": "Boost-Zähler ausblenden",
|
"hide_boost_count": "Boost-Zähler ausblenden",
|
||||||
"hide_favorite_count": "Favoritenzahl ausblenden",
|
"hide_favorite_count": "Favoritenzahl ausblenden",
|
||||||
"hide_follower_count": "Anzahl der Follower ausblenden",
|
"hide_follower_count": "Anzahl der Follower ausblenden",
|
||||||
"hide_translation": "Übersetzungen ausblenden",
|
"hide_translation": "Übersetzungen komplett ausblenden",
|
||||||
"label": "Einstellungen",
|
"label": "Einstellungen",
|
||||||
"title": "Experimentelle Funktionen",
|
"title": "Experimentelle Funktionen",
|
||||||
"user_picker": "Benutzerauswahl",
|
"user_picker": "Benutzerauswahl",
|
||||||
|
|
|
@ -316,7 +316,14 @@
|
||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"display_language": "Display Language",
|
"display_language": "Display Language",
|
||||||
"label": "Language"
|
"label": "Language",
|
||||||
|
"translations": {
|
||||||
|
"add": "Add",
|
||||||
|
"choose_language": "Choose language",
|
||||||
|
"heading": "Translations",
|
||||||
|
"hide_specific": "Hide specific translations",
|
||||||
|
"remove": "Remove"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"label": "Notifications",
|
"label": "Notifications",
|
||||||
|
|
|
@ -18,6 +18,10 @@ useHeadFixed({
|
||||||
<p font-medium>{{ $t('settings.language.display_language') }}</p>
|
<p font-medium>{{ $t('settings.language.display_language') }}</p>
|
||||||
<SettingsLanguage select-settings />
|
<SettingsLanguage select-settings />
|
||||||
</label>
|
</label>
|
||||||
|
<h2 py4 mt2 font-bold text-xl flex="~ gap-1" items-center>
|
||||||
|
{{ $t('settings.language.translations.heading') }}
|
||||||
|
</h2>
|
||||||
|
<SettingsTranslations />
|
||||||
</div>
|
</div>
|
||||||
</MainContent>
|
</MainContent>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -39,12 +39,6 @@ const userSettings = useUserSettings()
|
||||||
>
|
>
|
||||||
{{ $t('settings.preferences.hide_follower_count') }}
|
{{ $t('settings.preferences.hide_follower_count') }}
|
||||||
</SettingsToggleItem>
|
</SettingsToggleItem>
|
||||||
<SettingsToggleItem
|
|
||||||
:checked="getPreferences(userSettings, 'hideTranslation')"
|
|
||||||
@click="togglePreferences('hideTranslation')"
|
|
||||||
>
|
|
||||||
{{ $t('settings.preferences.hide_translation') }}
|
|
||||||
</SettingsToggleItem>
|
|
||||||
<SettingsToggleItem
|
<SettingsToggleItem
|
||||||
:checked="getPreferences(userSettings, 'hideAccountHoverCard')"
|
:checked="getPreferences(userSettings, 'hideAccountHoverCard')"
|
||||||
@click="togglePreferences('hideAccountHoverCard')"
|
@click="togglePreferences('hideAccountHoverCard')"
|
||||||
|
|
Loading…
Reference in New Issue