diff --git a/CHANGELOG.md b/CHANGELOG.md index eb6a45999057b625582958d53ccfbe74ef0fbb34..cef6d4013395620ffdccfa21d050a241d9223a2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Added a quick settings to timeline header for easier access - Added option to mark posts as sensitive by default +- Added quick filters for notifications + ## [2.3.0] - 2021-03-01 ### Fixed @@ -20,6 +22,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - Display 'people voted' instead of 'votes' for multi-choice polls +- Changed the "Timelines" link in side panel to toggle show all timeline options inside the panel +- Renamed "Timeline" to "Home Timeline" to be more clear - Optimized chat to not get horrible performance after keeping the same chat open for a long time - When opening emoji picker or react picker, it automatically focuses the search field - Language picker now uses native language names @@ -28,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Added reason field for registration when approval is required - Group staff members by role in the About page + ## [2.2.3] - 2021-01-18 ### Added - Added Report button to status ellipsis menu for easier reporting @@ -35,6 +40,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Follows/Followers tabs on user profiles now display the content properly. - Handle punycode in screen names +- Fixed local dev mode having non-functional websockets in some cases +- Show notices for websocket events (errors, abnormal closures, reconnections) +- Fix not being able to re-enable websocket until page refresh +- Fix annoying issue where timeline might have few posts when streaming is enabled ### Changed - Don't filter own posts when they hit your wordfilter diff --git a/config/index.js b/config/index.js index ccec419654c914ad52070c312a55f24614f48a78..7cb87c3b368cee12f95eb32d0fc03c65707a5997 100644 --- a/config/index.js +++ b/config/index.js @@ -3,6 +3,11 @@ const path = require('path') let settings = {} try { settings = require('./local.json') + if (settings.target && settings.target.endsWith('/')) { + // replacing trailing slash since it can conflict with some apis + // and that's how actual BE reports its url + settings.target = settings.target.replace(/\/$/, '') + } console.log('Using local dev server settings (/config/local.json):') console.log(JSON.stringify(settings, null, 2)) } catch (e) { diff --git a/src/App.scss b/src/App.scss index a537baed1324235146c944d6905f8a522ee85ba6..45071ba2a4bd58fbc569c0424998ee1f7a0c86a3 100644 --- a/src/App.scss +++ b/src/App.scss @@ -511,9 +511,21 @@ main-router { border-radius: var(--panelRadius, $fallback--panelRadius); } -.panel-footer { +/* TODO Should remove timeline-footer from here when we refactor panels into + * separate component and utilize slots + */ +.panel-footer, .timeline-footer { + display: flex; border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); + flex: none; + padding: 0.6em 0.6em; + text-align: left; + line-height: 28px; + align-items: baseline; + border-width: 1px 0 0 0; + border-style: solid; + border-color: var(--border, $fallback--border); .faint { color: $fallback--faint; @@ -670,6 +682,15 @@ nav { color: var(--alertWarningPanelText, $fallback--text); } } + + &.success { + background-color: var(--alertSuccess, $fallback--alertWarning); + color: var(--alertSuccessText, $fallback--text); + + .panel-heading & { + color: var(--alertSuccessPanelText, $fallback--text); + } + } } .faint { @@ -819,16 +840,10 @@ nav { } .new-status-notification { - position:relative; - margin-top: -1px; + position: relative; font-size: 1.1em; - border-width: 1px 0 0 0; - border-style: solid; - border-color: var(--border, $fallback--border); - padding: 10px; z-index: 1; - background-color: $fallback--fg; - background-color: var(--panel, $fallback--fg); + flex: 1; } .chat-layout { diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue deleted file mode 100644 index 8ffe34f8fb7c16e9413c569b7a86f979723dfc29..0000000000000000000000000000000000000000 --- a/src/components/export_import/export_import.vue +++ /dev/null @@ -1,102 +0,0 @@ -<template> - <div class="import-export-container"> - <slot name="before" /> - <button - class="btn button-default" - @click="exportData" - > - {{ exportLabel }} - </button> - <button - class="btn button-default" - @click="importData" - > - {{ importLabel }} - </button> - <slot name="afterButtons" /> - <p - v-if="importFailed" - class="alert error" - > - {{ importFailedText }} - </p> - <slot name="afterError" /> - </div> -</template> - -<script> -export default { - props: [ - 'exportObject', - 'importLabel', - 'exportLabel', - 'importFailedText', - 'validator', - 'onImport', - 'onImportFailure' - ], - data () { - return { - importFailed: false - } - }, - methods: { - exportData () { - const stringified = JSON.stringify(this.exportObject, null, 2) // Pretty-print and indent with 2 spaces - - // Create an invisible link with a data url and simulate a click - const e = document.createElement('a') - e.setAttribute('download', 'pleroma_theme.json') - e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified)) - e.style.display = 'none' - - document.body.appendChild(e) - e.click() - document.body.removeChild(e) - }, - importData () { - this.importFailed = false - const filePicker = document.createElement('input') - filePicker.setAttribute('type', 'file') - filePicker.setAttribute('accept', '.json') - - filePicker.addEventListener('change', event => { - if (event.target.files[0]) { - // eslint-disable-next-line no-undef - const reader = new FileReader() - reader.onload = ({ target }) => { - try { - const parsed = JSON.parse(target.result) - const valid = this.validator(parsed) - if (valid) { - this.onImport(parsed) - } else { - this.importFailed = true - // this.onImportFailure(valid) - } - } catch (e) { - // This will happen both if there is a JSON syntax error or the theme is missing components - this.importFailed = true - // this.onImportFailure(e) - } - } - reader.readAsText(event.target.files[0]) - } - }) - - document.body.appendChild(filePicker) - filePicker.click() - document.body.removeChild(filePicker) - } - } -} -</script> - -<style lang="scss"> -.import-export-container { - display: flex; - flex-wrap: wrap; - align-items: baseline; - justify-content: center; -} -</style> diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue index 049e23dbb01033225c2ba9e0627253d8bf14a70a..a45f4586518c71ca584ca7e6013544a5b382205f 100644 --- a/src/components/global_notice_list/global_notice_list.vue +++ b/src/components/global_notice_list/global_notice_list.vue @@ -71,6 +71,14 @@ } } + .global-success { + background-color: var(--alertPopupSuccess, $fallback--cGreen); + color: var(--alertPopupSuccessText, $fallback--text); + .svg-inline--fa { + color: var(--alertPopupSuccessText, $fallback--text); + } + } + .global-info { background-color: var(--alertPopupNeutral, $fallback--fg); color: var(--alertPopupNeutralText, $fallback--text); diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js index d4fdc53e3c1fcb29107b8c8828d0b91902e06ed9..2469327afea4d73fbeb9136a648b94d5690a1118 100644 --- a/src/components/moderation_tools/moderation_tools.js +++ b/src/components/moderation_tools/moderation_tools.js @@ -1,6 +1,11 @@ +import { library } from '@fortawesome/fontawesome-svg-core' +import { faChevronDown } from '@fortawesome/free-solid-svg-icons' + import DialogModal from '../dialog_modal/dialog_modal.vue' import Popover from '../popover/popover.vue' +library.add(faChevronDown) + const FORCE_NSFW = 'mrf_tag:media-force-nsfw' const STRIP_MEDIA = 'mrf_tag:media-strip' const FORCE_UNLISTED = 'mrf_tag:force-unlisted' diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue index bf25adc533aa7506f7a40c094a7a5e59648ed5ce..c4c6ee4629ee631897128e85aada8ea125f102d5 100644 --- a/src/components/moderation_tools/moderation_tools.vue +++ b/src/components/moderation_tools/moderation_tools.vue @@ -124,10 +124,11 @@ </div> <button slot="trigger" - class="btn button-default btn-block" + class="btn button-default btn-block moderation-tools-button" :class="{ toggled }" > {{ $t('user_card.admin_menu.moderation') }} + <FAIcon icon="chevron-down" /> </button> </Popover> <portal to="modal"> @@ -170,4 +171,10 @@ height: 100%; } } + +.moderation-tools-button { + svg,i { + font-size: 0.8em; + } +} </style> diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index 81d49cc26016d5af7d9690058770b68a17bc911e..37bcb4095b3366a1b169459b74618f93deca0e5f 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -1,4 +1,4 @@ -import { timelineNames } from '../timeline_menu/timeline_menu.js' +import TimelineMenuContent from '../timeline_menu/timeline_menu_content.vue' import { mapState, mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' @@ -7,10 +7,12 @@ import { faGlobe, faBookmark, faEnvelope, - faHome, + faChevronDown, + faChevronUp, faComments, faBell, - faInfoCircle + faInfoCircle, + faStream } from '@fortawesome/free-solid-svg-icons' library.add( @@ -18,10 +20,12 @@ library.add( faGlobe, faBookmark, faEnvelope, - faHome, + faChevronDown, + faChevronUp, faComments, faBell, - faInfoCircle + faInfoCircle, + faStream ) const NavPanel = { @@ -30,16 +34,20 @@ const NavPanel = { this.$store.dispatch('startFetchingFollowRequests') } }, + components: { + TimelineMenuContent + }, + data () { + return { + showTimelines: false + } + }, + methods: { + toggleTimelines () { + this.showTimelines = !this.showTimelines + } + }, computed: { - onTimelineRoute () { - return !!timelineNames()[this.$route.name] - }, - timelinesRoute () { - if (this.$store.state.interface.lastTimeline) { - return this.$store.state.interface.lastTimeline - } - return this.currentUser ? 'friends' : 'public-timeline' - }, ...mapState({ currentUser: state => state.users.currentUser, followRequestCount: state => state.api.followRequests.length, diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 0c83d0fe2c5119c7085f0c36a677c0133c3fab99..7ae7b1d6eda7fb99ca09b098f66ed2c43fa01204 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -3,19 +3,33 @@ <div class="panel panel-default"> <ul> <li v-if="currentUser || !privateMode"> - <router-link - :to="{ name: timelinesRoute }" - :class="onTimelineRoute && 'router-link-active'" + <button + class="button-unstyled menu-item" + @click="toggleTimelines" > <FAIcon fixed-width class="fa-scale-110" - icon="home" + icon="stream" />{{ $t("nav.timelines") }} - </router-link> + <FAIcon + class="timelines-chevron" + fixed-width + :icon="showTimelines ? 'chevron-up' : 'chevron-down'" + /> + </button> + <div + v-show="showTimelines" + class="timelines-background" + > + <TimelineMenuContent class="timelines" /> + </div> </li> <li v-if="currentUser"> - <router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }"> + <router-link + class="menu-item" + :to="{ name: 'interactions', params: { username: currentUser.screen_name } }" + > <FAIcon fixed-width class="fa-scale-110" @@ -24,7 +38,10 @@ </router-link> </li> <li v-if="currentUser && pleromaChatMessagesAvailable"> - <router-link :to="{ name: 'chats', params: { username: currentUser.screen_name } }"> + <router-link + class="menu-item" + :to="{ name: 'chats', params: { username: currentUser.screen_name } }" + > <div v-if="unreadChatCount" class="badge badge-notification" @@ -39,7 +56,10 @@ </router-link> </li> <li v-if="currentUser && currentUser.locked"> - <router-link :to="{ name: 'friend-requests' }"> + <router-link + class="menu-item" + :to="{ name: 'friend-requests' }" + > <FAIcon fixed-width class="fa-scale-110" @@ -54,7 +74,10 @@ </router-link> </li> <li> - <router-link :to="{ name: 'about' }"> + <router-link + class="menu-item" + :to="{ name: 'about' }" + > <FAIcon fixed-width class="fa-scale-110" @@ -91,14 +114,14 @@ border-color: var(--border, $fallback--border); padding: 0; - &:first-child a { + &:first-child .menu-item { border-top-right-radius: $fallback--panelRadius; border-top-right-radius: var(--panelRadius, $fallback--panelRadius); border-top-left-radius: $fallback--panelRadius; border-top-left-radius: var(--panelRadius, $fallback--panelRadius); } - &:last-child a { + &:last-child .menu-item { border-bottom-right-radius: $fallback--panelRadius; border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius); border-bottom-left-radius: $fallback--panelRadius; @@ -110,13 +133,15 @@ border: none; } - a { + .menu-item { display: block; box-sizing: border-box; - align-items: stretch; height: 3.5em; line-height: 3.5em; padding: 0 1em; + width: 100%; + color: $fallback--link; + color: var(--link, $fallback--link); &:hover { background-color: $fallback--lightBg; @@ -146,6 +171,25 @@ } } + .timelines-chevron { + margin-left: 0.8em; + font-size: 1.1em; + } + + .timelines-background { + padding: 0 0 0 0.6em; + background-color: $fallback--lightBg; + background-color: var(--selectedMenu, $fallback--lightBg); + border-top: 1px solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + } + + .timelines { + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); + } + .fa-scale-110 { margin-right: 0.8em; } diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue new file mode 100644 index 0000000000000000000000000000000000000000..e86a0fcc2f4dcd7b1dc91a80dbd5412afe24bd51 --- /dev/null +++ b/src/components/notifications/notification_filters.vue @@ -0,0 +1,122 @@ +<template> + <Popover + trigger="click" + class="NotificationFilters" + placement="bottom" + :bound-to="{ x: 'container' }" + > + <template + v-slot:content + > + <div class="dropdown-menu"> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('likes')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.likes }" + />{{ $t('settings.notification_visibility_likes') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('repeats')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.repeats }" + />{{ $t('settings.notification_visibility_repeats') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('follows')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.follows }" + />{{ $t('settings.notification_visibility_follows') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('mentions')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.mentions }" + />{{ $t('settings.notification_visibility_mentions') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('emojiReactions')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.emojiReactions }" + />{{ $t('settings.notification_visibility_emoji_reactions') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('moves')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.moves }" + />{{ $t('settings.notification_visibility_moves') }} + </button> + </div> + </template> + <template v-slot:trigger> + <FAIcon icon="filter" /> + </template> + </Popover> +</template> + +<script> +import Popover from '../popover/popover.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { faFilter } from '@fortawesome/free-solid-svg-icons' + +library.add( + faFilter +) + +export default { + components: { Popover }, + computed: { + filters () { + return this.$store.getters.mergedConfig.notificationVisibility + } + }, + methods: { + toggleNotificationFilter (type) { + this.$store.dispatch('setOption', { + name: 'notificationVisibility', + value: { + ...this.filters, + [type]: !this.filters[type] + } + }) + } + } +} +</script> + +<style lang="scss"> + +.NotificationFilters { + align-self: stretch; + + > button { + font-size: 1.2em; + padding-left: 0.7em; + padding-right: 0.2em; + line-height: 100%; + height: 100%; + } + + .dropdown-item { + margin: 0; + } +} + +</style> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 492585631d28599049d62bbb4c9a13a2a375b3dd..c8f1ebcbe2957192eac1c18a98f68e7aade11fed 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -1,5 +1,6 @@ import { mapGetters } from 'vuex' import Notification from '../notification/notification.vue' +import NotificationFilters from './notification_filters.vue' import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js' import { notificationsFromStore, @@ -17,6 +18,10 @@ library.add( const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30 const Notifications = { + components: { + Notification, + NotificationFilters + }, props: { // Disables display of panel header noHeading: Boolean, @@ -35,11 +40,6 @@ const Notifications = { seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT } }, - created () { - const store = this.$store - const credentials = store.state.users.currentUser.credentials - notificationsFetcher.fetchAndUpdate({ store, credentials }) - }, computed: { mainClass () { return this.minimalMode ? '' : 'panel panel-default' @@ -70,9 +70,6 @@ const Notifications = { }, ...mapGetters(['unreadChatCount']) }, - components: { - Notification - }, watch: { unseenCountTitle (count) { if (count > 0) { diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 682ae127d1748ae8ffa003d1e5cf77238429e17a..2bb627a8c92e6852ff08437263776104ee545cdf 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -1,6 +1,6 @@ @import '../../_variables.scss'; -.notifications { +.Notifications { &:not(.minimal) { // a bit of a hack to allow scrolling below notifications padding-bottom: 15em; @@ -11,6 +11,10 @@ color: var(--text, $fallback--text); } + .notifications-footer { + border: none; + } + .notification { position: relative; @@ -82,7 +86,6 @@ } } - .follow-text, .move-text { padding: 0.5em 0; overflow-wrap: break-word; diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index 725d1ad4edafc09be4861f6ea90f2c418f99d8bc..2ce5d56f7e3a4ae1c664a70130d6dee1c64a67f3 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -1,7 +1,7 @@ <template> <div :class="{ minimal: minimalMode }" - class="notifications" + class="Notifications" > <div :class="mainClass"> <div @@ -22,6 +22,7 @@ > {{ $t('notifications.read') }} </button> + <NotificationFilters /> </div> <div class="panel-body"> <div @@ -34,10 +35,10 @@ <notification :notification="notification" /> </div> </div> - <div class="panel-footer"> + <div class="panel-footer notifications-footer"> <div v-if="bottomedOut" - class="new-status-notification text-center panel-footer faint" + class="new-status-notification text-center faint" > {{ $t('notifications.no_more_notifications') }} </div> @@ -46,13 +47,13 @@ class="button-unstyled -link -fullwidth" @click.prevent="fetchOlderNotifications()" > - <div class="new-status-notification text-center panel-footer"> + <div class="new-status-notification text-center"> {{ minimalMode ? $t('interactions.load_older') : $t('notifications.load_older') }} </div> </button> <div v-else - class="new-status-notification text-center panel-footer" + class="new-status-notification text-center" > <FAIcon icon="circle-notch" diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue index a931cb5aedfb8a550cac3f84ebf2d8f629dd6886..3ffa5425cdfc01adb12defa81109aac759eaf6c9 100644 --- a/src/components/password_reset/password_reset.vue +++ b/src/components/password_reset/password_reset.vue @@ -53,7 +53,7 @@ type="submit" class="btn button-default btn-block" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> </div> diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index 391d6422d8987400c2c7982a57915f41d5550ac5..bc6cd8e73eaac035b2e319ef6e2b56aa4f11fd1b 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -56,6 +56,9 @@ const Popover = { // Popover will be anchored around this element, trigger ref is the container, so // its children are what are inside the slot. Expect only one slot="trigger". const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el + // SVGs don't have offsetWidth/Height, use fallback + const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth + const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight const screenBox = anchorEl.getBoundingClientRect() // Screen position of the origin point for popover const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top } @@ -114,11 +117,11 @@ const Popover = { const yOffset = (this.offset && this.offset.y) || 0 const translateY = usingTop - ? -anchorEl.offsetHeight + vPadding - yOffset - content.offsetHeight + ? -anchorHeight + vPadding - yOffset - content.offsetHeight : yOffset const xOffset = (this.offset && this.offset.x) || 0 - const translateX = (anchorEl.offsetWidth * 0.5) - content.offsetWidth * 0.5 + horizOffset + xOffset + const translateX = anchorWidth * 0.5 - content.offsetWidth * 0.5 + horizOffset + xOffset // Note, separate translateX and translateY avoids blurry text on chromium, // single translate or translate3d resulted in blurry text. diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index cc4f6251a668fcc10e383a0b2a5b6315c7c7b720..fbda41d6479fbd1a27d8839acbbf4de7892398ec 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -263,7 +263,7 @@ disabled class="btn button-default" > - {{ $t('general.submit') }} + {{ $t('post_status.post') }} </button> <!-- touchstart is used to keep the OSK at the same position after a message send --> <button @@ -273,7 +273,7 @@ @touchstart.stop.prevent="postStatus($event, newStatus)" @click.stop.prevent="postStatus($event, newStatus)" > - {{ $t('general.submit') }} + {{ $t('post_status.post') }} </button> </div> <div diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue index 062d4121e92ed027297944b33249249cbb353c4e..65b4bb33c9b6b0ec383fb4552b24f266c5b9c35a 100644 --- a/src/components/registration/registration.vue +++ b/src/components/registration/registration.vue @@ -230,7 +230,7 @@ type="submit" class="btn button-default" > - {{ $t('general.submit') }} + {{ $t('registration.register') }} </button> </div> </div> diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index f0d49c91587b4ac32f960815c577663154621715..0404348334859659f66454cfc4fc6d702c572b6a 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -2,10 +2,55 @@ import Modal from 'src/components/modal/modal.vue' import PanelLoading from 'src/components/panel_loading/panel_loading.vue' import AsyncComponentError from 'src/components/async_component_error/async_component_error.vue' import getResettableAsyncComponent from 'src/services/resettable_async_component.js' +import Popover from '../popover/popover.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { cloneDeep } from 'lodash' +import { + newImporter, + newExporter +} from 'src/services/export_import/export_import.js' +import { + faTimes, + faFileUpload, + faFileDownload, + faChevronDown +} from '@fortawesome/free-solid-svg-icons' +import { + faWindowMinimize +} from '@fortawesome/free-regular-svg-icons' + +const PLEROMAFE_SETTINGS_MAJOR_VERSION = 1 +const PLEROMAFE_SETTINGS_MINOR_VERSION = 0 + +library.add( + faTimes, + faWindowMinimize, + faFileUpload, + faFileDownload, + faChevronDown +) const SettingsModal = { + data () { + return { + dataImporter: newImporter({ + validator: this.importValidator, + onImport: this.onImport, + onImportFailure: this.onImportFailure + }), + dataThemeExporter: newExporter({ + filename: 'pleromafe_settings.full', + getExportedObject: () => this.generateExport(true) + }), + dataExporter: newExporter({ + filename: 'pleromafe_settings', + getExportedObject: () => this.generateExport() + }) + } + }, components: { Modal, + Popover, SettingsModalContent: getResettableAsyncComponent( () => import('./settings_modal_content.vue'), { @@ -21,6 +66,85 @@ const SettingsModal = { }, peekModal () { this.$store.dispatch('togglePeekSettingsModal') + }, + importValidator (data) { + if (!Array.isArray(data._pleroma_settings_version)) { + return { + messageKey: 'settings.file_import_export.invalid_file' + } + } + + const [major, minor] = data._pleroma_settings_version + + if (major > PLEROMAFE_SETTINGS_MAJOR_VERSION) { + return { + messageKey: 'settings.file_export_import.errors.file_too_new', + messageArgs: { + fileMajor: major, + feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION + } + } + } + + if (major < PLEROMAFE_SETTINGS_MAJOR_VERSION) { + return { + messageKey: 'settings.file_export_import.errors.file_too_old', + messageArgs: { + fileMajor: major, + feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION + } + } + } + + if (minor > PLEROMAFE_SETTINGS_MINOR_VERSION) { + this.$store.dispatch('pushGlobalNotice', { + level: 'warning', + messageKey: 'settings.file_export_import.errors.file_slightly_new' + }) + } + + return true + }, + onImportFailure (result) { + if (result.error) { + this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_settings_imported', level: 'error' }) + } else { + this.$store.dispatch('pushGlobalNotice', { ...result.validationResult, level: 'error' }) + } + }, + onImport (data) { + if (data) { this.$store.dispatch('loadSettings', data) } + }, + restore () { + this.dataImporter.importData() + }, + backup () { + this.dataExporter.exportData() + }, + backupWithTheme () { + this.dataThemeExporter.exportData() + }, + generateExport (theme = false) { + const { config } = this.$store.state + let sample = config + if (!theme) { + const ignoreList = new Set([ + 'customTheme', + 'customThemeSource', + 'colors' + ]) + sample = Object.fromEntries( + Object + .entries(sample) + .filter(([key]) => !ignoreList.has(key)) + ) + } + const clone = cloneDeep(sample) + clone._pleroma_settings_version = [ + PLEROMAFE_SETTINGS_MAJOR_VERSION, + PLEROMAFE_SETTINGS_MINOR_VERSION + ] + return clone } }, computed: { diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue index 552ca41f947a5b3ac2a2754f84815d598bbfc4c5..c7da54335829c8132133be175d386c61a374d28f 100644 --- a/src/components/settings_modal/settings_modal.vue +++ b/src/components/settings_modal/settings_modal.vue @@ -31,20 +31,86 @@ </transition> <button class="btn button-default" + :title="$t('general.peek')" @click="peekModal" > - {{ $t('general.peek') }} + <FAIcon + :icon="['far', 'window-minimize']" + fixed-width + /> </button> <button class="btn button-default" + :title="$t('general.close')" @click="closeModal" > - {{ $t('general.close') }} + <FAIcon + icon="times" + fixed-width + /> </button> </div> <div class="panel-body"> <SettingsModalContent v-if="modalOpenedOnce" /> </div> + <div class="panel-footer"> + <Popover + class="export" + trigger="click" + placement="top" + :offset="{ y: 5, x: 5 }" + :bound-to="{ x: 'container' }" + remove-padding + > + <button + slot="trigger" + class="btn button-default" + :title="$t('general.close')" + > + <span>{{ $t("settings.file_export_import.backup_restore") }}</span> + <FAIcon + icon="chevron-down" + /> + </button> + <div + slot="content" + slot-scope="{close}" + > + <div class="dropdown-menu"> + <button + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="backup" + @click="close" + > + <FAIcon + icon="file-download" + fixed-width + /><span>{{ $t("settings.file_export_import.backup_settings") }}</span> + </button> + <button + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="backupWithTheme" + @click="close" + > + <FAIcon + icon="file-download" + fixed-width + /><span>{{ $t("settings.file_export_import.backup_settings_theme") }}</span> + </button> + <button + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="restore" + @click="close" + > + <FAIcon + icon="file-upload" + fixed-width + /><span>{{ $t("settings.file_export_import.restore_settings") }}</span> + </button> + </div> + </div> + </Popover> + </div> </div> </Modal> </template> diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue index 8f8fe48e1a98ce2fb139981c9711f289d566c337..7e0568ead0a51204a62bdfce3de54cff69ecbd5d 100644 --- a/src/components/settings_modal/tabs/notifications_tab.vue +++ b/src/components/settings_modal/tabs/notifications_tab.vue @@ -24,7 +24,7 @@ class="btn button-default" @click="updateNotificationSettings" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> </div> diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue index 175a0219b213946b59b9cc8809992d624a0142fd..bb3c301d5c175415044f3474e9ee4502ee374c18 100644 --- a/src/components/settings_modal/tabs/profile_tab.vue +++ b/src/components/settings_modal/tabs/profile_tab.vue @@ -153,7 +153,7 @@ class="btn button-default" @click="updateProfile" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> <div class="setting-item"> @@ -227,7 +227,7 @@ class="btn button-default" @click="submitBanner(banner)" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> <div class="setting-item"> @@ -266,7 +266,7 @@ class="btn button-default" @click="submitBackground(background)" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> </div> diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.vue b/src/components/settings_modal/tabs/security_tab/security_tab.vue index 56bea1f466c9227d9da50f79e1a6e190182f39cd..275d46164b0e9e8ed88b46a5f6f2839b19799c47 100644 --- a/src/components/settings_modal/tabs/security_tab/security_tab.vue +++ b/src/components/settings_modal/tabs/security_tab/security_tab.vue @@ -22,7 +22,7 @@ class="btn button-default" @click="changeEmail" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> <p v-if="changedEmail"> {{ $t('settings.changed_email') }} @@ -60,7 +60,7 @@ class="btn button-default" @click="changePassword" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> <p v-if="changedPassword"> {{ $t('settings.changed_password') }} @@ -133,7 +133,7 @@ class="btn button-default" @click="confirmDelete" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> </div> diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js index 73068e19001e34c9181e75c08ec0fbf6e2f82a9e..1388f74b021f26fd3ad5b6502e60fc1d62ce83db 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.js +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.js @@ -15,6 +15,10 @@ import { shadows2to3, colors2to3 } from 'src/services/style_setter/style_setter.js' +import { + newImporter, + newExporter +} from 'src/services/export_import/export_import.js' import { SLOT_INHERITANCE } from 'src/services/theme_data/pleromafe.js' @@ -31,7 +35,6 @@ import ShadowControl from 'src/components/shadow_control/shadow_control.vue' import FontControl from 'src/components/font_control/font_control.vue' import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue' import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js' -import ExportImport from 'src/components/export_import/export_import.vue' import Checkbox from 'src/components/checkbox/checkbox.vue' import Select from 'src/components/select/select.vue' @@ -60,6 +63,15 @@ const colorConvert = (color) => { export default { data () { return { + themeImporter: newImporter({ + validator: this.importValidator, + onImport: this.onImport, + onImportFailure: this.onImportFailure + }), + themeExporter: newExporter({ + filename: 'pleroma_theme', + getExportedObject: () => this.exportedTheme + }), availableStyles: [], selected: this.$store.getters.mergedConfig.theme, themeWarning: undefined, @@ -376,7 +388,6 @@ export default { FontControl, TabSwitcher, Preview, - ExportImport, Checkbox, Select }, @@ -522,10 +533,15 @@ export default { this.previewColors.mod ) }, + importTheme () { this.themeImporter.importData() }, + exportTheme () { this.themeExporter.exportData() }, onImport (parsed, forceSource = false) { this.tempImportFile = parsed this.loadTheme(parsed, 'file', forceSource) }, + onImportFailure (result) { + this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_theme_imported', level: 'error' }) + }, importValidator (parsed) { const version = parsed._pleroma_theme_version return version >= 1 || version <= 2 diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue index 2b4b8ba0440f98c0ae46dfd08c004f887ccaa57c..570cd11b8f2cdb77d0912aa6569734db93e1d0eb 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue @@ -48,17 +48,13 @@ </template> </div> </div> - <ExportImport - :export-object="exportedTheme" - :export-label="$t("settings.export_theme")" - :import-label="$t("settings.import_theme")" - :import-failed-text="$t("settings.invalid_theme_imported")" - :on-import="onImport" - :validator="importValidator" - > - <template slot="before"> - <div class="presets"> - {{ $t('settings.presets') }} + <div class="top"> + <div class="presets"> + {{ $t('settings.presets') }} + <label + for="preset-switcher" + class="select" + > <Select id="preset-switcher" v-model="selected" @@ -76,9 +72,27 @@ {{ style[0] || style.name }} </option> </Select> - </div> - </template> - </ExportImport> + <FAIcon + class="select-down-icon" + icon="chevron-down" + /> + </label> + </div> + <div class="export-import"> + <button + class="btn button-default" + @click="importTheme" + > + {{ $t("settings.import_theme") }} + </button> + <button + class="btn button-default" + @click="exportTheme" + > + {{ $t("settings.export_theme") }} + </button> + </div> + </div> </div> <div class="save-load-options"> <span class="keep-option"> diff --git a/src/components/timeline/timeline.scss b/src/components/timeline/timeline.scss new file mode 100644 index 0000000000000000000000000000000000000000..2c5a67e2b73fa5bb24fb50cf1ff1c9336562d3aa --- /dev/null +++ b/src/components/timeline/timeline.scss @@ -0,0 +1,31 @@ +@import '../../_variables.scss'; + +.Timeline { + .loadmore-text { + opacity: 1; + } + + &.-blocked { + cursor: progress; + } + + .timeline-heading { + max-width: 100%; + flex-wrap: nowrap; + align-items: center; + position: relative; + + .loadmore-button { + flex-shrink: 0; + } + + .loadmore-text { + flex-shrink: 0; + line-height: 1em; + } + } + + .timeline-footer { + border: none; + } +} diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 286477c25ee25c36e96ecc76f87a0f8d7b4ebd03..767428f062575df914ca6eafea5a1b451476462f 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -52,13 +52,13 @@ <div :class="classes.footer"> <div v-if="count===0" - class="new-status-notification text-center panel-footer faint" + class="new-status-notification text-center faint" > {{ $t('timeline.no_statuses') }} </div> <div v-else-if="bottomedOut" - class="new-status-notification text-center panel-footer faint" + class="new-status-notification text-center faint" > {{ $t('timeline.no_more_statuses') }} </div> @@ -67,13 +67,13 @@ class="button-unstyled -link -fullwidth" @click.prevent="fetchOlderStatuses()" > - <div class="new-status-notification text-center panel-footer"> + <div class="new-status-notification text-center"> {{ $t('timeline.load_older') }} </div> </button> <div v-else - class="new-status-notification text-center panel-footer" + class="new-status-notification text-center" > <FAIcon icon="circle-notch" @@ -87,32 +87,4 @@ <script src="./timeline.js"></script> -<style lang="scss"> -@import '../../_variables.scss'; - -.Timeline { - .loadmore-text { - opacity: 1; - } - - &.-blocked { - cursor: progress; - } -} - -.timeline-heading { - max-width: 100%; - flex-wrap: nowrap; - align-items: center; - position: relative; - - .loadmore-button { - flex-shrink: 0; - } - - .loadmore-text { - flex-shrink: 0; - line-height: 1em; - } -} -</style> +<style src="./timeline.scss" lang="scss"> </style> diff --git a/src/components/timeline/timeline_quick_settings.js b/src/components/timeline/timeline_quick_settings.js index 9ec1a007f4f4695cdaa7848eb9096e6123d037e1..eae65a5587e8af0fb3064677b68c4e5b1299b220 100644 --- a/src/components/timeline/timeline_quick_settings.js +++ b/src/components/timeline/timeline_quick_settings.js @@ -1,5 +1,4 @@ import Popover from '../popover/popover.vue' -import BooleanSetting from '../settings_modal/helpers/boolean_setting.vue' import { mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons' @@ -12,8 +11,7 @@ library.add( const TimelineQuickSettings = { components: { - Popover, - BooleanSetting + Popover }, methods: { setReplyVisibility (visibility) { diff --git a/src/components/timeline/timeline_quick_settings.vue b/src/components/timeline/timeline_quick_settings.vue index 4ad2842b30ef1f4b67b14de2975a3f4a1a7f7b58..94997c0d730f86a45b27e6d999a60915bfa561f8 100644 --- a/src/components/timeline/timeline_quick_settings.vue +++ b/src/components/timeline/timeline_quick_settings.vue @@ -6,7 +6,7 @@ > <div slot="content" - class="timeline-settings-menu dropdown-menu" + class="dropdown-menu" > <div v-if="loggedIn"> <button @@ -96,12 +96,6 @@ .dropdown-item { margin: 0; } - - .timeline-settings-menu { - display: flex; - min-width: 12em; - flex-direction: column; - } } </style> diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js index 8d6a58b11e913bfe61b191f9fe2c2d6efb98dab5..bab51e75e2d20da93bf1a29754f90be5a2938b07 100644 --- a/src/components/timeline_menu/timeline_menu.js +++ b/src/components/timeline_menu/timeline_menu.js @@ -1,29 +1,17 @@ import Popover from '../popover/popover.vue' -import { mapState } from 'vuex' +import TimelineMenuContent from './timeline_menu_content.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { - faUsers, - faGlobe, - faBookmark, - faEnvelope, - faHome, faChevronDown } from '@fortawesome/free-solid-svg-icons' -library.add( - faUsers, - faGlobe, - faBookmark, - faEnvelope, - faHome, - faChevronDown -) +library.add(faChevronDown) // Route -> i18n key mapping, exported and not in the computed // because nav panel benefits from the same information. export const timelineNames = () => { return { - 'friends': 'nav.timeline', + 'friends': 'nav.home_timeline', 'bookmarks': 'nav.bookmarks', 'dms': 'nav.dms', 'public-timeline': 'nav.public_tl', @@ -33,7 +21,8 @@ export const timelineNames = () => { const TimelineMenu = { components: { - Popover + Popover, + TimelineMenuContent }, data () { return { @@ -41,9 +30,6 @@ const TimelineMenu = { } }, created () { - if (this.currentUser && this.currentUser.locked) { - this.$store.dispatch('startFetchingFollowRequests') - } if (timelineNames()[this.$route.name]) { this.$store.dispatch('setLastTimeline', this.$route.name) } @@ -75,13 +61,6 @@ const TimelineMenu = { const i18nkey = timelineNames()[this.$route.name] return i18nkey ? this.$t(i18nkey) : route } - }, - computed: { - ...mapState({ - currentUser: state => state.users.currentUser, - privateMode: state => state.instance.private, - federating: state => state.instance.federating - }) } } diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue index 3c86842b7d8f3ab4cf7a476c1751da649e4e2b2c..22dc3432ef570631c49a8a9e8518cb02f30c5bb1 100644 --- a/src/components/timeline_menu/timeline_menu.vue +++ b/src/components/timeline_menu/timeline_menu.vue @@ -13,53 +13,7 @@ slot="content" class="timeline-menu-popover panel panel-default" > - <ul> - <li v-if="currentUser"> - <router-link :to="{ name: 'friends' }"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="home" - />{{ $t("nav.timeline") }} - </router-link> - </li> - <li v-if="currentUser"> - <router-link :to="{ name: 'bookmarks'}"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="bookmark" - />{{ $t("nav.bookmarks") }} - </router-link> - </li> - <li v-if="currentUser"> - <router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="envelope" - />{{ $t("nav.dms") }} - </router-link> - </li> - <li v-if="currentUser || !privateMode"> - <router-link :to="{ name: 'public-timeline' }"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="users" - />{{ $t("nav.public_tl") }} - </router-link> - </li> - <li v-if="federating && (currentUser || !privateMode)"> - <router-link :to="{ name: 'public-external-timeline' }"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="globe" - />{{ $t("nav.twkn") }} - </router-link> - </li> - </ul> + <TimelineMenuContent /> </div> <div slot="trigger" diff --git a/src/components/timeline_menu/timeline_menu_content.js b/src/components/timeline_menu/timeline_menu_content.js new file mode 100644 index 0000000000000000000000000000000000000000..671570ddf7d6bb1729021e5b11160122073cdf24 --- /dev/null +++ b/src/components/timeline_menu/timeline_menu_content.js @@ -0,0 +1,29 @@ +import { mapState } from 'vuex' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faUsers, + faGlobe, + faBookmark, + faEnvelope, + faHome +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faUsers, + faGlobe, + faBookmark, + faEnvelope, + faHome +) + +const TimelineMenuContent = { + computed: { + ...mapState({ + currentUser: state => state.users.currentUser, + privateMode: state => state.instance.private, + federating: state => state.instance.federating + }) + } +} + +export default TimelineMenuContent diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue new file mode 100644 index 0000000000000000000000000000000000000000..bed1b679a4fddd1d4032235681b85faed22df98c --- /dev/null +++ b/src/components/timeline_menu/timeline_menu_content.vue @@ -0,0 +1,66 @@ +<template> + <ul> + <li v-if="currentUser"> + <router-link + class="menu-item" + :to="{ name: 'friends' }" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="home" + />{{ $t("nav.home_timeline") }} + </router-link> + </li> + <li v-if="currentUser || !privateMode"> + <router-link + class="menu-item" + :to="{ name: 'public-timeline' }" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="users" + />{{ $t("nav.public_tl") }} + </router-link> + </li> + <li v-if="federating && (currentUser || !privateMode)"> + <router-link + class="menu-item" + :to="{ name: 'public-external-timeline' }" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="globe" + />{{ $t("nav.twkn") }} + </router-link> + </li> + <li v-if="currentUser"> + <router-link + class="menu-item" + :to="{ name: 'bookmarks'}" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="bookmark" + />{{ $t("nav.bookmarks") }} + </router-link> + </li> + <li v-if="currentUser"> + <router-link + class="menu-item" + :to="{ name: 'dms', params: { username: currentUser.screen_name } }" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="envelope" + />{{ $t("nav.dms") }} + </router-link> + </li> + </ul> +</template> + +<script src="./timeline_menu_content.js" ></script> diff --git a/src/i18n/en.json b/src/i18n/en.json index 2aefebc9d385db2b99661a03637a5872acf910dc..d613848269949e5bd6523f79fceb24f449185e1f 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -3,27 +3,27 @@ "mrf": { "federation": "Federation", "keyword": { - "keyword_policies": "Keyword Policies", + "keyword_policies": "Keyword policies", "ftl_removal": "Removal from \"The Whole Known Network\" Timeline", "reject": "Reject", "replace": "Replace", "is_replaced_by": "→" }, - "mrf_policies": "Enabled MRF Policies", + "mrf_policies": "Enabled MRF policies", "mrf_policies_desc": "MRF policies manipulate the federation behaviour of the instance. The following policies are enabled:", "simple": { - "simple_policies": "Instance-specific Policies", + "simple_policies": "Instance-specific policies", "accept": "Accept", "accept_desc": "This instance only accepts messages from the following instances:", "reject": "Reject", "reject_desc": "This instance will not accept messages from the following instances:", "quarantine": "Quarantine", "quarantine_desc": "This instance will send only public posts to the following instances:", - "ftl_removal": "Removal from \"The Whole Known Network\" Timeline", - "ftl_removal_desc": "This instance removes these instances from \"The Whole Known Network\" timeline:", + "ftl_removal": "Removal from \"Known Network\" Timeline", + "ftl_removal_desc": "This instance removes these instances from \"Known Network\" timeline:", "media_removal": "Media Removal", "media_removal_desc": "This instance removes media from posts on the following instances:", - "media_nsfw": "Media Force-set As Sensitive", + "media_nsfw": "Media force-set as sensitive", "media_nsfw_desc": "This instance forces media to be set sensitive in posts on the following instances:" } }, @@ -118,12 +118,13 @@ "about": "About", "administration": "Administration", "back": "Back", - "friend_requests": "Follow Requests", + "friend_requests": "Follow requests", "mentions": "Mentions", "interactions": "Interactions", - "dms": "Direct Messages", - "public_tl": "Public Timeline", + "dms": "Direct messages", + "public_tl": "Public timeline", "timeline": "Timeline", + "home_timeline": "Home timeline", "twkn": "Known Network", "bookmarks": "Bookmarks", "user_search": "User Search", @@ -148,8 +149,8 @@ "reacted_with": "reacted with {0}" }, "polls": { - "add_poll": "Add Poll", - "add_option": "Add Option", + "add_poll": "Add poll", + "add_option": "Add option", "option": "Option", "votes": "votes", "people_voted_count": "{count} person voted | {count} people voted", @@ -178,7 +179,7 @@ "storage_unavailable": "Pleroma could not access browser storage. Your login or your local settings won't be saved and you might encounter unexpected issues. Try enabling cookies." }, "interactions": { - "favs_repeats": "Repeats and Favorites", + "favs_repeats": "Repeats and favorites", "follows": "New follows", "moves": "User migrates", "load_older": "Load older interactions" @@ -200,6 +201,7 @@ "direct_warning_to_all": "This post will be visible to all the mentioned users.", "direct_warning_to_first_only": "This post will only be visible to the mentioned users at the beginning of the message.", "posting": "Posting", + "post": "Post", "preview": "Preview", "preview_empty": "Empty", "empty_status_error": "Can't post an empty status with no files", @@ -210,10 +212,10 @@ "unlisted": "This post will not be visible in Public Timeline and The Whole Known Network" }, "scope": { - "direct": "Direct - Post to mentioned users only", - "private": "Followers-only - Post to followers only", - "public": "Public - Post to public timelines", - "unlisted": "Unlisted - Do not post to public timelines" + "direct": "Direct - post to mentioned users only", + "private": "Followers-only - post to followers only", + "public": "Public - post to public timelines", + "unlisted": "Unlisted - do not post to public timelines" } }, "registration": { @@ -230,6 +232,7 @@ "bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.", "reason": "Reason to register", "reason_placeholder": "This instance approves registrations manually.\nLet the administration know why you want to register.", + "register": "Register", "validations": { "username_required": "cannot be left blank", "fullname_required": "cannot be left blank", @@ -249,6 +252,7 @@ }, "settings": { "app_name": "App name", + "save": "Save changes", "security": "Security", "setting_changed": "Setting is different from default", "enter_current_password_to_confirm": "Enter your current password to confirm your identity", @@ -277,7 +281,7 @@ "attachmentRadius": "Attachments", "attachments": "Attachments", "avatar": "Avatar", - "avatarAltRadius": "Avatars (Notifications)", + "avatarAltRadius": "Avatars (notifications)", "avatarRadius": "Avatars", "background": "Background", "bio": "Bio", @@ -299,10 +303,10 @@ "cGreen": "Green (Retweet)", "cOrange": "Orange (Favorite)", "cRed": "Red (Cancel)", - "change_email": "Change Email", + "change_email": "Change email", "change_email_error": "There was an issue changing your email.", "changed_email": "Email changed successfully!", - "change_password": "Change Password", + "change_password": "Change password", "change_password_error": "There was an issue changing your password.", "changed_password": "Password changed successfully!", "chatMessageRadius": "Chat message", @@ -311,9 +315,9 @@ "confirm_new_password": "Confirm new password", "current_password": "Current password", "mutes_and_blocks": "Mutes and Blocks", - "data_import_export_tab": "Data Import / Export", + "data_import_export_tab": "Data import / export", "default_vis": "Default visibility scope", - "delete_account": "Delete Account", + "delete_account": "Delete account", "delete_account_description": "Permanently delete your data and deactivate your account.", "delete_account_error": "There was an issue deleting your account. If this persists please contact your instance administrator.", "delete_account_instructions": "Type your password in the input below to confirm account deletion.", @@ -364,20 +368,32 @@ "loop_video_silent_only": "Loop only videos without sound (i.e. Mastodon's \"gifs\")", "mutes_tab": "Mutes", "play_videos_in_modal": "Play videos in a popup frame", + "file_export_import": { + "backup_restore": "Settings backup", + "backup_settings": "Backup settings to file", + "backup_settings_theme": "Backup settings and theme to file", + "restore_settings": "Restore settings from file", + "errors": { + "invalid_file": "The selected file is not a supported Pleroma settings backup. No changes were made.", + "file_too_new": "Incompatile major version: {fileMajor}, this PleromaFE (settings ver {feMajor}) is too old to handle it", + "file_too_old": "Incompatile major version: {fileMajor}, file version is too old and not supported (min. set. ver. {feMajor})", + "file_slightly_new": "File minor version is different, some settings might not load" + } + }, "profile_fields": { "label": "Profile metadata", - "add_field": "Add Field", + "add_field": "Add field", "name": "Label", "value": "Content" }, "use_contain_fit": "Don't crop the attachment in thumbnails", "name": "Name", - "name_bio": "Name & Bio", - "new_email": "New Email", + "name_bio": "Name & bio", + "new_email": "New email", "new_password": "New password", "notification_visibility": "Types of notifications to show", "notification_visibility_follows": "Follows", - "notification_visibility_likes": "Likes", + "notification_visibility_likes": "Favorites", "notification_visibility_mentions": "Mentions", "notification_visibility_repeats": "Repeats", "notification_visibility_moves": "User Migrates", @@ -389,19 +405,19 @@ "hide_followers_description": "Don't show who's following me", "hide_follows_count_description": "Don't show follow count", "hide_followers_count_description": "Don't show follower count", - "show_admin_badge": "Show Admin badge in my profile", - "show_moderator_badge": "Show Moderator badge in my profile", + "show_admin_badge": "Show \"Admin\" badge in my profile", + "show_moderator_badge": "Show \"Moderator\" badge in my profile", "nsfw_clickthrough": "Enable clickthrough attachment and link preview image hiding for NSFW statuses", "oauth_tokens": "OAuth tokens", "token": "Token", - "refresh_token": "Refresh Token", - "valid_until": "Valid Until", + "refresh_token": "Refresh token", + "valid_until": "Valid until", "revoke_token": "Revoke", "panelRadius": "Panels", "pause_on_unfocused": "Pause streaming when tab is not focused", "presets": "Presets", - "profile_background": "Profile Background", - "profile_banner": "Profile Banner", + "profile_background": "Profile background", + "profile_banner": "Profile banner", "profile_tab": "Profile", "radii_help": "Set up interface edge rounding (in pixels)", "replies_in_timeline": "Replies in timeline", @@ -613,8 +629,8 @@ }, "version": { "title": "Version", - "backend_version": "Backend Version", - "frontend_version": "Frontend Version" + "backend_version": "Backend version", + "frontend_version": "Frontend version" } }, "time": { @@ -662,7 +678,9 @@ "reload": "Reload", "up_to_date": "Up-to-date", "no_more_statuses": "No more statuses", - "no_statuses": "No statuses" + "no_statuses": "No statuses", + "socket_reconnected": "Realtime connection established", + "socket_broke": "Realtime connection lost: CloseEvent code {0}" }, "status": { "favorites": "Favorites", @@ -755,7 +773,7 @@ } }, "user_profile": { - "timeline_title": "User Timeline", + "timeline_title": "User timeline", "profile_does_not_exist": "Sorry, this profile does not exist.", "profile_loading_error": "Sorry, there was an error loading this profile." }, @@ -773,7 +791,7 @@ "who_to_follow": "Who to follow" }, "tool_tip": { - "media_upload": "Upload Media", + "media_upload": "Upload media", "repeat": "Repeat", "reply": "Reply", "favorite": "Favorite", diff --git a/src/i18n/eo.json b/src/i18n/eo.json index 9f0491fbebc7d6fc172c2bbfaed1424300569007..0d24a8f8fdbdaf50764406d53ccd56f825b09584 100644 --- a/src/i18n/eo.json +++ b/src/i18n/eo.json @@ -156,7 +156,9 @@ "password_required": "ne povas resti malplena", "password_confirmation_required": "ne povas resti malplena", "password_confirmation_match": "samu la pasvorton" - } + }, + "reason_placeholder": "Ĉi-node oni aprobas registriÄojn permane.\nSciigu la administrantojn kial vi volas registriÄi.", + "reason": "Kialo registriÄi" }, "settings": { "app_name": "Nomo de aplikaĵo", @@ -523,7 +525,14 @@ "mute_export_button": "Elportu viajn silentigojn al CSV-dosiero", "mute_export": "Elporto de silentigoj", "hide_wallpaper": "KaÅi fonbildon de nodo", - "setting_changed": "Agordo malsamas de la implicita" + "setting_changed": "Agordo malsamas de la implicita", + "more_settings": "Pliaj agordoj", + "sensitive_by_default": "Implicite marki afiÅojn konsternaj", + "reply_visibility_following_short": "Montri respondojn por miaj abonatoj", + "hide_all_muted_posts": "KaÅi silentigitajn afiÅojn", + "hide_media_previews": "KaÅi antaÅrigardojn al vidaÅdaĵoj", + "word_filter": "Vortofiltro", + "reply_visibility_self_short": "Montri nur respondojn por mi" }, "timeline": { "collapse": "Maletendi", @@ -594,7 +603,13 @@ "hide_repeats": "KaÅi ripetojn", "unsubscribe": "Ne ricevi sciigojn", "subscribe": "Ricevi sciigojn", - "bot": "Roboto" + "bot": "Roboto", + "highlight": { + "side": "Flanka strio", + "striped": "Stria fono", + "solid": "Unueca fono", + "disabled": "Senemfaze" + } }, "user_profile": { "timeline_title": "Historio de uzanto", diff --git a/src/i18n/es.json b/src/i18n/es.json index 0ee67ed4230e2025857c5d025bbb3e41959f03f5..faaf34376f9d1e4cfe188af314ab057154cd8ed4 100644 --- a/src/i18n/es.json +++ b/src/i18n/es.json @@ -39,7 +39,11 @@ "dismiss": "Descartar", "retry": "Inténtalo de nuevo", "error_retry": "Por favor, inténtalo de nuevo", - "loading": "Cargando…" + "loading": "Cargando…", + "role": { + "admin": "Administrador/a", + "moderator": "Moderador/a" + } }, "image_cropper": { "crop_picture": "Recortar la foto", @@ -82,8 +86,8 @@ "friend_requests": "Solicitudes de seguimiento", "mentions": "Menciones", "interactions": "Interacciones", - "dms": "Mensajes Directos", - "public_tl": "LÃnea Temporal Pública", + "dms": "Mensajes directos", + "public_tl": "LÃnea temporal pública", "timeline": "LÃnea Temporal", "twkn": "Red Conocida", "user_search": "Búsqueda de Usuarios", @@ -92,7 +96,8 @@ "preferences": "Preferencias", "chats": "Chats", "timelines": "LÃneas de Tiempo", - "bookmarks": "Marcadores" + "bookmarks": "Marcadores", + "home_timeline": "LÃnea temporal personal" }, "notifications": { "broken_favorite": "Estado desconocido, buscándolo…", @@ -120,7 +125,9 @@ "expiry": "Tiempo de vida de la encuesta", "expires_in": "La encuesta termina en {0}", "expired": "La encuesta terminó hace {0}", - "not_enough_options": "Muy pocas opciones únicas en la encuesta" + "not_enough_options": "Muy pocas opciones únicas en la encuesta", + "people_voted_count": "{count} persona votó | {count} personas votaron", + "votes_count": "{count} voto | {count} votos" }, "emoji": { "stickers": "Pegatinas", @@ -137,14 +144,14 @@ "add_sticker": "Añadir Pegatina" }, "interactions": { - "favs_repeats": "Favoritos y Repetidos", + "favs_repeats": "Favoritos y repetidos", "follows": "Nuevos seguidores", "load_older": "Cargar interacciones más antiguas", "moves": "Usuario Migrado" }, "post_status": { "new_status": "Publicar un nuevo estado", - "account_not_locked_warning": "Tu cuenta no está {0}. Cualquiera puede seguirte y leer las entradas para Solo-Seguidores.", + "account_not_locked_warning": "Tu cuenta no está {0}. Cualquiera puede seguirte y leer las publicaciones para Solo-Seguidores.", "account_not_locked_warning_link": "bloqueada", "attachments_sensitive": "Contenido sensible", "content_type": { @@ -164,16 +171,17 @@ "unlisted": "Esta publicación no será visible en la LÃnea Temporal Pública ni en Toda La Red Conocida" }, "scope": { - "direct": "Directo - Solo para los usuarios mencionados", - "private": "Solo-seguidores - Solo tus seguidores leerán la publicación", - "public": "Público - Entradas visibles en las LÃneas Temporales Públicas", - "unlisted": "Sin listar - Entradas no visibles en las LÃneas Temporales Públicas" + "direct": "Directo - solo para los usuarios mencionados", + "private": "Solo-seguidores - solo tus seguidores leerán la publicación", + "public": "Público - publicaciones visibles en las lÃneas temporales públicas", + "unlisted": "Sin listar -publicaciones no visibles en las lÃneas temporales públicas" }, "media_description_error": "Error al actualizar el archivo, inténtalo de nuevo", "empty_status_error": "No se puede publicar un estado vacÃo y sin archivos adjuntos", "preview_empty": "VacÃo", "preview": "Vista previa", - "media_description": "Descripción multimedia" + "media_description": "Descripción multimedia", + "post": "Publicación" }, "registration": { "bio": "BiografÃa", @@ -194,7 +202,10 @@ "password_required": "no puede estar vacÃo", "password_confirmation_required": "no puede estar vacÃo", "password_confirmation_match": "la contraseña no coincide" - } + }, + "reason_placeholder": "Los registros de esta instancia son aprobados manualmente.\nComéntanos por qué quieres registrarte aquÃ.", + "reason": "Razón para registrarse", + "register": "Registrarse" }, "selectable_list": { "select_all": "Seleccionar todo" @@ -227,7 +238,7 @@ "attachmentRadius": "Adjuntos", "attachments": "Adjuntos", "avatar": "Avatar", - "avatarAltRadius": "Avatares (Notificaciones)", + "avatarAltRadius": "Avatares (notificaciones)", "avatarRadius": "Avatares", "background": "Fondo", "bio": "BiografÃa", @@ -245,19 +256,19 @@ "change_password": "Cambiar contraseña", "change_password_error": "Hubo un problema cambiando la contraseña.", "changed_password": "¡Contraseña cambiada correctamente!", - "collapse_subject": "Colapsar entradas con tema", + "collapse_subject": "Colapsar publicaciones con tema", "composing": "Redactando", "confirm_new_password": "Confirmar la nueva contraseña", "current_avatar": "Tu avatar actual", "current_password": "Contraseña actual", "current_profile_banner": "Tu cabecera actual", - "data_import_export_tab": "Importar / Exportar Datos", + "data_import_export_tab": "Importar / Exportar datos", "default_vis": "Alcance de visibilidad por defecto", "delete_account": "Eliminar la cuenta", "discoverable": "Permitir la aparición de esta cuenta en los resultados de búsqueda y otros servicios", "delete_account_description": "Eliminar para siempre los datos y desactivar la cuenta.", "pad_emoji": "Rellenar con espacios al agregar emojis desde el selector", - "delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el administrador de tu instancia.", + "delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el/la administrador/a de tu instancia.", "delete_account_instructions": "Escribe tu contraseña para confirmar la eliminación de tu cuenta.", "avatar_size_instruction": "El tamaño mÃnimo recomendado para el avatar es de 150X150 pÃxeles.", "export_theme": "Exportar tema", @@ -277,7 +288,7 @@ "hide_isp": "Ocultar el panel especÃfico de la instancia", "preload_images": "Precargar las imágenes", "use_one_click_nsfw": "Abrir los adjuntos NSFW con un solo click", - "hide_post_stats": "Ocultar las estadÃsticas de las entradas (p.ej. el número de favoritos)", + "hide_post_stats": "Ocultar las estadÃsticas de las publicaciones (p.ej. el número de favoritos)", "hide_user_stats": "Ocultar las estadÃsticas del usuario (p.ej. el número de seguidores)", "hide_filtered_statuses": "Ocultar estados filtrados", "import_blocks_from_a_csv_file": "Importar lista de usuarios bloqueados dese un archivo csv", @@ -299,22 +310,22 @@ "play_videos_in_modal": "Reproducir los vÃdeos en un marco emergente", "use_contain_fit": "No recortar los adjuntos en miniaturas", "name": "Nombre", - "name_bio": "Nombre y BiografÃa", + "name_bio": "Nombre y biografÃa", "new_password": "Nueva contraseña", "notification_visibility": "Tipos de notificaciones a mostrar", "notification_visibility_follows": "Nuevos seguidores", - "notification_visibility_likes": "Me gustan (Likes)", + "notification_visibility_likes": "Favoritos", "notification_visibility_mentions": "Menciones", "notification_visibility_repeats": "Repeticiones (Repeats)", - "no_rich_text_description": "Eliminar el formato de texto enriquecido de todas las entradas", + "no_rich_text_description": "Eliminar el formato de texto enriquecido de todas las publicaciones", "no_blocks": "No hay usuarios bloqueados", "no_mutes": "No hay usuarios silenciados", "hide_follows_description": "No mostrar a quién sigo", "hide_followers_description": "No mostrar quién me sigue", "hide_follows_count_description": "No mostrar el número de cuentas que sigo", "hide_followers_count_description": "No mostrar el número de cuentas que me siguen", - "show_admin_badge": "Mostrar la insignia de Administrador en mi perfil", - "show_moderator_badge": "Mostrar la insignia de Moderador en mi perfil", + "show_admin_badge": "Mostrar la insignia de \"Administrador/a\" en mi perfil", + "show_moderator_badge": "Mostrar la insignia de \"Moderador/a\" en mi perfil", "nsfw_clickthrough": "Habilitar la ocultación de la imagen de vista previa del enlace y el adjunto para los estados NSFW por defecto", "oauth_tokens": "Tokens de OAuth", "token": "Token", @@ -324,8 +335,8 @@ "panelRadius": "Paneles", "pause_on_unfocused": "Parar la transmisión cuando no estés en foco", "presets": "Por defecto", - "profile_background": "Fondo del Perfil", - "profile_banner": "Cabecera del Perfil", + "profile_background": "Imagen de fondo del perfil", + "profile_banner": "Imagen de cabecera del perfil", "profile_tab": "Perfil", "radii_help": "Establezca el redondeo de las esquinas de la interfaz (en pÃxeles)", "replies_in_timeline": "Réplicas en la lÃnea temporal", @@ -356,7 +367,7 @@ "theme": "Tema", "theme_help": "Use códigos de color hexadecimales (#rrggbb) para personalizar su tema de colores.", "theme_help_v2_1": "También puede invalidar los colores y la opacidad de ciertos componentes si activa la casilla de verificación. Use el botón \"Borrar todo\" para deshacer los cambios.", - "theme_help_v2_2": "Los iconos debajo de algunas entradas son indicadores de contraste de fondo/texto, desplace el ratón por encima para obtener información más detallada. Tenga en cuenta que cuando se utilizan indicadores de contraste de transparencia se muestra el peor caso posible.", + "theme_help_v2_2": "Los iconos debajo de algunas publicaciones son indicadores de contraste de fondo/texto, desplace el ratón por encima para obtener información más detallada. Tenga en cuenta que cuando se utilizan indicadores de contraste de transparencia se muestra el peor caso posible.", "tooltipRadius": "Información/alertas", "upload_a_photo": "Subir una foto", "user_settings": "Ajustes del Usuario", @@ -476,7 +487,7 @@ "panelHeader": "Cabecera del panel", "topBar": "Barra superior", "avatar": "Avatar del usuario (en la vista del perfil)", - "avatarStatus": "Avatar del usuario (en la vista de la entrada)", + "avatarStatus": "Avatar del usuario (en la vista de la publicación)", "popup": "Ventanas y textos emergentes (popups & tooltips)", "button": "Botones", "buttonHover": "Botón (encima)", @@ -517,8 +528,8 @@ }, "version": { "title": "Versión", - "backend_version": "Versión del Backend", - "frontend_version": "Versión del Frontend" + "backend_version": "Versión del backend", + "frontend_version": "Versión del frontend" }, "notification_visibility_moves": "Usuario Migrado", "greentext": "Texto verde (meme arrows)", @@ -529,7 +540,7 @@ "fun": "Divertido", "type_domains_to_mute": "Buscar dominios para silenciar", "useStreamingApiWarning": "(no recomendado, experimental, puede omitir publicaciones)", - "useStreamingApi": "Recibir entradas y notificaciones en tiempo real", + "useStreamingApi": "Recibir publicaciones y notificaciones en tiempo real", "user_mutes": "Usuarios", "reset_profile_background": "Restablecer el fondo de pantalla", "reset_background_confirm": "¿Estás seguro de restablecer el fondo de pantalla?", @@ -563,7 +574,15 @@ "mute_export_button": "Exportar los silenciados a un archivo csv", "mute_export": "Exportar silenciados", "hide_wallpaper": "Ocultar el fondo de pantalla de la instancia", - "setting_changed": "La configuración es diferente a la predeterminada" + "setting_changed": "La configuración es diferente a la predeterminada", + "hide_all_muted_posts": "Ocultar las publicaciones silenciadas", + "more_settings": "Más opciones", + "sensitive_by_default": "Identificar las publicaciones como sensibles de forma predeterminada", + "reply_visibility_self_short": "Mostrar respuestas solo a uno mismo", + "reply_visibility_following_short": "Mostrar las réplicas a mis seguidores", + "hide_media_previews": "Ocultar la vista previa multimedia", + "word_filter": "Filtro de palabras", + "save": "Guardar los cambios" }, "time": { "day": "{0} dÃa", @@ -611,7 +630,9 @@ "no_more_statuses": "No hay más estados", "no_statuses": "Sin estados", "reload": "Recargar", - "error": "Error obteniendo la linea de tiempo:{0}" + "error": "Error obteniendo la linea de tiempo:{0}", + "socket_broke": "Conexión en timpo real perdida: código del motivo {0}", + "socket_reconnected": "Establecida la conexión en tiempo real" }, "status": { "favorites": "Favoritos", @@ -635,7 +656,7 @@ "status_unavailable": "Estado no disponible", "bookmark": "Marcar", "unbookmark": "Desmarcar", - "status_deleted": "Esta entrada ha sido eliminada", + "status_deleted": "Esta publicación ha sido eliminada", "nsfw": "NSFW (No apropiado para el trabajo)", "expand": "Expandir", "external_source": "Fuente externa" @@ -674,10 +695,10 @@ "mute_progress": "Silenciando…", "admin_menu": { "moderation": "Moderación", - "grant_admin": "Conceder permisos de Administrador", - "revoke_admin": "Revocar permisos de Administrador", - "grant_moderator": "Conceder permisos de Moderador", - "revoke_moderator": "Revocar permisos de Moderador", + "grant_admin": "Conceder permisos de Administrador/a", + "revoke_admin": "Revocar permisos de Administrador/a", + "grant_moderator": "Conceder permisos de Moderador/a", + "revoke_moderator": "Revocar permisos de Moderador/a", "activate_account": "Activar cuenta", "deactivate_account": "Desactivar cuenta", "delete_account": "Eliminar cuenta", @@ -698,16 +719,23 @@ "roles": { "moderator": "Moderador", "admin": "Administrador" - } + }, + "highlight": { + "striped": "Fondo rayado", + "side": "Raya lateral", + "solid": "Fondo sólido", + "disabled": "Sin resaltado" + }, + "bot": "Bot" }, "user_profile": { - "timeline_title": "Linea Temporal del Usuario", + "timeline_title": "LÃnea temporal del usuario", "profile_does_not_exist": "Lo sentimos, este perfil no existe.", "profile_loading_error": "Lo sentimos, hubo un error al cargar este perfil." }, "user_reporting": { "title": "Reportando a {0}", - "add_comment_description": "El informe será enviado a los moderadores de su instancia. Puedes proporcionar una explicación de por qué estás reportando esta cuenta a continuación:", + "add_comment_description": "El informe será enviado a los/las moderadores/as de su instancia. Puedes proporcionar una explicación de por qué estás reportando esta cuenta a continuación:", "additional_comments": "Comentarios adicionales", "forward_description": "La cuenta es de otro servidor. ¿Enviar una copia del informe allà también?", "forward_to": "Reenviar a {0}", @@ -719,7 +747,7 @@ "who_to_follow": "A quién seguir" }, "tool_tip": { - "media_upload": "Subir Medios", + "media_upload": "Subir multimedia", "repeat": "Repetir", "reply": "Contestar", "favorite": "Favorito", @@ -777,12 +805,12 @@ "simple": { "accept_desc": "Esta instancia solo acepta mensajes de las siguientes instancias:", "media_nsfw_desc": "Esta instancia obliga a que los archivos multimedia se establezcan como sensibles en las publicaciones de las siguientes instancias:", - "media_nsfw": "Forzar Multimedia Como Sensible", + "media_nsfw": "Forzar contenido multimedia como sensible", "media_removal_desc": "Esta instancia elimina los archivos multimedia de las publicaciones de las siguientes instancias:", "media_removal": "Eliminar Multimedia", "quarantine": "Cuarentena", - "ftl_removal_desc": "Esta instancia elimina las siguientes instancias de la lÃnea de tiempo \"Toda la red conocida\":", - "ftl_removal": "Eliminar de la lÃnea de tiempo \"Toda La Red Conocida\"", + "ftl_removal_desc": "Esta instancia elimina las siguientes instancias de la lÃnea de tiempo \"Red Conocida\":", + "ftl_removal": "Eliminar de la lÃnea de tiempo \"Red Conocida\"", "quarantine_desc": "Esta instancia enviará solo publicaciones públicas a las siguientes instancias:", "simple_policies": "PolÃticas especÃficas de la instancia", "reject_desc": "Esta instancia no aceptará mensajes de las siguientes instancias:", diff --git a/src/i18n/eu.json b/src/i18n/eu.json index a45b7cfd767db1c7bce5b5ef3f16c31362fc5a02..e543fda0867c284ac1516e0562763f46727a99b2 100644 --- a/src/i18n/eu.json +++ b/src/i18n/eu.json @@ -14,7 +14,8 @@ "text_limit": "Testu limitea", "title": "Ezaugarriak", "who_to_follow": "Nori jarraitu", - "pleroma_chat_messages": "Pleroma Txata" + "pleroma_chat_messages": "Pleroma Txata", + "upload_limit": "Kargatzeko muga" }, "finder": { "error_fetching_user": "Errorea erabiltzailea eskuratzen", @@ -38,7 +39,11 @@ "dismiss": "Baztertu", "retry": "Saiatu berriro", "error_retry": "Saiatu berriro mesedez", - "loading": "Kargatzen…" + "loading": "Kargatzen…", + "role": { + "moderator": "Moderatzailea", + "admin": "Administratzailea" + } }, "image_cropper": { "crop_picture": "Moztu argazkia", @@ -81,8 +86,8 @@ "friend_requests": "Jarraitzeko eskaerak", "mentions": "Aipamenak", "interactions": "Interakzioak", - "dms": "Zuzeneko Mezuak", - "public_tl": "Denbora-lerro Publikoa", + "dms": "Zuzeneko mezuak", + "public_tl": "Denbora-lerro publikoa", "timeline": "Denbora-lerroa", "twkn": "Ezagutzen den Sarea", "user_search": "Erabiltzailea Bilatu", @@ -104,7 +109,8 @@ "no_more_notifications": "Ez dago jakinarazpen gehiago", "reacted_with": "{0}kin erreakzionatu zuen", "migrated_to": "hona migratua:", - "follow_request": "jarraitu nahi zaitu" + "follow_request": "jarraitu nahi zaitu", + "error": "Errorea jakinarazpenak eskuratzean: {0}" }, "polls": { "add_poll": "Inkesta gehitu", @@ -118,7 +124,9 @@ "expiry": "Inkestaren iraupena", "expires_in": "Inkesta {0} bukatzen da", "expired": "Inkesta {0} bukatu zen", - "not_enough_options": "Aukera gutxiegi inkestan" + "not_enough_options": "Aukera gutxiegi inkestan", + "votes_count": "{count} boto| {count} boto", + "people_voted_count": "Pertsona batek bozkatu du | {count} pertsonak bozkatu dute" }, "emoji": { "stickers": "Pegatinak", @@ -160,9 +168,9 @@ "unlisted": "Mezu hau ez da argitaratuko Denbora-lerro Publikoan ezta Ezagutzen den Sarean" }, "scope": { - "direct": "Zuzena: Bidali aipatutako erabiltzaileei besterik ez", - "private": "Jarraitzaileentzako bakarrik: Bidali jarraitzaileentzat bakarrik", - "public": "Publikoa: Bistaratu denbora-lerro publikoetan", + "direct": "Zuzena: bidali aipatutako erabiltzaileei besterik ez", + "private": "Jarraitzaileentzako bakarrik: bidali jarraitzaileentzat bakarrik", + "public": "Publikoa: bistaratu denbora-lerro publikoetan", "unlisted": "Zerrendatu gabea: ez bidali denbora-lerro publikoetara" } }, @@ -218,7 +226,7 @@ "attachmentRadius": "Eranskinak", "attachments": "Eranskinak", "avatar": "Avatarra", - "avatarAltRadius": "Avatarra (Aipamenak)", + "avatarAltRadius": "Abatarra (aipamenak)", "avatarRadius": "Avatarrak", "background": "Atzeko planoa", "bio": "Biografia", @@ -242,7 +250,7 @@ "current_avatar": "Zure uneko avatarra", "current_password": "Indarrean dagoen pasahitza", "current_profile_banner": "Zure profilaren banner-a", - "data_import_export_tab": "Datuak Inportatu / Esportatu", + "data_import_export_tab": "Datuak inportatu / esportatu", "default_vis": "Lehenetsitako ikusgaitasunak", "delete_account": "Ezabatu kontua", "discoverable": "Baimendu zure kontua kanpo bilaketa-emaitzetan eta bestelako zerbitzuetan agertzea", @@ -304,19 +312,19 @@ "hide_followers_description": "Ez erakutsi nor ari den ni jarraitzen", "hide_follows_count_description": "Ez erakutsi jarraitzen ari naizen kontuen kopurua", "hide_followers_count_description": "Ez erakutsi nire jarraitzaileen kontuen kopurua", - "show_admin_badge": "Erakutsi Administratzaile etiketa nire profilan", - "show_moderator_badge": "Erakutsi Moderatzaile etiketa nire profilan", + "show_admin_badge": "Erakutsi \"Administratzaile\" etiketa nire profilan", + "show_moderator_badge": "Erakutsi \"Moderatzaile\" etiketa nire profilan", "nsfw_clickthrough": "Gaitu klika hunkigarri eranskinak ezkutatzeko", "oauth_tokens": "OAuth tokenak", "token": "Tokena", - "refresh_token": "Berrgin Tokena", - "valid_until": "Baliozkoa Arte", + "refresh_token": "Berrgin tokena", + "valid_until": "Baliozkoa arte", "revoke_token": "Ezeztatu", "panelRadius": "Panelak", "pause_on_unfocused": "Eguneraketa automatikoa gelditu fitxatik kanpo", "presets": "Aurrezarpenak", "profile_background": "Profilaren atzeko planoa", - "profile_banner": "Profilaren Banner-a", + "profile_banner": "Profilaren banner-a", "profile_tab": "Profila", "radii_help": "Konfiguratu interfazearen ertzen biribiltzea (pixeletan)", "replies_in_timeline": "Denbora-lerroko erantzunak", @@ -470,8 +478,8 @@ }, "version": { "title": "Bertsioa", - "backend_version": "Backend Bertsioa", - "frontend_version": "Frontend Bertsioa" + "backend_version": "Backend bertsioa", + "frontend_version": "Frontend bertsioa" } }, "time": { @@ -657,7 +665,7 @@ "federation": "Federazioa", "simple": { "media_nsfw_desc": "Instantzia honek hurrengo instantzien multimediak sentikorrak izatera behartzen ditu:", - "media_nsfw": "Behartu Multimedia Sentikor", + "media_nsfw": "Behartu multimedia sentikor moduan", "media_removal_desc": "Instantzia honek atxikitutako multimedia hurrengo instantzietatik ezabatzen ditu:", "media_removal": "Multimedia Ezabatu", "ftl_removal_desc": "Instantzia honek hurrengo instantziak ezabatzen ditu \"Ezagutzen den Sarea\" denbora-lerrotik:", diff --git a/src/i18n/fr.json b/src/i18n/fr.json index c2eff8084acc91fd6ba5122c7d9c71df65157af3..e51657e49702eee4e8591e6c37c81fc3778e8868 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -9,16 +9,17 @@ "features_panel": { "chat": "Chat", "gopher": "Gopher", - "media_proxy": "Proxy média", + "media_proxy": "Proxy pièce-jointes", "scope_options": "Options de visibilité", - "text_limit": "Limite de texte", - "title": "Caractéristiques", - "who_to_follow": "Personnes à suivre", - "pleroma_chat_messages": "Chat Pleroma" + "text_limit": "Limite du texte", + "title": "Fonctionnalités", + "who_to_follow": "Suggestions de suivis", + "pleroma_chat_messages": "Chat Pleroma", + "upload_limit": "Limite de téléversement" }, "finder": { - "error_fetching_user": "Erreur lors de la recherche de l'utilisateur·ice", - "find_user": "Chercher un-e utilisateur·ice" + "error_fetching_user": "Erreur lors de la recherche du compte", + "find_user": "Rechercher un compte" }, "general": { "apply": "Appliquer", @@ -26,19 +27,23 @@ "more": "Plus", "generic_error": "Une erreur s'est produite", "optional": "optionnel", - "show_more": "Montrer plus", - "show_less": "Montrer moins", + "show_more": "Afficher plus", + "show_less": "Afficher moins", "cancel": "Annuler", "disable": "Désactiver", "enable": "Activer", "confirm": "Confirmer", "verify": "Vérifier", - "dismiss": "Rejeter", + "dismiss": "Ignorer", "peek": "Jeter un coup d'Å“il", "close": "Fermer", "retry": "Réessayez", "error_retry": "Veuillez réessayer", - "loading": "Chargement…" + "loading": "Chargement…", + "role": { + "moderator": "Modo'", + "admin": "Admin" + } }, "image_cropper": { "crop_picture": "Rogner l'image", @@ -47,7 +52,7 @@ "cancel": "Annuler" }, "importer": { - "submit": "Soumettre", + "submit": "Envoyer", "success": "Importé avec succès.", "error": "Une erreur est survenue pendant l'import de ce fichier." }, @@ -56,17 +61,17 @@ "description": "Connexion avec OAuth", "logout": "Déconnexion", "password": "Mot de passe", - "placeholder": "p.e. lain", + "placeholder": "ex. lain", "register": "S'inscrire", "username": "Identifiant", "hint": "Connectez-vous pour rejoindre la discussion", "authentication_code": "Code d'authentification", "enter_recovery_code": "Entrez un code de récupération", - "enter_two_factor_code": "Entrez un code à double authentification", + "enter_two_factor_code": "Entrez un code double-facteur", "recovery_code": "Code de récupération", "heading": { - "totp": "Authentification à double authentification", - "recovery": "Récuperation de la double authentification" + "totp": "Authentification à double-facteur", + "recovery": "Récupération de l'authentification à double-facteur" } }, "media_modal": { @@ -78,24 +83,26 @@ "back": "Retour", "chat": "Chat local", "friend_requests": "Demandes de suivi", - "mentions": "Notifications", + "mentions": "Mentions", "interactions": "Interactions", "dms": "Messages directs", - "public_tl": "Fil d'actualité public", - "timeline": "Fil d'actualité", + "public_tl": "Flux publique", + "timeline": "Flux personnel", "twkn": "Réseau connu", - "user_search": "Recherche d'utilisateur·ice", - "who_to_follow": "Qui suivre", + "user_search": "Recherche de comptes", + "who_to_follow": "Suggestion de suivit", "preferences": "Préférences", "search": "Recherche", "administration": "Administration", "chats": "Chats", - "bookmarks": "Marques-Pages" + "bookmarks": "Marques-Pages", + "timelines": "Flux", + "home_timeline": "Flux personnel" }, "notifications": { - "broken_favorite": "Message inconnu, chargement…", + "broken_favorite": "Message inconnu, recherche en cours…", "favorited_you": "a aimé votre statut", - "followed_you": "a commencé à vous suivre", + "followed_you": "vous suit", "load_older": "Charger les notifications précédentes", "notifications": "Notifications", "read": "Lu !", @@ -103,7 +110,8 @@ "no_more_notifications": "Aucune notification supplémentaire", "migrated_to": "a migré à ", "reacted_with": "a réagi avec {0}", - "follow_request": "veut vous suivre" + "follow_request": "veut vous suivre", + "error": "Erreur de chargement des notifications : {0}" }, "interactions": { "favs_repeats": "Partages et favoris", @@ -115,7 +123,7 @@ "new_status": "Poster un nouveau statut", "account_not_locked_warning": "Votre compte n'est pas {0}. N'importe qui peut vous suivre pour voir vos billets en Abonné·e·s uniquement.", "account_not_locked_warning_link": "verrouillé", - "attachments_sensitive": "Marquer le média comme sensible", + "attachments_sensitive": "Marquer les pièce-jointes comme sensible", "content_type": { "text/plain": "Texte brut", "text/html": "HTML", @@ -130,32 +138,33 @@ "scope_notice": { "public": "Ce statut sera visible par tout le monde", "private": "Ce statut sera visible par seulement vos abonné⋅eâ‹…s", - "unlisted": "Ce statut ne sera pas visible dans le Fil d'actualité public et l'Ensemble du réseau connu" + "unlisted": "Ce statut ne sera pas visible dans le Flux Public et le Flux Fédéré" }, "scope": { "direct": "Direct - N'envoyer qu'aux personnes mentionnées", - "private": "Abonné·e·s uniquement - Seul·e·s vos abonné·e·s verront vos billets", - "public": "Publique - Afficher dans les fils publics", - "unlisted": "Non-Listé - Ne pas afficher dans les fils publics" + "private": "Abonné·e·s uniquement - Seul·e·s vos abonné·e·s verront vos status", + "public": "Publique - Afficher dans les flux publics", + "unlisted": "Non-Listé - Ne pas afficher dans les flux publics" }, "media_description_error": "Échec de téléversement du media, essayez encore", - "empty_status_error": "Impossible de poster un statut vide sans attachements", + "empty_status_error": "Impossible de poster un statut vide sans pièces-jointes", "preview_empty": "Vide", "preview": "Prévisualisation", - "media_description": "Description de l'attachement" + "media_description": "Description de la pièce-jointe", + "post": "Post" }, "registration": { "bio": "Biographie", - "email": "Adresse mail", + "email": "Courriel", "fullname": "Pseudonyme", "password_confirm": "Confirmation du mot de passe", "registration": "Inscription", "token": "Jeton d'invitation", "captcha": "CAPTCHA", "new_captcha": "Cliquez sur l'image pour avoir un nouveau captcha", - "username_placeholder": "p.e. lain", - "fullname_placeholder": "p.e. Lain Iwakura", - "bio_placeholder": "p.e.\nSalut, je suis Lain\nJe suis une héroïne d'animé qui vit dans une banlieue japonaise. Vous me connaissez peut-être du Wired.", + "username_placeholder": "ex. lain", + "fullname_placeholder": "ex. Lain Iwakura", + "bio_placeholder": "ex.\nSalut, je suis Lain\nJe suis une héroïne d'animation qui vit dans une banlieue japonaise. Vous me connaissez peut-être du Wired.", "validations": { "username_required": "ne peut pas être laissé vide", "fullname_required": "ne peut pas être laissé vide", @@ -163,7 +172,10 @@ "password_required": "ne peut pas être laissé vide", "password_confirmation_required": "ne peut pas être laissé vide", "password_confirmation_match": "doit être identique au mot de passe" - } + }, + "reason_placeholder": "Cette instance modère les inscriptions manuellement.\nExpliquer ce qui motive votre inscription à l'administration.", + "reason": "Motivation d'inscription", + "register": "Enregistrer" }, "selectable_list": { "select_all": "Tout selectionner" @@ -177,20 +189,20 @@ "setup_otp": "Configurer OTP", "wait_pre_setup_otp": "préconfiguration OTP", "confirm_and_enable": "Confirmer & activer OTP", - "title": "Double authentification", + "title": "Authentification double-facteur", "generate_new_recovery_codes": "Générer de nouveaux codes de récupération", - "warning_of_generate_new_codes": "Quand vous générez de nouveauc codes de récupération, vos anciens codes ne fonctionnerons plus.", + "warning_of_generate_new_codes": "Quand vous générez de nouveaux codes de récupération, vos anciens codes ne fonctionnerons plus.", "recovery_codes": "Codes de récupération.", "waiting_a_recovery_codes": "Réception des codes de récupération…", - "recovery_codes_warning": "Écrivez les codes ou sauvez les quelquepart sécurisé - sinon vous ne les verrez plus jamais. Si vous perdez l'accès à votre application de double authentification et codes de récupération vous serez vérouillé en dehors de votre compte.", - "authentication_methods": "Methodes d'authentification", + "recovery_codes_warning": "Écrivez ces codes ou sauvegardez les dans un endroit sécurisé - sinon vous ne les verrez plus jamais. Si vous perdez l'accès à votre application de double authentification et codes de récupération vous serez verrouillé en dehors de votre compte.", + "authentication_methods": "Méthodes d'authentification", "scan": { "title": "Scanner", - "desc": "En utilisant votre application de double authentification, scannez ce QR code ou entrez la clé textuelle :", + "desc": "En utilisant votre application d'authentification à double-facteur, scannez ce QR code ou entrez la clé textuelle :", "secret_code": "Clé" }, "verify": { - "desc": "Pour activer la double authentification, entrez le code depuis votre application :" + "desc": "Pour activer l'authentification à double-facteur, entrez le code donné par votre application :" } }, "attachmentRadius": "Pièces jointes", @@ -201,10 +213,10 @@ "background": "Arrière-plan", "bio": "Biographie", "block_export": "Export des comptes bloqués", - "block_export_button": "Export des comptes bloqués vers un fichier csv", + "block_export_button": "Export des comptes bloqués vers un fichier CSV", "block_import": "Import des comptes bloqués", "block_import_error": "Erreur lors de l'import des comptes bloqués", - "blocks_imported": "Blocks importés ! Le traitement va prendre un moment.", + "blocks_imported": "Blocages importés ! Le traitement va prendre un moment.", "blocks_tab": "Bloqué·e·s", "btnRadius": "Boutons", "cBlue": "Bleu (répondre, suivre)", @@ -224,31 +236,31 @@ "default_vis": "Visibilité par défaut", "delete_account": "Supprimer le compte", "delete_account_description": "Supprimer définitivement vos données et désactiver votre compte.", - "delete_account_error": "Il y a eu un problème lors de la tentative de suppression de votre compte. Si le problème persiste, contactez l'administrateurâ‹…ice de cette instance.", + "delete_account_error": "Il y a eu un problème lors de la tentative de suppression de votre compte. Si le problème persiste, contactez l'administration de cette instance.", "delete_account_instructions": "Indiquez votre mot de passe ci-dessous pour confirmer la suppression de votre compte.", "avatar_size_instruction": "La taille minimale recommandée pour l'image de l'avatar est de 150x150 pixels.", "export_theme": "Enregistrer le thème", - "filtering": "Filtre", + "filtering": "Filtrage", "filtering_explanation": "Tous les statuts contenant ces mots seront masqués. Un mot par ligne", - "follow_export": "Exporter les abonnements", - "follow_export_button": "Exporter les abonnements en csv", - "follow_import": "Importer des abonnements", - "follow_import_error": "Erreur lors de l'importation des abonnements", - "follows_imported": "Abonnements importés ! Le traitement peut prendre un moment.", + "follow_export": "Exporter les suivis", + "follow_export_button": "Exporter les suivis dans un fichier CSV", + "follow_import": "Import des suivis", + "follow_import_error": "Erreur lors de l'importation des suivis", + "follows_imported": "Suivis importés ! Le traitement peut prendre un moment.", "foreground": "Premier plan", "general": "Général", "hide_attachments_in_convo": "Masquer les pièces jointes dans les conversations", - "hide_attachments_in_tl": "Masquer les pièces jointes dans le journal", - "hide_muted_posts": "Masquer les statuts des utilisateurs masqués", + "hide_attachments_in_tl": "Masquer les pièces jointes dans le flux", + "hide_muted_posts": "Masquer les statuts des comptes masqués", "max_thumbnails": "Nombre maximum de miniatures par statuts", - "hide_isp": "Masquer le panneau spécifique a l'instance", + "hide_isp": "Masquer le panneau de l'instance", "preload_images": "Précharger les images", - "use_one_click_nsfw": "Ouvrir les pièces-jointes NSFW avec un seul clic", - "hide_post_stats": "Masquer les statistiques de publication (le nombre de favoris)", - "hide_user_stats": "Masquer les statistiques de profil (le nombre d'amis)", + "use_one_click_nsfw": "Ouvrir les pièces-jointes sensibles avec un seul clic", + "hide_post_stats": "Masquer les statistiques des messages (ex. le nombre de favoris)", + "hide_user_stats": "Masquer les statistiques de compte (ex. le nombre de suivis)", "hide_filtered_statuses": "Masquer les statuts filtrés", - "import_blocks_from_a_csv_file": "Importer les blocages depuis un fichier csv", - "import_followers_from_a_csv_file": "Importer des abonnements depuis un fichier csv", + "import_blocks_from_a_csv_file": "Import de blocages depuis un fichier CSV", + "import_followers_from_a_csv_file": "Import de suivis depuis un fichier CSV", "import_theme": "Charger le thème", "inputRadius": "Champs de texte", "checkboxRadius": "Cases à cocher", @@ -269,7 +281,7 @@ "name_bio": "Nom & Bio", "new_password": "Nouveau mot de passe", "notification_visibility": "Types de notifications à afficher", - "notification_visibility_follows": "Abonnements", + "notification_visibility_follows": "Suivis", "notification_visibility_likes": "J'aime", "notification_visibility_mentions": "Mentionnés", "notification_visibility_repeats": "Partages", @@ -278,8 +290,8 @@ "no_mutes": "Aucun masqués", "hide_follows_description": "Ne pas afficher à qui je suis abonné", "hide_followers_description": "Ne pas afficher qui est abonné à moi", - "show_admin_badge": "Afficher le badge d'Administrateurâ‹…ice sur mon profil", - "show_moderator_badge": "Afficher le badge de Modérateurâ‹…ice sur mon profil", + "show_admin_badge": "Afficher le badge d'Admin sur mon profil", + "show_moderator_badge": "Afficher le badge de Modo' sur mon profil", "nsfw_clickthrough": "Activer le clic pour dévoiler les pièces jointes et cacher l'aperçu des liens pour les statuts marqués comme sensibles", "oauth_tokens": "Jetons OAuth", "token": "Jeton", @@ -289,11 +301,11 @@ "panelRadius": "Fenêtres", "pause_on_unfocused": "Suspendre le streaming lorsque l'onglet n'est pas actif", "presets": "Thèmes prédéfinis", - "profile_background": "Image de fond", + "profile_background": "Image de fond de profil", "profile_banner": "Bannière de profil", "profile_tab": "Profil", "radii_help": "Vous pouvez ici choisir le niveau d'arrondi des angles de l'interface (en pixels)", - "replies_in_timeline": "Réponses au journal", + "replies_in_timeline": "Réponses dans le flux", "reply_visibility_all": "Montrer toutes les réponses", "reply_visibility_following": "Afficher uniquement les réponses adressées à moi ou aux personnes que je suis", "reply_visibility_self": "Afficher uniquement les réponses adressées à moi", @@ -309,7 +321,7 @@ "set_new_profile_background": "Changer d'image de fond", "set_new_profile_banner": "Changer de bannière", "settings": "Paramètres", - "subject_input_always_show": "Toujours copier le champ de sujet", + "subject_input_always_show": "Toujours afficher le champ Sujet", "subject_line_behavior": "Copier le sujet en répondant", "subject_line_email": "Similaire au courriel : « re : sujet »", "subject_line_mastodon": "Comme mastodon : copier tel quel", @@ -348,7 +360,7 @@ "use_snapshot": "Ancienne version", "help": { "upgraded_from_v2": "PleromaFE à été mis à jour, le thème peut être un peu différent que dans vos souvenirs.", - "v2_imported": "Le fichier que vous avez importé vient d'un version antérieure. Nous essayons de maximizer la compatibilité mais il peu y avoir quelques incohérences.", + "v2_imported": "Le fichier que vous avez importé vient d'une version antérieure. Nous essayons de maximizer la compatibilité mais il peut y avoir quelques incohérences.", "future_version_imported": "Le fichier importé viens d'une version postérieure de PleromaFE.", "older_version_imported": "Le fichier importé viens d'une version antérieure de PleromaFE.", "snapshot_source_mismatch": "Conflict de version : Probablement due à un retour arrière puis remise à jour de la version de PleromaFE, si vous avez charger le thème en utilisant une version antérieure vous voulez probablement utiliser la version antérieure, autrement utiliser la version postérieure.", @@ -432,7 +444,7 @@ "filter_hint": { "always_drop_shadow": "Attention, cette ombre utilise toujours {0} quand le navigateur le supporte.", "drop_shadow_syntax": "{0} ne supporte pas le paramètre {1} et mot-clé {2}.", - "avatar_inset": "Veuillez noter que combiner a la fois les ombres internes et non-internes sur les avatars peut fournir des résultats innatendus avec la transparence des avatars.", + "avatar_inset": "Veuillez noter que combiner à la fois les ombres internes et non-internes sur les avatars peut fournir des résultats inattendus avec la transparence des avatars.", "spread_zero": "Les ombres avec une dispersion > 0 apparaitrons comme si ils étaient à zéro", "inset_classic": "L'ombre interne utilisera toujours {0}" }, @@ -487,15 +499,15 @@ }, "change_email": "Changer de courriel", "domain_mutes": "Domaines", - "pad_emoji": "Rajouter un espace autour de l'émoji après l’avoir choisit", + "pad_emoji": "Entourer les émoji d'espaces après leur sélections", "notification_visibility_emoji_reactions": "Réactions", "hide_follows_count_description": "Masquer le nombre de suivis", "useStreamingApiWarning": "(Non recommandé, expérimental, connu pour rater des messages)", "type_domains_to_mute": "Chercher les domaines à masquer", "fun": "Rigolo", "greentext": "greentexting", - "allow_following_move": "Suivre automatiquement quand ce compte migre", - "change_email_error": "Il y a eu un problème pour charger votre courriel.", + "allow_following_move": "Activer le suivit automatique à la migration des comptes", + "change_email_error": "Il y a eu un problème pour changer votre courriel.", "changed_email": "Courriel changé avec succès !", "discoverable": "Permettre de découvrir ce compte dans les résultats de recherche web et autres services", "emoji_reactions_on_timeline": "Montrer les émojis-réactions dans le flux", @@ -510,7 +522,7 @@ "accent": "Accent", "chatMessageRadius": "Message de chat", "bot": "Ce compte est un robot", - "import_mutes_from_a_csv_file": "Importer les masquages depuis un fichier CSV", + "import_mutes_from_a_csv_file": "Import de masquages depuis un fichier CSV", "mutes_imported": "Masquages importés ! Leur application peut prendre du temps.", "mute_import_error": "Erreur à l'import des masquages", "mute_import": "Import des masquages", @@ -518,24 +530,36 @@ "mute_export": "Export des masquages", "notification_setting_hide_notification_contents": "Cacher l'expéditeur et le contenu des notifications push", "notification_setting_block_from_strangers": "Bloquer les notifications des utilisateurâ‹…iceâ‹…s que vous ne suivez pas", - "virtual_scrolling": "Optimiser le rendu du fil d'actualité", + "virtual_scrolling": "Optimiser le rendu des flux", "reset_background_confirm": "Voulez-vraiment réinitialiser l'arrière-plan ?", "reset_banner_confirm": "Voulez-vraiment réinitialiser la bannière ?", "reset_avatar_confirm": "Voulez-vraiment réinitialiser l'avatar ?", "reset_profile_banner": "Réinitialiser la bannière du profil", - "reset_profile_background": "Réinitialiser l'arrière-plan du profil", + "reset_profile_background": "Réinitialiser le fond du profil", "reset_avatar": "Réinitialiser l'avatar", "profile_fields": { "value": "Contenu", - "name": "Étiquette", - "add_field": "Ajouter un champ" - } + "name": "Nom du champ", + "add_field": "Ajouter un champ", + "label": "Champs du profil" + }, + "hide_media_previews": "Cacher la prévisualisation des pièces jointes", + "mutes_and_blocks": "Masquage et Blocages", + "setting_changed": "Préférence modifiée", + "more_settings": "Plus de préférences", + "sensitive_by_default": "Marquer les messages comme sensible par défaut", + "reply_visibility_self_short": "Uniquement les réponses à moi", + "reply_visibility_following_short": "Montrer les réponses à mes suivis", + "hide_wallpaper": "Cacher le fond d'écran", + "hide_all_muted_posts": "Cacher les messages masqués", + "word_filter": "Filtrage par mots", + "save": "Enregistrer les changements" }, "timeline": { "collapse": "Fermer", "conversation": "Conversation", "error_fetching": "Erreur en cherchant les mises à jour", - "load_older": "Afficher plus", + "load_older": "Afficher des status plus ancien", "no_retweet_hint": "Le message est marqué en abonnés-seulement ou direct et ne peut pas être partagé", "repeated": "a partagé", "show_new": "Afficher plus", @@ -543,14 +567,16 @@ "no_more_statuses": "Pas plus de statuts", "no_statuses": "Aucun statuts", "reload": "Recharger", - "error": "Erreur lors de l'affichage du fil d'actualité : {0}" + "error": "Erreur lors de l'affichage du flux : {0}", + "socket_broke": "Connexion temps-réel perdue : CloseEvent code {0}", + "socket_reconnected": "Connexion temps-réel établie" }, "status": { "favorites": "Favoris", "repeats": "Partages", "delete": "Supprimer statuts", - "pin": "Agraffer sur le profil", - "unpin": "Dégraffer du profil", + "pin": "Agrafer sur le profil", + "unpin": "Dégrafer du profil", "pinned": "Agraffé", "delete_confirm": "Voulez-vous vraiment supprimer ce statuts ?", "reply_to": "Réponse à ", @@ -630,10 +656,17 @@ "moderator": "Modérateurâ‹…ice", "admin": "Administrateurâ‹…ice" }, - "message": "Message" + "message": "Message", + "highlight": { + "disabled": "Sans mise-en-valeur", + "solid": "Fond uni", + "side": "Coté rayé", + "striped": "Fond rayé" + }, + "bot": "Robot" }, "user_profile": { - "timeline_title": "Journal de l'utilisateurâ‹…ice", + "timeline_title": "Flux du compte", "profile_does_not_exist": "Désolé, ce profil n'existe pas.", "profile_loading_error": "Désolé, il y a eu une erreur au chargement du profil." }, @@ -669,45 +702,45 @@ "message": "Envoi échoué : {0}" }, "file_size_units": { - "B": "O", - "KiB": "KiO", - "MiB": "MiO", - "GiB": "GiO", - "TiB": "TiO" + "B": "o", + "KiB": "Ko", + "MiB": "Mo", + "GiB": "Go", + "TiB": "To" } }, "about": { "mrf": { "keyword": { - "reject": "Rejeté", - "replace": "Remplacer", - "keyword_policies": "Politiques par mot-clés", + "reject": "Rejette", + "replace": "Remplace", + "keyword_policies": "Filtrage par mots-clés", "ftl_removal": "Suppression du flux fédéré", "is_replaced_by": "→" }, "simple": { "simple_policies": "Politiques par instances", - "accept": "Accepter", - "accept_desc": "Cette instance accepte des messages seulement depuis ces instances :", - "reject": "Rejeter", + "accept": "Acceptées", + "accept_desc": "Cette instance accepte les messages seulement depuis ces instances :", + "reject": "Rejetées", "reject_desc": "Cette instance n'acceptera pas de message de ces instances :", "quarantine": "Quarantaine", - "quarantine_desc": "Cette instance enverras seulement des messages publics à ces instances :", - "ftl_removal_desc": "Cette instance supprime ces instance du flux fédéré :", - "media_removal": "Suppression multimédia", + "quarantine_desc": "Cette instance enverra seulement des messages publics à ces instances :", + "ftl_removal_desc": "Cette instance supprime les instance suivantes du flux fédéré :", + "media_removal": "Suppression des pièce-jointes", "media_removal_desc": "Cette instance supprime le contenu multimédia des instances suivantes :", "media_nsfw": "Force le contenu multimédia comme sensible", - "ftl_removal": "Suppression du flux fédéré", - "media_nsfw_desc": "Cette instance force le contenu multimédia comme sensible pour les messages des instances suivantes :" + "ftl_removal": "Supprimées du flux fédéré", + "media_nsfw_desc": "Cette instance force les pièce-jointes comme sensible pour les messages des instances suivantes :" }, "federation": "Fédération", - "mrf_policies": "Politiques MRF activées", + "mrf_policies": "Politiques MRF actives", "mrf_policies_desc": "Les politiques MRF modifient la fédération entre les instances. Les politiques suivantes sont activées :" }, "staff": "Staff" }, "domain_mute_card": { - "mute": "Muet", + "mute": "Masqué", "mute_progress": "Masquage…", "unmute": "Démasquer", "unmute_progress": "Démasquage…" @@ -724,7 +757,9 @@ "expires_in": "Fin du sondage dans {0}", "not_enough_options": "Trop peu d'options unique au sondage", "vote": "Voter", - "expired": "Sondage terminé il y a {0}" + "expired": "Sondage terminé il y a {0}", + "people_voted_count": "{count} voteur | {count} voteurs", + "votes_count": "{count} vote | {count} votes" }, "emoji": { "emoji": "Émoji", @@ -735,11 +770,11 @@ "load_all": "Charger tout les {emojiAmount} émojis", "load_all_hint": "{saneAmount} émojis chargé, charger tout les émojis peuvent causer des problèmes de performances.", "stickers": "Stickers", - "keep_open": "Garder le sélecteur ouvert" + "keep_open": "Garder ouvert" }, "remote_user_resolver": { "error": "Non trouvé.", - "searching_for": "Rechercher", + "searching_for": "Recherche pour", "remote_user_resolver": "Résolution de compte distant" }, "time": { diff --git a/src/i18n/it.json b/src/i18n/it.json index 320282624bf60740eef66fa70d2f93d263c68aeb..1c89bd7975f748c240d144c3efdbb3c9e4f74c07 100644 --- a/src/i18n/it.json +++ b/src/i18n/it.json @@ -27,7 +27,7 @@ "mentions": "Menzioni", "public_tl": "Sequenza pubblica", "timeline": "Sequenza personale", - "twkn": "Sequenza globale", + "twkn": "Sequenza federale", "chat": "Chat della stanza", "friend_requests": "Vogliono seguirti", "about": "Informazioni", @@ -41,14 +41,15 @@ "preferences": "Preferenze", "bookmarks": "Segnalibri", "chats": "Conversazioni", - "timelines": "Sequenze" + "timelines": "Sequenze", + "home_timeline": "Sequenza personale" }, "notifications": { "followed_you": "ti segue", "notifications": "Notifiche", "read": "Letto!", "broken_favorite": "Stato sconosciuto, lo sto cercando…", - "favorited_you": "gradisce il tuo messaggio", + "favorited_you": "ha gradito", "load_older": "Carica notifiche precedenti", "repeated_you": "ha condiviso il tuo messaggio", "follow_request": "vuole seguirti", @@ -71,10 +72,10 @@ "name_bio": "Nome ed introduzione", "nsfw_clickthrough": "Fai click per visualizzare gli allegati offuscati", "profile_background": "Sfondo della tua pagina", - "profile_banner": "Stendardo del tuo profilo", + "profile_banner": "Gonfalone del tuo profilo", "set_new_avatar": "Scegli una nuova icona", - "set_new_profile_background": "Scegli un nuovo sfondo per la tua pagina", - "set_new_profile_banner": "Scegli un nuovo stendardo per il tuo profilo", + "set_new_profile_background": "Scegli un nuovo sfondo", + "set_new_profile_banner": "Scegli un nuovo gonfalone", "settings": "Impostazioni", "theme": "Tema", "user_settings": "Impostazioni Utente", @@ -83,9 +84,9 @@ "avatarRadius": "Icone utente", "background": "Sfondo", "btnRadius": "Pulsanti", - "cBlue": "Blu (risposte, seguire)", + "cBlue": "Blu (rispondi, segui)", "cGreen": "Verde (ripeti)", - "cOrange": "Arancione (gradire)", + "cOrange": "Arancione (gradisci)", "cRed": "Rosso (annulla)", "change_password": "Cambia password", "change_password_error": "C'è stato un problema durante il cambiamento della password.", @@ -98,7 +99,7 @@ "delete_account": "Elimina profilo", "delete_account_description": "Elimina definitivamente i tuoi dati e disattiva il tuo profilo.", "delete_account_error": "C'è stato un problema durante l'eliminazione del tuo profilo. Se il problema persiste contatta l'amministratore della tua stanza.", - "delete_account_instructions": "Digita la tua password nel campo sottostante per confermare l'eliminazione del tuo profilo.", + "delete_account_instructions": "Digita la tua password nel campo sottostante per eliminare il tuo profilo.", "export_theme": "Salva impostazioni", "follow_export": "Esporta la lista di chi segui", "follow_export_button": "Esporta la lista di chi segui in un file CSV", @@ -109,7 +110,7 @@ "foreground": "Primo piano", "general": "Generale", "hide_post_stats": "Nascondi statistiche dei messaggi (es. il numero di preferenze)", - "hide_user_stats": "Nascondi statistiche dell'utente (es. il numero dei tuoi seguaci)", + "hide_user_stats": "Nascondi statistiche dell'utente (es. il numero di seguaci)", "import_followers_from_a_csv_file": "Importa una lista di chi segui da un file CSV", "import_theme": "Carica impostazioni", "inputRadius": "Campi di testo", @@ -118,12 +119,12 @@ "invalid_theme_imported": "Il file selezionato non è un tema supportato da Pleroma. Il tuo tema non è stato modificato.", "limited_availability": "Non disponibile nel tuo browser", "links": "Collegamenti", - "lock_account_description": "Limita il tuo account solo a seguaci approvati", + "lock_account_description": "Vaglia manualmente i nuovi seguaci", "loop_video": "Riproduci video in ciclo continuo", - "loop_video_silent_only": "Riproduci solo video senza audio in ciclo continuo (es. le \"gif\" di Mastodon)", + "loop_video_silent_only": "Riproduci solo video muti in ciclo continuo (es. le \"gif\" di Mastodon)", "new_password": "Nuova password", "notification_visibility": "Tipi di notifiche da mostrare", - "notification_visibility_follows": "Nuove persone ti seguono", + "notification_visibility_follows": "Nuovi seguaci", "notification_visibility_likes": "Preferiti", "notification_visibility_mentions": "Menzioni", "notification_visibility_repeats": "Condivisioni", @@ -138,7 +139,7 @@ "presets": "Valori predefiniti", "profile_tab": "Profilo", "radii_help": "Imposta il raggio degli angoli (in pixel)", - "replies_in_timeline": "Risposte nella sequenza personale", + "replies_in_timeline": "Risposte nelle sequenze", "reply_visibility_all": "Mostra tutte le risposte", "reply_visibility_following": "Mostra solo le risposte rivolte a me o agli utenti che seguo", "reply_visibility_self": "Mostra solo risposte rivolte a me", @@ -148,7 +149,7 @@ "stop_gifs": "Riproduci GIF al passaggio del cursore", "streaming": "Mostra automaticamente i nuovi messaggi quando sei in cima alla pagina", "text": "Testo", - "theme_help": "Usa codici colore esadecimali (#rrggbb) per personalizzare il tuo schema di colori.", + "theme_help": "Usa colori esadecimali (#rrggbb) per personalizzare il tuo schema di colori.", "tooltipRadius": "Suggerimenti/avvisi", "values": { "false": "no", @@ -156,7 +157,7 @@ }, "avatar_size_instruction": "La taglia minima per l'icona personale è 150x150 pixel.", "domain_mutes": "Domini", - "discoverable": "Permetti la scoperta di questo profilo da servizi di ricerca ed altro", + "discoverable": "Permetti la scoperta di questo profilo a servizi di ricerca ed altro", "composing": "Composizione", "changed_email": "Email cambiata con successo!", "change_email_error": "C'è stato un problema nel cambiare la tua email.", @@ -167,18 +168,18 @@ "block_import": "Importa blocchi", "block_export_button": "Esporta i tuoi blocchi in un file CSV", "block_export": "Esporta blocchi", - "allow_following_move": "Consenti", + "allow_following_move": "Consenti l'iscrizione automatica ai profili traslocati", "mfa": { "verify": { "desc": "Per abilitare l'autenticazione bifattoriale, inserisci il codice fornito dalla tua applicazione:" }, "scan": { "secret_code": "Codice", - "desc": "Con la tua applicazione bifattoriale, acquisisci questo QR o inserisci il codice manualmente:", + "desc": "Con la tua applicazione bifattoriale, acquisisci il QR o inserisci il codice:", "title": "Acquisisci" }, "authentication_methods": "Metodi di accesso", - "recovery_codes_warning": "Appuntati i codici o salvali in un posto sicuro, altrimenti rischi di non rivederli mai più. Se perderai l'accesso sia alla tua applicazione bifattoriale che ai codici di recupero non potrai più accedere al tuo profilo.", + "recovery_codes_warning": "Metti i codici al sicuro, perché non potrai più visualizzarli. Se perderai l'accesso sia alla tua applicazione bifattoriale che ai codici di recupero non potrai più accedere al tuo profilo.", "waiting_a_recovery_codes": "Ricevo codici di recupero…", "recovery_codes": "Codici di recupero.", "warning_of_generate_new_codes": "Alla generazione di nuovi codici di recupero, quelli vecchi saranno disattivati.", @@ -197,14 +198,14 @@ "help": { "older_version_imported": "Il tema importato è stato creato per una versione precedente dell'interfaccia.", "future_version_imported": "Il tema importato è stato creato per una versione più recente dell'interfaccia.", - "v2_imported": "Il tema importato è stato creato per una vecchia interfaccia. Non tutto potrebbe essere come prima.", - "upgraded_from_v2": "L'interfaccia è stata aggiornata, il tema potrebbe essere diverso da come lo intendevi.", + "v2_imported": "Il tema importato è stato creato per una vecchia interfaccia. Non tutto potrebbe essere come inteso.", + "upgraded_from_v2": "L'interfaccia è stata aggiornata, il tema potrebbe essere diverso da come lo ricordi.", "migration_snapshot_ok": "Ho caricato l'anteprima del tema. Puoi provare a caricarne i contenuti.", "fe_downgraded": "L'interfaccia è stata portata ad una versione precedente.", "fe_upgraded": "Lo schema dei temi è stato aggiornato insieme all'interfaccia.", "snapshot_missing": "Il tema non è provvisto di anteprima, quindi potrebbe essere diverso da come appare.", "snapshot_present": "Tutti i valori sono sostituiti dall'anteprima del tema. Puoi invece caricare i suoi contenuti.", - "snapshot_source_mismatch": "Conflitto di versione: probabilmente l'interfaccia è stata portata ad una versione precedente e poi aggiornata di nuovo. Se hai modificato il tema con una versione precedente dell'interfaccia, usa la vecchia versione del tema, altrimenti puoi usare la nuova.", + "snapshot_source_mismatch": "Conflitto di versione: probabilmente l'interfaccia è stata portata indietro e poi aggiornata di nuovo. Se hai modificato il tema con una vecchia versione usa il tema precedente, altrimenti puoi usare il nuovo.", "migration_napshot_gone": "Anteprima del tema non trovata, non tutto potrebbe essere come ricordi." }, "use_source": "Nuova versione", @@ -227,7 +228,7 @@ "contrast": { "context": { "text": "per il testo", - "18pt": "per il testo grande (oltre 17pt)" + "18pt": "per il testo oltre 17pt" }, "level": { "bad": "non soddisfa le linee guida di alcun livello", @@ -250,7 +251,7 @@ "selectedMenu": "Voce menù selezionata", "selectedPost": "Messaggio selezionato", "pressed": "Premuto", - "highlight": "Elementi evidenziati", + "highlight": "Elementi in risalto", "icons": "Icone", "poll": "Grafico sondaggi", "underlay": "Sottostante", @@ -312,8 +313,8 @@ "fonts": { "_tab_label": "Font", "custom": "Personalizzato", - "weight": "Peso (grassettatura)", - "size": "Dimensione (in pixel)", + "weight": "Grassettatura", + "size": "Dimensione in pixel", "family": "Nome font", "components": { "postCode": "Font a spaziatura fissa incluso in un messaggio", @@ -348,7 +349,7 @@ "greentext": "Frecce da meme", "upload_a_photo": "Carica un'immagine", "type_domains_to_mute": "Cerca domini da zittire", - "theme_help_v2_2": "Le icone dietro alcuni elementi sono indicatori del contrasto fra testo e sfondo, passaci sopra col puntatore per ulteriori informazioni. Se si usano delle trasparenze, questi indicatori mostrano il peggior caso possibile.", + "theme_help_v2_2": "Le icone vicino alcuni elementi sono indicatori del contrasto fra testo e sfondo, passaci sopra col puntatore per ulteriori informazioni. Se usani trasparenze, questi indicatori mostrano il peggior caso possibile.", "theme_help_v2_1": "Puoi anche forzare colore ed opacità di alcuni elementi selezionando la casella. Usa il pulsante \"Azzera\" per azzerare tutte le forzature.", "useStreamingApiWarning": "(Sconsigliato, sperimentale, può saltare messaggi)", "useStreamingApi": "Ricevi messaggi e notifiche in tempo reale", @@ -387,60 +388,70 @@ "preload_images": "Precarica immagini", "hide_isp": "Nascondi pannello della stanza", "max_thumbnails": "Numero massimo di anteprime per messaggio", - "hide_muted_posts": "Nascondi messaggi degli utenti zilenziati", + "hide_muted_posts": "Nascondi messaggi degli utenti silenziati", "accent": "Accento", - "emoji_reactions_on_timeline": "Mostra emoji di reazione sulle sequenze", + "emoji_reactions_on_timeline": "Mostra reazioni nelle sequenze", "pad_emoji": "Affianca spazi agli emoji inseriti tramite selettore", "notification_blocks": "Bloccando un utente non riceverai più le sue notifiche né lo seguirai più.", - "mutes_and_blocks": "Zittiti e bloccati", + "mutes_and_blocks": "Silenziati e bloccati", "profile_fields": { "value": "Contenuto", - "name": "Etichetta", + "name": "Descrizione", "add_field": "Aggiungi campo", "label": "Metadati profilo" }, - "bot": "Questo profilo è di un robot", + "bot": "Questo è un robot", "version": { "frontend_version": "Versione interfaccia", "backend_version": "Versione backend", "title": "Versione" }, "reset_avatar": "Azzera icona", - "reset_profile_background": "Azzera sfondo profilo", - "reset_profile_banner": "Azzera stendardo profilo", + "reset_profile_background": "Azzera sfondo", + "reset_profile_banner": "Azzera gonfalone", "reset_avatar_confirm": "Vuoi veramente azzerare l'icona?", - "reset_banner_confirm": "Vuoi veramente azzerare lo stendardo?", + "reset_banner_confirm": "Vuoi veramente azzerare il gonfalone?", "reset_background_confirm": "Vuoi veramente azzerare lo sfondo?", "chatMessageRadius": "Messaggi istantanei", "notification_setting_hide_notification_contents": "Nascondi mittente e contenuti delle notifiche push", "notification_setting_block_from_strangers": "Blocca notifiche da utenti che non segui", "virtual_scrolling": "Velocizza l'elaborazione delle sequenze", "import_mutes_from_a_csv_file": "Importa silenziati da un file CSV", - "mutes_imported": "Silenziati importati! Saranno elaborati a breve.", + "mutes_imported": "Silenziati importati! Elaborazione in corso.", "mute_import_error": "Errore nell'importazione", - "mute_import": "Importa silenziati", - "mute_export_button": "Esporta la tua lista di silenziati in un file CSV", + "mute_import": "Carica silenziati", + "mute_export_button": "Esporta i silenziati in un file CSV", "mute_export": "Esporta silenziati", "hide_wallpaper": "Nascondi sfondo della stanza", - "setting_changed": "Valore personalizzato" + "setting_changed": "Valore personalizzato", + "more_settings": "Altre impostazioni", + "sensitive_by_default": "Tutti i miei messaggi sono scabrosi", + "reply_visibility_self_short": "Vedi solo risposte a te", + "reply_visibility_following_short": "Vedi risposte a messaggi di altri", + "hide_all_muted_posts": "Nascondi messaggi silenziati", + "hide_media_previews": "Nascondi anteprime", + "word_filter": "Parole filtrate", + "save": "Salva modifiche" }, "timeline": { "error_fetching": "Errore nell'aggiornamento", "load_older": "Carica messaggi più vecchi", "show_new": "Mostra nuovi", "up_to_date": "Aggiornato", - "collapse": "Riduci", + "collapse": "Ripiega", "conversation": "Conversazione", "no_retweet_hint": "Il messaggio è diretto o solo per seguaci e non può essere condiviso", - "repeated": "condiviso", + "repeated": "ha condiviso", "no_statuses": "Nessun messaggio", "no_more_statuses": "Fine dei messaggi", "reload": "Ricarica", - "error": "Errore nel caricare la sequenza: {0}" + "error": "Errore nel caricare la sequenza: {0}", + "socket_broke": "Connessione tempo reale interrotta: codice {0}", + "socket_reconnected": "Connesso in tempo reale" }, "user_card": { "follow": "Segui", - "followees": "Chi stai seguendo", + "followees": "Segue", "followers": "Seguaci", "following": "Seguìto!", "follows_you": "Ti segue!", @@ -454,13 +465,13 @@ "deny": "Nega", "remote_follow": "Segui da remoto", "admin_menu": { - "delete_user_confirmation": "Ne sei completamente sicuro? Quest'azione non può essere annullata.", + "delete_user_confirmation": "Ne sei completamente sicuro? Non potrai tornare indietro.", "delete_user": "Elimina utente", "quarantine": "I messaggi non arriveranno alle altre stanze", "disable_any_subscription": "Rendi utente non seguibile", "disable_remote_subscription": "Blocca i tentativi di seguirlo da altre stanze", "sandbox": "Rendi tutti i messaggi solo per seguaci", - "force_unlisted": "Rendi tutti i messaggi invisibili", + "force_unlisted": "Nascondi tutti i messaggi", "strip_media": "Rimuovi ogni allegato ai messaggi", "force_nsfw": "Oscura tutti i messaggi", "delete_account": "Elimina profilo", @@ -474,7 +485,7 @@ }, "show_repeats": "Mostra condivisioni", "hide_repeats": "Nascondi condivisioni", - "mute_progress": "Zittisco…", + "mute_progress": "Silenzio…", "unmute_progress": "Riabilito…", "unmute": "Riabilita", "block_progress": "Blocco…", @@ -483,7 +494,7 @@ "unsubscribe": "Disdici", "subscribe": "Abbònati", "report": "Segnala", - "mention": "Menzioni", + "mention": "Menziona", "media": "Media", "its_you": "Sei tu!", "hidden": "Nascosto", @@ -493,7 +504,13 @@ "follow_sent": "Richiesta inviata!", "favorites": "Preferiti", "message": "Contatta", - "bot": "Bot" + "bot": "Bot", + "highlight": { + "side": "Nastro a lato", + "striped": "A righe", + "solid": "Un colore", + "disabled": "Nessun risalto" + } }, "chat": { "title": "Chat" @@ -549,21 +566,22 @@ "direct": "Diretto - Visibile solo agli utenti menzionati", "private": "Solo per seguaci - Visibile solo dai tuoi seguaci", "public": "Pubblico - Visibile sulla sequenza pubblica", - "unlisted": "Non elencato - Non visibile sulla sequenza pubblica" + "unlisted": "Nascosto - Non visibile sulla sequenza pubblica" }, "scope_notice": { "unlisted": "Questo messaggio non sarà visibile sulla sequenza locale né su quella pubblica", "private": "Questo messaggio sarà visibile solo ai tuoi seguaci", "public": "Questo messaggio sarà visibile a tutti" }, - "direct_warning_to_first_only": "Questo messaggio sarà visibile solo agli utenti menzionati all'inizio.", + "direct_warning_to_first_only": "Questo messaggio sarà visibile solo agli utenti menzionati in testa.", "direct_warning_to_all": "Questo messaggio sarà visibile a tutti i menzionati.", "new_status": "Nuovo messaggio", - "empty_status_error": "Non puoi pubblicare messaggi vuoti senza allegati", + "empty_status_error": "Aggiungi del testo o degli allegati", "preview_empty": "Vuoto", "preview": "Anteprima", "media_description_error": "Allegati non caricati, riprova", - "media_description": "Descrizione allegati" + "media_description": "Descrizione allegati", + "post": "Pubblica" }, "registration": { "bio": "Introduzione", @@ -583,13 +601,14 @@ "bio_placeholder": "es.\nCiao, sono Lupo Lucio.\nSono un lupo fantastico che vive nel Fantabosco. Forse mi hai visto alla Melevisione.", "fullname_placeholder": "es. Lupo Lucio", "username_placeholder": "es. mister_wolf", - "new_captcha": "Clicca l'immagine per avere un altro captcha", + "new_captcha": "Clicca il captcha per averne uno nuovo", "captcha": "CAPTCHA", "reason_placeholder": "L'amministratore esamina ciascuna richiesta.\nFornisci il motivo della tua iscrizione.", - "reason": "Motivo dell'iscrizione" + "reason": "Motivo dell'iscrizione", + "register": "Registrati" }, "user_profile": { - "timeline_title": "Sequenza dell'Utente", + "timeline_title": "Sequenza dell'utente", "profile_loading_error": "Spiacente, c'è stato un errore nel caricamento del profilo.", "profile_does_not_exist": "Spiacente, questo profilo non esiste." }, @@ -605,7 +624,7 @@ "replace": "Sostituisci", "is_replaced_by": "→", "keyword_policies": "Regole per parole chiave", - "ftl_removal": "Rimozione dalla sequenza globale" + "ftl_removal": "Rimozione dalla sequenza federale" }, "simple": { "reject": "Rifiuta", @@ -615,8 +634,8 @@ "reject_desc": "Questa stanza rifiuterà i messaggi provenienti dalle seguenti:", "quarantine": "Quarantena", "quarantine_desc": "Questa stanza inoltrerà solo messaggi pubblici alle seguenti:", - "ftl_removal": "Rimozione dalla sequenza globale", - "ftl_removal_desc": "Questa stanza rimuove le seguenti dalla sequenza globale:", + "ftl_removal": "Rimozione dalla sequenza federale", + "ftl_removal_desc": "Questa stanza rimuove le seguenti dalla sequenza federale:", "media_removal": "Rimozione multimedia", "media_removal_desc": "Questa istanza rimuove gli allegati dalle seguenti stanze:", "media_nsfw": "Allegati oscurati d'ufficio", @@ -705,8 +724,8 @@ "favorites": "Preferiti", "hide_content": "Nascondi contenuti", "show_content": "Mostra contenuti", - "hide_full_subject": "Nascondi intero oggetto", - "show_full_subject": "Mostra intero oggetto", + "hide_full_subject": "Nascondi oggetto intero", + "show_full_subject": "Mostra oggetto intero", "thread_muted_and_words": ", contiene:", "thread_muted": "Discussione silenziata", "copy_link": "Copia collegamento", @@ -714,46 +733,46 @@ "unmute_conversation": "Riabilita conversazione", "mute_conversation": "Silenzia conversazione", "replies_list": "Risposte:", - "reply_to": "Rispondi a", + "reply_to": "In risposta a", "delete_confirm": "Vuoi veramente eliminare questo messaggio?", "unbookmark": "Rimuovi segnalibro", "bookmark": "Aggiungi segnalibro", "status_deleted": "Questo messagio è stato cancellato", - "nsfw": "Pruriginoso", + "nsfw": "DISDICEVOLE", "external_source": "Vai al sito", "expand": "Espandi" }, "time": { - "years_short": "{0}a", - "year_short": "{0}a", + "years_short": "{0} a", + "year_short": "{0} a", "years": "{0} anni", "year": "{0} anno", - "weeks_short": "{0}set", - "week_short": "{0}set", - "seconds_short": "{0}sec", - "second_short": "{0}sec", + "weeks_short": "{0} set", + "week_short": "{0} set", + "seconds_short": "{0} sec", + "second_short": "{0} sec", "weeks": "{0} settimane", "week": "{0} settimana", "seconds": "{0} secondi", "second": "{0} secondo", - "now_short": "ora", + "now_short": "adesso", "now": "adesso", - "months_short": "{0}me", - "month_short": "{0}me", + "months_short": "{0} ms", + "month_short": "{0} ms", "months": "{0} mesi", "month": "{0} mese", - "minutes_short": "{0}min", - "minute_short": "{0}min", + "minutes_short": "{0} min", + "minute_short": "{0} min", "minutes": "{0} minuti", "minute": "{0} minuto", "in_past": "{0} fa", "in_future": "fra {0}", - "hours_short": "{0}h", - "days_short": "{0}g", - "hour_short": "{0}h", + "hours_short": "{0} h", + "days_short": "{0} g", + "hour_short": "{0} h", "hours": "{0} ore", "hour": "{0} ora", - "day_short": "{0}g", + "day_short": "{0} g", "days": "{0} giorni", "day": "{0} giorno" }, @@ -767,7 +786,7 @@ "add_comment_description": "La segnalazione sarà inviata ai moderatori della tua stanza. Puoi motivarla qui sotto:" }, "password_reset": { - "password_reset_required_but_mailer_is_disabled": "Devi reimpostare la tua password, ma non puoi farlo. Contatta il tuo amministratore.", + "password_reset_required_but_mailer_is_disabled": "Devi reimpostare la tua password, ma non puoi farlo. Contatta l'amministratore.", "password_reset_required": "Devi reimpostare la tua password per poter continuare.", "password_reset_disabled": "Non puoi azzerare la tua password. Contatta il tuo amministratore.", "too_many_requests": "Hai raggiunto il numero massimo di tentativi, riprova più tardi.", @@ -808,7 +827,7 @@ "add_reaction": "Reagisci", "favorite": "Gradisci", "reply": "Rispondi", - "repeat": "Ripeti", + "repeat": "Condividi", "media_upload": "Carica allegati" }, "display_date": { diff --git a/src/i18n/ja_pedantic.json b/src/i18n/ja_pedantic.json index 846c793ac1f23ec35861195f0581d0fe248af67f..9074f4bf8e029a30f905e4b06220cceb015a7a46 100644 --- a/src/i18n/ja_pedantic.json +++ b/src/i18n/ja_pedantic.json @@ -574,7 +574,13 @@ "allow_following_move": "フォãƒãƒ¼ä¸ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒå¼•ã£è¶Šã—ãŸã¨ãã€è‡ªå‹•ãƒ•ã‚©ãƒãƒ¼ã‚’許å¯ã™ã‚‹", "setting_changed": "è¦å®šã®è¨å®šã¨ç•°ãªã£ã¦ã„ã¾ã™", "greentext": "引用を緑色ã§è¡¨ç¤º", - "sensitive_by_default": "ã¯ã˜ã‚ã‹ã‚‰æŠ•ç¨¿ã‚’センシティブã¨ã—ã¦è¨å®š" + "sensitive_by_default": "ã¯ã˜ã‚ã‹ã‚‰æŠ•ç¨¿ã‚’センシティブã¨ã—ã¦è¨å®š", + "more_settings": "ãã®ä»–ã®è¨å®š", + "reply_visibility_self_short": "自分宛ã®ãƒªãƒ—ライを見る", + "reply_visibility_following_short": "フォãƒãƒ¼ã—ã¦ã„る人ã«å®›ã¦ã‚‰ã‚ŒãŸãƒªãƒ—ライを見る", + "hide_all_muted_posts": "ミュートã—ãŸæŠ•ç¨¿ã‚’éš ã™", + "hide_media_previews": "メディアã®ãƒ—ãƒ¬ãƒ“ãƒ¥ãƒ¼ã‚’éš ã™", + "word_filter": "å˜èªžãƒ•ã‚£ãƒ«ã‚¿" }, "time": { "day": "{0}æ—¥", @@ -710,7 +716,13 @@ "hide_repeats": "ãƒªãƒ”ãƒ¼ãƒˆã‚’éš ã™", "message": "メッセージ", "hidden": "éš ã™", - "bot": "bot" + "bot": "bot", + "highlight": { + "solid": "背景をå˜è‰²ã«ã™ã‚‹", + "striped": "背景を縞模様ã«ã™ã‚‹", + "side": "端ã«ç·šã‚’付ã‘ã‚‹", + "disabled": "強調ã—ãªã„" + } }, "user_profile": { "timeline_title": "ユーザータイムライン", diff --git a/src/i18n/ko.json b/src/i18n/ko.json index c8d213f3e03b63eea2f915059815db4095e4b15e..4aae13b7d357a5a5b7513302112e4b69522bc648 100644 --- a/src/i18n/ko.json +++ b/src/i18n/ko.json @@ -405,7 +405,8 @@ "change_email": "ì „ìžë©”ì¼ ì£¼ì†Œ 바꾸기", "changed_email": "ë©”ì¼ì£¼ì†Œê°€ ê°±ì‹ ë˜ì—ˆìŠµë‹ˆë‹¤!", "bot": "ì´ ê³„ì •ì€ bot입니다", - "mutes_tab": "침묵" + "mutes_tab": "침묵", + "app_name": "앱 ì´ë¦„" }, "timeline": { "collapse": "ì ‘ê¸°", @@ -468,7 +469,8 @@ }, "interactions": { "follows": "새 팔로워", - "favs_repeats": "반복과 ì¦ê²¨ì°¾ê¸°" + "favs_repeats": "반복과 ì¦ê²¨ì°¾ê¸°", + "moves": "ê³„ì • 통합" }, "emoji": { "load_all": "ì „ì²´ {emojiAmount} ì´ëª¨ì§€ 불러오기", @@ -581,6 +583,10 @@ "day": "{0} ì¼" }, "remote_user_resolver": { - "error": "ì°¾ì„ ìˆ˜ 없습니다." + "error": "ì°¾ì„ ìˆ˜ 없습니다.", + "searching_for": "검색중" + }, + "selectable_list": { + "select_all": "ëª¨ë‘ ì„ íƒ" } } diff --git a/src/i18n/nb.json b/src/i18n/nb.json index 8bd745f0f0e77de075140b8e59f0258ca42ed2b0..e0dffe833ba64b44d02035ce604b6b91393b16fd 100644 --- a/src/i18n/nb.json +++ b/src/i18n/nb.json @@ -41,8 +41,8 @@ }, "importer": { "submit": "Send", - "success": "Importering fullført", - "error": "Det oppsto en feil under importering av denne filen" + "success": "Importering fullført.", + "error": "Det oppsto en feil under importering av denne filen." }, "login": { "login": "Logg inn", @@ -85,7 +85,7 @@ "bookmarks": "Bokmerker" }, "notifications": { - "broken_favorite": "Ukjent status, leter etter den...", + "broken_favorite": "Ukjent status, leter etter den…", "favorited_you": "likte din status", "followed_you": "fulgte deg", "load_older": "Last eldre varsler", @@ -447,7 +447,8 @@ "title": "Versjon", "backend_version": "Backend Versjon", "frontend_version": "Frontend Versjon" - } + }, + "hide_wallpaper": "Skjul instansens bakgrunnsbilde" }, "time": { "day": "{0} dag", @@ -602,5 +603,22 @@ "person_talking": "{count} person snakker om dette", "people_talking": "{count} personer snakker om dette", "no_results": "Ingen resultater" + }, + "about": { + "mrf": { + "simple": { + "quarantine": "Karantene", + "reject_desc": "Denne instansen vil ikke godta meldinger fra følgende instanser:", + "reject": "Avvis", + "accept_desc": "Denne instansen godtar kun meldinger fra følgende instanser:", + "accept": "Aksepter" + }, + "keyword": { + "is_replaced_by": "→", + "replace": "Erstatt", + "reject": "Avvis", + "ftl_removal": "Fjerning fra \"Det hele kjente nettverket\" Tidslinjen" + } + } } } diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 6d230e69360a00802bc73fef5b395d078c912da3..7c20ad8b504ec284ac3938f28007c579550a4e62 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -13,7 +13,7 @@ "disable": "Оключить", "enable": "Включить", "confirm": "Подтвердить", - "verify": "Проверить", + "verify": "Подтверждение", "more": "Больше", "generic_error": "Произошла ошибка", "optional": "не обÑзательно", @@ -44,8 +44,8 @@ "heading": { "TotpForm": "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ", "RecoveryForm": "Two-factor recovery", - "totp": "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ", - "recovery": "Двухфакторное возвращение аккаунта" + "totp": "ДвухÑÑ‚Ð°Ð¿Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ", + "recovery": "ВоÑÑтановление двухÑтапной аутентификации" }, "hint": "Войдите чтобы приÑоединитьÑÑ Ðº диÑкуÑÑии", "description": "Войти Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ OAuth" @@ -55,8 +55,8 @@ "chat": "Локальный чат", "mentions": "УпоминаниÑ", "interactions": "ВзаимодейÑтвиÑ", - "public_tl": "ÐŸÑƒÐ±Ð»Ð¸Ñ‡Ð½Ð°Ñ Ð»ÐµÐ½Ñ‚Ð°", - "timeline": "Лента", + "public_tl": "Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ð»ÐµÐ½Ñ‚Ð°", + "timeline": "ГлавнаÑ", "twkn": "Ð¤ÐµÐ´ÐµÑ€Ð°Ñ‚Ð¸Ð²Ð½Ð°Ñ Ð»ÐµÐ½Ñ‚Ð°", "search": "ПоиÑк", "friend_requests": "ЗапроÑÑ‹ на чтение", @@ -65,10 +65,11 @@ "timelines": "Ленты", "preferences": "ÐаÑтройки", "who_to_follow": "Кого читать", - "dms": "Личные СообщениÑ", + "dms": "Личные ÑообщениÑ", "administration": "Панель админиÑтратора", - "about": "О Ñервере", - "user_search": "ПоиÑк пользователей" + "about": "Об узле", + "user_search": "ПоиÑк пользователей", + "home_timeline": "ГлавнаÑ" }, "notifications": { "broken_favorite": "ÐеизвеÑтный ÑтатуÑ, ищем…", @@ -79,35 +80,35 @@ "read": "ПрочеÑÑ‚ÑŒ", "repeated_you": "повторил(а) ваш ÑтатуÑ", "follow_request": "хочет читать ваÑ", - "reacted_with": "добавил реакцию: {0}", - "migrated_to": "мигрировал на", + "reacted_with": "добавил(а) реакцию: {0}", + "migrated_to": "перехал на", "no_more_notifications": "Ðет дальнейших уведомлений", "error": "Ошибка при обновлении уведомлений: {0}" }, "interactions": { - "favs_repeats": "Повторы и фавориты", + "favs_repeats": "Повторы и отметки «ÐравитÑÑ»", "follows": "Ðовые читатели", "load_older": "Загрузить Ñтарые взаимодейÑтвиÑ", - "moves": "Миграции пользователей" + "moves": "Переезды" }, "post_status": { - "account_not_locked_warning": "Ваш аккаунт не {0}. Кто угодно может начать читать Ð²Ð°Ñ Ñ‡Ñ‚Ð¾Ð±Ñ‹ видеть поÑÑ‚Ñ‹ только Ð´Ð»Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñчиков.", - "account_not_locked_warning_link": "залочен", - "attachments_sensitive": "Ð’Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñодержат чувÑтвительный контент", + "account_not_locked_warning": "Ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ не {0}. Кто угодно может начать читать Ð²Ð°Ñ Ñ‡Ñ‚Ð¾Ð±Ñ‹ видеть ÑтатуÑÑ‹ только Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ñ‚ÐµÐ»ÐµÐ¹.", + "account_not_locked_warning_link": "закрыт", + "attachments_sensitive": "Ð’Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð¼ÐµÑŽÑ‚ щекотливый характер", "content_warning": "Тема (не обÑзательно)", "default": "Что нового?", "direct_warning": "Ðтот поÑÑ‚ будет виден только упомÑнутым пользователÑм", "posting": "ОтправлÑетÑÑ", "scope_notice": { - "public": "Ðтот поÑÑ‚ будет виден вÑем", - "private": "Ðтот поÑÑ‚ будет виден только вашим подпиÑчикам", - "unlisted": "Ðтот поÑÑ‚ не будет виден в публичной и федеративной ленте" + "public": "Ðтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð±ÑƒÐ´ÐµÑ‚ виден вÑем", + "private": "Ðтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð±ÑƒÐ´ÐµÑ‚ виден только вашим читателÑм", + "unlisted": "Ðтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ðµ будет виден в локальной и федеративной ленте" }, "scope": { - "direct": "Личное - Ñтот поÑÑ‚ видÑÑ‚ только те кто в нём упомÑнут", - "private": "Ð”Ð»Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñчиков - Ñтот поÑÑ‚ видÑÑ‚ только подпиÑчики", - "public": "Публичный - Ñтот поÑÑ‚ виден вÑем", - "unlisted": "Ðепубличный - Ñтот поÑÑ‚ не виден на публичных лентах" + "direct": "Личное Ñообщение - Ñтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð²Ð¸Ð´ÑÑ‚ только те, кто в нём упомÑнут", + "private": "Ð”Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ñ‚ÐµÐ»ÐµÐ¹ - Ñтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð²Ð¸Ð´ÑÑ‚ только ваши читатели", + "public": "Публичный - Ñтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð²Ð¸Ð´ÐµÐ½ вÑем", + "unlisted": "Тихий - Ñтот поÑÑ‚ виден вÑем, но не отображаетÑÑ Ð² публичных лентах" }, "preview_empty": "ПуÑтой предпроÑмотр", "media_description_error": "Ðе удалоÑÑŒ обновить вложение, попробуйте еще раз", @@ -122,11 +123,12 @@ "text/plain": "ПроÑтой текÑÑ‚" }, "media_description": "ОпиÑание вложениÑ", - "new_status": "ÐапиÑать новый ÑтатуÑ" + "new_status": "ÐапиÑать новый ÑтатуÑ", + "post": "Опубликовать" }, "registration": { - "bio": "ОпиÑание", - "email": "Email", + "bio": "О Ñебе", + "email": "ÐÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð°", "fullname": "Отображаемое имÑ", "password_confirm": "Подтверждение паролÑ", "registration": "РегиÑтрациÑ", @@ -143,7 +145,10 @@ "fullname_placeholder": "например: Почтальон Печкин", "username_placeholder": "например: pechkin", "captcha": "Код подтверждениÑ", - "new_captcha": "Ðажмите на изображение чтобы получить новый код" + "new_captcha": "Ðажмите на изображение чтобы получить новый код", + "reason_placeholder": "Данный узел обрабатывает запроÑÑ‹ на региÑтрацию вручную.\nРаÑÑкажите админиÑтрации почему вы хотите зарегиÑтрироватьÑÑ.", + "reason": "Причина региÑтрации", + "register": "ЗарегиÑтрироватьÑÑ" }, "settings": { "enter_current_password_to_confirm": "Введите Ñвой текущий пароль", @@ -152,7 +157,7 @@ "setup_otp": "ÐаÑтройка OTP", "wait_pre_setup_otp": "Ð¿Ñ€ÐµÐ´Ð²Ð°Ñ€Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð½Ð°Ñтройка OTP", "confirm_and_enable": "Подтвердить и включить OTP", - "title": "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ", + "title": "ДвухÑÑ‚Ð°Ð¿Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ", "generate_new_recovery_codes": "Получить новые коды воÑтановлениÑ", "warning_of_generate_new_codes": "ПоÑле Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ñ‹Ñ… кодов воÑÑтановлениÑ, Ñтарые больше не будут работать.", "recovery_codes": "Коды воÑÑтановлениÑ.", @@ -161,11 +166,11 @@ "authentication_methods": "Методы аутентификации", "scan": { "title": "Сканирование", - "desc": "ИÑпользуйте приложение Ð´Ð»Ñ Ð´Ð²ÑƒÑ…Ñтапной аутентификации Ð´Ð»Ñ ÑÐºÐ°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñтого QR-код или введите текÑтовый ключ:", + "desc": "ОтÑканируйте QR-код приложением Ð´Ð»Ñ Ð´Ð²ÑƒÑ…Ñтапной аутентификации или введите текÑтовый ключ:", "secret_code": "Ключ" }, "verify": { - "desc": "Чтобы включить двухÑтапную аутентификации, введите код из вашего приложение Ð´Ð»Ñ Ð´Ð²ÑƒÑ…Ñтапной аутентификации:" + "desc": "Чтобы включить двухÑтапную аутентификацию, введите код из приложениÑ-аутентификатора:" } }, "attachmentRadius": "Прикреплённые файлы", @@ -174,16 +179,16 @@ "avatarAltRadius": "Ðватары в уведомлениÑÑ…", "avatarRadius": "Ðватары", "background": "Фон", - "bio": "ОпиÑание", + "bio": "О Ñебе", "btnRadius": "Кнопки", - "bot": "Ðто аккаунт бота", + "bot": "Ðто ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ бота", "cBlue": "Ответить, читать", "cGreen": "Повторить", "cOrange": "ÐравитÑÑ", "cRed": "Отменить", - "change_email": "Сменить email", - "change_email_error": "Произошла ошибка при попытке изменить email.", - "changed_email": "Email изменён уÑпешно!", + "change_email": "Сменить Ð°Ð´Ñ€ÐµÑ Ñлектронной почты", + "change_email_error": "Произошла ошибка при попытке изменить Ñлектронную почту.", + "changed_email": "ÐÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð° изменена уÑпешно!", "change_password": "Сменить пароль", "change_password_error": "Произошла ошибка при попытке изменить пароль.", "changed_password": "Пароль изменён уÑпешно!", @@ -193,9 +198,9 @@ "current_password": "Текущий пароль", "current_profile_banner": "Текущий баннер профилÑ", "data_import_export_tab": "Импорт / ÐкÑпорт данных", - "delete_account": "Удалить аккаунт", - "delete_account_description": "Удалить вашу учётную запиÑÑŒ и вÑе ваши ÑообщениÑ.", - "delete_account_error": "Возникла ошибка в процеÑÑе ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ аккаунта. ЕÑли Ñто повторÑетÑÑ, ÑвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором вашего Ñервера.", + "delete_account": "Удалить учетную запиÑÑŒ", + "delete_account_description": "ÐавÑегда удалить вашу учётную запиÑÑŒ и ваши ÑтатуÑÑ‹.", + "delete_account_error": "Возникла ошибка в процеÑÑе ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð²Ð°ÑˆÐµÐ¹ учетной запиÑи. ЕÑли Ñто повторÑетÑÑ, ÑвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором данного узла.", "delete_account_instructions": "Введите ваш пароль в поле ниже Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ.", "export_theme": "Сохранить Тему", "filtering": "ФильтрациÑ", @@ -221,28 +226,28 @@ "interfaceLanguage": "Язык интерфейÑа", "limited_availability": "Ðе доÑтупно в вашем браузере", "links": "СÑылки", - "lock_account_description": "Ðккаунт доÑтупен только подтверждённым подпиÑчикам", + "lock_account_description": "Сделать учетную запиÑÑŒ закрытой — подтверждать читателей вручную", "loop_video": "Зациливать видео", "loop_video_silent_only": "Зацикливать только беззвучные видео (Ñ‚.е. \"гифки\" Ñ Mastodon)", "name": "ИмÑ", - "name_bio": "Ð˜Ð¼Ñ Ð¸ опиÑание", - "new_email": "Ðовый email", + "name_bio": "Личные данные", + "new_email": "Ðовый Ð°Ð´Ñ€ÐµÑ Ñлектронной почты", "new_password": "Ðовый пароль", "fun": "Потешное", "greentext": "Мемные Ñтрелочки", "notification_visibility": "Показывать уведомлениÑ", - "notification_visibility_follows": "ПодпиÑки", + "notification_visibility_follows": "Ðовые читатели", "notification_visibility_likes": "Лайки", "notification_visibility_mentions": "УпоминаниÑ", "notification_visibility_repeats": "Повторы", - "no_rich_text_description": "Убрать форматирование из вÑех поÑтов", + "no_rich_text_description": "Убрать форматирование из вÑех ÑтатуÑов", "hide_follows_description": "Ðе показывать кого Ñ Ñ‡Ð¸Ñ‚Ð°ÑŽ", "hide_followers_description": "Ðе показывать кто читает менÑ", "hide_follows_count_description": "Ðе показывать чиÑло читаемых пользователей", - "hide_followers_count_description": "Ðе показывать чиÑло моих подпиÑчиков", + "hide_followers_count_description": "Ðе показывать чиÑло моих читателей", "show_admin_badge": "Показывать значок админиÑтратора в моем профиле", "show_moderator_badge": "Показывать значок модератора в моем профиле", - "nsfw_clickthrough": "Включить Ñкрытие вложений и предпроÑмотра ÑÑылок Ð´Ð»Ñ NSFW ÑтатуÑов", + "nsfw_clickthrough": "Включить Ñкрытие вложений и предпроÑмотра ÑÑылок Ð´Ð»Ñ ÑтатуÑов щекотливого характера", "oauth_tokens": "OAuth токены", "token": "Токен", "refresh_token": "Рефреш токен", @@ -257,14 +262,14 @@ "radii_help": "Скругление углов Ñлементов интерфейÑа (в пикÑелÑÑ…)", "replies_in_timeline": "Ответы в ленте", "reply_visibility_all": "Показывать вÑе ответы", - "reply_visibility_following": "Показывать только ответы мне или тех на кого Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñан", + "reply_visibility_following": "Показывать только ответы мне или тем кого Ñ Ñ‡Ð¸Ñ‚Ð°ÑŽ", "reply_visibility_self": "Показывать только ответы мне", - "autohide_floating_post_button": "ÐвтоматичеÑки Ñкрывать кнопку поÑтинга (в мобильной верÑии)", + "autohide_floating_post_button": "ÐвтоматичеÑки Ñкрывать кнопку \"ÐапиÑать новый ÑтатуÑ\" (в мобильной верÑии)", "saving_err": "Ðе удалоÑÑŒ Ñохранить наÑтройки", "saving_ok": "Сохранено", "security_tab": "БезопаÑноÑÑ‚ÑŒ", - "scope_copy": "Копировать видимоÑÑ‚ÑŒ поÑта при ответе (вÑегда включено Ð´Ð»Ñ Ð›Ð¸Ñ‡Ð½Ñ‹Ñ… Сообщений)", - "minimal_scopes_mode": "Минимизировать набор опций видимоÑти поÑта", + "scope_copy": "Копировать видимоÑÑ‚ÑŒ поÑта при ответе (вÑегда включено Ð´Ð»Ñ Ð»Ð¸Ñ‡Ð½Ñ‹Ñ… Ñообщений)", + "minimal_scopes_mode": "Показывать только личное Ñообщение и публичный ÑÑ‚Ð°Ñ‚ÑƒÑ Ð² опциÑÑ… видимоÑти", "set_new_avatar": "Загрузить новый аватар", "set_new_profile_background": "Загрузить новый фон профилÑ", "set_new_profile_banner": "Загрузить новый баннер профилÑ", @@ -273,7 +278,7 @@ "stop_gifs": "Проигрывать GIF анимации только при наведении", "streaming": "Включить автоматичеÑкую загрузку новых Ñообщений при прокрутке вверх", "useStreamingApi": "Получать ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð² реальном времени", - "useStreamingApiWarning": "(Ðе рекомендуетÑÑ, ÑкÑпериментально, ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ пропадать)", + "useStreamingApiWarning": "(Ðе рекомендуетÑÑ, ÑкÑпериментально, ÑтатуÑÑ‹ могут пропадать)", "text": "ТекÑÑ‚", "theme": "Тема", "theme_help": "ИÑпользуйте шеÑтнадцатеричные коды цветов (#rrggbb) Ð´Ð»Ñ Ð½Ð°Ñтройки темы.", @@ -305,7 +310,8 @@ "older_version_imported": "Файл, который вы импортировали, был Ñделан в Ñтарой верÑии фронт-Ñнда.", "future_version_imported": "Файл, который вы импортировали, был Ñделан в новой верÑии фронт-Ñнда.", "v2_imported": "Файл, который вы импортировали, был Ñделан под Ñтарый фронт-Ñнд. Мы ÑтараемÑÑ ÑƒÐ»ÑƒÑ‡ÑˆÐ¸Ñ‚ÑŒ ÑовмеÑтимоÑÑ‚ÑŒ, но вÑе еще возможны неÑоÑтыковки.", - "upgraded_from_v2": "Фронт-Ñнд Pleroma был изменен. Ð’Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð°Ñ Ñ‚ÐµÐ¼Ð° может выглÑдеть Ñлегка по-другому." + "upgraded_from_v2": "Фронт-Ñнд Pleroma был изменен. Ð’Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð°Ñ Ñ‚ÐµÐ¼Ð° может выглÑдеть Ñлегка по-другому.", + "fe_downgraded": "ВерÑÐ¸Ñ Ñ„Ñ€Ð¾Ð½Ñ‚-Ñнда Pleroma была откачена." } }, "common": { @@ -337,13 +343,29 @@ "badge": "Фон значков", "badge_notification": "УведомлениÑ", "panel_header": "Заголовок панели", - "top_bar": "ВернÑÑ Ð¿Ð¾Ð»Ð¾Ñка", + "top_bar": "ВерхнÑÑ Ð¿Ð¾Ð»Ð¾Ñка", "borders": "Границы", "buttons": "Кнопки", "inputs": "ÐŸÐ¾Ð»Ñ Ð²Ð²Ð¾Ð´Ð°", "faint_text": "Маловажный текÑÑ‚", - "post": "Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ опиÑание пользователÑ", - "alert_neutral": "Ðейтральный" + "post": "СтатуÑÑ‹ и раздел \"О Ñебе\"", + "alert_neutral": "Ðейтральный", + "alert_warning": "Предупреждение", + "selectedPost": "Выбранный ÑтатуÑ", + "pressed": "Ðажатие", + "highlight": "Выделенные Ñлементы", + "icons": "Иконки", + "poll": "График результатов опроÑа", + "wallpaper": "Фон", + "chat": { + "border": "Границы", + "outgoing": "ИÑходÑщие", + "incoming": "ВходÑщие" + }, + "tabs": "Вкладки", + "toggled": "Включено", + "disabled": "Отключено", + "selectedMenu": "Выбранный пункт меню" }, "radii": { "_tab_label": "Скругление" @@ -368,8 +390,8 @@ "panel": "Панель", "panelHeader": "Заголовок панели", "topBar": "ВерхнÑÑ Ð¿Ð¾Ð»Ð¾Ñка", - "avatar": "Ðватарка (профиль)", - "avatarStatus": "Ðватарка (в ленте)", + "avatar": "Ðватар (профиль)", + "avatarStatus": "Ðватар (в ленте)", "popup": "Ð’Ñплывающие подÑказки", "button": "Кнопки", "buttonHover": "Кнопки (наведен курÑор)", @@ -385,7 +407,7 @@ "interface": "ИнтерфейÑ", "input": "ÐŸÐ¾Ð»Ñ Ð²Ð²Ð¾Ð´Ð°", "post": "ТекÑÑ‚ поÑтов", - "postCode": "Моноширинный текÑÑ‚ в поÑте (форматирование)" + "postCode": "Моноширинный текÑÑ‚ в ÑтатуÑе (форматирование)" }, "family": "Шрифт", "size": "Размер (в пикÑелÑÑ…)", @@ -407,12 +429,12 @@ "link": "ÑÑылка" } }, - "allow_following_move": "Разрешить автоматичеÑки читать новый аккаунт при перемещении на другой Ñервер", + "allow_following_move": "ÐвтоматичеÑки начать читать новый профиль при переезде", "hide_user_stats": "Ðе показывать ÑтатиÑтику пользователей (например количеÑтво читателей)", - "discoverable": "Разрешить показ аккаунта в поиÑковиках и других ÑервиÑах", - "default_vis": "ВидимоÑÑ‚ÑŒ поÑтов по умолчанию", + "discoverable": "Разрешить показывать учетную запиÑÑŒ в поиÑковых ÑиÑтемах и прочих ÑервиÑах", + "default_vis": "ВидимоÑÑ‚ÑŒ ÑтатуÑов по умолчанию", "mutes_and_blocks": "Блокировки и игнорируемые", - "composing": "СоÑтавление поÑтов", + "composing": "СоÑтавление ÑтатуÑов", "chatMessageRadius": "Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² беÑеде", "blocks_tab": "Блокировки", "import_mutes_from_a_csv_file": "Импортировать игнорируемых из CSV файла", @@ -432,12 +454,12 @@ "post_status_content_type": "Формат ÑоÑтавлÑемых ÑтатуÑов по умолчанию", "subject_line_noop": "Ðе копировать", "subject_line_mastodon": "Как в Mastodon: Ñкопировать как еÑÑ‚ÑŒ", - "subject_line_email": "Как в e-mail: \"re: тема\"", + "subject_line_email": "Как в Ñлектронной почте: \"re: тема\"", "subject_line_behavior": "Копировать тему в ответах", "no_mutes": "Ðет игнорируемых", "no_blocks": "Ðет блокировок", "notification_visibility_emoji_reactions": "Реакции", - "notification_visibility_moves": "Миграции пользователей", + "notification_visibility_moves": "Переезды", "use_contain_fit": "Ðе обрезать Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð² миниатюрах", "profile_fields": { "value": "Значение", @@ -452,7 +474,7 @@ "hide_filtered_statuses": "Ðе показывать отфильтрованные ÑтатуÑÑ‹", "hide_muted_posts": "Ðе показывать ÑтатуÑÑ‹ игнорируемых пользователей", "hide_post_stats": "Ðе показывать ÑтатиÑтику ÑтатуÑов (например количеÑтво отметок «ÐравитÑÑ»)", - "use_one_click_nsfw": "Открывать NSFW Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¾Ð´Ð½Ð¸Ð¼ кликом", + "use_one_click_nsfw": "Открывать Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð¼ÐµÑŽÑ‰Ð¸Ðµ щекотливый характер одним кликом", "preload_images": "Предварительно загружать изображениÑ", "max_thumbnails": "МакÑимальное чиÑло миниатюр показываемых в ÑтатуÑе", "emoji_reactions_on_timeline": "Показывать Ñмодзи реакции в ленте", @@ -464,26 +486,43 @@ "virtual_scrolling": "Оптимизировать рендеринг ленты", "hide_wallpaper": "Скрыть обои узла", "accent": "Ðкцент", - "upload_a_photo": "Загрузить фото", - "notification_mutes": "Чтобы не получать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾Ñ‚ определённого пользователÑ, заглушите его.", - "reset_avatar_confirm": "Ð’Ñ‹ дейÑтвительно хотите ÑброÑить личный образ?", - "reset_profile_banner": "СброÑить личный баннер", - "reset_profile_background": "СброÑить личные обои", - "reset_avatar": "СброÑить личный образ", - "search_user_to_mute": "ИÑкать, кого вы хотите заглушить", - "search_user_to_block": "ИÑкать, кого вы хотите заблокировать", - "pad_emoji": "ВыделÑÑ‚ÑŒ Ñмодзи пробелами при добавлении из панели", - "avatar_size_instruction": "Желательный наименьший размер личного образа 150 на 150 пикÑелей.", + "upload_a_photo": "Загрузить изображение", + "notification_mutes": "Чтобы не получать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾Ñ‚ конкретного пользователÑ, заглушите его.", + "reset_avatar_confirm": "Ð’Ñ‹ точно хотите ÑброÑить аватар?", + "reset_profile_banner": "СброÑить баннер профилÑ", + "reset_profile_background": "СброÑить фон профилÑ", + "reset_avatar": "СброÑить аватар", + "search_user_to_mute": "ПоиÑк того, кого вы хотите заглушить", + "search_user_to_block": "ПоиÑк того, кого вы хотите заблокировать", + "pad_emoji": "РазделÑÑ‚ÑŒ Ñмодзи пробелами, когда они добавлÑÑŽÑ‚ÑÑ Ð¸Ð· меню", + "avatar_size_instruction": "РекомендуетÑÑ Ð¸Ñпользовать изображение больше чем 150 на 150 пикÑелей в качеÑтве аватара.", "enable_web_push_notifications": "Включить web push-уведомлениÑ", "notification_blocks": "Блокировка Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð²Ñ‹ÐºÐ»ÑŽÑ‡Ð°ÐµÑ‚ вÑе ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾Ñ‚ него, а также отпиÑывает Ð²Ð°Ñ Ð¾Ñ‚ него.", - "notification_setting_hide_notification_contents": "Скрыть Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÐµÐ»Ñ Ð¸ Ñодержимое push-уведомлений" + "notification_setting_hide_notification_contents": "Скрыть Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÐµÐ»Ñ Ð¸ Ñодержимое push-уведомлений", + "version": { + "title": "ВерÑиÑ", + "frontend_version": "ВерÑÐ¸Ñ Ñ„Ñ€Ð¾Ð½Ñ‚-Ñнда", + "backend_version": "ВерÑÐ¸Ñ Ð±Ñк-Ñнда" + }, + "word_filter": "Фильтр Ñлов", + "sensitive_by_default": "Помечать ÑтатуÑÑ‹ как имеющие щекотливый характер по умолчанию", + "reply_visibility_self_short": "Показывать ответы только вам", + "reply_visibility_following_short": "Показывать ответы тем кого вы читаете", + "hide_all_muted_posts": "Ðе показывать игнорируемые ÑтатуÑÑ‹", + "hide_media_previews": "Ðе показывать Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð² ленте", + "setting_changed": "ОтличаетÑÑ Ð¾Ñ‚ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию", + "reset_background_confirm": "Ð’Ñ‹ точно хотите ÑброÑить фон?", + "reset_banner_confirm": "Ð’Ñ‹ точно хотите ÑброÑить баннер?", + "type_domains_to_mute": "ПоиÑк узлов, которые вы хотите заглушить", + "more_settings": "ОÑтальные наÑтройки", + "save": "Сохранить изменениÑ" }, "timeline": { "collapse": "Свернуть", "conversation": "Разговор", "error_fetching": "Ошибка при обновлении", "load_older": "Загрузить Ñтарые ÑтатуÑÑ‹", - "no_retweet_hint": "ПоÑÑ‚ помечен как \"только Ð´Ð»Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñчиков\" или \"личное\" и поÑтому не может быть повторён", + "no_retweet_hint": "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¿Ð¾Ð¼ÐµÑ‡ÐµÐ½ как \"только Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ñ‚ÐµÐ»ÐµÐ¹\" или \"личное Ñообщение\" и потому не может быть повторён", "repeated": "повторил(а)", "show_new": "Показать новые", "up_to_date": "Обновлено", @@ -492,7 +531,7 @@ "status": { "bookmark": "Добавить в закладки", "unbookmark": "Удалить из закладок", - "status_deleted": "ПоÑÑ‚ удален", + "status_deleted": "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ ÑƒÐ´Ð°Ð»ÐµÐ½", "reply_to": "Ответ", "repeats": "Повторы", "favorites": "ПонравилоÑÑŒ", @@ -528,16 +567,16 @@ "revoke_admin": "Забрать права админиÑтратора", "grant_moderator": "Сделать модератором", "revoke_moderator": "Забрать права модератора", - "activate_account": "Ðктивировать аккаунт", - "deactivate_account": "Деактивировать аккаунт", - "delete_account": "Удалить аккаунт", - "force_nsfw": "Отмечать поÑÑ‚Ñ‹ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÐºÐ°Ðº NSFW", - "strip_media": "Убирать Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð· поÑтов пользователÑ", - "force_unlisted": "Ðе добавлÑÑ‚ÑŒ поÑÑ‚Ñ‹ в публичные ленты", + "activate_account": "Ðктивировать учетную запиÑÑŒ", + "deactivate_account": "Деактивировать учетную запиÑÑŒ", + "delete_account": "Удалить учетную запиÑÑŒ", + "force_nsfw": "Отмечать ÑтатуÑÑ‹ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÐºÐ°Ðº имеющие щекотливый характер", + "strip_media": "Убирать Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð· ÑтатуÑов пользователÑ", + "force_unlisted": "Ðе показывать ÑтатуÑÑ‹ в публичных лентах", "sandbox": "Принудить видимоÑÑ‚ÑŒ поÑтов только читателÑм", - "disable_remote_subscription": "Запретить читать Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð½Ñ‹Ñ… Ñерверов", + "disable_remote_subscription": "Запретить читать Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… узлов", "disable_any_subscription": "Запретить читать пользователÑ", - "quarantine": "Ðе федерировать поÑÑ‚Ñ‹ пользователÑ", + "quarantine": "Ðе федерировать ÑтатуÑÑ‹ пользователÑ", "delete_user": "Удалить пользователÑ", "delete_user_confirmation": "Ð’Ñ‹ уверены? Ðто дейÑтвие Ð½ÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ." }, @@ -545,7 +584,14 @@ "mention": "УпомÑнуть", "show_repeats": "Показывать повторы", "hide_repeats": "Скрыть повторы", - "report": "ПожаловатьÑÑ" + "report": "ПожаловатьÑÑ", + "message": "ÐапиÑать Ñообщение", + "highlight": { + "side": "ПолоÑка Ñбоку", + "striped": "Фон в полоÑку", + "solid": "Сплошной фон", + "disabled": "Ðет выделениÑ" + } }, "user_profile": { "timeline_title": "Лента пользователÑ" @@ -560,30 +606,31 @@ "password_reset": { "forgot_password": "Забыли пароль?", "password_reset": "Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ", - "instruction": "Введите ваш email или Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ, и мы отправим вам ÑÑылку Ð´Ð»Ñ ÑброÑа паролÑ.", - "placeholder": "Ваш email или Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ", - "check_email": "Проверьте ваш email и перейдите по ÑÑылке Ð´Ð»Ñ ÑброÑа паролÑ.", + "instruction": "Введите ваш Ð°Ð´Ñ€ÐµÑ Ñлектронной почты или Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ: на вашу Ñлектронную почту будет отправлена ÑÑылка Ð´Ð»Ñ ÑброÑа паролÑ.", + "placeholder": "Ваш Ð°Ð´Ñ€ÐµÑ Ñлектронной почты или Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ", + "check_email": "Проверьте вашу Ñлектронную почту и перейдите по ÑÑылке Ð´Ð»Ñ ÑброÑа паролÑ.", "return_home": "ВернутьÑÑ Ð½Ð° главную Ñтраницу", "too_many_requests": "Ð’Ñ‹ иÑчерпали допуÑтимое количеÑтво попыток, попробуйте позже.", - "password_reset_disabled": "Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½. CвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором вашего Ñервера." + "password_reset_disabled": "ÐвтоматичеÑкий ÑÐ±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½. СвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором данного узла Ð´Ð»Ñ ÑброÑа паролÑ.", + "password_reset_required_but_mailer_is_disabled": "Ð’Ñ‹ должны ÑброÑить Ñвой пароль, однако автоматичеÑкий ÑÐ±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½. ПожалуйÑта ÑвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором данного узла." }, "about": { "mrf": { "federation": "ФедерациÑ", "simple": { - "accept_desc": "Данный Ñервер принимает ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ñо Ñледующих Ñерверов:", - "ftl_removal_desc": "Данный Ñервер Ñкрывает Ñледующие Ñервера Ñ Ñ„ÐµÐ´ÐµÑ€Ð°Ñ‚Ð¸Ð²Ð½Ð¾Ð¹ ленты:", - "media_nsfw_desc": "Данный Ñервер принужденно помечает Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñо Ñледущих Ñерверов как NSFW:", - "simple_policies": "Правила Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð½Ñ‹Ñ… Ñерверов", - "accept": "Принимаемые ÑообщениÑ", - "reject": "ОтклонÑемые ÑообщениÑ", - "reject_desc": "Данный Ñервер не принимает ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñо Ñледующих Ñерверов:", + "accept_desc": "Данный узел принимает ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ñо Ñледующих узлов:", + "ftl_removal_desc": "Данный узел Ñкрывает Ñледующие узлы Ñ Ñ„ÐµÐ´ÐµÑ€Ð°Ñ‚Ð¸Ð²Ð½Ð¾Ð¹ ленты:", + "media_nsfw_desc": "Данный узел принужденно помечает Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñо Ñледующих узлов как имеющие щекотливый характер:", + "simple_policies": "Правила Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð½Ñ‹Ñ… узлов", + "accept": "Белый ÑпиÑок", + "reject": "Черный ÑпиÑок", + "reject_desc": "Данный узел не принимает ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñо Ñледующих узлов:", "quarantine": "Зона карантина", - "quarantine_desc": "Данный Ñервер отправлÑет только публичные поÑÑ‚Ñ‹ Ñледующим Ñерверам:", + "quarantine_desc": "Данный узел отправлÑет только публичные ÑтатуÑÑ‹ Ñледующим узлам:", "ftl_removal": "Скрытие Ñ Ñ„ÐµÐ´ÐµÑ€Ð°Ñ‚Ð¸Ð²Ð½Ð¾Ð¹ ленты", "media_removal": "Удаление вложений", - "media_removal_desc": "Данный Ñервер удалÑет Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñо Ñледующих Ñерверов:", - "media_nsfw": "Принужденно помеченно как NSFW" + "media_removal_desc": "Данный узел удалÑет Ð²Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñо Ñледующих узлов:", + "media_nsfw": "Принужденно помеченно как имеющее щекотливый характер" }, "keyword": { "ftl_removal": "Убрать из федеративной ленты", @@ -593,7 +640,7 @@ "is_replaced_by": "→" }, "mrf_policies": "Ðктивные правила MRF (модуль перепиÑÑ‹Ð²Ð°Ð½Ð¸Ñ Ñообщений)", - "mrf_policies_desc": "Правила MRF (модуль перепиÑÑ‹Ð²Ð°Ð½Ð¸Ñ Ñообщений) влиÑÑŽÑ‚ на федерацию данного Ñервера. Следующие правила активны:" + "mrf_policies_desc": "Правила MRF (модуль перепиÑÑ‹Ð²Ð°Ð½Ð¸Ñ Ñообщений) влиÑÑŽÑ‚ на федерацию данного узла. Следующие правила активны:" }, "staff": "ÐдминиÑтрациÑ" }, @@ -644,7 +691,9 @@ "votes": "голоÑов", "option": "Вариант", "add_option": "Добавить вариант", - "add_poll": "Прикрепить опроÑ" + "add_poll": "Прикрепить опроÑ", + "votes_count": "{count} Ð³Ð¾Ð»Ð¾Ñ | {count} голоÑов", + "people_voted_count": "{count} человек проголоÑовал | {count} человек проголоÑовали" }, "media_modal": { "next": "СледующаÑ", @@ -702,10 +751,26 @@ "chats": "БеÑеды", "delete": "Удалить", "message_user": "Ðапишите {nickname}", - "you": "Ð’Ñ‹:" + "you": "Ð’Ñ‹:", + "error_sending_message": "Произошла ошибка при отправке ÑообщениÑ." }, "remote_user_resolver": { "error": "Ðе найдено.", "searching_for": "Ищем" + }, + "upload": { + "error": { + "message": "Произошла ошибка при загрузке: {0}" + } + }, + "user_reporting": { + "add_comment_description": "Жалоба будет направлена модераторам вашего узла. Ð’Ñ‹ можете указать причину жалобы ниже:", + "forward_description": "Данный пользователь находитÑÑ Ð½Ð° другом узле. ОтоÑлать туда копию вашей жалобы?" + }, + "file_type": { + "file": "Файл", + "video": "ВидеозапиÑÑŒ", + "audio": "ÐудиозапиÑÑŒ", + "image": "Изображение" } } diff --git a/src/i18n/zh.json b/src/i18n/zh.json index 5e377f7c58cd74cf5e4e4044cb9064950b6faf04..a09ef3abb2aaa7eb6f9160f9252d85f4425c8df5 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -168,7 +168,8 @@ "preview": "预览", "media_description": "媒体æè¿°", "media_description_error": "更新媒体失败,请é‡è¯•", - "empty_status_error": "ä¸èƒ½å‘布没有内容ã€æ²¡æœ‰é™„件的å‘æ–‡" + "empty_status_error": "ä¸èƒ½å‘布没有内容ã€æ²¡æœ‰é™„件的å‘æ–‡", + "post": "å‘é€" }, "registration": { "bio": "简介", @@ -191,7 +192,8 @@ "password_confirmation_match": "密ç ä¸ä¸€è‡´" }, "reason_placeholder": "æ¤å®žä¾‹çš„注册需è¦æ‰‹åŠ¨æ‰¹å‡†ã€‚\n请让管ç†å‘˜çŸ¥é“您为什么想è¦æ³¨å†Œã€‚", - "reason": "注册ç†ç”±" + "reason": "注册ç†ç”±", + "register": "注册" }, "selectable_list": { "select_all": "选择全部" @@ -306,8 +308,8 @@ "no_mutes": "没有éšè—", "hide_follows_description": "ä¸è¦æ˜¾ç¤ºæˆ‘所关注的人", "hide_followers_description": "ä¸è¦æ˜¾ç¤ºå…³æ³¨æˆ‘的人", - "show_admin_badge": "在我的个人资料ä¸æ˜¾ç¤ºç®¡ç†å‘˜å¾½ç« ", - "show_moderator_badge": "在我的个人资料ä¸æ˜¾ç¤ºç›‘å¯Ÿå‘˜å¾½ç« ", + "show_admin_badge": "在我的个人资料ä¸æ˜¾ç¤ºâ€œç®¡ç†å‘˜â€å¾½ç« ", + "show_moderator_badge": "在我的个人资料ä¸æ˜¾ç¤ºâ€œç›‘察员â€å¾½ç« ", "nsfw_clickthrough": "å°†ä¸å’Œè°é™„件和链接预览éšè—,点击æ‰ä¼šæ˜¾ç¤º", "oauth_tokens": "OAuth令牌", "token": "令牌", @@ -561,7 +563,15 @@ "mute_export_button": "å¯¼å‡ºä½ çš„éšè—åå•åˆ°ä¸€ä¸ª csv 文件", "mute_export": "éšè—åå•å¯¼å‡º", "hide_wallpaper": "éšè—实例å£çº¸", - "setting_changed": "与默认设置ä¸åŒ" + "setting_changed": "与默认设置ä¸åŒ", + "more_settings": "更多设置", + "sensitive_by_default": "é»˜è®¤æ ‡è®°å‘文为æ•æ„Ÿå†…容", + "reply_visibility_self_short": "åªæ˜¾ç¤ºå¯¹æˆ‘本人的回å¤", + "reply_visibility_following_short": "显示对我关注的人的回å¤", + "hide_all_muted_posts": "ä¸æ˜¾ç¤ºå·²éšè—çš„å‘æ–‡", + "hide_media_previews": "éšè—媒体预览", + "word_filter": "è¯è¯è¿‡æ»¤", + "save": "ä¿å˜æ›´æ”¹" }, "time": { "day": "{0} 天", @@ -609,7 +619,9 @@ "no_more_statuses": "没有更多的状æ€", "no_statuses": "没有状æ€æ›´æ–°", "reload": "é‡æ–°è½½å…¥", - "error": "å–得时间轴时å‘生错误:{0}" + "error": "å–得时间轴时å‘生错误:{0}", + "socket_broke": "丢失实时连接:CloseEvent code {0}", + "socket_reconnected": "已建立实时连接" }, "status": { "favorites": "喜欢", @@ -693,7 +705,13 @@ "hide_repeats": "éšè—转å‘", "message": "消æ¯", "mention": "æåŠ", - "bot": "机器人" + "bot": "机器人", + "highlight": { + "side": "侧边æ¡çº¹", + "striped": "æ¡çº¹èƒŒæ™¯", + "solid": "å•ä¸€é¢œè‰²èƒŒæ™¯", + "disabled": "ä¸çªå‡ºæ˜¾ç¤º" + } }, "user_profile": { "timeline_title": "用户时间线", @@ -788,8 +806,8 @@ "media_nsfw_desc": "本实例将æ¥è‡ªä»¥ä¸‹å®žä¾‹çš„媒体内容强制设置为æ•æ„Ÿå†…容:", "media_nsfw": "强制设置媒体为æ•æ„Ÿå†…容", "media_removal_desc": "本实例移除æ¥è‡ªä»¥ä¸‹å®žä¾‹çš„媒体内容:", - "ftl_removal_desc": "该实例在从“全部已知网络â€æ—¶é—´çº¿ä¸Šç§»é™¤äº†ä¸‹åˆ—实例:", - "ftl_removal": "从“全部已知网络â€æ—¶é—´çº¿ä¸Šç§»é™¤" + "ftl_removal_desc": "该实例在从“已知网络â€æ—¶é—´çº¿ä¸Šç§»é™¤äº†ä¸‹åˆ—实例:", + "ftl_removal": "从“已知网络â€æ—¶é—´çº¿ä¸Šç§»é™¤" }, "mrf_policies_desc": "MRF ç–略会影å“本实例的互通行为。以下ç–略已å¯ç”¨ï¼š", "mrf_policies": "å·²å¯ç”¨çš„ MRF ç–ç•¥", diff --git a/src/i18n/zh_Hant.json b/src/i18n/zh_Hant.json index b7b6d7acefd5b89f8da507c75691ba3f1c31ad97..749a9b5f4678cb6a783df4d036ecab52a0e56b60 100644 --- a/src/i18n/zh_Hant.json +++ b/src/i18n/zh_Hant.json @@ -22,7 +22,9 @@ "votes": "票", "option": "é¸é …", "add_option": "å¢žåŠ é¸é …", - "add_poll": "å¢žåŠ æŠ•ç¥¨" + "add_poll": "å¢žåŠ æŠ•ç¥¨", + "votes_count": "{count} 票 | {count} 票", + "people_voted_count": "{count} 人已投票 | {count} 人已投票" }, "notifications": { "reacted_with": "作出了 {0} çš„å應", @@ -108,7 +110,11 @@ "loading": "載入ä¸â€¦", "more": "更多", "submit": "æ交", - "apply": "應用" + "apply": "應用", + "role": { + "moderator": "主æŒäºº", + "admin": "管ç†å“¡" + } }, "finder": { "find_user": "尋找用戶", @@ -424,7 +430,7 @@ "notification_blocks": "å°éŽ–一個用戶會åœæŽ‰æ‰€æœ‰ä»–的通知,ç‰åŒæ–¼å–消關注。", "enable_web_push_notifications": "啟用 web 推é€é€šçŸ¥", "presets": "é ç½®", - "profile_background": "個人背景圖", + "profile_background": "é…置文件背景圖", "profile_banner": "橫幅圖片", "profile_tab": "個人資料", "radii_help": "è¨ç½®ç•Œé¢é‚Šç·£çš„圓角 (å–®ä½ï¼šåƒç´ )", @@ -512,7 +518,7 @@ "show_moderator_badge": "顯示主æŒäººå¾½ç« ", "oauth_tokens": "OAuth代幣", "token": "代幣", - "refresh_token": "刷新代幣", + "refresh_token": "刷新token", "useStreamingApiWarning": "(ä¸æŽ¨è–¦ä½¿ç”¨ï¼Œå¯¦é©—性的,已知跳éŽæ–‡ç« )", "fun": "有趣", "notification_setting_hide_notification_contents": "éš±è—推é€é€šçŸ¥ä¸çš„發é€è€…與內容信æ¯", @@ -528,7 +534,14 @@ "mute_import_error": "å°Žå…¥éœéŸ³æ™‚出錯", "mute_export_button": "å°‡éœéŸ³å°Žå‡ºåˆ°csv文件", "mute_export": "éœéŸ³å°Žå‡º", - "hide_wallpaper": "éš±è—實例桌布" + "hide_wallpaper": "éš±è—實例桌布", + "reply_visibility_self_short": "åªé¡¯ç¤ºå°æˆ‘本人的回å¤", + "reply_visibility_following_short": "顯示å°æˆ‘關注的人的回å¤", + "hide_all_muted_posts": "ä¸é¡¯ç¤ºå·²éš±è—的帖å", + "hide_media_previews": "éš±è—媒體é 覽", + "word_filter": "è©žéŽæ¿¾", + "setting_changed": "與默èªè¨ç½®ä¸åŒ", + "more_settings": "更多è¨ç½®" }, "chats": { "more": "更多", @@ -667,7 +680,7 @@ "load_older": "載入更早的互動", "moves": "用戶é·ç§»", "follows": "新的關注者", - "favs_repeats": "轉發和收è—" + "favs_repeats": "轉發和喜æ¡" }, "selectable_list": { "select_all": "é¸æ“‡å…¨éƒ¨" @@ -696,7 +709,9 @@ "registration": "註冊", "password_confirm": "確èªå¯†ç¢¼", "email": "é›»å郵箱", - "bio": "簡介" + "bio": "簡介", + "reason_placeholder": "æ¤å¯¦ä¾‹çš„註冊需è¦æ‰‹å‹•æ‰¹å‡†ã€‚\n請讓管ç†çŸ¥é“您為什麼想è¦è¨»å†Šã€‚", + "reason": "註冊ç†ç”±" }, "user_card": { "its_you": "å°±æ˜¯ä½ ï¼!", @@ -756,7 +771,14 @@ "roles": { "moderator": "主æŒäºº", "admin": "管ç†å“¡" - } + }, + "highlight": { + "disabled": "ç„¡çªå‡ºé¡¯ç¤º", + "solid": "單色背景", + "striped": "æ¢ç´‹èƒŒæ™¯", + "side": "彩æ¢" + }, + "bot": "機器人" }, "user_profile": { "timeline_title": "用戶時間線", diff --git a/src/modules/api.js b/src/modules/api.js index 08485a30a5994984c1fe7d7fdc011168722026c4..8654b90a599d136043b0653924915f105123847a 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -3,8 +3,11 @@ import { WSConnectionStatus } from '../services/api/api.service.js' import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js' import { Socket } from 'phoenix' +const retryTimeout = (multiplier) => 1000 * multiplier + const api = { state: { + retryMultiplier: 1, backendInteractor: backendInteractorService(), fetchers: {}, socket: null, @@ -34,18 +37,43 @@ const api = { }, setMastoUserSocketStatus (state, value) { state.mastoUserSocketStatus = value + }, + incrementRetryMultiplier (state) { + state.retryMultiplier = Math.max(++state.retryMultiplier, 3) + }, + resetRetryMultiplier (state) { + state.retryMultiplier = 1 } }, actions: { - // Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets - enableMastoSockets (store) { - const { state, dispatch } = store - if (state.mastoUserSocket) return + /** + * Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets + * + * @param {Boolean} [initial] - whether this enabling happened at boot time or not + */ + enableMastoSockets (store, initial) { + const { state, dispatch, commit } = store + // Do not initialize unless nonexistent or closed + if ( + state.mastoUserSocket && + ![ + WebSocket.CLOSED, + WebSocket.CLOSING + ].includes(state.mastoUserSocket.getState()) + ) { + return + } + if (initial) { + commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING_INITIAL) + } else { + commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING) + } return dispatch('startMastoUserSocket') }, disableMastoSockets (store) { - const { state, dispatch } = store + const { state, dispatch, commit } = store if (!state.mastoUserSocket) return + commit('setMastoUserSocketStatus', WSConnectionStatus.DISABLED) return dispatch('stopMastoUserSocket') }, @@ -91,11 +119,29 @@ const api = { } ) state.mastoUserSocket.addEventListener('open', () => { + // Do not show notification when we just opened up the page + if (state.mastoUserSocketStatus !== WSConnectionStatus.STARTING_INITIAL) { + dispatch('pushGlobalNotice', { + level: 'success', + messageKey: 'timeline.socket_reconnected', + timeout: 5000 + }) + } + // Stop polling if we were errored or disabled + if (new Set([ + WSConnectionStatus.ERROR, + WSConnectionStatus.DISABLED + ]).has(state.mastoUserSocketStatus)) { + dispatch('stopFetchingTimeline', { timeline: 'friends' }) + dispatch('stopFetchingNotifications') + dispatch('stopFetchingChats') + } + commit('resetRetryMultiplier') commit('setMastoUserSocketStatus', WSConnectionStatus.JOINED) }) state.mastoUserSocket.addEventListener('error', ({ detail: error }) => { console.error('Error in MastoAPI websocket:', error) - commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR) + // TODO is this needed? dispatch('clearOpenedChats') }) state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => { @@ -106,14 +152,26 @@ const api = { const { code } = closeEvent if (ignoreCodes.has(code)) { console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`) + commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED) } else { console.warn(`MastoAPI websocket disconnected, restarting. CloseEvent code: ${code}`) - dispatch('startFetchingTimeline', { timeline: 'friends' }) - dispatch('startFetchingNotifications') - dispatch('startFetchingChats') - dispatch('restartMastoUserSocket') + setTimeout(() => { + dispatch('startMastoUserSocket') + }, retryTimeout(state.retryMultiplier)) + commit('incrementRetryMultiplier') + if (state.mastoUserSocketStatus !== WSConnectionStatus.ERROR) { + dispatch('startFetchingTimeline', { timeline: 'friends' }) + dispatch('startFetchingNotifications') + dispatch('startFetchingChats') + dispatch('pushGlobalNotice', { + level: 'error', + messageKey: 'timeline.socket_broke', + messageArgs: [code], + timeout: 5000 + }) + } + commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR) } - commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED) dispatch('clearOpenedChats') }) resolve() @@ -122,15 +180,6 @@ const api = { } }) }, - restartMastoUserSocket ({ dispatch }) { - // This basically starts MastoAPI user socket and stops conventional - // fetchers when connection reestablished - return dispatch('startMastoUserSocket').then(() => { - dispatch('stopFetchingTimeline', { timeline: 'friends' }) - dispatch('stopFetchingNotifications') - dispatch('stopFetchingChats') - }) - }, stopMastoUserSocket ({ state, dispatch }) { dispatch('startFetchingTimeline', { timeline: 'friends' }) dispatch('startFetchingNotifications') @@ -156,6 +205,13 @@ const api = { if (!fetcher) return store.commit('removeFetcher', { fetcherName: timeline, fetcher }) }, + fetchTimeline (store, timeline, { ...rest }) { + store.state.backendInteractor.fetchTimeline({ + store, + timeline, + ...rest + }) + }, // Notifications startFetchingNotifications (store) { @@ -168,6 +224,12 @@ const api = { if (!fetcher) return store.commit('removeFetcher', { fetcherName: 'notifications', fetcher }) }, + fetchNotifications (store, { ...rest }) { + store.state.backendInteractor.fetchNotifications({ + store, + ...rest + }) + }, // Follow requests startFetchingFollowRequests (store) { diff --git a/src/modules/config.js b/src/modules/config.js index eca58c12c38aae8858e2e56f4f370075a91fa80b..629588a84ef8477b71831a479de87b2591752114 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -44,7 +44,7 @@ export const defaultState = { likes: true, repeats: true, moves: true, - emojiReactions: false, + emojiReactions: true, followRequest: true, chatMention: true }, @@ -110,6 +110,20 @@ const config = { } }, actions: { + loadSettings ({ dispatch }, data) { + const knownKeys = new Set(Object.keys(defaultState)) + const presentKeys = new Set(Object.keys(data)) + const intersection = new Set() + for (let elem of presentKeys) { + if (knownKeys.has(elem)) { + intersection.add(elem) + } + } + + intersection.forEach( + name => dispatch('setOption', { name, value: data[name] }) + ) + }, setHighlight ({ commit, dispatch }, { user, color, type }) { commit('setHighlight', { user, color, type }) }, diff --git a/src/modules/users.js b/src/modules/users.js index 655db4c7ed08e6c35e92cd8bd375f838a474b644..8a764a16024579a6933857cde3672972f8eb73ef 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -547,9 +547,10 @@ const users = { } if (store.getters.mergedConfig.useStreamingApi) { - store.dispatch('enableMastoSockets').catch((error) => { + store.dispatch('fetchTimeline', 'friends', { since: null }) + store.dispatch('fetchNotifications', { since: null }) + store.dispatch('enableMastoSockets', true).catch((error) => { console.error('Failed initializing MastoAPI Streaming socket', error) - startPolling() }).then(() => { store.dispatch('fetchChats', { latest: true }) setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index f44831490555da1cfc468e83a32fc707acef9730..436b8b0a0e8484b8b4bdf3c18c5fecb7e87a0438 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -1152,6 +1152,7 @@ export const ProcessedWS = ({ // 1000 = Normal Closure eventTarget.close = () => { socket.close(1000, 'Shutting down socket') } + eventTarget.getState = () => socket.readyState return eventTarget } @@ -1183,7 +1184,10 @@ export const handleMastoWS = (wsEvent) => { export const WSConnectionStatus = Object.freeze({ 'JOINED': 1, 'CLOSED': 2, - 'ERROR': 3 + 'ERROR': 3, + 'DISABLED': 4, + 'STARTING': 5, + 'STARTING_INITIAL': 6 }) const chats = ({ credentials }) => { diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 45e6bd0e1735868976c6f59fba540d2679053246..4a40f5b5fab961317d5940892304211f06c85c2e 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -1,17 +1,25 @@ import apiService, { getMastodonSocketURI, ProcessedWS } from '../api/api.service.js' -import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js' +import timelineFetcher from '../timeline_fetcher/timeline_fetcher.service.js' import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js' import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service' const backendInteractorService = credentials => ({ startFetchingTimeline ({ timeline, store, userId = false, tag }) { - return timelineFetcherService.startFetching({ timeline, store, credentials, userId, tag }) + return timelineFetcher.startFetching({ timeline, store, credentials, userId, tag }) + }, + + fetchTimeline (args) { + return timelineFetcher.fetchAndUpdate({ ...args, credentials }) }, startFetchingNotifications ({ store }) { return notificationsFetcher.startFetching({ store, credentials }) }, + fetchNotifications (args) { + return notificationsFetcher.fetchAndUpdate({ ...args, credentials }) + }, + startFetchingFollowRequests ({ store }) { return followRequestFetcher.startFetching({ store, credentials }) }, diff --git a/src/services/export_import/export_import.js b/src/services/export_import/export_import.js new file mode 100644 index 0000000000000000000000000000000000000000..ac67cf9c528387112f10bc00df3972901d898643 --- /dev/null +++ b/src/services/export_import/export_import.js @@ -0,0 +1,55 @@ +export const newExporter = ({ + filename = 'data', + getExportedObject +}) => ({ + exportData () { + const stringified = JSON.stringify(getExportedObject(), null, 2) // Pretty-print and indent with 2 spaces + + // Create an invisible link with a data url and simulate a click + const e = document.createElement('a') + e.setAttribute('download', `${filename}.json`) + e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified)) + e.style.display = 'none' + + document.body.appendChild(e) + e.click() + document.body.removeChild(e) + } +}) + +export const newImporter = ({ + onImport, + onImportFailure, + validator = () => true +}) => ({ + importData () { + const filePicker = document.createElement('input') + filePicker.setAttribute('type', 'file') + filePicker.setAttribute('accept', '.json') + + filePicker.addEventListener('change', event => { + if (event.target.files[0]) { + // eslint-disable-next-line no-undef + const reader = new FileReader() + reader.onload = ({ target }) => { + try { + const parsed = JSON.parse(target.result) + const validationResult = validator(parsed) + if (validationResult === true) { + onImport(parsed) + } else { + onImportFailure({ validationResult }) + } + } catch (error) { + onImportFailure({ error }) + } + } + reader.readAsText(event.target.files[0]) + } + }) + + document.body.appendChild(filePicker) + filePicker.click() + document.body.removeChild(filePicker) + } +}) diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js index beeb167c445a42336f1334e9f28ba84ffb843569..f83f871e039b6d57dfe7abb096cf76b1b2723de8 100644 --- a/src/services/notifications_fetcher/notifications_fetcher.service.js +++ b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -5,7 +5,7 @@ const update = ({ store, notifications, older }) => { store.dispatch('addNewNotifications', { notifications, older }) } -const fetchAndUpdate = ({ store, credentials, older = false }) => { +const fetchAndUpdate = ({ store, credentials, older = false, since }) => { const args = { credentials } const { getters } = store const rootState = store.rootState || store.state @@ -22,8 +22,10 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => { return fetchNotifications({ store, args, older }) } else { // fetch new notifications - if (timelineData.maxId !== Number.POSITIVE_INFINITY) { + if (since === undefined && timelineData.maxId !== Number.POSITIVE_INFINITY) { args['since'] = timelineData.maxId + } else if (since !== null) { + args['since'] = since } const result = fetchNotifications({ store, args, older }) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index a2bba67bcee067733752d5559fe738d40f6336fb..f75e69168f7c7c63253b6251413496e91c0905a7 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -380,7 +380,7 @@ export const colors2to3 = (colors) => { */ export const shadows2to3 = (shadows, opacity) => { return Object.entries(shadows).reduce((shadowsAcc, [slotName, shadowDefs]) => { - const isDynamic = ({ color }) => color.startsWith('--') + const isDynamic = ({ color = '#000000' }) => color.startsWith('--') const getOpacity = ({ color }) => opacity[getOpacitySlot(color.substring(2).split(',')[0])] const newShadow = shadowDefs.reduce((shadowAcc, def) => [ ...shadowAcc, diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js index bec1eebdfc08246e3c426dc264b3670194cdca81..14aac9754e919ff8d8926dc5d8c71a25dcc2a8dc 100644 --- a/src/services/theme_data/pleromafe.js +++ b/src/services/theme_data/pleromafe.js @@ -616,6 +616,23 @@ export const SLOT_INHERITANCE = { textColor: true }, + alertSuccess: { + depends: ['cGreen'], + opacity: 'alert' + }, + alertSuccessText: { + depends: ['text'], + layer: 'alert', + variant: 'alertSuccess', + textColor: true + }, + alertSuccessPanelText: { + depends: ['panelText'], + layer: 'alertPanel', + variant: 'alertSuccess', + textColor: true + }, + alertNeutral: { depends: ['text'], opacity: 'alert' @@ -656,6 +673,17 @@ export const SLOT_INHERITANCE = { textColor: true }, + alertPopupSuccess: { + depends: ['alertSuccess'], + opacity: 'alertPopup' + }, + alertPopupSuccessText: { + depends: ['alertSuccessText'], + layer: 'popover', + variant: 'alertPopupSuccess', + textColor: true + }, + alertPopupNeutral: { depends: ['alertNeutral'], opacity: 'alertPopup' diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 921df3edac304e8c80ee8483d778107a7f88a372..46bba41a02f22f32c2747e002e9915dbf591ef87 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -23,7 +23,8 @@ const fetchAndUpdate = ({ showImmediately = false, userId = false, tag = false, - until + until, + since }) => { const args = { timeline, credentials } const rootState = store.rootState || store.state @@ -35,7 +36,11 @@ const fetchAndUpdate = ({ if (older) { args['until'] = until || timelineData.minId } else { - args['since'] = timelineData.maxId + if (since === undefined) { + args['since'] = timelineData.maxId + } else if (since !== null) { + args['since'] = since + } } args['userId'] = userId