add translation options

This commit is contained in:
FloatingGhost 2022-08-29 20:02:34 +01:00 committed by Sam Therapy
parent c48c23f406
commit 91703a2eee
Signed by: sam
GPG Key ID: 4D8B07C18F31ACBD
10 changed files with 235 additions and 190 deletions

View File

@ -258,6 +258,7 @@ const getNodeInfo = async ({ store }) => {
store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') })
store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
store.dispatch('setInstanceOption', { name: 'translationEnabled', value: features.includes('akkoma:machine_translation') })
const uploadLimits = metadata.uploadLimits
store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) })

View File

@ -67,6 +67,12 @@ const ExtraButtons = {
hideDeleteStatusConfirmDialog () {
this.showingDeleteDialog = false
},
translateStatus () {
this.$store.dispatch('translateStatus', { id: this.status.id, language: this.$store.state.instance.interfaceLanguage })
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
},
pinStatus () {
this.$store.dispatch('pinStatus', this.status.id)
.then(() => this.$emit('onSuccess'))
@ -140,6 +146,9 @@ const ExtraButtons = {
canMute () {
return !!this.currentUser
},
canTranslate () {
return this.$store.state.instance.translationEnabled === true
},
canBookmark () {
return !!this.currentUser
},

View File

@ -142,6 +142,17 @@
:icon="['far', 'flag']"
/><span>{{ $t("user_card.report") }}</span>
</button>
<button
v-if="canTranslate"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="translateStatus"
@click="close"
>
<FAIcon
fixed-width
icon="globe"
/><span>{{ $t("status.translate") }}</span>
</button>
</div>
</template>
<template #trigger>

View File

@ -4,6 +4,13 @@
display: flex;
flex-direction: column;
.translation {
border: 1px solid var(--accent, $fallback--link);
border-radius: var(--panelRadius, $fallback--panelRadius);
margin-top: 1em;
padding: 0.5em;
}
.emoji {
--_still_image-label-scale: 0.5;
}

View File

@ -52,6 +52,23 @@
:attentions="status.attentions"
@parseReady="onParseReady"
/>
<div
v-if="status.translation"
class="translation"
>
<h4>{{ $t('status.translated_from', { language: status.translation.detected_language }) }}</h4>
<RichContent
:class="{ '-single-line': singleLine }"
class="text media-body"
:html="status.translation.text"
:emoji="status.emojis"
:handle-links="true"
:mfm="renderMisskeyMarkdown && (status.media_type === 'text/x.misskeymarkdown')"
:greentext="mergedConfig.greentext"
:attentions="status.attentions"
@parseReady="onParseReady"
/>
</div>
<button
v-show="hideSubjectStatus"

View File

@ -901,6 +901,8 @@
"unpin": "Unpin from profile",
"pinned": "Pinned",
"bookmark": "Bookmark",
"translate": "Translate",
"translated_from": "Translated from {language}",
"unbookmark": "Unbookmark",
"delete_confirm": "Do you really want to delete this status?",
"delete_confirm_title": "Delete confirmation",

View File

@ -603,199 +603,179 @@
"backup_settings": "設定をファイルにバックアップする",
"backup_restore": "設定をバックアップ"
},
"save": "変更を保存",
"hide_shoutbox": "Shoutboxを表示しない",
"always_show_post_button": "投稿ボタンを常に表示",
"right_sidebar": "サイドバーを右に表示"
},
"time": {
"day": "{0}日",
"days": "{0}日",
"day_short": "{0}日",
"days_short": "{0}日",
"hour": "{0}時間",
"hours": "{0}時間",
"hour_short": "{0}時間",
"hours_short": "{0}時間",
"in_future": "{0}で",
"in_past": "{0}前",
"minute": "{0}分",
"minutes": "{0}分",
"minute_short": "{0}分",
"minutes_short": "{0}分",
"month": "{0}ヶ月前",
"months": "{0}ヶ月前",
"month_short": "{0}ヶ月前",
"months_short": "{0}ヶ月前",
"now": "たった今",
"now_short": "たった今",
"second": "{0}秒",
"seconds": "{0}秒",
"second_short": "{0}秒",
"seconds_short": "{0}秒",
"week": "{0}週間",
"weeks": "{0}週間",
"week_short": "{0}週間",
"weeks_short": "{0}週間",
"year": "{0}年",
"years": "{0}年",
"year_short": "{0}年",
"years_short": "{0}年"
},
"timeline": {
"collapse": "たたむ",
"conversation": "スレッド",
"error_fetching": "読み込みがエラーになりました",
"load_older": "古いステータス",
"no_retweet_hint": "投稿を「フォロワーのみ」または「ダイレクト」にすると、リピートできなくなります",
"repeated": "リピート",
"show_new": "読み込み",
"up_to_date": "最新",
"no_more_statuses": "これで終わりです",
"no_statuses": "ステータスはありません",
"reload": "再読み込み",
"error": "タイムラインの読み込みに失敗しました: {0}",
"socket_reconnected": "リアルタイム接続が確立されました",
"socket_broke": "コード{0}によりリアルタイム接続が切断されました"
},
"status": {
"favorites": "お気に入り",
"repeats": "リピート",
"delete": "ステータスを削除",
"pin": "プロフィールにピン留め",
"unpin": "プロフィールのピン留めを外す",
"pinned": "ピン留め",
"delete_confirm": "本当にこのステータスを削除してもよろしいですか?",
"reply_to": "返信",
"replies_list": "返信:",
"mute_conversation": "スレッドをミュート",
"unmute_conversation": "スレッドのミュートを解除",
"nsfw": "閲覧注意",
"expand": "広げる",
"status_deleted": "この投稿は削除されました",
"hide_content": "隠す",
"show_content": "見る",
"hide_full_subject": "隠す",
"show_full_subject": "全部見る",
"thread_muted_and_words": "以下の単語を含むため:",
"thread_muted": "ミュートされたスレッド",
"external_source": "外部ソース",
"copy_link": "リンクをコピー",
"status_unavailable": "利用できません",
"unbookmark": "ブックマーク解除",
"bookmark": "ブックマーク",
"mentions": "メンション",
"you": "(あなた)",
"plus_more": "ほか{number}件"
},
"user_card": {
"approve": "受け入れ",
"block": "ブロック",
"blocked": "ブロックしています!",
"deny": "お断り",
"favorites": "お気に入り",
"follow": "フォロー",
"follow_sent": "リクエストを送りました!",
"follow_progress": "リクエストしています…",
"follow_unfollow": "フォローをやめる",
"followees": "フォロー",
"followers": "フォロワー",
"following": "フォローしています!",
"follows_you": "フォローされました!",
"its_you": "これはあなたです!",
"media": "メディア",
"mention": "メンション",
"mute": "ミュート",
"muted": "ミュートしています",
"per_day": "/日",
"remote_follow": "リモートフォロー",
"report": "通報",
"statuses": "ステータス",
"subscribe": "購読",
"unsubscribe": "購読を解除",
"unblock": "ブロック解除",
"unblock_progress": "ブロックを解除しています…",
"block_progress": "ブロックしています…",
"unmute": "ミュート解除",
"unmute_progress": "ミュートを解除しています…",
"mute_progress": "ミュートしています…",
"admin_menu": {
"moderation": "モデレーション",
"grant_admin": "管理者権限を付与",
"revoke_admin": "管理者権限を解除",
"grant_moderator": "モデレーター権限を付与",
"revoke_moderator": "モデレーター権限を解除",
"activate_account": "アカウントをアクティブにする",
"deactivate_account": "アカウントをアクティブでなくする",
"delete_account": "アカウントを削除",
"force_nsfw": "すべての投稿をNSFWにする",
"strip_media": "投稿からメディアを除去する",
"force_unlisted": "投稿を未収載にする",
"sandbox": "投稿をフォロワーのみにする",
"disable_remote_subscription": "他のインスタンスからフォローされないようにする",
"disable_any_subscription": "フォローされないようにする",
"quarantine": "他のインスタンスからの投稿を止める",
"delete_user": "ユーザーを削除"
"status": {
"bookmark": "ブックマーク",
"copy_link": "リンクをコピー",
"delete": "ステータスを削除",
"delete_confirm": "本当にこのステータスを削除してもよろしいですか?",
"expand": "広げる",
"external_source": "外部ソース",
"favorites": "お気に入り",
"hide_content": "隠す",
"hide_full_subject": "隠す",
"mentions": "メンション",
"mute_conversation": "スレッドをミュート",
"nsfw": "閲覧注意",
"pin": "プロフィールにピン留め",
"pinned": "ピン留め",
"plus_more": "ほか{number}件",
"repeats": "リピート",
"replies_list": "返信:",
"reply_to": "返信",
"show_content": "見る",
"show_full_subject": "全部見る",
"status_deleted": "この投稿は削除されました",
"status_unavailable": "利用できません",
"thread_muted": "ミュートされたスレッド",
"thread_muted_and_words": "以下の単語を含むため:",
"unbookmark": "ブックマーク解除",
"unmute_conversation": "スレッドのミュートを解除",
"unpin": "プロフィールのピン留めを外す",
"you": "(あなた)"
},
"roles": {
"moderator": "モデレーター",
"admin": "管理者"
"time": {
"in_future": "{0}で",
"in_past": "{0}前",
"now": "たった今",
"now_short": "たった今",
"unit": {
"days": "{0}日",
"days_short": "{0}日",
"hours": "{0}時間",
"hours_short": "{0}時間",
"minutes": "{0}分",
"minutes_short": "{0}分",
"months": "{0}ヶ月",
"months_short": "{0}ヶ月",
"seconds": "{0}秒",
"seconds_short": "{0}秒",
"weeks": "{0}週間",
"weeks_short": "{0}週間",
"years": "{0}年",
"years_short": "{0}年"
}
},
"show_repeats": "リピートを見る",
"hide_repeats": "リピートを隠す",
"message": "メッセージ",
"hidden": "隠す",
"bot": "bot",
"highlight": {
"solid": "背景を単色にする",
"striped": "背景を縞模様にする",
"side": "端に線を付ける",
"disabled": "強調しない"
"timeline": {
"collapse": "たたむ",
"conversation": "スレッド",
"error": "タイムラインの読み込みに失敗しました: {0}",
"load_older": "古いステータス",
"no_more_statuses": "これで終わりです",
"no_retweet_hint": "投稿を「フォロワーのみ」または「ダイレクト」にすると、リピートできなくなります",
"no_statuses": "ステータスはありません",
"reload": "再読み込み",
"repeated": "リピート",
"show_new": "読み込み",
"socket_broke": "コード{0}によりリアルタイム接続が切断されました",
"socket_reconnected": "リアルタイム接続が確立されました",
"up_to_date": "最新"
},
"edit_profile": "プロフィールを編集"
},
"user_profile": {
"timeline_title": "ユーザータイムライン",
"profile_does_not_exist": "申し訳ない。このプロフィールは存在しません。",
"profile_loading_error": "申し訳ない。プロフィールの読み込みがエラーになりました。"
},
"user_reporting": {
"title": "通報する: {0}",
"add_comment_description": "この通報は、あなたのインスタンスのモデレーターに送られます。このアカウントを通報する理由を説明することができます:",
"additional_comments": "追加のコメント",
"forward_description": "このアカウントは他のサーバーに置かれています。この通報のコピーをリモートのサーバーに送りますか?",
"forward_to": "転送する: {0}",
"submit": "送信",
"generic_error": "あなたのリクエストを処理しようとしましたが、エラーになりました。"
},
"who_to_follow": {
"more": "詳細",
"who_to_follow": "おすすめユーザー"
},
"tool_tip": {
"media_upload": "メディアをアップロード",
"repeat": "リピート",
"reply": "返信",
"favorite": "お気に入り",
"user_settings": "ユーザー設定",
"bookmark": "ブックマーク",
"reject_follow_request": "フォローリクエストを拒否",
"accept_follow_request": "フォローリクエストを許可",
"add_reaction": "リアクションを追加"
},
"upload": {
"error": {
"base": "アップロードに失敗しました。",
"file_too_big": "ファイルが大きすぎます [{filesize} {filesizeunit} / {allowedsize} {allowedsizeunit}]",
"default": "しばらくしてから試してください",
"message": "アップロードに失敗: {0}"
"tool_tip": {
"accept_follow_request": "フォローリクエストを許可",
"add_reaction": "リアクションを追加",
"bookmark": "ブックマーク",
"favorite": "お気に入り",
"media_upload": "メディアをアップロード",
"reject_follow_request": "フォローリクエストを拒否",
"repeat": "リピート",
"reply": "返信",
"user_settings": "ユーザー設定"
},
"file_size_units": {
"B": "B",
"KiB": "KiB",
"MiB": "MiB",
"GiB": "GiB",
"TiB": "TiB"
"upload": {
"error": {
"base": "アップロードに失敗しました。",
"default": "しばらくしてから試してください",
"file_too_big": "ファイルが大きすぎます [{filesize} {filesizeunit} / {allowedsize} {allowedsizeunit}]",
"message": "アップロードに失敗: {0}"
},
"file_size_units": {
"B": "B",
"GiB": "GiB",
"KiB": "KiB",
"MiB": "MiB",
"TiB": "TiB"
}
},
"user_card": {
"admin_menu": {
"activate_account": "アカウントをアクティブにする",
"deactivate_account": "アカウントをアクティブでなくする",
"delete_account": "アカウントを削除",
"delete_user": "ユーザーを削除",
"disable_any_subscription": "フォローされないようにする",
"disable_remote_subscription": "他のインスタンスからフォローされないようにする",
"force_nsfw": "すべての投稿をNSFWにする",
"force_unlisted": "投稿を未収載にする",
"grant_admin": "管理者権限を付与",
"grant_moderator": "モデレーター権限を付与",
"moderation": "モデレーション",
"quarantine": "他のインスタンスからの投稿を止める",
"revoke_admin": "管理者権限を解除",
"revoke_moderator": "モデレーター権限を解除",
"sandbox": "投稿をフォロワーのみにする",
"strip_media": "投稿からメディアを除去する"
},
"approve": "受け入れ",
"block": "ブロック",
"block_progress": "ブロックしています…",
"blocked": "ブロックしています!",
"bot": "bot",
"deny": "お断り",
"edit_profile": "プロフィールを編集",
"favorites": "お気に入り",
"follow": "フォロー",
"follow_progress": "リクエストしています…",
"follow_sent": "リクエストを送りました!",
"follow_unfollow": "フォローをやめる",
"followees": "フォロー",
"followers": "フォロワー",
"following": "フォローしています!",
"follows_you": "フォローされました!",
"hidden": "隠す",
"hide_repeats": "リピートを隠す",
"highlight": {
"disabled": "強調しない",
"side": "端に線を付ける",
"solid": "背景を単色にする",
"striped": "背景を縞模様にする"
},
"its_you": "これはあなたです!",
"media": "メディア",
"mention": "メンション",
"message": "メッセージ",
"mute": "ミュート",
"mute_progress": "ミュートしています…",
"muted": "ミュートしています",
"note": "私的なメモ",
"per_day": "/日",
"remote_follow": "リモートフォロー",
"report": "通報",
"show_repeats": "リピートを見る",
"statuses": "ステータス",
"subscribe": "購読",
"unblock": "ブロック解除",
"unblock_progress": "ブロックを解除しています…",
"unmute": "ミュート解除",
"unmute_progress": "ミュートを解除しています…",
"unsubscribe": "購読を解除"
},
"user_profile": {
"profile_does_not_exist": "申し訳ない。このプロフィールは存在しません。",
"profile_loading_error": "申し訳ない。プロフィールの読み込みがエラーになりました。",
"timeline_title": "ユーザータイムライン"
},
"user_reporting": {
"add_comment_description": "この通報は、あなたのインスタンスのモデレーターに送られます。このアカウントを通報する理由を説明することができます:",
"additional_comments": "追加のコメント",
"forward_description": "このアカウントは他のサーバーに置かれています。この通報のコピーをリモートのサーバーに送りますか?",
"forward_to": "転送する: {0}",
"generic_error": "あなたのリクエストを処理しようとしましたが、エラーになりました。",
"submit": "送信",
"title": "通報する: {0}"
},
"who_to_follow": {
"more": "詳細",
"who_to_follow": "おすすめユーザー"
}
},
"search": {

View File

@ -205,6 +205,7 @@ const config = {
BACKEND_LANGUAGE_COOKIE_NAME,
localeService.internalToBackendLocaleMulti(value)
)
dispatch('setInstanceOption', { name: 'interfaceLanguage', value })
break
case 'thirdColumnMode':
dispatch('setLayoutWidth', undefined)

View File

@ -433,6 +433,10 @@ export const mutations = {
state.conversationsObject[newStatus.statusnet_conversation_id].forEach(status => { status.thread_muted = newStatus.thread_muted })
}
},
setTranslatedStatus (state, { id, translation }) {
const newStatus = state.allStatusesObject[id]
newStatus.translation = translation
},
setRetweeted (state, { status, value }) {
const newStatus = state.allStatusesObject[status.id]
@ -651,6 +655,10 @@ const statuses = {
rootState.api.backendInteractor.unpinOwnStatus({ id: statusId })
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
},
translateStatus ({ rootState, commit }, { id, translation, language }) {
return rootState.api.backendInteractor.translateStatus({ id: id, translation, language })
.then((translation) => commit('setTranslatedStatus', { id, translation }))
},
muteConversation ({ rootState, commit }, statusId) {
return rootState.api.backendInteractor.muteConversation({ id: statusId })
.then((status) => commit('setMutedStatus', status))

View File

@ -31,6 +31,7 @@ const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
const MASTODON_USER_NOTIFICATIONS_URL = '/api/v1/notifications'
const AKKOMA_TRANSLATE_URL = (id, lang) => `/api/v1/statuses/${id}/translations/${lang}`
const MASTODON_DISMISS_NOTIFICATION_URL = id => `/api/v1/notifications/${id}/dismiss`
const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite`
const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite`
@ -799,6 +800,13 @@ const unretweet = ({ id, credentials }) => {
.then((data) => parseStatus(data))
}
const translateStatus = ({ id, credentials, language }) => {
return promisedRequest({ url: AKKOMA_TRANSLATE_URL(id, language), method: 'GET', credentials })
.then((data) => {
return data
})
}
const bookmarkStatus = ({ id, credentials }) => {
return promisedRequest({
url: MASTODON_BOOKMARK_STATUS_URL(id),
@ -1786,7 +1794,8 @@ const apiService = {
postAnnouncement,
editAnnouncement,
deleteAnnouncement,
adminFetchAnnouncements
adminFetchAnnouncements,
translateStatus
}
export default apiService