From 1415354f2af0d5e92c503dceb5de5394b47c5a9b Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Mon, 29 Sep 2025 13:58:54 +0100 Subject: [PATCH] Update strings for the verification dialog (#30880) * Change 'Verify Session' to 'Start Verification' * Update strings for the verification dialog This completes the Element Web part of https://github.com/element-hq/element-meta/issues/2898 * fixup! Update strings for the verification dialog Remove separate _unsent string --- .../e2e/crypto/device-verification.spec.ts | 11 ++- playwright/e2e/oidc/oidc-native.spec.ts | 2 +- .../dialogs/VerificationRequestDialog.tsx | 26 +++++-- .../views/right_panel/EncryptionInfo.tsx | 2 +- .../views/right_panel/VerificationPanel.tsx | 67 ++++--------------- .../views/toasts/VerificationRequestToast.tsx | 2 +- .../verification/VerificationShowSas.tsx | 2 +- src/i18n/strings/en_EN.json | 28 ++++---- .../VerificationRequestDialog-test.tsx | 55 ++++++++------- .../VerificationRequestDialog-test.tsx.snap | 44 ++++++------ .../VerificationRequestToast-test.tsx.snap | 4 +- 11 files changed, 109 insertions(+), 134 deletions(-) diff --git a/playwright/e2e/crypto/device-verification.spec.ts b/playwright/e2e/crypto/device-verification.spec.ts index 2382a0c80e..2300b382b8 100644 --- a/playwright/e2e/crypto/device-verification.spec.ts +++ b/playwright/e2e/crypto/device-verification.spec.ts @@ -146,8 +146,8 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => { ); // Confirm that the bot user scanned successfully - await expect(infoDialog.getByText("Almost there! Is your other device showing the same shield?")).toBeVisible(); - await infoDialog.getByRole("button", { name: "Yes" }).click(); + await expect(infoDialog.getByText("Confirm that you see a green shield on your other device")).toBeVisible(); + await infoDialog.getByRole("button", { name: "Yes, I see a green shield" }).click(); await infoDialog.getByRole("button", { name: "Got it" }).click(); // wait for the bot to see we have finished @@ -270,7 +270,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => { // it should contain the device ID of the requesting device await expect(toast.getByText(`${aliceBotClient.credentials.deviceId} from `)).toBeVisible(); // Accept - await toast.getByRole("button", { name: "Verify Session" }).click(); + await toast.getByRole("button", { name: "Start verification" }).click(); /* Click 'Start' to start SAS verification */ await page.getByRole("button", { name: "Start" }).click(); @@ -285,10 +285,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => { /* And we're all done! */ const infoDialog = page.locator(".mx_InfoDialog"); await infoDialog.getByRole("button", { name: "They match" }).click(); - // We don't assert the full string as the device name is unset on Synapse but set to the user ID on Dendrite - await expect(infoDialog.getByText(`You've successfully verified`)).toContainText( - `(${aliceBotClient.credentials.deviceId})`, - ); + await expect(infoDialog.getByText("Device verified")).toBeVisible(); await infoDialog.getByRole("button", { name: "Got it" }).click(); }); }); diff --git a/playwright/e2e/oidc/oidc-native.spec.ts b/playwright/e2e/oidc/oidc-native.spec.ts index 99a7af3c23..2fedc0c451 100644 --- a/playwright/e2e/oidc/oidc-native.spec.ts +++ b/playwright/e2e/oidc/oidc-native.spec.ts @@ -228,7 +228,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => { */ async function verifyUsingOtherDevice(deviceToVerifyPage: Page, alreadyVerifiedDevicePage: Page) { await deviceToVerifyPage.getByRole("button", { name: "Use another device" }).click(); - await alreadyVerifiedDevicePage.getByRole("button", { name: "Verify session" }).click(); + await alreadyVerifiedDevicePage.getByRole("button", { name: "Start verification" }).click(); await alreadyVerifiedDevicePage.getByRole("button", { name: "Start" }).click(); await alreadyVerifiedDevicePage.getByRole("button", { name: "They match" }).click(); await deviceToVerifyPage.getByRole("button", { name: "They match" }).click(); diff --git a/src/components/views/dialogs/VerificationRequestDialog.tsx b/src/components/views/dialogs/VerificationRequestDialog.tsx index 801b745e71..9414c9107b 100644 --- a/src/components/views/dialogs/VerificationRequestDialog.tsx +++ b/src/components/views/dialogs/VerificationRequestDialog.tsx @@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details. import React from "react"; import { VerificationPhase, VerificationRequestEvent, type VerificationRequest } from "matrix-js-sdk/src/crypto-api"; +import { VerificationMethod } from "matrix-js-sdk/src/types"; import { type User } from "matrix-js-sdk/src/matrix"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; @@ -111,10 +112,27 @@ export default class VerificationRequestDialog extends React.Component = ({ }: IProps) => { let content: JSX.Element; if (waitingForOtherParty && isSelfVerification) { - content =
{_t("encryption|verification|self_verification_hint")}
; + content =
{_t("encryption|verification|once_accepted_can_continue")}
; } else if (waitingForOtherParty || waitingForNetwork) { let text: string; if (waitingForOtherParty) { diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index 1782a52a88..fa3a4dc4db 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -126,7 +126,7 @@ export default class VerificationPanel extends React.PureComponent - {_t("encryption|verification|qr_or_sas_header")} + {_t("encryption|verification|verify_by_completing_one_of")}
{qrBlockDialog} {or} @@ -224,7 +224,7 @@ export default class VerificationPanel extends React.PureComponent{description}

- - {_t("action|no")} - - {_t("action|yes")} + {_t("encryption|verification|qr_reciprocate_yes")} + + + {_t("encryption|verification|qr_reciprocate_no")}
@@ -260,12 +259,7 @@ export default class VerificationPanel extends React.PureComponent ); } - return ( -
-

{_t("encryption|verification|scan_qr")}

- {body} -
- ); + return
{body}
; } private renderVerifiedPhase(): JSX.Element { @@ -282,18 +276,7 @@ export default class VerificationPanel extends React.PureComponent -

{_t("common|verification_cancelled")}

-

{text}

+

{_t("encryption|verification|cancelled_verification")}

{_t("action|got_it")} diff --git a/src/components/views/toasts/VerificationRequestToast.tsx b/src/components/views/toasts/VerificationRequestToast.tsx index 12843c0b87..74db50d632 100644 --- a/src/components/views/toasts/VerificationRequestToast.tsx +++ b/src/components/views/toasts/VerificationRequestToast.tsx @@ -177,7 +177,7 @@ export default class VerificationRequestToast extends React.PureComponent
); sasCaption = this.props.isSelf - ? _t("encryption|verification|sas_emoji_caption_self") + ? _t("encryption|verification|confirm_the_emojis") : _t("encryption|verification|sas_emoji_caption_user"); } else if (this.props.sas.decimal) { const numberBlocks = this.props.sas.decimal.map((num, i) => {num}); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f9eab0839e..28efb495e4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -598,7 +598,6 @@ "user": "User", "user_avatar": "Profile picture", "username": "Username", - "verification_cancelled": "Verification cancelled", "verified": "Verified", "version": "Version", "video": "Video", @@ -993,9 +992,7 @@ "skip_verification": "Skip verification for now", "verify_this_device": "Verify this device" }, - "cancelled": "You cancelled verification.", - "cancelled_self": "You cancelled verification on your other device.", - "cancelled_user": "%(displayName)s cancelled verification.", + "cancelled_verification": "Either the request timed out, the request was denied, or there was a verification mismatch.", "cancelling": "Cancelling…", "cant_confirm": "Can't confirm?", "complete_action": "Got It", @@ -1003,6 +1000,7 @@ "complete_title": "Verified!", "confirm_identity_description": "Verify this device to set up secure messaging", "confirm_identity_title": "Confirm your identity", + "confirm_the_emojis": "Confirm that the emojis below match those shown on your other device.", "error_starting_description": "We were unable to start a chat with the other user.", "error_starting_title": "Error starting verification", "explainer": "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.", @@ -1029,36 +1027,32 @@ "wrong_fingerprint": "Unable to verify device '%(deviceId)s' - the supplied fingerprint '%(fingerprint)s' does not match the device fingerprint, '%(fprint)s'" }, "no_support_qr_emoji": "The device you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.", + "now_you_can": "Now you can read or send messages securely, and anyone you chat with can also trust this device.", + "once_accepted_can_continue": "Once accepted you'll be able to continue with the verification.", "other_party_cancelled": "The other party cancelled the verification.", "prompt_encrypted": "Verify all users in a room to ensure it's secure.", - "prompt_self": "Start verification again from the notification.", "prompt_unencrypted": "In encrypted rooms, verify all users to ensure it's secure.", - "prompt_user": "Start verification again from their profile.", "qr_or_sas": "%(qrCode)s or %(emojiCompare)s", - "qr_or_sas_header": "Verify this device by completing one of the following:", "qr_prompt": "Scan this unique code", - "qr_reciprocate_same_shield_device": "Almost there! Is your other device showing the same shield?", + "qr_reciprocate_check_again_device": "Check again on your other device to finish verification.", + "qr_reciprocate_no": "No, I don't see a green shield", "qr_reciprocate_same_shield_user": "Almost there! Is %(displayName)s showing the same shield?", - "request_toast_accept": "Verify Session", + "qr_reciprocate_yes": "Yes, I see a green shield", "request_toast_accept_user": "Verify User", "request_toast_decline_counter": "Ignore (%(counter)s)", "request_toast_detail": "%(deviceId)s from %(ip)s", + "request_toast_start_verification": "Start Verification", "sas_caption_self": "Verify this device by confirming the following number appears on its screen.", "sas_caption_user": "Verify this user by confirming the following number appears on their screen.", "sas_description": "Compare a unique set of emoji if you don't have a camera on either device", - "sas_emoji_caption_self": "Confirm the emoji below are displayed on both devices, in the same order:", "sas_emoji_caption_user": "Verify this user by confirming the following emoji appear on their screen.", "sas_match": "They match", "sas_no_match": "They don't match", "sas_prompt": "Compare unique emoji", "scan_qr": "Verify by scanning", "scan_qr_explainer": "Ask %(displayName)s to scan your code:", - "self_verification_hint": "To proceed, please accept the verification request on your other device.", "start_button": "Start Verification", - "successful_device": "You've successfully verified %(deviceName)s (%(deviceId)s)!", - "successful_own_device": "You've successfully verified your device!", "successful_user": "You've successfully verified %(displayName)s!", - "timed_out": "Verification timed out.", "unsupported_method": "Unable to find a supported verification method.", "unverified_session_toast_accept": "Yes, it was me", "unverified_session_toast_title": "New login. Was this you?", @@ -1067,12 +1061,18 @@ "unverified_sessions_toast_title": "You have unverified sessions", "use_another_device": "Use another device", "use_recovery_key": "Use recovery key", + "verification_dialog_title_choose": "Choose how to verify", + "verification_dialog_title_compare_emojis": "Compare emojis", + "verification_dialog_title_confirm_green_shield": "Confirm that you see a green shield on your other device", "verification_dialog_title_device": "Verify other device", "verification_dialog_title_failed": "Verification failed", + "verification_dialog_title_start_on_other_device": "Start verification on the other device", "verification_dialog_title_user": "Verification Request", + "verification_dialog_title_verified": "Device verified", "verification_skip_warning": "Without verifying, you won't have access to all your messages and may appear as untrusted to others.", "verification_success_with_backup": "Your new device is now verified. It has access to your encrypted messages, and other users will see it as trusted.", "verification_success_without_backup": "Your new device is now verified. Other users will see it as trusted.", + "verify_by_completing_one_of": "Verify by completing one of the following:", "verify_emoji": "Verify by emoji", "verify_emoji_prompt": "Verify by comparing unique emoji.", "verify_emoji_prompt_qr": "If you can't scan the code above, verify by comparing unique emoji.", diff --git a/test/unit-tests/components/views/dialogs/VerificationRequestDialog-test.tsx b/test/unit-tests/components/views/dialogs/VerificationRequestDialog-test.tsx index f373cfef14..e019faec4a 100644 --- a/test/unit-tests/components/views/dialogs/VerificationRequestDialog-test.tsx +++ b/test/unit-tests/components/views/dialogs/VerificationRequestDialog-test.tsx @@ -35,8 +35,8 @@ describe("VerificationRequestDialog", () => { it("Initially, asks how you would like to verify this device", async () => { const dialog = renderComponent(VerificationPhase.Ready); - expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); - expect(screen.getByText("Verify this device by completing one of the following:")).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Choose how to verify" })).toBeInTheDocument(); + expect(screen.getByText("Verify by completing one of the following:")).toBeInTheDocument(); expect(dialog.asFragment()).toMatchSnapshot(); }); @@ -44,11 +44,8 @@ describe("VerificationRequestDialog", () => { it("After we started verification here, says we are waiting for the other device", async () => { const dialog = renderComponent(VerificationPhase.Requested); - expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); - - expect( - screen.getByText("To proceed, please accept the verification request on your other device."), - ).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Start verification on the other device" })).toBeInTheDocument(); + expect(screen.getByText("Once accepted you'll be able to continue with the verification.")).toBeInTheDocument(); expect(dialog.asFragment()).toMatchSnapshot(); }); @@ -56,10 +53,10 @@ describe("VerificationRequestDialog", () => { it("When other device accepted emoji, displays emojis and asks for confirmation", async () => { const dialog = renderComponent(VerificationPhase.Started, "emoji"); - expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Compare emojis" })).toBeInTheDocument(); expect( - screen.getByText("Confirm the emoji below are displayed on both devices, in the same order:"), + screen.getByText("Confirm that the emojis below match those shown on your other device."), ).toBeInTheDocument(); expect(dialog.asFragment()).toMatchSnapshot(); @@ -68,10 +65,18 @@ describe("VerificationRequestDialog", () => { it("After scanning QR, shows confirmation dialog", async () => { const dialog = renderComponent(VerificationPhase.Started, "qr"); - expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); - expect(screen.getByRole("heading", { name: "Verify by scanning" })).toBeInTheDocument(); + expect( + screen.getByRole("heading", { + name: "Confirm that you see a green shield on your other device", + }), + ).toBeInTheDocument(); - expect(screen.getByText("Almost there! Is your other device showing the same shield?")).toBeInTheDocument(); + // We used to have a subheading here: confirm it is not present. + expect(screen.queryByRole("heading", { name: "Verify by scanning" })).not.toBeInTheDocument(); + + expect(screen.getByText("Check again on your other device to finish verification.")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Yes, I see a green shield" })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "No, I don't see a green shield" })).toBeInTheDocument(); expect(dialog.asFragment()).toMatchSnapshot(); }); @@ -79,8 +84,13 @@ describe("VerificationRequestDialog", () => { it("Shows a successful message if verification finished normally", async () => { const dialog = renderComponent(VerificationPhase.Done); - expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); - expect(screen.getByText("You've successfully verified your device!")).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Device verified" })).toBeInTheDocument(); + + expect( + screen.getByText( + "Now you can read or send messages securely, and anyone you chat with can also trust this device.", + ), + ).toBeInTheDocument(); expect(dialog.asFragment()).toMatchSnapshot(); }); @@ -89,11 +99,13 @@ describe("VerificationRequestDialog", () => { const dialog = renderComponent(VerificationPhase.Cancelled); expect(screen.getByRole("heading", { name: "Verification failed" })).toBeInTheDocument(); - expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument(); + + // We used to have a sub-heading here: confirm is it not present. + expect(screen.queryByRole("heading", { name: "Verification cancelled" })).not.toBeInTheDocument(); expect( screen.getByText( - "You cancelled verification on your other device. Start verification again from the notification.", + "Either the request timed out, the request was denied, or there was a verification mismatch.", ), ).toBeInTheDocument(); @@ -119,11 +131,10 @@ describe("VerificationRequestDialog", () => { // Then it renders the resolved information expect(screen.getByRole("heading", { name: "Verification failed" })).toBeInTheDocument(); - expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument(); expect( screen.getByText( - "You cancelled verification on your other device. Start verification again from the notification.", + "Either the request timed out, the request was denied, or there was a verification mismatch.", ), ).toBeInTheDocument(); }); @@ -149,11 +160,10 @@ describe("VerificationRequestDialog", () => { // Then it renders the information from the request in the promise expect(screen.getByRole("heading", { name: "Verification failed" })).toBeInTheDocument(); - expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument(); expect( screen.getByText( - "You cancelled verification on your other device. Start verification again from the notification.", + "Either the request timed out, the request was denied, or there was a verification mismatch.", ), ).toBeInTheDocument(); }); @@ -170,11 +180,10 @@ describe("VerificationRequestDialog", () => { // Then the dialog is updated to reflect that expect(screen.getByRole("heading", { name: "Verification failed" })).toBeInTheDocument(); - expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument(); expect( screen.getByText( - "You cancelled verification on your other device. Start verification again from the notification.", + "Either the request timed out, the request was denied, or there was a verification mismatch.", ), ).toBeInTheDocument(); }); @@ -327,7 +336,7 @@ class MockVerificationRequest } async generateQRCode(): Promise { - return undefined; + return new Uint8ClampedArray(); } get cancellationCode(): string | null { diff --git a/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap b/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap index a2fd15edd5..cec0876831 100644 --- a/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap +++ b/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap @@ -22,17 +22,14 @@ exports[`VerificationRequestDialog After scanning QR, shows confirmation dialog class="mx_Heading_h3 mx_Dialog_title" id="mx_BaseDialog_title" > - Verify other device + Confirm that you see a green shield on your other device
-

- Verify by scanning -

- Almost there! Is your other device showing the same shield? + Check again on your other device to finish verification.

-
- No -
- Yes + Yes, I see a green shield +
+
+ No, I don't see a green shield
@@ -99,11 +96,11 @@ exports[`VerificationRequestDialog After we started verification here, says we a class="mx_Heading_h3 mx_Dialog_title" id="mx_BaseDialog_title" > - Verify other device + Start verification on the other device
- To proceed, please accept the verification request on your other device. + Once accepted you'll be able to continue with the verification.
- Verify other device + Choose how to verify
- Verify this device by completing one of the following: + Verify by completing one of the following:
@@ -242,11 +239,8 @@ exports[`VerificationRequestDialog Shows a failure message if verification was c
-

- Verification cancelled -

- You cancelled verification on your other device. Start verification again from the notification. + Either the request timed out, the request was denied, or there was a verification mismatch.

- Verify other device + Device verified

- You've successfully verified your device! + Now you can read or send messages securely, and anyone you chat with can also trust this device.

- Verify other device + Compare emojis

- Confirm the emoji below are displayed on both devices, in the same order: + Confirm that the emojis below match those shown on your other device.

- Verify Session + Start Verification
@@ -68,7 +68,7 @@ exports[`VerificationRequestToast should render a self-verification 1`] = ` role="button" tabindex="0" > - Verify Session + Start Verification