Migrate more strings to translation keys (#11683)

This commit is contained in:
Michael Telatynski 2023-10-02 13:52:27 +01:00 committed by GitHub
parent 632d8f4bc7
commit 41a2325a2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
86 changed files with 4744 additions and 4051 deletions

View file

@ -172,7 +172,7 @@ export const CallEvent = forwardRef<any, CallEventProps>(({ mxEvent }, ref) => {
<div className="mx_CallEvent_wrapper" ref={ref}>
<div className="mx_CallEvent mx_CallEvent_inactive">
<div className="mx_CallEvent_columns">
<span className="mx_CallEvent_title">{_t("Video call ended")}</span>
<span className="mx_CallEvent_title">{_t("timeline|m.call|video_call_ended")}</span>
<CallDuration delta={latestEvent.getTs() - mxEvent.getTs()} />
</div>
</div>

View file

@ -170,17 +170,14 @@ export default class DateSeparator extends React.Component<IProps, IState> {
let friendlyErrorMessage = "An error occured while trying to find and jump to the given date.";
let submitDebugLogsContent: JSX.Element = <></>;
if (err instanceof ConnectionError) {
friendlyErrorMessage = _t(
"A network error occurred while trying to find and jump to the given date. Your homeserver might be down or there was just a temporary problem with your internet connection. Please try again. If this continues, please contact your homeserver administrator.",
);
friendlyErrorMessage = _t("room|error_jump_to_date_connection");
} else if (err instanceof MatrixError) {
if (err?.errcode === "M_NOT_FOUND") {
friendlyErrorMessage = _t(
"We were unable to find an event looking forwards from %(dateString)s. Try choosing an earlier date.",
{ dateString: formatFullDateNoDay(new Date(unixTimestamp)) },
);
friendlyErrorMessage = _t("room|error_jump_to_date_not_found", {
dateString: formatFullDateNoDay(new Date(unixTimestamp)),
});
} else {
friendlyErrorMessage = _t("Server returned %(statusCode)s with error code %(errorCode)s", {
friendlyErrorMessage = _t("room|error_jump_to_date", {
statusCode: err?.httpStatus || _t("unknown status code"),
errorCode: err?.errcode || _t("unavailable"),
});
@ -192,7 +189,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
submitDebugLogsContent = (
<p>
{_t(
"Please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.",
"room|error_jump_to_date_send_logs_prompt",
{},
{
debugLogsLink: (sub) => (
@ -215,13 +212,13 @@ export default class DateSeparator extends React.Component<IProps, IState> {
}
Modal.createDialog(ErrorDialog, {
title: _t("Unable to find event at that date"),
title: _t("room|error_jump_to_date_title"),
description: (
<div data-testid="jump-to-date-error-content">
<p>{friendlyErrorMessage}</p>
{submitDebugLogsContent}
<details>
<summary>{_t("Error details")}</summary>
<summary>{_t("room|error_jump_to_date_details")}</summary>
<p>{String(err)}</p>
</details>
</div>
@ -285,7 +282,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
data-testid="jump-to-date-last-month"
/>
<IconizedContextMenuOption
label={_t("The beginning of the room")}
label={_t("room|jump_to_date_beginning")}
onClick={this.onTheBeginningClicked}
data-testid="jump-to-date-beginning"
/>
@ -304,7 +301,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
data-testid="jump-to-date-separator-button"
onClick={this.onContextMenuOpenClick}
isExpanded={!!this.state.contextMenuPosition}
title={_t("Jump to date")}
title={_t("room|jump_to_date")}
>
<h2 className="mx_DateSeparator_dateHeading" aria-hidden="true">
{this.getLabel()}

View file

@ -22,7 +22,7 @@ import { IBodyProps } from "./IBodyProps";
function getErrorMessage(mxEvent?: MatrixEvent): string {
return mxEvent?.isEncryptedDisabledForUnverifiedDevices
? _t("The sender has blocked you from receiving this message")
? _t("timeline|decryption_failure_blocked")
: _t("threads|unable_to_decrypt");
}

View file

@ -55,7 +55,7 @@ export default class DisambiguatedProfile extends React.Component<IProps> {
if (member?.disambiguate) {
mxidElement = <span className="mx_DisambiguatedProfile_mxid">{identifier}</span>;
}
title = _t("%(displayName)s (%(matrixId)s)", {
title = _t("timeline|disambiguated_profile", {
displayName: rawDisplayName,
matrixId: identifier,
});

View file

@ -48,7 +48,7 @@ export default class DownloadActionButton extends React.PureComponent<IProps, IS
this.state = {
loading: false,
tooltip: _td("Downloading"),
tooltip: _td("timeline|download_action_downloading"),
};
}
@ -57,7 +57,7 @@ export default class DownloadActionButton extends React.PureComponent<IProps, IS
if (this.state.loading || !mediaEventHelper) return;
if (mediaEventHelper.media.isEncrypted) {
this.setState({ tooltip: _td("Decrypting") });
this.setState({ tooltip: _td("timeline|download_action_decrypting") });
}
this.setState({ loading: true });

View file

@ -49,19 +49,14 @@ const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent, timestamp
const dmPartner = DMRoomMap.shared().getUserIdForRoomId(roomId);
const room = cli?.getRoom(roomId);
if (prevContent.algorithm === ALGORITHM) {
subtitle = _t("Some encryption parameters have been changed.");
subtitle = _t("timeline|m.room.encryption|parameters_changed");
} else if (dmPartner) {
const displayName = room?.getMember(dmPartner)?.rawDisplayName || dmPartner;
subtitle = _t(
"Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their profile picture.",
{ displayName },
);
subtitle = _t("timeline|m.room.encryption|enabled_dm", { displayName });
} else if (room && isLocalRoom(room)) {
subtitle = _t("Messages in this chat will be end-to-end encrypted.");
subtitle = _t("timeline|m.room.encryption|enabled_local");
} else {
subtitle = _t(
"Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.",
);
subtitle = _t("timeline|m.room.encryption|enabled");
}
return (
@ -79,7 +74,7 @@ const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent, timestamp
<EventTileBubble
className="mx_cryptoEvent mx_cryptoEvent_icon"
title={_t("common|encryption_enabled")}
subtitle={_t("Ignored attempt to disable encryption")}
subtitle={_t("timeline|m.room.encryption|disable_attempt")}
timestamp={timestamp}
/>
);
@ -88,8 +83,8 @@ const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent, timestamp
return (
<EventTileBubble
className="mx_cryptoEvent mx_cryptoEvent_icon mx_cryptoEvent_icon_warning"
title={_t("Encryption not enabled")}
subtitle={_t("The encryption used by this room isn't supported.")}
title={_t("timeline|m.room.encryption|disabled")}
subtitle={_t("timeline|m.room.encryption|unsupported")}
ref={ref}
timestamp={timestamp}
/>

View file

@ -38,9 +38,9 @@ const HiddenBody = React.forwardRef<any, IProps | IBodyProps>(({ mxEvent }, ref)
throw new Error("HiddenBody should only be applied to hidden messages");
case false:
if (visibility.reason) {
text = _t("Message pending moderation: %(reason)s", { reason: visibility.reason });
text = _t("timeline|pending_moderation_reason", { reason: visibility.reason });
} else {
text = _t("Message pending moderation");
text = _t("timeline|pending_moderation");
}
break;
}

View file

@ -41,7 +41,7 @@ const JumpToDatePicker: React.FC<IProps> = ({ ts, onDatePicked }: IProps) => {
return (
<form className="mx_JumpToDatePicker_form" onSubmit={onJumpToDateSubmit}>
<span className="mx_JumpToDatePicker_label">{_t("Jump to date")}</span>
<span className="mx_JumpToDatePicker_label">{_t("room|jump_to_date")}</span>
<Field
element="input"
type="date"
@ -51,7 +51,7 @@ const JumpToDatePicker: React.FC<IProps> = ({ ts, onDatePicked }: IProps) => {
// events there anyway).
max={formatDateForInput(new Date())}
className="mx_JumpToDatePicker_datePicker"
label={_t("Pick a date to jump to")}
label={_t("room|jump_to_date_prompt")}
onFocus={onFocus}
inputRef={ref}
tabIndex={isActive ? 0 : -1}

View file

@ -157,23 +157,23 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
if (gotRejected) {
return (
<div className="mx_LegacyCallEvent_content">
{_t("Call declined")}
{this.renderCallBackButton(_t("Call back"))}
{_t("timeline|m.call.invite|declined")}
{this.renderCallBackButton(_t("timeline|m.call.invite|call_back_prompt"))}
{this.props.timestamp}
</div>
);
} else if (hangupReason === CallErrorCode.AnsweredElsewhere) {
return (
<div className="mx_LegacyCallEvent_content">
{_t("Answered elsewhere")}
{_t("timeline|m.call.invite|answered_elsewhere")}
{this.props.timestamp}
</div>
);
} else if (this.props.callEventGrouper.callWasMissed) {
return (
<div className="mx_LegacyCallEvent_content">
{_t("Missed call")}
{this.renderCallBackButton(_t("Call back"))}
{_t("timeline|m.call.invite|missed_call")}
{this.renderCallBackButton(_t("timeline|m.call.invite|call_back_prompt"))}
{this.props.timestamp}
</div>
);
@ -198,8 +198,8 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
} else if (hangupReason === CallErrorCode.InviteTimeout) {
return (
<div className="mx_LegacyCallEvent_content">
{_t("No answer")}
{this.renderCallBackButton(_t("Call back"))}
{_t("timeline|m.call.invite|no_answer")}
{this.renderCallBackButton(_t("timeline|m.call.invite|call_back_prompt"))}
{this.props.timestamp}
</div>
);
@ -208,22 +208,22 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
let reason;
if (hangupReason === CallErrorCode.IceFailed) {
// We couldn't establish a connection at all
reason = _t("Could not connect media");
reason = _t("timeline|m.call.invite|failed_connect_media");
} else if (hangupReason === "ice_timeout") {
// We established a connection but it died
reason = _t("Connection failed");
reason = _t("timeline|m.call.invite|failed_connection");
} else if (hangupReason === CallErrorCode.NoUserMedia) {
// The other side couldn't open capture devices
reason = _t("Their device couldn't start the camera or microphone");
reason = _t("timeline|m.call.invite|failed_opponent_media");
} else if (hangupReason === "unknown_error") {
// An error code the other side doesn't have a way to express
// (as opposed to an error code they gave but we don't know about,
// in which case we show the error code)
reason = _t("An unknown error occurred");
reason = _t("timeline|m.call.invite|unknown_error");
} else if (hangupReason === CallErrorCode.UserBusy) {
reason = _t("voip|user_busy_description");
} else {
reason = _t("Unknown failure: %(reason)s", { reason: hangupReason });
reason = _t("timeline|m.call.invite|unknown_failure", { reason: hangupReason });
}
return (
@ -233,7 +233,7 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
className="mx_LegacyCallEvent_content_tooltip"
kind={InfoTooltipKind.Warning}
/>
{_t("Connection failed")}
{_t("timeline|m.call.invite|failed_connection")}
{this.renderCallBackButton(_t("action|retry"))}
{this.props.timestamp}
</div>
@ -258,7 +258,7 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
return (
<div className="mx_LegacyCallEvent_content">
{_t("The call is in an unknown state!")}
{_t("timeline|m.call.invite|unknown_state")}
{this.props.timestamp}
</div>
);

View file

@ -98,7 +98,7 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
if (this.state.error) {
return (
<MediaProcessingError className="mx_MAudioBody">
{_t("Error processing audio message")}
{_t("timeline|m.audio|error_processing_audio")}
</MediaProcessingError>
);
}

View file

@ -228,7 +228,7 @@ const MBeaconBody = React.forwardRef<HTMLDivElement, IBodyProps>(({ mxEvent, get
className="mx_MBeaconBody_chin"
beacon={beacon}
displayStatus={displayStatus}
label={_t("View live location")}
label={_t("timeline|m.beacon_info|view_live_location")}
withIcon
/>
)}

View file

@ -150,7 +150,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
imgSrc: DOWNLOAD_ICON_URL,
imgStyle: null,
style: computedStyle(this.dummyLink.current),
textContent: _t("Download %(text)s", { text }),
textContent: _t("timeline|m.file|download_label", { text }),
},
});
}
@ -174,7 +174,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
logger.warn("Unable to decrypt attachment: ", err);
Modal.createDialog(ErrorDialog, {
title: _t("common|error"),
description: _t("Error decrypting attachment"),
description: _t("timeline|m.file|error_decrypting"),
});
}
};
@ -248,7 +248,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
{showDownloadLink && (
<div className="mx_MFileBody_download">
<AccessibleButton onClick={this.decryptFile}>
{_t("Decrypt %(text)s", { text: this.linkText })}
{_t("timeline|m.file|decrypt_label", { text: this.linkText })}
</AccessibleButton>
</div>
)}
@ -346,7 +346,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
<div className="mx_MFileBody_download">
<a {...downloadProps}>
<span className="mx_MFileBody_download_icon" />
{_t("Download %(text)s", { text: this.linkText })}
{_t("timeline|m.file|download_label", { text: this.linkText })}
</a>
{this.context.timelineRenderingType === TimelineRenderingType.File && (
<div className="mx_MImageBody_size">
@ -362,7 +362,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
return (
<span className="mx_MFileBody">
{placeholder}
{_t("Invalid file%(extra)s", { extra: extra })}
{_t("timeline|m.file|error_invalid", { extra: extra })}
</span>
);
}

View file

@ -604,11 +604,11 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
const content = this.props.mxEvent.getContent<ImageContent>();
if (this.state.error) {
let errorText = _t("Unable to show image due to error");
let errorText = _t("timeline|m.image|error");
if (this.state.error instanceof DecryptError) {
errorText = _t("Error decrypting image");
errorText = _t("timeline|m.image|error_decrypting");
} else if (this.state.error instanceof DownloadError) {
errorText = _t("Error downloading image");
errorText = _t("timeline|m.image|error_downloading");
}
return <MediaProcessingError className="mx_MImageBody">{errorText}</MediaProcessingError>;
@ -651,7 +651,7 @@ export class HiddenImagePlaceholder extends React.PureComponent<PlaceholderIProp
<div className={className} style={{ maxWidth: `min(100%, ${maxWidth}px)` }}>
<div className="mx_HiddenImagePlaceholder_button">
<span className="mx_HiddenImagePlaceholder_eye" />
<span>{_t("Show image")}</span>
<span>{_t("timeline|m.image|show_image")}</span>
</div>
</div>
);

View file

@ -42,9 +42,9 @@ export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
const widgetId = this.props.mxEvent.getStateKey();
const widget = WidgetStore.instance.getRoom(room.roomId, true).widgets.find((w) => w.id === widgetId);
let joinCopy: string | null = _t("Join the conference at the top of this room");
let joinCopy: string | null = _t("timeline|m.widget|jitsi_join_top_prompt");
if (widget && WidgetLayoutStore.instance.isInContainer(room, widget, Container.Right)) {
joinCopy = _t("Join the conference from the room information card on the right");
joinCopy = _t("timeline|m.widget|jitsi_join_right_prompt");
} else if (!widget) {
joinCopy = null;
}
@ -54,7 +54,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
return (
<EventTileBubble
className="mx_MJitsiWidgetEvent"
title={_t("Video conference ended by %(senderName)s", { senderName })}
title={_t("timeline|m.widget|jitsi_ended", { senderName })}
timestamp={this.props.timestamp}
/>
);
@ -63,7 +63,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
return (
<EventTileBubble
className="mx_MJitsiWidgetEvent"
title={_t("Video conference updated by %(senderName)s", { senderName })}
title={_t("timeline|m.widget|jitsi_updated", { senderName })}
subtitle={joinCopy}
timestamp={this.props.timestamp}
/>
@ -73,7 +73,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
return (
<EventTileBubble
className="mx_MJitsiWidgetEvent"
title={_t("Video conference started by %(senderName)s", { senderName })}
title={_t("timeline|m.widget|jitsi_started", { senderName })}
subtitle={joinCopy}
timestamp={this.props.timestamp}
/>

View file

@ -109,17 +109,17 @@ export default class MKeyVerificationConclusion extends React.Component<IProps>
let title: string | undefined;
if (request.phase === VerificationPhase.Done) {
title = _t("You verified %(name)s", {
title = _t("timeline|m.key.verification.done", {
name: getNameForEventRoom(client, request.otherUserId, mxEvent.getRoomId()!),
});
} else if (request.phase === VerificationPhase.Cancelled) {
const userId = request.cancellingUserId;
if (userId === myUserId) {
title = _t("You cancelled verifying %(name)s", {
title = _t("timeline|m.key.verification.cancel|you_cancelled", {
name: getNameForEventRoom(client, request.otherUserId, mxEvent.getRoomId()!),
});
} else if (userId) {
title = _t("%(name)s cancelled verifying", {
title = _t("timeline|m.key.verification.cancel|user_cancelled", {
name: getNameForEventRoom(client, userId, mxEvent.getRoomId()!),
});
}

View file

@ -95,7 +95,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
const client = MatrixClientPeg.safeGet();
const myUserId = client.getUserId();
if (userId === myUserId) {
return _t("You accepted");
return _t("timeline|m.key.verification.request|you_accepted");
} else {
return _t("%(name)s accepted", {
name: getNameForEventRoom(client, userId, this.props.mxEvent.getRoomId()!),
@ -110,9 +110,9 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
const declined = cancellationCode === "m.user";
if (userId === myUserId) {
if (declined) {
return _t("You declined");
return _t("timeline|m.key.verification.request|you_declined");
} else {
return _t("You cancelled");
return _t("timeline|m.key.verification.request|you_cancelled");
}
} else {
if (declined) {
@ -157,7 +157,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
} else if (request.accepting) {
stateLabel = _t("encryption|verification|accepting");
} else if (request.declining) {
stateLabel = _t("Declining…");
stateLabel = _t("timeline|m.key.verification.request|declining");
}
stateNode = <div className="mx_cryptoEvent_state">{stateLabel}</div>;
}
@ -180,7 +180,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
}
} else {
// request sent by us
title = _t("You sent a verification request");
title = _t("timeline|m.key.verification.request|you_started");
subtitle = userLabelForEventRoom(client, request.otherUserId, mxEvent.getRoomId()!);
}

View file

@ -126,8 +126,8 @@ export function launchPollEditor(mxEvent: MatrixEvent, getRelationsForEvent?: Ge
const room = MatrixClientPeg.safeGet().getRoom(mxEvent.getRoomId());
if (pollAlreadyHasVotes(mxEvent, getRelationsForEvent)) {
Modal.createDialog(ErrorDialog, {
title: _t("Can't edit poll"),
description: _t("Sorry, you can't edit a poll after votes have been cast."),
title: _t("poll|unable_edit_title"),
description: _t("poll|unable_edit_description"),
});
} else if (room) {
Modal.createDialog(
@ -229,8 +229,8 @@ export default class MPollBody extends React.Component<IBodyProps, IState> {
console.error("Failed to submit poll response event:", e);
Modal.createDialog(ErrorDialog, {
title: _t("Vote not registered"),
description: _t("Sorry, your vote was not registered. Please try again."),
title: _t("poll|error_voting_title"),
description: _t("poll|error_voting_description"),
});
});
@ -306,19 +306,19 @@ export default class MPollBody extends React.Component<IBodyProps, IState> {
let totalText: string;
if (showResults && poll.undecryptableRelationsCount) {
totalText = _t("Due to decryption errors, some votes may not be counted");
totalText = _t("poll|total_decryption_errors");
} else if (poll.isEnded) {
totalText = _t("Final result based on %(count)s votes", { count: totalVotes });
totalText = _t("right_panel|poll|final_result", { count: totalVotes });
} else if (!disclosed) {
totalText = _t("Results will be visible when the poll is ended");
totalText = _t("poll|total_not_ended");
} else if (myVote === undefined) {
if (totalVotes === 0) {
totalText = _t("No votes cast");
totalText = _t("poll|total_no_votes");
} else {
totalText = _t("%(count)s votes cast. Vote to see the results", { count: totalVotes });
totalText = _t("poll|total_n_votes", { count: totalVotes });
}
} else {
totalText = _t("Based on %(count)s votes", { count: totalVotes });
totalText = _t("poll|total_n_votes_voted", { count: totalVotes });
}
const editedSpan = this.props.mxEvent.replacingEvent() ? (

View file

@ -109,7 +109,7 @@ export const MPollEndBody = React.forwardRef<any, IBodyProps>(({ mxEvent, ...pro
return (
<div ref={ref}>
<Caption>{_t("Ended a poll")}</Caption>
<Caption>{_t("timeline|m.poll.end|ended")}</Caption>
<MPollBody mxEvent={pollStartEvent} {...props} />
</div>
);

View file

@ -250,7 +250,9 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
if (this.state.error !== null) {
return (
<MediaProcessingError className="mx_MVideoBody">{_t("Error decrypting video")}</MediaProcessingError>
<MediaProcessingError className="mx_MVideoBody">
{_t("timeline|m.video|error_decrypting")}
</MediaProcessingError>
);
}

View file

@ -29,7 +29,7 @@ export default class MVoiceMessageBody extends MAudioBody {
if (this.state.error) {
return (
<MediaProcessingError className="mx_MVoiceMessageBody">
{_t("Error processing voice message")}
{_t("timeline|m.audio|error_processing_voice_message")}
</MediaProcessingError>
);
}

View file

@ -237,16 +237,12 @@ const ReplyInThreadButton: React.FC<IReplyInThreadButton> = ({ mxEvent }) => {
<>
<div className="mx_Tooltip_title">
{!hasARelation
? _t("Reply in thread")
: _t("Can't create a thread from an event with an existing relation")}
? _t("action|reply_in_thread")
: _t("threads|error_start_thread_existing_relation")}
</div>
</>
}
title={
!hasARelation
? _t("Reply in thread")
: _t("Can't create a thread from an event with an existing relation")
}
title={!hasARelation ? _t("action|reply_in_thread") : _t("threads|error_start_thread_existing_relation")}
onClick={onClick}
onContextMenu={onClick}
>
@ -515,15 +511,23 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
const tooltip = (
<>
<div className="mx_Tooltip_title">
{this.props.isQuoteExpanded ? _t("Collapse quotes") : _t("Expand quotes")}
{this.props.isQuoteExpanded
? _t("timeline|mab|collapse_reply_chain")
: _t("timeline|mab|expand_reply_chain")}
</div>
<div className="mx_Tooltip_sub">
{_t(ALTERNATE_KEY_NAME[Key.SHIFT]) + " + " + _t("action|click")}
</div>
<div className="mx_Tooltip_sub">{_t(ALTERNATE_KEY_NAME[Key.SHIFT]) + " + " + _t("Click")}</div>
</>
);
toolbarOpts.push(
<RovingAccessibleTooltipButton
className={expandClassName}
title={this.props.isQuoteExpanded ? _t("Collapse quotes") : _t("Expand quotes")}
title={
this.props.isQuoteExpanded
? _t("timeline|mab|collapse_reply_chain")
: _t("timeline|mab|expand_reply_chain")
}
tooltip={tooltip}
onClick={this.props.toggleThreadExpanded}
key="expand"

View file

@ -35,7 +35,7 @@ export default class MjolnirBody extends React.Component<IBodyProps> {
<div className="mx_MjolnirBody">
<i>
{_t(
"You have ignored this user, so their message is hidden. <a>Show anyways.</a>",
"timeline|mjolnir|message_hidden",
{},
{
a: (sub) => (

View file

@ -555,9 +555,9 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
throw new Error("renderPendingModerationMarker should only be applied to hidden messages");
case false:
if (visibility.reason) {
text = _t("Message pending moderation: %(reason)s", { reason: visibility.reason });
text = _t("timeline|pending_moderation_reason", { reason: visibility.reason });
} else {
text = _t("Message pending moderation");
text = _t("timeline|pending_moderation");
}
break;
}