diff --git a/.eslint/rules/valid-i18n-keys.js b/.eslint/rules/valid-i18n-keys.js index 5d6f30659..7aa4ceebd 100644 --- a/.eslint/rules/valid-i18n-keys.js +++ b/.eslint/rules/valid-i18n-keys.js @@ -14,9 +14,11 @@ const messagesCacheKey = hashSum.digest('hex'); function isI18nCall(node) { return ( - node.type === 'CallExpression' && - node.callee.type === 'Identifier' && - node.callee.name === 'i18n' + (node.type === 'CallExpression' && + node.callee.type === 'Identifier' && + node.callee.name === 'i18n') || + (node.callee.type === 'MemberExpression' && + node.callee.property.name === 'i18n') ); } @@ -36,20 +38,7 @@ function valueToMessageKey(node) { if (isStringLiteral(node)) { return node.value; } - - if (node.type !== 'TemplateLiteral') { - return null; - } - - if (node.quasis.length === 1) { - return node.quasis[0].value.cooked; - } - - const parts = node.quasis.map(element => { - return element.value.cooked; - }); - - return new RegExp(`^${parts.join('(.*)')}$`); + return null; } function getI18nCallMessageKey(node) { @@ -80,24 +69,11 @@ function getIntlElementMessageKey(node) { let value = idAttribute.value; - if (value.type === 'JSXExpressionContainer') { - value = value.expression; - } - return valueToMessageKey(value); } function isValidMessageKey(key) { - if (typeof key === 'string') { - if (Object.hasOwn(messages, key)) { - return true; - } - } else if (key instanceof RegExp) { - if (messageKeys.some(k => key.test(k))) { - return true; - } - } - return false; + return Object.hasOwn(messages, key); } module.exports = { diff --git a/.eslint/rules/valid-i18n-keys.test.js b/.eslint/rules/valid-i18n-keys.test.js index 7b957d216..b59757782 100644 --- a/.eslint/rules/valid-i18n-keys.test.js +++ b/.eslint/rules/valid-i18n-keys.test.js @@ -27,27 +27,68 @@ ruleTester.run('valid-i18n-keys', rule, { options: [{ messagesCacheKey }], }, { - code: 'i18n(`AddCaptionModal__${title}`)', + code: `window.i18n("AddCaptionModal__title")`, options: [{ messagesCacheKey }], }, { code: `let jsx = `, options: [{ messagesCacheKey }], }, + ], + invalid: [ + { + code: 'i18n(`AddCaptionModal__${title}`)', + options: [{ messagesCacheKey }], + errors: [ + { + message: "i18n()'s first argument should always be a literal string", + type: 'CallExpression', + }, + ], + }, + { + code: 'window.i18n(`AddCaptionModal__${title}`)', + options: [{ messagesCacheKey }], + errors: [ + { + message: "i18n()'s first argument should always be a literal string", + type: 'CallExpression', + }, + ], + }, { code: `let jsx = `, options: [{ messagesCacheKey }], + errors: [ + { + message: + " must always be provided an 'id' attribute with a literal string", + type: 'JSXOpeningElement', + }, + ], }, { code: 'let jsx = ', options: [{ messagesCacheKey }], + errors: [ + { + message: + " must always be provided an 'id' attribute with a literal string", + type: 'JSXOpeningElement', + }, + ], }, { code: 'let jsx = ', options: [{ messagesCacheKey }], + errors: [ + { + message: + " must always be provided an 'id' attribute with a literal string", + type: 'JSXOpeningElement', + }, + ], }, - ], - invalid: [ { code: `i18n("THIS_KEY_SHOULD_NEVER_EXIST")`, options: [{ messagesCacheKey }], diff --git a/ts/components/ConversationList.stories.tsx b/ts/components/ConversationList.stories.tsx index ec2412c6a..86ef40991 100644 --- a/ts/components/ConversationList.stories.tsx +++ b/ts/components/ConversationList.stories.tsx @@ -604,19 +604,23 @@ export function Headers(): JSX.Element { rows={[ { type: RowType.Header, - i18nKey: 'conversationsHeader', + // eslint-disable-next-line @typescript-eslint/no-shadow + getHeaderText: i18n => i18n('conversationsHeader'), }, { type: RowType.Header, - i18nKey: 'messagesHeader', + // eslint-disable-next-line @typescript-eslint/no-shadow + getHeaderText: i18n => i18n('messagesHeader'), }, { type: RowType.Header, - i18nKey: 'findByUsernameHeader', + // eslint-disable-next-line @typescript-eslint/no-shadow + getHeaderText: i18n => i18n('findByUsernameHeader'), }, { type: RowType.Header, - i18nKey: 'findByPhoneNumberHeader', + // eslint-disable-next-line @typescript-eslint/no-shadow + getHeaderText: i18n => i18n('findByPhoneNumberHeader'), }, ]} /> @@ -629,7 +633,8 @@ export function FindByPhoneNumber(): JSX.Element { rows={[ { type: RowType.Header, - i18nKey: 'findByPhoneNumberHeader', + // eslint-disable-next-line @typescript-eslint/no-shadow + getHeaderText: i18n => i18n('findByPhoneNumberHeader'), }, { type: RowType.StartNewConversation, @@ -673,7 +678,8 @@ export function FindByUsername(): JSX.Element { rows={[ { type: RowType.Header, - i18nKey: 'findByUsernameHeader', + // eslint-disable-next-line @typescript-eslint/no-shadow + getHeaderText: i18n => i18n('findByUsernameHeader'), }, { type: RowType.UsernameSearchResult, @@ -745,7 +751,8 @@ export function KitchenSink(): JSX.Element { }, { type: RowType.Header, - i18nKey: 'contactsHeader', + // eslint-disable-next-line @typescript-eslint/no-shadow + getHeaderText: i18n => i18n('contactsHeader'), }, { type: RowType.Contact, @@ -753,7 +760,8 @@ export function KitchenSink(): JSX.Element { }, { type: RowType.Header, - i18nKey: 'messagesHeader', + // eslint-disable-next-line @typescript-eslint/no-shadow + getHeaderText: i18n => i18n('messagesHeader'), }, { type: RowType.Conversation, @@ -765,7 +773,8 @@ export function KitchenSink(): JSX.Element { }, { type: RowType.Header, - i18nKey: 'findByUsernameHeader', + // eslint-disable-next-line @typescript-eslint/no-shadow + getHeaderText: i18n => i18n('findByUsernameHeader'), }, { type: RowType.UsernameSearchResult, diff --git a/ts/components/ConversationList.tsx b/ts/components/ConversationList.tsx index b94b264fb..8d61c1394 100644 --- a/ts/components/ConversationList.tsx +++ b/ts/components/ConversationList.tsx @@ -103,9 +103,17 @@ type MessageRowType = { type HeaderRowType = { type: RowType.Header; - i18nKey: string; + getHeaderText: (i18n: LocalizerType) => string; }; +// Exported for tests across multiple files +export function _testHeaderText(row: Row | void): string | null { + if (row?.type === RowType.Header) { + return row.getHeaderText(((key: string) => key) as LocalizerType); + } + return null; +} + type SearchResultsLoadingFakeHeaderType = { type: RowType.SearchResultsLoadingFakeHeader; }; @@ -375,18 +383,18 @@ export function ConversationList({ /> ); break; - case RowType.Header: + case RowType.Header: { + const headerText = row.getHeaderText(i18n); result = (
- {/* eslint-disable-next-line local-rules/valid-i18n-keys */} - {i18n(row.i18nKey)} + {headerText}
); break; + } case RowType.MessageSearchResult: result = <>{renderMessageSearchResult?.(row.messageId)}; break; diff --git a/ts/components/GroupV1MigrationDialog.tsx b/ts/components/GroupV1MigrationDialog.tsx index 8f6d77506..9d223d604 100644 --- a/ts/components/GroupV1MigrationDialog.tsx +++ b/ts/components/GroupV1MigrationDialog.tsx @@ -7,6 +7,7 @@ import type { ConversationType } from '../state/ducks/conversations'; import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import { GroupDialog } from './GroupDialog'; import { sortByTitle } from '../util/sortByTitle'; +import { missingCaseError } from '../util'; export type DataPropsType = { conversationId: string; @@ -70,8 +71,6 @@ export const GroupV1MigrationDialog: React.FunctionComponent = const keepHistory = hasMigrated ? i18n('GroupV1--Migration--info--keep-history') : i18n('GroupV1--Migration--migrate--keep-history'); - const migrationKey = hasMigrated ? 'after' : 'before'; - const droppedMembersKey = `GroupV1--Migration--info--removed--${migrationKey}`; let primaryButtonText: string; let onClickPrimaryButton: () => void; @@ -116,14 +115,16 @@ export const GroupV1MigrationDialog: React.FunctionComponent = getPreferredBadge, i18n, members: invitedMembers, - prefix: 'GroupV1--Migration--info--invited', + hasMigrated, + kind: 'invited', theme, })} {renderMembers({ getPreferredBadge, i18n, members: droppedMembers, - prefix: droppedMembersKey, + hasMigrated, + kind: 'dropped', theme, })} @@ -136,26 +137,49 @@ function renderMembers({ getPreferredBadge, i18n, members, - prefix, + hasMigrated, + kind, theme, }: Readonly<{ getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; members: Array; - prefix: string; + hasMigrated: boolean; + kind: 'invited' | 'dropped'; theme: ThemeType; }>): React.ReactNode { if (!members.length) { return null; } - const postfix = members.length === 1 ? '--one' : '--many'; - const key = `${prefix}${postfix}`; + let text: string; + switch (kind) { + case 'invited': + text = + members.length === 1 + ? i18n('GroupV1--Migration--info--invited--one') + : i18n('GroupV1--Migration--info--invited--many'); + break; + case 'dropped': + if (hasMigrated) { + text = + members.length === 1 + ? i18n('GroupV1--Migration--info--removed--before--one') + : i18n('GroupV1--Migration--info--removed--before--many'); + } else { + text = + members.length === 1 + ? i18n('GroupV1--Migration--info--removed--after--one') + : i18n('GroupV1--Migration--info--removed--after--many'); + } + break; + default: + throw missingCaseError(kind); + } return ( <> - {/* eslint-disable-next-line local-rules/valid-i18n-keys */} - {i18n(key)} + {text} { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types public override render() { - const { components, id, i18n, renderText = defaultRenderText } = this.props; + const { + components, + id, + // Indirection for linter/migration tooling + i18n: localizer, + renderText = defaultRenderText, + } = this.props; if (!id) { log.error('Error: Intl id prop not provided'); return null; } - if (!i18n.isLegacyFormat(id)) { + if (!localizer.isLegacyFormat(id)) { strictAssert( !Array.isArray(components), `components cannot be an array for ICU message ${id}` ); - const intl = i18n.getIntl(); + const intl = localizer.getIntl(); return intl.formatMessage({ id }, components); } - // eslint-disable-next-line local-rules/valid-i18n-keys - const text = i18n(id); + const text = localizer(id); const results: Array< string | JSX.Element | Array | null > = []; diff --git a/ts/components/SendStoryModal.tsx b/ts/components/SendStoryModal.tsx index 5288973ff..08bcd1bbe 100644 --- a/ts/components/SendStoryModal.tsx +++ b/ts/components/SendStoryModal.tsx @@ -339,7 +339,7 @@ export function SendStoryModal({ { diff --git a/ts/components/StoriesSettingsModal.tsx b/ts/components/StoriesSettingsModal.tsx index 2e98fd3e5..3dbe56b6c 100644 --- a/ts/components/StoriesSettingsModal.tsx +++ b/ts/components/StoriesSettingsModal.tsx @@ -627,7 +627,7 @@ export function DistributionListSettingsModal({ {isMyStory && ( { setPage(Page.HideStoryFrom); @@ -791,7 +791,7 @@ function CheckboxRender({ type EditMyStoryPrivacyPropsType = { hasDisclaimerAbove?: boolean; i18n: LocalizerType; - learnMore: string; + kind: 'privacy' | 'mine'; myStories: StoryDistributionListWithMembersDataType; onClickExclude: () => unknown; onClickOnlyShareWith: () => unknown; @@ -805,7 +805,7 @@ type EditMyStoryPrivacyPropsType = { export function EditMyStoryPrivacy({ hasDisclaimerAbove, i18n, - learnMore, + kind, myStories, onClickExclude, onClickOnlyShareWith, @@ -814,24 +814,30 @@ export function EditMyStoryPrivacy({ toggleSignalConnectionsModal, signalConnectionsCount, }: EditMyStoryPrivacyPropsType): JSX.Element { + const learnMore = ( + + ); const disclaimerElement = (
- {/* eslint-disable-next-line local-rules/valid-i18n-keys */} - - {i18n('StoriesSettings__mine__disclaimer--learn-more')} - - ), - }} - i18n={i18n} - id={learnMore} - /> + {kind === 'mine' ? ( + + ) : ( + + )}
); diff --git a/ts/components/ToastManager.tsx b/ts/components/ToastManager.tsx index 73c2e72cc..e61c198cf 100644 --- a/ts/components/ToastManager.tsx +++ b/ts/components/ToastManager.tsx @@ -40,10 +40,9 @@ export function ToastManager({ if (toastType === ToastType.AddingUserToGroup) { return ( - {i18n( - 'AddUserToAnotherGroupModal__toast--adding-user-to-group', - toast.parameters - )} + {i18n('AddUserToAnotherGroupModal__toast--adding-user-to-group', { + ...toast.parameters, + })} ); } @@ -107,7 +106,9 @@ export function ToastManager({ if (toastType === ToastType.CannotStartGroupCall) { return ( - {i18n('GroupV2--cannot-start-group-call', toast.parameters)} + {i18n('GroupV2--cannot-start-group-call', { + ...toast.parameters, + })} ); } @@ -344,10 +345,9 @@ export function ToastManager({ if (toastType === ToastType.UserAddedToGroup) { return ( - {i18n( - 'AddUserToAnotherGroupModal__toast--user-added-to-group', - toast.parameters - )} + {i18n('AddUserToAnotherGroupModal__toast--user-added-to-group', { + ...toast.parameters, + })} ); } diff --git a/ts/components/WhatsNewModal.tsx b/ts/components/WhatsNewModal.tsx index 501796552..3b3c799a6 100644 --- a/ts/components/WhatsNewModal.tsx +++ b/ts/components/WhatsNewModal.tsx @@ -6,7 +6,6 @@ import React from 'react'; import moment from 'moment'; import { Modal } from './Modal'; -import type { IntlComponentsType } from './Intl'; import { Intl } from './Intl'; import { Emojify } from './conversation/Emojify'; import type { LocalizerType, RenderTextCallbackType } from '../types/Util'; @@ -19,61 +18,46 @@ export type PropsType = { type ReleaseNotesType = { date: Date; version: string; - features: Array<{ key: string; components: IntlComponentsType }>; + features: Array; }; const renderText: RenderTextCallbackType = ({ key, text }) => ( ); -const releaseNotes: ReleaseNotesType = { - date: new Date(window.getBuildCreation?.() || Date.now()), - version: window.getVersion?.(), - features: [ - { - key: 'icu:WhatsNew__v6.12--0', - components: {}, - }, - { - key: 'icu:WhatsNew__v6.12--1', - components: {}, - }, - ], -}; - export function WhatsNewModal({ i18n, hideWhatsNewModal, }: PropsType): JSX.Element { let contentNode: ReactChild; + const releaseNotes: ReleaseNotesType = { + date: new Date(window.getBuildCreation?.() || Date.now()), + version: window.getVersion?.(), + features: [ + , + , + ], + }; + if (releaseNotes.features.length === 1) { - const { key, components } = releaseNotes.features[0]; - contentNode = ( -

- {/* eslint-disable-next-line local-rules/valid-i18n-keys */} - -

- ); + contentNode =

{releaseNotes.features[0]}

; } else { contentNode = (
    - {releaseNotes.features.map(({ key, components }) => ( -
  • - {/* eslint-disable-next-line local-rules/valid-i18n-keys */} - -
  • - ))} + {releaseNotes.features.map(element => { + return
  • {element}
  • ; + })}
); } diff --git a/ts/components/conversation/GroupV1Migration.tsx b/ts/components/conversation/GroupV1Migration.tsx index 0e4478201..6c7bbbdac 100644 --- a/ts/components/conversation/GroupV1Migration.tsx +++ b/ts/components/conversation/GroupV1Migration.tsx @@ -60,16 +60,8 @@ export function GroupV1Migration(props: PropsType): React.ReactElement { i18n('GroupV1--Migration--invited--you') ) : ( <> - {renderUsers( - invitedMembers, - i18n, - 'GroupV1--Migration--invited' - )} - {renderUsers( - droppedMembers, - i18n, - 'GroupV1--Migration--removed' - )} + {renderUsers(invitedMembers, i18n, 'invited')} + {renderUsers(droppedMembers, i18n, 'removed')} )}

@@ -106,31 +98,52 @@ export function GroupV1Migration(props: PropsType): React.ReactElement { function renderUsers( members: Array, i18n: LocalizerType, - keyPrefix: string + kind: 'invited' | 'removed' ): React.ReactElement | null { if (!members || members.length === 0) { return null; } if (members.length === 1) { + const contact = ; return (

- , - }} - /> + {kind === 'invited' && ( + + )} + {kind === 'removed' && ( + + )}

); } + const count = members.length.toString(); + return (

- {i18n(`${keyPrefix}--many`, { - count: members.length.toString(), - })} + {kind === 'invited' && members.length > 1 && ( + + )} + {kind === 'removed' && members.length > 1 && ( + + )}

); } diff --git a/ts/components/conversation/MandatoryProfileSharingActions.tsx b/ts/components/conversation/MandatoryProfileSharingActions.tsx index f03aa7651..e6c3eec9f 100644 --- a/ts/components/conversation/MandatoryProfileSharingActions.tsx +++ b/ts/components/conversation/MandatoryProfileSharingActions.tsx @@ -40,6 +40,26 @@ export function MandatoryProfileSharingActions({ }: Props): JSX.Element { const [mrState, setMrState] = React.useState(MessageRequestState.default); + const firstNameContact = ( + + + + ); + + const learnMore = ( + + {i18n('MessageRequests--learn-more')} + + ); + return ( <> {mrState !== MessageRequestState.default ? ( @@ -62,34 +82,19 @@ export function MandatoryProfileSharingActions({ ) : null}

- - - - ), - learnMore: ( - - {i18n('MessageRequests--learn-more')} - - ), - }} - /> + {conversationType === 'direct' ? ( + + ) : ( + + )}

); diff --git a/ts/components/conversation/media-gallery/MediaGallery.tsx b/ts/components/conversation/media-gallery/MediaGallery.tsx index 15b913ad9..114ea12c6 100644 --- a/ts/components/conversation/media-gallery/MediaGallery.tsx +++ b/ts/components/conversation/media-gallery/MediaGallery.tsx @@ -71,11 +71,25 @@ function MediaSection({ const first = section.mediaItems[0]; const { message } = first; const date = moment(getMessageTimestamp(message)); - const header = - section.type === 'yearMonth' - ? date.format(MONTH_FORMAT) - : // eslint-disable-next-line local-rules/valid-i18n-keys - i18n(section.type); + + function getHeader(): string { + switch (section.type) { + case 'yearMonth': + return date.format(MONTH_FORMAT); + case 'today': + return i18n('today'); + case 'yesterday': + return i18n('yesterday'); + case 'thisWeek': + return i18n('thisWeek'); + case 'thisMonth': + return i18n('thisMonth'); + default: + throw missingCaseError(section); + } + } + + const header = getHeader(); return ( ( @@ -80,7 +83,7 @@ export const EmojiPicker = React.memo( ref ) => { const [firstRecent] = React.useState(recentEmojis); - const [selectedCategory, setSelectedCategory] = React.useState( + const [selectedCategory, setSelectedCategory] = React.useState( categories[0] ); const [searchMode, setSearchMode] = React.useState(false); @@ -277,7 +280,7 @@ export const EmojiPicker = React.memo( const { category } = e.currentTarget.dataset; if (category) { - setSelectedCategory(category); + setSelectedCategory(category as Category); setScrollToRow(catToRowOffsets[category]); } }, @@ -332,11 +335,36 @@ export const EmojiPicker = React.memo( findLast(catOffsetEntries, ([, row]) => rowStartIndex >= row) || categories; - setSelectedCategory(cat); + setSelectedCategory(cat as Category); }, 10), [catOffsetEntries] ); + function getCategoryButtonLabel(category: Category): string { + switch (category) { + case 'recents': + return i18n('EmojiPicker__button--recents'); + case 'emoji': + return i18n('EmojiPicker__button--emoji'); + case 'animal': + return i18n('EmojiPicker__button--animal'); + case 'food': + return i18n('EmojiPicker__button--food'); + case 'activity': + return i18n('EmojiPicker__button--activity'); + case 'travel': + return i18n('EmojiPicker__button--travel'); + case 'object': + return i18n('EmojiPicker__button--object'); + case 'symbol': + return i18n('EmojiPicker__button--symbol'); + case 'flag': + return i18n('EmojiPicker__button--flag'); + default: + throw missingCaseError(category); + } + } + return ( ) ) diff --git a/ts/components/leftPane/LeftPaneChooseGroupMembersHelper.tsx b/ts/components/leftPane/LeftPaneChooseGroupMembersHelper.tsx index eebf6390d..d896eeb21 100644 --- a/ts/components/leftPane/LeftPaneChooseGroupMembersHelper.tsx +++ b/ts/components/leftPane/LeftPaneChooseGroupMembersHelper.tsx @@ -314,7 +314,7 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper i18n('contactsHeader'), }; } @@ -342,7 +342,7 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper i18n('findByPhoneNumberHeader'), }; } if (virtualRowIndex === 1) { @@ -363,7 +363,7 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper i18n('findByUsernameHeader'), }; } if (virtualRowIndex === 1) { diff --git a/ts/components/leftPane/LeftPaneComposeHelper.tsx b/ts/components/leftPane/LeftPaneComposeHelper.tsx index 9e1091a15..fcf3aea0c 100644 --- a/ts/components/leftPane/LeftPaneComposeHelper.tsx +++ b/ts/components/leftPane/LeftPaneComposeHelper.tsx @@ -194,7 +194,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper i18n('contactsHeader'), }; } @@ -215,7 +215,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper i18n('groupsHeader'), }; } @@ -236,7 +236,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper i18n('findByUsernameHeader'), }; } @@ -258,7 +258,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper i18n('findByPhoneNumberHeader'), }; } diff --git a/ts/components/leftPane/LeftPaneInboxHelper.tsx b/ts/components/leftPane/LeftPaneInboxHelper.tsx index 966f1fc65..0dd173f1f 100644 --- a/ts/components/leftPane/LeftPaneInboxHelper.tsx +++ b/ts/components/leftPane/LeftPaneInboxHelper.tsx @@ -150,12 +150,12 @@ export class LeftPaneInboxHelper extends LeftPaneHelper case 0: return { type: RowType.Header, - i18nKey: 'LeftPane--pinned', + getHeaderText: i18n => i18n('LeftPane--pinned'), }; case pinnedConversations.length + 1: return { type: RowType.Header, - i18nKey: 'LeftPane--chats', + getHeaderText: i18n => i18n('LeftPane--chats'), }; case pinnedConversations.length + conversations.length + 2: if (archivedConversationsCount) { diff --git a/ts/components/leftPane/LeftPaneSearchHelper.tsx b/ts/components/leftPane/LeftPaneSearchHelper.tsx index 309825ab5..8cac80810 100644 --- a/ts/components/leftPane/LeftPaneSearchHelper.tsx +++ b/ts/components/leftPane/LeftPaneSearchHelper.tsx @@ -229,7 +229,7 @@ export class LeftPaneSearchHelper extends LeftPaneHelper i18n('conversationsHeader'), }; } assertDev( @@ -250,7 +250,7 @@ export class LeftPaneSearchHelper extends LeftPaneHelper i18n('contactsHeader'), }; } assertDev( @@ -274,7 +274,7 @@ export class LeftPaneSearchHelper extends LeftPaneHelper i18n('messagesHeader'), }; } assertDev( diff --git a/ts/components/leftPane/LeftPaneSetGroupMetadataHelper.tsx b/ts/components/leftPane/LeftPaneSetGroupMetadataHelper.tsx index d45d174a9..fa3e7610d 100644 --- a/ts/components/leftPane/LeftPaneSetGroupMetadataHelper.tsx +++ b/ts/components/leftPane/LeftPaneSetGroupMetadataHelper.tsx @@ -258,7 +258,7 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper i18n('setGroupMetadata__members-header'), }; } diff --git a/ts/groupChange.ts b/ts/groupChange.ts index 73d25c0b4..e63cc006d 100644 --- a/ts/groupChange.ts +++ b/ts/groupChange.ts @@ -67,7 +67,18 @@ export function renderChangeDetail( detail: GroupV2ChangeDetailType, options: RenderOptionsType ): T | string | ReadonlyArray { - const { from, i18n, ourACI, ourPNI, renderContact, renderString } = options; + const { + from, + i18n: localizer, + ourACI, + ourPNI, + renderContact, + renderString, + } = options; + + function i18n(id: string, components?: ReplacementValuesType) { + return renderString(id, localizer, components); + } const isOurUuid = (uuid?: UUIDStringType): boolean => { if (!uuid) { @@ -79,88 +90,88 @@ export function renderChangeDetail( if (detail.type === 'create') { if (fromYou) { - return renderString('GroupV2--create--you', i18n); + return i18n('GroupV2--create--you'); } if (from) { - return renderString('GroupV2--create--other', i18n, { + return i18n('GroupV2--create--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--create--unknown', i18n); + return i18n('GroupV2--create--unknown'); } if (detail.type === 'title') { const { newTitle } = detail; if (newTitle) { if (fromYou) { - return renderString('GroupV2--title--change--you', i18n, { newTitle }); + return i18n('GroupV2--title--change--you', { newTitle }); } if (from) { - return renderString('GroupV2--title--change--other', i18n, { + return i18n('GroupV2--title--change--other', { memberName: renderContact(from), newTitle, }); } - return renderString('GroupV2--title--change--unknown', i18n, { + return i18n('GroupV2--title--change--unknown', { newTitle, }); } if (fromYou) { - return renderString('GroupV2--title--remove--you', i18n); + return i18n('GroupV2--title--remove--you'); } if (from) { - return renderString('GroupV2--title--remove--other', i18n, { + return i18n('GroupV2--title--remove--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--title--remove--unknown', i18n); + return i18n('GroupV2--title--remove--unknown'); } if (detail.type === 'avatar') { if (detail.removed) { if (fromYou) { - return renderString('GroupV2--avatar--remove--you', i18n); + return i18n('GroupV2--avatar--remove--you'); } if (from) { - return renderString('GroupV2--avatar--remove--other', i18n, { + return i18n('GroupV2--avatar--remove--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--avatar--remove--unknown', i18n); + return i18n('GroupV2--avatar--remove--unknown'); } if (fromYou) { - return renderString('GroupV2--avatar--change--you', i18n); + return i18n('GroupV2--avatar--change--you'); } if (from) { - return renderString('GroupV2--avatar--change--other', i18n, { + return i18n('GroupV2--avatar--change--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--avatar--change--unknown', i18n); + return i18n('GroupV2--avatar--change--unknown'); } if (detail.type === 'access-attributes') { const { newPrivilege } = detail; if (newPrivilege === AccessControlEnum.ADMINISTRATOR) { if (fromYou) { - return renderString('GroupV2--access-attributes--admins--you', i18n); + return i18n('GroupV2--access-attributes--admins--you'); } if (from) { - return renderString('GroupV2--access-attributes--admins--other', i18n, { + return i18n('GroupV2--access-attributes--admins--other', { adminName: renderContact(from), }); } - return renderString('GroupV2--access-attributes--admins--unknown', i18n); + return i18n('GroupV2--access-attributes--admins--unknown'); } if (newPrivilege === AccessControlEnum.MEMBER) { if (fromYou) { - return renderString('GroupV2--access-attributes--all--you', i18n); + return i18n('GroupV2--access-attributes--all--you'); } if (from) { - return renderString('GroupV2--access-attributes--all--other', i18n, { + return i18n('GroupV2--access-attributes--all--other', { adminName: renderContact(from), }); } - return renderString('GroupV2--access-attributes--all--unknown', i18n); + return i18n('GroupV2--access-attributes--all--unknown'); } log.warn( `access-attributes change type, privilege ${newPrivilege} is unknown` @@ -172,25 +183,25 @@ export function renderChangeDetail( if (newPrivilege === AccessControlEnum.ADMINISTRATOR) { if (fromYou) { - return renderString('GroupV2--access-members--admins--you', i18n); + return i18n('GroupV2--access-members--admins--you'); } if (from) { - return renderString('GroupV2--access-members--admins--other', i18n, { + return i18n('GroupV2--access-members--admins--other', { adminName: renderContact(from), }); } - return renderString('GroupV2--access-members--admins--unknown', i18n); + return i18n('GroupV2--access-members--admins--unknown'); } if (newPrivilege === AccessControlEnum.MEMBER) { if (fromYou) { - return renderString('GroupV2--access-members--all--you', i18n); + return i18n('GroupV2--access-members--all--you'); } if (from) { - return renderString('GroupV2--access-members--all--other', i18n, { + return i18n('GroupV2--access-members--all--other', { adminName: renderContact(from), }); } - return renderString('GroupV2--access-members--all--unknown', i18n); + return i18n('GroupV2--access-members--all--unknown'); } log.warn( `access-members change type, privilege ${newPrivilege} is unknown` @@ -202,35 +213,29 @@ export function renderChangeDetail( if (newPrivilege === AccessControlEnum.ADMINISTRATOR) { if (fromYou) { - return renderString('GroupV2--access-invite-link--enabled--you', i18n); + return i18n('GroupV2--access-invite-link--enabled--you'); } if (from) { - return renderString( + return i18n( 'GroupV2--access-invite-link--enabled--other', - i18n, + { adminName: renderContact(from) } ); } - return renderString( - 'GroupV2--access-invite-link--enabled--unknown', - i18n - ); + return i18n('GroupV2--access-invite-link--enabled--unknown'); } if (newPrivilege === AccessControlEnum.ANY) { if (fromYou) { - return renderString('GroupV2--access-invite-link--disabled--you', i18n); + return i18n('GroupV2--access-invite-link--disabled--you'); } if (from) { - return renderString( + return i18n( 'GroupV2--access-invite-link--disabled--other', - i18n, + { adminName: renderContact(from) } ); } - return renderString( - 'GroupV2--access-invite-link--disabled--unknown', - i18n - ); + return i18n('GroupV2--access-invite-link--disabled--unknown'); } log.warn( `access-invite-link change type, privilege ${newPrivilege} is unknown` @@ -243,27 +248,27 @@ export function renderChangeDetail( if (weAreJoiner) { if (fromYou) { - return renderString('GroupV2--member-add--you--you', i18n); + return i18n('GroupV2--member-add--you--you'); } if (from) { - return renderString('GroupV2--member-add--you--other', i18n, { + return i18n('GroupV2--member-add--you--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--member-add--you--unknown', i18n); + return i18n('GroupV2--member-add--you--unknown'); } if (fromYou) { - return renderString('GroupV2--member-add--other--you', i18n, { + return i18n('GroupV2--member-add--other--you', { memberName: renderContact(uuid), }); } if (from) { - return renderString('GroupV2--member-add--other--other', i18n, { + return i18n('GroupV2--member-add--other--other', { adderName: renderContact(from), addeeName: renderContact(uuid), }); } - return renderString('GroupV2--member-add--other--unknown', i18n, { + return i18n('GroupV2--member-add--other--unknown', { memberName: renderContact(uuid), }); } @@ -276,54 +281,51 @@ export function renderChangeDetail( if (weAreJoiner) { // They can't be the same, no fromYou check here if (from) { - return renderString('GroupV2--member-add--you--other', i18n, { + return i18n('GroupV2--member-add--you--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--member-add--you--unknown', i18n); + return i18n('GroupV2--member-add--you--unknown'); } if (fromYou) { - return renderString('GroupV2--member-add--invited--you', i18n, { + return i18n('GroupV2--member-add--invited--you', { inviteeName: renderContact(uuid), }); } if (from) { - return renderString('GroupV2--member-add--invited--other', i18n, { + return i18n('GroupV2--member-add--invited--other', { memberName: renderContact(from), inviteeName: renderContact(uuid), }); } - return renderString('GroupV2--member-add--invited--unknown', i18n, { + return i18n('GroupV2--member-add--invited--unknown', { inviteeName: renderContact(uuid), }); } if (weAreJoiner) { if (inviter) { - return renderString('GroupV2--member-add--from-invite--you', i18n, { + return i18n('GroupV2--member-add--from-invite--you', { inviterName: renderContact(inviter), }); } - return renderString( - 'GroupV2--member-add--from-invite--you-no-from', - i18n - ); + return i18n('GroupV2--member-add--from-invite--you-no-from'); } if (weAreInviter) { - return renderString('GroupV2--member-add--from-invite--from-you', i18n, { + return i18n('GroupV2--member-add--from-invite--from-you', { inviteeName: renderContact(uuid), }); } if (inviter) { - return renderString('GroupV2--member-add--from-invite--other', i18n, { + return i18n('GroupV2--member-add--from-invite--other', { inviteeName: renderContact(uuid), inviterName: renderContact(inviter), }); } - return renderString( + return i18n( 'GroupV2--member-add--from-invite--other-no-from', - i18n, + { inviteeName: renderContact(uuid), } @@ -333,10 +335,10 @@ export function renderChangeDetail( const { uuid } = detail; if (fromYou && isOurUuid(uuid)) { - return renderString('GroupV2--member-add-from-link--you--you', i18n); + return i18n('GroupV2--member-add-from-link--you--you'); } if (from && uuid === from) { - return renderString('GroupV2--member-add-from-link--other', i18n, { + return i18n('GroupV2--member-add-from-link--other', { memberName: renderContact(from), }); } @@ -344,7 +346,7 @@ export function renderChangeDetail( // Note: this shouldn't happen, because we only capture 'add-from-link' status // from group change events, which always have a sender. log.warn('member-add-from-link change type; we have no from!'); - return renderString('GroupV2--member-add--other--unknown', i18n, { + return i18n('GroupV2--member-add--other--unknown', { memberName: renderContact(uuid), }); } @@ -354,9 +356,9 @@ export function renderChangeDetail( if (weAreJoiner) { if (from) { - return renderString( + return i18n( 'GroupV2--member-add-from-admin-approval--you--other', - i18n, + { adminName: renderContact(from) } ); } @@ -366,23 +368,20 @@ export function renderChangeDetail( log.warn( 'member-add-from-admin-approval change type; we have no from, and we are joiner!' ); - return renderString( - 'GroupV2--member-add-from-admin-approval--you--unknown', - i18n - ); + return i18n('GroupV2--member-add-from-admin-approval--you--unknown'); } if (fromYou) { - return renderString( + return i18n( 'GroupV2--member-add-from-admin-approval--other--you', - i18n, + { joinerName: renderContact(uuid) } ); } if (from) { - return renderString( + return i18n( 'GroupV2--member-add-from-admin-approval--other--other', - i18n, + { adminName: renderContact(from), joinerName: renderContact(uuid), @@ -393,9 +392,9 @@ export function renderChangeDetail( // Note: this shouldn't happen, because we only capture 'add-from-admin-approval' // status from group change events, which always have a sender. log.warn('member-add-from-admin-approval change type; we have no from'); - return renderString( + return i18n( 'GroupV2--member-add-from-admin-approval--other--unknown', - i18n, + { joinerName: renderContact(uuid) } ); } @@ -405,33 +404,33 @@ export function renderChangeDetail( if (weAreLeaver) { if (fromYou) { - return renderString('GroupV2--member-remove--you--you', i18n); + return i18n('GroupV2--member-remove--you--you'); } if (from) { - return renderString('GroupV2--member-remove--you--other', i18n, { + return i18n('GroupV2--member-remove--you--other', { adminName: renderContact(from), }); } - return renderString('GroupV2--member-remove--you--unknown', i18n); + return i18n('GroupV2--member-remove--you--unknown'); } if (fromYou) { - return renderString('GroupV2--member-remove--other--you', i18n, { + return i18n('GroupV2--member-remove--other--you', { memberName: renderContact(uuid), }); } if (from && from === uuid) { - return renderString('GroupV2--member-remove--other--self', i18n, { + return i18n('GroupV2--member-remove--other--self', { memberName: renderContact(from), }); } if (from) { - return renderString('GroupV2--member-remove--other--other', i18n, { + return i18n('GroupV2--member-remove--other--other', { adminName: renderContact(from), memberName: renderContact(uuid), }); } - return renderString('GroupV2--member-remove--other--unknown', i18n, { + return i18n('GroupV2--member-remove--other--unknown', { memberName: renderContact(uuid), }); } @@ -442,77 +441,59 @@ export function renderChangeDetail( if (newPrivilege === RoleEnum.ADMINISTRATOR) { if (weAreMember) { if (from) { - return renderString( + return i18n( 'GroupV2--member-privilege--promote--you--other', - i18n, + { adminName: renderContact(from) } ); } - return renderString( - 'GroupV2--member-privilege--promote--you--unknown', - i18n - ); + return i18n('GroupV2--member-privilege--promote--you--unknown'); } if (fromYou) { - return renderString( - 'GroupV2--member-privilege--promote--other--you', - i18n, - { memberName: renderContact(uuid) } - ); + return i18n('GroupV2--member-privilege--promote--other--you', { + memberName: renderContact(uuid), + }); } if (from) { - return renderString( - 'GroupV2--member-privilege--promote--other--other', - i18n, - { - adminName: renderContact(from), - memberName: renderContact(uuid), - } - ); + return i18n('GroupV2--member-privilege--promote--other--other', { + adminName: renderContact(from), + memberName: renderContact(uuid), + }); } - return renderString( - 'GroupV2--member-privilege--promote--other--unknown', - i18n, - { memberName: renderContact(uuid) } - ); + return i18n('GroupV2--member-privilege--promote--other--unknown', { + memberName: renderContact(uuid), + }); } if (newPrivilege === RoleEnum.DEFAULT) { if (weAreMember) { if (from) { - return renderString( - 'GroupV2--member-privilege--demote--you--other', - i18n, - { adminName: renderContact(from) } - ); + return i18n('GroupV2--member-privilege--demote--you--other', { + adminName: renderContact(from), + }); } - return renderString( - 'GroupV2--member-privilege--demote--you--unknown', - i18n - ); + return i18n('GroupV2--member-privilege--demote--you--unknown'); } if (fromYou) { - return renderString( - 'GroupV2--member-privilege--demote--other--you', - i18n, - { memberName: renderContact(uuid) } - ); + return i18n('GroupV2--member-privilege--demote--other--you', { + memberName: renderContact(uuid), + }); } if (from) { - return renderString( + return i18n( 'GroupV2--member-privilege--demote--other--other', - i18n, + { adminName: renderContact(from), memberName: renderContact(uuid), } ); } - return renderString( + return i18n( 'GroupV2--member-privilege--demote--other--unknown', - i18n, + { memberName: renderContact(uuid) } ); } @@ -526,39 +507,39 @@ export function renderChangeDetail( const weAreInvited = isOurUuid(uuid); if (weAreInvited) { if (from) { - return renderString('GroupV2--pending-add--one--you--other', i18n, { + return i18n('GroupV2--pending-add--one--you--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--pending-add--one--you--unknown', i18n); + return i18n('GroupV2--pending-add--one--you--unknown'); } if (fromYou) { - return renderString('GroupV2--pending-add--one--other--you', i18n, { + return i18n('GroupV2--pending-add--one--other--you', { inviteeName: renderContact(uuid), }); } if (from) { - return renderString('GroupV2--pending-add--one--other--other', i18n, { + return i18n('GroupV2--pending-add--one--other--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--pending-add--one--other--unknown', i18n); + return i18n('GroupV2--pending-add--one--other--unknown'); } if (detail.type === 'pending-add-many') { const { count } = detail; if (fromYou) { - return renderString('GroupV2--pending-add--many--you', i18n, { + return i18n('GroupV2--pending-add--many--you', { count: count.toString(), }); } if (from) { - return renderString('GroupV2--pending-add--many--other', i18n, { + return i18n('GroupV2--pending-add--many--other', { memberName: renderContact(from), count: count.toString(), }); } - return renderString('GroupV2--pending-add--many--unknown', i18n, { + return i18n('GroupV2--pending-add--many--unknown', { count: count.toString(), }); } @@ -571,91 +552,91 @@ export function renderChangeDetail( if (weAreInviter) { if (sentByInvited) { - return renderString('GroupV2--pending-remove--decline--you', i18n, { + return i18n('GroupV2--pending-remove--decline--you', { inviteeName: renderContact(uuid), }); } if (fromYou) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from-you--one--you', - i18n, + { inviteeName: renderContact(uuid) } ); } if (from) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from-you--one--other', - i18n, + { adminName: renderContact(from), inviteeName: renderContact(uuid), } ); } - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from-you--one--unknown', - i18n, + { inviteeName: renderContact(uuid) } ); } if (sentByInvited) { if (fromYou) { - return renderString('GroupV2--pending-remove--decline--from-you', i18n); + return i18n('GroupV2--pending-remove--decline--from-you'); } if (inviter) { - return renderString('GroupV2--pending-remove--decline--other', i18n, { + return i18n('GroupV2--pending-remove--decline--other', { memberName: renderContact(inviter), }); } - return renderString('GroupV2--pending-remove--decline--unknown', i18n); + return i18n('GroupV2--pending-remove--decline--unknown'); } if (inviter && sentByInviter) { if (weAreInvited) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-own--to-you', - i18n, + { inviterName: renderContact(inviter) } ); } - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-own--unknown', - i18n, + { inviterName: renderContact(inviter) } ); } if (inviter) { if (fromYou) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from--one--you', - i18n, + { memberName: renderContact(inviter) } ); } if (from) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from--one--other', - i18n, + { adminName: renderContact(from), memberName: renderContact(inviter), } ); } - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from--one--unknown', - i18n, + { memberName: renderContact(inviter) } ); } if (fromYou) { - return renderString('GroupV2--pending-remove--revoke--one--you', i18n); + return i18n('GroupV2--pending-remove--revoke--one--you'); } if (from) { - return renderString('GroupV2--pending-remove--revoke--one--other', i18n, { + return i18n('GroupV2--pending-remove--revoke--one--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--pending-remove--revoke--one--unknown', i18n); + return i18n('GroupV2--pending-remove--revoke--one--unknown'); } if (detail.type === 'pending-remove-many') { const { count, inviter } = detail; @@ -663,33 +644,33 @@ export function renderChangeDetail( if (weAreInviter) { if (fromYou) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from-you--many--you', - i18n, + { count: count.toString() } ); } if (from) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from-you--many--other', - i18n, + { adminName: renderContact(from), count: count.toString(), } ); } - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from-you--many--unknown', - i18n, + { count: count.toString() } ); } if (inviter) { if (fromYou) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from--many--you', - i18n, + { count: count.toString(), memberName: renderContact(inviter), @@ -697,9 +678,9 @@ export function renderChangeDetail( ); } if (from) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from--many--other', - i18n, + { adminName: renderContact(from), count: count.toString(), @@ -707,9 +688,9 @@ export function renderChangeDetail( } ); } - return renderString( + return i18n( 'GroupV2--pending-remove--revoke-invite-from--many--unknown', - i18n, + { count: count.toString(), memberName: renderContact(inviter), @@ -717,23 +698,23 @@ export function renderChangeDetail( ); } if (fromYou) { - return renderString('GroupV2--pending-remove--revoke--many--you', i18n, { + return i18n('GroupV2--pending-remove--revoke--many--you', { count: count.toString(), }); } if (from) { - return renderString( + return i18n( 'GroupV2--pending-remove--revoke--many--other', - i18n, + { memberName: renderContact(from), count: count.toString(), } ); } - return renderString( + return i18n( 'GroupV2--pending-remove--revoke--many--unknown', - i18n, + { count: count.toString() } ); } @@ -742,9 +723,9 @@ export function renderChangeDetail( const weAreJoiner = isOurUuid(uuid); if (weAreJoiner) { - return renderString('GroupV2--admin-approval-add-one--you', i18n); + return i18n('GroupV2--admin-approval-add-one--you'); } - return renderString('GroupV2--admin-approval-add-one--other', i18n, { + return i18n('GroupV2--admin-approval-add-one--other', { joinerName: renderContact(uuid), }); } @@ -754,35 +735,29 @@ export function renderChangeDetail( if (weAreJoiner) { if (fromYou) { - return renderString( - 'GroupV2--admin-approval-remove-one--you--you', - i18n - ); + return i18n('GroupV2--admin-approval-remove-one--you--you'); } - return renderString( - 'GroupV2--admin-approval-remove-one--you--unknown', - i18n - ); + return i18n('GroupV2--admin-approval-remove-one--you--unknown'); } if (fromYou) { - return renderString( + return i18n( 'GroupV2--admin-approval-remove-one--other--you', - i18n, + { joinerName: renderContact(uuid) } ); } if (from && from === uuid) { - return renderString( + return i18n( 'GroupV2--admin-approval-remove-one--other--own', - i18n, + { joinerName: renderContact(uuid) } ); } if (from) { - return renderString( + return i18n( 'GroupV2--admin-approval-remove-one--other--other', - i18n, + { adminName: renderContact(from), joinerName: renderContact(uuid), @@ -792,9 +767,9 @@ export function renderChangeDetail( // We default to the user canceling their request, because it is far more likely that // if an admin does the denial, we'll get a change event from them. - return renderString( + return i18n( 'GroupV2--admin-approval-remove-one--other--own', - i18n, + { joinerName: renderContact(uuid) } ); } @@ -803,11 +778,11 @@ export function renderChangeDetail( let firstMessage: T | string; if (times === 1) { - firstMessage = renderString('GroupV2--admin-approval-bounce--one', i18n, { + firstMessage = i18n('GroupV2--admin-approval-bounce--one', { joinerName: renderContact(uuid), }); } else { - firstMessage = renderString('GroupV2--admin-approval-bounce', i18n, { + firstMessage = i18n('GroupV2--admin-approval-bounce', { joinerName: renderContact(uuid), numberOfRequests: String(times), }); @@ -835,99 +810,99 @@ export function renderChangeDetail( if (privilege === AccessControlEnum.ADMINISTRATOR) { if (fromYou) { - return renderString('GroupV2--group-link-add--enabled--you', i18n); + return i18n('GroupV2--group-link-add--enabled--you'); } if (from) { - return renderString('GroupV2--group-link-add--enabled--other', i18n, { + return i18n('GroupV2--group-link-add--enabled--other', { adminName: renderContact(from), }); } - return renderString('GroupV2--group-link-add--enabled--unknown', i18n); + return i18n('GroupV2--group-link-add--enabled--unknown'); } if (privilege === AccessControlEnum.ANY) { if (fromYou) { - return renderString('GroupV2--group-link-add--disabled--you', i18n); + return i18n('GroupV2--group-link-add--disabled--you'); } if (from) { - return renderString('GroupV2--group-link-add--disabled--other', i18n, { + return i18n('GroupV2--group-link-add--disabled--other', { adminName: renderContact(from), }); } - return renderString('GroupV2--group-link-add--disabled--unknown', i18n); + return i18n('GroupV2--group-link-add--disabled--unknown'); } log.warn(`group-link-add change type, privilege ${privilege} is unknown`); return ''; } if (detail.type === 'group-link-reset') { if (fromYou) { - return renderString('GroupV2--group-link-reset--you', i18n); + return i18n('GroupV2--group-link-reset--you'); } if (from) { - return renderString('GroupV2--group-link-reset--other', i18n, { + return i18n('GroupV2--group-link-reset--other', { adminName: renderContact(from), }); } - return renderString('GroupV2--group-link-reset--unknown', i18n); + return i18n('GroupV2--group-link-reset--unknown'); } if (detail.type === 'group-link-remove') { if (fromYou) { - return renderString('GroupV2--group-link-remove--you', i18n); + return i18n('GroupV2--group-link-remove--you'); } if (from) { - return renderString('GroupV2--group-link-remove--other', i18n, { + return i18n('GroupV2--group-link-remove--other', { adminName: renderContact(from), }); } - return renderString('GroupV2--group-link-remove--unknown', i18n); + return i18n('GroupV2--group-link-remove--unknown'); } if (detail.type === 'description') { if (detail.removed) { if (fromYou) { - return renderString('GroupV2--description--remove--you', i18n); + return i18n('GroupV2--description--remove--you'); } if (from) { - return renderString('GroupV2--description--remove--other', i18n, { + return i18n('GroupV2--description--remove--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--description--remove--unknown', i18n); + return i18n('GroupV2--description--remove--unknown'); } if (fromYou) { - return renderString('GroupV2--description--change--you', i18n); + return i18n('GroupV2--description--change--you'); } if (from) { - return renderString('GroupV2--description--change--other', i18n, { + return i18n('GroupV2--description--change--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--description--change--unknown', i18n); + return i18n('GroupV2--description--change--unknown'); } if (detail.type === 'announcements-only') { if (detail.announcementsOnly) { if (fromYou) { - return renderString('GroupV2--announcements--admin--you', i18n); + return i18n('GroupV2--announcements--admin--you'); } if (from) { - return renderString('GroupV2--announcements--admin--other', i18n, { + return i18n('GroupV2--announcements--admin--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--announcements--admin--unknown', i18n); + return i18n('GroupV2--announcements--admin--unknown'); } if (fromYou) { - return renderString('GroupV2--announcements--member--you', i18n); + return i18n('GroupV2--announcements--member--you'); } if (from) { - return renderString('GroupV2--announcements--member--other', i18n, { + return i18n('GroupV2--announcements--member--other', { memberName: renderContact(from), }); } - return renderString('GroupV2--announcements--member--unknown', i18n); + return i18n('GroupV2--announcements--member--unknown'); } if (detail.type === 'summary') { - return renderString('icu:GroupV2--summary', i18n); + return i18n('icu:GroupV2--summary'); } throw missingCaseError(detail); diff --git a/ts/models/messages.ts b/ts/models/messages.ts index d8657c170..c2f8eb059 100644 --- a/ts/models/messages.ts +++ b/ts/models/messages.ts @@ -511,7 +511,10 @@ export class MessageModel extends window.Backbone.Model { key: string, _i18n: unknown, components: ReplacementValuesType | undefined - ) => window.i18n(key, components), + ) => { + // eslint-disable-next-line local-rules/valid-i18n-keys + return window.i18n(key, components); + }, }); return { text: changes.map(({ text }) => text).join(' ') }; diff --git a/ts/test-node/components/leftPane/LeftPaneChooseGroupMembersHelper_test.ts b/ts/test-node/components/leftPane/LeftPaneChooseGroupMembersHelper_test.ts index c51ef48db..fbf530c96 100644 --- a/ts/test-node/components/leftPane/LeftPaneChooseGroupMembersHelper_test.ts +++ b/ts/test-node/components/leftPane/LeftPaneChooseGroupMembersHelper_test.ts @@ -4,7 +4,7 @@ import { assert } from 'chai'; import * as sinon from 'sinon'; import { times } from 'lodash'; -import { RowType } from '../../../components/ConversationList'; +import { RowType, _testHeaderText } from '../../../components/ConversationList'; import { ContactCheckboxDisabledReason } from '../../../components/conversationList/ContactCheckbox'; import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; @@ -117,10 +117,7 @@ describe('LeftPaneChooseGroupMembersHelper', () => { selectedContacts: [candidateContacts[1]], }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'contactsHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(0)), 'contactsHeader'); assert.deepEqual(helper.getRow(1), { type: RowType.ContactCheckbox, contact: candidateContacts[0], @@ -167,10 +164,10 @@ describe('LeftPaneChooseGroupMembersHelper', () => { selectedContacts: [], }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'findByPhoneNumberHeader', - }); + assert.deepEqual( + _testHeaderText(helper.getRow(0)), + 'findByPhoneNumberHeader' + ); assert.deepEqual(helper.getRow(1), { type: RowType.PhoneNumberCheckbox, phoneNumber: { @@ -192,10 +189,10 @@ describe('LeftPaneChooseGroupMembersHelper', () => { selectedContacts: [], }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'findByUsernameHeader', - }); + assert.deepEqual( + _testHeaderText(helper.getRow(0)), + 'findByUsernameHeader' + ); assert.deepEqual(helper.getRow(1), { type: RowType.UsernameCheckbox, username: 'signal', diff --git a/ts/test-node/components/leftPane/LeftPaneComposeHelper_test.ts b/ts/test-node/components/leftPane/LeftPaneComposeHelper_test.ts index ab1c3eebb..eef559244 100644 --- a/ts/test-node/components/leftPane/LeftPaneComposeHelper_test.ts +++ b/ts/test-node/components/leftPane/LeftPaneComposeHelper_test.ts @@ -3,7 +3,7 @@ import { assert } from 'chai'; import * as sinon from 'sinon'; -import { RowType } from '../../../components/ConversationList'; +import { RowType, _testHeaderText } from '../../../components/ConversationList'; import { FindDirection } from '../../../components/leftPane/LeftPaneHelper'; import { getDefaultConversation, @@ -223,10 +223,7 @@ describe('LeftPaneComposeHelper', () => { assert.deepEqual(helper.getRow(0), { type: RowType.CreateNewGroup, }); - assert.deepEqual(helper.getRow(1), { - type: RowType.Header, - i18nKey: 'contactsHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(1)), 'contactsHeader'); assert.deepEqual(helper.getRow(2), { type: RowType.Contact, contact: composeContacts[0], @@ -258,11 +255,7 @@ describe('LeftPaneComposeHelper', () => { assert.deepEqual(helper.getRow(0), { type: RowType.CreateNewGroup, }); - - assert.deepEqual(helper.getRow(1), { - type: RowType.Header, - i18nKey: 'contactsHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(1)), 'contactsHeader'); assert.deepEqual(helper.getRow(2), { type: RowType.Contact, contact: composeContacts[0], @@ -271,10 +264,7 @@ describe('LeftPaneComposeHelper', () => { type: RowType.Contact, contact: composeContacts[1], }); - assert.deepEqual(helper.getRow(4), { - type: RowType.Header, - i18nKey: 'groupsHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(4)), 'groupsHeader'); assert.deepEqual(helper.getRow(5), { type: RowType.SelectSingleGroup, group: composeGroups[0], @@ -333,10 +323,10 @@ describe('LeftPaneComposeHelper', () => { uuidFetchState: {}, }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'findByPhoneNumberHeader', - }); + assert.deepEqual( + _testHeaderText(helper.getRow(0)), + 'findByPhoneNumberHeader' + ); assert.deepEqual(helper.getRow(1), { type: RowType.StartNewConversation, phoneNumber: { @@ -363,10 +353,10 @@ describe('LeftPaneComposeHelper', () => { }, }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'findByUsernameHeader', - }); + assert.deepEqual( + _testHeaderText(helper.getRow(0)), + 'findByUsernameHeader' + ); assert.deepEqual(helper.getRow(1), { type: RowType.UsernameSearchResult, username, @@ -389,10 +379,7 @@ describe('LeftPaneComposeHelper', () => { uuidFetchState: {}, }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'contactsHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(0)), 'contactsHeader'); assert.deepEqual(helper.getRow(1), { type: RowType.Contact, contact: composeContacts[0], @@ -401,10 +388,10 @@ describe('LeftPaneComposeHelper', () => { type: RowType.Contact, contact: composeContacts[1], }); - assert.deepEqual(helper.getRow(3), { - type: RowType.Header, - i18nKey: 'findByPhoneNumberHeader', - }); + assert.deepEqual( + _testHeaderText(helper.getRow(3)), + 'findByPhoneNumberHeader' + ); assert.deepEqual(helper.getRow(4), { type: RowType.StartNewConversation, phoneNumber: { diff --git a/ts/test-node/components/leftPane/LeftPaneInboxHelper_test.tsx b/ts/test-node/components/leftPane/LeftPaneInboxHelper_test.tsx index 4e6a11a8c..67c79de0b 100644 --- a/ts/test-node/components/leftPane/LeftPaneInboxHelper_test.tsx +++ b/ts/test-node/components/leftPane/LeftPaneInboxHelper_test.tsx @@ -3,7 +3,7 @@ import { assert } from 'chai'; import * as sinon from 'sinon'; -import { RowType } from '../../../components/ConversationList'; +import { RowType, _testHeaderText } from '../../../components/ConversationList'; import { FindDirection } from '../../../components/leftPane/LeftPaneHelper'; import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; @@ -339,10 +339,7 @@ describe('LeftPaneInboxHelper', () => { pinnedConversations, }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'LeftPane--pinned', - }); + assert.deepEqual(_testHeaderText(helper.getRow(0)), 'LeftPane--pinned'); assert.deepEqual(helper.getRow(1), { type: RowType.Conversation, conversation: pinnedConversations[0], @@ -351,10 +348,7 @@ describe('LeftPaneInboxHelper', () => { type: RowType.Conversation, conversation: pinnedConversations[1], }); - assert.deepEqual(helper.getRow(3), { - type: RowType.Header, - i18nKey: 'LeftPane--chats', - }); + assert.deepEqual(_testHeaderText(helper.getRow(3)), 'LeftPane--chats'); assert.deepEqual(helper.getRow(4), { type: RowType.Conversation, conversation: conversations[0], @@ -388,10 +382,7 @@ describe('LeftPaneInboxHelper', () => { archivedConversations: [getDefaultConversation()], }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'LeftPane--pinned', - }); + assert.deepEqual(_testHeaderText(helper.getRow(0)), 'LeftPane--pinned'); assert.deepEqual(helper.getRow(1), { type: RowType.Conversation, conversation: pinnedConversations[0], @@ -400,10 +391,7 @@ describe('LeftPaneInboxHelper', () => { type: RowType.Conversation, conversation: pinnedConversations[1], }); - assert.deepEqual(helper.getRow(3), { - type: RowType.Header, - i18nKey: 'LeftPane--chats', - }); + assert.deepEqual(_testHeaderText(helper.getRow(3)), 'LeftPane--chats'); assert.deepEqual(helper.getRow(4), { type: RowType.Conversation, conversation: conversations[0], diff --git a/ts/test-node/components/leftPane/LeftPaneSearchHelper_test.ts b/ts/test-node/components/leftPane/LeftPaneSearchHelper_test.ts index 50d511cf7..7ed32d85f 100644 --- a/ts/test-node/components/leftPane/LeftPaneSearchHelper_test.ts +++ b/ts/test-node/components/leftPane/LeftPaneSearchHelper_test.ts @@ -4,7 +4,7 @@ import { assert } from 'chai'; import * as sinon from 'sinon'; import { v4 as uuid } from 'uuid'; -import { RowType } from '../../../components/ConversationList'; +import { RowType, _testHeaderText } from '../../../components/ConversationList'; import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; import { LeftPaneSearchHelper } from '../../../components/leftPane/LeftPaneSearchHelper'; @@ -192,10 +192,10 @@ describe('LeftPaneSearchHelper', () => { startSearchCounter: 0, }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'conversationsHeader', - }); + assert.deepEqual( + _testHeaderText(helper.getRow(0)), + 'conversationsHeader' + ); assert.deepEqual(helper.getRow(1), { type: RowType.Conversation, conversation: conversations[0], @@ -204,18 +204,12 @@ describe('LeftPaneSearchHelper', () => { type: RowType.Conversation, conversation: conversations[1], }); - assert.deepEqual(helper.getRow(3), { - type: RowType.Header, - i18nKey: 'contactsHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(3)), 'contactsHeader'); assert.deepEqual(helper.getRow(4), { type: RowType.Conversation, conversation: contacts[0], }); - assert.deepEqual(helper.getRow(5), { - type: RowType.Header, - i18nKey: 'messagesHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(5)), 'messagesHeader'); assert.deepEqual(helper.getRow(6), { type: RowType.MessageSearchResult, messageId: messages[0].id, @@ -244,18 +238,12 @@ describe('LeftPaneSearchHelper', () => { startSearchCounter: 0, }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'contactsHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(0)), 'contactsHeader'); assert.deepEqual(helper.getRow(1), { type: RowType.Conversation, conversation: contacts[0], }); - assert.deepEqual(helper.getRow(2), { - type: RowType.Header, - i18nKey: 'messagesHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(2)), 'messagesHeader'); assert.deepEqual(helper.getRow(3), { type: RowType.MessageSearchResult, messageId: messages[0].id, @@ -287,10 +275,10 @@ describe('LeftPaneSearchHelper', () => { startSearchCounter: 0, }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'conversationsHeader', - }); + assert.deepEqual( + _testHeaderText(helper.getRow(0)), + 'conversationsHeader' + ); assert.deepEqual(helper.getRow(1), { type: RowType.Conversation, conversation: conversations[0], @@ -299,10 +287,7 @@ describe('LeftPaneSearchHelper', () => { type: RowType.Conversation, conversation: conversations[1], }); - assert.deepEqual(helper.getRow(3), { - type: RowType.Header, - i18nKey: 'messagesHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(3)), 'messagesHeader'); assert.deepEqual(helper.getRow(4), { type: RowType.MessageSearchResult, messageId: messages[0].id, @@ -332,10 +317,7 @@ describe('LeftPaneSearchHelper', () => { startSearchCounter: 0, }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'conversationsHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(0)), 'conversationsHeader'); assert.deepEqual(helper.getRow(1), { type: RowType.Conversation, conversation: conversations[0], @@ -344,10 +326,7 @@ describe('LeftPaneSearchHelper', () => { type: RowType.Conversation, conversation: conversations[1], }); - assert.deepEqual(helper.getRow(3), { - type: RowType.Header, - i18nKey: 'contactsHeader', - }); + assert.deepEqual(_testHeaderText(helper.getRow(3)), 'contactsHeader'); assert.deepEqual(helper.getRow(4), { type: RowType.Conversation, conversation: contacts[0], diff --git a/ts/test-node/components/leftPane/LeftPaneSetGroupMetadataHelper_test.ts b/ts/test-node/components/leftPane/LeftPaneSetGroupMetadataHelper_test.ts index 56ff19a5d..ddcdd4fc4 100644 --- a/ts/test-node/components/leftPane/LeftPaneSetGroupMetadataHelper_test.ts +++ b/ts/test-node/components/leftPane/LeftPaneSetGroupMetadataHelper_test.ts @@ -3,7 +3,7 @@ import { assert } from 'chai'; import * as sinon from 'sinon'; -import { RowType } from '../../../components/ConversationList'; +import { RowType, _testHeaderText } from '../../../components/ConversationList'; import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; import { DurationInSeconds } from '../../../util/durations'; @@ -92,10 +92,10 @@ describe('LeftPaneSetGroupMetadataHelper', () => { selectedContacts, }); - assert.deepEqual(helper.getRow(0), { - type: RowType.Header, - i18nKey: 'setGroupMetadata__members-header', - }); + assert.deepEqual( + _testHeaderText(helper.getRow(0)), + 'setGroupMetadata__members-header' + ); assert.deepEqual(helper.getRow(1), { type: RowType.Contact, contact: selectedContacts[0],