Skip to content

Commit 03c1a8d

Browse files
xuhongbocodex
andcommitted
feat(i18n): localize remaining hardcoded UI surfaces
Localize remaining hardcoded UI strings across web, space, editor, and shared surfaces using the existing react-i18next namespace JSON files introduced by #8898. Add the new keys to the relevant auth, common, editor, empty-state, inbox, page, project, work-item, and workspace locale files across all supported locales. Co-authored-by: Codex <noreply@openai.com>
1 parent 50a7b47 commit 03c1a8d

255 files changed

Lines changed: 6567 additions & 602 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/space/app/error.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,36 @@
55
*/
66

77
// ui
8+
import { useTranslation } from "@plane/i18n";
89
import { Button } from "@plane/propel/button";
910

11+
const handleRetry = () => {
12+
window.location.reload();
13+
};
14+
1015
function ErrorPage() {
11-
const handleRetry = () => {
12-
window.location.reload();
13-
};
16+
const { t } = useTranslation();
1417

1518
return (
1619
<div className="grid h-screen place-items-center bg-surface-1 p-4">
1720
<div className="space-y-8 text-center">
1821
<div className="space-y-2">
19-
<h3 className="text-16 font-semibold">Yikes! That doesn{"'"}t look good.</h3>
22+
<h3 className="text-16 font-semibold">{t("space_public.error_title")}</h3>
2023
<p className="mx-auto text-13 text-secondary md:w-1/2">
21-
That crashed Plane, pun intended. No worries, though. Our engineers have been notified. If you have more
22-
details, please write to{" "}
24+
{t("space_public.error_description_prefix")}{" "}
2325
<a href="mailto:support@plane.so" className="text-accent-primary">
2426
support@plane.so
2527
</a>{" "}
26-
or on our{" "}
28+
{t("space_public.error_description_middle")}{" "}
2729
<a href="https://forum.plane.so" target="_blank" className="text-accent-primary" rel="noopener noreferrer">
28-
Forum
30+
{t("space_public.forum")}
2931
</a>
3032
.
3133
</p>
3234
</div>
3335
<div className="flex items-center justify-center gap-2">
3436
<Button variant="primary" size="lg" onClick={handleRetry}>
35-
Refresh
37+
{t("space_public.refresh")}
3638
</Button>
3739
{/* <Button variant="secondary" size="lg" onClick={() => {}}>
3840
Sign out

apps/space/app/not-found.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@
44
* See the LICENSE file for details.
55
*/
66

7+
import { useTranslation } from "@plane/i18n";
78
// assets
89
import SomethingWentWrongImage from "@/app/assets/something-went-wrong.svg?url";
910

1011
function NotFound() {
12+
const { t } = useTranslation();
1113
return (
1214
<div className="grid h-screen w-screen place-items-center bg-surface-1">
1315
<div className="text-center">
1416
<div className="mx-auto grid size-32 place-items-center rounded-full md:size-52">
1517
<div className="grid size-16 place-items-center md:size-32">
16-
<img src={SomethingWentWrongImage} alt="Something went wrong" width={128} height={128} />
18+
<img src={SomethingWentWrongImage} alt={t("space_public.something_went_wrong")} width={128} height={128} />
1719
</div>
1820
</div>
19-
<h1 className="mt-8 text-18 font-semibold md:mt-12 md:text-24">That didn{"'"}t work</h1>
20-
<p className="mt-2 text-13 md:mt-4 md:text-14">
21-
Check the URL you are entering in the browser{"'"}s address bar and try again.
22-
</p>
21+
<h1 className="mt-8 text-18 font-semibold md:mt-12 md:text-24">{t("space_public.not_found_title")}</h1>
22+
<p className="mt-2 text-13 md:mt-4 md:text-14">{t("space_public.not_found_hint")}</p>
2323
</div>
2424
</div>
2525
);

apps/space/components/account/auth-forms/auth-header.tsx

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,24 @@
44
* See the LICENSE file for details.
55
*/
66

7+
import { useTranslation } from "@plane/i18n";
78
// helpers
89
import { EAuthModes } from "@/types/auth";
910

1011
type TAuthHeader = {
1112
authMode: EAuthModes;
1213
};
1314

14-
type TAuthHeaderContent = {
15-
header: string;
16-
subHeader: string;
17-
};
18-
19-
type TAuthHeaderDetails = {
20-
[mode in EAuthModes]: TAuthHeaderContent;
21-
};
22-
23-
const Titles: TAuthHeaderDetails = {
24-
[EAuthModes.SIGN_IN]: {
25-
header: "Sign in to upvote or comment",
26-
subHeader: "Contribute in nudging the features you want to get built.",
27-
},
28-
[EAuthModes.SIGN_UP]: {
29-
header: "View, comment, and do more",
30-
subHeader: "Sign up or log in to work with Plane work items and Pages.",
31-
},
32-
};
33-
3415
export function AuthHeader(props: TAuthHeader) {
3516
const { authMode } = props;
36-
37-
const getHeaderSubHeader = (mode: EAuthModes | null): TAuthHeaderContent => {
38-
if (mode) {
39-
return Titles[mode];
40-
}
41-
42-
return {
43-
header: "Comment or react to work items",
44-
subHeader: "Use plane to add your valuable inputs to features.",
45-
};
46-
};
47-
48-
const { header, subHeader } = getHeaderSubHeader(authMode);
17+
const { t } = useTranslation();
18+
19+
const header =
20+
authMode === EAuthModes.SIGN_IN ? t("space_public.auth.sign_in_header") : t("space_public.auth.sign_up_header");
21+
const subHeader =
22+
authMode === EAuthModes.SIGN_IN
23+
? t("space_public.auth.sign_in_subheader")
24+
: t("space_public.auth.sign_up_subheader");
4925

5026
return (
5127
<>

apps/space/components/account/auth-forms/auth-root.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useEffect, useState } from "react";
88
import { observer } from "mobx-react";
99
import { useSearchParams } from "next/navigation";
1010
// plane imports
11+
import { useTranslation } from "@plane/i18n";
1112
import { SitesAuthService } from "@plane/services";
1213
import type { IEmailCheckData } from "@plane/types";
1314
import { OAuthOptions } from "@plane/ui";
@@ -43,6 +44,7 @@ export const AuthRoot = observer(function AuthRoot() {
4344
const [isPasswordAutoset, setIsPasswordAutoset] = useState(true);
4445
// hooks
4546
const { config } = useInstance();
47+
const { t } = useTranslation();
4648

4749
useEffect(() => {
4850
if (error_code) {
@@ -84,7 +86,7 @@ export const AuthRoot = observer(function AuthRoot() {
8486
const isSMTPConfigured = config?.is_smtp_configured || false;
8587
const isMagicLoginEnabled = config?.is_magic_login_enabled || false;
8688
const isEmailPasswordEnabled = config?.is_email_password_enabled || false;
87-
const oAuthActionText = authMode === EAuthModes.SIGN_UP ? "Sign up" : "Sign in";
89+
const oAuthActionText = authMode === EAuthModes.SIGN_UP ? t("space_public.sign_up") : t("space_public.sign_in");
8890
const { isOAuthEnabled, oAuthOptions } = useOAuthConfig(oAuthActionText);
8991

9092
// submit handler- email verification
@@ -134,8 +136,8 @@ export const AuthRoot = observer(function AuthRoot() {
134136
};
135137

136138
// generating the unique code
137-
const generateEmailUniqueCode = async (email: string): Promise<{ code: string } | undefined> => {
138-
const payload = { email: email };
139+
const generateEmailUniqueCode = async (emailId: string): Promise<{ code: string } | undefined> => {
140+
const payload = { email: emailId };
139141
return await authService
140142
.generateUniqueCode(payload)
141143
.then(() => ({ code: "" }))
@@ -185,7 +187,7 @@ export const AuthRoot = observer(function AuthRoot() {
185187
}}
186188
/>
187189
)}
188-
<TermsAndConditions isSignUp={authMode === EAuthModes.SIGN_UP ? true : false} />
190+
<TermsAndConditions isSignUp={authMode === EAuthModes.SIGN_UP} />
189191
</div>
190192
</div>
191193
);

apps/space/components/account/auth-forms/email.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
*/
66

77
import type { FormEvent } from "react";
8-
import { useMemo, useRef, useState } from "react";
8+
import { useEffect, useMemo, useRef, useState } from "react";
99
import { observer } from "mobx-react";
1010
// icons
1111
import { CircleAlert, XCircle } from "lucide-react";
1212
// types
1313
import { Button } from "@plane/propel/button";
14+
import { useTranslation } from "@plane/i18n";
1415
import type { IEmailCheckData } from "@plane/types";
1516
// ui
1617
import { Input, Spinner } from "@plane/ui";
@@ -25,13 +26,14 @@ type TAuthEmailForm = {
2526

2627
export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailForm) {
2728
const { onSubmit, defaultEmail } = props;
29+
const { t } = useTranslation();
2830
// states
2931
const [isSubmitting, setIsSubmitting] = useState(false);
3032
const [email, setEmail] = useState(defaultEmail);
3133

3234
const emailError = useMemo(
33-
() => (email && !checkEmailValidity(email) ? { email: "Email is invalid" } : undefined),
34-
[email]
35+
() => (email && !checkEmailValidity(email) ? { email: t("space_auth.email_invalid") } : undefined),
36+
[email, t]
3537
);
3638

3739
const handleFormSubmit = async (event: FormEvent<HTMLFormElement>) => {
@@ -49,11 +51,15 @@ export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailFo
4951
const [isFocused, setIsFocused] = useState(true);
5052
const inputRef = useRef<HTMLInputElement>(null);
5153

54+
useEffect(() => {
55+
inputRef.current?.focus();
56+
}, []);
57+
5258
return (
5359
<form onSubmit={handleFormSubmit} className="mt-5 space-y-4">
5460
<div className="space-y-1">
5561
<label className="text-13 font-medium text-tertiary" htmlFor="email">
56-
Email
62+
{t("space_auth.email")}
5763
</label>
5864
<div
5965
className={cn(
@@ -76,13 +82,12 @@ export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailFo
7682
placeholder="name@company.com"
7783
className={`h-10 w-full border-0 disable-autofill-style placeholder:text-placeholder autofill:bg-danger-subtle focus:bg-none active:bg-transparent`}
7884
autoComplete="off"
79-
autoFocus
8085
ref={inputRef}
8186
/>
8287
{email.length > 0 && (
8388
<button
8489
type="button"
85-
aria-label="Clear email"
90+
aria-label={t("space_auth.clear_email")}
8691
onClick={() => {
8792
setEmail("");
8893
inputRef.current?.focus();
@@ -101,7 +106,7 @@ export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailFo
101106
)}
102107
</div>
103108
<Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}>
104-
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
109+
{isSubmitting ? <Spinner height="20px" width="20px" /> : t("space_auth.continue")}
105110
</Button>
106111
</form>
107112
);

apps/space/components/account/auth-forms/password.tsx

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { observer } from "mobx-react";
99
import { Eye, EyeOff, XCircle } from "lucide-react";
1010
// plane imports
1111
import { API_BASE_URL, E_PASSWORD_STRENGTH } from "@plane/constants";
12+
import { useTranslation } from "@plane/i18n";
1213
import { Button } from "@plane/propel/button";
1314
import { AuthService } from "@plane/services";
1415
import { Input, Spinner, PasswordStrengthIndicator } from "@plane/ui";
@@ -41,6 +42,7 @@ const authService = new AuthService();
4142

4243
export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) {
4344
const { email, nextPath, isSMTPConfigured, handleAuthStep, handleEmailClear, mode } = props;
45+
const { t } = useTranslation();
4446
// ref
4547
const formRef = useRef<HTMLFormElement>(null);
4648
// states
@@ -79,14 +81,11 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
7981

8082
const isButtonDisabled = useMemo(
8183
() =>
82-
!isSubmitting &&
83-
!!passwordFormData.password &&
84-
(mode === EAuthModes.SIGN_UP
85-
? getPasswordStrength(passwordFormData.password) === E_PASSWORD_STRENGTH.STRENGTH_VALID &&
86-
passwordFormData.password === passwordFormData.confirm_password
87-
: true)
88-
? false
89-
: true,
84+
isSubmitting ||
85+
!passwordFormData.password ||
86+
(mode === EAuthModes.SIGN_UP &&
87+
(getPasswordStrength(passwordFormData.password) !== E_PASSWORD_STRENGTH.STRENGTH_VALID ||
88+
passwordFormData.password !== passwordFormData.confirm_password)),
9089
[isSubmitting, mode, passwordFormData.confirm_password, passwordFormData.password]
9190
);
9291

@@ -123,7 +122,7 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
123122
<input type="hidden" value={nextPath} name="next_path" />
124123
<div className="space-y-1">
125124
<label className="text-13 font-medium text-tertiary" htmlFor="email">
126-
Email
125+
{t("space_auth.email")}
127126
</label>
128127
<div className={`relative flex items-center rounded-md border border-subtle bg-surface-1`}>
129128
<Input
@@ -147,20 +146,19 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
147146

148147
<div className="space-y-1">
149148
<label className="text-13 font-medium text-tertiary" htmlFor="password">
150-
{mode === EAuthModes.SIGN_IN ? "Password" : "Set a password"}
149+
{mode === EAuthModes.SIGN_IN ? t("space_auth.password") : t("space_auth.set_password")}
151150
</label>
152151
<div className="relative flex items-center rounded-md bg-surface-1">
153152
<Input
154153
type={showPassword?.password ? "text" : "password"}
155154
name="password"
156155
value={passwordFormData.password}
157156
onChange={(e) => handleFormChange("password", e.target.value)}
158-
placeholder="Enter password"
157+
placeholder={t("space_auth.enter_password")}
159158
className="h-10 w-full border border-subtle !bg-surface-1 pr-12 disable-autofill-style placeholder:text-placeholder"
160159
onFocus={() => setIsPasswordInputFocused(true)}
161160
onBlur={() => setIsPasswordInputFocused(false)}
162161
autoComplete="off"
163-
autoFocus
164162
/>
165163
{showPassword?.password ? (
166164
<EyeOff
@@ -180,15 +178,15 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
180178
{mode === EAuthModes.SIGN_UP && (
181179
<div className="space-y-1">
182180
<label className="text-13 font-medium text-tertiary" htmlFor="confirm_password">
183-
Confirm password
181+
{t("space_auth.confirm_password")}
184182
</label>
185183
<div className="relative flex items-center rounded-md bg-surface-1">
186184
<Input
187185
type={showPassword?.retypePassword ? "text" : "password"}
188186
name="confirm_password"
189187
value={passwordFormData.confirm_password}
190188
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
191-
placeholder="Confirm password"
189+
placeholder={t("space_auth.confirm_password")}
192190
className="h-10 w-full border border-subtle !bg-surface-1 pr-12 disable-autofill-style placeholder:text-placeholder"
193191
onFocus={() => setIsRetryPasswordInputFocused(true)}
194192
onBlur={() => setIsRetryPasswordInputFocused(false)}
@@ -208,7 +206,9 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
208206
</div>
209207
{!!passwordFormData.confirm_password &&
210208
passwordFormData.password !== passwordFormData.confirm_password &&
211-
renderPasswordMatchError && <span className="text-13 text-danger-primary">Passwords don{"'"}t match</span>}
209+
renderPasswordMatchError && (
210+
<span className="text-13 text-danger-primary">{t("space_auth.passwords_dont_match")}</span>
211+
)}
212212
</div>
213213
)}
214214

@@ -219,9 +219,9 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
219219
{isSubmitting ? (
220220
<Spinner height="20px" width="20px" />
221221
) : isSMTPConfigured ? (
222-
"Continue"
222+
t("space_auth.continue")
223223
) : (
224-
"Go to workspace"
224+
t("space_auth.go_to_workspace")
225225
)}
226226
</Button>
227227
{isSMTPConfigured && (
@@ -232,13 +232,13 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
232232
className="w-full"
233233
size="xl"
234234
>
235-
Sign in with unique code
235+
{t("space_auth.sign_in_with_unique_code")}
236236
</Button>
237237
)}
238238
</>
239239
) : (
240240
<Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}>
241-
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Create account"}
241+
{isSubmitting ? <Spinner height="20px" width="20px" /> : t("space_auth.create_account")}
242242
</Button>
243243
)}
244244
</div>

0 commit comments

Comments
 (0)