Prepare for ICU migration

This commit is contained in:
Jamie Kyle 2023-03-27 16:37:39 -07:00 committed by GitHub
parent d0f17a1398
commit 2781e621ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
62 changed files with 550 additions and 414 deletions

View file

@ -3093,7 +3093,7 @@
}, },
"Reactions--remove": { "Reactions--remove": {
"message": "Remove reaction", "message": "Remove reaction",
"describe": "Shown when you want to remove a reaction you've made" "description": "Shown when you want to remove a reaction you've made"
}, },
"Reactions--error": { "Reactions--error": {
"message": "Failed to send reaction. Please try again.", "message": "Failed to send reaction. Please try again.",
@ -3152,7 +3152,7 @@
"description": "Shown in a toast when you successfully block a user and report them as spam" "description": "Shown in a toast when you successfully block a user and report them as spam"
}, },
"MessageRequests--block-direct-confirm-title": { "MessageRequests--block-direct-confirm-title": {
"message": "Block $name$?", "message": "Block $title$?",
"description": "Shown as the title in the confirmation modal for blocking a private message request" "description": "Shown as the title in the confirmation modal for blocking a private message request"
}, },
"MessageRequests--block-direct-confirm-body": { "MessageRequests--block-direct-confirm-body": {
@ -3160,7 +3160,7 @@
"description": "Shown as the body in the confirmation modal for blocking a private message request" "description": "Shown as the body in the confirmation modal for blocking a private message request"
}, },
"MessageRequests--block-group-confirm-title": { "MessageRequests--block-group-confirm-title": {
"message": "Block and Leave $group$?", "message": "Block and Leave $title$?",
"description": "Shown as the title in the confirmation modal for blocking a group message request" "description": "Shown as the title in the confirmation modal for blocking a group message request"
}, },
"MessageRequests--block-group-confirm-body": { "MessageRequests--block-group-confirm-body": {
@ -3180,7 +3180,7 @@
"description": "Shown as the body in the confirmation modal for deleting a private message request" "description": "Shown as the body in the confirmation modal for deleting a private message request"
}, },
"MessageRequests--delete-group-confirm-title": { "MessageRequests--delete-group-confirm-title": {
"message": "Delete and Leave $group$?", "message": "Delete and Leave $title$?",
"description": "Shown as the title in the confirmation modal for deleting a group message request" "description": "Shown as the title in the confirmation modal for deleting a group message request"
}, },
"MessageRequests--delete-direct": { "MessageRequests--delete-direct": {
@ -6021,11 +6021,11 @@
}, },
"icu:GroupStorySettingsModal__remove_group": { "icu:GroupStorySettingsModal__remove_group": {
"messageformat": "Remove group story", "messageformat": "Remove group story",
"descirption": "Stories settings > Group Story > button to remove group story" "description": "Stories settings > Group Story > button to remove group story"
}, },
"icu:StoriesSettings__remove_group--confirm": { "icu:StoriesSettings__remove_group--confirm": {
"messageformat": "Are you sure you want to remove “{groupTitle}”?", "messageformat": "Are you sure you want to remove “{groupTitle}”?",
"descirption": "Stories settings > Group Story > confirm to remove group story" "description": "Stories settings > Group Story > confirm to remove group story"
}, },
"SendStoryModal__choose-who-can-view": { "SendStoryModal__choose-who-can-view": {
"message": "Choose who can view your story", "message": "Choose who can view your story",
@ -6279,10 +6279,6 @@
"message": "Cant download story. You will need to share it again.", "message": "Cant download story. You will need to share it again.",
"description": "Description for image errors but when it is your own image" "description": "Description for image errors but when it is your own image"
}, },
"StoryCreator__error--video-too-long": {
"message": "Cannot post video to story because it is too long",
"description": "(deleted 02/22/2023) Error string for when a video post to story fails"
},
"StoryCreator__error--video-unsupported": { "StoryCreator__error--video-unsupported": {
"message": "Cannot post video to story as it is an unsupported file format", "message": "Cannot post video to story as it is an unsupported file format",
"description": "Error string for when a video post to story fails" "description": "Error string for when a video post to story fails"

View file

@ -63,7 +63,7 @@ export function AddCaptionModal({
hasFooterDivider={!isScrolledBottom} hasFooterDivider={!isScrolledBottom}
moduleClassName="AddCaptionModal" moduleClassName="AddCaptionModal"
padded={false} padded={false}
title="Add a message" title={i18n('AddCaptionModal__title')}
onClose={onClose} onClose={onClose}
modalFooter={ modalFooter={
<Button onClick={handleSubmit}> <Button onClick={handleSubmit}>

View file

@ -37,17 +37,17 @@ export function AddGroupMemberErrorDialog(props: PropsType): JSX.Element {
case AddGroupMemberErrorDialogMode.MaximumGroupSize: { case AddGroupMemberErrorDialogMode.MaximumGroupSize: {
const { maximumNumberOfContacts } = props; const { maximumNumberOfContacts } = props;
title = i18n('chooseGroupMembers__maximum-group-size__title'); title = i18n('chooseGroupMembers__maximum-group-size__title');
body = i18n('chooseGroupMembers__maximum-group-size__body', [ body = i18n('chooseGroupMembers__maximum-group-size__body', {
maximumNumberOfContacts.toString(), max: maximumNumberOfContacts.toString(),
]); });
break; break;
} }
case AddGroupMemberErrorDialogMode.RecommendedMaximumGroupSize: { case AddGroupMemberErrorDialogMode.RecommendedMaximumGroupSize: {
const { recommendedMaximumNumberOfContacts } = props; const { recommendedMaximumNumberOfContacts } = props;
title = i18n('chooseGroupMembers__maximum-recommended-group-size__title'); title = i18n('chooseGroupMembers__maximum-recommended-group-size__title');
body = i18n('chooseGroupMembers__maximum-recommended-group-size__body', [ body = i18n('chooseGroupMembers__maximum-recommended-group-size__body', {
recommendedMaximumNumberOfContacts.toString(), max: recommendedMaximumNumberOfContacts.toString(),
]); });
break; break;
} }
default: default:

View file

@ -54,15 +54,17 @@ export function AnnouncementsOnlyGroupBanner({
<Intl <Intl
i18n={i18n} i18n={i18n}
id="AnnouncementsOnlyGroupBanner--announcements-only" id="AnnouncementsOnlyGroupBanner--announcements-only"
components={[ components={{
<button admins: (
className="AnnouncementsOnlyGroupBanner__banner--admins" <button
type="button" className="AnnouncementsOnlyGroupBanner__banner--admins"
onClick={() => setIsShowingAdmins(true)} type="button"
> onClick={() => setIsShowingAdmins(true)}
{i18n('AnnouncementsOnlyGroupBanner--admins')} >
</button>, {i18n('AnnouncementsOnlyGroupBanner--admins')}
]} </button>
),
}}
/> />
</div> </div>
</> </>

View file

@ -283,7 +283,9 @@ export function Avatar({
return ( return (
<div <div
aria-label={i18n('contactAvatarAlt', [title])} aria-label={i18n('contactAvatarAlt', {
name: title,
})}
className={classNames( className={classNames(
'module-Avatar', 'module-Avatar',
Boolean(storyRing) && 'module-Avatar--with-story', Boolean(storyRing) && 'module-Avatar--with-story',

View file

@ -64,7 +64,9 @@ export function CallNeedPermissionScreen({
<Intl <Intl
i18n={i18n} i18n={i18n}
id="callNeedPermission" id="callNeedPermission"
components={[<ContactName title={title} />]} components={{
title: <ContactName title={title} />,
}}
/> />
</p> </p>

View file

@ -118,7 +118,13 @@ function DirectCallHeaderMessage({
return <>{i18n('callReconnecting')}</>; return <>{i18n('callReconnecting')}</>;
} }
if (callState === CallState.Accepted && acceptedDuration) { if (callState === CallState.Accepted && acceptedDuration) {
return <>{i18n('callDuration', [renderDuration(acceptedDuration)])}</>; return (
<>
{i18n('callDuration', {
duration: renderDuration(acceptedDuration),
})}
</>
);
} }
return null; return null;
} }
@ -312,9 +318,9 @@ export function CallScreen({
if (isRinging) { if (isRinging) {
headerTitle = undefined; headerTitle = undefined;
} else if (currentPresenter) { } else if (currentPresenter) {
headerTitle = i18n('calling__presenting--person-ongoing', [ headerTitle = i18n('calling__presenting--person-ongoing', {
currentPresenter.title, name: currentPresenter.title,
]); });
} else if (!activeCall.remoteParticipants.length) { } else if (!activeCall.remoteParticipants.length) {
headerTitle = i18n('calling__in-this-call--zero'); headerTitle = i18n('calling__in-this-call--zero');
} }

View file

@ -49,15 +49,15 @@ export function CallingHeader({
{isGroupCall && participantCount ? ( {isGroupCall && participantCount ? (
<div className="module-calling-tools__button"> <div className="module-calling-tools__button">
<Tooltip <Tooltip
content={i18n('calling__participants', [ content={i18n('calling__participants', {
String(participantCount), people: String(participantCount),
])} })}
theme={Theme.Dark} theme={Theme.Dark}
> >
<button <button
aria-label={i18n('calling__participants', [ aria-label={i18n('calling__participants', {
String(participantCount), people: String(participantCount),
])} })}
className={classNames( className={classNames(
'CallingButton__participants--container', 'CallingButton__participants--container',
{ {

View file

@ -83,9 +83,9 @@ export const CallingParticipantsList = React.memo(
{participants.length === 1 && {participants.length === 1 &&
i18n('calling__in-this-call--one')} i18n('calling__in-this-call--one')}
{participants.length > 1 && {participants.length > 1 &&
i18n('calling__in-this-call--many', [ i18n('calling__in-this-call--many', {
String(participants.length), people: String(participants.length),
])} })}
</div> </div>
<button <button
type="button" type="button"

View file

@ -72,7 +72,9 @@ export function CallingPreCallInfo({
case 1: case 1:
subtitle = hasYou subtitle = hasYou
? i18n('calling__pre-call-info--another-device-in-call') ? i18n('calling__pre-call-info--another-device-in-call')
: i18n('calling__pre-call-info--1-person-in-call', participantNames); : i18n('calling__pre-call-info--1-person-in-call', {
first: participantNames[0],
});
break; break;
case 2: case 2:
subtitle = i18n('calling__pre-call-info--2-people-in-call', { subtitle = i18n('calling__pre-call-info--2-people-in-call', {
@ -117,42 +119,53 @@ export function CallingPreCallInfo({
subtitle = i18n('calling__pre-call-info--empty-group'); subtitle = i18n('calling__pre-call-info--empty-group');
break; break;
case 1: { case 1: {
const i18nValues = [memberNames[0]];
subtitle = ring subtitle = ring
? i18n('calling__pre-call-info--will-ring-1', i18nValues) ? i18n('calling__pre-call-info--will-ring-1', {
: i18n('calling__pre-call-info--will-notify-1', i18nValues); person: memberNames[0],
})
: i18n('calling__pre-call-info--will-notify-1', {
person: memberNames[0],
});
break; break;
} }
case 2: { case 2: {
const i18nValues = {
first: memberNames[0],
second: memberNames[1],
};
subtitle = ring subtitle = ring
? i18n('calling__pre-call-info--will-ring-2', i18nValues) ? i18n('calling__pre-call-info--will-ring-2', {
: i18n('calling__pre-call-info--will-notify-2', i18nValues); first: memberNames[0],
second: memberNames[1],
})
: i18n('calling__pre-call-info--will-notify-2', {
first: memberNames[0],
second: memberNames[1],
});
break; break;
} }
case 3: { case 3: {
const i18nValues = {
first: memberNames[0],
second: memberNames[1],
third: memberNames[2],
};
subtitle = ring subtitle = ring
? i18n('calling__pre-call-info--will-ring-3', i18nValues) ? i18n('calling__pre-call-info--will-ring-3', {
: i18n('calling__pre-call-info--will-notify-3', i18nValues); first: memberNames[0],
second: memberNames[1],
third: memberNames[2],
})
: i18n('calling__pre-call-info--will-notify-3', {
first: memberNames[0],
second: memberNames[1],
third: memberNames[2],
});
break; break;
} }
default: { default: {
const i18nValues = {
first: memberNames[0],
second: memberNames[1],
others: String(memberNames.length - 2),
};
subtitle = ring subtitle = ring
? i18n('calling__pre-call-info--will-ring-many', i18nValues) ? i18n('calling__pre-call-info--will-ring-many', {
: i18n('calling__pre-call-info--will-notify-many', i18nValues); first: memberNames[0],
second: memberNames[1],
others: String(memberNames.length - 2),
})
: i18n('calling__pre-call-info--will-notify-many', {
first: memberNames[0],
second: memberNames[1],
others: String(memberNames.length - 2),
});
break; break;
} }
} }

View file

@ -21,7 +21,9 @@ export function CallingScreenSharingController({
return ( return (
<div className="module-CallingScreenSharingController"> <div className="module-CallingScreenSharingController">
<div className="module-CallingScreenSharingController__text"> <div className="module-CallingScreenSharingController__text">
{i18n('calling__presenting--info', [presentedSourceName])} {i18n('calling__presenting--info', {
window: presentedSourceName,
})}
</div> </div>
<div className="module-CallingScreenSharingController__buttons"> <div className="module-CallingScreenSharingController__buttons">
<Button <Button

View file

@ -77,9 +77,9 @@ function useScreenSharingToast({ activeCall, i18n }: PropsType): ToastType {
} else if (previousPresenterTitle) { } else if (previousPresenterTitle) {
setResult({ setResult({
type: 'dismissable', type: 'dismissable',
message: i18n('calling__presenting--person-stopped', [ message: i18n('calling__presenting--person-stopped', {
previousPresenterTitle, name: previousPresenterTitle,
]), }),
}); });
} }
} }

View file

@ -348,9 +348,9 @@ function CustomColorBubble({
}} }}
title={i18n('ChatColorPicker__delete--title')} title={i18n('ChatColorPicker__delete--title')}
> >
{i18n('ChatColorPicker__delete--message', [ {i18n('ChatColorPicker__delete--message', {
String(confirmDeleteCount), num: String(confirmDeleteCount),
])} })}
</ConfirmationDialog> </ConfirmationDialog>
) : null} ) : null}
{isSelected ? ( {isSelected ? (

View file

@ -161,7 +161,9 @@ export function DialogUpdate({
} }
const versionTitle = version const versionTitle = version
? i18n('DialogUpdate--version-available', [version]) ? i18n('DialogUpdate--version-available', {
version,
})
: undefined; : undefined;
if (dialogType === DialogType.Downloading) { if (dialogType === DialogType.Downloading) {

View file

@ -269,7 +269,9 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
<Intl <Intl
i18n={i18n} i18n={i18n}
id="calling__you-have-blocked" id="calling__you-have-blocked"
components={[<ContactName key="name" title={title} />]} components={{
name: <ContactName key="name" title={title} />,
}}
/> />
</div> </div>
} }

View file

@ -93,7 +93,9 @@ export const GroupV2JoinDialog = React.memo(function GroupV2JoinDialogInner(
</div> </div>
<div className="module-group-v2-join-dialog__title">{title}</div> <div className="module-group-v2-join-dialog__title">{title}</div>
<div className="module-group-v2-join-dialog__metadata"> <div className="module-group-v2-join-dialog__metadata">
{i18n('GroupV2--join--group-metadata', [memberString])} {i18n('GroupV2--join--group-metadata', {
memberCount: memberString,
})}
</div> </div>
{groupDescription && ( {groupDescription && (
<div className="module-group-v2-join-dialog__description"> <div className="module-group-v2-join-dialog__description">

View file

@ -188,7 +188,9 @@ export function Inbox({
</div> </div>
<div className="message"> <div className="message">
{loadingMessageCount {loadingMessageCount
? i18n('loadingMessages', [String(loadingMessageCount)]) ? i18n('loadingMessages', {
count: String(loadingMessageCount),
})
: i18n('loading')} : i18n('loading')}
</div> </div>
<div id="toast" /> <div id="toast" />

View file

@ -35,17 +35,19 @@ NoReplacements.args = createProps({
export const SingleStringReplacement = Template.bind({}); export const SingleStringReplacement = Template.bind({});
SingleStringReplacement.args = createProps({ SingleStringReplacement.args = createProps({
id: 'leftTheGroup', id: 'leftTheGroup',
components: ['Theodora'], components: { name: 'Theodora' },
}); });
export const SingleTagReplacement = Template.bind({}); export const SingleTagReplacement = Template.bind({});
SingleTagReplacement.args = createProps({ SingleTagReplacement.args = createProps({
id: 'leftTheGroup', id: 'leftTheGroup',
components: [ components: {
<button type="button" key="a-button"> name: (
Theodora <button type="button" key="a-button">
</button>, Theodora
], </button>
),
},
}); });
export const MultipleStringReplacement = Template.bind({}); export const MultipleStringReplacement = Template.bind({});

View file

@ -16,10 +16,7 @@ export type FullJSXType =
| ReactNode | ReactNode
| JSX.Element | JSX.Element
| string; | string;
export type IntlComponentsType = export type IntlComponentsType = undefined | ReplacementValuesType<FullJSXType>;
| undefined
| Array<FullJSXType>
| ReplacementValuesType<FullJSXType>;
export type Props = { export type Props = {
/** The translation string id */ /** The translation string id */

View file

@ -39,7 +39,7 @@ export function NewlyCreatedGroupInvitedContactsDialog({
<Intl <Intl
i18n={i18n} i18n={i18n}
id="NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--one" id="NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--one"
components={[<ContactName title={contact.title} />]} components={{ name: <ContactName title={contact.title} /> }}
/> />
</GroupDialog.Paragraph> </GroupDialog.Paragraph>
<GroupDialog.Paragraph> <GroupDialog.Paragraph>
@ -48,9 +48,9 @@ export function NewlyCreatedGroupInvitedContactsDialog({
</> </>
); );
} else { } else {
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', [ title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', {
contacts.length.toString(), count: contacts.length.toString(),
]); });
body = ( body = (
<> <>
<GroupDialog.Paragraph> <GroupDialog.Paragraph>

View file

@ -874,12 +874,12 @@ export function Preferences({
left={i18n('Preferences--blocked')} left={i18n('Preferences--blocked')}
right={ right={
blockedCount === 1 blockedCount === 1
? i18n('Preferences--blocked-count-singular', [ ? i18n('Preferences--blocked-count-singular', {
String(blockedCount), num: String(blockedCount),
]) })
: i18n('Preferences--blocked-count-plural', [ : i18n('Preferences--blocked-count-plural', {
String(blockedCount || 0), num: String(blockedCount || 0),
]) })
} }
/> />
</SettingsRow> </SettingsRow>

View file

@ -73,7 +73,7 @@ export function SafetyNumberViewer({
<div className="module-SafetyNumberViewer__number"> <div className="module-SafetyNumberViewer__number">
{safetyNumber || getPlaceholder()} {safetyNumber || getPlaceholder()}
</div> </div>
<Intl i18n={i18n} id="verifyHelp" components={[boldName]} /> <Intl i18n={i18n} id="verifyHelp" components={{ name: boldName }} />
<div className="module-SafetyNumberViewer__verification-status"> <div className="module-SafetyNumberViewer__verification-status">
{isVerified ? ( {isVerified ? (
<span className="module-SafetyNumberViewer__icon--verified" /> <span className="module-SafetyNumberViewer__icon--verified" />
@ -81,9 +81,13 @@ export function SafetyNumberViewer({
<span className="module-SafetyNumberViewer__icon--shield" /> <span className="module-SafetyNumberViewer__icon--shield" />
)} )}
{isVerified ? ( {isVerified ? (
<Intl i18n={i18n} id="isVerified" components={[boldName]} /> <Intl i18n={i18n} id="isVerified" components={{ name: boldName }} />
) : ( ) : (
<Intl i18n={i18n} id="isNotVerified" components={[boldName]} /> <Intl
i18n={i18n}
id="isNotVerified"
components={{ name: boldName }}
/>
)} )}
</div> </div>
<div className="module-SafetyNumberViewer__button"> <div className="module-SafetyNumberViewer__button">

View file

@ -996,9 +996,9 @@ export function SendStoryModal({
}} }}
theme={Theme.Dark} theme={Theme.Dark}
> >
{i18n('StoriesSettings__delete-list--confirm', [ {i18n('StoriesSettings__delete-list--confirm', {
confirmDeleteList.name, name: confirmDeleteList.name,
])} })}
</ConfirmationDialog> </ConfirmationDialog>
)} )}
{confirmDiscardModal} {confirmDiscardModal}

View file

@ -507,9 +507,9 @@ export function StoriesSettingsModal({
}} }}
theme={Theme.Dark} theme={Theme.Dark}
> >
{i18n('StoriesSettings__delete-list--confirm', [ {i18n('StoriesSettings__delete-list--confirm', {
confirmDeleteList.name, name: confirmDeleteList.name,
])} })}
</ConfirmationDialog> </ConfirmationDialog>
)} )}
{confirmRemoveGroup != null && ( {confirmRemoveGroup != null && (
@ -687,9 +687,9 @@ export function DistributionListSettingsModal({
</span> </span>
<button <button
aria-label={i18n('StoriesSettings__remove--title', [ aria-label={i18n('StoriesSettings__remove--title', {
member.title, title: member.title,
])} })}
className="StoriesSettingsModal__list__delete" className="StoriesSettingsModal__list__delete"
onClick={() => { onClick={() => {
strictAssert(member.uuid, 'Story member was missing uuid'); strictAssert(member.uuid, 'Story member was missing uuid');
@ -753,9 +753,9 @@ export function DistributionListSettingsModal({
setConfirmRemoveMember(undefined); setConfirmRemoveMember(undefined);
}} }}
theme={Theme.Dark} theme={Theme.Dark}
title={i18n('StoriesSettings__remove--title', [ title={i18n('StoriesSettings__remove--title', {
confirmRemoveMember.title, title: confirmRemoveMember.title,
])} })}
> >
{i18n('StoriesSettings__remove--body')} {i18n('StoriesSettings__remove--body')}
</ConfirmationDialog> </ConfirmationDialog>

View file

@ -244,14 +244,16 @@ export function StoryDetailsModal({
<Intl <Intl
i18n={i18n} i18n={i18n}
id="StoryDetailsModal__sent-time" id="StoryDetailsModal__sent-time"
components={[ components={{
<Time time: (
className="StoryDetailsModal__debugger__button__text" <Time
timestamp={timestamp} className="StoryDetailsModal__debugger__button__text"
> timestamp={timestamp}
{formatDateTimeLong(i18n, timestamp)} >
</Time>, {formatDateTimeLong(i18n, timestamp)}
]} </Time>
),
}}
/> />
</div> </div>
{attachment && ( {attachment && (
@ -259,11 +261,13 @@ export function StoryDetailsModal({
<Intl <Intl
i18n={i18n} i18n={i18n}
id="StoryDetailsModal__file-size" id="StoryDetailsModal__file-size"
components={[ components={{
<span className="StoryDetailsModal__debugger__button__text"> size: (
{formatFileSize(attachment.size)} <span className="StoryDetailsModal__debugger__button__text">
</span>, {formatFileSize(attachment.size)}
]} </span>
),
}}
/> />
</div> </div>
)} )}
@ -272,13 +276,15 @@ export function StoryDetailsModal({
<Intl <Intl
i18n={i18n} i18n={i18n}
id="StoryDetailsModal__disappears-in" id="StoryDetailsModal__disappears-in"
components={[ components={{
<span className="StoryDetailsModal__debugger__button__text"> countdown: (
{formatRelativeTime(i18n, timeRemaining, { <span className="StoryDetailsModal__debugger__button__text">
largest: 2, {formatRelativeTime(i18n, timeRemaining, {
})} largest: 2,
</span>, })}
]} </span>
),
}}
/> />
</div> </div>
)} )}

View file

@ -158,7 +158,13 @@ export function StoryImage({
if (isMe) { if (isMe) {
content = <>{i18n('StoryImage__error--you')}</>; content = <>{i18n('StoryImage__error--you')}</>;
} else { } else {
content = <>{i18n('StoryImage__error2', [firstName])}</>; content = (
<>
{i18n('StoryImage__error2', {
name: firstName,
})}
</>
);
} }
} }

View file

@ -73,7 +73,9 @@ export function StoryLinkPreview({
{isImage && image ? ( {isImage && image ? (
<div className="StoryLinkPreview__icon-container"> <div className="StoryLinkPreview__icon-container">
<Image <Image
alt={i18n('stagedPreviewThumbnail', [location])} alt={i18n('stagedPreviewThumbnail', {
domain: location,
})}
attachment={image} attachment={image}
curveBottomLeft={CurveType.Tiny} curveBottomLeft={CurveType.Tiny}
curveBottomRight={CurveType.Tiny} curveBottomRight={CurveType.Tiny}

View file

@ -216,7 +216,9 @@ export function StoryListItem({
setHasConfirmHideStory(false); setHasConfirmHideStory(false);
}} }}
> >
{i18n('StoryListItem__hide-modal--body', [String(firstName)])} {i18n('StoryListItem__hide-modal--body', {
name: String(firstName),
})}
</ConfirmationDialog> </ConfirmationDialog>
)} )}
</> </>

View file

@ -857,13 +857,13 @@ export function StoryViewer({
<Intl <Intl
i18n={i18n} i18n={i18n}
id="MyStories__views--singular" id="MyStories__views--singular"
components={[<strong>{viewCount}</strong>]} components={{ num: <strong>{viewCount}</strong> }}
/> />
) : ( ) : (
<Intl <Intl
i18n={i18n} i18n={i18n}
id="MyStories__views--plural" id="MyStories__views--plural"
components={[<strong>{viewCount}</strong>]} components={{ num: <strong>{viewCount}</strong> }}
/> />
))} ))}
{(isSent || viewCount > 0) && replyCount > 0 && ' '} {(isSent || viewCount > 0) && replyCount > 0 && ' '}
@ -872,13 +872,13 @@ export function StoryViewer({
<Intl <Intl
i18n={i18n} i18n={i18n}
id="MyStories__replies--singular" id="MyStories__replies--singular"
components={[<strong>{replyCount}</strong>]} components={{ num: <strong>{replyCount}</strong> }}
/> />
) : ( ) : (
<Intl <Intl
i18n={i18n} i18n={i18n}
id="MyStories__replies--plural" id="MyStories__replies--plural"
components={[<strong>{replyCount}</strong>]} components={{ num: <strong>{replyCount}</strong> }}
/> />
))} ))}
</span> </span>
@ -978,7 +978,9 @@ export function StoryViewer({
setHasConfirmHideStory(false); setHasConfirmHideStory(false);
}} }}
> >
{i18n('StoryListItem__hide-modal--body', [String(firstName)])} {i18n('StoryListItem__hide-modal--body', {
name: String(firstName),
})}
</ConfirmationDialog> </ConfirmationDialog>
)} )}
{confirmDeleteStory && ( {confirmDeleteStory && (

View file

@ -19,11 +19,17 @@ export function WhatsNewLink(props: PropsType): JSX.Element {
<Intl <Intl
i18n={i18n} i18n={i18n}
id="whatsNew" id="whatsNew"
components={[ components={{
<button className="WhatsNew" type="button" onClick={showWhatsNewModal}> whatsNew: (
{i18n('viewReleaseNotes')} <button
</button>, className="WhatsNew"
]} type="button"
onClick={showWhatsNewModal}
>
{i18n('viewReleaseNotes')}
</button>
),
}}
/> />
); );
} }

View file

@ -102,9 +102,9 @@ export function AttachmentList<T extends AttachmentType | AttachmentDraftType>({
const imgElement = ( const imgElement = (
<Image <Image
key={key} key={key}
alt={i18n('stagedImageAttachment', [ alt={i18n('stagedImageAttachment', {
attachment.fileName || url || index.toString(), path: attachment.fileName || url || index.toString(),
])} })}
className="module-staged-attachment" className="module-staged-attachment"
i18n={i18n} i18n={i18n}
attachment={attachment} attachment={attachment}

View file

@ -140,7 +140,9 @@ function renderCallingNotificationButton(
buttonText = i18n('calling__call-is-full'); buttonText = i18n('calling__call-is-full');
disabledTooltipText = i18n( disabledTooltipText = i18n(
'calling__call-notification__button__call-full-tooltip', 'calling__call-notification__button__call-full-tooltip',
[String(deviceCount)] {
max: String(deviceCount),
}
); );
onClick = noop; onClick = noop;
} else { } else {

View file

@ -80,7 +80,7 @@ export class ContactDetail extends React.Component<Props> {
onSendMessage, onSendMessage,
}: { }: {
hasSignalAccount: boolean; hasSignalAccount: boolean;
i18n: (key: string, values?: Array<string>) => string; i18n: LocalizerType;
onSendMessage: () => void; onSendMessage: () => void;
}): JSX.Element | null { }): JSX.Element | null {
if (!hasSignalAccount) { if (!hasSignalAccount) {

View file

@ -125,8 +125,12 @@ export function ContactModal({
onClose={() => setSubModalState(SubModalState.None)} onClose={() => setSubModalState(SubModalState.None)}
> >
{isAdmin {isAdmin
? i18n('ContactModal--rm-admin-info', [contact.title]) ? i18n('ContactModal--rm-admin-info', {
: i18n('ContactModal--make-admin-info', [contact.title])} contact: contact.title,
})
: i18n('ContactModal--make-admin-info', {
contact: contact.title,
})}
</ConfirmationDialog> </ConfirmationDialog>
); );
break; break;

View file

@ -250,9 +250,9 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
contents = ( contents = (
<> <>
<p> <p>
{i18n('ContactSpoofingReviewDialog__group__description', [ {i18n('ContactSpoofingReviewDialog__group__description', {
conversationInfos.length.toString(), count: conversationInfos.length.toString(),
])} })}
</p> </p>
<h2>{i18n('ContactSpoofingReviewDialog__group__members-header')}</h2> <h2>{i18n('ContactSpoofingReviewDialog__group__members-header')}</h2>
{conversationInfos.map((conversationInfo, index) => { {conversationInfos.map((conversationInfo, index) => {

View file

@ -66,7 +66,11 @@ export class GroupNotification extends React.Component<Props> {
switch (type) { switch (type) {
case 'name': case 'name':
return ( return (
<Intl i18n={i18n} id="titleIsNow" components={[newName || '']} /> <Intl
i18n={i18n}
id="titleIsNow"
components={{ name: newName || '' }}
/>
); );
case 'avatar': case 'avatar':
return <Intl i18n={i18n} id="updatedGroupAvatar" />; return <Intl i18n={i18n} id="updatedGroupAvatar" />;
@ -83,13 +87,13 @@ export class GroupNotification extends React.Component<Props> {
<Intl <Intl
i18n={i18n} i18n={i18n}
id="joinedTheGroup" id="joinedTheGroup"
components={[otherPeopleWithCommas]} components={{ name: otherPeopleWithCommas }}
/> />
) : ( ) : (
<Intl <Intl
i18n={i18n} i18n={i18n}
id="multipleJoinedTheGroup" id="multipleJoinedTheGroup"
components={[otherPeopleWithCommas]} components={{ names: otherPeopleWithCommas }}
/> />
)} )}
</> </>
@ -114,13 +118,13 @@ export class GroupNotification extends React.Component<Props> {
<Intl <Intl
id="multipleLeftTheGroup" id="multipleLeftTheGroup"
i18n={i18n} i18n={i18n}
components={[otherPeopleWithCommas]} components={{ name: otherPeopleWithCommas }}
/> />
) : ( ) : (
<Intl <Intl
id="leftTheGroup" id="leftTheGroup"
i18n={i18n} i18n={i18n}
components={[otherPeopleWithCommas]} components={{ name: otherPeopleWithCommas }}
/> />
); );
case 'general': case 'general':
@ -147,7 +151,7 @@ export class GroupNotification extends React.Component<Props> {
<Intl <Intl
i18n={i18n} i18n={i18n}
id="updatedTheGroup" id="updatedTheGroup"
components={[<ContactName title={from.title} />]} components={{ name: <ContactName title={from.title} /> }}
/> />
); );

View file

@ -118,11 +118,19 @@ function renderUsers(
<Intl <Intl
i18n={i18n} i18n={i18n}
id={`${keyPrefix}--one`} id={`${keyPrefix}--one`}
components={[<ContactName title={members[0].title} />]} components={{
contact: <ContactName title={members[0].title} />,
}}
/> />
</p> </p>
); );
} }
return <p>{i18n(`${keyPrefix}--many`, [members.length.toString()])}</p>; return (
<p>
{i18n(`${keyPrefix}--many`, {
count: members.length.toString(),
})}
</p>
);
} }

View file

@ -55,7 +55,7 @@ export type PropsType = PropsDataType &
function renderStringToIntl( function renderStringToIntl(
id: string, id: string,
i18n: LocalizerType, i18n: LocalizerType,
components?: Array<FullJSXType> | ReplacementValuesType<FullJSXType> components?: ReplacementValuesType<FullJSXType>
): FullJSXType { ): FullJSXType {
// eslint-disable-next-line local-rules/valid-i18n-keys // eslint-disable-next-line local-rules/valid-i18n-keys
return <Intl id={id} i18n={i18n} components={components} />; return <Intl id={id} i18n={i18n} components={components} />;

View file

@ -15,7 +15,9 @@ export const LastSeenIndicator = forwardRef<HTMLDivElement, Props>(
const message = const message =
count === 1 count === 1
? i18n('unreadMessage') ? i18n('unreadMessage')
: i18n('unreadMessages', [String(count)]); : i18n('unreadMessages', {
count: String(count),
});
return ( return (
<div className="module-last-seen-indicator" ref={ref}> <div className="module-last-seen-indicator" ref={ref}>

View file

@ -1162,7 +1162,9 @@ export class Message extends React.PureComponent<Props, State> {
curveBottomRight={CurveType.Tiny} curveBottomRight={CurveType.Tiny}
curveTopRight={CurveType.Tiny} curveTopRight={CurveType.Tiny}
curveTopLeft={CurveType.Tiny} curveTopLeft={CurveType.Tiny}
alt={i18n('previewThumbnail', [first.domain])} alt={i18n('previewThumbnail', {
domain: first.domain,
})}
height={72} height={72}
width={72} width={72}
url={first.image.url} url={first.image.url}
@ -1536,7 +1538,9 @@ export class Message extends React.PureComponent<Props, State> {
<> <>
{storyReplyContext.emoji && ( {storyReplyContext.emoji && (
<div className="module-message__quote-story-reaction-header"> <div className="module-message__quote-story-reaction-header">
{i18n('Quote__story-reaction', [storyReplyContext.authorTitle])} {i18n('Quote__story-reaction', {
name: storyReplyContext.authorTitle,
})}
</div> </div>
)} )}
<Quote <Quote

View file

@ -58,18 +58,20 @@ export function MessageRequestActions({
id={`MessageRequests--message-${conversationType}${ id={`MessageRequests--message-${conversationType}${
isBlocked ? '-blocked' : '' isBlocked ? '-blocked' : ''
}`} }`}
components={[ components={{
<strong name: (
key="name" <strong
className="module-message-request-actions__message__name" key="name"
> className="module-message-request-actions__message__name"
<ContactName >
firstName={firstName} <ContactName
title={title} firstName={firstName}
preferFirstName title={title}
/> preferFirstName
</strong>, />
]} </strong>
),
}}
/> />
</p> </p>
<div className="module-message-request-actions__buttons"> <div className="module-message-request-actions__buttons">

View file

@ -52,7 +52,9 @@ export function MessageRequestActionsConfirmation({
<Intl <Intl
i18n={i18n} i18n={i18n}
id={`MessageRequests--block-${conversationType}-confirm-title`} id={`MessageRequests--block-${conversationType}-confirm-title`}
components={[<ContactName key="name" title={title} />]} components={{
title: <ContactName key="name" title={title} />,
}}
/> />
} }
actions={[ actions={[
@ -119,7 +121,9 @@ export function MessageRequestActionsConfirmation({
<Intl <Intl
i18n={i18n} i18n={i18n}
id={`MessageRequests--delete-${conversationType}-confirm-title`} id={`MessageRequests--delete-${conversationType}-confirm-title`}
components={[<ContactName key="name" title={title} />]} components={{
title: <ContactName key="name" title={title} />,
}}
/> />
} }
actions={[ actions={[

View file

@ -46,17 +46,19 @@ export function SafetyNumberNotification({
// eslint-disable-next-line local-rules/valid-i18n-keys // eslint-disable-next-line local-rules/valid-i18n-keys
<Intl <Intl
id={changeKey} id={changeKey}
components={[ components={{
<span name: (
key="external-1" <span
className="module-safety-number-notification__contact" key="external-1"
> className="module-safety-number-notification__contact"
<ContactName >
title={contact.title} <ContactName
module="module-safety-number-notification__contact" title={contact.title}
/> module="module-safety-number-notification__contact"
</span>, />
]} </span>
),
}}
i18n={i18n} i18n={i18n}
/> />
} }

View file

@ -86,7 +86,9 @@ export function StagedLinkPreview({
{isLoaded && image && isImage && domain ? ( {isLoaded && image && isImage && domain ? (
<div className={getClassName('__icon-container')}> <div className={getClassName('__icon-container')}>
<Image <Image
alt={i18n('stagedPreviewThumbnail', [domain])} alt={i18n('stagedPreviewThumbnail', {
domain,
})}
attachment={image} attachment={image}
curveBottomLeft={CurveType.Tiny} curveBottomLeft={CurveType.Tiny}
curveBottomRight={CurveType.Tiny} curveBottomRight={CurveType.Tiny}

View file

@ -69,17 +69,23 @@ export function TimerNotification(props: Props): JSX.Element {
case 'fromMe': case 'fromMe':
message = disabled message = disabled
? i18n('youDisabledDisappearingMessages') ? i18n('youDisabledDisappearingMessages')
: i18n('youChangedTheTimer', [timespan]); : i18n('youChangedTheTimer', {
time: timespan,
});
break; break;
case 'fromSync': case 'fromSync':
message = disabled message = disabled
? i18n('disappearingMessagesDisabled') ? i18n('disappearingMessagesDisabled')
: i18n('timerSetOnSync', [timespan]); : i18n('timerSetOnSync', {
time: timespan,
});
break; break;
case 'fromMember': case 'fromMember':
message = disabled message = disabled
? i18n('disappearingMessagesDisabledByMember') ? i18n('disappearingMessagesDisabledByMember')
: i18n('timerSetByMember', [timespan]); : i18n('timerSetByMember', {
time: timespan,
});
break; break;
default: default:
log.warn('TimerNotification: unsupported type provided:', type); log.warn('TimerNotification: unsupported type provided:', type);

View file

@ -53,17 +53,19 @@ export function UnsupportedMessage({
// eslint-disable-next-line local-rules/valid-i18n-keys // eslint-disable-next-line local-rules/valid-i18n-keys
<Intl <Intl
id={stringId} id={stringId}
components={[ components={{
<span contact: (
key="external-1" <span
className="module-unsupported-message__contact" key="external-1"
> className="module-unsupported-message__contact"
<ContactName >
title={contact.title} <ContactName
module="module-unsupported-message__contact" title={contact.title}
/> module="module-unsupported-message__contact"
</span>, />
]} </span>
),
}}
i18n={i18n} i18n={i18n}
/> />
} }

View file

@ -50,13 +50,15 @@ export class VerificationNotification extends React.Component<Props> {
// eslint-disable-next-line local-rules/valid-i18n-keys // eslint-disable-next-line local-rules/valid-i18n-keys
<Intl <Intl
id={id} id={id}
components={[ components={{
<ContactName name: (
key="external-1" <ContactName
title={contact.title} key="external-1"
module="module-verification-notification__contact" title={contact.title}
/>, module="module-verification-notification__contact"
]} />
),
}}
i18n={i18n} i18n={i18n}
/> />
); );

View file

@ -201,9 +201,9 @@ export function ConversationDetailsActions({
]} ]}
i18n={i18n} i18n={i18n}
onClose={() => gGroupBlock(false)} onClose={() => gGroupBlock(false)}
title={i18n('ConversationDetailsActions--block-group-modal-title', [ title={i18n('ConversationDetailsActions--block-group-modal-title', {
conversationTitle, groupName: conversationTitle,
])} })}
> >
{i18n('ConversationDetailsActions--block-group-modal-content')} {i18n('ConversationDetailsActions--block-group-modal-content')}
</ConfirmationDialog> </ConfirmationDialog>
@ -222,9 +222,9 @@ export function ConversationDetailsActions({
]} ]}
i18n={i18n} i18n={i18n}
onClose={() => gGroupUnblock(false)} onClose={() => gGroupUnblock(false)}
title={i18n('ConversationDetailsActions--unblock-group-modal-title', [ title={i18n('ConversationDetailsActions--unblock-group-modal-title', {
conversationTitle, groupName: conversationTitle,
])} })}
> >
{i18n('ConversationDetailsActions--unblock-group-modal-content')} {i18n('ConversationDetailsActions--unblock-group-modal-content')}
</ConfirmationDialog> </ConfirmationDialog>
@ -242,9 +242,9 @@ export function ConversationDetailsActions({
]} ]}
i18n={i18n} i18n={i18n}
onClose={() => gDirectBlock(false)} onClose={() => gDirectBlock(false)}
title={i18n('MessageRequests--block-direct-confirm-title', [ title={i18n('MessageRequests--block-direct-confirm-title', {
conversationTitle, name: conversationTitle,
])} })}
> >
{i18n('MessageRequests--block-direct-confirm-body')} {i18n('MessageRequests--block-direct-confirm-body')}
</ConfirmationDialog> </ConfirmationDialog>
@ -261,9 +261,9 @@ export function ConversationDetailsActions({
]} ]}
i18n={i18n} i18n={i18n}
onClose={() => gDirectUnblock(false)} onClose={() => gDirectUnblock(false)}
title={i18n('MessageRequests--unblock-direct-confirm-title', [ title={i18n('MessageRequests--unblock-direct-confirm-title', {
conversationTitle, name: conversationTitle,
])} })}
> >
{i18n('MessageRequests--unblock-direct-confirm-body')} {i18n('MessageRequests--unblock-direct-confirm-body')}
</ConfirmationDialog> </ConfirmationDialog>

View file

@ -66,9 +66,9 @@ export function ConversationDetailsHeader({
} else if (canEdit) { } else if (canEdit) {
subtitle = i18n('ConversationDetailsHeader--add-group-description'); subtitle = i18n('ConversationDetailsHeader--add-group-description');
} else { } else {
subtitle = i18n('ConversationDetailsHeader--members', [ subtitle = i18n('ConversationDetailsHeader--members', {
memberships.length.toString(), number: memberships.length.toString(),
]); });
} }
} else if (!isMe) { } else if (!isMe) {
subtitle = ( subtitle = (

View file

@ -93,9 +93,9 @@ export function ConversationDetailsMembershipList({
return ( return (
<PanelSection <PanelSection
title={i18n('ConversationDetailsMembershipList--title', [ title={i18n('ConversationDetailsMembershipList--title', {
sortedMemberships.length.toString(), number: sortedMemberships.length.toString(),
])} })}
> >
{canAddNewMembers && ( {canAddNewMembers && (
<PanelRow <PanelRow

View file

@ -403,7 +403,9 @@ function MembersPendingAdminApproval({
/> />
))} ))}
<div className="ConversationDetails__pending--info"> <div className="ConversationDetails__pending--info">
{i18n('PendingRequests--info', [conversation.title])} {i18n('PendingRequests--info', {
name: conversation.title,
})}
</div> </div>
</PanelSection> </PanelSection>
); );
@ -501,9 +503,9 @@ function MembersPendingProfileKey({
/> />
} }
label={member.title} label={member.title}
right={i18n('PendingInvites--invited-count', [ right={i18n('PendingInvites--invited-count', {
pendingMemberships.length.toString(), number: pendingMemberships.length.toString(),
])} })}
actions={ actions={
conversation.areWeAdmin ? ( conversation.areWeAdmin ? (
<ConversationDetailsIcon <ConversationDetailsIcon

View file

@ -132,11 +132,17 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
} else if (isCheckbox) { } else if (isCheckbox) {
let ariaLabel: string; let ariaLabel: string;
if (disabled) { if (disabled) {
ariaLabel = i18n('cannotSelectContact', [title]); ariaLabel = i18n('cannotSelectContact', {
name: title,
});
} else if (checked) { } else if (checked) {
ariaLabel = i18n('deselectContact', [title]); ariaLabel = i18n('deselectContact', {
name: title,
});
} else { } else {
ariaLabel = i18n('selectContact', [title]); ariaLabel = i18n('selectContact', {
name: title,
});
} }
actionNode = ( actionNode = (
<div className={CHECKBOX_CONTAINER_CLASS_NAME}> <div className={CHECKBOX_CONTAINER_CLASS_NAME}>

View file

@ -480,7 +480,9 @@ export const EmojiPicker = React.memo(
handlePickTone(event); handlePickTone(event);
} }
}} }}
title={i18n('EmojiPicker--skin-tone', [`${tone}`])} title={i18n('EmojiPicker--skin-tone', {
tone: `${tone}`,
})}
className={classNames( className={classNames(
'module-emoji-picker__button', 'module-emoji-picker__button',
'module-emoji-picker__button--footer', 'module-emoji-picker__button--footer',

View file

@ -161,7 +161,11 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
} else { } else {
noResults = ( noResults = (
<> <>
<div>{i18n('noSearchResults', [searchTerm])}</div> <div>
{i18n('noSearchResults', {
searchTerm,
})}
</div>
{primarySendsSms && ( {primarySendsSms && (
<div className="module-left-pane__no-search-results__sms-only"> <div className="module-left-pane__no-search-results__sms-only">
{i18n('noSearchResults--sms-only')} {i18n('noSearchResults--sms-only')}

View file

@ -14,7 +14,7 @@ export type SmartContactRendererType<T> = (uuid: UUIDStringType) => T | string;
export type StringRendererType<T> = ( export type StringRendererType<T> = (
id: string, id: string,
i18n: LocalizerType, i18n: LocalizerType,
components?: Array<T | string> | ReplacementValuesType<T | string> components?: ReplacementValuesType<T | string>
) => T | string; ) => T | string;
export type RenderOptionsType<T> = { export type RenderOptionsType<T> = {
@ -93,7 +93,7 @@ export function renderChangeDetail<T>(
if (newTitle) { if (newTitle) {
if (fromYou) { if (fromYou) {
return renderString('GroupV2--title--change--you', i18n, [newTitle]); return renderString('GroupV2--title--change--you', i18n, { newTitle });
} }
if (from) { if (from) {
return renderString('GroupV2--title--change--other', i18n, { return renderString('GroupV2--title--change--other', i18n, {
@ -101,15 +101,17 @@ export function renderChangeDetail<T>(
newTitle, newTitle,
}); });
} }
return renderString('GroupV2--title--change--unknown', i18n, [newTitle]); return renderString('GroupV2--title--change--unknown', i18n, {
newTitle,
});
} }
if (fromYou) { if (fromYou) {
return renderString('GroupV2--title--remove--you', i18n); return renderString('GroupV2--title--remove--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--title--remove--other', i18n, [ return renderString('GroupV2--title--remove--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--title--remove--unknown', i18n); return renderString('GroupV2--title--remove--unknown', i18n);
} }
@ -119,9 +121,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--avatar--remove--you', i18n); return renderString('GroupV2--avatar--remove--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--avatar--remove--other', i18n, [ return renderString('GroupV2--avatar--remove--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--avatar--remove--unknown', i18n); return renderString('GroupV2--avatar--remove--unknown', i18n);
} }
@ -129,9 +131,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--avatar--change--you', i18n); return renderString('GroupV2--avatar--change--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--avatar--change--other', i18n, [ return renderString('GroupV2--avatar--change--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--avatar--change--unknown', i18n); return renderString('GroupV2--avatar--change--unknown', i18n);
} }
@ -143,9 +145,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--access-attributes--admins--you', i18n); return renderString('GroupV2--access-attributes--admins--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--access-attributes--admins--other', i18n, [ return renderString('GroupV2--access-attributes--admins--other', i18n, {
renderContact(from), adminName: renderContact(from),
]); });
} }
return renderString('GroupV2--access-attributes--admins--unknown', i18n); return renderString('GroupV2--access-attributes--admins--unknown', i18n);
} }
@ -154,9 +156,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--access-attributes--all--you', i18n); return renderString('GroupV2--access-attributes--all--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--access-attributes--all--other', i18n, [ return renderString('GroupV2--access-attributes--all--other', i18n, {
renderContact(from), adminName: renderContact(from),
]); });
} }
return renderString('GroupV2--access-attributes--all--unknown', i18n); return renderString('GroupV2--access-attributes--all--unknown', i18n);
} }
@ -173,9 +175,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--access-members--admins--you', i18n); return renderString('GroupV2--access-members--admins--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--access-members--admins--other', i18n, [ return renderString('GroupV2--access-members--admins--other', i18n, {
renderContact(from), adminName: renderContact(from),
]); });
} }
return renderString('GroupV2--access-members--admins--unknown', i18n); return renderString('GroupV2--access-members--admins--unknown', i18n);
} }
@ -184,9 +186,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--access-members--all--you', i18n); return renderString('GroupV2--access-members--all--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--access-members--all--other', i18n, [ return renderString('GroupV2--access-members--all--other', i18n, {
renderContact(from), adminName: renderContact(from),
]); });
} }
return renderString('GroupV2--access-members--all--unknown', i18n); return renderString('GroupV2--access-members--all--unknown', i18n);
} }
@ -206,7 +208,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--access-invite-link--enabled--other', 'GroupV2--access-invite-link--enabled--other',
i18n, i18n,
[renderContact(from)] { adminName: renderContact(from) }
); );
} }
return renderString( return renderString(
@ -222,7 +224,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--access-invite-link--disabled--other', 'GroupV2--access-invite-link--disabled--other',
i18n, i18n,
[renderContact(from)] { adminName: renderContact(from) }
); );
} }
return renderString( return renderString(
@ -244,16 +246,16 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--member-add--you--you', i18n); return renderString('GroupV2--member-add--you--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--member-add--you--other', i18n, [ return renderString('GroupV2--member-add--you--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--member-add--you--unknown', i18n); return renderString('GroupV2--member-add--you--unknown', i18n);
} }
if (fromYou) { if (fromYou) {
return renderString('GroupV2--member-add--other--you', i18n, [ return renderString('GroupV2--member-add--other--you', i18n, {
renderContact(uuid), memberName: renderContact(uuid),
]); });
} }
if (from) { if (from) {
return renderString('GroupV2--member-add--other--other', i18n, { return renderString('GroupV2--member-add--other--other', i18n, {
@ -261,9 +263,9 @@ export function renderChangeDetail<T>(
addeeName: renderContact(uuid), addeeName: renderContact(uuid),
}); });
} }
return renderString('GroupV2--member-add--other--unknown', i18n, [ return renderString('GroupV2--member-add--other--unknown', i18n, {
renderContact(uuid), memberName: renderContact(uuid),
]); });
} }
if (detail.type === 'member-add-from-invite') { if (detail.type === 'member-add-from-invite') {
const { uuid, inviter } = detail; const { uuid, inviter } = detail;
@ -274,9 +276,9 @@ export function renderChangeDetail<T>(
if (weAreJoiner) { if (weAreJoiner) {
// They can't be the same, no fromYou check here // They can't be the same, no fromYou check here
if (from) { if (from) {
return renderString('GroupV2--member-add--you--other', i18n, [ return renderString('GroupV2--member-add--you--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--member-add--you--unknown', i18n); return renderString('GroupV2--member-add--you--unknown', i18n);
} }
@ -299,9 +301,9 @@ export function renderChangeDetail<T>(
if (weAreJoiner) { if (weAreJoiner) {
if (inviter) { if (inviter) {
return renderString('GroupV2--member-add--from-invite--you', i18n, [ return renderString('GroupV2--member-add--from-invite--you', i18n, {
renderContact(inviter), inviterName: renderContact(inviter),
]); });
} }
return renderString( return renderString(
'GroupV2--member-add--from-invite--you-no-from', 'GroupV2--member-add--from-invite--you-no-from',
@ -309,9 +311,9 @@ export function renderChangeDetail<T>(
); );
} }
if (weAreInviter) { if (weAreInviter) {
return renderString('GroupV2--member-add--from-invite--from-you', i18n, [ return renderString('GroupV2--member-add--from-invite--from-you', i18n, {
renderContact(uuid), inviteeName: renderContact(uuid),
]); });
} }
if (inviter) { if (inviter) {
return renderString('GroupV2--member-add--from-invite--other', i18n, { return renderString('GroupV2--member-add--from-invite--other', i18n, {
@ -334,17 +336,17 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--member-add-from-link--you--you', i18n); return renderString('GroupV2--member-add-from-link--you--you', i18n);
} }
if (from && uuid === from) { if (from && uuid === from) {
return renderString('GroupV2--member-add-from-link--other', i18n, [ return renderString('GroupV2--member-add-from-link--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
// Note: this shouldn't happen, because we only capture 'add-from-link' status // Note: this shouldn't happen, because we only capture 'add-from-link' status
// from group change events, which always have a sender. // from group change events, which always have a sender.
log.warn('member-add-from-link change type; we have no from!'); log.warn('member-add-from-link change type; we have no from!');
return renderString('GroupV2--member-add--other--unknown', i18n, [ return renderString('GroupV2--member-add--other--unknown', i18n, {
renderContact(uuid), memberName: renderContact(uuid),
]); });
} }
if (detail.type === 'member-add-from-admin-approval') { if (detail.type === 'member-add-from-admin-approval') {
const { uuid } = detail; const { uuid } = detail;
@ -355,7 +357,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--member-add-from-admin-approval--you--other', 'GroupV2--member-add-from-admin-approval--you--other',
i18n, i18n,
[renderContact(from)] { adminName: renderContact(from) }
); );
} }
@ -374,7 +376,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--member-add-from-admin-approval--other--you', 'GroupV2--member-add-from-admin-approval--other--you',
i18n, i18n,
[renderContact(uuid)] { joinerName: renderContact(uuid) }
); );
} }
if (from) { if (from) {
@ -394,7 +396,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--member-add-from-admin-approval--other--unknown', 'GroupV2--member-add-from-admin-approval--other--unknown',
i18n, i18n,
[renderContact(uuid)] { joinerName: renderContact(uuid) }
); );
} }
if (detail.type === 'member-remove') { if (detail.type === 'member-remove') {
@ -406,22 +408,22 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--member-remove--you--you', i18n); return renderString('GroupV2--member-remove--you--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--member-remove--you--other', i18n, [ return renderString('GroupV2--member-remove--you--other', i18n, {
renderContact(from), adminName: renderContact(from),
]); });
} }
return renderString('GroupV2--member-remove--you--unknown', i18n); return renderString('GroupV2--member-remove--you--unknown', i18n);
} }
if (fromYou) { if (fromYou) {
return renderString('GroupV2--member-remove--other--you', i18n, [ return renderString('GroupV2--member-remove--other--you', i18n, {
renderContact(uuid), memberName: renderContact(uuid),
]); });
} }
if (from && from === uuid) { if (from && from === uuid) {
return renderString('GroupV2--member-remove--other--self', i18n, [ return renderString('GroupV2--member-remove--other--self', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
if (from) { if (from) {
return renderString('GroupV2--member-remove--other--other', i18n, { return renderString('GroupV2--member-remove--other--other', i18n, {
@ -429,9 +431,9 @@ export function renderChangeDetail<T>(
memberName: renderContact(uuid), memberName: renderContact(uuid),
}); });
} }
return renderString('GroupV2--member-remove--other--unknown', i18n, [ return renderString('GroupV2--member-remove--other--unknown', i18n, {
renderContact(uuid), memberName: renderContact(uuid),
]); });
} }
if (detail.type === 'member-privilege') { if (detail.type === 'member-privilege') {
const { uuid, newPrivilege } = detail; const { uuid, newPrivilege } = detail;
@ -443,7 +445,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--member-privilege--promote--you--other', 'GroupV2--member-privilege--promote--you--other',
i18n, i18n,
[renderContact(from)] { adminName: renderContact(from) }
); );
} }
@ -457,7 +459,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--member-privilege--promote--other--you', 'GroupV2--member-privilege--promote--other--you',
i18n, i18n,
[renderContact(uuid)] { memberName: renderContact(uuid) }
); );
} }
if (from) { if (from) {
@ -473,7 +475,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--member-privilege--promote--other--unknown', 'GroupV2--member-privilege--promote--other--unknown',
i18n, i18n,
[renderContact(uuid)] { memberName: renderContact(uuid) }
); );
} }
if (newPrivilege === RoleEnum.DEFAULT) { if (newPrivilege === RoleEnum.DEFAULT) {
@ -482,7 +484,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--member-privilege--demote--you--other', 'GroupV2--member-privilege--demote--you--other',
i18n, i18n,
[renderContact(from)] { adminName: renderContact(from) }
); );
} }
return renderString( return renderString(
@ -495,7 +497,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--member-privilege--demote--other--you', 'GroupV2--member-privilege--demote--other--you',
i18n, i18n,
[renderContact(uuid)] { memberName: renderContact(uuid) }
); );
} }
if (from) { if (from) {
@ -511,7 +513,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--member-privilege--demote--other--unknown', 'GroupV2--member-privilege--demote--other--unknown',
i18n, i18n,
[renderContact(uuid)] { memberName: renderContact(uuid) }
); );
} }
log.warn( log.warn(
@ -524,21 +526,21 @@ export function renderChangeDetail<T>(
const weAreInvited = isOurUuid(uuid); const weAreInvited = isOurUuid(uuid);
if (weAreInvited) { if (weAreInvited) {
if (from) { if (from) {
return renderString('GroupV2--pending-add--one--you--other', i18n, [ return renderString('GroupV2--pending-add--one--you--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--pending-add--one--you--unknown', i18n); return renderString('GroupV2--pending-add--one--you--unknown', i18n);
} }
if (fromYou) { if (fromYou) {
return renderString('GroupV2--pending-add--one--other--you', i18n, [ return renderString('GroupV2--pending-add--one--other--you', i18n, {
renderContact(uuid), inviteeName: renderContact(uuid),
]); });
} }
if (from) { if (from) {
return renderString('GroupV2--pending-add--one--other--other', i18n, [ return renderString('GroupV2--pending-add--one--other--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--pending-add--one--other--unknown', i18n); return renderString('GroupV2--pending-add--one--other--unknown', i18n);
} }
@ -546,9 +548,9 @@ export function renderChangeDetail<T>(
const { count } = detail; const { count } = detail;
if (fromYou) { if (fromYou) {
return renderString('GroupV2--pending-add--many--you', i18n, [ return renderString('GroupV2--pending-add--many--you', i18n, {
count.toString(), count: count.toString(),
]); });
} }
if (from) { if (from) {
return renderString('GroupV2--pending-add--many--other', i18n, { return renderString('GroupV2--pending-add--many--other', i18n, {
@ -556,9 +558,9 @@ export function renderChangeDetail<T>(
count: count.toString(), count: count.toString(),
}); });
} }
return renderString('GroupV2--pending-add--many--unknown', i18n, [ return renderString('GroupV2--pending-add--many--unknown', i18n, {
count.toString(), count: count.toString(),
]); });
} }
if (detail.type === 'pending-remove-one') { if (detail.type === 'pending-remove-one') {
const { inviter, uuid } = detail; const { inviter, uuid } = detail;
@ -569,15 +571,15 @@ export function renderChangeDetail<T>(
if (weAreInviter) { if (weAreInviter) {
if (sentByInvited) { if (sentByInvited) {
return renderString('GroupV2--pending-remove--decline--you', i18n, [ return renderString('GroupV2--pending-remove--decline--you', i18n, {
renderContact(uuid), inviteeName: renderContact(uuid),
]); });
} }
if (fromYou) { if (fromYou) {
return renderString( return renderString(
'GroupV2--pending-remove--revoke-invite-from-you--one--you', 'GroupV2--pending-remove--revoke-invite-from-you--one--you',
i18n, i18n,
[renderContact(uuid)] { inviteeName: renderContact(uuid) }
); );
} }
if (from) { if (from) {
@ -593,7 +595,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--pending-remove--revoke-invite-from-you--one--unknown', 'GroupV2--pending-remove--revoke-invite-from-you--one--unknown',
i18n, i18n,
[renderContact(uuid)] { inviteeName: renderContact(uuid) }
); );
} }
if (sentByInvited) { if (sentByInvited) {
@ -601,9 +603,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--pending-remove--decline--from-you', i18n); return renderString('GroupV2--pending-remove--decline--from-you', i18n);
} }
if (inviter) { if (inviter) {
return renderString('GroupV2--pending-remove--decline--other', i18n, [ return renderString('GroupV2--pending-remove--decline--other', i18n, {
renderContact(inviter), memberName: renderContact(inviter),
]); });
} }
return renderString('GroupV2--pending-remove--decline--unknown', i18n); return renderString('GroupV2--pending-remove--decline--unknown', i18n);
} }
@ -612,13 +614,13 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--pending-remove--revoke-own--to-you', 'GroupV2--pending-remove--revoke-own--to-you',
i18n, i18n,
[renderContact(inviter)] { inviterName: renderContact(inviter) }
); );
} }
return renderString( return renderString(
'GroupV2--pending-remove--revoke-own--unknown', 'GroupV2--pending-remove--revoke-own--unknown',
i18n, i18n,
[renderContact(inviter)] { inviterName: renderContact(inviter) }
); );
} }
if (inviter) { if (inviter) {
@ -626,7 +628,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--pending-remove--revoke-invite-from--one--you', 'GroupV2--pending-remove--revoke-invite-from--one--you',
i18n, i18n,
[renderContact(inviter)] { memberName: renderContact(inviter) }
); );
} }
if (from) { if (from) {
@ -642,16 +644,16 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--pending-remove--revoke-invite-from--one--unknown', 'GroupV2--pending-remove--revoke-invite-from--one--unknown',
i18n, i18n,
[renderContact(inviter)] { memberName: renderContact(inviter) }
); );
} }
if (fromYou) { if (fromYou) {
return renderString('GroupV2--pending-remove--revoke--one--you', i18n); return renderString('GroupV2--pending-remove--revoke--one--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--pending-remove--revoke--one--other', i18n, [ return renderString('GroupV2--pending-remove--revoke--one--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--pending-remove--revoke--one--unknown', i18n); return renderString('GroupV2--pending-remove--revoke--one--unknown', i18n);
} }
@ -664,7 +666,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--pending-remove--revoke-invite-from-you--many--you', 'GroupV2--pending-remove--revoke-invite-from-you--many--you',
i18n, i18n,
[count.toString()] { count: count.toString() }
); );
} }
if (from) { if (from) {
@ -680,7 +682,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--pending-remove--revoke-invite-from-you--many--unknown', 'GroupV2--pending-remove--revoke-invite-from-you--many--unknown',
i18n, i18n,
[count.toString()] { count: count.toString() }
); );
} }
if (inviter) { if (inviter) {
@ -715,9 +717,9 @@ export function renderChangeDetail<T>(
); );
} }
if (fromYou) { if (fromYou) {
return renderString('GroupV2--pending-remove--revoke--many--you', i18n, [ return renderString('GroupV2--pending-remove--revoke--many--you', i18n, {
count.toString(), count: count.toString(),
]); });
} }
if (from) { if (from) {
return renderString( return renderString(
@ -732,7 +734,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--pending-remove--revoke--many--unknown', 'GroupV2--pending-remove--revoke--many--unknown',
i18n, i18n,
[count.toString()] { count: count.toString() }
); );
} }
if (detail.type === 'admin-approval-add-one') { if (detail.type === 'admin-approval-add-one') {
@ -742,9 +744,9 @@ export function renderChangeDetail<T>(
if (weAreJoiner) { if (weAreJoiner) {
return renderString('GroupV2--admin-approval-add-one--you', i18n); return renderString('GroupV2--admin-approval-add-one--you', i18n);
} }
return renderString('GroupV2--admin-approval-add-one--other', i18n, [ return renderString('GroupV2--admin-approval-add-one--other', i18n, {
renderContact(uuid), joinerName: renderContact(uuid),
]); });
} }
if (detail.type === 'admin-approval-remove-one') { if (detail.type === 'admin-approval-remove-one') {
const { uuid } = detail; const { uuid } = detail;
@ -767,14 +769,14 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--admin-approval-remove-one--other--you', 'GroupV2--admin-approval-remove-one--other--you',
i18n, i18n,
[renderContact(uuid)] { joinerName: renderContact(uuid) }
); );
} }
if (from && from === uuid) { if (from && from === uuid) {
return renderString( return renderString(
'GroupV2--admin-approval-remove-one--other--own', 'GroupV2--admin-approval-remove-one--other--own',
i18n, i18n,
[renderContact(uuid)] { joinerName: renderContact(uuid) }
); );
} }
if (from) { if (from) {
@ -793,7 +795,7 @@ export function renderChangeDetail<T>(
return renderString( return renderString(
'GroupV2--admin-approval-remove-one--other--own', 'GroupV2--admin-approval-remove-one--other--own',
i18n, i18n,
[renderContact(uuid)] { joinerName: renderContact(uuid) }
); );
} }
if (detail.type === 'admin-approval-bounce') { if (detail.type === 'admin-approval-bounce') {
@ -836,9 +838,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--group-link-add--enabled--you', i18n); return renderString('GroupV2--group-link-add--enabled--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--group-link-add--enabled--other', i18n, [ return renderString('GroupV2--group-link-add--enabled--other', i18n, {
renderContact(from), adminName: renderContact(from),
]); });
} }
return renderString('GroupV2--group-link-add--enabled--unknown', i18n); return renderString('GroupV2--group-link-add--enabled--unknown', i18n);
} }
@ -847,9 +849,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--group-link-add--disabled--you', i18n); return renderString('GroupV2--group-link-add--disabled--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--group-link-add--disabled--other', i18n, [ return renderString('GroupV2--group-link-add--disabled--other', i18n, {
renderContact(from), adminName: renderContact(from),
]); });
} }
return renderString('GroupV2--group-link-add--disabled--unknown', i18n); return renderString('GroupV2--group-link-add--disabled--unknown', i18n);
} }
@ -861,9 +863,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--group-link-reset--you', i18n); return renderString('GroupV2--group-link-reset--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--group-link-reset--other', i18n, [ return renderString('GroupV2--group-link-reset--other', i18n, {
renderContact(from), adminName: renderContact(from),
]); });
} }
return renderString('GroupV2--group-link-reset--unknown', i18n); return renderString('GroupV2--group-link-reset--unknown', i18n);
} }
@ -872,9 +874,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--group-link-remove--you', i18n); return renderString('GroupV2--group-link-remove--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--group-link-remove--other', i18n, [ return renderString('GroupV2--group-link-remove--other', i18n, {
renderContact(from), adminName: renderContact(from),
]); });
} }
return renderString('GroupV2--group-link-remove--unknown', i18n); return renderString('GroupV2--group-link-remove--unknown', i18n);
} }
@ -884,9 +886,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--description--remove--you', i18n); return renderString('GroupV2--description--remove--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--description--remove--other', i18n, [ return renderString('GroupV2--description--remove--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--description--remove--unknown', i18n); return renderString('GroupV2--description--remove--unknown', i18n);
} }
@ -895,9 +897,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--description--change--you', i18n); return renderString('GroupV2--description--change--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--description--change--other', i18n, [ return renderString('GroupV2--description--change--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--description--change--unknown', i18n); return renderString('GroupV2--description--change--unknown', i18n);
} }
@ -907,9 +909,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--announcements--admin--you', i18n); return renderString('GroupV2--announcements--admin--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--announcements--admin--other', i18n, [ return renderString('GroupV2--announcements--admin--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--announcements--admin--unknown', i18n); return renderString('GroupV2--announcements--admin--unknown', i18n);
} }
@ -918,9 +920,9 @@ export function renderChangeDetail<T>(
return renderString('GroupV2--announcements--member--you', i18n); return renderString('GroupV2--announcements--member--you', i18n);
} }
if (from) { if (from) {
return renderString('GroupV2--announcements--member--other', i18n, [ return renderString('GroupV2--announcements--member--other', i18n, {
renderContact(from), memberName: renderContact(from),
]); });
} }
return renderString('GroupV2--announcements--member--unknown', i18n); return renderString('GroupV2--announcements--member--unknown', i18n);
} }

