From 10a9d662b820e51bb5b639ec1e7cbbc389529a4b Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Thu, 23 Apr 2026 22:12:24 -0300 Subject: [PATCH 1/4] chore: set ttag for sign in and sign up strings --- .../AccountMenu/AdvancedOptions.tsx | 24 ++++++++++++------- .../AccountMenu/ConfirmNoMergeDialog.tsx | 13 +++++----- .../AccountMenu/MergeLocalDataCheckbox.tsx | 6 +++-- .../AccountMenu/ServerPicker/ServerPicker.tsx | 13 +++++----- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/packages/web/src/javascripts/Components/AccountMenu/AdvancedOptions.tsx b/packages/web/src/javascripts/Components/AccountMenu/AdvancedOptions.tsx index 0e61145d1bd..932d7ba7fa9 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/AdvancedOptions.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/AdvancedOptions.tsx @@ -6,6 +6,7 @@ import Icon from '@/Components/Icon/Icon' import { useApplication } from '../ApplicationProvider' import ServerPicker from './ServerPicker/ServerPicker' import { DefaultHost } from '@standardnotes/snjs' +import { c } from 'ttag' type Props = { disabled?: boolean @@ -50,7 +51,7 @@ const AdvancedOptions: FunctionComponent = ({ if (!identifier) { if (privateUsername?.length > 0) { - application.alerts.alert('Unable to compute private username.').catch(console.error) + application.alerts.alert(c('Error').t`Unable to compute private username.`).catch(console.error) } return } @@ -112,7 +113,7 @@ const AdvancedOptions: FunctionComponent = ({ onClick={toggleShowAdvanced} >
- Advanced options + {c('Action').t`Advanced options`}
@@ -124,12 +125,17 @@ const AdvancedOptions: FunctionComponent = ({
- +
@@ -140,7 +146,7 @@ const AdvancedOptions: FunctionComponent = ({ className={{ container: 'mb-2' }} left={[]} type="text" - placeholder="Username" + placeholder={c('Label').t`Username`} value={privateUsername} onChange={handlePrivateUsernameNameChange} disabled={disabled || isRecoveryCodes} @@ -154,7 +160,7 @@ const AdvancedOptions: FunctionComponent = ({
= ({ href="https://standardnotes.com/help/security" target="_blank" rel="noopener noreferrer" - title="Learn more" + title={c('Action').t`Learn more`} > @@ -174,7 +180,7 @@ const AdvancedOptions: FunctionComponent = ({
= ({ className={{ container: 'mb-2' }} left={[]} type="text" - placeholder="Recovery code" + placeholder={c('Label').t`Recovery code`} value={recoveryCodes} onChange={handleRecoveryCodesChange} disabled={disabled} diff --git a/packages/web/src/javascripts/Components/AccountMenu/ConfirmNoMergeDialog.tsx b/packages/web/src/javascripts/Components/AccountMenu/ConfirmNoMergeDialog.tsx index 7888be2173d..e1719a591d8 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/ConfirmNoMergeDialog.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/ConfirmNoMergeDialog.tsx @@ -2,6 +2,7 @@ import AlertDialog from '@/Components/AlertDialog/AlertDialog' import Button from '@/Components/Button/Button' import Icon from '@/Components/Icon/Icon' import { FunctionComponent } from 'react' +import { c } from 'ttag' type Props = { onClose: () => void @@ -12,7 +13,7 @@ const ConfirmNoMergeDialog: FunctionComponent = ({ onClose, onConfirm }) return (
- Delete local data? + {c('Title').t`Delete local data?`} @@ -20,18 +21,18 @@ const ConfirmNoMergeDialog: FunctionComponent = ({ onClose, onConfirm })

- You have chosen not to merge your local data. If you proceed, your local notes and tags will be permanently - deleted and replaced with data from your account. This action cannot be undone. + {c('Info') + .t`You have chosen not to merge your local data. If you proceed, your local notes and tags will be permanently deleted and replaced with data from your account. This action cannot be undone.`}

- Are you sure you want to continue without merging? + {c('Info').t`Are you sure you want to continue without merging?`}

- +
diff --git a/packages/web/src/javascripts/Components/AccountMenu/MergeLocalDataCheckbox.tsx b/packages/web/src/javascripts/Components/AccountMenu/MergeLocalDataCheckbox.tsx index cd2e10b2c8a..39e1826af2a 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/MergeLocalDataCheckbox.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/MergeLocalDataCheckbox.tsx @@ -1,6 +1,7 @@ import Icon from '@/Components/Icon/Icon' import StyledTooltip from '@/Components/StyledTooltip/StyledTooltip' import { ChangeEventHandler, FunctionComponent } from 'react' +import { c } from 'ttag' type Props = { checked: boolean @@ -21,9 +22,10 @@ const MergeLocalDataCheckbox: FunctionComponent = ({ checked, onChange, d onChange={onChange} disabled={disabled} /> - Merge local data ({notesAndTagsCount} notes and tags) + {c('Option').t`Merge local data (${notesAndTagsCount} notes and tags)`} diff --git a/packages/web/src/javascripts/Components/AccountMenu/ServerPicker/ServerPicker.tsx b/packages/web/src/javascripts/Components/AccountMenu/ServerPicker/ServerPicker.tsx index 45f1ba51f44..4e91bad8f74 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/ServerPicker/ServerPicker.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/ServerPicker/ServerPicker.tsx @@ -6,6 +6,7 @@ import { useApplication } from '@/Components/ApplicationProvider' import { isDesktopApplication } from '@/Utils' import RadioButtonGroup from '@/Components/RadioButtonGroup/RadioButtonGroup' import { DefaultHost } from '@standardnotes/snjs' +import { c } from 'ttag' type Props = { className?: string @@ -49,7 +50,7 @@ const ServerPicker = ({ className }: Props) => { } else if (type === 'home server') { if (!application.homeServer) { application.alerts - .alert('Home server is not running. Please open the prefences and home server tab to start it.') + .alert(c('Error').t`Home server is not running. Please open the prefences and home server tab to start it.`) .catch(console.error) return @@ -58,7 +59,7 @@ const ServerPicker = ({ className }: Props) => { const homeServerUrl = await application.homeServer.getHomeServerUrl() if (!homeServerUrl) { application.alerts - .alert('Home server is not running. Please open the prefences and home server tab to start it.') + .alert(c('Error').t`Home server is not running. Please open the prefences and home server tab to start it.`) .catch(console.error) return @@ -71,9 +72,9 @@ const ServerPicker = ({ className }: Props) => { const options = useMemo( () => [ - { label: 'Default', value: 'standard' }, - { label: 'Custom', value: 'custom' }, - ].concat(isDesktopApplication() ? [{ label: 'Home Server', value: 'home server' }] : []) as { + { label: c('Option').t`Default`, value: 'standard' }, + { label: c('Option').t`Custom`, value: 'custom' }, + ].concat(isDesktopApplication() ? [{ label: c('Option').t`Home Server`, value: 'home server' }] : []) as { label: string value: ServerType }[], @@ -82,7 +83,7 @@ const ServerPicker = ({ className }: Props) => { return (
-
Sync Server
+
{c('Label').t`Sync Server`}
{currentType === 'custom' && ( Date: Thu, 23 Apr 2026 22:50:13 -0300 Subject: [PATCH 2/4] chore: set ttag for account menu and session actions --- .../AccountMenu/GeneralAccountMenu.tsx | 28 ++++++------- .../Components/AccountMenu/User.tsx | 8 ++-- .../WorkspaceSwitcherMenu.tsx | 9 +++-- .../WorkspaceSwitcherOption.tsx | 5 ++- .../ConfirmDeleteAccountModal.tsx | 9 +++-- .../ConfirmSignoutModal.tsx | 21 +++++----- .../Panes/Account/ChangeEmail/ChangeEmail.tsx | 39 +++++++++++++------ .../Account/ChangeEmail/ChangeEmailForm.tsx | 5 ++- .../ChangeEmail/ChangeEmailSuccess.tsx | 7 ++-- .../Panes/Account/ClearSessionDataView.tsx | 8 ++-- .../Preferences/Panes/Account/Credentials.tsx | 22 +++++------ .../Panes/Account/DeleteAccount.tsx | 8 ++-- .../Preferences/Panes/Account/Email/Email.tsx | 23 +++++------ .../Preferences/Panes/Account/SignOutView.tsx | 17 ++++---- .../Preferences/Panes/Account/Sync.tsx | 13 ++++--- .../Panes/Backups/EmailBackups.tsx | 2 +- .../Preferences/Panes/Security/Privacy.tsx | 2 +- .../web/src/javascripts/Constants/Strings.ts | 28 +++++++------ 18 files changed, 145 insertions(+), 109 deletions(-) diff --git a/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx b/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx index 1e5ff811427..6eb132811f7 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx @@ -47,7 +47,7 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, } }) .catch(() => { - application.alerts.alert(STRING_GENERIC_SYNC_ERROR).catch(console.error) + application.alerts.alert(STRING_GENERIC_SYNC_ERROR()).catch(console.error) }) .finally(() => { setIsSyncingInProgress(false) @@ -69,9 +69,9 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, }, [application]) const openEmail = useCallback(() => { - const subject = 'Standard Notes Feedback' + const subject = c('MailtoSubject').t`Standard Notes Feedback` - const body = `App Version: ${application.version}` + const body = c('MailtoBody').t`App Version: ${application.version}` application.device.openUrl( `mailto:help@standardnotes.com?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`, @@ -103,7 +103,7 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, return ( <>
-
Account
+
{c('Title').t`Account`}
@@ -111,7 +111,7 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, {user ? ( <>
-
You're signed in as:
+
{c('Info').t`You're signed in as:`}
{user.email}
{application.getHost.execute().getValue()}
@@ -119,13 +119,13 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, {isSyncingInProgress ? (
- Syncing... + {c('Status').t`Syncing...`}
) : (
-
Last synced:
+
{c('Label').t`Last synced:`}
{lastSyncDate}
@@ -161,7 +161,7 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, {user ? ( - Account settings + {c('Action').t`Account settings`} ) : ( <> @@ -182,18 +182,18 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, }} > - Import + {c('Action').t`Import`} {application.isNativeMobileWeb() && ( - Email us + {c('Action').t`Email us`} )}
- Help & feedback + {c('Action').t`Help & feedback`}
v{application.version}
@@ -205,7 +205,7 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, }} > - Keyboard shortcuts + {c('Action').t`Keyboard shortcuts`} {keyboardShortcutsHelpShortcut && ( )} @@ -216,7 +216,7 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, }} > - Command palette + {c('Action').t`Command palette`} {commandPaletteShortcut && ( )} @@ -228,7 +228,7 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, - Sign out workspace + {c('Action').t`Sign out workspace`} ) : null} diff --git a/packages/web/src/javascripts/Components/AccountMenu/User.tsx b/packages/web/src/javascripts/Components/AccountMenu/User.tsx index 47a675eb872..7acd723443d 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/User.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/User.tsx @@ -1,6 +1,7 @@ import { observer } from 'mobx-react-lite' import { User as UserType } from '@standardnotes/snjs' import { useApplication } from '../ApplicationProvider' +import { c } from 'ttag' const User = () => { const application = useApplication() @@ -12,9 +13,10 @@ const User = () => {
{application.syncStatusController.errorMessage && (
-
Sync Unreachable
+
{c('Title').t`Sync Unreachable`}
- Hmm...we can't seem to sync your account. The reason: {application.syncStatusController.errorMessage} + {c('Error') + .t`Hmm...we can't seem to sync your account. The reason: ${application.syncStatusController.errorMessage}`}
{ rel="noopener" target="_blank" > - Need help? + {c('Action').t`Need help?`}
)} diff --git a/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu.tsx b/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu.tsx index e601fc897d9..30d5535f716 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu.tsx @@ -8,6 +8,7 @@ import MenuItem from '@/Components/Menu/MenuItem' import WorkspaceMenuItem from './WorkspaceMenuItem' import { useApplication } from '@/Components/ApplicationProvider' import MenuSection from '@/Components/Menu/MenuSection' +import { c } from 'ttag' type Props = { mainApplicationGroup: WebApplicationGroup @@ -42,9 +43,9 @@ const WorkspaceSwitcherMenu: FunctionComponent = ({ const signoutAll = useCallback(async () => { const confirmed = await application.alerts.confirm( - 'Are you sure you want to sign out of all workspaces on this device?', + c('Info').t`Are you sure you want to sign out of all workspaces on this device?`, undefined, - 'Sign out all', + c('Action').t`Sign out all`, ButtonType.Danger, ) if (!confirmed) { @@ -86,12 +87,12 @@ const WorkspaceSwitcherMenu: FunctionComponent = ({ - Add another workspace + {c('Action').t`Add another workspace`} {!hideWorkspaceOptions && ( - Sign out all workspaces + {c('Action').t`Sign out all workspaces`} )} diff --git a/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx b/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx index 845b3b7f089..4228eff3490 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherOption.tsx @@ -7,6 +7,7 @@ import WorkspaceSwitcherMenu from './WorkspaceSwitcherMenu' import MenuItem from '@/Components/Menu/MenuItem' import Popover from '@/Components/Popover/Popover' import { MenuItemIconSize } from '@/Constants/TailwindClassNames' +import { c } from 'ttag' type Props = { mainApplicationGroup: WebApplicationGroup @@ -25,12 +26,12 @@ const WorkspaceSwitcherOption: FunctionComponent = ({ mainApplicationGrou
- Switch workspace + {c('Action').t`Switch workspace`}
{ return (
- Delete account? + {c('Title').t`Delete account?`}
-

{STRING_DELETE_ACCOUNT_CONFIRMATION}

+

{STRING_DELETE_ACCOUNT_CONFIRMATION()}

diff --git a/packages/web/src/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx b/packages/web/src/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx index bd75a7a80f7..fadc31d4682 100644 --- a/packages/web/src/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx +++ b/packages/web/src/javascripts/Components/ConfirmSignoutModal/ConfirmSignoutModal.tsx @@ -8,6 +8,7 @@ import Button from '@/Components/Button/Button' import Icon from '../Icon/Icon' import AlertDialog from '../AlertDialog/AlertDialog' import HorizontalSeparator from '../Shared/HorizontalSeparator' +import { c } from 'ttag' type Props = { application: WebApplication @@ -37,22 +38,21 @@ const ConfirmSignoutModal: FunctionComponent = ({ application, applicatio return (
- Sign out workspace? + {c('Title').t`Sign out workspace?`}
-

{STRING_SIGN_OUT_CONFIRMATION}

+

{STRING_SIGN_OUT_CONFIRMATION()}

{showWorkspaceWarning && ( <>

- Note: - Because you have other workspaces signed in, this sign out may leave logs and other metadata of your - session on this device. For a more robust sign out that performs a hard clear of all app-related data, - use the Sign out all workspaces option under Switch workspace. + {c('Label').t`Note:`} + {c('Info') + .t`Because you have other workspaces signed in, this sign out may leave logs and other metadata of your session on this device. For a more robust sign out that performs a hard clear of all app-related data, use the "Sign out all workspaces" option under "Switch workspace".`}

)} @@ -66,7 +66,8 @@ const ConfirmSignoutModal: FunctionComponent = ({ application, applicatio

- Local backups are enabled for this workspace. Review your backup files manually to decide what to keep. + {c('Info') + .t`Local backups are enabled for this workspace. Review your backup files manually to decide what to keep.`}

@@ -83,10 +84,10 @@ const ConfirmSignoutModal: FunctionComponent = ({ application, applicatio
diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmail.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmail.tsx index 91c72d418fd..feb613cc748 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmail.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmail.tsx @@ -1,4 +1,5 @@ import { FunctionComponent, useCallback, useMemo, useState } from 'react' +import { c } from 'ttag' import { WebApplication } from '@/Application/WebApplication' import { useBeforeUnload } from '@/Hooks/useBeforeUnload' import ChangeEmailForm from './ChangeEmailForm' @@ -6,9 +7,9 @@ import ChangeEmailSuccess from './ChangeEmailSuccess' import Modal, { ModalAction } from '@/Components/Modal/Modal' enum SubmitButtonTitles { - Default = 'Continue', - GeneratingKeys = 'Generating Keys...', - Finish = 'Finish', + Default, + GeneratingKeys, + Finish, } enum Steps { @@ -35,7 +36,7 @@ const ChangeEmail: FunctionComponent = ({ onCloseDialog, application }) = const validateCurrentPassword = useCallback(async () => { if (!currentPassword || currentPassword.length === 0) { - applicationAlertService.alert('Please enter your current password.').catch(console.error) + applicationAlertService.alert(c('Error').t`Please enter your current password.`).catch(console.error) return false } @@ -43,7 +44,7 @@ const ChangeEmail: FunctionComponent = ({ onCloseDialog, application }) = const success = await application.validateAccountPassword(currentPassword) if (!success) { applicationAlertService - .alert('The current password you entered is not correct. Please try again.') + .alert(c('Error').t`The current password you entered is not correct. Please try again.`) .catch(console.error) return false @@ -73,7 +74,9 @@ const ChangeEmail: FunctionComponent = ({ onCloseDialog, application }) = const dismiss = useCallback(() => { if (lockContinue) { - applicationAlertService.alert('Cannot close window until pending tasks are complete.').catch(console.error) + applicationAlertService + .alert(c('Error').t`Cannot close window until pending tasks are complete.`) + .catch(console.error) } else { onCloseDialog() } @@ -115,32 +118,46 @@ const ChangeEmail: FunctionComponent = ({ onCloseDialog, application }) = const handleDialogClose = useCallback(() => { if (lockContinue) { - applicationAlertService.alert('Cannot close window until pending tasks are complete.').catch(console.error) + applicationAlertService + .alert(c('Error').t`Cannot close window until pending tasks are complete.`) + .catch(console.error) } else { onCloseDialog() } }, [applicationAlertService, lockContinue, onCloseDialog]) + const submitButtonLabel = useMemo(() => { + switch (submitButtonTitle) { + case SubmitButtonTitles.GeneratingKeys: + return c('Action').t`Generating Keys...` + case SubmitButtonTitles.Finish: + return c('Action').t`Finish` + case SubmitButtonTitles.Default: + default: + return c('Action').t`Continue` + } + }, [submitButtonTitle]) + const modalActions = useMemo( (): ModalAction[] => [ { - label: 'Cancel', + label: c('Action').t`Cancel`, onClick: handleDialogClose, type: 'cancel', mobileSlot: 'left', }, { - label: submitButtonTitle, + label: submitButtonLabel, onClick: handleSubmit, type: 'primary', mobileSlot: 'right', }, ], - [handleDialogClose, handleSubmit, submitButtonTitle], + [handleDialogClose, handleSubmit, submitButtonLabel], ) return ( - +
{currentStep === Steps.InitialStep && ( diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailForm.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailForm.tsx index 08b2602f235..aa46b10b97a 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailForm.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmailForm.tsx @@ -1,6 +1,7 @@ import DecoratedInput from '@/Components/Input/DecoratedInput' import DecoratedPasswordInput from '@/Components/Input/DecoratedPasswordInput' import { Dispatch, SetStateAction, FunctionComponent } from 'react' +import { c } from 'ttag' type Props = { setNewEmail: Dispatch> @@ -14,7 +15,7 @@ const ChangeEmailForm: FunctionComponent = ({ setNewEmail, setCurrentPass
= ({ setNewEmail, setCurrentPass
{ return (
-
Your email has been successfully changed.
+
{c('Info').t`Your email has been successfully changed.`}

- Please ensure you are running the latest version of Standard Notes on all platforms to ensure maximum - compatibility. + {c('Info') + .t`Please ensure you are running the latest version of Standard Notes on all platforms to ensure maximum compatibility.`}

) diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Account/ClearSessionDataView.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Account/ClearSessionDataView.tsx index 25c7b012803..7e1fa9e6871 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Account/ClearSessionDataView.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Account/ClearSessionDataView.tsx @@ -5,6 +5,7 @@ import { Title, Text } from '../../PreferencesComponents/Content' import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' import { useApplication } from '@/Components/ApplicationProvider' +import { c } from 'ttag' const ClearSessionDataView: FunctionComponent = () => { const application = useApplication() @@ -12,11 +13,12 @@ const ClearSessionDataView: FunctionComponent = () => { return ( - Clear workspace - Remove all data related to the current workspace from the application. + {c('Title').t`Clear workspace`} + {c('Info') + .t`Remove all data related to the current workspace from the application.`}
- This workspace - Remove all data related to the current workspace from the application. + {c('Subtitle').t`This workspace`} + {c('Info').t`Remove all data related to the current workspace from the application.`}
-
Your password has been successfully changed.
-

Ensure you are running the latest version of Standard Notes on all platforms for maximum compatibility.

+
{c('Info').t`Your password has been successfully changed.`}
+

+ {c('Info') + .t`Ensure you are running the latest version of Standard Notes on all platforms for maximum compatibility.`} +

diff --git a/packages/web/src/javascripts/Components/PasswordWizard/PasswordStep.tsx b/packages/web/src/javascripts/Components/PasswordWizard/PasswordStep.tsx index 89bd666c841..9cf1fdf92d4 100644 --- a/packages/web/src/javascripts/Components/PasswordWizard/PasswordStep.tsx +++ b/packages/web/src/javascripts/Components/PasswordWizard/PasswordStep.tsx @@ -1,4 +1,5 @@ import { useState } from 'react' +import { c } from 'ttag' import DecoratedPasswordInput from '../Input/DecoratedPasswordInput' export const PasswordStep = ({ @@ -33,7 +34,7 @@ export const PasswordStep = ({
void } +enum ContinueTitle { + Default, + Generating, + Finish, +} + type State = { - continueTitle: string + continueTitle: ContinueTitle formData: FormData isContinuing?: boolean lockContinue?: boolean @@ -22,10 +29,6 @@ type State = { step: Steps } -const DEFAULT_CONTINUE_TITLE = 'Continue' -const GENERATING_CONTINUE_TITLE = 'Generating Keys...' -const FINISH_CONTINUE_TITLE = 'Finish' - enum Steps { PreprocessingStep = 'preprocessing-step', PasswordStep = 'password-step', @@ -46,7 +49,7 @@ class PasswordWizard extends AbstractComponent { const baseState = { formData: {}, - continueTitle: DEFAULT_CONTINUE_TITLE, + continueTitle: ContinueTitle.Default, } if (props.application.featuresController.isVaultsEnabled()) { @@ -82,7 +85,7 @@ class PasswordWizard extends AbstractComponent { resetContinueState() { this.setState({ showSpinner: false, - continueTitle: DEFAULT_CONTINUE_TITLE, + continueTitle: ContinueTitle.Default, isContinuing: false, }) } @@ -109,7 +112,7 @@ class PasswordWizard extends AbstractComponent { this.setState({ isContinuing: true, showSpinner: true, - continueTitle: GENERATING_CONTINUE_TITLE, + continueTitle: ContinueTitle.Generating, }) const valid = await this.validateCurrentPassword() @@ -127,7 +130,7 @@ class PasswordWizard extends AbstractComponent { this.setState({ isContinuing: false, showSpinner: false, - continueTitle: FINISH_CONTINUE_TITLE, + continueTitle: ContinueTitle.Finish, step: Steps.FinishStep, }) } @@ -136,16 +139,18 @@ class PasswordWizard extends AbstractComponent { const currentPassword = this.state.formData.currentPassword const newPass = this.state.formData.newPassword if (!currentPassword || currentPassword.length === 0) { - this.application.alerts.alert('Please enter your current password.').catch(console.error) + this.application.alerts.alert(c('Error').t`Please enter your current password.`).catch(console.error) return false } if (!newPass || newPass.length === 0) { - this.application.alerts.alert('Please enter a new password.').catch(console.error) + this.application.alerts.alert(c('Error').t`Please enter a new password.`).catch(console.error) return false } if (newPass !== this.state.formData.newPasswordConfirmation) { - this.application.alerts.alert('Your new password does not match its confirmation.').catch(console.error) + this.application.alerts + .alert(c('Error').t`Your new password does not match its confirmation.`) + .catch(console.error) this.setFormDataState({ status: undefined, }).catch(console.error) @@ -154,7 +159,7 @@ class PasswordWizard extends AbstractComponent { if (!this.application.sessions.getUser()?.email) { this.application.alerts - .alert("We don't have your email stored. Please sign out then log back in to fix this issue.") + .alert(c('Error').t`We don't have your email stored. Please sign out then log back in to fix this issue.`) .catch(console.error) this.setFormDataState({ status: undefined, @@ -165,7 +170,7 @@ class PasswordWizard extends AbstractComponent { const success = await this.application.validateAccountPassword(this.state.formData.currentPassword as string) if (!success) { this.application.alerts - .alert('The current password you entered is not correct. Please try again.') + .alert(c('Error').t`The current password you entered is not correct. Please try again.`) .catch(console.error) } return success @@ -180,7 +185,7 @@ class PasswordWizard extends AbstractComponent { }) await this.setFormDataState({ - status: 'Processing encryption keys…', + status: c('Status').t`Processing encryption keys…`, }) const newPassword = this.state.formData.newPassword @@ -197,13 +202,13 @@ class PasswordWizard extends AbstractComponent { if (!success) { this.setFormDataState({ - status: 'Unable to process your password. Please try again.', + status: c('Status').t`Unable to process your password. Please try again.`, }).catch(console.error) } else { this.setState({ formData: { ...this.state.formData, - status: 'Successfully changed password.', + status: c('Status').t`Successfully changed password.`, }, }) } @@ -212,7 +217,9 @@ class PasswordWizard extends AbstractComponent { dismiss = () => { if (this.state.processing) { - this.application.alerts.alert('Cannot close window until pending tasks are complete.').catch(console.error) + this.application.alerts + .alert(c('Error').t`Cannot close window until pending tasks are complete.`) + .catch(console.error) } else { this.props.dismissModal() } @@ -266,25 +273,37 @@ class PasswordWizard extends AbstractComponent { } } + continueLabel(): string { + switch (this.state.continueTitle) { + case ContinueTitle.Generating: + return c('Action').t`Generating Keys...` + case ContinueTitle.Finish: + return c('Action').t`Finish` + case ContinueTitle.Default: + default: + return c('Action').t`Continue` + } + } + override render() { return (
) : ( - this.state.continueTitle + this.continueLabel() ), onClick: this.nextStep, type: 'primary', diff --git a/packages/web/src/javascripts/Components/PasswordWizard/PreprocessingStep.tsx b/packages/web/src/javascripts/Components/PasswordWizard/PreprocessingStep.tsx index e61fcde039b..7a110e30005 100644 --- a/packages/web/src/javascripts/Components/PasswordWizard/PreprocessingStep.tsx +++ b/packages/web/src/javascripts/Components/PasswordWizard/PreprocessingStep.tsx @@ -1,6 +1,7 @@ import Spinner from '../Spinner/Spinner' import { useApplication } from '../ApplicationProvider' import { useCallback, useEffect, useState } from 'react' +import { c } from 'ttag' export const PreprocessingStep = ({ onContinue, @@ -76,7 +77,7 @@ export const PreprocessingStep = ({ return (
-

Checking for data conflicts...

+

{c('Status').t`Checking for data conflicts...`}

) } @@ -88,9 +89,8 @@ export const PreprocessingStep = ({ return (

- You have pending vault invites. Changing your password will delete these invites. It is recommended you accept - or decline these invites before changing your password. If you choose to continue, these invites will be - deleted. + {c('Info') + .t`You have pending vault invites. Changing your password will delete these invites. It is recommended you accept or decline these invites before changing your password. If you choose to continue, these invites will be deleted.`}

) diff --git a/packages/web/src/javascripts/Constants/Strings.ts b/packages/web/src/javascripts/Constants/Strings.ts index fb158ced1d4..eee0f4cfaab 100644 --- a/packages/web/src/javascripts/Constants/Strings.ts +++ b/packages/web/src/javascripts/Constants/Strings.ts @@ -68,7 +68,8 @@ export const STRING_IMPORT_SUCCESS = 'Your data has been successfully imported.' export const STRING_REMOVE_PASSCODE_CONFIRMATION = 'Are you sure you want to remove your application passcode?' export const STRING_REMOVE_PASSCODE_OFFLINE_ADDENDUM = ' This will remove encryption from your local data.' export const STRING_NON_MATCHING_PASSCODES = 'The two passcodes you entered do not match. Please try again.' -export const STRING_NON_MATCHING_PASSWORDS = 'The two passwords you entered do not match. Please try again.' +export const STRING_NON_MATCHING_PASSWORDS = () => + c('Error').t`The two passwords you entered do not match. Please try again.` export const STRING_GENERATING_LOGIN_KEYS = 'Generating Login Keys...' export const STRING_GENERATING_REGISTER_KEYS = 'Generating Account Keys...' export const STRING_INVALID_IMPORT_FILE = 'Unable to open file. Ensure it is a proper JSON file and try again.' @@ -84,8 +85,9 @@ export const STRING_UNSUPPORTED_BACKUP_FILE_VERSION = export const STRING_FAILED_PASSWORD_CHANGE = 'There was an error re-encrypting your items. Your password was changed, but not all your items were properly re-encrypted and synced. You should try syncing again. If all else fails, you should restore your notes from backup.' -export const STRING_CONFIRM_APP_QUIT_DURING_UPGRADE = - 'The encryption upgrade is in progress. You may lose data if you quit the app. ' + 'Are you sure you want to quit?' +export const STRING_CONFIRM_APP_QUIT_DURING_UPGRADE = () => + c('Info') + .t`The encryption upgrade is in progress. You may lose data if you quit the app. Are you sure you want to quit?` export const STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_CHANGE = 'A passcode change is in progress. You may lose data if you quit the app. ' + 'Are you sure you want to quit?' @@ -93,13 +95,11 @@ export const STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_CHANGE = export const STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL = 'A passcode removal is in progress. You may lose data if you quit the app. ' + 'Are you sure you want to quit?' -export const STRING_UPGRADE_ACCOUNT_CONFIRM_TITLE = 'Encryption upgrade available' -export const STRING_UPGRADE_ACCOUNT_CONFIRM_TEXT = - 'Encryption version 004 is available. ' + - 'This version strengthens the encryption algorithms your account and ' + - 'local storage use. To learn more about this upgrade, visit our ' + - 'Security Upgrade page.' -export const STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON = 'Upgrade' +export const STRING_UPGRADE_ACCOUNT_CONFIRM_TITLE = () => c('Title').t`Encryption upgrade available` +export const STRING_UPGRADE_ACCOUNT_CONFIRM_TEXT = () => + c('Info') + .t`Encryption version 004 is available. This version strengthens the encryption algorithms your account and local storage use. To learn more about this upgrade, visit our Security Upgrade page.` +export const STRING_UPGRADE_ACCOUNT_CONFIRM_BUTTON = () => c('Action').t`Upgrade` export const STRING_REMOVE_OFFLINE_KEY_CONFIRMATION = 'This will delete the previously saved offline key.' From b47dc490f4aa35deca21da326c21b1c6ca207643 Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Thu, 23 Apr 2026 23:08:51 -0300 Subject: [PATCH 4/4] chore: set ttag for import and export account data --- .../Components/ImportModal/ImportModal.tsx | 17 ++++---- .../ImportModal/ImportModalFileItem.tsx | 40 +++++++++++-------- .../Components/ImportModal/InitialPage.tsx | 9 +++-- .../Preferences/Panes/Backups/DataBackups.tsx | 23 ++++++----- .../web/src/javascripts/Constants/Strings.ts | 18 +++++---- 5 files changed, 61 insertions(+), 46 deletions(-) diff --git a/packages/web/src/javascripts/Components/ImportModal/ImportModal.tsx b/packages/web/src/javascripts/Components/ImportModal/ImportModal.tsx index 930b00d0f0b..f79460ef851 100644 --- a/packages/web/src/javascripts/Components/ImportModal/ImportModal.tsx +++ b/packages/web/src/javascripts/Components/ImportModal/ImportModal.tsx @@ -13,6 +13,7 @@ import ItemSelectionDropdown from '../ItemSelectionDropdown/ItemSelectionDropdow import { ContentType, SNTag } from '@standardnotes/snjs' import Button from '../Button/Button' import { ClassicFileReader } from '@standardnotes/filepicker' +import { c } from 'ttag' const ImportModal = ({ importModalController }: { importModalController: ImportModalController }) => { const application = useApplication() @@ -41,7 +42,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM const modalActions: ModalAction[] = useMemo( () => [ { - label: 'Import', + label: c('Action').t`Import`, type: 'primary', onClick: parseAndImport, hidden: !isReadyToImport, @@ -49,7 +50,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM disabled: !isReadyToImport || (addImportsToTag && !shouldCreateTag && !existingTagForImports), }, { - label: importSuccessOrError ? 'Close' : 'Cancel', + label: importSuccessOrError ? c('Action').t`Close` : c('Action').t`Cancel`, type: 'cancel', onClick: close, mobileSlot: 'left', @@ -77,7 +78,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM return ( - +
{!files.length && } {files.length > 0 && ( @@ -101,7 +102,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM }} small > - Add files + {c('Action').t`Add files`} )} @@ -110,7 +111,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM {files.length > 0 && (
- Add all imported notes to tag + {c('Label').t`Add all imported notes to tag`} {addImportsToTag && ( <> @@ -124,7 +125,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM setShouldCreateTag(true) }} /> - Create new tag + {c('Label').t`Create new tag`}
@@ -138,7 +139,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM setShouldCreateTag(false) }} /> - Add to existing tag + {c('Label').t`Add to existing tag`} {existingTagForImports && ( setExistingTagForImports(tag as SNTag)} - placeholder="Select tag to add imported notes to..." + placeholder={c('Placeholder').t`Select tag to add imported notes to...`} contentTypes={[ContentType.TYPES.Tag]} />
diff --git a/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx b/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx index 6f3f18ad7e9..1e1b17c1c04 100644 --- a/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx +++ b/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx @@ -1,10 +1,11 @@ import { ImportModalController, ImportModalFile } from '@/Components/ImportModal/ImportModalController' -import { classNames, ContentType, pluralize } from '@standardnotes/snjs' +import { classNames, ContentType } from '@standardnotes/snjs' import { ConversionResult, Importer } from '@standardnotes/ui-services' import { observer } from 'mobx-react-lite' import { useCallback, useEffect, useState } from 'react' import Icon from '../Icon/Icon' import { Disclosure, DisclosureContent, DisclosureProvider } from '@ariakit/react' +import { c, msgid, ngettext } from 'ttag' const NoteImportTypeColors: Record = { evernote: 'bg-[#14cc45] text-[#000]', @@ -49,12 +50,17 @@ const countSuccessfulItemsByGroup = (successful: ConversionResult['successful']) } const ImportErroredAccordion = ({ errored }: { errored: ConversionResult['errored'] }) => { + const count = errored.length return (
- Could not import {errored.length} {pluralize(errored.length, 'item', 'items')} (click for details) + {ngettext( + msgid`Could not import ${count} item (click for details)`, + `Could not import ${count} items (click for details)`, + count, + )}
@@ -77,9 +83,9 @@ const ImportFinishedStatus = ({ file }: { file: ImportModalFile }) => { const { notes, tags, files } = countSuccessfulItemsByGroup(file.successful) - const notesStatus = notes > 0 ? `${notes} ${pluralize(notes, 'note', 'notes')}` : '' - const tagsStatus = tags > 0 ? `${tags} ${pluralize(tags, 'tag', 'tags')}` : '' - const filesStatus = files > 0 ? `${files} ${pluralize(files, 'file', 'files')}` : '' + const notesStatus = notes > 0 ? ngettext(msgid`${notes} note`, `${notes} notes`, notes) : '' + const tagsStatus = tags > 0 ? ngettext(msgid`${tags} tag`, `${tags} tags`, tags) : '' + const filesStatus = files > 0 ? ngettext(msgid`${files} file`, `${files} files`, files) : '' const status = [notesStatus, tagsStatus, filesStatus].filter(Boolean).join(', ') return ( @@ -87,7 +93,7 @@ const ImportFinishedStatus = ({ file }: { file: ImportModalFile }) => { {file.successful.length > 0 && (
- {status} imported + {c('Info').t`${status} imported`}
)} {file.errored.length > 0 && } @@ -151,14 +157,16 @@ const ImportModalFileItem = ({
{file.file.name}
{isDetectingService ? ( -
Detecting service...
+
{c('Status').t`Detecting service...`}
) : (
- {file.status === 'pending' && file.service && 'Ready to import'} - {file.status === 'pending' && !file.service && 'Could not auto-detect service. Please select manually.'} - {file.status === 'parsing' && 'Parsing...'} - {file.status === 'importing' && 'Importing...'} - {file.status === 'uploading-files' && 'Uploading and embedding files...'} + {file.status === 'pending' && file.service && c('Status').t`Ready to import`} + {file.status === 'pending' && + !file.service && + c('Status').t`Could not auto-detect service. Please select manually.`} + {file.status === 'parsing' && c('Status').t`Parsing...`} + {file.status === 'importing' && c('Status').t`Importing...`} + {file.status === 'uploading-files' && c('Status').t`Uploading and embedding files...`} {file.status === 'error' && file.error.message}
@@ -192,7 +200,7 @@ const ImportModalFileItem = ({ )} -
or import from:
+
{c('Info').t`or import from:`}
diff --git a/packages/web/src/javascripts/Constants/Strings.ts b/packages/web/src/javascripts/Constants/Strings.ts index eee0f4cfaab..4e8cb3c50fa 100644 --- a/packages/web/src/javascripts/Constants/Strings.ts +++ b/packages/web/src/javascripts/Constants/Strings.ts @@ -64,7 +64,7 @@ export const STRING_LOCAL_ENC_ENABLED = 'Encryption is enabled. Your data is encrypted using your passcode before it is saved to your device storage.' export const STRING_ENC_NOT_ENABLED = 'Encryption is not enabled. Sign in, register, or add a passcode lock to enable encryption.' -export const STRING_IMPORT_SUCCESS = 'Your data has been successfully imported.' +export const STRING_IMPORT_SUCCESS = () => c('Info').t`Your data has been successfully imported.` export const STRING_REMOVE_PASSCODE_CONFIRMATION = 'Are you sure you want to remove your application passcode?' export const STRING_REMOVE_PASSCODE_OFFLINE_ADDENDUM = ' This will remove encryption from your local data.' export const STRING_NON_MATCHING_PASSCODES = 'The two passcodes you entered do not match. Please try again.' @@ -72,14 +72,18 @@ export const STRING_NON_MATCHING_PASSWORDS = () => c('Error').t`The two passwords you entered do not match. Please try again.` export const STRING_GENERATING_LOGIN_KEYS = 'Generating Login Keys...' export const STRING_GENERATING_REGISTER_KEYS = 'Generating Account Keys...' -export const STRING_INVALID_IMPORT_FILE = 'Unable to open file. Ensure it is a proper JSON file and try again.' -export const STRING_IMPORTING_ZIP_FILE = - 'The file you selected is not a valid backup file. Please extract the contents of the zip file, then upload the contained .txt file.' +export const STRING_INVALID_IMPORT_FILE = () => + c('Error').t`Unable to open file. Ensure it is a proper JSON file and try again.` +export const STRING_IMPORTING_ZIP_FILE = () => + c('Error') + .t`The file you selected is not a valid backup file. Please extract the contents of the zip file, then upload the contained .txt file.` export function StringImportError(errorCount: number) { - return `Import complete. ${errorCount} items were not imported because there was an error decrypting them. Make sure the password is correct and try again.` + return c('Info') + .t`Import complete. ${errorCount} items were not imported because there was an error decrypting them. Make sure the password is correct and try again.` } -export const STRING_UNSUPPORTED_BACKUP_FILE_VERSION = - 'This backup file was created using an unsupported version of the application and cannot be imported here. Please update your application and try again.' +export const STRING_UNSUPPORTED_BACKUP_FILE_VERSION = () => + c('Error') + .t`This backup file was created using an unsupported version of the application and cannot be imported here. Please update your application and try again.` /** @password_change */ export const STRING_FAILED_PASSWORD_CHANGE =