Merge branch 'develop' into fedi-absturztau-be

This commit is contained in:
Absturztaube 2021-03-09 17:14:31 +01:00
commit 6ef591bdcc
71 changed files with 1211 additions and 300 deletions

1
.mailmap Normal file
View file

@ -0,0 +1 @@
rinpatch <rin@patch.cx> <rinpatch@sdf.org>

View file

@ -4,12 +4,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Added
- Added a quick settings to timeline header for easier access
- Added option to mark posts as sensitive by default
## [2.3.0] - 2021-03-01
### Fixed ### Fixed
- Button to remove uploaded media in post status form is now properly placed and sized. - Button to remove uploaded media in post status form is now properly placed and sized.
- Fixed shoutbox not working in mobile layout - Fixed shoutbox not working in mobile layout
- Fixed missing highlighted border in expanded conversations again
- Fixed some UI jumpiness when opening images particularly in chat view
- Fixed chat unread badge looking weird
- Fixed punycode names not working properly
- Fixed notifications crashing on an invalid notification
### Changed ### Changed
- Display 'people voted' instead of 'votes' for multi-choice polls - Display 'people voted' instead of 'votes' for multi-choice polls
- 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
### Added
- Added reason field for registration when approval is required
- Group staff members by role in the About page
## [2.2.3] - 2021-01-18 ## [2.2.3] - 2021-01-18
### Added ### Added
@ -21,7 +38,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed ### Changed
- Don't filter own posts when they hit your wordfilter - Don't filter own posts when they hit your wordfilter
- Language picker now uses native language names
## [2.2.2] - 2020-12-22 ## [2.2.2] - 2020-12-22
@ -31,7 +47,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Added some missing unicode emoji - Added some missing unicode emoji
- Added the upload limit to the Features panel in the About page - Added the upload limit to the Features panel in the About page
- Support for solid color wallpaper, instance doesn't have to define a wallpaper anymore - Support for solid color wallpaper, instance doesn't have to define a wallpaper anymore
- Group staff members by role in the About page
### Fixed ### Fixed
- Fixed the occasional bug where screen would scroll 1px when typing into a reply form - Fixed the occasional bug where screen would scroll 1px when typing into a reply form

View file

@ -34,7 +34,6 @@
"punycode.js": "^2.1.0", "punycode.js": "^2.1.0",
"v-click-outside": "^2.1.1", "v-click-outside": "^2.1.1",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-chat-scroll": "^1.2.1",
"vue-i18n": "^7.3.2", "vue-i18n": "^7.3.2",
"vue-router": "^3.0.1", "vue-router": "^3.0.1",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.6.11",
@ -103,7 +102,7 @@
"selenium-server": "2.53.1", "selenium-server": "2.53.1",
"semver": "^5.3.0", "semver": "^5.3.0",
"serviceworker-webpack-plugin": "^1.0.0", "serviceworker-webpack-plugin": "^1.0.0",
"shelljs": "^0.7.4", "shelljs": "^0.8.4",
"sinon": "^2.1.0", "sinon": "^2.1.0",
"sinon-chai": "^2.8.0", "sinon-chai": "^2.8.0",
"stylelint": "^13.6.1", "stylelint": "^13.6.1",

View file

@ -586,6 +586,7 @@ nav {
color: var(--faint, $fallback--faint); color: var(--faint, $fallback--faint);
box-shadow: 0px 0px 4px rgba(0,0,0,.6); box-shadow: 0px 0px 4px rgba(0,0,0,.6);
box-shadow: var(--topBarShadow); box-shadow: var(--topBarShadow);
box-sizing: border-box;
} }
.fade-enter-active, .fade-leave-active { .fade-enter-active, .fade-leave-active {
@ -878,6 +879,11 @@ nav {
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
// Get rid of scrollbar on body as scrolling happens on different element
body {
overflow: hidden;
}
// Ensures the fixed position of the mobile browser bars on scroll up / down events. // Ensures the fixed position of the mobile browser bars on scroll up / down events.
// Prevents the mobile browser bars from overlapping or hiding the message posting form. // Prevents the mobile browser bars from overlapping or hiding the message posting form.
@media all and (max-width: 800px) { @media all and (max-width: 800px) {

View file

@ -51,6 +51,7 @@ const getInstanceConfig = async ({ store }) => {
const vapidPublicKey = data.pleroma.vapid_public_key const vapidPublicKey = data.pleroma.vapid_public_key
store.dispatch('setInstanceOption', { name: 'textlimit', value: textlimit }) store.dispatch('setInstanceOption', { name: 'textlimit', value: textlimit })
store.dispatch('setInstanceOption', { name: 'accountApprovalRequired', value: data.approval_required })
if (vapidPublicKey) { if (vapidPublicKey) {
store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey }) store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey })

View file

@ -42,7 +42,7 @@
class="basic-user-card-screen-name" class="basic-user-card-screen-name"
:to="userProfileLink(user)" :to="userProfileLink(user)"
> >
@{{ user.screen_name }} @{{ user.screen_name_ui }}
</router-link> </router-link>
</div> </div>
<slot /> <slot />

View file

@ -73,7 +73,7 @@ const Chat = {
}, },
formPlaceholder () { formPlaceholder () {
if (this.recipient) { if (this.recipient) {
return this.$t('chats.message_user', { nickname: this.recipient.screen_name }) return this.$t('chats.message_user', { nickname: this.recipient.screen_name_ui })
} else { } else {
return '' return ''
} }
@ -234,6 +234,13 @@ const Chat = {
const scrollable = this.$refs.scrollable const scrollable = this.$refs.scrollable
return scrollable && scrollable.scrollTop <= 0 return scrollable && scrollable.scrollTop <= 0
}, },
cullOlderCheck () {
window.setTimeout(() => {
if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) {
this.$store.dispatch('cullOlderMessages', this.currentChatMessageService.chatId)
}
}, 5000)
},
handleScroll: _.throttle(function () { handleScroll: _.throttle(function () {
if (!this.currentChat) { return } if (!this.currentChat) { return }
@ -241,6 +248,7 @@ const Chat = {
this.fetchChat({ maxId: this.currentChatMessageService.minId }) this.fetchChat({ maxId: this.currentChatMessageService.minId })
} else if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) { } else if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) {
this.jumpToBottomButtonVisible = false this.jumpToBottomButtonVisible = false
this.cullOlderCheck()
if (this.newMessageCount > 0) { if (this.newMessageCount > 0) {
// Use a delay before marking as read to prevent situation where new messages // Use a delay before marking as read to prevent situation where new messages
// arrive just as you're leaving the view and messages that you didn't actually // arrive just as you're leaving the view and messages that you didn't actually

View file

@ -98,10 +98,10 @@
.unread-message-count { .unread-message-count {
font-size: 0.8em; font-size: 0.8em;
left: 50%; left: 50%;
transform: translate(-50%, 0);
border-radius: 100%;
margin-top: -1rem; margin-top: -1rem;
padding: 0; padding: 0.1em;
border-radius: 50px;
position: absolute;
} }
.chat-loading-error { .chat-loading-error {

View file

@ -35,6 +35,18 @@ const chatPanel = {
userProfileLink (user) { userProfileLink (user) {
return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames) return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames)
} }
},
watch: {
messages (newVal) {
const scrollEl = this.$el.querySelector('.chat-window')
if (!scrollEl) return
if (scrollEl.scrollTop + scrollEl.offsetHeight + 20 > scrollEl.scrollHeight) {
this.$nextTick(() => {
if (!scrollEl) return
scrollEl.scrollTop = scrollEl.scrollHeight - scrollEl.offsetHeight
})
}
}
} }
} }

View file

@ -10,17 +10,15 @@
@click.stop.prevent="togglePanel" @click.stop.prevent="togglePanel"
> >
<div class="title"> <div class="title">
<span>{{ $t('shoutbox.title') }}</span> {{ $t('shoutbox.title') }}
<FAIcon <FAIcon
v-if="floating" v-if="floating"
icon="times" icon="times"
class="close-icon"
/> />
</div> </div>
</div> </div>
<div <div class="chat-window">
v-chat-scroll
class="chat-window"
>
<div <div
v-for="message in messages" v-for="message in messages"
:key="message.id" :key="message.id"
@ -94,6 +92,13 @@
.icon { .icon {
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--text, $fallback--text);
margin-right: 0.5em;
}
.title {
display: flex;
justify-content: space-between;
align-items: center;
} }
} }

View file

@ -12,7 +12,7 @@ export default Vue.component('chat-title', {
], ],
computed: { computed: {
title () { title () {
return this.user ? this.user.screen_name : '' return this.user ? this.user.screen_name_ui : ''
}, },
htmlTitle () { htmlTitle () {
return this.user ? this.user.name_html : '' return this.user ? this.user.name_html : ''

View file

@ -50,7 +50,6 @@
.Conversation { .Conversation {
.conversation-status { .conversation-status {
border-left: none;
border-bottom-width: 1px; border-bottom-width: 1px;
border-bottom-style: solid; border-bottom-style: solid;
border-bottom-color: var(--border, $fallback--border); border-bottom-color: var(--border, $fallback--border);

View file

@ -194,11 +194,18 @@ const EmojiInput = {
} }
}, },
methods: { methods: {
focusPickerInput () {
const pickerEl = this.$refs.picker.$el
if (!pickerEl) return
const pickerInput = pickerEl.querySelector('input')
if (pickerInput) pickerInput.focus()
},
triggerShowPicker () { triggerShowPicker () {
this.showPicker = true this.showPicker = true
this.$refs.picker.startEmojiLoad() this.$refs.picker.startEmojiLoad()
this.$nextTick(() => { this.$nextTick(() => {
this.scrollIntoView() this.scrollIntoView()
this.focusPickerInput()
}) })
// This temporarily disables "click outside" handler // This temporarily disables "click outside" handler
// since external trigger also means click originates // since external trigger also means click originates
@ -214,6 +221,7 @@ const EmojiInput = {
if (this.showPicker) { if (this.showPicker) {
this.scrollIntoView() this.scrollIntoView()
this.$refs.picker.startEmojiLoad() this.$refs.picker.startEmojiLoad()
this.$nextTick(this.focusPickerInput)
} }
}, },
replace (replacement) { replace (replacement) {

View file

@ -9,8 +9,8 @@
<button <button
v-if="!hideEmojiButton" v-if="!hideEmojiButton"
class="button-unstyled emoji-picker-icon" class="button-unstyled emoji-picker-icon"
@click.prevent="togglePicker"
type="button" type="button"
@click.prevent="togglePicker"
> >
<FAIcon :icon="['far', 'smile-beam']" /> <FAIcon :icon="['far', 'smile-beam']" />
</button> </button>

View file

@ -116,8 +116,8 @@ export const suggestUsers = ({ dispatch, state }) => {
return diff + nameAlphabetically + screenNameAlphabetically return diff + nameAlphabetically + screenNameAlphabetically
/* eslint-disable camelcase */ /* eslint-disable camelcase */
}).map(({ screen_name, name, profile_image_url_original }) => ({ }).map(({ screen_name, screen_name_ui, name, profile_image_url_original }) => ({
displayText: screen_name, displayText: screen_name_ui,
detailText: name, detailText: name,
imageUrl: profile_image_url_original, imageUrl: profile_image_url_original,
replacement: '@' + screen_name + ' ' replacement: '@' + screen_name + ' '

View file

@ -73,11 +73,21 @@
} }
} }
@keyframes media-fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.modal-image { .modal-image {
max-width: 90%; max-width: 90%;
max-height: 90%; max-height: 90%;
box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5); box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5);
image-orientation: from-image; // NOTE: only FF supports this image-orientation: from-image; // NOTE: only FF supports this
animation: 0.1s cubic-bezier(0.7, 0, 1, 0.6) media-fadein;
} }
.modal-view-button-arrow { .modal-view-button-arrow {

View file

@ -25,16 +25,16 @@
<div> <div>
<button <button
class="button-unstyled -link" class="button-unstyled -link"
@click.prevent="requireTOTP"
type="button" type="button"
@click.prevent="requireTOTP"
> >
{{ $t('login.enter_two_factor_code') }} {{ $t('login.enter_two_factor_code') }}
</button> </button>
<br> <br>
<button <button
class="button-unstyled -link" class="button-unstyled -link"
@click.prevent="abortMFA"
type="button" type="button"
@click.prevent="abortMFA"
> >
{{ $t('general.cancel') }} {{ $t('general.cancel') }}
</button> </button>

View file

@ -27,16 +27,16 @@
<div> <div>
<button <button
class="button-unstyled -link" class="button-unstyled -link"
@click.prevent="requireRecovery"
type="button" type="button"
@click.prevent="requireRecovery"
> >
{{ $t('login.enter_recovery_code') }} {{ $t('login.enter_recovery_code') }}
</button> </button>
<br> <br>
<button <button
class="button-unstyled -link" class="button-unstyled -link"
@click.prevent="abortMFA"
type="button" type="button"
@click.prevent="abortMFA"
> >
{{ $t('general.cancel') }} {{ $t('general.cancel') }}
</button> </button>

View file

@ -50,74 +50,74 @@
class="button-default dropdown-item" class="button-default dropdown-item"
@click="toggleTag(tags.FORCE_NSFW)" @click="toggleTag(tags.FORCE_NSFW)"
> >
{{ $t('user_card.admin_menu.force_nsfw') }}
<span <span
class="menu-checkbox" class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_NSFW) }" :class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_NSFW) }"
/> />
{{ $t('user_card.admin_menu.force_nsfw') }}
</button> </button>
<button <button
class="button-default dropdown-item" class="button-default dropdown-item"
@click="toggleTag(tags.STRIP_MEDIA)" @click="toggleTag(tags.STRIP_MEDIA)"
> >
{{ $t('user_card.admin_menu.strip_media') }}
<span <span
class="menu-checkbox" class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hasTag(tags.STRIP_MEDIA) }" :class="{ 'menu-checkbox-checked': hasTag(tags.STRIP_MEDIA) }"
/> />
{{ $t('user_card.admin_menu.strip_media') }}
</button> </button>
<button <button
class="button-default dropdown-item" class="button-default dropdown-item"
@click="toggleTag(tags.FORCE_UNLISTED)" @click="toggleTag(tags.FORCE_UNLISTED)"
> >
{{ $t('user_card.admin_menu.force_unlisted') }}
<span <span
class="menu-checkbox" class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_UNLISTED) }" :class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_UNLISTED) }"
/> />
{{ $t('user_card.admin_menu.force_unlisted') }}
</button> </button>
<button <button
class="button-default dropdown-item" class="button-default dropdown-item"
@click="toggleTag(tags.SANDBOX)" @click="toggleTag(tags.SANDBOX)"
> >
{{ $t('user_card.admin_menu.sandbox') }}
<span <span
class="menu-checkbox" class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hasTag(tags.SANDBOX) }" :class="{ 'menu-checkbox-checked': hasTag(tags.SANDBOX) }"
/> />
{{ $t('user_card.admin_menu.sandbox') }}
</button> </button>
<button <button
v-if="user.is_local" v-if="user.is_local"
class="button-default dropdown-item" class="button-default dropdown-item"
@click="toggleTag(tags.DISABLE_REMOTE_SUBSCRIPTION)" @click="toggleTag(tags.DISABLE_REMOTE_SUBSCRIPTION)"
> >
{{ $t('user_card.admin_menu.disable_remote_subscription') }}
<span <span
class="menu-checkbox" class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_REMOTE_SUBSCRIPTION) }" :class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_REMOTE_SUBSCRIPTION) }"
/> />
{{ $t('user_card.admin_menu.disable_remote_subscription') }}
</button> </button>
<button <button
v-if="user.is_local" v-if="user.is_local"
class="button-default dropdown-item" class="button-default dropdown-item"
@click="toggleTag(tags.DISABLE_ANY_SUBSCRIPTION)" @click="toggleTag(tags.DISABLE_ANY_SUBSCRIPTION)"
> >
{{ $t('user_card.admin_menu.disable_any_subscription') }}
<span <span
class="menu-checkbox" class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_ANY_SUBSCRIPTION) }" :class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_ANY_SUBSCRIPTION) }"
/> />
{{ $t('user_card.admin_menu.disable_any_subscription') }}
</button> </button>
<button <button
v-if="user.is_local" v-if="user.is_local"
class="button-default dropdown-item" class="button-default dropdown-item"
@click="toggleTag(tags.QUARANTINE)" @click="toggleTag(tags.QUARANTINE)"
> >
{{ $t('user_card.admin_menu.quarantine') }}
<span <span
class="menu-checkbox" class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hasTag(tags.QUARANTINE) }" :class="{ 'menu-checkbox-checked': hasTag(tags.QUARANTINE) }"
/> />
{{ $t('user_card.admin_menu.quarantine') }}
</button> </button>
</span> </span>
</div> </div>
@ -163,25 +163,6 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
.menu-checkbox {
float: right;
min-width: 22px;
max-width: 22px;
min-height: 22px;
max-height: 22px;
line-height: 22px;
text-align: center;
border-radius: 0px;
background-color: $fallback--fg;
background-color: var(--input, $fallback--fg);
box-shadow: 0px 0px 2px black inset;
box-shadow: var(--inputShadow);
&.menu-checkbox-checked::after {
content: '✓';
}
}
.moderation-tools-popover { .moderation-tools-popover {
height: 100%; height: 100%;
.trigger { .trigger {

View file

@ -11,7 +11,7 @@
> >
<small> <small>
<router-link :to="userProfileLink"> <router-link :to="userProfileLink">
{{ notification.from_profile.screen_name }} {{ notification.from_profile.screen_name_ui }}
</router-link> </router-link>
</small> </small>
<button <button
@ -54,14 +54,14 @@
<bdi <bdi
v-if="!!notification.from_profile.name_html" v-if="!!notification.from_profile.name_html"
class="username" class="username"
:title="'@'+notification.from_profile.screen_name" :title="'@'+notification.from_profile.screen_name_ui"
v-html="notification.from_profile.name_html" v-html="notification.from_profile.name_html"
/> />
<!-- eslint-enable vue/no-v-html --> <!-- eslint-enable vue/no-v-html -->
<span <span
v-else v-else
class="username" class="username"
:title="'@'+notification.from_profile.screen_name" :title="'@'+notification.from_profile.screen_name_ui"
>{{ notification.from_profile.name }}</span> >{{ notification.from_profile.name }}</span>
<span v-if="notification.type === 'like'"> <span v-if="notification.type === 'like'">
<FAIcon <FAIcon
@ -155,7 +155,7 @@
:to="userProfileLink" :to="userProfileLink"
class="follow-name" class="follow-name"
> >
@{{ notification.from_profile.screen_name }} @{{ notification.from_profile.screen_name_ui }}
</router-link> </router-link>
<div <div
v-if="notification.type === 'follow_request'" v-if="notification.type === 'follow_request'"
@ -180,7 +180,7 @@
class="move-text" class="move-text"
> >
<router-link :to="targetUserProfileLink"> <router-link :to="targetUserProfileLink">
@{{ notification.target.screen_name }} @{{ notification.target.screen_name_ui }}
</router-link> </router-link>
</div> </div>
<template v-else> <template v-else>

View file

@ -151,6 +151,7 @@
border: none; border: none;
box-shadow: none; box-shadow: none;
background-color: transparent; background-color: transparent;
padding-right: 0.75em;
} }
} }

