+23
-4
src/components/PostControls/BookmarkButton.tsx
+23
-4
src/components/PostControls/BookmarkButton.tsx
···
6
6
import type React from 'react'
7
7
8
8
import {useCleanError} from '#/lib/hooks/useCleanError'
9
+
import {getTerminology} from '#/lib/strings/terminology'
10
+
import {useTerminologyPreference} from '#/state/preferences'
9
11
import {logger} from '#/logger'
10
12
import {type Shadow} from '#/state/cache/post-shadow'
11
13
import {useBookmarkMutation} from '#/state/queries/bookmarks/useBookmarkMutation'
···
29
31
}): React.ReactNode {
30
32
const t = useTheme()
31
33
const {_} = useLingui()
34
+
const terminologyPreference = useTerminologyPreference()
32
35
const {mutateAsync: bookmark} = useBookmarkMutation()
33
36
const cleanError = useCleanError()
34
37
const requireAuth = useRequireAuth()
···
56
59
<toast.Outer>
57
60
<toast.Icon />
58
61
<toast.Text>
59
-
<Trans>Skeet saved</Trans>
62
+
<Trans>{_(getTerminology(terminologyPreference, {
63
+
skeet: msg`Skeet saved`,
64
+
post: msg`Post saved`,
65
+
spell: msg`Spell saved`,
66
+
}))}</Trans>
60
67
</toast.Text>
61
68
{!disableUndo && (
62
69
<toast.Action
···
91
98
<toast.Outer>
92
99
<toast.Icon icon={TrashIcon} />
93
100
<toast.Text>
94
-
<Trans>Removed from saved skeets</Trans>
101
+
<Trans>{_(getTerminology(terminologyPreference, {
102
+
skeet: msg`Removed from saved skeets`,
103
+
post: msg`Removed from saved posts`,
104
+
spell: msg`Removed from saved spells`,
105
+
}))}</Trans>
95
106
</toast.Text>
96
107
{!disableUndo && (
97
108
<toast.Action
···
125
136
big={big}
126
137
label={
127
138
isBookmarked
128
-
? _(msg`Remove from saved skeets`)
129
-
: _(msg`Add to saved skeets`)
139
+
? _(getTerminology(terminologyPreference, {
140
+
skeet: msg`Remove from saved skeets`,
141
+
post: msg`Remove from saved posts`,
142
+
spell: msg`Remove from saved spells`,
143
+
}))
144
+
: _(getTerminology(terminologyPreference, {
145
+
skeet: msg`Add to saved skeets`,
146
+
post: msg`Add to saved posts`,
147
+
spell: msg`Add to saved spells`,
148
+
}))
130
149
}
131
150
onPress={onHandlePress}
132
151
hitSlop={hitSlop}>
+24
-22
src/components/PostControls/PostMenu/PostMenuItems.tsx
+24
-22
src/components/PostControls/PostMenu/PostMenuItems.tsx
···
47
47
import {useFeedFeedbackContext} from '#/state/feed-feedback'
48
48
import {useLanguagePrefs} from '#/state/preferences'
49
49
import {useHiddenPosts, useHiddenPostsApi} from '#/state/preferences'
50
+
import {useTerminology} from '#/lib/hooks/useTerminology'
50
51
import {usePinnedPostMutation} from '#/state/queries/pinned-post'
51
52
import {
52
53
usePostDeleteMutation,
···
188
189
postUri: post.uri,
189
190
rootPostUri: rootUri,
190
191
})
192
+
const terminology = useTerminology()
191
193
192
194
const href = useMemo(() => {
193
195
const urip = new AtUri(postUri)
···
197
199
const onDeletePost = () => {
198
200
deletePostMutate({uri: postUri}).then(
199
201
() => {
200
-
Toast.show(_(msg({message: 'Skeet deleted', context: 'toast'})))
202
+
Toast.show(_(msg({message: `${_(terminology.post)} deleted`, context: 'toast'})))
201
203
202
204
const route = getCurrentRoute(navigation.getState())
203
205
if (route.name === 'PostThread') {
···
217
219
},
218
220
e => {
219
221
logger.error('Failed to delete post', {message: e})
220
-
Toast.show(_(msg`Failed to delete skeet, please try again`), 'xmark')
222
+
Toast.show(_(msg`Failed to delete ${_(terminology.post)}, please try again`), 'xmark')
221
223
},
222
224
)
223
225
}
···
475
477
})
476
478
Toast.show(
477
479
isDetach
478
-
? _(msg`Quote skeet was successfully detached`)
479
-
: _(msg`Quote skeet was re-attached`),
480
+
? _(msg`Quote ${_(terminology.post)} was successfully detached`)
481
+
: _(msg`Quote ${_(terminology.post)} was re-attached`),
480
482
)
481
483
} catch (e: any) {
482
484
Toast.show(
···
667
669
<>
668
670
<Prompt.Basic
669
671
control={redraftPromptControl}
670
-
title={_(msg`Redraft this skeet?`)}
672
+
title={_(msg`Redraft this ${_(terminology.post)}?`)}
671
673
description={_(
672
-
msg`This will delete the original skeet and open the composer with its content.`,
674
+
msg`This will delete the original ${_(terminology.post)} and open the composer with its content.`,
673
675
)}
674
676
onConfirm={onConfirmRedraft}
675
677
confirmButtonCta={_(msg`Redraft`)}
···
753
755
754
756
<Menu.Item
755
757
testID="postDropdownCopyTextBtn"
756
-
label={_(msg`Copy post text`)}
758
+
label={_(msg`Copy ${_(terminology.post)} text`)}
757
759
onPress={onCopyPostText}>
758
-
<Menu.ItemText>{_(msg`Copy skeet text`)}</Menu.ItemText>
760
+
<Menu.ItemText>{_(msg`Copy ${_(terminology.post)} text`)}</Menu.ItemText>
759
761
<Menu.ItemIcon icon={ClipboardIcon} position="right" />
760
762
</Menu.Item>
761
763
</>
762
764
) : (
763
765
<Menu.Item
764
766
testID="postDropdownSignInBtn"
765
-
label={_(msg`Sign in to view skeet`)}
767
+
label={_(msg`Sign in to view ${_(terminology.post)}`)}
766
768
onPress={onSignIn}>
767
-
<Menu.ItemText>{_(msg`Sign in to view skeet`)}</Menu.ItemText>
769
+
<Menu.ItemText>{_(msg`Sign in to view ${_(terminology.post)}`)}</Menu.ItemText>
768
770
<Menu.ItemIcon icon={Eye} position="right" />
769
771
</Menu.Item>
770
772
)}
···
847
849
label={
848
850
isReply
849
851
? _(msg`Hide reply for me`)
850
-
: _(msg`Hide skeet for me`)
852
+
: _(msg`Hide ${_(terminology.post)} for me`)
851
853
}
852
854
onPress={() => hidePromptControl.open()}>
853
855
<Menu.ItemText>
854
856
{isReply
855
857
? _(msg`Hide reply for me`)
856
-
: _(msg`Hide skeet for me`)}
858
+
: _(msg`Hide ${_(terminology.post)} for me`)}
857
859
</Menu.ItemText>
858
860
<Menu.ItemIcon icon={EyeSlash} position="right" />
859
861
</Menu.Item>
···
955
957
956
958
<Menu.Item
957
959
testID="postDropdownReportBtn"
958
-
label={_(msg`Report skeet`)}
960
+
label={_(msg`Report ${_(terminology.post)}`)}
959
961
onPress={() => reportDialogControl.open()}>
960
-
<Menu.ItemText>{_(msg`Report skeet`)}</Menu.ItemText>
962
+
<Menu.ItemText>{_(msg`Report ${_(terminology.post)}`)}</Menu.ItemText>
961
963
<Menu.ItemIcon icon={Warning} position="right" />
962
964
</Menu.Item>
963
965
</>
···
986
988
</Menu.Item>
987
989
<Menu.Item
988
990
testID="postDropdownDeleteBtn"
989
-
label={_(msg`Delete post`)}
991
+
label={_(msg`Delete ${_(terminology.post)}`)}
990
992
onPress={() => deletePromptControl.open()}>
991
-
<Menu.ItemText>{_(msg`Delete skeet`)}</Menu.ItemText>
993
+
<Menu.ItemText>{_(msg`Delete ${_(terminology.post)}`)}</Menu.ItemText>
992
994
<Menu.ItemIcon icon={Trash} position="right" />
993
995
</Menu.Item>
994
996
</>
···
1000
1002
1001
1003
<Prompt.Basic
1002
1004
control={deletePromptControl}
1003
-
title={_(msg`Delete this skeet?`)}
1005
+
title={_(msg`Delete this ${_(terminology.post)}?`)}
1004
1006
description={_(
1005
-
msg`If you remove this skeet, you won't be able to recover it.`,
1007
+
msg`If you remove this ${_(terminology.post)}, you won't be able to recover it.`,
1006
1008
)}
1007
1009
onConfirm={onDeletePost}
1008
1010
confirmButtonCta={_(msg`Delete`)}
···
1011
1013
1012
1014
<Prompt.Basic
1013
1015
control={hidePromptControl}
1014
-
title={isReply ? _(msg`Hide this reply?`) : _(msg`Hide this skeet?`)}
1016
+
title={isReply ? _(msg`Hide this reply?`) : _(msg`Hide this ${_(terminology.post)}?`)}
1015
1017
description={_(
1016
-
msg`This skeet will be hidden from feeds and threads. This cannot be undone.`,
1018
+
msg`This ${_(terminology.post)} will be hidden from feeds and threads. This cannot be undone.`,
1017
1019
)}
1018
1020
onConfirm={onHidePost}
1019
1021
confirmButtonCta={_(msg`Hide`)}
···
1036
1038
1037
1039
<Prompt.Basic
1038
1040
control={quotePostDetachConfirmControl}
1039
-
title={_(msg`Detach quote skeet?`)}
1041
+
title={_(msg`Detach quote ${_(terminology.post)}?`)}
1040
1042
description={_(
1041
-
msg`This will remove your skeet from this quote skeet for all users, and replace it with a placeholder.`,
1043
+
msg`This will remove your ${_(terminology.post)} from this quote ${_(terminology.post)} for all users, and replace it with a placeholder.`,
1042
1044
)}
1043
1045
onConfirm={onToggleQuotePostAttachment}
1044
1046
confirmButtonCta={_(msg`Yes, detach`)}
+3
-1
src/components/PostControls/PostMenu/index.tsx
+3
-1
src/components/PostControls/PostMenu/index.tsx
···
10
10
import {useLingui} from '@lingui/react'
11
11
12
12
import {type Shadow} from '#/state/cache/post-shadow'
13
+
import {useTerminology} from '#/lib/hooks/useTerminology'
13
14
import {EventStopper} from '#/view/com/util/EventStopper'
14
15
import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
15
16
import {useMenuControl} from '#/components/Menu'
···
43
44
hitSlop?: Insets
44
45
}): React.ReactNode => {
45
46
const {_} = useLingui()
47
+
const terminology = useTerminology()
46
48
47
49
const menuControl = useMenuControl()
48
50
const [hasBeenOpen, setHasBeenOpen] = useState(false)
···
61
63
return (
62
64
<EventStopper onKeyDown={false}>
63
65
<Menu.Root control={lazyMenuControl}>
64
-
<Menu.Trigger label={_(msg`Open skeet options menu`)}>
66
+
<Menu.Trigger label={_(msg`Open ${_(terminology.post)} options menu`)}>
65
67
{({props}) => {
66
68
return (
67
69
<PostControlButton
+54
-10
src/components/PostControls/RepostButton.tsx
+54
-10
src/components/PostControls/RepostButton.tsx
···
4
4
import {useLingui} from '@lingui/react'
5
5
6
6
import {useHaptics} from '#/lib/haptics'
7
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
8
+
import {useTerminologyPreference} from '#/state/preferences'
7
9
import {useRequireAuth} from '#/state/session'
8
10
import {atoms as a, useTheme} from '#/alf'
9
11
import {Button, ButtonText} from '#/components/Button'
···
37
39
}: Props): React.ReactNode => {
38
40
const t = useTheme()
39
41
const {_} = useLingui()
42
+
const terminologyPreference = useTerminologyPreference()
40
43
const requireAuth = useRequireAuth()
41
44
const dialogControl = Dialog.useDialogControl()
42
45
const formatPostStatCount = useFormatPostStatCount()
···
121
124
}): React.ReactNode => {
122
125
const t = useTheme()
123
126
const {_} = useLingui()
127
+
const terminologyPreference = useTerminologyPreference()
124
128
const playHaptic = useHaptics()
125
129
const control = Dialog.useDialogContext()
126
130
···
142
146
const onPressClose = useCallback(() => control.close(), [control])
143
147
144
148
return (
145
-
<Dialog.ScrollableInner label={_(msg`Reskeet or quote skeet`)}>
149
+
<Dialog.ScrollableInner label={_(getTerminology(terminologyPreference, {
150
+
skeet: msg`Reskeet or quote skeet`,
151
+
post: msg`Repost or quote post`,
152
+
spell: msg`Respell or quote spell`,
153
+
}))}>
146
154
<View style={a.gap_xl}>
147
155
<View style={a.gap_xs}>
148
156
<Button
149
157
style={[a.justify_start, a.px_md, a.gap_sm]}
150
158
label={
151
159
isReposted
152
-
? _(msg`Remove reskeet`)
153
-
: _(msg({message: `Reskeet`, context: 'action'}))
160
+
? _(getTerminology(terminologyPreference, {
161
+
skeet: msg`Remove reskeet`,
162
+
post: msg`Remove repost`,
163
+
spell: msg`Remove respell`,
164
+
}))
165
+
: _(getTerminology(terminologyPreference, {
166
+
skeet: msg({message: `Reskeet`, context: 'action'}),
167
+
post: msg({message: `Repost`, context: 'action'}),
168
+
spell: msg({message: `Respell`, context: 'action'}),
169
+
}))
154
170
}
155
171
onPress={onPressRepost}
156
172
size="large"
···
159
175
<RepostIcon size="lg" fill={t.palette.primary_500} />
160
176
<Text style={[a.font_semi_bold, a.text_xl]}>
161
177
{isReposted ? (
162
-
<Trans>Remove reskeet</Trans>
178
+
<Trans>{_(getTerminology(terminologyPreference, {
179
+
skeet: msg`Remove reskeet`,
180
+
post: msg`Remove repost`,
181
+
spell: msg`Remove respell`,
182
+
}))}</Trans>
163
183
) : (
164
-
<Trans context="action">Reskeet</Trans>
184
+
<Trans>{_(getTerminology(terminologyPreference, {
185
+
skeet: msg({message: `Reskeet`, context: 'action'}),
186
+
post: msg({message: `Repost`, context: 'action'}),
187
+
spell: msg({message: `Respell`, context: 'action'}),
188
+
}))}</Trans>
165
189
)}
166
190
</Text>
167
191
</Button>
···
171
195
style={[a.justify_start, a.px_md, a.gap_sm]}
172
196
label={
173
197
embeddingDisabled
174
-
? _(msg`Quote skeets disabled`)
175
-
: _(msg`Quote skeet`)
198
+
? _(getTerminology(terminologyPreference, {
199
+
skeet: msg`Quote skeets disabled`,
200
+
post: msg`Quote posts disabled`,
201
+
spell: msg`Quote spells disabled`,
202
+
}))
203
+
: _(getTerminology(terminologyPreference, {
204
+
skeet: msg`Quote skeet`,
205
+
post: msg`Quote post`,
206
+
spell: msg`Quote spell`,
207
+
}))
176
208
}
177
209
onPress={onPressQuote}
178
210
size="large"
···
193
225
embeddingDisabled && t.atoms.text_contrast_low,
194
226
]}>
195
227
{embeddingDisabled ? (
196
-
<Trans>Quote skeets disabled</Trans>
228
+
<Trans>{_(getTerminology(terminologyPreference, {
229
+
skeet: msg`Quote skeets disabled`,
230
+
post: msg`Quote posts disabled`,
231
+
spell: msg`Quote spells disabled`,
232
+
}))}</Trans>
197
233
) : (
198
-
<Trans>Quote skeet</Trans>
234
+
<Trans>{_(getTerminology(terminologyPreference, {
235
+
skeet: msg`Quote skeet`,
236
+
post: msg`Quote post`,
237
+
spell: msg`Quote spell`,
238
+
}))}</Trans>
199
239
)}
200
240
</Text>
201
241
</Button>
202
242
</View>
203
243
<Button
204
-
label={_(msg`Cancel quote skeet`)}
244
+
label={_(getTerminology(terminologyPreference, {
245
+
skeet: msg`Cancel quote skeet`,
246
+
post: msg`Cancel quote post`,
247
+
spell: msg`Cancel quote spell`,
248
+
}))}
205
249
onPress={onPressClose}
206
250
size="large"
207
251
color="secondary">
+53
-10
src/components/PostControls/RepostButton.web.tsx
+53
-10
src/components/PostControls/RepostButton.web.tsx
···
1
1
import {msg} from '@lingui/macro'
2
2
import {useLingui} from '@lingui/react'
3
3
4
+
import {getTerminology} from '#/lib/strings/terminology'
5
+
import {useTerminologyPreference} from '#/state/preferences'
4
6
import {useRequireAuth} from '#/state/session'
5
7
import {useSession} from '#/state/session'
6
8
import {EventStopper} from '#/view/com/util/EventStopper'
···
34
36
}: Props) => {
35
37
const t = useTheme()
36
38
const {_} = useLingui()
39
+
const terminologyPreference = useTerminologyPreference()
37
40
const {hasSession} = useSession()
38
41
const requireAuth = useRequireAuth()
39
42
const formatPostStatCount = useFormatPostStatCount()
···
41
44
return hasSession ? (
42
45
<EventStopper onKeyDown={false}>
43
46
<Menu.Root>
44
-
<Menu.Trigger label={_(msg`Repost or quote post`)}>
47
+
<Menu.Trigger label={_(getTerminology(terminologyPreference, {
48
+
skeet: msg`Reskeet or quote skeet`,
49
+
post: msg`Repost or quote post`,
50
+
spell: msg`Respell or quote spell`,
51
+
}))}>
45
52
{({props}) => {
46
53
return (
47
54
<PostControlButton
···
65
72
<Menu.Item
66
73
label={
67
74
isReposted
68
-
? _(msg`Undo reskeet`)
69
-
: _(msg({message: `Reskeet`, context: `action`}))
75
+
? _(getTerminology(terminologyPreference, {
76
+
skeet: msg`Undo reskeet`,
77
+
post: msg`Undo repost`,
78
+
spell: msg`Undo respell`,
79
+
}))
80
+
: _(getTerminology(terminologyPreference, {
81
+
skeet: msg({message: `Reskeet`, context: `action`}),
82
+
post: msg({message: `Repost`, context: `action`}),
83
+
spell: msg({message: `Respell`, context: `action`}),
84
+
}))
70
85
}
71
86
testID="repostDropdownRepostBtn"
72
87
onPress={onRepost}>
73
88
<Menu.ItemText>
74
89
{isReposted
75
-
? _(msg`Undo reskeet`)
76
-
: _(msg({message: `Reskeet`, context: `action`}))}
90
+
? _(getTerminology(terminologyPreference, {
91
+
skeet: msg`Undo reskeet`,
92
+
post: msg`Undo repost`,
93
+
spell: msg`Undo respell`,
94
+
}))
95
+
: _(getTerminology(terminologyPreference, {
96
+
skeet: msg({message: `Reskeet`, context: `action`}),
97
+
post: msg({message: `Repost`, context: `action`}),
98
+
spell: msg({message: `Respell`, context: `action`}),
99
+
}))}
77
100
</Menu.ItemText>
78
101
<Menu.ItemIcon icon={Repost} position="right" />
79
102
</Menu.Item>
···
81
104
disabled={embeddingDisabled}
82
105
label={
83
106
embeddingDisabled
84
-
? _(msg`Quote skeets disabled`)
85
-
: _(msg`Quote skeet`)
107
+
? _(getTerminology(terminologyPreference, {
108
+
skeet: msg`Quote skeets disabled`,
109
+
post: msg`Quote posts disabled`,
110
+
spell: msg`Quote spells disabled`,
111
+
}))
112
+
: _(getTerminology(terminologyPreference, {
113
+
skeet: msg`Quote skeet`,
114
+
post: msg`Quote post`,
115
+
spell: msg`Quote spell`,
116
+
}))
86
117
}
87
118
testID="repostDropdownQuoteBtn"
88
119
onPress={onQuote}>
89
120
<Menu.ItemText>
90
121
{embeddingDisabled
91
-
? _(msg`Quote skeets disabled`)
92
-
: _(msg`Quote skeet`)}
122
+
? _(getTerminology(terminologyPreference, {
123
+
skeet: msg`Quote skeets disabled`,
124
+
post: msg`Quote posts disabled`,
125
+
spell: msg`Quote spells disabled`,
126
+
}))
127
+
: _(getTerminology(terminologyPreference, {
128
+
skeet: msg`Quote skeet`,
129
+
post: msg`Quote post`,
130
+
spell: msg`Quote spell`,
131
+
}))}
93
132
</Menu.ItemText>
94
133
<Menu.ItemIcon icon={Quote} position="right" />
95
134
</Menu.Item>
···
101
140
onPress={() => requireAuth(() => {})}
102
141
active={isReposted}
103
142
activeColor={t.palette.positive_500}
104
-
label={_(msg`Reskeet or quote skeet`)}
143
+
label={_(getTerminology(terminologyPreference, {
144
+
skeet: msg`Reskeet or quote skeet`,
145
+
post: msg`Repost or quote post`,
146
+
spell: msg`Respell or quote spell`,
147
+
}))}
105
148
big={big}>
106
149
<PostControlButtonIcon icon={Repost} />
107
150
{typeof repostCount !== 'undefined' && repostCount > 0 && (
+30
-8
src/components/WhoCanReply.tsx
+30
-8
src/components/WhoCanReply.tsx
···
17
17
18
18
import {HITSLOP_10} from '#/lib/constants'
19
19
import {makeListLink, makeProfileLink} from '#/lib/routes/links'
20
+
import {getTerminology} from '#/lib/strings/terminology'
21
+
import {useTerminologyPreference} from '#/state/preferences'
20
22
import {logger} from '#/logger'
21
23
import {isNative} from '#/platform/detection'
22
24
import {
···
212
214
embeddingDisabled: boolean
213
215
}) {
214
216
const {_} = useLingui()
217
+
const terminologyPreference = useTerminologyPreference()
215
218
216
219
return (
217
220
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
···
221
224
style={web({maxWidth: 400})}>
222
225
<View style={[a.gap_sm]}>
223
226
<Text style={[a.font_semi_bold, a.text_xl, a.pb_sm]}>
224
-
<Trans>Who can interact with this skeet?</Trans>
227
+
<Trans>{_(getTerminology(terminologyPreference, {
228
+
skeet: msg`Who can interact with this skeet?`,
229
+
post: msg`Who can interact with this post?`,
230
+
spell: msg`Who can interact with this spell?`,
231
+
}))}</Trans>
225
232
</Text>
226
233
<Rules
227
234
post={post}
···
258
265
embeddingDisabled: boolean
259
266
}) {
260
267
const t = useTheme()
268
+
const {_} = useLingui()
269
+
const terminologyPreference = useTerminologyPreference()
261
270
262
271
return (
263
272
<>
···
269
278
t.atoms.text_contrast_medium,
270
279
]}>
271
280
{settings.length === 0 ? (
272
-
<Trans>
273
-
This skeet has an unknown type of threadgate on it. Your app may be
274
-
out of date.
275
-
</Trans>
281
+
<Trans>{_(getTerminology(terminologyPreference, {
282
+
skeet: msg`This skeet has an unknown type of threadgate on it. Your app may be out of date.`,
283
+
post: msg`This post has an unknown type of threadgate on it. Your app may be out of date.`,
284
+
spell: msg`This spell has an unknown type of threadgate on it. Your app may be out of date.`,
285
+
}))}</Trans>
276
286
) : settings[0].type === 'everybody' ? (
277
-
<Trans>Everybody can reply to this skeet.</Trans>
287
+
<Trans>{_(getTerminology(terminologyPreference, {
288
+
skeet: msg`Everybody can reply to this skeet.`,
289
+
post: msg`Everybody can reply to this post.`,
290
+
spell: msg`Everybody can reply to this spell.`,
291
+
}))}</Trans>
278
292
) : settings[0].type === 'nobody' ? (
279
-
<Trans>Replies to this skeet are disabled.</Trans>
293
+
<Trans>{_(getTerminology(terminologyPreference, {
294
+
skeet: msg`Replies to this skeet are disabled.`,
295
+
post: msg`Replies to this post are disabled.`,
296
+
spell: msg`Replies to this spell are disabled.`,
297
+
}))}</Trans>
280
298
) : (
281
299
<Trans>
282
300
Only{' '}
···
298
316
a.flex_wrap,
299
317
t.atoms.text_contrast_medium,
300
318
]}>
301
-
<Trans>No one but the author can quote this skeet.</Trans>
319
+
<Trans>{_(getTerminology(terminologyPreference, {
320
+
skeet: msg`No one but the author can quote this skeet.`,
321
+
post: msg`No one but the author can quote this post.`,
322
+
spell: msg`No one but the author can quote this spell.`,
323
+
}))}</Trans>
302
324
</Text>
303
325
)}
304
326
</>
+18
-6
src/components/dialogs/Embed.tsx
+18
-6
src/components/dialogs/Embed.tsx
···
6
6
7
7
import {EMBED_SCRIPT} from '#/lib/constants'
8
8
import {niceDate} from '#/lib/strings/time'
9
+
import {getTerminology} from '#/lib/strings/terminology'
10
+
import {useTerminologyPreference} from '#/state/preferences'
9
11
import {toShareUrl} from '#/lib/strings/url-helpers'
10
12
import {atoms as a, useTheme} from '#/alf'
11
13
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
···
51
53
}: Omit<EmbedDialogProps, 'control'>) {
52
54
const t = useTheme()
53
55
const {_, i18n} = useLingui()
56
+
const terminologyPreference = useTerminologyPreference()
54
57
const [copied, setCopied] = useState(false)
55
58
const [showCustomisation, setShowCustomisation] = useState(false)
56
59
const [colorMode, setColorMode] = useState<ColorModeValues>('system')
···
101
104
}, [i18n, postUri, postCid, record, timestamp, postAuthor, colorMode])
102
105
103
106
return (
104
-
<Dialog.Inner label={_(msg`Embed post`)} style={[{maxWidth: 500}]}>
107
+
<Dialog.Inner label={_(getTerminology(terminologyPreference, {
108
+
skeet: msg`Embed skeet`,
109
+
post: msg`Embed post`,
110
+
spell: msg`Embed spell`,
111
+
}))} style={[{maxWidth: 500}]}>
105
112
<View style={[a.gap_lg]}>
106
113
<View style={[a.gap_sm]}>
107
114
<Text style={[a.text_2xl, a.font_bold]}>
108
-
<Trans>Embed skeet</Trans>
115
+
<Trans>{_(getTerminology(terminologyPreference, {
116
+
skeet: msg`Embed skeet`,
117
+
post: msg`Embed post`,
118
+
spell: msg`Embed spell`,
119
+
}))}</Trans>
109
120
</Text>
110
121
<Text
111
122
style={[a.text_md, t.atoms.text_contrast_medium, a.leading_normal]}>
112
-
<Trans>
113
-
Embed this skeet in your website. Simply copy the following snippet
114
-
and paste it into the HTML code of your website.
115
-
</Trans>
123
+
<Trans>{_(getTerminology(terminologyPreference, {
124
+
skeet: msg`Embed this skeet in your website. Simply copy the following snippet and paste it into the HTML code of your website.`,
125
+
post: msg`Embed this post in your website. Simply copy the following snippet and paste it into the HTML code of your website.`,
126
+
spell: msg`Embed this spell in your website. Simply copy the following snippet and paste it into the HTML code of your website.`,
127
+
}))}</Trans>
116
128
</Text>
117
129
</View>
118
130
<View
+3
-1
src/lib/api/index.ts
+3
-1
src/lib/api/index.ts
···
24
24
25
25
import {isNetworkError} from '#/lib/strings/errors'
26
26
import {parseMarkdownLinks,shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip'
27
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
27
28
import {logger} from '#/logger'
28
29
import {compressImage} from '#/state/gallery'
29
30
import {
···
49
50
replyTo?: string
50
51
onStateChange?: (state: string) => void
51
52
langs?: string[]
53
+
terminologyPreference?: string
52
54
}
53
55
54
56
export async function post(
···
183
185
})
184
186
if (isNetworkError(e)) {
185
187
throw new Error(
186
-
t`Skeet failed to upload. Please check your Internet connection and try again.`,
188
+
getTerminology(opts.terminologyPreference || 'skeet', TERMINOLOGY.uploadFailed),
187
189
)
188
190
} else {
189
191
throw e
+62
src/lib/hooks/useTerminology.ts
+62
src/lib/hooks/useTerminology.ts
···
1
+
import {useMemo} from 'react'
2
+
import {defineMessage} from '@lingui/macro'
3
+
4
+
import {useTerminologyPreference} from '#/state/preferences'
5
+
6
+
export type TerminologyKey =
7
+
| 'post'
8
+
| 'posts'
9
+
| 'repost'
10
+
| 'reposts'
11
+
| 'reposted'
12
+
| 'reposting'
13
+
14
+
const terminologyMap = {
15
+
skeet: {
16
+
post: defineMessage({message: 'skeet'}),
17
+
posts: defineMessage({message: 'skeets'}),
18
+
repost: defineMessage({message: 'reskeet'}),
19
+
reposts: defineMessage({message: 'reskeets'}),
20
+
reposted: defineMessage({message: 'reskeeted'}),
21
+
reposting: defineMessage({message: 'reskeeting'}),
22
+
},
23
+
post: {
24
+
post: defineMessage({message: 'post'}),
25
+
posts: defineMessage({message: 'posts'}),
26
+
repost: defineMessage({message: 'repost'}),
27
+
reposts: defineMessage({message: 'reposts'}),
28
+
reposted: defineMessage({message: 'reposted'}),
29
+
reposting: defineMessage({message: 'reposting'}),
30
+
},
31
+
spell: {
32
+
post: defineMessage({message: 'spell'}),
33
+
posts: defineMessage({message: 'spells'}),
34
+
repost: defineMessage({message: 'respell'}),
35
+
reposts: defineMessage({message: 'respells'}),
36
+
reposted: defineMessage({message: 'respelled'}),
37
+
reposting: defineMessage({message: 'respelling'}),
38
+
},
39
+
}
40
+
41
+
/**
42
+
* Hook to get the correct terminology based on user preference
43
+
* @returns An object with all terminology variants
44
+
*/
45
+
export function useTerminology() {
46
+
const preference = useTerminologyPreference()
47
+
48
+
return useMemo(() => {
49
+
const selectedTerminology = preference || 'skeet'
50
+
return terminologyMap[selectedTerminology]
51
+
}, [preference])
52
+
}
53
+
54
+
/**
55
+
* Hook to get a specific terminology term
56
+
* @param key - The terminology key to retrieve
57
+
* @returns The localized terminology message
58
+
*/
59
+
export function useTerm(key: TerminologyKey) {
60
+
const terminology = useTerminology()
61
+
return terminology[key]
62
+
}
+441
src/lib/strings/terminology.ts
+441
src/lib/strings/terminology.ts
···
1
+
import {msg} from '@lingui/macro'
2
+
3
+
export type TerminologyPreference = 'skeet' | 'post' | 'spell'
4
+
5
+
/**
6
+
* Returns the appropriate terminology based on the user's preference
7
+
* @param preference - The user's terminology preference ('skeet', 'post', or 'spell')
8
+
* @param variants - An object with message descriptors for each terminology option
9
+
* @returns The appropriate message descriptor based on the preference
10
+
*/
11
+
export function getTerminology<T extends {skeet: any; post: any; spell: any}>(
12
+
preference: TerminologyPreference | undefined,
13
+
variants: T,
14
+
): T[keyof T] {
15
+
const pref = preference ?? 'skeet'
16
+
return variants[pref]
17
+
}
18
+
19
+
/**
20
+
* Common terminology variants used throughout the app
21
+
*/
22
+
export const TERMINOLOGY = {
23
+
// Single post
24
+
singular: {
25
+
skeet: msg`skeet`,
26
+
post: msg`post`,
27
+
spell: msg`spell`,
28
+
},
29
+
// Multiple posts
30
+
plural: {
31
+
skeet: msg`skeets`,
32
+
post: msg`posts`,
33
+
spell: msg`spells`,
34
+
},
35
+
// "Skeet by @handle"
36
+
byHandle: (handle: string) => ({
37
+
skeet: msg`Skeet by @${handle}`,
38
+
post: msg`Post by @${handle}`,
39
+
spell: msg`Spell by @${handle}`,
40
+
}),
41
+
// Repost terminology
42
+
repost: {
43
+
singular: {
44
+
skeet: msg`reskeet`,
45
+
post: msg`repost`,
46
+
spell: msg`respell`,
47
+
},
48
+
plural: {
49
+
skeet: msg`reskeets`,
50
+
post: msg`reposts`,
51
+
spell: msg`respells`,
52
+
},
53
+
pastTense: {
54
+
skeet: msg`reskeeted`,
55
+
post: msg`reposted`,
56
+
spell: msg`respelled`,
57
+
},
58
+
byLine: {
59
+
skeet: msg`Reskeeted By`,
60
+
post: msg`Reposted By`,
61
+
spell: msg`Respelled By`,
62
+
},
63
+
action: {
64
+
skeet: msg`reskeet`,
65
+
post: msg`repost`,
66
+
spell: msg`respell`,
67
+
},
68
+
},
69
+
// For metrics/counts
70
+
metrics: {
71
+
skeet: msg`skeets metrics`,
72
+
post: msg`posts metrics`,
73
+
spell: msg`spells metrics`,
74
+
},
75
+
// For carousel
76
+
carousel: {
77
+
skeet: msg`Combine reskeets into a horizontal carousel`,
78
+
post: msg`Combine reposts into a horizontal carousel`,
79
+
spell: msg`Combine respells into a horizontal carousel`,
80
+
},
81
+
// For notifications
82
+
viaRepostNotification: {
83
+
skeet: msg`Disable "via reskeet" notifications`,
84
+
post: msg`Disable "via repost" notifications`,
85
+
spell: msg`Disable "via respell" notifications`,
86
+
},
87
+
viaRepostPrivacy: {
88
+
skeet: msg`Forcefully disables the notifications other people receive when you like/reskeet a skeet someone else has reskeeted for privacy.`,
89
+
post: msg`Forcefully disables the notifications other people receive when you like/repost a post someone else has reposted for privacy.`,
90
+
spell: msg`Forcefully disables the notifications other people receive when you like/respell a spell someone else has respelled for privacy.`,
91
+
},
92
+
// For external share buttons
93
+
externalShareButtons: {
94
+
skeet: msg`Show "Open original skeet" and "Open skeet in PDSls" buttons`,
95
+
post: msg`Show "Open original post" and "Open post in PDSls" buttons`,
96
+
spell: msg`Show "Open original spell" and "Open spell in PDSls" buttons`,
97
+
},
98
+
// For metrics labels
99
+
repostMetrics: {
100
+
skeet: msg`Disable reskeets metrics`,
101
+
post: msg`Disable reposts metrics`,
102
+
spell: msg`Disable respells metrics`,
103
+
},
104
+
// For deletion messages
105
+
deleted: {
106
+
skeet: msg`This skeet was deleted by its author`,
107
+
post: msg`This post was deleted by its author`,
108
+
spell: msg`This spell was deleted by its author`,
109
+
},
110
+
deletedShort: {
111
+
skeet: msg`Skeet has been deleted`,
112
+
post: msg`Post has been deleted`,
113
+
spell: msg`Spell has been deleted`,
114
+
},
115
+
// For error messages
116
+
notFound: {
117
+
skeet: msg`Skeet not found`,
118
+
post: msg`Post not found`,
119
+
spell: msg`Spell not found`,
120
+
},
121
+
blocked: {
122
+
skeet: msg`Skeet blocked`,
123
+
post: msg`Post blocked`,
124
+
spell: msg`Spell blocked`,
125
+
},
126
+
errorLoading: {
127
+
skeet: msg`Error loading skeet`,
128
+
post: msg`Error loading post`,
129
+
spell: msg`Error loading spell`,
130
+
},
131
+
// For success messages
132
+
sent: {
133
+
skeet: msg`Your skeet was sent`,
134
+
post: msg`Your post was sent`,
135
+
spell: msg`Your spell was sent`,
136
+
},
137
+
sentPlural: {
138
+
skeet: msg`Your skeets were sent`,
139
+
post: msg`Your posts were sent`,
140
+
spell: msg`Your spells were sent`,
141
+
},
142
+
pinned: {
143
+
skeet: msg`Skeet pinned`,
144
+
post: msg`Post pinned`,
145
+
spell: msg`Spell pinned`,
146
+
},
147
+
unpinned: {
148
+
skeet: msg`Skeet unpinned`,
149
+
post: msg`Post unpinned`,
150
+
spell: msg`Spell unpinned`,
151
+
},
152
+
failedToPin: {
153
+
skeet: msg`Failed to pin skeet`,
154
+
post: msg`Failed to pin post`,
155
+
spell: msg`Failed to pin spell`,
156
+
},
157
+
// For composer
158
+
addAnother: {
159
+
skeet: msg`Add another skeet`,
160
+
post: msg`Add another post`,
161
+
spell: msg`Add another spell`,
162
+
},
163
+
anythingBut: {
164
+
skeet: msg`Anything but skeet`,
165
+
post: msg`Anything but post`,
166
+
spell: msg`Anything but spell`,
167
+
},
168
+
delete: {
169
+
skeet: msg`Delete skeet`,
170
+
post: msg`Delete post`,
171
+
spell: msg`Delete spell`,
172
+
},
173
+
discard: {
174
+
skeet: msg`Discard skeet?`,
175
+
post: msg`Discard post?`,
176
+
spell: msg`Discard spell?`,
177
+
},
178
+
discardConfirm: {
179
+
skeet: msg`Are you sure you'd like to discard this skeet?`,
180
+
post: msg`Are you sure you'd like to discard this post?`,
181
+
spell: msg`Are you sure you'd like to discard this spell?`,
182
+
},
183
+
view: {
184
+
skeet: msg`View skeet`,
185
+
post: msg`View post`,
186
+
spell: msg`View spell`,
187
+
},
188
+
// For composer actions
189
+
composeNew: {
190
+
skeet: msg`Compose new skeet`,
191
+
post: msg`Compose new post`,
192
+
spell: msg`Compose new spell`,
193
+
},
194
+
newAction: {
195
+
skeet: msg`New Skeet`,
196
+
post: msg`New Post`,
197
+
spell: msg`New Spell`,
198
+
},
199
+
postAll: {
200
+
skeet: msg`Skeet All`,
201
+
post: msg`Post All`,
202
+
spell: msg`Spell All`,
203
+
},
204
+
postSingle: {
205
+
skeet: msg`Skeet`,
206
+
post: msg`Post`,
207
+
spell: msg`Spell`,
208
+
},
209
+
// For language selection
210
+
selectLanguage: {
211
+
skeet: msg`Select skeet language`,
212
+
post: msg`Select post language`,
213
+
spell: msg`Select spell language`,
214
+
},
215
+
chooseLanguages: {
216
+
skeet: msg`Choose Skeet Languages`,
217
+
post: msg`Choose Post Languages`,
218
+
spell: msg`Choose Spell Languages`,
219
+
},
220
+
languageDescription: {
221
+
skeet: msg`Select up to 3 languages used in this skeet`,
222
+
post: msg`Select up to 3 languages used in this post`,
223
+
spell: msg`Select up to 3 languages used in this spell`,
224
+
},
225
+
replyingLanguage: (langs: string) => ({
226
+
skeet: msg`The skeet you're replying to was marked as being written in ${langs}`,
227
+
post: msg`The post you're replying to was marked as being written in ${langs}`,
228
+
spell: msg`The spell you're replying to was marked as being written in ${langs}`,
229
+
}),
230
+
// For interaction settings
231
+
interactionSettings: {
232
+
skeet: msg`Skeet interaction settings`,
233
+
post: msg`Post interaction settings`,
234
+
spell: msg`Spell interaction settings`,
235
+
},
236
+
editInteractionSettings: {
237
+
skeet: msg`Edit skeet interaction settings`,
238
+
post: msg`Edit post interaction settings`,
239
+
spell: msg`Edit spell interaction settings`,
240
+
},
241
+
whoCanInteract: {
242
+
skeet: msg`Who can interact with this skeet?`,
243
+
post: msg`Who can interact with this post?`,
244
+
spell: msg`Who can interact with this spell?`,
245
+
},
246
+
// For embedding
247
+
embed: {
248
+
skeet: msg`Embed skeet`,
249
+
post: msg`Embed post`,
250
+
spell: msg`Embed spell`,
251
+
},
252
+
embedDescription: {
253
+
skeet: msg`Embed this skeet in your website. Simply copy the following snippet and paste it into the HTML code of your website.`,
254
+
post: msg`Embed this post in your website. Simply copy the following snippet and paste it into the HTML code of your website.`,
255
+
spell: msg`Embed this spell in your website. Simply copy the following snippet and paste it into the HTML code of your website.`,
256
+
},
257
+
// For sharing
258
+
copyLink: {
259
+
skeet: msg`Copy link to skeet`,
260
+
post: msg`Copy link to post`,
261
+
spell: msg`Copy link to spell`,
262
+
},
263
+
shareUri: {
264
+
skeet: msg`Share skeet at:// URI`,
265
+
post: msg`Share post at:// URI`,
266
+
spell: msg`Share spell at:// URI`,
267
+
},
268
+
copyUri: {
269
+
skeet: msg`Copy skeet at:// URI`,
270
+
post: msg`Copy post at:// URI`,
271
+
spell: msg`Copy spell at:// URI`,
272
+
},
273
+
sendTo: {
274
+
skeet: msg`Send skeet to...`,
275
+
post: msg`Send post to...`,
276
+
spell: msg`Send spell to...`,
277
+
},
278
+
openOriginal: {
279
+
skeet: msg`Open original skeet`,
280
+
post: msg`Open original post`,
281
+
spell: msg`Open original spell`,
282
+
},
283
+
openInPDSls: {
284
+
skeet: msg`Open skeet in PDSls`,
285
+
post: msg`Open post in PDSls`,
286
+
spell: msg`Open spell in PDSls`,
287
+
},
288
+
// For reporting
289
+
report: {
290
+
skeet: msg`Report this skeet`,
291
+
post: msg`Report this post`,
292
+
spell: msg`Report this spell`,
293
+
},
294
+
reportWhy: {
295
+
skeet: msg`Why should this skeet be reviewed?`,
296
+
post: msg`Why should this post be reviewed?`,
297
+
spell: msg`Why should this spell be reviewed?`,
298
+
},
299
+
// For search and browse
300
+
search: {
301
+
skeet: msg`Search skeets`,
302
+
post: msg`Search posts`,
303
+
spell: msg`Search spells`,
304
+
},
305
+
searchProfile: (handle: string) => ({
306
+
skeet: msg`Search @${handle}'s skeets`,
307
+
post: msg`Search @${handle}'s posts`,
308
+
spell: msg`Search @${handle}'s spells`,
309
+
}),
310
+
searchMy: {
311
+
skeet: msg`Search my skeets`,
312
+
post: msg`Search my posts`,
313
+
spell: msg`Search my spells`,
314
+
},
315
+
browse: (tag: string) => ({
316
+
skeet: msg`Browse skeets about ${tag}`,
317
+
post: msg`Browse posts about ${tag}`,
318
+
spell: msg`Browse spells about ${tag}`,
319
+
}),
320
+
browseTagged: (tag: string) => ({
321
+
skeet: msg`Browse skeets tagged with ${tag}`,
322
+
post: msg`Browse posts tagged with ${tag}`,
323
+
spell: msg`Browse spells tagged with ${tag}`,
324
+
}),
325
+
seeTag: (tag: string) => ({
326
+
skeet: msg`See ${tag} skeets`,
327
+
post: msg`See ${tag} posts`,
328
+
spell: msg`See ${tag} spells`,
329
+
}),
330
+
seeTagByUser: (tag: string) => ({
331
+
skeet: msg`See ${tag} skeets by user`,
332
+
post: msg`See ${tag} posts by user`,
333
+
spell: msg`See ${tag} spells by user`,
334
+
}),
335
+
// For loading and errors
336
+
loadNew: {
337
+
skeet: msg`Load new skeets`,
338
+
post: msg`Load new posts`,
339
+
spell: msg`Load new spells`,
340
+
},
341
+
fetchError: {
342
+
skeet: msg`There was an issue fetching skeets. Tap here to try again.`,
343
+
post: msg`There was an issue fetching posts. Tap here to try again.`,
344
+
spell: msg`There was an issue fetching spells. Tap here to try again.`,
345
+
},
346
+
uploadFailed: {
347
+
skeet: msg`Skeet failed to upload. Please check your Internet connection and try again.`,
348
+
post: msg`Post failed to upload. Please check your Internet connection and try again.`,
349
+
spell: msg`Spell failed to upload. Please check your Internet connection and try again.`,
350
+
},
351
+
// For feeds and filtering
352
+
ranOut: {
353
+
skeet: msg`We ran out of skeets from your follows. Here's the latest from`,
354
+
post: msg`We ran out of posts from your follows. Here's the latest from`,
355
+
spell: msg`We ran out of spells from your follows. Here's the latest from`,
356
+
},
357
+
// For quote posts
358
+
quoteAction: {
359
+
skeet: msg`Quote skeet`,
360
+
post: msg`Quote post`,
361
+
spell: msg`Quote spell`,
362
+
},
363
+
quoteDisabled: {
364
+
skeet: msg`Quote skeets disabled`,
365
+
post: msg`Quote posts disabled`,
366
+
spell: msg`Quote spells disabled`,
367
+
},
368
+
allowQuote: {
369
+
skeet: msg`Allow quote skeets`,
370
+
post: msg`Allow quote posts`,
371
+
spell: msg`Allow quote spells`,
372
+
},
373
+
quoteAuthorDisabled: {
374
+
skeet: msg`This skeet's author has disabled quote skeets.`,
375
+
post: msg`This post's author has disabled quote posts.`,
376
+
spell: msg`This spell's author has disabled quote spells.`,
377
+
},
378
+
cancelQuote: {
379
+
skeet: msg`Cancel quote skeet`,
380
+
post: msg`Cancel quote post`,
381
+
spell: msg`Cancel quote spell`,
382
+
},
383
+
noQuoteYet: {
384
+
skeet: msg`No one but the author can quote this skeet.`,
385
+
post: msg`No one but the author can quote this post.`,
386
+
spell: msg`No one but the author can quote this spell.`,
387
+
},
388
+
// For reply settings
389
+
repliesDisabled: {
390
+
skeet: msg`Replies to this skeet are disabled.`,
391
+
post: msg`Replies to this post are disabled.`,
392
+
spell: msg`Replies to this spell are disabled.`,
393
+
},
394
+
everyoneCanReply: {
395
+
skeet: msg`Everybody can reply to this skeet.`,
396
+
post: msg`Everybody can reply to this post.`,
397
+
spell: msg`Everybody can reply to this spell.`,
398
+
},
399
+
unknownThreadgate: {
400
+
skeet: msg`This skeet has an unknown type of threadgate on it. Your app may be out of date.`,
401
+
post: msg`This post has an unknown type of threadgate on it. Your app may be out of date.`,
402
+
spell: msg`This spell has an unknown type of threadgate on it. Your app may be out of date.`,
403
+
},
404
+
// For replies
405
+
repliedTo: {
406
+
skeet: msg`Replied to a skeet`,
407
+
post: msg`Replied to a post`,
408
+
spell: msg`Replied to a spell`,
409
+
},
410
+
repliedToBlocked: {
411
+
skeet: msg`Replied to a blocked skeet`,
412
+
post: msg`Replied to a blocked post`,
413
+
spell: msg`Replied to a blocked spell`,
414
+
},
415
+
replyWasDeleted: {
416
+
skeet: msg`We're sorry! The skeet you are replying to has been deleted.`,
417
+
post: msg`We're sorry! The post you are replying to has been deleted.`,
418
+
spell: msg`We're sorry! The spell you are replying to has been deleted.`,
419
+
},
420
+
sortReplies: {
421
+
skeet: msg`Sort replies to the same skeet by:`,
422
+
post: msg`Sort replies to the same post by:`,
423
+
spell: msg`Sort replies to the same spell by:`,
424
+
},
425
+
showRepliesTree: {
426
+
skeet: msg`Show skeet replies in a threaded tree view`,
427
+
post: msg`Show post replies in a threaded tree view`,
428
+
spell: msg`Show spell replies in a threaded tree view`,
429
+
},
430
+
// For visibility
431
+
onlyLoggedIn: {
432
+
skeet: msg`This skeet is only visible to logged-in users.`,
433
+
post: msg`This post is only visible to logged-in users.`,
434
+
spell: msg`This spell is only visible to logged-in users.`,
435
+
},
436
+
noteOnlyLoggedIn: {
437
+
skeet: msg`Note: This skeet is only visible to logged-in users.`,
438
+
post: msg`Note: This post is only visible to logged-in users.`,
439
+
spell: msg`Note: This spell is only visible to logged-in users.`,
440
+
},
441
+
}
+25
-4
src/screens/Bookmarks/index.tsx
+25
-4
src/screens/Bookmarks/index.tsx
···
16
16
import {useCleanError} from '#/lib/hooks/useCleanError'
17
17
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
18
18
import {usePostViewTracking} from '#/lib/hooks/usePostViewTracking'
19
+
import {getTerminology} from '#/lib/strings/terminology'
19
20
import {
20
21
type CommonNavigatorParams,
21
22
type NativeStackScreenProps,
22
23
} from '#/lib/routes/types'
23
24
import {logger} from '#/logger'
24
25
import {isIOS} from '#/platform/detection'
26
+
import {useTerminologyPreference} from '#/state/preferences'
25
27
import {useBookmarkMutation} from '#/state/queries/bookmarks/useBookmarkMutation'
26
28
import {useBookmarksQuery} from '#/state/queries/bookmarks/useBookmarksQuery'
27
29
import {useSetMinimalShellMode} from '#/state/shell'
···
42
44
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Bookmarks'>
43
45
44
46
export function BookmarksScreen({}: Props) {
47
+
const {_} = useLingui()
48
+
const terminologyPreference = useTerminologyPreference()
45
49
const setMinimalShellMode = useSetMinimalShellMode()
46
50
47
51
useFocusEffect(
···
57
61
<Layout.Header.BackButton />
58
62
<Layout.Header.Content>
59
63
<Layout.Header.TitleText>
60
-
<Trans>Saved Skeets</Trans>
64
+
<Trans>{_(getTerminology(terminologyPreference, {
65
+
skeet: msg`Saved Skeets`,
66
+
post: msg`Saved Posts`,
67
+
spell: msg`Saved Spells`,
68
+
}))}</Trans>
61
69
</Layout.Header.TitleText>
62
70
</Layout.Header.Content>
63
71
<Layout.Header.Slot />
···
209
217
}) {
210
218
const t = useTheme()
211
219
const {_} = useLingui()
220
+
const terminologyPreference = useTerminologyPreference()
212
221
const {mutateAsync: bookmark} = useBookmarkMutation()
213
222
const cleanError = useCleanError()
214
223
215
224
const remove = async () => {
216
225
try {
217
226
await bookmark({action: 'delete', uri: post.uri})
218
-
toast.show(_(msg`Removed from saved skeets`), {
227
+
toast.show(_(getTerminology(terminologyPreference, {
228
+
skeet: msg`Removed from saved skeets`,
229
+
post: msg`Removed from saved posts`,
230
+
spell: msg`Removed from saved spells`,
231
+
})), {
219
232
type: 'info',
220
233
})
221
234
} catch (e: any) {
···
253
266
a.italic,
254
267
t.atoms.text_contrast_medium,
255
268
]}>
256
-
<Trans>This skeet was deleted by its author</Trans>
269
+
<Trans>{_(getTerminology(terminologyPreference, {
270
+
skeet: msg`This skeet was deleted by its author`,
271
+
post: msg`This post was deleted by its author`,
272
+
spell: msg`This spell was deleted by its author`,
273
+
}))}</Trans>
257
274
</Text>
258
275
</View>
259
276
<Button
260
-
label={_(msg`Remove from saved skeets`)}
277
+
label={_(getTerminology(terminologyPreference, {
278
+
skeet: msg`Remove from saved skeets`,
279
+
post: msg`Remove from saved posts`,
280
+
spell: msg`Remove from saved spells`,
281
+
}))}
261
282
size="tiny"
262
283
color="secondary"
263
284
onPress={remove}>
+5
-1
src/screens/Post/PostLikedBy.tsx
+5
-1
src/screens/Post/PostLikedBy.tsx
···
4
4
import {useFocusEffect} from '@react-navigation/native'
5
5
6
6
import {useSetTitle} from '#/lib/hooks/useSetTitle'
7
+
import {useTerminology} from '#/lib/hooks/useTerminology'
7
8
import {
8
9
type CommonNavigatorParams,
9
10
type NativeStackScreenProps,
···
20
21
export const PostLikedByScreen = ({route}: Props) => {
21
22
const {_} = useLingui()
22
23
const setMinimalShellMode = useSetMinimalShellMode()
24
+
const terminology = useTerminology()
23
25
const {name, rkey} = route.params
24
26
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
25
27
const {data: post} = usePostQuery(uri)
···
27
29
const {data: resolvedDid} = useResolveDidQuery(name)
28
30
const {data: profile} = useProfileQuery({did: resolvedDid})
29
31
30
-
useSetTitle(profile ? _(msg`Skeet by @${profile.handle}`) : undefined)
32
+
useSetTitle(
33
+
profile ? `${_(terminology.post)} ${_(msg`by`)} @${profile.handle}` : undefined,
34
+
)
31
35
32
36
let likeCount
33
37
if (post) {
+5
-1
src/screens/Post/PostQuotes.tsx
+5
-1
src/screens/Post/PostQuotes.tsx
···
4
4
import {useFocusEffect} from '@react-navigation/native'
5
5
6
6
import {useSetTitle} from '#/lib/hooks/useSetTitle'
7
+
import {useTerminology} from '#/lib/hooks/useTerminology'
7
8
import {
8
9
type CommonNavigatorParams,
9
10
type NativeStackScreenProps,
···
20
21
export const PostQuotesScreen = ({route}: Props) => {
21
22
const {_} = useLingui()
22
23
const setMinimalShellMode = useSetMinimalShellMode()
24
+
const terminology = useTerminology()
23
25
const {name, rkey} = route.params
24
26
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
25
27
const {data: post} = usePostQuery(uri)
···
27
29
const {data: resolvedDid} = useResolveDidQuery(name)
28
30
const {data: profile} = useProfileQuery({did: resolvedDid})
29
31
30
-
useSetTitle(profile ? _(msg`Skeet by @${profile.handle}`) : undefined)
32
+
useSetTitle(
33
+
profile ? `${_(terminology.post)} ${_(msg`by`)} @${profile.handle}` : undefined,
34
+
)
31
35
32
36
let quoteCount
33
37
if (post) {
+8
-4
src/screens/Post/PostRepostedBy.tsx
+8
-4
src/screens/Post/PostRepostedBy.tsx
···
4
4
import {useFocusEffect} from '@react-navigation/native'
5
5
6
6
import {useSetTitle} from '#/lib/hooks/useSetTitle'
7
+
import {useTerminology} from '#/lib/hooks/useTerminology'
7
8
import {
8
9
type CommonNavigatorParams,
9
10
type NativeStackScreenProps,
···
19
20
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'>
20
21
export const PostRepostedByScreen = ({route}: Props) => {
21
22
const {_} = useLingui()
23
+
const terminology = useTerminology()
22
24
const {name, rkey} = route.params
23
25
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
24
26
const setMinimalShellMode = useSetMinimalShellMode()
···
27
29
const {data: resolvedDid} = useResolveDidQuery(name)
28
30
const {data: profile} = useProfileQuery({did: resolvedDid})
29
31
30
-
useSetTitle(profile ? _(msg`Skeet by @${profile.handle}`) : undefined)
32
+
useSetTitle(
33
+
profile ? `${_(terminology.post)} ${_(msg`by`)} @${profile.handle}` : undefined,
34
+
)
31
35
32
36
let quoteCount
33
37
if (post) {
···
48
52
{post && (
49
53
<>
50
54
<Layout.Header.TitleText>
51
-
<Trans>Reskeeted By</Trans>
55
+
<Trans>{_(terminology.reposted)} By</Trans>
52
56
</Layout.Header.TitleText>
53
57
<Layout.Header.SubtitleText>
54
58
<Plural
55
59
value={quoteCount ?? 0}
56
-
one="# reskeet"
57
-
other="# reskeets"
60
+
one={_(terminology.repost)}
61
+
other={_(terminology.reposts)}
58
62
/>
59
63
</Layout.Header.SubtitleText>
60
64
</>
+6
-3
src/screens/PostThread/components/ThreadError.tsx
+6
-3
src/screens/PostThread/components/ThreadError.tsx
···
4
4
import {useLingui} from '@lingui/react'
5
5
6
6
import {useCleanError} from '#/lib/hooks/useCleanError'
7
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
8
+
import {useTerminologyPreference} from '#/state/preferences'
7
9
import {OUTER_SPACE} from '#/screens/PostThread/const'
8
10
import {atoms as a, useTheme} from '#/alf'
9
11
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
···
20
22
}) {
21
23
const t = useTheme()
22
24
const {_} = useLingui()
25
+
const terminologyPreference = useTerminologyPreference()
23
26
const cleanError = useCleanError()
24
27
25
28
const {title, message} = useMemo(() => {
26
-
let title = _(msg`Error loading skeet`)
29
+
let title = _(getTerminology(terminologyPreference, TERMINOLOGY.errorLoading))
27
30
let message = _(msg`Something went wrong. Please try again in a moment.`)
28
31
29
32
const {raw, clean} = cleanError(error)
30
33
31
34
if (error.message.startsWith('Post not found')) {
32
-
title = _(msg`Skeet not found`)
35
+
title = _(getTerminology(terminologyPreference, TERMINOLOGY.notFound))
33
36
message = clean || raw || message
34
37
}
35
38
36
39
return {title, message}
37
-
}, [_, error, cleanError])
40
+
}, [_, error, cleanError, terminologyPreference])
38
41
39
42
return (
40
43
<Layout.Center>
+14
-15
src/screens/PostThread/components/ThreadItemAnchor.tsx
+14
-15
src/screens/PostThread/components/ThreadItemAnchor.tsx
···
11
11
import {useLingui} from '@lingui/react'
12
12
13
13
import {useActorStatus} from '#/lib/actor-status'
14
+
import {useTerminology} from '#/lib/hooks/useTerminology'
14
15
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
15
16
import {useTranslate} from '#/lib/hooks/useTranslate'
16
17
import {makeProfileLink} from '#/lib/routes/links'
···
102
103
103
104
function ThreadItemAnchorDeleted({isRoot}: {isRoot: boolean}) {
104
105
const t = useTheme()
106
+
const {_} = useLingui()
107
+
const terminology = useTerminology()
105
108
106
109
return (
107
110
<>
···
136
139
</View>
137
140
<Text
138
141
style={[a.text_md, a.font_semi_bold, t.atoms.text_contrast_medium]}>
139
-
<Trans>Skeet has been deleted</Trans>
142
+
<Trans>{_(terminology.post)} has been deleted</Trans>
140
143
</Text>
141
144
</View>
142
145
</View>
···
187
190
const {currentAccount, hasSession} = useSession()
188
191
const feedFeedback = useFeedFeedback(postSource?.feedSourceInfo, hasSession)
189
192
const formatPostStatCount = useFormatPostStatCount()
193
+
const terminology = useTerminology()
190
194
191
195
const post = postShadow
192
196
const record = item.value.post.record
···
446
450
{post.repostCount != null &&
447
451
post.repostCount !== 0 &&
448
452
!disableRepostsMetrics ? (
449
-
<Link to={repostsHref} label={_(msg`Reskeets of this skeet`)}>
453
+
<Link to={repostsHref} label={_(msg`${_(terminology.reposts)} of this ${_(terminology.post)}`)}>
450
454
<Text
451
455
testID="repostCount-expanded"
452
456
style={[a.text_md, t.atoms.text_contrast_medium]}>
453
457
<Text style={[a.text_md, a.font_semi_bold, t.atoms.text]}>
454
458
{formatPostStatCount(post.repostCount)}
455
459
</Text>{' '}
456
-
<Plural
457
-
value={post.repostCount}
458
-
one="reskeet"
459
-
other="reskeets"
460
-
/>
460
+
{post.repostCount === 1 ? _(terminology.repost) : _(terminology.reposts)}
461
461
</Text>
462
462
</Link>
463
463
) : null}
···
465
465
post.quoteCount !== 0 &&
466
466
!post.viewer?.embeddingDisabled &&
467
467
!disableQuotesMetrics ? (
468
-
<Link to={quotesHref} label={_(msg`Quotes of this skeet`)}>
468
+
<Link to={quotesHref} label={_(msg`Quotes of this ${_(terminology.post)}`)}>
469
469
<Text
470
470
testID="quoteCount-expanded"
471
471
style={[a.text_md, t.atoms.text_contrast_medium]}>
···
483
483
{post.likeCount != null &&
484
484
post.likeCount !== 0 &&
485
485
!disableLikesMetrics ? (
486
-
<Link to={likesHref} label={_(msg`Likes on this skeet`)}>
486
+
<Link to={likesHref} label={_(msg`Likes on this ${_(terminology.post)}`)}>
487
487
<Text
488
488
testID="likeCount-expanded"
489
489
style={[a.text_md, t.atoms.text_contrast_medium]}>
···
623
623
const {_, i18n} = useLingui()
624
624
const control = Prompt.usePromptControl()
625
625
const enableSquareButtons = useEnableSquareButtons()
626
+
const terminology = useTerminology()
626
627
627
628
const indexedAt = new Date(post.indexedAt)
628
629
const createdAt = bsky.dangerousIsType<AppBskyFeedPost.Record>(
···
643
644
return (
644
645
<>
645
646
<Button
646
-
label={_(msg`Archived post`)}
647
-
accessibilityHint={_(
648
-
msg`Shows information about when this skeet was created`,
649
-
)}
647
+
label={`${_(msg`Archived`)} ${_(terminology.post)}`}
648
+
accessibilityHint={`${_(msg`Shows information about when this`)} ${_(terminology.post)} ${_(msg`was created`)}`}
650
649
onPress={e => {
651
650
e.preventDefault()
652
651
e.stopPropagation()
···
682
681
683
682
<Prompt.Outer control={control}>
684
683
<Prompt.TitleText>
685
-
<Trans>Archived post</Trans>
684
+
<Trans>Archived {_(terminology.post)}</Trans>
686
685
</Prompt.TitleText>
687
686
<Prompt.DescriptionText>
688
687
<Trans>
689
-
This skeet claims to have been created on{' '}
688
+
This {_(terminology.post)} claims to have been created on{' '}
690
689
<RNText style={[a.font_semi_bold]}>
691
690
{niceDate(i18n, createdAt)}
692
691
</RNText>
+6
-1
src/screens/PostThread/components/ThreadItemPost.tsx
+6
-1
src/screens/PostThread/components/ThreadItemPost.tsx
···
7
7
RichText as RichTextAPI,
8
8
} from '@atproto/api'
9
9
import {Trans} from '@lingui/macro'
10
+
import {useLingui} from '@lingui/react'
10
11
11
12
import {useActorStatus} from '#/lib/actor-status'
12
13
import {MAX_POST_LINES} from '#/lib/constants'
13
14
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
14
15
import {makeProfileLink} from '#/lib/routes/links'
15
16
import {countLines} from '#/lib/strings/helpers'
17
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
18
+
import {useTerminologyPreference} from '#/state/preferences'
16
19
import {
17
20
POST_TOMBSTONE,
18
21
type Shadow,
···
83
86
overrides,
84
87
}: Pick<ThreadItemPostProps, 'item' | 'overrides'>) {
85
88
const t = useTheme()
89
+
const {_} = useLingui()
90
+
const terminologyPreference = useTerminologyPreference()
86
91
87
92
return (
88
93
<ThreadItemPostOuterWrapper item={item} overrides={overrides}>
···
109
114
</View>
110
115
<Text
111
116
style={[a.text_md, a.font_semi_bold, t.atoms.text_contrast_medium]}>
112
-
<Trans>Skeet has been deleted</Trans>
117
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.deletedShort))}</Trans>
113
118
</Text>
114
119
</View>
115
120
+6
-3
src/screens/PostThread/components/ThreadItemPostTombstone.tsx
+6
-3
src/screens/PostThread/components/ThreadItemPostTombstone.tsx
···
3
3
import {msg} from '@lingui/macro'
4
4
import {useLingui} from '@lingui/react'
5
5
6
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
7
+
import {useTerminologyPreference} from '#/state/preferences'
6
8
import {LINEAR_AVI_WIDTH, OUTER_SPACE} from '#/screens/PostThread/const'
7
9
import {atoms as a, useTheme} from '#/alf'
8
10
import {PersonX_Stroke2_Corner0_Rounded as PersonXIcon} from '#/components/icons/Person'
···
16
18
export function ThreadItemPostTombstone({type}: ThreadItemPostTombstoneProps) {
17
19
const t = useTheme()
18
20
const {_} = useLingui()
21
+
const terminologyPreference = useTerminologyPreference()
19
22
const {copy, Icon} = useMemo(() => {
20
23
switch (type) {
21
24
case 'blocked':
22
-
return {copy: _(msg`Skeet blocked`), Icon: PersonXIcon}
25
+
return {copy: _(getTerminology(terminologyPreference, TERMINOLOGY.blocked)), Icon: PersonXIcon}
23
26
case 'not-found':
24
27
default:
25
-
return {copy: _(msg`Skeet not found`), Icon: TrashIcon}
28
+
return {copy: _(getTerminology(terminologyPreference, TERMINOLOGY.notFound)), Icon: TrashIcon}
26
29
}
27
-
}, [_, type])
30
+
}, [_, type, terminologyPreference])
28
31
29
32
return (
30
33
<View
+8
-1
src/screens/Profile/Header/Metrics.tsx
+8
-1
src/screens/Profile/Header/Metrics.tsx
···
4
4
import {useLingui} from '@lingui/react'
5
5
6
6
import {makeProfileLink} from '#/lib/routes/links'
7
+
import {getTerminology} from '#/lib/strings/terminology'
7
8
import {type Shadow} from '#/state/cache/types'
9
+
import {useTerminologyPreference} from '#/state/preferences'
8
10
import {useDisableFollowersMetrics} from '#/state/preferences/disable-followers-metrics'
9
11
import {useDisableFollowingMetrics} from '#/state/preferences/disable-following-metrics'
10
12
import {useDisablePostsMetrics} from '#/state/preferences/disable-posts-metrics'
···
20
22
}) {
21
23
const t = useTheme()
22
24
const {_, i18n} = useLingui()
25
+
const terminologyPreference = useTerminologyPreference()
23
26
const following = formatCount(i18n, profile.followsCount || 0)
24
27
const followers = formatCount(i18n, profile.followersCount || 0)
25
28
const pluralizedFollowers = plural(profile.followersCount || 0, {
···
70
73
<Text style={[a.font_semi_bold, t.atoms.text, a.text_md]}>
71
74
{formatCount(i18n, profile.postsCount || 0)}{' '}
72
75
<Text style={[t.atoms.text_contrast_medium, a.font_normal, a.text_md]}>
73
-
{plural(profile.postsCount || 0, {one: 'skeet', other: 'skeets'})}
76
+
{_(getTerminology(terminologyPreference, {
77
+
skeet: msg`${plural(profile.postsCount || 0, {one: 'skeet', other: 'skeets'})}`,
78
+
post: msg`${plural(profile.postsCount || 0, {one: 'post', other: 'posts'})}`,
79
+
spell: msg`${plural(profile.postsCount || 0, {one: 'spell', other: 'spells'})}`,
80
+
}))}
74
81
</Text>
75
82
</Text>
76
83
) : null}
+4
-1
src/screens/Profile/ProfileFeed/index.tsx
+4
-1
src/screens/Profile/ProfileFeed/index.tsx
···
16
16
import {type CommonNavigatorParams} from '#/lib/routes/types'
17
17
import {type NavigationProp} from '#/lib/routes/types'
18
18
import {makeRecordUri} from '#/lib/strings/url-helpers'
19
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
19
20
import {s} from '#/lib/styles'
20
21
import {isNative} from '#/platform/detection'
21
22
import {listenSoftReset} from '#/state/events'
22
23
import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback'
24
+
import {useTerminologyPreference} from '#/state/preferences'
23
25
import {
24
26
type FeedSourceFeedInfo,
25
27
useFeedSourceInfoQuery,
···
159
161
feedParams: FeedParams | undefined
160
162
}) {
161
163
const {_} = useLingui()
164
+
const terminologyPreference = useTerminologyPreference()
162
165
const {hasSession} = useSession()
163
166
const {openComposer} = useOpenComposer()
164
167
const isScreenFocused = useIsFocused()
···
228
231
{(isScrolledDown || hasNew) && (
229
232
<LoadLatestBtn
230
233
onPress={onScrollToTop}
231
-
label={_(msg`Load new skeets`)}
234
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.loadNew))}
232
235
showIndicator={hasNew}
233
236
/>
234
237
)}
+6
-3
src/screens/Profile/ProfileSearch.tsx
+6
-3
src/screens/Profile/ProfileSearch.tsx
···
3
3
import {useLingui} from '@lingui/react'
4
4
5
5
import {useSetTitle} from '#/lib/hooks/useSetTitle'
6
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
7
+
import {useTerminologyPreference} from '#/state/preferences'
6
8
import {
7
9
type CommonNavigatorParams,
8
10
type NativeStackScreenProps,
···
16
18
export const ProfileSearchScreen = ({route}: Props) => {
17
19
const {name, q: queryParam = ''} = route.params
18
20
const {_} = useLingui()
21
+
const terminologyPreference = useTerminologyPreference()
19
22
const {currentAccount} = useSession()
20
23
21
24
const {data: resolvedDid} = useResolveDidQuery(name)
22
25
const {data: profile} = useProfileQuery({did: resolvedDid})
23
26
24
-
useSetTitle(profile ? _(msg`Search @${profile.handle}'s skeets`) : undefined)
27
+
useSetTitle(profile ? _(getTerminology(terminologyPreference, TERMINOLOGY.searchProfile(profile.handle))) : undefined)
25
28
26
29
const fixedParams = useMemo(
27
30
() => ({
···
36
39
inputPlaceholder={
37
40
profile
38
41
? currentAccount?.did === profile.did
39
-
? _(msg`Search my skeets`)
40
-
: _(msg`Search @${profile.handle}'s skeets`)
42
+
? _(getTerminology(terminologyPreference, TERMINOLOGY.searchMy))
43
+
: _(getTerminology(terminologyPreference, TERMINOLOGY.searchProfile(profile.handle)))
41
44
: _(msg`Search...`)
42
45
}
43
46
fixedParams={fixedParams}
+4
-1
src/screens/Profile/Sections/Feed.tsx
+4
-1
src/screens/Profile/Sections/Feed.tsx
···
5
5
import {useQueryClient} from '@tanstack/react-query'
6
6
7
7
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
8
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
8
9
import {isIOS, isNative} from '#/platform/detection'
10
+
import {useTerminologyPreference} from '#/state/preferences'
9
11
import {
10
12
type FeedDescriptor,
11
13
RQKEY as FEED_RQKEY,
···
49
51
emptyStateIcon,
50
52
}: FeedSectionProps) {
51
53
const {_} = useLingui()
54
+
const terminologyPreference = useTerminologyPreference()
52
55
const queryClient = useQueryClient()
53
56
const [hasNew, setHasNew] = useState(false)
54
57
const [isScrolledDown, setIsScrolledDown] = useState(false)
···
113
116
{(isScrolledDown || hasNew) && (
114
117
<LoadLatestBtn
115
118
onPress={onScrollToTop}
116
-
label={_(msg`Load new skeets`)}
119
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.loadNew))}
117
120
showIndicator={hasNew}
118
121
/>
119
122
)}
+12
-18
src/screens/Settings/DeerSettings.tsx
+12
-18
src/screens/Settings/DeerSettings.tsx
···
9
9
import {APPVIEW_DID_PROXY} from '#/lib/constants'
10
10
import {usePalette} from '#/lib/hooks/usePalette'
11
11
import {type CommonNavigatorParams} from '#/lib/routes/types'
12
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
12
13
import {type Gate} from '#/lib/statsig/gates'
13
14
import {
14
15
resetDeerGateCache,
···
559
560
</SettingsList.ItemText>
560
561
<Toggle.Item
561
562
name="external_share_buttons"
562
-
label={_(
563
-
msg`Show "Open original skeet" and "Open skeet in PDSls" buttons`,
564
-
)}
563
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.externalShareButtons))}
565
564
value={showExternalShareButtons}
566
565
onChange={value => setShowExternalShareButtons(value)}
567
566
style={[a.w_full]}>
568
567
<Toggle.LabelText style={[a.flex_1]}>
569
-
<Trans>
570
-
Show "Open original skeet" and "Open skeet in PDSls" buttons
571
-
</Trans>
568
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.externalShareButtons))}</Trans>
572
569
</Toggle.LabelText>
573
570
<Toggle.Platform />
574
571
</Toggle.Item>
···
741
738
742
739
<Toggle.Item
743
740
name="repost_carousel"
744
-
label={_(msg`Combine reskeets into a horizontal carousel`)}
741
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.carousel))}
745
742
value={repostCarouselEnabled}
746
743
onChange={value => setRepostCarouselEnabled(value)}
747
744
style={[a.w_full]}>
748
745
<Toggle.LabelText style={[a.flex_1]}>
749
-
<Trans>Combine reskeets into a horizontal carousel</Trans>
746
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.carousel))}</Trans>
750
747
</Toggle.LabelText>
751
748
<Toggle.Platform />
752
749
</Toggle.Item>
···
797
794
798
795
<Toggle.Item
799
796
name="disable_via_repost_notification"
800
-
label={_(msg`Disable "via reskeet" notifications`)}
797
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.viaRepostNotification))}
801
798
value={disableViaRepostNotification}
802
799
onChange={value => setDisableViaRepostNotification(value)}
803
800
style={[a.w_full]}>
804
801
<Toggle.LabelText style={[a.flex_1]}>
805
-
<Trans>Disable "via reskeet" notifications</Trans>
802
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.viaRepostNotification))}</Trans>
806
803
</Toggle.LabelText>
807
804
<Toggle.Platform />
808
805
</Toggle.Item>
809
806
<Admonition type="info" style={[a.flex_1]}>
810
-
<Trans>
811
-
Forcefully disables the notifications other people receive when
812
-
you like/reskeet a skeet someone else has reskeeted for privacy.
813
-
</Trans>
807
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.viaRepostPrivacy))}</Trans>
814
808
</Admonition>
815
809
816
810
<Toggle.Item
···
883
877
884
878
<Toggle.Item
885
879
name="disable_reposts_metrics"
886
-
label={_(msg`Disable reskeets metrics`)}
880
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.repostMetrics))}
887
881
value={disableRepostsMetrics}
888
882
onChange={value => setDisableRepostsMetrics(value)}
889
883
style={[a.w_full]}>
890
884
<Toggle.LabelText style={[a.flex_1]}>
891
-
<Trans>Disable reskeets metrics</Trans>
885
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.repostMetrics))}</Trans>
892
886
</Toggle.LabelText>
893
887
<Toggle.Platform />
894
888
</Toggle.Item>
···
967
961
968
962
<Toggle.Item
969
963
name="disable_posts_metrics"
970
-
label={_(msg`Disable skeets metrics`)}
964
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.metrics))}
971
965
value={disablePostsMetrics}
972
966
onChange={value => setDisablePostsMetrics(value)}
973
967
style={[a.w_full]}>
974
968
<Toggle.LabelText style={[a.flex_1]}>
975
-
<Trans>Disable skeets metrics</Trans>
969
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.metrics))}</Trans>
976
970
</Toggle.LabelText>
977
971
<Toggle.Platform />
978
972
</Toggle.Item>
+23
-4
src/screens/Settings/FollowingFeedPreferences.tsx
+23
-4
src/screens/Settings/FollowingFeedPreferences.tsx
···
5
5
type CommonNavigatorParams,
6
6
type NativeStackScreenProps,
7
7
} from '#/lib/routes/types'
8
+
import {getTerminology} from '#/lib/strings/terminology'
9
+
import {useTerminologyPreference} from '#/state/preferences'
8
10
import {
9
11
usePreferencesQuery,
10
12
useSetFeedViewPreferencesMutation,
···
25
27
>
26
28
export function FollowingFeedPreferencesScreen({}: Props) {
27
29
const {_} = useLingui()
30
+
const terminologyPreference = useTerminologyPreference()
28
31
29
32
const {data: preferences} = usePreferencesQuery()
30
33
const {mutate: setFeedViewPref, variables} =
···
86
89
<Toggle.Item
87
90
type="checkbox"
88
91
name="show-reposts"
89
-
label={_(msg`Show reskeets`)}
92
+
label={_(getTerminology(terminologyPreference, {
93
+
skeet: msg`Show reskeets`,
94
+
post: msg`Show reposts`,
95
+
spell: msg`Show respells`,
96
+
}))}
90
97
value={showReposts}
91
98
onChange={value =>
92
99
setFeedViewPref({
···
96
103
<SettingsList.Item>
97
104
<SettingsList.ItemIcon icon={RepostIcon} />
98
105
<SettingsList.ItemText>
99
-
<Trans>Show reskeets</Trans>
106
+
<Trans>{_(getTerminology(terminologyPreference, {
107
+
skeet: msg`Show reskeets`,
108
+
post: msg`Show reposts`,
109
+
spell: msg`Show respells`,
110
+
}))}</Trans>
100
111
</SettingsList.ItemText>
101
112
<Toggle.Platform />
102
113
</SettingsList.Item>
···
104
115
<Toggle.Item
105
116
type="checkbox"
106
117
name="show-quotes"
107
-
label={_(msg`Show quote skeets`)}
118
+
label={_(getTerminology(terminologyPreference, {
119
+
skeet: msg`Show quote skeets`,
120
+
post: msg`Show quote posts`,
121
+
spell: msg`Show quote spells`,
122
+
}))}
108
123
value={showQuotePosts}
109
124
onChange={value =>
110
125
setFeedViewPref({
···
114
129
<SettingsList.Item>
115
130
<SettingsList.ItemIcon icon={QuoteIcon} />
116
131
<SettingsList.ItemText>
117
-
<Trans>Show quote skeets</Trans>
132
+
<Trans>{_(getTerminology(terminologyPreference, {
133
+
skeet: msg`Show quote skeets`,
134
+
post: msg`Show quote posts`,
135
+
spell: msg`Show quote spells`,
136
+
}))}</Trans>
118
137
</SettingsList.ItemText>
119
138
<Toggle.Platform />
120
139
</SettingsList.Item>
+16
-5
src/screens/Settings/NotificationSettings/LikesOnRepostsNotificationSettings.tsx
+16
-5
src/screens/Settings/NotificationSettings/LikesOnRepostsNotificationSettings.tsx
···
1
1
import {View} from 'react-native'
2
-
import {Trans} from '@lingui/macro'
2
+
import {msg, Trans} from '@lingui/macro'
3
+
import {useLingui} from '@lingui/react'
3
4
4
5
import {
5
6
type AllNavigatorParams,
6
7
type NativeStackScreenProps,
7
8
} from '#/lib/routes/types'
9
+
import {getTerminology} from '#/lib/strings/terminology'
10
+
import {useTerminologyPreference} from '#/state/preferences'
8
11
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
9
12
import {atoms as a} from '#/alf'
10
13
import {Admonition} from '#/components/Admonition'
···
19
22
'LikesOnRepostsNotificationSettings'
20
23
>
21
24
export function LikesOnRepostsNotificationSettingsScreen({}: Props) {
25
+
const {_} = useLingui()
26
+
const terminologyPreference = useTerminologyPreference()
22
27
const {data: preferences, isError} = useNotificationSettingsQuery()
23
28
24
29
return (
···
38
43
<SettingsList.ItemIcon icon={LikeRepostIcon} />
39
44
<ItemTextWithSubtitle
40
45
bold
41
-
titleText={<Trans>Likes of your reskeets</Trans>}
46
+
titleText={<Trans>{_(getTerminology(terminologyPreference, {
47
+
skeet: msg`Likes of your reskeets`,
48
+
post: msg`Likes of your reposts`,
49
+
spell: msg`Likes of your respells`,
50
+
}))}</Trans>}
42
51
subtitleText={
43
-
<Trans>
44
-
Get notifications when people like skeets that you've reskeeted.
45
-
</Trans>
52
+
<Trans>{_(getTerminology(terminologyPreference, {
53
+
skeet: msg`Get notifications when people like skeets that you've reskeeted.`,
54
+
post: msg`Get notifications when people like posts that you've reposted.`,
55
+
spell: msg`Get notifications when people like spells that you've respelled.`,
56
+
}))}</Trans>
46
57
}
47
58
/>
48
59
</SettingsList.Item>
+16
-3
src/screens/Settings/NotificationSettings/RepostNotificationSettings.tsx
+16
-3
src/screens/Settings/NotificationSettings/RepostNotificationSettings.tsx
···
1
1
import {View} from 'react-native'
2
-
import {Trans} from '@lingui/macro'
2
+
import {msg, Trans} from '@lingui/macro'
3
+
import {useLingui} from '@lingui/react'
3
4
4
5
import {
5
6
type AllNavigatorParams,
6
7
type NativeStackScreenProps,
7
8
} from '#/lib/routes/types'
9
+
import {getTerminology} from '#/lib/strings/terminology'
10
+
import {useTerminologyPreference} from '#/state/preferences'
8
11
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
9
12
import {atoms as a} from '#/alf'
10
13
import {Admonition} from '#/components/Admonition'
···
19
22
'RepostNotificationSettings'
20
23
>
21
24
export function RepostNotificationSettingsScreen({}: Props) {
25
+
const {_} = useLingui()
26
+
const terminologyPreference = useTerminologyPreference()
22
27
const {data: preferences, isError} = useNotificationSettingsQuery()
23
28
24
29
return (
···
38
43
<SettingsList.ItemIcon icon={RepostIcon} />
39
44
<ItemTextWithSubtitle
40
45
bold
41
-
titleText={<Trans>Reskeets</Trans>}
46
+
titleText={<Trans>{_(getTerminology(terminologyPreference, {
47
+
skeet: msg`Reskeets`,
48
+
post: msg`Reposts`,
49
+
spell: msg`Respells`,
50
+
}))}</Trans>}
42
51
subtitleText={
43
-
<Trans>Get notifications when people reskeet your skeets.</Trans>
52
+
<Trans>{_(getTerminology(terminologyPreference, {
53
+
skeet: msg`Get notifications when people reskeet your skeets.`,
54
+
post: msg`Get notifications when people repost your posts.`,
55
+
spell: msg`Get notifications when people respell your spells.`,
56
+
}))}</Trans>
44
57
}
45
58
/>
46
59
</SettingsList.Item>
+16
-6
src/screens/Settings/NotificationSettings/RepostsOnRepostsNotificationSettings.tsx
+16
-6
src/screens/Settings/NotificationSettings/RepostsOnRepostsNotificationSettings.tsx
···
1
1
import {View} from 'react-native'
2
-
import {Trans} from '@lingui/macro'
2
+
import {msg, Trans} from '@lingui/macro'
3
+
import {useLingui} from '@lingui/react'
3
4
4
5
import {
5
6
type AllNavigatorParams,
6
7
type NativeStackScreenProps,
7
8
} from '#/lib/routes/types'
9
+
import {getTerminology} from '#/lib/strings/terminology'
10
+
import {useTerminologyPreference} from '#/state/preferences'
8
11
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
9
12
import {atoms as a} from '#/alf'
10
13
import {Admonition} from '#/components/Admonition'
···
19
22
'RepostsOnRepostsNotificationSettings'
20
23
>
21
24
export function RepostsOnRepostsNotificationSettingsScreen({}: Props) {
25
+
const {_} = useLingui()
26
+
const terminologyPreference = useTerminologyPreference()
22
27
const {data: preferences, isError} = useNotificationSettingsQuery()
23
28
24
29
return (
···
38
43
<SettingsList.ItemIcon icon={RepostRepostIcon} />
39
44
<ItemTextWithSubtitle
40
45
bold
41
-
titleText={<Trans>Reskeets of your reskeets</Trans>}
46
+
titleText={<Trans>{_(getTerminology(terminologyPreference, {
47
+
skeet: msg`Reskeets of your reskeets`,
48
+
post: msg`Reposts of your reposts`,
49
+
spell: msg`Respells of your respells`,
50
+
}))}</Trans>}
42
51
subtitleText={
43
-
<Trans>
44
-
Get notifications when people reskeet skeets that you've
45
-
reskeeted.
46
-
</Trans>
52
+
<Trans>{_(getTerminology(terminologyPreference, {
53
+
skeet: msg`Get notifications when people reskeet skeets that you've reskeeted.`,
54
+
post: msg`Get notifications when people repost posts that you've reposted.`,
55
+
spell: msg`Get notifications when people respell spells that you've respelled.`,
56
+
}))}</Trans>
47
57
}
48
58
/>
49
59
</SettingsList.Item>
+6
-3
src/state/queries/pinned-post.ts
+6
-3
src/state/queries/pinned-post.ts
···
3
3
import {useMutation, useQueryClient} from '@tanstack/react-query'
4
4
5
5
import {logger} from '#/logger'
6
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
7
+
import {useTerminologyPreference} from '#/state/preferences'
6
8
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
7
9
import * as Toast from '#/view/com/util/Toast'
8
10
import {updatePostShadow} from '../cache/post-shadow'
···
11
13
12
14
export function usePinnedPostMutation() {
13
15
const {_} = useLingui()
16
+
const terminologyPreference = useTerminologyPreference()
14
17
const {currentAccount} = useSession()
15
18
const agent = useAgent()
16
19
const queryClient = useQueryClient()
···
56
59
})
57
60
58
61
if (pinCurrentPost) {
59
-
Toast.show(_(msg({message: 'Skeet pinned', context: 'toast'})))
62
+
Toast.show(_(getTerminology(terminologyPreference, TERMINOLOGY.pinned)))
60
63
} else {
61
-
Toast.show(_(msg({message: 'Skeet unpinned', context: 'toast'})))
64
+
Toast.show(_(getTerminology(terminologyPreference, TERMINOLOGY.unpinned)))
62
65
}
63
66
64
67
queryClient.invalidateQueries({
···
72
75
),
73
76
})
74
77
} catch (e: any) {
75
-
Toast.show(_(msg`Failed to pin skeet`))
78
+
Toast.show(_(getTerminology(terminologyPreference, TERMINOLOGY.failedToPin)))
76
79
logger.error('Failed to pin post', {message: String(e)})
77
80
// revert optimistic update
78
81
updatePostShadow(queryClient, postUri, {
+16
-15
src/view/com/composer/Composer.tsx
+16
-15
src/view/com/composer/Composer.tsx
···
74
74
import {type NavigationProp} from '#/lib/routes/types'
75
75
import {logEvent} from '#/lib/statsig/statsig'
76
76
import {cleanError} from '#/lib/strings/errors'
77
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
77
78
import {colors} from '#/lib/styles'
78
79
import {logger} from '#/logger'
79
80
import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection'
···
85
86
pasteImage,
86
87
} from '#/state/gallery'
87
88
import {useModalControls} from '#/state/modals'
88
-
import {useRequireAltTextEnabled} from '#/state/preferences'
89
+
import {useRequireAltTextEnabled, useTerminologyPreference} from '#/state/preferences'
89
90
import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons'
90
91
import {
91
92
fromPostLanguages,
···
185
186
const currentDid = currentAccount!.did
186
187
const {closeComposer} = useComposerControls()
187
188
const {_} = useLingui()
189
+
const terminologyPreference = useTerminologyPreference()
188
190
const requireAltTextEnabled = useRequireAltTextEnabled()
189
191
const langPrefs = useLanguagePrefs()
190
192
const setLangPrefs = useLanguagePrefsApi()
···
456
458
replyTo: replyTo?.uri,
457
459
onStateChange: setPublishingStage,
458
460
langs: currentLanguages,
461
+
terminologyPreference,
459
462
})
460
463
).uris[0]
461
464
···
509
512
510
513
let err = cleanError(e.message)
511
514
if (err.includes('not locate record')) {
512
-
err = _(
513
-
msg`We're sorry! The skeet you are replying to has been deleted.`,
514
-
)
515
+
err = _(getTerminology(terminologyPreference, TERMINOLOGY.replyWasDeleted))
515
516
} else if (e instanceof EmbeddingDisabledError) {
516
-
err = _(msg`This skeet's author has disabled quote skeets.`)
517
+
err = _(getTerminology(terminologyPreference, TERMINOLOGY.quoteAuthorDisabled))
517
518
}
518
519
setError(err)
519
520
setIsPublishing(false)
···
573
574
<Toast.Icon />
574
575
<Toast.Text>
575
576
{thread.posts.length > 1
576
-
? _(msg`Your skeets were sent`)
577
+
? _(getTerminology(terminologyPreference, TERMINOLOGY.sentPlural))
577
578
: replyTo
578
579
? _(msg`Your reply was sent`)
579
-
: _(msg`Your skeet was sent`)}
580
+
: _(getTerminology(terminologyPreference, TERMINOLOGY.sent))}
580
581
</Toast.Text>
581
582
{postUri && (
582
583
<Toast.Action
583
-
label={_(msg`View skeet`)}
584
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.view))}
584
585
onPress={() => {
585
586
const {host: name, rkey} = new AtUri(postUri)
586
587
navigation.navigate('PostThread', {name, rkey})
···
857
858
const selectTextInputPlaceholder = isReply
858
859
? isFirstPost
859
860
? _(msg`Write your reply`)
860
-
: _(msg`Add another skeet`)
861
-
: _(msg`Anything but skeet`)
861
+
: _(getTerminology(terminologyPreference, TERMINOLOGY.addAnother))
862
+
: _(getTerminology(terminologyPreference, TERMINOLOGY.anythingBut))
862
863
const discardPromptControl = Prompt.usePromptControl()
863
864
864
865
const enableSquareButtons = useEnableSquareButtons()
···
972
973
{canRemovePost && isActive && (
973
974
<>
974
975
<Button
975
-
label={_(msg`Delete skeet`)}
976
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.delete))}
976
977
size="small"
977
978
color="secondary"
978
979
variant="ghost"
···
997
998
</Button>
998
999
<Prompt.Basic
999
1000
control={discardPromptControl}
1000
-
title={_(msg`Discard skeet?`)}
1001
-
description={_(msg`Are you sure you'd like to discard this skeet?`)}
1001
+
title={_(getTerminology(terminologyPreference, TERMINOLOGY.discard))}
1002
+
description={_(getTerminology(terminologyPreference, TERMINOLOGY.discardConfirm))}
1002
1003
onConfirm={() => {
1003
1004
dispatch({
1004
1005
type: 'remove_post',
···
1133
1134
{isReply ? (
1134
1135
<Trans context="action">Reply</Trans>
1135
1136
) : isThread ? (
1136
-
<Trans context="action">Skeet All</Trans>
1137
+
<Trans context="action">{_(getTerminology(terminologyPreference, TERMINOLOGY.postAll))}</Trans>
1137
1138
) : (
1138
-
<Trans context="action">Skeet</Trans>
1139
+
<Trans context="action">{_(getTerminology(terminologyPreference, TERMINOLOGY.postSingle))}</Trans>
1139
1140
)}
1140
1141
</ButtonText>
1141
1142
</Button>
+6
-7
src/view/com/composer/select-language/PostLanguageSelect.tsx
+6
-7
src/view/com/composer/select-language/PostLanguageSelect.tsx
···
3
3
4
4
import {LANG_DROPDOWN_HITSLOP} from '#/lib/constants'
5
5
import {codeToLanguageName} from '#/locale/helpers'
6
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
7
+
import {useTerminologyPreference} from '#/state/preferences'
6
8
import {
7
9
toPostLanguages,
8
10
useLanguagePrefs,
···
25
27
onSelectLanguage?: (language: string) => void
26
28
}) {
27
29
const {_} = useLingui()
30
+
const terminologyPreference = useTerminologyPreference()
28
31
const langPrefs = useLanguagePrefs()
29
32
const setLangPrefs = useLanguagePrefsApi()
30
33
const languageDialogControl = Dialog.useDialogControl()
···
54
57
return (
55
58
<>
56
59
<Menu.Root>
57
-
<Menu.Trigger label={_(msg`Select skeet language`)}>
60
+
<Menu.Trigger label={_(getTerminology(terminologyPreference, TERMINOLOGY.selectLanguage))}>
58
61
{({props}) => (
59
62
<LanguageBtn currentLanguages={currentLanguages} {...props} />
60
63
)}
···
109
112
},
110
113
) {
111
114
const {_} = useLingui()
115
+
const terminologyPreference = useTerminologyPreference()
112
116
const langPrefs = useLanguagePrefs()
113
117
const t = useTheme()
114
118
···
120
124
testID="selectLangBtn"
121
125
size="small"
122
126
hitSlop={LANG_DROPDOWN_HITSLOP}
123
-
label={_(
124
-
msg({
125
-
message: `Skeet language selection`,
126
-
comment: `Accessibility label for button that opens dialog to choose post language settings`,
127
-
}),
128
-
)}
127
+
label={_(getTerminology(terminologyPreference, TERMINOLOGY.selectLanguage))}
129
128
accessibilityHint={_(msg`Opens post language settings`)}
130
129
style={[a.mr_xs]}
131
130
{...props}>
+5
-2
src/view/com/composer/select-language/PostLanguageSelectDialog.tsx
+5
-2
src/view/com/composer/select-language/PostLanguageSelectDialog.tsx
···
7
7
import {languageName} from '#/locale/helpers'
8
8
import {type Language, LANGUAGES, LANGUAGES_MAP_CODE2} from '#/locale/languages'
9
9
import {isNative, isWeb} from '#/platform/detection'
10
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
11
+
import {useTerminologyPreference} from '#/state/preferences'
10
12
import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons'
11
13
import {
12
14
toPostLanguages,
···
93
95
const setLangPrefs = useLanguagePrefsApi()
94
96
const t = useTheme()
95
97
const {_} = useLingui()
98
+
const terminologyPreference = useTerminologyPreference()
96
99
97
100
const enableSquareButtons = useEnableSquareButtons()
98
101
···
184
187
a.text_xl,
185
188
a.mb_sm,
186
189
]}>
187
-
<Trans>Choose Skeet Languages</Trans>
190
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.chooseLanguages))}</Trans>
188
191
</Text>
189
192
<Text
190
193
nativeID="dialog-description"
···
194
197
a.text_md,
195
198
a.mb_lg,
196
199
]}>
197
-
<Trans>Select up to 3 languages used in this skeet</Trans>
200
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.languageDescription))}</Trans>
198
201
</Text>
199
202
</View>
200
203
+13
-4
src/view/com/post-thread/PostRepostedBy.tsx
+13
-4
src/view/com/post-thread/PostRepostedBy.tsx
···
5
5
6
6
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
7
7
import {cleanError} from '#/lib/strings/errors'
8
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
9
+
import {useTerminologyPreference} from '#/state/preferences'
8
10
import {logger} from '#/logger'
9
11
import {usePostRepostedByQuery} from '#/state/queries/post-reposted-by'
10
12
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
···
34
36
35
37
export function PostRepostedBy({uri}: {uri: string}) {
36
38
const {_} = useLingui()
39
+
const terminologyPreference = useTerminologyPreference()
37
40
const initialNumToRender = useInitialNumToRender()
38
41
39
42
const [isPTRing, setIsPTRing] = useState(false)
···
87
90
isLoading={isLoadingUri || isLoadingRepostedBy}
88
91
isError={isError}
89
92
emptyType="results"
90
-
emptyTitle={_(msg`No reskeets yet`)}
91
-
emptyMessage={_(
92
-
msg`Nobody has reskeeted this yet. Maybe you should be the first!`,
93
-
)}
93
+
emptyTitle={_(getTerminology(terminologyPreference, {
94
+
skeet: msg`No reskeets yet`,
95
+
post: msg`No reposts yet`,
96
+
spell: msg`No respells yet`,
97
+
}))}
98
+
emptyMessage={_(getTerminology(terminologyPreference, {
99
+
skeet: msg`Nobody has reskeeted this yet. Maybe you should be the first!`,
100
+
post: msg`Nobody has reposted this yet. Maybe you should be the first!`,
101
+
spell: msg`Nobody has respelled this yet. Maybe you should be the first!`,
102
+
}))}
94
103
errorMessage={cleanError(resolveError || error)}
95
104
sideBorders={false}
96
105
/>
+8
-3
src/view/com/posts/PostFeedReason.tsx
+8
-3
src/view/com/posts/PostFeedReason.tsx
···
6
6
import {isReasonFeedSource, type ReasonFeedSource} from '#/lib/api/feed/types'
7
7
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
8
8
import {makeProfileLink} from '#/lib/routes/links'
9
+
import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology'
10
+
import {useTerminologyPreference} from '#/state/preferences'
9
11
import {useSession} from '#/state/session'
10
12
import {atoms as a, useTheme} from '#/alf'
11
13
import {Pin_Stroke2_Corner0_Rounded as PinIcon} from '#/components/icons/Pin'
···
30
32
}) {
31
33
const t = useTheme()
32
34
const {_} = useLingui()
35
+
const terminologyPreference = useTerminologyPreference()
33
36
34
37
const {currentAccount} = useSession()
35
38
···
74
77
style={styles.includeReason}
75
78
to={makeProfileLink(reason.by)}
76
79
label={
77
-
isOwner ? _(msg`Reskeeted by you`) : _(msg`Reskeeted by ${reposter}`)
80
+
isOwner
81
+
? _(getTerminology(terminologyPreference, TERMINOLOGY.repost.byLine))
82
+
: `${_(getTerminology(terminologyPreference, TERMINOLOGY.repost.byLine))} ${reposter}`
78
83
}
79
84
onPress={onOpenReposter}>
80
85
<RepostIcon
···
91
96
]}
92
97
numberOfLines={1}>
93
98
{isOwner ? (
94
-
<Trans>Reskeeted by you</Trans>
99
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.repost.byLine))}</Trans>
95
100
) : (
96
-
<Trans>Reskeeted by {reposter}</Trans>
101
+
<Trans>{_(getTerminology(terminologyPreference, TERMINOLOGY.repost.byLine))} {reposter}</Trans>
97
102
)}
98
103
</Text>
99
104
</ProfileHoverCard>