From b9f140e53e2d59c19d7c2b7236dcc4d196070994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kayla=20=E3=81=AF=E3=81=AA?= Date: Tue, 31 Mar 2026 17:26:59 -0400 Subject: [PATCH] chore: remove `Language` objects (#23866) --- site/src/api/errors.ts | 9 +- .../UserDropdown/UserDropdownContent.test.tsx | 6 +- .../UserDropdown/UserDropdownContent.tsx | 8 +- site/src/modules/resources/SensitiveValue.tsx | 7 +- .../WorkspaceBuildLogs/WorkspaceBuildLogs.tsx | 8 +- site/src/pages/AuditPage/AuditHelpPopover.tsx | 15 +-- site/src/pages/AuditPage/AuditPageView.tsx | 9 +- .../ConnectionLogHelpPopover.tsx | 16 ++- .../ConnectionLogPageView.tsx | 11 +- site/src/pages/LoginPage/Language.ts | 9 -- site/src/pages/LoginPage/LoginPage.test.tsx | 29 ++--- site/src/pages/LoginPage/OAuthSignInForm.tsx | 5 +- .../pages/LoginPage/PasswordSignInForm.tsx | 11 +- .../UserTable/TableColumnHelpPopover.tsx | 16 ++- site/src/pages/SetupPage/SetupPage.test.tsx | 7 +- site/src/pages/SetupPage/SetupPageView.tsx | 72 ++++-------- site/src/pages/TemplatePage/TemplateStats.tsx | 27 +---- .../TemplateVersionsPage/VersionsTable.tsx | 9 +- .../PublishTemplateVersionDialog.tsx | 28 ++--- .../TemplateVersionEditorPage.test.tsx | 3 +- .../pages/TemplatesPage/TemplatesPageView.tsx | 41 +++---- .../pages/TerminalPage/TerminalPage.jest.tsx | 4 +- site/src/pages/TerminalPage/TerminalPage.tsx | 12 +- .../AccountPage/AccountForm.tsx | 22 +--- .../AccountPage/AccountPage.test.tsx | 3 +- .../SSHKeysPage/SSHKeysPage.test.tsx | 14 +-- .../SSHKeysPage/SSHKeysPage.tsx | 30 ++--- .../SecurityPage/SecurityForm.tsx | 35 +++--- .../SecurityPage/SecurityPage.jest.tsx | 3 +- .../pages/UsersPage/ResetPasswordDialog.tsx | 21 ++-- .../pages/UsersPage/UsersTable/UsersTable.tsx | 22 +--- .../WorkspaceScheduleForm.test.tsx | 23 ++-- .../WorkspaceScheduleForm.tsx | 105 ++++++++---------- .../WorkspaceSchedulePage.test.tsx | 21 +--- .../WorkspacesPage/WorkspaceHelpPopover.tsx | 20 ++-- .../WorkspacesPage/WorkspacesPageView.tsx | 11 +- site/src/utils/formUtils.ts | 28 ++--- site/src/utils/schedule.tsx | 17 +-- 38 files changed, 251 insertions(+), 486 deletions(-) delete mode 100644 site/src/pages/LoginPage/Language.ts diff --git a/site/src/api/errors.ts b/site/src/api/errors.ts index d2c1043b3d..69b41d3492 100644 --- a/site/src/api/errors.ts +++ b/site/src/api/errors.ts @@ -1,11 +1,5 @@ import { type AxiosError, type AxiosResponse, isAxiosError } from "axios"; -const Language = { - errorsByCode: { - defaultErrorCode: "Invalid value", - }, -}; - export interface FieldError { field: string; detail: string; @@ -64,8 +58,7 @@ export const mapApiErrorToFieldErrors = ( if (apiErrorResponse.validations) { for (const error of apiErrorResponse.validations) { - result[error.field] = - error.detail || Language.errorsByCode.defaultErrorCode; + result[error.field] = error.detail || "Invalid value"; } } diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.test.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.test.tsx index 601cbb76b7..83a6fb52e3 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.test.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.test.tsx @@ -6,7 +6,7 @@ import { } from "#/components/DropdownMenu/DropdownMenu"; import { MockUserOwner } from "#/testHelpers/entities"; import { render, waitForLoaderToBeRemoved } from "#/testHelpers/renderHelpers"; -import { Language, UserDropdownContent } from "./UserDropdownContent"; +import { UserDropdownContent } from "./UserDropdownContent"; const renderUserDropdownContent = (props: { onSignOut: () => void }) => { return render( @@ -28,7 +28,7 @@ describe("UserDropdownContent", () => { renderUserDropdownContent({ onSignOut: vi.fn() }); await waitForLoaderToBeRemoved(); - const link = screen.getByText(Language.accountLabel).closest("a"); + const link = screen.getByText("Account").closest("a"); if (!link) { throw new Error("Anchor tag not found for the account menu item"); } @@ -40,7 +40,7 @@ describe("UserDropdownContent", () => { const onSignOut = vi.fn(); renderUserDropdownContent({ onSignOut }); await waitForLoaderToBeRemoved(); - screen.getByText(Language.signOutLabel).click(); + screen.getByText("Sign Out").click(); expect(onSignOut).toBeCalledTimes(1); }); }); diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx index eee919763a..e9d86938cc 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx @@ -21,12 +21,6 @@ import { import { useClipboard } from "#/hooks/useClipboard"; import { SupportIcon } from "../SupportIcon"; -export const Language = { - accountLabel: "Account", - signOutLabel: "Sign Out", - copyrightText: `\u00a9 ${new Date().getFullYear()} Coder Technologies, Inc.`, -}; - interface UserDropdownContentProps { user: TypesGen.User; buildInfo?: TypesGen.BuildInfoResponse; @@ -126,7 +120,7 @@ export const UserDropdownContent: FC = ({ )} - {Language.copyrightText} + © {new Date().getFullYear()} Coder Technologies, Inc. ); diff --git a/site/src/modules/resources/SensitiveValue.tsx b/site/src/modules/resources/SensitiveValue.tsx index 99f5da9027..576b2464d0 100644 --- a/site/src/modules/resources/SensitiveValue.tsx +++ b/site/src/modules/resources/SensitiveValue.tsx @@ -8,11 +8,6 @@ import { TooltipTrigger, } from "#/components/Tooltip/Tooltip"; -const Language = { - showLabel: "Show value", - hideLabel: "Hide value", -}; - interface SensitiveValueProps { value: string; } @@ -20,7 +15,7 @@ interface SensitiveValueProps { export const SensitiveValue: FC = ({ value }) => { const [shouldDisplay, setShouldDisplay] = useState(false); const displayValue = shouldDisplay ? value : "••••••••"; - const buttonLabel = shouldDisplay ? Language.hideLabel : Language.showLabel; + const buttonLabel = shouldDisplay ? "Hide value" : "Show value"; const icon = shouldDisplay ? ( ) : ( diff --git a/site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx b/site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx index 097d3c0131..3c8f6acd54 100644 --- a/site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx +++ b/site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx @@ -13,10 +13,6 @@ import { DEFAULT_LOG_LINE_SIDE_PADDING, Logs } from "#/components/Logs/Logs"; import { BODY_FONT_FAMILY } from "#/theme/constants"; import { cn } from "#/utils/cn"; -const Language = { - seconds: "seconds", -}; - type Stage = ProvisionerJobLog["stage"]; type LogsGroupedByStage = Record; type GroupLogsByStageFn = (logs: ProvisionerJobLog[]) => LogsGroupedByStage; @@ -98,9 +94,7 @@ export const WorkspaceBuildLogs: FC = ({ >
{stage}
{shouldDisplayDuration && ( -
- {duration} {Language.seconds} -
+
{duration} seconds
)} {!isEmpty && } diff --git a/site/src/pages/AuditPage/AuditHelpPopover.tsx b/site/src/pages/AuditPage/AuditHelpPopover.tsx index e43af14286..142e1dc22f 100644 --- a/site/src/pages/AuditPage/AuditHelpPopover.tsx +++ b/site/src/pages/AuditPage/AuditHelpPopover.tsx @@ -10,23 +10,20 @@ import { } from "#/components/HelpPopover/HelpPopover"; import { docs } from "#/utils/docs"; -const Language = { - title: "What is an audit log?", - body: "An audit log is a record of events and changes made throughout a system.", - docs: "Events we track", -}; - export const AuditHelpPopover: FC = () => { return ( - {Language.title} - {Language.body} + What is an audit log? + + An audit log is a record of events and changes made throughout a + system. + - {Language.docs} + Events we track diff --git a/site/src/pages/AuditPage/AuditPageView.tsx b/site/src/pages/AuditPage/AuditPageView.tsx index a28e14492e..22a980bf31 100644 --- a/site/src/pages/AuditPage/AuditPageView.tsx +++ b/site/src/pages/AuditPage/AuditPageView.tsx @@ -27,11 +27,6 @@ import { AuditFilter } from "./AuditFilter"; import { AuditHelpPopover } from "./AuditHelpPopover"; import { AuditLogRow } from "./AuditLogRow/AuditLogRow"; -const Language = { - title: "Audit", - subtitle: "View events in your audit log.", -}; - interface AuditPageViewProps { auditLogs?: readonly AuditLog[]; isNonInitialPage: boolean; @@ -62,11 +57,11 @@ export const AuditPageView: FC = ({ - {Language.title} + Audit - {Language.subtitle} + View events in your audit log. diff --git a/site/src/pages/ConnectionLogPage/ConnectionLogHelpPopover.tsx b/site/src/pages/ConnectionLogPage/ConnectionLogHelpPopover.tsx index fcd489d4a9..dc11779326 100644 --- a/site/src/pages/ConnectionLogPage/ConnectionLogHelpPopover.tsx +++ b/site/src/pages/ConnectionLogPage/ConnectionLogHelpPopover.tsx @@ -10,23 +10,21 @@ import { } from "#/components/HelpPopover/HelpPopover"; import { docs } from "#/utils/docs"; -const Language = { - title: "Why are some events missing?", - body: "The connection log is a best-effort log of workspace access. Some events are reported by workspace agents, and receipt of these events by the server is not guaranteed.", - docs: "Connection log documentation", -}; - export const ConnectionLogHelpPopover: FC = () => { return ( - {Language.title} - {Language.body} + Why are some events missing? + + The connection log is a best-effort log of workspace access. Some + events are reported by workspace agents, and receipt of these events + by the server is not guaranteed. + - {Language.docs} + Connection log documentation diff --git a/site/src/pages/ConnectionLogPage/ConnectionLogPageView.tsx b/site/src/pages/ConnectionLogPage/ConnectionLogPageView.tsx index c8c3bf0082..a559ea029a 100644 --- a/site/src/pages/ConnectionLogPage/ConnectionLogPageView.tsx +++ b/site/src/pages/ConnectionLogPage/ConnectionLogPageView.tsx @@ -27,11 +27,6 @@ import { ConnectionLogFilter } from "./ConnectionLogFilter"; import { ConnectionLogHelpPopover } from "./ConnectionLogHelpPopover"; import { ConnectionLogRow } from "./ConnectionLogRow/ConnectionLogRow"; -const Language = { - title: "Connection Log", - subtitle: "View workspace connection events.", -}; - interface ConnectionLogPageViewProps { connectionLogs?: readonly ConnectionLog[]; isNonInitialPage: boolean; @@ -61,11 +56,13 @@ export const ConnectionLogPageView: FC = ({ - {Language.title} + Connection Log - {Language.subtitle} + + View workspace connection events. + diff --git a/site/src/pages/LoginPage/Language.ts b/site/src/pages/LoginPage/Language.ts deleted file mode 100644 index 199a36beba..0000000000 --- a/site/src/pages/LoginPage/Language.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const Language = { - emailLabel: "Email", - passwordLabel: "Password", - emailInvalid: "Please enter a valid email address.", - emailRequired: "Please enter an email address.", - passwordSignIn: "Sign In", - githubSignIn: "GitHub", - oidcSignIn: "OpenID Connect", -}; diff --git a/site/src/pages/LoginPage/LoginPage.test.tsx b/site/src/pages/LoginPage/LoginPage.test.tsx index 91dd44a245..7cf06e92eb 100644 --- a/site/src/pages/LoginPage/LoginPage.test.tsx +++ b/site/src/pages/LoginPage/LoginPage.test.tsx @@ -9,7 +9,6 @@ import { waitForLoaderToBeRemoved, } from "#/testHelpers/renderHelpers"; import { server } from "#/testHelpers/server"; -import { Language } from "./Language"; import LoginPage from "./LoginPage"; describe("LoginPage", () => { @@ -35,12 +34,12 @@ describe("LoginPage", () => { // When render(); await waitForLoaderToBeRemoved(); - const email = screen.getByLabelText(new RegExp(Language.emailLabel)); - const password = screen.getByLabelText(new RegExp(Language.passwordLabel)); + const email = screen.getByLabelText(/Email/); + const password = screen.getByLabelText(/Password/); await userEvent.type(email, "test@coder.com"); await userEvent.type(password, "password"); // Click sign-in - const signInButton = await screen.findByText(Language.passwordSignIn); + const signInButton = await screen.findByText("Sign In"); fireEvent.click(signInButton); // Then @@ -53,10 +52,8 @@ describe("LoginPage", () => { render(); await waitForLoaderToBeRemoved(); - const emailInput = screen.getByLabelText(new RegExp(Language.emailLabel)); - const passwordInput = screen.getByLabelText( - new RegExp(Language.passwordLabel), - ); + const emailInput = screen.getByLabelText(/Email/); + const passwordInput = screen.getByLabelText(/Password/); expect(emailInput).not.toHaveAttribute("aria-invalid", "true"); expect(emailInput).not.toHaveAttribute( "aria-describedby", @@ -68,11 +65,13 @@ describe("LoginPage", () => { "signin-password-error", ); - const signInButton = await screen.findByText(Language.passwordSignIn); + const signInButton = await screen.findByText("Sign In"); fireEvent.click(signInButton); // Then - const emailError = await screen.findByText(Language.emailRequired); + const emailError = await screen.findByText( + "Please enter an email address.", + ); expect(emailInput).toHaveAttribute("aria-invalid", "true"); expect(emailInput).toHaveAttribute( "aria-describedby", @@ -81,7 +80,9 @@ describe("LoginPage", () => { const emailErrorElement = document.getElementById("signin-email-error"); expect(emailErrorElement).toBe(emailError); - expect(emailErrorElement).toHaveTextContent(Language.emailRequired); + expect(emailErrorElement).toHaveTextContent( + "Please enter an email address.", + ); expect(passwordInput).not.toHaveAttribute("aria-invalid", "true"); expect(passwordInput).not.toHaveAttribute( @@ -240,13 +241,13 @@ describe("LoginPage", () => { await waitForLoaderToBeRemoved(); - const email = screen.getByLabelText(new RegExp(Language.emailLabel)); - const password = screen.getByLabelText(new RegExp(Language.passwordLabel)); + const email = screen.getByLabelText(/Email/); + const password = screen.getByLabelText(/Password/); await userEvent.type(email, "test@coder.com"); await userEvent.type(password, "password"); - const signInButton = await screen.findByText(Language.passwordSignIn); + const signInButton = await screen.findByText("Sign In"); fireEvent.click(signInButton); // Then - it should hard redirect to OAuth endpoint diff --git a/site/src/pages/LoginPage/OAuthSignInForm.tsx b/site/src/pages/LoginPage/OAuthSignInForm.tsx index 577a57ea01..6e059ca487 100644 --- a/site/src/pages/LoginPage/OAuthSignInForm.tsx +++ b/site/src/pages/LoginPage/OAuthSignInForm.tsx @@ -3,7 +3,6 @@ import { type FC, useId } from "react"; import type { AuthMethods } from "#/api/typesGenerated"; import { Button } from "#/components/Button/Button"; import { ExternalImage } from "#/components/ExternalImage/ExternalImage"; -import { Language } from "./Language"; type OAuthSignInFormProps = { isSigningIn: boolean; @@ -33,7 +32,7 @@ export const OAuthSignInForm: FC = ({ )}`} > - {Language.githubSignIn} + GitHub )} @@ -57,7 +56,7 @@ export const OAuthSignInForm: FC = ({ ) : ( )} - {authMethods.oidc.signInText || Language.oidcSignIn} + {authMethods.oidc.signInText || "OpenID Connect"} )} diff --git a/site/src/pages/LoginPage/PasswordSignInForm.tsx b/site/src/pages/LoginPage/PasswordSignInForm.tsx index b6783b6a0b..0fed2d2992 100644 --- a/site/src/pages/LoginPage/PasswordSignInForm.tsx +++ b/site/src/pages/LoginPage/PasswordSignInForm.tsx @@ -8,7 +8,6 @@ import { Label } from "#/components/Label/Label"; import { Link } from "#/components/Link/Link"; import { Spinner } from "#/components/Spinner/Spinner"; import { getFormHelpers, onChangeTrimmed } from "#/utils/formUtils"; -import { Language } from "./Language"; type PasswordSignInFormProps = { onSubmit: (credentials: { email: string; password: string }) => void; @@ -24,8 +23,8 @@ export const PasswordSignInForm: FC = ({ const validationSchema = Yup.object({ email: Yup.string() .trim() - .email(Language.emailInvalid) - .required(Language.emailRequired), + .email("Please enter a valid email address.") + .required("Please enter an email address."), password: Yup.string(), }); @@ -48,7 +47,7 @@ export const PasswordSignInForm: FC = ({
= ({
= ({ = { roles: { title: "What is a role?", text: @@ -26,7 +26,6 @@ const Language = { "View our docs on how to use the available roles.", links: [{ text: "User Roles", href: docs("/admin/users/groups-roles") }], }, - groups: { title: "What is a group?", text: @@ -34,7 +33,6 @@ const Language = { "to specific templates. View our docs on how to use groups.", links: [{ text: "User Groups", href: docs("/admin/users/groups-roles") }], }, - ai_addon: { title: "What is the AI add-on?", text: @@ -42,24 +40,24 @@ const Language = { "who are actively consuming a seat.", links: [], }, -} as const satisfies Record; +}; type Props = { variant: ColumnHeader; }; export const TableColumnHelpPopover: FC = ({ variant }) => { - const variantLang = Language[variant]; + const data = tooltipData[variant]; return ( - {variantLang.title} - {variantLang.text} - {variantLang.links.length > 0 && ( + {data.title} + {data.text} + {data.links.length > 0 && ( - {variantLang.links.map((link) => ( + {data.links.map((link) => ( {link.text} diff --git a/site/src/pages/SetupPage/SetupPage.test.tsx b/site/src/pages/SetupPage/SetupPage.test.tsx index b9de311a08..e6676c4561 100644 --- a/site/src/pages/SetupPage/SetupPage.test.tsx +++ b/site/src/pages/SetupPage/SetupPage.test.tsx @@ -10,7 +10,6 @@ import { } from "#/testHelpers/renderHelpers"; import { server } from "#/testHelpers/server"; import { SetupPage } from "./SetupPage"; -import { Language as PageViewLanguage } from "./SetupPageView"; const fillForm = async ({ email = "someone@coder.com", @@ -20,12 +19,12 @@ const fillForm = async ({ email?: string; password?: string; } = {}) => { - const emailField = screen.getByLabelText(PageViewLanguage.emailLabel); - const passwordField = screen.getByLabelText(PageViewLanguage.passwordLabel); + const emailField = screen.getByLabelText("Email"); + const passwordField = screen.getByLabelText("Password"); await userEvent.type(emailField, email); await userEvent.type(passwordField, password); const submitButton = screen.getByRole("button", { - name: PageViewLanguage.create, + name: "Continue with email", }); await userEvent.click(submitButton); }; diff --git a/site/src/pages/SetupPage/SetupPageView.tsx b/site/src/pages/SetupPage/SetupPageView.tsx index cf0fe3fff0..a2e4fbae21 100644 --- a/site/src/pages/SetupPage/SetupPageView.tsx +++ b/site/src/pages/SetupPage/SetupPageView.tsx @@ -24,33 +24,7 @@ import { onChangeTrimmed, } from "#/utils/formUtils"; -export const Language = { - emailLabel: "Email", - passwordLabel: "Password", - nameLabel: "Full Name", - usernameLabel: "Username", - emailInvalid: "Please enter a valid email address.", - emailRequired: "Please enter an email address.", - passwordRequired: "Please enter a password.", - create: "Continue with email", - githubCreate: "Continue with GitHub", - welcomeMessage: <>Welcome to Coder, - firstNameLabel: "First name", - lastNameLabel: "Last name", - companyLabel: "Company", - jobTitleLabel: "Job title", - phoneNumberLabel: "Phone number", - countryLabel: "Country", - developersLabel: "Number of developers", - firstNameRequired: "Please enter your first name.", - phoneNumberRequired: "Please enter your phone number.", - jobTitleRequired: "Please enter your job title.", - companyNameRequired: "Please enter your company name.", - countryRequired: "Please select your country.", - developersRequired: "Please select the number of developers in your company.", -}; - -const usernameValidator = nameValidator(Language.usernameLabel); +const usernameValidator = nameValidator("Username"); const usernameFromEmail = (email: string): string => { try { const emailPrefix = email.split("@")[0]; @@ -69,22 +43,24 @@ const usernameFromEmail = (email: string): string => { const validationSchema = Yup.object({ email: Yup.string() .trim() - .email(Language.emailInvalid) - .required(Language.emailRequired), - password: Yup.string().required(Language.passwordRequired), + .email("Please enter a valid email address.") + .required("Please enter an email address."), + password: Yup.string().required("Please enter a password."), username: usernameValidator, trial: Yup.bool(), trial_info: Yup.object().when("trial", { is: true, then: (schema) => schema.shape({ - first_name: Yup.string().required(Language.firstNameRequired), - last_name: Yup.string().required(Language.firstNameRequired), - phone_number: Yup.string().required(Language.phoneNumberRequired), - job_title: Yup.string().required(Language.jobTitleRequired), - company_name: Yup.string().required(Language.companyNameRequired), - country: Yup.string().required(Language.countryRequired), - developers: Yup.string().required(Language.developersRequired), + first_name: Yup.string().required("Please enter your first name."), + last_name: Yup.string().required("Please enter your last name."), + phone_number: Yup.string().required("Please enter your phone number."), + job_title: Yup.string().required("Please enter your job title."), + company_name: Yup.string().required("Please enter your company name."), + country: Yup.string().required("Please select your country."), + developers: Yup.string().required( + "Please select the number of developers in your company.", + ), }), }), }); @@ -163,7 +139,7 @@ export const SetupPageView: FC = ({
@@ -185,13 +161,13 @@ export const SetupPageView: FC = ({ }} autoComplete="email" fullWidth - label={Language.emailLabel} + label="Email" />