View file

@ -3,25 +3,32 @@ const Popover = {
props: { props: {
// Action to trigger popover: either 'hover' or 'click' // Action to trigger popover: either 'hover' or 'click'
trigger: String, trigger: String,
// Either 'top' or 'bottom' // Either 'top' or 'bottom'
placement: String, placement: String,
// Takes object with properties 'x' and 'y', values of these can be // Takes object with properties 'x' and 'y', values of these can be
// 'container' for using offsetParent as boundaries for either axis // 'container' for using offsetParent as boundaries for either axis
// or 'viewport' // or 'viewport'
boundTo: Object, boundTo: Object,
// Takes a selector to use as a replacement for the parent container // Takes a selector to use as a replacement for the parent container
// for getting boundaries for x an y axis // for getting boundaries for x an y axis
boundToSelector: String, boundToSelector: String,
// Takes a top/bottom/left/right object, how much space to leave // Takes a top/bottom/left/right object, how much space to leave
// between boundary and popover element // between boundary and popover element
margin: Object, margin: Object,
// Takes a x/y object and tells how many pixels to offset from // Takes a x/y object and tells how many pixels to offset from
// anchor point on either axis // anchor point on either axis
offset: Object, offset: Object,
// Replaces the classes you may want for the popover container. // Replaces the classes you may want for the popover container.
// Use 'popover-default' in addition to get the default popover // Use 'popover-default' in addition to get the default popover
// styles with your custom class. // styles with your custom class.
popoverClass: String, popoverClass: String,
// If true, subtract padding when calculating position for the popover, // If true, subtract padding when calculating position for the popover,
// use it when popover offset looks to be different on top vs bottom. // use it when popover offset looks to be different on top vs bottom.
removePadding: Boolean removePadding: Boolean
@ -121,9 +128,12 @@ const Popover = {
} }
}, },
showPopover () { showPopover () {
if (this.hidden) this.$emit('show') const wasHidden = this.hidden
this.hidden = false this.hidden = false
this.$nextTick(this.updateStyles) this.$nextTick(() => {
if (wasHidden) this.$emit('show')
this.updateStyles()
})
}, },
hidePopover () { hidePopover () {
if (!this.hidden) this.$emit('close') if (!this.hidden) this.$emit('close')

View file

@ -6,8 +6,8 @@
<button <button
ref="trigger" ref="trigger"
class="button-unstyled -fullwidth popover-trigger-button" class="button-unstyled -fullwidth popover-trigger-button"
@click="onClick"
type="button" type="button"
@click="onClick"
> >
<slot name="trigger" /> <slot name="trigger" />
</button> </button>
@ -82,10 +82,9 @@
.dropdown-item { .dropdown-item {
line-height: 21px; line-height: 21px;
margin-right: 5px;
overflow: auto; overflow: auto;
display: block; display: block;
padding: .25rem 1.0rem .25rem 1.5rem; padding: .5em 0.75em;
clear: both; clear: both;
font-weight: 400; font-weight: 400;
text-align: inherit; text-align: inherit;
@ -101,10 +100,9 @@
--btnText: var(--popoverText, $fallback--text); --btnText: var(--popoverText, $fallback--text);
&-icon { &-icon {
padding-left: 0.5rem;
svg { svg {
margin-right: 0.25rem; width: 22px;
margin-right: 0.75rem;
color: var(--menuPopoverIcon, $fallback--icon) color: var(--menuPopoverIcon, $fallback--icon)
} }
} }
@ -123,6 +121,33 @@
} }
} }
.menu-checkbox {
display: inline-block;
vertical-align: middle;
min-width: 22px;
max-width: 22px;
min-height: 22px;
max-height: 22px;
line-height: 22px;
text-align: center;
border-radius: 0px;
background-color: $fallback--fg;
background-color: var(--input, $fallback--fg);
box-shadow: 0px 0px 2px black inset;
box-shadow: var(--inputShadow);
margin-right: 0.75em;
&.menu-checkbox-checked::after {
font-size: 1.25em;
content: '✓';
}
&.menu-checkbox-radio::after {
font-size: 2em;
content: '•';
}
}
} }
} }
</style> </style>

View file

@ -115,7 +115,7 @@ const PostStatusForm = {
? this.copyMessageScope ? this.copyMessageScope
: this.$store.state.users.currentUser.default_scope : this.$store.state.users.currentUser.default_scope
const { postContentType: contentType } = this.$store.getters.mergedConfig const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig
return { return {
dropFiles: [], dropFiles: [],
@ -126,7 +126,7 @@ const PostStatusForm = {
newStatus: { newStatus: {
spoilerText: this.subject || '', spoilerText: this.subject || '',
status: statusText, status: statusText,
nsfw: false, nsfw: !!sensitiveByDefault,
files: [], files: [],
poll: {}, poll: {},
mediaDescriptions: {}, mediaDescriptions: {},

View file

@ -23,6 +23,12 @@ const ReactButton = {
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
} }
close() close()
},
focusInput () {
this.$nextTick(() => {
const input = this.$el.querySelector('input')
if (input) input.focus()
})
} }
}, },
computed: { computed: {

View file

@ -6,6 +6,7 @@
:offset="{ y: 5 }" :offset="{ y: 5 }"
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
remove-padding remove-padding
@show="focusInput"
> >
<div <div
slot="content" slot="content"

View file

@ -10,7 +10,8 @@ const registration = {
fullname: '', fullname: '',
username: '', username: '',
password: '', password: '',
confirm: '' confirm: '',
reason: ''
}, },
captcha: {} captcha: {}
}), }),
@ -24,7 +25,8 @@ const registration = {
confirm: { confirm: {
required, required,
sameAsPassword: sameAs('password') sameAsPassword: sameAs('password')
} },
reason: { required: requiredIf(() => this.accountApprovalRequired) }
} }
} }
}, },
@ -38,7 +40,10 @@ const registration = {
computed: { computed: {
token () { return this.$route.params.token }, token () { return this.$route.params.token },
bioPlaceholder () { bioPlaceholder () {
return this.$t('registration.bio_placeholder').replace(/\s*\n\s*/g, ' \n') return this.replaceNewlines(this.$t('registration.bio_placeholder'))
},
reasonPlaceholder () {
return this.replaceNewlines(this.$t('registration.reason_placeholder'))
}, },
...mapState({ ...mapState({
registrationOpen: (state) => state.instance.registrationOpen, registrationOpen: (state) => state.instance.registrationOpen,
@ -46,7 +51,8 @@ const registration = {
isPending: (state) => state.users.signUpPending, isPending: (state) => state.users.signUpPending,
serverValidationErrors: (state) => state.users.signUpErrors, serverValidationErrors: (state) => state.users.signUpErrors,
termsOfService: (state) => state.instance.tos, termsOfService: (state) => state.instance.tos,
accountActivationRequired: (state) => state.instance.accountActivationRequired accountActivationRequired: (state) => state.instance.accountActivationRequired,
accountApprovalRequired: (state) => state.instance.accountApprovalRequired
}) })
}, },
methods: { methods: {
@ -73,6 +79,9 @@ const registration = {
}, },
setCaptcha () { setCaptcha () {
this.getCaptcha().then(cpt => { this.captcha = cpt }) this.getCaptcha().then(cpt => { this.captcha = cpt })
},
replaceNewlines (str) {
return str.replace(/\s*\n\s*/g, ' \n')
} }
} }
} }

View file

@ -162,6 +162,23 @@
</ul> </ul>
</div> </div>
<div
v-if="accountApprovalRequired"
class="form-group"
>
<label
class="form--label"
for="reason"
>{{ $t('registration.reason') }}</label>
<textarea
id="reason"
v-model="user.reason"
:disabled="isPending"
class="form-control"
:placeholder="reasonPlaceholder"
/>
</div>
<div <div
v-if="captcha.type != 'none'" v-if="captcha.type != 'none'"
id="captcha-group" id="captcha-group"

View file

@ -8,8 +8,8 @@
class="button-unstyled scope" class="button-unstyled scope"
:class="css.direct" :class="css.direct"
:title="$t('post_status.scope.direct')" :title="$t('post_status.scope.direct')"
@click="changeVis('direct')"
type="button" type="button"
@click="changeVis('direct')"
> >
<FAIcon <FAIcon
icon="envelope" icon="envelope"
@ -21,8 +21,8 @@
class="button-unstyled scope" class="button-unstyled scope"
:class="css.private" :class="css.private"
:title="$t('post_status.scope.private')" :title="$t('post_status.scope.private')"
@click="changeVis('private')"
type="button" type="button"
@click="changeVis('private')"
> >
<FAIcon <FAIcon
icon="lock" icon="lock"
@ -34,8 +34,8 @@
class="button-unstyled scope" class="button-unstyled scope"
:class="css.unlisted" :class="css.unlisted"
:title="$t('post_status.scope.unlisted')" :title="$t('post_status.scope.unlisted')"
@click="changeVis('unlisted')"
type="button" type="button"
@click="changeVis('unlisted')"
> >
<FAIcon <FAIcon
icon="lock-open" icon="lock-open"
@ -47,8 +47,8 @@
class="button-unstyled scope" class="button-unstyled scope"
:class="css.public" :class="css.public"
:title="$t('post_status.scope.public')" :title="$t('post_status.scope.public')"
@click="changeVis('public')"
type="button" type="button"
@click="changeVis('public')"
> >
<FAIcon <FAIcon
icon="globe" icon="globe"

View file

@ -15,8 +15,8 @@
> >
<button <button
class="btn button-default search-button" class="btn button-default search-button"
@click="newQuery(searchTerm)"
type="submit" type="submit"
@click="newQuery(searchTerm)"
> >
<FAIcon icon="search" /> <FAIcon icon="search" />
</button> </button>

View file

@ -7,8 +7,8 @@
v-if="hidden" v-if="hidden"
class="button-unstyled nav-icon" class="button-unstyled nav-icon"
:title="$t('nav.search')" :title="$t('nav.search')"
@click.prevent.stop="toggleHidden"
type="button" type="button"
@click.prevent.stop="toggleHidden"
> >
<FAIcon <FAIcon
fixed-width fixed-width
@ -28,8 +28,8 @@
> >
<button <button
class="button-default search-button" class="button-default search-button"
@click="find(searchTerm)"
type="submit" type="submit"
@click="find(searchTerm)"
> >
<FAIcon <FAIcon
fixed-width fixed-width
@ -38,8 +38,8 @@
</button> </button>
<button <button
class="button-unstyled cancel-search" class="button-unstyled cancel-search"
@click.prevent.stop="toggleHidden"
type="button" type="button"
@click.prevent.stop="toggleHidden"
> >
<FAIcon <FAIcon
fixed-width fixed-width

View file

