@ethora/chat-component-rn 26.7.10 → 26.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/Header/HeaderRoomList.js +3 -1
- package/lib/commonjs/components/Header/HeaderRoomList.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js +25 -0
- package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ReduxWrapper.js +24 -3
- package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageTranslate.js +16 -7
- package/lib/commonjs/components/MessageBubble/MessageTranslate.js.map +1 -1
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +7 -3
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +12 -10
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/DeleteChatModal.js +9 -7
- package/lib/commonjs/components/Modals/ChatProfileModal/DeleteChatModal.js.map +1 -1
- package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js +3 -3
- package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
- package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js +8 -1
- package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +32 -2
- package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
- package/lib/commonjs/components/styled/DateLabel.js +10 -3
- package/lib/commonjs/components/styled/DateLabel.js.map +1 -1
- package/lib/commonjs/components/styled/NewMessageLabel.js +3 -1
- package/lib/commonjs/components/styled/NewMessageLabel.js.map +1 -1
- package/lib/commonjs/components/styled/SendInput.js +6 -6
- package/lib/commonjs/components/styled/SendInput.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +7 -0
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/getDataFromXml.js +24 -1
- package/lib/commonjs/helpers/getDataFromXml.js.map +1 -1
- package/lib/commonjs/helpers/insertMessageWithDelimiter.js +13 -0
- package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
- package/lib/commonjs/helpers/isUnresolvedSenderId.js +6 -1
- package/lib/commonjs/helpers/isUnresolvedSenderId.js.map +1 -1
- package/lib/commonjs/helpers/resolveLangSource.js +38 -0
- package/lib/commonjs/helpers/resolveLangSource.js.map +1 -0
- package/lib/commonjs/helpers/xmppUsername.js +39 -0
- package/lib/commonjs/helpers/xmppUsername.js.map +1 -0
- package/lib/commonjs/hooks/useChatWrapperInit.js +4 -2
- package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/commonjs/i18n/strings.js +120 -0
- package/lib/commonjs/i18n/strings.js.map +1 -1
- package/lib/commonjs/i18n/useT.js +1 -1
- package/lib/commonjs/networking/stanzaHandlers.js +41 -70
- package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
- package/lib/commonjs/roomStore/persistence.js +17 -1
- package/lib/commonjs/roomStore/persistence.js.map +1 -1
- package/lib/commonjs/version.js +3 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/module/components/Header/HeaderRoomList.js +3 -1
- package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
- package/lib/module/components/MainComponents/ChatWrapper.js +26 -1
- package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/ReduxWrapper.js +26 -5
- package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageTranslate.js +16 -7
- package/lib/module/components/MessageBubble/MessageTranslate.js.map +1 -1
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js +7 -3
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +12 -10
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/DeleteChatModal.js +9 -7
- package/lib/module/components/Modals/ChatProfileModal/DeleteChatModal.js.map +1 -1
- package/lib/module/components/Modals/NewChatModal/NewChatModal.js +3 -3
- package/lib/module/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
- package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js +8 -1
- package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -1
- package/lib/module/components/RoomComponents/ChatRoomItem.js +33 -3
- package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
- package/lib/module/components/styled/DateLabel.js +10 -3
- package/lib/module/components/styled/DateLabel.js.map +1 -1
- package/lib/module/components/styled/NewMessageLabel.js +3 -1
- package/lib/module/components/styled/NewMessageLabel.js.map +1 -1
- package/lib/module/components/styled/SendInput.js +6 -6
- package/lib/module/components/styled/SendInput.js.map +1 -1
- package/lib/module/context/xmppProvider.js +7 -0
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/getDataFromXml.js +23 -1
- package/lib/module/helpers/getDataFromXml.js.map +1 -1
- package/lib/module/helpers/insertMessageWithDelimiter.js +13 -0
- package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
- package/lib/module/helpers/isUnresolvedSenderId.js +6 -1
- package/lib/module/helpers/isUnresolvedSenderId.js.map +1 -1
- package/lib/module/helpers/resolveLangSource.js +32 -0
- package/lib/module/helpers/resolveLangSource.js.map +1 -0
- package/lib/module/helpers/xmppUsername.js +35 -0
- package/lib/module/helpers/xmppUsername.js.map +1 -0
- package/lib/module/hooks/useChatWrapperInit.js +4 -2
- package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/module/i18n/strings.js +120 -0
- package/lib/module/i18n/strings.js.map +1 -1
- package/lib/module/i18n/useT.js +1 -1
- package/lib/module/networking/stanzaHandlers.js +42 -71
- package/lib/module/networking/stanzaHandlers.js.map +1 -1
- package/lib/module/roomStore/persistence.js +17 -1
- package/lib/module/roomStore/persistence.js.map +1 -1
- package/lib/module/version.js +3 -1
- package/lib/module/version.js.map +1 -1
- package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageTranslate.d.ts.map +1 -1
- package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ChatProfileModal/DeleteChatModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/NewChatModal/NewChatModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ProfileHeader/ProfileHeader.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
- package/lib/typescript/components/styled/DateLabel.d.ts.map +1 -1
- package/lib/typescript/components/styled/NewMessageLabel.d.ts.map +1 -1
- package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/getDataFromXml.d.ts.map +1 -1
- package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
- package/lib/typescript/helpers/isUnresolvedSenderId.d.ts.map +1 -1
- package/lib/typescript/helpers/resolveLangSource.d.ts +30 -0
- package/lib/typescript/helpers/resolveLangSource.d.ts.map +1 -0
- package/lib/typescript/helpers/xmppUsername.d.ts +13 -0
- package/lib/typescript/helpers/xmppUsername.d.ts.map +1 -0
- package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
- package/lib/typescript/i18n/strings.d.ts.map +1 -1
- package/lib/typescript/i18n/useT.d.ts +1 -1
- package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
- package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/lib/typescript/version.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/components/Header/HeaderRoomList.tsx +5 -1
- package/src/components/MainComponents/ChatWrapper.tsx +33 -0
- package/src/components/MainComponents/ReduxWrapper.tsx +18 -1
- package/src/components/MessageBubble/MessageTranslate.tsx +17 -12
- package/src/components/Modals/AttachSheet/AttachSheet.tsx +7 -3
- package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +11 -11
- package/src/components/Modals/ChatProfileModal/DeleteChatModal.tsx +9 -7
- package/src/components/Modals/NewChatModal/NewChatModal.tsx +7 -4
- package/src/components/Modals/ProfileHeader/ProfileHeader.tsx +11 -2
- package/src/components/RoomComponents/ChatRoomItem.tsx +47 -3
- package/src/components/styled/DateLabel.tsx +10 -3
- package/src/components/styled/NewMessageLabel.tsx +3 -1
- package/src/components/styled/SendInput.tsx +6 -8
- package/src/context/xmppProvider.tsx +7 -0
- package/src/helpers/getDataFromXml.ts +25 -1
- package/src/helpers/insertMessageWithDelimiter.ts +13 -0
- package/src/helpers/isUnresolvedSenderId.ts +9 -1
- package/src/helpers/resolveLangSource.ts +40 -0
- package/src/helpers/xmppUsername.ts +38 -0
- package/src/hooks/useChatWrapperInit.ts +6 -2
- package/src/i18n/strings.ts +120 -0
- package/src/i18n/useT.ts +1 -1
- package/src/networking/stanzaHandlers.ts +42 -67
- package/src/roomStore/persistence.ts +16 -0
- package/src/version.ts +3 -1
|
@@ -3,6 +3,7 @@ import styled from 'styled-components/native';
|
|
|
3
3
|
import { Line } from './StyledComponents';
|
|
4
4
|
import { Text } from 'react-native';
|
|
5
5
|
import { getDateLabelColor } from '../../helpers/getDateLabelColor';
|
|
6
|
+
import { useT, useUiLocale } from '../../i18n/useT';
|
|
6
7
|
|
|
7
8
|
interface DateLabelProps {
|
|
8
9
|
date: Date;
|
|
@@ -40,6 +41,10 @@ export const StyledDateText = styled.Text<{ color?: string }>`
|
|
|
40
41
|
`;
|
|
41
42
|
|
|
42
43
|
const DateLabel: React.FC<DateLabelProps> = ({ date, colors }) => {
|
|
44
|
+
const t = useT();
|
|
45
|
+
// The reader's locale, so the fallback branch formats the date the way
|
|
46
|
+
// their language writes it ("15 septembre" and not "September 15").
|
|
47
|
+
const uiLocale = useUiLocale();
|
|
43
48
|
const today = new Date();
|
|
44
49
|
const yesterday = new Date(today);
|
|
45
50
|
yesterday.setDate(today.getDate() - 1);
|
|
@@ -54,14 +59,16 @@ const DateLabel: React.FC<DateLabelProps> = ({ date, colors }) => {
|
|
|
54
59
|
|
|
55
60
|
let label: string;
|
|
56
61
|
if (sameDay(date, today)) {
|
|
57
|
-
label = '
|
|
62
|
+
label = t('date.today');
|
|
58
63
|
} else if (sameDay(date, yesterday)) {
|
|
59
|
-
label = '
|
|
64
|
+
label = t('date.yesterday');
|
|
60
65
|
} else {
|
|
61
66
|
const options: Intl.DateTimeFormatOptions = sameYear(date, today)
|
|
62
67
|
? { month: 'long', day: 'numeric' }
|
|
63
68
|
: { month: 'long', day: 'numeric', year: 'numeric' };
|
|
64
|
-
|
|
69
|
+
// `undefined` means "the runtime's own locale", which is the right
|
|
70
|
+
// default when the host has pinned nothing — never a hardcoded 'en-US'.
|
|
71
|
+
label = date.toLocaleDateString(uiLocale, options);
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
const textColor = getDateLabelColor({ colors });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import styled from 'styled-components/native';
|
|
3
3
|
import { Line } from './StyledComponents';
|
|
4
|
+
import { useT } from '../../i18n/useT';
|
|
4
5
|
|
|
5
6
|
const Container = styled.View`
|
|
6
7
|
flex-direction: row;
|
|
@@ -39,11 +40,12 @@ interface NewMessageLabelProps {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
const NewMessageLabel: React.FC<NewMessageLabelProps> = () => {
|
|
43
|
+
const t = useT();
|
|
42
44
|
return (
|
|
43
45
|
<Container>
|
|
44
46
|
<Line />
|
|
45
47
|
<StyledLabel>
|
|
46
|
-
<StyledLabelText>
|
|
48
|
+
<StyledLabelText>{t('message.newMessages')}</StyledLabelText>
|
|
47
49
|
</StyledLabel>
|
|
48
50
|
<Line />
|
|
49
51
|
</Container>
|
|
@@ -171,16 +171,16 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
171
171
|
// rolled check/request flow.
|
|
172
172
|
|
|
173
173
|
const promptOpenSettings = (message: string) => {
|
|
174
|
-
Alert.alert('
|
|
175
|
-
{ text: '
|
|
176
|
-
{ text: '
|
|
174
|
+
Alert.alert(t('permission.requiredTitle'), message, [
|
|
175
|
+
{ text: t('action.cancel'), style: 'cancel' },
|
|
176
|
+
{ text: t('action.openSettings'), onPress: () => Linking.openSettings() },
|
|
177
177
|
]);
|
|
178
178
|
};
|
|
179
179
|
|
|
180
180
|
const handleCameraSelection = async () => {
|
|
181
181
|
const perm = await ImagePicker.requestCameraPermissionsAsync();
|
|
182
182
|
if (!perm.granted) {
|
|
183
|
-
promptOpenSettings('
|
|
183
|
+
promptOpenSettings(t('permission.camera'));
|
|
184
184
|
return;
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -220,7 +220,7 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
220
220
|
const handleGallerySelection = async () => {
|
|
221
221
|
const perm = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
|
222
222
|
if (!perm.granted) {
|
|
223
|
-
promptOpenSettings('
|
|
223
|
+
promptOpenSettings(t('permission.gallery'));
|
|
224
224
|
return;
|
|
225
225
|
}
|
|
226
226
|
|
|
@@ -405,9 +405,7 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
405
405
|
try {
|
|
406
406
|
const perm = await requestRecordingPermissionsAsync();
|
|
407
407
|
if (!perm.granted) {
|
|
408
|
-
promptOpenSettings(
|
|
409
|
-
'Microphone permission is needed to record voice messages.'
|
|
410
|
-
);
|
|
408
|
+
promptOpenSettings(t('permission.microphone'));
|
|
411
409
|
return;
|
|
412
410
|
}
|
|
413
411
|
// `allowsRecording` is not just an iOS session flag: expo-audio's
|
|
@@ -330,6 +330,13 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
330
330
|
|
|
331
331
|
if (completedBootstrapKeyRef.current === key) {
|
|
332
332
|
devPushLog('rn', 'initBeforeLoad: already completed (same key) — skip');
|
|
333
|
+
// This run does no work, so it must not leave the budget it armed
|
|
334
|
+
// above running: 45 s later it would flip an already-'ready'
|
|
335
|
+
// provider to 'failed' and surface "Could not authenticate" over a
|
|
336
|
+
// healthy connection. Any dep change that leaves the key intact
|
|
337
|
+
// triggers this - e.g. a rotated `userLogin.user.token`.
|
|
338
|
+
reachedReady = true;
|
|
339
|
+
clearBudget();
|
|
333
340
|
return;
|
|
334
341
|
}
|
|
335
342
|
if (inflightBootstrapKeyRef.current === key) {
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { Element } from 'ltx';
|
|
2
2
|
import { Iso639_1Codes, IUser } from '../types/types';
|
|
3
3
|
import { transformArrayToObject } from './transformTranslatations';
|
|
4
|
+
import { normalizeXmppUsername } from './xmppUsername';
|
|
5
|
+
|
|
6
|
+
const getLocalpartFromJid = (jid?: string): string | undefined => {
|
|
7
|
+
const value = String(jid || '').trim();
|
|
8
|
+
if (!value) {return undefined;}
|
|
9
|
+
return value.split('@')[0] || undefined;
|
|
10
|
+
};
|
|
4
11
|
|
|
5
12
|
// Tolerant JSON parse: a malformed `translations` attr must not reject the
|
|
6
13
|
// whole stanza (which would silently drop the message). Mirrors the web
|
|
@@ -64,7 +71,7 @@ export const getDataFromXml = async (stanza: Element): Promise<DataXml | undefin
|
|
|
64
71
|
|
|
65
72
|
const xmppId = fullData?.attrs.id;
|
|
66
73
|
const xmppFrom = fullData?.attrs?.from;
|
|
67
|
-
const [roomJid,
|
|
74
|
+
const [roomJid, userWalletFromResource] = (xmppFrom || '').split('/');
|
|
68
75
|
let id =
|
|
69
76
|
stanza.getChild('result')?.attrs.id ||
|
|
70
77
|
extractTimestamp(stanza?.getChild('stanza-id')?.attrs?.id, stanza);
|
|
@@ -93,6 +100,23 @@ export const getDataFromXml = async (stanza: Element): Promise<DataXml | undefin
|
|
|
93
100
|
const data = fullData?.getChild('data') || stanza?.getChild('data');
|
|
94
101
|
const photoURL = data?.attrs?.photo;
|
|
95
102
|
|
|
103
|
+
// Sender identity, resolved the way the web SDK does it: the occupant
|
|
104
|
+
// RESOURCE of `from` first, then the localpart of `<data senderJID>`.
|
|
105
|
+
//
|
|
106
|
+
// RN only had the first. The translate-tagged send path arrives with no
|
|
107
|
+
// occupant resource and no senderJID either, so `user.id` came out
|
|
108
|
+
// undefined and every downstream fallback landed on the bare room jid's
|
|
109
|
+
// localpart — which for a 1:1 room is `<appId>_<userA>-<appId>_<userB>`.
|
|
110
|
+
// That composite id is what got rendered as the sender's name.
|
|
111
|
+
//
|
|
112
|
+
// normalizeXmppUsername collapses a doubled `<appId>_` prefix so the key
|
|
113
|
+
// matches what usersSet is indexed by.
|
|
114
|
+
const rawUserWallet =
|
|
115
|
+
userWalletFromResource ||
|
|
116
|
+
getLocalpartFromJid(data?.attrs?.senderJID) ||
|
|
117
|
+
undefined;
|
|
118
|
+
const userWallet = normalizeXmppUsername(rawUserWallet) || rawUserWallet;
|
|
119
|
+
|
|
96
120
|
const user = {
|
|
97
121
|
id: userWallet,
|
|
98
122
|
photoURL,
|
|
@@ -33,6 +33,19 @@ function resolveMarkerMs(
|
|
|
33
33
|
|
|
34
34
|
function deepMerge(target: any, source: any): any {
|
|
35
35
|
for (const key in source) {
|
|
36
|
+
if (source[key] === undefined) {
|
|
37
|
+
// `for...in` visits a key even when its value is `undefined` — every
|
|
38
|
+
// stanza parser (getDataFromXml/createMessageFromXml) returns an
|
|
39
|
+
// object literal with `translations` always present, explicitly
|
|
40
|
+
// `undefined` whenever the wire message didn't carry a
|
|
41
|
+
// <translations> element. Without this guard, merging THAT message
|
|
42
|
+
// over an existing row would blow away a translation the row
|
|
43
|
+
// already had (e.g. a MAM/live echo of a message arriving without
|
|
44
|
+
// its translation re-attached) — a real message flips from
|
|
45
|
+
// translated back to original text for no reason. Absence on the
|
|
46
|
+
// incoming side must mean "leave it alone", never "clear it".
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
36
49
|
if (
|
|
37
50
|
source[key] &&
|
|
38
51
|
typeof source[key] === 'object' &&
|
|
@@ -14,7 +14,15 @@
|
|
|
14
14
|
* matches. A raw objectId on its own is matched too, since some senders
|
|
15
15
|
* carry only that half.
|
|
16
16
|
*/
|
|
17
|
-
const
|
|
17
|
+
const ETHORA_ID_PART = '[0-9a-f]{24}(?:_[0-9a-f]{24})?';
|
|
18
|
+
// A private (1:1) room's own id is two of those joined by a hyphen —
|
|
19
|
+
// `<appId>_<userA>-<appId>_<userB>` — and that is what a message whose
|
|
20
|
+
// `from` carries no occupant resource resolves to. Matched as well, so a
|
|
21
|
+
// bubble is never captioned with a room id.
|
|
22
|
+
const ETHORA_ID = new RegExp(
|
|
23
|
+
`^${ETHORA_ID_PART}(?:-${ETHORA_ID_PART})?$`,
|
|
24
|
+
'i'
|
|
25
|
+
);
|
|
18
26
|
|
|
19
27
|
export const isUnresolvedSenderId = (name?: string): boolean => {
|
|
20
28
|
const trimmed = String(name || '').trim();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { IConfig, Iso639_1Codes } from '../types/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Legacy single-locale config (`translates.translations`) seeds langSource
|
|
5
|
+
* only when the host ACTUALLY set it.
|
|
6
|
+
*
|
|
7
|
+
* The condition used to be inverted (`!translates.translations`), so merely
|
|
8
|
+
* enabling translation fired `setLangSource(undefined)` on every XMPP init
|
|
9
|
+
* and wiped whatever the reader had picked from the globe — the language
|
|
10
|
+
* reset itself on every reconnect. Web fixed this; RN had not.
|
|
11
|
+
*/
|
|
12
|
+
export const resolveLegacyTranslatesLangSource = (
|
|
13
|
+
translatesConfig?: IConfig['translates']
|
|
14
|
+
): Iso639_1Codes | undefined =>
|
|
15
|
+
translatesConfig?.enabled && translatesConfig?.translations
|
|
16
|
+
? translatesConfig.translations
|
|
17
|
+
: undefined;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A host drives the reader's language from OUTSIDE the component by setting
|
|
21
|
+
* `config.translates.readerLocale` (their own language switcher, or the
|
|
22
|
+
* testbed's Setup tab). Nothing was syncing it into `langSource`, so pinning
|
|
23
|
+
* it only redirected the incoming-translation lookup: the UI captions and
|
|
24
|
+
* the `<translate source>` on outgoing messages both kept following the
|
|
25
|
+
* unset `langSource`.
|
|
26
|
+
*
|
|
27
|
+
* Unlike web this keeps the FULL locale rather than the base language. RN's
|
|
28
|
+
* LANGUAGE_OPTIONS are region-tagged (`es-US`, `fr-CA`) because the region
|
|
29
|
+
* is forwarded to the translation service, and the in-chat picker stores
|
|
30
|
+
* that full form — so the external path has to store the same thing or the
|
|
31
|
+
* two would disagree. Caption lookups strip to the base themselves.
|
|
32
|
+
*
|
|
33
|
+
* Resolves only when actually set, so a host that leaves it unset never
|
|
34
|
+
* clobbers what the reader chose for themselves.
|
|
35
|
+
*/
|
|
36
|
+
export const resolveExternalReaderLocaleLangSource = (
|
|
37
|
+
readerLocale?: string
|
|
38
|
+
): Iso639_1Codes | undefined =>
|
|
39
|
+
(readerLocale || undefined) as Iso639_1Codes | undefined;
|
|
40
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { store } from '../roomStore';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Collapse a duplicated `<appId>_` prefix down to one.
|
|
5
|
+
*
|
|
6
|
+
* Ported from the web SDK (src/helpers/xmppUsername.ts). Some senders stamp
|
|
7
|
+
* an already-qualified username and it gets qualified a second time, which
|
|
8
|
+
* yields `<appId>_<appId>_<userId>` — a key `usersSet` will never match.
|
|
9
|
+
*
|
|
10
|
+
* RN reads the appId off the logged-in user's own xmppUsername
|
|
11
|
+
* (`<appId>_<userId>`) since the chat store has no standalone `appId` field
|
|
12
|
+
* the way web's does.
|
|
13
|
+
*/
|
|
14
|
+
export const normalizeXmppUsername = (
|
|
15
|
+
value: string | undefined | null
|
|
16
|
+
): string => {
|
|
17
|
+
if (!value) {
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
let result = String(value).trim();
|
|
21
|
+
if (!result) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ownXmpp = String(
|
|
26
|
+
store.getState().chatSettingStore?.user?.xmppUsername || ''
|
|
27
|
+
);
|
|
28
|
+
const appId = ownXmpp.includes('_') ? ownXmpp.split('_')[0] : '';
|
|
29
|
+
if (!appId) {
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const prefix = `${appId}_`;
|
|
34
|
+
while (result.startsWith(prefix + prefix)) {
|
|
35
|
+
result = result.slice(prefix.length);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useEffect, useRef, useState } from 'react';
|
|
4
4
|
import { useDispatch, useSelector } from 'react-redux';
|
|
5
5
|
import { IConfig } from '../types/types';
|
|
6
|
+
import { resolveLegacyTranslatesLangSource } from '../helpers/resolveLangSource';
|
|
6
7
|
import XmppClient from '../networking/xmppClient';
|
|
7
8
|
import { AppDispatch, RootState } from '../roomStore';
|
|
8
9
|
import { useXmppClient } from '../context/xmppProvider';
|
|
@@ -111,8 +112,11 @@ const useChatWrapperInit = ({
|
|
|
111
112
|
let retryTimeout: NodeJS.Timeout;
|
|
112
113
|
|
|
113
114
|
const initXmmpClient = async () => {
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
const legacyLangSource = resolveLegacyTranslatesLangSource(
|
|
116
|
+
config?.translates
|
|
117
|
+
);
|
|
118
|
+
if (legacyLangSource) {
|
|
119
|
+
dispatch(setLangSource(legacyLangSource));
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
dispatch(setConfig(config));
|
package/src/i18n/strings.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type UiStringTable = Record<string, string>;
|
|
|
15
15
|
|
|
16
16
|
const en: UiStringTable = {
|
|
17
17
|
'search.placeholder': 'Search...',
|
|
18
|
+
'roomList.title': 'Chats',
|
|
18
19
|
'search.members': 'Search members',
|
|
19
20
|
'search.noResults': 'Nobody found',
|
|
20
21
|
'input.placeholder': 'Type message',
|
|
@@ -24,6 +25,8 @@ const en: UiStringTable = {
|
|
|
24
25
|
'date.yesterday': 'Yesterday',
|
|
25
26
|
'message.deleted': 'Message deleted',
|
|
26
27
|
'message.edited': 'edited',
|
|
28
|
+
'message.newMessages': 'New messages',
|
|
29
|
+
'notification.senderFallback': 'New message',
|
|
27
30
|
'presence.online': 'online',
|
|
28
31
|
'presence.offline': 'offline',
|
|
29
32
|
'presence.onlineCount': '{count} online',
|
|
@@ -95,6 +98,17 @@ const en: UiStringTable = {
|
|
|
95
98
|
'action.edit': 'Edit',
|
|
96
99
|
'action.deleteAndLeave': 'Delete and Leave',
|
|
97
100
|
'status.banned': 'banned',
|
|
101
|
+
'action.unban': 'Unban',
|
|
102
|
+
'action.openSettings': 'Open Settings',
|
|
103
|
+
'attach.takePhoto': 'Take photo',
|
|
104
|
+
'attach.photosAndVideos': 'Photos & Videos',
|
|
105
|
+
'attach.viewLibrary': 'View Library',
|
|
106
|
+
'attach.uploadFile': 'Upload a File',
|
|
107
|
+
'permission.requiredTitle': 'Permission required',
|
|
108
|
+
'permission.photoLibrary': 'Photo library permission is needed to select images.',
|
|
109
|
+
'permission.camera': 'Camera permission is needed to take photos and videos.',
|
|
110
|
+
'permission.gallery': 'Gallery permission is needed to select photos and videos.',
|
|
111
|
+
'permission.microphone': 'Microphone permission is needed to record voice messages.',
|
|
98
112
|
'status.connecting': 'Connecting…',
|
|
99
113
|
'status.noInternet': 'No internet connection',
|
|
100
114
|
'translation.translating': 'Translating…',
|
|
@@ -117,8 +131,13 @@ const en: UiStringTable = {
|
|
|
117
131
|
'toast.roomCreationTitle': 'Room creation',
|
|
118
132
|
'toast.roomCreating': 'Room is being created...',
|
|
119
133
|
'toast.success': 'Success!',
|
|
134
|
+
'toast.successTitle': 'Success',
|
|
120
135
|
'toast.roomCreatedSuccess': 'Room created successfully!',
|
|
121
136
|
'toast.usersAddedSuccess': 'Users added successfully!',
|
|
137
|
+
'toast.roomImageUpdated': 'Room image updated successfully',
|
|
138
|
+
'toast.failedToUploadImage': 'Failed to upload image',
|
|
139
|
+
'toast.chatDeletedSuccess': 'Chat deleted successfully',
|
|
140
|
+
'toast.failedToDeleteChat': 'Failed to delete chat',
|
|
122
141
|
'settings.menu.title': 'Settings',
|
|
123
142
|
'modal.filePreview.title': 'File preview',
|
|
124
143
|
'toast.error': 'Error',
|
|
@@ -186,6 +205,7 @@ const en: UiStringTable = {
|
|
|
186
205
|
'modal.chatProfile.membersLabel': 'Members: {count}',
|
|
187
206
|
'modal.chatProfile.membersUnavailable': 'Member list unavailable',
|
|
188
207
|
'modal.chatProfile.removeMember': 'Remove from this chat?',
|
|
208
|
+
'modal.chatProfile.memberListUnavailable': 'Member list unavailable',
|
|
189
209
|
'modal.deleteChat.title': 'Delete this chat ?',
|
|
190
210
|
'modal.addMembers.title': 'Add New Member',
|
|
191
211
|
'modal.addMembers.userIdPlaceholder': 'Enter User Id',
|
|
@@ -243,6 +263,7 @@ const en: UiStringTable = {
|
|
|
243
263
|
|
|
244
264
|
const fr: UiStringTable = {
|
|
245
265
|
'search.placeholder': 'Rechercher...',
|
|
266
|
+
'roomList.title': 'Discussions',
|
|
246
267
|
'search.members': 'Rechercher des membres',
|
|
247
268
|
'search.noResults': 'Personne trouvée',
|
|
248
269
|
'input.placeholder': 'Écrire un message',
|
|
@@ -252,6 +273,8 @@ const fr: UiStringTable = {
|
|
|
252
273
|
'date.yesterday': 'Hier',
|
|
253
274
|
'message.deleted': 'Message supprimé',
|
|
254
275
|
'message.edited': 'modifié',
|
|
276
|
+
'message.newMessages': 'Nouveaux messages',
|
|
277
|
+
'notification.senderFallback': 'Nouveau message',
|
|
255
278
|
'presence.online': 'en ligne',
|
|
256
279
|
'presence.offline': 'hors ligne',
|
|
257
280
|
'presence.onlineCount': '{count} en ligne',
|
|
@@ -323,6 +346,17 @@ const fr: UiStringTable = {
|
|
|
323
346
|
'action.edit': 'Modifier',
|
|
324
347
|
'action.deleteAndLeave': 'Supprimer et quitter',
|
|
325
348
|
'status.banned': 'banni',
|
|
349
|
+
'action.unban': 'Débannir',
|
|
350
|
+
'action.openSettings': 'Ouvrir les réglages',
|
|
351
|
+
'attach.takePhoto': 'Prendre une photo',
|
|
352
|
+
'attach.photosAndVideos': 'Photos et vidéos',
|
|
353
|
+
'attach.viewLibrary': 'Voir la bibliothèque',
|
|
354
|
+
'attach.uploadFile': 'Téléverser un fichier',
|
|
355
|
+
'permission.requiredTitle': 'Autorisation requise',
|
|
356
|
+
'permission.photoLibrary': "L'autorisation d'accès à la photothèque est nécessaire pour sélectionner des images.",
|
|
357
|
+
'permission.camera': "L'autorisation d'accès à la caméra est nécessaire pour prendre des photos et des vidéos.",
|
|
358
|
+
'permission.gallery': "L'autorisation d'accès à la galerie est nécessaire pour sélectionner des photos et des vidéos.",
|
|
359
|
+
'permission.microphone': "L'autorisation d'accès au microphone est nécessaire pour enregistrer des messages vocaux.",
|
|
326
360
|
'status.connecting': 'Connexion…',
|
|
327
361
|
'status.noInternet': 'Pas de connexion internet',
|
|
328
362
|
'translation.translating': 'Traduction…',
|
|
@@ -345,8 +379,13 @@ const fr: UiStringTable = {
|
|
|
345
379
|
'toast.roomCreationTitle': 'Création du salon',
|
|
346
380
|
'toast.roomCreating': 'Le salon est en cours de création...',
|
|
347
381
|
'toast.success': 'Succès !',
|
|
382
|
+
'toast.successTitle': 'Succès',
|
|
348
383
|
'toast.roomCreatedSuccess': 'Salon créé avec succès !',
|
|
349
384
|
'toast.usersAddedSuccess': 'Utilisateurs ajoutés avec succès !',
|
|
385
|
+
'toast.roomImageUpdated': 'Image du salon mise à jour avec succès',
|
|
386
|
+
'toast.failedToUploadImage': "Échec du téléchargement de l'image",
|
|
387
|
+
'toast.chatDeletedSuccess': 'Discussion supprimée avec succès',
|
|
388
|
+
'toast.failedToDeleteChat': 'Échec de la suppression de la discussion',
|
|
350
389
|
'settings.menu.title': 'Paramètres',
|
|
351
390
|
'modal.filePreview.title': 'Aperçu du fichier',
|
|
352
391
|
'toast.error': 'Erreur',
|
|
@@ -414,6 +453,7 @@ const fr: UiStringTable = {
|
|
|
414
453
|
'modal.chatProfile.membersLabel': 'Membres : {count}',
|
|
415
454
|
'modal.chatProfile.membersUnavailable': 'Liste des membres indisponible',
|
|
416
455
|
'modal.chatProfile.removeMember': 'Retirer de cette discussion ?',
|
|
456
|
+
'modal.chatProfile.memberListUnavailable': 'Liste des membres indisponible',
|
|
417
457
|
'modal.deleteChat.title': 'Supprimer cette discussion ?',
|
|
418
458
|
'modal.addMembers.title': 'Ajouter un nouveau membre',
|
|
419
459
|
'modal.addMembers.userIdPlaceholder': "Entrez l'ID utilisateur",
|
|
@@ -471,6 +511,7 @@ const fr: UiStringTable = {
|
|
|
471
511
|
|
|
472
512
|
const es: UiStringTable = {
|
|
473
513
|
'search.placeholder': 'Buscar...',
|
|
514
|
+
'roomList.title': 'Chats',
|
|
474
515
|
'search.members': 'Buscar miembros',
|
|
475
516
|
'search.noResults': 'No se encontró a nadie',
|
|
476
517
|
'input.placeholder': 'Escribe un mensaje',
|
|
@@ -480,6 +521,8 @@ const es: UiStringTable = {
|
|
|
480
521
|
'date.yesterday': 'Ayer',
|
|
481
522
|
'message.deleted': 'Mensaje eliminado',
|
|
482
523
|
'message.edited': 'editado',
|
|
524
|
+
'message.newMessages': 'Nuevos mensajes',
|
|
525
|
+
'notification.senderFallback': 'Nuevo mensaje',
|
|
483
526
|
'presence.online': 'en línea',
|
|
484
527
|
'presence.offline': 'desconectado',
|
|
485
528
|
'presence.onlineCount': '{count} en línea',
|
|
@@ -551,6 +594,17 @@ const es: UiStringTable = {
|
|
|
551
594
|
'action.edit': 'Editar',
|
|
552
595
|
'action.deleteAndLeave': 'Eliminar y salir',
|
|
553
596
|
'status.banned': 'bloqueado',
|
|
597
|
+
'action.unban': 'Desbanear',
|
|
598
|
+
'action.openSettings': 'Abrir configuración',
|
|
599
|
+
'attach.takePhoto': 'Tomar foto',
|
|
600
|
+
'attach.photosAndVideos': 'Fotos y videos',
|
|
601
|
+
'attach.viewLibrary': 'Ver galería',
|
|
602
|
+
'attach.uploadFile': 'Subir un archivo',
|
|
603
|
+
'permission.requiredTitle': 'Permiso requerido',
|
|
604
|
+
'permission.photoLibrary': 'Se necesita permiso de acceso a la galería de fotos para seleccionar imágenes.',
|
|
605
|
+
'permission.camera': 'Se necesita permiso de cámara para tomar fotos y videos.',
|
|
606
|
+
'permission.gallery': 'Se necesita permiso de galería para seleccionar fotos y videos.',
|
|
607
|
+
'permission.microphone': 'Se necesita permiso de micrófono para grabar mensajes de voz.',
|
|
554
608
|
'status.connecting': 'Conectando…',
|
|
555
609
|
'status.noInternet': 'Sin conexión a internet',
|
|
556
610
|
'translation.translating': 'Traduciendo…',
|
|
@@ -573,8 +627,13 @@ const es: UiStringTable = {
|
|
|
573
627
|
'toast.roomCreationTitle': 'Creación de sala',
|
|
574
628
|
'toast.roomCreating': 'La sala se está creando...',
|
|
575
629
|
'toast.success': '¡Éxito!',
|
|
630
|
+
'toast.successTitle': 'Éxito',
|
|
576
631
|
'toast.roomCreatedSuccess': '¡Sala creada correctamente!',
|
|
577
632
|
'toast.usersAddedSuccess': '¡Usuarios añadidos correctamente!',
|
|
633
|
+
'toast.roomImageUpdated': 'Imagen de la sala actualizada correctamente',
|
|
634
|
+
'toast.failedToUploadImage': 'No se pudo subir la imagen',
|
|
635
|
+
'toast.chatDeletedSuccess': 'Chat eliminado correctamente',
|
|
636
|
+
'toast.failedToDeleteChat': 'No se pudo eliminar el chat',
|
|
578
637
|
'settings.menu.title': 'Configuración',
|
|
579
638
|
'modal.filePreview.title': 'Vista previa del archivo',
|
|
580
639
|
'toast.error': 'Error',
|
|
@@ -642,6 +701,7 @@ const es: UiStringTable = {
|
|
|
642
701
|
'modal.chatProfile.membersLabel': 'Miembros: {count}',
|
|
643
702
|
'modal.chatProfile.membersUnavailable': 'Lista de miembros no disponible',
|
|
644
703
|
'modal.chatProfile.removeMember': '¿Quitar de este chat?',
|
|
704
|
+
'modal.chatProfile.memberListUnavailable': 'Lista de miembros no disponible',
|
|
645
705
|
'modal.deleteChat.title': '¿Eliminar este chat?',
|
|
646
706
|
'modal.addMembers.title': 'Añadir nuevo miembro',
|
|
647
707
|
'modal.addMembers.userIdPlaceholder': 'Introduce el ID de usuario',
|
|
@@ -699,6 +759,7 @@ const es: UiStringTable = {
|
|
|
699
759
|
|
|
700
760
|
const pt: UiStringTable = {
|
|
701
761
|
'search.placeholder': 'Pesquisar...',
|
|
762
|
+
'roomList.title': 'Conversas',
|
|
702
763
|
'search.members': 'Pesquisar membros',
|
|
703
764
|
'search.noResults': 'Ninguém encontrado',
|
|
704
765
|
'input.placeholder': 'Escreva uma mensagem',
|
|
@@ -708,6 +769,8 @@ const pt: UiStringTable = {
|
|
|
708
769
|
'date.yesterday': 'Ontem',
|
|
709
770
|
'message.deleted': 'Mensagem eliminada',
|
|
710
771
|
'message.edited': 'editado',
|
|
772
|
+
'message.newMessages': 'Novas mensagens',
|
|
773
|
+
'notification.senderFallback': 'Nova mensagem',
|
|
711
774
|
'presence.online': 'online',
|
|
712
775
|
'presence.offline': 'offline',
|
|
713
776
|
'presence.onlineCount': '{count} online',
|
|
@@ -779,6 +842,17 @@ const pt: UiStringTable = {
|
|
|
779
842
|
'action.edit': 'Editar',
|
|
780
843
|
'action.deleteAndLeave': 'Eliminar e sair',
|
|
781
844
|
'status.banned': 'banido',
|
|
845
|
+
'action.unban': 'Desbanir',
|
|
846
|
+
'action.openSettings': 'Abrir definições',
|
|
847
|
+
'attach.takePhoto': 'Tirar foto',
|
|
848
|
+
'attach.photosAndVideos': 'Fotos e vídeos',
|
|
849
|
+
'attach.viewLibrary': 'Ver biblioteca',
|
|
850
|
+
'attach.uploadFile': 'Carregar um ficheiro',
|
|
851
|
+
'permission.requiredTitle': 'Permissão necessária',
|
|
852
|
+
'permission.photoLibrary': 'É necessária permissão de acesso à biblioteca de fotos para selecionar imagens.',
|
|
853
|
+
'permission.camera': 'É necessária permissão da câmara para tirar fotos e vídeos.',
|
|
854
|
+
'permission.gallery': 'É necessária permissão da galeria para selecionar fotos e vídeos.',
|
|
855
|
+
'permission.microphone': 'É necessária permissão do microfone para gravar mensagens de voz.',
|
|
782
856
|
'status.connecting': 'A ligar…',
|
|
783
857
|
'status.noInternet': 'Sem ligação à internet',
|
|
784
858
|
'translation.translating': 'A traduzir…',
|
|
@@ -801,8 +875,13 @@ const pt: UiStringTable = {
|
|
|
801
875
|
'toast.roomCreationTitle': 'Criação de sala',
|
|
802
876
|
'toast.roomCreating': 'A sala está a ser criada...',
|
|
803
877
|
'toast.success': 'Sucesso!',
|
|
878
|
+
'toast.successTitle': 'Sucesso',
|
|
804
879
|
'toast.roomCreatedSuccess': 'Sala criada com sucesso!',
|
|
805
880
|
'toast.usersAddedSuccess': 'Utilizadores adicionados com sucesso!',
|
|
881
|
+
'toast.roomImageUpdated': 'Imagem da sala atualizada com sucesso',
|
|
882
|
+
'toast.failedToUploadImage': 'Falha ao carregar a imagem',
|
|
883
|
+
'toast.chatDeletedSuccess': 'Chat eliminado com sucesso',
|
|
884
|
+
'toast.failedToDeleteChat': 'Falha ao eliminar o chat',
|
|
806
885
|
'settings.menu.title': 'Definições',
|
|
807
886
|
'modal.filePreview.title': 'Pré-visualização do ficheiro',
|
|
808
887
|
'toast.error': 'Erro',
|
|
@@ -870,6 +949,7 @@ const pt: UiStringTable = {
|
|
|
870
949
|
'modal.chatProfile.membersLabel': 'Membros: {count}',
|
|
871
950
|
'modal.chatProfile.membersUnavailable': 'Lista de membros indisponível',
|
|
872
951
|
'modal.chatProfile.removeMember': 'Remover deste chat?',
|
|
952
|
+
'modal.chatProfile.memberListUnavailable': 'Lista de membros indisponível',
|
|
873
953
|
'modal.deleteChat.title': 'Eliminar este chat?',
|
|
874
954
|
'modal.addMembers.title': 'Adicionar novo membro',
|
|
875
955
|
'modal.addMembers.userIdPlaceholder': 'Introduza o ID do utilizador',
|
|
@@ -927,6 +1007,7 @@ const pt: UiStringTable = {
|
|
|
927
1007
|
|
|
928
1008
|
const ht: UiStringTable = {
|
|
929
1009
|
'search.placeholder': 'Chèche...',
|
|
1010
|
+
'roomList.title': 'Chat yo',
|
|
930
1011
|
'search.members': 'Chèche manm',
|
|
931
1012
|
'search.noResults': 'Pa jwenn pèsonn',
|
|
932
1013
|
'input.placeholder': 'Ekri yon mesaj',
|
|
@@ -936,6 +1017,8 @@ const ht: UiStringTable = {
|
|
|
936
1017
|
'date.yesterday': 'Yè',
|
|
937
1018
|
'message.deleted': 'Mesaj efase',
|
|
938
1019
|
'message.edited': 'modifye',
|
|
1020
|
+
'message.newMessages': 'Nouvo mesaj yo',
|
|
1021
|
+
'notification.senderFallback': 'Nouvo mesaj',
|
|
939
1022
|
'presence.online': 'an liy',
|
|
940
1023
|
'presence.offline': 'pa an liy',
|
|
941
1024
|
'presence.onlineCount': '{count} an liy',
|
|
@@ -1007,6 +1090,17 @@ const ht: UiStringTable = {
|
|
|
1007
1090
|
'action.edit': 'Modifye',
|
|
1008
1091
|
'action.deleteAndLeave': 'Efase epi kite',
|
|
1009
1092
|
'status.banned': 'bloke',
|
|
1093
|
+
'action.unban': 'Retire Ban',
|
|
1094
|
+
'action.openSettings': 'Louvri Paramèt yo',
|
|
1095
|
+
'attach.takePhoto': 'Pran yon foto',
|
|
1096
|
+
'attach.photosAndVideos': 'Foto ak videyo',
|
|
1097
|
+
'attach.viewLibrary': 'Wè bibliyotèk la',
|
|
1098
|
+
'attach.uploadFile': 'Telechaje yon fichye',
|
|
1099
|
+
'permission.requiredTitle': 'Otorizasyon nesesè',
|
|
1100
|
+
'permission.photoLibrary': 'Nou bezwen otorizasyon pou aksè nan bibliyotèk foto ou pou chwazi imaj.',
|
|
1101
|
+
'permission.camera': 'Nou bezwen otorizasyon kamera pou pran foto ak videyo.',
|
|
1102
|
+
'permission.gallery': 'Nou bezwen otorizasyon galri pou chwazi foto ak videyo.',
|
|
1103
|
+
'permission.microphone': 'Nou bezwen otorizasyon mikwo pou anrejistre mesaj vokal.',
|
|
1010
1104
|
'status.connecting': 'Koneksyon…',
|
|
1011
1105
|
'status.noInternet': 'Pa gen koneksyon entènèt',
|
|
1012
1106
|
'translation.translating': 'Ap tradui…',
|
|
@@ -1029,8 +1123,13 @@ const ht: UiStringTable = {
|
|
|
1029
1123
|
'toast.roomCreationTitle': 'Kreyasyon sal',
|
|
1030
1124
|
'toast.roomCreating': 'Sal la ap kreye...',
|
|
1031
1125
|
'toast.success': 'Siksè!',
|
|
1126
|
+
'toast.successTitle': 'Siksè',
|
|
1032
1127
|
'toast.roomCreatedSuccess': 'Sal la kreye avèk siksè!',
|
|
1033
1128
|
'toast.usersAddedSuccess': 'Itilizatè yo ajoute avèk siksè!',
|
|
1129
|
+
'toast.roomImageUpdated': 'Imaj sal la mete ajou avèk siksè',
|
|
1130
|
+
'toast.failedToUploadImage': 'Echèk pou telechaje imaj la',
|
|
1131
|
+
'toast.chatDeletedSuccess': 'Chat la efase avèk siksè',
|
|
1132
|
+
'toast.failedToDeleteChat': 'Echèk pou efase chat la',
|
|
1034
1133
|
'settings.menu.title': 'Paramèt',
|
|
1035
1134
|
'modal.filePreview.title': 'Apèsi fichye',
|
|
1036
1135
|
'toast.error': 'Erè',
|
|
@@ -1098,6 +1197,7 @@ const ht: UiStringTable = {
|
|
|
1098
1197
|
'modal.chatProfile.membersLabel': 'Manm: {count}',
|
|
1099
1198
|
'modal.chatProfile.membersUnavailable': 'Lis manm yo pa disponib',
|
|
1100
1199
|
'modal.chatProfile.removeMember': 'Retire nan chat sa a?',
|
|
1200
|
+
'modal.chatProfile.memberListUnavailable': 'Lis manm yo pa disponib',
|
|
1101
1201
|
'modal.deleteChat.title': 'Efase chat sa a?',
|
|
1102
1202
|
'modal.addMembers.title': 'Ajoute yon nouvo manm',
|
|
1103
1203
|
'modal.addMembers.userIdPlaceholder': 'Antre ID itilizatè a',
|
|
@@ -1155,6 +1255,7 @@ const ht: UiStringTable = {
|
|
|
1155
1255
|
|
|
1156
1256
|
const zh: UiStringTable = {
|
|
1157
1257
|
'search.placeholder': '搜索...',
|
|
1258
|
+
'roomList.title': '聊天',
|
|
1158
1259
|
'search.members': '搜索成员',
|
|
1159
1260
|
'search.noResults': '未找到成员',
|
|
1160
1261
|
'input.placeholder': '输入消息',
|
|
@@ -1164,6 +1265,8 @@ const zh: UiStringTable = {
|
|
|
1164
1265
|
'date.yesterday': '昨天',
|
|
1165
1266
|
'message.deleted': '消息已删除',
|
|
1166
1267
|
'message.edited': '已编辑',
|
|
1268
|
+
'message.newMessages': '新消息',
|
|
1269
|
+
'notification.senderFallback': '新消息',
|
|
1167
1270
|
'presence.online': '在线',
|
|
1168
1271
|
'presence.offline': '离线',
|
|
1169
1272
|
'presence.onlineCount': '{count} 人在线',
|
|
@@ -1235,6 +1338,17 @@ const zh: UiStringTable = {
|
|
|
1235
1338
|
'action.edit': '编辑',
|
|
1236
1339
|
'action.deleteAndLeave': '删除并退出',
|
|
1237
1340
|
'status.banned': '已封禁',
|
|
1341
|
+
'action.unban': '解除封禁',
|
|
1342
|
+
'action.openSettings': '打开设置',
|
|
1343
|
+
'attach.takePhoto': '拍照',
|
|
1344
|
+
'attach.photosAndVideos': '照片和视频',
|
|
1345
|
+
'attach.viewLibrary': '查看相册',
|
|
1346
|
+
'attach.uploadFile': '上传文件',
|
|
1347
|
+
'permission.requiredTitle': '需要权限',
|
|
1348
|
+
'permission.photoLibrary': '需要照片图库权限才能选择图片。',
|
|
1349
|
+
'permission.camera': '需要相机权限才能拍摄照片和视频。',
|
|
1350
|
+
'permission.gallery': '需要相册权限才能选择照片和视频。',
|
|
1351
|
+
'permission.microphone': '需要麦克风权限才能录制语音消息。',
|
|
1238
1352
|
'status.connecting': '连接中…',
|
|
1239
1353
|
'status.noInternet': '无网络连接',
|
|
1240
1354
|
'translation.translating': '翻译中…',
|
|
@@ -1257,8 +1371,13 @@ const zh: UiStringTable = {
|
|
|
1257
1371
|
'toast.roomCreationTitle': '创建聊天室',
|
|
1258
1372
|
'toast.roomCreating': '正在创建聊天室...',
|
|
1259
1373
|
'toast.success': '成功!',
|
|
1374
|
+
'toast.successTitle': '成功',
|
|
1260
1375
|
'toast.roomCreatedSuccess': '聊天室创建成功!',
|
|
1261
1376
|
'toast.usersAddedSuccess': '用户添加成功!',
|
|
1377
|
+
'toast.roomImageUpdated': '聊天室图片更新成功',
|
|
1378
|
+
'toast.failedToUploadImage': '图片上传失败',
|
|
1379
|
+
'toast.chatDeletedSuccess': '聊天已成功删除',
|
|
1380
|
+
'toast.failedToDeleteChat': '删除聊天失败',
|
|
1262
1381
|
'settings.menu.title': '设置',
|
|
1263
1382
|
'modal.filePreview.title': '文件预览',
|
|
1264
1383
|
'toast.error': '错误',
|
|
@@ -1326,6 +1445,7 @@ const zh: UiStringTable = {
|
|
|
1326
1445
|
'modal.chatProfile.membersLabel': '成员:{count}',
|
|
1327
1446
|
'modal.chatProfile.membersUnavailable': '成员列表不可用',
|
|
1328
1447
|
'modal.chatProfile.removeMember': '将其移出该聊天?',
|
|
1448
|
+
'modal.chatProfile.memberListUnavailable': '成员列表不可用',
|
|
1329
1449
|
'modal.deleteChat.title': '删除此聊天?',
|
|
1330
1450
|
'modal.addMembers.title': '添加新成员',
|
|
1331
1451
|
'modal.addMembers.userIdPlaceholder': '输入用户 ID',
|
package/src/i18n/useT.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { interpolate, resolveStringTable } from './strings';
|
|
|
17
17
|
* When the host doesn't set `config.i18n.locale` explicitly, this falls
|
|
18
18
|
* back to `langSource` - the same reader-language state the in-chat globe
|
|
19
19
|
* picker (LanguageSelectorButton) and `config.translates.readerLocale`
|
|
20
|
-
* drive (see
|
|
20
|
+
* drive (see ChatWrapper's resolveExternalReaderLocaleLangSource effect).
|
|
21
21
|
* So picking a language in the chat changes both message translations AND
|
|
22
22
|
* static captions together, as one language switch, unless the host is
|
|
23
23
|
* explicitly managing UI locale separately via `config.i18n.locale`.
|