Normalize i18n() calls to prepare for ICU migration

This commit is contained in:
Jamie Kyle 2023-03-28 11:26:46 -07:00 committed by GitHub
parent 7c8e7c1013
commit c02c8d9640
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 377 additions and 273 deletions

View file

@ -213,6 +213,16 @@ const typescriptRules = {
'@typescript-eslint/consistent-type-imports': 'error', '@typescript-eslint/consistent-type-imports': 'error',
// Future: Maybe switch to never and always use `satisfies`
'@typescript-eslint/consistent-type-assertions': [
'error',
{
assertionStyle: 'as',
// Future: Maybe switch to allow-as-parameter or never
objectLiteralTypeAssertions: 'allow',
},
],
// Already enforced by TypeScript // Already enforced by TypeScript
'consistent-return': 'off', 'consistent-return': 'off',

View file

@ -1404,7 +1404,7 @@ export class SignalProtocolStore extends EventEmitter {
const sessionResets = window.storage.get( const sessionResets = window.storage.get(
'sessionResets', 'sessionResets',
<SessionResetsType>{} {} as SessionResetsType
); );
const lastReset = sessionResets[id]; const lastReset = sessionResets[id];

View file

@ -184,7 +184,7 @@ export function isOverHourIntoPast(timestamp: number): boolean {
export async function cleanupSessionResets(): Promise<void> { export async function cleanupSessionResets(): Promise<void> {
const sessionResets = window.storage.get( const sessionResets = window.storage.get(
'sessionResets', 'sessionResets',
<SessionResetsType>{} {} as SessionResetsType
); );
const keys = Object.keys(sessionResets); const keys = Object.keys(sessionResets);

View file

@ -21,9 +21,11 @@ export type PropsType = Readonly<{
onClose: () => void; onClose: () => void;
}>; }>;
const UNITS = ['seconds', 'minutes', 'hours', 'days', 'weeks']; const UNITS = ['seconds', 'minutes', 'hours', 'days', 'weeks'] as const;
const UNIT_TO_SEC = new Map<string, number>([ export type Unit = typeof UNITS[number];
const UNIT_TO_SEC = new Map<Unit, number>([
['seconds', 1], ['seconds', 1],
['minutes', 60], ['minutes', 60],
['hours', 60 * 60], ['hours', 60 * 60],
@ -31,7 +33,7 @@ const UNIT_TO_SEC = new Map<string, number>([
['weeks', 7 * 24 * 60 * 60], ['weeks', 7 * 24 * 60 * 60],
]); ]);
const RANGES = new Map<string, [number, number]>([ const RANGES = new Map<Unit, [number, number]>([
['seconds', [1, 60]], ['seconds', [1, 60]],
['minutes', [1, 60]], ['minutes', [1, 60]],
['hours', [1, 24]], ['hours', [1, 24]],
@ -48,7 +50,7 @@ export function DisappearingTimeDialog(props: PropsType): JSX.Element {
onClose, onClose,
} = props; } = props;
let initialUnit = 'seconds'; let initialUnit: Unit = 'seconds';
let initialUnitValue = 1; let initialUnitValue = 1;
for (const unit of UNITS) { for (const unit of UNITS) {
const sec = UNIT_TO_SEC.get(unit) || 1; const sec = UNIT_TO_SEC.get(unit) || 1;
@ -62,7 +64,7 @@ export function DisappearingTimeDialog(props: PropsType): JSX.Element {
} }
const [unitValue, setUnitValue] = useState(initialUnitValue); const [unitValue, setUnitValue] = useState(initialUnitValue);
const [unit, setUnit] = useState(initialUnit); const [unit, setUnit] = useState<Unit>(initialUnit);
const range = RANGES.get(unit) || [1, 1]; const range = RANGES.get(unit) || [1, 1];
@ -108,9 +110,9 @@ export function DisappearingTimeDialog(props: PropsType): JSX.Element {
moduleClassName={`${CSS_MODULE}__time-boxes__units`} moduleClassName={`${CSS_MODULE}__time-boxes__units`}
value={unit} value={unit}
onChange={newUnit => { onChange={newUnit => {
setUnit(newUnit); setUnit(newUnit as Unit);
const ranges = RANGES.get(newUnit); const ranges = RANGES.get(newUnit as Unit);
if (!ranges) { if (!ranges) {
return; return;
} }
@ -121,7 +123,13 @@ export function DisappearingTimeDialog(props: PropsType): JSX.Element {
options={UNITS.map(unitName => { options={UNITS.map(unitName => {
return { return {
value: unitName, value: unitName,
text: i18n(`DisappearingTimeDialog__${unitName}`), text: {
seconds: i18n('DisappearingTimeDialog__seconds'),
minutes: i18n('DisappearingTimeDialog__minutes'),
hours: i18n('DisappearingTimeDialog__hours'),
days: i18n('DisappearingTimeDialog__days'),
weeks: i18n('DisappearingTimeDialog__weeks'),
}[unitName],
}; };
})} })}
/> />

View file

@ -177,9 +177,11 @@ function OverflowAreaScrollMarker({
type="button" type="button"
className={`${baseClassName}__button`} className={`${baseClassName}__button`}
onClick={onClick} onClick={onClick}
aria-label={i18n( aria-label={
`calling__overflow__scroll-${placement === 'top' ? 'up' : 'down'}` placement === 'top'
)} ? i18n('calling__overflow__scroll-up')
: i18n('calling__overflow__scroll-down')
}
/> />
</div> </div>
); );

View file

@ -96,28 +96,30 @@ type DefaultBio = {
shortName: string; shortName: string;
}; };
const DEFAULT_BIOS: Array<DefaultBio> = [ function getDefaultBios(i18n: LocalizerType): Array<DefaultBio> {
{ return [
i18nLabel: 'Bio--speak-freely', {
shortName: 'wave', i18nLabel: i18n('Bio--speak-freely'),
}, shortName: 'wave',
{ },
i18nLabel: 'Bio--encrypted', {
shortName: 'zipper_mouth_face', i18nLabel: i18n('Bio--encrypted'),
}, shortName: 'zipper_mouth_face',
{ },
i18nLabel: 'Bio--free-to-chat', {
shortName: '+1', i18nLabel: i18n('Bio--free-to-chat'),
}, shortName: '+1',
{ },
i18nLabel: 'Bio--coffee-lover', {
shortName: 'coffee', i18nLabel: i18n('Bio--coffee-lover'),
}, shortName: 'coffee',
{ },
i18nLabel: 'Bio--taking-break', {
shortName: 'mobile_phone_off', i18nLabel: i18n('Bio--taking-break'),
}, shortName: 'mobile_phone_off',
]; },
];
}
export function ProfileEditor({ export function ProfileEditor({
aboutEmoji, aboutEmoji,
@ -371,6 +373,8 @@ export function ProfileEditor({
(stagedProfile.aboutText === fullBio.aboutText && (stagedProfile.aboutText === fullBio.aboutText &&
stagedProfile.aboutEmoji === fullBio.aboutEmoji); stagedProfile.aboutEmoji === fullBio.aboutEmoji);
const defaultBios = getDefaultBios(i18n);
content = ( content = (
<> <>
<Input <Input
@ -415,7 +419,7 @@ export function ProfileEditor({
whenToShowRemainingCount={40} whenToShowRemainingCount={40}
/> />
{DEFAULT_BIOS.map(defaultBio => ( {defaultBios.map(defaultBio => (
<PanelRow <PanelRow
className="ProfileEditor__row" className="ProfileEditor__row"
key={defaultBio.shortName} key={defaultBio.shortName}
@ -424,16 +428,14 @@ export function ProfileEditor({
<Emoji shortName={defaultBio.shortName} size={24} /> <Emoji shortName={defaultBio.shortName} size={24} />
</div> </div>
} }
// eslint-disable-next-line local-rules/valid-i18n-keys label={defaultBio.i18nLabel}
label={i18n(defaultBio.i18nLabel)}
onClick={() => { onClick={() => {
const emojiData = getEmojiData(defaultBio.shortName, skinTone); const emojiData = getEmojiData(defaultBio.shortName, skinTone);
setStagedProfile(profileData => ({ setStagedProfile(profileData => ({
...profileData, ...profileData,
aboutEmoji: unifiedToEmoji(emojiData.unified), aboutEmoji: unifiedToEmoji(emojiData.unified),
// eslint-disable-next-line local-rules/valid-i18n-keys aboutText: defaultBio.i18nLabel,
aboutText: i18n(defaultBio.i18nLabel),
})); }));
}} }}
/> />

View file

@ -44,191 +44,241 @@ type KeyType =
| 'Y' | 'Y'
| '1 to 9'; | '1 to 9';
type ShortcutType = { type ShortcutType = {
id: string;
description: string; description: string;
keys: Array<Array<KeyType>>; keys: Array<Array<KeyType>>;
}; };
const NAVIGATION_SHORTCUTS: Array<ShortcutType> = [ function getNavigationShortcuts(i18n: LocalizerType): Array<ShortcutType> {
{ return [
description: 'Keyboard--navigate-by-section', {
keys: [['commandOrCtrl', 'T']], id: 'Keyboard--navigate-by-section',
}, description: i18n('Keyboard--navigate-by-section'),
{ keys: [['commandOrCtrl', 'T']],
description: 'Keyboard--previous-conversation', },
keys: [ {
['optionOrAlt', '↑'], id: 'Keyboard--previous-conversation',
['ctrl', 'shift', 'tab'], description: i18n('Keyboard--previous-conversation'),
], keys: [
}, ['optionOrAlt', '↑'],
{ ['ctrl', 'shift', 'tab'],
description: 'Keyboard--next-conversation', ],
keys: [ },
['optionOrAlt', '↓'], {
['ctrl', 'tab'], id: 'Keyboard--next-conversation',
], description: i18n('Keyboard--next-conversation'),
}, keys: [
{ ['optionOrAlt', '↓'],
description: 'Keyboard--previous-unread-conversation', ['ctrl', 'tab'],
keys: [['optionOrAlt', 'shift', '↑']], ],
}, },
{ {
description: 'Keyboard--next-unread-conversation', id: 'Keyboard--previous-unread-conversation',
keys: [['optionOrAlt', 'shift', '↓']], description: i18n('Keyboard--previous-unread-conversation'),
}, keys: [['optionOrAlt', 'shift', '↑']],
{ },
description: 'Keyboard--conversation-by-index', {
keys: [['commandOrCtrl', '1 to 9']], id: 'Keyboard--next-unread-conversation',
}, description: i18n('Keyboard--next-unread-conversation'),
{ keys: [['optionOrAlt', 'shift', '↓']],
description: 'Keyboard--preferences', },
keys: [['commandOrCtrl', ',']], {
}, id: 'Keyboard--conversation-by-index',
{ description: i18n('Keyboard--conversation-by-index'),
description: 'Keyboard--open-conversation-menu', keys: [['commandOrCtrl', '1 to 9']],
keys: [['commandOrCtrl', 'shift', 'L']], },
}, {
{ id: 'Keyboard--preferences',
description: 'Keyboard--new-conversation', description: i18n('Keyboard--preferences'),
keys: [['commandOrCtrl', 'N']], keys: [['commandOrCtrl', ',']],
}, },
{ {
description: 'Keyboard--search', id: 'Keyboard--open-conversation-menu',
keys: [['commandOrCtrl', 'F']], description: i18n('Keyboard--open-conversation-menu'),
}, keys: [['commandOrCtrl', 'shift', 'L']],
{ },
description: 'Keyboard--search-in-conversation', {
keys: [['commandOrCtrl', 'shift', 'F']], id: 'Keyboard--new-conversation',
}, description: i18n('Keyboard--new-conversation'),
{ keys: [['commandOrCtrl', 'N']],
description: 'Keyboard--focus-composer', },
keys: [['commandOrCtrl', 'shift', 'T']], {
}, id: 'Keyboard--search',
{ description: i18n('Keyboard--search'),
description: 'Keyboard--open-all-media-view', keys: [['commandOrCtrl', 'F']],
keys: [['commandOrCtrl', 'shift', 'M']], },
}, {
{ id: 'Keyboard--search-in-conversation',
description: 'Keyboard--open-emoji-chooser', description: i18n('Keyboard--search-in-conversation'),
keys: [['commandOrCtrl', 'shift', 'J']], keys: [['commandOrCtrl', 'shift', 'F']],
}, },
{ {
description: 'Keyboard--open-sticker-chooser', id: 'Keyboard--focus-composer',
keys: [['commandOrCtrl', 'shift', 'S']], description: i18n('Keyboard--focus-composer'),
}, keys: [['commandOrCtrl', 'shift', 'T']],
{ },
description: 'Keyboard--begin-recording-voice-note', {
keys: [['commandOrCtrl', 'shift', 'V']], id: 'Keyboard--open-all-media-view',
}, description: i18n('Keyboard--open-all-media-view'),
{ keys: [['commandOrCtrl', 'shift', 'M']],
description: 'Keyboard--archive-conversation', },
keys: [['commandOrCtrl', 'shift', 'A']], {
}, id: 'Keyboard--open-emoji-chooser',
{ description: i18n('Keyboard--open-emoji-chooser'),
description: 'Keyboard--unarchive-conversation', keys: [['commandOrCtrl', 'shift', 'J']],
keys: [['commandOrCtrl', 'shift', 'U']], },
}, {
{ id: 'Keyboard--open-sticker-chooser',
description: 'Keyboard--scroll-to-top', description: i18n('Keyboard--open-sticker-chooser'),
keys: [['commandOrCtrl', '↑']], keys: [['commandOrCtrl', 'shift', 'S']],
}, },
{ {
description: 'Keyboard--scroll-to-bottom', id: 'Keyboard--begin-recording-voice-note',
keys: [['commandOrCtrl', '↓']], description: i18n('Keyboard--begin-recording-voice-note'),
}, keys: [['commandOrCtrl', 'shift', 'V']],
{ },
description: 'Keyboard--close-curent-conversation', {
keys: [['commandOrCtrl', 'shift', 'C']], id: 'Keyboard--archive-conversation',
}, description: i18n('Keyboard--archive-conversation'),
]; keys: [['commandOrCtrl', 'shift', 'A']],
},
{
id: 'Keyboard--unarchive-conversation',
description: i18n('Keyboard--unarchive-conversation'),
keys: [['commandOrCtrl', 'shift', 'U']],
},
{
id: 'Keyboard--scroll-to-top',
description: i18n('Keyboard--scroll-to-top'),
keys: [['commandOrCtrl', '↑']],
},
{
id: 'Keyboard--scroll-to-bottom',
description: i18n('Keyboard--scroll-to-bottom'),
keys: [['commandOrCtrl', '↓']],
},
{
id: 'Keyboard--close-curent-conversation',
description: i18n('Keyboard--close-curent-conversation'),
keys: [['commandOrCtrl', 'shift', 'C']],
},
];
}
const MESSAGE_SHORTCUTS: Array<ShortcutType> = [ function getMessageShortcuts(i18n: LocalizerType): Array<ShortcutType> {
{ return [
description: 'Keyboard--default-message-action', {
keys: [['enter']], id: 'Keyboard--default-message-action',
}, description: i18n('Keyboard--default-message-action'),
{ keys: [['enter']],
description: 'Keyboard--view-details-for-selected-message', },
keys: [['commandOrCtrl', 'D']], {
}, id: 'Keyboard--view-details-for-selected-message',
{ description: i18n('Keyboard--view-details-for-selected-message'),
description: 'Keyboard--toggle-reply', keys: [['commandOrCtrl', 'D']],
keys: [['commandOrCtrl', 'shift', 'R']], },
}, {
{ id: 'Keyboard--toggle-reply',
description: 'Keyboard--toggle-reaction-picker', description: i18n('Keyboard--toggle-reply'),
keys: [['commandOrCtrl', 'shift', 'E']], keys: [['commandOrCtrl', 'shift', 'R']],
}, },
{ {
description: 'Keyboard--save-attachment', id: 'Keyboard--toggle-reaction-picker',
keys: [['commandOrCtrl', 'S']], description: i18n('Keyboard--toggle-reaction-picker'),
}, keys: [['commandOrCtrl', 'shift', 'E']],
{ },
description: 'Keyboard--delete-messages', {
keys: [['commandOrCtrl', 'shift', 'D']], id: 'Keyboard--save-attachment',
}, description: i18n('Keyboard--save-attachment'),
]; keys: [['commandOrCtrl', 'S']],
},
{
id: 'Keyboard--delete-message',
description: i18n('Keyboard--delete-message'),
keys: [['commandOrCtrl', 'shift', 'D']],
},
];
}
const COMPOSER_SHORTCUTS: Array<ShortcutType> = [ function getComposerShortcuts(i18n: LocalizerType): Array<ShortcutType> {
{ return [
description: 'Keyboard--add-newline', {
keys: [['shift', 'enter']], id: 'Keyboard--add-newline',
}, description: i18n('Keyboard--add-newline'),
{ keys: [['shift', 'enter']],
description: 'Keyboard--expand-composer', },
keys: [['commandOrCtrl', 'shift', 'X']], {
}, id: 'Keyboard--expand-composer',
{ description: i18n('Keyboard--expand-composer'),
description: 'Keyboard--send-in-expanded-composer', keys: [['commandOrCtrl', 'shift', 'X']],
keys: [['commandOrCtrl', 'enter']], },
}, {
{ id: 'Keyboard--send-in-expanded-composer',
description: 'Keyboard--attach-file', description: i18n('Keyboard--send-in-expanded-composer'),
keys: [['commandOrCtrl', 'U']], keys: [['commandOrCtrl', 'enter']],
}, },
{ {
description: 'Keyboard--remove-draft-link-preview', id: 'Keyboard--attach-file',
keys: [['commandOrCtrl', 'P']], description: i18n('Keyboard--attach-file'),
}, keys: [['commandOrCtrl', 'U']],
{ },
description: 'Keyboard--remove-draft-attachments', {
keys: [['commandOrCtrl', 'shift', 'P']], id: 'Keyboard--remove-draft-link-preview',
}, description: i18n('Keyboard--remove-draft-link-preview'),
]; keys: [['commandOrCtrl', 'P']],
},
{
id: 'Keyboard--remove-draft-attachments',
description: i18n('Keyboard--remove-draft-attachments'),
keys: [['commandOrCtrl', 'shift', 'P']],
},
];
}
const CALLING_SHORTCUTS: Array<ShortcutType> = [ function getCallingShortcuts(i18n: LocalizerType): Array<ShortcutType> {
{ return [
description: 'Keyboard--toggle-audio', {
keys: [['shift', 'M']], id: 'Keyboard--toggle-audio',
}, description: i18n('Keyboard--toggle-audio'),
{ keys: [['shift', 'M']],
description: 'Keyboard--toggle-video', },
keys: [['shift', 'V']], {
}, id: 'Keyboard--toggle-video',
{ description: i18n('Keyboard--toggle-video'),
description: 'icu:Keyboard--accept-video-call', keys: [['shift', 'V']],
keys: [['ctrlOrAlt', 'shift', 'V']], },
}, {
{ id: 'icu:Keyboard--accept-video-call',
description: 'icu:Keyboard--accept-call-without-video', description: i18n('icu:Keyboard--accept-video-call'),
keys: [['ctrlOrAlt', 'shift', 'A']], keys: [['ctrlOrAlt', 'shift', 'V']],
}, },
{ {
description: 'Keyboard--decline-call', id: 'icu:Keyboard--accept-call-without-video',
keys: [['ctrlOrAlt', 'shift', 'D']], description: i18n('icu:Keyboard--accept-call-without-video'),
}, keys: [['ctrlOrAlt', 'shift', 'A']],
{ },
description: 'Keyboard--start-audio-call', {
keys: [['ctrlOrAlt', 'shift', 'C']], id: 'Keyboard--decline-call',
}, description: i18n('Keyboard--decline-call'),
{ keys: [['ctrlOrAlt', 'shift', 'D']],
description: 'Keyboard--start-video-call', },
keys: [['ctrlOrAlt', 'shift', 'Y']], {
}, id: 'Keyboard--start-audio-call',
{ description: i18n('Keyboard--start-audio-call'),
description: 'Keyboard--hang-up', keys: [['ctrlOrAlt', 'shift', 'C']],
keys: [['ctrlOrAlt', 'shift', 'E']], },
}, {
]; id: 'Keyboard--start-video-call',
description: i18n('Keyboard--start-video-call'),
keys: [['ctrlOrAlt', 'shift', 'Y']],
},
{
id: 'Keyboard--hang-up',
description: i18n('Keyboard--hang-up'),
keys: [['ctrlOrAlt', 'shift', 'E']],
},
];
}
export function ShortcutGuide(props: Props): JSX.Element { export function ShortcutGuide(props: Props): JSX.Element {
const { i18n, close, hasInstalledStickers, platform } = props; const { i18n, close, hasInstalledStickers, platform } = props;
@ -262,7 +312,7 @@ export function ShortcutGuide(props: Props): JSX.Element {
{i18n('Keyboard--navigation-header')} {i18n('Keyboard--navigation-header')}
</div> </div>
<div className="module-shortcut-guide__section-list"> <div className="module-shortcut-guide__section-list">
{NAVIGATION_SHORTCUTS.map((shortcut, index) => { {getNavigationShortcuts(i18n).map((shortcut, index) => {
if ( if (
!hasInstalledStickers && !hasInstalledStickers &&
shortcut.description === 'Keyboard--open-sticker-chooser' shortcut.description === 'Keyboard--open-sticker-chooser'
@ -279,7 +329,7 @@ export function ShortcutGuide(props: Props): JSX.Element {
{i18n('Keyboard--messages-header')} {i18n('Keyboard--messages-header')}
</div> </div>
<div className="module-shortcut-guide__section-list"> <div className="module-shortcut-guide__section-list">
{MESSAGE_SHORTCUTS.map((shortcut, index) => {getMessageShortcuts(i18n).map((shortcut, index) =>
renderShortcut(shortcut, index, isMacOS, i18n) renderShortcut(shortcut, index, isMacOS, i18n)
)} )}
</div> </div>
@ -289,7 +339,7 @@ export function ShortcutGuide(props: Props): JSX.Element {
{i18n('Keyboard--composer-header')} {i18n('Keyboard--composer-header')}
</div> </div>
<div className="module-shortcut-guide__section-list"> <div className="module-shortcut-guide__section-list">
{COMPOSER_SHORTCUTS.map((shortcut, index) => {getComposerShortcuts(i18n).map((shortcut, index) =>
renderShortcut(shortcut, index, isMacOS, i18n) renderShortcut(shortcut, index, isMacOS, i18n)
)} )}
</div> </div>
@ -299,7 +349,7 @@ export function ShortcutGuide(props: Props): JSX.Element {
{i18n('Keyboard--calling-header')} {i18n('Keyboard--calling-header')}
</div> </div>
<div className="module-shortcut-guide__section-list"> <div className="module-shortcut-guide__section-list">
{CALLING_SHORTCUTS.map((shortcut, index) => {getCallingShortcuts(i18n).map((shortcut, index) =>
renderShortcut(shortcut, index, isMacOS, i18n) renderShortcut(shortcut, index, isMacOS, i18n)
)} )}
</div> </div>
@ -319,8 +369,7 @@ function renderShortcut(
return ( return (
<div key={index} className="module-shortcut-guide__shortcut"> <div key={index} className="module-shortcut-guide__shortcut">
<div className="module-shortcut-guide__shortcut__description"> <div className="module-shortcut-guide__shortcut__description">
{/* eslint-disable-next-line local-rules/valid-i18n-keys */} {shortcut.description}
{i18n(shortcut.description)}
</div> </div>
<div className="module-shortcut-guide__shortcut__key-container"> <div className="module-shortcut-guide__shortcut__key-container">
{shortcut.keys.map(keys => ( {shortcut.keys.map(keys => (

View file

@ -37,32 +37,35 @@ export type PropsType = {
const contactSortCollator = new window.Intl.Collator(); const contactSortCollator = new window.Intl.Collator();
function getI18nKey(sendStatus: SendStatus | undefined): string { function getSendStatusLabel(
sendStatus: SendStatus | undefined,
i18n: LocalizerType
): string {
if (sendStatus === SendStatus.Failed) { if (sendStatus === SendStatus.Failed) {
return 'MessageDetailsHeader--Failed'; return i18n('MessageDetailsHeader--Failed');
} }
if (sendStatus === SendStatus.Viewed) { if (sendStatus === SendStatus.Viewed) {
return 'MessageDetailsHeader--Viewed'; return i18n('MessageDetailsHeader--Viewed');
} }
if (sendStatus === SendStatus.Read) { if (sendStatus === SendStatus.Read) {
return 'MessageDetailsHeader--Read'; return i18n('MessageDetailsHeader--Read');
} }
if (sendStatus === SendStatus.Delivered) { if (sendStatus === SendStatus.Delivered) {
return 'MessageDetailsHeader--Delivered'; return i18n('MessageDetailsHeader--Delivered');
} }
if (sendStatus === SendStatus.Sent) { if (sendStatus === SendStatus.Sent) {
return 'MessageDetailsHeader--Sent'; return i18n('MessageDetailsHeader--Sent');
} }
if (sendStatus === SendStatus.Pending) { if (sendStatus === SendStatus.Pending) {
return 'MessageDetailsHeader--Pending'; return i18n('MessageDetailsHeader--Pending');
} }
return 'from'; return i18n('from');
} }
export function StoryDetailsModal({ export function StoryDetailsModal({
@ -105,17 +108,19 @@ export function StoryDetailsModal({
return null; return null;
} }
const i18nKey = getI18nKey(sendStatus); const sendStatusLabel = getSendStatusLabel(sendStatus, i18n);
const sortedContacts = [...contacts].sort((a, b) => const sortedContacts = [...contacts].sort((a, b) =>
contactSortCollator.compare(a.recipient.title, b.recipient.title) contactSortCollator.compare(a.recipient.title, b.recipient.title)
); );
return ( return (
<div key={i18nKey} className="StoryDetailsModal__contact-group"> <div
key={sendStatusLabel}
className="StoryDetailsModal__contact-group"
>
<div className="StoryDetailsModal__contact-group__header"> <div className="StoryDetailsModal__contact-group__header">
{/* eslint-disable-next-line local-rules/valid-i18n-keys */} {sendStatusLabel}
{i18n(i18nKey)}
</div> </div>
{sortedContacts.map(status => { {sortedContacts.map(status => {
const contact = status.recipient; const contact = status.recipient;

View file

@ -23,13 +23,15 @@ export function UsernameOnboardingModalBody({
<div className={CLASS}> <div className={CLASS}>
<div className={`${CLASS}__large-at`} /> <div className={`${CLASS}__large-at`} />
<div className={`${CLASS}__title`}>{i18n(`icu:${CLASS}__title`)}</div> <div className={`${CLASS}__title`}>
{i18n('icu:UsernameOnboardingModalBody__title')}
</div>
<div className={`${CLASS}__row`}> <div className={`${CLASS}__row`}>
<div className={`${CLASS}__row__icon ${CLASS}__row__icon--number`} /> <div className={`${CLASS}__row__icon ${CLASS}__row__icon--number`} />
<div className={`${CLASS}__row__body`}> <div className={`${CLASS}__row__body`}>
{i18n(`icu:${CLASS}__row__number`)} {i18n('icu:UsernameOnboardingModalBody__row__number')}
</div> </div>
</div> </div>
@ -37,7 +39,7 @@ export function UsernameOnboardingModalBody({
<div className={`${CLASS}__row__icon ${CLASS}__row__icon--link`} /> <div className={`${CLASS}__row__icon ${CLASS}__row__icon--link`} />
<div className={`${CLASS}__row__body`}> <div className={`${CLASS}__row__body`}>
{i18n(`icu:${CLASS}__row__link`)} {i18n('icu:UsernameOnboardingModalBody__row__link')}
</div> </div>
</div> </div>
@ -45,7 +47,7 @@ export function UsernameOnboardingModalBody({
<div className={`${CLASS}__row__icon ${CLASS}__row__icon--lock`} /> <div className={`${CLASS}__row__icon ${CLASS}__row__icon--lock`} />
<div className={`${CLASS}__row__body`}> <div className={`${CLASS}__row__body`}>
{i18n(`icu:${CLASS}__row__lock`)} {i18n('icu:UsernameOnboardingModalBody__row__lock')}
</div> </div>
</div> </div>
@ -56,12 +58,12 @@ export function UsernameOnboardingModalBody({
rel="noreferrer" rel="noreferrer"
target="_blank" target="_blank"
> >
{i18n(`icu:${CLASS}__learn-more`)} {i18n('icu:UsernameOnboardingModalBody__learn-more')}
</a> </a>
</div> </div>
<Button className={`${CLASS}__submit`} onClick={onNext}> <Button className={`${CLASS}__submit`} onClick={onNext}>
{i18n(`icu:${CLASS}__continue`)} {i18n('icu:UsernameOnboardingModalBody__continue')}
</Button> </Button>
</div> </div>
); );

View file

@ -571,7 +571,10 @@ export class Message extends React.PureComponent<Props, State> {
} }
if (giftBadge) { if (giftBadge) {
const description = i18n(`icu:message--donation--unopened--${direction}`); const description =
direction === 'incoming'
? i18n('icu:message--donation--unopened--incoming')
: i18n('icu:message--donation--unopened--outgoing');
const isDescriptionRTL = getDirection(description) === 'rtl'; const isDescriptionRTL = getDirection(description) === 'rtl';
if (giftBadge.state === GiftBadgeStates.Unopened && !isDescriptionRTL) { if (giftBadge.state === GiftBadgeStates.Unopened && !isDescriptionRTL) {

View file

@ -49,13 +49,23 @@ export function MessageRequestActionsConfirmation({
onChangeState(MessageRequestState.default); onChangeState(MessageRequestState.default);
}} }}
title={ title={
<Intl conversationType === 'direct' ? (
i18n={i18n} <Intl
id={`MessageRequests--block-${conversationType}-confirm-title`} i18n={i18n}
components={{ id="MessageRequests--block-direct-confirm-title"
title: <ContactName key="name" title={title} />, components={{
}} title: <ContactName key="name" title={title} />,
/> }}
/>
) : (
<Intl
i18n={i18n}
id="MessageRequests--block-group-confirm-title"
components={{
title: <ContactName key="name" title={title} />,
}}
/>
)
} }
actions={[ actions={[
...(conversationType === 'direct' ...(conversationType === 'direct'
@ -74,7 +84,9 @@ export function MessageRequestActionsConfirmation({
}, },
]} ]}
> >
{i18n(`MessageRequests--block-${conversationType}-confirm-body`)} {conversationType === 'direct'
? i18n('MessageRequests--block-direct-confirm-body')
: i18n('MessageRequests--block-group-confirm-body')}
</ConfirmationDialog> </ConfirmationDialog>
); );
} }
@ -104,7 +116,9 @@ export function MessageRequestActionsConfirmation({
}, },
]} ]}
> >
{i18n(`MessageRequests--unblock-${conversationType}-confirm-body`)} {conversationType === 'direct'
? i18n('MessageRequests--unblock-direct-confirm-body')
: i18n('MessageRequests--unblock-group-confirm-body')}
</ConfirmationDialog> </ConfirmationDialog>
); );
} }
@ -128,13 +142,18 @@ export function MessageRequestActionsConfirmation({
} }
actions={[ actions={[
{ {
text: i18n(`MessageRequests--delete-${conversationType}`), text:
conversationType === 'direct'
? i18n('MessageRequests--delete-direct')
: i18n('MessageRequests--delete-group'),
action: () => deleteConversation(conversationId), action: () => deleteConversation(conversationId),
style: 'negative', style: 'negative',
}, },
]} ]}
> >
{i18n(`MessageRequests--delete-${conversationType}-confirm-body`)} {conversationType === 'direct'
? i18n('MessageRequests--delete-direct-confirm-body')
: i18n('MessageRequests--delete-group-confirm-body')}
</ConfirmationDialog> </ConfirmationDialog>
); );
} }

View file

@ -285,12 +285,13 @@ function getConfirmationMessage({
// Requesting a membership since they weren't added by anyone // Requesting a membership since they weren't added by anyone
if (membershipType === StageType.DENY_REQUEST) { if (membershipType === StageType.DENY_REQUEST) {
const params = {
name: firstMembership.member.title,
};
return isAccessControlEnabled(conversation.accessControlAddFromInviteLink) return isAccessControlEnabled(conversation.accessControlAddFromInviteLink)
? i18n('PendingRequests--deny-for--with-link', params) ? i18n('PendingRequests--deny-for--with-link', {
: i18n('PendingRequests--deny-for', params); name: firstMembership.member.title,
})
: i18n('PendingRequests--deny-for', {
name: firstMembership.member.title,
});
} }
if (membershipType === StageType.APPROVE_REQUEST) { if (membershipType === StageType.APPROVE_REQUEST) {

View file

@ -30,7 +30,7 @@ async function clearResetsTracking(idForTracking: string | undefined) {
const sessionResets = window.storage.get( const sessionResets = window.storage.get(
'sessionResets', 'sessionResets',
<SessionResetsType>{} {} as SessionResetsType
); );
delete sessionResets[idForTracking]; delete sessionResets[idForTracking];
await window.storage.put('sessionResets', sessionResets); await window.storage.put('sessionResets', sessionResets);

View file

@ -765,7 +765,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
fromContact?.getTitle() ?? window.i18n('unknownContact'); fromContact?.getTitle() ?? window.i18n('unknownContact');
return { return {
emoji, emoji,
text: window.i18n('icu:message--giftBadge--preview--sent', { text: window.i18n('icu:message--donation--preview--sent', {
recipient, recipient,
}), }),
}; };
@ -776,10 +776,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
emoji, emoji,
text: text:
giftBadge.state === GiftBadgeStates.Unopened giftBadge.state === GiftBadgeStates.Unopened
? window.i18n('icu:message--giftBadge--preview--unopened', { ? window.i18n('icu:message--donation--preview--unopened', {
sender, sender,
}) })
: window.i18n('icu:message--giftBadge--preview--redeemed'), : window.i18n('icu:message--donation--preview--redeemed'),
}; };
} }

View file

@ -83,9 +83,9 @@ async function notifyForCall(
icon: isVideoCall icon: isVideoCall
? 'images/icons/v2/video-solid-24.svg' ? 'images/icons/v2/video-solid-24.svg'
: 'images/icons/v2/phone-right-solid-24.svg', : 'images/icons/v2/phone-right-solid-24.svg',
message: window.i18n( message: isVideoCall
isVideoCall ? 'incomingVideoCall' : 'incomingAudioCall' ? window.i18n('incomingVideoCall')
), : window.i18n('incomingAudioCall'),
onNotificationClick: () => { onNotificationClick: () => {
window.IPC.showWindow(); window.IPC.showWindow();
}, },

View file

@ -100,10 +100,11 @@ describe('both/state/ducks/audioPlayer', () => {
it('active is not changed when changing the conversation', () => { it('active is not changed when changing the conversation', () => {
const state = getInitializedState(); const state = getInitializedState();
const updated = rootReducer(state, <TargetedConversationChangedActionType>{ const action: TargetedConversationChangedActionType = {
type: TARGETED_CONVERSATION_CHANGED, type: TARGETED_CONVERSATION_CHANGED,
payload: { id: 'any' }, payload: { conversationId: 'any' },
}); };
const updated = rootReducer(state, action);
const content = updated.audioPlayer.active?.content; const content = updated.audioPlayer.active?.content;
assert.isTrue(content && AudioPlayerContent.isVoiceNote(content)); assert.isTrue(content && AudioPlayerContent.isVoiceNote(content));

View file

@ -60,9 +60,10 @@ describe('NativeThemeListener', () => {
const listener = createNativeThemeListener(ipc, holder); const listener = createNativeThemeListener(ipc, holder);
ipc.emit('native-theme:changed', null, <NativeThemeState>{ const state: NativeThemeState = {
shouldUseDarkColors: false, shouldUseDarkColors: false,
}); };
ipc.emit('native-theme:changed', null, state);
assert.strictEqual(holder.systemTheme, 'light'); assert.strictEqual(holder.systemTheme, 'light');
assert.strictEqual(listener.getSystemTheme(), 'light'); assert.strictEqual(listener.getSystemTheme(), 'light');
@ -80,8 +81,9 @@ describe('NativeThemeListener', () => {
done(); done();
}); });
ipc.emit('native-theme:changed', null, <NativeThemeState>{ const state: NativeThemeState = {
shouldUseDarkColors: false, shouldUseDarkColors: false,
}); };
ipc.emit('native-theme:changed', null, state);
}); });
}); });