@ -4,13 +4,13 @@
> >
<Checkbox <Checkbox
:checked="state" :checked="state"
@change="update"
:disabled="disabled" :disabled="disabled"
@change="update"
> >
<span <span
v-if="!!$slots.default" v-if="!!$slots.default"
class="label" class="label"
> >
<slot /> <slot />
</span> </span>
<ModifiedIndicator :changed="isChanged" /> <ModifiedIndicator :changed="isChanged" />
@ -23,14 +23,14 @@ import { get, set } from 'lodash'
import Checkbox from 'src/components/checkbox/checkbox.vue' import Checkbox from 'src/components/checkbox/checkbox.vue'
import ModifiedIndicator from './modified_indicator.vue' import ModifiedIndicator from './modified_indicator.vue'
export default { export default {
props: [
'path',
'disabled'
],
components: { components: {
Checkbox, Checkbox,
ModifiedIndicator ModifiedIndicator
}, },
props: [
'path',
'disabled'
],
computed: { computed: {
pathDefault () { pathDefault () {
const [firstSegment, ...rest] = this.path.split('.') const [firstSegment, ...rest] = this.path.split('.')

View file

@ -2,21 +2,21 @@
<span <span
v-if="changed" v-if="changed"
class="ModifiedIndicator" class="ModifiedIndicator"
> >
<Popover <Popover
trigger="hover" trigger="hover"
> >
<span slot="trigger"> <span slot="trigger">
&nbsp; &nbsp;
<FAIcon <FAIcon
icon="wrench" icon="wrench"
/> />
</span> </span>
<div <div
class="modified-tooltip"
slot="content" slot="content"
> class="modified-tooltip"
{{ $t('settings.setting_changed') }} >
{{ $t('settings.setting_changed') }}
</div> </div>
</Popover> </Popover>
</span> </span>
@ -32,8 +32,8 @@ library.add(
) )
export default { export default {
props: ['changed'], components: { Popover },
components: { Popover } props: ['changed']
} }
</script> </script>

View file

@ -75,8 +75,8 @@
<p>{{ $t('settings.filtering_explanation') }}</p> <p>{{ $t('settings.filtering_explanation') }}</p>
<textarea <textarea
id="muteWords" id="muteWords"
class="resize-height"
v-model="muteWordsString" v-model="muteWordsString"
class="resize-height"
/> />
</div> </div>
<div> <div>

View file

@ -159,7 +159,12 @@
</li> </li>
<li> <li>
<BooleanSetting path="minimalScopesMode"> <BooleanSetting path="minimalScopesMode">
{{ $t('settings.minimal_scopes_mode') }} {{ minimalScopesModeDefaultValue }} {{ $t('settings.minimal_scopes_mode') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="sensitiveByDefault">
{{ $t('settings.sensitive_by_default') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li> <li>

View file

@ -136,7 +136,7 @@ const Status = {
} }
}, },
retweet () { return !!this.statusoid.retweeted_status }, retweet () { return !!this.statusoid.retweeted_status },
retweeter () { return this.statusoid.user.name || this.statusoid.user.screen_name }, retweeter () { return this.statusoid.user.name || this.statusoid.user.screen_name_ui },
retweeterHtml () { return this.statusoid.user.name_html }, retweeterHtml () { return this.statusoid.user.name_html },
retweeterProfileLink () { return this.generateUserProfileLink(this.statusoid.user.id, this.statusoid.user.screen_name) }, retweeterProfileLink () { return this.generateUserProfileLink(this.statusoid.user.id, this.statusoid.user.screen_name) },
status () { status () {
@ -216,7 +216,7 @@ const Status = {
return this.status.in_reply_to_screen_name return this.status.in_reply_to_screen_name
} else { } else {
const user = this.$store.getters.findUser(this.status.in_reply_to_user_id) const user = this.$store.getters.findUser(this.status.in_reply_to_user_id)
return user && user.screen_name return user && user.screen_name_ui
} }
}, },
replySubject () { replySubject () {

View file

@ -26,7 +26,7 @@
icon="retweet" icon="retweet"
/> />
<router-link :to="userProfileLink"> <router-link :to="userProfileLink">
{{ status.user.screen_name }} {{ status.user.screen_name_ui }}
</router-link> </router-link>
</small> </small>
<small <small
@ -156,10 +156,10 @@
</h4> </h4>
<router-link <router-link
class="account-name" class="account-name"
:title="status.user.screen_name" :title="status.user.screen_name_ui"
:to="userProfileLink" :to="userProfileLink"
> >
{{ status.user.screen_name }} {{ status.user.screen_name_ui }}
</router-link> </router-link>
<img <img
v-if="!!(status.user && status.user.favicon)" v-if="!!(status.user && status.user.favicon)"

View file

@ -2,12 +2,14 @@ import Status from '../status/status.vue'
import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js' import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js'
import Conversation from '../conversation/conversation.vue' import Conversation from '../conversation/conversation.vue'
import TimelineMenu from '../timeline_menu/timeline_menu.vue' import TimelineMenu from '../timeline_menu/timeline_menu.vue'
import TimelineQuickSettings from './timeline_quick_settings.vue'
import { debounce, throttle, keyBy } from 'lodash' import { debounce, throttle, keyBy } from 'lodash'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import { faCircleNotch, faCog } from '@fortawesome/free-solid-svg-icons'
library.add( library.add(
faCircleNotch faCircleNotch,
faCog
) )
export const getExcludedStatusIdsByPinning = (statuses, pinnedStatusIds) => { export const getExcludedStatusIdsByPinning = (statuses, pinnedStatusIds) => {
@ -47,7 +49,8 @@ const Timeline = {
components: { components: {
Status, Status,
Conversation, Conversation,
TimelineMenu TimelineMenu,
TimelineQuickSettings
}, },
computed: { computed: {
newStatusCount () { newStatusCount () {

View file

@ -16,6 +16,7 @@
> >
{{ $t('timeline.up_to_date') }} {{ $t('timeline.up_to_date') }}
</div> </div>
<TimelineQuickSettings v-if="!embedded" />
</div> </div>
<div :class="classes.body"> <div :class="classes.body">
<div <div
@ -103,9 +104,12 @@
max-width: 100%; max-width: 100%;
flex-wrap: nowrap; flex-wrap: nowrap;
align-items: center; align-items: center;
position: relative;
.loadmore-button { .loadmore-button {
flex-shrink: 0; flex-shrink: 0;
} }
.loadmore-text { .loadmore-text {
flex-shrink: 0; flex-shrink: 0;
line-height: 1em; line-height: 1em;

View file

@ -0,0 +1,63 @@
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'
library.add(
faFilter,
faFont,
faWrench
)
const TimelineQuickSettings = {
components: {
Popover,
BooleanSetting
},
methods: {
setReplyVisibility (visibility) {
this.$store.dispatch('setOption', { name: 'replyVisibility', value: visibility })
this.$store.dispatch('queueFlushAll')
},
openTab (tab) {
this.$store.dispatch('openSettingsModalTab', tab)
}
},
computed: {
...mapGetters(['mergedConfig']),
loggedIn () {
return !!this.$store.state.users.currentUser
},
replyVisibilitySelf: {
get () { return this.mergedConfig.replyVisibility === 'self' },
set () { this.setReplyVisibility('self') }
},
replyVisibilityFollowing: {
get () { return this.mergedConfig.replyVisibility === 'following' },
set () { this.setReplyVisibility('following') }
},
replyVisibilityAll: {
get () { return this.mergedConfig.replyVisibility === 'all' },
set () { this.setReplyVisibility('all') }
},
hideMedia: {
get () { return this.mergedConfig.hideAttachments || this.mergedConfig.hideAttachmentsInConv },
set () {
const value = !this.hideMedia
this.$store.dispatch('setOption', { name: 'hideAttachments', value })
this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value })
}
},
hideMutedPosts: {
get () { return this.mergedConfig.hideMutedPosts || this.mergedConfig.hideFilteredStatuses },
set () {
const value = !this.hideMutedPosts
this.$store.dispatch('setOption', { name: 'hideMutedPosts', value })
this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value })
}
}
}
}
export default TimelineQuickSettings

View file

@ -0,0 +1,107 @@
<template>
<Popover
trigger="click"
class="TimelineQuickSettings"
:bound-to="{ x: 'container' }"
>
<div
slot="content"
class="timeline-settings-menu dropdown-menu"
>
<div v-if="loggedIn">
<button
class="button-default dropdown-item"
@click="replyVisibilityAll = true"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityAll }"
/>{{ $t('settings.reply_visibility_all') }}
</button>
<button
class="button-default dropdown-item"
@click="replyVisibilityFollowing = true"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityFollowing }"
/>{{ $t('settings.reply_visibility_following_short') }}
</button>
<button
class="button-default dropdown-item"
@click="replyVisibilitySelf = true"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilitySelf }"
/>{{ $t('settings.reply_visibility_self_short') }}
</button>
<div
role="separator"
class="dropdown-divider"
/>
</div>
<button
class="button-default dropdown-item"
@click="hideMedia = !hideMedia"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hideMedia }"
/>{{ $t('settings.hide_media_previews') }}
</button>
<button
class="button-default dropdown-item"
@click="hideMutedPosts = !hideMutedPosts"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hideMutedPosts }"
/>{{ $t('settings.hide_all_muted_posts') }}
</button>
<button
class="button-default dropdown-item dropdown-item-icon"
@click="openTab('filtering')"
>
<FAIcon icon="font" />{{ $t('settings.word_filter') }}
</button>
<button
class="button-default dropdown-item dropdown-item-icon"
@click="openTab('general')"
>
<FAIcon icon="wrench" />{{ $t('settings.more_settings') }}
</button>
</div>
<div slot="trigger">
<FAIcon icon="filter" />
</div>
</Popover>
</template>
<script src="./timeline_quick_settings.js"></script>
<style lang="scss">
.TimelineQuickSettings {
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;
}
.timeline-settings-menu {
display: flex;
min-width: 12em;
flex-direction: column;
}
}
</style>

View file

@ -2,8 +2,8 @@
<StillImage <StillImage
v-if="user" v-if="user"
class="Avatar" class="Avatar"
:alt="user.screen_name" :alt="user.screen_name_ui"
:title="user.screen_name" :title="user.screen_name_ui"
:src="imgSrc(user.profile_image_url_original)" :src="imgSrc(user.profile_image_url_original)"
:class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }" :class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }"
:image-load-error="imageLoadError" :image-load-error="imageLoadError"

View file

@ -73,17 +73,17 @@
<div class="bottom-line"> <div class="bottom-line">
<router-link <router-link
class="user-screen-name" class="user-screen-name"
:title="user.screen_name" :title="user.screen_name_ui"
:to="userProfileLink(user)" :to="userProfileLink(user)"
> >
@{{ user.screen_name }} @{{ user.screen_name_ui }}
</router-link> </router-link>
<template v-if="!hideBio"> <template v-if="!hideBio">
<span <span
v-if="!!visibleRole" v-if="!!visibleRole"
class="alert user-role" class="alert user-role"
> >
{{ $t(`user_card.roles.${visibleRole}`) }} {{ $t(`general.role.${visibleRole}`) }}
</span> </span>
<span <span
v-if="user.bot" v-if="user.bot"
@ -141,10 +141,10 @@
v-model="userHighlightType" v-model="userHighlightType"
class="userHighlightSel" class="userHighlightSel"
> >
<option value="disabled">No highlight</option> <option value="disabled">{{ $t('user_card.highlight.disabled') }}</option>
<option value="solid">Solid bg</option> <option value="solid">{{ $t('user_card.highlight.solid') }}</option>
<option value="striped">Striped bg</option> <option value="striped">{{ $t('user_card.highlight.striped') }}</option>
<option value="side">Side stripe</option> <option value="side">{{ $t('user_card.highlight.side') }}</option>
</select> </select>
<FAIcon <FAIcon
class="select-down-icon" class="select-down-icon"

View file

@ -26,7 +26,7 @@
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<span v-html="user.name_html" /> <span v-html="user.name_html" />
<!-- eslint-enable vue/no-v-html --> <!-- eslint-enable vue/no-v-html -->
<span class="user-list-screen-name">{{ user.screen_name }}</span> <span class="user-list-screen-name">{{ user.screen_name_ui }}</span>
</div> </div>
</div> </div>
</div> </div>

View file

@ -6,7 +6,7 @@
<div class="user-reporting-panel panel"> <div class="user-reporting-panel panel">
<div class="panel-heading"> <div class="panel-heading">
<div class="title"> <div class="title">
{{ $t('user_reporting.title', [user.screen_name]) }} {{ $t('user_reporting.title', [user.screen_name_ui]) }}
</div> </div>
</div> </div>
<div class="panel-body"> <div class="panel-body">

View file

@ -228,6 +228,8 @@
"username_placeholder": "e.g. lain", "username_placeholder": "e.g. lain",
"fullname_placeholder": "e.g. Lain Iwakura", "fullname_placeholder": "e.g. Lain Iwakura",
"bio_placeholder": "e.g.\nHi, I'm Lain.\nIm an anime girl living in suburban Japan. You may know me from the Wired.", "bio_placeholder": "e.g.\nHi, I'm Lain.\nIm 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.",
"validations": { "validations": {
"username_required": "cannot be left blank", "username_required": "cannot be left blank",
"fullname_required": "cannot be left blank", "fullname_required": "cannot be left blank",
@ -323,6 +325,7 @@
"export_theme": "Save preset", "export_theme": "Save preset",
"filtering": "Filtering", "filtering": "Filtering",
"filtering_explanation": "All statuses containing these words will be muted, one per line", "filtering_explanation": "All statuses containing these words will be muted, one per line",
"word_filter": "Word filter",
"follow_export": "Follow export", "follow_export": "Follow export",
"follow_export_button": "Export your follows to a csv file", "follow_export_button": "Export your follows to a csv file",
"follow_import": "Follow import", "follow_import": "Follow import",
@ -333,7 +336,9 @@
"general": "General", "general": "General",
"hide_attachments_in_convo": "Hide attachments in conversations", "hide_attachments_in_convo": "Hide attachments in conversations",
"hide_attachments_in_tl": "Hide attachments in timeline", "hide_attachments_in_tl": "Hide attachments in timeline",
"hide_media_previews": "Hide media previews",
"hide_muted_posts": "Hide posts of muted users", "hide_muted_posts": "Hide posts of muted users",
"hide_all_muted_posts": "Hide muted posts",
"max_thumbnails": "Maximum amount of thumbnails per post", "max_thumbnails": "Maximum amount of thumbnails per post",
"hide_isp": "Hide instance-specific panel", "hide_isp": "Hide instance-specific panel",
"show_third_column": "Move Notifications to a seperate column", "show_third_column": "Move Notifications to a seperate column",
@ -406,6 +411,8 @@
"reply_visibility_all": "Show all replies", "reply_visibility_all": "Show all replies",
"reply_visibility_following": "Only show replies directed at me or users I'm following", "reply_visibility_following": "Only show replies directed at me or users I'm following",
"reply_visibility_self": "Only show replies directed at me", "reply_visibility_self": "Only show replies directed at me",
"reply_visibility_following_short": "Show replies to my follows",
"reply_visibility_self_short": "Show replies to self only",
"autohide_floating_post_button": "Automatically hide New Post button (mobile)", "autohide_floating_post_button": "Automatically hide New Post button (mobile)",
"saving_err": "Error saving settings", "saving_err": "Error saving settings",
"saving_ok": "Settings saved", "saving_ok": "Settings saved",
@ -430,6 +437,7 @@
"subject_line_mastodon": "Like mastodon: copy as is", "subject_line_mastodon": "Like mastodon: copy as is",
"subject_line_noop": "Do not copy", "subject_line_noop": "Do not copy",
"post_status_content_type": "Post status content type", "post_status_content_type": "Post status content type",
"sensitive_by_default": "Mark posts as sensitive by default",
"stop_gifs": "Play-on-hover GIFs", "stop_gifs": "Play-on-hover GIFs",
"streaming": "Enable automatic streaming of new posts when scrolled to the top", "streaming": "Enable automatic streaming of new posts when scrolled to the top",
"user_mutes": "Users", "user_mutes": "Users",
@ -459,6 +467,7 @@
"notification_mutes": "To stop receiving notifications from a specific user, use a mute.", "notification_mutes": "To stop receiving notifications from a specific user, use a mute.",
"notification_blocks": "Blocking a user stops all notifications as well as unsubscribes them.", "notification_blocks": "Blocking a user stops all notifications as well as unsubscribes them.",
"enable_web_push_notifications": "Enable web push notifications", "enable_web_push_notifications": "Enable web push notifications",
"more_settings": "More settings",
"style": { "style": {
"switcher": { "switcher": {
"keep_color": "Keep colors", "keep_color": "Keep colors",
@ -741,9 +750,11 @@
"delete_user": "Delete user", "delete_user": "Delete user",
"delete_user_confirmation": "Are you absolutely sure? This action cannot be undone." "delete_user_confirmation": "Are you absolutely sure? This action cannot be undone."
}, },
"roles": { "highlight": {
"admin": "Admin", "disabled": "No highlight",
"moderator": "Moderator" "solid": "Solid bg",
"striped": "Striped bg",
"side": "Side stripe"
} }
}, },
"user_profile": { "user_profile": {

View file

@ -35,7 +35,11 @@
"retry": "Reprovi", "retry": "Reprovi",
"error_retry": "Bonvolu reprovi", "error_retry": "Bonvolu reprovi",
"loading": "Enlegante…", "loading": "Enlegante…",
"peek": "Antaŭmontri" "peek": "Antaŭmontri",
"role": {
"moderator": "Reguligisto",
"admin": "Administranto"
}
}, },
"image_cropper": { "image_cropper": {
"crop_picture": "Tondi bildon", "crop_picture": "Tondi bildon",
@ -365,7 +369,8 @@
"post": "Afiŝoj/Priskriboj de uzantoj", "post": "Afiŝoj/Priskriboj de uzantoj",
"alert_neutral": "Neŭtrala", "alert_neutral": "Neŭtrala",
"alert_warning": "Averto", "alert_warning": "Averto",
"toggled": "Ŝaltita" "toggled": "Ŝaltita",
"wallpaper": "Fonbildo"
}, },
"radii": { "radii": {
"_tab_label": "Rondeco" "_tab_label": "Rondeco"
@ -516,7 +521,9 @@
"mute_import_error": "Eraris enporto de silentigoj", "mute_import_error": "Eraris enporto de silentigoj",
"mute_import": "Enporto de silentigoj", "mute_import": "Enporto de silentigoj",
"mute_export_button": "Elportu viajn silentigojn al CSV-dosiero", "mute_export_button": "Elportu viajn silentigojn al CSV-dosiero",
"mute_export": "Elporto de silentigoj" "mute_export": "Elporto de silentigoj",
"hide_wallpaper": "Kaŝi fonbildon de nodo",
"setting_changed": "Agordo malsamas de la implicita"
}, },
"timeline": { "timeline": {
"collapse": "Maletendi", "collapse": "Maletendi",
@ -586,7 +593,8 @@
"show_repeats": "Montri ripetojn", "show_repeats": "Montri ripetojn",
"hide_repeats": "Kaŝi ripetojn", "hide_repeats": "Kaŝi ripetojn",
"unsubscribe": "Ne ricevi sciigojn", "unsubscribe": "Ne ricevi sciigojn",
"subscribe": "Ricevi sciigojn" "subscribe": "Ricevi sciigojn",
"bot": "Roboto"
}, },
"user_profile": { "user_profile": {
"timeline_title": "Historio de uzanto", "timeline_title": "Historio de uzanto",
@ -612,7 +620,8 @@
"error": { "error": {
"base": "Alŝuto malsukcesis.", "base": "Alŝuto malsukcesis.",
"file_too_big": "Dosiero estas tro granda [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "file_too_big": "Dosiero estas tro granda [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
"default": "Reprovu pli poste" "default": "Reprovu pli poste",
"message": "Malsukcesis alŝuto: {0}"
}, },
"file_size_units": { "file_size_units": {
"B": "B", "B": "B",
@ -645,7 +654,9 @@
"votes": "voĉoj", "votes": "voĉoj",
"option": "Elekteblo", "option": "Elekteblo",
"add_option": "Aldoni elekteblon", "add_option": "Aldoni elekteblon",
"add_poll": "Aldoni enketon" "add_poll": "Aldoni enketon",
"votes_count": "{count} voĉdono | {count} voĉdonoj",
"people_voted_count": "{count} persono voĉdonis | {count} personoj voĉdonis"
}, },
"importer": { "importer": {
"error": "Eraris enporto de ĉi tiu dosiero.", "error": "Eraris enporto de ĉi tiu dosiero.",
@ -732,7 +743,9 @@
"repeats": "Ripetoj", "repeats": "Ripetoj",
"favorites": "Ŝatoj", "favorites": "Ŝatoj",
"status_deleted": "Ĉi tiu afiŝo foriĝis", "status_deleted": "Ĉi tiu afiŝo foriĝis",
"nsfw": "Konsterna" "nsfw": "Konsterna",
"expand": "Etendi",
"external_source": "Ekstera fonto"
}, },
"time": { "time": {
"years_short": "{0}j", "years_short": "{0}j",

View file

@ -562,7 +562,8 @@
"mute_import": "Importar silenciados", "mute_import": "Importar silenciados",
"mute_export_button": "Exportar los silenciados a un archivo csv", "mute_export_button": "Exportar los silenciados a un archivo csv",
"mute_export": "Exportar silenciados", "mute_export": "Exportar silenciados",
"hide_wallpaper": "Ocultar el fondo de pantalla de la instancia" "hide_wallpaper": "Ocultar el fondo de pantalla de la instancia",
"setting_changed": "La configuración es diferente a la predeterminada"
}, },
"time": { "time": {
"day": "{0} día", "day": "{0} día",
@ -693,7 +694,11 @@
"show_repeats": "Mostrar repetidos", "show_repeats": "Mostrar repetidos",
"hide_repeats": "Ocultar repetidos", "hide_repeats": "Ocultar repetidos",
"message": "Mensaje", "message": "Mensaje",
"hidden": "Oculto" "hidden": "Oculto",
"roles": {
"moderator": "Moderador",
"admin": "Administrador"
}
}, },
"user_profile": { "user_profile": {
"timeline_title": "Linea Temporal del Usuario", "timeline_title": "Linea Temporal del Usuario",

View file

@ -280,7 +280,7 @@
"hide_followers_description": "Ne pas afficher qui est abonné à moi", "hide_followers_description": "Ne pas afficher qui est abonné à moi",
"show_admin_badge": "Afficher le badge d'Administrateur⋅ice sur mon profil", "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_moderator_badge": "Afficher le badge de Modérateur⋅ice sur mon profil",
"nsfw_clickthrough": "Masquer les images marquées comme contenu adulte ou sensible", "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", "oauth_tokens": "Jetons OAuth",
"token": "Jeton", "token": "Jeton",
"refresh_token": "Rafraichir le jeton", "refresh_token": "Rafraichir le jeton",
@ -409,7 +409,13 @@
"tabs": "Onglets", "tabs": "Onglets",
"toggled": "(Dés)activé", "toggled": "(Dés)activé",
"highlight": "Éléments mis en valeur", "highlight": "Éléments mis en valeur",
"popover": "Infobulles, menus" "popover": "Infobulles, menus",
"chat": {
"border": "Bordure",
"outgoing": "Sortant(s)",
"incoming": "Entrant(s)"
},
"wallpaper": "Fond d'écran"
}, },
"radii": { "radii": {
"_tab_label": "Rondeur" "_tab_label": "Rondeur"
@ -485,7 +491,7 @@
"notification_visibility_emoji_reactions": "Réactions", "notification_visibility_emoji_reactions": "Réactions",
"hide_follows_count_description": "Masquer le nombre de suivis", "hide_follows_count_description": "Masquer le nombre de suivis",
"useStreamingApiWarning": "(Non recommandé, expérimental, connu pour rater des messages)", "useStreamingApiWarning": "(Non recommandé, expérimental, connu pour rater des messages)",
"type_domains_to_mute": "Écrire les domaines à masquer", "type_domains_to_mute": "Chercher les domaines à masquer",
"fun": "Rigolo", "fun": "Rigolo",
"greentext": "greentexting", "greentext": "greentexting",
"allow_following_move": "Suivre automatiquement quand ce compte migre", "allow_following_move": "Suivre automatiquement quand ce compte migre",
@ -509,7 +515,21 @@
"mute_import_error": "Erreur à l'import des masquages", "mute_import_error": "Erreur à l'import des masquages",
"mute_import": "Import des masquages", "mute_import": "Import des masquages",
"mute_export_button": "Exporter vos masquages dans un fichier CSV", "mute_export_button": "Exporter vos masquages dans un fichier CSV",
"mute_export": "Export des masquages" "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é",
"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_avatar": "Réinitialiser l'avatar",
"profile_fields": {
"value": "Contenu",
"name": "Étiquette",
"add_field": "Ajouter un champ"
}
}, },
"timeline": { "timeline": {
"collapse": "Fermer", "collapse": "Fermer",
@ -521,7 +541,9 @@
"show_new": "Afficher plus", "show_new": "Afficher plus",
"up_to_date": "À jour", "up_to_date": "À jour",
"no_more_statuses": "Pas plus de statuts", "no_more_statuses": "Pas plus de statuts",
"no_statuses": "Aucun statuts" "no_statuses": "Aucun statuts",
"reload": "Recharger",
"error": "Erreur lors de l'affichage du fil d'actualité : {0}"
}, },
"status": { "status": {
"favorites": "Favoris", "favorites": "Favoris",
@ -536,7 +558,19 @@
"mute_conversation": "Masquer la conversation", "mute_conversation": "Masquer la conversation",
"unmute_conversation": "Démasquer la conversation", "unmute_conversation": "Démasquer la conversation",
"status_unavailable": "Status indisponible", "status_unavailable": "Status indisponible",
"copy_link": "Copier le lien au status" "copy_link": "Copier le lien au status",
"expand": "Développer",
"nsfw": "Contenu sensible",
"status_deleted": "Ce post a été effacé",
"hide_content": "Cacher le contenu",
"show_content": "Montrer le contenu",
"hide_full_subject": "Cacher le sujet",
"show_full_subject": "Montrer le sujet en entier",
"thread_muted_and_words": ", contient les mots :",
"thread_muted": "Fil de discussion masqué",
"external_source": "Source externe",
"unbookmark": "Supprimer des favoris",
"bookmark": "Ajouter aux favoris"
}, },
"user_card": { "user_card": {
"approve": "Accepter", "approve": "Accepter",
@ -591,7 +625,12 @@
"subscribe": "Abonner", "subscribe": "Abonner",
"unsubscribe": "Désabonner", "unsubscribe": "Désabonner",
"hide_repeats": "Cacher les partages", "hide_repeats": "Cacher les partages",
"show_repeats": "Montrer les partages" "show_repeats": "Montrer les partages",
"roles": {
"moderator": "Modérateur⋅ice",
"admin": "Administrateur⋅ice"
},
"message": "Message"
}, },
"user_profile": { "user_profile": {
"timeline_title": "Journal de l'utilisateur⋅ice", "timeline_title": "Journal de l'utilisateur⋅ice",
@ -619,13 +658,15 @@
"user_settings": "Paramètres utilisateur", "user_settings": "Paramètres utilisateur",
"add_reaction": "Ajouter une réaction", "add_reaction": "Ajouter une réaction",
"accept_follow_request": "Accepter la demande de suivit", "accept_follow_request": "Accepter la demande de suivit",
"reject_follow_request": "Rejeter la demande de suivit" "reject_follow_request": "Rejeter la demande de suivit",
"bookmark": "Favori"
}, },
"upload": { "upload": {
"error": { "error": {
"base": "L'envoi a échoué.", "base": "L'envoi a échoué.",
"file_too_big": "Fichier trop gros [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "file_too_big": "Fichier trop gros [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
"default": "Réessayez plus tard" "default": "Réessayez plus tard",
"message": "Envoi échoué : {0}"
}, },
"file_size_units": { "file_size_units": {
"B": "O", "B": "O",
@ -759,5 +800,27 @@
}, },
"shoutbox": { "shoutbox": {
"title": "Shoutbox" "title": "Shoutbox"
},
"display_date": {
"today": "Aujourd'hui"
},
"file_type": {
"file": "Fichier",
"image": "Image",
"video": "Vidéo",
"audio": "Audio"
},
"chats": {
"empty_chat_list_placeholder": "Vous n'avez pas encore de discussions. Démarrez-en une nouvelle !",
"error_sending_message": "Quelque chose s'est mal passé pendant l'envoi du message.",
"error_loading_chat": "Quelque chose s'est mal passé au chargement de la discussion.",
"delete_confirm": "Voulez-vous vraiment effacer ce message ?",
"more": "Plus",
"empty_message_error": "Impossible d'envoyer un message vide",
"new": "Nouvelle discussion",
"chats": "Discussions",
"delete": "Effacer",
"message_user": "Message à {nickname}",
"you": "Vous :"
} }
} }

View file

@ -17,7 +17,11 @@
"close": "Chiudi", "close": "Chiudi",
"retry": "Riprova", "retry": "Riprova",
"error_retry": "Per favore, riprova", "error_retry": "Per favore, riprova",
"loading": "Carico…" "loading": "Carico…",
"role": {
"moderator": "Moderatore",
"admin": "Amministratore"
}
}, },
"nav": { "nav": {
"mentions": "Menzioni", "mentions": "Menzioni",
@ -417,7 +421,8 @@
"mute_import": "Importa silenziati", "mute_import": "Importa silenziati",
"mute_export_button": "Esporta la tua lista di silenziati in un file CSV", "mute_export_button": "Esporta la tua lista di silenziati in un file CSV",
"mute_export": "Esporta silenziati", "mute_export": "Esporta silenziati",
"hide_wallpaper": "Nascondi sfondo della stanza" "hide_wallpaper": "Nascondi sfondo della stanza",
"setting_changed": "Valore personalizzato"
}, },
"timeline": { "timeline": {
"error_fetching": "Errore nell'aggiornamento", "error_fetching": "Errore nell'aggiornamento",
@ -488,10 +493,7 @@
"follow_sent": "Richiesta inviata!", "follow_sent": "Richiesta inviata!",
"favorites": "Preferiti", "favorites": "Preferiti",
"message": "Contatta", "message": "Contatta",
"roles": { "bot": "Bot"
"moderator": "Moderatore",
"admin": "Amministratore"
}
}, },
"chat": { "chat": {
"title": "Chat" "title": "Chat"
@ -582,7 +584,9 @@
"fullname_placeholder": "es. Lupo Lucio", "fullname_placeholder": "es. Lupo Lucio",
"username_placeholder": "es. mister_wolf", "username_placeholder": "es. mister_wolf",
"new_captcha": "Clicca l'immagine per avere un altro captcha", "new_captcha": "Clicca l'immagine per avere un altro captcha",
"captcha": "CAPTCHA" "captcha": "CAPTCHA",
"reason_placeholder": "L'amministratore esamina ciascuna richiesta.\nFornisci il motivo della tua iscrizione.",
"reason": "Motivo dell'iscrizione"
}, },
"user_profile": { "user_profile": {
"timeline_title": "Sequenza dell'Utente", "timeline_title": "Sequenza dell'Utente",
@ -660,7 +664,9 @@
"expiry": "Età", "expiry": "Età",
"expires_in": "Chiude fra {0}", "expires_in": "Chiude fra {0}",
"expired": "Chiuso {0} fa", "expired": "Chiuso {0} fa",
"not_enough_options": "Aggiungi altre risposte" "not_enough_options": "Aggiungi altre risposte",
"votes_count": "{count} voto | {count} voti",
"people_voted_count": "{count} votante | {count} votanti"
}, },
"interactions": { "interactions": {
"favs_repeats": "Condivisi e Graditi", "favs_repeats": "Condivisi e Graditi",

View file

@ -4,7 +4,7 @@
}, },
"exporter": { "exporter": {
"export": "エクスポート", "export": "エクスポート",
"processing": "処理中です。処理が完了すると、ファイルをダウンロードするよう指示があります" "processing": "処理中です。処理が完了すると、ファイルをダウンロードするよう指示があります"
}, },
"features_panel": { "features_panel": {
"chat": "チャット", "chat": "チャット",
@ -13,10 +13,12 @@
"scope_options": "公開範囲選択", "scope_options": "公開範囲選択",
"text_limit": "文字の数", "text_limit": "文字の数",
"title": "有効な機能", "title": "有効な機能",
"who_to_follow": "おすすめユーザー" "who_to_follow": "おすすめユーザー",
"upload_limit": "ファイルサイズの上限",
"pleroma_chat_messages": "Pleroma チャット"
}, },
"finder": { "finder": {
"error_fetching_user": "ユーザー検索がエラーになりました", "error_fetching_user": "ユーザー検索がエラーになりました",
"find_user": "ユーザーを探す" "find_user": "ユーザーを探す"
}, },
"general": { "general": {
@ -31,7 +33,17 @@
"disable": "無効", "disable": "無効",
"enable": "有効", "enable": "有効",
"confirm": "確認", "confirm": "確認",
"verify": "検査" "verify": "検査",
"peek": "隠す",
"close": "閉じる",
"dismiss": "無視",
"retry": "もう一度お試し下さい",
"error_retry": "もう一度お試し下さい",
"loading": "読み込み中…",
"role": {
"moderator": "モデレーター",
"admin": "管理者"
}
}, },
"image_cropper": { "image_cropper": {
"crop_picture": "画像を切り抜く", "crop_picture": "画像を切り抜く",
@ -57,9 +69,9 @@
"enter_recovery_code": "リカバリーコードを入力してください", "enter_recovery_code": "リカバリーコードを入力してください",
"enter_two_factor_code": "2段階認証コードを入力してください", "enter_two_factor_code": "2段階認証コードを入力してください",
"recovery_code": "リカバリーコード", "recovery_code": "リカバリーコード",
"heading" : { "heading": {
"totp" : "2段階認証", "totp": "2段階認証",
"recovery" : "2段階リカバリー" "recovery": "2段階リカバリー"
} }
}, },
"media_modal": { "media_modal": {
@ -76,21 +88,29 @@
"dms": "ダイレクトメッセージ", "dms": "ダイレクトメッセージ",
"public_tl": "パブリックタイムライン", "public_tl": "パブリックタイムライン",
"timeline": "タイムライン", "timeline": "タイムライン",
"twkn": "接続しているすべてのネットワーク", "twkn": "すべてのネットワーク",
"user_search": "ユーザーを探す", "user_search": "ユーザーを探す",
"search": "検索", "search": "検索",
"who_to_follow": "おすすめユーザー", "who_to_follow": "おすすめユーザー",
"preferences": "設定" "preferences": "設定",
"administration": "管理",
"bookmarks": "ブックマーク",
"timelines": "タイムライン",
"chats": "チャット"
}, },
"notifications": { "notifications": {
"broken_favorite": "ステータスが見つかりません。探しています...", "broken_favorite": "ステータスが見つかりません。探しています",
"favorited_you": "あなたのステータスがお気に入りされました", "favorited_you": "あなたのステータスがお気に入りされました",
"followed_you": "フォローされました", "followed_you": "フォローされました",
"load_older": "古い通知をみる", "load_older": "古い通知をみる",
"notifications": "通知", "notifications": "通知",
"read": "読んだ!", "read": "読んだ!",
"repeated_you": "あなたのステータスがリピートされました", "repeated_you": "あなたのステータスがリピートされました",
"no_more_notifications": "通知はありません" "no_more_notifications": "通知はありません",
"reacted_with": "{0} でリアクションしました",
"migrated_to": "インスタンスを引っ越しました",
"follow_request": "あなたをフォローしたいです",
"error": "通知の取得に失敗しました: {0}"
}, },
"polls": { "polls": {
"add_poll": "投票を追加", "add_poll": "投票を追加",
@ -104,7 +124,9 @@
"expiry": "投票期間", "expiry": "投票期間",
"expires_in": "投票は {0} で終了します", "expires_in": "投票は {0} で終了します",
"expired": "投票は {0} 前に終了しました", "expired": "投票は {0} 前に終了しました",
"not_enough_options": "相異なる選択肢が不足しています" "not_enough_options": "相異なる選択肢が不足しています",
"votes_count": "{count} 票 | {count} 票",
"people_voted_count": "{count} 人投票 | {count} 人投票"
}, },
"emoji": { "emoji": {
"stickers": "ステッカー", "stickers": "ステッカー",
@ -113,7 +135,9 @@
"search_emoji": "絵文字を検索", "search_emoji": "絵文字を検索",
"add_emoji": "絵文字を挿入", "add_emoji": "絵文字を挿入",
"custom": "カスタム絵文字", "custom": "カスタム絵文字",
"unicode": "Unicode絵文字" "unicode": "Unicode絵文字",
"load_all": "全 {emojiAmount} 絵文字を読み込む",
"load_all_hint": "最初の {saneAmount} 絵文字を読み込みました、全て読み込むと重くなる可能性があります。"
}, },
"stickers": { "stickers": {
"add_sticker": "ステッカーを追加" "add_sticker": "ステッカーを追加"
@ -121,7 +145,8 @@
"interactions": { "interactions": {
"favs_repeats": "リピートとお気に入り", "favs_repeats": "リピートとお気に入り",
"follows": "新しいフォロワー", "follows": "新しいフォロワー",
"load_older": "古いインタラクションを見る" "load_older": "古いインタラクションを見る",
"moves": "ユーザーの引っ越し"
}, },
"post_status": { "post_status": {
"new_status": "投稿する", "new_status": "投稿する",
@ -142,15 +167,20 @@
"posting": "投稿", "posting": "投稿",
"scope_notice": { "scope_notice": {
"public": "この投稿は、誰でも見ることができます", "public": "この投稿は、誰でも見ることができます",
"private": "この投稿は、あなたのフォロワーだけが、見ることができます", "private": "この投稿は、あなたのフォロワーだけが、見ることができます",
"unlisted": "この投稿は、パブリックタイムラインと、接続しているすべてのネットワークには、表示されません" "unlisted": "この投稿は、パブリックタイムラインと、接続しているすべてのネットワークには、表示されません"
}, },
"scope": { "scope": {
"direct": "ダイレクト: メンションされたユーザーのみに届きます。", "direct": "ダイレクト: メンションされたユーザーのみに届きます",
"private": "フォロワーげんてい: フォロワーのみに届きます。", "private": "フォロワー限定: フォロワーのみに届きます",
"public": "パブリック: パブリックタイムラインに届きます。", "public": "パブリック: パブリックタイムラインに届きます",
"unlisted": "アンリステッド: パブリックタイムラインに届きません。" "unlisted": "アンリステッド: パブリックタイムラインに届きません"
} },
"media_description_error": "メディアのアップロードに失敗しました。もう一度お試しください",
"empty_status_error": "投稿内容を入力してください",
"preview_empty": "何もありません",
"preview": "プレビュー",
"media_description": "メディアの説明"
}, },
"registration": { "registration": {
"bio": "プロフィール", "bio": "プロフィール",
@ -171,7 +201,9 @@
"password_required": "必須", "password_required": "必須",
"password_confirmation_required": "必須", "password_confirmation_required": "必須",
"password_confirmation_match": "パスワードが違います" "password_confirmation_match": "パスワードが違います"
} },
"reason_placeholder": "このインスタンスは、新規登録を手動で受け付けています。\n登録したい理由を、インスタンスの管理者に教えてください。",
"reason": "登録するための目的"
}, },
"selectable_list": { "selectable_list": {
"select_all": "すべて選択" "select_all": "すべて選択"
@ -181,17 +213,17 @@
"security": "セキュリティ", "security": "セキュリティ",
"enter_current_password_to_confirm": "あなたのアイデンティティを証明するため、現在のパスワードを入力してください", "enter_current_password_to_confirm": "あなたのアイデンティティを証明するため、現在のパスワードを入力してください",
"mfa": { "mfa": {
"otp" : "OTP", "otp": "OTP",
"setup_otp" : "OTPのセットアップ", "setup_otp": "OTPのセットアップ",
"wait_pre_setup_otp" : "OTPのプリセット", "wait_pre_setup_otp": "OTPのプリセット",
"confirm_and_enable" : "OTPの確認と有効化", "confirm_and_enable": "OTPの確認と有効化",
"title": "2段階認証", "title": "2段階認証",
"generate_new_recovery_codes" : "新しいリカバリーコードを生成", "generate_new_recovery_codes": "新しいリカバリーコードを生成",
"warning_of_generate_new_codes" : "新しいリカバリーコードを生成すると、古いコードは使用できなくなります。", "warning_of_generate_new_codes": "新しいリカバリーコードを生成すると、古いコードは使用できなくなります。",
"recovery_codes" : "リカバリーコード。", "recovery_codes": "リカバリーコード。",
"waiting_a_recovery_codes": "バックアップコードを受信しています...", "waiting_a_recovery_codes": "バックアップコードを受信しています",
"recovery_codes_warning" : "コードを紙に書くか、安全な場所に保存してください。そうでなければ、あなたはコードを再び見ることはできません。もし2段階認証アプリのアクセスを喪失し、なおかつ、リカバリーコードもないならば、あなたは自分のアカウントから閉め出されます。", "recovery_codes_warning": "コードを紙に書くか、安全な場所に保存してください。そうでなければ、あなたはコードを再び見ることはできません。もし2段階認証アプリのアクセスを喪失し、なおかつ、リカバリーコードもないならば、あなたは自分のアカウントから閉め出されます。",
"authentication_methods" : "認証方法", "authentication_methods": "認証方法",
"scan": { "scan": {
"title": "スキャン", "title": "スキャン",
"desc": "あなたの2段階認証アプリを使って、このQRコードをスキャンするか、テキストキーを入力してください:", "desc": "あなたの2段階認証アプリを使って、このQRコードをスキャンするか、テキストキーを入力してください:",
@ -231,7 +263,7 @@
"data_import_export_tab": "インポートとエクスポート", "data_import_export_tab": "インポートとエクスポート",
"default_vis": "デフォルトの公開範囲", "default_vis": "デフォルトの公開範囲",
"delete_account": "アカウントを消す", "delete_account": "アカウントを消す",
"delete_account_description": "あなたのアカウントとメッセージが、消えます。", "delete_account_description": "あなたのデータが消えて、アカウントが使えなくなります。",
"delete_account_error": "アカウントを消すことが、できなかったかもしれません。インスタンスの管理者に、連絡してください。", "delete_account_error": "アカウントを消すことが、できなかったかもしれません。インスタンスの管理者に、連絡してください。",
"delete_account_instructions": "本当にアカウントを消してもいいなら、パスワードを入力してください。", "delete_account_instructions": "本当にアカウントを消してもいいなら、パスワードを入力してください。",
"discoverable": "検索などのサービスでこのアカウントを見つけることを許可する", "discoverable": "検索などのサービスでこのアカウントを見つけることを許可する",
@ -239,12 +271,12 @@
"pad_emoji": "ピッカーから絵文字を挿入するとき、絵文字の両側にスペースを入れる", "pad_emoji": "ピッカーから絵文字を挿入するとき、絵文字の両側にスペースを入れる",
"export_theme": "保存", "export_theme": "保存",
"filtering": "フィルタリング", "filtering": "フィルタリング",
"filtering_explanation": "これらの言葉を含むすべてのものがミュートされます。1行に1つの言葉を書いてください", "filtering_explanation": "これらの言葉を含むすべてのものがミュートされます。1行に1つの言葉を書いてください",
"follow_export": "フォローのエクスポート", "follow_export": "フォローのエクスポート",
"follow_export_button": "エクスポート", "follow_export_button": "エクスポート",
"follow_export_processing": "お待ちください。まもなくファイルをダウンロードできます。", "follow_export_processing": "お待ちください。まもなくファイルをダウンロードできます。",
"follow_import": "フォローのインポート", "follow_import": "フォローのインポート",
"follow_import_error": "フォローのインポートがエラーになりました", "follow_import_error": "フォローのインポートがエラーになりました",
"follows_imported": "フォローがインポートされました! 少し時間がかかるかもしれません。", "follows_imported": "フォローがインポートされました! 少し時間がかかるかもしれません。",
"foreground": "フォアグラウンド", "foreground": "フォアグラウンド",
"general": "全般", "general": "全般",
@ -305,7 +337,7 @@
"profile_background": "プロフィールのバックグラウンド", "profile_background": "プロフィールのバックグラウンド",
"profile_banner": "プロフィールバナー", "profile_banner": "プロフィールバナー",
"profile_tab": "プロフィール", "profile_tab": "プロフィール",
"radii_help": "インターフェースの丸さを設定する", "radii_help": "インターフェースの丸さを設定する",
"replies_in_timeline": "タイムラインのリプライ", "replies_in_timeline": "タイムラインのリプライ",
"reply_visibility_all": "すべてのリプライを見る", "reply_visibility_all": "すべてのリプライを見る",
"reply_visibility_following": "私に宛てられたリプライと、フォローしている人からのリプライを見る", "reply_visibility_following": "私に宛てられたリプライと、フォローしている人からのリプライを見る",
@ -332,7 +364,7 @@
"streaming": "上までスクロールしたとき、自動的にストリーミングする", "streaming": "上までスクロールしたとき、自動的にストリーミングする",
"text": "文字", "text": "文字",
"theme": "テーマ", "theme": "テーマ",
"theme_help": "カラーテーマをカスタマイズできます", "theme_help": "カラーテーマをカスタマイズできます",
"theme_help_v2_1": "チェックボックスをONにすると、コンポーネントごとに、色と透明度をオーバーライドできます。「すべてクリア」ボタンを押すと、すべてのオーバーライドをやめます。", "theme_help_v2_1": "チェックボックスをONにすると、コンポーネントごとに、色と透明度をオーバーライドできます。「すべてクリア」ボタンを押すと、すべてのオーバーライドをやめます。",
"theme_help_v2_2": "バックグラウンドとテキストのコントラストを表すアイコンがあります。マウスをホバーすると、詳しい説明が出ます。透明な色を使っているときは、最悪の場合のコントラストが示されます。", "theme_help_v2_2": "バックグラウンドとテキストのコントラストを表すアイコンがあります。マウスをホバーすると、詳しい説明が出ます。透明な色を使っているときは、最悪の場合のコントラストが示されます。",
"tooltipRadius": "ツールチップとアラート", "tooltipRadius": "ツールチップとアラート",
@ -356,7 +388,24 @@
"save_load_hint": "「残す」オプションをONにすると、テーマを選んだときとロードしたとき、現在の設定を残します。また、テーマをエクスポートするとき、これらのオプションを維持します。すべてのチェックボックスをOFFにすると、テーマをエクスポートしたとき、すべての設定を保存します。", "save_load_hint": "「残す」オプションをONにすると、テーマを選んだときとロードしたとき、現在の設定を残します。また、テーマをエクスポートするとき、これらのオプションを維持します。すべてのチェックボックスをOFFにすると、テーマをエクスポートしたとき、すべての設定を保存します。",
"reset": "リセット", "reset": "リセット",
"clear_all": "すべてクリア", "clear_all": "すべてクリア",
"clear_opacity": "透明度をクリア" "clear_opacity": "透明度をクリア",
"help": {
"snapshot_missing": "テーマのスナップショットがありません。思っていた見た目と違うかもしれません。",
"migration_snapshot_ok": "念のために、テーマのスナップショットが読み込まれました。テーマのデータを読み込むことができます。",
"fe_downgraded": "フロントエンドが前のバージョンに戻りました。",
"fe_upgraded": "フロントエンドと一緒に、テーマエンジンが新しくなりました。",
"older_version_imported": "古いフロントエンドで作られたファイルをインポートしました。",
"future_version_imported": "新しいフロントエンドで作られたファイルをインポートしました。",
"v2_imported": "古いフロントエンドのためのファイルをインポートしました。設定した通りにならないかもしれません。",
"upgraded_from_v2": "フロントエンドが新しくなったので、今までの見た目と少し違うかもしれません。",
"snapshot_source_mismatch": "フロントエンドがロールバックと更新を繰り返したため、バージョンが競合しています。",
"migration_napshot_gone": "スナップショットがありません、覚えているものと見た目が違うかもしれません。",
"snapshot_present": "テーマのスナップショットが読み込まれました。設定は上書きされました。代わりとして実データを読み込むことができます。"
},
"use_source": "新しいバージョン",
"use_snapshot": "古いバージョン",
"load_theme": "テーマの読み込み",
"keep_as_is": "変更しない"
}, },
"common": { "common": {
"color": "色", "color": "色",
@ -364,9 +413,9 @@
"contrast": { "contrast": {
"hint": "コントラストは {ratio} です。{level}。({context})", "hint": "コントラストは {ratio} です。{level}。({context})",
"level": { "level": {
"aa": "AAレベルガイドライン (ミニマル) を満たします", "aa": "AAレベルガイドライン (最低限) を満たします",
"aaa": "AAAレベルガイドライン (レコメンデッド) を満たします。", "aaa": "AAAレベルガイドライン (推奨) を満たします",
"bad": "ガイドラインを満たしません" "bad": "ガイドラインを満たしません"
}, },
"context": { "context": {
"18pt": "大きい (18ポイント以上) テキスト", "18pt": "大きい (18ポイント以上) テキスト",
@ -391,7 +440,27 @@
"borders": "境界", "borders": "境界",
"buttons": "ボタン", "buttons": "ボタン",
"inputs": "インプットフィールド", "inputs": "インプットフィールド",
"faint_text": "薄いテキスト" "faint_text": "薄いテキスト",
"alert_neutral": "それ以外",
"chat": {
"border": "境界線",
"outgoing": "送信",
"incoming": "受信"
},
"tabs": "タブ",
"toggled": "切り替えたとき",
"disabled": "無効なとき",
"selectedMenu": "選択されたメニューアイテム",
"selectedPost": "選択された投稿",
"pressed": "押したとき",
"highlight": "強調された要素",
"icons": "アイコン",
"poll": "投票グラフ",
"wallpaper": "壁紙",
"underlay": "アンダーレイ",
"popover": "ツールチップ、メニュー、ポップオーバー",
"post": "投稿/プロフィール",
"alert_warning": "警告"
}, },
"radii": { "radii": {
"_tab_label": "丸さ" "_tab_label": "丸さ"
@ -409,8 +478,8 @@
"always_drop_shadow": "ブラウザーがサポートしていれば、常に {0} が使われます。", "always_drop_shadow": "ブラウザーがサポートしていれば、常に {0} が使われます。",
"drop_shadow_syntax": "{0} は、{1} パラメーターと {2} キーワードをサポートしていません。", "drop_shadow_syntax": "{0} は、{1} パラメーターと {2} キーワードをサポートしていません。",
"avatar_inset": "内側の影と外側の影を同時に使うと、透明なアバターの表示が乱れます。", "avatar_inset": "内側の影と外側の影を同時に使うと、透明なアバターの表示が乱れます。",
"spread_zero": "広がりが 0 よりも大きな影は、0 と同じです", "spread_zero": "広がりが 0 よりも大きな影は、0 と同じです",
"inset_classic": "内側の影は {0} を使います" "inset_classic": "内側の影は {0} を使います"
}, },
"components": { "components": {
"panel": "パネル", "panel": "パネル",
@ -424,7 +493,8 @@
"buttonPressed": "ボタン (押されているとき)", "buttonPressed": "ボタン (押されているとき)",
"buttonPressedHover": "ボタン (ホバー、かつ、押されているとき)", "buttonPressedHover": "ボタン (ホバー、かつ、押されているとき)",
"input": "インプットフィールド" "input": "インプットフィールド"
} },
"hintV3": "影の場合は、 {0} 表記を使って他の色スロットを使うこともできます。"
}, },
"fonts": { "fonts": {
"_tab_label": "フォント", "_tab_label": "フォント",
@ -445,7 +515,7 @@
"content": "本文", "content": "本文",
"error": "エラーの例", "error": "エラーの例",
"button": "ボタン", "button": "ボタン",
"text": "これは{0}と{1}の例です", "text": "これは{0}と{1}の例です",
"mono": "monospace", "mono": "monospace",
"input": "羽田空港に着きました。", "input": "羽田空港に着きました。",
"faint_link": "とても助けになるマニュアル", "faint_link": "とても助けになるマニュアル",
@ -459,7 +529,52 @@
"title": "バージョン", "title": "バージョン",
"backend_version": "バックエンドのバージョン", "backend_version": "バックエンドのバージョン",
"frontend_version": "フロントエンドのバージョン" "frontend_version": "フロントエンドのバージョン"
} },
"notification_setting_hide_notification_contents": "送った人と内容を、プッシュ通知に表示しない",
"notification_setting_privacy": "プライバシー",
"notification_setting_block_from_strangers": "フォローしていないユーザーからの通知を拒否する",
"notification_setting_filters": "フィルター",
"fun": "お楽しみ",
"virtual_scrolling": "タイムラインの描画を最適化する",
"type_domains_to_mute": "ミュートしたいドメインを検索",
"useStreamingApiWarning": "(実験中で、投稿を取りこぼすかもしれないので、おすすめしません)",
"useStreamingApi": "投稿と通知を、すぐに受け取る",
"user_mutes": "ユーザー",
"reset_background_confirm": "本当にバックグラウンドを初期化しますか?",
"reset_banner_confirm": "本当にバナーを初期化しますか?",
"reset_avatar_confirm": "本当にアバターを初期化しますか?",
"hide_wallpaper": "インスタンスのバックグラウンドを隠す",
"reset_profile_background": "プロフィールのバックグラウンドを初期化",
"reset_profile_banner": "プロフィールのバナーを初期化",
"reset_avatar": "アバターを初期化",
"notification_visibility_emoji_reactions": "リアクション",
"notification_visibility_moves": "ユーザーの引っ越し",
"new_email": "新しいメールアドレス",
"profile_fields": {
"value": "内容",
"name": "ラベル",
"add_field": "枠を追加",
"label": "プロフィール補足情報"
},
"accent": "アクセント",
"mutes_imported": "ミュートをインポートしました!少し時間がかかるかもしれません。",
"emoji_reactions_on_timeline": "絵文字リアクションをタイムラインに表示",
"domain_mutes": "ドメイン",
"mutes_and_blocks": "ミュートとブロック",
"chatMessageRadius": "チャットメッセージ",
"change_email_error": "メールアドレスを変えることが、できなかったかもしれません。",
"changed_email": "メールアドレスが、変わりました!",
"change_email": "メールアドレスを変える",
"bot": "これは bot アカウントです",
"mute_export_button": "ミュートをCSVファイルにエクスポートする",
"import_mutes_from_a_csv_file": "CSVファイルからミュートをインポートする",
"mute_import_error": "ミュートのインポートに失敗しました",
"mute_import": "ミュートのインポート",
"mute_export": "ミュートのエクスポート",
"allow_following_move": "フォロー中のアカウントが引っ越したとき、自動フォローを許可する",
"setting_changed": "規定の設定と異なっています",
"greentext": "引用を緑色で表示",
"sensitive_by_default": "はじめから投稿をセンシティブとして設定"
}, },
"time": { "time": {
"day": "{0}日", "day": "{0}日",
@ -505,7 +620,9 @@
"show_new": "読み込み", "show_new": "読み込み",
"up_to_date": "最新", "up_to_date": "最新",
"no_more_statuses": "これで終わりです", "no_more_statuses": "これで終わりです",
"no_statuses": "ステータスはありません" "no_statuses": "ステータスはありません",
"reload": "再読み込み",
"error": "タイムラインの読み込みに失敗しました: {0}"
}, },
"status": { "status": {
"favorites": "お気に入り", "favorites": "お気に入り",
@ -518,7 +635,21 @@
"reply_to": "返信", "reply_to": "返信",
"replies_list": "返信:", "replies_list": "返信:",
"mute_conversation": "スレッドをミュート", "mute_conversation": "スレッドをミュート",
"unmute_conversation": "スレッドのミュートを解除" "unmute_conversation": "スレッドのミュートを解除",
"nsfw": "閲覧注意",
"expand": "広げる",
"status_deleted": "この投稿は削除されました",
"hide_content": "隠す",
"show_content": "見る",
"hide_full_subject": "隠す",
"show_full_subject": "全部見る",
"thread_muted_and_words": "以下の単語を含むため:",
"thread_muted": "ミュートされたスレッド",
"external_source": "外部ソース",
"copy_link": "リンクをコピー",
"status_unavailable": "利用できません",
"unbookmark": "ブックマーク解除",
"bookmark": "ブックマーク"
}, },
"user_card": { "user_card": {
"approve": "受け入れ", "approve": "受け入れ",
@ -539,7 +670,7 @@
"media": "メディア", "media": "メディア",
"mention": "メンション", "mention": "メンション",
"mute": "ミュート", "mute": "ミュート",
"muted": "ミュートしています", "muted": "ミュートしています",
"per_day": "/日", "per_day": "/日",
"remote_follow": "リモートフォロー", "remote_follow": "リモートフォロー",
"report": "通報", "report": "通報",
@ -547,11 +678,11 @@
"subscribe": "購読", "subscribe": "購読",
"unsubscribe": "購読を解除", "unsubscribe": "購読を解除",
"unblock": "ブロック解除", "unblock": "ブロック解除",
"unblock_progress": "ブロックを解除しています...", "unblock_progress": "ブロックを解除しています",
"block_progress": "ブロックしています...", "block_progress": "ブロックしています",
"unmute": "ミュート解除", "unmute": "ミュート解除",
"unmute_progress": "ミュートを解除しています...", "unmute_progress": "ミュートを解除しています",
"mute_progress": "ミュートしています...", "mute_progress": "ミュートしています",
"admin_menu": { "admin_menu": {
"moderation": "モデレーション", "moderation": "モデレーション",
"grant_admin": "管理者権限を付与", "grant_admin": "管理者権限を付与",
@ -570,7 +701,16 @@
"quarantine": "他のインスタンスからの投稿を止める", "quarantine": "他のインスタンスからの投稿を止める",
"delete_user": "ユーザーを削除", "delete_user": "ユーザーを削除",
"delete_user_confirmation": "あなたの精神状態に何か問題はございませんか? この操作を取り消すことはできません。" "delete_user_confirmation": "あなたの精神状態に何か問題はございませんか? この操作を取り消すことはできません。"
} },
"roles": {
"moderator": "モデレーター",
"admin": "管理者"
},
"show_repeats": "リピートを見る",
"hide_repeats": "リピートを隠す",
"message": "メッセージ",
"hidden": "隠す",
"bot": "bot"
}, },
"user_profile": { "user_profile": {
"timeline_title": "ユーザータイムライン", "timeline_title": "ユーザータイムライン",
@ -595,13 +735,18 @@
"repeat": "リピート", "repeat": "リピート",
"reply": "返信", "reply": "返信",
"favorite": "お気に入り", "favorite": "お気に入り",
"user_settings": "ユーザー設定" "user_settings": "ユーザー設定",
"bookmark": "ブックマーク",
"reject_follow_request": "フォローリクエストを拒否",
"accept_follow_request": "フォローリクエストを許可",
"add_reaction": "リアクションを追加"
}, },
"upload":{ "upload": {
"error": { "error": {
"base": "アップロードに失敗しました。", "base": "アップロードに失敗しました。",
"file_too_big": "ファイルが大きすぎます [{filesize} {filesizeunit} / {allowedsize} {allowedsizeunit}]", "file_too_big": "ファイルが大きすぎます [{filesize} {filesizeunit} / {allowedsize} {allowedsizeunit}]",
"default": "しばらくしてから試してください" "default": "しばらくしてから試してください",
"message": "アップロードに失敗: {0}"
}, },
"file_size_units": { "file_size_units": {
"B": "B", "B": "B",
@ -626,6 +771,77 @@
"check_email": "パスワードをリセットするためのリンクが記載されたメールが届いているか確認してください。", "check_email": "パスワードをリセットするためのリンクが記載されたメールが届いているか確認してください。",
"return_home": "ホームページに戻る", "return_home": "ホームページに戻る",
"too_many_requests": "試行回数の制限に達しました。しばらく時間を置いてから再試行してください。", "too_many_requests": "試行回数の制限に達しました。しばらく時間を置いてから再試行してください。",
"password_reset_disabled": "このインスタンスではパスワードリセットは無効になっています。インスタンスの管理者に連絡してください。" "password_reset_disabled": "このインスタンスではパスワードリセットは無効になっています。インスタンスの管理者に連絡してください。",
"password_reset_required_but_mailer_is_disabled": "パスワードの初期化が必要ですが、初期化は使えません。インスタンスの管理者に連絡してください。",
"password_reset_required": "ログインするためにパスワードを初期化してください。"
},
"about": {
"mrf": {
"mrf_policies_desc": "MRFポリシーは、インスタンスの振る舞いを操作します。以下のポリシーが有効になっています:",
"federation": "連合",
"simple": {
"media_nsfw_desc": "このインスタンスでは、以下のインスタンスからの投稿に対して、メディアを閲覧注意に設定します:",
"media_nsfw": "メディアを閲覧注意に設定",
"media_removal_desc": "このインスタンスでは、以下のインスタンスからの投稿に対して、メディアを除去します:",
"media_removal": "メディア除去",
"ftl_removal": "「接続しているすべてのネットワーク」タイムラインから除外",
"ftl_removal_desc": "このインスタンスでは、以下のインスタンスを「接続しているすべてのネットワーク」タイムラインから除外します:",
"quarantine_desc": "このインスタンスでは、以下のインスタンスに対して公開投稿のみを送信します:",
"quarantine": "検疫",
"reject_desc": "このインスタンスでは、以下のインスタンスからのメッセージを受け付けません:",
"accept_desc": "このインスタンスでは、以下のインスタンスからのメッセージのみを受け付けます:",
"accept": "許可",
"simple_policies": "インスタンス固有のポリシー",
"reject": "拒否"
},
"mrf_policies": "有効なMRFポリシー",
"keyword": {
"replace": "置き換え",
"ftl_removal": "「接続しているすべてのネットワーク」タイムラインから除外",
"keyword_policies": "キーワードポリシー",
"is_replaced_by": "→",
"reject": "拒否"
}
},
"staff": "スタッフ"
},
"display_date": {
"today": "今日"
},
"file_type": {
"file": "ファイル",
"image": "画像",
"video": "ビデオ",
"audio": "オーディオ"
},
"remote_user_resolver": {
"error": "見つかりませんでした。",
"searching_for": "検索中",
"remote_user_resolver": "リモートユーザーリゾルバ"
},
"errors": {
"storage_unavailable": "ブラウザのストレージに接続できなかったため、ログインや設定情報は保存されません。Cookieを有効にしてください。"
},
"shoutbox": {
"title": "Shoutbox"
},
"chats": {
"empty_chat_list_placeholder": "チャットはありません。新規チャットのボタンを押して始めましょう!",
"error_sending_message": "メッセージの送信に失敗しました。",
"error_loading_chat": "チャットの読み込みに失敗しました。",
"delete_confirm": "このメッセージを本当に消してもいいですか?",
"more": "もっと見る",
"empty_message_error": "メッセージを入力して下さい",
"new": "新規チャット",
"chats": "チャット一覧",
"delete": "削除",
"message_user": "{nickname} にメッセージ",
"you": "あなた:"
},
"domain_mute_card": {
"unmute_progress": "ミュート解除中…",
"unmute": "ミュート解除",
"mute_progress": "ミュート中…",
"mute": "ミュート"
} }
} }

View file

@ -9,7 +9,9 @@
"scope_options": "범위 옵션", "scope_options": "범위 옵션",
"text_limit": "텍스트 제한", "text_limit": "텍스트 제한",
"title": "기능", "title": "기능",
"who_to_follow": "팔로우 추천" "who_to_follow": "팔로우 추천",
"upload_limit": "최대 파일용량",
"pleroma_chat_messages": "Pleroma 채트"
}, },
"finder": { "finder": {
"error_fetching_user": "사용자 정보 불러오기 실패", "error_fetching_user": "사용자 정보 불러오기 실패",
@ -17,7 +19,27 @@
}, },
"general": { "general": {
"apply": "적용", "apply": "적용",
"submit": "보내기" "submit": "보내기",
"loading": "로딩중…",
"peek": "숨기기",
"close": "닫기",
"verify": "검사",
"confirm": "확인",
"enable": "유효",
"disable": "무효",
"cancel": "취소",
"dismiss": "무시",
"show_less": "접기",
"show_more": "더 보기",
"optional": "필수 아님",
"retry": "다시 시도하십시오",
"error_retry": "다시 시도하십시오",
"generic_error": "잘못되었습니다",
"more": "더 보기",
"role": {
"moderator": "중재자",
"admin": "관리자"
}
}, },
"login": { "login": {
"login": "로그인", "login": "로그인",
@ -26,10 +48,19 @@
"password": "암호", "password": "암호",
"placeholder": "예시: lain", "placeholder": "예시: lain",
"register": "가입", "register": "가입",
"username": "사용자 이름" "username": "사용자 이름",
"heading": {
"recovery": "2단계 복구",
"totp": "2단계인증"
},
"recovery_code": "복구 코드",
"enter_two_factor_code": "2단계인증 코드를 입력하십시오",
"enter_recovery_code": "복구 코드를 입력하십시오",
"authentication_code": "인증 코드",
"hint": "로그인하여 대화에 참가합시다"
}, },
"nav": { "nav": {
"about": "About", "about": "인스턴스 소개",
"back": "뒤로", "back": "뒤로",
"chat": "로컬 챗", "chat": "로컬 챗",
"friend_requests": "팔로우 요청", "friend_requests": "팔로우 요청",
@ -37,18 +68,29 @@
"dms": "다이렉트 메시지", "dms": "다이렉트 메시지",
"public_tl": "공개 타임라인", "public_tl": "공개 타임라인",
"timeline": "타임라인", "timeline": "타임라인",
"twkn": "모든 알려진 네트워크", "twkn": "알려진 네트워크",
"user_search": "사용자 검색", "user_search": "사용자 검색",
"preferences": "환경설정" "preferences": "환경설정",
"chats": "채트",
"timelines": "타임라인",
"who_to_follow": "추천된 사용자",
"search": "검색",
"bookmarks": "북마크",
"interactions": "대화",
"administration": "관리"
}, },
"notifications": { "notifications": {
"broken_favorite": "알 수 없는 게시물입니다, 검색 합니다...", "broken_favorite": "알 수 없는 게시물입니다, 검색합니다…",
"favorited_you": "당신의 게시물을 즐겨찾기", "favorited_you": "당신의 게시물을 즐겨찾기",
"followed_you": "당신을 팔로우", "followed_you": "당신을 팔로우",
"load_older": "오래 된 알림 불러오기", "load_older": "오래 된 알림 불러오기",
"notifications": "알림", "notifications": "알림",
"read": "읽음!", "read": "읽음!",
"repeated_you": "당신의 게시물을 리핏" "repeated_you": "당신의 게시물을 리핏",
"no_more_notifications": "알림이 없습니다",
"migrated_to": "이사했습니다",
"reacted_with": "{0} 로 반응했습니다",
"error": "알림 불러오기 실패: {0}"
}, },
"post_status": { "post_status": {
"new_status": "새 게시물 게시", "new_status": "새 게시물 게시",
@ -56,10 +98,13 @@
"account_not_locked_warning_link": "잠김", "account_not_locked_warning_link": "잠김",
"attachments_sensitive": "첨부물을 민감함으로 설정", "attachments_sensitive": "첨부물을 민감함으로 설정",
"content_type": { "content_type": {
"text/plain": "평문" "text/plain": "평문",
"text/bbcode": "BBCode",
"text/markdown": "Markdown",
"text/html": "HTML"
}, },
"content_warning": "주제 (필수 아님)", "content_warning": "주제 (필수 아님)",
"default": "LA에 도착!", "default": "인천공항에 도착했습니다.",
"direct_warning": "이 게시물을 멘션 된 사용자들에게만 보여집니다", "direct_warning": "이 게시물을 멘션 된 사용자들에게만 보여집니다",
"posting": "게시", "posting": "게시",
"scope": { "scope": {
@ -67,7 +112,15 @@
"private": "팔로워 전용 - 팔로워들에게만", "private": "팔로워 전용 - 팔로워들에게만",
"public": "공개 - 공개 타임라인으로", "public": "공개 - 공개 타임라인으로",
"unlisted": "비공개 - 공개 타임라인에 게시 안 함" "unlisted": "비공개 - 공개 타임라인에 게시 안 함"
} },
"preview_empty": "아무것도 없습니다",
"preview": "미리보기",
"scope_notice": {
"public": "이 글은 누구나 볼 수 있습니다"
},
"media_description_error": "파일을 올리지 못하였습니다. 다시한번 시도하여 주십시오",
"empty_status_error": "글을 입력하십시오",
"media_description": "첨부파일 설명"
}, },
"registration": { "registration": {
"bio": "소개", "bio": "소개",
@ -85,7 +138,9 @@
"password_required": "공백으로 둘 수 없습니다", "password_required": "공백으로 둘 수 없습니다",
"password_confirmation_required": "공백으로 둘 수 없습니다", "password_confirmation_required": "공백으로 둘 수 없습니다",
"password_confirmation_match": "패스워드와 일치해야 합니다" "password_confirmation_match": "패스워드와 일치해야 합니다"
} },
"fullname_placeholder": "예: 김례인",
"username_placeholder": "예: lain"
}, },
"settings": { "settings": {
"attachmentRadius": "첨부물", "attachmentRadius": "첨부물",
@ -112,7 +167,7 @@
"data_import_export_tab": "데이터 불러오기 / 내보내기", "data_import_export_tab": "데이터 불러오기 / 내보내기",
"default_vis": "기본 공개 범위", "default_vis": "기본 공개 범위",
"delete_account": "계정 삭제", "delete_account": "계정 삭제",
"delete_account_description": "계정과 메시지를 영구히 삭제.", "delete_account_description": "데이터가 영구히 삭제되고 계정이 불활성화됩니다.",
"delete_account_error": "계정을 삭제하는데 문제가 있습니다. 계속 발생한다면 인스턴스 관리자에게 문의하세요.", "delete_account_error": "계정을 삭제하는데 문제가 있습니다. 계속 발생한다면 인스턴스 관리자에게 문의하세요.",
"delete_account_instructions": "계정 삭제를 확인하기 위해 아래에 패스워드 입력.", "delete_account_instructions": "계정 삭제를 확인하기 위해 아래에 패스워드 입력.",
"export_theme": "프리셋 저장", "export_theme": "프리셋 저장",
@ -156,7 +211,7 @@
"notification_visibility_repeats": "반복", "notification_visibility_repeats": "반복",
"no_rich_text_description": "모든 게시물의 서식을 지우기", "no_rich_text_description": "모든 게시물의 서식을 지우기",
"hide_follows_description": "내가 팔로우하는 사람을 표시하지 않음", "hide_follows_description": "내가 팔로우하는 사람을 표시하지 않음",
"hide_followers_description": "나를 따르는 사람을 보여주지 마라.", "hide_followers_description": "나를 따르는 사람을 숨기기",
"nsfw_clickthrough": "NSFW 이미지 \"클릭해서 보이기\"를 활성화", "nsfw_clickthrough": "NSFW 이미지 \"클릭해서 보이기\"를 활성화",
"oauth_tokens": "OAuth 토큰", "oauth_tokens": "OAuth 토큰",
"token": "토큰", "token": "토큰",
@ -247,7 +302,16 @@
"borders": "테두리", "borders": "테두리",
"buttons": "버튼", "buttons": "버튼",
"inputs": "입력칸", "inputs": "입력칸",
"faint_text": "흐려진 텍스트" "faint_text": "흐려진 텍스트",
"chat": {
"border": "경계선",
"outgoing": "송신",
"incoming": "수신"
},
"selectedMenu": "선택된 메뉴 요소",
"selectedPost": "선택된 글",
"icons": "아이콘",
"alert_warning": "경고"
}, },
"radii": { "radii": {
"_tab_label": "둥글기" "_tab_label": "둥글기"
@ -303,14 +367,45 @@
"button": "버튼", "button": "버튼",
"text": "더 많은 {0} 그리고 {1}", "text": "더 많은 {0} 그리고 {1}",
"mono": "내용", "mono": "내용",
"input": "LA에 막 도착!", "input": "인천공항에 도착했습니다.",
"faint_link": "도움 되는 설명서", "faint_link": "도움 되는 설명서",
"fine_print": "우리의 {0} 를 읽고 도움 되지 않는 것들을 배우자!", "fine_print": "우리의 {0} 를 읽고 도움 되지 않는 것들을 배우자!",
"header_faint": "이건 괜찮아", "header_faint": "이건 괜찮아",
"checkbox": "나는 약관을 대충 훑어보았습니다", "checkbox": "나는 약관을 대충 훑어보았습니다",
"link": "작고 귀여운 링크" "link": "작고 귀여운 링크"
} }
} },
"block_export": "차단 목록 내보내기",
"mfa": {
"scan": {
"secret_code": "키",
"title": "스캔"
},
"authentication_methods": "인증 방법",
"waiting_a_recovery_codes": "예비 코드를 수신하고 있습니다…",
"recovery_codes": "복구 코드.",
"generate_new_recovery_codes": "새로운 복구 코드를 작성",
"title": "2단계인증",
"confirm_and_enable": "OTP 확인과 활성화",
"setup_otp": "OTP 설치",
"otp": "OTP"
},
"security": "보안",
"emoji_reactions_on_timeline": "이모지 반응을 타임라인으로 표시",
"avatar_size_instruction": "크기를 150x150 이상으로 설정할 것을 추장합니다.",
"blocks_tab": "차단",
"notification_setting_privacy": "보안",
"user_mutes": "사용자",
"notification_visibility_emoji_reactions": "반응",
"profile_fields": {
"value": "내용"
},
"mutes_and_blocks": "침묵과 차단",
"chatMessageRadius": "챗 메시지",
"change_email": "전자메일 주소 바꾸기",
"changed_email": "메일주소가 갱신되었습니다!",
"bot": "이 계정은 bot입니다",
"mutes_tab": "침묵"
}, },
"timeline": { "timeline": {
"collapse": "접기", "collapse": "접기",
@ -339,7 +434,7 @@
"its_you": "당신입니다!", "its_you": "당신입니다!",
"mute": "침묵", "mute": "침묵",
"muted": "침묵 됨", "muted": "침묵 됨",
"per_day": " / 하루", "per_day": "/ 하루",
"remote_follow": "원격 팔로우", "remote_follow": "원격 팔로우",
"statuses": "게시물" "statuses": "게시물"
}, },
@ -357,11 +452,11 @@
"favorite": "즐겨찾기", "favorite": "즐겨찾기",
"user_settings": "사용자 설정" "user_settings": "사용자 설정"
}, },
"upload":{ "upload": {
"error": { "error": {
"base": "업로드 실패.", "base": "업로드 실패.",
"file_too_big": "파일이 너무 커요 [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "file_too_big": "파일이 너무 커요 [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
"default": "잠시 후에 다시 시도해 보세요" "default": "잠시 후에 다시 시도해 보세요"
}, },
"file_size_units": { "file_size_units": {
"B": "바이트", "B": "바이트",
@ -370,5 +465,122 @@
"GiB": "기비바이트", "GiB": "기비바이트",
"TiB": "테비바이트" "TiB": "테비바이트"
} }
},
"interactions": {
"follows": "새 팔로워",
"favs_repeats": "반복과 즐겨찾기"
},
"emoji": {
"load_all": "전체 {emojiAmount} 이모지 불러오기",
"unicode": "Unicode 이모지",
"custom": "전용 이모지",
"add_emoji": "이모지 넣기",
"search_emoji": "이모지 검색",
"emoji": "이모지",
"stickers": "스티커"
},
"polls": {
"add_poll": "투표를 추가",
"votes": "표",
"vote": "투표",
"type": "투표 형식",
"expiry": "투표 기간",
"votes_count": "{count} 표 | {count} 표",
"people_voted_count": "{count} 명 투표 | {count} 명 투표",
"option": "선택지",
"add_option": "선택지 추가"
},
"media_modal": {
"next": "다음",
"previous": "이전"
},
"importer": {
"error": "이 파일을 가져올 때 오류가 발생하였습니다.",
"success": "정상히 불러왔습니다.",
"submit": "보내기"
},
"image_cropper": {
"cancel": "취소",
"save_without_cropping": "그대로 저장",
"save": "저장",
"crop_picture": "사진 자르기"
},
"exporter": {
"processing": "처리중입니다, 처리가 끝나면 파일을 다운로드하라는 지시가 있겠습니다",
"export": "내보내기"
},
"domain_mute_card": {
"unmute_progress": "침묵을 해제중…",
"unmute": "침묵 해제",
"mute_progress": "침묵으로 설정중…",
"mute": "침묵"
},
"about": {
"staff": "운영자",
"mrf": {
"simple": {
"media_nsfw_desc": "이 인스턴스에서는 아래의 인스턴스로부터 보내온 투고에 붙혀 있는 매체는 민감함으로 설정됩니다:",
"media_nsfw": "매체를 민감함으로 설정",
"media_removal_desc": "이 인스턴스에서는 아래의 인스턴스로부터 보내온 투고에 붙혀 있는 매체는 제거됩니다:",
"media_removal": "매체 제거",
"ftl_removal_desc": "이 인스턴스에서 아래의 인스턴스들은 \"알려진 모든 네트워크\" 타임라인에서 제외됩니다:",
"ftl_removal": "\"알려진 모든 네트워크\" 타임라인에서 제외",
"quarantine_desc": "이 인스턴스는 아래의 인스턴스에게 공개투고만을 보냅니다:",
"quarantine": "검역",
"reject_desc": "이 인스턴스에서는 아래의 인스턴스로부터 보내온 투고를 받아들이지 않습니다:",
"accept_desc": "이 인스턴스에서는 아래의 인스턴스로부터 보내온 투고만이 접수됩니다:",
"reject": "거부",
"accept": "허가",
"simple_policies": "인스턴스 특유의 폴리시"
},
"mrf_policies": "사용되는 MRF 폴리시",
"keyword": {
"is_replaced_by": "→",
"replace": "바꾸기",
"reject": "거부",
"ftl_removal": "\"알려진 모든 네트워크\" 타임라인에서 제외",
"keyword_policies": "단어 폴리시"
},
"federation": "연합"
}
},
"shoutbox": {
"title": "Shoutbox"
},
"time": {
"years_short": "{0} 년",
"year_short": "{0} 년",
"years": "{0} 년",
"year": "{0} 년",
"weeks_short": "{0} 주일",
"week_short": "{0} 주일",
"weeks": "{0} 주일",
"week": "{0} 주일",
"seconds_short": "{0} 초",
"second_short": "{0} 초",
"seconds": "{0} 초",
"second": "{0} 초",
"now_short": "방금",
"now": "방끔",
"months_short": "{0} 달 전",
"month_short": "{0} 달 전",
"months": "{0} 달 전",
"month": "{0} 달 전",
"minutes_short": "{0} 분",
"minute_short": "{0} 분",
"minutes": "{0} 분",
"minute": "{0} 분",
"in_past": "{0} 전",
"hours_short": "{0} 시간",
"hour_short": "{0} 시간",
"hours": "{0} 시간",
"hour": "{0} 시간",
"days_short": "{0} 일",
"day_short": "{0} 일",
"days": "{0} 일",
"day": "{0} 일"
},
"remote_user_resolver": {
"error": "찾을 수 없습니다."
} }
} }

View file

@ -57,9 +57,9 @@
"enter_recovery_code": "Skriv inn en gjenopprettingskode", "enter_recovery_code": "Skriv inn en gjenopprettingskode",
"enter_two_factor_code": "Skriv inn en to-faktors kode", "enter_two_factor_code": "Skriv inn en to-faktors kode",
"recovery_code": "Gjenopprettingskode", "recovery_code": "Gjenopprettingskode",
"heading" : { "heading": {
"totp" : "To-faktors autentisering", "totp": "To-faktors autentisering",
"recovery" : "To-faktors gjenoppretting" "recovery": "To-faktors gjenoppretting"
} }
}, },
"media_modal": { "media_modal": {
@ -72,7 +72,7 @@
"chat": "Lokal nettprat", "chat": "Lokal nettprat",
"friend_requests": "Følgeforespørsler", "friend_requests": "Følgeforespørsler",
"mentions": "Nevnt", "mentions": "Nevnt",
"interactions": "Interaksjooner", "interactions": "Interaksjoner",
"dms": "Direktemeldinger", "dms": "Direktemeldinger",
"public_tl": "Offentlig Tidslinje", "public_tl": "Offentlig Tidslinje",
"timeline": "Tidslinje", "timeline": "Tidslinje",
@ -80,7 +80,9 @@
"user_search": "Søk etter brukere", "user_search": "Søk etter brukere",
"search": "Søk", "search": "Søk",
"who_to_follow": "Kontoer å følge", "who_to_follow": "Kontoer å følge",
"preferences": "Innstillinger" "preferences": "Innstillinger",
"timelines": "Tidslinjer",
"bookmarks": "Bokmerker"
}, },
"notifications": { "notifications": {
"broken_favorite": "Ukjent status, leter etter den...", "broken_favorite": "Ukjent status, leter etter den...",
@ -90,7 +92,8 @@
"notifications": "Varslinger", "notifications": "Varslinger",
"read": "Les!", "read": "Les!",
"repeated_you": "Gjentok din status", "repeated_you": "Gjentok din status",
"no_more_notifications": "Ingen gjenstående varsler" "no_more_notifications": "Ingen gjenstående varsler",
"follow_request": "ønsker å følge deg"
}, },
"polls": { "polls": {
"add_poll": "Legg til undersøkelse", "add_poll": "Legg til undersøkelse",
@ -134,7 +137,7 @@
"public": "Denne statusen vil være synlig for alle", "public": "Denne statusen vil være synlig for alle",
"private": "Denne statusen vil være synlig for dine følgere", "private": "Denne statusen vil være synlig for dine følgere",
"unlisted": "Denne statusen vil ikke være synlig i Offentlig Tidslinje eller Det Hele Kjente Nettverket" "unlisted": "Denne statusen vil ikke være synlig i Offentlig Tidslinje eller Det Hele Kjente Nettverket"
}, },
"scope": { "scope": {
"direct": "Direkte, publiser bare til nevnte brukere", "direct": "Direkte, publiser bare til nevnte brukere",
"private": "Bare følgere, publiser bare til brukere som følger deg", "private": "Bare følgere, publiser bare til brukere som følger deg",
@ -171,17 +174,17 @@
"security": "Sikkerhet", "security": "Sikkerhet",
"enter_current_password_to_confirm": "Skriv inn ditt nåverende passord for å bekrefte din identitet", "enter_current_password_to_confirm": "Skriv inn ditt nåverende passord for å bekrefte din identitet",
"mfa": { "mfa": {
"otp" : "OTP", "otp": "OTP",
"setup_otp" : "Set opp OTP", "setup_otp": "Set opp OTP",
"wait_pre_setup_otp" : "forhåndsstiller OTP", "wait_pre_setup_otp": "forhåndsstiller OTP",
"confirm_and_enable" : "Bekreft og slå på OTP", "confirm_and_enable": "Bekreft og slå på OTP",
"title": "To-faktors autentisering", "title": "To-faktors autentisering",
"generate_new_recovery_codes" : "Generer nye gjenopprettingskoder", "generate_new_recovery_codes": "Generer nye gjenopprettingskoder",
"warning_of_generate_new_codes" : "Når du genererer nye gjenopprettingskoder, vil de gamle slutte å fungere.", "warning_of_generate_new_codes": "Når du genererer nye gjenopprettingskoder, vil de gamle slutte å fungere.",
"recovery_codes" : "Gjenopprettingskoder.", "recovery_codes": "Gjenopprettingskoder.",
"waiting_a_recovery_codes": "Mottar gjenopprettingskoder...", "waiting_a_recovery_codes": "Mottar gjenopprettingskoder...",
"recovery_codes_warning" : "Skriv disse kodene ned eller plasser dem ett sikkert sted - ellers så vil du ikke se dem igjen. Dersom du mister tilgang til din to-faktors app og dine gjenopprettingskoder, vil du bli stengt ute av kontoen din.", "recovery_codes_warning": "Skriv disse kodene ned eller plasser dem ett sikkert sted - ellers så vil du ikke se dem igjen. Dersom du mister tilgang til din to-faktors app og dine gjenopprettingskoder, vil du bli stengt ute av kontoen din.",
"authentication_methods" : "Autentiseringsmetoder", "authentication_methods": "Autentiseringsmetoder",
"scan": { "scan": {
"title": "Skann", "title": "Skann",
"desc": "Ved hjelp av din to-faktors applikasjon, skann denne QR-koden eller skriv inn tekstnøkkelen", "desc": "Ved hjelp av din to-faktors applikasjon, skann denne QR-koden eller skriv inn tekstnøkkelen",
@ -579,7 +582,7 @@
"favorite": "Lik", "favorite": "Lik",
"user_settings": "Brukerinnstillinger" "user_settings": "Brukerinnstillinger"
}, },
"upload":{ "upload": {
"error": { "error": {
"base": "Det oppsto en feil under opplastning.", "base": "Det oppsto en feil under opplastning.",
"file_too_big": "Fil for stor [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "file_too_big": "Fil for stor [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",

View file

@ -35,7 +35,12 @@
"retry": "Tenta novamente", "retry": "Tenta novamente",
"error_retry": "Por favor, tenta novamente", "error_retry": "Por favor, tenta novamente",
"loading": "A carregar…", "loading": "A carregar…",
"dismiss": "Ignorar" "dismiss": "Ignorar",
"role":
{
"moderator": "Moderador",
"admin": "Admin"
}
}, },
"image_cropper": { "image_cropper": {
"crop_picture": "Cortar imagem", "crop_picture": "Cortar imagem",
@ -615,11 +620,7 @@
"report": "Denunciar", "report": "Denunciar",
"message": "Mensagem", "message": "Mensagem",
"mention": "Mencionar", "mention": "Mencionar",
"hidden": "Ocultar", "hidden": "Ocultar"
"roles": {
"moderator": "Moderador",
"admin": "Admin"
}
}, },
"user_profile": { "user_profile": {
"timeline_title": "Cronologia do Utilizador", "timeline_title": "Cronologia do Utilizador",

View file

@ -24,7 +24,11 @@
"retry": "Попробуйте еще раз", "retry": "Попробуйте еще раз",
"error_retry": "Пожалуйста попробуйте еще раз", "error_retry": "Пожалуйста попробуйте еще раз",
"close": "Закрыть", "close": "Закрыть",
"loading": "Загрузка…" "loading": "Загрузка…",
"role": {
"moderator": "Модератор",
"admin": "Администратор"
}
}, },
"login": { "login": {
"login": "Войти", "login": "Войти",
@ -541,11 +545,7 @@
"mention": "Упомянуть", "mention": "Упомянуть",
"show_repeats": "Показывать повторы", "show_repeats": "Показывать повторы",
"hide_repeats": "Скрыть повторы", "hide_repeats": "Скрыть повторы",
"report": "Пожаловаться", "report": "Пожаловаться"
"roles": {
"moderator": "Модератор",
"admin": "Администратор"
}
}, },
"user_profile": { "user_profile": {
"timeline_title": "Лента пользователя" "timeline_title": "Лента пользователя"
@ -615,7 +615,8 @@
"gopher": "Gopher", "gopher": "Gopher",
"who_to_follow": "Предложения кого читать", "who_to_follow": "Предложения кого читать",
"pleroma_chat_messages": "Pleroma Чат", "pleroma_chat_messages": "Pleroma Чат",
"upload_limit": "Наибольший размер загружаемого файла" "upload_limit": "Наибольший размер загружаемого файла",
"scope_options": "Настраиваемая видимость статусов"
}, },
"tool_tip": { "tool_tip": {
"accept_follow_request": "Принять запрос на чтение", "accept_follow_request": "Принять запрос на чтение",

View file

@ -17,7 +17,11 @@
"more": "Більше", "more": "Більше",
"submit": "Відправити", "submit": "Відправити",
"apply": "Застосувати", "apply": "Застосувати",
"peek": "Глянути" "peek": "Глянути",
"role": {
"moderator": "Модератор",
"admin": "Адміністратор"
}
}, },
"finder": { "finder": {
"error_fetching_user": "Користувача не знайдено", "error_fetching_user": "Користувача не знайдено",
@ -760,11 +764,7 @@
"unblock": "Розблокувати", "unblock": "Розблокувати",
"remote_follow": "Підписатись", "remote_follow": "Підписатись",
"muted": "Заглушений", "muted": "Заглушений",
"mute": "Заглушити", "mute": "Заглушити"
"roles": {
"moderator": "Модератор",
"admin": "Адміністратор"
}
}, },
"status": { "status": {
"copy_link": "Скопіювати посилання на допис", "copy_link": "Скопіювати посилання на допис",

View file

@ -39,7 +39,11 @@
"close": "关闭", "close": "关闭",
"retry": "重试", "retry": "重试",
"error_retry": "请重试", "error_retry": "请重试",
"loading": "载入中…" "loading": "载入中…",
"role": {
"moderator": "监察员",
"admin": "管理员"
}
}, },
"image_cropper": { "image_cropper": {
"crop_picture": "裁剪图片", "crop_picture": "裁剪图片",
@ -120,7 +124,9 @@
"expiry": "投票期限", "expiry": "投票期限",
"expires_in": "投票于 {0} 后结束", "expires_in": "投票于 {0} 后结束",
"expired": "投票 {0} 前已结束", "expired": "投票 {0} 前已结束",
"not_enough_options": "投票的选项太少" "not_enough_options": "投票的选项太少",
"votes_count": "{count} 票 | {count} 票",
"people_voted_count": "{count} 人已投票 | {count} 人已投票"
}, },
"stickers": { "stickers": {
"add_sticker": "添加贴纸" "add_sticker": "添加贴纸"
@ -183,7 +189,9 @@
"password_required": "不能留空", "password_required": "不能留空",
"password_confirmation_required": "不能留空", "password_confirmation_required": "不能留空",
"password_confirmation_match": "密码不一致" "password_confirmation_match": "密码不一致"
} },
"reason_placeholder": "此实例的注册需要手动批准。\n请让管理员知道您为什么想要注册。",
"reason": "注册理由"
}, },
"selectable_list": { "selectable_list": {
"select_all": "选择全部" "select_all": "选择全部"
@ -552,7 +560,8 @@
"mute_import": "隐藏名单导入", "mute_import": "隐藏名单导入",
"mute_export_button": "导出你的隐藏名单到一个 csv 文件", "mute_export_button": "导出你的隐藏名单到一个 csv 文件",
"mute_export": "隐藏名单导出", "mute_export": "隐藏名单导出",
"hide_wallpaper": "隐藏实例壁纸" "hide_wallpaper": "隐藏实例壁纸",
"setting_changed": "与默认设置不同"
}, },
"time": { "time": {
"day": "{0} 天", "day": "{0} 天",
@ -683,7 +692,8 @@
"show_repeats": "显示转发", "show_repeats": "显示转发",
"hide_repeats": "隐藏转发", "hide_repeats": "隐藏转发",
"message": "消息", "message": "消息",
"mention": "提及" "mention": "提及",
"bot": "机器人"
}, },
"user_profile": { "user_profile": {
"timeline_title": "用户时间线", "timeline_title": "用户时间线",

View file

@ -25,7 +25,7 @@
"add_poll": "增加投票" "add_poll": "增加投票"
}, },
"notifications": { "notifications": {
"reacted_with": "和 {0} 互動過", "reacted_with": "作出了 {0} 的反應",
"migrated_to": "遷移到", "migrated_to": "遷移到",
"no_more_notifications": "沒有更多的通知", "no_more_notifications": "沒有更多的通知",
"repeated_you": "轉發了你的發文", "repeated_you": "轉發了你的發文",
@ -54,7 +54,7 @@
"mentions": "提及", "mentions": "提及",
"friend_requests": "關注請求", "friend_requests": "關注請求",
"back": "後退", "back": "後退",
"administration": "管理", "administration": "管理",
"about": "關於" "about": "關於"
}, },
"media_modal": { "media_modal": {
@ -216,7 +216,8 @@
"incoming": "收到", "incoming": "收到",
"outgoing": "發出", "outgoing": "發出",
"border": "邊框" "border": "邊框"
} },
"wallpaper": "桌布"
}, },
"preview": { "preview": {
"header_faint": "這很正常", "header_faint": "這很正常",
@ -412,7 +413,7 @@
"hide_follows_description": "不要顯示我所關注的人", "hide_follows_description": "不要顯示我所關注的人",
"hide_followers_description": "不要顯示關注我的人", "hide_followers_description": "不要顯示關注我的人",
"hide_follows_count_description": "不顯示關注數", "hide_follows_count_description": "不顯示關注數",
"nsfw_clickthrough": "將敏感附件隱藏,點擊才能打開", "nsfw_clickthrough": "將敏感附件和鏈接隱藏,點擊才能打開",
"valid_until": "有效期至", "valid_until": "有效期至",
"panelRadius": "面板", "panelRadius": "面板",
"pause_on_unfocused": "在離開頁面時暫停時間線推送", "pause_on_unfocused": "在離開頁面時暫停時間線推送",
@ -572,16 +573,20 @@
"thread_muted_and_words": ",有这些字:", "thread_muted_and_words": ",有这些字:",
"hide_full_subject": "隱藏完整標題", "hide_full_subject": "隱藏完整標題",
"show_content": "顯示內容", "show_content": "顯示內容",
"hide_content": "隱藏內容" "hide_content": "隱藏內容",
"status_deleted": "該帖已被刪除",
"expand": "展开",
"external_source": "外部來源",
"nsfw": "工作不安全"
}, },
"time": { "time": {
"hours": "{0} 小時", "hours": "{0} 時",
"days_short": "{0}天", "days_short": "{0}天",
"day_short": "{0}天", "day_short": "{0}天",
"days": "{0} 天", "days": "{0} 天",
"hour": "{0} 小时", "hour": "{0} ",
"hour_short": "{0}h", "hour_short": "{0}",
"hours_short": "{0}h", "hours_short": "{0}",
"years_short": "{0} y", "years_short": "{0} y",
"now": "剛剛", "now": "剛剛",
"day": "{0} 天", "day": "{0} 天",
@ -655,7 +660,8 @@
"reload": "重新載入", "reload": "重新載入",
"up_to_date": "已是最新", "up_to_date": "已是最新",
"no_more_statuses": "没有更多發文", "no_more_statuses": "没有更多發文",
"no_statuses": "没有發文" "no_statuses": "没有發文",
"error": "取得時間線時發生錯誤:{0}"
}, },
"interactions": { "interactions": {
"load_older": "載入更早的互動", "load_older": "載入更早的互動",
@ -746,7 +752,11 @@
"unmute": "取消靜音", "unmute": "取消靜音",
"unmute_progress": "取消靜音中…", "unmute_progress": "取消靜音中…",
"hide_repeats": "隱藏轉發", "hide_repeats": "隱藏轉發",
"show_repeats": "顯示轉發" "show_repeats": "顯示轉發",
"roles": {
"moderator": "主持人",
"admin": "管理員"
}
}, },
"user_profile": { "user_profile": {
"timeline_title": "用戶時間線", "timeline_title": "用戶時間線",
@ -788,7 +798,8 @@
"error": { "error": {
"base": "上傳失敗。", "base": "上傳失敗。",
"file_too_big": "文件太大[{filesize} {filesizeunit} / {allowedsize} {allowedsizeunit}]", "file_too_big": "文件太大[{filesize} {filesizeunit} / {allowedsize} {allowedsizeunit}]",
"default": "稍後再試" "default": "稍後再試",
"message": "上傳錯誤:{0}"
} }
}, },
"search": { "search": {

View file

@ -28,7 +28,6 @@ import pushNotifications from './lib/push_notifications_plugin.js'
import messages from './i18n/messages.js' import messages from './i18n/messages.js'
import VueChatScroll from 'vue-chat-scroll'
import VueClickOutside from 'v-click-outside' import VueClickOutside from 'v-click-outside'
import PortalVue from 'portal-vue' import PortalVue from 'portal-vue'
import VBodyScrollLock from './directives/body_scroll_lock' import VBodyScrollLock from './directives/body_scroll_lock'
@ -42,7 +41,6 @@ const currentLocale = (window.navigator.language || 'en').split('-')[0]
Vue.use(Vuex) Vue.use(Vuex)
Vue.use(VueRouter) Vue.use(VueRouter)
Vue.use(VueI18n) Vue.use(VueI18n)
Vue.use(VueChatScroll)
Vue.use(VueClickOutside) Vue.use(VueClickOutside)
Vue.use(PortalVue) Vue.use(PortalVue)
Vue.use(VBodyScrollLock) Vue.use(VBodyScrollLock)

View file

@ -18,6 +18,7 @@ const chat = {
actions: { actions: {
initializeChat (store, socket) { initializeChat (store, socket) {
const channel = socket.channel('chat:public') const channel = socket.channel('chat:public')
channel.on('new_msg', (msg) => { channel.on('new_msg', (msg) => {
store.commit('addMessage', msg) store.commit('addMessage', msg)
}) })

View file

@ -115,6 +115,9 @@ const chats = {
}, },
handleMessageError ({ commit }, value) { handleMessageError ({ commit }, value) {
commit('handleMessageError', { commit, ...value }) commit('handleMessageError', { commit, ...value })
},
cullOlderMessages ({ commit }, chatId) {
commit('cullOlderMessages', chatId)
} }
}, },
mutations: { mutations: {
@ -227,6 +230,9 @@ const chats = {
handleMessageError (state, { chatId, fakeId, isRetry }) { handleMessageError (state, { chatId, fakeId, isRetry }) {
const chatMessageService = state.openedChatMessageServices[chatId] const chatMessageService = state.openedChatMessageServices[chatId]
chatService.handleMessageError(chatMessageService, fakeId, isRetry) chatService.handleMessageError(chatMessageService, fakeId, isRetry)
},
cullOlderMessages (state, chatId) {
chatService.cullOlderMessages(state.openedChatMessageServices[chatId])
} }
} }
} }

View file

@ -70,7 +70,8 @@ export const defaultState = {
greentext: undefined, // instance default greentext: undefined, // instance default
hidePostStats: undefined, // instance default hidePostStats: undefined, // instance default
hideUserStats: undefined, // instance default hideUserStats: undefined, // instance default
virtualScrolling: undefined // instance default virtualScrolling: undefined, // instance default
sensitiveByDefault: undefined // instance default
} }
// caching the instance default properties // caching the instance default properties

View file

@ -43,6 +43,7 @@ const defaultState = {
subjectLineBehavior: 'email', subjectLineBehavior: 'email',
theme: 'pleroma-dark', theme: 'pleroma-dark',
virtualScrolling: true, virtualScrolling: true,
sensitiveByDefault: false,
// Nasty stuff // Nasty stuff
customEmoji: [], customEmoji: [],

View file

@ -13,7 +13,11 @@ import {
omitBy omitBy
} from 'lodash' } from 'lodash'
import { set } from 'vue' import { set } from 'vue'
import { isStatusNotification, maybeShowNotification } from '../services/notification_utils/notification_utils.js' import {
isStatusNotification,
isValidNotification,
maybeShowNotification
} from '../services/notification_utils/notification_utils.js'
import apiService from '../services/api/api.service.js' import apiService from '../services/api/api.service.js'
const emptyTl = (userId = 0) => ({ const emptyTl = (userId = 0) => ({
@ -310,8 +314,24 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
} }
} }
const updateNotificationsMinMaxId = (state, notification) => {
state.notifications.maxId = notification.id > state.notifications.maxId
? notification.id
: state.notifications.maxId
state.notifications.minId = notification.id < state.notifications.minId
? notification.id
: state.notifications.minId
}
const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters, newNotificationSideEffects }) => { const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters, newNotificationSideEffects }) => {
each(notifications, (notification) => { each(notifications, (notification) => {
// If invalid notification, update ids but don't add it to store
if (!isValidNotification(notification)) {
console.error('Invalid notification:', notification)
updateNotificationsMinMaxId(state, notification)
return
}
if (isStatusNotification(notification.type)) { if (isStatusNotification(notification.type)) {
notification.action = addStatusToGlobalStorage(state, notification.action).item notification.action = addStatusToGlobalStorage(state, notification.action).item
notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item
@ -323,12 +343,7 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
// Only add a new notification if we don't have one for the same action // Only add a new notification if we don't have one for the same action
if (!state.notifications.idStore.hasOwnProperty(notification.id)) { if (!state.notifications.idStore.hasOwnProperty(notification.id)) {
state.notifications.maxId = notification.id > state.notifications.maxId updateNotificationsMinMaxId(state, notification)
? notification.id
: state.notifications.maxId
state.notifications.minId = notification.id < state.notifications.minId
? notification.id
: state.notifications.minId
state.notifications.data.push(notification) state.notifications.data.push(notification)
state.notifications.idStore[notification.id] = notification state.notifications.idStore[notification.id] = notification

View file

@ -48,6 +48,22 @@ const deleteMessage = (storage, messageId) => {
} }
} }
const cullOlderMessages = (storage) => {
const maxIndex = storage.messages.length
const minIndex = maxIndex - 50
if (maxIndex <= 50) return
storage.messages = _.sortBy(storage.messages, ['id'])
storage.minId = storage.messages[minIndex].id
for (const message of storage.messages) {
if (message.id < storage.minId) {
delete storage.idIndex[message.id]
delete storage.idempotencyKeyIndex[message.idempotency_key]
}
}
storage.messages = storage.messages.slice(minIndex, maxIndex)
}
const handleMessageError = (storage, fakeId, isRetry) => { const handleMessageError = (storage, fakeId, isRetry) => {
if (!storage) { return } if (!storage) { return }
const fakeMessage = storage.idIndex[fakeId] const fakeMessage = storage.idIndex[fakeId]
@ -201,6 +217,7 @@ const ChatService = {
empty, empty,
getView, getView,
deleteMessage, deleteMessage,
cullOlderMessages,
resetNewMessageCount, resetNewMessageCount,
clear, clear,
handleMessageError handleMessageError

View file

@ -203,7 +203,8 @@ export const parseUser = (data) => {
output.rights = output.rights || {} output.rights = output.rights || {}
output.notification_settings = output.notification_settings || {} output.notification_settings = output.notification_settings || {}
// Convert punycode to unicode // Convert punycode to unicode for UI
output.screen_name_ui = output.screen_name
if (output.screen_name.includes('@')) { if (output.screen_name.includes('@')) {
const parts = output.screen_name.split('@') const parts = output.screen_name.split('@')
let unicodeDomain = punycode.toUnicode(parts[1]) let unicodeDomain = punycode.toUnicode(parts[1])
@ -211,7 +212,7 @@ export const parseUser = (data) => {
// Add some identifier so users can potentially spot spoofing attempts: // Add some identifier so users can potentially spot spoofing attempts:
// lain.com and xn--lin-6cd.com would appear identical otherwise. // lain.com and xn--lin-6cd.com would appear identical otherwise.
unicodeDomain = '🌏' + unicodeDomain unicodeDomain = '🌏' + unicodeDomain
output.screen_name = [parts[0], unicodeDomain].join('@') output.screen_name_ui = [parts[0], unicodeDomain].join('@')
} }
} }

View file

@ -22,6 +22,13 @@ const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reactio
export const isStatusNotification = (type) => includes(statusNotifications, type) export const isStatusNotification = (type) => includes(statusNotifications, type)
export const isValidNotification = (notification) => {
if (isStatusNotification(notification.type) && !notification.status) {
return false
}
return true
}
const sortById = (a, b) => { const sortById = (a, b) => {
const seqA = Number(a.id) const seqA = Number(a.id)
const seqB = Number(b.id) const seqB = Number(b.id)

View file

@ -31,13 +31,15 @@ const testGetters = {
const localUser = { const localUser = {
id: 100, id: 100,
is_local: true, is_local: true,
screen_name: 'testUser' screen_name: 'testUser',
screen_name_ui: 'testUser'
} }
const extUser = { const extUser = {
id: 100, id: 100,
is_local: false, is_local: false,
screen_name: 'testUser@test.instance' screen_name: 'testUser@test.instance',
screen_name_ui: 'testUser@test.instance'
} }
const externalProfileStore = new Vuex.Store({ const externalProfileStore = new Vuex.Store({

View file

@ -88,4 +88,21 @@ describe('chatService', () => {
expect(view.map(i => i.type)).to.eql(['date', 'message', 'message', 'date', 'message']) expect(view.map(i => i.type)).to.eql(['date', 'message', 'message', 'date', 'message'])
}) })
}) })
describe('.cullOlderMessages', () => {
it('keeps 50 newest messages and idIndex matches', () => {
const chat = chatService.empty()
for (let i = 100; i > 0; i--) {
// Use decimal values with toFixed to hack together constant length predictable strings
chatService.add(chat, { messages: [{ ...message1, id: 'a' + (i / 1000).toFixed(3), idempotency_key: i }] })
}
chatService.cullOlderMessages(chat)
expect(chat.messages.length).to.eql(50)
expect(chat.messages[0].id).to.eql('a0.051')
expect(chat.minId).to.eql('a0.051')
expect(chat.messages[49].id).to.eql('a0.100')
expect(Object.keys(chat.idIndex).length).to.eql(50)
})
})
}) })

View file

@ -315,7 +315,7 @@ describe('API Entities normalizer', () => {
it('converts IDN to unicode and marks it as internatonal', () => { it('converts IDN to unicode and marks it as internatonal', () => {
const user = makeMockUserMasto({ acct: 'lain@xn--lin-6cd.com' }) const user = makeMockUserMasto({ acct: 'lain@xn--lin-6cd.com' })
expect(parseUser(user)).to.have.property('screen_name').that.equal('lain@🌏lаin.com') expect(parseUser(user)).to.have.property('screen_name_ui').that.equal('lain@🌏lаin.com')
}) })
}) })

View file

@ -7842,9 +7842,10 @@ shebang-regex@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
shelljs@^0.7.4: shelljs@^0.8.4:
version "0.7.8" version "0.8.4"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2"
integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==
dependencies: dependencies:
glob "^7.0.0" glob "^7.0.0"
interpret "^1.0.0" interpret "^1.0.0"
@ -8922,10 +8923,6 @@ void-elements@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
vue-chat-scroll@^1.2.1:
version "1.3.5"
resolved "https://registry.yarnpkg.com/vue-chat-scroll/-/vue-chat-scroll-1.3.5.tgz#a5ee5bae5058f614818a96eac5ee3be4394a2f68"
vue-eslint-parser@^5.0.0: vue-eslint-parser@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1" resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1"