-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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 all 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 |
|---|---|---|
|
|
@@ -5,12 +5,13 @@ | |
| */ | ||
|
|
||
| import type { FormEvent } from "react"; | ||
| import { useMemo, useRef, useState } from "react"; | ||
| import { useEffect, useMemo, useRef, useState } from "react"; | ||
| import { observer } from "mobx-react"; | ||
| // icons | ||
| import { CircleAlert, XCircle } from "lucide-react"; | ||
| // types | ||
| import { Button } from "@plane/propel/button"; | ||
| import { useTranslation } from "@plane/i18n"; | ||
| import type { IEmailCheckData } from "@plane/types"; | ||
| // ui | ||
| import { Input, Spinner } from "@plane/ui"; | ||
|
|
@@ -25,13 +26,14 @@ type TAuthEmailForm = { | |
|
|
||
| export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailForm) { | ||
| const { onSubmit, defaultEmail } = props; | ||
| const { t } = useTranslation(); | ||
| // states | ||
| const [isSubmitting, setIsSubmitting] = useState(false); | ||
| const [email, setEmail] = useState(defaultEmail); | ||
|
|
||
| const emailError = useMemo( | ||
| () => (email && !checkEmailValidity(email) ? { email: "Email is invalid" } : undefined), | ||
| [email] | ||
| () => (email && !checkEmailValidity(email) ? { email: t("localized_ui.space_auth.email_invalid") } : undefined), | ||
| [email, t] | ||
| ); | ||
|
|
||
| const handleFormSubmit = async (event: FormEvent<HTMLFormElement>) => { | ||
|
|
@@ -49,11 +51,15 @@ export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailFo | |
| const [isFocused, setIsFocused] = useState(true); | ||
| const inputRef = useRef<HTMLInputElement>(null); | ||
|
|
||
| useEffect(() => { | ||
| inputRef.current?.focus(); | ||
| }, []); | ||
|
|
||
| return ( | ||
| <form onSubmit={handleFormSubmit} className="mt-5 space-y-4"> | ||
| <div className="space-y-1"> | ||
| <label className="text-13 font-medium text-tertiary" htmlFor="email"> | ||
| {t("localized_ui.space_auth.email")} | ||
| </label> | ||
| <div | ||
| className={cn( | ||
|
|
@@ -76,13 +82,12 @@ export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailFo | |
| placeholder="name@company.com" | ||
| className={`h-10 w-full border-0 disable-autofill-style placeholder:text-placeholder autofill:bg-danger-subtle focus:bg-none active:bg-transparent`} | ||
| autoComplete="off" | ||
| autoFocus | ||
| ref={inputRef} | ||
| /> | ||
|
Comment on lines
82
to
86
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 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 Useful? React with 👍 / 👎. |
||
| {email.length > 0 && ( | ||
| <button | ||
| type="button" | ||
| aria-label="Clear email" | ||
| aria-label={t("localized_ui.space_auth.clear_email")} | ||
| onClick={() => { | ||
| setEmail(""); | ||
| inputRef.current?.focus(); | ||
|
|
@@ -101,7 +106,7 @@ export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailFo | |
| )} | ||
| </div> | ||
| <Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}> | ||
| {isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"} | ||
| {isSubmitting ? <Spinner height="20px" width="20px" /> : t("localized_ui.space_auth.continue")} | ||
| </Button> | ||
| </form> | ||
| ); | ||
|
|
||
| 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> | ||
|
|
||
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.
🧩 Analysis chain
🏁 Script executed:
Repository: makeplane/plane
Length of output: 6187
Translation keys
something_went_wrong,not_found_title, andnot_found_hintare not defined in any locale files.The code references these three translation keys under
localized_ui.space_public.*, but they do not exist in any of the 19 locale files (packages/i18n/src/locales/{cs,de,en,es,fr,id,it,ja,ko,pl,pt-BR,ro,ru,sk,tr-TR,ua,vi-VN,zh-CN,zh-TW}/translations.ts). This will cause missing translations at runtime. Add these keys to all 19 locale files before merging.🤖 Prompt for AI Agents