-
Notifications
You must be signed in to change notification settings - Fork 4.4k
fix(i18n): localize hardcoded UI surfaces #9069
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
4d1d5d7
3aba2bf
129ad14
e16877a
bc78612
fa72160
8836325
fe255e8
e84bc5d
2337a98
9710a55
6078a30
059867a
bf0f5a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import { observer } from "mobx-react"; | |
| import { Eye, EyeOff, XCircle } from "lucide-react"; | ||
| // plane imports | ||
| import { API_BASE_URL, E_PASSWORD_STRENGTH } from "@plane/constants"; | ||
| import { useTranslation } from "@plane/i18n"; | ||
| import { Button } from "@plane/propel/button"; | ||
| import { AuthService } from "@plane/services"; | ||
| import { Input, Spinner, PasswordStrengthIndicator } from "@plane/ui"; | ||
|
|
@@ -41,6 +42,7 @@ const authService = new AuthService(); | |
|
|
||
| export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) { | ||
| const { email, nextPath, isSMTPConfigured, handleAuthStep, handleEmailClear, mode } = props; | ||
| const { t } = useTranslation(); | ||
| // ref | ||
| const formRef = useRef<HTMLFormElement>(null); | ||
| // states | ||
|
|
@@ -79,14 +81,11 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) | |
|
|
||
| const isButtonDisabled = useMemo( | ||
| () => | ||
| !isSubmitting && | ||
| !!passwordFormData.password && | ||
| (mode === EAuthModes.SIGN_UP | ||
| ? getPasswordStrength(passwordFormData.password) === E_PASSWORD_STRENGTH.STRENGTH_VALID && | ||
| passwordFormData.password === passwordFormData.confirm_password | ||
| : true) | ||
| ? false | ||
| : true, | ||
| isSubmitting || | ||
| !passwordFormData.password || | ||
| (mode === EAuthModes.SIGN_UP && | ||
| (getPasswordStrength(passwordFormData.password) !== E_PASSWORD_STRENGTH.STRENGTH_VALID || | ||
| passwordFormData.password !== passwordFormData.confirm_password)), | ||
| [isSubmitting, mode, passwordFormData.confirm_password, passwordFormData.password] | ||
| ); | ||
|
|
||
|
|
@@ -123,7 +122,7 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) | |
| <input type="hidden" value={nextPath} name="next_path" /> | ||
| <div className="space-y-1"> | ||
| <label className="text-13 font-medium text-tertiary" htmlFor="email"> | ||
| {t("localized_ui.space_auth.email")} | ||
| </label> | ||
| <div className={`relative flex items-center rounded-md border border-subtle bg-surface-1`}> | ||
| <Input | ||
|
|
@@ -147,20 +146,21 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) | |
|
|
||
| <div className="space-y-1"> | ||
| <label className="text-13 font-medium text-tertiary" htmlFor="password"> | ||
| {mode === EAuthModes.SIGN_IN ? "Password" : "Set a password"} | ||
| {mode === EAuthModes.SIGN_IN | ||
| ? t("localized_ui.space_auth.password") | ||
| : t("localized_ui.space_auth.set_password")} | ||
| </label> | ||
| <div className="relative flex items-center rounded-md bg-surface-1"> | ||
| <Input | ||
| type={showPassword?.password ? "text" : "password"} | ||
| name="password" | ||
| value={passwordFormData.password} | ||
| onChange={(e) => handleFormChange("password", e.target.value)} | ||
| placeholder="Enter password" | ||
| placeholder={t("localized_ui.space_auth.enter_password")} | ||
| className="h-10 w-full border border-subtle !bg-surface-1 pr-12 disable-autofill-style placeholder:text-placeholder" | ||
| onFocus={() => setIsPasswordInputFocused(true)} | ||
| onBlur={() => setIsPasswordInputFocused(false)} | ||
| autoComplete="off" | ||
| autoFocus | ||
| /> | ||
|
Comment on lines
156
to
164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The password step no longer sets initial focus on its primary input after Useful? React with 👍 / 👎. |
||
| {showPassword?.password ? ( | ||
| <EyeOff | ||
|
|
@@ -180,15 +180,15 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) | |
| {mode === EAuthModes.SIGN_UP && ( | ||
| <div className="space-y-1"> | ||
| <label className="text-13 font-medium text-tertiary" htmlFor="confirm_password"> | ||
| Confirm password | ||
| {t("localized_ui.space_auth.confirm_password")} | ||
| </label> | ||
| <div className="relative flex items-center rounded-md bg-surface-1"> | ||
| <Input | ||
| type={showPassword?.retypePassword ? "text" : "password"} | ||
| name="confirm_password" | ||
| value={passwordFormData.confirm_password} | ||
| onChange={(e) => handleFormChange("confirm_password", e.target.value)} | ||
| placeholder="Confirm password" | ||
| placeholder={t("localized_ui.space_auth.confirm_password")} | ||
| className="h-10 w-full border border-subtle !bg-surface-1 pr-12 disable-autofill-style placeholder:text-placeholder" | ||
| onFocus={() => setIsRetryPasswordInputFocused(true)} | ||
| onBlur={() => setIsRetryPasswordInputFocused(false)} | ||
|
|
@@ -208,7 +208,9 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) | |
| </div> | ||
| {!!passwordFormData.confirm_password && | ||
| passwordFormData.password !== passwordFormData.confirm_password && | ||
| renderPasswordMatchError && <span className="text-13 text-danger-primary">Passwords don{"'"}t match</span>} | ||
| renderPasswordMatchError && ( | ||
| <span className="text-13 text-danger-primary">{t("localized_ui.space_auth.passwords_dont_match")}</span> | ||
| )} | ||
| </div> | ||
| )} | ||
|
|
||
|
|
@@ -219,9 +221,9 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) | |
| {isSubmitting ? ( | ||
| <Spinner height="20px" width="20px" /> | ||
| ) : isSMTPConfigured ? ( | ||
| "Continue" | ||
| t("localized_ui.space_auth.continue") | ||
| ) : ( | ||
| "Go to workspace" | ||
| t("localized_ui.space_auth.go_to_workspace") | ||
| )} | ||
| </Button> | ||
| {isSMTPConfigured && ( | ||
|
|
@@ -232,13 +234,13 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) | |
| className="w-full" | ||
| size="xl" | ||
| > | ||
| Sign in with unique code | ||
| {t("localized_ui.space_auth.sign_in_with_unique_code")} | ||
| </Button> | ||
| )} | ||
| </> | ||
| ) : ( | ||
| <Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}> | ||
| {isSubmitting ? <Spinner height="20px" width="20px" /> : "Create account"} | ||
| {isSubmitting ? <Spinner height="20px" width="20px" /> : t("localized_ui.space_auth.create_account")} | ||
| </Button> | ||
| )} | ||
| </div> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ import React, { useEffect, useState } from "react"; | |
| import { CircleCheck, XCircle } from "lucide-react"; | ||
| // plane imports | ||
| import { API_BASE_URL } from "@plane/constants"; | ||
| import { useTranslation } from "@plane/i18n"; | ||
| import { Button } from "@plane/propel/button"; | ||
| import { AuthService } from "@plane/services"; | ||
| import { Input, Spinner } from "@plane/ui"; | ||
|
|
@@ -39,6 +40,7 @@ const defaultValues: TUniqueCodeFormValues = { | |
|
|
||
| export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) { | ||
| const { mode, email, nextPath, handleEmailClear, generateEmailUniqueCode } = props; | ||
| const { t } = useTranslation(); | ||
| // derived values | ||
| const defaultResetTimerValue = 5; | ||
| // states | ||
|
|
@@ -52,10 +54,10 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) { | |
| const handleFormChange = (key: keyof TUniqueCodeFormValues, value: string) => | ||
| setUniqueCodeFormData((prev) => ({ ...prev, [key]: value })); | ||
|
|
||
| const generateNewCode = async (email: string) => { | ||
| const generateNewCode = async (targetEmail: string) => { | ||
| try { | ||
| setIsRequestingNewCode(true); | ||
| const uniqueCode = await generateEmailUniqueCode(email); | ||
| const uniqueCode = await generateEmailUniqueCode(targetEmail); | ||
| setResendCodeTimer(defaultResetTimerValue); | ||
| handleFormChange("code", uniqueCode?.code || ""); | ||
| setIsRequestingNewCode(false); | ||
|
|
@@ -87,7 +89,7 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) { | |
| <input type="hidden" value={nextPath} name="next_path" /> | ||
| <div className="space-y-1"> | ||
| <label className="text-13 font-medium text-tertiary" htmlFor="email"> | ||
| {t("localized_ui.space_auth.email")} | ||
| </label> | ||
| <div className={`relative flex items-center rounded-md border border-subtle bg-surface-1`}> | ||
| <Input | ||
|
|
@@ -112,7 +114,7 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) { | |
|
|
||
| <div className="space-y-1"> | ||
| <label className="text-13 font-medium text-tertiary" htmlFor="code"> | ||
| Unique code | ||
| {t("localized_ui.space_auth.unique_code")} | ||
| </label> | ||
| <Input | ||
| name="code" | ||
|
|
@@ -121,12 +123,11 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) { | |
| placeholder="123456" | ||
| className="h-10 w-full border border-subtle !bg-surface-1 pr-12 disable-autofill-style placeholder:text-placeholder" | ||
| autoComplete="off" | ||
| autoFocus | ||
| /> | ||
|
Comment on lines
123
to
126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The unique-code form also lost its input autofocus in this change, so when users switch to the OTP step they cannot immediately paste or type the code without first clicking the field. That is a direct usability regression for the email-code auth path and slows down the primary verification flow. Useful? React with 👍 / 👎. |
||
| <div className="flex w-full items-center justify-between px-1 pt-1 text-11"> | ||
| <p className="flex items-center gap-1 font-medium text-success-primary"> | ||
| <CircleCheck height={12} width={12} /> | ||
| Paste the code sent to your email | ||
| {t("localized_ui.space_auth.paste_code_sent")} | ||
| </p> | ||
| <button | ||
| type="button" | ||
|
|
@@ -139,17 +140,23 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) { | |
| disabled={isRequestNewCodeDisabled} | ||
| > | ||
| {resendTimerCode > 0 | ||
| ? `Resend in ${resendTimerCode}s` | ||
| ? t("localized_ui.space_auth.resend_in", { seconds: resendTimerCode }) | ||
| : isRequestingNewCode | ||
| ? "Requesting new code" | ||
| : "Resend"} | ||
| ? t("localized_ui.space_auth.requesting_new_code") | ||
| : t("localized_ui.space_auth.resend")} | ||
| </button> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="space-y-2.5"> | ||
| <Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}> | ||
| {isRequestingNewCode ? "Sending code" : isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"} | ||
| {isRequestingNewCode ? ( | ||
| t("localized_ui.space_auth.sending_code") | ||
| ) : isSubmitting ? ( | ||
| <Spinner height="20px" width="20px" /> | ||
| ) : ( | ||
| t("localized_ui.space_auth.continue") | ||
| )} | ||
| </Button> | ||
| </div> | ||
| </form> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The email form no longer autofocuses its primary input, so users who open Space auth and immediately type or press Enter must first click into the field. This is a workflow regression from the previous behavior and affects keyboard-first sign-in/sign-up flows across the first auth step; please restore
autoFocus(or equivalent mount-time focus logic) on the initial email field.Useful? React with 👍 / 👎.