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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -72,7 +72,9 @@ export function CallingPreCallInfo({
case 1:
subtitle = hasYou
? 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;
case 2:
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');
break;
case 1: {
const i18nValues = [memberNames[0]];
subtitle = ring
? i18n('calling__pre-call-info--will-ring-1', i18nValues)
: i18n('calling__pre-call-info--will-notify-1', i18nValues);
? i18n('calling__pre-call-info--will-ring-1', {
person: memberNames[0],
})
: i18n('calling__pre-call-info--will-notify-1', {
person: memberNames[0],
});
break;
}
case 2: {
const i18nValues = {
first: memberNames[0],
second: memberNames[1],
};
subtitle = ring
? i18n('calling__pre-call-info--will-ring-2', i18nValues)
: i18n('calling__pre-call-info--will-notify-2', i18nValues);
? i18n('calling__pre-call-info--will-ring-2', {
first: memberNames[0],
second: memberNames[1],
})
: i18n('calling__pre-call-info--will-notify-2', {
first: memberNames[0],
second: memberNames[1],
});
break;
}
case 3: {
const i18nValues = {
first: memberNames[0],
second: memberNames[1],
third: memberNames[2],
};
subtitle = ring
? i18n('calling__pre-call-info--will-ring-3', i18nValues)
: i18n('calling__pre-call-info--will-notify-3', i18nValues);
? i18n('calling__pre-call-info--will-ring-3', {
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;
}
default: {
const i18nValues = {
first: memberNames[0],
second: memberNames[1],
others: String(memberNames.length - 2),
};
subtitle = ring
? i18n('calling__pre-call-info--will-ring-many', i18nValues)
: i18n('calling__pre-call-info--will-notify-many', i18nValues);
? i18n('calling__pre-call-info--will-ring-many', {
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;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -19,11 +19,17 @@ export function WhatsNewLink(props: PropsType): JSX.Element {
<Intl
i18n={i18n}
id="whatsNew"
components={[
<button className="WhatsNew" type="button" onClick={showWhatsNewModal}>
{i18n('viewReleaseNotes')}
</button>,
]}
components={{
whatsNew: (
<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 = (
<Image
key={key}
alt={i18n('stagedImageAttachment', [
attachment.fileName || url || index.toString(),
])}
alt={i18n('stagedImageAttachment', {
path: attachment.fileName || url || index.toString(),
})}
className="module-staged-attachment"
i18n={i18n}
attachment={attachment}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -118,11 +118,19 @@ function renderUsers(
<Intl
i18n={i18n}
id={`${keyPrefix}--one`}
components={[<ContactName title={members[0].title} />]}
components={{
contact: <ContactName title={members[0].title} />,
}}
/>
</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(
id: string,
i18n: LocalizerType,
components?: Array<FullJSXType> | ReplacementValuesType<FullJSXType>
components?: ReplacementValuesType<FullJSXType>
): FullJSXType {
// eslint-disable-next-line local-rules/valid-i18n-keys
return <Intl id={id} i18n={i18n} components={components} />;

View file

@ -15,7 +15,9 @@ export const LastSeenIndicator = forwardRef<HTMLDivElement, Props>(
const message =
count === 1
? i18n('unreadMessage')
: i18n('unreadMessages', [String(count)]);
: i18n('unreadMessages', {
count: String(count),
});
return (
<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}
curveTopRight={CurveType.Tiny}
curveTopLeft={CurveType.Tiny}
alt={i18n('previewThumbnail', [first.domain])}
alt={i18n('previewThumbnail', {
domain: first.domain,
})}
height={72}
width={72}
url={first.image.url}
@ -1536,7 +1538,9 @@ export class Message extends React.PureComponent<Props, State> {
<>
{storyReplyContext.emoji && (
<div className="module-message__quote-story-reaction-header">
{i18n('Quote__story-reaction', [storyReplyContext.authorTitle])}
{i18n('Quote__story-reaction', {
name: storyReplyContext.authorTitle,
})}
</div>
)}
<Quote

View file

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

View file

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

View file

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

View file

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

View file

@ -69,17 +69,23 @@ export function TimerNotification(props: Props): JSX.Element {
case 'fromMe':
message = disabled
? i18n('youDisabledDisappearingMessages')
: i18n('youChangedTheTimer', [timespan]);
: i18n('youChangedTheTimer', {
time: timespan,
});
break;
case 'fromSync':
message = disabled
? i18n('disappearingMessagesDisabled')
: i18n('timerSetOnSync', [timespan]);
: i18n('timerSetOnSync', {
time: timespan,
});
break;
case 'fromMember':
message = disabled
? i18n('disappearingMessagesDisabledByMember')
: i18n('timerSetByMember', [timespan]);
: i18n('timerSetByMember', {
time: timespan,
});
break;
default:
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
<Intl
id={stringId}
components={[
<span
key="external-1"
className="module-unsupported-message__contact"
>
<ContactName
title={contact.title}
module="module-unsupported-message__contact"
/>
</span>,
]}
components={{
contact: (
<span
key="external-1"
className="module-unsupported-message__contact"
>
<ContactName
title={contact.title}
module="module-unsupported-message__contact"
/>
</span>
),
}}
i18n={i18n}
/>
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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