View file

@ -579,7 +579,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
renderString: ( renderString: (
key: string, key: string,
_i18n: unknown, _i18n: unknown,
components: Array<string> | ReplacementValuesType<string> | undefined components: ReplacementValuesType<string> | undefined
) => window.i18n(key, components), ) => window.i18n(key, components),
}); });
@ -640,9 +640,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
if (groupUpdate.left) { if (groupUpdate.left) {
return { return {
text: window.i18n('leftTheGroup', [ text: window.i18n('leftTheGroup', {
this.getNameForNumber(groupUpdate.left), name: this.getNameForNumber(groupUpdate.left),
]), }),
}; };
} }
@ -653,7 +653,11 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (isMe(fromContact.attributes)) { if (isMe(fromContact.attributes)) {
messages.push(window.i18n('youUpdatedTheGroup')); messages.push(window.i18n('youUpdatedTheGroup'));
} else { } else {
messages.push(window.i18n('updatedTheGroup', [fromContact.getTitle()])); messages.push(
window.i18n('updatedTheGroup', {
name: fromContact.getTitle(),
})
);
} }
if (groupUpdate.joined && groupUpdate.joined.length) { if (groupUpdate.joined && groupUpdate.joined.length) {
@ -666,9 +670,11 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (joinedContacts.length > 1) { if (joinedContacts.length > 1) {
messages.push( messages.push(
window.i18n('multipleJoinedTheGroup', [ window.i18n('multipleJoinedTheGroup', {
joinedWithoutMe.map(contact => contact.getTitle()).join(', '), names: joinedWithoutMe
]) .map(contact => contact.getTitle())
.join(', '),
})
); );
if (joinedWithoutMe.length < joinedContacts.length) { if (joinedWithoutMe.length < joinedContacts.length) {
@ -683,14 +689,20 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
messages.push(window.i18n('youJoinedTheGroup')); messages.push(window.i18n('youJoinedTheGroup'));
} else { } else {
messages.push( messages.push(
window.i18n('joinedTheGroup', [joinedContacts[0].getTitle()]) window.i18n('joinedTheGroup', {
name: joinedContacts[0].getTitle(),
})
); );
} }
} }
} }
if (groupUpdate.name) { if (groupUpdate.name) {
messages.push(window.i18n('titleIsNow', [groupUpdate.name])); messages.push(
window.i18n('titleIsNow', {
name: groupUpdate.name,
})
);
} }
if (groupUpdate.avatarUpdated) { if (groupUpdate.avatarUpdated) {
messages.push(window.i18n('updatedGroupAvatar')); messages.push(window.i18n('updatedGroupAvatar'));
@ -788,9 +800,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
return { return {
text: window.i18n('timerSetTo', [ text: window.i18n('timerSetTo', {
expirationTimer.format(window.i18n, expireTimer), time: expirationTimer.format(window.i18n, expireTimer),
]), }),
}; };
} }
@ -798,9 +810,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const identifier = this.get('key_changed'); const identifier = this.get('key_changed');
const conversation = window.ConversationController.get(identifier); const conversation = window.ConversationController.get(identifier);
return { return {
text: window.i18n('safetyNumberChangedGroup', [ text: window.i18n('safetyNumberChangedGroup', {
conversation ? conversation.getTitle() : '', name: conversation ? conversation.getTitle() : '',
]), }),
}; };
} }
const contacts = this.get('contact'); const contacts = this.get('contact');

