mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Merge branch 'main' into release
This commit is contained in:
1
fastlane/metadata/android/it-IT/full_description.txt
Symbolic link
1
fastlane/metadata/android/it-IT/full_description.txt
Symbolic link
@ -0,0 +1 @@
|
||||
../../it/description.txt
|
1
fastlane/metadata/android/it-IT/short_description.txt
Symbolic link
1
fastlane/metadata/android/it-IT/short_description.txt
Symbolic link
@ -0,0 +1 @@
|
||||
../../it/subtitle.txt
|
@ -1,5 +1,10 @@
|
||||
tooot is an open source, simple yet elegant Mastodon mobile client.
|
||||
tooot is an open source, simple yet elegant Mastodon mobile client. A Mastodon (https://joinmastodon.org/) account is required to use this app.
|
||||
|
||||
A Mastodon (https://joinmastodon.org/) account is required to use this app.
|
||||
tooot supports:
|
||||
- Cross platform, including iPadOS and MacOS
|
||||
- Multiple accounts
|
||||
- Dark mode or adapt to system
|
||||
- Adjustable toot font size
|
||||
- Push notification
|
||||
|
||||
If you have suggestions, please reach out to @tooot@xmflsct.com or support@tooot.ap.
|
||||
If you have suggestions, please reach out to @tooot@xmflsct.com or support@tooot.app.
|
||||
|
@ -1,10 +1,5 @@
|
||||
Enjoy toooting! This version includes following improvements and fixes:
|
||||
- Added Ukrainian (Slava Ukraini)
|
||||
- Automatic setting detected language when tooting
|
||||
- Remember public timeline type selection
|
||||
- Show diffing of edit history
|
||||
- Allow hiding boosts and replies in home timeline
|
||||
- Support toot in RTL languages
|
||||
- Added notification for admins
|
||||
- Fix whole word filter matching
|
||||
- Fix tablet cannot delete toot drafts
|
||||
- Align filter experience with v4.0 and above
|
||||
- Supports enlarging user's avatar and banner
|
||||
- Fix iPad weird sizing (not optimisation)
|
||||
- Experiment (!) support of Pleroma
|
||||
|
10
fastlane/metadata/it/description.txt
Normal file
10
fastlane/metadata/it/description.txt
Normal file
@ -0,0 +1,10 @@
|
||||
tooot è un client Mastodon semplice e open source. Per utilizzare questo client, devi disporre di un account Mastodon. (https://joinmastodon.org/).
|
||||
|
||||
Tooot supporta:
|
||||
- Multipiattaforma, inclusi iPadOS e MacOS
|
||||
- Accesso a più account
|
||||
- Modalità scura o adattiva
|
||||
- Dimensione del carattere del testo regolabile
|
||||
- Notifiche push e altre funzioni
|
||||
|
||||
Per suggerimenti o commenti sull'utilizzo, contattare @tooot@xmflsct.com o support@tooot.app.
|
1
fastlane/metadata/it/subtitle.txt
Normal file
1
fastlane/metadata/it/subtitle.txt
Normal file
@ -0,0 +1 @@
|
||||
Client open source per Mastodon
|
@ -1,11 +1,10 @@
|
||||
tooot是一个专门为中文用户社区所打造的开源、简洁长毛象客户端。使用此客户端需要已经拥有一个长毛象(https://joinmastodon.org/)账号。
|
||||
tooot起始于专注中文社区的简洁、开源长毛象手机客户端。使用此客户端需要已经拥有一个长毛象(https://joinmastodon.org/)账号。
|
||||
|
||||
tooot支持:
|
||||
- iPad
|
||||
- 跨平台,及iPadOS、MacOS
|
||||
- 多账号登录
|
||||
- 黑暗或自适应模式
|
||||
- 可调整正文字体大小
|
||||
- 可调正文字体尺寸
|
||||
- 消息推送
|
||||
等功能。
|
||||
|
||||
如有使用建议或意见,请联系@tooot@xmflsct.com或者support@tooot.app。
|
@ -1,10 +1,5 @@
|
||||
toooting愉快!此版本包括以下改进和修复:
|
||||
- 增加乌克兰语(Slava Ukraini)
|
||||
- 自动识别发嘟语言
|
||||
- 记住上次公共时间轴选项
|
||||
- 显示编辑历史的差异
|
||||
- 关注列表可隐藏转嘟和回复
|
||||
- 新增管理员推送通知
|
||||
- 支持嘟文右到左文字
|
||||
- 修复过滤整词功能
|
||||
- 修复平板不能删除草稿
|
||||
- 改进过滤体验,与v4.0以上版本一致
|
||||
- 支持查看用户的头像和横幅图片
|
||||
- 修复iPad部分尺寸问题(非优化)
|
||||
- 试验性(!)支持Pleroma
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tooot",
|
||||
"version": "4.7.0",
|
||||
"version": "4.7.1",
|
||||
"description": "tooot for Mastodon",
|
||||
"author": "xmflsct <me@xmflsct.com>",
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
29
src/@types/mastodon.d.ts
vendored
29
src/@types/mastodon.d.ts
vendored
@ -263,7 +263,8 @@ declare namespace Mastodon {
|
||||
verified_at: string | null
|
||||
}
|
||||
|
||||
type Filter = {
|
||||
type Filter<T extends 'v1' | 'v2'> = T extends 'v2' ? Filter_V2 : Filter_V1
|
||||
type Filter_V1 = {
|
||||
id: string
|
||||
phrase: string
|
||||
context: ('home' | 'notifications' | 'public' | 'thread' | 'account')[]
|
||||
@ -271,6 +272,25 @@ declare namespace Mastodon {
|
||||
irreversible: boolean
|
||||
whole_word: boolean
|
||||
}
|
||||
type Filter_V2 = {
|
||||
id: string
|
||||
title: string
|
||||
context: ('home' | 'notifications' | 'public' | 'thread' | 'account')[]
|
||||
expires_at?: string
|
||||
filter_action: 'warn' | 'hide'
|
||||
keywords: FilterKeyword[]
|
||||
statuses: FilterStatus[]
|
||||
}
|
||||
|
||||
type FilterKeyword = { id: string; keyword: string; whole_word: boolean }
|
||||
|
||||
type FilterStatus = { id: string; status_id: string }
|
||||
|
||||
type FilterResult = {
|
||||
filter: Filter<'v2'>
|
||||
keyword_matches?: FilterKeyword['keyword'][]
|
||||
status_matches?: FilterStatus['id'][]
|
||||
}
|
||||
|
||||
type List = {
|
||||
id: string
|
||||
@ -406,6 +426,8 @@ declare namespace Mastodon {
|
||||
id: string
|
||||
following: boolean
|
||||
showing_reblogs: boolean
|
||||
notifying?: boolean
|
||||
languages?: string[]
|
||||
followed_by: boolean
|
||||
blocking: boolean
|
||||
blocked_by: boolean
|
||||
@ -459,7 +481,7 @@ declare namespace Mastodon {
|
||||
sensitive: boolean
|
||||
spoiler_text?: string
|
||||
media_attachments: Attachment[]
|
||||
application: Application
|
||||
application?: Application
|
||||
|
||||
// Attributes
|
||||
mentions: Mention[]
|
||||
@ -470,7 +492,7 @@ declare namespace Mastodon {
|
||||
reblogs_count: number
|
||||
favourites_count: number
|
||||
replies_count: number
|
||||
edited_at?: string // FEATURE edit_post
|
||||
edited_at?: string
|
||||
favourited: boolean
|
||||
reblogged: boolean
|
||||
muted: boolean
|
||||
@ -486,6 +508,7 @@ declare namespace Mastodon {
|
||||
card?: Card
|
||||
language?: string
|
||||
text?: string
|
||||
filtered?: FilterResult[]
|
||||
}
|
||||
|
||||
type StatusHistory = {
|
||||
|
@ -56,7 +56,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||
useEffect(() => {
|
||||
const screenshotListener = addScreenshotListener(() =>
|
||||
Alert.alert(t('screenshot.title'), t('screenshot.message'), [
|
||||
{ text: t('screenshot.button'), style: 'destructive' }
|
||||
{ text: t('common:buttons.confirm'), style: 'destructive' }
|
||||
])
|
||||
)
|
||||
Platform.select({ ios: screenshotListener })
|
||||
|
@ -53,7 +53,7 @@ const ComponentHashtag: React.FC<PropsWithChildren & Props> = ({
|
||||
#{hashtag.name}
|
||||
</CustomText>
|
||||
<View
|
||||
style={{ flexDirection: 'row', alignItems: 'center' }}
|
||||
style={{ flexDirection: 'row', alignItems: 'center', alignSelf: 'stretch' }}
|
||||
onLayout={({
|
||||
nativeEvent: {
|
||||
layout: { height }
|
||||
@ -61,7 +61,7 @@ const ComponentHashtag: React.FC<PropsWithChildren & Props> = ({
|
||||
}) => setHeight(height)}
|
||||
>
|
||||
<Sparkline
|
||||
data={hashtag.history.map(h => parseInt(h.uses)).reverse()}
|
||||
data={hashtag.history?.map(h => parseInt(h.uses)).reverse()}
|
||||
width={width}
|
||||
height={height}
|
||||
margin={children ? StyleConstants.Spacing.S : undefined}
|
||||
|
@ -15,6 +15,7 @@ import { Alert, Image, KeyboardAvoidingView, Platform, TextInput, View } from 'r
|
||||
import { ScrollView } from 'react-native-gesture-handler'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { Placeholder } from 'rn-placeholder'
|
||||
import validUrl from 'valid-url'
|
||||
import InstanceInfo from './Info'
|
||||
import CustomText from '../Text'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
@ -39,12 +40,26 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
const navigation = useNavigation<TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>>()
|
||||
|
||||
const [domain, setDomain] = useState<string>('')
|
||||
const [errorCode, setErrorCode] = useState<number | null>(null)
|
||||
const whitelisted: boolean =
|
||||
!!domain.length &&
|
||||
!!errorCode &&
|
||||
!!validUrl.isHttpsUri(`https://${domain}`) &&
|
||||
errorCode === 401
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
const instances = useSelector(getInstances, () => true)
|
||||
const instanceQuery = useInstanceQuery({
|
||||
domain,
|
||||
options: { enabled: !!domain, retry: false }
|
||||
options: {
|
||||
enabled: !!domain,
|
||||
retry: false,
|
||||
onError: err => {
|
||||
if (err.status) {
|
||||
setErrorCode(err.status)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const deprecateAuthFollow = useSelector(checkInstanceFeature('deprecate_auth_follow'))
|
||||
@ -146,7 +161,11 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
borderBottomWidth: 1,
|
||||
...StyleConstants.FontStyle.M,
|
||||
color: colors.primaryDefault,
|
||||
borderBottomColor: instanceQuery.isError ? colors.red : colors.border,
|
||||
borderBottomColor: instanceQuery.isError
|
||||
? whitelisted
|
||||
? colors.yellow
|
||||
: colors.red
|
||||
: colors.border,
|
||||
...(Platform.OS === 'android' && { paddingRight: 0 })
|
||||
}}
|
||||
editable={false}
|
||||
@ -159,12 +178,23 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
...StyleConstants.FontStyle.M,
|
||||
marginRight: StyleConstants.Spacing.M,
|
||||
color: colors.primaryDefault,
|
||||
borderBottomColor: instanceQuery.isError ? colors.red : colors.border,
|
||||
borderBottomColor: instanceQuery.isError
|
||||
? whitelisted
|
||||
? colors.yellow
|
||||
: colors.red
|
||||
: colors.border,
|
||||
...(Platform.OS === 'android' && { paddingLeft: 0 })
|
||||
}}
|
||||
onChangeText={debounce(text => setDomain(text.replace(/^http(s)?\:\/\//i, '')), 1000, {
|
||||
onChangeText={debounce(
|
||||
text => {
|
||||
setDomain(text.replace(/^http(s)?\:\/\//i, ''))
|
||||
setErrorCode(null)
|
||||
},
|
||||
1000,
|
||||
{
|
||||
trailing: true
|
||||
})}
|
||||
}
|
||||
)}
|
||||
autoCapitalize='none'
|
||||
clearButtonMode='never'
|
||||
keyboardType='url'
|
||||
@ -194,12 +224,24 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
type='text'
|
||||
content={t('server.button')}
|
||||
onPress={processUpdate}
|
||||
disabled={!instanceQuery.data?.uri}
|
||||
disabled={!instanceQuery.data?.uri && !whitelisted}
|
||||
loading={instanceQuery.isFetching || appsMutation.isLoading}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View>
|
||||
{whitelisted ? (
|
||||
<CustomText
|
||||
fontStyle='S'
|
||||
style={{
|
||||
color: colors.yellow,
|
||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingTop: StyleConstants.Spacing.XS
|
||||
}}
|
||||
>
|
||||
{t('server.whitelisted')}
|
||||
</CustomText>
|
||||
) : (
|
||||
<Placeholder>
|
||||
<InstanceInfo
|
||||
header={t('server.information.name')}
|
||||
@ -227,6 +269,7 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
/>
|
||||
</View>
|
||||
</Placeholder>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
|
@ -4,8 +4,8 @@ import { getSettingsFontsize } from '@utils/slices/settingsSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { adaptiveScale } from '@utils/styles/scaling'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Platform, StyleSheet, TextStyle } from 'react-native'
|
||||
import React from 'react'
|
||||
import { Platform, TextStyle } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { useSelector } from 'react-redux'
|
||||
import validUrl from 'valid-url'
|
||||
@ -36,23 +36,19 @@ const ParseEmojis = React.memo(
|
||||
)
|
||||
|
||||
const { colors, theme } = useTheme()
|
||||
const styles = useMemo(() => {
|
||||
return StyleSheet.create({
|
||||
text: {
|
||||
|
||||
return (
|
||||
<CustomText
|
||||
style={[
|
||||
{
|
||||
color: colors.primaryDefault,
|
||||
fontSize: adaptedFontsize,
|
||||
lineHeight: adaptedLineheight
|
||||
},
|
||||
image: {
|
||||
width: adaptedFontsize,
|
||||
height: adaptedFontsize,
|
||||
...(Platform.OS === 'android' && { transform: [{ translateY: 2 }] })
|
||||
}
|
||||
})
|
||||
}, [theme, adaptiveFontsize])
|
||||
|
||||
return (
|
||||
<CustomText style={[styles.text, style]} fontWeight={fontBold ? 'Bold' : undefined}>
|
||||
style
|
||||
]}
|
||||
fontWeight={fontBold ? 'Bold' : undefined}
|
||||
>
|
||||
{emojis ? (
|
||||
content
|
||||
.split(regexEmoji)
|
||||
@ -73,7 +69,14 @@ const ParseEmojis = React.memo(
|
||||
return (
|
||||
<CustomText key={emojiShortcode + i}>
|
||||
{i === 0 ? ' ' : undefined}
|
||||
<FastImage source={{ uri }} style={styles.image} />
|
||||
<FastImage
|
||||
source={{ uri }}
|
||||
style={{
|
||||
width: adaptedFontsize,
|
||||
height: adaptedFontsize,
|
||||
transform: [{ translateY: Platform.OS === 'ios' ? -1 : 2 }]
|
||||
}}
|
||||
/>
|
||||
</CustomText>
|
||||
)
|
||||
} else {
|
||||
|
@ -102,7 +102,7 @@ const renderNode = ({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
const domain = href?.split(new RegExp(/:\/\/(.[^\/]+)/))
|
||||
const domain = href?.split(new RegExp(/:\/\/(.[^\/]+\/.{3})/))
|
||||
// Need example here
|
||||
const content = node.children && node.children[0] && node.children[0].data
|
||||
const shouldBeTag = tags && tags.filter(tag => `#${tag.name}` === content).length > 0
|
||||
@ -128,17 +128,7 @@ const renderNode = ({
|
||||
}}
|
||||
>
|
||||
{content && content !== href ? content : showFullLink ? href : domain?.[1]}
|
||||
{!shouldBeTag ? (
|
||||
<Icon
|
||||
color={colors.blue}
|
||||
name='ExternalLink'
|
||||
size={adaptedFontsize}
|
||||
style={{
|
||||
marginLeft: StyleConstants.Spacing.XS,
|
||||
...(Platform.OS === 'android' && { transform: [{ translateY: 2 }] })
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{!shouldBeTag ? '...' : null}
|
||||
</CustomText>
|
||||
)
|
||||
}
|
||||
|
@ -6,21 +6,24 @@ import {
|
||||
useRelationshipMutation,
|
||||
useRelationshipQuery
|
||||
} from '@utils/queryHooks/relationship'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
|
||||
export interface Props {
|
||||
id: Mastodon.Account['id']
|
||||
}
|
||||
|
||||
const RelationshipOutgoing = React.memo(
|
||||
({ id }: Props) => {
|
||||
const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('componentRelationship')
|
||||
|
||||
const canFollowNotify = useSelector(checkInstanceFeature('account_follow_notify'))
|
||||
|
||||
const query = useRelationshipQuery({ id })
|
||||
|
||||
const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }]
|
||||
@ -120,6 +123,27 @@ const RelationshipOutgoing = React.memo(
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{canFollowNotify && query.data?.following ? (
|
||||
<Button
|
||||
type='icon'
|
||||
content={query.data.notifying ? 'BellOff' : 'Bell'}
|
||||
round
|
||||
onPress={() =>
|
||||
mutation.mutate({
|
||||
id,
|
||||
type: 'outgoing',
|
||||
payload: {
|
||||
action: 'follow',
|
||||
state: false,
|
||||
notify: !query.data.notifying
|
||||
}
|
||||
})
|
||||
}
|
||||
loading={query.isLoading || mutation.isLoading}
|
||||
style={{ marginRight: StyleConstants.Spacing.S }}
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
type='text'
|
||||
content={content}
|
||||
@ -127,9 +151,8 @@ const RelationshipOutgoing = React.memo(
|
||||
loading={query.isLoading || mutation.isLoading}
|
||||
disabled={query.isError || query.data?.blocked_by}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
},
|
||||
() => true
|
||||
)
|
||||
}
|
||||
|
||||
export default RelationshipOutgoing
|
||||
|
@ -98,7 +98,6 @@ const TimelineConversation: React.FC<Props> = ({ conversation, queryKey, highlig
|
||||
</View>
|
||||
|
||||
{conversation.last_status ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
||||
@ -107,10 +106,9 @@ const TimelineConversation: React.FC<Props> = ({ conversation, queryKey, highlig
|
||||
>
|
||||
<TimelineContent />
|
||||
<TimelinePoll />
|
||||
</View>
|
||||
|
||||
<TimelineActions />
|
||||
</>
|
||||
</View>
|
||||
) : null}
|
||||
</Pressable>
|
||||
</StatusContext.Provider>
|
||||
|
@ -9,11 +9,12 @@ import TimelineCard from '@components/Timeline/Shared/Card'
|
||||
import TimelineContent from '@components/Timeline/Shared/Content'
|
||||
import TimelineHeaderDefault from '@components/Timeline/Shared/HeaderDefault'
|
||||
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||
import removeHTML from '@helpers/removeHTML'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { StackNavigationProp } from '@react-navigation/stack'
|
||||
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { checkInstanceFeature, getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useRef, useState } from 'react'
|
||||
@ -22,7 +23,7 @@ import { useSelector } from 'react-redux'
|
||||
import * as ContextMenu from 'zeego/context-menu'
|
||||
import StatusContext from './Shared/Context'
|
||||
import TimelineFeedback from './Shared/Feedback'
|
||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||
import TimelineFiltered, { FilteredProps, shouldFilter } from './Shared/Filtered'
|
||||
import TimelineFullConversation from './Shared/FullConversation'
|
||||
import TimelineHeaderAndroid from './Shared/HeaderAndroid'
|
||||
import TimelineTranslate from './Shared/Translate'
|
||||
@ -47,12 +48,20 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
disableOnPress = false,
|
||||
isConversation = false
|
||||
}) => {
|
||||
const status = item.reblog ? item.reblog : item
|
||||
const rawContent = useRef<string[]>([])
|
||||
if (highlighted) {
|
||||
rawContent.current = [
|
||||
removeHTML(status.content),
|
||||
status.spoiler_text ? removeHTML(status.spoiler_text) : ''
|
||||
].filter(c => c.length)
|
||||
}
|
||||
|
||||
const { colors } = useTheme()
|
||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
|
||||
const instanceAccount = useSelector(getInstanceAccount, () => true)
|
||||
|
||||
const status = item.reblog ? item.reblog : item
|
||||
const ownAccount = status.account?.id === instanceAccount?.id
|
||||
const [spoilerExpanded, setSpoilerExpanded] = useState(
|
||||
instanceAccount?.preferences?.['reading:expand:spoilers'] || false
|
||||
@ -60,15 +69,7 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
const spoilerHidden = status.spoiler_text?.length
|
||||
? !instanceAccount?.preferences?.['reading:expand:spoilers'] && !spoilerExpanded
|
||||
: false
|
||||
const copiableContent = useRef<{ content: string; complete: boolean }>({
|
||||
content: '',
|
||||
complete: false
|
||||
})
|
||||
|
||||
const filtered = queryKey && shouldFilter({ copiableContent, status, queryKey })
|
||||
if (queryKey && filtered && !highlighted) {
|
||||
return <TimelineFiltered phrase={filtered} />
|
||||
}
|
||||
const detectedLanguage = useRef<string>(status.language || '')
|
||||
|
||||
const mainStyle: StyleProp<ViewStyle> = {
|
||||
flex: 1,
|
||||
@ -102,8 +103,9 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
||||
paddingLeft: highlighted
|
||||
? 0
|
||||
: (disableDetails ? StyleConstants.Avatar.XS : StyleConstants.Avatar.M) +
|
||||
StyleConstants.Spacing.S,
|
||||
: (disableDetails || isConversation
|
||||
? StyleConstants.Avatar.XS
|
||||
: StyleConstants.Avatar.M) + StyleConstants.Spacing.S,
|
||||
...(disableDetails && { marginTop: -StyleConstants.Spacing.S })
|
||||
}}
|
||||
>
|
||||
@ -114,9 +116,9 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
<TimelineFullConversation />
|
||||
<TimelineTranslate />
|
||||
<TimelineFeedback />
|
||||
</View>
|
||||
|
||||
<TimelineActions />
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -124,11 +126,36 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
visibility: status.visibility,
|
||||
type: 'status',
|
||||
url: status.url || status.uri,
|
||||
copiableContent
|
||||
rawContent
|
||||
})
|
||||
const mStatus = menuStatus({ status, queryKey, rootQueryKey })
|
||||
const mInstance = menuInstance({ status, queryKey, rootQueryKey })
|
||||
|
||||
if (!ownAccount) {
|
||||
let filterResults: FilteredProps['filterResults'] = []
|
||||
const [filterRevealed, setFilterRevealed] = useState(false)
|
||||
const hasFilterServerSide = useSelector(checkInstanceFeature('filter_server_side'))
|
||||
if (hasFilterServerSide) {
|
||||
if (status.filtered?.length) {
|
||||
filterResults = status.filtered?.map(filter => filter.filter)
|
||||
}
|
||||
} else {
|
||||
if (queryKey) {
|
||||
const checkFilter = shouldFilter({ queryKey, status })
|
||||
if (checkFilter?.length) {
|
||||
filterResults = checkFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
if (queryKey && !highlighted && filterResults?.length && !filterRevealed) {
|
||||
return !filterResults.filter(result => result.filter_action === 'hide').length ? (
|
||||
<Pressable onPress={() => setFilterRevealed(!filterRevealed)}>
|
||||
<TimelineFiltered filterResults={filterResults} />
|
||||
</Pressable>
|
||||
) : null
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<StatusContext.Provider
|
||||
value={{
|
||||
@ -138,7 +165,8 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
reblogStatus: item.reblog ? item : undefined,
|
||||
ownAccount,
|
||||
spoilerHidden,
|
||||
copiableContent,
|
||||
rawContent,
|
||||
detectedLanguage,
|
||||
highlighted,
|
||||
inThread: queryKey?.[1].page === 'Toot',
|
||||
disableDetails,
|
||||
|
@ -13,7 +13,7 @@ import { useNavigation } from '@react-navigation/native'
|
||||
import { StackNavigationProp } from '@react-navigation/stack'
|
||||
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { checkInstanceFeature, getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useRef, useState } from 'react'
|
||||
@ -21,7 +21,7 @@ import { Pressable, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import * as ContextMenu from 'zeego/context-menu'
|
||||
import StatusContext from './Shared/Context'
|
||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||
import TimelineFiltered, { FilteredProps, shouldFilter } from './Shared/Filtered'
|
||||
import TimelineFullConversation from './Shared/FullConversation'
|
||||
import TimelineHeaderAndroid from './Shared/HeaderAndroid'
|
||||
|
||||
@ -47,21 +47,6 @@ const TimelineNotifications: React.FC<Props> = ({ notification, queryKey }) => {
|
||||
const spoilerHidden = notification.status?.spoiler_text?.length
|
||||
? !instanceAccount.preferences?.['reading:expand:spoilers'] && !spoilerExpanded
|
||||
: false
|
||||
const copiableContent = useRef<{ content: string; complete: boolean }>({
|
||||
content: '',
|
||||
complete: false
|
||||
})
|
||||
|
||||
const filtered =
|
||||
notification.status &&
|
||||
shouldFilter({
|
||||
copiableContent,
|
||||
status: notification.status,
|
||||
queryKey
|
||||
})
|
||||
if (notification.status && filtered) {
|
||||
return <TimelineFiltered phrase={filtered} />
|
||||
}
|
||||
|
||||
const { colors } = useTheme()
|
||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
@ -112,11 +97,11 @@ const TimelineNotifications: React.FC<Props> = ({ notification, queryKey }) => {
|
||||
<TimelineAttachment />
|
||||
<TimelineCard />
|
||||
<TimelineFullConversation />
|
||||
|
||||
<TimelineActions />
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<TimelineActions />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -124,20 +109,44 @@ const TimelineNotifications: React.FC<Props> = ({ notification, queryKey }) => {
|
||||
const mShare = menuShare({
|
||||
visibility: notification.status?.visibility,
|
||||
type: 'status',
|
||||
url: notification.status?.url || notification.status?.uri,
|
||||
copiableContent
|
||||
url: notification.status?.url || notification.status?.uri
|
||||
})
|
||||
const mStatus = menuStatus({ status: notification.status, queryKey })
|
||||
const mInstance = menuInstance({ status: notification.status, queryKey })
|
||||
|
||||
if (!ownAccount) {
|
||||
let filterResults: FilteredProps['filterResults'] = []
|
||||
const [filterRevealed, setFilterRevealed] = useState(false)
|
||||
const hasFilterServerSide = useSelector(checkInstanceFeature('filter_server_side'))
|
||||
if (notification.status) {
|
||||
if (hasFilterServerSide) {
|
||||
if (notification.status.filtered?.length) {
|
||||
filterResults = notification.status.filtered.map(filter => filter.filter)
|
||||
}
|
||||
} else {
|
||||
const checkFilter = shouldFilter({ queryKey, status: notification.status })
|
||||
if (checkFilter?.length) {
|
||||
filterResults = checkFilter
|
||||
}
|
||||
}
|
||||
|
||||
if (filterResults?.length && !filterRevealed) {
|
||||
return !filterResults.filter(result => result.filter_action === 'hide').length ? (
|
||||
<Pressable onPress={() => setFilterRevealed(!filterRevealed)}>
|
||||
<TimelineFiltered filterResults={filterResults} />
|
||||
</Pressable>
|
||||
) : null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<StatusContext.Provider
|
||||
value={{
|
||||
queryKey,
|
||||
status,
|
||||
ownAccount,
|
||||
spoilerHidden,
|
||||
copiableContent
|
||||
spoilerHidden
|
||||
}}
|
||||
>
|
||||
<ContextMenu.Root>
|
||||
|
@ -55,7 +55,7 @@ const TimelineRefresh: React.FC<Props> = ({
|
||||
firstPage?.links?.prev && {
|
||||
...(firstPage.links.prev.isOffset
|
||||
? { offset: firstPage.links.prev.id }
|
||||
: { max_id: firstPage.links.prev.id }),
|
||||
: { min_id: firstPage.links.prev.id }),
|
||||
// https://github.com/facebook/react-native/issues/25239#issuecomment-731100372
|
||||
limit: '3'
|
||||
},
|
||||
|
@ -2,6 +2,7 @@ import Icon from '@components/Icon'
|
||||
import { displayMessage } from '@components/Message'
|
||||
import CustomText from '@components/Text'
|
||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||
import { androidActionSheetStyles } from '@helpers/androidActionSheetStyles'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { StackNavigationProp } from '@react-navigation/stack'
|
||||
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||
@ -99,7 +100,8 @@ const TimelineActions: React.FC = () => {
|
||||
t('shared.actions.reblogged.options.unlisted'),
|
||||
t('common:buttons.cancel')
|
||||
],
|
||||
cancelButtonIndex: 2
|
||||
cancelButtonIndex: 2,
|
||||
...androidActionSheetStyles(colors)
|
||||
},
|
||||
(selectedIndex: number) => {
|
||||
switch (selectedIndex) {
|
||||
@ -263,11 +265,6 @@ const TimelineActions: React.FC = () => {
|
||||
}, [status.bookmarked])
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingLeft: highlighted ? 0 : StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<View style={{ flexDirection: 'row' }}>
|
||||
<Pressable
|
||||
{...(highlighted
|
||||
@ -320,7 +317,6 @@ const TimelineActions: React.FC = () => {
|
||||
children={childrenBookmark}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,6 @@ const TimelineAttachment = () => {
|
||||
preview_url: attachment.preview_url,
|
||||
url: attachment.url,
|
||||
remote_url: attachment.remote_url,
|
||||
blurhash: attachment.blurhash,
|
||||
width: attachment.meta?.original?.width,
|
||||
height: attachment.meta?.original?.height
|
||||
}
|
||||
@ -90,7 +89,6 @@ const TimelineAttachment = () => {
|
||||
preview_url: attachment.preview_url,
|
||||
url: attachment.url,
|
||||
remote_url: attachment.remote_url,
|
||||
blurhash: attachment.blurhash,
|
||||
width: attachment.meta?.original?.width,
|
||||
height: attachment.meta?.original?.height
|
||||
}
|
||||
|
@ -48,8 +48,7 @@ const TimelineAvatar: React.FC<Props> = ({ account }) => {
|
||||
style={{
|
||||
borderRadius: StyleConstants.Avatar.M,
|
||||
overflow: 'hidden',
|
||||
marginRight: StyleConstants.Spacing.S,
|
||||
marginLeft: isConversation ? StyleConstants.Avatar.M - StyleConstants.Avatar.XS : undefined
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
@ -145,6 +145,7 @@ const TimelineCard: React.FC = () => {
|
||||
/>
|
||||
) : null}
|
||||
<View style={{ flex: 1, padding: StyleConstants.Spacing.S }}>
|
||||
{status.card?.title.length ? (
|
||||
<CustomText
|
||||
fontStyle='S'
|
||||
numberOfLines={2}
|
||||
@ -155,9 +156,10 @@ const TimelineCard: React.FC = () => {
|
||||
fontWeight='Bold'
|
||||
testID='title'
|
||||
>
|
||||
{status.card?.title}
|
||||
{status.card.title}
|
||||
</CustomText>
|
||||
{status.card?.description ? (
|
||||
) : null}
|
||||
{status.card?.description.length ? (
|
||||
<CustomText
|
||||
fontStyle='S'
|
||||
numberOfLines={1}
|
||||
@ -170,9 +172,11 @@ const TimelineCard: React.FC = () => {
|
||||
{status.card.description}
|
||||
</CustomText>
|
||||
) : null}
|
||||
{status.card?.url.length ? (
|
||||
<CustomText fontStyle='S' numberOfLines={1} style={{ color: colors.secondary }}>
|
||||
{status.card?.url}
|
||||
{status.card.url}
|
||||
</CustomText>
|
||||
) : null}
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
@ -187,10 +191,6 @@ const TimelineCard: React.FC = () => {
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
minHeight:
|
||||
(isStatus && foundStatus) || (isAccount && foundAccount)
|
||||
? undefined
|
||||
: StyleConstants.Font.LineHeight.M * 5,
|
||||
marginTop: StyleConstants.Spacing.M,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderRadius: StyleConstants.Spacing.S,
|
||||
|
@ -1,8 +1,12 @@
|
||||
import { ParseHTML } from '@components/Parse'
|
||||
import CustomText from '@components/Text'
|
||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform } from 'react-native'
|
||||
import { Platform, StyleSheet, View } from 'react-native'
|
||||
import { Path, Svg } from 'react-native-svg'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { isRtlLang } from 'rtl-detect'
|
||||
import StatusContext from './Context'
|
||||
@ -16,6 +20,7 @@ const TimelineContent: React.FC<Props> = ({ notificationOwnToot = false, setSpoi
|
||||
const { status, highlighted, inThread, disableDetails } = useContext(StatusContext)
|
||||
if (!status || typeof status.content !== 'string' || !status.content.length) return null
|
||||
|
||||
const { colors } = useTheme()
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const instanceAccount = useSelector(getInstanceAccount, () => true)
|
||||
|
||||
@ -39,6 +44,11 @@ const TimelineContent: React.FC<Props> = ({ notificationOwnToot = false, setSpoi
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
{inThread ? (
|
||||
<CustomText fontStyle='S' style={{ textAlign: 'center', color: colors.secondary, paddingVertical: StyleConstants.Spacing.XS }}>
|
||||
{t('shared.content.expandHint')}
|
||||
</CustomText>
|
||||
) : null}
|
||||
<ParseHTML
|
||||
content={status.content}
|
||||
size={highlighted ? 'L' : 'M'}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { createContext } from 'react'
|
||||
|
||||
type ContextType = {
|
||||
export type HighlightedStatusContextType = {}
|
||||
|
||||
type StatusContextType = {
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
|
||||
@ -10,10 +12,8 @@ type ContextType = {
|
||||
reblogStatus?: Mastodon.Status // When it is a reblog, pass the root status
|
||||
ownAccount?: boolean
|
||||
spoilerHidden?: boolean
|
||||
copiableContent?: React.MutableRefObject<{
|
||||
content: string
|
||||
complete: boolean
|
||||
}>
|
||||
rawContent?: React.MutableRefObject<string[]> // When highlighted, for translate, edit history
|
||||
detectedLanguage?: React.MutableRefObject<string>
|
||||
|
||||
highlighted?: boolean
|
||||
inThread?: boolean
|
||||
@ -21,6 +21,6 @@ type ContextType = {
|
||||
disableOnPress?: boolean
|
||||
isConversation?: boolean
|
||||
}
|
||||
const StatusContext = createContext<ContextType>({} as ContextType)
|
||||
const StatusContext = createContext<StatusContextType>({} as StatusContextType)
|
||||
|
||||
export default StatusContext
|
||||
|
@ -11,7 +11,7 @@ import { StyleSheet, View } from 'react-native'
|
||||
import StatusContext from './Context'
|
||||
|
||||
const TimelineFeedback = () => {
|
||||
const { status, highlighted } = useContext(StatusContext)
|
||||
const { status, highlighted, detectedLanguage } = useContext(StatusContext)
|
||||
if (!status || !highlighted) return null
|
||||
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
@ -80,7 +80,12 @@ const TimelineFeedback = () => {
|
||||
accessibilityHint={t('shared.actionsUsers.history.accessibilityHint')}
|
||||
accessibilityRole='button'
|
||||
style={[styles.text, { marginRight: 0, color: colors.blue }]}
|
||||
onPress={() => navigation.push('Tab-Shared-History', { id: status.id })}
|
||||
onPress={() =>
|
||||
navigation.push('Tab-Shared-History', {
|
||||
id: status.id,
|
||||
detectedLanguage: detectedLanguage?.current || status.language || ''
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('shared.actionsUsers.history.text', {
|
||||
count: data.length - 1
|
||||
|
@ -1,19 +1,46 @@
|
||||
import CustomText from '@components/Text'
|
||||
import removeHTML from '@helpers/removeHTML'
|
||||
import { store } from '@root/store'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import { getInstance, getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { getInstance } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import htmlparser2 from 'htmlparser2-without-node-native'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { View } from 'react-native'
|
||||
|
||||
const TimelineFiltered = React.memo(
|
||||
({ phrase }: { phrase: string }) => {
|
||||
export interface FilteredProps {
|
||||
filterResults: { title: string; filter_action: Mastodon.Filter<'v2'>['filter_action'] }[]
|
||||
}
|
||||
|
||||
const TimelineFiltered: React.FC<FilteredProps> = ({ filterResults }) => {
|
||||
const { colors } = useTheme()
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
const main = () => {
|
||||
if (!filterResults?.length) {
|
||||
return <></>
|
||||
}
|
||||
switch (typeof filterResults[0]) {
|
||||
case 'string': // v1 filter
|
||||
return <>{t('shared.filtered.match', { context: 'v1', phrase: filterResults[0] })}</>
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
{t('shared.filtered.match', {
|
||||
context: 'v2',
|
||||
count: filterResults.length,
|
||||
filters: filterResults.map(result => result.title).join(t('common:separator'))
|
||||
})}
|
||||
<CustomText
|
||||
style={{ color: colors.blue }}
|
||||
children={`\n${t('shared.filtered.reveal')}`}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{ backgroundColor: colors.backgroundDefault }}>
|
||||
<CustomText
|
||||
@ -25,67 +52,47 @@ const TimelineFiltered = React.memo(
|
||||
paddingLeft: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
{t('shared.filtered', { phrase })}
|
||||
{main()}
|
||||
</CustomText>
|
||||
</View>
|
||||
)
|
||||
},
|
||||
() => true
|
||||
)
|
||||
}
|
||||
|
||||
export const shouldFilter = ({
|
||||
copiableContent,
|
||||
status,
|
||||
queryKey
|
||||
queryKey,
|
||||
status
|
||||
}: {
|
||||
copiableContent: React.MutableRefObject<{
|
||||
content: string
|
||||
complete: boolean
|
||||
}>
|
||||
status: Mastodon.Status
|
||||
queryKey: QueryKeyTimeline
|
||||
}): string | null => {
|
||||
status: Pick<Mastodon.Status, 'content' | 'spoiler_text'>
|
||||
}): FilteredProps['filterResults'] | undefined => {
|
||||
const page = queryKey[1]
|
||||
const instance = getInstance(store.getState())
|
||||
const ownAccount = getInstanceAccount(store.getState())?.id === status.account?.id
|
||||
|
||||
let shouldFilter: string | null = null
|
||||
let returnFilter: FilteredProps['filterResults'] | undefined
|
||||
|
||||
if (!ownAccount) {
|
||||
let rawContent = ''
|
||||
const parser = new htmlparser2.Parser({
|
||||
ontext: (text: string) => {
|
||||
if (!copiableContent.current.complete) {
|
||||
copiableContent.current.content = copiableContent.current.content + text
|
||||
}
|
||||
|
||||
rawContent = rawContent + text
|
||||
}
|
||||
})
|
||||
if (status.spoiler_text) {
|
||||
parser.write(status.spoiler_text)
|
||||
rawContent = rawContent + `\n\n`
|
||||
}
|
||||
parser.write(status.content)
|
||||
parser.end()
|
||||
|
||||
const checkFilter = (filter: Mastodon.Filter) => {
|
||||
const rawContentCombined = [
|
||||
removeHTML(status.content),
|
||||
status.spoiler_text ? removeHTML(status.spoiler_text) : ''
|
||||
]
|
||||
.filter(c => c.length)
|
||||
.join(`\n`)
|
||||
const checkFilter = (filter: Mastodon.Filter<'v1'>) => {
|
||||
const escapedPhrase = filter.phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string
|
||||
switch (filter.whole_word) {
|
||||
case true:
|
||||
if (new RegExp(`\\b${escapedPhrase}\\b`, 'i').test(rawContent)) {
|
||||
shouldFilter = filter.phrase
|
||||
if (new RegExp(`\\b${escapedPhrase}\\b`, 'i').test(rawContentCombined)) {
|
||||
returnFilter = [{ title: filter.phrase, filter_action: 'warn' }]
|
||||
}
|
||||
break
|
||||
case false:
|
||||
if (new RegExp(escapedPhrase, 'i').test(rawContent)) {
|
||||
shouldFilter = filter.phrase
|
||||
if (new RegExp(escapedPhrase, 'i').test(rawContentCombined)) {
|
||||
returnFilter = [{ title: filter.phrase, filter_action: 'warn' }]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
instance?.filters?.forEach(filter => {
|
||||
if (shouldFilter) {
|
||||
if (returnFilter) {
|
||||
return
|
||||
}
|
||||
if (filter.expires_at) {
|
||||
@ -100,30 +107,27 @@ export const shouldFilter = ({
|
||||
case 'List':
|
||||
case 'Account':
|
||||
if (filter.context.includes('home')) {
|
||||
checkFilter(filter)
|
||||
checkFilter(filter as Mastodon.Filter<'v1'>)
|
||||
}
|
||||
break
|
||||
case 'Notifications':
|
||||
if (filter.context.includes('notifications')) {
|
||||
checkFilter(filter)
|
||||
checkFilter(filter as Mastodon.Filter<'v1'>)
|
||||
}
|
||||
break
|
||||
case 'LocalPublic':
|
||||
if (filter.context.includes('public')) {
|
||||
checkFilter(filter)
|
||||
checkFilter(filter as Mastodon.Filter<'v1'>)
|
||||
}
|
||||
break
|
||||
case 'Toot':
|
||||
if (filter.context.includes('thread')) {
|
||||
checkFilter(filter)
|
||||
checkFilter(filter as Mastodon.Filter<'v1'>)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
copiableContent.current.complete = true
|
||||
}
|
||||
|
||||
return shouldFilter
|
||||
return returnFilter
|
||||
}
|
||||
|
||||
export default TimelineFiltered
|
||||
|
@ -10,7 +10,7 @@ import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||
import StatusContext from './Context'
|
||||
|
||||
const TimelineHeaderAndroid: React.FC = () => {
|
||||
const { queryKey, rootQueryKey, status, disableDetails, disableOnPress } =
|
||||
const { queryKey, rootQueryKey, status, disableDetails, disableOnPress, rawContent } =
|
||||
useContext(StatusContext)
|
||||
|
||||
if (Platform.OS !== 'android' || !status || disableDetails || disableOnPress) return null
|
||||
@ -21,7 +21,8 @@ const TimelineHeaderAndroid: React.FC = () => {
|
||||
const mShare = menuShare({
|
||||
visibility: status.visibility,
|
||||
type: 'status',
|
||||
url: status.url || status.uri
|
||||
url: status.url || status.uri,
|
||||
rawContent
|
||||
})
|
||||
const mAccount = menuAccount({
|
||||
type: 'status',
|
||||
|
@ -16,7 +16,7 @@ import HeaderSharedMuted from './HeaderShared/Muted'
|
||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||
|
||||
const TimelineHeaderDefault: React.FC = () => {
|
||||
const { queryKey, rootQueryKey, status, copiableContent, highlighted, disableDetails } =
|
||||
const { queryKey, rootQueryKey, status, highlighted, disableDetails, rawContent } =
|
||||
useContext(StatusContext)
|
||||
if (!status) return null
|
||||
|
||||
@ -28,7 +28,7 @@ const TimelineHeaderDefault: React.FC = () => {
|
||||
visibility: status.visibility,
|
||||
type: 'status',
|
||||
url: status.url || status.uri,
|
||||
copiableContent
|
||||
rawContent
|
||||
})
|
||||
const mAccount = menuAccount({
|
||||
type: 'status',
|
||||
|
@ -13,39 +13,25 @@ import { Circle } from 'react-native-animated-spinkit'
|
||||
import StatusContext from './Context'
|
||||
|
||||
const TimelineTranslate = () => {
|
||||
const { status, highlighted, copiableContent } = useContext(StatusContext)
|
||||
if (!status || !highlighted) return null
|
||||
const { status, highlighted, rawContent, detectedLanguage } = useContext(StatusContext)
|
||||
if (!status || !highlighted || !rawContent?.current.length) return null
|
||||
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { colors } = useTheme()
|
||||
|
||||
const backupTextProcessing = (): string[] => {
|
||||
const text = status.spoiler_text ? [status.spoiler_text, status.content] : [status.content]
|
||||
|
||||
for (const i in text) {
|
||||
for (const emoji of status.emojis) {
|
||||
text[i] = text[i].replaceAll(`:${emoji.shortcode}:`, ' ')
|
||||
}
|
||||
text[i] = text[i]
|
||||
.replace(/(<([^>]+)>)/gi, ' ')
|
||||
.replace(/@.*? /gi, ' ')
|
||||
.replace(/#.*? /gi, ' ')
|
||||
.replace(/http(s):\/\/.*? /gi, ' ')
|
||||
}
|
||||
return text
|
||||
}
|
||||
const text = copiableContent?.current.content
|
||||
? [copiableContent?.current.content]
|
||||
: backupTextProcessing()
|
||||
|
||||
const [detectedLanguage, setDetectedLanguage] = useState<{
|
||||
const [detected, setDetected] = useState<{
|
||||
language: string
|
||||
confidence: number
|
||||
}>({ language: status.language || '', confidence: 0 })
|
||||
useEffect(() => {
|
||||
const detect = async () => {
|
||||
const result = await detectLanguage(text.join('\n\n'))
|
||||
result && setDetectedLanguage(result)
|
||||
const result = await detectLanguage(rawContent.current.join('\n\n'))
|
||||
if (result) {
|
||||
setDetected(result)
|
||||
if (detectedLanguage) {
|
||||
detectedLanguage.current = result.language
|
||||
}
|
||||
}
|
||||
}
|
||||
detect()
|
||||
}, [])
|
||||
@ -57,18 +43,18 @@ const TimelineTranslate = () => {
|
||||
|
||||
const [enabled, setEnabled] = useState(false)
|
||||
const { refetch, data, isFetching, isSuccess, isError } = useTranslateQuery({
|
||||
source: detectedLanguage.language,
|
||||
source: detected.language,
|
||||
target: targetLanguage,
|
||||
text,
|
||||
text: rawContent.current,
|
||||
options: { enabled }
|
||||
})
|
||||
|
||||
const devView = () => {
|
||||
return __DEV__ ? (
|
||||
<CustomText fontStyle='S' style={{ color: colors.secondary }}>{` Source: ${
|
||||
detectedLanguage?.language
|
||||
detected?.language
|
||||
}; Confidence: ${
|
||||
detectedLanguage?.confidence.toString().slice(0, 5) || 'null'
|
||||
detected?.confidence.toString().slice(0, 5) || 'null'
|
||||
}; Target: ${targetLanguage}`}</CustomText>
|
||||
) : null
|
||||
}
|
||||
@ -78,13 +64,13 @@ const TimelineTranslate = () => {
|
||||
}
|
||||
if (
|
||||
Platform.OS === 'ios' &&
|
||||
Localization.locale.slice(0, 2).includes(detectedLanguage.language.slice(0, 2))
|
||||
Localization.locale.slice(0, 2).includes(detected.language.slice(0, 2))
|
||||
) {
|
||||
return devView()
|
||||
}
|
||||
if (
|
||||
Platform.OS === 'android' &&
|
||||
settingsLanguage?.slice(0, 2).includes(detectedLanguage.language.slice(0, 2))
|
||||
settingsLanguage?.slice(0, 2).includes(detected.language.slice(0, 2))
|
||||
) {
|
||||
return devView()
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform } from 'react-native'
|
||||
import { Alert, Platform } from 'react-native'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
@ -186,12 +186,22 @@ const menuAccount = ({
|
||||
key: 'account-block',
|
||||
item: {
|
||||
onSelect: () =>
|
||||
Alert.alert(t('account.block.alert.title', { username: account.username }), undefined, [
|
||||
{
|
||||
text: t('common:buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: () =>
|
||||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
id: account.id,
|
||||
payload: { property: 'block', currentValue: data?.blocking }
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.cancel')
|
||||
}
|
||||
]),
|
||||
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
|
||||
destructive: !data?.blocking,
|
||||
hidden: false
|
||||
@ -204,7 +214,15 @@ const menuAccount = ({
|
||||
{
|
||||
key: 'account-reports',
|
||||
item: {
|
||||
onSelect: () => {
|
||||
onSelect: () =>
|
||||
Alert.alert(
|
||||
t('account.reports.alert.title', { username: account.username }),
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
text: t('common:buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: () => {
|
||||
timelineMutation.mutate({
|
||||
type: 'updateAccountProperty',
|
||||
queryKey,
|
||||
@ -217,7 +235,13 @@ const menuAccount = ({
|
||||
id: account.id,
|
||||
payload: { property: 'block', currentValue: false }
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.cancel')
|
||||
}
|
||||
]
|
||||
),
|
||||
disabled: false,
|
||||
destructive: true,
|
||||
hidden: false
|
||||
|
@ -49,7 +49,7 @@ const menuInstance = ({
|
||||
t('instance.block.alert.message'),
|
||||
[
|
||||
{
|
||||
text: t('instance.block.alert.buttons.confirm'),
|
||||
text: t('common:buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: () => {
|
||||
mutation.mutate({
|
||||
|
@ -7,10 +7,7 @@ const menuShare = (
|
||||
params:
|
||||
| {
|
||||
visibility?: Mastodon.Status['visibility']
|
||||
copiableContent?: React.MutableRefObject<{
|
||||
content?: string | undefined
|
||||
complete: boolean
|
||||
}>
|
||||
rawContent?: React.MutableRefObject<string[]>
|
||||
type: 'status'
|
||||
url?: string
|
||||
}
|
||||
@ -48,17 +45,17 @@ const menuShare = (
|
||||
icon: 'square.and.arrow.up'
|
||||
})
|
||||
}
|
||||
if (params.type === 'status' && Platform.OS === 'ios')
|
||||
if (params.type === 'status')
|
||||
menus[0].push({
|
||||
key: 'copy',
|
||||
item: {
|
||||
onSelect: () => {
|
||||
Clipboard.setString(params.copiableContent?.current.content || '')
|
||||
Clipboard.setString(params.rawContent?.current.join(`\n\n`) || '')
|
||||
displayMessage({ type: 'success', message: t(`copy.succeed`) })
|
||||
},
|
||||
disabled: false,
|
||||
destructive: false,
|
||||
hidden: !params.copiableContent?.current.content?.length
|
||||
hidden: !params.rawContent?.current.length
|
||||
},
|
||||
title: t('copy.action'),
|
||||
icon: 'doc.on.doc'
|
||||
|
@ -109,7 +109,7 @@ const menuStatus = ({
|
||||
onSelect: () =>
|
||||
Alert.alert(t('status.deleteEdit.alert.title'), t('status.deleteEdit.alert.message'), [
|
||||
{
|
||||
text: t('status.deleteEdit.alert.buttons.confirm'),
|
||||
text: t('common:buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
let replyToStatus: Mastodon.Status | undefined = undefined
|
||||
@ -153,7 +153,7 @@ const menuStatus = ({
|
||||
onSelect: () =>
|
||||
Alert.alert(t('status.delete.alert.title'), t('status.delete.alert.message'), [
|
||||
{
|
||||
text: t('status.delete.alert.buttons.confirm'),
|
||||
text: t('common:buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
mutation.mutate({
|
||||
|
5
src/helpers/androidActionSheetStyles.ts
Normal file
5
src/helpers/androidActionSheetStyles.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const androidActionSheetStyles = (colors: any) => ({
|
||||
containerStyle: { backgroundColor: colors.backgroundDefault },
|
||||
textStyle: { color: colors.primaryDefault },
|
||||
titleTextStyle: { color: colors.secondary }
|
||||
})
|
@ -1,4 +1,8 @@
|
||||
[
|
||||
{
|
||||
"feature": "account_follow_notify",
|
||||
"version": 3.3
|
||||
},
|
||||
{
|
||||
"feature": "notification_type_status",
|
||||
"version": 3.3
|
||||
@ -38,5 +42,9 @@
|
||||
{
|
||||
"feature": "notification_type_admin_report",
|
||||
"version": 4.0
|
||||
},
|
||||
{
|
||||
"feature": "filter_server_side",
|
||||
"version": 4.0
|
||||
}
|
||||
]
|
@ -1,5 +1,21 @@
|
||||
import { QueryClient } from '@tanstack/react-query'
|
||||
|
||||
const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 1000 * 60 * 5 } } })
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 1000 * 60 * 5,
|
||||
retry: (failureCount, error: any) => {
|
||||
if (error?.status === 404) {
|
||||
return false
|
||||
}
|
||||
if (failureCount <= 3) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default queryClient
|
||||
|
@ -6,6 +6,9 @@ const removeHTML = (text: string): string => {
|
||||
const parser = new htmlparser2.Parser({
|
||||
ontext: (text: string) => {
|
||||
raw = raw + text
|
||||
},
|
||||
onclosetag: (tag: string) => {
|
||||
if (['p', 'br'].includes(tag)) raw = raw + `\n`
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -7,7 +7,8 @@
|
||||
"continue": "Continua",
|
||||
"create": "Crea",
|
||||
"delete": "Esborra",
|
||||
"done": "Fet"
|
||||
"done": "Fet",
|
||||
"confirm": "Confirma"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "Emoji personalitzat {{emoji}}"
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "Bloqueja l'usuari",
|
||||
"action_true": "Deixa de bloquejar l'usuari"
|
||||
"action_true": "Deixa de bloquejar l'usuari",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": "Denuncia i bloqueja l'usuari"
|
||||
"action": "Denuncia i bloqueja l'usuari",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -32,11 +38,8 @@
|
||||
"block": {
|
||||
"action": "Bloquejar la instància {{instance}}",
|
||||
"alert": {
|
||||
"title": "Confirma el bloqueig de la instància {{instance}}?",
|
||||
"message": "Pots silenciar o bloquejar a un usuari.\n\nDesprés de bloquejar una instància, tot el seu contingut, amb els seus seguidors, seran esborrats!",
|
||||
"buttons": {
|
||||
"confirm": "Confirma"
|
||||
}
|
||||
"title": "Vols bloquejar la instància {{instance}}?",
|
||||
"message": "Pots silenciar o bloquejar a un usuari.\n\nDesprés de bloquejar una instància, tot el seu contingut, amb els seus seguidors, seran esborrats!"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -56,21 +59,15 @@
|
||||
"delete": {
|
||||
"action": "Elimina la publicació",
|
||||
"alert": {
|
||||
"title": "Confirma l'eliminació?",
|
||||
"message": "Tots els impulsos i favorits s'esborraran, incloses totes les respostes.",
|
||||
"buttons": {
|
||||
"confirm": "Confirma"
|
||||
}
|
||||
"title": "Vols eliminar-ho?",
|
||||
"message": "Tots els impulsos i favorits s'esborraran, incloses totes les respostes."
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "Elimina la publicació i torna a publicar",
|
||||
"action": "Elimina i torna-ho a publicar",
|
||||
"alert": {
|
||||
"title": "Confirma l'eliminació i tornar a publicar?",
|
||||
"message": "Tots els impulsos i favorits s'esborraran, incloses totes les respostes.",
|
||||
"buttons": {
|
||||
"confirm": "Confirma"
|
||||
}
|
||||
"title": "Vols eliminar i tornar-ho a publicar?",
|
||||
"message": "Tots els impulsos i favorits s'esborraran, incloses totes les respostes."
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"server": {
|
||||
"textInput": {
|
||||
"placeholder": ""
|
||||
"placeholder": "Domini de la instància"
|
||||
},
|
||||
"whitelisted": "Pot ser una instància que estigui a la llista blanca de la qual tooot no pot obtenir les dades abans d'iniciar la sessió.",
|
||||
"button": "Inicia la sessió",
|
||||
"information": {
|
||||
"name": "Nom",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"title": "Selecciona origen multimèdia",
|
||||
"message": "Les dades multimèdia EXIF no s'han penjat",
|
||||
"message": "Les dades multimèdia EXIF no es penjen",
|
||||
"options": {
|
||||
"image": "Penja fotos",
|
||||
"image_max": "Penja fotos (màx. {{max}})",
|
||||
|
@ -31,7 +31,7 @@
|
||||
"notification": "{{name}} ha impulsat la teva publicació"
|
||||
},
|
||||
"update": "L'impuls ha sigut editat",
|
||||
"admin.sign_up": "",
|
||||
"admin.sign_up": "{{name}} s'ha unit a la instància",
|
||||
"admin.report": ""
|
||||
},
|
||||
"actions": {
|
||||
@ -55,7 +55,7 @@
|
||||
"accessibilityLabel": "Afegeix aquesta publicació a marcadors",
|
||||
"function": "Afegeix la publicació a marcadors"
|
||||
},
|
||||
"openReport": ""
|
||||
"openReport": "Obre la denúncia"
|
||||
},
|
||||
"actionsUsers": {
|
||||
"reblogged_by": {
|
||||
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": "Contingut ocult"
|
||||
},
|
||||
"filtered": "Filtrat: {{phrase}}.",
|
||||
"filtered": {
|
||||
"reveal": "Mostra-ho de totes maneres",
|
||||
"match_v1": "Filtrat: {{phrase}}.",
|
||||
"match_v2_one": "Filtrat per {{filters}}.",
|
||||
"match_v2_other": "Filtrat per {{count}} filtres, {{filters}}."
|
||||
},
|
||||
"fullConversation": "Llegeix conversacions",
|
||||
"translate": {
|
||||
"default": "Tradueix",
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "Protecció de la privacitat",
|
||||
"message": "Si us plau, no revelis la identitat d'altres usuaris, així com el nom d'usuari, avatar, etc. Gràcies!",
|
||||
"button": "Confirma"
|
||||
"message": "Si us plau, no revelis la identitat d'altres usuaris, així com el nom d'usuari, avatar, etc. Gràcies!"
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": "La sessió ha sigut expirada. Si us plau, torna a iniciar la sessió"
|
||||
|
@ -11,11 +11,11 @@
|
||||
},
|
||||
"right": {
|
||||
"button": {
|
||||
"default": "Publicació",
|
||||
"default": "Publica",
|
||||
"conversation": "Envia un missatge directe",
|
||||
"reply": "Resposta de la publicació",
|
||||
"reply": "Publica la resposta",
|
||||
"deleteEdit": "Publicació",
|
||||
"edit": "Publicació",
|
||||
"edit": "Publica l'edició",
|
||||
"share": "Publicació"
|
||||
},
|
||||
"alert": {
|
||||
|
@ -3,15 +3,15 @@
|
||||
"local": {
|
||||
"name": "Seguint",
|
||||
"options": {
|
||||
"showBoosts": "",
|
||||
"showReplies": ""
|
||||
"showBoosts": "Mostra les publicacions",
|
||||
"showReplies": "Mostra les respostes"
|
||||
}
|
||||
},
|
||||
"public": {
|
||||
"segments": {
|
||||
"federated": "Federat",
|
||||
"local": "Local",
|
||||
"trending": "En tendència"
|
||||
"trending": "Tendència"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
@ -31,13 +31,13 @@
|
||||
"title": "Mostra les notificacions",
|
||||
"options": {
|
||||
"follow": "$t(screenTabs:me.push.follow.heading)",
|
||||
"follow_request": "Sol·licitud de seguiment",
|
||||
"follow_request": "Sol·licituds de seguiment",
|
||||
"favourite": "$t(screenTabs:me.push.favourite.heading)",
|
||||
"reblog": "$t(screenTabs:me.push.reblog.heading)",
|
||||
"mention": "$t(screenTabs:me.push.mention.heading)",
|
||||
"poll": "$t(screenTabs:me.push.poll.heading)",
|
||||
"status": "Publicació d'usuaris subscrits",
|
||||
"update": "L'impuls ha sigut editat",
|
||||
"status": "Publicacions d'usuaris subscrits",
|
||||
"update": "Edicions d'impulsos",
|
||||
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
|
||||
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
|
||||
}
|
||||
@ -163,7 +163,7 @@
|
||||
"total_other": "{{count}} camps"
|
||||
},
|
||||
"visibility": {
|
||||
"title": "Visibilitat de la publicació",
|
||||
"title": "Visibilitat",
|
||||
"options": {
|
||||
"public": "Públic",
|
||||
"unlisted": "Sense llistar",
|
||||
@ -171,7 +171,7 @@
|
||||
}
|
||||
},
|
||||
"sensitive": {
|
||||
"title": "Publica contingut multimèdia sensible"
|
||||
"title": "Continguts multimèdia sensibles"
|
||||
},
|
||||
"lock": {
|
||||
"title": "Fes el compte privat",
|
||||
@ -211,28 +211,28 @@
|
||||
"heading": "Per defecte"
|
||||
},
|
||||
"follow": {
|
||||
"heading": "Nou seguidor"
|
||||
"heading": "Seguidors nous"
|
||||
},
|
||||
"follow_request": {
|
||||
"heading": "Sol·licitud de seguiment"
|
||||
"heading": "Sol·licituds de seguiment"
|
||||
},
|
||||
"favourite": {
|
||||
"heading": "Favorits"
|
||||
},
|
||||
"reblog": {
|
||||
"heading": "Impulsat"
|
||||
"heading": "Impulsos"
|
||||
},
|
||||
"mention": {
|
||||
"heading": "T'ha mencionat"
|
||||
"heading": "Mencions"
|
||||
},
|
||||
"poll": {
|
||||
"heading": "Actualització d'una votació"
|
||||
"heading": "Actualitzacions d'una votació"
|
||||
},
|
||||
"status": {
|
||||
"heading": "Publicació d'usuaris subscrits"
|
||||
"heading": "Publicacions d'usuaris subscrits"
|
||||
},
|
||||
"update": {
|
||||
"heading": "L'impuls ha sigut editat"
|
||||
"heading": "Edicions d'impulsos"
|
||||
},
|
||||
"admin.sign_up": {
|
||||
"heading": "Administració: Registra"
|
||||
@ -399,7 +399,7 @@
|
||||
"reblogged_by": "{{count}} impulsats",
|
||||
"favourited_by": "{{count}} favorits"
|
||||
},
|
||||
"resultIncomplete": ""
|
||||
"resultIncomplete": "Els resultats d'una instància remota són incomplets"
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,8 @@
|
||||
"continue": "",
|
||||
"create": "",
|
||||
"delete": "",
|
||||
"done": ""
|
||||
"done": "",
|
||||
"confirm": ""
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": ""
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "",
|
||||
"action_true": ""
|
||||
"action_true": "",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": ""
|
||||
"action": "",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "",
|
||||
"alert": {
|
||||
"title": "",
|
||||
"message": "",
|
||||
"buttons": {
|
||||
"confirm": ""
|
||||
}
|
||||
"message": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "",
|
||||
"alert": {
|
||||
"title": "",
|
||||
"message": "",
|
||||
"buttons": {
|
||||
"confirm": ""
|
||||
}
|
||||
"message": ""
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "",
|
||||
"alert": {
|
||||
"title": "",
|
||||
"message": "",
|
||||
"buttons": {
|
||||
"confirm": ""
|
||||
}
|
||||
"message": ""
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -3,6 +3,7 @@
|
||||
"textInput": {
|
||||
"placeholder": ""
|
||||
},
|
||||
"whitelisted": "",
|
||||
"button": "",
|
||||
"information": {
|
||||
"name": "",
|
||||
|
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": ""
|
||||
},
|
||||
"filtered": "",
|
||||
"filtered": {
|
||||
"reveal": "",
|
||||
"match_v1": "",
|
||||
"match_v2_one": "",
|
||||
"match_v2_other": ""
|
||||
},
|
||||
"fullConversation": "",
|
||||
"translate": {
|
||||
"default": "",
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "",
|
||||
"message": "",
|
||||
"button": ""
|
||||
"message": ""
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": ""
|
||||
|
@ -7,7 +7,8 @@
|
||||
"continue": "Weiter",
|
||||
"create": "Erstellen",
|
||||
"delete": "Löschen",
|
||||
"done": "Fertig"
|
||||
"done": "Fertig",
|
||||
"confirm": "Bestätigen"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "Eigenes Emoji {{emoji}}"
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "Nutzer blockieren",
|
||||
"action_true": "User entblocken"
|
||||
"action_true": "User entblocken",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": "Nutzer melden und blockieren"
|
||||
"action": "Nutzer melden und blockieren",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "Instanz {{instance}} blockieren",
|
||||
"alert": {
|
||||
"title": "{{instance}} wirklich blockieren?",
|
||||
"message": "Üblicherweise kannst du einen User stummschalten oder blockieren.\nBlockierst du hingegegen eine Instanz, wird deren gesamter Inhalt samt Usern, die dir von dieser Instanz folgen, entfernt!",
|
||||
"buttons": {
|
||||
"confirm": "Bestätigen"
|
||||
}
|
||||
"message": "Üblicherweise kannst du einen User stummschalten oder blockieren.\nBlockierst du hingegegen eine Instanz, wird deren gesamter Inhalt samt Usern, die dir von dieser Instanz folgen, entfernt!"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "Tröt löschen",
|
||||
"alert": {
|
||||
"title": "Löschen bestätigen?",
|
||||
"message": "Alle Boosts, Sterne und Antworten werden entfernt.",
|
||||
"buttons": {
|
||||
"confirm": "Bestätigen"
|
||||
}
|
||||
"message": "Alle Boosts, Sterne und Antworten werden entfernt."
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "Tröt neu entwerfen",
|
||||
"alert": {
|
||||
"title": "Beitrag wirklich entfernen?",
|
||||
"message": "Alle Boosts und Likes inklusive der Antworten werden gelöscht.",
|
||||
"buttons": {
|
||||
"confirm": "Bestätigen"
|
||||
}
|
||||
"message": "Alle Boosts und Likes inklusive der Antworten werden gelöscht."
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -3,6 +3,7 @@
|
||||
"textInput": {
|
||||
"placeholder": "Domain der Instanz"
|
||||
},
|
||||
"whitelisted": "",
|
||||
"button": "Login",
|
||||
"information": {
|
||||
"name": "Name",
|
||||
|
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": "Ausgeblendeter Inhalt"
|
||||
},
|
||||
"filtered": "Gefiltert: {{phrase}}.",
|
||||
"filtered": {
|
||||
"reveal": "Trotzdem anzeigen",
|
||||
"match_v1": "Gefiltert: {{phrase}}.",
|
||||
"match_v2_one": "Gefiltert durch {{filters}}.",
|
||||
"match_v2_other": "Gefiltert durch {{count}} Filter, {{filters}}."
|
||||
},
|
||||
"fullConversation": "Unterhaltung anzeigen",
|
||||
"translate": {
|
||||
"default": "Übersetzen",
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "Datenschutz",
|
||||
"message": "Bitte geben Sie nicht die Identität anderer Nutzer preis, wie z. B. Benutzername, Avatar, etc. Vielen Dank!",
|
||||
"button": "Bestätigen"
|
||||
"message": "Bitte geben Sie nicht die Identität anderer Nutzer preis, wie z. B. Benutzername, Avatar, etc. Vielen Dank!"
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": "Login abgelaufen, bitte erneut anmelden"
|
||||
|
@ -7,7 +7,8 @@
|
||||
"continue": "Continue",
|
||||
"create": "Create",
|
||||
"delete": "Delete",
|
||||
"done": "Done"
|
||||
"done": "Done",
|
||||
"confirm": "Confirm"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "Custom emoji {{emoji}}"
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "Block user",
|
||||
"action_true": "Unblock user"
|
||||
"action_true": "Unblock user",
|
||||
"alert": {
|
||||
"title": "Confirm blocking user @{{username}} ?"
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": "Report and block user"
|
||||
"action": "Report and block user",
|
||||
"alert": {
|
||||
"title": "Confirm report and blocking user @{{username}} ?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "Block instance {{instance}}",
|
||||
"alert": {
|
||||
"title": "Confirm blocking instance {{instance}} ?",
|
||||
"message": "Mostly you can mute or block certain user.\n\nAfter blocking instance, all its content including followers from this instance will be removed!",
|
||||
"buttons": {
|
||||
"confirm": "Confirm"
|
||||
}
|
||||
"message": "Mostly you can mute or block certain user.\n\nAfter blocking instance, all its content including followers from this instance will be removed!"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "Delete toot",
|
||||
"alert": {
|
||||
"title": "Confirm deleting?",
|
||||
"message": "All boosts and favourites will be cleared, including all replies.",
|
||||
"buttons": {
|
||||
"confirm": "Confirm"
|
||||
}
|
||||
"message": "All boosts and favourites will be cleared, including all replies."
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "Delete toot and repost",
|
||||
"alert": {
|
||||
"title": "Confirm deleting and repost?",
|
||||
"message": "All boosts and favourites will be cleared, including all replies.",
|
||||
"buttons": {
|
||||
"confirm": "Confirm"
|
||||
}
|
||||
"message": "All boosts and favourites will be cleared, including all replies."
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -3,6 +3,7 @@
|
||||
"textInput": {
|
||||
"placeholder": "Instance's domain"
|
||||
},
|
||||
"whitelisted": "This may be a whitelisted instance that tooot cannot retrieve data from before logging in.",
|
||||
"button": "Login",
|
||||
"information": {
|
||||
"name": "Name",
|
||||
|
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": "Hidden content"
|
||||
},
|
||||
"filtered": "Filtered: {{phrase}}.",
|
||||
"filtered": {
|
||||
"reveal": "Show anyway",
|
||||
"match_v1": "Filtered: {{phrase}}.",
|
||||
"match_v2_one": "Filtered by {{filters}}.",
|
||||
"match_v2_other": "Filtered by {{count}} filters, {{filters}}."
|
||||
},
|
||||
"fullConversation": "Read conversations",
|
||||
"translate": {
|
||||
"default": "Translate",
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "Privacy Protection",
|
||||
"message": "Please do not disclose other user's identity, such as username, avatar, etc. Thank you!",
|
||||
"button": "Confirm"
|
||||
"message": "Please do not disclose other user's identity, such as username, avatar, etc. Thank you!"
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": "Login expired, please login again"
|
||||
|
@ -7,7 +7,8 @@
|
||||
"continue": "Continuar",
|
||||
"create": "Crear",
|
||||
"delete": "Borrar",
|
||||
"done": "Hecho"
|
||||
"done": "Hecho",
|
||||
"confirm": "Confirmar"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "Emoji personalizado {{emoji}}"
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "Bloquear usuario",
|
||||
"action_true": "Desbloquear usuario"
|
||||
"action_true": "Desbloquear usuario",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": "Reportar y bloquear usuario"
|
||||
"action": "Reportar y bloquear usuario",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "Bloquear instancia {{instance}}",
|
||||
"alert": {
|
||||
"title": "¿Confirmar bloqueo de la instancia {{instance}}?",
|
||||
"message": "Puedes silenciar o bloquear a un usuario.\n\nTras bloquear una instancia, todo su contenido junto con sus seguidores se eliminarán.",
|
||||
"buttons": {
|
||||
"confirm": "Confirmar"
|
||||
}
|
||||
"message": "Puedes silenciar o bloquear a un usuario.\n\nTras bloquear una instancia, todo su contenido junto con sus seguidores se eliminarán."
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "Eliminar toot",
|
||||
"alert": {
|
||||
"title": "¿Confirmar eliminación?",
|
||||
"message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas.",
|
||||
"buttons": {
|
||||
"confirm": "Confirmar"
|
||||
}
|
||||
"message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas."
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "Eliminar toot y volver a publicar",
|
||||
"alert": {
|
||||
"title": "¿Confirmar eliminación y volver a publicar?",
|
||||
"message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas.",
|
||||
"buttons": {
|
||||
"confirm": "Confirmar"
|
||||
}
|
||||
"message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas."
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"server": {
|
||||
"textInput": {
|
||||
"placeholder": ""
|
||||
"placeholder": "Dominio de la instancia"
|
||||
},
|
||||
"whitelisted": "Puede ser que la instancia esté en una lista blanca por la que tooot no pueda obtener los datos antes de iniciar la sesión.",
|
||||
"button": "Iniciar sesión",
|
||||
"information": {
|
||||
"name": "Nombre",
|
||||
|
@ -31,7 +31,7 @@
|
||||
"notification": "{{name}} ha impulsado tu toot"
|
||||
},
|
||||
"update": "El impulso ha sido editado",
|
||||
"admin.sign_up": "",
|
||||
"admin.sign_up": "{{name}} se unió a la instancia",
|
||||
"admin.report": ""
|
||||
},
|
||||
"actions": {
|
||||
@ -55,7 +55,7 @@
|
||||
"accessibilityLabel": "Añadir este toot en marcadores",
|
||||
"function": "Añadir toot a marcadores"
|
||||
},
|
||||
"openReport": ""
|
||||
"openReport": "Abrir denuncia"
|
||||
},
|
||||
"actionsUsers": {
|
||||
"reblogged_by": {
|
||||
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": "Contenido oculto"
|
||||
},
|
||||
"filtered": "Filtrado: {{phrase}}.",
|
||||
"filtered": {
|
||||
"reveal": "Mostrar de todos modos",
|
||||
"match_v1": "Filtrado: {{phrase}}.",
|
||||
"match_v2_one": "Filtrado por {{filters}}.",
|
||||
"match_v2_other": "Filtrado por {{count}} filtros, {{filters}}."
|
||||
},
|
||||
"fullConversation": "Leer conversaciones",
|
||||
"translate": {
|
||||
"default": "Traducir",
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "Protección de la privacidad",
|
||||
"message": "Por favor, no revele la identidad de otros usuarios, como el nombre de usuario, avatar, etc. ¡Gracias!",
|
||||
"button": "Confirmar"
|
||||
"message": "Por favor, no revele la identidad de otros usuarios, como el nombre de usuario, avatar, etc. ¡Gracias!"
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": "La sesión se ha expirado. Por favor, vuelve a iniciar sesión"
|
||||
|
@ -15,7 +15,7 @@
|
||||
"conversation": "Mensaje privado",
|
||||
"reply": "Respuesta al toot",
|
||||
"deleteEdit": "Toot",
|
||||
"edit": "Toot",
|
||||
"edit": "Edita el toot",
|
||||
"share": "Toot"
|
||||
},
|
||||
"alert": {
|
||||
|
@ -399,7 +399,7 @@
|
||||
"reblogged_by": "{{count}} impulsados",
|
||||
"favourited_by": "{{count}} favoritos"
|
||||
},
|
||||
"resultIncomplete": ""
|
||||
"resultIncomplete": "Los resultados de una instancia remota están incompletos"
|
||||
}
|
||||
}
|
||||
}
|
@ -3,11 +3,12 @@
|
||||
"OK": "Ok",
|
||||
"apply": "Confirmer",
|
||||
"cancel": "Annuler",
|
||||
"discard": "Ne pas tenir compte",
|
||||
"discard": "Abandonner",
|
||||
"continue": "Continuer",
|
||||
"create": "",
|
||||
"delete": "",
|
||||
"done": ""
|
||||
"create": "Créer",
|
||||
"delete": "Supprimer",
|
||||
"done": "Fait",
|
||||
"confirm": "Confirmer"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "Émoji personnalisé {{emoji}}"
|
||||
@ -20,12 +21,12 @@
|
||||
"message": ""
|
||||
},
|
||||
"error": {
|
||||
"message": "Échec de la connexion, veuillez réessayer"
|
||||
"message": "{{function}} a échoué, veuillez réessayer"
|
||||
}
|
||||
},
|
||||
"separator": ", ",
|
||||
"discard": {
|
||||
"title": "Modifications non sauvegardées",
|
||||
"message": "Votre modification n'a pas été enregistrée. Voulez-vous annuler l'enregistrement des modifications ?"
|
||||
"message": "Votre modification n'a pas été enregistrée. Voulez-vous renoncer à enregistrer les modifications ?"
|
||||
}
|
||||
}
|
@ -4,24 +4,30 @@
|
||||
"title": "Actions de l'utilisateur",
|
||||
"following": {
|
||||
"action_false": "Suivre l'utilisateur",
|
||||
"action_true": ""
|
||||
"action_true": "Ne plus suivre l'utilisateur"
|
||||
},
|
||||
"inLists": "",
|
||||
"inLists": "Gérer l'utilisateur des listes",
|
||||
"mute": {
|
||||
"action_false": "Rendre muet l'utilisateur",
|
||||
"action_true": "Rendre la parole"
|
||||
},
|
||||
"block": {
|
||||
"action_false": "Bloquer l'utilisateur",
|
||||
"action_true": "Débloquer l'utilisateur"
|
||||
"action_true": "Débloquer l'utilisateur",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": ""
|
||||
"action": "Signaler et bloquer un utilisateur",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
"direct": "Message direct",
|
||||
"public": ""
|
||||
"public": "Message public"
|
||||
},
|
||||
"copy": {
|
||||
"action": "Copier le Pouet",
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "Bloquer l'instance {{instance}}",
|
||||
"alert": {
|
||||
"title": "Confirmer le blocage de l'instance {{instance}}?",
|
||||
"message": "Vous pouvez masquer ou bloquer certains utilisateurs.\n\nAprès avoir bloqué l'instance, tout son contenu, y compris les followers de cette instance, sera supprimé !",
|
||||
"buttons": {
|
||||
"confirm": "Confirmer"
|
||||
}
|
||||
"message": "Vous pouvez masquer ou bloquer certains utilisateurs.\n\nAprès avoir bloqué l'instance, tout son contenu, y compris les followers de cette instance, sera supprimé !"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "Supprimer le pouet",
|
||||
"alert": {
|
||||
"title": "Confirmer la suppression ?",
|
||||
"message": "Tous les boosts et favoris seront effacés, y compris toutes les réponses.",
|
||||
"buttons": {
|
||||
"confirm": "Confirmer"
|
||||
}
|
||||
"message": "Tous les boosts et favoris seront effacés, y compris toutes les réponses."
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "Supprimer le pouet et le republié",
|
||||
"alert": {
|
||||
"title": "Confirmer la suppression et le repost ?",
|
||||
"message": "Tous les boosts et favoris seront effacés, y compris toutes les réponses.",
|
||||
"buttons": {
|
||||
"confirm": "Confirmer"
|
||||
}
|
||||
"message": "Tous les boosts et favoris seront effacés, y compris toutes les réponses."
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"server": {
|
||||
"textInput": {
|
||||
"placeholder": ""
|
||||
"placeholder": "URL de l’instance"
|
||||
},
|
||||
"whitelisted": "Il peut s’agir d’une instance sur liste blanche à partir de laquelle tooot ne peut pas récupérer de données avant de se connecter.",
|
||||
"button": "Connexion",
|
||||
"information": {
|
||||
"name": "Nom",
|
||||
@ -20,7 +21,7 @@
|
||||
"update": {
|
||||
"alert": {
|
||||
"title": "Connecté à cette instance",
|
||||
"message": "Vous pouvez vous connecter à un autre compte, en maintenant un compte connecté existant"
|
||||
"message": "Vous pouvez vous connecter à un autre compte, en conservant le compte connecté existant"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"HTML": {
|
||||
"accessibilityHint": "Appuyez pour agrandir ou réduire le contenu",
|
||||
"accessibilityHint": "Touchez pour développer ou réduire le contenu",
|
||||
"expanded": "{{hint}}{{moreLines}}",
|
||||
"moreLines": " ({{count}} lignes en plus)",
|
||||
"defaultHint": "Pouet long"
|
||||
|
@ -5,7 +5,7 @@
|
||||
"button": "Réessayer"
|
||||
},
|
||||
"success": {
|
||||
"message": "La chronologie est vide"
|
||||
"message": "Le fil est vide"
|
||||
}
|
||||
},
|
||||
"end": {
|
||||
@ -31,8 +31,8 @@
|
||||
"notification": "{{name}} a partagé votre message"
|
||||
},
|
||||
"update": "Le reblog a été modifié",
|
||||
"admin.sign_up": "",
|
||||
"admin.report": ""
|
||||
"admin.sign_up": "{{name}} a rejoint l'instance",
|
||||
"admin.report": "{{name}} a signalé:"
|
||||
},
|
||||
"actions": {
|
||||
"reply": {
|
||||
@ -40,7 +40,7 @@
|
||||
},
|
||||
"reblogged": {
|
||||
"accessibilityLabel": "Partager ce pouet",
|
||||
"function": "Pouet de Boost",
|
||||
"function": "Pouet de boost",
|
||||
"options": {
|
||||
"title": "Choisir la visibilité du boost",
|
||||
"public": "Boost public",
|
||||
@ -49,13 +49,13 @@
|
||||
},
|
||||
"favourited": {
|
||||
"accessibilityLabel": "Ajouter ce pouet aux favoris",
|
||||
"function": "Mettre le pouet en favori"
|
||||
"function": "Pouet en favoris"
|
||||
},
|
||||
"bookmarked": {
|
||||
"accessibilityLabel": "Ajouter ce pouet aux signets",
|
||||
"function": "Pouet de signet"
|
||||
"accessibilityLabel": "Ajouter ce pouet aux marque-pages",
|
||||
"function": "Pouet en marque-pages"
|
||||
},
|
||||
"openReport": ""
|
||||
"openReport": "Ouvrir un rapport"
|
||||
},
|
||||
"actionsUsers": {
|
||||
"reblogged_by": {
|
||||
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": "Contenu masqué"
|
||||
},
|
||||
"filtered": "Filtré: {{phrase}}.",
|
||||
"filtered": {
|
||||
"reveal": "Montrer quand même",
|
||||
"match_v1": "Filtré : {{phrase}}.",
|
||||
"match_v2_one": "Filtré par {{filters}}.",
|
||||
"match_v2_other": "Filtré par {{count}} filtres, {{filters}}."
|
||||
},
|
||||
"fullConversation": "Conversations lues",
|
||||
"translate": {
|
||||
"default": "Traduire",
|
||||
@ -104,7 +109,7 @@
|
||||
"shared": {
|
||||
"account": {
|
||||
"name": {
|
||||
"accessibilityHint": "Nom de l'utilisateur"
|
||||
"accessibilityHint": "Nom d'affichage de l'utilisateur"
|
||||
},
|
||||
"account": {
|
||||
"accessibilityHint": "Compte de l'utilisateur"
|
||||
@ -119,7 +124,7 @@
|
||||
},
|
||||
"visibility": {
|
||||
"direct": {
|
||||
"accessibilityLabel": "Envoyer un message direct"
|
||||
"accessibilityLabel": "Envoyer un message privé"
|
||||
},
|
||||
"private": {
|
||||
"accessibilityLabel": "Visible uniquement pour les abonné·e·s"
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "Protection de la confidentialité",
|
||||
"message": "Veuillez ne pas divulguer l'identité d'un autre utilisateur, tel que le nom d'utilisateur, l'avatar, etc. Merci!",
|
||||
"button": "Confirmer"
|
||||
"message": "Veuillez ne pas divulguer l'identité d'un autre utilisateur, tel que le nom d'utilisateur, l'avatar, etc. Merci!"
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": "Session expirée, veuillez ré-essayer"
|
||||
|
@ -2,7 +2,7 @@
|
||||
"heading": {
|
||||
"left": {
|
||||
"alert": {
|
||||
"title": "Annuler l’édition?",
|
||||
"title": "Annuler l’édition ?",
|
||||
"buttons": {
|
||||
"save": "Enregistrer comme brouillon",
|
||||
"delete": "Supprimer le brouillon"
|
||||
@ -12,7 +12,7 @@
|
||||
"right": {
|
||||
"button": {
|
||||
"default": "Pouet",
|
||||
"conversation": "Pouet DM",
|
||||
"conversation": "Pouet MP",
|
||||
"reply": "Réponse de pouet",
|
||||
"deleteEdit": "Pouet",
|
||||
"edit": "Pouet",
|
||||
@ -24,8 +24,8 @@
|
||||
"button": "Réessayer"
|
||||
},
|
||||
"removeReply": {
|
||||
"title": "Le pouet répondu est introuvable",
|
||||
"description": "Le pouet répondu a peut-être été supprimé. Voulez-vous le supprimer de votre référence ?",
|
||||
"title": "Le pouet réponse est introuvable",
|
||||
"description": "Le pouet réponse a peut-être été supprimé. Voulez-vous le supprimer de votre référence ?",
|
||||
"confirm": "Supprimer la référence"
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@
|
||||
}
|
||||
},
|
||||
"emojis": {
|
||||
"accessibilityHint": "Tapotez pour ajouter des émojis au pouet"
|
||||
"accessibilityHint": "Appuyez pour ajouter des émojis au pouet"
|
||||
},
|
||||
"poll": {
|
||||
"option": {
|
||||
@ -90,7 +90,7 @@
|
||||
}
|
||||
},
|
||||
"expiration": {
|
||||
"heading": "Validité",
|
||||
"heading": "Durée",
|
||||
"options": {
|
||||
"300": "5 minutes",
|
||||
"1800": "30 minutes",
|
||||
@ -106,7 +106,7 @@
|
||||
"actions": {
|
||||
"attachment": {
|
||||
"accessibilityLabel": "Téléchargez une pièce-jointe",
|
||||
"accessibilityHint": "La fonction de sondage sera désactivée lorsqu'il y a une pièce jointe",
|
||||
"accessibilityHint": "La fonction de sondage sera désactivée s'il y a une pièce jointe",
|
||||
"failed": {
|
||||
"alert": {
|
||||
"title": "Le téléchargement a échoué",
|
||||
|
@ -3,15 +3,15 @@
|
||||
"local": {
|
||||
"name": "Suit",
|
||||
"options": {
|
||||
"showBoosts": "",
|
||||
"showReplies": ""
|
||||
"showBoosts": "Afficher les boosts",
|
||||
"showReplies": "Afficher les réponses"
|
||||
}
|
||||
},
|
||||
"public": {
|
||||
"segments": {
|
||||
"federated": "Fédéré",
|
||||
"local": "Local",
|
||||
"trending": ""
|
||||
"trending": "Tendance"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
@ -28,7 +28,7 @@
|
||||
"filters": {
|
||||
"accessibilityLabel": "Filtrer",
|
||||
"accessibilityHint": "Filtrer les types de notifications affichés",
|
||||
"title": "",
|
||||
"title": "Afficher les notifications",
|
||||
"options": {
|
||||
"follow": "$t(screenTabs:me.push.follow.heading)",
|
||||
"follow_request": "Demande d'abonnement",
|
||||
@ -55,7 +55,7 @@
|
||||
"name": "Favoris"
|
||||
},
|
||||
"followedTags": {
|
||||
"name": ""
|
||||
"name": "Hashtags suivis"
|
||||
},
|
||||
"fontSize": {
|
||||
"name": "Taille de la police de Pouet"
|
||||
@ -67,25 +67,25 @@
|
||||
"name": "Liste : {{list}}"
|
||||
},
|
||||
"listAccounts": {
|
||||
"name": ""
|
||||
"name": "Utilisateurs dans la liste : {{list}}"
|
||||
},
|
||||
"listAdd": {
|
||||
"name": ""
|
||||
"name": "Créer une liste"
|
||||
},
|
||||
"listEdit": {
|
||||
"name": ""
|
||||
"name": "Modifier les détails de la liste"
|
||||
},
|
||||
"lists": {
|
||||
"name": "Listes"
|
||||
},
|
||||
"push": {
|
||||
"name": "Push de Notification"
|
||||
"name": "Push de notification"
|
||||
},
|
||||
"profile": {
|
||||
"name": "Modifier le profil"
|
||||
},
|
||||
"profileName": {
|
||||
"name": "Editer le nom d'affichage"
|
||||
"name": "Éditer le nom d'affichage"
|
||||
},
|
||||
"profileNote": {
|
||||
"name": "Éditer la description"
|
||||
@ -114,33 +114,33 @@
|
||||
}
|
||||
},
|
||||
"listAccounts": {
|
||||
"heading": "",
|
||||
"error": "",
|
||||
"empty": ""
|
||||
"heading": "Gérer les utilisateurs",
|
||||
"error": "Supprimer l'utilisateur de la liste",
|
||||
"empty": "Aucun utilisateur ajouté dans cette liste"
|
||||
},
|
||||
"listEdit": {
|
||||
"heading": "",
|
||||
"title": "",
|
||||
"heading": "Modifier les détails de la liste",
|
||||
"title": "Titre",
|
||||
"repliesPolicy": {
|
||||
"heading": "",
|
||||
"heading": "Montrer les réponses à :",
|
||||
"options": {
|
||||
"none": "",
|
||||
"list": "",
|
||||
"followed": ""
|
||||
"none": "Personne",
|
||||
"list": "Un membre de la liste",
|
||||
"followed": "N'importe quel utilisateur suivi"
|
||||
}
|
||||
}
|
||||
},
|
||||
"listDelete": {
|
||||
"heading": "",
|
||||
"heading": "Supprimer la liste",
|
||||
"confirm": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Supprimer la liste \"{{list}}\" ?",
|
||||
"message": "Cette action ne peut être annulé."
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
"feedback": {
|
||||
"succeed": "{{type}} mis à jour",
|
||||
"failed": "{{type}} Échec de la mise à jour, veuillez ré-essayer"
|
||||
"failed": "La mise à jour de {{type}} a échoué, veuillez réessayer"
|
||||
},
|
||||
"root": {
|
||||
"name": {
|
||||
@ -178,13 +178,13 @@
|
||||
"description": "Nécessite que vous approuviez manuellement chaque abonné·e"
|
||||
},
|
||||
"bot": {
|
||||
"title": "Compte Bot",
|
||||
"title": "Compte bot",
|
||||
"description": "Ce compte effectue principalement des actions automatisées et peut ne pas être surveillé"
|
||||
}
|
||||
},
|
||||
"fields": {
|
||||
"group": "Groupe {{index}}",
|
||||
"label": "Étiquette",
|
||||
"label": "Libellé",
|
||||
"content": "Contenu"
|
||||
},
|
||||
"mediaSelectionFailed": "Le traitement de l'image a échoué. Veuillez réessayer."
|
||||
@ -196,8 +196,8 @@
|
||||
"settings": "Activer dans les paramètres"
|
||||
},
|
||||
"missingServerKey": {
|
||||
"message": "",
|
||||
"description": ""
|
||||
"message": "Le serveur n'est pas configuré pour le push",
|
||||
"description": "Veuillez contacter l'administrateur de votre serveur pour configurer le support push"
|
||||
},
|
||||
"global": {
|
||||
"heading": "Activer pour {{acct}}",
|
||||
@ -205,13 +205,13 @@
|
||||
},
|
||||
"decode": {
|
||||
"heading": "Détails du message",
|
||||
"description": "Les messages acheminés par le serveur de tooot sont chiffrés, mais vous pouvez choisir de décoder le message sur le serveur. Le code source de notre serveur est open source et aucune politique de log."
|
||||
"description": "Les messages acheminés par le serveur de tooot sont chiffrés, mais vous pouvez choisir de décoder le message sur le serveur. Le code source de notre serveur est open source et il n'y a aucune politique de log."
|
||||
},
|
||||
"default": {
|
||||
"heading": "Par défaut"
|
||||
},
|
||||
"follow": {
|
||||
"heading": "Nouvel abonné"
|
||||
"heading": "Nouveau⋅elle abonné⋅e"
|
||||
},
|
||||
"follow_request": {
|
||||
"heading": "Demande d'abonnement"
|
||||
@ -229,16 +229,16 @@
|
||||
"heading": "Mise à jour du sondage"
|
||||
},
|
||||
"status": {
|
||||
"heading": "Pouet des utilisateurs inscrits"
|
||||
"heading": "Pouet des utilisateurs auxquels vous êtes abonnés"
|
||||
},
|
||||
"update": {
|
||||
"heading": ""
|
||||
"heading": "Le reblog a été édité"
|
||||
},
|
||||
"admin.sign_up": {
|
||||
"heading": ""
|
||||
"heading": "Admin : s'inscrire"
|
||||
},
|
||||
"admin.report": {
|
||||
"heading": ""
|
||||
"heading": "Admin : signalement"
|
||||
},
|
||||
"howitworks": "Apprenez comment cela fonctionne"
|
||||
},
|
||||
@ -261,7 +261,7 @@
|
||||
"button": "Se déconnecter",
|
||||
"alert": {
|
||||
"title": "Déconnexion?",
|
||||
"message": "Après vous être déconnecté, vous devez vous reconnecter",
|
||||
"message": "Après vous être déconnecté, vous devrez vous reconnecter",
|
||||
"buttons": {
|
||||
"logout": "Déconnexion"
|
||||
}
|
||||
@ -317,7 +317,7 @@
|
||||
"contact": {
|
||||
"heading": "Contacter tooot"
|
||||
},
|
||||
"version": "Version {{version}}",
|
||||
"version": "Version v{{version}}",
|
||||
"instanceVersion": "Version de Mastodon v{{version}}"
|
||||
},
|
||||
"switch": {
|
||||
@ -346,12 +346,12 @@
|
||||
"suspended": "Compte suspendu par les modérateurs de votre serveur"
|
||||
},
|
||||
"accountInLists": {
|
||||
"name": "",
|
||||
"inLists": "",
|
||||
"notInLists": ""
|
||||
"name": "Listes de @{{username}}",
|
||||
"inLists": "Dans les listes",
|
||||
"notInLists": "Autres listes"
|
||||
},
|
||||
"attachments": {
|
||||
"name": ""
|
||||
"name": "Média de <0 /><1></1>"
|
||||
},
|
||||
"hashtag": {
|
||||
"follow": "Suivre",
|
||||
@ -377,7 +377,7 @@
|
||||
}
|
||||
},
|
||||
"trending": {
|
||||
"tags": ""
|
||||
"tags": "Tags tendance"
|
||||
}
|
||||
},
|
||||
"sections": {
|
||||
@ -399,7 +399,7 @@
|
||||
"reblogged_by": "{{count}} boosté",
|
||||
"favourited_by": "{{count}} mis en favori"
|
||||
},
|
||||
"resultIncomplete": ""
|
||||
"resultIncomplete": "Les résultats d'une instance distante sont incomplets"
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,8 @@
|
||||
"continue": "Continua",
|
||||
"create": "",
|
||||
"delete": "",
|
||||
"done": ""
|
||||
"done": "",
|
||||
"confirm": "Ho capito"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "Emoji personalizzata {{emoji}}"
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "Blocca utente",
|
||||
"action_true": "Sblocca utente"
|
||||
"action_true": "Sblocca utente",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": ""
|
||||
"action": "",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "Blocca istanza {{instance}}",
|
||||
"alert": {
|
||||
"title": "Confermi di voler bloccare l'istanza {{instance}}?",
|
||||
"message": "Sarebbe meglio mutare o bloccare singoli utenti.\n\nSe blocchi un'istanza, tutti i suoi contenuti a te relativi, inclusi tutti i tuoi seguaci da questa, saranno rimossi.",
|
||||
"buttons": {
|
||||
"confirm": "Ho capito"
|
||||
}
|
||||
"message": "Sarebbe meglio mutare o bloccare singoli utenti.\n\nSe blocchi un'istanza, tutti i suoi contenuti a te relativi, inclusi tutti i tuoi seguaci da questa, saranno rimossi."
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "Cancella toot",
|
||||
"alert": {
|
||||
"title": "Conferma?",
|
||||
"message": "Tutti i retoot, gli apprezzamenti, e le risposte, saranno cancellati.",
|
||||
"buttons": {
|
||||
"confirm": "Ho capito"
|
||||
}
|
||||
"message": "Tutti i retoot, gli apprezzamenti, e le risposte, saranno cancellati."
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "Cancella e ripubblica toot",
|
||||
"alert": {
|
||||
"title": "Confermi cancellazione e ripubblicazione?",
|
||||
"message": "Tutti i retoot, gli apprezzamenti, e le risposte, saranno cancellati.",
|
||||
"buttons": {
|
||||
"confirm": "Ho capito"
|
||||
}
|
||||
"message": "Tutti i retoot, gli apprezzamenti, e le risposte, saranno cancellati."
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -3,6 +3,7 @@
|
||||
"textInput": {
|
||||
"placeholder": ""
|
||||
},
|
||||
"whitelisted": "",
|
||||
"button": "Accedi",
|
||||
"information": {
|
||||
"name": "Nome",
|
||||
|
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": "Contenuto nascosto"
|
||||
},
|
||||
"filtered": "Filtrato: {{phrase}}.",
|
||||
"filtered": {
|
||||
"reveal": "",
|
||||
"match_v1": "",
|
||||
"match_v2_one": "",
|
||||
"match_v2_other": ""
|
||||
},
|
||||
"fullConversation": "Leggi la conversazione",
|
||||
"translate": {
|
||||
"default": "Traduci",
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "Tutela della privacy",
|
||||
"message": "Per favore, non rivelare l'identità degli altri utenti (nome, foto profilo, ecc..). Grazie!",
|
||||
"button": "Ho capito"
|
||||
"message": "Per favore, non rivelare l'identità degli altri utenti (nome, foto profilo, ecc..). Grazie!"
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": "La sessione è scaduta, devi riaccedere"
|
||||
|
@ -5,9 +5,10 @@
|
||||
"cancel": "キャンセル",
|
||||
"discard": "変更を破棄",
|
||||
"continue": "続ける",
|
||||
"create": "",
|
||||
"create": "作成",
|
||||
"delete": "削除",
|
||||
"done": "完了"
|
||||
"done": "完了",
|
||||
"confirm": "確認"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "カスタム絵文字 {{emoji}}"
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "ユーザーをブロック",
|
||||
"action_true": "ユーザーのブロックを解除"
|
||||
"action_true": "ユーザーのブロックを解除",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": "ユーザーの報告とブロック"
|
||||
"action": "ユーザーの報告とブロック",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "インスタンスをブロック {{instance}}",
|
||||
"alert": {
|
||||
"title": "インスタンス {{instance}} をブロックしますか?",
|
||||
"message": "ほとんどの場合、特定のユーザーをミュートまたはブロックすることができます。\n\nインスタンスをブロックすると、このインスタンスからフォロワーを含むすべてのコンテンツが削除されます!",
|
||||
"buttons": {
|
||||
"confirm": "確定"
|
||||
}
|
||||
"message": "ほとんどの場合、特定のユーザーをミュートまたはブロックすることができます。\n\nインスタンスをブロックすると、このインスタンスからフォロワーを含むすべてのコンテンツが削除されます!"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "トゥートを削除",
|
||||
"alert": {
|
||||
"title": "削除しますか?",
|
||||
"message": "この投稿へのすべてのお気に入り登録やブーストは消去され、すべての返信は孤立することになります。",
|
||||
"buttons": {
|
||||
"confirm": "確定"
|
||||
}
|
||||
"message": "この投稿へのすべてのお気に入り登録やブーストは消去され、すべての返信は孤立することになります。"
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "トゥートを削除し、再投稿する",
|
||||
"alert": {
|
||||
"title": "削除して再投稿しますか?",
|
||||
"message": "この投稿へのすべてのお気に入り登録やブーストは消去され、すべての返信は孤立することになります。",
|
||||
"buttons": {
|
||||
"confirm": "確定"
|
||||
}
|
||||
"message": "この投稿へのすべてのお気に入り登録やブーストは消去され、すべての返信は孤立することになります。"
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -3,6 +3,7 @@
|
||||
"textInput": {
|
||||
"placeholder": "インスタンスのドメイン"
|
||||
},
|
||||
"whitelisted": "",
|
||||
"button": "ログイン",
|
||||
"information": {
|
||||
"name": "名前",
|
||||
|
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": "内容を非表示にする"
|
||||
},
|
||||
"filtered": "フィルター: {{phrase}}.",
|
||||
"filtered": {
|
||||
"reveal": "",
|
||||
"match_v1": "",
|
||||
"match_v2_one": "",
|
||||
"match_v2_other": ""
|
||||
},
|
||||
"fullConversation": "スレッドを読む",
|
||||
"translate": {
|
||||
"default": "翻訳",
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "プライバシー保護",
|
||||
"message": "ユーザー名やアバターなど、他のユーザーを特定する情報は公開しないでください。",
|
||||
"button": "確認"
|
||||
"message": "ユーザー名やアバターなど、他のユーザーを特定する情報は公開しないでください。"
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": "ログインの有効期限が切れました。もう一度ログインしてください。"
|
||||
|
@ -5,9 +5,10 @@
|
||||
"cancel": "취소",
|
||||
"discard": "취소",
|
||||
"continue": "계속",
|
||||
"create": "",
|
||||
"create": "생성",
|
||||
"delete": "삭제",
|
||||
"done": "완료"
|
||||
"done": "완료",
|
||||
"confirm": "확인"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "커스텀 에모지 {{emoji}}"
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "사용자 차단",
|
||||
"action_true": "사용자 차단 해제"
|
||||
"action_true": "사용자 차단 해제",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": "사용자 신고 및 차단"
|
||||
"action": "사용자 신고 및 차단",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "{{instance}} 인스턴스 차단",
|
||||
"alert": {
|
||||
"title": "정말 {{instance}} 인스턴스를 차단할까요?",
|
||||
"message": "보통은 사용자 뮤트나 차단으로 충분해요.\n\n인스턴스를 차단하면, 팔로워를 포함한 인스턴스의 모든 콘텐츠가 삭제됩니다!",
|
||||
"buttons": {
|
||||
"confirm": "확인"
|
||||
}
|
||||
"message": "보통은 사용자 뮤트나 차단으로 충분해요.\n\n인스턴스를 차단하면, 팔로워를 포함한 인스턴스의 모든 콘텐츠가 삭제됩니다!"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "툿 삭제",
|
||||
"alert": {
|
||||
"title": "정말 삭제할까요?",
|
||||
"message": "답장을 포함한 모든 부스트와 즐겨찾기가 지워져요.",
|
||||
"buttons": {
|
||||
"confirm": "확인"
|
||||
}
|
||||
"message": "답장을 포함한 모든 부스트와 즐겨찾기가 지워져요."
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "툿 삭제 후 다시 게시",
|
||||
"alert": {
|
||||
"title": "툿을 정말 삭제하고 다시 게시할까요?",
|
||||
"message": "답장을 포함한 모든 부스트와 즐겨찾기가 지워져요.",
|
||||
"buttons": {
|
||||
"confirm": "확인"
|
||||
}
|
||||
"message": "답장을 포함한 모든 부스트와 즐겨찾기가 지워져요."
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"server": {
|
||||
"textInput": {
|
||||
"placeholder": ""
|
||||
"placeholder": "인스턴스 도메인"
|
||||
},
|
||||
"whitelisted": "화이트리스트 등록이 필요한 인스턴스에서 tooot이 정보를 읽어올 수 없는 문제일 수 있습니다.",
|
||||
"button": "로그인",
|
||||
"information": {
|
||||
"name": "이름",
|
||||
|
@ -31,8 +31,8 @@
|
||||
"notification": "{{name}} 님이 내 툿을 부스트했어요"
|
||||
},
|
||||
"update": "부스트한 툿이 수정됨",
|
||||
"admin.sign_up": "",
|
||||
"admin.report": ""
|
||||
"admin.sign_up": "{{name}} 님이 인스턴스에 가입함",
|
||||
"admin.report": "{{name}} 님의 신고:"
|
||||
},
|
||||
"actions": {
|
||||
"reply": {
|
||||
@ -55,7 +55,7 @@
|
||||
"accessibilityLabel": "툿 북마크에 추가",
|
||||
"function": "툿 북마크"
|
||||
},
|
||||
"openReport": ""
|
||||
"openReport": "신고 열기"
|
||||
},
|
||||
"actionsUsers": {
|
||||
"reblogged_by": {
|
||||
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": "숨겨진 콘텐츠"
|
||||
},
|
||||
"filtered": "필터: {{phrase}}.",
|
||||
"filtered": {
|
||||
"reveal": "무시하고 보기",
|
||||
"match_v1": "필터됨: {{phrase}}.",
|
||||
"match_v2_one": "{{filters}}에 의해 필터됨.",
|
||||
"match_v2_other": "{{count}}개의 필터 {{filters}}에 의해 필터됨."
|
||||
},
|
||||
"fullConversation": "대화 보기",
|
||||
"translate": {
|
||||
"default": "번역",
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "개인정보 보호",
|
||||
"message": "다른 사용자의 이름이나, 프로필 사진 등의 정보를 유출하지 말아주세요. 고마워요!",
|
||||
"button": "확인"
|
||||
"message": "다른 사용자의 이름이나, 프로필 사진 등의 정보를 유출하지 말아주세요. 고마워요!"
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": "로그인이 만료되었어요. 다시 로그인해주세요"
|
||||
|
@ -3,15 +3,15 @@
|
||||
"local": {
|
||||
"name": "팔로우 중",
|
||||
"options": {
|
||||
"showBoosts": "",
|
||||
"showReplies": ""
|
||||
"showBoosts": "부스트 표시",
|
||||
"showReplies": "답글 표시"
|
||||
}
|
||||
},
|
||||
"public": {
|
||||
"segments": {
|
||||
"federated": "연합",
|
||||
"local": "로컬",
|
||||
"trending": ""
|
||||
"trending": "유행"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
@ -28,7 +28,7 @@
|
||||
"filters": {
|
||||
"accessibilityLabel": "필터",
|
||||
"accessibilityHint": "받는 알림 종류 선택",
|
||||
"title": "",
|
||||
"title": "알림 표시",
|
||||
"options": {
|
||||
"follow": "$t(screenTabs:me.push.follow.heading)",
|
||||
"follow_request": "팔로우 요청",
|
||||
@ -55,7 +55,7 @@
|
||||
"name": "즐겨찾기"
|
||||
},
|
||||
"followedTags": {
|
||||
"name": ""
|
||||
"name": "팔로우 중인 해시태그"
|
||||
},
|
||||
"fontSize": {
|
||||
"name": "툿 폰트 크기"
|
||||
@ -235,10 +235,10 @@
|
||||
"heading": "부스트한 툿이 수정됨"
|
||||
},
|
||||
"admin.sign_up": {
|
||||
"heading": ""
|
||||
"heading": "관리자: 신규 가입"
|
||||
},
|
||||
"admin.report": {
|
||||
"heading": ""
|
||||
"heading": "관리자: 신고 요청"
|
||||
},
|
||||
"howitworks": "메시지 라우팅 방식 더 알아보기"
|
||||
},
|
||||
@ -399,7 +399,7 @@
|
||||
"reblogged_by": "{{count}} 부스트",
|
||||
"favourited_by": "{{count}} 즐겨찾기"
|
||||
},
|
||||
"resultIncomplete": ""
|
||||
"resultIncomplete": "원격 인스턴스의 응답 형태가 올바르지 않아요"
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,8 @@
|
||||
"continue": "Ga verder",
|
||||
"create": "Maak",
|
||||
"delete": "Verwijder",
|
||||
"done": "Gereed"
|
||||
"done": "Gereed",
|
||||
"confirm": "Bevestig"
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "Aangepaste emoji {{emoji}}"
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "Gebruiker blokkeren",
|
||||
"action_true": "Gebruiker deblokkeren"
|
||||
"action_true": "Gebruiker deblokkeren",
|
||||
"alert": {
|
||||
"title": "Bevestig blokkeren van @{{username}} ?"
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": "Rapporteren en blokkeren"
|
||||
"action": "Rapporteren en blokkeren",
|
||||
"alert": {
|
||||
"title": "Bevestig rapporteren en blokkeren van @{{username}} ?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "Blokkeer instantie {{instance}}",
|
||||
"alert": {
|
||||
"title": "Bevestig blokkeren van {{instance}} ?",
|
||||
"message": "Meestal kunt u bepaalde gebruiker dempen of blokkeren.\n\nNa het blokkeren van de instantie, wordt alle inhoud inclusief volgers van deze instantie verwijderd!",
|
||||
"buttons": {
|
||||
"confirm": "Bevestig"
|
||||
}
|
||||
"message": "Meestal kunt u bepaalde gebruiker dempen of blokkeren.\n\nNa het blokkeren van de instantie, wordt alle inhoud inclusief volgers van deze instantie verwijderd!"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "Toot verwijderen",
|
||||
"alert": {
|
||||
"title": "Verwijderen bevestigen?",
|
||||
"message": "Alle boosts en favorieten zullen worden gewist, inclusief alle antwoorden.",
|
||||
"buttons": {
|
||||
"confirm": "Bevestig"
|
||||
}
|
||||
"message": "Alle boosts en favorieten zullen worden gewist, inclusief alle antwoorden."
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "Toot verwijderen en opnieuw plaatsen",
|
||||
"alert": {
|
||||
"title": "Bevestig verwijderen en opnieuw plaatsen?",
|
||||
"message": "Alle boosts en favorieten zullen worden gewist, inclusief alle antwoorden.",
|
||||
"buttons": {
|
||||
"confirm": "Bevestig"
|
||||
}
|
||||
"message": "Alle boosts en favorieten zullen worden gewist, inclusief alle antwoorden."
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
@ -3,6 +3,7 @@
|
||||
"textInput": {
|
||||
"placeholder": "Domeinnaam van instantie"
|
||||
},
|
||||
"whitelisted": "Dit kan een gewhiteliste instantie zijn waarvan tooot geen gegevens kan ophalen voordat er ingelogd is.",
|
||||
"button": "Inloggen",
|
||||
"information": {
|
||||
"name": "Naam",
|
||||
|
@ -91,7 +91,12 @@
|
||||
"content": {
|
||||
"expandHint": "Verborgen inhoud"
|
||||
},
|
||||
"filtered": "Gefilterd: {{phrase}}.",
|
||||
"filtered": {
|
||||
"reveal": "Toch weergeven",
|
||||
"match_v1": "Gefilterd: {{phrase}}.",
|
||||
"match_v2_one": "Gefilterd door {{filters}}.",
|
||||
"match_v2_other": "Gefilterd door {{count}} filters, {{filters}}."
|
||||
},
|
||||
"fullConversation": "Gesprekken lezen",
|
||||
"translate": {
|
||||
"default": "Vertaal",
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"screenshot": {
|
||||
"title": "Privacy Bescherming",
|
||||
"message": "Gelieve de identiteit van een andere gebruiker niet openbaar te maken, zoals gebruikersnaam of avatar en meer. Bedankt!",
|
||||
"button": "Bevestig"
|
||||
"message": "Gelieve de identiteit van een andere gebruiker niet openbaar te maken, zoals gebruikersnaam of avatar en meer. Bedankt!"
|
||||
},
|
||||
"localCorrupt": {
|
||||
"message": "Sessie verlopen. Log opnieuw in"
|
||||
|
@ -7,7 +7,8 @@
|
||||
"continue": "Dalej",
|
||||
"create": "",
|
||||
"delete": "Usuń",
|
||||
"done": ""
|
||||
"done": "",
|
||||
"confirm": ""
|
||||
},
|
||||
"customEmoji": {
|
||||
"accessibilityLabel": "Własne emoji {{emoji}}"
|
||||
|
@ -13,10 +13,16 @@
|
||||
},
|
||||
"block": {
|
||||
"action_false": "Zablokuj użytkownika",
|
||||
"action_true": "Odblokuj użytkownika"
|
||||
"action_true": "Odblokuj użytkownika",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"reports": {
|
||||
"action": "Zgłoś i zablokuj"
|
||||
"action": "Zgłoś i zablokuj",
|
||||
"alert": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
@ -33,10 +39,7 @@
|
||||
"action": "Zablokuj instancję {{instance}}",
|
||||
"alert": {
|
||||
"title": "Na pewno zablokować {{instance}}?",
|
||||
"message": "Zazwyczaj wycisza się (albo blokuje) konkretnych użytkowników. \n\nGdy zablokujesz instancję, cała jej zawartość (włączając np. obserwujące Cię osoby, które do niej należą) zostanie usunięta!",
|
||||
"buttons": {
|
||||
"confirm": "Na pewno?"
|
||||
}
|
||||
"message": "Zazwyczaj wycisza się (albo blokuje) konkretnych użytkowników. \n\nGdy zablokujesz instancję, cała jej zawartość (włączając np. obserwujące Cię osoby, które do niej należą) zostanie usunięta!"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,20 +60,14 @@
|
||||
"action": "Usuń wpis",
|
||||
"alert": {
|
||||
"title": "Na pewno usunąć?",
|
||||
"message": "Wszystkie podbite i polubione wpisy zostaną wyczyszczone - wraz z odpowiedziami.",
|
||||
"buttons": {
|
||||
"confirm": "Na pewno?"
|
||||
}
|
||||
"message": "Wszystkie podbite i polubione wpisy zostaną wyczyszczone - wraz z odpowiedziami."
|
||||
}
|
||||
},
|
||||
"deleteEdit": {
|
||||
"action": "",
|
||||
"alert": {
|
||||
"title": "",
|
||||
"message": "",
|
||||
"buttons": {
|
||||
"confirm": ""
|
||||
}
|
||||
"message": ""
|
||||
}
|
||||
},
|
||||
"mute": {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user