diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index a18367f1b..14db17275 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -1,5 +1,5 @@ import { isLoggedIn } from 'soapbox/utils/auth'; -import { getFeatures } from 'soapbox/utils/features'; +import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features'; import api, { getLinks } from '../api'; @@ -359,14 +359,30 @@ const unblockAccountFail = (error: AxiosError) => ({ error, }); -const muteAccount = (id: string, notifications?: boolean) => +const muteAccount = (id: string, notifications?: boolean, duration = 0) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return null; dispatch(muteAccountRequest(id)); + const params: Record = { + notifications, + }; + + if (duration) { + const state = getState(); + const instance = state.instance; + const v = parseVersion(instance.version); + + if (v.software === PLEROMA) { + params.expires_in = duration; + } else { + params.duration = duration; + } + } + return api(getState) - .post(`/api/v1/accounts/${id}/mute`, { notifications }) + .post(`/api/v1/accounts/${id}/mute`, params) .then(response => { // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers return dispatch(muteAccountSuccess(response.data, getState().statuses)); diff --git a/app/soapbox/actions/alerts.ts b/app/soapbox/actions/alerts.ts index 3e5aed4b3..8f200563a 100644 --- a/app/soapbox/actions/alerts.ts +++ b/app/soapbox/actions/alerts.ts @@ -5,7 +5,7 @@ import { httpErrorMessages } from 'soapbox/utils/errors'; import type { SnackbarActionSeverity } from './snackbar'; import type { AnyAction } from '@reduxjs/toolkit'; import type { AxiosError } from 'axios'; -import type { NotificationObject } from 'soapbox/react-notification'; +import type { NotificationObject } from 'react-notification'; const messages = defineMessages({ unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' }, diff --git a/app/soapbox/actions/group_editor.ts b/app/soapbox/actions/group_editor.ts deleted file mode 100644 index 23f3491ad..000000000 --- a/app/soapbox/actions/group_editor.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { isLoggedIn } from 'soapbox/utils/auth'; - -import api from '../api'; - -import type { AxiosError } from 'axios'; -import type { History } from 'history'; -import type { AppDispatch, RootState } from 'soapbox/store'; -import type { APIEntity } from 'soapbox/types/entities'; - -const GROUP_CREATE_REQUEST = 'GROUP_CREATE_REQUEST'; -const GROUP_CREATE_SUCCESS = 'GROUP_CREATE_SUCCESS'; -const GROUP_CREATE_FAIL = 'GROUP_CREATE_FAIL'; - -const GROUP_UPDATE_REQUEST = 'GROUP_UPDATE_REQUEST'; -const GROUP_UPDATE_SUCCESS = 'GROUP_UPDATE_SUCCESS'; -const GROUP_UPDATE_FAIL = 'GROUP_UPDATE_FAIL'; - -const GROUP_EDITOR_VALUE_CHANGE = 'GROUP_EDITOR_VALUE_CHANGE'; -const GROUP_EDITOR_RESET = 'GROUP_EDITOR_RESET'; -const GROUP_EDITOR_SETUP = 'GROUP_EDITOR_SETUP'; - -const submit = (routerHistory: History) => - (dispatch: AppDispatch, getState: () => RootState) => { - const groupId = getState().group_editor.get('groupId') as string; - const title = getState().group_editor.get('title') as string; - const description = getState().group_editor.get('description') as string; - const coverImage = getState().group_editor.get('coverImage') as any; - - if (groupId === null) { - dispatch(create(title, description, coverImage, routerHistory)); - } else { - dispatch(update(groupId, title, description, coverImage, routerHistory)); - } - }; - -const create = (title: string, description: string, coverImage: File, routerHistory: History) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(createRequest()); - - const formData = new FormData(); - formData.append('title', title); - formData.append('description', description); - - if (coverImage !== null) { - formData.append('cover_image', coverImage); - } - - api(getState).post('/api/v1/groups', formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(({ data }) => { - dispatch(createSuccess(data)); - routerHistory.push(`/groups/${data.id}`); - }).catch(err => dispatch(createFail(err))); - }; - -const createRequest = (id?: string) => ({ - type: GROUP_CREATE_REQUEST, - id, -}); - -const createSuccess = (group: APIEntity) => ({ - type: GROUP_CREATE_SUCCESS, - group, -}); - -const createFail = (error: AxiosError) => ({ - type: GROUP_CREATE_FAIL, - error, -}); - -const update = (groupId: string, title: string, description: string, coverImage: File, routerHistory: History) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(updateRequest(groupId)); - - const formData = new FormData(); - formData.append('title', title); - formData.append('description', description); - - if (coverImage !== null) { - formData.append('cover_image', coverImage); - } - - api(getState).put(`/api/v1/groups/${groupId}`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(({ data }) => { - dispatch(updateSuccess(data)); - routerHistory.push(`/groups/${data.id}`); - }).catch(err => dispatch(updateFail(err))); - }; - -const updateRequest = (id: string) => ({ - type: GROUP_UPDATE_REQUEST, - id, -}); - -const updateSuccess = (group: APIEntity) => ({ - type: GROUP_UPDATE_SUCCESS, - group, -}); - -const updateFail = (error: AxiosError) => ({ - type: GROUP_UPDATE_FAIL, - error, -}); - -const changeValue = (field: string, value: string | File) => ({ - type: GROUP_EDITOR_VALUE_CHANGE, - field, - value, -}); - -const reset = () => ({ - type: GROUP_EDITOR_RESET, -}); - -const setUp = (group: string) => ({ - type: GROUP_EDITOR_SETUP, - group, -}); - -export { - GROUP_CREATE_REQUEST, - GROUP_CREATE_SUCCESS, - GROUP_CREATE_FAIL, - GROUP_UPDATE_REQUEST, - GROUP_UPDATE_SUCCESS, - GROUP_UPDATE_FAIL, - GROUP_EDITOR_VALUE_CHANGE, - GROUP_EDITOR_RESET, - GROUP_EDITOR_SETUP, - submit, - create, - createRequest, - createSuccess, - createFail, - update, - updateRequest, - updateSuccess, - updateFail, - changeValue, - reset, - setUp, -}; diff --git a/app/soapbox/actions/groups.ts b/app/soapbox/actions/groups.ts deleted file mode 100644 index 808cc3204..000000000 --- a/app/soapbox/actions/groups.ts +++ /dev/null @@ -1,550 +0,0 @@ -import { AxiosError } from 'axios'; - -import { isLoggedIn } from 'soapbox/utils/auth'; - -import api, { getLinks } from '../api'; - -import { fetchRelationships } from './accounts'; -import { importFetchedAccounts } from './importer'; - -import type { AppDispatch, RootState } from 'soapbox/store'; -import type { APIEntity } from 'soapbox/types/entities'; - -const GROUP_FETCH_REQUEST = 'GROUP_FETCH_REQUEST'; -const GROUP_FETCH_SUCCESS = 'GROUP_FETCH_SUCCESS'; -const GROUP_FETCH_FAIL = 'GROUP_FETCH_FAIL'; - -const GROUP_RELATIONSHIPS_FETCH_REQUEST = 'GROUP_RELATIONSHIPS_FETCH_REQUEST'; -const GROUP_RELATIONSHIPS_FETCH_SUCCESS = 'GROUP_RELATIONSHIPS_FETCH_SUCCESS'; -const GROUP_RELATIONSHIPS_FETCH_FAIL = 'GROUP_RELATIONSHIPS_FETCH_FAIL'; - -const GROUPS_FETCH_REQUEST = 'GROUPS_FETCH_REQUEST'; -const GROUPS_FETCH_SUCCESS = 'GROUPS_FETCH_SUCCESS'; -const GROUPS_FETCH_FAIL = 'GROUPS_FETCH_FAIL'; - -const GROUP_JOIN_REQUEST = 'GROUP_JOIN_REQUEST'; -const GROUP_JOIN_SUCCESS = 'GROUP_JOIN_SUCCESS'; -const GROUP_JOIN_FAIL = 'GROUP_JOIN_FAIL'; - -const GROUP_LEAVE_REQUEST = 'GROUP_LEAVE_REQUEST'; -const GROUP_LEAVE_SUCCESS = 'GROUP_LEAVE_SUCCESS'; -const GROUP_LEAVE_FAIL = 'GROUP_LEAVE_FAIL'; - -const GROUP_MEMBERS_FETCH_REQUEST = 'GROUP_MEMBERS_FETCH_REQUEST'; -const GROUP_MEMBERS_FETCH_SUCCESS = 'GROUP_MEMBERS_FETCH_SUCCESS'; -const GROUP_MEMBERS_FETCH_FAIL = 'GROUP_MEMBERS_FETCH_FAIL'; - -const GROUP_MEMBERS_EXPAND_REQUEST = 'GROUP_MEMBERS_EXPAND_REQUEST'; -const GROUP_MEMBERS_EXPAND_SUCCESS = 'GROUP_MEMBERS_EXPAND_SUCCESS'; -const GROUP_MEMBERS_EXPAND_FAIL = 'GROUP_MEMBERS_EXPAND_FAIL'; - -const GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST = 'GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST'; -const GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS'; -const GROUP_REMOVED_ACCOUNTS_FETCH_FAIL = 'GROUP_REMOVED_ACCOUNTS_FETCH_FAIL'; - -const GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST = 'GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST'; -const GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS'; -const GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL = 'GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL'; - -const GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST = 'GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST'; -const GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS'; -const GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL = 'GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL'; - -const GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST = 'GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST'; -const GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS = 'GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS'; -const GROUP_REMOVED_ACCOUNTS_CREATE_FAIL = 'GROUP_REMOVED_ACCOUNTS_CREATE_FAIL'; - -const GROUP_REMOVE_STATUS_REQUEST = 'GROUP_REMOVE_STATUS_REQUEST'; -const GROUP_REMOVE_STATUS_SUCCESS = 'GROUP_REMOVE_STATUS_SUCCESS'; -const GROUP_REMOVE_STATUS_FAIL = 'GROUP_REMOVE_STATUS_FAIL'; - -const fetchGroup = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(fetchGroupRelationships([id])); - - if (getState().groups.get(id)) { - return; - } - - dispatch(fetchGroupRequest(id)); - - api(getState).get(`/api/v1/groups/${id}`) - .then(({ data }) => dispatch(fetchGroupSuccess(data))) - .catch(err => dispatch(fetchGroupFail(id, err))); -}; - -const fetchGroupRequest = (id: string) => ({ - type: GROUP_FETCH_REQUEST, - id, -}); - -const fetchGroupSuccess = (group: APIEntity) => ({ - type: GROUP_FETCH_SUCCESS, - group, -}); - -const fetchGroupFail = (id: string, error: AxiosError) => ({ - type: GROUP_FETCH_FAIL, - id, - error, -}); - -const fetchGroupRelationships = (groupIds: string[]) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - const loadedRelationships = getState().group_relationships; - const newGroupIds = groupIds.filter(id => loadedRelationships.get(id, null) === null); - - if (newGroupIds.length === 0) { - return; - } - - dispatch(fetchGroupRelationshipsRequest(newGroupIds)); - - api(getState).get(`/api/v1/groups/${newGroupIds[0]}/relationships?${newGroupIds.map(id => `id[]=${id}`).join('&')}`).then(response => { - dispatch(fetchGroupRelationshipsSuccess(response.data)); - }).catch(error => { - dispatch(fetchGroupRelationshipsFail(error)); - }); - }; - -const fetchGroupRelationshipsRequest = (ids: string[]) => ({ - type: GROUP_RELATIONSHIPS_FETCH_REQUEST, - ids, - skipLoading: true, -}); - -const fetchGroupRelationshipsSuccess = (relationships: APIEntity[]) => ({ - type: GROUP_RELATIONSHIPS_FETCH_SUCCESS, - relationships, - skipLoading: true, -}); - -const fetchGroupRelationshipsFail = (error: AxiosError) => ({ - type: GROUP_RELATIONSHIPS_FETCH_FAIL, - error, - skipLoading: true, -}); - -const fetchGroups = (tab: string) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(fetchGroupsRequest()); - - api(getState).get('/api/v1/groups?tab=' + tab) - .then(({ data }) => { - dispatch(fetchGroupsSuccess(data, tab)); - dispatch(fetchGroupRelationships(data.map((item: APIEntity) => item.id))); - }) - .catch(err => dispatch(fetchGroupsFail(err))); -}; - -const fetchGroupsRequest = () => ({ - type: GROUPS_FETCH_REQUEST, -}); - -const fetchGroupsSuccess = (groups: APIEntity[], tab: string) => ({ - type: GROUPS_FETCH_SUCCESS, - groups, - tab, -}); - -const fetchGroupsFail = (error: AxiosError) => ({ - type: GROUPS_FETCH_FAIL, - error, -}); - -const joinGroup = (id: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(joinGroupRequest(id)); - - api(getState).post(`/api/v1/groups/${id}/accounts`).then(response => { - dispatch(joinGroupSuccess(response.data)); - }).catch(error => { - dispatch(joinGroupFail(id, error)); - }); - }; - -const leaveGroup = (id: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(leaveGroupRequest(id)); - - api(getState).delete(`/api/v1/groups/${id}/accounts`).then(response => { - dispatch(leaveGroupSuccess(response.data)); - }).catch(error => { - dispatch(leaveGroupFail(id, error)); - }); - }; - -const joinGroupRequest = (id: string) => ({ - type: GROUP_JOIN_REQUEST, - id, -}); - -const joinGroupSuccess = (relationship: APIEntity) => ({ - type: GROUP_JOIN_SUCCESS, - relationship, -}); - -const joinGroupFail = (id: string, error: AxiosError) => ({ - type: GROUP_JOIN_FAIL, - id, - error, -}); - -const leaveGroupRequest = (id: string) => ({ - type: GROUP_LEAVE_REQUEST, - id, -}); - -const leaveGroupSuccess = (relationship: APIEntity) => ({ - type: GROUP_LEAVE_SUCCESS, - relationship, -}); - -const leaveGroupFail = (id: string, error: AxiosError) => ({ - type: GROUP_LEAVE_FAIL, - id, - error, -}); - -const fetchMembers = (id: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(fetchMembersRequest(id)); - - api(getState).get(`/api/v1/groups/${id}/accounts`).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); - - dispatch(importFetchedAccounts(response.data)); - dispatch(fetchMembersSuccess(id, response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); - }).catch(error => { - dispatch(fetchMembersFail(id, error)); - }); - }; - -const fetchMembersRequest = (id: string) => ({ - type: GROUP_MEMBERS_FETCH_REQUEST, - id, -}); - -const fetchMembersSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({ - type: GROUP_MEMBERS_FETCH_SUCCESS, - id, - accounts, - next, -}); - -const fetchMembersFail = (id: string, error: AxiosError) => ({ - type: GROUP_MEMBERS_FETCH_FAIL, - id, - error, -}); - -const expandMembers = (id: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - const url = getState().user_lists.groups.get(id)!.next; - - if (url === null) { - return; - } - - dispatch(expandMembersRequest(id)); - - api(getState).get(url).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); - - dispatch(importFetchedAccounts(response.data)); - dispatch(expandMembersSuccess(id, response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); - }).catch(error => { - dispatch(expandMembersFail(id, error)); - }); - }; - -const expandMembersRequest = (id: string) => ({ - type: GROUP_MEMBERS_EXPAND_REQUEST, - id, -}); - -const expandMembersSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({ - type: GROUP_MEMBERS_EXPAND_SUCCESS, - id, - accounts, - next, -}); - -const expandMembersFail = (id: string, error: AxiosError) => ({ - type: GROUP_MEMBERS_EXPAND_FAIL, - id, - error, -}); - -const fetchRemovedAccounts = (id: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(fetchRemovedAccountsRequest(id)); - - api(getState).get(`/api/v1/groups/${id}/removed_accounts`).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); - - dispatch(importFetchedAccounts(response.data)); - dispatch(fetchRemovedAccountsSuccess(id, response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); - }).catch(error => { - dispatch(fetchRemovedAccountsFail(id, error)); - }); - }; - -const fetchRemovedAccountsRequest = (id: string) => ({ - type: GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST, - id, -}); - -const fetchRemovedAccountsSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({ - type: GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS, - id, - accounts, - next, -}); - -const fetchRemovedAccountsFail = (id: string, error: AxiosError) => ({ - type: GROUP_REMOVED_ACCOUNTS_FETCH_FAIL, - id, - error, -}); - -const expandRemovedAccounts = (id: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - const url = getState().user_lists.groups_removed_accounts.get(id)!.next; - - if (url === null) { - return; - } - - dispatch(expandRemovedAccountsRequest(id)); - - api(getState).get(url).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); - - dispatch(importFetchedAccounts(response.data)); - dispatch(expandRemovedAccountsSuccess(id, response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); - }).catch(error => { - dispatch(expandRemovedAccountsFail(id, error)); - }); - }; - -const expandRemovedAccountsRequest = (id: string) => ({ - type: GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST, - id, -}); - -const expandRemovedAccountsSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({ - type: GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS, - id, - accounts, - next, -}); - -const expandRemovedAccountsFail = (id: string, error: AxiosError) => ({ - type: GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL, - id, - error, -}); - -const removeRemovedAccount = (groupId: string, id: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(removeRemovedAccountRequest(groupId, id)); - - api(getState).delete(`/api/v1/groups/${groupId}/removed_accounts?account_id=${id}`).then(response => { - dispatch(removeRemovedAccountSuccess(groupId, id)); - }).catch(error => { - dispatch(removeRemovedAccountFail(groupId, id, error)); - }); - }; - -const removeRemovedAccountRequest = (groupId: string, id: string) => ({ - type: GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST, - groupId, - id, -}); - -const removeRemovedAccountSuccess = (groupId: string, id: string) => ({ - type: GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS, - groupId, - id, -}); - -const removeRemovedAccountFail = (groupId: string, id: string, error: AxiosError) => ({ - type: GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL, - groupId, - id, - error, -}); - -const createRemovedAccount = (groupId: string, id: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(createRemovedAccountRequest(groupId, id)); - - api(getState).post(`/api/v1/groups/${groupId}/removed_accounts?account_id=${id}`).then(response => { - dispatch(createRemovedAccountSuccess(groupId, id)); - }).catch(error => { - dispatch(createRemovedAccountFail(groupId, id, error)); - }); - }; - -const createRemovedAccountRequest = (groupId: string, id: string) => ({ - type: GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST, - groupId, - id, -}); - -const createRemovedAccountSuccess = (groupId: string, id: string) => ({ - type: GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS, - groupId, - id, -}); - -const createRemovedAccountFail = (groupId: string, id: string, error: AxiosError) => ({ - type: GROUP_REMOVED_ACCOUNTS_CREATE_FAIL, - groupId, - id, - error, -}); - -const groupRemoveStatus = (groupId: string, id: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; - - dispatch(groupRemoveStatusRequest(groupId, id)); - - api(getState).delete(`/api/v1/groups/${groupId}/statuses/${id}`).then(response => { - dispatch(groupRemoveStatusSuccess(groupId, id)); - }).catch(error => { - dispatch(groupRemoveStatusFail(groupId, id, error)); - }); - }; - -const groupRemoveStatusRequest = (groupId: string, id: string) => ({ - type: GROUP_REMOVE_STATUS_REQUEST, - groupId, - id, -}); - -const groupRemoveStatusSuccess = (groupId: string, id: string) => ({ - type: GROUP_REMOVE_STATUS_SUCCESS, - groupId, - id, -}); - -const groupRemoveStatusFail = (groupId: string, id: string, error: AxiosError) => ({ - type: GROUP_REMOVE_STATUS_FAIL, - groupId, - id, - error, -}); - -export { - GROUP_FETCH_REQUEST, - GROUP_FETCH_SUCCESS, - GROUP_FETCH_FAIL, - GROUP_RELATIONSHIPS_FETCH_REQUEST, - GROUP_RELATIONSHIPS_FETCH_SUCCESS, - GROUP_RELATIONSHIPS_FETCH_FAIL, - GROUPS_FETCH_REQUEST, - GROUPS_FETCH_SUCCESS, - GROUPS_FETCH_FAIL, - GROUP_JOIN_REQUEST, - GROUP_JOIN_SUCCESS, - GROUP_JOIN_FAIL, - GROUP_LEAVE_REQUEST, - GROUP_LEAVE_SUCCESS, - GROUP_LEAVE_FAIL, - GROUP_MEMBERS_FETCH_REQUEST, - GROUP_MEMBERS_FETCH_SUCCESS, - GROUP_MEMBERS_FETCH_FAIL, - GROUP_MEMBERS_EXPAND_REQUEST, - GROUP_MEMBERS_EXPAND_SUCCESS, - GROUP_MEMBERS_EXPAND_FAIL, - GROUP_REMOVED_ACCOUNTS_FETCH_REQUEST, - GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS, - GROUP_REMOVED_ACCOUNTS_FETCH_FAIL, - GROUP_REMOVED_ACCOUNTS_EXPAND_REQUEST, - GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS, - GROUP_REMOVED_ACCOUNTS_EXPAND_FAIL, - GROUP_REMOVED_ACCOUNTS_REMOVE_REQUEST, - GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS, - GROUP_REMOVED_ACCOUNTS_REMOVE_FAIL, - GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST, - GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS, - GROUP_REMOVED_ACCOUNTS_CREATE_FAIL, - GROUP_REMOVE_STATUS_REQUEST, - GROUP_REMOVE_STATUS_SUCCESS, - GROUP_REMOVE_STATUS_FAIL, - fetchGroup, - fetchGroupRequest, - fetchGroupSuccess, - fetchGroupFail, - fetchGroupRelationships, - fetchGroupRelationshipsRequest, - fetchGroupRelationshipsSuccess, - fetchGroupRelationshipsFail, - fetchGroups, - fetchGroupsRequest, - fetchGroupsSuccess, - fetchGroupsFail, - joinGroup, - leaveGroup, - joinGroupRequest, - joinGroupSuccess, - joinGroupFail, - leaveGroupRequest, - leaveGroupSuccess, - leaveGroupFail, - fetchMembers, - fetchMembersRequest, - fetchMembersSuccess, - fetchMembersFail, - expandMembers, - expandMembersRequest, - expandMembersSuccess, - expandMembersFail, - fetchRemovedAccounts, - fetchRemovedAccountsRequest, - fetchRemovedAccountsSuccess, - fetchRemovedAccountsFail, - expandRemovedAccounts, - expandRemovedAccountsRequest, - expandRemovedAccountsSuccess, - expandRemovedAccountsFail, - removeRemovedAccount, - removeRemovedAccountRequest, - removeRemovedAccountSuccess, - removeRemovedAccountFail, - createRemovedAccount, - createRemovedAccountRequest, - createRemovedAccountSuccess, - createRemovedAccountFail, - groupRemoveStatus, - groupRemoveStatusRequest, - groupRemoveStatusSuccess, - groupRemoveStatusFail, -}; diff --git a/app/soapbox/actions/instance.ts b/app/soapbox/actions/instance.ts index 60a6b2e89..151ad3672 100644 --- a/app/soapbox/actions/instance.ts +++ b/app/soapbox/actions/instance.ts @@ -66,7 +66,5 @@ export const loadInstance = createAsyncThunk( export const fetchNodeinfo = createAsyncThunk( 'nodeinfo/fetch', - async(_arg, { getState }) => { - return await api(getState).get('/nodeinfo/2.1.json'); - }, + async(_arg, { getState }) => await api(getState).get('/nodeinfo/2.1.json'), ); diff --git a/app/soapbox/actions/mutes.ts b/app/soapbox/actions/mutes.ts index 050a513f0..bb684b0d6 100644 --- a/app/soapbox/actions/mutes.ts +++ b/app/soapbox/actions/mutes.ts @@ -21,6 +21,7 @@ const MUTES_EXPAND_FAIL = 'MUTES_EXPAND_FAIL'; const MUTES_INIT_MODAL = 'MUTES_INIT_MODAL'; const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS'; +const MUTES_CHANGE_DURATION = 'MUTES_CHANGE_DURATION'; const fetchMutes = () => (dispatch: AppDispatch, getState: () => RootState) => { @@ -103,6 +104,14 @@ const toggleHideNotifications = () => dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS }); }; +const changeMuteDuration = (duration: number) => + (dispatch: AppDispatch) => { + dispatch({ + type: MUTES_CHANGE_DURATION, + duration, + }); + }; + export { MUTES_FETCH_REQUEST, MUTES_FETCH_SUCCESS, @@ -112,6 +121,7 @@ export { MUTES_EXPAND_FAIL, MUTES_INIT_MODAL, MUTES_TOGGLE_HIDE_NOTIFICATIONS, + MUTES_CHANGE_DURATION, fetchMutes, fetchMutesRequest, fetchMutesSuccess, @@ -122,4 +132,5 @@ export { expandMutesFail, initMuteModal, toggleHideNotifications, + changeMuteDuration, }; diff --git a/app/soapbox/actions/notifications.ts b/app/soapbox/actions/notifications.ts index 4edd76c22..db8fd688f 100644 --- a/app/soapbox/actions/notifications.ts +++ b/app/soapbox/actions/notifications.ts @@ -11,7 +11,7 @@ import { getFilters, regexFromFilters } from 'soapbox/selectors'; import { isLoggedIn } from 'soapbox/utils/auth'; import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features'; import { unescapeHTML } from 'soapbox/utils/html'; -import { NOTIFICATION_TYPES } from 'soapbox/utils/notification'; +import { EXCLUDE_TYPES, NOTIFICATION_TYPES } from 'soapbox/utils/notification'; import { joinPublicPath } from 'soapbox/utils/static'; import { fetchRelationships } from './accounts'; @@ -195,7 +195,9 @@ const expandNotifications = ({ maxId }: Record = {}, done: () => an if (activeFilter === 'all') { if (features.notificationsIncludeTypes) { - params.types = NOTIFICATION_TYPES; + params.types = NOTIFICATION_TYPES.filter(type => !EXCLUDE_TYPES.includes(type as any)); + } else { + params.exclude_types = EXCLUDE_TYPES; } } else { if (features.notificationsIncludeTypes) { diff --git a/app/soapbox/actions/statuses.ts b/app/soapbox/actions/statuses.ts index b1bced1f0..f9bfca3b0 100644 --- a/app/soapbox/actions/statuses.ts +++ b/app/soapbox/actions/statuses.ts @@ -43,6 +43,11 @@ const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL'; const STATUS_REVEAL = 'STATUS_REVEAL'; const STATUS_HIDE = 'STATUS_HIDE'; +const STATUS_TRANSLATE_REQUEST = 'STATUS_TRANSLATE_REQUEST'; +const STATUS_TRANSLATE_SUCCESS = 'STATUS_TRANSLATE_SUCCESS'; +const STATUS_TRANSLATE_FAIL = 'STATUS_TRANSLATE_FAIL'; +const STATUS_TRANSLATE_UNDO = 'STATUS_TRANSLATE_UNDO'; + const statusExists = (getState: () => RootState, statusId: string) => { return (getState().statuses.get(statusId) || null) !== null; }; @@ -305,6 +310,31 @@ const toggleStatusHidden = (status: Status) => { } }; +const translateStatus = (id: string, targetLanguage?: string) => (dispatch: AppDispatch, getState: () => RootState) => { + dispatch({ type: STATUS_TRANSLATE_REQUEST, id }); + + api(getState).post(`/api/v1/statuses/${id}/translate`, { + target_language: targetLanguage, + }).then(response => { + dispatch({ + type: STATUS_TRANSLATE_SUCCESS, + id, + translation: response.data, + }); + }).catch(error => { + dispatch({ + type: STATUS_TRANSLATE_FAIL, + id, + error, + }); + }); +}; + +const undoStatusTranslation = (id: string) => ({ + type: STATUS_TRANSLATE_UNDO, + id, +}); + export { STATUS_CREATE_REQUEST, STATUS_CREATE_SUCCESS, @@ -329,6 +359,10 @@ export { STATUS_UNMUTE_FAIL, STATUS_REVEAL, STATUS_HIDE, + STATUS_TRANSLATE_REQUEST, + STATUS_TRANSLATE_SUCCESS, + STATUS_TRANSLATE_FAIL, + STATUS_TRANSLATE_UNDO, createStatus, editStatus, fetchStatus, @@ -345,4 +379,6 @@ export { hideStatus, revealStatus, toggleStatusHidden, + translateStatus, + undoStatusTranslation, }; diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx index 438d2f0f4..9f2d17a0c 100644 --- a/app/soapbox/components/account.tsx +++ b/app/soapbox/components/account.tsx @@ -235,6 +235,14 @@ const Account = ({ ) : null} + + {actionType === 'muting' && account.mute_expires_at ? ( + <> + · + + + + ) : null} {withAccountNote && ( diff --git a/app/soapbox/components/avatar_composite.js b/app/soapbox/components/avatar_composite.js deleted file mode 100644 index 59e4bab96..000000000 --- a/app/soapbox/components/avatar_composite.js +++ /dev/null @@ -1,89 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; - -import StillImage from 'soapbox/components/still_image'; - -export default class AvatarComposite extends React.PureComponent { - - static propTypes = { - accounts: ImmutablePropTypes.list.isRequired, - size: PropTypes.number.isRequired, - }; - - renderItem(account, size, index) { - - let width = 50; - let height = 100; - let top = 'auto'; - let left = 'auto'; - let bottom = 'auto'; - let right = 'auto'; - - if (size === 1) { - width = 100; - } - - if (size === 4 || (size === 3 && index > 0)) { - height = 50; - } - - if (size === 2) { - if (index === 0) { - right = '2px'; - } else { - left = '2px'; - } - } else if (size === 3) { - if (index === 0) { - right = '2px'; - } else if (index > 0) { - left = '2px'; - } - - if (index === 1) { - bottom = '2px'; - } else if (index > 1) { - top = '2px'; - } - } else if (size === 4) { - if (index === 0 || index === 2) { - right = '2px'; - } - - if (index === 1 || index === 3) { - left = '2px'; - } - - if (index < 2) { - bottom = '2px'; - } else { - top = '2px'; - } - } - - const style = { - left: left, - top: top, - right: right, - bottom: bottom, - width: `${width}%`, - height: `${height}%`, - }; - - return ( - - ); - } - - render() { - const { accounts, size } = this.props; - - return ( -
- {accounts.take(4).map((account, i) => this.renderItem(account, accounts.size, i))} -
- ); - } - -} diff --git a/app/soapbox/components/column_header.js b/app/soapbox/components/column_header.js index 42f8eebb8..83a61b7ea 100644 --- a/app/soapbox/components/column_header.js +++ b/app/soapbox/components/column_header.js @@ -14,7 +14,6 @@ import SubNavigation from 'soapbox/components/sub_navigation'; // hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' }, // }); -export default @withRouter class ColumnHeader extends React.PureComponent { static propTypes = { @@ -126,3 +125,5 @@ class ColumnHeader extends React.PureComponent { // } } + +export default withRouter(ColumnHeader); \ No newline at end of file diff --git a/app/soapbox/components/error_boundary.tsx b/app/soapbox/components/error_boundary.tsx index 436b40134..76adf9728 100644 --- a/app/soapbox/components/error_boundary.tsx +++ b/app/soapbox/components/error_boundary.tsx @@ -8,6 +8,7 @@ import { Text, Stack } from 'soapbox/components/ui'; import { captureException } from 'soapbox/monitoring'; import KVStore from 'soapbox/storage/kv_store'; import sourceCode from 'soapbox/utils/code'; +import { unregisterSw } from 'soapbox/utils/sw'; import SiteLogo from './site-logo'; @@ -15,16 +16,6 @@ import type { RootState } from 'soapbox/store'; const goHome = () => location.href = '/'; -/** Unregister the ServiceWorker */ -// https://stackoverflow.com/a/49771828/8811886 -const unregisterSw = async(): Promise => { - if (navigator.serviceWorker) { - const registrations = await navigator.serviceWorker.getRegistrations(); - const unregisterAll = registrations.map(r => r.unregister()); - await Promise.all(unregisterAll); - } -}; - const mapStateToProps = (state: RootState) => { const { links, logo } = getSoapboxConfig(state); diff --git a/app/soapbox/components/filter_bar.js b/app/soapbox/components/filter_bar.js deleted file mode 100644 index 601ca04a2..000000000 --- a/app/soapbox/components/filter_bar.js +++ /dev/null @@ -1,155 +0,0 @@ -import classNames from 'clsx'; -import debounce from 'lodash/debounce'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { withRouter } from 'react-router-dom'; - -export default @withRouter -class FilterBar extends React.PureComponent { - - static propTypes = { - items: PropTypes.array.isRequired, - active: PropTypes.string, - className: PropTypes.string, - history: PropTypes.object, - }; - - state = { - mounted: false, - }; - - componentDidMount() { - this.node.addEventListener('keydown', this.handleKeyDown, false); - window.addEventListener('resize', this.handleResize, { passive: true }); - - const { left, width } = this.getActiveTabIndicationSize(); - this.setState({ mounted: true, left, width }); - } - - componentWillUnmount() { - this.node.removeEventListener('keydown', this.handleKeyDown, false); - document.removeEventListener('resize', this.handleResize, false); - } - - handleResize = debounce(() => { - this.setState(this.getActiveTabIndicationSize()); - }, 300, { - trailing: true, - }); - - componentDidUpdate(prevProps) { - if (this.props.active !== prevProps.active) { - this.setState(this.getActiveTabIndicationSize()); - } - } - - setRef = c => { - this.node = c; - } - - setFocusRef = c => { - this.focusedItem = c; - } - - handleKeyDown = e => { - const items = Array.from(this.node.getElementsByTagName('a')); - const index = items.indexOf(document.activeElement); - let element = null; - - switch (e.key) { - case 'ArrowRight': - element = items[index + 1] || items[0]; - break; - case 'ArrowLeft': - element = items[index - 1] || items[items.length - 1]; - break; - } - - if (element) { - element.focus(); - e.preventDefault(); - e.stopPropagation(); - } - } - - handleItemKeyPress = e => { - if (e.key === 'Enter' || e.key === ' ') { - this.handleClick(e); - } - } - - handleClick = e => { - const i = Number(e.currentTarget.getAttribute('data-index')); - const { action, to } = this.props.items[i]; - - if (typeof action === 'function') { - e.preventDefault(); - action(e); - } else if (to) { - e.preventDefault(); - this.props.history.push(to); - } - } - - getActiveTabIndicationSize() { - const { active, items } = this.props; - - if (!active || !this.node) return { width: null }; - - const index = items.findIndex(({ name }) => name === active); - const elements = Array.from(this.node.getElementsByTagName('a')); - const element = elements[index]; - - if (!element) return { width: null }; - - const left = element.offsetLeft; - const { width } = element.getBoundingClientRect(); - - return { left, width }; - } - - renderActiveTabIndicator() { - const { left, width } = this.state; - - return ( -
- ); - } - - renderItem(option, i) { - if (option === null) { - return
  • ; - } - - const { name, text, href, to, title } = option; - - return ( - - {text} - - ); - } - - render() { - const { className, items } = this.props; - const { mounted } = this.state; - - return ( -
    - {mounted && this.renderActiveTabIndicator()} - {items.map((option, i) => this.renderItem(option, i))} -
    - ); - } - -} diff --git a/app/soapbox/components/quoted-status.tsx b/app/soapbox/components/quoted-status.tsx index 9755e7120..b521c1172 100644 --- a/app/soapbox/components/quoted-status.tsx +++ b/app/soapbox/components/quoted-status.tsx @@ -116,7 +116,7 @@ const QuotedStatus: React.FC = ({ status, onCancel, compose }) => collapsable /> - {(status.media_attachments.size > 0) && ( + {(status.card || status.media_attachments.size > 0) && ( { - this.props.onChange(this.props.settingKey, e.target.value); - } - - render() { - const { settings, settingKey, label } = this.props; - - return ( - - ); - } - -} diff --git a/app/soapbox/components/status.tsx b/app/soapbox/components/status.tsx index 02eca62fe..961ca5fbe 100644 --- a/app/soapbox/components/status.tsx +++ b/app/soapbox/components/status.tsx @@ -9,6 +9,7 @@ import { toggleFavourite, toggleReblog } from 'soapbox/actions/interactions'; import { openModal } from 'soapbox/actions/modals'; import { toggleStatusHidden } from 'soapbox/actions/statuses'; import Icon from 'soapbox/components/icon'; +import TranslateButton from 'soapbox/components/translate-button'; import AccountContainer from 'soapbox/containers/account_container'; import QuotedStatus from 'soapbox/features/status/containers/quoted_status_container'; import { useAppDispatch, useSettings } from 'soapbox/hooks'; @@ -370,9 +371,12 @@ const Status: React.FC = (props) => { status={actualStatus} onClick={handleClick} collapsable + translatable /> - {(quote || actualStatus.media_attachments.size > 0) && ( + + + {(quote || actualStatus.card || actualStatus.media_attachments.size > 0) && ( void, collapsable?: boolean, + translatable?: boolean, } /** Renders the text content of a status */ -const StatusContent: React.FC = ({ status, onClick, collapsable = false }) => { +const StatusContent: React.FC = ({ status, onClick, collapsable = false, translatable }) => { const history = useHistory(); const [collapsed, setCollapsed] = useState(false); @@ -154,14 +155,14 @@ const StatusContent: React.FC = ({ status, onClick, collapsable }; const parsedHtml = useMemo((): string => { - const { contentHtml: html } = status; + const html = translatable && status.translation ? status.translation.get('content')! : status.contentHtml; if (greentext) { return addGreentext(html); } else { return html; } - }, [status.contentHtml]); + }, [status.contentHtml, status.translation]); if (status.content.length === 0) { return null; diff --git a/app/soapbox/components/sub_navigation.tsx b/app/soapbox/components/sub_navigation.tsx index b8e2b310d..1e6afb85a 100644 --- a/app/soapbox/components/sub_navigation.tsx +++ b/app/soapbox/components/sub_navigation.tsx @@ -4,34 +4,22 @@ import { defineMessages, useIntl } from 'react-intl'; // import { connect } from 'react-redux'; import { useHistory } from 'react-router-dom'; -// import { openModal } from 'soapbox/actions/modals'; -// import { useAppDispatch } from 'soapbox/hooks'; - import { CardHeader, CardTitle } from './ui'; const messages = defineMessages({ back: { id: 'column_back_button.label', defaultMessage: 'Back' }, - settings: { id: 'column_header.show_settings', defaultMessage: 'Show settings' }, }); interface ISubNavigation { - message: String, + message: React.ReactNode, + /** @deprecated Unused. */ settings?: React.ComponentType, } const SubNavigation: React.FC = ({ message }) => { const intl = useIntl(); - // const dispatch = useAppDispatch(); const history = useHistory(); - // const ref = useRef(null); - - // const [scrolled, setScrolled] = useState(false); - - // const onOpenSettings = () => { - // dispatch(openModal('COMPONENT', { component: Settings })); - // }; - const handleBackClick = () => { if (window.history && window.history.length === 1) { history.push('/'); @@ -40,36 +28,6 @@ const SubNavigation: React.FC = ({ message }) => { } }; - // const handleBackKeyUp = (e) => { - // if (e.key === 'Enter') { - // handleClick(); - // } - // } - - // const handleOpenSettings = () => { - // onOpenSettings(); - // } - - // useEffect(() => { - // const handleScroll = throttle(() => { - // if (this.node) { - // const { offsetTop } = this.node; - - // if (offsetTop > 0) { - // setScrolled(true); - // } else { - // setScrolled(false); - // } - // } - // }, 150, { trailing: true }); - - // window.addEventListener('scroll', handleScroll); - - // return () => { - // window.removeEventListener('scroll', handleScroll); - // }; - // }, []); - return ( = ({ status }) => { + const dispatch = useAppDispatch(); + const intl = useIntl(); + const features = useFeatures(); + + const me = useAppSelector((state) => state.me); + + const renderTranslate = /* translationEnabled && */ me && ['public', 'unlisted'].includes(status.visibility) && status.contentHtml.length > 0 && status.language !== null && intl.locale !== status.language; + + const handleTranslate: React.MouseEventHandler = (e) => { + e.stopPropagation(); + + if (status.translation) { + dispatch(undoStatusTranslation(status.id)); + } else { + dispatch(translateStatus(status.id, intl.locale)); + } + }; + + if (!features.translations || !renderTranslate) return null; + + if (status.translation) { + const languageNames = new Intl.DisplayNames([intl.locale], { type: 'language' }); + const languageName = languageNames.of(status.language!); + const provider = status.translation.get('provider'); + + return ( + + + + + + ); + } + + return ( + + ); +}; + +export default TranslateButton; diff --git a/app/soapbox/components/ui/hstack/hstack.tsx b/app/soapbox/components/ui/hstack/hstack.tsx index a109da608..996320dea 100644 --- a/app/soapbox/components/ui/hstack/hstack.tsx +++ b/app/soapbox/components/ui/hstack/hstack.tsx @@ -40,6 +40,8 @@ interface IHStack { space?: keyof typeof spaces /** Whether to let the flexbox grow. */ grow?: boolean + /** HTML element to use for container. */ + element?: keyof JSX.IntrinsicElements, /** Extra CSS styles for the
    */ style?: React.CSSProperties /** Whether to let the flexbox wrap onto multiple lines. */ @@ -48,10 +50,12 @@ interface IHStack { /** Horizontal row of child elements. */ const HStack = forwardRef((props, ref) => { - const { space, alignItems, grow, justifyContent, wrap, className, ...filteredProps } = props; + const { space, alignItems, justifyContent, className, grow, element = 'div', wrap, ...filteredProps } = props; + + const Elem = element as 'div'; return ( -
    { - /** Size of the gap between elements. */ - space?: keyof typeof spaces /** Horizontal alignment of children. */ - alignItems?: 'center' | 'start', + alignItems?: keyof typeof alignItemsOptions + /** Extra class names on the element. */ + className?: string /** Vertical alignment of children. */ justifyContent?: keyof typeof justifyContentOptions - /** Extra class names on the
    element. */ - className?: string + /** Size of the gap between elements. */ + space?: keyof typeof spaces /** Whether to let the flexbox grow. */ grow?: boolean + /** HTML element to use for container. */ + element?: keyof JSX.IntrinsicElements, } /** Vertical stack of child elements. */ const Stack = React.forwardRef((props, ref: React.LegacyRef | undefined) => { - const { space, alignItems, justifyContent, className, grow, ...filteredProps } = props; + const { space, alignItems, justifyContent, className, grow, element = 'div', ...filteredProps } = props; + + const Elem = element as 'div'; return ( -
    = ({ token }) => { +const AuthToken: React.FC = ({ token, isCurrent }) => { const dispatch = useAppDispatch(); const intl = useIntl(); const handleRevoke = () => { - dispatch(revokeOAuthTokenById(token.id)); + if (isCurrent) + dispatch(openModal('CONFIRM', { + icon: require('@tabler/icons/alert-triangle.svg'), + heading: intl.formatMessage(messages.revokeSessionHeading), + message: intl.formatMessage(messages.revokeSessionMessage), + confirm: intl.formatMessage(messages.revokeSessionConfirm), + onConfirm: () => { + dispatch(revokeOAuthTokenById(token.id)); + }, + })); + else { + dispatch(revokeOAuthTokenById(token.id)); + } }; return ( @@ -42,7 +61,7 @@ const AuthToken: React.FC = ({ token }) => {
    -
    @@ -55,6 +74,11 @@ const AuthTokenList: React.FC = () => { const dispatch = useAppDispatch(); const intl = useIntl(); const tokens = useAppSelector(state => state.security.get('tokens').reverse()); + const currentTokenId = useAppSelector(state => { + const currentToken = state.auth.get('tokens').valueSeq().find((token: ImmutableMap) => token.get('me') === state.auth.get('me')); + + return currentToken?.get('id'); + }); useEffect(() => { dispatch(fetchOAuthTokens()); @@ -63,7 +87,7 @@ const AuthTokenList: React.FC = () => { const body = tokens ? (
    {tokens.map((token) => ( - + ))}
    ) : ; diff --git a/app/soapbox/features/community_timeline/components/column_settings.js b/app/soapbox/features/community_timeline/components/column_settings.js deleted file mode 100644 index 52781f1f4..000000000 --- a/app/soapbox/features/community_timeline/components/column_settings.js +++ /dev/null @@ -1,51 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; - -import IconButton from 'soapbox/components/icon_button'; - -import SettingToggle from '../../notifications/components/setting_toggle'; - -const messages = defineMessages({ - close: { id: 'lightbox.close', defaultMessage: 'Close' }, -}); - -export default @injectIntl -class ColumnSettings extends React.PureComponent { - - static propTypes = { - intl: PropTypes.object.isRequired, - settings: ImmutablePropTypes.map.isRequired, - onChange: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - }; - - render() { - const { intl, settings, onChange, onClose } = this.props; - - return ( -
    -
    -

    - -

    -
    - -
    -
    - -
    -
    - } /> -
    - -
    - } /> -
    -
    -
    - ); - } - -} diff --git a/app/soapbox/features/community_timeline/containers/column_settings_container.js b/app/soapbox/features/community_timeline/containers/column_settings_container.js deleted file mode 100644 index d20838089..000000000 --- a/app/soapbox/features/community_timeline/containers/column_settings_container.js +++ /dev/null @@ -1,18 +0,0 @@ -import { connect } from 'react-redux'; - -import { getSettings, changeSetting } from '../../../actions/settings'; -import ColumnSettings from '../components/column_settings'; - -const mapStateToProps = state => ({ - settings: getSettings(state).get('community'), -}); - -const mapDispatchToProps = (dispatch) => { - return { - onChange(key, checked) { - dispatch(changeSetting(['community', ...key], checked)); - }, - }; -}; - -export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings); diff --git a/app/soapbox/features/community_timeline/index.tsx b/app/soapbox/features/community_timeline/index.tsx index 8c0adc2cb..4fd4e1d0a 100644 --- a/app/soapbox/features/community_timeline/index.tsx +++ b/app/soapbox/features/community_timeline/index.tsx @@ -10,8 +10,6 @@ import { useAppDispatch, useSettings } from 'soapbox/hooks'; import Timeline from '../ui/components/timeline'; -import ColumnSettings from './containers/column_settings_container'; - const messages = defineMessages({ title: { id: 'column.community', defaultMessage: 'Local timeline' }, }); @@ -44,7 +42,10 @@ const CommunityTimeline = () => { return ( - +
    + +
    + ({ id, shouldCondense, autoFocus, clickab setComposeFocused(true); }; - const handleSubmit = () => { + const handleSubmit = (e?: React.FormEvent) => { if (text !== autosuggestTextareaRef.current?.textarea?.value) { // Something changed the text inside the textarea (e.g. browser extensions like Grammarly) // Update the state to match the current text @@ -142,6 +142,10 @@ const ComposeForm = ({ id, shouldCondense, autoFocus, clickab // Submit disabled: const fulltext = [spoilerText, countableText(text)].join(''); + if (e) { + e.preventDefault(); + } + if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > maxTootChars || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) { return; } @@ -261,7 +265,7 @@ const ComposeForm = ({ id, shouldCondense, autoFocus, clickab } return ( - + {scheduledStatusCount > 0 && ( ({ id, shouldCondense, autoFocus, clickab
    )} -
    diff --git a/app/soapbox/features/compose/components/polls/poll-form.tsx b/app/soapbox/features/compose/components/polls/poll-form.tsx index 4daf54048..e61f1975f 100644 --- a/app/soapbox/features/compose/components/polls/poll-form.tsx +++ b/app/soapbox/features/compose/components/polls/poll-form.tsx @@ -168,7 +168,7 @@ const PollForm: React.FC = ({ composeId }) => { -
    diff --git a/app/soapbox/features/compose/components/spoiler-input.tsx b/app/soapbox/features/compose/components/spoiler-input.tsx index e6f53d04c..873450116 100644 --- a/app/soapbox/features/compose/components/spoiler-input.tsx +++ b/app/soapbox/features/compose/components/spoiler-input.tsx @@ -68,7 +68,7 @@ const SpoilerInput = React.forwardRef(({ />
    -
    @@ -77,4 +77,4 @@ const SpoilerInput = React.forwardRef(({ ); }); -export default SpoilerInput; \ No newline at end of file +export default SpoilerInput; diff --git a/app/soapbox/features/developers/components/indicator.tsx b/app/soapbox/features/developers/components/indicator.tsx new file mode 100644 index 000000000..5cb7763b5 --- /dev/null +++ b/app/soapbox/features/developers/components/indicator.tsx @@ -0,0 +1,24 @@ +import classNames from 'clsx'; +import React from 'react'; + +interface IIndicator { + state?: 'active' | 'pending' | 'error' | 'inactive', + size?: 'sm', +} + +/** Indicator dot component. */ +const Indicator: React.FC = ({ state = 'inactive', size = 'sm' }) => { + return ( +
    + ); +}; + +export default Indicator; \ No newline at end of file diff --git a/app/soapbox/features/developers/developers-menu.tsx b/app/soapbox/features/developers/developers-menu.tsx index 76320e530..a94eb09a6 100644 --- a/app/soapbox/features/developers/developers-menu.tsx +++ b/app/soapbox/features/developers/developers-menu.tsx @@ -89,6 +89,14 @@ const Developers: React.FC = () => { + + + + + + + + diff --git a/app/soapbox/features/developers/service-worker-info.tsx b/app/soapbox/features/developers/service-worker-info.tsx new file mode 100644 index 000000000..9b7751587 --- /dev/null +++ b/app/soapbox/features/developers/service-worker-info.tsx @@ -0,0 +1,140 @@ +import React, { useEffect, useState } from 'react'; +import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; + +import List, { ListItem } from 'soapbox/components/list'; +import { HStack, Text, Column, FormActions, Button, Stack, Icon } from 'soapbox/components/ui'; +import { unregisterSw } from 'soapbox/utils/sw'; + +import Indicator from './components/indicator'; + +const messages = defineMessages({ + heading: { id: 'column.developers.service_worker', defaultMessage: 'Service Worker' }, + status: { id: 'sw.status', defaultMessage: 'Status' }, + url: { id: 'sw.url', defaultMessage: 'Script URL' }, +}); + +/** Hook that returns the active ServiceWorker registration. */ +const useRegistration = () => { + const [isLoading, setLoading] = useState(true); + const [registration, setRegistration] = useState(); + + const isSupported = 'serviceWorker' in navigator; + + useEffect(() => { + if (isSupported) { + navigator.serviceWorker.getRegistration() + .then(r => { + setRegistration(r); + setLoading(false); + }) + .catch(() => setLoading(false)); + } else { + setLoading(false); + } + }, []); + + return { + isLoading, + registration, + }; +}; + +interface IServiceWorkerInfo { +} + +/** Mini ServiceWorker debugging component. */ +const ServiceWorkerInfo: React.FC = () => { + const intl = useIntl(); + const { isLoading, registration } = useRegistration(); + + const url = registration?.active?.scriptURL; + + const getState = () => { + if (registration?.waiting) { + return 'pending'; + } else if (registration?.active) { + return 'active'; + } else { + return 'inactive'; + } + }; + + const getMessage = () => { + if (isLoading) { + return ( + + ); + } else if (!isLoading && !registration) { + return ( + + ); + } else if (registration?.waiting) { + return ( + + ); + } else if (registration?.active) { + return ( + + ); + } else { + return ( + + ); + } + }; + + const handleRestart = async() => { + await unregisterSw(); + window.location.reload(); + }; + + return ( + + + + + + + {getMessage()} + + + + {url && ( + + + {url} + + + + )} + + + + + + + + ); +}; + +export default ServiceWorkerInfo; \ No newline at end of file diff --git a/app/soapbox/features/federation_restrictions/components/instance_restrictions.js b/app/soapbox/features/federation_restrictions/components/instance_restrictions.tsx similarity index 80% rename from app/soapbox/features/federation_restrictions/components/instance_restrictions.js rename to app/soapbox/features/federation_restrictions/components/instance_restrictions.tsx index fdb3cd328..ac86039fd 100644 --- a/app/soapbox/features/federation_restrictions/components/instance_restrictions.js +++ b/app/soapbox/features/federation_restrictions/components/instance_restrictions.tsx @@ -1,39 +1,29 @@ 'use strict'; -import PropTypes from 'prop-types'; import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; import { FormattedMessage } from 'react-intl'; -import { connect } from 'react-redux'; import Icon from 'soapbox/components/icon'; import { Text } from 'soapbox/components/ui'; +import { useAppSelector } from 'soapbox/hooks'; -const hasRestrictions = remoteInstance => { +import type { Map as ImmutableMap } from 'immutable'; + +const hasRestrictions = (remoteInstance: ImmutableMap): boolean => { return remoteInstance .get('federation') .deleteAll(['accept', 'reject_deletes', 'report_removal']) - .reduce((acc, value) => acc || value, false); + .reduce((acc: boolean, value: boolean) => acc || value, false); }; -const mapStateToProps = state => { - return { - instance: state.get('instance'), - }; -}; - -export default @connect(mapStateToProps) -class InstanceRestrictions extends ImmutablePureComponent { +interface IInstanceRestrictions { + remoteInstance: ImmutableMap, +} - static propTypes = { - intl: PropTypes.object.isRequired, - remoteInstance: ImmutablePropTypes.map.isRequired, - instance: ImmutablePropTypes.map, - }; +const InstanceRestrictions: React.FC = ({ remoteInstance }) => { + const instance = useAppSelector(state => state.instance); - renderRestrictions = () => { - const { remoteInstance } = this.props; + const renderRestrictions = () => { const items = []; const { @@ -105,10 +95,9 @@ class InstanceRestrictions extends ImmutablePureComponent { } return items; - } + }; - renderContent = () => { - const { instance, remoteInstance } = this.props; + const renderContent = () => { if (!instance || !remoteInstance) return null; const host = remoteInstance.get('host'); @@ -136,7 +125,7 @@ class InstanceRestrictions extends ImmutablePureComponent { /> ), - this.renderRestrictions(), + renderRestrictions(), ]; } else { return ( @@ -150,14 +139,13 @@ class InstanceRestrictions extends ImmutablePureComponent { ); } - } + }; - render() { - return ( -
    - {this.renderContent()} -
    - ); - } + return ( +
    + {renderContent()} +
    + ); +}; -} +export default InstanceRestrictions; diff --git a/app/soapbox/features/hashtag-timeline/index.tsx b/app/soapbox/features/hashtag-timeline/index.tsx index a8b97cbab..7ead1a9f5 100644 --- a/app/soapbox/features/hashtag-timeline/index.tsx +++ b/app/soapbox/features/hashtag-timeline/index.tsx @@ -3,10 +3,10 @@ import { FormattedMessage } from 'react-intl'; import { connectHashtagStream } from 'soapbox/actions/streaming'; import { expandHashtagTimeline, clearTimeline } from 'soapbox/actions/timelines'; -import ColumnHeader from 'soapbox/components/column_header'; +import SubNavigation from 'soapbox/components/sub_navigation'; import { Column } from 'soapbox/components/ui'; import Timeline from 'soapbox/features/ui/components/timeline'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch } from 'soapbox/hooks'; import type { Tag as TagEntity } from 'soapbox/types/entities'; @@ -27,7 +27,6 @@ export const HashtagTimeline: React.FC = ({ params }) => { const tags = params?.tags || { any: [], all: [], none: [] }; const dispatch = useAppDispatch(); - const hasUnread = useAppSelector(state => (state.timelines.getIn([`hashtag:${id}`, 'unread']) as number) > 0); const disconnects = useRef<(() => void)[]>([]); // Mastodon supports displaying results from multiple hashtags. @@ -100,7 +99,10 @@ export const HashtagTimeline: React.FC = ({ params }) => { return ( - +
    + +
    + -
    -

    - -

    -
    - -
    -
    - -
    -
    - } /> -
    - -
    - } /> -
    - -
    - } /> -
    -
    -
    - ); - } - -} diff --git a/app/soapbox/features/home_timeline/containers/column_settings_container.js b/app/soapbox/features/home_timeline/containers/column_settings_container.js deleted file mode 100644 index 0bcbafae8..000000000 --- a/app/soapbox/features/home_timeline/containers/column_settings_container.js +++ /dev/null @@ -1,26 +0,0 @@ -import { connect } from 'react-redux'; - -import { - getSettings, - changeSetting, - saveSettings, -} from '../../../actions/settings'; -import ColumnSettings from '../components/column_settings'; - -const mapStateToProps = state => ({ - settings: getSettings(state).get('home'), -}); - -const mapDispatchToProps = dispatch => ({ - - onChange(key, checked) { - dispatch(changeSetting(['home', ...key], checked)); - }, - - onSave() { - dispatch(saveSettings()); - }, - -}); - -export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings); diff --git a/app/soapbox/features/notifications/components/column_settings.js b/app/soapbox/features/notifications/components/column_settings.js deleted file mode 100644 index b71e8fa30..000000000 --- a/app/soapbox/features/notifications/components/column_settings.js +++ /dev/null @@ -1,192 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; - -import IconButton from 'soapbox/components/icon_button'; - -import ClearColumnButton from './clear_column_button'; -import MultiSettingToggle from './multi_setting_toggle'; -import SettingToggle from './setting_toggle'; - -const messages = defineMessages({ - close: { id: 'lightbox.close', defaultMessage: 'Close' }, -}); - -export default @injectIntl -class ColumnSettings extends React.PureComponent { - - static propTypes = { - intl: PropTypes.object.isRequired, - settings: ImmutablePropTypes.map.isRequired, - pushSettings: ImmutablePropTypes.map.isRequired, - onChange: PropTypes.func.isRequired, - onClear: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - supportsEmojiReacts: PropTypes.bool, - supportsBirthdays: PropTypes.bool, - }; - - onPushChange = (path, checked) => { - this.props.onChange(['push', ...path], checked); - } - - onAllSoundsChange = (path, checked) => { - const soundSettings = [['sounds', 'follow'], ['sounds', 'favourite'], ['sounds', 'pleroma:emoji_reaction'], ['sounds', 'mention'], ['sounds', 'reblog'], ['sounds', 'poll'], ['sounds', 'move']]; - - for (let i = 0; i < soundSettings.length; i++) { - this.props.onChange(soundSettings[i], checked); - } - } - - render() { - const { intl, settings, pushSettings, onChange, onClear, onClose, supportsEmojiReacts, supportsBirthdays } = this.props; - - const filterShowStr = ; - const filterAdvancedStr = ; - const alertStr = ; - const allSoundsStr = ; - const showStr = ; - const soundStr = ; - const soundSettings = [['sounds', 'follow'], ['sounds', 'favourite'], ['sounds', 'pleroma:emoji_reaction'], ['sounds', 'mention'], ['sounds', 'reblog'], ['sounds', 'poll'], ['sounds', 'move']]; - const showPushSettings = pushSettings.get('browserSupport') && pushSettings.get('isSubscribed'); - const pushStr = showPushSettings && ; - const birthdaysStr = ; - - return ( -
    -
    -

    - -

    -
    - -
    -
    - -
    -
    - -
    - -
    - - - - -
    - -
    - - - -
    - - -
    -
    - - {supportsBirthdays && -
    - - - -
    - -
    -
    - } - -
    - - -
    - - {showPushSettings && } - - -
    -
    - -
    - - -
    - - {showPushSettings && } - - -
    -
    - -
    - - -
    - - {showPushSettings && } - - -
    -
    - - {supportsEmojiReacts &&
    - - -
    - - {showPushSettings && } - - -
    -
    } - -
    - - -
    - - {showPushSettings && } - - -
    -
    - -
    - - -
    - - {showPushSettings && } - - -
    -
    - -
    - - -
    - - {showPushSettings && } - - -
    -
    - -
    - - -
    - - {showPushSettings && } - - -
    -
    -
    -
    - ); - } - -} diff --git a/app/soapbox/features/notifications/components/follow_request.js b/app/soapbox/features/notifications/components/follow_request.js deleted file mode 100644 index be781a948..000000000 --- a/app/soapbox/features/notifications/components/follow_request.js +++ /dev/null @@ -1,60 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Fragment } from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { defineMessages, injectIntl } from 'react-intl'; - -import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display-name'; -import IconButton from 'soapbox/components/icon_button'; -import Permalink from 'soapbox/components/permalink'; - -const messages = defineMessages({ - authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' }, - reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }, -}); - -export default @injectIntl -class FollowRequest extends ImmutablePureComponent { - - static propTypes = { - account: ImmutablePropTypes.record.isRequired, - onAuthorize: PropTypes.func.isRequired, - onReject: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, - }; - - render() { - const { intl, hidden, account, onAuthorize, onReject } = this.props; - - if (!account) { - return
    ; - } - - if (hidden) { - return ( - - {account.get('display_name')} - {account.get('username')} - - ); - } - - return ( -
    -
    - -
    - -
    - -
    - - -
    -
    -
    - ); - } - -} diff --git a/app/soapbox/features/notifications/components/multi_setting_toggle.js b/app/soapbox/features/notifications/components/multi_setting_toggle.js deleted file mode 100644 index 68c382872..000000000 --- a/app/soapbox/features/notifications/components/multi_setting_toggle.js +++ /dev/null @@ -1,39 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import Toggle from 'react-toggle'; - -export default class MultiSettingToggle extends React.PureComponent { - - static propTypes = { - prefix: PropTypes.string, - settings: ImmutablePropTypes.map.isRequired, - settingPaths: PropTypes.array.isRequired, - label: PropTypes.node, - onChange: PropTypes.func.isRequired, - ariaLabel: PropTypes.string, - } - - onChange = ({ target }) => { - for (let i = 0; i < this.props.settingPaths.length; i++) { - this.props.onChange(this.props.settingPaths[i], target.checked); - } - } - - areTrue = (settingPath) => { - return this.props.settings.getIn(settingPath) === true; - } - - render() { - const { prefix, settingPaths, label, ariaLabel } = this.props; - const id = ['setting-toggle', prefix].filter(Boolean).join('-'); - - return ( -
    - - {label && ()} -
    - ); - } - -} diff --git a/app/soapbox/features/notifications/containers/column_settings_container.js b/app/soapbox/features/notifications/containers/column_settings_container.js deleted file mode 100644 index 6375e59f5..000000000 --- a/app/soapbox/features/notifications/containers/column_settings_container.js +++ /dev/null @@ -1,55 +0,0 @@ -import { defineMessages, injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; - -import { openModal } from 'soapbox/actions/modals'; -import { clearNotifications, setFilter } from 'soapbox/actions/notifications'; -import { changeAlerts as changePushNotifications } from 'soapbox/actions/push_notifications'; -import { getSettings, changeSetting } from 'soapbox/actions/settings'; -import { getFeatures } from 'soapbox/utils/features'; - -import ColumnSettings from '../components/column_settings'; - -const messages = defineMessages({ - clearHeading: { id: 'notifications.clear_heading', defaultMessage: 'Clear notifications' }, - clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' }, - clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' }, -}); - -const mapStateToProps = state => { - const instance = state.get('instance'); - const features = getFeatures(instance); - - return { - settings: getSettings(state).get('notifications'), - pushSettings: state.get('push_notifications'), - supportsEmojiReacts: features.emojiReacts, - supportsBirthdays: features.birthdays, - }; -}; - -const mapDispatchToProps = (dispatch, { intl }) => ({ - - onChange(path, checked) { - if (path[0] === 'push') { - dispatch(changePushNotifications(path.slice(1), checked)); - } else if (path[0] === 'quickFilter') { - dispatch(changeSetting(['notifications', ...path], checked)); - dispatch(setFilter('all')); - } else { - dispatch(changeSetting(['notifications', ...path], checked)); - } - }, - - onClear() { - dispatch(openModal('CONFIRM', { - icon: require('@tabler/icons/eraser.svg'), - heading: intl.formatMessage(messages.clearHeading), - message: intl.formatMessage(messages.clearMessage), - confirm: intl.formatMessage(messages.clearConfirm), - onConfirm: () => dispatch(clearNotifications()), - })); - }, - -}); - -export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ColumnSettings)); diff --git a/app/soapbox/features/notifications/containers/follow_request_container.js b/app/soapbox/features/notifications/containers/follow_request_container.js deleted file mode 100644 index c793ac3de..000000000 --- a/app/soapbox/features/notifications/containers/follow_request_container.js +++ /dev/null @@ -1,28 +0,0 @@ -import { connect } from 'react-redux'; - -import { authorizeFollowRequest, rejectFollowRequest } from 'soapbox/actions/accounts'; -import { makeGetAccount } from 'soapbox/selectors'; - -import FollowRequest from '../components/follow_request'; - -const makeMapStateToProps = () => { - const getAccount = makeGetAccount(); - - const mapStateToProps = (state, props) => ({ - account: getAccount(state, props.id), - }); - - return mapStateToProps; -}; - -const mapDispatchToProps = (dispatch, { id }) => ({ - onAuthorize() { - dispatch(authorizeFollowRequest(id)); - }, - - onReject() { - dispatch(rejectFollowRequest(id)); - }, -}); - -export default connect(makeMapStateToProps, mapDispatchToProps)(FollowRequest); diff --git a/app/soapbox/features/public_timeline/components/column_settings.js b/app/soapbox/features/public_timeline/components/column_settings.js deleted file mode 100644 index 1d7022fa4..000000000 --- a/app/soapbox/features/public_timeline/components/column_settings.js +++ /dev/null @@ -1,55 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; - -import IconButton from 'soapbox/components/icon_button'; - -import SettingToggle from '../../notifications/components/setting_toggle'; - -const messages = defineMessages({ - close: { id: 'lightbox.close', defaultMessage: 'Close' }, -}); - -export default @injectIntl -class ColumnSettings extends React.PureComponent { - - static propTypes = { - intl: PropTypes.object.isRequired, - settings: ImmutablePropTypes.map.isRequired, - onChange: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - }; - - render() { - const { intl, settings, onChange, onClose } = this.props; - - return ( -
    -
    -

    - -

    -
    - -
    -
    - -
    -
    - } /> -
    - -
    - } /> -
    - -
    - } /> -
    -
    -
    - ); - } - -} diff --git a/app/soapbox/features/public_timeline/containers/column_settings_container.js b/app/soapbox/features/public_timeline/containers/column_settings_container.js deleted file mode 100644 index 63a629007..000000000 --- a/app/soapbox/features/public_timeline/containers/column_settings_container.js +++ /dev/null @@ -1,18 +0,0 @@ -import { connect } from 'react-redux'; - -import { getSettings, changeSetting } from '../../../actions/settings'; -import ColumnSettings from '../components/column_settings'; - -const mapStateToProps = state => ({ - settings: getSettings(state).get('public'), -}); - -const mapDispatchToProps = (dispatch) => { - return { - onChange(key, checked) { - dispatch(changeSetting(['public', ...key], checked)); - }, - }; -}; - -export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings); diff --git a/app/soapbox/features/public_timeline/index.tsx b/app/soapbox/features/public_timeline/index.tsx index 6fe5afec8..34017a0d3 100644 --- a/app/soapbox/features/public_timeline/index.tsx +++ b/app/soapbox/features/public_timeline/index.tsx @@ -14,8 +14,6 @@ import { useAppDispatch, useAppSelector, useSettings } from 'soapbox/hooks'; import PinnedHostsPicker from '../remote_timeline/components/pinned_hosts_picker'; import Timeline from '../ui/components/timeline'; -import ColumnSettings from './containers/column_settings_container'; - const messages = defineMessages({ title: { id: 'column.public', defaultMessage: 'Fediverse timeline' }, dismiss: { id: 'fediverse_tab.explanation_box.dismiss', defaultMessage: 'Don\'t show again' }, @@ -65,8 +63,12 @@ const CommunityTimeline = () => { return ( - +
    + +
    + + {showExplanationBox &&
    } diff --git a/app/soapbox/features/soapbox_config/components/icon-picker-menu.js b/app/soapbox/features/soapbox_config/components/icon-picker-menu.js new file mode 100644 index 000000000..c89b00488 --- /dev/null +++ b/app/soapbox/features/soapbox_config/components/icon-picker-menu.js @@ -0,0 +1,154 @@ +import classNames from 'clsx'; +import { supportsPassiveEvents } from 'detect-passive-events'; +import Picker from 'emoji-mart/dist-es/components/picker/picker'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { defineMessages, injectIntl } from 'react-intl'; + +const messages = defineMessages({ + emoji: { id: 'icon_button.label', defaultMessage: 'Select icon' }, + emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search…' }, + emoji_not_found: { id: 'icon_button.not_found', defaultMessage: 'No icons!! (╯°□°)╯︵ ┻━┻' }, + custom: { id: 'icon_button.icons', defaultMessage: 'Icons' }, + search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' }, +}); + +const backgroundImageFn = () => ''; +const listenerOptions = supportsPassiveEvents ? { passive: true } : false; + +const categoriesSort = ['custom']; + + +class IconPickerMenu extends React.PureComponent { + + static propTypes = { + custom_emojis: PropTypes.object, + loading: PropTypes.bool, + onClose: PropTypes.func.isRequired, + onPick: PropTypes.func.isRequired, + style: PropTypes.object, + placement: PropTypes.string, + arrowOffsetLeft: PropTypes.string, + arrowOffsetTop: PropTypes.string, + intl: PropTypes.object.isRequired, + }; + + static defaultProps = { + style: {}, + loading: true, + }; + + state = { + modifierOpen: false, + placement: null, + }; + + handleDocumentClick = e => { + if (this.node && !this.node.contains(e.target)) { + this.props.onClose(); + } + } + + componentDidMount() { + document.addEventListener('click', this.handleDocumentClick, false); + document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); + } + + componentWillUnmount() { + document.removeEventListener('click', this.handleDocumentClick, false); + document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); + } + + setRef = c => { + this.node = c; + + if (!c) return; + + // Nice and dirty hack to display the icons + c.querySelectorAll('button.emoji-mart-emoji > img').forEach(elem => { + const newIcon = document.createElement('span'); + newIcon.innerHTML = ``; + elem.parentNode.replaceChild(newIcon, elem); + }); + } + + getI18n = () => { + const { intl } = this.props; + + return { + search: intl.formatMessage(messages.emoji_search), + notfound: intl.formatMessage(messages.emoji_not_found), + categories: { + search: intl.formatMessage(messages.search_results), + custom: intl.formatMessage(messages.custom), + }, + }; + } + + handleClick = emoji => { + emoji.native = emoji.colons; + + this.props.onClose(); + this.props.onPick(emoji); + } + + buildIcons = (customEmojis, autoplay = false) => { + const emojis = []; + + Object.values(customEmojis).forEach(category => { + category.forEach(function(icon) { + const name = icon.replace('fa fa-', ''); + if (icon !== 'email' && icon !== 'memo') { + emojis.push({ + id: name, + name, + short_names: [name], + emoticons: [], + keywords: [name], + imageUrl: '', + }); + } + }); + }); + + return emojis; + }; + + render() { + const { loading, style, intl, custom_emojis } = this.props; + + if (loading) { + return
    ; + } + + const data = { compressed: true, categories: [], aliases: [], emojis: [] }; + const title = intl.formatMessage(messages.emoji); + const { modifierOpen } = this.state; + + return ( +
    + +
    + ); + } + +} + +export default injectIntl(IconPickerMenu); diff --git a/app/soapbox/features/soapbox_config/components/icon_picker_dropdown.js b/app/soapbox/features/soapbox_config/components/icon_picker_dropdown.js index cc2986a7b..5393f0dc8 100644 --- a/app/soapbox/features/soapbox_config/components/icon_picker_dropdown.js +++ b/app/soapbox/features/soapbox_config/components/icon_picker_dropdown.js @@ -1,6 +1,3 @@ -import classNames from 'clsx'; -import { supportsPassiveEvents } from 'detect-passive-events'; -import Picker from 'emoji-mart/dist-es/components/picker/picker'; import PropTypes from 'prop-types'; import React from 'react'; import { defineMessages, injectIntl } from 'react-intl'; @@ -8,153 +5,12 @@ import Overlay from 'react-overlays/lib/Overlay'; import Icon from 'soapbox/components/icon'; +import IconPickerMenu from './icon-picker-menu'; + const messages = defineMessages({ emoji: { id: 'icon_button.label', defaultMessage: 'Select icon' }, - emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search…' }, - emoji_not_found: { id: 'icon_button.not_found', defaultMessage: 'No icons!! (╯°□°)╯︵ ┻━┻' }, - custom: { id: 'icon_button.icons', defaultMessage: 'Icons' }, - search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' }, }); -const backgroundImageFn = () => ''; -const listenerOptions = supportsPassiveEvents ? { passive: true } : false; - -const categoriesSort = ['custom']; - -@injectIntl -class IconPickerMenu extends React.PureComponent { - - static propTypes = { - custom_emojis: PropTypes.object, - loading: PropTypes.bool, - onClose: PropTypes.func.isRequired, - onPick: PropTypes.func.isRequired, - style: PropTypes.object, - placement: PropTypes.string, - arrowOffsetLeft: PropTypes.string, - arrowOffsetTop: PropTypes.string, - intl: PropTypes.object.isRequired, - }; - - static defaultProps = { - style: {}, - loading: true, - }; - - state = { - modifierOpen: false, - placement: null, - }; - - handleDocumentClick = e => { - if (this.node && !this.node.contains(e.target)) { - this.props.onClose(); - } - } - - componentDidMount() { - document.addEventListener('click', this.handleDocumentClick, false); - document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); - } - - componentWillUnmount() { - document.removeEventListener('click', this.handleDocumentClick, false); - document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); - } - - setRef = c => { - this.node = c; - - if (!c) return; - - // Nice and dirty hack to display the icons - c.querySelectorAll('button.emoji-mart-emoji > img').forEach(elem => { - const newIcon = document.createElement('span'); - newIcon.innerHTML = ``; - elem.parentNode.replaceChild(newIcon, elem); - }); - } - - getI18n = () => { - const { intl } = this.props; - - return { - search: intl.formatMessage(messages.emoji_search), - notfound: intl.formatMessage(messages.emoji_not_found), - categories: { - search: intl.formatMessage(messages.search_results), - custom: intl.formatMessage(messages.custom), - }, - }; - } - - handleClick = emoji => { - emoji.native = emoji.colons; - - this.props.onClose(); - this.props.onPick(emoji); - } - - buildIcons = (customEmojis, autoplay = false) => { - const emojis = []; - - Object.values(customEmojis).forEach(category => { - category.forEach(function(icon) { - const name = icon.replace('fa fa-', ''); - if (icon !== 'email' && icon !== 'memo') { - emojis.push({ - id: name, - name, - short_names: [name], - emoticons: [], - keywords: [name], - imageUrl: '', - }); - } - }); - }); - - return emojis; - }; - - render() { - const { loading, style, intl, custom_emojis } = this.props; - - if (loading) { - return
    ; - } - - const data = { compressed: true, categories: [], aliases: [], emojis: [] }; - const title = intl.formatMessage(messages.emoji); - const { modifierOpen } = this.state; - - return ( -
    - -
    - ); - } - -} - -export default @injectIntl class IconPickerDropdown extends React.PureComponent { static propTypes = { @@ -243,3 +99,5 @@ class IconPickerDropdown extends React.PureComponent { } } + +export default injectIntl(IconPickerDropdown); \ No newline at end of file diff --git a/app/soapbox/features/status/components/detailed-status.tsx b/app/soapbox/features/status/components/detailed-status.tsx index 28c2d020c..eb6fb240f 100644 --- a/app/soapbox/features/status/components/detailed-status.tsx +++ b/app/soapbox/features/status/components/detailed-status.tsx @@ -7,6 +7,7 @@ import StatusMedia from 'soapbox/components/status-media'; import StatusReplyMentions from 'soapbox/components/status-reply-mentions'; import StatusContent from 'soapbox/components/status_content'; import SensitiveContentOverlay from 'soapbox/components/statuses/sensitive-content-overlay'; +import TranslateButton from 'soapbox/components/translate-button'; import { HStack, Stack, Text } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account_container'; import QuotedStatus from 'soapbox/features/status/containers/quoted_status_container'; @@ -101,9 +102,11 @@ const DetailedStatus: React.FC = ({ )} - + - {(quote || actualStatus.media_attachments.size > 0) && ( + + + {(quote || actualStatus.card || actualStatus.media_attachments.size > 0) && ( -
    - - - {title} - -
    -
    -
    - {accountIds.take(limit).map(accountId => ( - - ))} -
    -
    - {canExpand && - {expandMessage} - } -
    - ); - } - -} diff --git a/app/soapbox/features/ui/components/mute_modal.tsx b/app/soapbox/features/ui/components/mute_modal.tsx index be0eda49a..44ba725fe 100644 --- a/app/soapbox/features/ui/components/mute_modal.tsx +++ b/app/soapbox/features/ui/components/mute_modal.tsx @@ -4,9 +4,10 @@ import Toggle from 'react-toggle'; import { muteAccount } from 'soapbox/actions/accounts'; import { closeModal } from 'soapbox/actions/modals'; -import { toggleHideNotifications } from 'soapbox/actions/mutes'; +import { toggleHideNotifications, changeMuteDuration } from 'soapbox/actions/mutes'; import { Modal, HStack, Stack, Text } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import DurationSelector from 'soapbox/features/compose/components/polls/duration-selector'; +import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { makeGetAccount } from 'soapbox/selectors'; const getAccount = makeGetAccount(); @@ -16,12 +17,14 @@ const MuteModal = () => { const account = useAppSelector((state) => getAccount(state, state.mutes.new.accountId!)); const notifications = useAppSelector((state) => state.mutes.new.notifications); + const duration = useAppSelector((state) => state.mutes.new.duration); + const mutesDuration = useFeatures().mutesDuration; if (!account) return null; const handleClick = () => { dispatch(closeModal()); - dispatch(muteAccount(account.id, notifications)); + dispatch(muteAccount(account.id, notifications, duration)); }; const handleCancel = () => { @@ -32,6 +35,12 @@ const MuteModal = () => { dispatch(toggleHideNotifications()); }; + const handleChangeMuteDuration = (expiresIn: number): void => { + dispatch(changeMuteDuration(expiresIn)); + }; + + const toggleAutoExpire = () => handleChangeMuteDuration(duration ? 0 : 2 * 60 * 60 * 24); + return ( { /> + + {mutesDuration && ( + <> + + + {duration !== 0 && ( + + : + + + + )} + + )} ); diff --git a/app/soapbox/features/ui/components/profile-dropdown.tsx b/app/soapbox/features/ui/components/profile-dropdown.tsx index 04d535add..318c62e35 100644 --- a/app/soapbox/features/ui/components/profile-dropdown.tsx +++ b/app/soapbox/features/ui/components/profile-dropdown.tsx @@ -39,6 +39,8 @@ const ProfileDropdown: React.FC = ({ account, children }) => { const features = useFeatures(); const intl = useIntl(); + useAppSelector((state) => console.log(state.auth.toJS())); + const authUsers = useAppSelector((state) => state.auth.get('users')); const otherAccounts = useAppSelector((state) => authUsers.map((authUser: any) => getAccount(state, authUser.get('id')))); diff --git a/app/soapbox/features/ui/containers/notifications_container.tsx b/app/soapbox/features/ui/containers/notifications_container.tsx index 79d8724c1..2119228bf 100644 --- a/app/soapbox/features/ui/containers/notifications_container.tsx +++ b/app/soapbox/features/ui/containers/notifications_container.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { useIntl, MessageDescriptor } from 'react-intl'; +import { NotificationStack, NotificationObject, StyleFactoryFn } from 'react-notification'; import { useHistory } from 'react-router-dom'; import { dismissAlert } from 'soapbox/actions/alerts'; import { Button } from 'soapbox/components/ui'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; -import { NotificationStack, NotificationObject, StyleFactoryFn } from 'soapbox/react-notification'; import type { Alert } from 'soapbox/reducers/alerts'; diff --git a/app/soapbox/features/ui/index.tsx b/app/soapbox/features/ui/index.tsx index 3a8ae388a..23a1d36ed 100644 --- a/app/soapbox/features/ui/index.tsx +++ b/app/soapbox/features/ui/index.tsx @@ -106,6 +106,7 @@ import { TestTimeline, LogoutPage, AuthTokenList, + ServiceWorkerInfo, } from './util/async-components'; import { WrappedRoute } from './util/react_router_helpers'; @@ -295,6 +296,7 @@ const SwitchingColumnsArea: React.FC = ({ children }) => { + new Promise((_resolve, reject) => reject())} content={children} /> diff --git a/app/soapbox/features/ui/util/async-components.ts b/app/soapbox/features/ui/util/async-components.ts index 5f83d0ed2..d59fe2cb2 100644 --- a/app/soapbox/features/ui/util/async-components.ts +++ b/app/soapbox/features/ui/util/async-components.ts @@ -470,6 +470,10 @@ export function TestTimeline() { return import(/* webpackChunkName: "features/test_timeline" */'../../test_timeline'); } +export function ServiceWorkerInfo() { + return import(/* webpackChunkName: "features/developers" */'../../developers/service-worker-info'); +} + export function DatePicker() { return import(/* webpackChunkName: "date_picker" */'../../birthdays/date_picker'); } diff --git a/app/soapbox/locales/defaultMessages.json b/app/soapbox/locales/defaultMessages.json index fca9d50cd..1ef84f4e3 100644 --- a/app/soapbox/locales/defaultMessages.json +++ b/app/soapbox/locales/defaultMessages.json @@ -48,6 +48,10 @@ "defaultMessage": "Video exceeds the current file size limit ({limit})", "id": "upload_error.video_size_limit" }, + { + "defaultMessage": "Video exceeds the current duration limit ({limit} seconds)", + "id": "upload_error.video_duration_limit" + }, { "defaultMessage": "You must schedule a post at least 5 minutes out.", "id": "compose.invalid_schedule" @@ -56,6 +60,10 @@ "defaultMessage": "Your post was sent", "id": "compose.submit_success" }, + { + "defaultMessage": "Your post was edited", + "id": "compose.edit_success" + }, { "defaultMessage": "File upload limit exceeded.", "id": "upload_error.limit" @@ -67,6 +75,14 @@ { "defaultMessage": "View", "id": "snackbar.view" + }, + { + "defaultMessage": "Reply", + "id": "confirmations.reply.confirm" + }, + { + "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "id": "confirmations.reply.message" } ], "path": "app/soapbox/actions/compose.json" @@ -265,7 +281,7 @@ "id": "status_list.queue_label" } ], - "path": "app/soapbox/components/__tests__/timeline_queue_button_header-test.json" + "path": "app/soapbox/components/__tests__/scroll-top-button.test.json" }, { "descriptors": [ @@ -285,15 +301,6 @@ ], "path": "app/soapbox/components/account_search.json" }, - { - "descriptors": [ - { - "defaultMessage": "Follows you", - "id": "account.follows_you" - } - ], - "path": "app/soapbox/components/account.json" - }, { "descriptors": [ { @@ -319,23 +326,6 @@ ], "path": "app/soapbox/components/birthday_input.json" }, - { - "descriptors": [ - { - "defaultMessage": "{name} has a birthday today", - "id": "notification.birthday" - }, - { - "defaultMessage": "{name} and {more} have birthday today", - "id": "notification.birthday_plural" - }, - { - "defaultMessage": "{count} more {count, plural, one {friend} other {friends}}", - "id": "notification.birthday.more" - } - ], - "path": "app/soapbox/components/birthday_reminders.json" - }, { "descriptors": [ { @@ -431,15 +421,6 @@ ], "path": "app/soapbox/components/load_more.json" }, - { - "descriptors": [ - { - "defaultMessage": "Loading…", - "id": "loading_indicator.label" - } - ], - "path": "app/soapbox/components/loading_indicator.json" - }, { "descriptors": [ { @@ -532,56 +513,91 @@ { "descriptors": [ { - "defaultMessage": "Home", - "id": "tabs_bar.home" + "defaultMessage": "Closed", + "id": "poll.closed" }, { - "defaultMessage": "Search", - "id": "navigation.search" + "defaultMessage": "Other instances may display the options you voted for", + "id": "poll.non_anonymous.label" }, { - "defaultMessage": "Notifications", - "id": "tabs_bar.notifications" + "defaultMessage": "{count, plural, one {# person} other {# people}}", + "id": "poll.total_people" }, { - "defaultMessage": "Chats", - "id": "tabs_bar.chats" + "defaultMessage": "{count, plural, one {# vote} other {# votes}}", + "id": "poll.total_votes" }, { - "defaultMessage": "Messages", - "id": "navigation.direct_messages" + "defaultMessage": "Vote", + "id": "poll.vote" }, { - "defaultMessage": "Dashboard", - "id": "tabs_bar.dashboard" + "defaultMessage": "Public poll", + "id": "poll.non_anonymous" }, { - "defaultMessage": "Invites", - "id": "navigation.invites" - }, + "defaultMessage": "Refresh", + "id": "poll.refresh" + } + ], + "path": "app/soapbox/components/polls/poll-footer.json" + }, + { + "descriptors": [ { - "defaultMessage": "Developers", - "id": "navigation.developers" + "defaultMessage": "You voted for this answer", + "id": "poll.voted" }, { - "defaultMessage": "All", - "id": "tabs_bar.all" - }, + "defaultMessage": "{votes, plural, one {# vote} other {# votes}}", + "id": "poll.votes" + } + ], + "path": "app/soapbox/components/polls/poll-option.json" + }, + { + "descriptors": [ { - "defaultMessage": "Fediverse", - "id": "tabs_bar.fediverse" + "defaultMessage": "Choose as many as you'd like.", + "id": "poll.choose_multiple" } ], - "path": "app/soapbox/components/primary_navigation.json" + "path": "app/soapbox/components/polls/poll.json" }, { "descriptors": [ + { + "defaultMessage": "Joined {date}", + "id": "account.member_since" + }, { "defaultMessage": "Follows you", "id": "account.follows_you" } ], - "path": "app/soapbox/components/profile_hover_card.json" + "path": "app/soapbox/components/profile-hover-card.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Cancel", + "id": "reply_indicator.cancel" + }, + { + "defaultMessage": "Replying to {accounts}", + "id": "reply_mentions.reply" + }, + { + "defaultMessage": "Replying to post", + "id": "reply_mentions.reply_empty" + }, + { + "defaultMessage": "{count} more", + "id": "reply_mentions.more" + } + ], + "path": "app/soapbox/components/quoted-status.json" }, { "descriptors": [ @@ -628,6 +644,51 @@ ], "path": "app/soapbox/components/relative_timestamp.json" }, + { + "descriptors": [ + { + "defaultMessage": "now", + "id": "relative_time.just_now" + }, + { + "defaultMessage": "{number}s", + "id": "relative_time.seconds" + }, + { + "defaultMessage": "{number}m", + "id": "relative_time.minutes" + }, + { + "defaultMessage": "{number}h", + "id": "relative_time.hours" + }, + { + "defaultMessage": "{number}d", + "id": "relative_time.days" + }, + { + "defaultMessage": "Moments remaining", + "id": "time_remaining.moments" + }, + { + "defaultMessage": "{number, plural, one {# second} other {# seconds}} left", + "id": "time_remaining.seconds" + }, + { + "defaultMessage": "{number, plural, one {# minute} other {# minutes}} left", + "id": "time_remaining.minutes" + }, + { + "defaultMessage": "{number, plural, one {# hour} other {# hours}} left", + "id": "time_remaining.hours" + }, + { + "defaultMessage": "{number, plural, one {# day} other {# days}} left", + "id": "time_remaining.days" + } + ], + "path": "app/soapbox/components/relative-timestamp.json" + }, { "descriptors": [ { @@ -687,10 +748,18 @@ "defaultMessage": "Move account", "id": "navigation_bar.account_migration" }, + { + "defaultMessage": "Account aliases", + "id": "navigation_bar.account_aliases" + }, { "defaultMessage": "Logout", "id": "navigation_bar.logout" }, + { + "defaultMessage": "Switch accounts", + "id": "tabs_bar.switch_accounts" + }, { "defaultMessage": "Bookmarks", "id": "column.bookmarks" @@ -736,10 +805,6 @@ "defaultMessage": "Lists", "id": "column.lists" }, - { - "defaultMessage": "Invites", - "id": "navigation.invites" - }, { "defaultMessage": "Developers", "id": "navigation.developers" @@ -769,13 +834,17 @@ "id": "tabs_bar.home" }, { - "defaultMessage": "Profile", - "id": "tabs_bar.profile" + "defaultMessage": "Search", + "id": "tabs_bar.search" }, { "defaultMessage": "Notifications", "id": "tabs_bar.notifications" }, + { + "defaultMessage": "Profile", + "id": "tabs_bar.profile" + }, { "defaultMessage": "Settings", "id": "tabs_bar.settings" @@ -797,6 +866,10 @@ "defaultMessage": "Delete & re-draft", "id": "status.redraft" }, + { + "defaultMessage": "Edit", + "id": "status.edit" + }, { "defaultMessage": "Direct message @{name}", "id": "status.direct" @@ -965,24 +1038,12 @@ { "defaultMessage": "Read more", "id": "status.read_more" - }, - { - "defaultMessage": "Show more", - "id": "status.show_more" - }, - { - "defaultMessage": "Show less", - "id": "status.show_less" } ], "path": "app/soapbox/components/status_content.json" }, { "descriptors": [ - { - "defaultMessage": "Click to see {count} new {count, plural, one {post} other {posts}}", - "id": "status_list.queue_label" - }, { "defaultMessage": "Loading…", "id": "regeneration_indicator.label" @@ -1001,7 +1062,7 @@ "id": "reply_mentions.reply_empty" }, { - "defaultMessage": "Replying to {accounts}{more}", + "defaultMessage": "Replying to {accounts}{more}", "id": "reply_mentions.reply" }, { @@ -1014,83 +1075,384 @@ { "descriptors": [ { - "defaultMessage": "Filtered", - "id": "status.filtered" + "defaultMessage": "Delete", + "id": "status.delete" }, { - "defaultMessage": "Pinned post", - "id": "status.pinned" + "defaultMessage": "Delete & re-draft", + "id": "status.redraft" }, { - "defaultMessage": "{name} reposted", - "id": "status.reblogged_by" + "defaultMessage": "Edit", + "id": "status.edit" }, { - "defaultMessage": "Post is unavailable.", - "id": "statuses.quote_tombstone" - } - ], - "path": "app/soapbox/components/status.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Back", - "id": "column_back_button.label" + "defaultMessage": "Direct message @{name}", + "id": "status.direct" }, { - "defaultMessage": "Show settings", - "id": "column_header.show_settings" - } - ], - "path": "app/soapbox/components/sub_navigation.json" - }, - { - "descriptors": [ + "defaultMessage": "Chat with @{name}", + "id": "status.chat" + }, { - "defaultMessage": "Chats", - "id": "navigation.chats" + "defaultMessage": "Mention @{name}", + "id": "status.mention" }, { - "defaultMessage": "Messages", - "id": "navigation.direct_messages" + "defaultMessage": "Mute @{name}", + "id": "account.mute" }, { - "defaultMessage": "Home", - "id": "navigation.home" + "defaultMessage": "Block @{name}", + "id": "account.block" }, { - "defaultMessage": "Search", - "id": "navigation.search" + "defaultMessage": "Reply", + "id": "status.reply" }, { - "defaultMessage": "Alerts", - "id": "navigation.notifications" + "defaultMessage": "Share", + "id": "status.share" }, { - "defaultMessage": "Dashboard", - "id": "navigation.dashboard" - } - ], - "path": "app/soapbox/components/thumb_navigation.json" - }, - { - "descriptors": [ + "defaultMessage": "More", + "id": "status.more" + }, { - "defaultMessage": "Back", - "id": "card.back.label" - } - ], - "path": "app/soapbox/components/ui/card/card.json" - }, - { - "descriptors": [ + "defaultMessage": "Reply to thread", + "id": "status.replyAll" + }, { - "defaultMessage": "Show password", - "id": "input.password.show_password" + "defaultMessage": "Repost", + "id": "status.reblog" }, { - "defaultMessage": "Hide password", + "defaultMessage": "Repost to original audience", + "id": "status.reblog_private" + }, + { + "defaultMessage": "Un-repost", + "id": "status.cancel_reblog_private" + }, + { + "defaultMessage": "This post cannot be reposted", + "id": "status.cannot_reblog" + }, + { + "defaultMessage": "Like", + "id": "status.favourite" + }, + { + "defaultMessage": "Expand this post", + "id": "status.open" + }, + { + "defaultMessage": "Bookmark", + "id": "status.bookmark" + }, + { + "defaultMessage": "Remove bookmark", + "id": "status.unbookmark" + }, + { + "defaultMessage": "Report @{name}", + "id": "status.report" + }, + { + "defaultMessage": "Mute conversation", + "id": "status.mute_conversation" + }, + { + "defaultMessage": "Unmute conversation", + "id": "status.unmute_conversation" + }, + { + "defaultMessage": "Pin on profile", + "id": "status.pin" + }, + { + "defaultMessage": "Unpin from profile", + "id": "status.unpin" + }, + { + "defaultMessage": "Embed", + "id": "status.embed" + }, + { + "defaultMessage": "Moderate @{name}", + "id": "status.admin_account" + }, + { + "defaultMessage": "Open this post in the moderation interface", + "id": "status.admin_status" + }, + { + "defaultMessage": "Copy link to post", + "id": "status.copy" + }, + { + "defaultMessage": "Remove account from group", + "id": "status.remove_account_from_group" + }, + { + "defaultMessage": "Remove post from group", + "id": "status.remove_post_from_group" + }, + { + "defaultMessage": "Deactivate @{name}", + "id": "admin.users.actions.deactivate_user" + }, + { + "defaultMessage": "Delete @{name}", + "id": "admin.users.actions.delete_user" + }, + { + "defaultMessage": "Delete post", + "id": "admin.statuses.actions.delete_status" + }, + { + "defaultMessage": "Mark post sensitive", + "id": "admin.statuses.actions.mark_status_sensitive" + }, + { + "defaultMessage": "Mark post not sensitive", + "id": "admin.statuses.actions.mark_status_not_sensitive" + }, + { + "defaultMessage": "Like", + "id": "status.reactions.like" + }, + { + "defaultMessage": "Love", + "id": "status.reactions.heart" + }, + { + "defaultMessage": "Haha", + "id": "status.reactions.laughing" + }, + { + "defaultMessage": "Wow", + "id": "status.reactions.open_mouth" + }, + { + "defaultMessage": "Sad", + "id": "status.reactions.cry" + }, + { + "defaultMessage": "Weary", + "id": "status.reactions.weary" + }, + { + "defaultMessage": "Quote post", + "id": "status.quote" + }, + { + "defaultMessage": "Delete", + "id": "confirmations.delete.confirm" + }, + { + "defaultMessage": "Delete post", + "id": "confirmations.delete.heading" + }, + { + "defaultMessage": "Are you sure you want to delete this post?", + "id": "confirmations.delete.message" + }, + { + "defaultMessage": "Delete & redraft", + "id": "confirmations.redraft.confirm" + }, + { + "defaultMessage": "Are you sure you want to delete this post and re-draft it? Favorites and reposts will be lost, and replies to the original post will be orphaned.", + "id": "confirmations.redraft.message" + }, + { + "defaultMessage": "Block", + "id": "confirmations.block.confirm" + }, + { + "defaultMessage": "Reply", + "id": "confirmations.reply.confirm" + }, + { + "defaultMessage": "Delete & redraft", + "id": "confirmations.redraft.heading" + }, + { + "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "id": "confirmations.reply.message" + }, + { + "defaultMessage": "Block & Report", + "id": "confirmations.block.block_and_report" + }, + { + "defaultMessage": "Block @{name}", + "id": "confirmations.block.heading" + }, + { + "defaultMessage": "Are you sure you want to block {name}?", + "id": "confirmations.block.message" + } + ], + "path": "app/soapbox/components/status-action-bar.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Replying to post", + "id": "reply_mentions.reply_empty" + }, + { + "defaultMessage": "{count} more", + "id": "reply_mentions.more" + }, + { + "defaultMessage": "Replying to {accounts}", + "id": "reply_mentions.reply" + } + ], + "path": "app/soapbox/components/status-reply-mentions.json" + }, + { + "descriptors": [ + { + "defaultMessage": "{name} reposted", + "id": "status.reblogged_by" + }, + { + "defaultMessage": "Filtered", + "id": "status.filtered" + }, + { + "defaultMessage": "Post is unavailable.", + "id": "statuses.quote_tombstone" + }, + { + "defaultMessage": "Pinned post", + "id": "status.pinned" + } + ], + "path": "app/soapbox/components/status.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Hide content", + "id": "moderation_overlay.hide" + }, + { + "defaultMessage": "Sensitive content", + "id": "status.sensitive_warning" + }, + { + "defaultMessage": "Content Under Review", + "id": "moderation_overlay.title" + }, + { + "defaultMessage": "This Post has been sent to Moderation for review and is only visible to you. If you believe this is an error please contact Support.", + "id": "moderation_overlay.subtitle" + }, + { + "defaultMessage": "This content may not be suitable for all audiences.", + "id": "status.sensitive_warning.subtitle" + }, + { + "defaultMessage": "Contact", + "id": "moderation_overlay.contact" + }, + { + "defaultMessage": "Show Content", + "id": "moderation_overlay.show" + } + ], + "path": "app/soapbox/components/statuses/sensitive-content-overlay.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Back", + "id": "column_back_button.label" + }, + { + "defaultMessage": "Show settings", + "id": "column_header.show_settings" + } + ], + "path": "app/soapbox/components/sub_navigation.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Chats", + "id": "navigation.chats" + }, + { + "defaultMessage": "Messages", + "id": "navigation.direct_messages" + }, + { + "defaultMessage": "Home", + "id": "navigation.home" + }, + { + "defaultMessage": "Search", + "id": "navigation.search" + }, + { + "defaultMessage": "Alerts", + "id": "navigation.notifications" + }, + { + "defaultMessage": "Dashboard", + "id": "navigation.dashboard" + } + ], + "path": "app/soapbox/components/thumb_navigation.json" + }, + { + "descriptors": [ + { + "defaultMessage": "One or more posts are unavailable.", + "id": "statuses.tombstone" + } + ], + "path": "app/soapbox/components/tombstone.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Back", + "id": "card.back.label" + } + ], + "path": "app/soapbox/components/ui/card/card.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Month", + "id": "datepicker.month" + }, + { + "defaultMessage": "Day", + "id": "datepicker.day" + }, + { + "defaultMessage": "Year", + "id": "datepicker.year" + } + ], + "path": "app/soapbox/components/ui/datepicker/datepicker.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Show password", + "id": "input.password.show_password" + }, + { + "defaultMessage": "Hide password", "id": "input.password.hide_password" } ], @@ -1118,6 +1480,28 @@ ], "path": "app/soapbox/components/ui/spinner/spinner.json" }, + { + "descriptors": [ + { + "defaultMessage": "Add", + "id": "streamfield.add" + }, + { + "defaultMessage": "Remove", + "id": "streamfield.remove" + } + ], + "path": "app/soapbox/components/ui/streamfield/streamfield.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Uploading…", + "id": "upload_progress.label" + } + ], + "path": "app/soapbox/components/upload-progress.json" + }, { "descriptors": [ { @@ -1144,23 +1528,6 @@ ], "path": "app/soapbox/containers/account_container.json" }, - { - "descriptors": [ - { - "defaultMessage": "Hide entire domain", - "id": "confirmations.domain_block.confirm" - }, - { - "defaultMessage": "Block {domain}", - "id": "confirmations.domain_block.heading" - }, - { - "defaultMessage": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", - "id": "confirmations.domain_block.message" - } - ], - "path": "app/soapbox/containers/domain_container.json" - }, { "descriptors": [ { @@ -1236,31 +1603,6 @@ ], "path": "app/soapbox/features/account_gallery/index.json" }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Account timeline settings", - "id": "account.column_settings.title" - }, - { - "defaultMessage": "These settings apply to all account timelines.", - "id": "account.column_settings.description" - }, - { - "defaultMessage": "Show pinned posts", - "id": "account_timeline.column_settings.show_pinned" - }, - { - "defaultMessage": "Show reposts", - "id": "home.column_settings.show_reblogs" - } - ], - "path": "app/soapbox/features/account_timeline/components/column_settings.json" - }, { "descriptors": [ { @@ -1463,72 +1805,92 @@ "id": "account.endorse" }, { - "defaultMessage": "Don't feature on profile", - "id": "account.unendorse" + "defaultMessage": "Don't feature on profile", + "id": "account.unendorse" + }, + { + "defaultMessage": "Remove this follower", + "id": "account.remove_from_followers" + }, + { + "defaultMessage": "Moderate @{name}", + "id": "status.admin_account" + }, + { + "defaultMessage": "Add or Remove from lists", + "id": "account.add_or_remove_from_list" + }, + { + "defaultMessage": "Search from @{name}", + "id": "account.search" + }, + { + "defaultMessage": "Search your posts", + "id": "account.search_self" }, { - "defaultMessage": "Open moderation interface for @{name}", - "id": "status.admin_account" + "defaultMessage": "Unfollow", + "id": "confirmations.unfollow.confirm" }, { - "defaultMessage": "Add or Remove from lists", - "id": "account.add_or_remove_from_list" + "defaultMessage": "Block", + "id": "confirmations.block.confirm" }, { - "defaultMessage": "Deactivate @{name}", - "id": "admin.users.actions.deactivate_user" + "defaultMessage": "Hide entire domain", + "id": "confirmations.domain_block.confirm" }, { - "defaultMessage": "Delete @{name}", - "id": "admin.users.actions.delete_user" + "defaultMessage": "Block & Report", + "id": "confirmations.block.block_and_report" }, { - "defaultMessage": "Verify @{name}", - "id": "admin.users.actions.verify_user" + "defaultMessage": "Remove", + "id": "confirmations.remove_from_followers.confirm" }, { - "defaultMessage": "Unverify @{name}", - "id": "admin.users.actions.unverify_user" + "defaultMessage": "You are now featuring @{acct} on your profile", + "id": "account.endorse.success" }, { - "defaultMessage": "Set @{name} as a donor", - "id": "admin.users.actions.set_donor" + "defaultMessage": "You are no longer featuring @{acct}", + "id": "account.unendorse.success" }, { - "defaultMessage": "Remove @{name} as a donor", - "id": "admin.users.actions.remove_donor" + "defaultMessage": "Block @{name}", + "id": "confirmations.block.heading" }, { - "defaultMessage": "Promote @{name} to an admin", - "id": "admin.users.actions.promote_to_admin" + "defaultMessage": "Are you sure you want to block {name}?", + "id": "confirmations.block.message" }, { - "defaultMessage": "Promote @{name} to a moderator", - "id": "admin.users.actions.promote_to_moderator" + "defaultMessage": "Block {domain}", + "id": "confirmations.domain_block.heading" }, { - "defaultMessage": "Demote @{name} to a moderator", - "id": "admin.users.actions.demote_to_moderator" + "defaultMessage": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications.", + "id": "confirmations.domain_block.message" }, { - "defaultMessage": "Demote @{name} to a regular user", - "id": "admin.users.actions.demote_to_user" + "defaultMessage": "Are you sure you want to remove {name} from your followers?", + "id": "confirmations.remove_from_followers.message" }, { - "defaultMessage": "Subscribe to notifications from @{name}", - "id": "account.subscribe" + "defaultMessage": "Follows you", + "id": "account.follows_you" }, { - "defaultMessage": "Unsubscribe to notifications from @{name}", - "id": "account.unsubscribe" + "defaultMessage": "Blocked", + "id": "account.blocked" }, { - "defaultMessage": "Suggest @{name}", - "id": "admin.users.actions.suggest_user" + "defaultMessage": "Muted", + "id": "account.muted" }, { - "defaultMessage": "Unsuggest @{name}", - "id": "admin.users.actions.unsuggest_user" + "defaultMessage": "Domain hidden", + "id": "account.domain_blocked" } ], "path": "app/soapbox/features/account/components/header.json" @@ -1800,6 +2162,23 @@ ], "path": "app/soapbox/features/admin/user_index.json" }, + { + "descriptors": [ + { + "defaultMessage": "Sponsored post", + "id": "sponsored.subtitle" + }, + { + "defaultMessage": "Why am I seeing this ad?", + "id": "sponsored.info.title" + }, + { + "defaultMessage": "{siteTitle} displays ads to help fund our service.", + "id": "sponsored.info.message" + } + ], + "path": "app/soapbox/features/ads/components/ad.json" + }, { "descriptors": [ { @@ -1894,6 +2273,10 @@ }, { "descriptors": [ + { + "defaultMessage": "Enter the pictured text", + "id": "registration.captcha.placeholder" + }, { "defaultMessage": "Click the image to get a new captcha", "id": "registration.captcha.hint" @@ -1901,6 +2284,24 @@ ], "path": "app/soapbox/features/auth_login/components/captcha.json" }, + { + "descriptors": [ + { + "defaultMessage": "Sign in with {provider}", + "id": "oauth_consumer.tooltip" + } + ], + "path": "app/soapbox/features/auth_login/components/consumer-button.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Other ways to sign in", + "id": "oauth_consumers.title" + } + ], + "path": "app/soapbox/features/auth_login/components/consumers-list.json" + }, { "descriptors": [ { @@ -1911,6 +2312,10 @@ "defaultMessage": "Password", "id": "login.fields.password_placeholder" }, + { + "defaultMessage": "Sign In", + "id": "login_form.header" + }, { "defaultMessage": "Trouble logging in?", "id": "login.reset_password_hint" @@ -1932,6 +2337,10 @@ "defaultMessage": "Two-factor code:", "id": "login.fields.otp_code_label" }, + { + "defaultMessage": "Invalid code, please try again.", + "id": "login.otp_log_in.fail" + }, { "defaultMessage": "OTP Login", "id": "login.otp_log_in" @@ -1945,6 +2354,10 @@ }, { "descriptors": [ + { + "defaultMessage": "Expired token, please try again.", + "id": "reset_password.fail" + }, { "defaultMessage": "Set New Password", "id": "reset_password.header" @@ -1983,6 +2396,10 @@ "defaultMessage": "Only letters, numbers, and underscores are allowed.", "id": "registration.fields.username_hint" }, + { + "defaultMessage": "Username is already taken.", + "id": "registration.username_unavailable" + }, { "defaultMessage": "E-Mail address", "id": "registration.fields.email_placeholder" @@ -1991,6 +2408,10 @@ "defaultMessage": "Password", "id": "registration.fields.password_placeholder" }, + { + "defaultMessage": "Passwords don't match.", + "id": "registration.password_mismatch" + }, { "defaultMessage": "Password (again)", "id": "registration.fields.confirm_placeholder" @@ -2027,14 +2448,6 @@ "defaultMessage": "Your account will be manually approved by an admin. Please be patient while we review your details.", "id": "confirmations.register.needs_approval" }, - { - "defaultMessage": "Username is already taken.", - "id": "registration.username_unavailable" - }, - { - "defaultMessage": "Passwords don't match.", - "id": "registration.password_mismatch" - }, { "defaultMessage": "Why do you want to join?", "id": "registration.reason" @@ -2050,6 +2463,19 @@ ], "path": "app/soapbox/features/auth_login/components/registration_form.json" }, + { + "descriptors": [ + { + "defaultMessage": "Sessions", + "id": "security.headers.tokens" + }, + { + "defaultMessage": "Revoke", + "id": "security.tokens.revoke" + } + ], + "path": "app/soapbox/features/auth_token_list/index.json" + }, { "descriptors": [ { @@ -2263,27 +2689,198 @@ "id": "compose_form.publish" }, { - "defaultMessage": "{publish}!", - "id": "compose_form.publish_loud" + "defaultMessage": "{publish}!", + "id": "compose_form.publish_loud" + }, + { + "defaultMessage": "Message", + "id": "compose_form.message" + }, + { + "defaultMessage": "Schedule", + "id": "compose_form.schedule" + }, + { + "defaultMessage": "Save changes", + "id": "compose_form.save_changes" + }, + { + "defaultMessage": "You have scheduled posts. {click_here} to see them.", + "id": "compose_form.scheduled_statuses.message" + }, + { + "defaultMessage": "Click here", + "id": "compose_form.scheduled_statuses.click_here" + } + ], + "path": "app/soapbox/features/compose/components/compose_form.json" + }, + { + "descriptors": [ + { + "defaultMessage": "What's on your mind?", + "id": "compose_form.placeholder" + }, + { + "defaultMessage": "Add a poll topic...", + "id": "compose_form.poll_placeholder" + }, + { + "defaultMessage": "Write your warning here (optional)", + "id": "compose_form.spoiler_placeholder" + }, + { + "defaultMessage": "Post", + "id": "compose_form.publish" + }, + { + "defaultMessage": "{publish}!", + "id": "compose_form.publish_loud" + }, + { + "defaultMessage": "Message", + "id": "compose_form.message" + }, + { + "defaultMessage": "Schedule", + "id": "compose_form.schedule" + }, + { + "defaultMessage": "Save changes", + "id": "compose_form.save_changes" + }, + { + "defaultMessage": "You have scheduled posts. {click_here} to see them.", + "id": "compose_form.scheduled_statuses.message" + }, + { + "defaultMessage": "Click here", + "id": "compose_form.scheduled_statuses.click_here" + } + ], + "path": "app/soapbox/features/compose/components/compose-form.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Insert emoji", + "id": "emoji_button.label" + }, + { + "defaultMessage": "Search…", + "id": "emoji_button.search" + }, + { + "defaultMessage": "No emoji's found.", + "id": "emoji_button.not_found" + }, + { + "defaultMessage": "Custom", + "id": "emoji_button.custom" + }, + { + "defaultMessage": "Frequently used", + "id": "emoji_button.recent" + }, + { + "defaultMessage": "Search results", + "id": "emoji_button.search_results" + }, + { + "defaultMessage": "People", + "id": "emoji_button.people" + }, + { + "defaultMessage": "Nature", + "id": "emoji_button.nature" + }, + { + "defaultMessage": "Food & Drink", + "id": "emoji_button.food" + }, + { + "defaultMessage": "Activity", + "id": "emoji_button.activity" + }, + { + "defaultMessage": "Travel & Places", + "id": "emoji_button.travel" + }, + { + "defaultMessage": "Objects", + "id": "emoji_button.objects" + }, + { + "defaultMessage": "Symbols", + "id": "emoji_button.symbols" + }, + { + "defaultMessage": "Flags", + "id": "emoji_button.flags" + } + ], + "path": "app/soapbox/features/compose/components/emoji_picker_dropdown.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Insert emoji", + "id": "emoji_button.label" + }, + { + "defaultMessage": "Search…", + "id": "emoji_button.search" + }, + { + "defaultMessage": "No emoji's found.", + "id": "emoji_button.not_found" + }, + { + "defaultMessage": "Custom", + "id": "emoji_button.custom" + }, + { + "defaultMessage": "Frequently used", + "id": "emoji_button.recent" + }, + { + "defaultMessage": "Search results", + "id": "emoji_button.search_results" + }, + { + "defaultMessage": "People", + "id": "emoji_button.people" + }, + { + "defaultMessage": "Nature", + "id": "emoji_button.nature" + }, + { + "defaultMessage": "Food & Drink", + "id": "emoji_button.food" + }, + { + "defaultMessage": "Activity", + "id": "emoji_button.activity" }, { - "defaultMessage": "Message", - "id": "compose_form.message" + "defaultMessage": "Travel & Places", + "id": "emoji_button.travel" }, { - "defaultMessage": "Schedule", - "id": "compose_form.schedule" + "defaultMessage": "Objects", + "id": "emoji_button.objects" }, { - "defaultMessage": "You have scheduled posts. {click_here} to see them.", - "id": "compose_form.scheduled_statuses.message" + "defaultMessage": "Symbols", + "id": "emoji_button.symbols" }, { - "defaultMessage": "Click here", - "id": "compose_form.scheduled_statuses.click_here" + "defaultMessage": "Flags", + "id": "emoji_button.flags" } ], - "path": "app/soapbox/features/compose/components/compose_form.json" + "path": "app/soapbox/features/compose/components/emoji-picker/emoji-picker-dropdown.json" }, { "descriptors": [ @@ -2344,7 +2941,7 @@ "id": "emoji_button.flags" } ], - "path": "app/soapbox/features/compose/components/emoji_picker_dropdown.json" + "path": "app/soapbox/features/compose/components/emoji-picker/emoji-picker-menu.json" }, { "descriptors": [ @@ -2411,7 +3008,77 @@ "id": "intervals.full.days" } ], - "path": "app/soapbox/features/compose/components/poll-form.json" + "path": "app/soapbox/features/compose/components/poll_form.json" + }, + { + "descriptors": [ + { + "defaultMessage": "{number, plural, one {# minute} other {# minutes}}", + "id": "intervals.full.minutes" + }, + { + "defaultMessage": "{number, plural, one {# hour} other {# hours}}", + "id": "intervals.full.hours" + }, + { + "defaultMessage": "{number, plural, one {# day} other {# days}}", + "id": "intervals.full.days" + } + ], + "path": "app/soapbox/features/compose/components/polls/duration-selector.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Answer #{number}", + "id": "compose_form.poll.option_placeholder" + }, + { + "defaultMessage": "Add an answer", + "id": "compose_form.poll.add_option" + }, + { + "defaultMessage": "Remove this answer", + "id": "compose_form.poll.remove_option" + }, + { + "defaultMessage": "Duration", + "id": "compose_form.poll.duration" + }, + { + "defaultMessage": "Remove poll", + "id": "compose_form.poll.remove" + }, + { + "defaultMessage": "Change poll to allow multiple answers", + "id": "compose_form.poll.switch_to_multiple" + }, + { + "defaultMessage": "Change poll to allow for a single answer", + "id": "compose_form.poll.switch_to_single" + }, + { + "defaultMessage": "{number, plural, one {# minute} other {# minutes}}", + "id": "intervals.full.minutes" + }, + { + "defaultMessage": "{number, plural, one {# hour} other {# hours}}", + "id": "intervals.full.hours" + }, + { + "defaultMessage": "{number, plural, one {# day} other {# days}}", + "id": "intervals.full.days" + }, + { + "defaultMessage": "Multi-Select", + "id": "compose_form.poll.multiselect" + }, + { + "defaultMessage": "Allow users to select multiple answers", + "id": "compose_form.poll.multiselect_detail" + } + ], + "path": "app/soapbox/features/compose/components/polls/poll-form.json" }, { "descriptors": [ @@ -2454,15 +3121,6 @@ ], "path": "app/soapbox/features/compose/components/privacy_dropdown.json" }, - { - "descriptors": [ - { - "defaultMessage": "Cancel", - "id": "reply_indicator.cancel" - } - ], - "path": "app/soapbox/features/compose/components/reply_indicator.json" - }, { "descriptors": [ { @@ -2470,12 +3128,12 @@ "id": "reply_mentions.reply_empty" }, { - "defaultMessage": "Replying to {accounts}{more}", - "id": "reply_mentions.reply" + "defaultMessage": "{count} more", + "id": "reply_mentions.more" }, { - "defaultMessage": "and {count} more", - "id": "reply_mentions.more" + "defaultMessage": "Replying to {accounts}", + "id": "reply_mentions.reply" } ], "path": "app/soapbox/features/compose/components/reply_mentions.json" @@ -2568,20 +3226,28 @@ { "descriptors": [ { - "defaultMessage": "Add media attachment", - "id": "upload_button.label" + "defaultMessage": "Sensitive content", + "id": "compose_form.spoiler_title" + }, + { + "defaultMessage": "Write your warning here (optional)", + "id": "compose_form.spoiler_placeholder" + }, + { + "defaultMessage": "Remove sensitive", + "id": "compose_form.spoiler_remove" } ], - "path": "app/soapbox/features/compose/components/upload_button.json" + "path": "app/soapbox/features/compose/components/spoiler-input.json" }, { "descriptors": [ { - "defaultMessage": "Uploading…", - "id": "upload_progress.label" + "defaultMessage": "Add media attachment", + "id": "upload_button.label" } ], - "path": "app/soapbox/features/compose/components/upload_progress.json" + "path": "app/soapbox/features/compose/components/upload_button.json" }, { "descriptors": [ @@ -2618,23 +3284,6 @@ ], "path": "app/soapbox/features/compose/components/visual_character_counter.json" }, - { - "descriptors": [ - { - "defaultMessage": "Media is marked as sensitive", - "id": "compose_form.sensitive.marked" - }, - { - "defaultMessage": "Media is not marked as sensitive", - "id": "compose_form.sensitive.unmarked" - }, - { - "defaultMessage": "Mark media as sensitive", - "id": "compose_form.sensitive.hide" - } - ], - "path": "app/soapbox/features/compose/containers/sensitive_button_container.json" - }, { "descriptors": [ { @@ -3160,6 +3809,14 @@ "defaultMessage": "Opt-in to news and marketing updates.", "id": "edit_profile.hints.accepts_email_list" }, + { + "defaultMessage": "Profile fields", + "id": "edit_profile.fields.meta_fields_label" + }, + { + "defaultMessage": "You can have up to {count, plural, one {# custom field} other {# custom fields}} displayed on your profile.", + "id": "edit_profile.hints.meta_fields" + }, { "defaultMessage": "Save", "id": "edit_profile.save" @@ -3216,23 +3873,6 @@ ], "path": "app/soapbox/features/export_data/index.json" }, - { - "descriptors": [ - { - "defaultMessage": "Instance", - "id": "login.fields.instance_label" - }, - { - "defaultMessage": "example.com", - "id": "login.fields.instance_placeholder" - }, - { - "defaultMessage": "Log in", - "id": "login.log_in" - } - ], - "path": "app/soapbox/features/external_login/components/external_login_form.json" - }, { "descriptors": [ { @@ -3333,6 +3973,28 @@ ], "path": "app/soapbox/features/federation_restrictions/index.json" }, + { + "descriptors": [ + { + "defaultMessage": "Something isn't right. Try reloading the page.", + "id": "common.error" + } + ], + "path": "app/soapbox/features/feed-filtering/feed-carousel.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Suggested Profiles", + "id": "feed_suggestions.heading" + }, + { + "defaultMessage": "View all", + "id": "feed_suggestions.view_all" + } + ], + "path": "app/soapbox/features/feed-suggestions/feed-suggestions.json" + }, { "descriptors": [ { @@ -3671,66 +4333,6 @@ ], "path": "app/soapbox/features/groups/removed_accounts/index.json" }, - { - "descriptors": [ - { - "defaultMessage": "Groups You're In", - "id": "groups.sidebar-panel.title" - }, - { - "defaultMessage": "Show all", - "id": "groups.sidebar-panel.show_all" - } - ], - "path": "app/soapbox/features/groups/sidebar_panel/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "new posts", - "id": "groups.sidebar-panel.item.view" - }, - { - "defaultMessage": "No recent activity", - "id": "groups.sidebar-panel.item.no_recent_activity" - } - ], - "path": "app/soapbox/features/groups/sidebar_panel/item.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Join group", - "id": "groups.join" - }, - { - "defaultMessage": "Leave group", - "id": "groups.leave" - }, - { - "defaultMessage": "Removed Accounts", - "id": "groups.removed_accounts" - }, - { - "defaultMessage": "Edit", - "id": "groups.edit" - } - ], - "path": "app/soapbox/features/groups/timeline/components/header.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Archived group", - "id": "group.detail.archived_group" - }, - { - "defaultMessage": "You're an admin", - "id": "groups.detail.role_admin" - } - ], - "path": "app/soapbox/features/groups/timeline/components/panel.json" - }, { "descriptors": [ { @@ -3761,31 +4363,6 @@ ], "path": "app/soapbox/features/hashtag_timeline/index.json" }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Home settings", - "id": "home.column_settings.title" - }, - { - "defaultMessage": "Show reposts", - "id": "home.column_settings.show_reblogs" - }, - { - "defaultMessage": "Show replies", - "id": "home.column_settings.show_replies" - }, - { - "defaultMessage": "Show direct messages", - "id": "home.column_settings.show_direct" - } - ], - "path": "app/soapbox/features/home_timeline/components/column_settings.json" - }, { "descriptors": [ { @@ -3793,7 +4370,7 @@ "id": "column.home" }, { - "defaultMessage": "Or you can visit {public} to get started and meet other users.", + "defaultMessage": "Your home timeline is empty! Visit {public} to get started and meet other users.", "id": "empty_column.home" }, { @@ -3831,83 +4408,26 @@ }, { "defaultMessage": "CSV file containing a list of blocked accounts", - "id": "import_data.hints.blocks" - }, - { - "defaultMessage": "Import blocks", - "id": "import_data.actions.import_blocks" - }, - { - "defaultMessage": "Mutes", - "id": "import_data.mutes_label" - }, - { - "defaultMessage": "CSV file containing a list of muted accounts", - "id": "import_data.hints.mutes" - }, - { - "defaultMessage": "Import mutes", - "id": "import_data.actions.import_mutes" - } - ], - "path": "app/soapbox/features/import_data/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "First steps", - "id": "introduction.welcome.headline" - }, - { - "defaultMessage": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", - "id": "introduction.welcome.text" - }, - { - "defaultMessage": "Let's go!", - "id": "introduction.welcome.action" - }, - { - "defaultMessage": "Home", - "id": "introduction.federation.home.headline" - }, - { - "defaultMessage": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", - "id": "introduction.federation.home.text" - }, - { - "defaultMessage": "Next", - "id": "introduction.federation.action" - }, - { - "defaultMessage": "Reply", - "id": "introduction.interactions.reply.headline" - }, - { - "defaultMessage": "You can reply to other people's and your own posts, which will chain them together in a conversation.", - "id": "introduction.interactions.reply.text" - }, - { - "defaultMessage": "Repost", - "id": "introduction.interactions.reblog.headline" + "id": "import_data.hints.blocks" }, { - "defaultMessage": "You can share other people's posts with your followers by reposting them.", - "id": "introduction.interactions.reblog.text" + "defaultMessage": "Import blocks", + "id": "import_data.actions.import_blocks" }, { - "defaultMessage": "Favorite", - "id": "introduction.interactions.favourite.headline" + "defaultMessage": "Mutes", + "id": "import_data.mutes_label" }, { - "defaultMessage": "You can save a post for later, and let the author know that you liked it, by favoriting it.", - "id": "introduction.interactions.favourite.text" + "defaultMessage": "CSV file containing a list of muted accounts", + "id": "import_data.hints.mutes" }, { - "defaultMessage": "Finish tutorial!", - "id": "introduction.interactions.action" + "defaultMessage": "Import mutes", + "id": "import_data.actions.import_mutes" } ], - "path": "app/soapbox/features/introduction/index.json" + "path": "app/soapbox/features/import_data/index.json" }, { "descriptors": [ @@ -3918,6 +4438,22 @@ { "defaultMessage": "{instance} is not accepting new members.", "id": "registration.closed_message" + }, + { + "defaultMessage": "Let's get started!", + "id": "registrations.get_started" + }, + { + "defaultMessage": "Sign in with {provider}", + "id": "oauth_consumer.tooltip" + }, + { + "defaultMessage": "Social Media Without Discrimination", + "id": "registrations.tagline" + }, + { + "defaultMessage": "Create an account", + "id": "registrations.create_account" } ], "path": "app/soapbox/features/landing_page/index.json" @@ -4154,104 +4690,6 @@ ], "path": "app/soapbox/features/mutes/index.json" }, - { - "descriptors": [ - { - "defaultMessage": "Clear notifications", - "id": "notifications.clear" - } - ], - "path": "app/soapbox/features/notifications/components/clear_column_button.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Show", - "id": "notifications.column_settings.filter_bar.show" - }, - { - "defaultMessage": "Display all categories", - "id": "notifications.column_settings.filter_bar.advanced" - }, - { - "defaultMessage": "Desktop notifications", - "id": "notifications.column_settings.alert" - }, - { - "defaultMessage": "Play sound for all notifications", - "id": "notifications.column_settings.sounds.all_sounds" - }, - { - "defaultMessage": "Show in column", - "id": "notifications.column_settings.show" - }, - { - "defaultMessage": "Play sound", - "id": "notifications.column_settings.sound" - }, - { - "defaultMessage": "Push notifications", - "id": "notifications.column_settings.push" - }, - { - "defaultMessage": "Show birthday reminders", - "id": "notifications.column_settings.birthdays.show" - }, - { - "defaultMessage": "Notification settings", - "id": "notifications.column_settings.title" - }, - { - "defaultMessage": "Sounds", - "id": "notifications.column_settings.sounds" - }, - { - "defaultMessage": "Quick filter bar", - "id": "notifications.column_settings.filter_bar.category" - }, - { - "defaultMessage": "Birthdays", - "id": "notifications.column_settings.birthdays.category" - }, - { - "defaultMessage": "New followers:", - "id": "notifications.column_settings.follow" - }, - { - "defaultMessage": "New follow requests:", - "id": "notifications.column_settings.follow_request" - }, - { - "defaultMessage": "Likes:", - "id": "notifications.column_settings.favourite" - }, - { - "defaultMessage": "Emoji reacts:", - "id": "notifications.column_settings.emoji_react" - }, - { - "defaultMessage": "Mentions:", - "id": "notifications.column_settings.mention" - }, - { - "defaultMessage": "Reposts:", - "id": "notifications.column_settings.reblog" - }, - { - "defaultMessage": "Poll results:", - "id": "notifications.column_settings.poll" - }, - { - "defaultMessage": "Moves:", - "id": "notifications.column_settings.move" - } - ], - "path": "app/soapbox/features/notifications/components/column_settings.json" - }, { "descriptors": [ { @@ -4293,19 +4731,6 @@ ], "path": "app/soapbox/features/notifications/components/filter_bar.json" }, - { - "descriptors": [ - { - "defaultMessage": "Authorize", - "id": "follow_request.authorize" - }, - { - "defaultMessage": "Reject", - "id": "follow_request.reject" - } - ], - "path": "app/soapbox/features/notifications/components/follow_request.json" - }, { "descriptors": [ { @@ -4317,12 +4742,8 @@ "id": "notification.follow_request" }, { - "defaultMessage": "{name} sent you a message", - "id": "notification.pleroma:chat_mention" - }, - { - "defaultMessage": "{name} reacted to your post", - "id": "notification.pleroma:emoji_reaction" + "defaultMessage": "{name} mentioned you", + "id": "notification.mentioned" }, { "defaultMessage": "{name} liked your post", @@ -4343,26 +4764,29 @@ { "defaultMessage": "{name} moved to {targetName}", "id": "notification.move" - } - ], - "path": "app/soapbox/features/notifications/components/notification.json" - }, - { - "descriptors": [ + }, + { + "defaultMessage": "{name} sent you a message", + "id": "notification.pleroma:chat_mention" + }, { - "defaultMessage": "Clear notifications", - "id": "notifications.clear_heading" + "defaultMessage": "{name} reacted to your post", + "id": "notification.pleroma:emoji_reaction" + }, + { + "defaultMessage": "Welcome to {instance}!", + "id": "notification.user_approved" }, { - "defaultMessage": "Are you sure you want to permanently clear all your notifications?", - "id": "notifications.clear_confirmation" + "defaultMessage": "{name} edited a post you interacted with", + "id": "notification.update" }, { - "defaultMessage": "Clear notifications", - "id": "notifications.clear" + "defaultMessage": " + {count} {count, plural, one {other} other {others}}", + "id": "notification.others" } ], - "path": "app/soapbox/features/notifications/containers/column_settings_container.json" + "path": "app/soapbox/features/notifications/components/notification.json" }, { "descriptors": [ @@ -4550,6 +4974,10 @@ "defaultMessage": "Show replies", "id": "home.column_settings.show_replies" }, + { + "defaultMessage": "Theme", + "id": "preferences.fields.theme" + }, { "defaultMessage": "Language", "id": "preferences.fields.language_label" @@ -4610,18 +5038,17 @@ { "defaultMessage": "Forgot password?", "id": "header.login.forgot_password" - } - ], - "path": "app/soapbox/features/public_layout/components/header.json" - }, - { - "descriptors": [ + }, { - "defaultMessage": "Close", - "id": "pre_header.close" + "defaultMessage": "Preview Timeline", + "id": "header.preview_timeline.label" + }, + { + "defaultMessage": "Help Center", + "id": "landing_page_modal.helpCenter" } ], - "path": "app/soapbox/features/public_layout/components/pre_header.json" + "path": "app/soapbox/features/public_layout/components/header.json" }, { "descriptors": [ @@ -4711,171 +5138,57 @@ "descriptors": [ { "defaultMessage": "Remove from mentions", - "id": "reply_mentions.account.remove" - }, - { - "defaultMessage": "Add to mentions", - "id": "reply_mentions.account.add" - } - ], - "path": "app/soapbox/features/reply_mentions/account.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Cancel", - "id": "scheduled_status.cancel" - }, - { - "defaultMessage": "Cancel", - "id": "confirmations.scheduled_status_delete.confirm" - }, - { - "defaultMessage": "Cancel scheduled post", - "id": "confirmations.scheduled_status_delete.heading" - }, - { - "defaultMessage": "Are you sure you want to cancel this scheduled post?", - "id": "confirmations.scheduled_status_delete.message" - } - ], - "path": "app/soapbox/features/scheduled_statuses/components/scheduled_status_action_bar.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Scheduled Posts", - "id": "column.scheduled_statuses" - }, - { - "defaultMessage": "You don't have any scheduled statuses yet. When you add one, it will show up here.", - "id": "empty_column.scheduled_statuses" - } - ], - "path": "app/soapbox/features/scheduled_statuses/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Top", - "id": "search_results.top" - } - ], - "path": "app/soapbox/features/search/components/header.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Search", - "id": "column.search" - } - ], - "path": "app/soapbox/features/search/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Security", - "id": "column.security" - }, - { - "defaultMessage": "Save changes", - "id": "security.submit" - }, - { - "defaultMessage": "Email successfully updated.", - "id": "security.update_email.success" - }, - { - "defaultMessage": "Update email failed.", - "id": "security.update_email.fail" - }, - { - "defaultMessage": "Password successfully updated.", - "id": "security.update_password.success" - }, - { - "defaultMessage": "Update password failed.", - "id": "security.update_password.fail" - }, - { - "defaultMessage": "Email address", - "id": "security.fields.email.label" - }, - { - "defaultMessage": "Password", - "id": "security.fields.password.label" - }, - { - "defaultMessage": "Current password", - "id": "security.fields.old_password.label" - }, - { - "defaultMessage": "New password", - "id": "security.fields.new_password.label" - }, - { - "defaultMessage": "New password (again)", - "id": "security.fields.password_confirmation.label" - }, - { - "defaultMessage": "Revoke", - "id": "security.tokens.revoke" - }, - { - "defaultMessage": "Change Email", - "id": "security.headers.update_email" - }, - { - "defaultMessage": "Change Password", - "id": "security.headers.update_password" - }, - { - "defaultMessage": "Sessions", - "id": "security.headers.tokens" - }, - { - "defaultMessage": "Delete Account", - "id": "security.headers.delete" - }, - { - "defaultMessage": "To delete your account, enter your password then click Delete Account. This is a permanent action that cannot be undone. Your account will be destroyed from this server, and a deletion request will be sent to other servers. It's not guaranteed that all servers will purge your account.", - "id": "security.text.delete" - }, - { - "defaultMessage": "Delete Account", - "id": "security.submit.delete" - }, - { - "defaultMessage": "Account successfully deleted.", - "id": "security.delete_account.success" + "id": "reply_mentions.account.remove" }, { - "defaultMessage": "Account deletion failed.", - "id": "security.delete_account.fail" - }, + "defaultMessage": "Add to mentions", + "id": "reply_mentions.account.add" + } + ], + "path": "app/soapbox/features/reply_mentions/account.json" + }, + { + "descriptors": [ { - "defaultMessage": "Set up 2-Factor Auth", - "id": "security.mfa" + "defaultMessage": "Cancel", + "id": "scheduled_status.cancel" }, { - "defaultMessage": "Configure multi-factor authentication with OTP", - "id": "security.mfa_setup_hint" + "defaultMessage": "Cancel", + "id": "confirmations.scheduled_status_delete.confirm" }, { - "defaultMessage": "You have multi-factor authentication set up with OTP.", - "id": "security.mfa_enabled" + "defaultMessage": "Cancel scheduled post", + "id": "confirmations.scheduled_status_delete.heading" }, { - "defaultMessage": "Disable", - "id": "security.disable_mfa" + "defaultMessage": "Are you sure you want to cancel this scheduled post?", + "id": "confirmations.scheduled_status_delete.message" + } + ], + "path": "app/soapbox/features/scheduled_statuses/components/scheduled_status_action_bar.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Scheduled Posts", + "id": "column.scheduled_statuses" }, { - "defaultMessage": "Authorization Methods", - "id": "security.mfa_header" + "defaultMessage": "You don't have any scheduled statuses yet. When you add one, it will show up here.", + "id": "empty_column.scheduled_statuses" + } + ], + "path": "app/soapbox/features/scheduled_statuses/index.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Search", + "id": "column.search" } ], - "path": "app/soapbox/features/security/index.json" + "path": "app/soapbox/features/search/index.json" }, { "descriptors": [ @@ -5040,6 +5353,10 @@ "defaultMessage": "Configure MFA", "id": "settings.configure_mfa" }, + { + "defaultMessage": "Active sessions", + "id": "settings.sessions" + }, { "defaultMessage": "Delete Account", "id": "settings.delete_account" @@ -5068,6 +5385,36 @@ ], "path": "app/soapbox/features/settings/media_display.json" }, + { + "descriptors": [ + { + "defaultMessage": "Ticker", + "id": "soapbox_config.crypto_address.meta_fields.ticker_placeholder" + }, + { + "defaultMessage": "Address", + "id": "soapbox_config.crypto_address.meta_fields.address_placeholder" + }, + { + "defaultMessage": "Note (optional)", + "id": "soapbox_config.crypto_address.meta_fields.note_placeholder" + } + ], + "path": "app/soapbox/features/soapbox_config/components/crypto-address-input.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Label", + "id": "soapbox_config.home_footer.meta_fields.label_placeholder" + }, + { + "defaultMessage": "URL", + "id": "soapbox_config.home_footer.meta_fields.url_placeholder" + } + ], + "path": "app/soapbox/features/soapbox_config/components/footer-link-input.json" + }, { "descriptors": [ { @@ -5095,18 +5442,6 @@ }, { "descriptors": [ - { - "defaultMessage": "Soapbox config", - "id": "column.soapbox_config" - }, - { - "defaultMessage": "Soapbox config saved!", - "id": "soapbox_config.saved" - }, - { - "defaultMessage": "Copyright footer", - "id": "soapbox_config.copyright_footer.meta_fields.label_placeholder" - }, { "defaultMessage": "Icon", "id": "soapbox_config.promo_panel.meta_fields.icon_placeholder" @@ -5118,310 +5453,159 @@ { "defaultMessage": "URL", "id": "soapbox_config.promo_panel.meta_fields.url_placeholder" - }, - { - "defaultMessage": "Label", - "id": "soapbox_config.home_footer.meta_fields.label_placeholder" - }, - { - "defaultMessage": "URL", - "id": "soapbox_config.home_footer.meta_fields.url_placeholder" - }, - { - "defaultMessage": "Ticker", - "id": "soapbox_config.crypto_address.meta_fields.ticker_placeholder" - }, - { - "defaultMessage": "Address", - "id": "soapbox_config.crypto_address.meta_fields.address_placeholder" - }, - { - "defaultMessage": "Note (optional)", - "id": "soapbox_config.crypto_address.meta_fields.note_placeholder" - }, - { - "defaultMessage": "Number of items to display in the crypto homepage widget", - "id": "soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder" - }, - { - "defaultMessage": "URL", - "id": "soapbox_config.custom_css.meta_fields.url_placeholder" - }, - { - "defaultMessage": "Advanced: Edit raw JSON data", - "id": "soapbox_config.raw_json_label" - }, - { - "defaultMessage": "Edit the settings data directly. Changes made directly to the JSON file will override the form fields above. Click \"Save\" to apply your changes.", - "id": "soapbox_config.raw_json_hint" - }, - { - "defaultMessage": "Allow verified users to edit their own display name.", - "id": "soapbox_config.verified_can_edit_name_label" - }, - { - "defaultMessage": "Display domain (eg @user@domain) for local accounts.", - "id": "soapbox_config.display_fqn_label" - }, - { - "defaultMessage": "Enable greentext support", - "id": "soapbox_config.greentext_label" - }, - { - "defaultMessage": "Soapbox Icons List", - "id": "soapbox_config.hints.promo_panel_icons.link" - }, - { - "defaultMessage": "Profiles require authentication", - "id": "soapbox_config.authenticated_profile_label" - }, - { - "defaultMessage": "Users must be logged-in to view replies and media on user profiles.", - "id": "soapbox_config.authenticated_profile_hint" - }, - { - "defaultMessage": "Single user mode", - "id": "soapbox_config.single_user_mode_label" - }, - { - "defaultMessage": "Front page will redirect to a given user profile.", - "id": "soapbox_config.single_user_mode_hint" - }, - { - "defaultMessage": "Main user handle", - "id": "soapbox_config.single_user_mode_profile_label" - }, - { - "defaultMessage": "@handle", - "id": "soapbox_config.single_user_mode_profile_hint" - }, - { - "defaultMessage": "Brand color", - "id": "soapbox_config.fields.brand_color_label" - }, - { - "defaultMessage": "Accent color", - "id": "soapbox_config.fields.accent_color_label" - }, - { - "defaultMessage": "Default theme", - "id": "soapbox_config.fields.theme_label" - }, - { - "defaultMessage": "Logo", - "id": "soapbox_config.fields.logo_label" - }, - { - "defaultMessage": "SVG. At most 2 MB. Will be displayed to 50px height, maintaining aspect ratio", - "id": "soapbox_config.hints.logo" - }, - { - "defaultMessage": "Promo panel items", - "id": "soapbox_config.fields.promo_panel_fields_label" - }, - { - "defaultMessage": "You can have custom defined links displayed on the right panel of the timelines page.", - "id": "soapbox_config.hints.promo_panel_fields" - }, - { - "defaultMessage": "{ link }", - "id": "soapbox_config.hints.promo_panel_icons" - }, - { - "defaultMessage": "Add new Promo panel item", - "id": "soapbox_config.fields.promo_panel.add" - }, - { - "defaultMessage": "Home footer items", - "id": "soapbox_config.fields.home_footer_fields_label" - }, - { - "defaultMessage": "You can have custom defined links displayed on the footer of your static pages", - "id": "soapbox_config.hints.home_footer_fields" - }, - { - "defaultMessage": "Add new Home Footer Item", - "id": "soapbox_config.fields.home_footer.add" - }, - { - "defaultMessage": "Cryptocurrency addresses", - "id": "soapbox_config.fields.crypto_addresses_label" - }, - { - "defaultMessage": "Add cryptocurrency addresses so users of your site can donate to you. Order matters, and you must use lowercase ticker values.", - "id": "soapbox_config.hints.crypto_addresses" - }, - { - "defaultMessage": "Add new crypto address", - "id": "soapbox_config.fields.crypto_address.add" - }, - { - "defaultMessage": "Save", - "id": "soapbox_config.save" } ], - "path": "app/soapbox/features/soapbox_config/index.json" + "path": "app/soapbox/features/soapbox_config/components/promo-panel-input.json" }, { "descriptors": [ { - "defaultMessage": "Delete", - "id": "status.delete" - }, - { - "defaultMessage": "Delete & re-draft", - "id": "status.redraft" - }, - { - "defaultMessage": "Direct message @{name}", - "id": "status.direct" - }, - { - "defaultMessage": "Chat with @{name}", - "id": "status.chat" - }, - { - "defaultMessage": "Mention @{name}", - "id": "status.mention" - }, + "defaultMessage": "Preview", + "id": "site_preview.preview" + } + ], + "path": "app/soapbox/features/soapbox_config/components/site-preview.json" + }, + { + "descriptors": [ { - "defaultMessage": "Reply", - "id": "status.reply" + "defaultMessage": "Soapbox config", + "id": "column.soapbox_config" }, { - "defaultMessage": "Repost", - "id": "status.reblog" + "defaultMessage": "Soapbox config saved!", + "id": "soapbox_config.saved" }, { - "defaultMessage": "Repost to original audience", - "id": "status.reblog_private" + "defaultMessage": "Copyright footer", + "id": "soapbox_config.copyright_footer.meta_fields.label_placeholder" }, { - "defaultMessage": "Un-repost", - "id": "status.cancel_reblog_private" + "defaultMessage": "Number of items to display in the crypto homepage widget", + "id": "soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder" }, { - "defaultMessage": "This post cannot be reposted", - "id": "status.cannot_reblog" + "defaultMessage": "URL", + "id": "soapbox_config.custom_css.meta_fields.url_placeholder" }, { - "defaultMessage": "Like", - "id": "status.favourite" + "defaultMessage": "Advanced: Edit raw JSON data", + "id": "soapbox_config.raw_json_label" }, { - "defaultMessage": "Mute @{name}", - "id": "status.mute" + "defaultMessage": "Edit the settings data directly. Changes made directly to the JSON file will override the form fields above. Click \"Save\" to apply your changes.", + "id": "soapbox_config.raw_json_hint" }, { - "defaultMessage": "Mute conversation", - "id": "status.mute_conversation" + "defaultMessage": "Allow verified users to edit their own display name.", + "id": "soapbox_config.verified_can_edit_name_label" }, { - "defaultMessage": "Unmute conversation", - "id": "status.unmute_conversation" + "defaultMessage": "Display domain (eg @user@domain) for local accounts.", + "id": "soapbox_config.display_fqn_label" }, { - "defaultMessage": "Block @{name}", - "id": "status.block" + "defaultMessage": "Enable greentext support", + "id": "soapbox_config.greentext_label" }, { - "defaultMessage": "Report @{name}", - "id": "status.report" + "defaultMessage": "Soapbox Icons List", + "id": "soapbox_config.hints.promo_panel_icons.link" }, { - "defaultMessage": "Share", - "id": "status.share" + "defaultMessage": "Profiles require authentication", + "id": "soapbox_config.authenticated_profile_label" }, { - "defaultMessage": "Pin on profile", - "id": "status.pin" + "defaultMessage": "Users must be logged-in to view replies and media on user profiles.", + "id": "soapbox_config.authenticated_profile_hint" }, { - "defaultMessage": "Unpin from profile", - "id": "status.unpin" + "defaultMessage": "Single user mode", + "id": "soapbox_config.single_user_mode_label" }, { - "defaultMessage": "Embed", - "id": "status.embed" + "defaultMessage": "Front page will redirect to a given user profile.", + "id": "soapbox_config.single_user_mode_hint" }, { - "defaultMessage": "Open moderation interface for @{name}", - "id": "status.admin_account" + "defaultMessage": "Main user handle", + "id": "soapbox_config.single_user_mode_profile_label" }, { - "defaultMessage": "Open this post in the moderation interface", - "id": "status.admin_status" + "defaultMessage": "@handle", + "id": "soapbox_config.single_user_mode_profile_hint" }, { - "defaultMessage": "Copy link to post", - "id": "status.copy" + "defaultMessage": "Logo", + "id": "soapbox_config.fields.logo_label" }, { - "defaultMessage": "Bookmark", - "id": "status.bookmark" + "defaultMessage": "SVG. At most 2 MB. Will be displayed to 50px height, maintaining aspect ratio", + "id": "soapbox_config.hints.logo" }, { - "defaultMessage": "Remove bookmark", - "id": "status.unbookmark" + "defaultMessage": "Theme", + "id": "soapbox_config.headings.theme" }, { - "defaultMessage": "Deactivate @{name}", - "id": "admin.users.actions.deactivate_user" + "defaultMessage": "Default theme", + "id": "soapbox_config.fields.theme_label" }, { - "defaultMessage": "Delete @{name}", - "id": "admin.users.actions.delete_user" + "defaultMessage": "Brand color", + "id": "soapbox_config.fields.brand_color_label" }, { - "defaultMessage": "Delete post", - "id": "admin.statuses.actions.delete_status" + "defaultMessage": "Accent color", + "id": "soapbox_config.fields.accent_color_label" }, { - "defaultMessage": "Mark post sensitive", - "id": "admin.statuses.actions.mark_status_sensitive" + "defaultMessage": "Options", + "id": "soapbox_config.headings.options" }, { - "defaultMessage": "Mark post not sensitive", - "id": "admin.statuses.actions.mark_status_not_sensitive" + "defaultMessage": "Navigation", + "id": "soapbox_config.headings.navigation" }, { - "defaultMessage": "Like", - "id": "status.reactions.like" + "defaultMessage": "Promo panel items", + "id": "soapbox_config.fields.promo_panel_fields_label" }, { - "defaultMessage": "Love", - "id": "status.reactions.heart" + "defaultMessage": "You can have custom defined links displayed on the right panel of the timelines page.", + "id": "soapbox_config.hints.promo_panel_fields" }, { - "defaultMessage": "Haha", - "id": "status.reactions.laughing" + "defaultMessage": "Home footer items", + "id": "soapbox_config.fields.home_footer_fields_label" }, { - "defaultMessage": "Wow", - "id": "status.reactions.open_mouth" + "defaultMessage": "You can have custom defined links displayed on the footer of your static pages", + "id": "soapbox_config.hints.home_footer_fields" }, { - "defaultMessage": "Sad", - "id": "status.reactions.cry" + "defaultMessage": "Cryptocurrency", + "id": "soapbox_config.headings.cryptocurrency" }, { - "defaultMessage": "Weary", - "id": "status.reactions.weary" + "defaultMessage": "Cryptocurrency addresses", + "id": "soapbox_config.fields.crypto_addresses_label" }, { - "defaultMessage": "Select emoji", - "id": "status.reactions_expand" + "defaultMessage": "Add cryptocurrency addresses so users of your site can donate to you. Order matters, and you must use lowercase ticker values.", + "id": "soapbox_config.hints.crypto_addresses" }, { - "defaultMessage": "More", - "id": "status.actions.more" + "defaultMessage": "Advanced", + "id": "soapbox_config.headings.advanced" }, { - "defaultMessage": "Quote post", - "id": "status.quote" + "defaultMessage": "Save", + "id": "soapbox_config.save" } ], - "path": "app/soapbox/features/status/components/action_bar.json" + "path": "app/soapbox/features/soapbox_config/index.json" }, { "descriptors": [ @@ -5433,6 +5617,10 @@ "defaultMessage": "Delete & re-draft", "id": "status.redraft" }, + { + "defaultMessage": "Edit", + "id": "status.edit" + }, { "defaultMessage": "Direct message @{name}", "id": "status.direct" @@ -5588,16 +5776,11 @@ "descriptors": [ { "defaultMessage": "Post is unavailable.", - "id": "statuses.quote_tombstone" - } - ], - "path": "app/soapbox/features/status/components/detailed_status.json" - }, - { - "descriptors": [ + "id": "actualStatuses.quote_tombstone" + }, { - "defaultMessage": "Post is unavailable.", - "id": "statuses.quote_tombstone" + "defaultMessage": "Edited {date}", + "id": "actualStatus.edited" } ], "path": "app/soapbox/features/status/components/detailed-status.json" @@ -5609,7 +5792,7 @@ "id": "reply_indicator.cancel" }, { - "defaultMessage": "Replying to {accounts}{more}", + "defaultMessage": "Replying to {accounts}{more}", "id": "reply_mentions.reply" }, { @@ -5626,55 +5809,23 @@ { "descriptors": [ { - "defaultMessage": "Delete", - "id": "confirmations.delete.confirm" - }, - { - "defaultMessage": "Delete post", - "id": "confirmations.delete.heading" - }, - { - "defaultMessage": "Are you sure you want to delete this post?", - "id": "confirmations.delete.message" - }, - { - "defaultMessage": "Delete & redraft", - "id": "confirmations.redraft.confirm" - }, - { - "defaultMessage": "Delete & redraft", - "id": "confirmations.redraft.heading" - }, - { - "defaultMessage": "Are you sure you want to delete this post and re-draft it? Favorites and reposts will be lost, and replies to the original post will be orphaned.", - "id": "confirmations.redraft.message" - }, - { - "defaultMessage": "Block", - "id": "confirmations.block.confirm" - }, - { - "defaultMessage": "Reply", - "id": "confirmations.reply.confirm" - }, - { - "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", - "id": "confirmations.reply.message" + "defaultMessage": "Continue the conversation", + "id": "thread_login.title" }, { - "defaultMessage": "Block & Report", - "id": "confirmations.block.block_and_report" + "defaultMessage": "Join {siteTitle} to get the full story and details.", + "id": "thread_login.message" }, { - "defaultMessage": "Block @{name}", - "id": "confirmations.block.heading" + "defaultMessage": "Log in", + "id": "thread_login.login" }, { - "defaultMessage": "Are you sure you want to block {name}?", - "id": "confirmations.block.message" + "defaultMessage": "Sign up", + "id": "thread_login.signup" } ], - "path": "app/soapbox/features/status/containers/detailed_status_container.json" + "path": "app/soapbox/features/status/components/thread-login-cta.json" }, { "descriptors": [ @@ -5737,18 +5888,6 @@ { "defaultMessage": "Block & Report", "id": "confirmations.block.block_and_report" - }, - { - "defaultMessage": "Block @{name}", - "id": "confirmations.block.heading" - }, - { - "defaultMessage": "Are you sure you want to block {name}?", - "id": "confirmations.block.message" - }, - { - "defaultMessage": "One or more posts are unavailable.", - "id": "statuses.tombstone" } ], "path": "app/soapbox/features/status/index.json" @@ -5758,6 +5897,10 @@ { "defaultMessage": "Test timeline", "id": "column.test" + }, + { + "defaultMessage": "The test timeline is empty.", + "id": "empty_column.test" } ], "path": "app/soapbox/features/test_timeline/index.json" @@ -5833,6 +5976,63 @@ ], "path": "app/soapbox/features/ui/components/action_button.json" }, + { + "descriptors": [ + { + "defaultMessage": "Block @{name}", + "id": "account.block" + }, + { + "defaultMessage": "Blocked", + "id": "account.blocked" + }, + { + "defaultMessage": "Edit profile", + "id": "account.edit_profile" + }, + { + "defaultMessage": "Follow", + "id": "account.follow" + }, + { + "defaultMessage": "Mute @{name}", + "id": "account.mute" + }, + { + "defaultMessage": "Remote follow", + "id": "account.remote_follow" + }, + { + "defaultMessage": "Awaiting approval. Click to cancel follow request", + "id": "account.requested" + }, + { + "defaultMessage": "Awaiting approval", + "id": "account.requested_small" + }, + { + "defaultMessage": "Unblock @{name}", + "id": "account.unblock" + }, + { + "defaultMessage": "Unfollow", + "id": "account.unfollow" + }, + { + "defaultMessage": "Unmute @{name}", + "id": "account.unmute" + }, + { + "defaultMessage": "Authorize", + "id": "follow_request.authorize" + }, + { + "defaultMessage": "Reject", + "id": "follow_request.reject" + } + ], + "path": "app/soapbox/features/ui/components/action-button.json" + }, { "descriptors": [ { @@ -5919,6 +6119,15 @@ ], "path": "app/soapbox/features/ui/components/column_forbidden.json" }, + { + "descriptors": [ + { + "defaultMessage": "Edit history", + "id": "compare_history_modal.header" + } + ], + "path": "app/soapbox/features/ui/components/compare_history_modal.json" + }, { "descriptors": [ { @@ -5937,6 +6146,10 @@ "defaultMessage": "Are you sure you want to delete this post?", "id": "confirmations.delete.message" }, + { + "defaultMessage": "Edit post", + "id": "navigation_bar.compose_edit" + }, { "defaultMessage": "Direct message", "id": "navigation_bar.compose_direct" @@ -5968,11 +6181,32 @@ { "descriptors": [ { - "defaultMessage": "Cancel", - "id": "confirmation_modal.cancel" + "defaultMessage": "Cancel", + "id": "confirmation_modal.cancel" + } + ], + "path": "app/soapbox/features/ui/components/confirmation_modal.json" + }, + { + "descriptors": [ + { + "defaultMessage": "New to {site_title}?", + "id": "signup_panel.title" + }, + { + "defaultMessage": "Sign up now to discuss what's happening.", + "id": "signup_panel.subtitle" + }, + { + "defaultMessage": "Log in", + "id": "account.login" + }, + { + "defaultMessage": "Sign up", + "id": "account.register" } ], - "path": "app/soapbox/features/ui/components/confirmation_modal.json" + "path": "app/soapbox/features/ui/components/cta-banner.json" }, { "descriptors": [ @@ -6016,10 +6250,6 @@ { "defaultMessage": "Embed this post on your website by copying the code below.", "id": "embed.instructions" - }, - { - "defaultMessage": "Here is what it will look like:", - "id": "embed.preview" } ], "path": "app/soapbox/features/ui/components/embed_modal.json" @@ -6035,36 +6265,7 @@ "id": "column.favourites" } ], - "path": "app/soapbox/features/ui/components/modals/favourites-modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Edit Profile", - "id": "account.edit_profile" - }, - { - "defaultMessage": "Preferences", - "id": "navigation_bar.preferences" - }, - { - "defaultMessage": "Security", - "id": "navigation_bar.security" - }, - { - "defaultMessage": "Lists", - "id": "column.lists" - }, - { - "defaultMessage": "Bookmarks", - "id": "column.bookmarks" - }, - { - "defaultMessage": "Follow requests", - "id": "navigation_bar.follow_requests" - } - ], - "path": "app/soapbox/features/ui/components/features_panel.json" + "path": "app/soapbox/features/ui/components/favourites_modal.json" }, { "descriptors": [ @@ -6244,6 +6445,10 @@ "defaultMessage": "Domain blocks", "id": "navigation_bar.domain_blocks" }, + { + "defaultMessage": "Soapbox config", + "id": "navigation_bar.soapbox_config" + }, { "defaultMessage": "Follow requests", "id": "navigation_bar.follow_requests" @@ -6252,10 +6457,6 @@ "defaultMessage": "Import data", "id": "navigation_bar.import_data" }, - { - "defaultMessage": "Move account", - "id": "navigation_bar.account_migration" - }, { "defaultMessage": "Logout", "id": "navigation_bar.logout" @@ -6335,10 +6536,118 @@ { "defaultMessage": "Register", "id": "header.register.label" + }, + { + "defaultMessage": "Preview Timeline", + "id": "header.preview_timeline.label" } ], "path": "app/soapbox/features/ui/components/modals/landing-page-modal.json" }, + { + "descriptors": [ + { + "defaultMessage": "You have removed all statuses from being selected.", + "id": "report.reason.blankslate" + }, + { + "defaultMessage": "Done", + "id": "report.done" + }, + { + "defaultMessage": "Next", + "id": "report.next" + }, + { + "defaultMessage": "Close", + "id": "lightbox.close" + }, + { + "defaultMessage": "Additional comments", + "id": "report.placeholder" + }, + { + "defaultMessage": "Submit", + "id": "report.submit" + }, + { + "defaultMessage": "Reporting {target}", + "id": "report.target" + } + ], + "path": "app/soapbox/features/ui/components/modals/report-modal/report-modal.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Thanks for submitting your report.", + "id": "report.confirmation.title" + }, + { + "defaultMessage": "If we find that this account is violating the {link} we will take further action on the matter.", + "id": "report.confirmation.content" + }, + { + "defaultMessage": "Terms of Service", + "id": "shared.tos" + } + ], + "path": "app/soapbox/features/ui/components/modals/report-modal/steps/confirmation-step.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Would you like to add additional statuses to this report?", + "id": "report.otherActions.addAdditional" + }, + { + "defaultMessage": "Add more", + "id": "report.otherActions.addMore" + }, + { + "defaultMessage": "Further actions:", + "id": "report.otherActions.furtherActions" + }, + { + "defaultMessage": "Hide additional statuses", + "id": "report.otherActions.hideAdditional" + }, + { + "defaultMessage": "Include other statuses?", + "id": "report.otherActions.otherStatuses" + }, + { + "defaultMessage": "Do you also want to block this account?", + "id": "report.block_hint" + }, + { + "defaultMessage": "Block {target}", + "id": "report.block" + }, + { + "defaultMessage": "The account is from another server. Send a copy of the report there as well?", + "id": "report.forward_hint" + }, + { + "defaultMessage": "Forward to {target}", + "id": "report.forward" + } + ], + "path": "app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Additional comments", + "id": "report.placeholder" + }, + { + "defaultMessage": "Reason for reporting", + "id": "report.reason.title" + } + ], + "path": "app/soapbox/features/ui/components/modals/report-modal/steps/reason-step.json" + }, { "descriptors": [ { @@ -6366,6 +6675,22 @@ }, { "descriptors": [ + { + "defaultMessage": "Log in", + "id": "navbar.login.action" + }, + { + "defaultMessage": "Email or username", + "id": "navbar.login.username.placeholder" + }, + { + "defaultMessage": "Password", + "id": "navbar.login.password.label" + }, + { + "defaultMessage": "Forgot password?", + "id": "navbar.login.forgot_password" + }, { "defaultMessage": "Home", "id": "tabs_bar.home" @@ -6381,6 +6706,23 @@ ], "path": "app/soapbox/features/ui/components/navbar.json" }, + { + "descriptors": [ + { + "defaultMessage": "New to {site_title}?", + "id": "signup_panel.title" + }, + { + "defaultMessage": "Sign up now to discuss.", + "id": "signup_panel.subtitle" + }, + { + "defaultMessage": "Sign up", + "id": "account.register" + } + ], + "path": "app/soapbox/features/ui/components/panels/sign-up-panel.json" + }, { "descriptors": [ { @@ -6393,15 +6735,15 @@ { "descriptors": [ { - "defaultMessage": "Add an existing account", - "id": "profile_dropdown.add_account" + "defaultMessage": "{count} {count, plural, one {other} other {others}} you follow", + "id": "account.familiar_followers.more" }, { - "defaultMessage": "Log out @{acct}", - "id": "profile_dropdown.logout" + "defaultMessage": "Followed by {accounts}", + "id": "account.familiar_followers" } ], - "path": "app/soapbox/features/ui/components/profile_dropdown.json" + "path": "app/soapbox/features/ui/components/profile_familiar_followers.json" }, { "descriptors": [ @@ -6493,6 +6835,10 @@ "defaultMessage": "Add an existing account", "id": "profile_dropdown.add_account" }, + { + "defaultMessage": "Theme", + "id": "profile_dropdown.theme" + }, { "defaultMessage": "Log out @{acct}", "id": "profile_dropdown.logout" @@ -6599,7 +6945,7 @@ "id": "account.register" } ], - "path": "app/soapbox/features/ui/components/panels/sign-up-panel.json" + "path": "app/soapbox/features/ui/components/sign_up_panel.json" }, { "descriptors": [ @@ -6612,44 +6958,23 @@ "id": "account.unsubscribe" }, { - "defaultMessage": "Subscribed", - "id": "account.subscribed" - } - ], - "path": "app/soapbox/features/ui/components/subscription_button.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Post", - "id": "tabs_bar.post" - }, - { - "defaultMessage": "Home", - "id": "tabs_bar.home" - }, - { - "defaultMessage": "Notifications", - "id": "tabs_bar.notifications" - }, - { - "defaultMessage": "Chats", - "id": "tabs_bar.chats" + "defaultMessage": "You have subscribed to this account.", + "id": "account.subscribe.success" }, { - "defaultMessage": "Dashboard", - "id": "tabs_bar.dashboard" + "defaultMessage": "You have unsubscribed from this account.", + "id": "account.unsubscribe.success" }, { - "defaultMessage": "Log In", - "id": "account.login" + "defaultMessage": "An error occurred trying to subscribe to this account.", + "id": "account.subscribe.failure" }, { - "defaultMessage": "Sign up", - "id": "account.register" + "defaultMessage": "An error occurred trying to unsubscribe to this account.", + "id": "account.unsubscribe.failure" } ], - "path": "app/soapbox/features/ui/components/tabs_bar.json" + "path": "app/soapbox/features/ui/components/subscription-button.json" }, { "descriptors": [ @@ -6667,14 +6992,26 @@ { "descriptors": [ { - "defaultMessage": "Trends", - "id": "trends.title" + "defaultMessage": "Light", + "id": "theme_toggle.light" + }, + { + "defaultMessage": "Dark", + "id": "theme_toggle.dark" + }, + { + "defaultMessage": "System", + "id": "theme_toggle.system" } ], - "path": "app/soapbox/features/ui/components/trends_panel.json" + "path": "app/soapbox/features/ui/components/theme-selector.json" }, { "descriptors": [ + { + "defaultMessage": "View all", + "id": "trendsPanel.viewAll" + }, { "defaultMessage": "Trends", "id": "trends.title" @@ -6794,15 +7131,6 @@ ], "path": "app/soapbox/features/ui/components/video_modal.json" }, - { - "descriptors": [ - { - "defaultMessage": "Welcome", - "id": "account.welcome" - } - ], - "path": "app/soapbox/features/ui/components/welcome_button.json" - }, { "descriptors": [ { @@ -6810,37 +7138,41 @@ "id": "suggestions.dismiss" }, { - "defaultMessage": "Who To Follow", + "defaultMessage": "People To Follow", "id": "who_to_follow.title" } ], - "path": "app/soapbox/features/ui/components/who_to_follow_panel.json" + "path": "app/soapbox/features/ui/components/who-to-follow-panel.json" }, { "descriptors": [ { - "defaultMessage": "Dismiss suggestion", - "id": "suggestions.dismiss" + "defaultMessage": "Your draft will be lost if you leave.", + "id": "ui.beforeunload" }, { - "defaultMessage": "People To Follow", - "id": "who_to_follow.title" + "defaultMessage": "Publish", + "id": "compose_form.publish" } ], - "path": "app/soapbox/features/ui/components/who-to-follow-panel.json" + "path": "app/soapbox/features/ui/index.json" }, { "descriptors": [ { - "defaultMessage": "Your draft will be lost if you leave.", - "id": "ui.beforeunload" + "defaultMessage": "8 characters", + "id": "registration.validation.minimum_characters" }, { - "defaultMessage": "Publish", - "id": "compose_form.publish" + "defaultMessage": "1 capital letter", + "id": "registration.validation.capital_letter" + }, + { + "defaultMessage": "1 lowercase letter", + "id": "registration.validation.lowercase_letter" } ], - "path": "app/soapbox/features/ui/index.json" + "path": "app/soapbox/features/verification/components/password-indicator.json" }, { "descriptors": [ @@ -6879,6 +7211,56 @@ ], "path": "app/soapbox/features/verification/email_passthru.json" }, + { + "descriptors": [ + { + "defaultMessage": "Welcome to {siteTitle}!", + "id": "registrations.success" + }, + { + "defaultMessage": "May only contain A-Z, 0-9, and underscores", + "id": "registrations.username.hint" + }, + { + "defaultMessage": "This username has already been taken.", + "id": "registrations.unprocessable_entity" + }, + { + "defaultMessage": "Failed to register your account.", + "id": "registrations.error" + }, + { + "defaultMessage": "Register your account", + "id": "registration.header" + }, + { + "defaultMessage": "By registering, you agree to the {terms} and {privacy}.", + "id": "registration.acceptance" + }, + { + "defaultMessage": "Terms of Service", + "id": "registration.tos" + }, + { + "defaultMessage": "Privacy Policy", + "id": "registration.privacy" + } + ], + "path": "app/soapbox/features/verification/registration.json" + }, + { + "descriptors": [ + { + "defaultMessage": "You must be {ageMinimum, plural, one {# year} other {# years}} old or older.", + "id": "age_verification.fail" + }, + { + "defaultMessage": "Enter your birth date", + "id": "age_verification.header" + } + ], + "path": "app/soapbox/features/verification/steps/age-verification.json" + }, { "descriptors": [ { diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index 0df6705c3..3f377400f 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -1211,8 +1211,11 @@ "status.show_less_all": "Zwiń wszystkie", "status.show_more": "Rozwiń", "status.show_more_all": "Rozwiń wszystkie", + "status.show_original": "Pokaż oryginalny wpis", "status.title": "Wpis", "status.title_direct": "Wiadomość bezpośrednia", + "status.translated_from_with": "Przetłumaczono z {lang} z użyciem {provider}", + "status.translate": "Przetłumacz wpis", "status.unbookmark": "Usuń z zakładek", "status.unbookmarked": "Usunięto z zakładek.", "status.unmute_conversation": "Cofnij wyciszenie konwersacji", diff --git a/app/soapbox/locales/zh-CN.json b/app/soapbox/locales/zh-CN.json index 0e508ea9c..97ac5114d 100644 --- a/app/soapbox/locales/zh-CN.json +++ b/app/soapbox/locales/zh-CN.json @@ -4,18 +4,20 @@ "accordion.expand": "展开", "account.add_or_remove_from_list": "从列表中添加或删除", "account.badges.bot": "机器人", - "account.birthday": "Born {date}", - "account.birthday_today": "Birthday is today!", + "account.birthday": "出生于 {date}", + "account.birthday_today": "祝你今天生日快乐!", "account.block": "屏蔽 @{name}", "account.block_domain": "隐藏来自 {domain} 的内容", "account.blocked": "已屏蔽。", "account.chat": "与 @{name} 聊天", - "account.column_settings.description": "These settings apply to all account timelines.", - "account.column_settings.title": "Account timeline settings", "account.deactivated": "帐号被禁用", "account.direct": "发送私信给 @{name}", + "account.domain_blocked": "网域被隐藏", "account.edit_profile": "修改个人资料", "account.endorse": "在个人资料中推荐此用户", + "account.endorse.success": "现在你在个人资料上展示了 @{acct}", + "account.familiar_followers": "被 {accounts} 关注", + "account.familiar_followers.more": "你关注了 {count} 位其他用户", "account.follow": "关注", "account.followers": "关注者", "account.followers.empty": "目前无人关注此用户。", @@ -23,8 +25,8 @@ "account.follows.empty": "此用户目前尚未关注任何人。", "account.follows_you": "关注了你", "account.hide_reblogs": "隐藏来自 @{name} 的转发", - "account.last_status": "Last active", - "account.link_verified_on": "此链接的所有权已在 {date} 被检查", + "account.last_status": "最后活跃", + "account.link_verified_on": "此链接的所有权已在 {date} 被验证", "account.locked_info": "此帐号已上锁。帐号的主人会手动审核关注者。", "account.login": "登录", "account.media": "媒体", @@ -32,45 +34,53 @@ "account.mention": "提及", "account.moved_to": "{name} 已经迁移到:", "account.mute": "隐藏 @{name}", - "account.never_active": "Never", + "account.muted": "已隐藏", + "account.never_active": "从未", "account.posts": "帖文", "account.posts_with_replies": "帖文和回复", "account.profile": "个人资料", "account.register": "注册", - "account.remote_follow": "取消关注", + "account.remote_follow": "远程关注", + "account.remove_from_followers": "删除此关注者", "account.report": "举报 @{name}", "account.requested": "正在等待对方同意。点击以取消发送关注请求。", "account.requested_small": "等待同意", + "account.search": "在 @{name} 的内容中搜索", + "account.search_self": "搜索你的帖文", "account.share": "分享 @{name} 的个人资料", "account.show_reblogs": "显示来自 @{name} 的转发", "account.subscribe": "订阅 @{name}", - "account.subscribed": "已订阅", + "account.subscribe.failure": "尝试订阅此帐户时发生错误", + "account.subscribe.success": "您已订阅此帐号", "account.unblock": "解除屏蔽 @{name}", "account.unblock_domain": "不再隐藏来自 {domain} 的内容", "account.unendorse": "不在个人资料中推荐此用户", + "account.unendorse.success": "你已不再展示 @{acct}", "account.unfollow": "取消关注", "account.unmute": "不再隐藏 @{name}", "account.unsubscribe": "取消订阅 @{name}", - "account.verified": "Verified Account", - "account.welcome": "Welcome", - "account_gallery.none": "没有内容", - "account_note.hint": "You can keep notes about this user for yourself (this will not be shared with them):", - "account_note.placeholder": "No comment provided", - "account_note.save": "Save", - "account_note.target": "Note for @{target}", + "account.unsubscribe.failure": "尝试取消订阅此帐户时发生错误", + "account.unsubscribe.success": "您已取消订阅此帐号", + "account.verified": "已认证账户", + "account_gallery.none": "没有可显示的媒体", + "account_note.hint": "你可以为自己保留关于这个用户的笔记(这不会与他们分享):", + "account_note.placeholder": "没有评论", + "account_note.save": "保存", + "account_note.target": " @{target} 的笔记", "account_search.placeholder": "搜索帐号", - "account_timeline.column_settings.show_pinned": "显示置顶", + "actualStatus.edited": "在 {date} 编辑", + "actualStatuses.quote_tombstone": "帖文不可用", "admin.awaiting_approval.approved_message": "{acct} 的注册申请已通过!", - "admin.awaiting_approval.empty_message": "没有未处理的举报,如果有新的举报,它就会显示在这里。", + "admin.awaiting_approval.empty_message": "没有未处理的注册申请,如果有新的申请,它就会显示在这里。", "admin.awaiting_approval.rejected_message": "{acct} 的注册请求被拒绝。", - "admin.dashboard.registration_mode.approval_hint": "在管理员同意注册请求后,任何人都可以加入。", + "admin.dashboard.registration_mode.approval_hint": "在管理员同意注册请求后才可以加入。", "admin.dashboard.registration_mode.approval_label": "需要审核", - "admin.dashboard.registration_mode.closed_hint": "关闭公开注册,但你仍然可以邀请其他人加入。", + "admin.dashboard.registration_mode.closed_hint": "关闭公开注册,但已注册用户仍然可以邀请其他人加入。", "admin.dashboard.registration_mode.closed_label": "私密", "admin.dashboard.registration_mode.open_hint": "任何人都可以加入。", "admin.dashboard.registration_mode.open_label": "公开", "admin.dashboard.registration_mode_label": "注册模式", - "admin.dashboard.settings_saved": "设定已保存!", + "admin.dashboard.settings_saved": "设置已保存!", "admin.dashcounters.domain_count_label": "互联站点", "admin.dashcounters.mau_label": "月度活跃用户", "admin.dashcounters.retention_label": "用户留存", @@ -78,13 +88,14 @@ "admin.dashcounters.user_count_label": "总用户数", "admin.dashwidgets.email_list_header": "邮件列表", "admin.dashwidgets.software_header": "软件版本", - "admin.latest_accounts_panel.more": "Click to see {count} {count, plural, one {account} other {accounts}}", + "admin.latest_accounts_panel.expand_message": "点击查看更多帐号", + "admin.latest_accounts_panel.more": "点击查看 {count} 个帐号", "admin.latest_accounts_panel.title": "最近帐号", "admin.moderation_log.empty_message": "没有管理记录,如果你进行管理,历史记录就会显示在这里。", "admin.reports.actions.close": "关闭举报", "admin.reports.actions.view_status": "查看帖文", "admin.reports.empty_message": "没有未处理的举报,如果有新的举报,它就会显示在这里。", - "admin.reports.report_closed_message": "对@{name} 的举报已关闭", + "admin.reports.report_closed_message": "对 @{name} 的举报已关闭", "admin.reports.report_title": "举报 {acct} 的帖文", "admin.statuses.actions.delete_status": "删除帖文", "admin.statuses.actions.mark_status_not_sensitive": "不再标记为敏感", @@ -104,29 +115,31 @@ "admin.users.actions.promote_to_admin_message": "@{acct} 已被升级为站长", "admin.users.actions.promote_to_moderator": "升级 @{name} 为站务", "admin.users.actions.promote_to_moderator_message": "@{acct} 已被升级为站务", - "admin.users.actions.remove_donor": "Remove @{name} as a donor", - "admin.users.actions.set_donor": "Set @{name} as a donor", - "admin.users.actions.suggest_user": "Suggest @{name}", - "admin.users.actions.unsuggest_user": "Unsuggest @{name}", + "admin.users.actions.remove_donor": "取消 @{name} 的捐赠者头衔", + "admin.users.actions.set_donor": "设置 @{name} 为捐赠者", + "admin.users.actions.suggest_user": "推荐 @{name}", + "admin.users.actions.unsuggest_user": "取消推荐 @{name}", "admin.users.actions.unverify_user": "取消认证 @{name}", "admin.users.actions.verify_user": "认证帐号 @{name}", - "admin.users.remove_donor_message": "@{acct} was removed as a donor", - "admin.users.set_donor_message": "@{acct} was set as a donor", + "admin.users.remove_donor_message": "@{acct} 从捐赠者列表中移除", + "admin.users.set_donor_message": "@{acct} 被设置为捐赠者", "admin.users.user_deactivated_message": "@{acct} 被禁用。", "admin.users.user_deleted_message": "@{acct} 被删除。", - "admin.users.user_suggested_message": "@{acct} was suggested", - "admin.users.user_unsuggested_message": "@{acct} was unsuggested", + "admin.users.user_suggested_message": "@{acct} 被推荐了", + "admin.users.user_unsuggested_message": "@{acct} 被取消推荐了", "admin.users.user_unverified_message": "@{acct} 被取消认证。", "admin.users.user_verified_message": "@{acct} 被认证。", "admin_nav.awaiting_approval": "等待同意", "admin_nav.dashboard": "管理中心", "admin_nav.reports": "举报", - "alert.unexpected.body": "We're sorry for the interruption. If the problem persists, please reach out to our support team. You may also try to {clearCookies} (this will log you out).", - "alert.unexpected.browser": "Browser", + "age_verification.fail": "你必须最少满 {ageMinimum} 岁", + "age_verification.header": "输入你的生日", + "alert.unexpected.body": "我们对这次故障感到抱歉。如果问题持续存在,请联系我们的支持团队。你也可以尝试 {clearCookies} (注意:你的帐户会自动退出)。", + "alert.unexpected.browser": "浏览器", "alert.unexpected.clear_cookies": "清除cookie和浏览器数据", - "alert.unexpected.links.help": "Help Center", - "alert.unexpected.links.status": "Status", - "alert.unexpected.links.support": "Support", + "alert.unexpected.links.help": "帮助中心", + "alert.unexpected.links.status": "状态", + "alert.unexpected.links.support": "支持", "alert.unexpected.message": "发生了意外错误。", "alert.unexpected.return_home": "回到首页", "alert.unexpected.title": "哎呀!", @@ -136,16 +149,16 @@ "aliases.search": "搜索旧帐号", "aliases.success.add": "帐号别名创建成功", "aliases.success.remove": "帐号别名删除成功", - "app_create.name_label": "应用别名", + "app_create.name_label": "应用名称", "app_create.name_placeholder": "例如 'Soapbox'", "app_create.redirect_uri_label": "重定向网址", "app_create.restart": "创建另一个", "app_create.results.app_label": "应用", - "app_create.results.explanation_text": "你已创建一个新应用及其令牌,请复制密钥等信息,离开本页面后这些信息将不再展示。", + "app_create.results.explanation_text": "你已成功创建一个新应用及其令牌,请复制密钥等信息,离开本页面后这些信息将不再展示。", "app_create.results.explanation_title": "应用创建成功", "app_create.results.token_label": "OAuth令牌", "app_create.scopes_label": "权限范围", - "app_create.scopes_placeholder": "例如'read write follow'", + "app_create.scopes_placeholder": "例如 '读取 写入 关注'", "app_create.submit": "创建应用", "app_create.website_label": "网站", "auth.invalid_credentials": "用户名或密码错误", @@ -154,8 +167,8 @@ "backups.empty_message": "没有找到备份 {action}。", "backups.empty_message.action": "现在要备份吗?", "backups.pending": "等待备份", - "beta.also_available": "Available in:", - "birthday_panel.title": "Birthdays", + "beta.also_available": "在此时可用:", + "birthday_panel.title": "生日", "boost_modal.combo": "下次按住 {combo} 即可跳过此提示", "bundle_column_error.body": "载入组件时发生错误。", "bundle_column_error.retry": "重试", @@ -163,16 +176,16 @@ "bundle_modal_error.close": "关闭", "bundle_modal_error.message": "载入组件时发生错误。", "bundle_modal_error.retry": "重试", - "card.back.label": "Back", + "card.back.label": "返回", "chat.actions.send": "发送", - "chat.input.placeholder": "发送聊天信息", + "chat.input.placeholder": "发送聊天信息…", "chat_panels.main_window.empty": "还没有聊天信息,找人聊聊吧!", "chat_panels.main_window.title": "聊天", - "chats.actions.delete": "删除", + "chats.actions.delete": "删除信息", "chats.actions.more": "更多选项", "chats.actions.report": "举报用户", - "chats.attachment": "Attachment", - "chats.attachment_image": "Image", + "chats.attachment": "附件", + "chats.attachment_image": "图片", "chats.audio_toggle_off": "关闭声音提醒", "chats.audio_toggle_on": "打开声音提醒", "chats.dividers.today": "此刻", @@ -182,16 +195,16 @@ "column.admin.moderation_log": "管理记录", "column.admin.reports": "举报", "column.admin.reports.menu.moderation_log": "管理记录", - "column.admin.users": "Users", + "column.admin.users": "用户", "column.aliases": "帐号别名", "column.aliases.create_error": "创建别名出错", "column.aliases.delete": "删除", "column.aliases.delete_error": "删除别名出错", - "column.aliases.subheading_add_new": "新增别名", + "column.aliases.subheading_add_new": "添加新别名", "column.aliases.subheading_aliases": "当前别名", "column.app_create": "创建应用", "column.backups": "备份", - "column.birthdays": "Birthdays", + "column.birthdays": "生日", "column.blocks": "已屏蔽的用户", "column.bookmarks": "书签", "column.chats": "聊天", @@ -199,69 +212,71 @@ "column.crypto_donate": "加密货币捐款", "column.developers": "开发者", "column.direct": "私信", - "column.directory": "Browse profiles", + "column.directory": "发现更多", "column.domain_blocks": "已屏蔽的站点", "column.edit_profile": "编辑个人资料", "column.export_data": "导出数据", - "column.favourited_statuses": "Liked posts", + "column.favourited_statuses": "点赞的帖文", "column.favourites": "点赞", "column.federation_restrictions": "联邦限制", "column.filters": "过滤词", - "column.filters.add_new": "添加新的过滤词", + "column.filters.add_new": "新增过滤词", "column.filters.conversations": "对话", "column.filters.create_error": "添加过滤词时出错。", "column.filters.delete": "删除过滤词", "column.filters.delete_error": "删除过滤词时出错。", "column.filters.drop_header": "丢弃而非隐藏", - "column.filters.drop_hint": "被丢弃的帖文会不可逆转地消失,即便移除过滤词之后也一样。", + "column.filters.drop_hint": "被丢弃的帖文会不可逆转地消失,即便移除过滤词之后也不会回复", "column.filters.expires": "失效时间", "column.filters.expires_hint": "还未支持失效时间。", - "column.filters.home_timeline": "本站时间轴", + "column.filters.home_timeline": "主页时间轴", "column.filters.keyword": "关键词", "column.filters.notifications": "通知", "column.filters.public_timeline": "公共时间轴", "column.filters.subheading_add_new": "添加新的过滤词", "column.filters.subheading_filters": "查看已有过滤词", - "column.filters.whole_word_header": "整个词条", + "column.filters.whole_word_header": "全词匹配", "column.filters.whole_word_hint": "如果关键词只包含字母和数字,将只在词语完全匹配时才会应用。", "column.follow_requests": "关注请求", - "column.followers": "Followers", - "column.following": "Following", - "column.groups": "列表", + "column.followers": "关注者", + "column.following": "正在关注", + "column.groups": "群组", "column.home": "主页", "column.import_data": "导入数据", "column.info": "站点信息", "column.lists": "列表", - "column.mentions": "Mentions", + "column.mentions": "提及", "column.mfa": "双重认证", "column.mfa_cancel": "取消", "column.mfa_confirm_button": "确定", "column.mfa_disable_button": "关闭", "column.mfa_setup": "同意并继续", - "column.migration": "Account migration", + "column.migration": "账户迁移", "column.mutes": "已静音的用户", "column.notifications": "通知", - "column.pins": "Pinned posts", + "column.pins": "置顶帖文", "column.preferences": "选项", "column.public": "跨站公共时间轴", - "column.reactions": "Reactions", - "column.reblogs": "Reposts", + "column.reactions": "互动", + "column.reblogs": "转帖", "column.remote": "跨站公共时间轴", "column.scheduled_statuses": "定时帖文", "column.search": "搜索", - "column.security": "安全", - "column.settings_store": "Settings store", + "column.settings_store": "设置储存", "column.soapbox_config": "Soapbox设置", - "column.test": "Test timeline", + "column.test": "测试时间线", "column_back_button.label": "返回", - "column_forbidden.body": "You do not have permission to access this page.", - "column_forbidden.title": "Forbidden", + "column_forbidden.body": "你没有权限访问这个页面。", + "column_forbidden.title": "无权访问", "column_header.show_settings": "显示设置", - "common.cancel": "Cancel", + "common.cancel": "取消", + "common.error": "好像有什么不对劲?尝试重新加载页面", "community.column_settings.media_only": "仅媒体", "community.column_settings.title": "本地时间线设置", + "compare_history_modal.header": "编辑历史", "compose.character_counter.title": "最大字符数: {maxChars}; 已使用 {chars}", - "compose.invalid_schedule": "定时帖文只能设置为五分钟后或更晚", + "compose.edit_success": "你的帖文已编辑", + "compose.invalid_schedule": "定时帖文只能设置为五分钟后或更晚发送", "compose.submit_success": "帖文已发送", "compose_form.direct_message_warning": "这条帖文仅对所有被提及的用户可见。", "compose_form.hashtag_warning": "这条帖文被设置为“不公开”,因此它不会出现在任何话题标签的列表下。只有公开的帖文才能通过话题标签进行搜索。", @@ -273,149 +288,162 @@ "compose_form.placeholder": "在想什么?", "compose_form.poll.add_option": "添加一个选项", "compose_form.poll.duration": "投票持续时间", + "compose_form.poll.multiselect": "多选", + "compose_form.poll.multiselect_detail": "允许用户多选答案", "compose_form.poll.option_placeholder": "选项 {number}", + "compose_form.poll.remove": "删除投票", "compose_form.poll.remove_option": "移除这个选项", "compose_form.poll.switch_to_multiple": "投票改为多选", "compose_form.poll.switch_to_single": "投票改为单选", + "compose_form.poll_placeholder": "添加投票主题...", "compose_form.publish": "发布", "compose_form.publish_loud": "{publish}!", + "compose_form.save_changes": "保存更改", "compose_form.schedule": "定时发布", "compose_form.scheduled_statuses.click_here": "点击此处", "compose_form.scheduled_statuses.message": "你有定时帖文,{click_here}查看。", - "compose_form.sensitive.hide": "标记媒体为敏感内容", - "compose_form.sensitive.marked": "媒体已被标记为敏感内容。", - "compose_form.sensitive.unmarked": "媒体未被标记为敏感内容。", "compose_form.spoiler.marked": "正文已被折叠在警告信息之后。", "compose_form.spoiler.unmarked": "正文未被折叠。", "compose_form.spoiler_placeholder": "折叠部分的警告消息", + "compose_form.spoiler_remove": "移除敏感内容", + "compose_form.spoiler_title": "敏感内容", "confirmation_modal.cancel": "取消", - "confirmations.admin.deactivate_user.confirm": "禁用 @{name}", - "confirmations.admin.deactivate_user.heading": "Deactivate @{acct}", - "confirmations.admin.deactivate_user.message": "你确定要禁用帐号 @{acct} 吗?这无法撤回!", + "confirmations.admin.deactivate_user.confirm": "禁用帐号 @{name}", + "confirmations.admin.deactivate_user.heading": "禁用帐号 @{acct}", + "confirmations.admin.deactivate_user.message": "你确定要禁用帐号 @{acct} 吗?该操作无法撤回!", "confirmations.admin.delete_local_user.checkbox": "我确定我要删除本站帐号", "confirmations.admin.delete_status.confirm": "删除帖文", - "confirmations.admin.delete_status.heading": "Delete post", - "confirmations.admin.delete_status.message": "你确定要删除 @{acct} 的帖文吗?这无法撤回!", + "confirmations.admin.delete_status.heading": "删除帖文", + "confirmations.admin.delete_status.message": "你确定要删除帖文 @{acct} 吗?该操作无法撤回!", "confirmations.admin.delete_user.confirm": "删除帐号 @{name}", - "confirmations.admin.delete_user.heading": "Delete @{acct}", - "confirmations.admin.delete_user.message": "你确定要删除本站帐号 @{acct} 吗?这无法撤回!", - "confirmations.admin.mark_status_not_sensitive.confirm": "不再标记为敏感帖文", - "confirmations.admin.mark_status_not_sensitive.heading": "Mark post not sensitive.", - "confirmations.admin.mark_status_not_sensitive.message": "你要标记帐号 @{acct} 的帖文不再敏感", + "confirmations.admin.delete_user.heading": "删除帐号 @{acct}", + "confirmations.admin.delete_user.message": "你确定要删除本站帐号 @{acct} 吗?该操作无法撤回!", + "confirmations.admin.mark_status_not_sensitive.confirm": "敏感为不敏感", + "confirmations.admin.mark_status_not_sensitive.heading": "敏感为不敏感", + "confirmations.admin.mark_status_not_sensitive.message": "你要标记帐号 @{acct} 的帖文为不敏感", "confirmations.admin.mark_status_sensitive.confirm": "标记为敏感帖文", - "confirmations.admin.mark_status_sensitive.heading": "Mark post sensitive", + "confirmations.admin.mark_status_sensitive.heading": "标记为敏感帖文", "confirmations.admin.mark_status_sensitive.message": "你要标记帐号 @{acct} 的帖文为敏感", - "confirmations.admin.reject_user.confirm": "Reject @{name}", - "confirmations.admin.reject_user.heading": "Reject @{acct}", - "confirmations.admin.reject_user.message": "You are about to reject @{acct} registration request. This action cannot be undone.", + "confirmations.admin.reject_user.confirm": "拒绝 @{name}", + "confirmations.admin.reject_user.heading": "拒绝 @{acct}", + "confirmations.admin.reject_user.message": "你正准备拒绝 @{acct} 的注册请求。这一操作不能撤销。", "confirmations.block.block_and_report": "屏蔽与举报", "confirmations.block.confirm": "屏蔽", - "confirmations.block.heading": "Block @{name}", + "confirmations.block.heading": "屏蔽 @{name}", "confirmations.block.message": "你确定要屏蔽 {name} 吗?", "confirmations.delete.confirm": "删除", - "confirmations.delete.heading": "Delete post", + "confirmations.delete.heading": "删除帖文", "confirmations.delete.message": "你确定要删除这条帖文吗?", "confirmations.delete_list.confirm": "删除", - "confirmations.delete_list.heading": "Delete list", + "confirmations.delete_list.heading": "删除列表", "confirmations.delete_list.message": "你确定要永久删除这个列表吗?", - "confirmations.domain_block.confirm": "隐藏整个站点的内容", - "confirmations.domain_block.heading": "Block {domain}", - "confirmations.domain_block.message": "你真的确定要隐藏所有来自 {domain} 的内容吗?多数情况下,屏蔽或隐藏几个特定的用户就已经足够了。来自该站点的内容将不再出现在你的任何公共时间轴或通知列表里。来自该站点的关注者将会被移除。", + "confirmations.domain_block.confirm": "阻止整个站点的内容", + "confirmations.domain_block.heading": "阻止 {domain}", + "confirmations.domain_block.message": "你真的确定要阻止所有来自 {domain} 的内容吗?多数情况下,阻止几个特定的用户就已经足够了。来自该站点的内容将不再出现在你的任何公共时间轴或通知列表里。来自该站点的关注者将会被移除。", "confirmations.mute.confirm": "隐藏", - "confirmations.mute.heading": "Mute @{name}", + "confirmations.mute.heading": "隐藏 @{name}", "confirmations.mute.message": "你确定要隐藏 {name} 吗?", "confirmations.redraft.confirm": "删除并重新编辑", - "confirmations.redraft.heading": "Delete & redraft", + "confirmations.redraft.heading": "删除并重新编辑", "confirmations.redraft.message": "你确定要删除这条帖文并重新编辑它吗?所有相关的转发和点赞都会被清除,回复将会失去关联。", "confirmations.register.needs_approval": "你的帐号在被管理员审核,请稍等", - "confirmations.register.needs_approval.header": "Approval needed", - "confirmations.register.needs_confirmation": "请检查你的邮箱 {email} ,我们需要邮箱验证注册", - "confirmations.register.needs_confirmation.header": "Confirmation needed", + "confirmations.register.needs_approval.header": "需要审核", + "confirmations.register.needs_confirmation": "我们已经发送了指示到你的邮箱 {email} 中,请检查收件箱并点击邮件中的链接以继续。", + "confirmations.register.needs_confirmation.header": "需要验证", + "confirmations.remove_from_followers.confirm": "删除", + "confirmations.remove_from_followers.message": "确定要从你的关注者中删除 {name} 吗?", "confirmations.reply.confirm": "回复", "confirmations.reply.message": "回复此消息将会覆盖当前正在编辑的信息。确定继续吗?", - "confirmations.scheduled_status_delete.confirm": "Cancel", - "confirmations.scheduled_status_delete.heading": "Cancel scheduled post", - "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", + "confirmations.scheduled_status_delete.confirm": "取消", + "confirmations.scheduled_status_delete.heading": "取消帖文定时发布", + "confirmations.scheduled_status_delete.message": "你确定要取消这篇帖文的定时发布吗?", "confirmations.unfollow.confirm": "取消关注", - "confirmations.unfollow.heading": "Unfollow {name}", + "confirmations.unfollow.heading": "取消关注 {name}", "confirmations.unfollow.message": "你确定要取消关注 {name} 吗?", "crypto_donate.explanation_box.message": "{siteTitle} 接受用户向以下钱包地址捐赠任意数量的加密货币。感谢你的支持!", "crypto_donate.explanation_box.title": "发送加密货币捐赠", - "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", + "crypto_donate_panel.actions.view": "点击查看 {count} {count, plural, one {wallet} other {wallets}}", "crypto_donate_panel.heading": "捐赠加密货币", "crypto_donate_panel.intro.message": "{siteTitle} 接受用户捐赠加密货币。感谢你的支持!", + "datepicker.day": "日", "datepicker.hint": "设定发送时间……", - "datepicker.next_month": "Next month", - "datepicker.next_year": "Next year", - "datepicker.previous_month": "Previous month", - "datepicker.previous_year": "Previous year", - "developers.challenge.answer_label": "Answer", - "developers.challenge.answer_placeholder": "Your answer", - "developers.challenge.fail": "Wrong answer", - "developers.challenge.message": "What is the result of calling {function}?", - "developers.challenge.submit": "Become a developer", - "developers.challenge.success": "You are now a developer", - "developers.leave": "You have left developers", + "datepicker.month": "月", + "datepicker.next_month": "下个月", + "datepicker.next_year": "明年", + "datepicker.previous_month": "上个月", + "datepicker.previous_year": "去年", + "datepicker.year": "年", + "developers.challenge.answer_label": "回答", + "developers.challenge.answer_placeholder": "你的回答", + "developers.challenge.fail": "回答错误", + "developers.challenge.message": "调用函数 {function} 的结果是什么?", + "developers.challenge.submit": "成为开发者", + "developers.challenge.success": "你已成为开发者", + "developers.leave": "你已退出开发者", "developers.navigation.app_create_label": "创建应用", - "developers.navigation.intentional_error_label": "触发一个错误", - "developers.navigation.leave_developers_label": "Leave developers", - "developers.navigation.network_error_label": "Network error", - "developers.navigation.settings_store_label": "Settings store", - "developers.navigation.test_timeline_label": "Test timeline", - "developers.settings_store.hint": "It is possible to directly edit your user settings here. BE CAREFUL! Editing this section can break your account, and you will only be able to recover through the API.", + "developers.navigation.intentional_error_label": "触发错误", + "developers.navigation.leave_developers_label": "退出开发者", + "developers.navigation.network_error_label": "网络错误", + "developers.navigation.settings_store_label": "设置储存", + "developers.navigation.test_timeline_label": "测试时间线", + "developers.settings_store.hint": "在这里可以直接编辑你的用户设置。请注意! 编辑这一部分会破坏你的账户,你只能通过API恢复。", + "direct.body": "一个全新的私聊系统将很快推出。敬请期待!", "direct.search_placeholder": "发送私信给……", - "directory.federated": "From known fediverse", - "directory.local": "From {domain} only", - "directory.new_arrivals": "New arrivals", - "directory.recently_active": "Recently active", + "directory.federated": "来自已知联邦宇宙", + "directory.local": "仅来自 {domain}", + "directory.new_arrivals": "新增访客", + "directory.recently_active": "最近活跃", "edit_federation.followers_only": "对关注者以外的用户隐藏帖文", "edit_federation.force_nsfw": "将附件强制标记为敏感", "edit_federation.media_removal": "去掉媒体", "edit_federation.reject": "拒绝所有信息交互", "edit_federation.save": "保存", "edit_federation.success": "{host} 联邦设定已保存", - "edit_federation.unlisted": "将帖文强制标记为不公开", - "edit_password.header": "Change Password", + "edit_federation.unlisted": "将帖文强制标记移出公共时间轴", + "edit_password.header": "修改密码", "edit_profile.error": "个人资料更新失败", "edit_profile.fields.accepts_email_list_label": "接收邮件列表", "edit_profile.fields.avatar_label": "头像", "edit_profile.fields.bio_label": "简介", - "edit_profile.fields.bio_placeholder": "请介绍一下你自己。", - "edit_profile.fields.birthday_label": "Birthday", - "edit_profile.fields.birthday_placeholder": "Your birthday", - "edit_profile.fields.bot_label": "这是一个机器人帐号", - "edit_profile.fields.discoverable_label": "Allow account discovery", + "edit_profile.fields.bio_placeholder": "请介绍一下你自己", + "edit_profile.fields.birthday_label": "生日", + "edit_profile.fields.birthday_placeholder": "你的生日", + "edit_profile.fields.bot_label": "这是一个机器人账户", + "edit_profile.fields.discoverable_label": "允许别人发现你", "edit_profile.fields.display_name_label": "昵称", "edit_profile.fields.display_name_placeholder": "你的昵称", "edit_profile.fields.header_label": "个人资料页横幅图片", - "edit_profile.fields.hide_network_label": "隐藏关注信息", - "edit_profile.fields.location_label": "Location", - "edit_profile.fields.location_placeholder": "Location", - "edit_profile.fields.locked_label": "保护你的帐号", + "edit_profile.fields.hide_network_label": "隐藏网络状态", + "edit_profile.fields.location_label": "地点", + "edit_profile.fields.location_placeholder": "地点", + "edit_profile.fields.locked_label": "锁定账户", "edit_profile.fields.meta_fields.content_placeholder": "内容", "edit_profile.fields.meta_fields.label_placeholder": "标签", - "edit_profile.fields.stranger_notifications_label": "不接收来自陌生人的通知", - "edit_profile.fields.website_label": "Website", - "edit_profile.fields.website_placeholder": "Display a Link", - "edit_profile.header": "Edit Profile", + "edit_profile.fields.verified_display_name": "经过验证的用户无法修改昵称", + "edit_profile.fields.meta_fields_label": "自定义个人资料字段", + "edit_profile.fields.stranger_notifications_label": "屏蔽来自陌生人的通知", + "edit_profile.fields.website_label": "网站", + "edit_profile.fields.website_placeholder": "显示链接", + "edit_profile.header": "编辑个人资料", "edit_profile.hints.accepts_email_list": "接收新闻和推广邮件", - "edit_profile.hints.avatar": "只支持 PNG、GIF 或 JPG 格式,大小不超过{size}。图片分辨率将会压缩至 1500x500px。", - "edit_profile.hints.bot": "来自这个帐号的绝大多数操作都是自动进行的,并且可能无人监控。", - "edit_profile.hints.discoverable": "Display account in profile directory and allow indexing by external services", - "edit_profile.hints.header": "只支持 PNG、GIF 或 JPG 格式,大小不超过{size}。图片分辨率将会压缩至 1500x500px。", - "edit_profile.hints.hide_network": "个人资料中不显示你关注的用户和关注你的用户。", - "edit_profile.hints.locked": "你需要手动审核所有关注请求。", - "edit_profile.hints.stranger_notifications": "只显示来自你所关注用户的通知。", + "edit_profile.hints.avatar": "只支持 PNG、GIF 或 JPG 格式,大小将会被缩放至 {size}。", + "edit_profile.hints.bot": "此帐号主要执行自动化操作,可能不受用户控制", + "edit_profile.hints.discoverable": "在配置文件目录中显示帐户并允许外部服务索引", + "edit_profile.hints.header": "只支持 PNG、GIF 或 JPG 格式,大小将会被缩放至 {size}。", + "edit_profile.hints.hide_network": "您关注的人和关注您的人不会显示在您的个人资料中", + "edit_profile.hints.locked": "需要您手动批准关注请求", + "edit_profile.hints.meta_fields": "你能在个人资料页面上最多显示 {count} 条自定义信息", + "edit_profile.hints.stranger_notifications": "仅显示来自您关注的人的通知", "edit_profile.save": "保存", - "edit_profile.success": "个人资料已保存。", - "email_passthru.confirmed.body": "Close this tab and continue the registration process on the {bold} from which you sent this email confirmation.", - "email_passthru.confirmed.heading": "Email Confirmed!", - "email_passthru.generic_fail.body": "Please request a new email confirmation.", - "email_passthru.generic_fail.heading": "Something Went Wrong", - "email_passthru.token_expired.body": "Your email token has expired. Please request a new email confirmation from the {bold} from which you sent this email confirmation.", - "email_passthru.token_expired.heading": "Token Expired", - "email_passthru.token_not_found.body": "Your email token was not found. Please request a new email confirmation from the {bold} from which you sent this email confirmation.", - "email_passthru.token_not_found.heading": "Invalid Token", + "edit_profile.success": "个人资料已保存", + "email_passthru.confirmed.body": "关闭此标签,并在您发送此电子邮件确认的 {bold} 上继续注册过程", + "email_passthru.confirmed.heading": "邮箱地址已确认!", + "email_passthru.generic_fail.body": "请重新请求确认邮件。", + "email_passthru.generic_fail.heading": "有点不对劲…", + "email_passthru.token_expired.body": "您的电子邮件令牌已经过期。请从您发送此电子邮件确认的 {bold} 处申请新的电子邮件确认。", + "email_passthru.token_expired.heading": "令牌已经过期", + "email_passthru.token_not_found.body": "您的电子邮件令牌已经过期。请从您发送此电子邮件确认的 {bold} 处申请新的电子邮件确认。", + "email_passthru.token_not_found.heading": "非法令牌!", "embed.instructions": "要在你的站点上嵌入这条帖文,请复制以下代码:", "embed.preview": "它会像这样显示出来:", "emoji_button.activity": "活动", @@ -432,7 +460,7 @@ "emoji_button.search_results": "搜索结果", "emoji_button.symbols": "符号", "emoji_button.travel": "旅行和地点", - "empty_column.account_blocked": "你被 @{accountUsername} 屏蔽了。", + "empty_column.account_blocked": "你被 @{accountUsername} 屏蔽了", "empty_column.account_favourited_statuses": "没有点赞帖文", "empty_column.account_timeline": "这里没有帖文!", "empty_column.account_unavailable": "个人资料不可用", @@ -462,6 +490,7 @@ "empty_column.search.accounts": "无帐号匹配 \"{term}\"", "empty_column.search.hashtags": "无标签匹配 \"{term}\"", "empty_column.search.statuses": "无帖文匹配 \"{term}\"", + "empty_column.test": "测试时间线是空的。", "export_data.actions.export": "导出", "export_data.actions.export_blocks": "导出屏蔽列表", "export_data.actions.export_follows": "导出关注列表", @@ -475,19 +504,21 @@ "export_data.success.blocks": "屏蔽列表导出完毕", "export_data.success.followers": "关注列表导出完毕", "export_data.success.mutes": "静音列表导出完毕", - "federation_restriction.federated_timeline_removal": "Fediverse timeline removal", - "federation_restriction.followers_only": "Hidden except to followers", - "federation_restriction.full_media_removal": "Full media removal", - "federation_restriction.media_nsfw": "Attachments marked NSFW", - "federation_restriction.partial_media_removal": "Partial media removal", - "federation_restrictions.empty_message": "{siteTitle} has not restricted any instances.", - "federation_restrictions.explanation_box.message": "Normally servers on the Fediverse can communicate freely. {siteTitle} has imposed restrictions on the following servers.", - "federation_restrictions.explanation_box.title": "Instance-specific policies", - "federation_restrictions.not_disclosed_message": "{siteTitle} does not disclose federation restrictions through the API.", + "federation_restriction.federated_timeline_removal": "从联邦宇宙时间线移除", + "federation_restriction.followers_only": "仅关注者可见", + "federation_restriction.full_media_removal": "完全移除媒体", + "federation_restriction.media_nsfw": "附件标注为 NSFW", + "federation_restriction.partial_media_removal": "部分移除媒体", + "federation_restrictions.empty_message": "{siteTitle} 没有限制任何实例。", + "federation_restrictions.explanation_box.message": "通常情况下,联邦宇宙上的服务器可以自由通信。然而 {siteTitle} 对以下服务器实施了限制。", + "federation_restrictions.explanation_box.title": "实例相关政策", + "federation_restrictions.not_disclosed_message": "{siteTitle} 没有通过API向联邦宇宙披露限制。", "fediverse_tab.explanation_box.dismiss": "不再显示", "fediverse_tab.explanation_box.explanation": "{site_title} 是联邦宇宙的一部分, 一个由数个站点组成的社交网络。你在这里看到的帖文来自其他站点。你可以自由地与他们打交道,或者屏蔽任何你不喜欢的站点。请注意第二个@符号后的完整用户名,以了解帖文来自哪个站点。要想只看到 {site_title} 的帖文,请访问 {local} 。", "fediverse_tab.explanation_box.title": "什么是联邦宇宙?", - "filters.added": "过滤已添加", + "feed_suggestions.heading": "建议的个人资料", + "feed_suggestions.view_all": "查看全部", + "filters.added": "过滤条件已添加", "filters.context_header": "过滤词场景", "filters.context_hint": "过滤词的应用场景", "filters.filters_list_context_label": "过滤词场景:", @@ -498,56 +529,46 @@ "filters.filters_list_phrase_label": "关键词:", "filters.filters_list_whole-word": "整个词条", "filters.removed": "过滤已移除", - "follow_recommendation.subhead": "Let's get started!", + "follow_recommendation.subhead": "让我们开始吧!", "follow_recommendations.done": "完成", - "follow_recommendations.heading": "Follow people you'd like to see posts from! Here are some suggestions.", - "follow_recommendations.lead": "Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!", + "follow_recommendations.heading": "关注你感兴趣的用户,这里是我们的推荐清单", + "follow_recommendations.lead": "你关注的人的帖子将按时间顺序显示在你的主页上。不用担心搞错,你可以在任何时候轻松地取消对别人的关注!", "follow_request.authorize": "同意", "follow_request.reject": "拒绝", "forms.copy": "复制", - "forms.hide_password": "Hide password", - "forms.show_password": "Show password", + "forms.hide_password": "隐藏密码", + "forms.show_password": "显示密码", "getting_started.open_source_notice": "{code_name} 是开源软件。欢迎前往 GitLab({code_link} (v{code_version}))贡献代码或反馈问题。", - "group.detail.archived_group": "Archived group", - "group.members.empty": "这个列表没有任何帐号。", + "group.members.empty": "这个列表没有任何帐号", "group.removed_accounts.empty": "这个列表没有被移除的帐号", "groups.card.join": "加入", "groups.card.members": "成员", - "groups.card.roles.admin": "You're an admin", - "groups.card.roles.member": "You're a member", + "groups.card.roles.admin": "你是管理员", + "groups.card.roles.member": "你是成员", "groups.card.view": "查看", - "groups.create": "创建列表", - "groups.detail.role_admin": "You're an admin", - "groups.edit": "Edit", - "groups.form.coverImage": "上传(可选)", + "groups.create": "创建群组", + "groups.form.coverImage": "上传横幅图片(可选)", "groups.form.coverImageChange": "横幅图片已上传", - "groups.form.create": "创建列表", + "groups.form.create": "创建群组", "groups.form.description": "描述", "groups.form.title": "标题", - "groups.form.update": "更新列表", - "groups.join": "Join group", - "groups.leave": "Leave group", + "groups.form.update": "更新群组", "groups.removed_accounts": "已移除帐号", - "groups.sidebar-panel.item.no_recent_activity": "No recent activity", - "groups.sidebar-panel.item.view": "new posts", - "groups.sidebar-panel.show_all": "Show all", - "groups.sidebar-panel.title": "Groups You're In", "groups.tab_admin": "管理", "groups.tab_featured": "热门", "groups.tab_member": "成员", - "hashtag.column_header.tag_mode.all": "以及 {additional}", - "hashtag.column_header.tag_mode.any": "或是 {additional}", - "hashtag.column_header.tag_mode.none": "而不用 {additional}", + "hashtag.column_header.tag_mode.all": "以及{additional}", + "hashtag.column_header.tag_mode.any": "或是{additional}", + "hashtag.column_header.tag_mode.none": "而不用{additional}", "header.home.label": "主页", - "header.login.forgot_password": "Forgot password?", + "header.login.forgot_password": "忘记了密码?", "header.login.label": "登录", - "header.login.password.label": "Password", - "header.login.username.placeholder": "Email or username", - "header.register.label": "Register", - "home.column_settings.show_direct": "显示私信", + "header.login.password.label": "密码", + "header.login.username.placeholder": "邮箱或用户名", + "header.preview_timeline.label": "浏览首页", + "header.register.label": "注册", "home.column_settings.show_reblogs": "显示转发", "home.column_settings.show_replies": "显示回复", - "home.column_settings.title": "Home settings", "icon_button.icons": "图标", "icon_button.label": "选择图标", "icon_button.not_found": "没有图标! (╯°□°)╯︵ ┻━┻", @@ -564,24 +585,11 @@ "import_data.success.blocks": "屏蔽帐号列表导入完成", "import_data.success.followers": "关注帐号列表导入完成", "import_data.success.mutes": "静音帐号列表导入完成", - "input.password.hide_password": "Hide password", - "input.password.show_password": "Show password", + "input.password.hide_password": "隐藏密码", + "input.password.show_password": "显示密码", "intervals.full.days": "{number} 天", "intervals.full.hours": "{number} 小时", "intervals.full.minutes": "{number} 分钟", - "introduction.federation.action": "下一步", - "introduction.federation.home.headline": "主页", - "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", - "introduction.interactions.action": "Finish tutorial!", - "introduction.interactions.favourite.headline": "Favorite", - "introduction.interactions.favourite.text": "You can save a post for later, and let the author know that you liked it, by favoriting it.", - "introduction.interactions.reblog.headline": "Repost", - "introduction.interactions.reblog.text": "You can share other people's posts with your followers by reposting them.", - "introduction.interactions.reply.headline": "Reply", - "introduction.interactions.reply.text": "You can reply to other people's and your own posts, which will chain them together in a conversation.", - "introduction.welcome.action": "Let's go!", - "introduction.welcome.headline": "First steps", - "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", "keyboard_shortcuts.back": "返回上一页", "keyboard_shortcuts.blocked": "打开被屏蔽用户列表", "keyboard_shortcuts.boost": "转发", @@ -610,17 +618,17 @@ "keyboard_shortcuts.toot": "发帖", "keyboard_shortcuts.unfocus": "取消输入", "keyboard_shortcuts.up": "在列表中让光标上移", - "landing_page_modal.download": "Download", - "landing_page_modal.helpCenter": "Help Center", + "landing_page_modal.download": "下载", + "landing_page_modal.helpCenter": "帮助中心", "lightbox.close": "关闭", "lightbox.next": "下一个", "lightbox.previous": "上一个", "lightbox.view_context": "查看上下文", "list.click_to_add": "点击以添加帐号到列表", - "list_adder.header_title": "在列表内添加或删除帐号", + "list_adder.header_title": "从列表中添加或删除帐号", "lists.account.add": "添加到列表", "lists.account.remove": "从列表中移除", - "lists.delete": "Delete list", + "lists.delete": "删除列表", "lists.edit": "编辑列表", "lists.edit.submit": "更改列表", "lists.new.create": "新建列表", @@ -635,48 +643,59 @@ "login.fields.otp_code_hint": "输入双重认证应用里的代码,或者输入恢复代码", "login.fields.otp_code_label": "双重认证代码:", "login.fields.password_placeholder": "密码", - "login.fields.username_label": "Email or username", + "login.fields.username_label": "邮箱或用户名", "login.log_in": "登录", - "login.otp_log_in": "OTP方式登录", + "login.otp_log_in": "双重认证登录", + "login.otp_log_in.fail": "代码无效,请重新输入", "login.reset_password_hint": "登录时出现问题了吗?", - "login.sign_in": "Sign in", + "login.sign_in": "登录", + "login_form.header": "登录", "media_gallery.toggle_visible": "切换显示/隐藏", "media_panel.empty_message": "未找到媒体", "media_panel.title": "媒体", - "mfa.confirm.success_message": "MFA confirmed", - "mfa.disable.success_message": "MFA disabled", + "mfa.confirm.success_message": "多重身份认证 (MFA) 已成功启用", + "mfa.disable.success_message": "多重身份认证 (MFA) 已成功禁用", "mfa.mfa_disable_enter_password": "输入帐号密码以禁用双重认证:", - "mfa.mfa_setup.code_hint": "Enter the code from your two-factor app.", - "mfa.mfa_setup.code_placeholder": "Code", - "mfa.mfa_setup.password_hint": "Enter your current password to confirm your identity.", - "mfa.mfa_setup.password_placeholder": "Password", + "mfa.mfa_setup.code_hint": "输入显示在双重认证应用里的代码", + "mfa.mfa_setup.code_placeholder": "代码", + "mfa.mfa_setup.password_hint": "输入你的当前密码来确认身份", + "mfa.mfa_setup.password_placeholder": "密码", "mfa.mfa_setup_scan_description": "请使用 Google 身份验证器或其他的TOTP双重认证手机应用扫描此处的二维码。启用双重认证后,在登录时,你需要输入该应用生成的代码。", "mfa.mfa_setup_scan_title": "如果你无法扫描二维码,请手动输入下列文本:", "mfa.mfa_setup_verify_title": "启用", "mfa.otp_enabled_description": "你已经启用双重认证。", "mfa.otp_enabled_title": "双重认证已启用", "mfa.setup_recoverycodes": "恢复代码", - "mfa.setup_warning": "请立即将恢复代码保存或写到纸上,否则你可能无法登录帐号。", - "migration.fields.acct.label": "Handle of the new account", - "migration.fields.acct.placeholder": "username@domain", - "migration.fields.confirm_password.label": "Current password", - "migration.hint": "This will move your followers to the new account. No other data will be moved. To perform migration, you need to {link} on your new account first.", - "migration.hint.link": "create an account alias", - "migration.move_account.fail": "Account migration failed.", - "migration.move_account.success": "Account successfully moved.", - "migration.submit": "Move followers", + "mfa.setup_warning": "请立即将恢复代码保存或写到纸上,并建议与个人信息分开存放,否则在丢失双重认证应用时,你可能无法登录帐号。", + "migration.fields.acct.label": "新账户的用户名", + "migration.fields.acct.placeholder": "用户名@域名", + "migration.fields.confirm_password.label": "当前密码", + "migration.hint": "这将把你的关注者转移到新账户。没有其他数据会被转移。要执行迁移,你需要先{link}你的新账户", + "migration.hint.link": "创建一个账户别名", + "migration.move_account.fail": "账户迁移失败。", + "migration.move_account.success": "账户迁移成功了!", + "migration.submit": "迁移关注者", "missing_description_modal.cancel": "取消", "missing_description_modal.continue": "发布", "missing_description_modal.description": "仍然继续发布吗?", - "missing_description_modal.text": "附件没有描述信息。", + "missing_description_modal.text": "附件没有描述信息。仍然继续发布吗?", "missing_indicator.label": "找不到内容", "missing_indicator.sublabel": "无法找到此资源", - "mobile.also_available": "Available in:", - "morefollows.followers_label": "和{count} 来自其他站点的 {count, plural, one {关注者} other {关注者}} 。", - "morefollows.following_label": "和{count} 来自其他站点的 {count, plural, one {正在关注} other {正在关注}} 。", + "mobile.also_available": "在此处可用:", + "moderation_overlay.contact": "联系管理员", + "moderation_overlay.hide": "隐藏内容", + "moderation_overlay.show": "显示内容", + "moderation_overlay.subtitle": "此帖文已发送至站点管理员以供审核,目前仅自己可见。如果你认为这是系统错误,请联系管理员以获取支持。", + "moderation_overlay.title": "内容审核中", + "morefollows.followers_label": "和{count}来自其他站点的{count, plural, one {关注者} other {关注者}} 。", + "morefollows.following_label": "和{count}来自其他站点的{count, plural, one {正在关注} other {正在关注}} 。", "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知?", + "navbar.login.action": "登录", + "navbar.login.forgot_password": "忘记密码?", + "navbar.login.password.label": "密码", + "navbar.login.username.placeholder": "邮箱或用户名", "navigation.chats": "聊天", - "navigation.compose": "Compose", + "navigation.compose": "发布新帖文", "navigation.dashboard": "管理中心", "navigation.developers": "开发者", "navigation.direct_messages": "私信", @@ -684,61 +703,43 @@ "navigation.invites": "邀请", "navigation.notifications": "通知", "navigation.search": "搜索", - "navigation_bar.account_migration": "Move account", + "navigation_bar.account_aliases": "用户别名", + "navigation_bar.account_migration": "迁移账户", "navigation_bar.blocks": "屏蔽", "navigation_bar.compose": "撰写新帖", "navigation_bar.compose_direct": "撰写私信", - "navigation_bar.compose_quote": "Quote post", - "navigation_bar.compose_reply": "Reply to post", + "navigation_bar.compose_edit": "编辑帖文", + "navigation_bar.compose_quote": "引用帖文", + "navigation_bar.compose_reply": "回复帖文", "navigation_bar.domain_blocks": "屏蔽站点", "navigation_bar.favourites": "点赞的内容", "navigation_bar.filters": "过滤", "navigation_bar.follow_requests": "关注请求", "navigation_bar.import_data": "导入数据", - "navigation_bar.in_reply_to": "In reply to", + "navigation_bar.in_reply_to": "在回复中", "navigation_bar.invites": "邀请", "navigation_bar.logout": "登出", "navigation_bar.mutes": "静音", "navigation_bar.preferences": "首选项", - "navigation_bar.profile_directory": "Profile directory", - "navigation_bar.security": "安全", + "navigation_bar.profile_directory": "发现更多用户", "navigation_bar.soapbox_config": "Soapbox设置", - "notification.birthday": "{name} has a birthday today", - "notification.birthday.more": "{count} more {count, plural, one {friend} other {friends}}", - "notification.birthday_plural": "{name} and {more} have birthday today", - "notification.pleroma:chat_mention": "{name} 回复了你", + "notification.birthday": "{name} 今天生日啦!", + "notification.birthday.more": "{count} 位朋友", + "notification.birthday_plural": "{name} 和 {more} 今天生日", "notification.favourite": "{name} 赞了你的帖文", "notification.follow": "{name} 开始关注你", "notification.follow_request": "{name} 请求关注你", "notification.mention": "{name} 提及了你", - "notification.move": "{name} moved to {targetName}", - "notification.pleroma:emoji_reaction": "{name} 对你的帖文回应了表情", - "notification.poll": "你参与的一个投票已经结束", - "notification.reblog": "{name} 转发了你的帖文", - "notification.status": "{name} just posted", - "notifications.clear": "清空通知列表", - "notifications.clear_confirmation": "你确定要永久清空通知列表吗?", - "notifications.clear_heading": "Clear notifications", - "notifications.column_settings.alert": "桌面通知", - "notifications.column_settings.birthdays.category": "Birthdays", - "notifications.column_settings.birthdays.show": "Show birthday reminders", - "notifications.column_settings.emoji_react": "用Emoji互动:", - "notifications.column_settings.favourite": "当你的帖文被点赞时:", - "notifications.column_settings.filter_bar.advanced": "显示所有类别", - "notifications.column_settings.filter_bar.category": "快速过滤栏", - "notifications.column_settings.filter_bar.show": "显示", - "notifications.column_settings.follow": "当有人关注你时:", - "notifications.column_settings.follow_request": "新的关注请求:", - "notifications.column_settings.mention": "当有人在帖文中提及你时:", - "notifications.column_settings.move": "迁移:", - "notifications.column_settings.poll": "投票结果:", - "notifications.column_settings.push": "推送通知", - "notifications.column_settings.reblog": "当有人转发了你的帖文时:", - "notifications.column_settings.show": "在通知栏显示", - "notifications.column_settings.sound": "播放音效", - "notifications.column_settings.sounds": "音效设置", - "notifications.column_settings.sounds.all_sounds": "收到所有类型的通知时播放音效", - "notifications.column_settings.title": "通知设置", + "notification.mentioned": "{name} 提及了你", + "notification.move": "{name} 移动到了 {targetName}", + "notification.others": " + {count} 其他通知", + "notification.pleroma:chat_mention": "{name} 给你发送了信息", + "notification.pleroma:emoji_reaction": "{name} 表情回应了你的帖文", + "notification.poll": "一项你参加的投票已经结束", + "notification.reblog": "{name} 转载了你的帖文", + "notification.status": "{name} 刚刚发帖", + "notification.update": "{name} 修改了你参与互动的帖文", + "notification.user_approved": "欢迎来到 {instance}!", "notifications.filter.all": "全部", "notifications.filter.boosts": "转发", "notifications.filter.emoji_reacts": "Emoji互动", @@ -747,89 +748,110 @@ "notifications.filter.mentions": "提及", "notifications.filter.moves": "迁移", "notifications.filter.polls": "投票结果", - "notifications.filter.statuses": "Updates from people you follow", + "notifications.filter.statuses": "来自关注的人的更新", "notifications.group": "{count} 条通知", - "notifications.queue_label": "点击查看 {count} 新 {count, plural, one {通知} other {通知}}", - "onboarding.avatar.subtitle": "Just have fun with it.", - "onboarding.avatar.title": "Choose a profile picture", - "onboarding.display_name.subtitle": "You can always edit this later.", - "onboarding.display_name.title": "Choose a display name", - "onboarding.done": "Done", - "onboarding.finished.message": "We are very excited to welcome you to our community! Tap the button below to get started.", - "onboarding.finished.title": "Onboarding complete", - "onboarding.header.subtitle": "This will be shown at the top of your profile.", - "onboarding.header.title": "Pick a cover image", - "onboarding.next": "Next", - "onboarding.note.subtitle": "You can always edit this later.", - "onboarding.note.title": "Write a short bio", - "onboarding.saving": "Saving…", - "onboarding.skip": "Skip for now", - "onboarding.suggestions.subtitle": "Here are a few of the most popular accounts you might like.", - "onboarding.suggestions.title": "Suggested accounts", - "onboarding.view_feed": "View Feed", + "notifications.queue_label": "点击查看 {count} 条新通知", + "oauth_consumer.tooltip": "使用 {provider} 登录", + "oauth_consumers.title": "更多方式登录", + "onboarding.avatar.subtitle": "祝你玩得开心!", + "onboarding.avatar.title": "选择一张个人资料图片", + "onboarding.display_name.subtitle": "你可以稍后编辑", + "onboarding.display_name.title": "选择你的昵称", + "onboarding.done": "完成", + "onboarding.finished.message": "我们非常高兴地欢迎你加入我们的社区! 点击下面的按钮,让我们开始吧!", + "onboarding.finished.title": "新手教程结束", + "onboarding.header.subtitle": "这将显示在你个人资料的顶部。", + "onboarding.header.title": "选择封面图片", + "onboarding.next": "下一步", + "onboarding.note.subtitle": "你可以稍后编辑", + "onboarding.note.title": "简短地介绍你自己", + "onboarding.saving": "保存中…", + "onboarding.skip": "现在跳过", + "onboarding.suggestions.subtitle": "以下是几个受欢迎的账户,你可能会喜欢", + "onboarding.suggestions.title": "推荐账户", + "onboarding.view_feed": "浏览列表", "password_reset.confirmation": "请查阅确认邮件。", "password_reset.fields.username_placeholder": "电子邮件或用户名", "password_reset.reset": "重置密码", - "patron.donate": "Donate", - "patron.title": "Funding Goal", - "pinned_accounts.title": "{name}’s choices", + "patron.donate": "捐赠", + "patron.title": "筹集目标", + "pinned_accounts.title": "{name} 的选择", "pinned_statuses.none": "没有置顶帖文", + "poll.choose_multiple": "选你所想", "poll.closed": "已关闭", + "poll.non_anonymous": "公开投票", + "poll.non_anonymous.label": "投票的选择能被其他实例看到", "poll.refresh": "刷新", - "poll.total_votes": "{count} 票", + "poll.total_people": "还有 {count} 人", + "poll.total_votes": "投票", "poll.vote": "投票", "poll.voted": "你投给了这个选项", - "poll.votes": "{votes, plural, one {# vote} other {# votes}}", + "poll.votes": "投票", "poll_button.add_poll": "发起投票", "poll_button.remove_poll": "移除投票", - "pre_header.close": "Close", "preferences.fields.auto_play_gif_label": "自动播放GIF动图", "preferences.fields.autoload_more_label": "滚动到时间线底部时自动加载更多帖文", "preferences.fields.autoload_timelines_label": "滚动到时间线顶部时自动加载新帖", "preferences.fields.boost_modal_label": "转发前确认", "preferences.fields.delete_modal_label": "删除帖文前确认", "preferences.fields.display_media.default": "隐藏被标记为敏感内容的媒体", - "preferences.fields.display_media.hide_all": "显示所有的媒体", - "preferences.fields.display_media.show_all": "隐藏所有媒体", + "preferences.fields.display_media.hide_all": "总是隐藏所有媒体", + "preferences.fields.display_media.show_all": "总是显示所有媒体", "preferences.fields.expand_spoilers_label": "始终展开标有内容警告的帖文", "preferences.fields.language_label": "语言", "preferences.fields.media_display_label": "媒体展示", - "preferences.hints.feed": "In your home feed", + "preferences.fields.theme": "主题", + "preferences.hints.feed": "在你的主页信息流中", "privacy.change": "设置帖文可见范围", "privacy.direct.long": "只有被提及的用户能看到。", - "privacy.direct.short": "私信", + "privacy.direct.short": "仅被提及者", "privacy.private.long": "只有关注你的用户能看到。", "privacy.private.short": "仅关注者", - "privacy.public.long": "所有人可见,并会出现在公共时间轴上。", - "privacy.public.short": "公开", + "privacy.public.long": "所有人可见,出现在公共时间轴上。", + "privacy.public.short": "公共时间轴", "privacy.unlisted.long": "所有人可见,但不会出现在公共时间轴上。", - "privacy.unlisted.short": "不公开", + "privacy.unlisted.short": "所有人", "profile_dropdown.add_account": "添加一个已有帐号", - "profile_dropdown.logout": "登出 @{acct}", - "profile_fields_panel.title": "Profile fields", + "profile_dropdown.logout": "注销 @{acct}", + "profile_dropdown.theme": "主题", + "profile_fields_panel.title": "个人资料字段", "public.column_settings.title": "跨站公共时间轴设置", - "reactions.all": "All", + "reactions.all": "全部", "regeneration_indicator.label": "加载中……", "regeneration_indicator.sublabel": "你的主页时间轴正在准备中!", "register_invite.lead": "填写以下表单以创建帐号", - "register_invite.title": "你已被邀请加入 {siteTitle}!", - "registration.agreement": "我同意本站用户条款 {tos}。", - "registration.captcha.hint": "点击图像以重新加载验证码。", - "registration.closed_message": "{instance} 不再接受新的用户注册。", + "register_invite.title": "你已被邀请加入 {siteTitle}!", + "registration.acceptance": "一旦注册,意味着你已经同意本站的 {terms} 和 {privacy}.", + "registration.agreement": "我同意本站用户条款 {tos}", + "registration.captcha.hint": "点击图像以重新加载验证码", + "registration.captcha.placeholder": "输入图片中的文字", + "registration.closed_message": "{instance} 暂不开放注册", "registration.closed_title": "暂停注册", "registration.confirmation_modal.close": "关闭", - "registration.fields.confirm_placeholder": "请再输入密码", + "registration.fields.confirm_placeholder": "再次输入密码", "registration.fields.email_placeholder": "邮箱地址", "registration.fields.password_placeholder": "密码", - "registration.fields.username_hint": "只可以使用字母、数字、下划线。", + "registration.fields.username_hint": "只可以使用英文字母、数字和下划线。", "registration.fields.username_placeholder": "用户名", + "registration.header": "创建新账户", "registration.newsletter": "订阅新闻邮件", "registration.password_mismatch": "密码不匹配", - "registration.reason": "请填写注册理由", - "registration.reason_hint": "这会帮助我们审核你的帐号", + "registration.privacy": "隐私政策", + "registration.reason": "你为什么想要注册本站?", + "registration.reason_hint": "认真填写将会加快你通过的速度", "registration.sign_up": "注册", "registration.tos": "用户条款", - "registration.username_unavailable": "Username is already taken.", + "registration.username_unavailable": "用户名已被他人占用", + "registration.validation.capital_letter": "1 个大写字母", + "registration.validation.lowercase_letter": "1 个小写字母", + "registration.validation.minimum_characters": "8 个字符", + "registrations.create_account": "创建新账户", + "registrations.error": "创建您的账户失败,请联系管理员", + "registrations.get_started": "让我们开始吧!", + "registrations.success": "欢迎来到 {siteTitle}!", + "registrations.tagline": "一个没有言论审查的社交平台", + "registrations.unprocessable_entity": "用户名已被他人占用", + "registrations.username.hint": "只能包含字母、数字和下划线", "relative_time.days": "{number}天", "relative_time.hours": "{number}时", "relative_time.just_now": "刚刚", @@ -837,55 +859,67 @@ "relative_time.seconds": "{number}秒", "remote_instance.edit_federation": "编辑联邦设置", "remote_instance.federation_panel.heading": "联邦站点限制", - "remote_instance.federation_panel.no_restrictions_message": "{siteTitle} 未对 {host} 设置限制。", - "remote_instance.federation_panel.restricted_message": "{siteTitle} 完全屏蔽了 {host} 。", - "remote_instance.federation_panel.some_restrictions_message": "{siteTitle} 部分限制了 {host} 。", - "remote_instance.pin_host": "收藏 {host}", - "remote_instance.unpin_host": "取消收藏 {host}", - "remote_interaction.account_placeholder": "Enter your username@domain you want to act from", - "remote_interaction.divider": "or", - "remote_interaction.favourite": "Proceed to like", - "remote_interaction.favourite_title": "Like a post remotely", - "remote_interaction.follow": "Proceed to follow", - "remote_interaction.follow_title": "Follow {user} remotely", - "remote_interaction.poll_vote": "Proceed to vote", - "remote_interaction.poll_vote_title": "Vote in a poll remotely", - "remote_interaction.reblog": "Proceed to repost", - "remote_interaction.reblog_title": "Reblog a post remotely", - "remote_interaction.reply": "Proceed to reply", - "remote_interaction.reply_title": "Reply to a post remotely", - "remote_interaction.user_not_found_error": "Couldn't find given user", - "remote_timeline.filter_message": "你在查看 {instance} 的时间轴。", + "remote_instance.federation_panel.no_restrictions_message": "{siteTitle} 未对 {host} 设置限制", + "remote_instance.federation_panel.restricted_message": "{siteTitle} 完全屏蔽了 {host}", + "remote_instance.federation_panel.some_restrictions_message": "{siteTitle} 对 {host} 实施了部分限制", + "remote_instance.pin_host": "置顶 {host}", + "remote_instance.unpin_host": "取消置顶 {host}", + "remote_interaction.account_placeholder": "输入你想采取行动的账户(格式:用户名@域名)", + "remote_interaction.divider": "或", + "remote_interaction.favourite": "点赞", + "remote_interaction.favourite_title": "远程点赞一个帖文", + "remote_interaction.follow": "关注", + "remote_interaction.follow_title": "远程关注 {user} ", + "remote_interaction.poll_vote": "投票", + "remote_interaction.poll_vote_title": "远程参与投票", + "remote_interaction.reblog": "转帖", + "remote_interaction.reblog_title": "远程转帖", + "remote_interaction.reply": "回复", + "remote_interaction.reply_title": "远程回复", + "remote_interaction.user_not_found_error": "找不到该用户", + "remote_timeline.filter_message": "你在查看 {instance} 的时间轴", "reply_indicator.cancel": "取消", - "reply_mentions.account.add": "Add to mentions", - "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.reply_empty": "Replying to post", + "reply_mentions.account.add": "添加到提及列表", + "reply_mentions.account.remove": "从提及列表中移除", + "reply_mentions.more": "添加 {count} 个", + "reply_mentions.reply": "回复 {accounts}{more}", + "reply_mentions.reply_empty": "回复帖文", "report.block": "屏蔽帐号 {target}", "report.block_hint": "你是否也要屏蔽这个帐号呢?", + "report.confirmation.content": "如果我们发现这个帐号确实违反了 {link} ,我们会采取进一步的惩罚措施", + "report.confirmation.title": "感谢你提交的举报", + "report.done": "完成", "report.forward": "发送举报至 {target}", "report.forward_hint": "这名用户来自另一个服务器。是否要向那个服务器发送一条匿名的举报?", "report.hint": "举报将会发送给你所在服务器的监察员。你可以在下面填写举报该用户的理由:", + "report.next": "下一步", + "report.otherActions.addAdditional": "您想为这个举报添加更多的状态吗?", + "report.otherActions.addMore": "添加更多", + "report.otherActions.furtherActions": "进一步的措施:", + "report.otherActions.hideAdditional": "隐藏额外状态", + "report.otherActions.otherStatuses": "包括其他状态?", "report.placeholder": "备注", + "report.reason.blankslate": "你已移除选中的状态", + "report.reason.title": "举报原因", "report.submit": "提交", "report.target": "举报 {target}", - "reset_password.header": "Set New Password", - "schedule.post_time": "发布日期与时间", + "reset_password.fail": "令牌已过期,请重试", + "reset_password.header": "设置新的密码", + "schedule.post_time": "发布时间", "schedule.remove": "取消发布", "schedule_button.add_schedule": "定时发布", - "schedule_button.remove_schedule": "立即发布", + "schedule_button.remove_schedule": "取消定时发布", "scheduled_status.cancel": "取消", "search.action": "搜索 “{query}”", "search.placeholder": "搜索", "search_results.accounts": "用户", "search_results.hashtags": "话题标签", "search_results.statuses": "帖文", - "search_results.top": "结果", "security.codes.fail": "恢复代码错误", "security.confirm.fail": "密码错误,请重试。", - "security.delete_account.fail": "删除帐号失败。.", - "security.delete_account.success": "帐号删除成功。", + "security.delete_account.fail": "删除帐号失败", + "security.delete_account.success": "帐号删除成功", "security.disable.fail": "密码错误,请重试。", - "security.disable_mfa": "关闭", "security.fields.email.label": "邮箱地址", "security.fields.new_password.label": "输入新密码", "security.fields.old_password.label": "输入原密码", @@ -893,57 +927,59 @@ "security.fields.password_confirmation.label": "再次输入新密码", "security.headers.delete": "删除帐号", "security.headers.tokens": "会话", - "security.headers.update_email": "更改邮箱", - "security.headers.update_password": "更改密码", - "security.mfa": "设置双重认证", - "security.mfa_enabled": "你已经设置好了通过OTP进行双重认证。", - "security.mfa_header": "验证方式", - "security.mfa_setup_hint": "通过OTP进行双重认证", "security.qr.fail": "加载密钥失败", "security.submit": "保存更改", "security.submit.delete": "删除帐号", "security.text.delete": "输入密码后,本站会立即删除你的帐号,并且通知其他站点,但你的信息不会在其他站点上立即删除。", - "security.tokens.revoke": "吊销", - "security.update_email.fail": "更新邮箱地址失败。", - "security.update_email.success": "邮箱地址已更新。", - "security.update_password.fail": "更改密码失败。", - "security.update_password.success": "密码已更改。", - "settings.change_email": "Change Email", - "settings.change_password": "Change Password", - "settings.configure_mfa": "Configure MFA", - "settings.delete_account": "Delete Account", - "settings.edit_profile": "Edit Profile", - "settings.preferences": "Preferences", - "settings.profile": "Profile", - "settings.save.success": "Your preferences have been saved!", - "settings.security": "Security", - "settings.settings": "Settings", - "signup_panel.subtitle": "注册以参与讨论。", + "security.tokens.revoke": "撤销", + "security.update_email.fail": "更新邮箱地址失败", + "security.update_email.success": "邮箱地址已更新", + "security.update_password.fail": "更改密码失败", + "security.update_password.success": "密码已更改", + "settings.change_email": "修改邮箱", + "settings.change_password": "修改密码", + "settings.configure_mfa": "配置多重认证 (MFA)", + "settings.delete_account": "删除账户", + "settings.edit_profile": "编辑个人资料", + "settings.preferences": "偏好设置", + "settings.profile": "个人资料", + "settings.save.success": "你的偏好设置已保存!", + "settings.security": "安全性", + "settings.sessions": "活动会话", + "settings.settings": "设置", + "shared.tos": "服务条款", + "signup_panel.subtitle": "注册以参与讨论", "signup_panel.title": "还没有注册{site_title} ?", - "snackbar.view": "View", - "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", - "soapbox_config.authenticated_profile_label": "Profiles require authentication", + "site_preview.preview": "预览", + "snackbar.view": "浏览", + "soapbox_config.authenticated_profile_hint": "用户必须登录后才能查看用户个人资料上的回复和媒体。", + "soapbox_config.authenticated_profile_label": "个人资料需要授权才能查看", "soapbox_config.copyright_footer.meta_fields.label_placeholder": "版权页底", "soapbox_config.crypto_address.meta_fields.address_placeholder": "地址", - "soapbox_config.crypto_address.meta_fields.note_placeholder": "Note (optional)", - "soapbox_config.crypto_address.meta_fields.ticker_placeholder": "Ticker", - "soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "Number of items to display in the crypto homepage widget", + "soapbox_config.crypto_address.meta_fields.note_placeholder": "备注 (可选)", + "soapbox_config.crypto_address.meta_fields.ticker_placeholder": "币种", + "soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "在主页加密货币小组件中显示的项目数量", "soapbox_config.custom_css.meta_fields.url_placeholder": "URL", - "soapbox_config.display_fqn_label": "显示本站帐号的域名(如@user@domain)。", - "soapbox_config.fields.accent_color_label": "Accent color", + "soapbox_config.display_fqn_label": "显示本站帐号的域名(如 @用户名@域名 )", + "soapbox_config.fields.accent_color_label": "强调色", "soapbox_config.fields.brand_color_label": "主题颜色", - "soapbox_config.fields.crypto_address.add": "添加加密币地址", - "soapbox_config.fields.crypto_addresses_label": "加密币地址", - "soapbox_config.fields.home_footer.add": "添加主页页底", - "soapbox_config.fields.home_footer_fields_label": "主页页底", + "soapbox_config.fields.crypto_address.add": "添加加密货币地址", + "soapbox_config.fields.crypto_addresses_label": "加密货币地址", + "soapbox_config.fields.home_footer.add": "添加主页页尾", + "soapbox_config.fields.home_footer_fields_label": "主页页尾", "soapbox_config.fields.logo_label": "Logo", "soapbox_config.fields.promo_panel.add": "添加时间轴页底", "soapbox_config.fields.promo_panel_fields_label": "时间轴页底", "soapbox_config.fields.theme_label": "默认主题", "soapbox_config.greentext_label": "打开greentext支持", - "soapbox_config.hints.crypto_addresses": "Add cryptocurrency addresses so users of your site can donate to you. Order matters, and you must use lowercase ticker values.", + "soapbox_config.headings.advanced": "高级", + "soapbox_config.headings.cryptocurrency": "加密货币", + "soapbox_config.headings.navigation": "导航", + "soapbox_config.headings.options": "选项", + "soapbox_config.headings.theme": "主题", + "soapbox_config.hints.crypto_addresses": "添加加密货币地址以便用户可以向你捐赠。请注意顺序,同时你必须使用小写币种代码。", "soapbox_config.hints.home_footer_fields": "你可以将自定义的链接插入在未登录时显示的主页页底(如about)。", - "soapbox_config.hints.logo": "SVG。最多2MB。将被显示为50px高度,保持长宽比。", + "soapbox_config.hints.logo": "SVG。最多2MB。将被显示为50px高度,保持长宽比", "soapbox_config.hints.promo_panel_fields": "你可以将自定义的链接插入在时间轴右方或页底(如about)。", "soapbox_config.hints.promo_panel_icons": "{ link }", "soapbox_config.hints.promo_panel_icons.link": "Soapbox图标列表", @@ -956,17 +992,20 @@ "soapbox_config.raw_json_label": "高级:编辑JSON数据", "soapbox_config.save": "保存更改", "soapbox_config.saved": "Soapbox配置已保存", - "soapbox_config.single_user_mode_hint": "Front page will redirect to a given user profile.", - "soapbox_config.single_user_mode_label": "Single user mode", - "soapbox_config.single_user_mode_profile_hint": "@handle", - "soapbox_config.single_user_mode_profile_label": "Main user handle", + "soapbox_config.single_user_mode_hint": "头部页面将重定向到给定的用户资料。", + "soapbox_config.single_user_mode_label": "单用户模式", + "soapbox_config.single_user_mode_profile_hint": "@用户名", + "soapbox_config.single_user_mode_profile_label": "主账户的用户名", "soapbox_config.verified_can_edit_name_label": "允许经过验证的用户编辑他们自己的昵称。", - "status.actions.more": "More", + "sponsored.info.message": "{siteTitle} 展示广告以使服务可持续运行。", + "sponsored.info.title": "为什么我会看到这个广告?", + "sponsored.subtitle": "赞助", + "status.actions.more": "更多", "status.admin_account": "打开 @{name} 的管理界面", "status.admin_status": "打开这条帖文的管理界面", "status.block": "屏蔽 @{name}", "status.bookmark": "书签", - "status.bookmarked": "书签已添加。", + "status.bookmarked": "书签已添加", "status.cancel_reblog_private": "取消转发", "status.cannot_reblog": "这条帖文不允许被转发。", "status.chat": "与 @{name} 聊天", @@ -974,6 +1013,8 @@ "status.delete": "删除", "status.detailed_status": "对话详情", "status.direct": "发送私信给 @{name}", + "status.edit": "编辑", + "status.edited": "已在 {date} 编辑", "status.embed": "嵌入", "status.favourite": "点赞", "status.filtered": "已过滤", @@ -986,14 +1027,14 @@ "status.open": "展开帖文", "status.pin": "在个人资料页面置顶", "status.pinned": "置顶帖文", - "status.quote": "Quote post", - "status.reactions.cry": "Sad", + "status.quote": "引用帖文", + "status.reactions.cry": "哭", "status.reactions.empty": "尚未有人回应表情", - "status.reactions.heart": "Love", - "status.reactions.laughing": "Haha", - "status.reactions.like": "Like", - "status.reactions.open_mouth": "Wow", - "status.reactions.weary": "Weary", + "status.reactions.heart": "爱心", + "status.reactions.laughing": "哈哈", + "status.reactions.like": "喜欢", + "status.reactions.open_mouth": "哇哦", + "status.reactions.weary": "疲倦", "status.reactions_expand": "选择表情", "status.read_more": "阅读全文", "status.reblog": "转发", @@ -1008,33 +1049,44 @@ "status.report": "举报 @{name}", "status.sensitive_warning": "敏感内容", "status.share": "分享", - "status.show_less": "隐藏内容", - "status.show_less_all": "隐藏所有内容", - "status.show_more": "显示内容", - "status.show_more_all": "显示所有内容", + "status.show_less": "减少显示", + "status.show_less_all": "减少这类帖文的展示", + "status.show_more": "增加显示", + "status.show_more_all": "增加这类帖文的展示", "status.title": "帖文", "status.title_direct": "私信", "status.unbookmark": "移除书签", - "status.unbookmarked": "书签已移除。", + "status.unbookmarked": "书签已移除", "status.unmute_conversation": "不再隐藏此对话", "status.unpin": "在个人资料页面取消置顶", - "status_list.queue_label": "点击查看 {count} 则新{count, plural, one {帖文} other {帖文}}", - "statuses.quote_tombstone": "Post is unavailable.", + "status.sensitive_warning.subtitle": "此条内容可能并不适宜所有人查看", + "status.sensitive_warning.action": "显示", + "status_list.queue_label": "点击查看 {count} 则新帖文", + "statuses.quote_tombstone": "帖文不可用", "statuses.tombstone": "部分帖文不可见", + "streamfield.add": "添加", + "streamfield.remove": "移除", "suggestions.dismiss": "关闭建议", "tabs_bar.all": "全部", "tabs_bar.chats": "聊天", "tabs_bar.dashboard": "管理中心", "tabs_bar.fediverse": "联邦宇宙", "tabs_bar.home": "主页", - "tabs_bar.more": "More", + "tabs_bar.more": "更多", "tabs_bar.notifications": "通知", - "tabs_bar.post": "发帖", - "tabs_bar.profile": "Profile", + "tabs_bar.profile": "个人资料", "tabs_bar.search": "搜索", - "tabs_bar.settings": "Settings", + "tabs_bar.settings": "设置", + "tabs_bar.switch_accounts": "切换帐号", "tabs_bar.theme_toggle_dark": "切换到暗色主题", "tabs_bar.theme_toggle_light": "切换到明亮主题", + "theme_toggle.dark": "黑暗", + "theme_toggle.light": "明亮", + "theme_toggle.system": "系统", + "thread_login.login": "登录", + "thread_login.message": "加入 {siteTitle} 以获取完整资讯", + "thread_login.signup": "注册", + "thread_login.title": "继续这个对话", "time_remaining.days": "离预定时间还有 {number, plural, one {# 天} other {# 天}}", "time_remaining.hours": "离预定时间还有 {number, plural, one {# 小时} other {# 小时}}", "time_remaining.minutes": "离预定时间还有 {number, plural, one {# 分钟} other {# 分钟}}", @@ -1042,15 +1094,17 @@ "time_remaining.seconds": "离预定时间还有 {number, plural, one {# 秒} other {# 秒}}", "trends.count_by_accounts": "{count} 人正在讨论", "trends.title": "热门", + "trendsPanel.viewAll": "查看全部", "ui.beforeunload": "如果你现在离开网站,你的草稿内容将会丢失。", - "unauthorized_modal.text": "你需要帐号才能继续", - "unauthorized_modal.title": "注册帐号 {site_title}", + "unauthorized_modal.text": "你需要登录才能继续", + "unauthorized_modal.title": "注册 {site_title} 帐号", "upload_area.title": "将文件拖放到此处开始上传", "upload_button.label": "上传媒体文件 (JPEG, PNG, GIF, WebM, MP4, MOV)", - "upload_error.image_size_limit": "Image exceeds the current file size limit ({limit})", + "upload_error.image_size_limit": "图片超过了当前的文件大小限制 ({limit})", "upload_error.limit": "文件大小超过限制。", "upload_error.poll": "投票中不允许上传文件。", - "upload_error.video_size_limit": "Video exceeds the current file size limit ({limit})", + "upload_error.video_duration_limit": "视频超出当前时长限制 ({limit} 秒)", + "upload_error.video_size_limit": "视频超过了当前的文件大小限制 ({limit})", "upload_form.description": "为视觉障碍人士添加文字说明", "upload_form.preview": "预览", "upload_form.undo": "删除", diff --git a/app/soapbox/locales/zh-HK.json b/app/soapbox/locales/zh-HK.json index d81607b61..adc0480d8 100644 --- a/app/soapbox/locales/zh-HK.json +++ b/app/soapbox/locales/zh-HK.json @@ -1,1071 +1,1120 @@ { - "about.also_available": "Available in:", - "accordion.collapse": "Collapse", - "accordion.expand": "Expand", - "account.add_or_remove_from_list": "Add or Remove from lists", - "account.badges.bot": "機械人", - "account.birthday": "Born {date}", - "account.birthday_today": "Birthday is today!", + "about.also_available": "其他版本:", + "accordion.collapse": "收起", + "accordion.expand": "展开", + "account.add_or_remove_from_list": "從名單中新增或移除", + "account.badges.bot": "機器人", + "account.birthday": "出生於 {date}", + "account.birthday_today": "今天是你的生日!", "account.block": "封鎖 @{name}", - "account.block_domain": "隱藏來自 {domain} 的一切文章", - "account.blocked": "封鎖", - "account.chat": "Chat with @{name}", - "account.column_settings.description": "These settings apply to all account timelines.", - "account.column_settings.title": "Acccount timeline settings", - "account.deactivated": "Deactivated", - "account.direct": "私訊 @{name}", - "account.edit_profile": "修改個人資料", - "account.endorse": "Feature on profile", - "account.follow": "關注", - "account.followers": "關注的人", - "account.followers.empty": "No one follows this user yet.", - "account.follows": "正關注", - "account.follows.empty": "This user doesn't follow anyone yet.", - "account.follows_you": "關注你", - "account.hide_reblogs": "隱藏 @{name} 的轉推", - "account.last_status": "Last active", - "account.link_verified_on": "Ownership of this link was checked on {date}", - "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", - "account.login": "Log in", + "account.block_domain": "隱藏來自 {domain} 的所有內容", + "account.blocked": "已封鎖", + "account.chat": "和 @{name} 聊天", + "account.deactivated": "帳户已被停權", + "account.direct": "傳私訊給 @{name}", + "account.domain_blocked": "隱藏網域", + "account.edit_profile": "編輯個人資料", + "account.endorse": "在個人資料推薦對方", + "account.endorse.success": "你正在你的個人資料中展示 @{acct} ", + "account.familiar_followers": "被 {accounts} 追蹤", + "account.familiar_followers.more": "你追蹤了 {count} 位其他用户", + "account.follow": "追蹤", + "account.followers": "追蹤者", + "account.followers.empty": "尚沒有人追蹤這位使用者。", + "account.follows": "正在追蹤", + "account.follows.empty": "這位使用者尚未追蹤任何使用者。", + "account.follows_you": "追蹤了你", + "account.hide_reblogs": "隱藏來自 @{name} 的轉推", + "account.last_status": "最後活動", + "account.link_verified_on": "已在 {date} 檢查此連結的擁有者權限", + "account.locked_info": "這隻帳户的隱私狀態被設成鎖定。該擁有者會手動審核能追蹤這隻帳號的人。", + "account.login": "登入", "account.media": "媒體", - "account.member_since": "Joined {date}", + "account.member_since": "加入於 {date}", "account.mention": "提及", - "account.moved_to": "{name} 已經遷移到:", - "account.mute": "將 @{name} 靜音", - "account.never_active": "Never", - "account.posts": "文章", - "account.posts_with_replies": "包含回覆的文章", - "account.profile": "Profile", - "account.register": "Sign up", - "account.remote_follow": "Remote follow", - "account.report": "舉報 @{name}", - "account.requested": "等候審批", - "account.requested_small": "Awaiting approval", + "account.moved_to": "{name} 已遷移至:", + "account.mute": "隱藏 @{name}", + "account.muted": "已隱藏", + "account.never_active": "從未", + "account.posts": "帖文", + "account.posts_with_replies": "帖文與回覆", + "account.profile": "個人資料", + "account.register": "註冊", + "account.remote_follow": "遠端追蹤", + "account.remove_from_followers": "移除此追蹤者", + "account.report": "檢舉 @{name}", + "account.requested": "正在等待核准。按一下取消追蹤請求", + "account.requested_small": "等待核准", + "account.search": "搜尋關於 @{name} 的內容", + "account.search_self": "搜尋你的帖文", "account.share": "分享 @{name} 的個人資料", - "account.show_reblogs": "顯示 @{name} 的推文", - "account.subscribe": "Subscribe to notifications from @{name}", - "account.subscribed": "Subscribed", - "account.unblock": "解除對 @{name} 的封鎖", - "account.unblock_domain": "不再隱藏 {domain}", - "account.unendorse": "Don't feature on profile", - "account.unfollow": "取消關注", - "account.unmute": "取消 @{name} 的靜音", - "account.unsubscribe": "Unsubscribe to notifications from @{name}", - "account.verified": "Verified Account", - "account.welcome": "Welcome", - "account_gallery.none": "No media to show.", - "account_note.hint": "You can keep notes about this user for yourself (this will not be shared with them):", - "account_note.placeholder": "No comment provided", - "account_note.save": "Save", - "account_note.target": "Note for @{target}", - "account_search.placeholder": "Search for an account", - "account_timeline.column_settings.show_pinned": "Show pinned posts", - "admin.awaiting_approval.approved_message": "{acct} was approved!", - "admin.awaiting_approval.empty_message": "There is nobody waiting for approval. When a new user signs up, you can review them here.", - "admin.awaiting_approval.rejected_message": "{acct} was rejected.", - "admin.dashboard.registration_mode.approval_hint": "Users can sign up, but their account only gets activated when an admin approves it.", - "admin.dashboard.registration_mode.approval_label": "Approval Required", - "admin.dashboard.registration_mode.closed_hint": "Nobody can sign up. You can still invite people.", - "admin.dashboard.registration_mode.closed_label": "Closed", - "admin.dashboard.registration_mode.open_hint": "Anyone can join.", - "admin.dashboard.registration_mode.open_label": "Open", - "admin.dashboard.registration_mode_label": "Registrations", - "admin.dashboard.settings_saved": "Settings saved!", - "admin.dashcounters.domain_count_label": "peers", - "admin.dashcounters.mau_label": "monthly active users", - "admin.dashcounters.retention_label": "user retention", - "admin.dashcounters.status_count_label": "posts", - "admin.dashcounters.user_count_label": "total users", - "admin.dashwidgets.email_list_header": "Email list", - "admin.dashwidgets.software_header": "Software", - "admin.latest_accounts_panel.more": "Click to see {count} {count, plural, one {account} other {accounts}}", - "admin.latest_accounts_panel.title": "Latest Accounts", - "admin.moderation_log.empty_message": "You have not performed any moderation actions yet. When you do, a history will be shown here.", - "admin.reports.actions.close": "Close", - "admin.reports.actions.view_status": "View post", - "admin.reports.empty_message": "There are no open reports. If a user gets reported, they will show up here.", - "admin.reports.report_closed_message": "Report on @{name} was closed", - "admin.reports.report_title": "Report on {acct}", - "admin.statuses.actions.delete_status": "Delete post", - "admin.statuses.actions.mark_status_not_sensitive": "Mark post not sensitive", - "admin.statuses.actions.mark_status_sensitive": "Mark post sensitive", - "admin.statuses.status_deleted_message": "Post by @{acct} was deleted", - "admin.statuses.status_marked_message_not_sensitive": "Post by @{acct} was marked not sensitive", - "admin.statuses.status_marked_message_sensitive": "Post by @{acct} was marked sensitive", - "admin.user_index.empty": "No users found.", - "admin.user_index.search_input_placeholder": "Who are you looking for?", - "admin.users.actions.deactivate_user": "Deactivate @{name}", - "admin.users.actions.delete_user": "Delete @{name}", - "admin.users.actions.demote_to_moderator": "Demote @{name} to a moderator", - "admin.users.actions.demote_to_moderator_message": "@{acct} was demoted to a moderator", - "admin.users.actions.demote_to_user": "Demote @{name} to a regular user", - "admin.users.actions.demote_to_user_message": "@{acct} was demoted to a regular user", - "admin.users.actions.promote_to_admin": "Promote @{name} to an admin", - "admin.users.actions.promote_to_admin_message": "@{acct} was promoted to an admin", - "admin.users.actions.promote_to_moderator": "Promote @{name} to a moderator", - "admin.users.actions.promote_to_moderator_message": "@{acct} was promoted to a moderator", - "admin.users.actions.remove_donor": "Remove @{name} as a donor", - "admin.users.actions.set_donor": "Set @{name} as a donor", - "admin.users.actions.suggest_user": "Suggest @{name}", - "admin.users.actions.unsuggest_user": "Unsuggest @{name}", - "admin.users.actions.unverify_user": "Unverify @{name}", - "admin.users.actions.verify_user": "Verify @{name}", - "admin.users.remove_donor_message": "@{acct} was removed as a donor", - "admin.users.set_donor_message": "@{acct} was set as a donor", - "admin.users.user_deactivated_message": "@{acct} was deactivated", - "admin.users.user_deleted_message": "@{acct} was deleted", - "admin.users.user_suggested_message": "@{acct} was suggested", - "admin.users.user_unsuggested_message": "@{acct} was unsuggested", - "admin.users.user_unverified_message": "@{acct} was unverified", - "admin.users.user_verified_message": "@{acct} was verified", - "admin_nav.awaiting_approval": "Awaiting Approval", - "admin_nav.dashboard": "Dashboard", - "admin_nav.reports": "Reports", - "alert.unexpected.body": "We're sorry for the interruption. If the problem persists, please reach out to our support team. You may also try to {clearCookies} (this will log you out).", - "alert.unexpected.browser": "Browser", - "alert.unexpected.clear_cookies": "clear cookies and browser data", - "alert.unexpected.links.help": "Help Center", - "alert.unexpected.links.status": "Status", - "alert.unexpected.links.support": "Support", - "alert.unexpected.message": "發生不可預期的錯誤。", - "alert.unexpected.return_home": "Return Home", - "alert.unexpected.title": "噢!", - "aliases.account.add": "Create alias", - "aliases.account_label": "Old account:", - "aliases.aliases_list_delete": "Unlink alias", - "aliases.search": "Search your old account", - "aliases.success.add": "Account alias created successfully", - "aliases.success.remove": "Account alias removed successfully", - "app_create.name_label": "App name", - "app_create.name_placeholder": "e.g. 'Soapbox'", - "app_create.redirect_uri_label": "Redirect URIs", - "app_create.restart": "Create another", - "app_create.results.app_label": "App", - "app_create.results.explanation_text": "You created a new app and token! Please copy the credentials somewhere; you will not see them again after navigating away from this page.", - "app_create.results.explanation_title": "App created successfully", - "app_create.results.token_label": "OAuth token", - "app_create.scopes_label": "Scopes", - "app_create.scopes_placeholder": "e.g. 'read write follow'", - "app_create.submit": "Create app", - "app_create.website_label": "Website", - "auth.invalid_credentials": "Wrong username or password", - "auth.logged_out": "Logged out.", - "backups.actions.create": "Create backup", - "backups.empty_message": "No backups found. {action}", - "backups.empty_message.action": "Create one now?", - "backups.pending": "Pending", - "beta.also_available": "Available in:", - "birthday_panel.title": "Birthdays", - "boost_modal.combo": "如你想在下次路過這顯示,請按{combo},", - "bundle_column_error.body": "加載本組件出錯。", + "account.show_reblogs": "顯示來自 @{name} 的帖文", + "account.subscribe": "訂閲 @{name}", + "account.subscribe.failure": "訂閲此帳户時發生錯誤", + "account.subscribe.success": "您已訂閲此帳户", + "account.unblock": "取消封鎖 @{name}", + "account.unblock_domain": "取消隱藏 {domain}", + "account.unendorse": "不再於個人資料頁面推薦對方", + "account.unendorse.success": "您已不再展示 @{acct}", + "account.unfollow": "取消追蹤", + "account.unmute": "取消隱藏 @{name}", + "account.unsubscribe": "取消訂閲 @{name}", + "account.unsubscribe.failure": "取消訂閲此帳户時發生錯誤", + "account.unsubscribe.success": "你已取消訂閲此帳户", + "account.verified": "已認證的帳户", + "account_gallery.none": "沒有可顯示的媒體", + "account_note.hint": "你可以為自己保留關於這個用户的筆記(這不會和他們分享):", + "account_note.placeholder": "沒有評論", + "account_note.save": "儲存", + "account_note.target": "@{target} 的筆記", + "account_search.placeholder": "搜尋使用者", + "actualStatus.edited": "在 {date} 時編輯", + "actualStatuses.quote_tombstone": "帖文不可用", + "admin.awaiting_approval.approved_message": "{acct} 已經通過審核!", + "admin.awaiting_approval.empty_message": "沒有新使用者等待審核,如果有新的申請,它就會顯示在這裏。", + "admin.awaiting_approval.rejected_message": "{acct} 的註冊請求被拒絕!", + "admin.dashboard.registration_mode.approval_hint": "在管理員同意註冊申請後才可加入。", + "admin.dashboard.registration_mode.approval_label": "需要審核", + "admin.dashboard.registration_mode.closed_hint": "公開註冊已經關閉,但已註冊用户仍然可以邀請其他人加入。", + "admin.dashboard.registration_mode.closed_label": "私密", + "admin.dashboard.registration_mode.open_hint": "任何人都可以加入。", + "admin.dashboard.registration_mode.open_label": "公開", + "admin.dashboard.registration_mode_label": "註冊模式", + "admin.dashboard.settings_saved": "設定已儲存!", + "admin.dashcounters.domain_count_label": "互聯站點", + "admin.dashcounters.mau_label": "月度活躍使用者", + "admin.dashcounters.retention_label": "使用者留存", + "admin.dashcounters.status_count_label": "總帖數", + "admin.dashcounters.user_count_label": "使用者總數", + "admin.dashwidgets.email_list_header": "電郵列表", + "admin.dashwidgets.software_header": "軟體版本", + "admin.latest_accounts_panel.expand_message": "點擊展開更多帳户", + "admin.latest_accounts_panel.more": "點擊展開 {count} 個帳户", + "admin.latest_accounts_panel.title": "最近加入的帳户", + "admin.moderation_log.empty_message": "你還沒有進行任何管理,如果有任何操作,操作歷史就會顯示在這裏。", + "admin.reports.actions.close": "關閉檢舉", + "admin.reports.actions.view_status": "查看帖文", + "admin.reports.empty_message": "沒有未處理的檢舉,如果有任何檢舉,它就會顯示在這裏。", + "admin.reports.report_closed_message": "對 @{name} 的檢舉已關閉", + "admin.reports.report_title": "檢舉 {acct} 的帖文", + "admin.statuses.actions.delete_status": "刪除帖文", + "admin.statuses.actions.mark_status_not_sensitive": "不再標記為敏感內容", + "admin.statuses.actions.mark_status_sensitive": "標記為敏感內容", + "admin.statuses.status_deleted_message": "@{acct} 的帖文已被刪除", + "admin.statuses.status_marked_message_not_sensitive": "@{acct} 的帖文不會被標記為敏感內容", + "admin.statuses.status_marked_message_sensitive": "@{acct} 的帖文被標記為敏感內容", + "admin.user_index.empty": "找不到用户。", + "admin.user_index.search_input_placeholder": "你正在找誰?", + "admin.users.actions.deactivate_user": "禁用帳户 @{name}", + "admin.users.actions.delete_user": "刪除帳户 @{name}", + "admin.users.actions.demote_to_moderator": "降級 @{name} 為站務", + "admin.users.actions.demote_to_moderator_message": "@{acct} 被降級為站務", + "admin.users.actions.demote_to_user": "降級 @{name} 為普通用户", + "admin.users.actions.demote_to_user_message": "@{acct} 被降級為普通用户", + "admin.users.actions.promote_to_admin": "升級 @{name} 為管理員", + "admin.users.actions.promote_to_admin_message": "@{acct} 被升級為管理員", + "admin.users.actions.promote_to_moderator": "升級 @{name} 為站務", + "admin.users.actions.promote_to_moderator_message": "@{acct} 被升級為站務", + "admin.users.actions.remove_donor": "移除 @{name} 的捐贈者頭銜", + "admin.users.actions.set_donor": "將 @{name} 設為捐贈者", + "admin.users.actions.suggest_user": "推薦 @{name}", + "admin.users.actions.unsuggest_user": "取消推薦 @{name}", + "admin.users.actions.unverify_user": "撤銷認證 @{name}", + "admin.users.actions.verify_user": "認證帳户 @{name}", + "admin.users.remove_donor_message": "@{acct} 被移除出捐贈者名單", + "admin.users.set_donor_message": "@{acct} 被設為捐贈者", + "admin.users.user_deactivated_message": "@{acct} 被停權", + "admin.users.user_deleted_message": "@{acct} 被刪除了", + "admin.users.user_suggested_message": "@{acct} 被推薦", + "admin.users.user_unsuggested_message": "@{acct} 被取消推薦", + "admin.users.user_unverified_message": "@{acct} 未認證", + "admin.users.user_verified_message": "@{acct} 被認證", + "admin_nav.awaiting_approval": "等待核准", + "admin_nav.dashboard": "控制台", + "admin_nav.reports": "檢舉", + "age_verification.fail": "你至少必須滿 {ageMinimum} 歲", + "age_verification.header": "提供你的生日", + "alert.unexpected.body": "我們對這次的中斷感到抱歉。如果問題持續存在,請聯絡我們的支援團隊。你也可以嘗試 {clearCookies} (注意:你的帳户會自動退出)。", + "alert.unexpected.browser": "瀏覽器", + "alert.unexpected.clear_cookies": "清除cookie和瀏覽器資料", + "alert.unexpected.links.help": "支援中心", + "alert.unexpected.links.status": "狀態", + "alert.unexpected.links.support": "支援", + "alert.unexpected.message": "發生了非預期的錯誤。", + "alert.unexpected.return_home": "回到主頁", + "alert.unexpected.title": "哎喲!", + "aliases.account.add": "創建別名", + "aliases.account_label": "原帳户:", + "aliases.aliases_list_delete": "刪除別名", + "aliases.search": "搜尋原帳户", + "aliases.success.add": "帳户別名創建成功", + "aliases.success.remove": "帳户別名刪除成功", + "app_create.name_label": "應用名稱", + "app_create.name_placeholder": "例如 'Soapbox'", + "app_create.redirect_uri_label": "重定向網域", + "app_create.restart": "創建另一個", + "app_create.results.app_label": "應用", + "app_create.results.explanation_text": "您已成功創建一個新應用及其令牌,請複製密鑰等資訊,離開本頁面後這些資訊將不會再次顯示。", + "app_create.results.explanation_title": "應用創建成功", + "app_create.results.token_label": "OAuth令牌", + "app_create.scopes_label": "權限範圍", + "app_create.scopes_placeholder": "例如 '讀取 寫入 追蹤'", + "app_create.submit": "創建應用", + "app_create.website_label": "網站", + "auth.invalid_credentials": "無效的帳户名稱或密碼", + "auth.logged_out": "您已登出", + "backups.actions.create": "創建備份", + "backups.empty_message": "找不到備份 {action}", + "backups.empty_message.action": "現在創建嗎?", + "backups.pending": "等待備份", + "beta.also_available": "在此時可用:", + "birthday_panel.title": "生日", + "boost_modal.combo": "下次您可以按 {combo} 跳過", + "bundle_column_error.body": "載入此元件時發生錯誤。", "bundle_column_error.retry": "重試", - "bundle_column_error.title": "網絡錯誤", + "bundle_column_error.title": "網路錯誤", "bundle_modal_error.close": "關閉", - "bundle_modal_error.message": "加載本組件出錯。", + "bundle_modal_error.message": "載入此元件時發生錯誤。", "bundle_modal_error.retry": "重試", - "card.back.label": "Back", - "chat.actions.send": "Send", - "chat.input.placeholder": "Type a message", - "chat_panels.main_window.empty": "No chats found. To start a chat, visit a user's profile.", - "chat_panels.main_window.title": "Chats", - "chats.actions.delete": "Delete for both", - "chats.actions.more": "More", - "chats.actions.report": "Report", - "chats.attachment": "Attachment", - "chats.attachment_image": "Image", - "chats.audio_toggle_off": "Audio notification off", - "chats.audio_toggle_on": "Audio notification on", - "chats.dividers.today": "Today", - "chats.search_placeholder": "Search inbox", - "column.admin.awaiting_approval": "Awaiting Approval", - "column.admin.dashboard": "Dashboard", - "column.admin.moderation_log": "Moderation Log", - "column.admin.reports": "Reports", - "column.admin.reports.menu.moderation_log": "Moderation Log", - "column.admin.users": "Users", - "column.aliases": "Account aliases", - "column.aliases.create_error": "Error creating alias", - "column.aliases.delete": "Delete", - "column.aliases.delete_error": "Error deleting alias", - "column.aliases.subheading_add_new": "Add New Alias", - "column.aliases.subheading_aliases": "Current aliases", - "column.app_create": "Create app", - "column.backups": "Backups", - "column.birthdays": "Birthdays", - "column.blocks": "封鎖用戶", - "column.bookmarks": "Bookmarks", - "column.chats": "Chats", + "card.back.label": "返回", + "chat.actions.send": "發送", + "chat.input.placeholder": "發送聊天訊息…", + "chat_panels.main_window.empty": "還沒有訊息。要開始聊天,可以從用户的個人資料頁面發起。", + "chat_panels.main_window.title": "聊天", + "chats.actions.delete": "刪除訊息", + "chats.actions.more": "更多選項", + "chats.actions.report": "檢舉用户", + "chats.attachment": "附件", + "chats.attachment_image": "圖片", + "chats.audio_toggle_off": "關閉消息提醒", + "chats.audio_toggle_on": "開啟消息提醒", + "chats.dividers.today": "今天", + "chats.search_placeholder": "開始聊天…", + "column.admin.awaiting_approval": "等待核准", + "column.admin.dashboard": "控制台", + "column.admin.moderation_log": "管理日誌", + "column.admin.reports": "檢舉", + "column.admin.reports.menu.moderation_log": "管理日誌", + "column.admin.users": "用户", + "column.aliases": "帳户別名", + "column.aliases.create_error": "創建帳户別名時出錯", + "column.aliases.delete": "刪除", + "column.aliases.delete_error": "刪除帳户別名時出錯", + "column.aliases.subheading_add_new": "添加新別名", + "column.aliases.subheading_aliases": "當前帳户別名", + "column.app_create": "創建應用", + "column.backups": "備份", + "column.birthdays": "生日", + "column.blocks": "封鎖的使用者", + "column.bookmarks": "書籤", + "column.chats": "聊天", "column.community": "本站時間軸", - "column.crypto_donate": "Donate Cryptocurrency", - "column.developers": "Developers", - "column.direct": "個人訊息", - "column.directory": "Browse profiles", - "column.domain_blocks": "隱藏的服務站", - "column.edit_profile": "Edit profile", - "column.export_data": "Export data", - "column.favourited_statuses": "Liked posts", - "column.favourites": "Likes", - "column.federation_restrictions": "Federation Restrictions", - "column.filters": "Muted words", - "column.filters.add_new": "Add New Filter", - "column.filters.conversations": "Conversations", - "column.filters.create_error": "Error adding filter", - "column.filters.delete": "Delete", - "column.filters.delete_error": "Error deleting filter", - "column.filters.drop_header": "Drop instead of hide", - "column.filters.drop_hint": "Filtered posts will disappear irreversibly, even if filter is later removed", - "column.filters.expires": "Expire after", - "column.filters.expires_hint": "Expiration dates are not currently supported", - "column.filters.home_timeline": "Home timeline", - "column.filters.keyword": "Keyword or phrase", - "column.filters.notifications": "Notifications", - "column.filters.public_timeline": "Public timeline", - "column.filters.subheading_add_new": "Add New Filter", - "column.filters.subheading_filters": "Current Filters", - "column.filters.whole_word_header": "Whole word", - "column.filters.whole_word_hint": "When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word", - "column.follow_requests": "關注請求", - "column.followers": "Followers", - "column.following": "Following", - "column.groups": "Groups", + "column.crypto_donate": "數字貨幣捐贈", + "column.developers": "開發者", + "column.direct": "私訊", + "column.directory": "發現更多", + "column.domain_blocks": "隱藏的網域", + "column.edit_profile": "編輯個人資料", + "column.export_data": "匯出資料", + "column.favourited_statuses": "按讚的貼文", + "column.favourites": "按讚", + "column.federation_restrictions": "聯邦限制", + "column.filters": "過濾詞", + "column.filters.add_new": "新增過濾詞", + "column.filters.conversations": "聊天", + "column.filters.create_error": "新增過濾詞時出錯。", + "column.filters.delete": "刪除過濾詞", + "column.filters.delete_error": "刪除過濾詞時出錯。", + "column.filters.drop_header": "丟棄而非隱藏", + "column.filters.drop_hint": "被丟棄的帖文會不可逆地消失,即使移除過濾詞之後也不會恢復", + "column.filters.expires": "過期時間", + "column.filters.expires_hint": "過期時間暫未被支援", + "column.filters.home_timeline": "主頁時間軸", + "column.filters.keyword": "關鍵詞", + "column.filters.notifications": "通知", + "column.filters.public_timeline": "公共時間軸", + "column.filters.subheading_add_new": "新增過濾詞", + "column.filters.subheading_filters": "當前過濾詞", + "column.filters.whole_word_header": "全詞匹配", + "column.filters.whole_word_hint": "如果關鍵詞只含字母和數字,則只有全詞匹配才會被過濾", + "column.follow_requests": "追蹤請求", + "column.followers": "追蹤者", + "column.following": "正在追蹤", + "column.groups": "群組", "column.home": "主頁", - "column.import_data": "Import data", - "column.info": "Server information", - "column.lists": "列表", - "column.mentions": "Mentions", - "column.mfa": "Multi-Factor Authentication", - "column.mfa_cancel": "Cancel", - "column.mfa_confirm_button": "Confirm", - "column.mfa_disable_button": "Disable", - "column.mfa_setup": "Proceed to Setup", - "column.migration": "Account migration", - "column.mutes": "靜音名單", + "column.import_data": "匯入資料", + "column.info": "伺服器資訊", + "column.lists": "名單", + "column.mentions": "提及", + "column.mfa": "兩步驟驗證", + "column.mfa_cancel": "取消", + "column.mfa_confirm_button": "確認", + "column.mfa_disable_button": "停用", + "column.mfa_setup": "同意並繼續", + "column.migration": "帳户遷移", + "column.mutes": "被隱藏的使用者", "column.notifications": "通知", - "column.pins": "Pinned posts", - "column.preferences": "Preferences", - "column.public": "跨站時間軸", - "column.reactions": "Reactions", - "column.reblogs": "Reposts", - "column.remote": "Federated timeline", - "column.scheduled_statuses": "Scheduled Posts", - "column.search": "Search", - "column.security": "Security", - "column.settings_store": "Settings store", - "column.soapbox_config": "Soapbox config", - "column.test": "Test timeline", - "column_back_button.label": "返回", - "column_forbidden.body": "You do not have permission to access this page.", - "column_forbidden.title": "Forbidden", + "column.pins": "釘選的貼文", + "column.preferences": "偏好設定", + "column.public": "聯邦時間軸", + "column.reactions": "心情回應", + "column.reblogs": "轉帖", + "column.remote": "聯邦時間軸", + "column.scheduled_statuses": "定時帖文", + "column.search": "搜尋", + "column.settings_store": "設定儲存", + "column.soapbox_config": "Soapbox設定", + "column.test": "測試時間軸", + "column_back_button.label": "上一頁", + "column_forbidden.body": "您無權訪問這個頁面。", + "column_forbidden.title": "無權訪問", "column_header.show_settings": "顯示設定", - "common.cancel": "Cancel", + "common.cancel": "取消", + "common.error": "有些東西好像壞了...嘗試重載頁面", "community.column_settings.media_only": "僅媒體", - "community.column_settings.title": "Local timeline settings", - "compose.character_counter.title": "Used {chars} out of {maxChars} characters", - "compose.invalid_schedule": "You must schedule a post at least 5 minutes out.", - "compose.submit_success": "Your post was sent", - "compose_form.direct_message_warning": "這文章只有被提及的用戶才可以看到。", - "compose_form.hashtag_warning": "這文章因為不是公開,所以不會被標籤搜索。只有公開的文章才會被標籤搜索。", - "compose_form.lock_disclaimer": "你的用戶狀態為「{locked}」,任何人都能立即關注你,然後看到「只有關注者能看」的文章。", - "compose_form.lock_disclaimer.lock": "公共", - "compose_form.markdown.marked": "Post markdown enabled", - "compose_form.markdown.unmarked": "Post markdown disabled", - "compose_form.message": "Message", - "compose_form.placeholder": "你在想甚麼?", - "compose_form.poll.add_option": "Add a choice", - "compose_form.poll.duration": "Poll duration", - "compose_form.poll.option_placeholder": "Choice {number}", - "compose_form.poll.remove_option": "Remove this choice", - "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices", - "compose_form.poll.switch_to_single": "Change poll to allow for a single choice", - "compose_form.publish": "發文", + "community.column_settings.title": "本地時間軸設定", + "compare_history_modal.header": "編輯歷史", + "compose.character_counter.title": "最大字符數: {maxChars}; 已使用 {chars}", + "compose.edit_success": "你的帖文已編輯", + "compose.invalid_schedule": "定時帖文只能設定在五分鐘後或更遲發送", + "compose.submit_success": "帖文已送出", + "compose_form.direct_message_warning": "這條帖文只有被提及的使用者才看得到。", + "compose_form.hashtag_warning": "由於這則帖文被設定成「不公開」,所以它將不會被列在任何主題標籤下。只有公開的帖文才能藉主題標籤找到。", + "compose_form.lock_disclaimer": "您的帳户尚未{locked}。任何人都能追蹤您並看到您設定成只有追蹤者能看的帖文。", + "compose_form.lock_disclaimer.lock": "上鎖", + "compose_form.markdown.marked": "Markdown已啟用", + "compose_form.markdown.unmarked": "Markdown已禁用", + "compose_form.message": "私訊", + "compose_form.placeholder": "您正在想些什麼?", + "compose_form.poll.add_option": "新增選擇", + "compose_form.poll.duration": "投票期限", + "compose_form.poll.multiselect": "多選", + "compose_form.poll.multiselect_detail": "允許參與者選擇多個答案", + "compose_form.poll.option_placeholder": "第 {number} 個選擇", + "compose_form.poll.remove": "移除投票", + "compose_form.poll.remove_option": "移除此選擇", + "compose_form.poll.switch_to_multiple": "投票改為多選", + "compose_form.poll.switch_to_single": "投票改為單選", + "compose_form.poll_placeholder": "添加投票主題...", + "compose_form.publish": "發佈", "compose_form.publish_loud": "{publish}!", - "compose_form.schedule": "Schedule", - "compose_form.scheduled_statuses.click_here": "Click here", - "compose_form.scheduled_statuses.message": "You have scheduled posts. {click_here} to see them.", - "compose_form.sensitive.hide": "Mark media as sensitive", - "compose_form.sensitive.marked": "媒體被標示為敏感", - "compose_form.sensitive.unmarked": "媒體沒有被標示為敏感", - "compose_form.spoiler.marked": "文字被警告隱藏", - "compose_form.spoiler.unmarked": "文字沒有被隱藏", - "compose_form.spoiler_placeholder": "敏感警告訊息", + "compose_form.save_changes": "儲存變更", + "compose_form.schedule": "定時發佈", + "compose_form.scheduled_statuses.click_here": "點擊此處", + "compose_form.scheduled_statuses.message": "你有定時帖文, {click_here} 查看", + "compose_form.spoiler.marked": "正文已隱藏到警告之後", + "compose_form.spoiler.unmarked": "正文未被隱藏", + "compose_form.spoiler_placeholder": "請在此處寫入警告訊息", + "compose_form.spoiler_remove": "移除敏感內容", + "compose_form.spoiler_title": "敏感內容", "confirmation_modal.cancel": "取消", - "confirmations.admin.deactivate_user.confirm": "Deactivate @{name}", - "confirmations.admin.deactivate_user.heading": "Deactivate @{acct}", - "confirmations.admin.deactivate_user.message": "You are about to deactivate @{acct}. Deactivating a user is a reversible action.", - "confirmations.admin.delete_local_user.checkbox": "I understand that I am about to delete a local user.", - "confirmations.admin.delete_status.confirm": "Delete post", - "confirmations.admin.delete_status.heading": "Delete post", - "confirmations.admin.delete_status.message": "You are about to delete a post by @{acct}. This action cannot be undone.", - "confirmations.admin.delete_user.confirm": "Delete @{name}", - "confirmations.admin.delete_user.heading": "Delete @{acct}", - "confirmations.admin.delete_user.message": "You are about to delete @{acct}. THIS IS A DESTRUCTIVE ACTION THAT CANNOT BE UNDONE.", - "confirmations.admin.mark_status_not_sensitive.confirm": "Mark post not sensitive", - "confirmations.admin.mark_status_not_sensitive.heading": "Mark post not sensitive.", - "confirmations.admin.mark_status_not_sensitive.message": "You are about to mark a post by @{acct} not sensitive.", - "confirmations.admin.mark_status_sensitive.confirm": "Mark post sensitive", - "confirmations.admin.mark_status_sensitive.heading": "Mark post sensitive", - "confirmations.admin.mark_status_sensitive.message": "You are about to mark a post by @{acct} sensitive.", - "confirmations.admin.reject_user.confirm": "Reject @{name}", - "confirmations.admin.reject_user.heading": "Reject @{acct}", - "confirmations.admin.reject_user.message": "You are about to reject @{acct} registration request. This action cannot be undone.", - "confirmations.block.block_and_report": "Block & Report", + "confirmations.admin.deactivate_user.confirm": "禁用帳户 @{name}", + "confirmations.admin.deactivate_user.heading": "禁用帳户 @{acct}", + "confirmations.admin.deactivate_user.message": "你確定要禁用帳户 @{acct} 嗎?此操作不能撤回!", + "confirmations.admin.delete_local_user.checkbox": "我確定我不再需要這個帳户", + "confirmations.admin.delete_status.confirm": "刪除帖文", + "confirmations.admin.delete_status.heading": "刪除帖文", + "confirmations.admin.delete_status.message": "你確定要刪除帖文 @{acct} 嗎?此操作不能撤回!", + "confirmations.admin.delete_user.confirm": "刪除帳户 @{name}", + "confirmations.admin.delete_user.heading": "刪除帳户 @{acct}", + "confirmations.admin.delete_user.message": "你確定要刪除帳户 @{acct}嗎?此操作不能撤回!", + "confirmations.admin.mark_status_not_sensitive.confirm": "標記為不敏感", + "confirmations.admin.mark_status_not_sensitive.heading": "標記為不敏感", + "confirmations.admin.mark_status_not_sensitive.message": "你要標記帳户 @{acct} 的帖文為不敏感", + "confirmations.admin.mark_status_sensitive.confirm": "標記為敏感帖文", + "confirmations.admin.mark_status_sensitive.heading": "標記為敏感帖文", + "confirmations.admin.mark_status_sensitive.message": "你要標記帳户 @{acct} 的帖文為敏感", + "confirmations.admin.reject_user.confirm": "拒絕 @{name}", + "confirmations.admin.reject_user.heading": "拒絕 @{acct}", + "confirmations.admin.reject_user.message": "你正準備拒絕 @{acct} 的註冊請求。此操作不能撤銷。", + "confirmations.block.block_and_report": "封鎖並檢舉", "confirmations.block.confirm": "封鎖", - "confirmations.block.heading": "Block @{name}", - "confirmations.block.message": "你確定要封鎖{name}嗎?", + "confirmations.block.heading": "封鎖 @{name}", + "confirmations.block.message": "確定封鎖 {name} ?", "confirmations.delete.confirm": "刪除", - "confirmations.delete.heading": "Delete post", - "confirmations.delete.message": "你確定要刪除這文章嗎?", + "confirmations.delete.heading": "刪除帖文", + "confirmations.delete.message": "你確定要刪除這條帖文?", "confirmations.delete_list.confirm": "刪除", - "confirmations.delete_list.heading": "Delete list", - "confirmations.delete_list.message": "你確定要永久刪除這列表嗎?", - "confirmations.domain_block.confirm": "隱藏整個網站", + "confirmations.delete_list.heading": "刪除列表", + "confirmations.delete_list.message": "你確定要永久刪除這個列表?", + "confirmations.domain_block.confirm": "隱藏整個網域", "confirmations.domain_block.heading": "Block {domain}", - "confirmations.domain_block.message": "你真的真的確定要隱藏整個 {domain} ?多數情況下,比較推薦封鎖或靜音幾個特定目標就好。你從此將不會再看到該站的內容和通知。來自該站的關注者亦會被移除。", - "confirmations.mute.confirm": "靜音", + "confirmations.domain_block.message": "真的非常確定封鎖整個 {domain} 嗎?大部分情況下,你只需要封鎖或隱藏少數特定的人就能滿足需求了。你將不能在任何公開的時間軸及通知中看到那個網域的內容。你來自該網域的追蹤者也會被移除。", + "confirmations.mute.confirm": "隱藏", "confirmations.mute.heading": "Mute @{name}", - "confirmations.mute.message": "你確定要將{name}靜音嗎?", - "confirmations.redraft.confirm": "刪除並編輯", - "confirmations.redraft.heading": "Delete & redraft", - "confirmations.redraft.message": "你確定要刪除並重新編輯嗎?所有相關的回覆、轉推與最愛都會被刪除。", - "confirmations.register.needs_approval": "Your account will be manually approved by an admin. Please be patient while we review your details.", - "confirmations.register.needs_approval.header": "Approval needed", - "confirmations.register.needs_confirmation": "Please check your inbox at {email} for confirmation instructions. You will need to verify your email address to continue.", - "confirmations.register.needs_confirmation.header": "Confirmation needed", - "confirmations.reply.confirm": "Reply", - "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", - "confirmations.scheduled_status_delete.confirm": "Cancel", - "confirmations.scheduled_status_delete.heading": "Cancel scheduled post", - "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", - "confirmations.unfollow.confirm": "取消關注", - "confirmations.unfollow.heading": "Unfollow {name}", - "confirmations.unfollow.message": "真的不要繼續關注 {name} 了嗎?", - "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", - "crypto_donate.explanation_box.title": "Sending cryptocurrency donations", - "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", - "crypto_donate_panel.heading": "Donate Cryptocurrency", - "crypto_donate_panel.intro.message": "{siteTitle} accepts cryptocurrency donations to fund our service. Thank you for your support!", - "datepicker.hint": "Scheduled to post at…", - "datepicker.next_month": "Next month", - "datepicker.next_year": "Next year", - "datepicker.previous_month": "Previous month", - "datepicker.previous_year": "Previous year", - "developers.challenge.answer_label": "Answer", - "developers.challenge.answer_placeholder": "Your answer", - "developers.challenge.fail": "Wrong answer", - "developers.challenge.message": "What is the result of calling {function}?", - "developers.challenge.submit": "Become a developer", - "developers.challenge.success": "You are now a developer", - "developers.leave": "You have left developers", - "developers.navigation.app_create_label": "Create an app", - "developers.navigation.intentional_error_label": "Trigger an error", - "developers.navigation.leave_developers_label": "Leave developers", - "developers.navigation.network_error_label": "Network error", - "developers.navigation.settings_store_label": "Settings store", - "developers.navigation.test_timeline_label": "Test timeline", - "developers.settings_store.hint": "It is possible to directly edit your user settings here. BE CAREFUL! Editing this section can break your account, and you will only be able to recover through the API.", - "direct.search_placeholder": "Send a message to…", - "directory.federated": "From known fediverse", - "directory.local": "From {domain} only", - "directory.new_arrivals": "New arrivals", - "directory.recently_active": "Recently active", - "edit_federation.followers_only": "Hide posts except to followers", - "edit_federation.force_nsfw": "Force attachments to be marked sensitive", - "edit_federation.media_removal": "Strip media", - "edit_federation.reject": "Reject all activities", - "edit_federation.save": "Save", - "edit_federation.success": "{host} federation was updated", - "edit_federation.unlisted": "Force posts unlisted", - "edit_password.header": "Change Password", - "edit_profile.error": "Profile update failed", - "edit_profile.fields.accepts_email_list_label": "Subscribe to newsletter", - "edit_profile.fields.avatar_label": "Avatar", - "edit_profile.fields.bio_label": "Bio", - "edit_profile.fields.bio_placeholder": "Tell us about yourself.", - "edit_profile.fields.birthday_label": "Birthday", - "edit_profile.fields.birthday_placeholder": "Your birthday", - "edit_profile.fields.bot_label": "This is a bot account", - "edit_profile.fields.discoverable_label": "Allow account discovery", - "edit_profile.fields.display_name_label": "Display name", - "edit_profile.fields.display_name_placeholder": "Name", - "edit_profile.fields.header_label": "Header", - "edit_profile.fields.hide_network_label": "Hide network", - "edit_profile.fields.location_label": "Location", - "edit_profile.fields.location_placeholder": "Location", - "edit_profile.fields.locked_label": "Lock account", - "edit_profile.fields.meta_fields.content_placeholder": "Content", - "edit_profile.fields.meta_fields.label_placeholder": "Label", - "edit_profile.fields.stranger_notifications_label": "Block notifications from strangers", - "edit_profile.fields.website_label": "Website", - "edit_profile.fields.website_placeholder": "Display a Link", - "edit_profile.header": "Edit Profile", - "edit_profile.hints.accepts_email_list": "Opt-in to news and marketing updates.", - "edit_profile.hints.avatar": "PNG, GIF or JPG. Will be downscaled to {size}", - "edit_profile.hints.bot": "This account mainly performs automated actions and might not be monitored", - "edit_profile.hints.discoverable": "Display account in profile directory and allow indexing by external services", - "edit_profile.hints.header": "PNG, GIF or JPG. Will be downscaled to {size}", - "edit_profile.hints.hide_network": "Who you follow and who follows you will not be shown on your profile", - "edit_profile.hints.locked": "Requires you to manually approve followers", - "edit_profile.hints.stranger_notifications": "Only show notifications from people you follow", - "edit_profile.save": "Save", - "edit_profile.success": "Profile saved!", - "email_passthru.confirmed.body": "Close this tab and continue the registration process on the {bold} from which you sent this email confirmation.", - "email_passthru.confirmed.heading": "Email Confirmed!", - "email_passthru.generic_fail.body": "Please request a new email confirmation.", - "email_passthru.generic_fail.heading": "Something Went Wrong", - "email_passthru.token_expired.body": "Your email token has expired. Please request a new email confirmation from the {bold} from which you sent this email confirmation.", - "email_passthru.token_expired.heading": "Token Expired", - "email_passthru.token_not_found.body": "Your email token was not found. Please request a new email confirmation from the {bold} from which you sent this email confirmation.", - "email_passthru.token_not_found.heading": "Invalid Token", - "embed.instructions": "要內嵌此文章,請將以下代碼貼進你的網站。", - "embed.preview": "看上去會是這樣:", + "confirmations.mute.message": "確定隱藏 {name} ?", + "confirmations.redraft.confirm": "刪除並重新編輯", + "confirmations.redraft.heading": "刪除並重新編輯", + "confirmations.redraft.message": "確定刪掉這則帖文並重新編輯嗎?將會失去這則帖文的轉帖及收藏,且回覆這則的帖文將會變成獨立的帖文。", + "confirmations.register.needs_approval": "你的帳户正在被管理員審核中,請等待一會", + "confirmations.register.needs_approval.header": "需要審核", + "confirmations.register.needs_confirmation": "我們已經發送了指引到你的電郵 {email} 中,請檢查收件箱並點擊鏈接以繼續。", + "confirmations.register.needs_confirmation.header": "需要確認", + "confirmations.remove_from_followers.confirm": "刪除", + "confirmations.remove_from_followers.message": "確定要從你的追蹤者中移走 {name} 嗎?", + "confirmations.reply.confirm": "回覆", + "confirmations.reply.message": "現在回覆將蓋掉您目前正在撰寫的訊息。是否仍要回覆?", + "confirmations.scheduled_status_delete.confirm": "取消", + "confirmations.scheduled_status_delete.heading": "取消帖文定時發佈", + "confirmations.scheduled_status_delete.message": "你確定要取消這篇帖文定時發佈嗎?", + "confirmations.unfollow.confirm": "取消追蹤", + "confirmations.unfollow.heading": "取消追蹤 {name}", + "confirmations.unfollow.message": "真的要取消追蹤 {name} 嗎?", + "crypto_donate.explanation_box.message": "{siteTitle} 接受用户向以下錢包地址捐贈任意數量的數字資產。你的抖內會令我們做得更好!", + "crypto_donate.explanation_box.title": "發起數字貨幣捐贈", + "crypto_donate_panel.actions.view": "點擊查看 {count} {count, plural, one {wallet} other {wallets}}", + "crypto_donate_panel.heading": "捐贈數字貨幣", + "crypto_donate_panel.intro.message": "{siteTitle} 接受用户捐贈數字貨幣。感謝你的支持!", + "datepicker.day": "Day", + "datepicker.hint": "設定發送時間…", + "datepicker.month": "Month", + "datepicker.next_month": "下個月", + "datepicker.next_year": "明年", + "datepicker.previous_month": "上個月", + "datepicker.previous_year": "去年", + "datepicker.year": "Year", + "developers.challenge.answer_label": "回答", + "developers.challenge.answer_placeholder": "你的回答", + "developers.challenge.fail": "回答錯誤", + "developers.challenge.message": "調用函數 {function} 的結果是什麼?", + "developers.challenge.submit": "成為開發者", + "developers.challenge.success": "你已成為開發者", + "developers.leave": "您已退出開發者", + "developers.navigation.app_create_label": "創建應用", + "developers.navigation.intentional_error_label": "觸發錯誤", + "developers.navigation.leave_developers_label": "退出開發者", + "developers.navigation.network_error_label": "網絡錯誤", + "developers.navigation.settings_store_label": "設置儲存", + "developers.navigation.test_timeline_label": "測試時間軸", + "developers.settings_store.hint": "可以在此處直接編輯您的用户設置。 當心!編輯此部分可能會破壞您的帳户,您只能通過 API 恢復。", + "direct.body": "新的私訊傳遞體驗即將推出。 敬請期待。", + "direct.search_placeholder": "發送私信給…", + "directory.federated": "來自已知聯邦宇宙", + "directory.local": "僅來自 {domain}", + "directory.new_arrivals": "新增訪客", + "directory.recently_active": "最近活動", + "edit_federation.followers_only": "對追蹤者以外的用户隱藏帖文", + "edit_federation.force_nsfw": "將附件強制標記為敏感內容", + "edit_federation.media_removal": "去除媒體", + "edit_federation.reject": "拒絕所有資訊交互", + "edit_federation.save": "儲存", + "edit_federation.success": "{host} 聯邦設定已儲存", + "edit_federation.unlisted": "將帖文強制標記移出公共時間軸", + "edit_password.header": "修改密碼", + "edit_profile.error": "個人資料更新失敗", + "edit_profile.fields.accepts_email_list_label": "訂閲電子郵件列表", + "edit_profile.fields.avatar_label": "頭帖", + "edit_profile.fields.bio_label": "簡介", + "edit_profile.fields.bio_placeholder": "介紹一下自己", + "edit_profile.fields.birthday_label": "生日", + "edit_profile.fields.birthday_placeholder": "你的生日", + "edit_profile.fields.bot_label": "這是一個機械人帳户", + "edit_profile.fields.discoverable_label": "公開自己", + "edit_profile.fields.display_name_label": "昵稱", + "edit_profile.fields.display_name_placeholder": "你的昵稱", + "edit_profile.fields.header_label": "個人資料頁橫幅圖片", + "edit_profile.fields.hide_network_label": "隱藏網絡狀態", + "edit_profile.fields.location_label": "地點", + "edit_profile.fields.location_placeholder": "地點", + "edit_profile.fields.locked_label": "鎖定帳户", + "edit_profile.fields.meta_fields.content_placeholder": "內容", + "edit_profile.fields.meta_fields.label_placeholder": "標籤", + "edit_profile.fields.verified_display_name": "經過驗證的用户無法修改昵稱", + "edit_profile.fields.meta_fields_label": "個人資料自定義列", + "edit_profile.fields.stranger_notifications_label": "封鎖來自陌生人的通知", + "edit_profile.fields.website_label": "網站", + "edit_profile.fields.website_placeholder": "顯示連結", + "edit_profile.header": "編輯個人資料", + "edit_profile.hints.accepts_email_list": "接收新聞和推廣電郵", + "edit_profile.hints.avatar": "只支援 PNG, GIF or JPG。 尺寸將會被縮放至 {size}", + "edit_profile.hints.bot": "此帳户主要執行自動化操作,可能不受用户控制", + "edit_profile.hints.discoverable": "在配置文件目錄中顯示帳户並允許外部服務索引", + "edit_profile.hints.header": "只支援 PNG, GIF or JPG。 尺寸將會被縮放至 {size}", + "edit_profile.hints.hide_network": "您追蹤的人和追蹤您的人不會顯示在您的個人資料中", + "edit_profile.hints.locked": "需要您手動批准追蹤請求", + "edit_profile.hints.meta_fields": "你能在個人資料頁面上最多顯示 {count} 行自定義列", + "edit_profile.hints.stranger_notifications": "僅顯示來自您追蹤的人的通知", + "edit_profile.save": "儲存", + "edit_profile.success": "個人資料已儲存", + "email_passthru.confirmed.body": "關閉此頁面,並在你發送此電子郵件確認的 {bold} 上繼續註冊過程", + "email_passthru.confirmed.heading": "電郵地址已確認!", + "email_passthru.generic_fail.body": "請重新請求確認郵件", + "email_passthru.generic_fail.heading": "好像出了一點問題…", + "email_passthru.token_expired.body": "你的電郵令牌已經過期。請從你發送此電郵確認的 {bold} 處重新申請電郵確認。", + "email_passthru.token_expired.heading": "令牌已過期", + "email_passthru.token_not_found.body": "你的電郵令牌已經過期。請從你發送此電郵確認的 {bold} 處重新申請電郵確認。", + "email_passthru.token_not_found.heading": "無效令牌!", + "embed.instructions": "要嵌入此帖文,請將以下程式碼貼進你的網站。", + "embed.preview": "他會顯示成這樣:", "emoji_button.activity": "活動", "emoji_button.custom": "自訂", - "emoji_button.flags": "旗幟", - "emoji_button.food": "飲飲食食", - "emoji_button.label": "加入表情符號", - "emoji_button.nature": "自然", - "emoji_button.not_found": "沒有表情符號!! (╯°□°)╯︵ ┻━┻", - "emoji_button.objects": "物品", - "emoji_button.people": "人物", - "emoji_button.recent": "常用", + "emoji_button.flags": "旗標", + "emoji_button.food": "飲食", + "emoji_button.label": "插入表情符號", + "emoji_button.nature": "大自然", + "emoji_button.not_found": "啊就沒這表情符號吼!! (╯°□°)╯︵ ┻━┻", + "emoji_button.objects": "物件", + "emoji_button.people": "使用者", + "emoji_button.recent": "最常使用", "emoji_button.search": "搜尋…", "emoji_button.search_results": "搜尋結果", "emoji_button.symbols": "符號", - "emoji_button.travel": "旅遊景物", - "empty_column.account_blocked": "You are blocked by @{accountUsername}.", - "empty_column.account_favourited_statuses": "This user doesn't have any liked posts yet.", - "empty_column.account_timeline": "No posts here!", - "empty_column.account_unavailable": "Profile unavailable", - "empty_column.aliases": "You haven't created any account alias yet.", - "empty_column.aliases.suggestions": "There are no account suggestions available for the provided term.", - "empty_column.blocks": "You haven't blocked any users yet.", - "empty_column.bookmarks": "You don't have any bookmarks yet. When you add one, it will show up here.", - "empty_column.community": "本站時間軸暫時未有內容,快寫一點東西來搶頭香啊!", - "empty_column.direct": "你沒有個人訊息。當你發出或接收個人訊息,就會在這裡出現。", - "empty_column.domain_blocks": "There are no hidden domains yet.", - "empty_column.favourited_statuses": "You don't have any favorite posts yet. When you favorite one, it will show up here.", - "empty_column.favourites": "No one has favorited this post yet. When someone does, they will show up here.", - "empty_column.filters": "You haven't created any muted words yet.", - "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.", - "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.", - "empty_column.group": "There is nothing in this group yet. When members of this group make new posts, they will appear here.", - "empty_column.hashtag": "這個標籤暫時未有內容。", - "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。", - "empty_column.home.local_tab": "the {site_title} tab", - "empty_column.list": "這個列表暫時未有內容。", - "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.", - "empty_column.mutes": "You haven't muted any users yet.", - "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。", - "empty_column.public": "跨站時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。", - "empty_column.remote": "There is nothing here! Manually follow users from {instance} to fill it up.", - "empty_column.scheduled_statuses": "You don't have any scheduled statuses yet. When you add one, it will show up here.", - "empty_column.search.accounts": "There are no people results for \"{term}\"", - "empty_column.search.hashtags": "There are no hashtags results for \"{term}\"", - "empty_column.search.statuses": "There are no posts results for \"{term}\"", - "export_data.actions.export": "Export", - "export_data.actions.export_blocks": "Export blocks", - "export_data.actions.export_follows": "Export follows", - "export_data.actions.export_mutes": "Export mutes", - "export_data.blocks_label": "Blocks", - "export_data.follows_label": "Follows", - "export_data.hints.blocks": "Get a CSV file containing a list of blocked accounts", - "export_data.hints.follows": "Get a CSV file containing a list of followed accounts", - "export_data.hints.mutes": "Get a CSV file containing a list of muted accounts", - "export_data.mutes_label": "Mutes", - "export_data.success.blocks": "Blocks exported successfully", - "export_data.success.followers": "Followers exported successfully", - "export_data.success.mutes": "Mutes exported successfully", - "federation_restriction.federated_timeline_removal": "Fediverse timeline removal", - "federation_restriction.followers_only": "Hidden except to followers", - "federation_restriction.full_media_removal": "Full media removal", - "federation_restriction.media_nsfw": "Attachments marked NSFW", - "federation_restriction.partial_media_removal": "Partial media removal", - "federation_restrictions.empty_message": "{siteTitle} has not restricted any instances.", - "federation_restrictions.explanation_box.message": "Normally servers on the Fediverse can communicate freely. {siteTitle} has imposed restrictions on the following servers.", - "federation_restrictions.explanation_box.title": "Instance-specific policies", - "federation_restrictions.not_disclosed_message": "{siteTitle} does not disclose federation restrictions through the API.", - "fediverse_tab.explanation_box.dismiss": "Don't show again", - "fediverse_tab.explanation_box.explanation": "{site_title} is part of the Fediverse, a social network made up of thousands of independent social media sites (aka \"servers\"). The posts you see here are from 3rd-party servers. You have the freedom to engage with them, or to block any server you don't like. Pay attention to the full username after the second @ symbol to know which server a post is from. To see only {site_title} posts, visit {local}.", - "fediverse_tab.explanation_box.title": "What is the Fediverse?", - "filters.added": "Filter added.", - "filters.context_header": "Filter contexts", - "filters.context_hint": "One or multiple contexts where the filter should apply", - "filters.filters_list_context_label": "Filter contexts:", - "filters.filters_list_delete": "Delete", - "filters.filters_list_details_label": "Filter settings:", - "filters.filters_list_drop": "Drop", - "filters.filters_list_hide": "Hide", - "filters.filters_list_phrase_label": "Keyword or phrase:", - "filters.filters_list_whole-word": "Whole word", - "filters.removed": "Filter deleted.", - "follow_recommendation.subhead": "Let's get started!", - "follow_recommendations.done": "Done", - "follow_recommendations.heading": "Follow people you'd like to see posts from! Here are some suggestions.", - "follow_recommendations.lead": "Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!", - "follow_request.authorize": "批准", + "emoji_button.travel": "旅遊與地點", + "empty_column.account_blocked": "你被 @{accountUsername} 封鎖了", + "empty_column.account_favourited_statuses": "他還沒有按讚任何貼文", + "empty_column.account_timeline": "這裡還沒有帖文!", + "empty_column.account_unavailable": "無法取得個人資料", + "empty_column.aliases": "你還沒有設定任何別名", + "empty_column.aliases.suggestions": "暫時沒有可用的帳户建議", + "empty_column.blocks": "你還沒有封鎖任何使用者。", + "empty_column.bookmarks": "你還沒有任何書籤收藏,一旦你開始將帖文加入書籤,它將會在這裡顯示", + "empty_column.community": "本地時間軸是空的。快公開發佈些文搶頭香啊!", + "empty_column.direct": "您還沒有任何私訊。當您私訊別人或收到私訊時,它將於此顯示。", + "empty_column.domain_blocks": "尚未隱藏任何網域。", + "empty_column.favourited_statuses": "你還沒收藏任何帖文。這裡將會顯示你收藏的帖文。", + "empty_column.favourites": "還沒有人收藏這則帖文。這裡將會顯示被收藏的帖文。", + "empty_column.filters": "你還未有添加任何過濾詞。", + "empty_column.follow_recommendations": "目前似乎暫時沒有推薦訊息,你可以嘗試搜尋用户或者瀏覽熱門標籤。", + "empty_column.follow_requests": "您尚未收到任何追蹤請求。這裡將會顯示收到的追蹤請求。", + "empty_column.group": "該組中還沒有任何內容。 當該群組的成員發布新帖子時,他們會出現在此處。", + "empty_column.hashtag": "這個主題標籤下什麼也沒有。", + "empty_column.home": "您的首頁時間軸是空的!前往 {public} 或使用搜尋功能來認識其他人。", + "empty_column.home.local_tab": "{site_title} 本站時間軸", + "empty_column.list": "這份名單還沒有東西。當此名單的成員發佈了新的帖文時,它們就會顯示於此。", + "empty_column.lists": "你還沒有建立任何名單。這裡將會顯示你所建立的名單。", + "empty_column.mutes": "你尚未隱藏任何使用者。", + "empty_column.notifications": "您尚未收到任何通知,和別人互動開啟對話吧。", + "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的帖文,或著自己追蹤其他伺服器的使用者後就會有帖文出現了", + "empty_column.remote": "這裡什麼都沒有! 關注本站或者其他站點的成員,就會有用户出現在這裡。", + "empty_column.scheduled_statuses": "暫時沒有定時帖文。當你發佈定時帖文後,它們會顯示在這裡。", + "empty_column.search.accounts": "沒有匹配的帳户 \"{term}\"", + "empty_column.search.hashtags": "沒有匹配的標籤 \"{term}\"", + "empty_column.search.statuses": "沒有匹配的帖文 \"{term}\"", + "empty_column.test": "測試時間軸是空的", + "export_data.actions.export": "匯出", + "export_data.actions.export_blocks": "匯出封鎖名單", + "export_data.actions.export_follows": "匯出追蹤名單", + "export_data.actions.export_mutes": "匯出隱藏名單", + "export_data.blocks_label": "封鎖", + "export_data.follows_label": "追蹤", + "export_data.hints.blocks": "匯出封鎖名單為CSV檔案", + "export_data.hints.follows": "匯出追蹤名單為CSV檔案", + "export_data.hints.mutes": "匯出隱藏名單為CSV檔案", + "export_data.mutes_label": "隱藏", + "export_data.success.blocks": "封鎖名單匯出成功", + "export_data.success.followers": "追蹤名單匯出成功", + "export_data.success.mutes": "隱藏名單匯出成功", + "federation_restriction.federated_timeline_removal": "從聯邦宇宙時間軸移除", + "federation_restriction.followers_only": "僅追蹤者可見", + "federation_restriction.full_media_removal": "完全移除媒體", + "federation_restriction.media_nsfw": "附件標註為 NSFW", + "federation_restriction.partial_media_removal": "部分移除媒體", + "federation_restrictions.empty_message": "{siteTitle} 沒有限制任何實例", + "federation_restrictions.explanation_box.message": "通常情況下,聯邦宇宙上的伺服器可以自由通訊。然而 {siteTitle} 對以下伺服器實施了限制", + "federation_restrictions.explanation_box.title": "實例相關政策", + "federation_restrictions.not_disclosed_message": "{siteTitle} 沒有通過API向聯邦宇宙公開限制。", + "fediverse_tab.explanation_box.dismiss": "不再顯示", + "fediverse_tab.explanation_box.explanation": "{site_title} 是聯邦宇宙的一份子, 一個由數個站點組成的社交網絡集合。你在這裏看到的帖文來自於其他站點。你可以自由地與他們打交道,或者封鎖任何你不喜歡的站點。第二個 @ 符號後的完整帳户名表示帖文來自哪個站點。要想只看到 {site_title} 的帖文, 請瀏覽 {local} 。", + "fediverse_tab.explanation_box.title": "什麼是聯邦宇宙?", + "feed_suggestions.heading": "建議的個人資料", + "feed_suggestions.view_all": "檢視全部", + "filters.added": "過濾器已添加。", + "filters.context_header": "過濾器場景", + "filters.context_hint": "一個或多個應用至過濾器的條件", + "filters.filters_list_context_label": "過濾器場景:", + "filters.filters_list_delete": "刪除過濾詞", + "filters.filters_list_details_label": "過濾詞設定:", + "filters.filters_list_drop": "丟棄", + "filters.filters_list_hide": "隱藏", + "filters.filters_list_phrase_label": "關鍵詞:", + "filters.filters_list_whole-word": "全詞", + "filters.removed": "過濾器已移除", + "follow_recommendation.subhead": "讓我們開始吧!", + "follow_recommendations.done": "完成", + "follow_recommendations.heading": "追蹤你感興趣的人,這是我們的推薦列表", + "follow_recommendations.lead": "你追蹤的人的帖文將按照時間順序顯示在你的主頁上。不用擔心搞錯,你可以在任何時候輕鬆地撤銷追蹤!", + "follow_request.authorize": "授權", "follow_request.reject": "拒絕", - "forms.copy": "Copy", - "forms.hide_password": "Hide password", - "forms.show_password": "Show password", - "getting_started.open_source_notice": "{code_name}(萬象)是一個開放源碼的軟件。你可以在官方 GitLab ({code_link} (v{code_version})) 貢獻或者回報問題。", - "group.detail.archived_group": "Archived group", - "group.members.empty": "This group does not has any members.", - "group.removed_accounts.empty": "This group does not has any removed accounts.", - "groups.card.join": "Join", - "groups.card.members": "Members", - "groups.card.roles.admin": "You're an admin", - "groups.card.roles.member": "You're a member", - "groups.card.view": "View", - "groups.create": "Create group", - "groups.detail.role_admin": "You're an admin", - "groups.edit": "Edit", - "groups.form.coverImage": "Upload new banner image (optional)", - "groups.form.coverImageChange": "Banner image selected", - "groups.form.create": "Create group", - "groups.form.description": "Description", - "groups.form.title": "Title", - "groups.form.update": "Update group", - "groups.join": "Join group", - "groups.leave": "Leave group", - "groups.removed_accounts": "Removed Accounts", - "groups.sidebar-panel.item.no_recent_activity": "No recent activity", - "groups.sidebar-panel.item.view": "new posts", - "groups.sidebar-panel.show_all": "Show all", - "groups.sidebar-panel.title": "Groups You're In", - "groups.tab_admin": "Manage", - "groups.tab_featured": "Featured", - "groups.tab_member": "Member", - "hashtag.column_header.tag_mode.all": "and {additional}", - "hashtag.column_header.tag_mode.any": "or {additional}", - "hashtag.column_header.tag_mode.none": "without {additional}", - "header.home.label": "Home", - "header.login.forgot_password": "Forgot password?", - "header.login.label": "Log in", - "header.login.password.label": "Password", - "header.login.username.placeholder": "Email or username", - "header.register.label": "Register", - "home.column_settings.show_direct": "Show direct messages", - "home.column_settings.show_reblogs": "顯示被轉推的文章", - "home.column_settings.show_replies": "顯示回應文章", - "home.column_settings.title": "Home settings", - "icon_button.icons": "Icons", - "icon_button.label": "Select icon", - "icon_button.not_found": "No icons!! (╯°□°)╯︵ ┻━┻", - "import_data.actions.import": "Import", - "import_data.actions.import_blocks": "Import blocks", - "import_data.actions.import_follows": "Import follows", - "import_data.actions.import_mutes": "Import mutes", - "import_data.blocks_label": "Blocks", - "import_data.follows_label": "Follows", - "import_data.hints.blocks": "CSV file containing a list of blocked accounts", - "import_data.hints.follows": "CSV file containing a list of followed accounts", - "import_data.hints.mutes": "CSV file containing a list of muted accounts", - "import_data.mutes_label": "Mutes", - "import_data.success.blocks": "Blocks imported successfully", - "import_data.success.followers": "Followers imported successfully", - "import_data.success.mutes": "Mutes imported successfully", - "input.password.hide_password": "Hide password", - "input.password.show_password": "Show password", - "intervals.full.days": "{number, plural, one {# day} other {# days}}", - "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", - "introduction.federation.action": "Next", - "introduction.federation.home.headline": "Home", - "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", - "introduction.interactions.action": "Finish tutorial!", - "introduction.interactions.favourite.headline": "Favorite", - "introduction.interactions.favourite.text": "You can save a post for later, and let the author know that you liked it, by favoriting it.", - "introduction.interactions.reblog.headline": "Repost", - "introduction.interactions.reblog.text": "You can share other people's posts with your followers by reposting them.", - "introduction.interactions.reply.headline": "Reply", - "introduction.interactions.reply.text": "You can reply to other people's and your own posts, which will chain them together in a conversation.", - "introduction.welcome.action": "Let's go!", - "introduction.welcome.headline": "First steps", - "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", - "keyboard_shortcuts.back": "後退", - "keyboard_shortcuts.blocked": "to open blocked users list", - "keyboard_shortcuts.boost": "轉推", - "keyboard_shortcuts.compose": "把標示移動到文字輸入區", - "keyboard_shortcuts.down": "在列表往下移動", - "keyboard_shortcuts.enter": "打開文章", + "forms.copy": "複製", + "forms.hide_password": "隱藏密碼", + "forms.show_password": "顯示密碼", + "getting_started.open_source_notice": "{code_name} 是開源軟體。你可以在 GitLab {code_link} (v{code_version}) 上貢獻或是回報問題。", + "group.members.empty": "這個名單中沒有任何成員", + "group.removed_accounts.empty": "這個名單中沒有任何被移除的帳户", + "groups.card.join": "加入", + "groups.card.members": "成員", + "groups.card.roles.admin": "你是管理員", + "groups.card.roles.member": "你是成員", + "groups.card.view": "檢視", + "groups.create": "創建群組", + "groups.form.coverImage": "上載橫幅圖片 (可選)", + "groups.form.coverImageChange": "橫幅圖片已上載", + "groups.form.create": "創建群組", + "groups.form.description": "描述", + "groups.form.title": "標題", + "groups.form.update": "更新群組", + "groups.removed_accounts": "已移除帳户", + "groups.tab_admin": "管理", + "groups.tab_featured": "精選", + "groups.tab_member": "成員", + "hashtag.column_header.tag_mode.all": "以及{additional}", + "hashtag.column_header.tag_mode.any": "或是{additional}", + "hashtag.column_header.tag_mode.none": "而無需{additional}", + "header.home.label": "首頁", + "header.login.forgot_password": "忘記了密碼?", + "header.login.label": "登入", + "header.login.password.label": "密碼", + "header.login.username.placeholder": "電郵地址或帳户名稱", + "header.preview_timeline.label": "瀏覽首頁", + "header.register.label": "註冊", + "home.column_settings.show_reblogs": "顯示轉帖", + "home.column_settings.show_replies": "顯示回覆", + "icon_button.icons": "圖示", + "icon_button.label": "選取圖示", + "icon_button.not_found": "沒有圖示!! (╯°□°)╯︵ ┻━┻", + "import_data.actions.import": "匯入", + "import_data.actions.import_blocks": "匯入封鎖名單", + "import_data.actions.import_follows": "匯入追蹤名單", + "import_data.actions.import_mutes": "匯入隱藏名單", + "import_data.blocks_label": "封鎖帳户", + "import_data.follows_label": "追蹤帳户", + "import_data.hints.blocks": "上載包含封鎖帳户名單的CSV檔案", + "import_data.hints.follows": "上載包含追蹤帳户名單的CSV檔案", + "import_data.hints.mutes": "上載包含隱藏帳户名單的CSV檔案", + "import_data.mutes_label": "隱藏帳户", + "import_data.success.blocks": "封鎖帳户名單已匯入", + "import_data.success.followers": "追蹤帳户名單已匯入", + "import_data.success.mutes": "隱藏帳户名單已匯入", + "input.password.hide_password": "隱藏密碼", + "input.password.show_password": "顯示密碼", + "intervals.full.days": "{number} 天", + "intervals.full.hours": "{number} 小時", + "intervals.full.minutes": "{number} 分鐘", + "keyboard_shortcuts.back": "返回上一頁", + "keyboard_shortcuts.blocked": "開啟「封鎖使用者」名單", + "keyboard_shortcuts.boost": "轉帖", + "keyboard_shortcuts.compose": "將焦點移至撰寫文字區塊", + "keyboard_shortcuts.down": "往下移動名單項目", + "keyboard_shortcuts.enter": "檢視帖文", "keyboard_shortcuts.favourite": "收藏", - "keyboard_shortcuts.favourites": "to open favorites list", + "keyboard_shortcuts.favourites": "開啟收藏名單", "keyboard_shortcuts.heading": "鍵盤快速鍵", - "keyboard_shortcuts.home": "to open home timeline", + "keyboard_shortcuts.home": "開啟首頁時間軸", "keyboard_shortcuts.hotkey": "快速鍵", - "keyboard_shortcuts.legend": "顯示這個說明", + "keyboard_shortcuts.legend": "顯示此名單", "keyboard_shortcuts.mention": "提及作者", - "keyboard_shortcuts.muted": "to open muted users list", - "keyboard_shortcuts.my_profile": "to open your profile", - "keyboard_shortcuts.notifications": "to open notifications column", - "keyboard_shortcuts.open_media": "to open media", - "keyboard_shortcuts.pinned": "to open pinned posts list", - "keyboard_shortcuts.profile": "to open author's profile", - "keyboard_shortcuts.react": "to react", + "keyboard_shortcuts.muted": "開啟隱藏使用者名單", + "keyboard_shortcuts.my_profile": "開啟個人資料頁面", + "keyboard_shortcuts.notifications": "開啟通知欄", + "keyboard_shortcuts.open_media": "開啟媒體", + "keyboard_shortcuts.pinned": "開啟釘選的帖文名單", + "keyboard_shortcuts.profile": "開啟作者的個人資料頁面", + "keyboard_shortcuts.react": "心情回應", "keyboard_shortcuts.reply": "回覆", - "keyboard_shortcuts.requests": "to open follow requests list", - "keyboard_shortcuts.search": "把標示移動到搜索", - "keyboard_shortcuts.toggle_hidden": "顯示或隱藏被標為敏感的文字", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", - "keyboard_shortcuts.toot": "新的推文", - "keyboard_shortcuts.unfocus": "把標示移離文字輸入和搜索", - "keyboard_shortcuts.up": "在列表往上移動", - "landing_page_modal.download": "Download", - "landing_page_modal.helpCenter": "Help Center", + "keyboard_shortcuts.requests": "開啟追蹤請求名單", + "keyboard_shortcuts.search": "將焦點移至搜尋框", + "keyboard_shortcuts.toggle_hidden": "顯示/隱藏在內容警告之後的正文", + "keyboard_shortcuts.toggle_sensitivity": "顯示 / 隱藏媒體", + "keyboard_shortcuts.toot": "開始發出新帖文", + "keyboard_shortcuts.unfocus": "取消輸入文字區塊 / 搜尋的焦點", + "keyboard_shortcuts.up": "往上移動名單項目", + "landing_page_modal.download": "下載", + "landing_page_modal.helpCenter": "支援中心", "lightbox.close": "關閉", - "lightbox.next": "繼續", - "lightbox.previous": "回退", - "lightbox.view_context": "View context", - "list.click_to_add": "Click here to add people", - "list_adder.header_title": "Add or Remove from Lists", - "lists.account.add": "新增到列表", - "lists.account.remove": "從列表刪除", - "lists.delete": "Delete list", - "lists.edit": "編輯列表", - "lists.edit.submit": "Change title", - "lists.new.create": "新增列表", - "lists.new.create_title": "Add list", - "lists.new.save_title": "Save Title", - "lists.new.title_placeholder": "新列表標題", - "lists.search": "從你關注的用戶中搜索", - "lists.subheading": "列表", - "loading_indicator.label": "載入中...", - "login.fields.instance_label": "Instance", + "lightbox.next": "下一步", + "lightbox.previous": "上一步", + "lightbox.view_context": "檢視內文", + "list.click_to_add": "點擊新增帳户到名單", + "list_adder.header_title": "從名單中添加或刪除帳户", + "lists.account.add": "新增至名單", + "lists.account.remove": "從名單中移除", + "lists.delete": "刪除名單", + "lists.edit": "編輯名單", + "lists.edit.submit": "變更標題", + "lists.new.create": "新增名單", + "lists.new.create_title": "新增名單", + "lists.new.save_title": "儲存名單", + "lists.new.title_placeholder": "新名單標題", + "lists.search": "搜尋您追蹤的使用者", + "lists.subheading": "您的名單", + "loading_indicator.label": "讀取中...", + "login.fields.instance_label": "實例", "login.fields.instance_placeholder": "example.com", - "login.fields.otp_code_hint": "Enter the two-factor code generated by your phone app or use one of your recovery codes", - "login.fields.otp_code_label": "Two-factor code:", - "login.fields.password_placeholder": "Password", - "login.fields.username_label": "Email or username", - "login.log_in": "Log in", - "login.otp_log_in": "OTP Login", - "login.reset_password_hint": "Trouble logging in?", - "login.sign_in": "Sign in", - "media_gallery.toggle_visible": "打開或關上", - "media_panel.empty_message": "No media found.", - "media_panel.title": "Media", - "mfa.confirm.success_message": "MFA confirmed", - "mfa.disable.success_message": "MFA disabled", - "mfa.mfa_disable_enter_password": "Enter your current password to disable two-factor auth:", - "mfa.mfa_setup.code_hint": "Enter the code from your two-factor app.", - "mfa.mfa_setup.code_placeholder": "Code", - "mfa.mfa_setup.password_hint": "Enter your current password to confirm your identity.", - "mfa.mfa_setup.password_placeholder": "Password", - "mfa.mfa_setup_scan_description": "Using your two-factor app, scan this QR code or enter text key:", - "mfa.mfa_setup_scan_title": "Scan", - "mfa.mfa_setup_verify_title": "Verify", - "mfa.otp_enabled_description": "You have enabled two-factor authentication via OTP.", - "mfa.otp_enabled_title": "OTP Enabled", - "mfa.setup_recoverycodes": "Recovery codes", - "mfa.setup_warning": "Write these codes down or save them somewhere secure - otherwise you won't see them again. If you lose access to your 2FA app and recovery codes you'll be locked out of your account.", - "migration.fields.acct.label": "Handle of the new account", - "migration.fields.acct.placeholder": "username@domain", - "migration.fields.confirm_password.label": "Current password", - "migration.hint": "This will move your followers to the new account. No other data will be moved. To perform migration, you need to {link} on your new account first.", - "migration.hint.link": "create an account alias", - "migration.move_account.fail": "Account migration failed.", - "migration.move_account.success": "Account successfully moved.", - "migration.submit": "Move followers", - "missing_description_modal.cancel": "Cancel", - "missing_description_modal.continue": "Post", - "missing_description_modal.description": "Continue anyway?", - "missing_description_modal.text": "You have not entered a description for all attachments. Continue anyway?", - "missing_indicator.label": "找不到內容", - "missing_indicator.sublabel": "無法找到內容", - "mobile.also_available": "Available in:", - "morefollows.followers_label": "…and {count} more {count, plural, one {follower} other {followers}} on remote sites.", - "morefollows.following_label": "…and {count} more {count, plural, one {follow} other {follows}} on remote sites.", - "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?", - "navigation.chats": "Chats", - "navigation.compose": "Compose", - "navigation.dashboard": "Dashboard", - "navigation.developers": "Developers", - "navigation.direct_messages": "Messages", - "navigation.home": "Home", - "navigation.invites": "Invites", - "navigation.notifications": "Notifications", - "navigation.search": "Search", - "navigation_bar.account_migration": "Move account", - "navigation_bar.blocks": "被你封鎖的用戶", - "navigation_bar.compose": "Compose new post", - "navigation_bar.compose_direct": "Direct message", - "navigation_bar.compose_quote": "Quote post", - "navigation_bar.compose_reply": "Reply to post", - "navigation_bar.domain_blocks": "隱藏的服務站", - "navigation_bar.favourites": "最愛的內容", - "navigation_bar.filters": "Muted words", - "navigation_bar.follow_requests": "關注請求", - "navigation_bar.import_data": "Import data", - "navigation_bar.in_reply_to": "In reply to", - "navigation_bar.invites": "Invites", + "login.fields.otp_code_hint": "輸入兩步驟驗證應用程式裏的代碼,或者輸入恢復代碼", + "login.fields.otp_code_label": "兩步驟驗證代碼:", + "login.fields.password_placeholder": "密碼", + "login.fields.username_label": "電郵地址或帳户名稱", + "login.log_in": "登入", + "login.otp_log_in": "兩步驟驗證登入", + "login.otp_log_in.fail": "兩步驟驗證代號無效,請重新輸入", + "login.reset_password_hint": "登入遇到了問題?", + "login.sign_in": "登入", + "login_form.header": "登入", + "media_gallery.toggle_visible": "切換可見性", + "media_panel.empty_message": "未找到媒體", + "media_panel.title": "媒體", + "mfa.confirm.success_message": "多重要素驗證 (MFA) 已啟用", + "mfa.disable.success_message": "多重要素驗證 (MFA) 已停用", + "mfa.mfa_disable_enter_password": "輸入當前密碼以停用兩步驟驗證:", + "mfa.mfa_setup.code_hint": "輸入兩步驟驗證應用程式裏的代碼", + "mfa.mfa_setup.code_placeholder": "代碼", + "mfa.mfa_setup.password_hint": "輸入當前密碼以確認你的身份", + "mfa.mfa_setup.password_placeholder": "密碼", + "mfa.mfa_setup_scan_description": "請使用 Google Authenticator 或其他應用程式來掃描 QR 碼。啟用兩步驟驗證後,在登入時你需要提供該應用程式生成的代碼", + "mfa.mfa_setup_scan_title": "如果你無法掃描 QR 碼,請手動輸入下列文本:", + "mfa.mfa_setup_verify_title": "啟用", + "mfa.otp_enabled_description": "您已成功啟用兩步驟驗證", + "mfa.otp_enabled_title": "兩步驟驗證已啟用", + "mfa.setup_recoverycodes": "恢復代碼", + "mfa.setup_warning": "請將你的恢復代碼記在安全的地方或寫在紙上,建議與個人資訊分開存放,以便在丟失兩步驟驗證應用程式時可以恢復你的帳户。", + "migration.fields.acct.label": "新帳户的帳户名稱", + "migration.fields.acct.placeholder": "帳户名稱@網域", + "migration.fields.confirm_password.label": "當前密碼", + "migration.hint": "你的追蹤者將被轉移到新帳户,除此之外其他資料將不會被轉移。要執行遷移,您需要先{link}你的新帳户", + "migration.hint.link": "新建一個帳户別名", + "migration.move_account.fail": "帳户遷移失敗。", + "migration.move_account.success": "帳户遷移成功了!", + "migration.submit": "遷移關注者", + "missing_description_modal.cancel": "取消", + "missing_description_modal.continue": "發佈", + "missing_description_modal.description": "仍然繼續發佈嗎?", + "missing_description_modal.text": "附件沒有提供描述。仍然繼續發佈嗎?", + "missing_indicator.label": "找不到", + "missing_indicator.sublabel": "找不到此資源", + "mobile.also_available": "在這裏可用:", + "moderation_overlay.contact": "聯絡", + "moderation_overlay.hide": "隱藏內容", + "moderation_overlay.show": "顯示內容", + "moderation_overlay.subtitle": "此帖文已發送至站務以供審核,目前只允許本人查看。如你認為該消息有誤,請聯絡站務", + "moderation_overlay.title": "內容正被審核中", + "morefollows.followers_label": "和{count}來自其他站點的{count, plural, one {追蹤者} other {追蹤者}} 。", + "morefollows.following_label": "和{count}來自其他站點的{count, plural, one {正在追蹤} other {正在追蹤}} 。", + "mute_modal.hide_notifications": "隱藏來自這個帳户的通知?", + "navbar.login.action": "登入", + "navbar.login.forgot_password": "忘記密碼?", + "navbar.login.password.label": "密碼", + "navbar.login.username.placeholder": "郵箱地址或帳户名稱", + "navigation.chats": "聊天", + "navigation.compose": "發佈新帖文", + "navigation.dashboard": "控制台", + "navigation.developers": "開發者", + "navigation.direct_messages": "私人訊息", + "navigation.home": "首頁", + "navigation.invites": "邀請", + "navigation.notifications": "通知", + "navigation.search": "搜尋", + "navigation_bar.account_aliases": "帳户別名", + "navigation_bar.account_migration": "帳户遷移", + "navigation_bar.blocks": "封鎖使用者", + "navigation_bar.compose": "撰寫新帖文", + "navigation_bar.compose_direct": "發送私人訊息", + "navigation_bar.compose_edit": "編輯帖文", + "navigation_bar.compose_quote": "引用帖文", + "navigation_bar.compose_reply": "回覆帖文", + "navigation_bar.domain_blocks": "隱藏的網域", + "navigation_bar.favourites": "收藏", + "navigation_bar.filters": "隱藏", + "navigation_bar.follow_requests": "追蹤請求", + "navigation_bar.import_data": "匯入資料", + "navigation_bar.in_reply_to": "回覆", + "navigation_bar.invites": "邀請", "navigation_bar.logout": "登出", - "navigation_bar.mutes": "被你靜音的用戶", + "navigation_bar.mutes": "隱藏的使用者", "navigation_bar.preferences": "偏好設定", - "navigation_bar.profile_directory": "Profile directory", - "navigation_bar.security": "安全", - "navigation_bar.soapbox_config": "Soapbox config", - "notification.birthday": "{name} has a birthday today", - "notification.birthday.more": "{count} more {count, plural, one {friend} other {friends}}", - "notification.birthday_plural": "{name} and {more} have birthday today", - "notification.pleroma:chat_mention": "{name} sent you a message", - "notification.favourite": "{name} 收藏了你的文章", - "notification.follow": "{name} 開始關注你", - "notification.follow_request": "{name} has requested to follow you", - "notification.mention": "{name} 提及你", - "notification.move": "{name} moved to {targetName}", - "notification.pleroma:emoji_reaction": "{name} reacted to your post", - "notification.poll": "A poll you have voted in has ended", - "notification.reblog": "{name} 轉推你的文章", - "notification.status": "{name} just posted", - "notifications.clear": "清空通知紀錄", - "notifications.clear_confirmation": "你確定要清空通知紀錄嗎?", - "notifications.clear_heading": "Clear notifications", - "notifications.column_settings.alert": "顯示桌面通知", - "notifications.column_settings.birthdays.category": "Birthdays", - "notifications.column_settings.birthdays.show": "Show birthday reminders", - "notifications.column_settings.emoji_react": "Emoji reacts:", - "notifications.column_settings.favourite": "收藏了你的文章:", - "notifications.column_settings.filter_bar.advanced": "Display all categories", - "notifications.column_settings.filter_bar.category": "Quick filter bar", - "notifications.column_settings.filter_bar.show": "Show", - "notifications.column_settings.follow": "關注你:", - "notifications.column_settings.follow_request": "New follow requests:", - "notifications.column_settings.mention": "提及你:", - "notifications.column_settings.move": "Moves:", - "notifications.column_settings.poll": "Poll results:", - "notifications.column_settings.push": "推送通知", - "notifications.column_settings.reblog": "轉推你的文章:", - "notifications.column_settings.show": "在通知欄顯示", - "notifications.column_settings.sound": "播放音效", - "notifications.column_settings.sounds": "Sounds", - "notifications.column_settings.sounds.all_sounds": "Play sound for all notifications", - "notifications.column_settings.title": "Notification settings", - "notifications.filter.all": "All", - "notifications.filter.boosts": "Reposts", - "notifications.filter.emoji_reacts": "Emoji reacts", - "notifications.filter.favourites": "Favorites", - "notifications.filter.follows": "Follows", - "notifications.filter.mentions": "Mentions", - "notifications.filter.moves": "Moves", - "notifications.filter.polls": "Poll results", - "notifications.filter.statuses": "Updates from people you follow", + "navigation_bar.profile_directory": "發現更多帳户", + "navigation_bar.soapbox_config": "Soapbox配置", + "notification.favourite": "{name} 讚了你的帖文", + "notification.follow": "{name} 追蹤了你", + "notification.follow_request": "{name} 請求追蹤你", + "notification.mention": "{name} 提到了你", + "notification.mentioned": "{name} 提到了你", + "notification.move": "{name} 移動到了 {targetName}", + "notification.others": " + {count} 其他通知", + "notification.pleroma:chat_mention": "{name} 給你發送了訊息", + "notification.pleroma:emoji_reaction": "{name} 用表情回應了你的帖文", + "notification.poll": "你參與的一項投票已經結束", + "notification.reblog": "{name} 轉帖了你的帖文", + "notification.status": "{name} 剛剛發帖", + "notification.update": "{name} 編輯了你參與互動的帖文", + "notification.user_approved": "歡迎來到 {instance}!", + "notifications.filter.all": "全部", + "notifications.filter.boosts": "轉帖", + "notifications.filter.emoji_reacts": "Emoji心情回應", + "notifications.filter.favourites": "最愛", + "notifications.filter.follows": "追蹤的使用者", + "notifications.filter.mentions": "提及", + "notifications.filter.moves": "移動", + "notifications.filter.polls": "投票結果", + "notifications.filter.statuses": "來自追蹤的人的更新", "notifications.group": "{count} 條通知", - "notifications.queue_label": "Click to see {count} new {count, plural, one {notification} other {notifications}}", - "onboarding.avatar.subtitle": "Just have fun with it.", - "onboarding.avatar.title": "Choose a profile picture", - "onboarding.display_name.subtitle": "You can always edit this later.", - "onboarding.display_name.title": "Choose a display name", - "onboarding.done": "Done", - "onboarding.finished.message": "We are very excited to welcome you to our community! Tap the button below to get started.", - "onboarding.finished.title": "Onboarding complete", - "onboarding.header.subtitle": "This will be shown at the top of your profile.", - "onboarding.header.title": "Pick a cover image", - "onboarding.next": "Next", - "onboarding.note.subtitle": "You can always edit this later.", - "onboarding.note.title": "Write a short bio", - "onboarding.saving": "Saving…", - "onboarding.skip": "Skip for now", - "onboarding.suggestions.subtitle": "Here are a few of the most popular accounts you might like.", - "onboarding.suggestions.title": "Suggested accounts", - "onboarding.view_feed": "View Feed", - "password_reset.confirmation": "Check your email for confirmation.", - "password_reset.fields.username_placeholder": "Email or username", - "password_reset.reset": "Reset password", - "patron.donate": "Donate", - "patron.title": "Funding Goal", - "pinned_accounts.title": "{name}’s choices", - "pinned_statuses.none": "No pins to show.", - "poll.closed": "Closed", - "poll.refresh": "Refresh", - "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", - "poll.vote": "Vote", - "poll.voted": "You voted for this answer", - "poll.votes": "{votes, plural, one {# vote} other {# votes}}", - "poll_button.add_poll": "Add a poll", - "poll_button.remove_poll": "Remove poll", - "pre_header.close": "Close", - "preferences.fields.auto_play_gif_label": "Auto-play animated GIFs", - "preferences.fields.autoload_more_label": "Automatically load more items when scrolled to the bottom of the page", - "preferences.fields.autoload_timelines_label": "Automatically load new posts when scrolled to the top of the page", - "preferences.fields.boost_modal_label": "Show confirmation dialog before reposting", - "preferences.fields.delete_modal_label": "Show confirmation dialog before deleting a post", - "preferences.fields.display_media.default": "Hide media marked as sensitive", - "preferences.fields.display_media.hide_all": "Always hide media", - "preferences.fields.display_media.show_all": "Always show media", - "preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings", - "preferences.fields.language_label": "Language", - "preferences.fields.media_display_label": "Media display", - "preferences.hints.feed": "In your home feed", - "privacy.change": "調整私隱設定", - "privacy.direct.long": "只有提及的用戶能看到", - "privacy.direct.short": "私人訊息", - "privacy.private.long": "只有關注你用戶能看到", - "privacy.private.short": "關注者", - "privacy.public.long": "在公共時間軸顯示", - "privacy.public.short": "公共", - "privacy.unlisted.long": "公開,但不在公共時間軸顯示", - "privacy.unlisted.short": "公開", - "profile_dropdown.add_account": "Add an existing account", - "profile_dropdown.logout": "Log out @{acct}", - "profile_fields_panel.title": "Profile fields", - "public.column_settings.title": "Fediverse timeline settings", - "reactions.all": "All", - "regeneration_indicator.label": "載入中……", + "notifications.queue_label": "點擊查看 {count} 條新通知", + "oauth_consumer.tooltip": "通過 {provider} 登入", + "oauth_consumers.title": "更多登入方式", + "onboarding.avatar.subtitle": "祝你玩得開心!", + "onboarding.avatar.title": "設定你的頭像", + "onboarding.display_name.subtitle": "你可以稍後更改", + "onboarding.display_name.title": "設定你的顯示名稱", + "onboarding.done": "完成", + "onboarding.finished.message": "我們很高興歡迎您加入我們的社區! 點擊下面的按鈕,讓我們開始吧!", + "onboarding.finished.title": "新手教程完成", + "onboarding.header.subtitle": "這將顯示在你個人資料的上方。", + "onboarding.header.title": "選擇封面圖片", + "onboarding.next": "下一步", + "onboarding.note.subtitle": "你可以稍後更改", + "onboarding.note.title": "簡短地介紹下自己", + "onboarding.saving": "儲存中…", + "onboarding.skip": "現在跳過", + "onboarding.suggestions.subtitle": "以下是幾個受歡迎的帳户,你可能會喜歡", + "onboarding.suggestions.title": "推薦帳户", + "onboarding.view_feed": "檢視列表", + "password_reset.confirmation": "請查閲確認電郵", + "password_reset.fields.username_placeholder": "電郵地址或帳户名稱", + "password_reset.reset": "重設密碼", + "patron.donate": "捐贈", + "patron.title": "籌款目標", + "pinned_accounts.title": "{name} 的釘選", + "pinned_statuses.none": "沒有釘選的帖文", + "poll.choose_multiple": "盡情選擇你所感興趣的", + "poll.closed": "已關閉", + "poll.non_anonymous": "公共投票", + "poll.non_anonymous.label": "其他的實例可能可以見到你投票的選擇", + "poll.refresh": "重新整理", + "poll.total_people": "還有 {count} 人", + "poll.total_votes": "投票", + "poll.vote": "投票", + "poll.voted": "你投票給了這個選項", + "poll.votes": "投票", + "poll_button.add_poll": "發起投票", + "poll_button.remove_poll": "移除投票", + "preferences.fields.auto_play_gif_label": "自動播放GIF", + "preferences.fields.autoload_more_label": "滾動至時間軸底部自動載入更多帖文", + "preferences.fields.autoload_timelines_label": "滾動至時間軸頂部自動載入更多新帖", + "preferences.fields.boost_modal_label": "轉帖前顯示確認提示", + "preferences.fields.delete_modal_label": "刪除帖文前顯示確認提示", + "preferences.fields.display_media.default": "隱藏被標記為敏感內容的媒體", + "preferences.fields.display_media.hide_all": "始終隱藏所有媒體", + "preferences.fields.display_media.show_all": "始終顯示所有媒體", + "preferences.fields.expand_spoilers_label": "始終展開標有內容警告的帖文", + "preferences.fields.language_label": "語言", + "preferences.fields.media_display_label": "媒體顯示", + "preferences.fields.theme": "主題", + "preferences.hints.feed": "在你的主頁信息流中", + "privacy.change": "調整隱私狀態", + "privacy.direct.long": "只有被提到的使用者能看到", + "privacy.direct.short": "僅被提及的使用者", + "privacy.private.long": "只有追蹤你的使用者能看到", + "privacy.private.short": "僅追蹤者", + "privacy.public.long": "所有人可見,出現在公共時間軸上", + "privacy.public.short": "公共時間軸", + "privacy.unlisted.long": "公開,但不會顯示在公共時間軸", + "privacy.unlisted.short": "所有人", + "profile_dropdown.add_account": "新增已有帳户", + "profile_dropdown.logout": "登出 @{acct}", + "profile_dropdown.theme": "主題", + "profile_fields_panel.title": "個人資料字段", + "public.column_settings.title": "聯邦宇宙工具時間軸設定", + "reactions.all": "全部", + "regeneration_indicator.label": "載入中…", "regeneration_indicator.sublabel": "你的主頁時間軸正在準備中!", - "register_invite.lead": "Complete the form below to create an account.", - "register_invite.title": "You've been invited to join {siteTitle}!", - "registration.agreement": "I agree to the {tos}.", - "registration.captcha.hint": "Click the image to get a new captcha", - "registration.closed_message": "{instance} is not accepting new members", - "registration.closed_title": "Registrations Closed", - "registration.confirmation_modal.close": "Close", - "registration.fields.confirm_placeholder": "Password (again)", - "registration.fields.email_placeholder": "E-Mail address", - "registration.fields.password_placeholder": "Password", - "registration.fields.username_hint": "Only letters, numbers, and underscores are allowed.", - "registration.fields.username_placeholder": "Username", - "registration.newsletter": "Subscribe to newsletter.", - "registration.password_mismatch": "Passwords don't match.", - "registration.reason": "Why do you want to join?", - "registration.reason_hint": "This will help us review your application", - "registration.sign_up": "Sign up", - "registration.tos": "Terms of Service", - "registration.privacy": "Privacy Policy", - "registration.acceptance": "By registering, you agree to the {terms} and {privacy}.", - "registration.username_unavailable": "Username is already taken.", - "relative_time.days": "{number}日", + "register_invite.lead": "填寫下列表單以註冊帳户", + "register_invite.title": "你已被邀請加入 {siteTitle}!", + "registration.acceptance": "註冊同時意味着你已經同意本站的 {terms} 和 {privacy}.", + "registration.agreement": "我同意本站用户條款 {tos}.", + "registration.captcha.hint": "點擊圖像以重載驗證碼", + "registration.captcha.placeholder": "輸入照片中顯示的文字", + "registration.closed_message": "{instance} 公開註冊暫時關閉", + "registration.closed_title": "暫停註冊", + "registration.confirmation_modal.close": "關閉", + "registration.fields.confirm_placeholder": "再次輸入密碼", + "registration.fields.email_placeholder": "電郵地址", + "registration.fields.password_placeholder": "密碼", + "registration.fields.username_hint": "只能使用英文字母、數字和下劃線", + "registration.fields.username_placeholder": "帳户名稱", + "registration.header": "創建你的帳户", + "registration.newsletter": "訂閲我們的新聞郵件", + "registration.password_mismatch": "密碼不匹配", + "registration.privacy": "隱私條款", + "registration.reason": "你為什麼想要註冊本站?", + "registration.reason_hint": "認真填寫將能加快你通過註冊的速度", + "registration.sign_up": "註冊", + "registration.tos": "用户條款", + "registration.username_unavailable": "帳户名稱已被使用", + "registration.validation.capital_letter": "1 個大寫字母", + "registration.validation.lowercase_letter": "1 個小寫字母", + "registration.validation.minimum_characters": "8 個字符", + "registrations.create_account": "創建新帳户", + "registrations.error": "創建你的帳户失敗,請聯絡管理員.", + "registrations.get_started": "讓我們開始吧!", + "registrations.success": "歡迎來到 {siteTitle}!", + "registrations.tagline": "一個沒有言論審查的社交平台", + "registrations.unprocessable_entity": "此用户名已被佔用", + "registrations.username.hint": "只能包含數字、字母和下劃線", + "relative_time.days": "{number}天", "relative_time.hours": "{number}小時", "relative_time.just_now": "剛剛", - "relative_time.minutes": "{number}分鐘", + "relative_time.minutes": "{number}分", "relative_time.seconds": "{number}秒", - "remote_instance.edit_federation": "Edit federation", - "remote_instance.federation_panel.heading": "Federation Restrictions", - "remote_instance.federation_panel.no_restrictions_message": "{siteTitle} has placed no restrictions on {host}.", - "remote_instance.federation_panel.restricted_message": "{siteTitle} blocks all activities from {host}.", - "remote_instance.federation_panel.some_restrictions_message": "{siteTitle} has placed some restrictions on {host}.", - "remote_instance.pin_host": "Pin {host}", - "remote_instance.unpin_host": "Unpin {host}", - "remote_interaction.account_placeholder": "Enter your username@domain you want to act from", - "remote_interaction.divider": "or", - "remote_interaction.favourite": "Proceed to like", - "remote_interaction.favourite_title": "Like a post remotely", - "remote_interaction.follow": "Proceed to follow", - "remote_interaction.follow_title": "Follow {user} remotely", - "remote_interaction.poll_vote": "Proceed to vote", - "remote_interaction.poll_vote_title": "Vote in a poll remotely", - "remote_interaction.reblog": "Proceed to repost", - "remote_interaction.reblog_title": "Reblog a post remotely", - "remote_interaction.reply": "Proceed to reply", - "remote_interaction.reply_title": "Reply to a post remotely", - "remote_interaction.user_not_found_error": "Couldn't find given user", - "remote_timeline.filter_message": "You are viewing the timeline of {instance}.", + "remote_instance.edit_federation": "編輯聯邦設定", + "remote_instance.federation_panel.heading": "聯邦限制", + "remote_instance.federation_panel.no_restrictions_message": "{siteTitle} 未對 {host} 採取限制措施", + "remote_instance.federation_panel.restricted_message": "{siteTitle} 完全封鎖了 {host}", + "remote_instance.federation_panel.some_restrictions_message": "{siteTitle} 對 {host} 實施了部分限制", + "remote_instance.pin_host": "釘選 {host}", + "remote_instance.unpin_host": "取消釘選 {host}", + "remote_interaction.account_placeholder": "輸入您想採取行動的帳户 (格式:帳户名@網域)", + "remote_interaction.divider": "或", + "remote_interaction.favourite": "按讚", + "remote_interaction.favourite_title": "遠端按讚一條帖文", + "remote_interaction.follow": "開始追蹤", + "remote_interaction.follow_title": "遠端追蹤 {user}", + "remote_interaction.poll_vote": "投票", + "remote_interaction.poll_vote_title": "遠端參與投票", + "remote_interaction.reblog": "轉帖", + "remote_interaction.reblog_title": "遠端轉帖", + "remote_interaction.reply": "回覆", + "remote_interaction.reply_title": "遠端回覆", + "remote_interaction.user_not_found_error": "找不到該帳户", + "remote_timeline.filter_message": "你正在查看 {instance} 的時間軸", "reply_indicator.cancel": "取消", - "reply_mentions.account.add": "Add to mentions", - "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.reply_empty": "Replying to post", - "report.block": "Block {target}", - "report.block_hint": "Do you also want to block this account?", + "reply_mentions.account.add": "添加到提及名單", + "reply_mentions.account.remove": "從提及名單中移除", + "reply_mentions.more": "添加 {count} 個", + "reply_mentions.reply": "回覆 {accounts}{more}", + "reply_mentions.reply_empty": "回覆帖文", + "report.block": "封鎖帳户 {target}", + "report.block_hint": "你是否要封鎖這個帳户呢?", + "report.confirmation.content": "如果我們發現此帳户確實違反了 {link} ,我們會採取進一步的措施", + "report.confirmation.title": "感謝你提交的檢舉", + "report.done": "完成", "report.forward": "轉寄到 {target}", - "report.forward_hint": "這個帳戶屬於其他服務站。要向該服務站發送匿名的舉報訊息嗎?", - "report.hint": "這訊息會發送到你服務站的管理員。你可以提供舉報這個帳戶的理由:", - "report.placeholder": "額外訊息", - "report.submit": "提交", - "report.target": "舉報", - "reset_password.header": "Set New Password", - "schedule.post_time": "Post Date/Time", - "schedule.remove": "Remove schedule", - "schedule_button.add_schedule": "Schedule post for later", - "schedule_button.remove_schedule": "Post immediately", - "scheduled_status.cancel": "Cancel", - "search.action": "Search for “{query}”", + "report.forward_hint": "這個帳户屬於其他站點。要像該站點發送匿名的檢舉訊息嗎?", + "report.hint": "這項訊息會發送到您伺服器的管理員。你可以提供檢舉這個帳户的理由:", + "report.next": "下一步", + "report.otherActions.addAdditional": "你還想為你的檢舉添加更多狀態嗎?", + "report.otherActions.addMore": "添加更多", + "report.otherActions.furtherActions": "進一步措施:", + "report.otherActions.hideAdditional": "隱藏額外狀態", + "report.otherActions.otherStatuses": "包含其他狀態?", + "report.placeholder": "更多訊息", + "report.reason.blankslate": "你已移除選中的所有狀態", + "report.reason.title": "檢舉原因", + "report.submit": "送出", + "report.target": "檢舉 {target}", + "reset_password.fail": "令牌已過期,請重試", + "reset_password.header": "設定新的密碼", + "schedule.post_time": "發佈時間", + "schedule.remove": "取消發佈", + "schedule_button.add_schedule": "定時發佈", + "schedule_button.remove_schedule": "取消定時發佈", + "scheduled_status.cancel": "取消", + "search.action": "搜尋 “{query}”", "search.placeholder": "搜尋", "search_results.accounts": "使用者", - "search_results.hashtags": "標籤", - "search_results.statuses": "文章", - "search_results.top": "Top", - "security.codes.fail": "Failed to fetch backup codes", - "security.confirm.fail": "Incorrect code or password. Try again.", - "security.delete_account.fail": "Account deletion failed.", - "security.delete_account.success": "Account successfully deleted.", - "security.disable.fail": "Incorrect password. Try again.", - "security.disable_mfa": "Disable", - "security.fields.email.label": "Email address", - "security.fields.new_password.label": "New password", - "security.fields.old_password.label": "Current password", - "security.fields.password.label": "Password", - "security.fields.password_confirmation.label": "New password (again)", - "security.headers.delete": "Delete Account", - "security.headers.tokens": "Sessions", - "security.headers.update_email": "Change Email", - "security.headers.update_password": "Change Password", - "security.mfa": "Set up 2-Factor Auth", - "security.mfa_enabled": "You have multi-factor authentication set up with OTP.", - "security.mfa_header": "Authorization Methods", - "security.mfa_setup_hint": "Configure multi-factor authentication with OTP", - "security.qr.fail": "Failed to fetch setup key", - "security.submit": "Save changes", - "security.submit.delete": "Delete Account", - "security.text.delete": "To delete your account, enter your password then click Delete Account. This is a permanent action that cannot be undone. Your account will be destroyed from this server, and a deletion request will be sent to other servers. It's not guaranteed that all servers will purge your account.", - "security.tokens.revoke": "Revoke", - "security.update_email.fail": "Update email failed.", - "security.update_email.success": "Email successfully updated.", - "security.update_password.fail": "Update password failed.", - "security.update_password.success": "Password successfully updated.", - "settings.change_email": "Change Email", - "settings.change_password": "Change Password", - "settings.configure_mfa": "Configure MFA", - "settings.delete_account": "Delete Account", - "settings.edit_profile": "Edit Profile", - "settings.preferences": "Preferences", - "settings.profile": "Profile", - "settings.save.success": "Your preferences have been saved!", - "settings.security": "Security", - "settings.settings": "Settings", - "signup_panel.subtitle": "Sign up now to discuss what's happening.", - "signup_panel.title": "New to {site_title}?", - "snackbar.view": "View", - "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", - "soapbox_config.authenticated_profile_label": "Profiles require authentication", - "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer", - "soapbox_config.crypto_address.meta_fields.address_placeholder": "Address", - "soapbox_config.crypto_address.meta_fields.note_placeholder": "Note (optional)", - "soapbox_config.crypto_address.meta_fields.ticker_placeholder": "Ticker", - "soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "Number of items to display in the crypto homepage widget", + "search_results.hashtags": "主題標籤", + "search_results.statuses": "帖文", + "security.codes.fail": "恢復代碼錯誤", + "security.confirm.fail": "密碼錯誤,請重試。", + "security.delete_account.fail": "帳户刪除失敗", + "security.delete_account.success": "帳户刪除成功", + "security.disable.fail": "密碼錯誤,請重試。", + "security.fields.email.label": "電郵地址", + "security.fields.new_password.label": "輸入新密碼", + "security.fields.old_password.label": "輸入原密碼", + "security.fields.password.label": "密碼", + "security.fields.password_confirmation.label": "再次輸入新密碼", + "security.headers.delete": "刪除帳户", + "security.headers.tokens": "會話", + "security.qr.fail": "加載密鑰失敗", + "security.submit": "儲存變更", + "security.submit.delete": "刪除帳户", + "security.text.delete": "要刪除您的帳户,請輸入您的密碼。注意:這是無法撤消的永久性操作!您的帳户將從該服務器中銷毀,並將向其他服務器發送刪除請求。但你的資訊不一定會在其他站點上立即刪除。", + "security.tokens.revoke": "撤銷", + "security.update_email.fail": "更新電郵地址失敗", + "security.update_email.success": "電郵地址已更新", + "security.update_password.fail": "更新密碼失敗", + "security.update_password.success": "密碼已更新", + "settings.change_email": "更改電郵地址", + "settings.change_password": "更改密碼", + "settings.configure_mfa": "設定多重要素驗證 (MFA)", + "settings.delete_account": "刪除帳户", + "settings.edit_profile": "編輯個人資料", + "settings.preferences": "首選項", + "settings.profile": "個人資料", + "settings.save.success": "你的設定已儲存", + "settings.security": "安全性", + "settings.sessions": "活動會話", + "settings.settings": "設定", + "shared.tos": "服務條款", + "signup_panel.subtitle": "註冊以參與討論", + "signup_panel.title": "初來乍到 {site_title} 嗎?", + "site_preview.preview": "預覽", + "snackbar.view": "檢視", + "soapbox_config.authenticated_profile_hint": "用户必須登錄才能查看用户個人資料上的回覆和媒體。", + "soapbox_config.authenticated_profile_label": "個人資料需要授權才能查看", + "soapbox_config.copyright_footer.meta_fields.label_placeholder": "版權頁底", + "soapbox_config.crypto_address.meta_fields.address_placeholder": "地址", + "soapbox_config.crypto_address.meta_fields.note_placeholder": "備註 (可選)", + "soapbox_config.crypto_address.meta_fields.ticker_placeholder": "幣種", + "soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "在主頁數字貨幣小部件中顯示的數量", "soapbox_config.custom_css.meta_fields.url_placeholder": "URL", - "soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.", - "soapbox_config.fields.accent_color_label": "Accent color", - "soapbox_config.fields.brand_color_label": "Brand color", - "soapbox_config.fields.crypto_address.add": "Add new crypto address", - "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", - "soapbox_config.fields.home_footer.add": "Add new Home Footer Item", - "soapbox_config.fields.home_footer_fields_label": "Home footer items", + "soapbox_config.display_fqn_label": "顯示本站帳户的網域 (如 @帳户名稱@網域) ", + "soapbox_config.fields.accent_color_label": "強調色", + "soapbox_config.fields.brand_color_label": "主題色", + "soapbox_config.fields.crypto_address.add": "添加數字貨幣地址", + "soapbox_config.fields.crypto_addresses_label": "數字貨幣地址", + "soapbox_config.fields.home_footer.add": "添加頁尾", + "soapbox_config.fields.home_footer_fields_label": "主頁頁眉", "soapbox_config.fields.logo_label": "Logo", - "soapbox_config.fields.promo_panel.add": "Add new Promo panel item", - "soapbox_config.fields.promo_panel_fields_label": "Promo panel items", - "soapbox_config.fields.theme_label": "Default theme", - "soapbox_config.greentext_label": "Enable greentext support", - "soapbox_config.hints.crypto_addresses": "Add cryptocurrency addresses so users of your site can donate to you. Order matters, and you must use lowercase ticker values.", - "soapbox_config.hints.home_footer_fields": "You can have custom defined links displayed on the footer of your static pages", - "soapbox_config.hints.logo": "SVG. At most 2 MB. Will be displayed to 50px height, maintaining aspect ratio", - "soapbox_config.hints.promo_panel_fields": "You can have custom defined links displayed on the right panel of the timelines page.", + "soapbox_config.fields.promo_panel.add": "添加時間軸頁底", + "soapbox_config.fields.promo_panel_fields_label": "時間軸頁底", + "soapbox_config.fields.theme_label": "默認主題", + "soapbox_config.greentext_label": "啟用greentext支援", + "soapbox_config.headings.advanced": "進階", + "soapbox_config.headings.cryptocurrency": "數字貨幣", + "soapbox_config.headings.navigation": "導航列", + "soapbox_config.headings.options": "選項", + "soapbox_config.headings.theme": "主題", + "soapbox_config.hints.crypto_addresses": "添加加密貨幣地址,以便您網站的用户可以向您捐款。請注意順序,同時您必須使用小寫的幣種代碼。", + "soapbox_config.hints.home_footer_fields": "您可以在靜態頁面的頁腳顯示自定義鏈接(如about)。", + "soapbox_config.hints.logo": "SVG. 最多 2 MB。 將顯示到 50px 高度,保持縱橫比", + "soapbox_config.hints.promo_panel_fields": "您可以在時間線頁面的右側面板上顯示自定義鏈接。", "soapbox_config.hints.promo_panel_icons": "{ link }", - "soapbox_config.hints.promo_panel_icons.link": "Soapbox Icons List", - "soapbox_config.home_footer.meta_fields.label_placeholder": "Label", + "soapbox_config.hints.promo_panel_icons.link": "Soapbox圖示", + "soapbox_config.home_footer.meta_fields.label_placeholder": "標籤", "soapbox_config.home_footer.meta_fields.url_placeholder": "URL", - "soapbox_config.promo_panel.meta_fields.icon_placeholder": "Icon", - "soapbox_config.promo_panel.meta_fields.label_placeholder": "Label", + "soapbox_config.promo_panel.meta_fields.icon_placeholder": "圖示", + "soapbox_config.promo_panel.meta_fields.label_placeholder": "標籤", "soapbox_config.promo_panel.meta_fields.url_placeholder": "URL", - "soapbox_config.raw_json_hint": "Edit the settings data directly. Changes made directly to the JSON file will override the form fields above. Click Save to apply your changes.", - "soapbox_config.raw_json_label": "Advanced: Edit raw JSON data", - "soapbox_config.save": "Save", - "soapbox_config.saved": "Soapbox config saved!", - "soapbox_config.single_user_mode_hint": "Front page will redirect to a given user profile.", - "soapbox_config.single_user_mode_label": "Single user mode", - "soapbox_config.single_user_mode_profile_hint": "@handle", - "soapbox_config.single_user_mode_profile_label": "Main user handle", - "soapbox_config.verified_can_edit_name_label": "Allow verified users to edit their own display name.", - "status.actions.more": "More", - "status.admin_account": "Open moderation interface for @{name}", - "status.admin_status": "Open this post in the moderation interface", + "soapbox_config.raw_json_hint": "直接編輯設置數據。 直接對 JSON 文件進行的更改將覆蓋上面的表單字段。 單擊保存以應用您的更改。", + "soapbox_config.raw_json_label": "高級: 編輯原始JSON", + "soapbox_config.save": "儲存更改", + "soapbox_config.saved": "Soapbox配置已儲存!", + "soapbox_config.single_user_mode_hint": "首頁將重定向到給定的用户配置文件。", + "soapbox_config.single_user_mode_label": "單用户模式", + "soapbox_config.single_user_mode_profile_hint": "@帳户名稱", + "soapbox_config.single_user_mode_profile_label": "主帳户的帳户名稱", + "soapbox_config.verified_can_edit_name_label": "允許經過驗證的用户編輯自己的顯示名稱。", + "sponsored.info.message": "{siteTitle} 展示廣告以使實例可持續運行", + "sponsored.info.title": "為什麼我會看到這條廣告?", + "sponsored.subtitle": "贊助", + "status.actions.more": "更多", + "status.admin_account": "開啟 @{name} 的管理介面", + "status.admin_status": "在管理介面開啟此帖文", "status.block": "封鎖 @{name}", - "status.bookmark": "Bookmark", - "status.bookmarked": "Bookmark added.", - "status.cancel_reblog_private": "取消轉推", - "status.cannot_reblog": "這篇文章無法被轉推", - "status.chat": "Chat with @{name}", - "status.copy": "Copy link to post", + "status.bookmark": "書籤", + "status.bookmarked": "書籤已添加", + "status.cancel_reblog_private": "取消轉帖", + "status.cannot_reblog": "這篇帖文無法被轉載", + "status.chat": "和 @{name} 聊天", + "status.copy": "將連結複製到帖文中", "status.delete": "刪除", - "status.detailed_status": "Detailed conversation view", - "status.direct": "私訊 @{name}", - "status.embed": "鑲嵌", - "status.favourite": "收藏", - "status.filtered": "Filtered", + "status.detailed_status": "對話的詳細內容", + "status.direct": "發送私訊給 @{name}", + "status.edit": "編輯", + "status.edited": "已於 {date} 編輯", + "status.embed": "嵌入", + "status.favourite": "最愛", + "status.filtered": "已過濾", "status.load_more": "載入更多", "status.media_hidden": "隱藏媒體內容", - "status.mention": "提及 @{name}", + "status.mention": "提到 @{name}", "status.more": "更多", - "status.mute": "把 @{name} 靜音", - "status.mute_conversation": "靜音對話", - "status.open": "展開文章", - "status.pin": "置頂到資料頁", - "status.pinned": "置頂文章", - "status.quote": "Quote post", - "status.reactions.cry": "Sad", - "status.reactions.empty": "No one has reacted to this post yet. When someone does, they will show up here.", - "status.reactions.heart": "Love", - "status.reactions.laughing": "Haha", - "status.reactions.like": "Like", - "status.reactions.open_mouth": "Wow", - "status.reactions.weary": "Weary", - "status.reactions_expand": "Select emoji", - "status.read_more": "Read more", - "status.reblog": "轉推", - "status.reblog_private": "轉推到原讀者", - "status.reblogged_by": "{name} 轉推", - "status.reblogs.empty": "No one has reposted this post yet. When someone does, they will show up here.", - "status.redraft": "刪除並編輯", - "status.remove_account_from_group": "Remove account from group", - "status.remove_post_from_group": "Remove post from group", - "status.reply": "回應", - "status.replyAll": "回應所有人", - "status.report": "舉報 @{name}", + "status.mute": "隱藏 @{name}", + "status.mute_conversation": "隱藏對話", + "status.open": "展開帖文", + "status.pin": "釘選到個人資料頁", + "status.pinned": "釘選的帖文", + "status.quote": "引用帖文", + "status.reactions.cry": "傷心", + "status.reactions.empty": "尚未有人回應心情", + "status.reactions.heart": "愛", + "status.reactions.laughing": "哈哈哈", + "status.reactions.like": "讚哦", + "status.reactions.open_mouth": "哇!", + "status.reactions.weary": "疲憊", + "status.reactions_expand": "選擇心情", + "status.read_more": "閱讀更多", + "status.reblog": "轉帖", + "status.reblog_private": "轉帖給原有追蹤者", + "status.reblogged_by": "{name} 轉帖了", + "status.reblogs.empty": "還沒有人轉帖。如果有,會顯示在這裡。", + "status.redraft": "刪除 & 編輯", + "status.remove_account_from_group": "將帳户移出群組", + "status.remove_post_from_group": "將帖文移出群組", + "status.reply": "回覆", + "status.replyAll": "回覆所有人", + "status.report": "檢舉 @{name}", "status.sensitive_warning": "敏感內容", "status.share": "分享", "status.show_less": "減少顯示", - "status.show_less_all": "減少顯示這類文章", + "status.show_less_all": "減少顯示這類帖文", "status.show_more": "顯示更多", - "status.show_more_all": "顯示更多這類文章", - "status.title": "Post", - "status.title_direct": "Direct message", - "status.unbookmark": "Remove bookmark", - "status.unbookmarked": "Bookmark removed.", - "status.unmute_conversation": "解禁對話", - "status.unpin": "解除置頂", - "status_list.queue_label": "Click to see {count} new {count, plural, one {post} other {posts}}", - "statuses.quote_tombstone": "Post is unavailable.", - "statuses.tombstone": "One or more posts are unavailable.", - "suggestions.dismiss": "Dismiss suggestion", - "tabs_bar.all": "All", - "tabs_bar.chats": "Chats", - "tabs_bar.dashboard": "Dashboard", - "tabs_bar.fediverse": "Fediverse", + "status.show_more_all": "顯示更多這類帖文", + "status.title": "帖文", + "status.title_direct": "私訊", + "status.unbookmark": "移除書籤", + "status.unbookmarked": "書籤已移除", + "status.unmute_conversation": "解除此對話的隱藏", + "status.unpin": "解除釘選", + "status.sensitive_warning.subtitle": "這則貼文可能含有不宜觀看的消息", + "status.sensitive_warning.action": "顯示", + "status_list.queue_label": "點選查看 {count} 個新帖文", + "statuses.quote_tombstone": "帖文不可用", + "statuses.tombstone": "部分帖文不可見", + "streamfield.add": "新增", + "streamfield.remove": "移除", + "suggestions.dismiss": "關閉建議", + "tabs_bar.all": "全部", + "tabs_bar.chats": "對話", + "tabs_bar.dashboard": "控制台", + "tabs_bar.fediverse": "聯邦宇宙", "tabs_bar.home": "主頁", - "tabs_bar.more": "More", + "tabs_bar.more": "更多", "tabs_bar.notifications": "通知", - "tabs_bar.post": "Post", - "tabs_bar.profile": "Profile", + "tabs_bar.profile": "個人資料", "tabs_bar.search": "搜尋", - "tabs_bar.settings": "Settings", - "tabs_bar.theme_toggle_dark": "Switch to dark theme", - "tabs_bar.theme_toggle_light": "Switch to light theme", - "time_remaining.days": "{number, plural, one {# day} other {# days}} left", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", - "time_remaining.moments": "Moments remaining", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", - "trends.count_by_accounts": "{count} 位用戶在討論", - "trends.title": "Trends", - "ui.beforeunload": "如果你現在離開 Soapbox,你的草稿內容將會被丟棄。", - "unauthorized_modal.text": "You need to be logged in to do that.", - "unauthorized_modal.title": "Sign up for {site_title}", - "upload_area.title": "將檔案拖放至此上載", - "upload_button.label": "上載媒體檔案", - "upload_error.image_size_limit": "Image exceeds the current file size limit ({limit})", - "upload_error.limit": "File upload limit exceeded.", - "upload_error.poll": "File upload not allowed with polls.", - "upload_error.video_size_limit": "Video exceeds the current file size limit ({limit})", - "upload_form.description": "為視覺障礙人士添加文字說明", - "upload_form.preview": "Preview", + "tabs_bar.settings": "設定", + "tabs_bar.switch_accounts": "切換帳户", + "tabs_bar.theme_toggle_dark": "切換為深色主題", + "tabs_bar.theme_toggle_light": "切換為淺色主題", + "theme_toggle.dark": "深色", + "theme_toggle.light": "淺色", + "theme_toggle.system": "系統", + "thread_login.login": "登入", + "thread_login.message": "加入 {siteTitle} 以獲取完整資訊", + "thread_login.signup": "註冊", + "thread_login.title": "繼續這個對話", + "time_remaining.days": "剩餘{number, plural, one {# 天數} other {# 天數}}", + "time_remaining.hours": "剩餘{number, plural, one {# 小時} other {# 小時}}", + "time_remaining.minutes": "剩餘{number, plural, one {# 分鐘} other {# 分鐘}}", + "time_remaining.moments": "剩餘時間", + "time_remaining.seconds": "剩餘 {number, plural, one {# 秒} other {# 秒}}", + "trends.count_by_accounts": "{count} 位使用者在討論", + "trends.title": "趨勢", + "trendsPanel.viewAll": "顯示全部", + "ui.beforeunload": "如果離開,你的草稿將會丟失。", + "unauthorized_modal.text": "你需要登入才能繼續", + "unauthorized_modal.title": "註冊 {site_title} 帳户", + "upload_area.title": "拖放來上傳", + "upload_button.label": "上傳媒體檔案 (JPEG, PNG, GIF, WebM, MP4, MOV)", + "upload_error.image_size_limit": "圖片超出當前文件大小限制 ({limit})", + "upload_error.limit": "已達到檔案上傳限制。", + "upload_error.poll": "不允許在投票上傳檔案。", + "upload_error.video_duration_limit": "影片超出當前時長限制 ({limit} 秒)", + "upload_error.video_size_limit": "影片超出當前文件大小限制 ({limit})", + "upload_form.description": "為視障人士增加文字說明", + "upload_form.preview": "預覽", "upload_form.undo": "刪除", - "upload_progress.label": "上載中……", + "upload_progress.label": "上傳中...", "video.close": "關閉影片", - "video.download": "Download file", - "video.exit_fullscreen": "退出全熒幕", + "video.download": "下載", + "video.exit_fullscreen": "退出全螢幕", "video.expand": "展開影片", - "video.fullscreen": "全熒幕", + "video.fullscreen": "全螢幕", "video.hide": "隱藏影片", - "video.mute": "靜音", + "video.mute": "隱藏", "video.pause": "暫停", "video.play": "播放", - "video.unmute": "解除靜音", - "who_to_follow.title": "Who To Follow" -} \ No newline at end of file + "video.unmute": "解除隱藏", + "who_to_follow.title": "推薦追蹤" +} diff --git a/app/soapbox/locales/zh-TW.json b/app/soapbox/locales/zh-TW.json index 488a3259c..c266bec2c 100644 --- a/app/soapbox/locales/zh-TW.json +++ b/app/soapbox/locales/zh-TW.json @@ -1,161 +1,174 @@ { - "about.also_available": "Available in:", - "accordion.collapse": "Collapse", - "accordion.expand": "Expand", + "about.also_available": "其他版本:", + "accordion.collapse": "收起", + "accordion.expand": "展开", "account.add_or_remove_from_list": "從名單中新增或移除", "account.badges.bot": "機器人", - "account.birthday": "Born {date}", - "account.birthday_today": "Birthday is today!", + "account.birthday": "出生於 {date}", + "account.birthday_today": "今天是你的生日!", "account.block": "封鎖 @{name}", "account.block_domain": "隱藏來自 {domain} 的所有內容", "account.blocked": "已封鎖", - "account.chat": "Chat with @{name}", - "account.column_settings.description": "These settings apply to all account timelines.", - "account.column_settings.title": "Acccount timeline settings", - "account.deactivated": "Deactivated", + "account.chat": "和 @{name} 聊天", + "account.deactivated": "帳戶已被停權", "account.direct": "傳私訊給 @{name}", + "account.domain_blocked": "隱藏網域", "account.edit_profile": "編輯個人資料", "account.endorse": "在個人資料推薦對方", - "account.follow": "關注", - "account.followers": "關注者", - "account.followers.empty": "尚沒有人關注這位使用者。", - "account.follows": "正在關注", - "account.follows.empty": "這位使用者尚未關注任何使用者。", - "account.follows_you": "關注了你", + "account.endorse.success": "你正在你的個人資料中展示 @{acct} ", + "account.familiar_followers": "被 {accounts} 追蹤", + "account.familiar_followers.more": "你追蹤了 {count} 位其他用戶", + "account.follow": "追蹤", + "account.followers": "追蹤者", + "account.followers.empty": "尚沒有人追蹤這位使用者。", + "account.follows": "正在追蹤", + "account.follows.empty": "這位使用者尚未追蹤任何使用者。", + "account.follows_you": "追蹤了你", "account.hide_reblogs": "隱藏來自 @{name} 的轉推", - "account.last_status": "Last active", + "account.last_status": "最後活動", "account.link_verified_on": "已在 {date} 檢查此連結的擁有者權限", - "account.locked_info": "這隻帳戶的隱私狀態被設成鎖定。該擁有者會手動審核能關注這隻帳號的人。", - "account.login": "Log in", + "account.locked_info": "這隻帳戶的隱私狀態被設成鎖定。該擁有者會手動審核能追蹤這隻帳號的人。", + "account.login": "登入", "account.media": "媒體", - "account.member_since": "Joined {date}", + "account.member_since": "加入於 {date}", "account.mention": "提及", "account.moved_to": "{name} 已遷移至:", - "account.mute": "靜音 @{name}", - "account.never_active": "Never", - "account.posts": "嘟文", - "account.posts_with_replies": "嘟文與回覆", - "account.profile": "Profile", - "account.register": "Sign up", - "account.remote_follow": "Remote follow", + "account.mute": "隱藏 @{name}", + "account.muted": "已隱藏", + "account.never_active": "從未", + "account.posts": "帖文", + "account.posts_with_replies": "帖文與回覆", + "account.profile": "個人資料", + "account.register": "註冊", + "account.remote_follow": "遠端追蹤", + "account.remove_from_followers": "移除此追蹤者", "account.report": "檢舉 @{name}", - "account.requested": "正在等待核准。按一下取消關注請求", - "account.requested_small": "Awaiting approval", + "account.requested": "正在等待核准。按一下取消追蹤請求", + "account.requested_small": "等待核准", + "account.search": "搜尋關於 @{name} 的內容", + "account.search_self": "搜尋你的帖文", "account.share": "分享 @{name} 的個人資料", - "account.show_reblogs": "顯示來自 @{name} 的嘟文", - "account.subscribe": "Subscribe to notifications from @{name}", - "account.subscribed": "Subscribed", + "account.show_reblogs": "顯示來自 @{name} 的帖文", + "account.subscribe": "訂閲 @{name}", + "account.subscribe.failure": "訂閲此帳戶時發生錯誤", + "account.subscribe.success": "您已訂閲此帳戶", "account.unblock": "取消封鎖 @{name}", "account.unblock_domain": "取消隱藏 {domain}", "account.unendorse": "不再於個人資料頁面推薦對方", - "account.unfollow": "取消關注", - "account.unmute": "取消靜音 @{name}", - "account.unsubscribe": "Unsubscribe to notifications from @{name}", - "account.verified": "Verified Account", - "account.welcome": "Welcome", - "account_gallery.none": "No media to show.", - "account_note.hint": "You can keep notes about this user for yourself (this will not be shared with them):", - "account_note.placeholder": "No comment provided", - "account_note.save": "Save", - "account_note.target": "Note for @{target}", - "account_search.placeholder": "Search for an account", - "account_timeline.column_settings.show_pinned": "Show pinned posts", - "admin.awaiting_approval.approved_message": "{acct} was approved!", - "admin.awaiting_approval.empty_message": "There is nobody waiting for approval. When a new user signs up, you can review them here.", - "admin.awaiting_approval.rejected_message": "{acct} was rejected.", - "admin.dashboard.registration_mode.approval_hint": "Users can sign up, but their account only gets activated when an admin approves it.", - "admin.dashboard.registration_mode.approval_label": "Approval Required", - "admin.dashboard.registration_mode.closed_hint": "Nobody can sign up. You can still invite people.", - "admin.dashboard.registration_mode.closed_label": "Closed", - "admin.dashboard.registration_mode.open_hint": "Anyone can join.", - "admin.dashboard.registration_mode.open_label": "Open", - "admin.dashboard.registration_mode_label": "Registrations", - "admin.dashboard.settings_saved": "Settings saved!", - "admin.dashcounters.domain_count_label": "peers", - "admin.dashcounters.mau_label": "monthly active users", - "admin.dashcounters.retention_label": "user retention", - "admin.dashcounters.status_count_label": "posts", - "admin.dashcounters.user_count_label": "total users", - "admin.dashwidgets.email_list_header": "Email list", - "admin.dashwidgets.software_header": "Software", - "admin.latest_accounts_panel.more": "Click to see {count} {count, plural, one {account} other {accounts}}", - "admin.latest_accounts_panel.title": "Latest Accounts", - "admin.moderation_log.empty_message": "You have not performed any moderation actions yet. When you do, a history will be shown here.", - "admin.reports.actions.close": "Close", - "admin.reports.actions.view_status": "View post", - "admin.reports.empty_message": "There are no open reports. If a user gets reported, they will show up here.", - "admin.reports.report_closed_message": "Report on @{name} was closed", - "admin.reports.report_title": "Report on {acct}", - "admin.statuses.actions.delete_status": "Delete post", - "admin.statuses.actions.mark_status_not_sensitive": "Mark post not sensitive", - "admin.statuses.actions.mark_status_sensitive": "Mark post sensitive", - "admin.statuses.status_deleted_message": "Post by @{acct} was deleted", - "admin.statuses.status_marked_message_not_sensitive": "Post by @{acct} was marked not sensitive", - "admin.statuses.status_marked_message_sensitive": "Post by @{acct} was marked sensitive", - "admin.user_index.empty": "No users found.", - "admin.user_index.search_input_placeholder": "Who are you looking for?", - "admin.users.actions.deactivate_user": "Deactivate @{name}", - "admin.users.actions.delete_user": "Delete @{name}", - "admin.users.actions.demote_to_moderator": "Demote @{name} to a moderator", - "admin.users.actions.demote_to_moderator_message": "@{acct} was demoted to a moderator", - "admin.users.actions.demote_to_user": "Demote @{name} to a regular user", - "admin.users.actions.demote_to_user_message": "@{acct} was demoted to a regular user", - "admin.users.actions.promote_to_admin": "Promote @{name} to an admin", - "admin.users.actions.promote_to_admin_message": "@{acct} was promoted to an admin", - "admin.users.actions.promote_to_moderator": "Promote @{name} to a moderator", - "admin.users.actions.promote_to_moderator_message": "@{acct} was promoted to a moderator", - "admin.users.actions.remove_donor": "Remove @{name} as a donor", - "admin.users.actions.set_donor": "Set @{name} as a donor", - "admin.users.actions.suggest_user": "Suggest @{name}", - "admin.users.actions.unsuggest_user": "Unsuggest @{name}", - "admin.users.actions.unverify_user": "Unverify @{name}", - "admin.users.actions.verify_user": "Verify @{name}", - "admin.users.remove_donor_message": "@{acct} was removed as a donor", - "admin.users.set_donor_message": "@{acct} was set as a donor", - "admin.users.user_deactivated_message": "@{acct} was deactivated", - "admin.users.user_deleted_message": "@{acct} was deleted", - "admin.users.user_suggested_message": "@{acct} was suggested", - "admin.users.user_unsuggested_message": "@{acct} was unsuggested", - "admin.users.user_unverified_message": "@{acct} was unverified", - "admin.users.user_verified_message": "@{acct} was verified", - "admin_nav.awaiting_approval": "Awaiting Approval", - "admin_nav.dashboard": "Dashboard", - "admin_nav.reports": "Reports", - "alert.unexpected.body": "We're sorry for the interruption. If the problem persists, please reach out to our support team. You may also try to {clearCookies} (this will log you out).", - "alert.unexpected.browser": "Browser", - "alert.unexpected.clear_cookies": "clear cookies and browser data", - "alert.unexpected.links.help": "Help Center", - "alert.unexpected.links.status": "Status", - "alert.unexpected.links.support": "Support", + "account.unendorse.success": "您已不再展示 @{acct}", + "account.unfollow": "取消追蹤", + "account.unmute": "取消隱藏 @{name}", + "account.unsubscribe": "取消訂閲 @{name}", + "account.unsubscribe.failure": "取消訂閲此帳戶時發生錯誤", + "account.unsubscribe.success": "你已取消訂閲此帳戶", + "account.verified": "已認證的帳戶", + "account_gallery.none": "沒有可顯示的媒體", + "account_note.hint": "你可以為自己保留關於這個用戶的筆記(這不會和他們分享):", + "account_note.placeholder": "沒有評論", + "account_note.save": "儲存", + "account_note.target": "@{target} 的筆記", + "account_search.placeholder": "搜尋使用者", + "actualStatus.edited": "在 {date} 時編輯", + "actualStatuses.quote_tombstone": "帖文不可用", + "admin.awaiting_approval.approved_message": "{acct} 已經通過審核!", + "admin.awaiting_approval.empty_message": "沒有新使用者等待審核,如果有新的申請,它就會顯示在這裏。", + "admin.awaiting_approval.rejected_message": "{acct} 的註冊請求被拒絕!", + "admin.dashboard.registration_mode.approval_hint": "在管理員同意註冊申請後才可加入。", + "admin.dashboard.registration_mode.approval_label": "需要審核", + "admin.dashboard.registration_mode.closed_hint": "公開註冊已經關閉,但已註冊用戶仍然可以邀請其他人加入。", + "admin.dashboard.registration_mode.closed_label": "私密", + "admin.dashboard.registration_mode.open_hint": "任何人都可以加入。", + "admin.dashboard.registration_mode.open_label": "公開", + "admin.dashboard.registration_mode_label": "註冊模式", + "admin.dashboard.settings_saved": "設定已儲存!", + "admin.dashcounters.domain_count_label": "互聯站點", + "admin.dashcounters.mau_label": "月度活躍使用者", + "admin.dashcounters.retention_label": "使用者留存", + "admin.dashcounters.status_count_label": "總帖數", + "admin.dashcounters.user_count_label": "使用者總數", + "admin.dashwidgets.email_list_header": "電郵列表", + "admin.dashwidgets.software_header": "軟體版本", + "admin.latest_accounts_panel.expand_message": "點擊展開更多帳戶", + "admin.latest_accounts_panel.more": "點擊展開 {count} 個帳戶", + "admin.latest_accounts_panel.title": "最近加入的帳戶", + "admin.moderation_log.empty_message": "你還沒有進行任何管理,如果有任何操作,操作歷史就會顯示在這裏。", + "admin.reports.actions.close": "關閉檢舉", + "admin.reports.actions.view_status": "查看帖文", + "admin.reports.empty_message": "沒有未處理的檢舉,如果有任何檢舉,它就會顯示在這裏。", + "admin.reports.report_closed_message": "對 @{name} 的檢舉已關閉", + "admin.reports.report_title": "檢舉 {acct} 的帖文", + "admin.statuses.actions.delete_status": "刪除帖文", + "admin.statuses.actions.mark_status_not_sensitive": "不再標記為敏感內容", + "admin.statuses.actions.mark_status_sensitive": "標記為敏感內容", + "admin.statuses.status_deleted_message": "@{acct} 的帖文已被刪除", + "admin.statuses.status_marked_message_not_sensitive": "@{acct} 的帖文不會被標記為敏感內容", + "admin.statuses.status_marked_message_sensitive": "@{acct} 的帖文被標記為敏感內容", + "admin.user_index.empty": "找不到用戶。", + "admin.user_index.search_input_placeholder": "你正在找誰?", + "admin.users.actions.deactivate_user": "禁用帳戶 @{name}", + "admin.users.actions.delete_user": "刪除帳戶 @{name}", + "admin.users.actions.demote_to_moderator": "降級 @{name} 為站務", + "admin.users.actions.demote_to_moderator_message": "@{acct} 被降級為站務", + "admin.users.actions.demote_to_user": "降級 @{name} 為普通用戶", + "admin.users.actions.demote_to_user_message": "@{acct} 被降級為普通用戶", + "admin.users.actions.promote_to_admin": "升級 @{name} 為管理員", + "admin.users.actions.promote_to_admin_message": "@{acct} 被升級為管理員", + "admin.users.actions.promote_to_moderator": "升級 @{name} 為站務", + "admin.users.actions.promote_to_moderator_message": "@{acct} 被升級為站務", + "admin.users.actions.remove_donor": "移除 @{name} 的捐贈者頭銜", + "admin.users.actions.set_donor": "將 @{name} 設為捐贈者", + "admin.users.actions.suggest_user": "推薦 @{name}", + "admin.users.actions.unsuggest_user": "取消推薦 @{name}", + "admin.users.actions.unverify_user": "撤銷認證 @{name}", + "admin.users.actions.verify_user": "認證帳戶 @{name}", + "admin.users.remove_donor_message": "@{acct} 被移除出捐贈者名單", + "admin.users.set_donor_message": "@{acct} 被設為捐贈者", + "admin.users.user_deactivated_message": "@{acct} 被停權", + "admin.users.user_deleted_message": "@{acct} 被刪除了", + "admin.users.user_suggested_message": "@{acct} 被推薦", + "admin.users.user_unsuggested_message": "@{acct} 被取消推薦", + "admin.users.user_unverified_message": "@{acct} 未認證", + "admin.users.user_verified_message": "@{acct} 被認證", + "admin_nav.awaiting_approval": "等待核准", + "admin_nav.dashboard": "控制台", + "admin_nav.reports": "檢舉", + "age_verification.fail": "你至少必須滿 {ageMinimum} 歲", + "age_verification.header": "提供你的生日", + "alert.unexpected.body": "我們對這次的中斷感到抱歉。如果問題持續存在,請聯絡我們的支援團隊。你也可以嘗試 {clearCookies} (注意:你的帳戶會自動退出)。", + "alert.unexpected.browser": "瀏覽器", + "alert.unexpected.clear_cookies": "清除cookie和瀏覽器資料", + "alert.unexpected.links.help": "支援中心", + "alert.unexpected.links.status": "狀態", + "alert.unexpected.links.support": "支援", "alert.unexpected.message": "發生了非預期的錯誤。", - "alert.unexpected.return_home": "Return Home", - "alert.unexpected.title": "哎呀!", - "aliases.account.add": "Create alias", - "aliases.account_label": "Old account:", - "aliases.aliases_list_delete": "Unlink alias", - "aliases.search": "Search your old account", - "aliases.success.add": "Account alias created successfully", - "aliases.success.remove": "Account alias removed successfully", - "app_create.name_label": "App name", - "app_create.name_placeholder": "e.g. 'Soapbox'", - "app_create.redirect_uri_label": "Redirect URIs", - "app_create.restart": "Create another", - "app_create.results.app_label": "App", - "app_create.results.explanation_text": "You created a new app and token! Please copy the credentials somewhere; you will not see them again after navigating away from this page.", - "app_create.results.explanation_title": "App created successfully", - "app_create.results.token_label": "OAuth token", - "app_create.scopes_label": "Scopes", - "app_create.scopes_placeholder": "e.g. 'read write follow'", - "app_create.submit": "Create app", - "app_create.website_label": "Website", - "auth.invalid_credentials": "Wrong username or password", - "auth.logged_out": "Logged out.", - "backups.actions.create": "Create backup", - "backups.empty_message": "No backups found. {action}", - "backups.empty_message.action": "Create one now?", - "backups.pending": "Pending", - "beta.also_available": "Available in:", - "birthday_panel.title": "Birthdays", + "alert.unexpected.return_home": "回到主頁", + "alert.unexpected.title": "哎喲!", + "aliases.account.add": "創建別名", + "aliases.account_label": "原帳戶:", + "aliases.aliases_list_delete": "刪除別名", + "aliases.search": "搜尋原帳戶", + "aliases.success.add": "帳戶別名創建成功", + "aliases.success.remove": "帳戶別名刪除成功", + "app_create.name_label": "應用名稱", + "app_create.name_placeholder": "例如 'Soapbox'", + "app_create.redirect_uri_label": "重定向網域", + "app_create.restart": "創建另一個", + "app_create.results.app_label": "應用", + "app_create.results.explanation_text": "您已成功創建一個新應用及其令牌,請複製密鑰等資訊,離開本頁面後這些資訊將不會再次顯示。", + "app_create.results.explanation_title": "應用創建成功", + "app_create.results.token_label": "OAuth令牌", + "app_create.scopes_label": "權限範圍", + "app_create.scopes_placeholder": "例如 '讀取 寫入 追蹤'", + "app_create.submit": "創建應用", + "app_create.website_label": "網站", + "auth.invalid_credentials": "無效的帳戶名稱或密碼", + "auth.logged_out": "您已登出", + "backups.actions.create": "創建備份", + "backups.empty_message": "找不到備份 {action}", + "backups.empty_message.action": "現在創建嗎?", + "backups.pending": "等待備份", + "beta.also_available": "在此時可用:", + "birthday_panel.title": "生日", "boost_modal.combo": "下次您可以按 {combo} 跳過", "bundle_column_error.body": "載入此元件時發生錯誤。", "bundle_column_error.retry": "重試", @@ -163,260 +176,275 @@ "bundle_modal_error.close": "關閉", "bundle_modal_error.message": "載入此元件時發生錯誤。", "bundle_modal_error.retry": "重試", - "card.back.label": "Back", - "chat.actions.send": "Send", - "chat.input.placeholder": "Type a message", - "chat_panels.main_window.empty": "No chats found. To start a chat, visit a user's profile.", - "chat_panels.main_window.title": "Chats", - "chats.actions.delete": "Delete for both", - "chats.actions.more": "More", - "chats.actions.report": "Report", - "chats.attachment": "Attachment", - "chats.attachment_image": "Image", - "chats.audio_toggle_off": "Audio notification off", - "chats.audio_toggle_on": "Audio notification on", - "chats.dividers.today": "Today", - "chats.search_placeholder": "Search inbox", - "column.admin.awaiting_approval": "Awaiting Approval", - "column.admin.dashboard": "Dashboard", - "column.admin.moderation_log": "Moderation Log", - "column.admin.reports": "Reports", - "column.admin.reports.menu.moderation_log": "Moderation Log", - "column.admin.users": "Users", - "column.aliases": "Account aliases", - "column.aliases.create_error": "Error creating alias", - "column.aliases.delete": "Delete", - "column.aliases.delete_error": "Error deleting alias", - "column.aliases.subheading_add_new": "Add New Alias", - "column.aliases.subheading_aliases": "Current aliases", - "column.app_create": "Create app", - "column.backups": "Backups", - "column.birthdays": "Birthdays", + "card.back.label": "返回", + "chat.actions.send": "發送", + "chat.input.placeholder": "發送聊天訊息…", + "chat_panels.main_window.empty": "還沒有訊息。要開始聊天,可以從用戶的個人資料頁面發起。", + "chat_panels.main_window.title": "聊天", + "chats.actions.delete": "刪除訊息", + "chats.actions.more": "更多選項", + "chats.actions.report": "檢舉用戶", + "chats.attachment": "附件", + "chats.attachment_image": "圖片", + "chats.audio_toggle_off": "關閉消息提醒", + "chats.audio_toggle_on": "開啟消息提醒", + "chats.dividers.today": "今天", + "chats.search_placeholder": "開始聊天…", + "column.admin.awaiting_approval": "等待核准", + "column.admin.dashboard": "控制台", + "column.admin.moderation_log": "管理日誌", + "column.admin.reports": "檢舉", + "column.admin.reports.menu.moderation_log": "管理日誌", + "column.admin.users": "用戶", + "column.aliases": "帳戶別名", + "column.aliases.create_error": "創建帳戶別名時出錯", + "column.aliases.delete": "刪除", + "column.aliases.delete_error": "刪除帳戶別名時出錯", + "column.aliases.subheading_add_new": "添加新別名", + "column.aliases.subheading_aliases": "當前帳戶別名", + "column.app_create": "創建應用", + "column.backups": "備份", + "column.birthdays": "生日", "column.blocks": "封鎖的使用者", - "column.bookmarks": "Bookmarks", - "column.chats": "Chats", - "column.community": "本機時間軸", - "column.crypto_donate": "Donate Cryptocurrency", - "column.developers": "Developers", + "column.bookmarks": "書籤", + "column.chats": "聊天", + "column.community": "本站時間軸", + "column.crypto_donate": "數字貨幣捐贈", + "column.developers": "開發者", "column.direct": "私訊", - "column.directory": "Browse profiles", + "column.directory": "發現更多", "column.domain_blocks": "隱藏的網域", - "column.edit_profile": "Edit profile", - "column.export_data": "Export data", - "column.favourited_statuses": "Liked posts", - "column.favourites": "Likes", - "column.federation_restrictions": "Federation Restrictions", - "column.filters": "Muted words", - "column.filters.add_new": "Add New Filter", - "column.filters.conversations": "Conversations", - "column.filters.create_error": "Error adding filter", - "column.filters.delete": "Delete", - "column.filters.delete_error": "Error deleting filter", - "column.filters.drop_header": "Drop instead of hide", - "column.filters.drop_hint": "Filtered posts will disappear irreversibly, even if filter is later removed", - "column.filters.expires": "Expire after", - "column.filters.expires_hint": "Expiration dates are not currently supported", - "column.filters.home_timeline": "Home timeline", - "column.filters.keyword": "Keyword or phrase", - "column.filters.notifications": "Notifications", - "column.filters.public_timeline": "Public timeline", - "column.filters.subheading_add_new": "Add New Filter", - "column.filters.subheading_filters": "Current Filters", - "column.filters.whole_word_header": "Whole word", - "column.filters.whole_word_hint": "When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word", - "column.follow_requests": "關注請求", - "column.followers": "Followers", - "column.following": "Following", - "column.groups": "Groups", + "column.edit_profile": "編輯個人資料", + "column.export_data": "匯出資料", + "column.favourited_statuses": "按讚的貼文", + "column.favourites": "按讚", + "column.federation_restrictions": "聯邦限制", + "column.filters": "過濾詞", + "column.filters.add_new": "新增過濾詞", + "column.filters.conversations": "聊天", + "column.filters.create_error": "新增過濾詞時出錯。", + "column.filters.delete": "刪除過濾詞", + "column.filters.delete_error": "刪除過濾詞時出錯。", + "column.filters.drop_header": "丟棄而非隱藏", + "column.filters.drop_hint": "被丟棄的帖文會不可逆地消失,即使移除過濾詞之後也不會恢復", + "column.filters.expires": "過期時間", + "column.filters.expires_hint": "過期時間暫未被支援", + "column.filters.home_timeline": "主頁時間軸", + "column.filters.keyword": "關鍵詞", + "column.filters.notifications": "通知", + "column.filters.public_timeline": "公共時間軸", + "column.filters.subheading_add_new": "新增過濾詞", + "column.filters.subheading_filters": "當前過濾詞", + "column.filters.whole_word_header": "全詞匹配", + "column.filters.whole_word_hint": "如果關鍵詞只含字母和數字,則只有全詞匹配才會被過濾", + "column.follow_requests": "追蹤請求", + "column.followers": "追蹤者", + "column.following": "正在追蹤", + "column.groups": "群組", "column.home": "主頁", - "column.import_data": "Import data", - "column.info": "Server information", + "column.import_data": "匯入資料", + "column.info": "伺服器資訊", "column.lists": "名單", - "column.mentions": "Mentions", - "column.mfa": "Multi-Factor Authentication", - "column.mfa_cancel": "Cancel", - "column.mfa_confirm_button": "Confirm", - "column.mfa_disable_button": "Disable", - "column.mfa_setup": "Proceed to Setup", - "column.migration": "Account migration", - "column.mutes": "被靜音的使用者", + "column.mentions": "提及", + "column.mfa": "兩步驟驗證", + "column.mfa_cancel": "取消", + "column.mfa_confirm_button": "確認", + "column.mfa_disable_button": "停用", + "column.mfa_setup": "同意並繼續", + "column.migration": "帳戶遷移", + "column.mutes": "被隱藏的使用者", "column.notifications": "通知", - "column.pins": "Pinned posts", - "column.preferences": "Preferences", + "column.pins": "釘選的貼文", + "column.preferences": "偏好設定", "column.public": "聯邦時間軸", - "column.reactions": "Reactions", - "column.reblogs": "Reposts", - "column.remote": "Federated timeline", - "column.scheduled_statuses": "Scheduled Posts", - "column.search": "Search", - "column.security": "Security", - "column.settings_store": "Settings store", - "column.soapbox_config": "Soapbox config", - "column.test": "Test timeline", + "column.reactions": "心情回應", + "column.reblogs": "轉帖", + "column.remote": "聯邦時間軸", + "column.scheduled_statuses": "定時帖文", + "column.search": "搜尋", + "column.settings_store": "設定儲存", + "column.soapbox_config": "Soapbox設定", + "column.test": "測試時間軸", "column_back_button.label": "上一頁", - "column_forbidden.body": "You do not have permission to access this page.", - "column_forbidden.title": "Forbidden", + "column_forbidden.body": "您無權訪問這個頁面。", + "column_forbidden.title": "無權訪問", "column_header.show_settings": "顯示設定", - "common.cancel": "Cancel", - "community.column_settings.media_only": "只有媒體", - "community.column_settings.title": "Local timeline settings", - "compose.character_counter.title": "Used {chars} out of {maxChars} characters", - "compose.invalid_schedule": "You must schedule a post at least 5 minutes out.", - "compose.submit_success": "Your post was sent", - "compose_form.direct_message_warning": "這條嘟文只有被提及的使用者才看得到。", - "compose_form.hashtag_warning": "由於這則嘟文被設定成「不公開」,所以它將不會被列在任何主題標籤下。只有公開的嘟文才能藉主題標籤找到。", - "compose_form.lock_disclaimer": "您的帳戶尚未{locked}。任何人都能關注您並看到您設定成只有關注者能看的嘟文。", + "common.cancel": "取消", + "common.error": "有些東西好像壞了...嘗試重載頁面", + "community.column_settings.media_only": "僅媒體", + "community.column_settings.title": "本地時間軸設定", + "compare_history_modal.header": "編輯歷史", + "compose.character_counter.title": "最大字符數: {maxChars}; 已使用 {chars}", + "compose.edit_success": "你的帖文已編輯", + "compose.invalid_schedule": "定時帖文只能設定在五分鐘後或更遲發送", + "compose.submit_success": "帖文已送出", + "compose_form.direct_message_warning": "這條帖文只有被提及的使用者才看得到。", + "compose_form.hashtag_warning": "由於這則帖文被設定成「不公開」,所以它將不會被列在任何主題標籤下。只有公開的帖文才能藉主題標籤找到。", + "compose_form.lock_disclaimer": "您的帳戶尚未{locked}。任何人都能追蹤您並看到您設定成只有追蹤者能看的帖文。", "compose_form.lock_disclaimer.lock": "上鎖", - "compose_form.markdown.marked": "Post markdown enabled", - "compose_form.markdown.unmarked": "Post markdown disabled", - "compose_form.message": "Message", + "compose_form.markdown.marked": "Markdown已啟用", + "compose_form.markdown.unmarked": "Markdown已禁用", + "compose_form.message": "私訊", "compose_form.placeholder": "您正在想些什麼?", "compose_form.poll.add_option": "新增選擇", "compose_form.poll.duration": "投票期限", + "compose_form.poll.multiselect": "多選", + "compose_form.poll.multiselect_detail": "允許參與者選擇多個答案", "compose_form.poll.option_placeholder": "第 {number} 個選擇", + "compose_form.poll.remove": "移除投票", "compose_form.poll.remove_option": "移除此選擇", - "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices", - "compose_form.poll.switch_to_single": "Change poll to allow for a single choice", - "compose_form.publish": "嘟出去", + "compose_form.poll.switch_to_multiple": "投票改為多選", + "compose_form.poll.switch_to_single": "投票改為單選", + "compose_form.poll_placeholder": "添加投票主題...", + "compose_form.publish": "發佈", "compose_form.publish_loud": "{publish}!", - "compose_form.schedule": "Schedule", - "compose_form.scheduled_statuses.click_here": "Click here", - "compose_form.scheduled_statuses.message": "You have scheduled posts. {click_here} to see them.", - "compose_form.sensitive.hide": "標記媒體為敏感內容", - "compose_form.sensitive.marked": "此媒體被標記為敏感內容", - "compose_form.sensitive.unmarked": "此媒體未標記為敏感內容", + "compose_form.save_changes": "儲存變更", + "compose_form.schedule": "定時發佈", + "compose_form.scheduled_statuses.click_here": "點擊此處", + "compose_form.scheduled_statuses.message": "你有定時帖文, {click_here} 查看", "compose_form.spoiler.marked": "正文已隱藏到警告之後", "compose_form.spoiler.unmarked": "正文未被隱藏", "compose_form.spoiler_placeholder": "請在此處寫入警告訊息", + "compose_form.spoiler_remove": "移除敏感內容", + "compose_form.spoiler_title": "敏感內容", "confirmation_modal.cancel": "取消", - "confirmations.admin.deactivate_user.confirm": "Deactivate @{name}", - "confirmations.admin.deactivate_user.heading": "Deactivate @{acct}", - "confirmations.admin.deactivate_user.message": "You are about to deactivate @{acct}. Deactivating a user is a reversible action.", - "confirmations.admin.delete_local_user.checkbox": "I understand that I am about to delete a local user.", - "confirmations.admin.delete_status.confirm": "Delete post", - "confirmations.admin.delete_status.heading": "Delete post", - "confirmations.admin.delete_status.message": "You are about to delete a post by @{acct}. This action cannot be undone.", - "confirmations.admin.delete_user.confirm": "Delete @{name}", - "confirmations.admin.delete_user.heading": "Delete @{acct}", - "confirmations.admin.delete_user.message": "You are about to delete @{acct}. THIS IS A DESTRUCTIVE ACTION THAT CANNOT BE UNDONE.", - "confirmations.admin.mark_status_not_sensitive.confirm": "Mark post not sensitive", - "confirmations.admin.mark_status_not_sensitive.heading": "Mark post not sensitive.", - "confirmations.admin.mark_status_not_sensitive.message": "You are about to mark a post by @{acct} not sensitive.", - "confirmations.admin.mark_status_sensitive.confirm": "Mark post sensitive", - "confirmations.admin.mark_status_sensitive.heading": "Mark post sensitive", - "confirmations.admin.mark_status_sensitive.message": "You are about to mark a post by @{acct} sensitive.", - "confirmations.admin.reject_user.confirm": "Reject @{name}", - "confirmations.admin.reject_user.heading": "Reject @{acct}", - "confirmations.admin.reject_user.message": "You are about to reject @{acct} registration request. This action cannot be undone.", + "confirmations.admin.deactivate_user.confirm": "禁用帳戶 @{name}", + "confirmations.admin.deactivate_user.heading": "禁用帳戶 @{acct}", + "confirmations.admin.deactivate_user.message": "你確定要禁用帳戶 @{acct} 嗎?此操作不能撤回!", + "confirmations.admin.delete_local_user.checkbox": "我確定我不再需要這個帳戶", + "confirmations.admin.delete_status.confirm": "刪除帖文", + "confirmations.admin.delete_status.heading": "刪除帖文", + "confirmations.admin.delete_status.message": "你確定要刪除帖文 @{acct} 嗎?此操作不能撤回!", + "confirmations.admin.delete_user.confirm": "刪除帳戶 @{name}", + "confirmations.admin.delete_user.heading": "刪除帳戶 @{acct}", + "confirmations.admin.delete_user.message": "你確定要刪除帳戶 @{acct}嗎?此操作不能撤回!", + "confirmations.admin.mark_status_not_sensitive.confirm": "標記為不敏感", + "confirmations.admin.mark_status_not_sensitive.heading": "標記為不敏感", + "confirmations.admin.mark_status_not_sensitive.message": "你要標記帳戶 @{acct} 的帖文為不敏感", + "confirmations.admin.mark_status_sensitive.confirm": "標記為敏感帖文", + "confirmations.admin.mark_status_sensitive.heading": "標記為敏感帖文", + "confirmations.admin.mark_status_sensitive.message": "你要標記帳戶 @{acct} 的帖文為敏感", + "confirmations.admin.reject_user.confirm": "拒絕 @{name}", + "confirmations.admin.reject_user.heading": "拒絕 @{acct}", + "confirmations.admin.reject_user.message": "你正準備拒絕 @{acct} 的註冊請求。此操作不能撤銷。", "confirmations.block.block_and_report": "封鎖並檢舉", "confirmations.block.confirm": "封鎖", - "confirmations.block.heading": "Block @{name}", + "confirmations.block.heading": "封鎖 @{name}", "confirmations.block.message": "確定封鎖 {name} ?", "confirmations.delete.confirm": "刪除", - "confirmations.delete.heading": "Delete post", - "confirmations.delete.message": "你確定要刪除這條嘟文?", + "confirmations.delete.heading": "刪除帖文", + "confirmations.delete.message": "你確定要刪除這條帖文?", "confirmations.delete_list.confirm": "刪除", - "confirmations.delete_list.heading": "Delete list", - "confirmations.delete_list.message": "確定永久刪除此名單?", + "confirmations.delete_list.heading": "刪除列表", + "confirmations.delete_list.message": "你確定要永久刪除這個列表?", "confirmations.domain_block.confirm": "隱藏整個網域", "confirmations.domain_block.heading": "Block {domain}", - "confirmations.domain_block.message": "真的非常確定封鎖整個 {domain} 嗎?大部分情況下,你只需要封鎖或靜音少數特定的人就能滿足需求了。你將不能在任何公開的時間軸及通知中看到那個網域的內容。你來自該網域的關注者也會被移除。", - "confirmations.mute.confirm": "靜音", + "confirmations.domain_block.message": "真的非常確定封鎖整個 {domain} 嗎?大部分情況下,你只需要封鎖或隱藏少數特定的人就能滿足需求了。你將不能在任何公開的時間軸及通知中看到那個網域的內容。你來自該網域的追蹤者也會被移除。", + "confirmations.mute.confirm": "隱藏", "confirmations.mute.heading": "Mute @{name}", - "confirmations.mute.message": "確定靜音 {name} ?", + "confirmations.mute.message": "確定隱藏 {name} ?", "confirmations.redraft.confirm": "刪除並重新編輯", - "confirmations.redraft.heading": "Delete & redraft", - "confirmations.redraft.message": "確定刪掉這則嘟文並重新編輯嗎?將會失去這則嘟文的轉嘟及收藏,且回覆這則的嘟文將會變成獨立的嘟文。", - "confirmations.register.needs_approval": "Your account will be manually approved by an admin. Please be patient while we review your details.", - "confirmations.register.needs_approval.header": "Approval needed", - "confirmations.register.needs_confirmation": "Please check your inbox at {email} for confirmation instructions. You will need to verify your email address to continue.", - "confirmations.register.needs_confirmation.header": "Confirmation needed", + "confirmations.redraft.heading": "刪除並重新編輯", + "confirmations.redraft.message": "確定刪掉這則帖文並重新編輯嗎?將會失去這則帖文的轉帖及收藏,且回覆這則的帖文將會變成獨立的帖文。", + "confirmations.register.needs_approval": "你的帳戶正在被管理員審核中,請等待一會", + "confirmations.register.needs_approval.header": "需要審核", + "confirmations.register.needs_confirmation": "我們已經發送了指引到你的電郵 {email} 中,請檢查收件箱並點擊鏈接以繼續。", + "confirmations.register.needs_confirmation.header": "需要確認", + "confirmations.remove_from_followers.confirm": "刪除", + "confirmations.remove_from_followers.message": "確定要從你的追蹤者中移走 {name} 嗎?", "confirmations.reply.confirm": "回覆", "confirmations.reply.message": "現在回覆將蓋掉您目前正在撰寫的訊息。是否仍要回覆?", - "confirmations.scheduled_status_delete.confirm": "Cancel", - "confirmations.scheduled_status_delete.heading": "Cancel scheduled post", - "confirmations.scheduled_status_delete.message": "Are you sure you want to cancel this scheduled post?", - "confirmations.unfollow.confirm": "取消關注", - "confirmations.unfollow.heading": "Unfollow {name}", - "confirmations.unfollow.message": "真的要取消關注 {name} 嗎?", - "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", - "crypto_donate.explanation_box.title": "Sending cryptocurrency donations", - "crypto_donate_panel.actions.view": "Click to see {count} {count, plural, one {wallet} other {wallets}}", - "crypto_donate_panel.heading": "Donate Cryptocurrency", - "crypto_donate_panel.intro.message": "{siteTitle} accepts cryptocurrency donations to fund our service. Thank you for your support!", - "datepicker.hint": "Scheduled to post at…", - "datepicker.next_month": "Next month", - "datepicker.next_year": "Next year", - "datepicker.previous_month": "Previous month", - "datepicker.previous_year": "Previous year", - "developers.challenge.answer_label": "Answer", - "developers.challenge.answer_placeholder": "Your answer", - "developers.challenge.fail": "Wrong answer", - "developers.challenge.message": "What is the result of calling {function}?", - "developers.challenge.submit": "Become a developer", - "developers.challenge.success": "You are now a developer", - "developers.leave": "You have left developers", - "developers.navigation.app_create_label": "Create an app", - "developers.navigation.intentional_error_label": "Trigger an error", - "developers.navigation.leave_developers_label": "Leave developers", - "developers.navigation.network_error_label": "Network error", - "developers.navigation.settings_store_label": "Settings store", - "developers.navigation.test_timeline_label": "Test timeline", - "developers.settings_store.hint": "It is possible to directly edit your user settings here. BE CAREFUL! Editing this section can break your account, and you will only be able to recover through the API.", - "direct.search_placeholder": "Send a message to…", - "directory.federated": "From known fediverse", - "directory.local": "From {domain} only", - "directory.new_arrivals": "New arrivals", - "directory.recently_active": "Recently active", - "edit_federation.followers_only": "Hide posts except to followers", - "edit_federation.force_nsfw": "Force attachments to be marked sensitive", - "edit_federation.media_removal": "Strip media", - "edit_federation.reject": "Reject all activities", - "edit_federation.save": "Save", - "edit_federation.success": "{host} federation was updated", - "edit_federation.unlisted": "Force posts unlisted", - "edit_password.header": "Change Password", - "edit_profile.error": "Profile update failed", - "edit_profile.fields.accepts_email_list_label": "Subscribe to newsletter", - "edit_profile.fields.avatar_label": "Avatar", - "edit_profile.fields.bio_label": "Bio", - "edit_profile.fields.bio_placeholder": "Tell us about yourself.", - "edit_profile.fields.birthday_label": "Birthday", - "edit_profile.fields.birthday_placeholder": "Your birthday", - "edit_profile.fields.bot_label": "This is a bot account", - "edit_profile.fields.discoverable_label": "Allow account discovery", - "edit_profile.fields.display_name_label": "Display name", - "edit_profile.fields.display_name_placeholder": "Name", - "edit_profile.fields.header_label": "Header", - "edit_profile.fields.hide_network_label": "Hide network", - "edit_profile.fields.location_label": "Location", - "edit_profile.fields.location_placeholder": "Location", - "edit_profile.fields.locked_label": "Lock account", - "edit_profile.fields.meta_fields.content_placeholder": "Content", - "edit_profile.fields.meta_fields.label_placeholder": "Label", - "edit_profile.fields.stranger_notifications_label": "Block notifications from strangers", - "edit_profile.fields.website_label": "Website", - "edit_profile.fields.website_placeholder": "Display a Link", - "edit_profile.header": "Edit Profile", - "edit_profile.hints.accepts_email_list": "Opt-in to news and marketing updates.", - "edit_profile.hints.avatar": "PNG, GIF or JPG. Will be downscaled to {size}", - "edit_profile.hints.bot": "This account mainly performs automated actions and might not be monitored", - "edit_profile.hints.discoverable": "Display account in profile directory and allow indexing by external services", - "edit_profile.hints.header": "PNG, GIF or JPG. Will be downscaled to {size}", - "edit_profile.hints.hide_network": "Who you follow and who follows you will not be shown on your profile", - "edit_profile.hints.locked": "Requires you to manually approve followers", - "edit_profile.hints.stranger_notifications": "Only show notifications from people you follow", - "edit_profile.save": "Save", - "edit_profile.success": "Profile saved!", - "email_passthru.confirmed.body": "Close this tab and continue the registration process on the {bold} from which you sent this email confirmation.", - "email_passthru.confirmed.heading": "Email Confirmed!", - "email_passthru.generic_fail.body": "Please request a new email confirmation.", - "email_passthru.generic_fail.heading": "Something Went Wrong", - "email_passthru.token_expired.body": "Your email token has expired. Please request a new email confirmation from the {bold} from which you sent this email confirmation.", - "email_passthru.token_expired.heading": "Token Expired", - "email_passthru.token_not_found.body": "Your email token was not found. Please request a new email confirmation from the {bold} from which you sent this email confirmation.", - "email_passthru.token_not_found.heading": "Invalid Token", - "embed.instructions": "要嵌入此嘟文,請將以下程式碼貼進你的網站。", + "confirmations.scheduled_status_delete.confirm": "取消", + "confirmations.scheduled_status_delete.heading": "取消帖文定時發佈", + "confirmations.scheduled_status_delete.message": "你確定要取消這篇帖文定時發佈嗎?", + "confirmations.unfollow.confirm": "取消追蹤", + "confirmations.unfollow.heading": "取消追蹤 {name}", + "confirmations.unfollow.message": "真的要取消追蹤 {name} 嗎?", + "crypto_donate.explanation_box.message": "{siteTitle} 接受用戶向以下錢包地址捐贈任意數量的數字資產。你的抖內會令我們做得更好!", + "crypto_donate.explanation_box.title": "發起數字貨幣捐贈", + "crypto_donate_panel.actions.view": "點擊查看 {count} {count, plural, one {wallet} other {wallets}}", + "crypto_donate_panel.heading": "捐贈數字貨幣", + "crypto_donate_panel.intro.message": "{siteTitle} 接受用戶捐贈數字貨幣。感謝你的支持!", + "datepicker.day": "Day", + "datepicker.hint": "設定發送時間…", + "datepicker.month": "Month", + "datepicker.next_month": "下個月", + "datepicker.next_year": "明年", + "datepicker.previous_month": "上個月", + "datepicker.previous_year": "去年", + "datepicker.year": "Year", + "developers.challenge.answer_label": "回答", + "developers.challenge.answer_placeholder": "你的回答", + "developers.challenge.fail": "回答錯誤", + "developers.challenge.message": "調用函數 {function} 的結果是什麼?", + "developers.challenge.submit": "成為開發者", + "developers.challenge.success": "你已成為開發者", + "developers.leave": "您已退出開發者", + "developers.navigation.app_create_label": "創建應用", + "developers.navigation.intentional_error_label": "觸發錯誤", + "developers.navigation.leave_developers_label": "退出開發者", + "developers.navigation.network_error_label": "網路錯誤", + "developers.navigation.settings_store_label": "設置儲存", + "developers.navigation.test_timeline_label": "測試時間軸", + "developers.settings_store.hint": "可以在此處直接編輯您的用戶設置。 當心!編輯此部分可能會破壞您的帳戶,您只能通過 API 恢復。", + "direct.body": "新的私訊傳遞體驗即將推出。 敬請期待。", + "direct.search_placeholder": "發送私信給…", + "directory.federated": "來自已知聯邦宇宙", + "directory.local": "僅來自 {domain}", + "directory.new_arrivals": "新增訪客", + "directory.recently_active": "最近活動", + "edit_federation.followers_only": "對追蹤者以外的用戶隱藏帖文", + "edit_federation.force_nsfw": "將附件強制標記為敏感內容", + "edit_federation.media_removal": "去除媒體", + "edit_federation.reject": "拒絕所有資訊交互", + "edit_federation.save": "儲存", + "edit_federation.success": "{host} 聯邦設定已儲存", + "edit_federation.unlisted": "將帖文強制標記移出公共時間軸", + "edit_password.header": "修改密碼", + "edit_profile.error": "個人資料更新失敗", + "edit_profile.fields.accepts_email_list_label": "訂閲電子郵件列表", + "edit_profile.fields.avatar_label": "頭帖", + "edit_profile.fields.bio_label": "簡介", + "edit_profile.fields.bio_placeholder": "介紹一下自己", + "edit_profile.fields.birthday_label": "生日", + "edit_profile.fields.birthday_placeholder": "你的生日", + "edit_profile.fields.bot_label": "這是一個機械人帳戶", + "edit_profile.fields.discoverable_label": "公開自己", + "edit_profile.fields.display_name_label": "昵稱", + "edit_profile.fields.display_name_placeholder": "你的昵稱", + "edit_profile.fields.header_label": "個人資料頁橫幅圖片", + "edit_profile.fields.hide_network_label": "隱藏網路狀態", + "edit_profile.fields.location_label": "地點", + "edit_profile.fields.location_placeholder": "地點", + "edit_profile.fields.locked_label": "鎖定帳戶", + "edit_profile.fields.meta_fields.content_placeholder": "內容", + "edit_profile.fields.meta_fields.label_placeholder": "標籤", + "edit_profile.fields.verified_display_name": "經過驗證的用戶無法修改昵稱", + "edit_profile.fields.meta_fields_label": "個人資料自定義列", + "edit_profile.fields.stranger_notifications_label": "封鎖來自陌生人的通知", + "edit_profile.fields.website_label": "網站", + "edit_profile.fields.website_placeholder": "顯示連結", + "edit_profile.header": "編輯個人資料", + "edit_profile.hints.accepts_email_list": "接收新聞和推廣電郵", + "edit_profile.hints.avatar": "只支援 PNG, GIF or JPG。 尺寸將會被縮放至 {size}", + "edit_profile.hints.bot": "此帳戶主要執行自動化操作,可能不受用戶控制", + "edit_profile.hints.discoverable": "在配置文件目錄中顯示帳戶並允許外部服務索引", + "edit_profile.hints.header": "只支援 PNG, GIF or JPG。 尺寸將會被縮放至 {size}", + "edit_profile.hints.hide_network": "您追蹤的人和追蹤您的人不會顯示在您的個人資料中", + "edit_profile.hints.locked": "需要您手動批准追蹤請求", + "edit_profile.hints.meta_fields": "你能在個人資料頁面上最多顯示 {count} 行自定義列", + "edit_profile.hints.stranger_notifications": "僅顯示來自您追蹤的人的通知", + "edit_profile.save": "儲存", + "edit_profile.success": "個人資料已儲存", + "email_passthru.confirmed.body": "關閉此頁面,並在你發送此電子郵件確認的 {bold} 上繼續註冊過程", + "email_passthru.confirmed.heading": "電郵地址已確認!", + "email_passthru.generic_fail.body": "請重新請求確認郵件", + "email_passthru.generic_fail.heading": "好像出了一點問題…", + "email_passthru.token_expired.body": "你的電郵令牌已經過期。請從你發送此電郵確認的 {bold} 處重新申請電郵確認。", + "email_passthru.token_expired.heading": "令牌已過期", + "email_passthru.token_not_found.body": "你的電郵令牌已經過期。請從你發送此電郵確認的 {bold} 處重新申請電郵確認。", + "email_passthru.token_not_found.heading": "無效令牌!", + "embed.instructions": "要嵌入此帖文,請將以下程式碼貼進你的網站。", "embed.preview": "他會顯示成這樣:", "emoji_button.activity": "活動", "emoji_button.custom": "自訂", @@ -432,550 +460,558 @@ "emoji_button.search_results": "搜尋結果", "emoji_button.symbols": "符號", "emoji_button.travel": "旅遊與地點", - "empty_column.account_blocked": "You are blocked by @{accountUsername}.", - "empty_column.account_favourited_statuses": "This user doesn't have any liked posts yet.", - "empty_column.account_timeline": "這裡還沒有嘟文!", + "empty_column.account_blocked": "你被 @{accountUsername} 封鎖了", + "empty_column.account_favourited_statuses": "他還沒有按讚任何貼文", + "empty_column.account_timeline": "這裡還沒有帖文!", "empty_column.account_unavailable": "無法取得個人資料", - "empty_column.aliases": "You haven't created any account alias yet.", - "empty_column.aliases.suggestions": "There are no account suggestions available for the provided term.", + "empty_column.aliases": "你還沒有設定任何別名", + "empty_column.aliases.suggestions": "暫時沒有可用的帳戶建議", "empty_column.blocks": "你還沒有封鎖任何使用者。", - "empty_column.bookmarks": "You don't have any bookmarks yet. When you add one, it will show up here.", - "empty_column.community": "本地時間軸是空的。快公開嘟些文搶頭香啊!", + "empty_column.bookmarks": "你還沒有任何書籤收藏,一旦你開始將帖文加入書籤,它將會在這裡顯示", + "empty_column.community": "本地時間軸是空的。快公開發佈些文搶頭香啊!", "empty_column.direct": "您還沒有任何私訊。當您私訊別人或收到私訊時,它將於此顯示。", "empty_column.domain_blocks": "尚未隱藏任何網域。", - "empty_column.favourited_statuses": "你還沒收藏任何嘟文。這裡將會顯示你收藏的嘟文。", - "empty_column.favourites": "還沒有人收藏這則嘟文。這裡將會顯示被收藏的嘟文。", - "empty_column.filters": "You haven't created any muted words yet.", - "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.", - "empty_column.follow_requests": "您尚未收到任何關注請求。這裡將會顯示收到的關注請求。", - "empty_column.group": "There is nothing in this group yet. When members of this group make new posts, they will appear here.", + "empty_column.favourited_statuses": "你還沒收藏任何帖文。這裡將會顯示你收藏的帖文。", + "empty_column.favourites": "還沒有人收藏這則帖文。這裡將會顯示被收藏的帖文。", + "empty_column.filters": "你還未有添加任何過濾詞。", + "empty_column.follow_recommendations": "目前似乎暫時沒有推薦訊息,你可以嘗試搜尋用戶或者瀏覽熱門標籤。", + "empty_column.follow_requests": "您尚未收到任何追蹤請求。這裡將會顯示收到的追蹤請求。", + "empty_column.group": "該組中還沒有任何內容。 當該群組的成員發布新帖子時,他們會出現在此處。", "empty_column.hashtag": "這個主題標籤下什麼也沒有。", "empty_column.home": "您的首頁時間軸是空的!前往 {public} 或使用搜尋功能來認識其他人。", - "empty_column.home.local_tab": "the {site_title} tab", - "empty_column.list": "這份名單還沒有東西。當此名單的成員嘟出了新的嘟文時,它們就會顯示於此。", + "empty_column.home.local_tab": "{site_title} 本站時間軸", + "empty_column.list": "這份名單還沒有東西。當此名單的成員發佈了新的帖文時,它們就會顯示於此。", "empty_column.lists": "你還沒有建立任何名單。這裡將會顯示你所建立的名單。", - "empty_column.mutes": "你尚未靜音任何使用者。", + "empty_column.mutes": "你尚未隱藏任何使用者。", "empty_column.notifications": "您尚未收到任何通知,和別人互動開啟對話吧。", - "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或著自己關注其他伺服器的使用者後就會有嘟文出現了", - "empty_column.remote": "There is nothing here! Manually follow users from {instance} to fill it up.", - "empty_column.scheduled_statuses": "You don't have any scheduled statuses yet. When you add one, it will show up here.", - "empty_column.search.accounts": "There are no people results for \"{term}\"", - "empty_column.search.hashtags": "There are no hashtags results for \"{term}\"", - "empty_column.search.statuses": "There are no posts results for \"{term}\"", - "export_data.actions.export": "Export", - "export_data.actions.export_blocks": "Export blocks", - "export_data.actions.export_follows": "Export follows", - "export_data.actions.export_mutes": "Export mutes", - "export_data.blocks_label": "Blocks", - "export_data.follows_label": "Follows", - "export_data.hints.blocks": "Get a CSV file containing a list of blocked accounts", - "export_data.hints.follows": "Get a CSV file containing a list of followed accounts", - "export_data.hints.mutes": "Get a CSV file containing a list of muted accounts", - "export_data.mutes_label": "Mutes", - "export_data.success.blocks": "Blocks exported successfully", - "export_data.success.followers": "Followers exported successfully", - "export_data.success.mutes": "Mutes exported successfully", - "federation_restriction.federated_timeline_removal": "Fediverse timeline removal", - "federation_restriction.followers_only": "Hidden except to followers", - "federation_restriction.full_media_removal": "Full media removal", - "federation_restriction.media_nsfw": "Attachments marked NSFW", - "federation_restriction.partial_media_removal": "Partial media removal", - "federation_restrictions.empty_message": "{siteTitle} has not restricted any instances.", - "federation_restrictions.explanation_box.message": "Normally servers on the Fediverse can communicate freely. {siteTitle} has imposed restrictions on the following servers.", - "federation_restrictions.explanation_box.title": "Instance-specific policies", - "federation_restrictions.not_disclosed_message": "{siteTitle} does not disclose federation restrictions through the API.", - "fediverse_tab.explanation_box.dismiss": "Don't show again", - "fediverse_tab.explanation_box.explanation": "{site_title} is part of the Fediverse, a social network made up of thousands of independent social media sites (aka \"servers\"). The posts you see here are from 3rd-party servers. You have the freedom to engage with them, or to block any server you don't like. Pay attention to the full username after the second @ symbol to know which server a post is from. To see only {site_title} posts, visit {local}.", - "fediverse_tab.explanation_box.title": "What is the Fediverse?", - "filters.added": "Filter added.", - "filters.context_header": "Filter contexts", - "filters.context_hint": "One or multiple contexts where the filter should apply", - "filters.filters_list_context_label": "Filter contexts:", - "filters.filters_list_delete": "Delete", - "filters.filters_list_details_label": "Filter settings:", - "filters.filters_list_drop": "Drop", - "filters.filters_list_hide": "Hide", - "filters.filters_list_phrase_label": "Keyword or phrase:", - "filters.filters_list_whole-word": "Whole word", - "filters.removed": "Filter deleted.", - "follow_recommendation.subhead": "Let's get started!", - "follow_recommendations.done": "Done", - "follow_recommendations.heading": "Follow people you'd like to see posts from! Here are some suggestions.", - "follow_recommendations.lead": "Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!", + "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的帖文,或著自己追蹤其他伺服器的使用者後就會有帖文出現了", + "empty_column.remote": "這裡什麼都沒有! 關注本站或者其他站點的成員,就會有用戶出現在這裡。", + "empty_column.scheduled_statuses": "暫時沒有定時帖文。當你發佈定時帖文後,它們會顯示在這裡。", + "empty_column.search.accounts": "沒有匹配的帳戶 \"{term}\"", + "empty_column.search.hashtags": "沒有匹配的標籤 \"{term}\"", + "empty_column.search.statuses": "沒有匹配的帖文 \"{term}\"", + "empty_column.test": "測試時間軸是空的", + "export_data.actions.export": "匯出", + "export_data.actions.export_blocks": "匯出封鎖名單", + "export_data.actions.export_follows": "匯出追蹤名單", + "export_data.actions.export_mutes": "匯出隱藏名單", + "export_data.blocks_label": "封鎖", + "export_data.follows_label": "追蹤", + "export_data.hints.blocks": "匯出封鎖名單為CSV檔案", + "export_data.hints.follows": "匯出追蹤名單為CSV檔案", + "export_data.hints.mutes": "匯出隱藏名單為CSV檔案", + "export_data.mutes_label": "隱藏", + "export_data.success.blocks": "封鎖名單匯出成功", + "export_data.success.followers": "追蹤名單匯出成功", + "export_data.success.mutes": "隱藏名單匯出成功", + "federation_restriction.federated_timeline_removal": "從聯邦宇宙時間軸移除", + "federation_restriction.followers_only": "僅追蹤者可見", + "federation_restriction.full_media_removal": "完全移除媒體", + "federation_restriction.media_nsfw": "附件標註為 NSFW", + "federation_restriction.partial_media_removal": "部分移除媒體", + "federation_restrictions.empty_message": "{siteTitle} 沒有限制任何實例", + "federation_restrictions.explanation_box.message": "通常情況下,聯邦宇宙上的伺服器可以自由通訊。然而 {siteTitle} 對以下伺服器實施了限制", + "federation_restrictions.explanation_box.title": "實例相關政策", + "federation_restrictions.not_disclosed_message": "{siteTitle} 沒有通過API向聯邦宇宙公開限制。", + "fediverse_tab.explanation_box.dismiss": "不再顯示", + "fediverse_tab.explanation_box.explanation": "{site_title} 是聯邦宇宙的一份子, 一個由數個站點組成的社交網路集合。你在這裏看到的帖文來自於其他站點。你可以自由地與他們打交道,或者封鎖任何你不喜歡的站點。第二個 @ 符號後的完整帳戶名表示帖文來自哪個站點。要想只看到 {site_title} 的帖文, 請瀏覽 {local} 。", + "fediverse_tab.explanation_box.title": "什麼是聯邦宇宙?", + "feed_suggestions.heading": "建議的個人資料", + "feed_suggestions.view_all": "檢視全部", + "filters.added": "過濾器已添加。", + "filters.context_header": "過濾器場景", + "filters.context_hint": "一個或多個應用至過濾器的條件", + "filters.filters_list_context_label": "過濾器場景:", + "filters.filters_list_delete": "刪除過濾詞", + "filters.filters_list_details_label": "過濾詞設定:", + "filters.filters_list_drop": "丟棄", + "filters.filters_list_hide": "隱藏", + "filters.filters_list_phrase_label": "關鍵詞:", + "filters.filters_list_whole-word": "全詞", + "filters.removed": "過濾器已移除", + "follow_recommendation.subhead": "讓我們開始吧!", + "follow_recommendations.done": "完成", + "follow_recommendations.heading": "追蹤你感興趣的人,這是我們的推薦列表", + "follow_recommendations.lead": "你追蹤的人的帖文將按照時間順序顯示在你的主頁上。不用擔心搞錯,你可以在任何時候輕鬆地撤銷追蹤!", "follow_request.authorize": "授權", "follow_request.reject": "拒絕", - "forms.copy": "Copy", - "forms.hide_password": "Hide password", - "forms.show_password": "Show password", + "forms.copy": "複製", + "forms.hide_password": "隱藏密碼", + "forms.show_password": "顯示密碼", "getting_started.open_source_notice": "{code_name} 是開源軟體。你可以在 GitLab {code_link} (v{code_version}) 上貢獻或是回報問題。", - "group.detail.archived_group": "Archived group", - "group.members.empty": "This group does not has any members.", - "group.removed_accounts.empty": "This group does not has any removed accounts.", - "groups.card.join": "Join", - "groups.card.members": "Members", - "groups.card.roles.admin": "You're an admin", - "groups.card.roles.member": "You're a member", - "groups.card.view": "View", - "groups.create": "Create group", - "groups.detail.role_admin": "You're an admin", - "groups.edit": "Edit", - "groups.form.coverImage": "Upload new banner image (optional)", - "groups.form.coverImageChange": "Banner image selected", - "groups.form.create": "Create group", - "groups.form.description": "Description", - "groups.form.title": "Title", - "groups.form.update": "Update group", - "groups.join": "Join group", - "groups.leave": "Leave group", - "groups.removed_accounts": "Removed Accounts", - "groups.sidebar-panel.item.no_recent_activity": "No recent activity", - "groups.sidebar-panel.item.view": "new posts", - "groups.sidebar-panel.show_all": "Show all", - "groups.sidebar-panel.title": "Groups You're In", - "groups.tab_admin": "Manage", - "groups.tab_featured": "Featured", - "groups.tab_member": "Member", + "group.members.empty": "這個名單中沒有任何成員", + "group.removed_accounts.empty": "這個名單中沒有任何被移除的帳戶", + "groups.card.join": "加入", + "groups.card.members": "成員", + "groups.card.roles.admin": "你是管理員", + "groups.card.roles.member": "你是成員", + "groups.card.view": "檢視", + "groups.create": "創建群組", + "groups.form.coverImage": "上載橫幅圖片 (可選)", + "groups.form.coverImageChange": "橫幅圖片已上載", + "groups.form.create": "創建群組", + "groups.form.description": "描述", + "groups.form.title": "標題", + "groups.form.update": "更新群組", + "groups.removed_accounts": "已移除帳戶", + "groups.tab_admin": "管理", + "groups.tab_featured": "精選", + "groups.tab_member": "成員", "hashtag.column_header.tag_mode.all": "以及{additional}", "hashtag.column_header.tag_mode.any": "或是{additional}", "hashtag.column_header.tag_mode.none": "而無需{additional}", - "header.home.label": "Home", - "header.login.forgot_password": "Forgot password?", - "header.login.label": "Log in", - "header.login.password.label": "Password", - "header.login.username.placeholder": "Email or username", - "header.register.label": "Register", - "home.column_settings.show_direct": "Show direct messages", - "home.column_settings.show_reblogs": "顯示轉嘟", + "header.home.label": "首頁", + "header.login.forgot_password": "忘記了密碼?", + "header.login.label": "登入", + "header.login.password.label": "密碼", + "header.login.username.placeholder": "電郵地址或帳戶名稱", + "header.preview_timeline.label": "瀏覽首頁", + "header.register.label": "註冊", + "home.column_settings.show_reblogs": "顯示轉帖", "home.column_settings.show_replies": "顯示回覆", - "home.column_settings.title": "Home settings", - "icon_button.icons": "Icons", - "icon_button.label": "Select icon", - "icon_button.not_found": "No icons!! (╯°□°)╯︵ ┻━┻", - "import_data.actions.import": "Import", - "import_data.actions.import_blocks": "Import blocks", - "import_data.actions.import_follows": "Import follows", - "import_data.actions.import_mutes": "Import mutes", - "import_data.blocks_label": "Blocks", - "import_data.follows_label": "Follows", - "import_data.hints.blocks": "CSV file containing a list of blocked accounts", - "import_data.hints.follows": "CSV file containing a list of followed accounts", - "import_data.hints.mutes": "CSV file containing a list of muted accounts", - "import_data.mutes_label": "Mutes", - "import_data.success.blocks": "Blocks imported successfully", - "import_data.success.followers": "Followers imported successfully", - "import_data.success.mutes": "Mutes imported successfully", - "input.password.hide_password": "Hide password", - "input.password.show_password": "Show password", - "intervals.full.days": "{number, plural, one {# 天} other {# 天}}", - "intervals.full.hours": "{number, plural, one {# 小時} other {# 小時}}", - "intervals.full.minutes": "{number, plural, one {# 分鐘} other {# 分鐘}}", - "introduction.federation.action": "Next", - "introduction.federation.home.headline": "Home", - "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", - "introduction.interactions.action": "Finish tutorial!", - "introduction.interactions.favourite.headline": "Favorite", - "introduction.interactions.favourite.text": "You can save a post for later, and let the author know that you liked it, by favoriting it.", - "introduction.interactions.reblog.headline": "Repost", - "introduction.interactions.reblog.text": "You can share other people's posts with your followers by reposting them.", - "introduction.interactions.reply.headline": "Reply", - "introduction.interactions.reply.text": "You can reply to other people's and your own posts, which will chain them together in a conversation.", - "introduction.welcome.action": "Let's go!", - "introduction.welcome.headline": "First steps", - "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "icon_button.icons": "圖示", + "icon_button.label": "選取圖示", + "icon_button.not_found": "沒有圖示!! (╯°□°)╯︵ ┻━┻", + "import_data.actions.import": "匯入", + "import_data.actions.import_blocks": "匯入封鎖名單", + "import_data.actions.import_follows": "匯入追蹤名單", + "import_data.actions.import_mutes": "匯入隱藏名單", + "import_data.blocks_label": "封鎖帳戶", + "import_data.follows_label": "追蹤帳戶", + "import_data.hints.blocks": "上載包含封鎖帳戶名單的CSV檔案", + "import_data.hints.follows": "上載包含追蹤帳戶名單的CSV檔案", + "import_data.hints.mutes": "上載包含隱藏帳戶名單的CSV檔案", + "import_data.mutes_label": "隱藏帳戶", + "import_data.success.blocks": "封鎖帳戶名單已匯入", + "import_data.success.followers": "追蹤帳戶名單已匯入", + "import_data.success.mutes": "隱藏帳戶名單已匯入", + "input.password.hide_password": "隱藏密碼", + "input.password.show_password": "顯示密碼", + "intervals.full.days": "{number} 天", + "intervals.full.hours": "{number} 小時", + "intervals.full.minutes": "{number} 分鐘", "keyboard_shortcuts.back": "返回上一頁", "keyboard_shortcuts.blocked": "開啟「封鎖使用者」名單", - "keyboard_shortcuts.boost": "轉嘟", + "keyboard_shortcuts.boost": "轉帖", "keyboard_shortcuts.compose": "將焦點移至撰寫文字區塊", "keyboard_shortcuts.down": "往下移動名單項目", - "keyboard_shortcuts.enter": "檢視嘟文", + "keyboard_shortcuts.enter": "檢視帖文", "keyboard_shortcuts.favourite": "收藏", "keyboard_shortcuts.favourites": "開啟收藏名單", "keyboard_shortcuts.heading": "鍵盤快速鍵", "keyboard_shortcuts.home": "開啟首頁時間軸", "keyboard_shortcuts.hotkey": "快速鍵", - "keyboard_shortcuts.legend": "顯示此列表", + "keyboard_shortcuts.legend": "顯示此名單", "keyboard_shortcuts.mention": "提及作者", - "keyboard_shortcuts.muted": "開啟靜音使用者名單", + "keyboard_shortcuts.muted": "開啟隱藏使用者名單", "keyboard_shortcuts.my_profile": "開啟個人資料頁面", "keyboard_shortcuts.notifications": "開啟通知欄", - "keyboard_shortcuts.open_media": "to open media", - "keyboard_shortcuts.pinned": "開啟釘選的嘟文名單", + "keyboard_shortcuts.open_media": "開啟媒體", + "keyboard_shortcuts.pinned": "開啟釘選的帖文名單", "keyboard_shortcuts.profile": "開啟作者的個人資料頁面", - "keyboard_shortcuts.react": "to react", + "keyboard_shortcuts.react": "心情回應", "keyboard_shortcuts.reply": "回覆", - "keyboard_shortcuts.requests": "開啟關注請求名單", + "keyboard_shortcuts.requests": "開啟追蹤請求名單", "keyboard_shortcuts.search": "將焦點移至搜尋框", "keyboard_shortcuts.toggle_hidden": "顯示/隱藏在內容警告之後的正文", "keyboard_shortcuts.toggle_sensitivity": "顯示 / 隱藏媒體", - "keyboard_shortcuts.toot": "開始發出新嘟文", + "keyboard_shortcuts.toot": "開始發出新帖文", "keyboard_shortcuts.unfocus": "取消輸入文字區塊 / 搜尋的焦點", "keyboard_shortcuts.up": "往上移動名單項目", - "landing_page_modal.download": "Download", - "landing_page_modal.helpCenter": "Help Center", + "landing_page_modal.download": "下載", + "landing_page_modal.helpCenter": "支援中心", "lightbox.close": "關閉", "lightbox.next": "下一步", "lightbox.previous": "上一步", "lightbox.view_context": "檢視內文", - "list.click_to_add": "Click here to add people", - "list_adder.header_title": "Add or Remove from Lists", + "list.click_to_add": "點擊新增帳戶到名單", + "list_adder.header_title": "從名單中添加或刪除帳戶", "lists.account.add": "新增至名單", "lists.account.remove": "從名單中移除", - "lists.delete": "Delete list", + "lists.delete": "刪除名單", "lists.edit": "編輯名單", "lists.edit.submit": "變更標題", "lists.new.create": "新增名單", - "lists.new.create_title": "Add list", - "lists.new.save_title": "Save Title", + "lists.new.create_title": "新增名單", + "lists.new.save_title": "儲存名單", "lists.new.title_placeholder": "新名單標題", - "lists.search": "搜尋您關注的使用者", + "lists.search": "搜尋您追蹤的使用者", "lists.subheading": "您的名單", "loading_indicator.label": "讀取中...", - "login.fields.instance_label": "Instance", + "login.fields.instance_label": "實例", "login.fields.instance_placeholder": "example.com", - "login.fields.otp_code_hint": "Enter the two-factor code generated by your phone app or use one of your recovery codes", - "login.fields.otp_code_label": "Two-factor code:", - "login.fields.password_placeholder": "Password", - "login.fields.username_label": "Email or username", - "login.log_in": "Log in", - "login.otp_log_in": "OTP Login", - "login.reset_password_hint": "Trouble logging in?", - "login.sign_in": "Sign in", + "login.fields.otp_code_hint": "輸入兩步驟驗證應用程式裏的代碼,或者輸入恢復代碼", + "login.fields.otp_code_label": "兩步驟驗證代碼:", + "login.fields.password_placeholder": "密碼", + "login.fields.username_label": "電郵地址或帳戶名稱", + "login.log_in": "登入", + "login.otp_log_in": "兩步驟驗證登入", + "login.otp_log_in.fail": "兩步驟驗證代號無效,請重新輸入", + "login.reset_password_hint": "登入遇到了問題?", + "login.sign_in": "登入", + "login_form.header": "登入", "media_gallery.toggle_visible": "切換可見性", - "media_panel.empty_message": "No media found.", - "media_panel.title": "Media", - "mfa.confirm.success_message": "MFA confirmed", - "mfa.disable.success_message": "MFA disabled", - "mfa.mfa_disable_enter_password": "Enter your current password to disable two-factor auth:", - "mfa.mfa_setup.code_hint": "Enter the code from your two-factor app.", - "mfa.mfa_setup.code_placeholder": "Code", - "mfa.mfa_setup.password_hint": "Enter your current password to confirm your identity.", - "mfa.mfa_setup.password_placeholder": "Password", - "mfa.mfa_setup_scan_description": "Using your two-factor app, scan this QR code or enter text key:", - "mfa.mfa_setup_scan_title": "Scan", - "mfa.mfa_setup_verify_title": "Verify", - "mfa.otp_enabled_description": "You have enabled two-factor authentication via OTP.", - "mfa.otp_enabled_title": "OTP Enabled", - "mfa.setup_recoverycodes": "Recovery codes", - "mfa.setup_warning": "Write these codes down or save them somewhere secure - otherwise you won't see them again. If you lose access to your 2FA app and recovery codes you'll be locked out of your account.", - "migration.fields.acct.label": "Handle of the new account", - "migration.fields.acct.placeholder": "username@domain", - "migration.fields.confirm_password.label": "Current password", - "migration.hint": "This will move your followers to the new account. No other data will be moved. To perform migration, you need to {link} on your new account first.", - "migration.hint.link": "create an account alias", - "migration.move_account.fail": "Account migration failed.", - "migration.move_account.success": "Account successfully moved.", - "migration.submit": "Move followers", - "missing_description_modal.cancel": "Cancel", - "missing_description_modal.continue": "Post", - "missing_description_modal.description": "Continue anyway?", - "missing_description_modal.text": "You have not entered a description for all attachments. Continue anyway?", + "media_panel.empty_message": "未找到媒體", + "media_panel.title": "媒體", + "mfa.confirm.success_message": "多重要素驗證 (MFA) 已啟用", + "mfa.disable.success_message": "多重要素驗證 (MFA) 已停用", + "mfa.mfa_disable_enter_password": "輸入當前密碼以停用兩步驟驗證:", + "mfa.mfa_setup.code_hint": "輸入兩步驟驗證應用程式裏的代碼", + "mfa.mfa_setup.code_placeholder": "代碼", + "mfa.mfa_setup.password_hint": "輸入當前密碼以確認你的身份", + "mfa.mfa_setup.password_placeholder": "密碼", + "mfa.mfa_setup_scan_description": "請使用 Google Authenticator 或其他應用程式來掃描 QR 碼。啟用兩步驟驗證後,在登入時你需要提供該應用程式生成的代碼", + "mfa.mfa_setup_scan_title": "如果你無法掃描 QR 碼,請手動輸入下列文本:", + "mfa.mfa_setup_verify_title": "啟用", + "mfa.otp_enabled_description": "您已成功啟用兩步驟驗證", + "mfa.otp_enabled_title": "兩步驟驗證已啟用", + "mfa.setup_recoverycodes": "恢復代碼", + "mfa.setup_warning": "請將你的恢復代碼記在安全的地方或寫在紙上,建議與個人資訊分開存放,以便在丟失兩步驟驗證應用程式時可以恢復你的帳戶。", + "migration.fields.acct.label": "新帳戶的帳戶名稱", + "migration.fields.acct.placeholder": "帳戶名稱@網域", + "migration.fields.confirm_password.label": "當前密碼", + "migration.hint": "你的追蹤者將被轉移到新帳戶,除此之外其他資料將不會被轉移。要執行遷移,您需要先{link}你的新帳戶", + "migration.hint.link": "新建一個帳戶別名", + "migration.move_account.fail": "帳戶遷移失敗。", + "migration.move_account.success": "帳戶遷移成功了!", + "migration.submit": "遷移關注者", + "missing_description_modal.cancel": "取消", + "missing_description_modal.continue": "發佈", + "missing_description_modal.description": "仍然繼續發佈嗎?", + "missing_description_modal.text": "附件沒有提供描述。仍然繼續發佈嗎?", "missing_indicator.label": "找不到", "missing_indicator.sublabel": "找不到此資源", - "mobile.also_available": "Available in:", - "morefollows.followers_label": "…and {count} more {count, plural, one {follower} other {followers}} on remote sites.", - "morefollows.following_label": "…and {count} more {count, plural, one {follow} other {follows}} on remote sites.", - "mute_modal.hide_notifications": "隱藏來自這位使用者的通知?", - "navigation.chats": "Chats", - "navigation.compose": "Compose", - "navigation.dashboard": "Dashboard", - "navigation.developers": "Developers", - "navigation.direct_messages": "Messages", - "navigation.home": "Home", - "navigation.invites": "Invites", - "navigation.notifications": "Notifications", - "navigation.search": "Search", - "navigation_bar.account_migration": "Move account", + "mobile.also_available": "在這裏可用:", + "moderation_overlay.contact": "聯絡", + "moderation_overlay.hide": "隱藏內容", + "moderation_overlay.show": "顯示內容", + "moderation_overlay.subtitle": "此帖文已發送至站務以供審核,目前只允許本人查看。如你認為該消息有誤,請聯絡站務", + "moderation_overlay.title": "內容正被審核中", + "morefollows.followers_label": "和{count}來自其他站點的{count, plural, one {追蹤者} other {追蹤者}} 。", + "morefollows.following_label": "和{count}來自其他站點的{count, plural, one {正在追蹤} other {正在追蹤}} 。", + "mute_modal.hide_notifications": "隱藏來自這個帳戶的通知?", + "navbar.login.action": "登入", + "navbar.login.forgot_password": "忘記密碼?", + "navbar.login.password.label": "密碼", + "navbar.login.username.placeholder": "郵箱地址或帳戶名稱", + "navigation.chats": "聊天", + "navigation.compose": "發佈新帖文", + "navigation.dashboard": "控制台", + "navigation.developers": "開發者", + "navigation.direct_messages": "私人訊息", + "navigation.home": "首頁", + "navigation.invites": "邀請", + "navigation.notifications": "通知", + "navigation.search": "搜尋", + "navigation_bar.account_aliases": "帳戶別名", + "navigation_bar.account_migration": "帳戶遷移", "navigation_bar.blocks": "封鎖使用者", - "navigation_bar.compose": "撰寫新嘟文", - "navigation_bar.compose_direct": "Direct message", - "navigation_bar.compose_quote": "Quote post", - "navigation_bar.compose_reply": "Reply to post", + "navigation_bar.compose": "撰寫新帖文", + "navigation_bar.compose_direct": "發送私人訊息", + "navigation_bar.compose_edit": "編輯帖文", + "navigation_bar.compose_quote": "引用帖文", + "navigation_bar.compose_reply": "回覆帖文", "navigation_bar.domain_blocks": "隱藏的網域", "navigation_bar.favourites": "收藏", - "navigation_bar.filters": "靜音詞彙", - "navigation_bar.follow_requests": "關注請求", - "navigation_bar.import_data": "Import data", - "navigation_bar.in_reply_to": "In reply to", - "navigation_bar.invites": "Invites", + "navigation_bar.filters": "隱藏", + "navigation_bar.follow_requests": "追蹤請求", + "navigation_bar.import_data": "匯入資料", + "navigation_bar.in_reply_to": "回覆", + "navigation_bar.invites": "邀請", "navigation_bar.logout": "登出", - "navigation_bar.mutes": "靜音的使用者", + "navigation_bar.mutes": "隱藏的使用者", "navigation_bar.preferences": "偏好設定", - "navigation_bar.profile_directory": "Profile directory", - "navigation_bar.security": "安全性", - "navigation_bar.soapbox_config": "Soapbox config", - "notification.birthday": "{name} has a birthday today", - "notification.birthday.more": "{count} more {count, plural, one {friend} other {friends}}", - "notification.birthday_plural": "{name} and {more} have birthday today", - "notification.pleroma:chat_mention": "{name} sent you a message", - "notification.favourite": "{name} 把你的嘟文加入了最愛", - "notification.follow": "{name} 關注了你", - "notification.follow_request": "{name} has requested to follow you", + "navigation_bar.profile_directory": "發現更多帳戶", + "navigation_bar.soapbox_config": "Soapbox配置", + "notification.favourite": "{name} 讚了你的帖文", + "notification.follow": "{name} 追蹤了你", + "notification.follow_request": "{name} 請求追蹤你", "notification.mention": "{name} 提到了你", - "notification.move": "{name} moved to {targetName}", - "notification.pleroma:emoji_reaction": "{name} reacted to your post", - "notification.poll": "您投過的投票已經結束", - "notification.reblog": "{name}轉嘟了你的嘟文", - "notification.status": "{name} just posted", - "notifications.clear": "清除通知", - "notifications.clear_confirmation": "確定要永久清除你的通知嗎?", - "notifications.clear_heading": "Clear notifications", - "notifications.column_settings.alert": "桌面通知", - "notifications.column_settings.birthdays.category": "Birthdays", - "notifications.column_settings.birthdays.show": "Show birthday reminders", - "notifications.column_settings.emoji_react": "Emoji reacts:", - "notifications.column_settings.favourite": "最愛:", - "notifications.column_settings.filter_bar.advanced": "顯示所有分類", - "notifications.column_settings.filter_bar.category": "快速過濾欄", - "notifications.column_settings.filter_bar.show": "顯示", - "notifications.column_settings.follow": "新關注者:", - "notifications.column_settings.follow_request": "New follow requests:", - "notifications.column_settings.mention": "提及:", - "notifications.column_settings.move": "Moves:", - "notifications.column_settings.poll": "投票結果:", - "notifications.column_settings.push": "推送通知", - "notifications.column_settings.reblog": "轉嘟:", - "notifications.column_settings.show": "在欄位中顯示", - "notifications.column_settings.sound": "播放音效", - "notifications.column_settings.sounds": "Sounds", - "notifications.column_settings.sounds.all_sounds": "Play sound for all notifications", - "notifications.column_settings.title": "Notification settings", + "notification.mentioned": "{name} 提到了你", + "notification.move": "{name} 移動到了 {targetName}", + "notification.others": " + {count} 其他通知", + "notification.pleroma:chat_mention": "{name} 給你發送了訊息", + "notification.pleroma:emoji_reaction": "{name} 用表情回應了你的帖文", + "notification.poll": "你參與的一項投票已經結束", + "notification.reblog": "{name} 轉帖了你的帖文", + "notification.status": "{name} 剛剛發帖", + "notification.update": "{name} 編輯了你參與互動的帖文", + "notification.user_approved": "歡迎來到 {instance}!", "notifications.filter.all": "全部", - "notifications.filter.boosts": "轉嘟", - "notifications.filter.emoji_reacts": "Emoji reacts", + "notifications.filter.boosts": "轉帖", + "notifications.filter.emoji_reacts": "Emoji心情回應", "notifications.filter.favourites": "最愛", - "notifications.filter.follows": "關注的使用者", + "notifications.filter.follows": "追蹤的使用者", "notifications.filter.mentions": "提及", - "notifications.filter.moves": "Moves", + "notifications.filter.moves": "移動", "notifications.filter.polls": "投票結果", - "notifications.filter.statuses": "Updates from people you follow", + "notifications.filter.statuses": "來自追蹤的人的更新", "notifications.group": "{count} 條通知", - "notifications.queue_label": "Click to see {count} new {count, plural, one {notification} other {notifications}}", - "onboarding.avatar.subtitle": "Just have fun with it.", - "onboarding.avatar.title": "Choose a profile picture", - "onboarding.display_name.subtitle": "You can always edit this later.", - "onboarding.display_name.title": "Choose a display name", - "onboarding.done": "Done", - "onboarding.finished.message": "We are very excited to welcome you to our community! Tap the button below to get started.", - "onboarding.finished.title": "Onboarding complete", - "onboarding.header.subtitle": "This will be shown at the top of your profile.", - "onboarding.header.title": "Pick a cover image", - "onboarding.next": "Next", - "onboarding.note.subtitle": "You can always edit this later.", - "onboarding.note.title": "Write a short bio", - "onboarding.saving": "Saving…", - "onboarding.skip": "Skip for now", - "onboarding.suggestions.subtitle": "Here are a few of the most popular accounts you might like.", - "onboarding.suggestions.title": "Suggested accounts", - "onboarding.view_feed": "View Feed", - "password_reset.confirmation": "Check your email for confirmation.", - "password_reset.fields.username_placeholder": "Email or username", - "password_reset.reset": "Reset password", - "patron.donate": "Donate", - "patron.title": "Funding Goal", - "pinned_accounts.title": "{name}’s choices", - "pinned_statuses.none": "No pins to show.", + "notifications.queue_label": "點擊查看 {count} 條新通知", + "oauth_consumer.tooltip": "通過 {provider} 登入", + "oauth_consumers.title": "更多登入方式", + "onboarding.avatar.subtitle": "祝你玩得開心!", + "onboarding.avatar.title": "設定你的頭像", + "onboarding.display_name.subtitle": "你可以稍後更改", + "onboarding.display_name.title": "設定你的顯示名稱", + "onboarding.done": "完成", + "onboarding.finished.message": "我們很高興歡迎您加入我們的社區! 點擊下面的按鈕,讓我們開始吧!", + "onboarding.finished.title": "新手教程完成", + "onboarding.header.subtitle": "這將顯示在你個人資料的上方。", + "onboarding.header.title": "選擇封面圖片", + "onboarding.next": "下一步", + "onboarding.note.subtitle": "你可以稍後更改", + "onboarding.note.title": "簡短地介紹下自己", + "onboarding.saving": "儲存中…", + "onboarding.skip": "現在跳過", + "onboarding.suggestions.subtitle": "以下是幾個受歡迎的帳戶,你可能會喜歡", + "onboarding.suggestions.title": "推薦帳戶", + "onboarding.view_feed": "檢視列表", + "password_reset.confirmation": "請查閲確認電郵", + "password_reset.fields.username_placeholder": "電郵地址或帳戶名稱", + "password_reset.reset": "重設密碼", + "patron.donate": "抖內", + "patron.title": "籌集目標", + "pinned_accounts.title": "{name} 的釘選", + "pinned_statuses.none": "沒有釘選的帖文", + "poll.choose_multiple": "盡情選擇你所感興趣的", "poll.closed": "已關閉", + "poll.non_anonymous": "公共投票", + "poll.non_anonymous.label": "其他的實例可能可以見到你投票的選擇", "poll.refresh": "重新整理", - "poll.total_votes": "{count, plural, one {# 個投票} other {# 個投票}}", + "poll.total_people": "還有 {count} 人", + "poll.total_votes": "投票", "poll.vote": "投票", - "poll.voted": "You voted for this answer", - "poll.votes": "{votes, plural, one {# vote} other {# votes}}", - "poll_button.add_poll": "建立投票", + "poll.voted": "你投票給了這個選項", + "poll.votes": "投票", + "poll_button.add_poll": "發起投票", "poll_button.remove_poll": "移除投票", - "pre_header.close": "Close", - "preferences.fields.auto_play_gif_label": "Auto-play animated GIFs", - "preferences.fields.autoload_more_label": "Automatically load more items when scrolled to the bottom of the page", - "preferences.fields.autoload_timelines_label": "Automatically load new posts when scrolled to the top of the page", - "preferences.fields.boost_modal_label": "Show confirmation dialog before reposting", - "preferences.fields.delete_modal_label": "Show confirmation dialog before deleting a post", - "preferences.fields.display_media.default": "Hide media marked as sensitive", - "preferences.fields.display_media.hide_all": "Always hide media", - "preferences.fields.display_media.show_all": "Always show media", - "preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings", - "preferences.fields.language_label": "Language", - "preferences.fields.media_display_label": "Media display", - "preferences.hints.feed": "In your home feed", + "preferences.fields.auto_play_gif_label": "自動播放GIF", + "preferences.fields.autoload_more_label": "滾動至時間軸底部自動載入更多帖文", + "preferences.fields.autoload_timelines_label": "滾動至時間軸頂部自動載入更多新帖", + "preferences.fields.boost_modal_label": "轉帖前顯示確認提示", + "preferences.fields.delete_modal_label": "刪除帖文前顯示確認提示", + "preferences.fields.display_media.default": "隱藏被標記為敏感內容的媒體", + "preferences.fields.display_media.hide_all": "始終隱藏所有媒體", + "preferences.fields.display_media.show_all": "始終顯示所有媒體", + "preferences.fields.expand_spoilers_label": "始終展開標有內容警告的帖文", + "preferences.fields.language_label": "語言", + "preferences.fields.media_display_label": "媒體顯示", + "preferences.fields.theme": "主題", + "preferences.hints.feed": "在你的主頁信息流中", "privacy.change": "調整隱私狀態", "privacy.direct.long": "只有被提到的使用者能看到", - "privacy.direct.short": "私訊", - "privacy.private.long": "只有關注你的使用者能看到", - "privacy.private.short": "僅關注者", - "privacy.public.long": "嘟到公開時間軸", - "privacy.public.short": "公開", - "privacy.unlisted.long": "公開,但不會顯示在公開時間軸", - "privacy.unlisted.short": "不公開", - "profile_dropdown.add_account": "Add an existing account", - "profile_dropdown.logout": "Log out @{acct}", - "profile_fields_panel.title": "Profile fields", - "public.column_settings.title": "Fediverse timeline settings", - "reactions.all": "All", + "privacy.direct.short": "僅被提及的使用者", + "privacy.private.long": "只有追蹤你的使用者能看到", + "privacy.private.short": "僅追蹤者", + "privacy.public.long": "所有人可見,出現在公共時間軸上", + "privacy.public.short": "公共時間軸", + "privacy.unlisted.long": "公開,但不會顯示在公共時間軸", + "privacy.unlisted.short": "所有人", + "profile_dropdown.add_account": "新增已有帳戶", + "profile_dropdown.logout": "登出 @{acct}", + "profile_dropdown.theme": "主題", + "profile_fields_panel.title": "個人資料字段", + "public.column_settings.title": "聯邦宇宙工具時間軸設定", + "reactions.all": "全部", "regeneration_indicator.label": "載入中…", - "regeneration_indicator.sublabel": "你的主頁時間軸正在準備中!", - "register_invite.lead": "Complete the form below to create an account.", - "register_invite.title": "You've been invited to join {siteTitle}!", - "registration.agreement": "I agree to the {tos}.", - "registration.captcha.hint": "Click the image to get a new captcha", - "registration.closed_message": "{instance} is not accepting new members", - "registration.closed_title": "Registrations Closed", - "registration.confirmation_modal.close": "Close", - "registration.fields.confirm_placeholder": "Password (again)", - "registration.fields.email_placeholder": "E-Mail address", - "registration.fields.password_placeholder": "Password", - "registration.fields.username_hint": "Only letters, numbers, and underscores are allowed.", - "registration.fields.username_placeholder": "Username", - "registration.newsletter": "Subscribe to newsletter.", - "registration.password_mismatch": "Passwords don't match.", - "registration.reason": "Why do you want to join?", - "registration.reason_hint": "This will help us review your application", - "registration.sign_up": "Sign up", - "registration.tos": "Terms of Service", - "registration.privacy": "Privacy Policy", - "registration.acceptance": "By registering, you agree to the {terms} and {privacy}.", - "registration.username_unavailable": "Username is already taken.", - "relative_time.days": "{number} 天", - "relative_time.hours": "{number} 小時", + "regeneration_indicator.sublabel": "你的主頁時間軸正在準備中!", + "register_invite.lead": "填寫下列表單以註冊帳戶", + "register_invite.title": "你已被邀請加入 {siteTitle}!", + "registration.acceptance": "註冊同時意味着你已經同意本站的 {terms} 和 {privacy}.", + "registration.agreement": "我同意本站用戶條款 {tos}.", + "registration.captcha.hint": "點擊圖像以重載驗證碼", + "registration.captcha.placeholder": "輸入照片中顯示的文字", + "registration.closed_message": "{instance} 公開註冊暫時關閉", + "registration.closed_title": "暫停註冊", + "registration.confirmation_modal.close": "關閉", + "registration.fields.confirm_placeholder": "再次輸入密碼", + "registration.fields.email_placeholder": "電郵地址", + "registration.fields.password_placeholder": "密碼", + "registration.fields.username_hint": "只能使用英文字母、數字和下橫線", + "registration.fields.username_placeholder": "帳戶名稱", + "registration.header": "創建你的帳戶", + "registration.newsletter": "訂閲我們的新聞郵件", + "registration.password_mismatch": "密碼不匹配", + "registration.privacy": "隱私條款", + "registration.reason": "你為什麼想要註冊本站?", + "registration.reason_hint": "認真填寫將能加快你通過註冊的速度", + "registration.sign_up": "註冊", + "registration.tos": "用戶條款", + "registration.username_unavailable": "帳戶名稱已被使用", + "registration.validation.capital_letter": "1 個大寫字母", + "registration.validation.lowercase_letter": "1 個小寫字母", + "registration.validation.minimum_characters": "8 個字符", + "registrations.create_account": "創建新帳戶", + "registrations.error": "創建你的帳戶失敗,請聯絡管理員.", + "registrations.get_started": "讓我們開始吧!", + "registrations.success": "歡迎來到 {siteTitle}!", + "registrations.tagline": "一個沒有言論審查的社交平台", + "registrations.unprocessable_entity": "此用戶名已被佔用", + "registrations.username.hint": "只能包含數字、字母和下橫線", + "relative_time.days": "{number}天", + "relative_time.hours": "{number}小時", "relative_time.just_now": "剛剛", - "relative_time.minutes": "{number} 分", - "relative_time.seconds": "{number} 秒", - "remote_instance.edit_federation": "Edit federation", - "remote_instance.federation_panel.heading": "Federation Restrictions", - "remote_instance.federation_panel.no_restrictions_message": "{siteTitle} has placed no restrictions on {host}.", - "remote_instance.federation_panel.restricted_message": "{siteTitle} blocks all activities from {host}.", - "remote_instance.federation_panel.some_restrictions_message": "{siteTitle} has placed some restrictions on {host}.", - "remote_instance.pin_host": "Pin {host}", - "remote_instance.unpin_host": "Unpin {host}", - "remote_interaction.account_placeholder": "Enter your username@domain you want to act from", - "remote_interaction.divider": "or", - "remote_interaction.favourite": "Proceed to like", - "remote_interaction.favourite_title": "Like a post remotely", - "remote_interaction.follow": "Proceed to follow", - "remote_interaction.follow_title": "Follow {user} remotely", - "remote_interaction.poll_vote": "Proceed to vote", - "remote_interaction.poll_vote_title": "Vote in a poll remotely", - "remote_interaction.reblog": "Proceed to repost", - "remote_interaction.reblog_title": "Reblog a post remotely", - "remote_interaction.reply": "Proceed to reply", - "remote_interaction.reply_title": "Reply to a post remotely", - "remote_interaction.user_not_found_error": "Couldn't find given user", - "remote_timeline.filter_message": "You are viewing the timeline of {instance}.", + "relative_time.minutes": "{number}分", + "relative_time.seconds": "{number}秒", + "remote_instance.edit_federation": "編輯聯邦設定", + "remote_instance.federation_panel.heading": "聯邦限制", + "remote_instance.federation_panel.no_restrictions_message": "{siteTitle} 未對 {host} 採取限制措施", + "remote_instance.federation_panel.restricted_message": "{siteTitle} 完全封鎖了 {host}", + "remote_instance.federation_panel.some_restrictions_message": "{siteTitle} 對 {host} 實施了部分限制", + "remote_instance.pin_host": "釘選 {host}", + "remote_instance.unpin_host": "取消釘選 {host}", + "remote_interaction.account_placeholder": "輸入您想採取行動的帳戶 (格式:帳戶名@網域)", + "remote_interaction.divider": "或", + "remote_interaction.favourite": "按讚", + "remote_interaction.favourite_title": "遠端按讚一條帖文", + "remote_interaction.follow": "開始追蹤", + "remote_interaction.follow_title": "遠端追蹤 {user}", + "remote_interaction.poll_vote": "投票", + "remote_interaction.poll_vote_title": "遠端參與投票", + "remote_interaction.reblog": "轉帖", + "remote_interaction.reblog_title": "遠端轉帖", + "remote_interaction.reply": "回覆", + "remote_interaction.reply_title": "遠端回覆", + "remote_interaction.user_not_found_error": "找不到該帳戶", + "remote_timeline.filter_message": "你正在查看 {instance} 的時間軸", "reply_indicator.cancel": "取消", - "reply_mentions.account.add": "Add to mentions", - "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.reply_empty": "Replying to post", - "report.block": "Block {target}", - "report.block_hint": "Do you also want to block this account?", + "reply_mentions.account.add": "添加到提及名單", + "reply_mentions.account.remove": "從提及名單中移除", + "reply_mentions.more": "添加 {count} 個", + "reply_mentions.reply": "回覆 {accounts}{more}", + "reply_mentions.reply_empty": "回覆帖文", + "report.block": "封鎖帳戶 {target}", + "report.block_hint": "你是否要封鎖這個帳戶呢?", + "report.confirmation.content": "如果我們發現此帳戶確實違反了 {link} ,我們會採取進一步的措施", + "report.confirmation.title": "感謝你提交的檢舉", + "report.done": "完成", "report.forward": "轉寄到 {target}", - "report.forward_hint": "這個帳戶屬於其他站點。要像該站點發送匿名的檢舉訊息嗎?", + "report.forward_hint": "這個帳戶屬於其他站點。要像該站點發送匿名的檢舉訊息嗎?", "report.hint": "這項訊息會發送到您伺服器的管理員。你可以提供檢舉這個帳戶的理由:", + "report.next": "下一步", + "report.otherActions.addAdditional": "你還想為你的檢舉添加更多狀態嗎?", + "report.otherActions.addMore": "添加更多", + "report.otherActions.furtherActions": "進一步措施:", + "report.otherActions.hideAdditional": "隱藏額外狀態", + "report.otherActions.otherStatuses": "包含其他狀態?", "report.placeholder": "更多訊息", + "report.reason.blankslate": "你已移除選中的所有狀態", + "report.reason.title": "檢舉原因", "report.submit": "送出", "report.target": "檢舉 {target}", - "reset_password.header": "Set New Password", - "schedule.post_time": "Post Date/Time", - "schedule.remove": "Remove schedule", - "schedule_button.add_schedule": "Schedule post for later", - "schedule_button.remove_schedule": "Post immediately", - "scheduled_status.cancel": "Cancel", - "search.action": "Search for “{query}”", + "reset_password.fail": "令牌已過期,請重試", + "reset_password.header": "設定新的密碼", + "schedule.post_time": "發佈時間", + "schedule.remove": "取消發佈", + "schedule_button.add_schedule": "定時發佈", + "schedule_button.remove_schedule": "取消定時發佈", + "scheduled_status.cancel": "取消", + "search.action": "搜尋 “{query}”", "search.placeholder": "搜尋", "search_results.accounts": "使用者", "search_results.hashtags": "主題標籤", - "search_results.statuses": "嘟文", - "search_results.top": "Top", - "security.codes.fail": "Failed to fetch backup codes", - "security.confirm.fail": "Incorrect code or password. Try again.", - "security.delete_account.fail": "Account deletion failed.", - "security.delete_account.success": "Account successfully deleted.", - "security.disable.fail": "Incorrect password. Try again.", - "security.disable_mfa": "Disable", - "security.fields.email.label": "Email address", - "security.fields.new_password.label": "New password", - "security.fields.old_password.label": "Current password", - "security.fields.password.label": "Password", - "security.fields.password_confirmation.label": "New password (again)", - "security.headers.delete": "Delete Account", - "security.headers.tokens": "Sessions", - "security.headers.update_email": "Change Email", - "security.headers.update_password": "Change Password", - "security.mfa": "Set up 2-Factor Auth", - "security.mfa_enabled": "You have multi-factor authentication set up with OTP.", - "security.mfa_header": "Authorization Methods", - "security.mfa_setup_hint": "Configure multi-factor authentication with OTP", - "security.qr.fail": "Failed to fetch setup key", - "security.submit": "Save changes", - "security.submit.delete": "Delete Account", - "security.text.delete": "To delete your account, enter your password then click Delete Account. This is a permanent action that cannot be undone. Your account will be destroyed from this server, and a deletion request will be sent to other servers. It's not guaranteed that all servers will purge your account.", - "security.tokens.revoke": "Revoke", - "security.update_email.fail": "Update email failed.", - "security.update_email.success": "Email successfully updated.", - "security.update_password.fail": "Update password failed.", - "security.update_password.success": "Password successfully updated.", - "settings.change_email": "Change Email", - "settings.change_password": "Change Password", - "settings.configure_mfa": "Configure MFA", - "settings.delete_account": "Delete Account", - "settings.edit_profile": "Edit Profile", - "settings.preferences": "Preferences", - "settings.profile": "Profile", - "settings.save.success": "Your preferences have been saved!", - "settings.security": "Security", - "settings.settings": "Settings", - "signup_panel.subtitle": "Sign up now to discuss what's happening.", - "signup_panel.title": "New to {site_title}?", - "snackbar.view": "View", - "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", - "soapbox_config.authenticated_profile_label": "Profiles require authentication", - "soapbox_config.copyright_footer.meta_fields.label_placeholder": "Copyright footer", - "soapbox_config.crypto_address.meta_fields.address_placeholder": "Address", - "soapbox_config.crypto_address.meta_fields.note_placeholder": "Note (optional)", - "soapbox_config.crypto_address.meta_fields.ticker_placeholder": "Ticker", - "soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "Number of items to display in the crypto homepage widget", + "search_results.statuses": "帖文", + "security.codes.fail": "恢復代碼錯誤", + "security.confirm.fail": "密碼錯誤,請重試。", + "security.delete_account.fail": "帳戶刪除失敗", + "security.delete_account.success": "帳戶刪除成功", + "security.disable.fail": "密碼錯誤,請重試。", + "security.fields.email.label": "電郵地址", + "security.fields.new_password.label": "輸入新密碼", + "security.fields.old_password.label": "輸入原密碼", + "security.fields.password.label": "密碼", + "security.fields.password_confirmation.label": "再次輸入新密碼", + "security.headers.delete": "刪除帳戶", + "security.headers.tokens": "會話", + "security.qr.fail": "加載密鑰失敗", + "security.submit": "儲存變更", + "security.submit.delete": "刪除帳戶", + "security.text.delete": "要刪除您的帳戶,請輸入您的密碼。注意:這是無法撤消的永久性操作!您的帳戶將從該服務器中銷毀,並將向其他服務器發送刪除請求。但你的資訊不一定會在其他站點上立即刪除。", + "security.tokens.revoke": "撤銷", + "security.update_email.fail": "更新電郵地址失敗", + "security.update_email.success": "電郵地址已更新", + "security.update_password.fail": "更新密碼失敗", + "security.update_password.success": "密碼已更新", + "settings.change_email": "更改電郵地址", + "settings.change_password": "更改密碼", + "settings.configure_mfa": "設定多重要素驗證 (MFA)", + "settings.delete_account": "刪除帳戶", + "settings.edit_profile": "編輯個人資料", + "settings.preferences": "首選項", + "settings.profile": "個人資料", + "settings.save.success": "你的設定已儲存", + "settings.security": "安全性", + "settings.sessions": "活動會話", + "settings.settings": "設定", + "shared.tos": "服務條款", + "signup_panel.subtitle": "註冊以參與討論", + "signup_panel.title": "初來乍到 {site_title} 嗎?", + "site_preview.preview": "預覽", + "snackbar.view": "檢視", + "soapbox_config.authenticated_profile_hint": "用戶必須登錄才能查看用戶個人資料上的回覆和媒體。", + "soapbox_config.authenticated_profile_label": "個人資料需要授權才能查看", + "soapbox_config.copyright_footer.meta_fields.label_placeholder": "版權頁底", + "soapbox_config.crypto_address.meta_fields.address_placeholder": "地址", + "soapbox_config.crypto_address.meta_fields.note_placeholder": "備註 (可選)", + "soapbox_config.crypto_address.meta_fields.ticker_placeholder": "幣種", + "soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "在主頁數字貨幣小部件中顯示的數量", "soapbox_config.custom_css.meta_fields.url_placeholder": "URL", - "soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.", - "soapbox_config.fields.accent_color_label": "Accent color", - "soapbox_config.fields.brand_color_label": "Brand color", - "soapbox_config.fields.crypto_address.add": "Add new crypto address", - "soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses", - "soapbox_config.fields.home_footer.add": "Add new Home Footer Item", - "soapbox_config.fields.home_footer_fields_label": "Home footer items", + "soapbox_config.display_fqn_label": "顯示本站帳戶的網域 (如 @帳戶名稱@網域) ", + "soapbox_config.fields.accent_color_label": "強調色", + "soapbox_config.fields.brand_color_label": "主題色", + "soapbox_config.fields.crypto_address.add": "添加數字貨幣地址", + "soapbox_config.fields.crypto_addresses_label": "數字貨幣地址", + "soapbox_config.fields.home_footer.add": "添加頁尾", + "soapbox_config.fields.home_footer_fields_label": "主頁頁眉", "soapbox_config.fields.logo_label": "Logo", - "soapbox_config.fields.promo_panel.add": "Add new Promo panel item", - "soapbox_config.fields.promo_panel_fields_label": "Promo panel items", - "soapbox_config.fields.theme_label": "Default theme", - "soapbox_config.greentext_label": "Enable greentext support", - "soapbox_config.hints.crypto_addresses": "Add cryptocurrency addresses so users of your site can donate to you. Order matters, and you must use lowercase ticker values.", - "soapbox_config.hints.home_footer_fields": "You can have custom defined links displayed on the footer of your static pages", - "soapbox_config.hints.logo": "SVG. At most 2 MB. Will be displayed to 50px height, maintaining aspect ratio", - "soapbox_config.hints.promo_panel_fields": "You can have custom defined links displayed on the right panel of the timelines page.", + "soapbox_config.fields.promo_panel.add": "添加時間軸頁底", + "soapbox_config.fields.promo_panel_fields_label": "時間軸頁底", + "soapbox_config.fields.theme_label": "默認主題", + "soapbox_config.greentext_label": "啟用greentext支援", + "soapbox_config.headings.advanced": "進階", + "soapbox_config.headings.cryptocurrency": "數字貨幣", + "soapbox_config.headings.navigation": "導航列", + "soapbox_config.headings.options": "選項", + "soapbox_config.headings.theme": "主題", + "soapbox_config.hints.crypto_addresses": "添加加密貨幣地址,以便您網站的用戶可以向您捐款。請注意順序,同時您必須使用小寫的幣種代碼。", + "soapbox_config.hints.home_footer_fields": "您可以在靜態頁面的頁腳顯示自定義鏈接(如about)。", + "soapbox_config.hints.logo": "SVG. 最多 2 MB。 將顯示到 50px 高度,保持縱橫比", + "soapbox_config.hints.promo_panel_fields": "您可以在時間線頁面的右側面板上顯示自定義鏈接。", "soapbox_config.hints.promo_panel_icons": "{ link }", - "soapbox_config.hints.promo_panel_icons.link": "Soapbox Icons List", - "soapbox_config.home_footer.meta_fields.label_placeholder": "Label", + "soapbox_config.hints.promo_panel_icons.link": "Soapbox圖示", + "soapbox_config.home_footer.meta_fields.label_placeholder": "標籤", "soapbox_config.home_footer.meta_fields.url_placeholder": "URL", - "soapbox_config.promo_panel.meta_fields.icon_placeholder": "Icon", - "soapbox_config.promo_panel.meta_fields.label_placeholder": "Label", + "soapbox_config.promo_panel.meta_fields.icon_placeholder": "圖示", + "soapbox_config.promo_panel.meta_fields.label_placeholder": "標籤", "soapbox_config.promo_panel.meta_fields.url_placeholder": "URL", - "soapbox_config.raw_json_hint": "Edit the settings data directly. Changes made directly to the JSON file will override the form fields above. Click Save to apply your changes.", - "soapbox_config.raw_json_label": "Advanced: Edit raw JSON data", - "soapbox_config.save": "Save", - "soapbox_config.saved": "Soapbox config saved!", - "soapbox_config.single_user_mode_hint": "Front page will redirect to a given user profile.", - "soapbox_config.single_user_mode_label": "Single user mode", - "soapbox_config.single_user_mode_profile_hint": "@handle", - "soapbox_config.single_user_mode_profile_label": "Main user handle", - "soapbox_config.verified_can_edit_name_label": "Allow verified users to edit their own display name.", - "status.actions.more": "More", + "soapbox_config.raw_json_hint": "直接編輯設置數據。 直接對 JSON 文件進行的更改將覆蓋上面的表單字段。 單擊保存以應用您的更改。", + "soapbox_config.raw_json_label": "高級: 編輯原始JSON", + "soapbox_config.save": "儲存更改", + "soapbox_config.saved": "Soapbox配置已儲存!", + "soapbox_config.single_user_mode_hint": "首頁將重定向到給定的用戶配置文件。", + "soapbox_config.single_user_mode_label": "單用戶模式", + "soapbox_config.single_user_mode_profile_hint": "@帳戶名稱", + "soapbox_config.single_user_mode_profile_label": "主帳戶的帳戶名稱", + "soapbox_config.verified_can_edit_name_label": "允許經過驗證的用戶編輯自己的顯示名稱。", + "sponsored.info.message": "{siteTitle} 展示廣告以使實例可持續運行", + "sponsored.info.title": "為什麼我會看到這條廣告?", + "sponsored.subtitle": "贊助", + "status.actions.more": "更多", "status.admin_account": "開啟 @{name} 的管理介面", - "status.admin_status": "在管理介面開啟此嘟文", + "status.admin_status": "在管理介面開啟此帖文", "status.block": "封鎖 @{name}", - "status.bookmark": "Bookmark", - "status.bookmarked": "Bookmark added.", - "status.cancel_reblog_private": "取消轉嘟", - "status.cannot_reblog": "這篇嘟文無法被轉嘟", - "status.chat": "Chat with @{name}", - "status.copy": "將連結複製到嘟文中", + "status.bookmark": "書籤", + "status.bookmarked": "書籤已添加", + "status.cancel_reblog_private": "取消轉帖", + "status.cannot_reblog": "這篇帖文無法被轉載", + "status.chat": "和 @{name} 聊天", + "status.copy": "將連結複製到帖文中", "status.delete": "刪除", "status.detailed_status": "對話的詳細內容", "status.direct": "發送私訊給 @{name}", + "status.edit": "編輯", + "status.edited": "已於 {date} 編輯", "status.embed": "嵌入", "status.favourite": "最愛", "status.filtered": "已過濾", @@ -983,89 +1019,102 @@ "status.media_hidden": "隱藏媒體內容", "status.mention": "提到 @{name}", "status.more": "更多", - "status.mute": "靜音 @{name}", - "status.mute_conversation": "靜音對話", - "status.open": "展開嘟文", + "status.mute": "隱藏 @{name}", + "status.mute_conversation": "隱藏對話", + "status.open": "展開帖文", "status.pin": "釘選到個人資料頁", - "status.pinned": "釘選的嘟文", - "status.quote": "Quote post", - "status.reactions.cry": "Sad", - "status.reactions.empty": "No one has reacted to this post yet. When someone does, they will show up here.", - "status.reactions.heart": "Love", - "status.reactions.laughing": "Haha", - "status.reactions.like": "Like", - "status.reactions.open_mouth": "Wow", - "status.reactions.weary": "Weary", - "status.reactions_expand": "Select emoji", + "status.pinned": "釘選的帖文", + "status.quote": "引用帖文", + "status.reactions.cry": "傷心", + "status.reactions.empty": "尚未有人回應心情", + "status.reactions.heart": "愛", + "status.reactions.laughing": "哈哈哈", + "status.reactions.like": "讚哦", + "status.reactions.open_mouth": "哇!", + "status.reactions.weary": "疲憊", + "status.reactions_expand": "選擇心情", "status.read_more": "閱讀更多", - "status.reblog": "轉嘟", - "status.reblog_private": "轉嘟給原有關注者", - "status.reblogged_by": "{name} 轉嘟了", - "status.reblogs.empty": "還沒有人轉嘟。如果有,會顯示在這裡。", + "status.reblog": "轉帖", + "status.reblog_private": "轉帖給原有追蹤者", + "status.reblogged_by": "{name} 轉帖了", + "status.reblogs.empty": "還沒有人轉帖。如果有,會顯示在這裡。", "status.redraft": "刪除 & 編輯", - "status.remove_account_from_group": "Remove account from group", - "status.remove_post_from_group": "Remove post from group", + "status.remove_account_from_group": "將帳戶移出群組", + "status.remove_post_from_group": "將帖文移出群組", "status.reply": "回覆", "status.replyAll": "回覆所有人", "status.report": "檢舉 @{name}", "status.sensitive_warning": "敏感內容", "status.share": "分享", "status.show_less": "減少顯示", - "status.show_less_all": "減少顯示這類嘟文", + "status.show_less_all": "減少顯示這類帖文", "status.show_more": "顯示更多", - "status.show_more_all": "顯示更多這類嘟文", - "status.title": "Post", - "status.title_direct": "Direct message", - "status.unbookmark": "Remove bookmark", - "status.unbookmarked": "Bookmark removed.", - "status.unmute_conversation": "解除此對話的靜音", - "status.unpin": "解除置頂", - "status_list.queue_label": "Click to see {count} new {count, plural, one {post} other {posts}}", - "statuses.quote_tombstone": "Post is unavailable.", - "statuses.tombstone": "One or more posts are unavailable.", + "status.show_more_all": "顯示更多這類帖文", + "status.title": "帖文", + "status.title_direct": "私訊", + "status.unbookmark": "移除書籤", + "status.unbookmarked": "書籤已移除", + "status.unmute_conversation": "解除此對話的隱藏", + "status.unpin": "解除釘選", + "status.sensitive_warning.subtitle": "這則貼文可能含有不宜觀看的消息", + "status.sensitive_warning.action": "顯示", + "status_list.queue_label": "點選查看 {count} 個新帖文", + "statuses.quote_tombstone": "帖文不可用", + "statuses.tombstone": "部分帖文不可見", + "streamfield.add": "新增", + "streamfield.remove": "移除", "suggestions.dismiss": "關閉建議", - "tabs_bar.all": "All", - "tabs_bar.chats": "Chats", - "tabs_bar.dashboard": "Dashboard", - "tabs_bar.fediverse": "Fediverse", + "tabs_bar.all": "全部", + "tabs_bar.chats": "對話", + "tabs_bar.dashboard": "控制台", + "tabs_bar.fediverse": "聯邦宇宙", "tabs_bar.home": "主頁", - "tabs_bar.more": "More", + "tabs_bar.more": "更多", "tabs_bar.notifications": "通知", - "tabs_bar.post": "Post", - "tabs_bar.profile": "Profile", + "tabs_bar.profile": "個人資料", "tabs_bar.search": "搜尋", - "tabs_bar.settings": "Settings", - "tabs_bar.theme_toggle_dark": "Switch to dark theme", - "tabs_bar.theme_toggle_light": "Switch to light theme", + "tabs_bar.settings": "設定", + "tabs_bar.switch_accounts": "切換帳戶", + "tabs_bar.theme_toggle_dark": "切換為深色主題", + "tabs_bar.theme_toggle_light": "切換為淺色主題", + "theme_toggle.dark": "深色", + "theme_toggle.light": "淺色", + "theme_toggle.system": "系統", + "thread_login.login": "登入", + "thread_login.message": "加入 {siteTitle} 以獲取完整資訊", + "thread_login.signup": "註冊", + "thread_login.title": "繼續這個對話", "time_remaining.days": "剩餘{number, plural, one {# 天數} other {# 天數}}", "time_remaining.hours": "剩餘{number, plural, one {# 小時} other {# 小時}}", "time_remaining.minutes": "剩餘{number, plural, one {# 分鐘} other {# 分鐘}}", "time_remaining.moments": "剩餘時間", "time_remaining.seconds": "剩餘 {number, plural, one {# 秒} other {# 秒}}", "trends.count_by_accounts": "{count} 位使用者在討論", - "trends.title": "Trends", - "ui.beforeunload": "如果離開 Soapbox,你的草稿將會不見。", - "unauthorized_modal.text": "You need to be logged in to do that.", - "unauthorized_modal.title": "Sign up for {site_title}", + "trends.title": "趨勢", + "trendsPanel.viewAll": "顯示全部", + "ui.beforeunload": "如果離開,你的草稿將會丟失。", + "unauthorized_modal.text": "你需要登入才能繼續", + "unauthorized_modal.title": "註冊 {site_title} 帳戶", "upload_area.title": "拖放來上傳", "upload_button.label": "上傳媒體檔案 (JPEG, PNG, GIF, WebM, MP4, MOV)", - "upload_error.image_size_limit": "Image exceeds the current file size limit ({limit})", + "upload_error.image_size_limit": "圖片超出當前文件大小限制 ({limit})", "upload_error.limit": "已達到檔案上傳限制。", "upload_error.poll": "不允許在投票上傳檔案。", - "upload_error.video_size_limit": "Video exceeds the current file size limit ({limit})", + "upload_error.video_duration_limit": "影片超出當前時長限制 ({limit} 秒)", + "upload_error.video_size_limit": "影片超出當前文件大小限制 ({limit})", "upload_form.description": "為視障人士增加文字說明", - "upload_form.preview": "Preview", + "upload_form.preview": "預覽", "upload_form.undo": "刪除", "upload_progress.label": "上傳中...", "video.close": "關閉影片", - "video.download": "Download file", + "video.download": "下載", "video.exit_fullscreen": "退出全螢幕", "video.expand": "展開影片", "video.fullscreen": "全螢幕", "video.hide": "隱藏影片", - "video.mute": "靜音", + "video.mute": "隱藏", "video.pause": "暫停", "video.play": "播放", - "video.unmute": "解除靜音", - "who_to_follow.title": "Who To Follow" -} \ No newline at end of file + "video.unmute": "解除隱藏", + "who_to_follow.title": "推薦追蹤" +} diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index 30cfd512d..2c3bc7c0d 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -44,6 +44,7 @@ export const AccountRecord = ImmutableRecord({ location: '', locked: false, moved: null as EmbeddedEntity, + mute_expires_at: null as string | null, note: '', pleroma: ImmutableMap(), source: ImmutableMap(), diff --git a/app/soapbox/normalizers/status.ts b/app/soapbox/normalizers/status.ts index ea971c52f..41ebacfc7 100644 --- a/app/soapbox/normalizers/status.ts +++ b/app/soapbox/normalizers/status.ts @@ -63,6 +63,7 @@ export const StatusRecord = ImmutableRecord({ hidden: false, search_index: '', spoilerHtml: '', + translation: null as ImmutableMap | null, }); const normalizeAttachments = (status: ImmutableMap) => { diff --git a/app/soapbox/precheck.ts b/app/soapbox/precheck.ts index 79d523d87..03fea80a1 100644 --- a/app/soapbox/precheck.ts +++ b/app/soapbox/precheck.ts @@ -3,10 +3,10 @@ * @module soapbox/precheck */ -/** Whether pre-rendered data exists in Mastodon's format. */ +/** Whether pre-rendered data exists in Pleroma's format. */ const hasPrerenderPleroma = Boolean(document.getElementById('initial-results')); -/** Whether pre-rendered data exists in Pleroma's format. */ +/** Whether pre-rendered data exists in Mastodon's format. */ const hasPrerenderMastodon = Boolean(document.getElementById('initial-state')); /** Whether initial data was loaded into the page by server-side-rendering (SSR). */ diff --git a/app/soapbox/react-notification/defaultPropTypes.js b/app/soapbox/react-notification/defaultPropTypes.js deleted file mode 100644 index 1a3dd9d4e..000000000 --- a/app/soapbox/react-notification/defaultPropTypes.js +++ /dev/null @@ -1,31 +0,0 @@ -import PropTypes from 'prop-types'; - -export default { - message: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.element, - ]).isRequired, - action: PropTypes.oneOfType([ - PropTypes.bool, - PropTypes.string, - PropTypes.node, - ]), - onClick: PropTypes.func, - style: PropTypes.bool, - actionStyle: PropTypes.object, - titleStyle: PropTypes.object, - barStyle: PropTypes.object, - activeBarStyle: PropTypes.object, - dismissAfter: PropTypes.oneOfType([ - PropTypes.bool, - PropTypes.number, - ]), - onDismiss: PropTypes.func, - className: PropTypes.string, - activeClassName: PropTypes.string, - isActive: PropTypes.bool, - title: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.node, - ]), -}; diff --git a/app/soapbox/react-notification/index.d.ts b/app/soapbox/react-notification/index.d.ts deleted file mode 100644 index 22f0211c8..000000000 --- a/app/soapbox/react-notification/index.d.ts +++ /dev/null @@ -1,88 +0,0 @@ -declare module 'soapbox/react-notification' { - import { Component, ReactElement } from 'react'; - - interface StyleFactoryFn { - (index: number, style: object | void, notification: NotificationProps): object; - } - - interface OnClickNotificationProps { - /** - * Callback function to run when the action is clicked. - * @param notification Notification currently being clicked - * @param deactivate Function that can be called to set the notification to inactive. - * Used to activate notification exit animation on click. - */ - onClick?(notification: NotificationProps, deactivate: () => void): void; - } - - interface NotificationProps extends OnClickNotificationProps { - /** The name of the action, e.g., "close" or "undo". */ - action?: string; - /** Custom action styles. */ - actionStyle?: object; - /** Custom snackbar styles when the bar is active. */ - activeBarStyle?: object; - /** - * Custom class to apply to the top-level component when active. - * @default 'notification-bar-active' - */ - activeClassName?: string; - /** Custom snackbar styles. */ - barStyle?: object; - /** Custom class to apply to the top-level component. */ - className?: string; - /** - * Timeout for onDismiss event. - * @default 2000 - */ - dismissAfter?: boolean | number; - /** - * If true, the notification is visible. - * @default false - */ - isActive?: boolean; - /** The message or component for the notification. */ - message: string | ReactElement; - /** Setting this prop to `false` will disable all inline styles. */ - style?: boolean; - /** The title for the notification. */ - title?: string | ReactElement; - /** Custom title styles. */ - titleStyle?: object; - - /** - * Callback function to run when dismissAfter timer runs out - * @param notification Notification currently being dismissed. - */ - onDismiss?(notification: NotificationProps): void; - } - - interface NotificationStackProps extends OnClickNotificationProps { - /** Create the style of the actions. */ - actionStyleFactory?: StyleFactoryFn; - /** Create the style of the active notification. */ - activeBarStyleFactory?: StyleFactoryFn; - /** Create the style of the notification. */ - barStyleFactory?: StyleFactoryFn; - /** - * If false, notification dismiss timers start immediately. - * @default true - */ - dismissInOrder?: boolean; - /** Array of notifications to render. */ - notifications: NotificationObject[]; - /** - * Callback function to run when dismissAfter timer runs out - * @param notification Notification currently being dismissed. - */ - onDismiss?(notification: NotificationObject): void; - } - - export interface NotificationObject extends NotificationProps { - key: number | string; - } - - export class Notification extends Component {} - - export class NotificationStack extends Component {} -} diff --git a/app/soapbox/react-notification/index.js b/app/soapbox/react-notification/index.js deleted file mode 100644 index 3d7da7cee..000000000 --- a/app/soapbox/react-notification/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as Notification } from './notification'; -export { default as NotificationStack } from './notificationStack'; diff --git a/app/soapbox/react-notification/notification.js b/app/soapbox/react-notification/notification.js deleted file mode 100644 index ab1cddf9b..000000000 --- a/app/soapbox/react-notification/notification.js +++ /dev/null @@ -1,175 +0,0 @@ -/* linting temp disabled while working on updates */ -/* eslint-disable */ -import React, { Component } from 'react'; -import defaultPropTypes from './defaultPropTypes'; - -class Notification extends Component { - constructor(props) { - super(props); - - this.getBarStyle = this.getBarStyle.bind(this); - this.getActionStyle = this.getActionStyle.bind(this); - this.getTitleStyle = this.getTitleStyle.bind(this); - this.handleClick = this.handleClick.bind(this); - - if (props.onDismiss && props.isActive) { - this.dismissTimeout = setTimeout( - props.onDismiss, - props.dismissAfter - ); - } - } - - componentWillReceiveProps(nextProps) { - if (nextProps.dismissAfter === false) return; - - // See http://eslint.org/docs/rules/no-prototype-builtins - if (!{}.hasOwnProperty.call(nextProps, 'isLast')) { - clearTimeout(this.dismissTimeout); - } - - if (nextProps.onDismiss) { - if ( - (nextProps.isActive && !this.props.isActive) || - (nextProps.dismissAfter && this.props.dismissAfter === false) - ) { - this.dismissTimeout = setTimeout( - nextProps.onDismiss, - nextProps.dismissAfter - ); - } - } - } - - componentWillUnmount() { - if (this.props.dismissAfter) clearTimeout(this.dismissTimeout); - } - - /* - * @description Dynamically get the styles for the bar. - * @returns {object} result The style. - */ - getBarStyle() { - if (this.props.style === false) return {}; - - const { isActive, barStyle, activeBarStyle } = this.props; - - const baseStyle = { - position: 'fixed', - bottom: '2rem', - left: '-100%', - width: 'auto', - padding: '1rem', - margin: 0, - color: '#fafafa', - font: '1rem normal Roboto, sans-serif', - borderRadius: '5px', - background: '#212121', - borderSizing: 'border-box', - boxShadow: '0 0 1px 1px rgba(10, 10, 11, .125)', - cursor: 'default', - WebKitTransition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)', - MozTransition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)', - msTransition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)', - OTransition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)', - transition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)', - WebkitTransform: 'translatez(0)', - MozTransform: 'translatez(0)', - msTransform: 'translatez(0)', - OTransform: 'translatez(0)', - transform: 'translatez(0)' - }; - - return isActive ? - Object.assign({}, baseStyle, { left: '1rem' }, barStyle, activeBarStyle) : - Object.assign({}, baseStyle, barStyle); - } - - /* - * @function getActionStyle - * @description Dynamically get the styles for the action text. - * @returns {object} result The style. - */ - getActionStyle() { - return this.props.style !== false ? Object.assign({}, { - padding: '0.125rem', - marginLeft: '1rem', - color: '#f44336', - font: '.75rem normal Roboto, sans-serif', - lineHeight: '1rem', - letterSpacing: '.125ex', - textTransform: 'uppercase', - borderRadius: '5px', - cursor: 'pointer' - }, this.props.actionStyle) : {}; - } - - /* - * @function getTitleStyle - * @description Dynamically get the styles for the title. - * @returns {object} result The style. - */ - getTitleStyle() { - return this.props.style !== false ? Object.assign({}, { - fontWeight: '700', - marginRight: '.5rem' - }, this.props.titleStyle) : {}; - } - - /* - * @function handleClick - * @description Handle click events on the action button. - */ - handleClick() { - if (this.props.onClick && typeof this.props.onClick === 'function') { - return this.props.onClick(); - } - } - - render() { - let className = 'notification-bar'; - - if (this.props.isActive) className += ` ${this.props.activeClassName}`; - if (this.props.className) className += ` ${this.props.className}`; - - return ( -
    -
    - {this.props.title ? ( - - {this.props.title} - - ) : null} - - {/* eslint-disable */} - - {this.props.message} - - - {this.props.action ? ( - - {this.props.action} - - ) : null} -
    -
    - ); - } -} - -Notification.propTypes = defaultPropTypes; - -Notification.defaultProps = { - isActive: false, - dismissAfter: 2000, - activeClassName: 'notification-bar-active' -}; - -export default Notification; diff --git a/app/soapbox/react-notification/notificationStack.js b/app/soapbox/react-notification/notificationStack.js deleted file mode 100644 index dc9c2459b..000000000 --- a/app/soapbox/react-notification/notificationStack.js +++ /dev/null @@ -1,95 +0,0 @@ -/* linting temp disabled while working on updates */ -/* eslint-disable */ -import React from 'react'; -import PropTypes from 'prop-types'; -import StackedNotification from './stackedNotification'; -import defaultPropTypes from './defaultPropTypes'; - -function defaultBarStyleFactory(index, style) { - return Object.assign( - {}, - style, - { bottom: `${2 + (index * 4)}rem` } - ); -} - -function defaultActionStyleFactory(index, style) { - return Object.assign( - {}, - style, - {} - ); -} - -/** -* The notification list does not have any state, so use a -* pure function here. It just needs to return the stacked array -* of notification components. -*/ -const NotificationStack = props => ( -
    - {props.notifications.map((notification, index) => { - const isLast = index === 0 && props.notifications.length === 1; - const dismissNow = isLast || !props.dismissInOrder; - - // Handle styles - const barStyle = props.barStyleFactory(index, notification.barStyle, notification); - const actionStyle = props.actionStyleFactory(index, notification.actionStyle, notification); - const activeBarStyle = props.activeBarStyleFactory( - index, - notification.activeBarStyle, - notification - ); - - // Allow onClick from notification stack or individual notifications - const onClick = notification.onClick || props.onClick; - const onDismiss = props.onDismiss; - - let { dismissAfter } = notification; - - if (dismissAfter !== false) { - if (dismissAfter == null) dismissAfter = props.dismissAfter; - if (!dismissNow) dismissAfter += index * 1000; - } - - return ( - - ); - })} -
    -); - -/* eslint-disable react/no-unused-prop-types, react/forbid-prop-types */ -NotificationStack.propTypes = { - activeBarStyleFactory: PropTypes.func, - barStyleFactory: PropTypes.func, - actionStyleFactory: PropTypes.func, - dismissInOrder: PropTypes.bool, - notifications: PropTypes.array.isRequired, - onDismiss: PropTypes.func.isRequired, - onClick: PropTypes.func, - action: defaultPropTypes.action -}; - -NotificationStack.defaultProps = { - activeBarStyleFactory: defaultBarStyleFactory, - barStyleFactory: defaultBarStyleFactory, - actionStyleFactory: defaultActionStyleFactory, - dismissInOrder: true, - dismissAfter: 1000, - onClick: () => {} -}; -/* eslint-enable no-alert, no-console */ - -export default NotificationStack; diff --git a/app/soapbox/react-notification/stackedNotification.js b/app/soapbox/react-notification/stackedNotification.js deleted file mode 100644 index c8d7200d4..000000000 --- a/app/soapbox/react-notification/stackedNotification.js +++ /dev/null @@ -1,69 +0,0 @@ -/* linting temp disabled while working on updates */ -/* eslint-disable */ -import React, { Component } from 'react'; -import defaultPropTypes from './defaultPropTypes'; -import Notification from './notification'; - -class StackedNotification extends Component { - constructor(props) { - super(props); - - this.state = { - isActive: false - }; - - this.handleClick = this.handleClick.bind(this); - } - - componentDidMount() { - this.activeTimeout = setTimeout(this.setState.bind(this, { - isActive: true - }), 1); - - this.dismiss(this.props.dismissAfter); - } - - componentWillReceiveProps(nextProps) { - if (nextProps.dismissAfter !== this.props.dismissAfter) { - this.dismiss(nextProps.dismissAfter); - } - } - - componentWillUnmount() { - clearTimeout(this.activeTimeout); - clearTimeout(this.dismissTimeout); - } - - dismiss(dismissAfter) { - if (dismissAfter === false) return; - - this.dismissTimeout = setTimeout(this.setState.bind(this, { - isActive: false - }), dismissAfter); - } - - /* - * @function handleClick - * @description Bind deactivate Notification function to Notification click handler - */ - handleClick() { - if (this.props.onClick && typeof this.props.onClick === 'function') { - return this.props.onClick(this.setState.bind(this, { isActive: false })); - } - } - - render() { - return ( - setTimeout(this.props.onDismiss, 300)} - isActive={this.state.isActive} - /> - ); - } -} - -StackedNotification.propTypes = defaultPropTypes; - -export default StackedNotification; diff --git a/app/soapbox/reducers/__tests__/group_editor.test.ts b/app/soapbox/reducers/__tests__/group_editor.test.ts deleted file mode 100644 index 516b6df43..000000000 --- a/app/soapbox/reducers/__tests__/group_editor.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import reducer from '../group_editor'; - -describe('group_editor reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(ImmutableMap({ - groupId: null, - isSubmitting: false, - isChanged: false, - title: '', - description: '', - coverImage: null, - })); - }); -}); diff --git a/app/soapbox/reducers/__tests__/group_lists.test.ts b/app/soapbox/reducers/__tests__/group_lists.test.ts deleted file mode 100644 index 46527f682..000000000 --- a/app/soapbox/reducers/__tests__/group_lists.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; - -import reducer from '../group_lists'; - -describe('group_lists reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(ImmutableMap({ - featured: ImmutableList(), - member: ImmutableList(), - admin: ImmutableList(), - })); - }); -}); diff --git a/app/soapbox/reducers/__tests__/group_relationships.test.ts b/app/soapbox/reducers/__tests__/group_relationships.test.ts deleted file mode 100644 index 31e3e354f..000000000 --- a/app/soapbox/reducers/__tests__/group_relationships.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import reducer from '../group_relationships'; - -describe('group_relationships reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(ImmutableMap()); - }); -}); diff --git a/app/soapbox/reducers/__tests__/groups.test.ts b/app/soapbox/reducers/__tests__/groups.test.ts deleted file mode 100644 index 05b88402f..000000000 --- a/app/soapbox/reducers/__tests__/groups.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import reducer from '../groups'; - -describe('groups reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {} as any)).toEqual(ImmutableMap()); - }); -}); diff --git a/app/soapbox/reducers/__tests__/list_editor-test.js b/app/soapbox/reducers/__tests__/list_editor-test.js deleted file mode 100644 index 1b351bca1..000000000 --- a/app/soapbox/reducers/__tests__/list_editor-test.js +++ /dev/null @@ -1,153 +0,0 @@ -import { Map as ImmutableMap, List as ImmutableList, Record as ImmutableRecord } from 'immutable'; - -import * as actions from 'soapbox/actions/lists'; - -import reducer from '../list_editor'; - -describe('list_editor reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {})).toMatchObject({ - listId: null, - isSubmitting: false, - isChanged: false, - title: '', - - accounts: { - items: ImmutableList(), - loaded: false, - isLoading: false, - }, - - suggestions: { - value: '', - items: ImmutableList(), - }, - }); - }); - - it('should handle LIST_EDITOR_RESET', () => { - const state = ImmutableRecord({ - listId: null, - isSubmitting: false, - isChanged: false, - title: '', - - accounts: ImmutableRecord({ - items: ImmutableList(), - loaded: false, - isLoading: false, - })(), - - suggestions: ImmutableRecord({ - value: '', - items: ImmutableList(), - })(), - })(); - const action = { - type: actions.LIST_EDITOR_RESET, - }; - expect(reducer(state, action)).toMatchObject({ - listId: null, - isSubmitting: false, - isChanged: false, - title: '', - - accounts: { - items: ImmutableList(), - loaded: false, - isLoading: false, - }, - - suggestions: { - value: '', - items: ImmutableList(), - }, - }); - }); - - it('should handle LIST_EDITOR_SETUP', () => { - const state = ImmutableRecord({ - listId: null, - isSubmitting: false, - isChanged: false, - title: '', - - accounts: ImmutableRecord({ - items: ImmutableList(), - loaded: false, - isLoading: false, - })(), - - suggestions: ImmutableRecord({ - value: '', - items: ImmutableList(), - })(), - })(); - const action = { - type: actions.LIST_EDITOR_SETUP, - list: ImmutableMap({ - id: '22', - title: 'list 1', - }), - }; - expect(reducer(state, action)).toMatchObject({ - listId: '22', - isSubmitting: false, - isChanged: false, - title: 'list 1', - - accounts: { - items: ImmutableList(), - loaded: false, - isLoading: false, - }, - - suggestions: { - value: '', - items: ImmutableList(), - }, - }); - }); - - it('should handle LIST_EDITOR_TITLE_CHANGE', () => { - const state = ImmutableMap({ - title: 'list 1', - isChanged: false, - }); - const action = { - type: actions.LIST_EDITOR_TITLE_CHANGE, - value: 'list 1 edited', - }; - expect(reducer(state, action).toJS()).toMatchObject({ - isChanged: true, - title: 'list 1 edited', - }); - }); - - it('should handle LIST_UPDATE_REQUEST', () => { - const state = ImmutableMap({ - isSubmitting: false, - isChanged: true, - }); - const action = { - type: actions.LIST_UPDATE_REQUEST, - }; - expect(reducer(state, action).toJS()).toMatchObject({ - isSubmitting: true, - isChanged: false, - }); - }); - - it('should handle LIST_UPDATE_FAIL', () => { - const state = ImmutableMap({ - isSubmitting: true, - }); - const action = { - type: actions.LIST_UPDATE_FAIL, - }; - expect(reducer(state, action).toJS()).toMatchObject({ - isSubmitting: false, - }); - }); - -}); diff --git a/app/soapbox/reducers/__tests__/mutes.test.ts b/app/soapbox/reducers/__tests__/mutes.test.ts index 66a866958..27d38c002 100644 --- a/app/soapbox/reducers/__tests__/mutes.test.ts +++ b/app/soapbox/reducers/__tests__/mutes.test.ts @@ -14,6 +14,7 @@ describe('mutes reducer', () => { isSubmitting: false, accountId: null, notifications: true, + duration: 0, }, }); }); @@ -24,6 +25,7 @@ describe('mutes reducer', () => { isSubmitting: false, accountId: null, notifications: true, + duration: 0, })(), })(); const action = { @@ -35,6 +37,7 @@ describe('mutes reducer', () => { isSubmitting: false, accountId: 'account1', notifications: true, + duration: 0, }, }); }); @@ -45,6 +48,7 @@ describe('mutes reducer', () => { isSubmitting: false, accountId: null, notifications: true, + duration: 0, })(), })(); const action = { @@ -55,6 +59,7 @@ describe('mutes reducer', () => { isSubmitting: false, accountId: null, notifications: false, + duration: 0, }, }); }); diff --git a/app/soapbox/reducers/__tests__/notifications-test.js b/app/soapbox/reducers/__tests__/notifications-test.js deleted file mode 100644 index 1ea296c60..000000000 --- a/app/soapbox/reducers/__tests__/notifications-test.js +++ /dev/null @@ -1,673 +0,0 @@ -import { - Map as ImmutableMap, - OrderedMap as ImmutableOrderedMap, - Record as ImmutableRecord, -} from 'immutable'; -import take from 'lodash/take'; - -import intlMessages from 'soapbox/__fixtures__/intlMessages.json'; -import notification from 'soapbox/__fixtures__/notification.json'; -import notifications from 'soapbox/__fixtures__/notifications.json'; -import relationship from 'soapbox/__fixtures__/relationship.json'; -import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'soapbox/actions/accounts'; -import { - MARKER_FETCH_SUCCESS, - MARKER_SAVE_REQUEST, - MARKER_SAVE_SUCCESS, -} from 'soapbox/actions/markers'; -import { - NOTIFICATIONS_EXPAND_SUCCESS, - NOTIFICATIONS_EXPAND_REQUEST, - NOTIFICATIONS_EXPAND_FAIL, - NOTIFICATIONS_FILTER_SET, - NOTIFICATIONS_SCROLL_TOP, - NOTIFICATIONS_UPDATE, - NOTIFICATIONS_UPDATE_QUEUE, - NOTIFICATIONS_DEQUEUE, - NOTIFICATIONS_CLEAR, - NOTIFICATIONS_MARK_READ_REQUEST, -} from 'soapbox/actions/notifications'; -import { TIMELINE_DELETE } from 'soapbox/actions/timelines'; -import { applyActions } from 'soapbox/jest/test-helpers'; - -import reducer from '../notifications'; - -const initialState = reducer(undefined, {}); - -describe('notifications reducer', () => { - it('should return the initial state', () => { - const expected = { - items: {}, - hasMore: true, - top: false, - unread: 0, - isLoading: false, - queuedNotifications: {}, - totalQueuedNotificationsCount: 0, - lastRead: -1, - }; - - expect(ImmutableRecord.isRecord(initialState)).toBe(true); - expect(initialState.toJS()).toMatchObject(expected); - }); - - describe('NOTIFICATIONS_EXPAND_SUCCESS', () => { - it('imports the notifications', () => { - const action = { - type: NOTIFICATIONS_EXPAND_SUCCESS, - notifications: take(notifications, 3), - next: null, - skipLoading: true, - }; - - const result = reducer(undefined, action); - - // The items are parsed as records - expect(ImmutableOrderedMap.isOrderedMap(result.items)).toBe(true); - expect(ImmutableRecord.isRecord(result.items.get('10743'))).toBe(true); - - // We can get an item - expect(result.items.get('10744').emoji).toEqual('😢'); - - // hasMore is set to false because `next` is null - expect(result.hasMore).toBe(false); - }); - - it('drops invalid notifications', () => { - const action = { - type: NOTIFICATIONS_EXPAND_SUCCESS, - notifications: [ - { id: '1', type: 'mention', status: null, account: { id: '10' } }, - { id: '2', type: 'reblog', status: null, account: { id: '9' } }, - { id: '3', type: 'favourite', status: null, account: { id: '8' } }, - { id: '4', type: 'mention', status: { id: 'a' }, account: { id: '7' } }, - { id: '5', type: 'reblog', status: { id: 'b' }, account: null }, - ], - next: null, - skipLoading: true, - }; - - const result = reducer(undefined, action); - - // Only '4' is valid - expect(result.items.size).toEqual(1); - expect(result.items.get('4').id).toEqual('4'); - }); - - it('imports move notification', () => { - const action = { - type: NOTIFICATIONS_EXPAND_SUCCESS, - notifications: [ - require('soapbox/__fixtures__/pleroma-notification-move.json'), - ], - next: null, - skipLoading: true, - }; - - const result = reducer(undefined, action).items.get('406814'); - - expect(result.account).toEqual('AFmHQ18XZ7Lco68MW8'); - expect(result.target).toEqual('A5c5LK7EJTFR0u26Pg'); - }); - }); - - describe('NOTIFICATIONS_EXPAND_REQUEST', () => { - it('sets isLoading to true', () => { - const state = initialState.set('isLoading', false); - const action = { type: NOTIFICATIONS_EXPAND_REQUEST }; - - expect(reducer(state, action).isLoading).toBe(true); - }); - }); - - describe('NOTIFICATIONS_EXPAND_FAIL', () => { - it('sets isLoading to false', () => { - const state = initialState.set('isLoading', true); - const action = { type: NOTIFICATIONS_EXPAND_FAIL }; - - expect(reducer(state, action).isLoading).toBe(false); - }); - }); - - describe('NOTIFICATIONS_FILTER_SET', () => { - it('clears the items', () => { - const actions = [{ - type: NOTIFICATIONS_EXPAND_SUCCESS, - notifications: [ - { id: '1', type: 'mention', status: { id: '4' }, account: { id: '7' } }, - { id: '2', type: 'mention', status: { id: '5' }, account: { id: '8' } }, - { id: '3', type: 'mention', status: { id: '6' }, account: { id: '9' } }, - ], - next: null, - skipLoading: true, - }, { - type: NOTIFICATIONS_FILTER_SET, - }]; - - // Setup by expanding, then calling `NOTIFICATIONS_FILTER_SET` - const result = applyActions(initialState, actions, reducer); - - // Setting the filter wipes notifications - expect(result.items.isEmpty()).toBe(true); - }); - - it('sets hasMore to true', () => { - const state = initialState.set('hasMore', false); - const action = { type: NOTIFICATIONS_FILTER_SET }; - const result = reducer(state, action); - - expect(result.hasMore).toBe(true); - }); - }); - - describe('NOTIFICATIONS_SCROLL_TOP', () => { - it('resets `unread` counter to 0 when top is true (ie, scrolled to the top)', () => { - const state = initialState.set('unread', 1); - const action = { type: NOTIFICATIONS_SCROLL_TOP, top: true }; - const result = reducer(state, action); - - expect(result.unread).toEqual(0); - expect(result.top).toBe(true); - }); - - it('leaves `unread` alone when top is false (ie, not scrolled to top)', () => { - const state = initialState.set('unread', 3); - const action = { type: NOTIFICATIONS_SCROLL_TOP, top: false }; - const result = reducer(state, action); - - expect(result.unread).toEqual(3); - expect(result.top).toBe(false); - }); - }); - - describe('NOTIFICATIONS_UPDATE', () => { - it('imports the notification', () => { - const action = { type: NOTIFICATIONS_UPDATE, notification }; - const result = reducer(initialState, action); - - expect(result.items.get('10743').type).toEqual('favourite'); - }); - - it('imports follow_request notification', () => { - const action = { - type: NOTIFICATIONS_UPDATE, - notification: require('soapbox/__fixtures__/notification-follow_request.json'), - }; - - const result = reducer(initialState, action); - expect(result.items.get('87967').type).toEqual('follow_request'); - }); - - it('increments `unread` counter when top is false', () => { - const action = { type: NOTIFICATIONS_UPDATE, notification }; - const result = reducer(initialState, action); - - expect(result.unread).toEqual(1); - }); - }); - - describe('NOTIFICATIONS_UPDATE_QUEUE', () => { - it('adds the notification to the queue (and increases the counter)', () => { - const action = { - type: NOTIFICATIONS_UPDATE_QUEUE, - notification, - intlMessages, - intlLocale: 'en', - }; - - const result = reducer(initialState, action); - - // Doesn't add it as a regular item - expect(result.items.isEmpty()).toBe(true); - - // Adds it to the queued items - expect(result.queuedNotifications.size).toEqual(1); - expect(result.totalQueuedNotificationsCount).toEqual(1); - expect(result.queuedNotifications.getIn(['10743', 'notification', 'type'])).toEqual('favourite'); - }); - }); - - describe('NOTIFICATIONS_DEQUEUE', () => { - it('resets the queued counter to 0', () => { - const state = initialState.set('totalQueuedNotificationsCount', 1); - const action = { type: NOTIFICATIONS_DEQUEUE }; - const result = reducer(state, action); - - expect(result.totalQueuedNotificationsCount).toEqual(0); - }); - }); - - describe('NOTIFICATIONS_EXPAND_SUCCESS', () => { - it('with non-empty items and next set true', () => { - const state = ImmutableMap({ - items: ImmutableOrderedMap([ - ['10734', ImmutableMap({ - id: '10734', - type: 'pleroma:emoji_reaction', - account: '9vMAje101ngtjlMj7w', - target: null, - created_at: '2020-06-10T02:54:39.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: '😢', - chat_message: null, - })], - ]), - unread: 1, - hasMore: true, - isLoading: false, - }); - - const action = { - type: NOTIFICATIONS_EXPAND_SUCCESS, - notifications: take(notifications, 3), - next: true, - }; - - const expected = ImmutableMap({ - items: ImmutableOrderedMap([ - ['10744', ImmutableMap({ - id: '10744', - type: 'pleroma:emoji_reaction', - account: '9vMAje101ngtjlMj7w', - target: null, - created_at: '2020-06-10T02:54:39.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: '😢', - chat_message: null, - total_count: null, - })], - ['10743', ImmutableMap({ - id: '10743', - type: 'favourite', - account: '9v5c6xSEgAi3Zu1Lv6', - target: null, - created_at: '2020-06-10T02:51:05.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - total_count: null, - })], - ['10741', ImmutableMap({ - id: '10741', - type: 'favourite', - account: '9v5cKMOPGqPcgfcWp6', - target: null, - created_at: '2020-06-10T02:05:06.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - total_count: null, - })], - ['10734', ImmutableMap({ - id: '10734', - type: 'pleroma:emoji_reaction', - account: '9vMAje101ngtjlMj7w', - target: null, - created_at: '2020-06-10T02:54:39.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: '😢', - chat_message: null, - })], - ]), - unread: 1, - hasMore: true, - isLoading: false, - }); - - expect(reducer(state, action).toJS()).toEqual(expected.toJS()); - }); - - it('with empty items and next set true', () => { - const state = ImmutableMap({ - items: ImmutableOrderedMap(), - unread: 1, - hasMore: true, - isLoading: false, - }); - - const action = { - type: NOTIFICATIONS_EXPAND_SUCCESS, - notifications: take(notifications, 3), - next: true, - }; - - const expected = ImmutableMap({ - items: ImmutableOrderedMap([ - ['10744', ImmutableMap({ - id: '10744', - type: 'pleroma:emoji_reaction', - account: '9vMAje101ngtjlMj7w', - target: null, - created_at: '2020-06-10T02:54:39.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: '😢', - chat_message: null, - total_count: null, - })], - ['10743', ImmutableMap({ - id: '10743', - type: 'favourite', - account: '9v5c6xSEgAi3Zu1Lv6', - target: null, - created_at: '2020-06-10T02:51:05.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - total_count: null, - })], - ['10741', ImmutableMap({ - id: '10741', - type: 'favourite', - account: '9v5cKMOPGqPcgfcWp6', - target: null, - created_at: '2020-06-10T02:05:06.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - total_count: null, - })], - ]), - unread: 1, - hasMore: true, - isLoading: false, - }); - - expect(reducer(state, action).toJS()).toEqual(expected.toJS()); - }); - }); - - describe('ACCOUNT_BLOCK_SUCCESS', () => { - it('should handle', () => { - const state = ImmutableMap({ - items: ImmutableOrderedMap([ - ['10744', ImmutableMap({ - id: '10744', - type: 'pleroma:emoji_reaction', - account: '9vMAje101ngtjlMj7w', - target: null, - created_at: '2020-06-10T02:54:39.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: '😢', - chat_message: null, - })], - ['10743', ImmutableMap({ - id: '10743', - type: 'favourite', - account: '9v5c6xSEgAi3Zu1Lv6', - target: null, - created_at: '2020-06-10T02:51:05.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - })], - ['10741', ImmutableMap({ - id: '10741', - type: 'favourite', - account: '9v5cKMOPGqPcgfcWp6', - target: null, - created_at: '2020-06-10T02:05:06.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - })], - ]), - }); - const action = { - type: ACCOUNT_BLOCK_SUCCESS, - relationship, - }; - expect(reducer(state, action)).toEqual(ImmutableMap({ - items: ImmutableOrderedMap([ - ['10743', ImmutableMap({ - id: '10743', - type: 'favourite', - account: '9v5c6xSEgAi3Zu1Lv6', - target: null, - created_at: '2020-06-10T02:51:05.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - })], - ['10741', ImmutableMap({ - id: '10741', - type: 'favourite', - account: '9v5cKMOPGqPcgfcWp6', - target: null, - created_at: '2020-06-10T02:05:06.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - })], - ]), - })); - }); - }); - - describe('ACCOUNT_MUTE_SUCCESS', () => { - it('should handle', () => { - const state = ImmutableMap({ - items: ImmutableOrderedMap([ - ['10744', ImmutableMap({ - id: '10744', - type: 'pleroma:emoji_reaction', - account: '9vMAje101ngtjlMj7w', - target: null, - created_at: '2020-06-10T02:54:39.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: '😢', - chat_message: null, - })], - ['10743', ImmutableMap({ - id: '10743', - type: 'favourite', - account: '9v5c6xSEgAi3Zu1Lv6', - target: null, - created_at: '2020-06-10T02:51:05.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - })], - ['10741', ImmutableMap({ - id: '10741', - type: 'favourite', - account: '9v5cKMOPGqPcgfcWp6', - target: null, - created_at: '2020-06-10T02:05:06.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - })], - ]), - }); - const action = { - type: ACCOUNT_MUTE_SUCCESS, - relationship: relationship, - }; - expect(reducer(state, action)).toEqual(ImmutableMap({ - items: ImmutableOrderedMap([ - ['10743', ImmutableMap({ - id: '10743', - type: 'favourite', - account: '9v5c6xSEgAi3Zu1Lv6', - target: null, - created_at: '2020-06-10T02:51:05.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - })], - ['10741', ImmutableMap({ - id: '10741', - type: 'favourite', - account: '9v5cKMOPGqPcgfcWp6', - target: null, - created_at: '2020-06-10T02:05:06.000Z', - status: '9vvNxoo5EFbbnfdXQu', - emoji: null, - chat_message: null, - })], - ]), - })); - }); - }); - - describe('NOTIFICATIONS_CLEAR', () => { - it('clears the items', () => { - const state = initialState.set('items', ImmutableOrderedMap([['1', {}], ['2', {}]])); - const action = { type: NOTIFICATIONS_CLEAR }; - const result = reducer(state, action); - - expect(result.items.isEmpty()).toBe(true); - }); - }); - - describe('NOTIFICATIONS_MARK_READ_REQUEST', () => { - it('sets lastRead to the one in the action', () => { - const action = { type: NOTIFICATIONS_MARK_READ_REQUEST, lastRead: '1234' }; - const result = reducer(undefined, action); - - expect(result.lastRead).toEqual('1234'); - }); - }); - - describe('TIMELINE_DELETE', () => { - it('deletes notifications corresponding to the status ID', () => { - const actions = [{ - type: NOTIFICATIONS_EXPAND_SUCCESS, - notifications: [ - { id: '1', type: 'mention', status: { id: '4' }, account: { id: '7' } }, - { id: '2', type: 'mention', status: { id: '5' }, account: { id: '8' } }, - { id: '3', type: 'mention', status: { id: '6' }, account: { id: '9' } }, - { id: '4', type: 'mention', status: { id: '5' }, account: { id: '7' } }, - ], - next: null, - skipLoading: true, - }, { - type: TIMELINE_DELETE, - id: '5', - }]; - - // Setup by expanding, then calling `NOTIFICATIONS_FILTER_SET` - const result = applyActions(initialState, actions, reducer); - - expect(result.items.size).toEqual(2); - expect(result.items.get('5')).toBe(undefined); - }); - }); - - describe('MARKER_FETCH_SUCCESS', () => { - it('sets lastRead', () => { - const action = { - type: MARKER_FETCH_SUCCESS, - timeline: ['notifications'], - marker: { - notifications: { - last_read_id: '1234', - }, - }, - }; - - expect(reducer(undefined, action).get('lastRead')).toEqual('1234'); - }); - - it('updates the unread count', () => { - const action = { - type: MARKER_FETCH_SUCCESS, - timeline: ['notifications'], - marker: { - notifications: { - last_read_id: '5678', - }, - }, - }; - - const state = ImmutableMap({ - items: ImmutableOrderedMap({ - '9012': ImmutableMap({ id: '9012' }), - '5678': ImmutableMap({ id: '5678' }), - '1234': ImmutableMap({ id: '1234' }), - }), - unread: 3, - }); - - expect(reducer(state, action).get('unread')).toEqual(1); - }); - }); - - describe('MARKER_SAVE_REQUEST', () => { - it('sets lastRead', () => { - const action = { - type: MARKER_SAVE_REQUEST, - timeline: ['notifications'], - marker: { - notifications: { - last_read_id: '1234', - }, - }, - }; - - expect(reducer(undefined, action).get('lastRead')).toEqual('1234'); - }); - - it('updates the unread count', () => { - const action = { - type: MARKER_SAVE_REQUEST, - timeline: ['notifications'], - marker: { - notifications: { - last_read_id: '5678', - }, - }, - }; - - const state = ImmutableMap({ - items: ImmutableOrderedMap({ - '9012': ImmutableMap({ id: '9012' }), - '5678': ImmutableMap({ id: '5678' }), - '1234': ImmutableMap({ id: '1234' }), - }), - unread: 3, - }); - - expect(reducer(state, action).get('unread')).toEqual(1); - }); - }); - - describe('MARKER_SAVE_SUCCESS', () => { - it('sets lastRead', () => { - const action = { - type: MARKER_SAVE_SUCCESS, - timeline: ['notifications'], - marker: { - notifications: { - last_read_id: '5678', - }, - }, - }; - - expect(reducer(undefined, action).get('lastRead')).toEqual('5678'); - }); - - it('updates the unread count', () => { - const action = { - type: MARKER_SAVE_SUCCESS, - timeline: ['notifications'], - marker: { - notifications: { - last_read_id: '9012', - }, - }, - }; - - const state = ImmutableMap({ - items: ImmutableOrderedMap({ - '9012': ImmutableMap({ id: '9012' }), - '5678': ImmutableMap({ id: '5678' }), - '1234': ImmutableMap({ id: '1234' }), - }), - unread: 3, - }); - - expect(reducer(state, action).get('unread')).toEqual(0); - }); - }); -}); diff --git a/app/soapbox/reducers/__tests__/search-test.js b/app/soapbox/reducers/__tests__/search-test.js deleted file mode 100644 index 497f5b08c..000000000 --- a/app/soapbox/reducers/__tests__/search-test.js +++ /dev/null @@ -1,131 +0,0 @@ -import { Map as ImmutableMap, List as ImmutableList, Record as ImmutableRecord } from 'immutable'; - -import { - SEARCH_CHANGE, - SEARCH_CLEAR, - SEARCH_EXPAND_SUCCESS, -} from 'soapbox/actions/search'; - -import reducer from '../search'; - -describe('search reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {}).toJS()).toEqual({ - value: '', - submitted: false, - submittedValue: '', - hidden: false, - results: { - accounts: [], - statuses: [], - hashtags: [], - accountsHasMore: false, - statusesHasMore: false, - hashtagsHasMore: false, - accountsLoaded: false, - statusesLoaded: false, - hashtagsLoaded: false, - }, - filter: 'accounts', - accountId: null, - }); - }); - - describe('SEARCH_CHANGE', () => { - it('sets the value', () => { - const state = ImmutableMap({ value: 'hell' }); - const action = { type: SEARCH_CHANGE, value: 'hello' }; - expect(reducer(state, action).get('value')).toEqual('hello'); - }); - }); - - describe('SEARCH_CLEAR', () => { - it('resets the state', () => { - const state = ImmutableRecord({ - value: 'hello world', - submitted: true, - submittedValue: 'hello world', - hidden: false, - results: ImmutableRecord({})(), - filter: 'statuses', - })(); - - const action = { type: SEARCH_CLEAR }; - - const expected = { - value: '', - submitted: false, - submittedValue: '', - hidden: false, - results: { - accounts: [], - statuses: [], - hashtags: [], - accountsHasMore: false, - statusesHasMore: false, - hashtagsHasMore: false, - accountsLoaded: false, - statusesLoaded: false, - hashtagsLoaded: false, - }, - filter: 'accounts', - accountId: null, - }; - - expect(reducer(state, action).toJS()).toEqual(expected); - }); - }); - - describe(SEARCH_EXPAND_SUCCESS, () => { - it('imports hashtags as maps', () => { - const state = ImmutableRecord({ - value: 'artist', - submitted: true, - submittedValue: 'artist', - hidden: false, - results: ImmutableRecord({ - hashtags: ImmutableList(), - hashtagsHasMore: false, - hashtagsLoaded: true, - })(), - filter: 'hashtags', - })(); - - const action = { - type: SEARCH_EXPAND_SUCCESS, - results: { - accounts: [], - statuses: [], - hashtags: [{ - name: 'artist', - url: 'https://gleasonator.com/tags/artist', - history: [], - }], - }, - searchTerm: 'artist', - searchType: 'hashtags', - }; - - const expected = { - value: 'artist', - submitted: true, - submittedValue: 'artist', - hidden: false, - results: { - hashtags: [ - { - name: 'artist', - url: 'https://gleasonator.com/tags/artist', - history: [], - }, - ], - hashtagsHasMore: false, - hashtagsLoaded: true, - }, - filter: 'hashtags', - }; - - expect(reducer(state, action).toJS()).toEqual(expected); - }); - }); -}); diff --git a/app/soapbox/reducers/__tests__/user_lists.test.ts b/app/soapbox/reducers/__tests__/user_lists.test.ts index 5fbd5f1ea..fa23f845f 100644 --- a/app/soapbox/reducers/__tests__/user_lists.test.ts +++ b/app/soapbox/reducers/__tests__/user_lists.test.ts @@ -14,8 +14,6 @@ describe('user_lists reducer', () => { blocks: { next: null, items: ImmutableOrderedSet(), isLoading: false }, mutes: { next: null, items: ImmutableOrderedSet(), isLoading: false }, directory: { next: null, items: ImmutableOrderedSet(), isLoading: true }, - groups: {}, - groups_removed_accounts: {}, pinned: {}, birthday_reminders: {}, familiar_followers: {}, diff --git a/app/soapbox/reducers/group_editor.js b/app/soapbox/reducers/group_editor.js deleted file mode 100644 index 109182b1f..000000000 --- a/app/soapbox/reducers/group_editor.js +++ /dev/null @@ -1,58 +0,0 @@ -import { Map as ImmutableMap } from 'immutable'; - -import { - GROUP_CREATE_REQUEST, - GROUP_CREATE_FAIL, - GROUP_CREATE_SUCCESS, - GROUP_UPDATE_REQUEST, - GROUP_UPDATE_FAIL, - GROUP_UPDATE_SUCCESS, - GROUP_EDITOR_RESET, - GROUP_EDITOR_SETUP, - GROUP_EDITOR_VALUE_CHANGE, -} from '../actions/group_editor'; - -const initialState = ImmutableMap({ - groupId: null, - isSubmitting: false, - isChanged: false, - title: '', - description: '', - coverImage: null, -}); - -export default function groupEditorReducer(state = initialState, action) { - switch (action.type) { - case GROUP_EDITOR_RESET: - return initialState; - case GROUP_EDITOR_SETUP: - return state.withMutations(map => { - map.set('groupId', action.group.get('id')); - map.set('title', action.group.get('title')); - map.set('description', action.group.get('description')); - map.set('isSubmitting', false); - }); - case GROUP_EDITOR_VALUE_CHANGE: - return state.withMutations(map => { - map.set(action.field, action.value); - map.set('isChanged', true); - }); - case GROUP_CREATE_REQUEST: - case GROUP_UPDATE_REQUEST: - return state.withMutations(map => { - map.set('isSubmitting', true); - map.set('isChanged', false); - }); - case GROUP_CREATE_FAIL: - case GROUP_UPDATE_FAIL: - return state.set('isSubmitting', false); - case GROUP_CREATE_SUCCESS: - case GROUP_UPDATE_SUCCESS: - return state.withMutations(map => { - map.set('isSubmitting', false); - map.set('groupId', action.group.id); - }); - default: - return state; - } -} diff --git a/app/soapbox/reducers/group_lists.js b/app/soapbox/reducers/group_lists.js deleted file mode 100644 index 25b62648c..000000000 --- a/app/soapbox/reducers/group_lists.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; - -import { GROUPS_FETCH_SUCCESS } from '../actions/groups'; - -const initialState = ImmutableMap({ - featured: ImmutableList(), - member: ImmutableList(), - admin: ImmutableList(), -}); - -const normalizeList = (state, type, id, groups) => { - return state.set(type, ImmutableList(groups.map(item => item.id))); -}; - -export default function groupLists(state = initialState, action) { - switch (action.type) { - case GROUPS_FETCH_SUCCESS: - return normalizeList(state, action.tab, action.id, action.groups); - default: - return state; - } -} diff --git a/app/soapbox/reducers/group_relationships.js b/app/soapbox/reducers/group_relationships.js deleted file mode 100644 index f74fdb02e..000000000 --- a/app/soapbox/reducers/group_relationships.js +++ /dev/null @@ -1,27 +0,0 @@ -import { Map as ImmutableMap, fromJS } from 'immutable'; - -import { GROUP_RELATIONSHIPS_FETCH_SUCCESS, GROUP_JOIN_SUCCESS, GROUP_LEAVE_SUCCESS } from '../actions/groups'; - -const normalizeRelationship = (state, relationship) => state.set(relationship.id, fromJS(relationship)); - -const normalizeRelationships = (state, relationships) => { - relationships.forEach(relationship => { - state = normalizeRelationship(state, relationship); - }); - - return state; -}; - -const initialState = ImmutableMap(); - -export default function group_relationships(state = initialState, action) { - switch (action.type) { - case GROUP_JOIN_SUCCESS: - case GROUP_LEAVE_SUCCESS: - return normalizeRelationship(state, action.relationship); - case GROUP_RELATIONSHIPS_FETCH_SUCCESS: - return normalizeRelationships(state, action.relationships); - default: - return state; - } -} diff --git a/app/soapbox/reducers/groups.js b/app/soapbox/reducers/groups.js deleted file mode 100644 index 8e301e468..000000000 --- a/app/soapbox/reducers/groups.js +++ /dev/null @@ -1,34 +0,0 @@ -import { Map as ImmutableMap, fromJS } from 'immutable'; - -import { GROUP_UPDATE_SUCCESS } from '../actions/group_editor'; -import { - GROUP_FETCH_SUCCESS, - GROUP_FETCH_FAIL, - GROUPS_FETCH_SUCCESS, -} from '../actions/groups'; - -const initialState = ImmutableMap(); - -const normalizeGroup = (state, group) => state.set(group.id, fromJS(group)); - -const normalizeGroups = (state, groups) => { - groups.forEach(group => { - state = normalizeGroup(state, group); - }); - - return state; -}; - -export default function groups(state = initialState, action) { - switch (action.type) { - case GROUP_FETCH_SUCCESS: - case GROUP_UPDATE_SUCCESS: - return normalizeGroup(state, action.group); - case GROUPS_FETCH_SUCCESS: - return normalizeGroups(state, action.groups); - case GROUP_FETCH_FAIL: - return state.set(action.id, false); - default: - return state; - } -} diff --git a/app/soapbox/reducers/index.ts b/app/soapbox/reducers/index.ts index 87750381b..8c59c014f 100644 --- a/app/soapbox/reducers/index.ts +++ b/app/soapbox/reducers/index.ts @@ -25,10 +25,6 @@ import custom_emojis from './custom_emojis'; import domain_lists from './domain_lists'; import dropdown_menu from './dropdown_menu'; import filters from './filters'; -import group_editor from './group_editor'; -import group_lists from './group_lists'; -import group_relationships from './group_relationships'; -import groups from './groups'; import history from './history'; import instance from './instance'; import listAdder from './list_adder'; @@ -95,10 +91,6 @@ const reducers = { suggestions, polls, trends, - groups, - group_relationships, - group_lists, - group_editor, sidebar, patron, soapbox, diff --git a/app/soapbox/reducers/mutes.ts b/app/soapbox/reducers/mutes.ts index e232d4039..4c0b08c39 100644 --- a/app/soapbox/reducers/mutes.ts +++ b/app/soapbox/reducers/mutes.ts @@ -3,6 +3,7 @@ import { Record as ImmutableRecord } from 'immutable'; import { MUTES_INIT_MODAL, MUTES_TOGGLE_HIDE_NOTIFICATIONS, + MUTES_CHANGE_DURATION, } from '../actions/mutes'; import type { AnyAction } from 'redux'; @@ -11,6 +12,7 @@ const NewMuteRecord = ImmutableRecord({ isSubmitting: false, accountId: null, notifications: true, + duration: 0, }); const ReducerRecord = ImmutableRecord({ @@ -29,6 +31,8 @@ export default function mutes(state: State = ReducerRecord(), action: AnyAction) }); case MUTES_TOGGLE_HIDE_NOTIFICATIONS: return state.updateIn(['new', 'notifications'], (old) => !old); + case MUTES_CHANGE_DURATION: + return state.setIn(['new', 'duration'], action.duration); default: return state; } diff --git a/app/soapbox/reducers/statuses.ts b/app/soapbox/reducers/statuses.ts index 49c946aa0..088191edd 100644 --- a/app/soapbox/reducers/statuses.ts +++ b/app/soapbox/reducers/statuses.ts @@ -1,5 +1,5 @@ import escapeTextContentForBrowser from 'escape-html'; -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; import emojify from 'soapbox/features/emoji/emoji'; import { normalizeStatus } from 'soapbox/normalizers'; @@ -30,6 +30,8 @@ import { STATUS_HIDE, STATUS_DELETE_REQUEST, STATUS_DELETE_FAIL, + STATUS_TRANSLATE_SUCCESS, + STATUS_TRANSLATE_UNDO, } from '../actions/statuses'; import { TIMELINE_DELETE } from '../actions/timelines'; @@ -255,6 +257,10 @@ export default function statuses(state = initialState, action: AnyAction): State return decrementReplyCount(state, action.params); case STATUS_DELETE_FAIL: return incrementReplyCount(state, action.params); + case STATUS_TRANSLATE_SUCCESS: + return state.setIn([action.id, 'translation'], fromJS(action.translation)); + case STATUS_TRANSLATE_UNDO: + return state.deleteIn([action.id, 'translation']); case TIMELINE_DELETE: return deleteStatus(state, action.id, action.references); default: diff --git a/app/soapbox/reducers/timelines.ts b/app/soapbox/reducers/timelines.ts index def257407..97975d658 100644 --- a/app/soapbox/reducers/timelines.ts +++ b/app/soapbox/reducers/timelines.ts @@ -12,7 +12,6 @@ import { ACCOUNT_MUTE_SUCCESS, ACCOUNT_UNFOLLOW_SUCCESS, } from '../actions/accounts'; -import { GROUP_REMOVE_STATUS_SUCCESS } from '../actions/groups'; import { STATUS_CREATE_REQUEST, STATUS_CREATE_SUCCESS, @@ -210,10 +209,6 @@ const filterTimelines = (state: State, relationship: APIEntity, statuses: Immuta }); }; -const removeStatusFromGroup = (state: State, groupId: string, statusId: string) => { - return state.updateIn([`group:${groupId}`, 'items'], ImmutableOrderedSet(), ids => (ids as ImmutableOrderedSet).delete(statusId)); -}; - const timelineDequeue = (state: State, timelineId: string) => { const top = state.getIn([timelineId, 'top']); @@ -348,8 +343,6 @@ export default function timelines(state: State = initialState, action: AnyAction return timelineConnect(state, action.timeline); case TIMELINE_DISCONNECT: return timelineDisconnect(state, action.timeline); - case GROUP_REMOVE_STATUS_SUCCESS: - return removeStatusFromGroup(state, action.groupId, action.id); case TIMELINE_REPLACE: return state .update('home', TimelineRecord(), timeline => timeline.withMutations(timeline => { diff --git a/app/soapbox/reducers/user_lists.ts b/app/soapbox/reducers/user_lists.ts index 38017f0bb..4c84f1836 100644 --- a/app/soapbox/reducers/user_lists.ts +++ b/app/soapbox/reducers/user_lists.ts @@ -32,13 +32,6 @@ import { import { FAMILIAR_FOLLOWERS_FETCH_SUCCESS, } from '../actions/familiar_followers'; -import { - GROUP_MEMBERS_FETCH_SUCCESS, - GROUP_MEMBERS_EXPAND_SUCCESS, - GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS, - GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS, - GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS, -} from '../actions/groups'; import { REBLOGS_FETCH_SUCCESS, FAVOURITES_FETCH_SUCCESS, @@ -82,8 +75,6 @@ export const ReducerRecord = ImmutableRecord({ blocks: ListRecord(), mutes: ListRecord(), directory: ListRecord({ isLoading: true }), - groups: ImmutableMap(), - groups_removed_accounts: ImmutableMap(), pinned: ImmutableMap(), birthday_reminders: ImmutableMap(), familiar_followers: ImmutableMap(), @@ -94,7 +85,7 @@ export type List = ReturnType; type Reaction = ReturnType; type ReactionList = ReturnType; type Items = ImmutableOrderedSet; -type NestedListPath = ['followers' | 'following' | 'reblogged_by' | 'favourited_by' | 'reactions' | 'groups' | 'groups_removed_accounts' | 'pinned' | 'birthday_reminders' | 'familiar_followers', string]; +type NestedListPath = ['followers' | 'following' | 'reblogged_by' | 'favourited_by' | 'reactions' | 'pinned' | 'birthday_reminders' | 'familiar_followers', string]; type ListPath = ['follow_requests' | 'blocks' | 'mutes' | 'directory']; const normalizeList = (state: State, path: NestedListPath | ListPath, accounts: APIEntity[], next?: string | null) => { @@ -170,16 +161,6 @@ export default function userLists(state = ReducerRecord(), action: AnyAction) { case DIRECTORY_FETCH_FAIL: case DIRECTORY_EXPAND_FAIL: return state.setIn(['directory', 'isLoading'], false); - case GROUP_MEMBERS_FETCH_SUCCESS: - return normalizeList(state, ['groups', action.id], action.accounts, action.next); - case GROUP_MEMBERS_EXPAND_SUCCESS: - return appendToList(state, ['groups', action.id], action.accounts, action.next); - case GROUP_REMOVED_ACCOUNTS_FETCH_SUCCESS: - return normalizeList(state, ['groups_removed_accounts', action.id], action.accounts, action.next); - case GROUP_REMOVED_ACCOUNTS_EXPAND_SUCCESS: - return appendToList(state, ['groups_removed_accounts', action.id], action.accounts, action.next); - case GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS: - return removeFromList(state, ['groups_removed_accounts', action.groupId], action.id); case PINNED_ACCOUNTS_FETCH_SUCCESS: return normalizeList(state, ['pinned', action.id], action.accounts, action.next); case BIRTHDAY_REMINDERS_FETCH_SUCCESS: diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index a245987bd..16e178cd6 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -424,6 +424,15 @@ const getInstanceFeatures = (instance: Instance) => { */ muteStrangers: v.software === PLEROMA, + /** + * Ability to specify how long the account mute should last. + * @see PUT /api/v1/accounts/:id/mute + */ + mutesDuration: any([ + v.software === PLEROMA && gte(v.version, '2.3.0'), + v.software === MASTODON && gte(v.compatVersion, '3.3.0'), + ]), + /** * Add private notes to accounts. * @see POST /api/v1/accounts/:id/note @@ -639,6 +648,12 @@ const getInstanceFeatures = (instance: Instance) => { features.includes('v2_suggestions'), ]), + /** + * Can translate statuses. + * @see POST /api/v1/statuses/:id/translate + */ + translations: features.includes('translation'), + /** * Trending statuses. * @see GET /api/v1/trends/statuses diff --git a/app/soapbox/utils/notification.ts b/app/soapbox/utils/notification.ts index 907a97434..49c360b92 100644 --- a/app/soapbox/utils/notification.ts +++ b/app/soapbox/utils/notification.ts @@ -14,6 +14,12 @@ const NOTIFICATION_TYPES = [ 'update', ] as const; +/** Notification types to exclude from the "All" filter by default. */ +const EXCLUDE_TYPES = [ + 'pleroma:chat_mention', + 'chat', // TruthSocial +] as const; + type NotificationType = typeof NOTIFICATION_TYPES[number]; /** Ensure the Notification is a valid, known type. */ @@ -21,6 +27,7 @@ const validType = (type: string): type is NotificationType => NOTIFICATION_TYPES export { NOTIFICATION_TYPES, + EXCLUDE_TYPES, NotificationType, validType, }; diff --git a/app/soapbox/utils/sw.ts b/app/soapbox/utils/sw.ts new file mode 100644 index 000000000..910d7189a --- /dev/null +++ b/app/soapbox/utils/sw.ts @@ -0,0 +1,15 @@ +/** Unregister the ServiceWorker */ +// https://stackoverflow.com/a/49771828/8811886 +const unregisterSw = async(): Promise => { + if (navigator.serviceWorker) { + // FIXME: this only works if using a single tab. + // Send a message to sw.js instead to refresh all tabs. + const registrations = await navigator.serviceWorker.getRegistrations(); + const unregisterAll = registrations.map(r => r.unregister()); + await Promise.all(unregisterAll); + } +}; + +export { + unregisterSw, +}; \ No newline at end of file diff --git a/app/styles/components/status.scss b/app/styles/components/status.scss index 4a2c57d3e..bdbdfa7a4 100644 --- a/app/styles/components/status.scss +++ b/app/styles/components/status.scss @@ -102,7 +102,7 @@ } .status-card { - @apply flex text-sm border border-solid border-gray-200 dark:border-gray-800 rounded-lg text-gray-800 dark:text-gray-200 mt-3 min-h-[150px] no-underline overflow-hidden; + @apply flex text-sm border border-solid border-gray-200 dark:border-gray-800 rounded-lg text-gray-800 dark:text-gray-200 min-h-[150px] no-underline overflow-hidden; } a.status-card { diff --git a/babel.config.js b/babel.config.js index 17af1a21c..f999f83ef 100644 --- a/babel.config.js +++ b/babel.config.js @@ -17,7 +17,6 @@ module.exports = (api) => { plugins: [ '@babel/syntax-dynamic-import', ['@babel/proposal-object-rest-spread', { useBuiltIns: true }], - ['@babel/proposal-decorators', { legacy: true }], '@babel/proposal-class-properties', ['react-intl', { messagesDir: './build/messages/' }], 'preval', diff --git a/package.json b/package.json index 7340c7bd4..ee419cf05 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "dependencies": { "@babel/core": "^7.18.2", "@babel/plugin-proposal-class-properties": "^7.17.12", - "@babel/plugin-proposal-decorators": "^7.18.2", "@babel/plugin-proposal-object-rest-spread": "^7.18.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-react-inline-elements": "^7.16.7", @@ -67,7 +66,7 @@ "@sentry/browser": "^7.11.1", "@sentry/react": "^7.11.1", "@sentry/tracing": "^7.11.1", - "@tabler/icons": "^1.73.0", + "@tabler/icons": "^1.109.0", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.7", "@tanstack/react-query": "^4.0.10", @@ -167,6 +166,7 @@ "react-inlinesvg": "^3.0.0", "react-intl": "^5.0.0", "react-motion": "^0.5.2", + "react-notification": "^6.8.5", "react-otp-input": "^2.4.0", "react-overlays": "^0.9.0", "react-popper": "^2.3.0", diff --git a/tsconfig.json b/tsconfig.json index 04ec4a470..c47b1a33f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,6 @@ "allowJs": true, "moduleResolution": "node", "resolveJsonModule": true, - "experimentalDecorators": true, "esModuleInterop": true, "typeRoots": [ "./types", "./node_modules/@types"] }, diff --git a/yarn.lock b/yarn.lock index 44997bdbe..a6d136520 100644 --- a/yarn.lock +++ b/yarn.lock @@ -538,18 +538,6 @@ "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-decorators@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.2.tgz#dbe4086d2d42db489399783c3aa9272e9700afd4" - integrity sha512-kbDISufFOxeczi0v4NQP3p5kIeW6izn/6klfWBrIIdGZZe4UpHR+QU03FAoWjGGd9SUXAwbw2pup1kaL4OQsJQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-replace-supers" "^7.18.2" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/plugin-syntax-decorators" "^7.17.12" - charcodes "^0.2.0" - "@babel/plugin-proposal-dynamic-import@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" @@ -688,13 +676,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-decorators@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.12.tgz#02e8f678602f0af8222235271efea945cfdb018a" - integrity sha512-D1Hz0qtGTza8K2xGyEdVNCYLdVHukAcbQr4K3/s6r/esadyEriZovpJimQOpu8ju4/jV8dW/1xdaE0UpDroidw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" @@ -2290,10 +2271,10 @@ remark "^13.0.0" unist-util-find-all-after "^3.0.2" -"@tabler/icons@^1.73.0": - version "1.73.0" - resolved "https://registry.yarnpkg.com/@tabler/icons/-/icons-1.73.0.tgz#26d81858baf41be939504e1f9b4b32835eda6fdb" - integrity sha512-MhAHFzVj79ZWlAIRD++7Mk55PZsdlEdkfkjO3DD257mqj8iJZQRAQtkx2UFJXVs2mMrcOUu1qtj4rlVC8BfnKA== +"@tabler/icons@^1.109.0": + version "1.109.0" + resolved "https://registry.yarnpkg.com/@tabler/icons/-/icons-1.109.0.tgz#11626c3fc097f2f70c4c197e4b9909fb05380752" + integrity sha512-B0YetE4pB6HY2Wa57v/LJ3NgkJzKYPze4U0DurIqPoKSptatKv2ga76FZSkO6EUpkYfHMtGPM6QjpJljfuCmAQ== "@tailwindcss/forms@^0.5.3": version "0.5.3" @@ -4196,11 +4177,6 @@ character-reference-invalid@^1.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== -charcodes@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/charcodes/-/charcodes-0.2.0.tgz#5208d327e6cc05f99eb80ffc814707572d1f14e4" - integrity sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ== - cheerio-select@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" @@ -9946,6 +9922,13 @@ react-motion@^0.5.2: prop-types "^15.5.8" raf "^3.1.0" +react-notification@^6.8.5: + version "6.8.5" + resolved "https://registry.yarnpkg.com/react-notification/-/react-notification-6.8.5.tgz#7ea90a633bb2a280d899e30c93cf372265cce4f0" + integrity sha512-3pJPhSsWNYizpyeMeWuC+jVthqE9WKqQ6rHq2naiiP4fLGN4irwL2Xp2Q8Qn7agW/e4BIDxarab6fJOUp1cKUw== + dependencies: + prop-types "^15.6.2" + react-onclickoutside@^6.12.0: version "6.12.1" resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz#92dddd28f55e483a1838c5c2930e051168c1e96b"