View file

@ -2266,9 +2266,9 @@ export class CallingClass {
notificationTitle = notificationTitle =
conversation?.getTitle() || FALLBACK_NOTIFICATION_TITLE; conversation?.getTitle() || FALLBACK_NOTIFICATION_TITLE;
notificationMessage = creatorConversation notificationMessage = creatorConversation
? window.i18n('calling__call-notification__started', [ ? window.i18n('calling__call-notification__started', {
creatorConversation.getTitle(), name: creatorConversation.getTitle(),
]) })
: window.i18n('calling__call-notification__started-by-someone'); : window.i18n('calling__call-notification__started-by-someone');
break; break;
} }

View file

@ -22,7 +22,9 @@ describe('setupI18n', () => {
assert.strictEqual(i18n('reportIssue'), 'Contact Support'); assert.strictEqual(i18n('reportIssue'), 'Contact Support');
}); });
it('returns message with single substitution', () => { it('returns message with single substitution', () => {
const actual = i18n('migratingToSQLCipher', ['45/200']); const actual = i18n('migratingToSQLCipher', {
status: '45/200',
});
assert.equal(actual, 'Optimizing messages... 45/200 complete.'); assert.equal(actual, 'Optimizing messages... 45/200 complete.');
}); });
it('returns message with multiple substitutions', () => { it('returns message with multiple substitutions', () => {

View file

@ -40,11 +40,9 @@ export type RenderTextCallbackType = (options: {
key: number; key: number;
}) => JSX.Element | string; }) => JSX.Element | string;
export type ReplacementValuesType = export type ReplacementValuesType = {
| Array<string> [key: string]: string | number | undefined;
| { };
[key: string]: string | number | undefined;
};
export type LocalizerType = { export type LocalizerType = {
(key: string, values?: ReplacementValuesType): string; (key: string, values?: ReplacementValuesType): string;

View file

@ -86,9 +86,9 @@ function getGroupCallNotificationText(
if (notification.creator.isMe) { if (notification.creator.isMe) {
return i18n('calling__call-notification__started-by-you'); return i18n('calling__call-notification__started-by-you');
} }
return i18n('calling__call-notification__started', [ return i18n('calling__call-notification__started', {
notification.creator.systemGivenName ?? notification.creator.title, name: notification.creator.systemGivenName ?? notification.creator.title,
]); });
} }
export function getCallingNotificationText( export function getCallingNotificationText(

View file

@ -23,5 +23,7 @@ export function getMutedUntilText(
? expires.format('LT') ? expires.format('LT')
: expires.format('L, LT'); : expires.format('L, LT');
return i18n('muteExpirationLabel', [muteExpirationUntil]); return i18n('muteExpirationLabel', {
duration: muteExpirationUntil,
});
} }

View file

@ -125,21 +125,21 @@ export function formatDateTimeLong(
const timestamp = rawTimestamp.valueOf(); const timestamp = rawTimestamp.valueOf();
if (isToday(rawTimestamp)) { if (isToday(rawTimestamp)) {
return i18n('timestampFormat__long--today', [ return i18n('timestampFormat__long--today', {
new Intl.DateTimeFormat(locale, { time: new Intl.DateTimeFormat(locale, {
hour: 'numeric', hour: 'numeric',
minute: 'numeric', minute: 'numeric',
}).format(timestamp), }).format(timestamp),
]); });
} }
if (isYesterday(rawTimestamp)) { if (isYesterday(rawTimestamp)) {
return i18n('timestampFormat__long--yesterday', [ return i18n('timestampFormat__long--yesterday', {
new Intl.DateTimeFormat(locale, { time: new Intl.DateTimeFormat(locale, {
hour: 'numeric', hour: 'numeric',
minute: 'numeric', minute: 'numeric',
}).format(timestamp), }).format(timestamp),
]); });
} }
return new Intl.DateTimeFormat(locale, { return new Intl.DateTimeFormat(locale, {
@ -165,11 +165,15 @@ export function formatTime(
} }
if (diff < HOUR) { if (diff < HOUR) {
return i18n('minutesAgo', [Math.floor(diff / MINUTE).toString()]); return i18n('minutesAgo', {
minutes: Math.floor(diff / MINUTE).toString(),
});
} }
if (isRelativeTime) { if (isRelativeTime) {
return i18n('hoursAgo', [Math.floor(diff / HOUR).toString()]); return i18n('hoursAgo', {
hours: Math.floor(diff / HOUR).toString(),
});
} }
return new Date(timestamp).toLocaleTimeString([], { return new Date(timestamp).toLocaleTimeString([], {