fix: replace hard-coded strings with translation keys for internationalization (#20672)

* fix: replace hard-coded strings with translation keys for internationalization

Co-Authored-By: benny@cal.com <benny@cal.com>

* fix: replace loading text with translation key

Co-Authored-By: benny@cal.com <benny@cal.com>

* fix: add translation keys for platform plans view

Co-Authored-By: benny@cal.com <benny@cal.com>

* fix: add translation keys for oauth view

Co-Authored-By: benny@cal.com <benny@cal.com>

* fix: add translation keys for users table

Co-Authored-By: benny@cal.com <benny@cal.com>

* fix: add translation keys for oauth view toast messages

Co-Authored-By: benny@cal.com <benny@cal.com>

* fix: add translation keys for verify view

Co-Authored-By: benny@cal.com <benny@cal.com>

* fix: update sendVerificationLogin to use t function

Co-Authored-By: benny@cal.com <benny@cal.com>

* fix: remove duplicate translation keys in common.json

Co-Authored-By: benny@cal.com <benny@cal.com>

* fix: add missing verification_email_sent translation key

Co-Authored-By: benny@cal.com <benny@cal.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: benny@cal.com <benny@cal.com>
This commit is contained in:
devin-ai-integration[bot]
2025-04-12 01:50:04 -04:00
committed by GitHub
co-authored by Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> benny@cal.com <benny@cal.com>
parent 027cad2cfd
commit 4383d23d4c
13 changed files with 93 additions and 57 deletions
@@ -1,3 +1,4 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/components/button";
type PlatformBillingCardProps = {
@@ -19,6 +20,7 @@ export const PlatformBillingCard = ({
handleSubscribe,
currentPlan,
}: PlatformBillingCardProps) => {
const { t } = useLocale();
return (
<div className="border-subtle max-w-[450px] rounded-2xl border p-5 md:mx-4">
<div className="pb-5">
@@ -30,7 +32,7 @@ export const PlatformBillingCard = ({
type="button"
StartIcon="circle-check"
className="bg-default hover:bg-default cursor-none text-green-500 hover:cursor-pointer"
tooltip="This is your current plan"
tooltip={t("this_is_your_current_plan")}
/>
</>
)}
@@ -39,7 +41,7 @@ export const PlatformBillingCard = ({
<h1 className="text-3xl font-semibold">
{pricing !== undefined && (
<>
US${pricing} <span className="text-sm">per month</span>
US${pricing} <span className="text-sm">{t("per_month")}</span>
</>
)}
</h1>
@@ -50,12 +52,12 @@ export const PlatformBillingCard = ({
loading={isLoading}
onClick={handleSubscribe}
className="flex w-[100%] items-center justify-center">
{pricing !== undefined ? "Subscribe" : "Schedule a time"}
{pricing !== undefined ? t("subscribe") : t("schedule_a_time")}
</Button>
</div>
)}
<div className="mt-5">
<p>This includes:</p>
<p>{t("this_includes")}</p>
{includes.map((feature) => {
return (
<div key={feature} className="my-2 flex">
@@ -29,7 +29,7 @@ export default function Apps({ apps, category }: CategoryDataProps) {
</Link>
{category && (
<span className="text-default gap-1">
<span>&nbsp;/&nbsp;</span>
<span>{t("slash_separator")}</span>
{t("category_apps", { category: category[0].toUpperCase() + category?.slice(1) })}
</span>
)}
+21 -21
View File
@@ -8,16 +8,17 @@ import z from "zod";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
import { trpc } from "@calcom/trpc/react";
import { showToast } from "@calcom/ui/components/toast";
import { Button } from "@calcom/ui/components/button";
import classNames from "@calcom/ui/classNames";
import { Button } from "@calcom/ui/components/button";
import { Icon } from "@calcom/ui/components/icon";
import { showToast } from "@calcom/ui/components/toast";
import Loader from "@components/Loader";
async function sendVerificationLogin(email: string, username: string) {
async function sendVerificationLogin(email: string, username: string, t: (key: string) => string) {
await signIn("email", {
email: email.toLowerCase(),
username: username.toLowerCase(),
@@ -25,7 +26,7 @@ async function sendVerificationLogin(email: string, username: string) {
callbackUrl: WEBAPP_URL || "https://app.cal.com",
})
.then(() => {
showToast("Verification email sent", "success");
showToast(t("verification_email_sent"), "success");
})
.catch((err) => {
showToast(err, "error");
@@ -40,15 +41,16 @@ function useSendFirstVerificationLogin({
username: string | undefined;
}) {
const sent = useRef(false);
const { t } = useLocale();
useEffect(() => {
if (!email || !username || sent.current) {
return;
}
(async () => {
await sendVerificationLogin(email, username);
await sendVerificationLogin(email, username, t);
sent.current = true;
})();
}, [email, username]);
}, [email, username, t]);
}
const querySchema = z.object({
@@ -117,7 +119,8 @@ export default function Verify({ EMAIL_FROM }: { EMAIL_FROM?: string }) {
const pathname = usePathname();
const router = useRouter();
const routerQuery = useRouterQuery();
const { t, sessionId, stripeCustomerId } = querySchema.parse(routerQuery);
const { t: tParam, sessionId, stripeCustomerId } = querySchema.parse(routerQuery);
const { t } = useLocale();
const [secondsLeft, setSecondsLeft] = useState(30);
const { data } = trpc.viewer.public.stripeCheckoutSession.useQuery(
{
@@ -166,7 +169,7 @@ export default function Verify({ EMAIL_FROM }: { EMAIL_FROM?: string }) {
}
if (!stripeCustomerId && !sessionId) {
return <div>Invalid Link</div>;
return <div>{t("invalid_link")}</div>;
}
return (
@@ -176,18 +179,15 @@ export default function Verify({ EMAIL_FROM }: { EMAIL_FROM?: string }) {
{hasPaymentFailed ? <PaymentFailedIcon /> : sessionId ? <PaymentSuccess /> : <MailOpenIcon />}
<h3 className="font-cal text-emphasis my-6 text-2xl font-normal leading-none">
{hasPaymentFailed
? "Your payment failed"
? t("your_payment_failed")
: sessionId
? "Payment successful!"
: "Check your Inbox"}
? t("payment_successful")
: t("check_your_inbox")}
</h3>
{hasPaymentFailed && (
<p className="my-6">Your account has been created, but your premium has not been reserved.</p>
)}
{hasPaymentFailed && <p className="my-6">{t("account_created_premium_not_reserved")}</p>}
<p className="text-muted dark:text-subtle text-base font-normal">
We have sent an email to <b>{customer?.email} </b>with a link to activate your account.{" "}
{hasPaymentFailed &&
"Once you activate your account you will be able to try purchase your premium username again or select a different one."}
{t("email_sent_with_activation_link", { email: customer?.email })}{" "}
{hasPaymentFailed && t("activate_account_to_purchase_username")}
</p>
<div className="mt-7">
<Button
@@ -199,12 +199,12 @@ export default function Verify({ EMAIL_FROM }: { EMAIL_FROM?: string }) {
}
target="_blank"
EndIcon="external-link">
Open in Gmail
{t("open_in_gmail")}
</Button>
</div>
</div>
<div className="flex items-center gap-2">
<p className="text-subtle text-base font-normal ">Dont seen an email?</p>
<p className="text-subtle text-base font-normal ">{t("dont_see_email")}</p>
<button
className={classNames(
"font-light",
@@ -221,9 +221,9 @@ export default function Verify({ EMAIL_FROM }: { EMAIL_FROM?: string }) {
const _searchParams = new URLSearchParams(searchParams?.toString());
_searchParams.set("t", `${Date.now()}`);
router.replace(`${pathname}?${_searchParams.toString()}`);
return await sendVerificationLogin(customer.email, customer.username);
return await sendVerificationLogin(customer.email, customer.username, t);
}}>
{secondsLeft > 0 ? `Resend in ${secondsLeft} seconds` : "Resend"}
{secondsLeft > 0 ? t("resend_in_seconds", { seconds: secondsLeft }) : t("resend")}
</button>
</div>
</div>
@@ -1,18 +1,17 @@
"use client";
import { WEBSITE_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/components/button";
export default function MaintenancePage() {
const { t } = useLocale();
return (
<div className="bg-subtle flex h-screen">
<div className="bg-default m-auto rounded-md p-10 text-right ltr:text-left">
<h1 className="text-emphasis text-2xl font-medium">Down for maintenance</h1>
<p className="text-default mb-6 mt-4 max-w-2xl text-sm">
The Cal.com team are performing scheduled maintenance. If you have any questions, please contact
support.
</p>
<Button href={`${WEBSITE_URL}/support`}>Contact Support</Button>
<h1 className="text-emphasis text-2xl font-medium">{t("down_for_maintenance")}</h1>
<p className="text-default mb-6 mt-4 max-w-2xl text-sm">{t("maintenance_message")}</p>
<Button href={`${WEBSITE_URL}/support`}>{t("contact_support")}</Button>
</div>
</div>
);
@@ -1,5 +1,6 @@
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { SMSLockState } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
import { Avatar } from "@calcom/ui/components/avatar";
@@ -51,6 +52,7 @@ const LockStatusTable = ({
teams?: Team[];
setSMSLockState: (param: { userId?: number; teamId?: number; lock: boolean }) => void;
}) => {
const { t } = useLocale();
function getActions({ user, team }: { user?: User; team?: Team }) {
const smsLockState = user?.smsLockState ?? team?.smsLockState;
if (!smsLockState) return [];
@@ -89,10 +91,10 @@ const LockStatusTable = ({
<>
<Table>
<Header>
<ColumnTitle widthClassNames="w-auto">User/Team</ColumnTitle>
<ColumnTitle>Status</ColumnTitle>
<ColumnTitle widthClassNames="w-auto">{t("user_team")}</ColumnTitle>
<ColumnTitle>{t("status")}</ColumnTitle>
<ColumnTitle widthClassNames="w-auto">
<span className="sr-only">Edit</span>
<span className="sr-only">{t("edit")}</span>
</ColumnTitle>
</Header>
@@ -5,12 +5,12 @@ import { useForm } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { ImageUploader } from "@calcom/ui/components/image-uploader";
import { Avatar } from "@calcom/ui/components/avatar";
import { Button } from "@calcom/ui/components/button";
import { Form } from "@calcom/ui/components/form";
import { TextField } from "@calcom/ui/components/form";
import { Icon } from "@calcom/ui/components/icon";
import { ImageUploader } from "@calcom/ui/components/image-uploader";
import { showToast } from "@calcom/ui/components/toast";
import { Tooltip } from "@calcom/ui/components/tooltip";
@@ -95,7 +95,7 @@ export default function OAuthView() {
) : (
<div>
<div className="text-emphasis mb-5 text-xl font-semibold">{oAuthForm.getValues("name")}</div>
<div className="mb-2 font-medium">Client Id</div>
<div className="mb-2 font-medium">{t("client_id")}</div>
<div className="flex">
<code className="bg-subtle text-default w-full truncate rounded-md rounded-r-none py-[6px] pl-2 pr-2 align-middle font-mono">
{" "}
@@ -105,7 +105,7 @@ export default function OAuthView() {
<Button
onClick={() => {
navigator.clipboard.writeText(clientId);
showToast("Client ID copied!", "success");
showToast(t("client_id_copied"), "success");
}}
type="button"
className="rounded-l-none text-base"
@@ -116,7 +116,7 @@ export default function OAuthView() {
</div>
{clientSecret ? (
<>
<div className="mb-2 mt-4 font-medium">Client Secret</div>
<div className="mb-2 mt-4 font-medium">{t("client_secret")}</div>
<div className="flex">
<code className="bg-subtle text-default w-full truncate rounded-md rounded-r-none py-[6px] pl-2 pr-2 align-middle font-mono">
{" "}
@@ -127,7 +127,7 @@ export default function OAuthView() {
onClick={() => {
navigator.clipboard.writeText(clientSecret);
setClientSecret("");
showToast("Client secret copied!", "success");
showToast(t("client_secret_copied"), "success");
}}
type="button"
className="rounded-l-none text-base"
@@ -38,7 +38,7 @@ export default function PlatformBillingUpgrade() {
useGetUserAttributes();
if (isUserLoading || (isUserBillingDataLoading && !userBillingData)) {
return <div className="m-5">Loading...</div>;
return <div className="m-5">{t("loading")}</div>;
}
if (isPlatformUser && !isPaidUser)
@@ -47,7 +47,7 @@ export default function PlatformBillingUpgrade() {
teamId={userOrgId}
heading={
<div className="mb-5 text-center text-2xl font-semibold">
<h1>Subscribe to Platform</h1>
<h1>{t("subscribe_to_platform")}</h1>
</div>
}
/>
@@ -28,7 +28,7 @@ export default function EditOAuthClient() {
const { data, isFetched, isFetching, isError, refetch } = useOAuthClient(clientId);
const { mutateAsync: update, isPending: isUpdating } = useUpdateOAuthClient({
onSuccess: () => {
showToast("OAuth client updated successfully", "success");
showToast(t("oauth_client_updated_successfully"), "success");
refetch();
router.push("/settings/platform/");
},
@@ -63,7 +63,7 @@ export default function EditOAuthClient() {
});
};
if (isUserLoading) return <div className="m-5">Loading...</div>;
if (isUserLoading) return <div className="m-5">{t("loading")}</div>;
if (isPlatformUser && isPaidUser) {
return (
@@ -80,7 +80,7 @@ export default function EditOAuthClient() {
</p>
</div>
</div>
{(!Boolean(clientId) || (isFetched && !data)) && <p>OAuth Client not found.</p>}
{(!Boolean(clientId) || (isFetched && !data)) && <p>{t("oauth_client_not_found")}</p>}
{isFetched && !!data && (
<EditOAuthClientForm
defaultValues={{
@@ -106,8 +106,8 @@ export default function EditOAuthClient() {
isPending={isUpdating}
/>
)}
{isFetching && <p>Loading...</p>}
{isError && <p>Something went wrong.</p>}
{isFetching && <p>{t("loading")}</p>}
{isError && <p>{t("something_went_wrong")}</p>}
</div>
</Shell>
</div>
@@ -37,7 +37,7 @@ export default function EditOAuthClientWebhooks() {
const { mutateAsync: createWebhook } = useCreateOAuthClientWebhook(clientId);
const { mutateAsync: updateWebhook } = useUpdateOAuthClientWebhook(clientId);
if (isUserLoading) return <div className="m-5">Loading...</div>;
if (isUserLoading) return <div className="m-5">{t("loading")}</div>;
if (isPlatformUser && isPaidUser) {
return (
@@ -55,7 +55,7 @@ export default function EditOAuthClientWebhooks() {
</div>
</div>
{webhooksStatus !== "success" && <p>Error while trying to access webhooks.</p>}
{webhooksStatus !== "success" && <p>{t("error_accessing_webhooks")}</p>}
{isWebhooksFetched && webhooksStatus === "success" && (
<WebhookForm
@@ -96,7 +96,7 @@ export default function EditOAuthClientWebhooks() {
await refetchWebhooks();
router.push("/settings/platform/");
} catch (err) {
showToast(`Failed to ${webhookId ? "update" : "create"} webhook.`, "error");
showToast(t(webhookId ? "webhook_update_failed" : "webhook_create_failed"), "error");
}
}}
onCancel={() => {
@@ -59,7 +59,7 @@ export default function CreateOAuthClient() {
});
};
if (isUserLoading) return <div className="m-5">Loading...</div>;
if (isUserLoading) return <div className="m-5">{t("loading")}</div>;
if (isPlatformUser && isPaidUser) {
return (
@@ -1,17 +1,19 @@
"use client";
import Shell from "@calcom/features/shell/Shell";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import NoPlatformPlan from "@components/settings/platform/dashboard/NoPlatformPlan";
import { useGetUserAttributes } from "@components/settings/platform/hooks/useGetUserAttributes";
import { PlatformPricing } from "@components/settings/platform/pricing/platform-pricing";
export default function PlatformPlans() {
const { t } = useLocale();
const { isUserLoading, isUserBillingDataLoading, isPlatformUser, isPaidUser, userBillingData, userOrgId } =
useGetUserAttributes();
if (isUserLoading || (isUserBillingDataLoading && !userBillingData)) {
return <div className="m-5">Loading...</div>;
return <div className="m-5">{t("loading")}</div>;
}
if (!isPlatformUser) return <NoPlatformPlan />;
@@ -23,8 +25,10 @@ export default function PlatformPlans() {
isPlatformUser={true}
heading={
<h1 className="mx-2 mt-4 text-center text-xl md:text-2xl">
You are currently subscribed to {userBillingData?.plan[0]}
{userBillingData?.plan.slice(1).toLocaleLowerCase()} plan
{t("currently_subscribed_to_plan", {
planFirstLetter: userBillingData?.plan[0],
planRest: userBillingData?.plan.slice(1).toLocaleLowerCase(),
})}
</h1>
}
withoutMain={false}
@@ -44,10 +44,10 @@ export default function Platform() {
setInitialClientName(data[0]?.name);
}, [data]);
if (isUserLoading || isOAuthClientLoading) return <div className="m-5">Loading...</div>;
if (isUserLoading || isOAuthClientLoading) return <div className="m-5">{t("loading")}</div>;
if (isUserBillingDataLoading && !userBillingData) {
return <div className="m-5">Loading...</div>;
return <div className="m-5">{t("loading")}</div>;
}
if (isPlatformUser && !isPaidUser)
@@ -56,7 +56,7 @@ export default function Platform() {
teamId={userOrgId}
heading={
<div className="mb-5 text-center text-2xl font-semibold">
<h1>Subscribe to Platform</h1>
<h1>{t("subscribe_to_platform")}</h1>
</div>
}
/>
@@ -3080,5 +3080,34 @@
"disable_delegation_credential_description": "Once delegation credential is disabled, organization members who havent connected their calendars will need to do so manually.",
"salesforce_on_cancel_write_to_event": "On cancelled booking, write to event record instead of deleting event",
"salesforce_on_every_cancellation": "On every cancellation",
"this_is_your_current_plan": "This is your current plan",
"per_month": "per month",
"schedule_a_time": "Schedule a time",
"this_includes": "This includes:",
"oauth_client_updated_successfully": "OAuth client updated successfully",
"oauth_client_not_found": "OAuth Client not found.",
"subscribe_to_platform": "Subscribe to Platform",
"error_accessing_webhooks": "Error while trying to access webhooks.",
"slash_separator": "&nbsp;/&nbsp;",
"webhook_update_failed": "Failed to update webhook.",
"webhook_create_failed": "Failed to create webhook.",
"invalid_link": "Invalid Link",
"currently_subscribed_to_plan": "You are currently subscribed to {{planFirstLetter}}{{planRest}} plan",
"user_team": "User/Team",
"client_id_copied": "Client ID copied!",
"client_secret_copied": "Client secret copied!",
"down_for_maintenance": "Down for maintenance",
"maintenance_message": "The Cal.com team are performing scheduled maintenance. If you have any questions, please contact support.",
"your_payment_failed": "Your payment failed",
"payment_successful": "Payment successful!",
"check_your_inbox": "Check your Inbox",
"account_created_premium_not_reserved": "Your account has been created, but your premium has not been reserved.",
"email_sent_with_activation_link": "We have sent an email to {{email}} with a link to activate your account.",
"activate_account_to_purchase_username": "Once you activate your account you will be able to try purchase your premium username again or select a different one.",
"open_in_gmail": "Open in Gmail",
"dont_see_email": "Don't seen an email?",
"resend_in_seconds": "Resend in {{seconds}} seconds",
"resend": "Resend",
"verification_email_sent": "Verification email sent",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}