chore: ttag setup and authentication flow localization (#2987)

* chore: add ttag dependency

* chore: wrap strings for authentication flows

* chore: fix lint errors
This commit is contained in:
Antonella Sgarlatta
2026-03-05 23:46:58 -03:00
committed by GitHub
parent 92b7be4221
commit 7f3d2d0c72
12 changed files with 111 additions and 57 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -120,6 +120,7 @@
"comlink": "^4.4.1", "comlink": "^4.4.1",
"fast-diff": "^1.3.0", "fast-diff": "^1.3.0",
"lexical": "0.38.1", "lexical": "0.38.1",
"ttag": "^1.8.12",
"unicode-script": "^1.2.0" "unicode-script": "^1.2.0"
} }
} }

View File

@@ -20,6 +20,7 @@ import { useCaptcha } from '@/Hooks/useCaptcha'
import { isErrorResponse } from '@standardnotes/snjs' import { isErrorResponse } from '@standardnotes/snjs'
import MergeLocalDataCheckbox from './MergeLocalDataCheckbox' import MergeLocalDataCheckbox from './MergeLocalDataCheckbox'
import ConfirmNoMergeDialog from './ConfirmNoMergeDialog' import ConfirmNoMergeDialog from './ConfirmNoMergeDialog'
import { c } from 'ttag'
type Props = { type Props = {
setMenuPane: (pane: AccountMenuPane) => void setMenuPane: (pane: AccountMenuPane) => void
@@ -165,9 +166,9 @@ const ConfirmPassword: FunctionComponent<Props> = ({ setMenuPane, email, passwor
const confirmPasswordForm = ( const confirmPasswordForm = (
<> <>
<div className="mb-3 px-3 text-sm"> <div className="mb-3 px-3 text-sm">
Because your notes are encrypted using your password,{' '} {c('Info').jt`Because your notes are encrypted using your password, ${(
<span className="text-danger">Standard Notes does not have a password reset option</span>. If you forget your <span className="text-danger">Standard Notes does not have a password reset option</span>
password, you will permanently lose access to your data. )}. If you forget your password, you will permanently lose access to your data.`}
</div> </div>
<form onSubmit={handleConfirmFormSubmit} className="mb-1 px-3"> <form onSubmit={handleConfirmFormSubmit} className="mb-1 px-3">
{!isRegistering && ( {!isRegistering && (
@@ -177,7 +178,7 @@ const ConfirmPassword: FunctionComponent<Props> = ({ setMenuPane, email, passwor
left={[<Icon type="password" className="text-neutral" />]} left={[<Icon type="password" className="text-neutral" />]}
onChange={handlePasswordChange} onChange={handlePasswordChange}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder="Confirm password" placeholder={c('Label').t`Confirm password`}
ref={passwordInputRef} ref={passwordInputRef}
value={confirmPassword} value={confirmPassword}
/> />
@@ -187,13 +188,13 @@ const ConfirmPassword: FunctionComponent<Props> = ({ setMenuPane, email, passwor
primary primary
fullWidth fullWidth
className="mb-3 mt-1" className="mb-3 mt-1"
label={isRegistering ? 'Creating account...' : 'Create account & sign in'} label={isRegistering ? c('Action').t`Creating account...` : c('Action').t`Create account & sign in`}
onClick={handleConfirmFormSubmit} onClick={handleConfirmFormSubmit}
disabled={isRegistering} disabled={isRegistering}
/> />
<Checkbox <Checkbox
name="is-ephemeral" name="is-ephemeral"
label="Stay signed in" label={c('Option').t`Stay signed in`}
checked={!isEphemeral} checked={!isEphemeral}
onChange={handleEphemeralChange} onChange={handleEphemeralChange}
disabled={isRegistering} disabled={isRegistering}
@@ -215,13 +216,15 @@ const ConfirmPassword: FunctionComponent<Props> = ({ setMenuPane, email, passwor
<div className="mb-3 mt-1 flex items-center px-3"> <div className="mb-3 mt-1 flex items-center px-3">
<IconButton <IconButton
icon="arrow-left" icon="arrow-left"
title="Go back" title={c('Action').t`Go back`}
className="mr-2 flex p-0 text-neutral" className="mr-2 flex p-0 text-neutral"
onClick={handleGoBack} onClick={handleGoBack}
focusable={true} focusable={true}
disabled={isRegistering} disabled={isRegistering}
/> />
<div className="text-base font-bold">{captchaURL ? 'Human verification' : 'Confirm password'}</div> <div className="text-base font-bold">
{captchaURL ? c('Title').t`Human verification` : c('Title').t`Confirm password`}
</div>
</div> </div>
{captchaURL ? <div className="p-[10px]">{captchaIframe}</div> : confirmPasswordForm} {captchaURL ? <div className="p-[10px]">{captchaIframe}</div> : confirmPasswordForm}
{showNoMergeConfirmation && ( {showNoMergeConfirmation && (

View File

@@ -16,6 +16,7 @@ import Icon from '@/Components/Icon/Icon'
import IconButton from '@/Components/Button/IconButton' import IconButton from '@/Components/Button/IconButton'
import AdvancedOptions from './AdvancedOptions' import AdvancedOptions from './AdvancedOptions'
import HorizontalSeparator from '../Shared/HorizontalSeparator' import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { c } from 'ttag'
type Props = { type Props = {
setMenuPane: (pane: AccountMenuPane) => void setMenuPane: (pane: AccountMenuPane) => void
@@ -101,12 +102,12 @@ const CreateAccount: FunctionComponent<Props> = ({ setMenuPane, email, setEmail,
<div className="mb-3 mt-1 flex items-center px-3"> <div className="mb-3 mt-1 flex items-center px-3">
<IconButton <IconButton
icon="arrow-left" icon="arrow-left"
title="Go back" title={c('Action').t`Go back`}
className="mr-2 flex p-0 text-neutral" className="mr-2 flex p-0 text-neutral"
onClick={handleClose} onClick={handleClose}
focusable={true} focusable={true}
/> />
<div className="text-base font-bold">Create account</div> <div className="text-base font-bold">{c('Title').t`Create account`}</div>
</div> </div>
<form onSubmit={handleRegisterFormSubmit} className="mb-1 px-3"> <form onSubmit={handleRegisterFormSubmit} className="mb-1 px-3">
<DecoratedInput <DecoratedInput
@@ -115,7 +116,7 @@ const CreateAccount: FunctionComponent<Props> = ({ setMenuPane, email, setEmail,
left={[<Icon type="email" className="text-neutral" />]} left={[<Icon type="email" className="text-neutral" />]}
onChange={handleEmailChange} onChange={handleEmailChange}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder="Email" placeholder={c('Label').t`Email`}
ref={emailInputRef} ref={emailInputRef}
type="email" type="email"
value={email} value={email}
@@ -126,11 +127,17 @@ const CreateAccount: FunctionComponent<Props> = ({ setMenuPane, email, setEmail,
left={[<Icon type="password" className="text-neutral" />]} left={[<Icon type="password" className="text-neutral" />]}
onChange={handlePasswordChange} onChange={handlePasswordChange}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder="Password" placeholder={c('Label').t`Password`}
ref={passwordInputRef} ref={passwordInputRef}
value={password} value={password}
/> />
<Button className="mt-1" label="Next" primary onClick={handleRegisterFormSubmit} fullWidth={true} /> <Button
className="mt-1"
label={c('Action').t`Next`}
primary
onClick={handleRegisterFormSubmit}
fullWidth={true}
/>
</form> </form>
<HorizontalSeparator classes="my-2" /> <HorizontalSeparator classes="my-2" />
<AdvancedOptions onPrivateUsernameModeChange={onPrivateUsernameChange} /> <AdvancedOptions onPrivateUsernameModeChange={onPrivateUsernameChange} />

View File

@@ -15,6 +15,7 @@ import { useApplication } from '../ApplicationProvider'
import MenuSection from '../Menu/MenuSection' import MenuSection from '../Menu/MenuSection'
import { TOGGLE_COMMAND_PALETTE, TOGGLE_KEYBOARD_SHORTCUTS_MODAL, isMobilePlatform } from '@standardnotes/ui-services' import { TOGGLE_COMMAND_PALETTE, TOGGLE_KEYBOARD_SHORTCUTS_MODAL, isMobilePlatform } from '@standardnotes/ui-services'
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator' import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
import { c } from 'ttag'
type Props = { type Props = {
mainApplicationGroup: WebApplicationGroup mainApplicationGroup: WebApplicationGroup
@@ -138,12 +139,12 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({ setMenuPane, closeMenu,
<> <>
<div className="mb-1 px-4 md:px-3"> <div className="mb-1 px-4 md:px-3">
<div className="mb-3 text-base text-foreground lg:text-sm"> <div className="mb-3 text-base text-foreground lg:text-sm">
Youre offline. Sign in to sync your notes and preferences across all your devices and enable end-to-end {c('Info')
encryption. .t`Youre offline. Sign in to sync your notes and preferences across all your devices and enable end-to-end encryption.`}
</div> </div>
<div className="flex items-center text-passive-1"> <div className="flex items-center text-passive-1">
<Icon type="cloud-off" className={`mr-2 ${MenuItemIconSize}`} /> <Icon type="cloud-off" className={`mr-2 ${MenuItemIconSize}`} />
<span className="text-lg font-semibold lg:text-sm">Offline</span> <span className="text-lg font-semibold lg:text-sm">{c('Status').t`Offline`}</span>
</div> </div>
</div> </div>
</> </>
@@ -166,11 +167,11 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({ setMenuPane, closeMenu,
<> <>
<MenuItem onClick={activateRegisterPane}> <MenuItem onClick={activateRegisterPane}>
<Icon type="user" className={iconClassName} /> <Icon type="user" className={iconClassName} />
Create free account {c('Action').t`Create free account`}
</MenuItem> </MenuItem>
<MenuItem onClick={activateSignInPane}> <MenuItem onClick={activateSignInPane}>
<Icon type="signIn" className={iconClassName} /> <Icon type="signIn" className={iconClassName} />
Sign in {c('Action').t`Sign in`}
</MenuItem> </MenuItem>
</> </>
)} )}

View File

@@ -15,6 +15,7 @@ import { useApplication } from '../ApplicationProvider'
import { useCaptcha } from '@/Hooks/useCaptcha' import { useCaptcha } from '@/Hooks/useCaptcha'
import MergeLocalDataCheckbox from './MergeLocalDataCheckbox' import MergeLocalDataCheckbox from './MergeLocalDataCheckbox'
import ConfirmNoMergeDialog from './ConfirmNoMergeDialog' import ConfirmNoMergeDialog from './ConfirmNoMergeDialog'
import { c } from 'ttag'
type Props = { type Props = {
setMenuPane: (pane: AccountMenuPane) => void setMenuPane: (pane: AccountMenuPane) => void
@@ -252,7 +253,7 @@ const SignInPane: FunctionComponent<Props> = ({ setMenuPane }) => {
className={{ container: `mb-2 ${error ? 'border-danger' : null}` }} className={{ container: `mb-2 ${error ? 'border-danger' : null}` }}
left={[<Icon type="email" className="text-neutral" />]} left={[<Icon type="email" className="text-neutral" />]}
type="email" type="email"
placeholder="Email" placeholder={c('Label').t`Email`}
value={email} value={email}
onChange={handleEmailChange} onChange={handleEmailChange}
onFocus={resetInvalid} onFocus={resetInvalid}
@@ -268,14 +269,14 @@ const SignInPane: FunctionComponent<Props> = ({ setMenuPane }) => {
onChange={handlePasswordChange} onChange={handlePasswordChange}
onFocus={resetInvalid} onFocus={resetInvalid}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder="Password" placeholder={c('Label').t`Password`}
ref={passwordInputRef} ref={passwordInputRef}
value={password} value={password}
/> />
{error ? <div className="my-2 text-danger">{error}</div> : null} {error ? <div className="my-2 text-danger">{error}</div> : null}
<Button <Button
className="mb-3 mt-1" className="mb-3 mt-1"
label={isSigningIn ? 'Signing in...' : 'Sign in'} label={isSigningIn ? c('Action').t`Signing in...` : c('Action').t`Sign in`}
primary primary
onClick={handleSignInFormSubmit} onClick={handleSignInFormSubmit}
disabled={isSigningIn} disabled={isSigningIn}
@@ -283,7 +284,7 @@ const SignInPane: FunctionComponent<Props> = ({ setMenuPane }) => {
/> />
<Checkbox <Checkbox
name="is-ephemeral" name="is-ephemeral"
label="Stay signed in" label={c('Option').t`Stay signed in`}
checked={!isEphemeral} checked={!isEphemeral}
disabled={isSigningIn || isRecoverySignIn} disabled={isSigningIn || isRecoverySignIn}
onChange={handleEphemeralChange} onChange={handleEphemeralChange}
@@ -325,13 +326,15 @@ const SignInPane: FunctionComponent<Props> = ({ setMenuPane }) => {
<div className="mb-3 mt-1 flex items-center px-3"> <div className="mb-3 mt-1 flex items-center px-3">
<IconButton <IconButton
icon="arrow-left" icon="arrow-left"
title="Go back" title={c('Action').t`Go back`}
className="mr-2 flex p-0 text-neutral" className="mr-2 flex p-0 text-neutral"
onClick={() => setMenuPane(AccountMenuPane.GeneralMenu)} onClick={() => setMenuPane(AccountMenuPane.GeneralMenu)}
focusable={true} focusable={true}
disabled={isSigningIn} disabled={isSigningIn}
/> />
<div className="text-base font-bold">{showCaptcha ? 'Human verification' : 'Sign in'}</div> <div className="text-base font-bold">
{showCaptcha ? c('Title').t`Human verification` : c('Title').t`Sign in`}
</div>
</div> </div>
{showCaptcha ? <div className="p-[10px]">{captchaIframe}</div> : signInForm} {showCaptcha ? <div className="p-[10px]">{captchaIframe}</div> : signInForm}
{showNoMergeConfirmation && ( {showNoMergeConfirmation && (

View File

@@ -7,6 +7,7 @@ import { AccountIllustration } from '@standardnotes/icons'
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane' import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup' import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment' import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
import { c } from 'ttag'
type Props = { type Props = {
application: WebApplication application: WebApplication
@@ -25,22 +26,24 @@ const Authentication: FunctionComponent<Props> = ({ application }) => {
application.accountMenuController.setShow(true) application.accountMenuController.setShow(true)
} }
const loginLink = (
<button className="cursor-pointer border-0 bg-default p-0 text-info underline" onClick={clickSignIn}>
{c('Action').t`Sign in`}
</button>
)
return ( return (
<PreferencesGroup> <PreferencesGroup>
<PreferencesSegment> <PreferencesSegment>
<div className="flex flex-col items-center px-4 md:px-12"> <div className="flex flex-col items-center px-4 md:px-12">
<AccountIllustration className="mb-3" /> <AccountIllustration className="mb-3" />
<Title>You're not signed in</Title> <Title>{c('Title').t`You're not signed in`}</Title>
<div className="mb-3 text-center text-base lg:text-sm"> <div className="mb-3 text-center text-base lg:text-sm">
Sign in to sync your notes and preferences across all your devices and enable end-to-end encryption. {c('Info')
</div> .t`Sign in to sync your notes and preferences across all your devices and enable end-to-end encryption.`}
<Button primary label="Create free account" onClick={clickRegister} className="mb-3" />
<div className="text-base lg:text-sm">
Already have an account?{' '}
<button className="cursor-pointer border-0 bg-default p-0 text-info underline" onClick={clickSignIn}>
Sign in
</button>
</div> </div>
<Button primary label={c('Action').t`Create free account`} onClick={clickRegister} className="mb-3" />
<div className="text-base lg:text-sm">{c('Info').jt`Already have an account? ${loginLink}`}</div>
</div> </div>
</PreferencesSegment> </PreferencesSegment>
</PreferencesGroup> </PreferencesGroup>

View File

@@ -9,6 +9,7 @@ import { BlueDotIcon, CircleIcon, DiamondIcon, CreateAccountIllustration } from
import { useCaptcha } from '@/Hooks/useCaptcha' import { useCaptcha } from '@/Hooks/useCaptcha'
import { AccountMenuPane } from '../../AccountMenu/AccountMenuPane' import { AccountMenuPane } from '../../AccountMenu/AccountMenuPane'
import { isErrorResponse } from '@standardnotes/snjs' import { isErrorResponse } from '@standardnotes/snjs'
import { c } from 'ttag'
type Props = { type Props = {
application: WebApplication application: WebApplication
@@ -150,19 +151,19 @@ const CreateAccount: FunctionComponent<Props> = ({ application }) => {
className={`min-w-auto md:min-w-90 ${isEmailInvalid ? 'mb-2' : 'mb-4'}`} className={`min-w-auto md:min-w-90 ${isEmailInvalid ? 'mb-2' : 'mb-4'}`}
id="purchase-sign-in-email" id="purchase-sign-in-email"
type="email" type="email"
label="Email" label={c('Label').t`Email`}
value={email} value={email}
onChange={handleEmailChange} onChange={handleEmailChange}
ref={emailInputRef} ref={emailInputRef}
disabled={isCreatingAccount} disabled={isCreatingAccount}
isInvalid={isEmailInvalid} isInvalid={isEmailInvalid}
/> />
{isEmailInvalid ? <div className="mb-4 text-danger">Please provide a valid email.</div> : null} {isEmailInvalid ? <div className="mb-4 text-danger">{c('Error').t`Please provide a valid email.`}</div> : null}
<FloatingLabelInput <FloatingLabelInput
className="min-w-auto mb-4 md:min-w-90" className="min-w-auto mb-4 md:min-w-90"
id="purchase-create-account-password" id="purchase-create-account-password"
type="password" type="password"
label="Password" label={c('Label').t`Password`}
value={password} value={password}
onChange={handlePasswordChange} onChange={handlePasswordChange}
ref={passwordInputRef} ref={passwordInputRef}
@@ -172,7 +173,7 @@ const CreateAccount: FunctionComponent<Props> = ({ application }) => {
className={`min-w-auto md:min-w-90 ${isPasswordNotMatching ? 'mb-2' : 'mb-4'}`} className={`min-w-auto md:min-w-90 ${isPasswordNotMatching ? 'mb-2' : 'mb-4'}`}
id="create-account-confirm" id="create-account-confirm"
type="password" type="password"
label="Repeat password" label={c('Label').t`Repeat password`}
value={confirmPassword} value={confirmPassword}
onChange={handleConfirmPasswordChange} onChange={handleConfirmPasswordChange}
ref={confirmPasswordInputRef} ref={confirmPasswordInputRef}
@@ -180,7 +181,7 @@ const CreateAccount: FunctionComponent<Props> = ({ application }) => {
isInvalid={isPasswordNotMatching} isInvalid={isPasswordNotMatching}
/> />
{isPasswordNotMatching ? ( {isPasswordNotMatching ? (
<div className="mb-4 text-danger">Passwords don't match. Please try again.</div> <div className="mb-4 text-danger">{c('Error').t`Passwords don't match. Please try again.`}</div>
) : null} ) : null}
</div> </div>
</form> </form>
@@ -197,8 +198,10 @@ const CreateAccount: FunctionComponent<Props> = ({ application }) => {
<DiamondIcon className="absolute -right-2 top-0 -z-[1] h-18 w-18 translate-x-1/2" /> <DiamondIcon className="absolute -right-2 top-0 -z-[1] h-18 w-18 translate-x-1/2" />
<div className="mr-0 lg:mr-12"> <div className="mr-0 lg:mr-12">
<h1 className="mb-2 mt-0 text-2xl font-bold">Create your free account</h1> // translator: Full sentence: "Create your free account"
<div className="mb-4 text-sm font-medium">to continue to Standard Notes.</div> <h1 className="mb-2 mt-0 text-2xl font-bold">{c('Title').t`Create your free account`}</h1>
// translator: Full sentence: "Create your free account to continue to Standard Notes."
<div className="mb-4 text-sm font-medium">{c('Info').t`to continue to Standard Notes.`}</div>
{captchaURL ? captchaIframe : CreateAccountForm} {captchaURL ? captchaIframe : CreateAccountForm}
<div className="flex flex-col-reverse items-start justify-between md:flex-row md:items-center"> <div className="flex flex-col-reverse items-start justify-between md:flex-row md:items-center">
<div className="flex flex-col"> <div className="flex flex-col">
@@ -207,7 +210,8 @@ const CreateAccount: FunctionComponent<Props> = ({ application }) => {
disabled={isCreatingAccount} disabled={isCreatingAccount}
className="mb-2 flex cursor-pointer items-start border-0 bg-default p-0 font-medium text-info hover:underline" className="mb-2 flex cursor-pointer items-start border-0 bg-default p-0 font-medium text-info hover:underline"
> >
Sign in instead // translator: "Instead" here refers to "instead of creating an account"
{c('Action').t`Sign in instead`}
</button> </button>
{!application.isNativeIOS() && ( {!application.isNativeIOS() && (
<button <button
@@ -215,14 +219,14 @@ const CreateAccount: FunctionComponent<Props> = ({ application }) => {
disabled={isCreatingAccount} disabled={isCreatingAccount}
className="flex cursor-pointer items-start border-0 bg-default p-0 font-medium text-info hover:underline" className="flex cursor-pointer items-start border-0 bg-default p-0 font-medium text-info hover:underline"
> >
Subscribe without account {c('Action').t`Subscribe without account`}
</button> </button>
)} )}
</div> </div>
<Button <Button
className="mb-4 py-2.5 md:mb-0" className="mb-4 py-2.5 md:mb-0"
primary primary
label={isCreatingAccount ? 'Creating account...' : 'Create account'} label={isCreatingAccount ? c('Action').t`Creating account...` : c('Action').t`Create account`}
onClick={handleCreateAccount} onClick={handleCreateAccount}
disabled={isCreatingAccount} disabled={isCreatingAccount}
/> />

View File

@@ -8,6 +8,7 @@ import { isEmailValid } from '@/Utils'
import { BlueDotIcon, CircleIcon, DiamondIcon } from '@standardnotes/icons' import { BlueDotIcon, CircleIcon, DiamondIcon } from '@standardnotes/icons'
import { isErrorResponse, getCaptchaHeader } from '@standardnotes/snjs' import { isErrorResponse, getCaptchaHeader } from '@standardnotes/snjs'
import { useCaptcha } from '@/Hooks/useCaptcha' import { useCaptcha } from '@/Hooks/useCaptcha'
import { c } from 'ttag'
type Props = { type Props = {
application: WebApplication application: WebApplication
@@ -102,7 +103,7 @@ const SignIn: FunctionComponent<Props> = ({ application }) => {
if ((err as Error).toString().includes('Invalid email or password')) { if ((err as Error).toString().includes('Invalid email or password')) {
setIsEmailInvalid(true) setIsEmailInvalid(true)
setIsPasswordInvalid(true) setIsPasswordInvalid(true)
setOtherErrorMessage('Invalid email or password.') setOtherErrorMessage(c('Error').t`Invalid email or password.`)
setPassword('') setPassword('')
} else { } else {
application.alerts.alert(err as string).catch(console.error) application.alerts.alert(err as string).catch(console.error)
@@ -119,7 +120,7 @@ const SignIn: FunctionComponent<Props> = ({ application }) => {
className={`min-w-auto sm:min-w-90 ${isEmailInvalid && !otherErrorMessage ? 'mb-2' : 'mb-4'}`} className={`min-w-auto sm:min-w-90 ${isEmailInvalid && !otherErrorMessage ? 'mb-2' : 'mb-4'}`}
id="purchase-sign-in-email" id="purchase-sign-in-email"
type="email" type="email"
label="Email" label={c('Label').t`Email`}
value={email} value={email}
onChange={handleEmailChange} onChange={handleEmailChange}
ref={emailInputRef} ref={emailInputRef}
@@ -127,13 +128,13 @@ const SignIn: FunctionComponent<Props> = ({ application }) => {
isInvalid={isEmailInvalid} isInvalid={isEmailInvalid}
/> />
{isEmailInvalid && !otherErrorMessage ? ( {isEmailInvalid && !otherErrorMessage ? (
<div className="mb-4 text-danger">Please provide a valid email.</div> <div className="mb-4 text-danger">{c('Error').t`Please provide a valid email.`}</div>
) : null} ) : null}
<FloatingLabelInput <FloatingLabelInput
className={`min-w-auto sm:min-w-90 ${otherErrorMessage ? 'mb-2' : 'mb-4'}`} className={`min-w-auto sm:min-w-90 ${otherErrorMessage ? 'mb-2' : 'mb-4'}`}
id="purchase-sign-in-password" id="purchase-sign-in-password"
type="password" type="password"
label="Password" label={c('Label').t`Password`}
value={password} value={password}
onChange={handlePasswordChange} onChange={handlePasswordChange}
ref={passwordInputRef} ref={passwordInputRef}
@@ -145,7 +146,7 @@ const SignIn: FunctionComponent<Props> = ({ application }) => {
<Button <Button
className={`${isSigningIn ? 'min-w-30' : 'min-w-24'} mb-5 py-2.5`} className={`${isSigningIn ? 'min-w-30' : 'min-w-24'} mb-5 py-2.5`}
primary primary
label={isSigningIn ? 'Signing in...' : 'Sign in'} label={isSigningIn ? c('Action').t`Signing in...` : c('Action').t`Sign in`}
onClick={handleSignIn} onClick={handleSignIn}
disabled={isSigningIn} disabled={isSigningIn}
/> />
@@ -163,17 +164,18 @@ const SignIn: FunctionComponent<Props> = ({ application }) => {
<DiamondIcon className="absolute -right-2 top-0 -z-[1] h-18 w-18 translate-x-1/2" /> <DiamondIcon className="absolute -right-2 top-0 -z-[1] h-18 w-18 translate-x-1/2" />
<div> <div>
<h1 className="mb-2 mt-0 text-2xl font-bold">Sign in</h1> <h1 className="mb-2 mt-0 text-2xl font-bold">{c('Title').t`Sign in`}</h1>
<div className="mb-4 text-sm font-medium">to continue to Standard Notes.</div> <div className="mb-4 text-sm font-medium">{c('Info').t`to continue to Standard Notes.`}</div>
{showCaptcha ? captchaIframe : signInForm} {showCaptcha ? captchaIframe : signInForm}
<div className="text-sm font-medium text-passive-1"> <div className="text-sm font-medium text-passive-1">
Dont have an account yet?{' '} {c('Info').jt`Dont have an account yet? ${(
<a <a
className={`text-info ${isSigningIn ? 'cursor-not-allowed' : 'cursor-pointer '}`} className={`text-info ${isSigningIn ? 'cursor-not-allowed' : 'cursor-pointer '}`}
onClick={handleCreateAccountInstead} onClick={handleCreateAccountInstead}
> >
Create account {c('Action').t`Create account`}
</a> </a>
)}`}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -9239,6 +9239,7 @@ __metadata:
tailwindcss: ^3.4.0 tailwindcss: ^3.4.0
ts-jest: ^29.0.3 ts-jest: ^29.0.3
ts-loader: ^9.4.2 ts-loader: ^9.4.2
ttag: ^1.8.12
typescript: "*" typescript: "*"
unicode-script: ^1.2.0 unicode-script: ^1.2.0
webextension-polyfill: ^0.10.0 webextension-polyfill: ^0.10.0
@@ -14484,6 +14485,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"dedent@npm:1.5.1":
version: 1.5.1
resolution: "dedent@npm:1.5.1"
peerDependencies:
babel-plugin-macros: ^3.1.0
peerDependenciesMeta:
babel-plugin-macros:
optional: true
checksum: c3c300a14edf1bdf5a873f9e4b22e839d62490bc5c8d6169c1f15858a1a76733d06a9a56930e963d677a2ceeca4b6b0894cc5ea2f501aa382ca5b92af3413c2a
languageName: node
linkType: hard
"dedent@npm:^1.0.0": "dedent@npm:^1.0.0":
version: 1.3.0 version: 1.3.0
resolution: "dedent@npm:1.3.0" resolution: "dedent@npm:1.3.0"
@@ -24214,6 +24227,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"plural-forms@npm:^0.5.3":
version: 0.5.5
resolution: "plural-forms@npm:0.5.5"
checksum: 97e1ff5bb63c6aaad1150a7118a5d17dcbe19aa194be464b5384f5aad6480e299edc40afa22784ebbd2a7265790eab91872d633eddd53d866d47e3d721107a43
languageName: node
linkType: hard
"pod-install@npm:^0.3.5": "pod-install@npm:^0.3.5":
version: 0.3.7 version: 0.3.7
resolution: "pod-install@npm:0.3.7" resolution: "pod-install@npm:0.3.7"
@@ -28394,6 +28414,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ttag@npm:^1.8.12":
version: 1.8.12
resolution: "ttag@npm:1.8.12"
dependencies:
dedent: 1.5.1
plural-forms: ^0.5.3
checksum: 7bb7713fdcd846c69884eadb28cf27df0043a2f91a0350a8270d55da2c87f362a1406d8ffae890b99f0fec7691e8955c47ac95a2e6c89f6dac3154a380ff71ca
languageName: node
linkType: hard
"tuf-js@npm:^1.1.7": "tuf-js@npm:^1.1.7":
version: 1.1.7 version: 1.1.7
resolution: "tuf-js@npm:1.1.7" resolution: "tuf-js@npm:1.1.7"