i18n: continued (#949)

* more extractions

* fix: failing build
pull/951/head^2
Mihai C 2021-10-14 17:24:21 +03:00 committed by GitHub
parent 55d77993af
commit f955ccdef9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 269 additions and 173 deletions

View File

@ -4,40 +4,42 @@ import Link from "next/link";
import { useRouter } from "next/router";
import React from "react";
import { useLocale } from "@lib/hooks/useLocale";
import { HeadSeo } from "@components/seo/head-seo";
const links = [
{
title: "Documentation",
description: "Learn how to integrate our tools with your app",
icon: DocumentTextIcon,
href: "https://docs.cal.com",
},
{
title: "API Reference",
description: "A complete API reference for our libraries",
icon: CodeIcon,
href: "https://api.docs.cal.com",
},
{
title: "Blog",
description: "Read our latest news and articles",
icon: BookOpenIcon,
href: "https://cal.com/blog",
},
];
export default function Custom404() {
const { t } = useLocale();
const router = useRouter();
const username = router.asPath.replace("%20", "-");
const links = [
{
title: t("documentation"),
description: t("documentation_description"),
icon: DocumentTextIcon,
href: "https://docs.cal.com",
},
{
title: t("api_reference"),
description: t("api_reference_description"),
icon: CodeIcon,
href: "https://api.docs.cal.com",
},
{
title: t("blog"),
description: t("blog_description"),
icon: BookOpenIcon,
href: "https://cal.com/blog",
},
];
const isEventType404 = router.asPath.includes("/event-types");
return (
<>
<HeadSeo
title="404: This page could not be found."
description="404: This page could not be found."
title={t("404_page_not_found")}
description={t("404_page_not_found")}
nextSeoProps={{
nofollow: true,
noindex: true,
@ -48,21 +50,21 @@ export default function Custom404() {
<div className="text-center">
<p className="text-sm font-semibold text-black uppercase tracking-wide">404 error</p>
<h1 className="font-cal mt-2 text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl">
This page does not exist.
{t("page_doesnt_exist")}
</h1>
{isEventType404 ? (
<span className="inline-block mt-2 text-lg ">
Check for spelling mistakes or go back to the previous page.
</span>
<span className="inline-block mt-2 text-lg ">{t("check_spelling_mistakes_or_go_back")}</span>
) : (
<a href="https://cal.com/signup" className="inline-block mt-2 text-lg ">
The username <strong className="text-blue-500">cal.com{username}</strong> is still available.{" "}
<span className="text-blue-500">Register now</span>.
{t("the_username")} <strong className="text-blue-500">cal.com{username}</strong>{" "}
{t("is_still_available")} <span className="text-blue-500">{t("register_now")}</span>.
</a>
)}
</div>
<div className="mt-12">
<h2 className="text-sm font-semibold text-gray-500 tracking-wide uppercase">Popular pages</h2>
<h2 className="text-sm font-semibold text-gray-500 tracking-wide uppercase">
{t("popular_pages")}
</h2>
{!isEventType404 && (
<ul role="list" className="mt-4">
<li className="border-2 border-green-500 px-4 py-2">
@ -77,11 +79,11 @@ export default function Custom404() {
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500">
<span className="focus:outline-none">
<span className="absolute inset-0" aria-hidden="true" />
Register <strong className="text-green-500">{username}</strong>
{t("register")} <strong className="text-green-500">{username}</strong>
</span>
</span>
</h3>
<p className="text-base text-gray-500">Claim your username and schedule events</p>
<p className="text-base text-gray-500">{t("claim_username_and_schedule_events")}</p>
</div>
<div className="flex-shrink-0 self-center">
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
@ -146,7 +148,7 @@ export default function Custom404() {
Slack
</span>
</h3>
<p className="text-base text-gray-500">Join our community</p>
<p className="text-base text-gray-500">{t("join_our_community")}</p>
</div>
<div className="flex-shrink-0 self-center">
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
@ -157,7 +159,8 @@ export default function Custom404() {
<div className="mt-8">
<Link href="/">
<a className="text-base font-medium text-black hover:text-gray-500">
Or go back home<span aria-hidden="true"> &rarr;</span>
{t("or_go_back_home")}
<span aria-hidden="true"> &rarr;</span>
</a>
</Link>
</div>

View File

@ -2,9 +2,12 @@ import { XIcon } from "@heroicons/react/outline";
import Link from "next/link";
import { useRouter } from "next/router";
import { useLocale } from "@lib/hooks/useLocale";
import { HeadSeo } from "@components/seo/head-seo";
export default function Error() {
const { t } = useLocale();
const router = useRouter();
const { error } = router.query;
@ -14,7 +17,7 @@ export default function Error() {
aria-labelledby="modal-title"
role="dialog"
aria-modal="true">
<HeadSeo title="Error" description="Error" />
<HeadSeo title={t("error")} description={t("error")} />
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
&#8203;
@ -29,16 +32,14 @@ export default function Error() {
{error}
</h3>
<div className="mt-2">
<p className="text-sm text-gray-500">
An error occurred when logging you in. Head back to the login screen and try again.
</p>
<p className="text-sm text-gray-500">{t("error_during_login")}</p>
</div>
</div>
</div>
<div className="mt-5 sm:mt-6">
<Link href="/auth/login">
<a className="inline-flex justify-center w-full rounded-sm border border-transparent shadow-sm px-4 py-2 bg-neutral-900 text-base font-medium text-white hover:bg-neutral-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-500 sm:text-sm">
Go back to the login page
{t("go_back_login")}
</a>
</Link>
</div>

View File

@ -6,6 +6,7 @@ import { getCsrfToken } from "next-auth/client";
import Link from "next/link";
import React, { useMemo } from "react";
import { useLocale } from "@lib/hooks/useLocale";
import prisma from "@lib/prisma";
import { HeadSeo } from "@components/seo/head-seo";
@ -17,6 +18,7 @@ type Props = {
};
export default function Page({ resetPasswordRequest, csrfToken }: Props) {
const { t } = useLocale();
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState(null);
const [success, setSuccess] = React.useState(false);
@ -46,7 +48,7 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
return json;
} catch (reason) {
setError({ message: "An unexpected error occurred. Try again." });
setError({ message: t("unexpected_error_try_again") });
} finally {
setLoading(false);
}
@ -77,14 +79,16 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
<>
<div className="space-y-6">
<div>
<h2 className="font-cal mt-6 text-center text-3xl font-extrabold text-gray-900">Success</h2>
<h2 className="font-cal mt-6 text-center text-3xl font-extrabold text-gray-900">
{t("success")}
</h2>
</div>
<p>Your password has been reset. You can now login with your newly created password.</p>
<p>{t("password_has_been_reset_login")}</p>
<Link href="/auth/login">
<button
type="button"
className="w-full flex justify-center py-2 px-4 text-sm font-medium text-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black">
Login
{t("login")}
</button>
</Link>
</div>
@ -97,18 +101,15 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
<>
<div className="space-y-6">
<div>
<h2 className="font-cal mt-6 text-center text-3xl font-extrabold text-gray-900">Whoops</h2>
<h2 className="text-center text-3xl font-extrabold text-gray-900">That Request is Expired.</h2>
<h2 className="font-cal mt-6 text-center text-3xl font-extrabold text-gray-900">{t("whoops")}</h2>
<h2 className="text-center text-3xl font-extrabold text-gray-900">{t("request_is_expired")}</h2>
</div>
<p>
That request is expired. You can back and enter the email associated with your account and we will
you another link to reset your password.
</p>
<p>{t("request_is_expired_instructions")}</p>
<Link href="/auth/forgot-password">
<button
type="button"
className="w-full flex justify-center py-2 px-4 text-sm font-medium text-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black">
Try Again
{t("try_again")}
</button>
</Link>
</div>
@ -123,7 +124,7 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
return (
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<HeadSeo title="Reset Password" description="Change your password" />
<HeadSeo title={t("reset_password")} description={t("change_your_password")} />
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="bg-white py-8 px-4 mx-2 shadow rounded-lg sm:px-10 space-y-6">
{isRequestExpired && <Expired />}
@ -131,16 +132,16 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
<>
<div className="space-y-6">
<h2 className="font-cal mt-6 text-center text-3xl font-extrabold text-gray-900">
Reset Password
{t("reset_password")}
</h2>
<p>Enter the new password you&apos;d like for your account.</p>
<p>{t("enter_new_password")}</p>
{error && <p className="text-red-600">{error.message}</p>}
</div>
<form className="space-y-6" onSubmit={handleSubmit} action="#">
<input name="csrfToken" type="hidden" defaultValue={csrfToken} hidden />
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
New Password
{t("new_password")}
</label>
<div className="mt-1">
<input
@ -181,7 +182,7 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
)}
Submit
{t("submit")}
</button>
</div>
</form>

View File

@ -4,10 +4,12 @@ import Link from "next/link";
import React from "react";
import { getSession } from "@lib/auth";
import { useLocale } from "@lib/hooks/useLocale";
import { HeadSeo } from "@components/seo/head-seo";
export default function ForgotPassword({ csrfToken }) {
const { t } = useLocale();
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState(null);
const [success, setSuccess] = React.useState(false);
@ -36,7 +38,7 @@ export default function ForgotPassword({ csrfToken }) {
return json;
} catch (reason) {
setError({ message: "An unexpected error occurred. Try again." });
setError({ message: t("unexpected_error_try_again") });
} finally {
setLoading(false);
}
@ -65,8 +67,8 @@ export default function ForgotPassword({ csrfToken }) {
const Success = () => {
return (
<div className="space-y-6">
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">Done</h2>
<p>Check your email. We sent you a link to reset your password.</p>
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">{t("done")}</h2>
<p>{t("check_email_reset_password")}</p>
{error && <p className="text-red-600">{error.message}</p>}
</div>
);
@ -74,7 +76,7 @@ export default function ForgotPassword({ csrfToken }) {
return (
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<HeadSeo title="Forgot Password" description="Forgot Password" />
<HeadSeo title={t("forgot_password")} description={t("forgot_password")} />
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="bg-white py-8 px-4 mx-2 shadow rounded-lg sm:px-10 space-y-6">
{success && <Success />}
@ -82,19 +84,16 @@ export default function ForgotPassword({ csrfToken }) {
<>
<div className="space-y-6">
<h2 className="font-cal mt-6 text-center text-3xl font-extrabold text-gray-900">
Forgot Password
{t("forgot_password")}
</h2>
<p>
Enter the email address associated with your account and we will send you a link to reset
your password.
</p>
<p>{t("reset_instructions")}</p>
{error && <p className="text-red-600">{error.message}</p>}
</div>
<form className="space-y-6" onSubmit={handleSubmit} action="#">
<input name="csrfToken" type="hidden" defaultValue={csrfToken} hidden />
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email address
{t("email_address")}
</label>
<div className="mt-1">
<input
@ -137,7 +136,7 @@ export default function ForgotPassword({ csrfToken }) {
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
)}
Request Password Reset
{t("request_password_reset")}
</button>
</div>
<div className="space-y-2">
@ -145,7 +144,7 @@ export default function ForgotPassword({ csrfToken }) {
<button
type="button"
className="w-full flex justify-center py-2 px-4 text-sm font-medium text-black focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black">
Login
{t("login")}
</button>
</Link>
</div>

View File

@ -4,22 +4,14 @@ import { useRouter } from "next/router";
import { useState } from "react";
import { ErrorCode, getSession } from "@lib/auth";
import { useLocale } from "@lib/hooks/useLocale";
import AddToHomescreen from "@components/AddToHomescreen";
import Loader from "@components/Loader";
import { HeadSeo } from "@components/seo/head-seo";
const errorMessages: { [key: string]: string } = {
[ErrorCode.SecondFactorRequired]:
"Two-factor authentication enabled. Please enter the six-digit code from your authenticator app.",
[ErrorCode.IncorrectPassword]: "Password is incorrect. Please try again.",
[ErrorCode.UserNotFound]: "No account exists matching that email address.",
[ErrorCode.IncorrectTwoFactorCode]: "Two-factor code is incorrect. Please try again.",
[ErrorCode.InternalServerError]:
"Something went wrong. Please try again and contact us if the issue persists.",
};
export default function Login({ csrfToken }) {
const { t } = useLocale();
const router = useRouter();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
@ -27,6 +19,13 @@ export default function Login({ csrfToken }) {
const [isSubmitting, setIsSubmitting] = useState(false);
const [secondFactorRequired, setSecondFactorRequired] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const errorMessages: { [key: string]: string } = {
[ErrorCode.SecondFactorRequired]: t("2fa_enabled_instructions"),
[ErrorCode.IncorrectPassword]: `${t("incorrect_password")} ${t("please_try_again")}`,
[ErrorCode.UserNotFound]: t("no_account_exists"),
[ErrorCode.IncorrectTwoFactorCode]: `${t("incorrect_2fa_code")} ${t("please_try_again")}`,
[ErrorCode.InternalServerError]: `${t("something_went_wrong")} ${t("please_try_again_and_contact_us")}`,
};
const callbackUrl = typeof router.query?.callbackUrl === "string" ? router.query.callbackUrl : "/";
@ -62,18 +61,18 @@ export default function Login({ csrfToken }) {
setSecondFactorRequired(true);
setErrorMessage(errorMessages[ErrorCode.SecondFactorRequired]);
} else {
setErrorMessage(errorMessages[response.error] || "Something went wrong.");
setErrorMessage(errorMessages[response.error] || t("something_went_wrong"));
}
setIsSubmitting(false);
} catch (e) {
setErrorMessage("Something went wrong.");
setErrorMessage(t("something_went_wrong"));
setIsSubmitting(false);
}
}
return (
<div className="min-h-screen bg-neutral-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<HeadSeo title="Login" description="Login" />
<HeadSeo title={t("login")} description={t("login")} />
{isSubmitting && (
<div className="z-50 absolute w-full h-screen bg-gray-50 flex items-center">
@ -84,7 +83,7 @@ export default function Login({ csrfToken }) {
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<img className="h-6 mx-auto" src="/calendso-logo-white-word.svg" alt="Cal.com Logo" />
<h2 className="font-cal mt-6 text-center text-3xl font-bold text-neutral-900">
Sign in to your account
{t("sign_in_account")}
</h2>
</div>
@ -94,7 +93,7 @@ export default function Login({ csrfToken }) {
<input name="csrfToken" type="hidden" defaultValue={csrfToken} hidden />
<div>
<label htmlFor="email" className="block text-sm font-medium text-neutral-700">
Email address
{t("email_address")}
</label>
<div className="mt-1">
<input
@ -115,13 +114,13 @@ export default function Login({ csrfToken }) {
<div className="flex">
<div className="w-1/2">
<label htmlFor="password" className="block text-sm font-medium text-neutral-700">
Password
{t("password")}
</label>
</div>
<div className="w-1/2 text-right">
<Link href="/auth/forgot-password">
<a tabIndex={-1} className="font-medium text-primary-600 text-sm">
Forgot?
{t("forgot")}
</a>
</Link>
</div>
@ -143,7 +142,7 @@ export default function Login({ csrfToken }) {
{secondFactorRequired && (
<div>
<label htmlFor="email" className="block text-sm font-medium text-neutral-700">
Two-Factor Code
{t("2fa_code")}
</label>
<div className="mt-1">
<input
@ -166,7 +165,7 @@ export default function Login({ csrfToken }) {
type="submit"
disabled={isSubmitting}
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-sm shadow-sm text-sm font-medium text-white bg-neutral-900 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black">
Sign in
{t("sign_in")}
</button>
</div>
@ -174,9 +173,9 @@ export default function Login({ csrfToken }) {
</form>
</div>
<div className="mt-4 text-neutral-600 text-center text-sm">
Don&apos;t have an account? {/* replace this with your account creation flow */}
{t("dont_have_an_account")} {/* replace this with your account creation flow */}
<a href="https://cal.com/signup" className="font-medium text-neutral-900">
Create an account
{t("create_an_account")}
</a>
</div>
</div>

View File

@ -1,16 +1,20 @@
import { CheckIcon } from "@heroicons/react/outline";
import Link from "next/link";
import { useLocale } from "@lib/hooks/useLocale";
import { HeadSeo } from "@components/seo/head-seo";
export default function Logout() {
const { t } = useLocale();
return (
<div
className="fixed z-50 inset-0 overflow-y-auto"
aria-labelledby="modal-title"
role="dialog"
aria-modal="true">
<HeadSeo title="Logged out" description="Logged out" />
<HeadSeo title={t("logged_out")} description={t("logged_out")} />
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
&#8203;
@ -22,17 +26,17 @@ export default function Logout() {
</div>
<div className="mt-3 text-center sm:mt-5">
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
You&apos;ve been logged out
{t("youve_been_logged_out")}
</h3>
<div className="mt-2">
<p className="text-sm text-gray-500">We hope to see you again soon!</p>
<p className="text-sm text-gray-500">{t("hope_to_see_you_soon")}</p>
</div>
</div>
</div>
<div className="mt-5 sm:mt-6">
<Link href="/auth/login">
<a className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-black text-base font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black sm:text-sm">
Go back to the login page
{t("go_back_login")}
</a>
</Link>
</div>

View File

@ -2,6 +2,7 @@ import { signIn } from "next-auth/client";
import { useRouter } from "next/router";
import { useState } from "react";
import { useLocale } from "@lib/hooks/useLocale";
import prisma from "@lib/prisma";
import { HeadSeo } from "@components/seo/head-seo";
@ -9,6 +10,7 @@ import { UsernameInput } from "@components/ui/UsernameInput";
import ErrorAlert from "@components/ui/alerts/Error";
export default function Signup(props) {
const { t } = useLocale();
const router = useRouter();
const [hasErrors, setHasErrors] = useState(false);
@ -56,9 +58,11 @@ export default function Signup(props) {
aria-labelledby="modal-title"
role="dialog"
aria-modal="true">
<HeadSeo title="Sign up" description="Sign up" />
<HeadSeo title={t("sign_up")} description={t("sign_up")} />
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<h2 className="font-cal text-center text-3xl font-extrabold text-gray-900">Create your account</h2>
<h2 className="font-cal text-center text-3xl font-extrabold text-gray-900">
{t("create_your_account")}
</h2>
</div>
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="bg-white py-8 px-4 shadow mx-2 sm:rounded-lg sm:px-10">
@ -70,7 +74,7 @@ export default function Signup(props) {
</div>
<div className="mb-2">
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email
{t("email")}
</label>
<input
type="email"
@ -86,7 +90,7 @@ export default function Signup(props) {
</div>
<div className="mb-2">
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
Password
{t("password")}
</label>
<input
type="password"
@ -99,7 +103,7 @@ export default function Signup(props) {
</div>
<div>
<label htmlFor="passwordcheck" className="block text-sm font-medium text-gray-700">
Confirm password
{t("confirm_password")}
</label>
<input
type="password"
@ -114,13 +118,13 @@ export default function Signup(props) {
<div className="mt-3 sm:mt-4 flex">
<input
type="submit"
value="Create Account"
value={t("create_account")}
className="btn btn-primary w-7/12 mr-2 inline-flex justify-center rounded-md border border-transparent cursor-pointer shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black sm:text-sm"
/>
<a
onClick={() => signIn("Cal.com", { callbackUrl: (router.query.callbackUrl || "") as string })}
className="w-5/12 inline-flex justify-center text-sm text-gray-500 font-medium border px-4 py-2 rounded btn cursor-pointer">
Login instead
{t("login_instead")}
</a>
</div>
</form>

View File

@ -21,6 +21,7 @@ import React, { useEffect, useRef, useState } from "react";
import TimezoneSelect from "react-timezone-select";
import { getSession } from "@lib/auth";
import { useLocale } from "@lib/hooks/useLocale";
import AddCalDavIntegration, {
ADD_CALDAV_INTEGRATION_FORM_TITLE,
} from "@lib/integrations/CalDav/components/AddCalDavIntegration";
@ -40,25 +41,6 @@ import getEventTypes from "../lib/queries/event-types/get-event-types";
dayjs.extend(utc);
dayjs.extend(timezone);
const DEFAULT_EVENT_TYPES = [
{
title: "15 Min Meeting",
slug: "15min",
length: 15,
},
{
title: "30 Min Meeting",
slug: "30min",
length: 30,
},
{
title: "Secret Meeting",
slug: "secret",
length: 15,
hidden: true,
},
];
type OnboardingProps = {
user: User;
integrations?: Record<string, string>[];
@ -67,8 +49,28 @@ type OnboardingProps = {
};
export default function Onboarding(props: OnboardingProps) {
const { t } = useLocale();
const router = useRouter();
const DEFAULT_EVENT_TYPES = [
{
title: t("15min_meeting"),
slug: "15min",
length: 15,
},
{
title: t("30min_meeting"),
slug: "30min",
length: 30,
},
{
title: t("secret_meeting"),
slug: "secret",
length: 15,
hidden: true,
},
];
const [isSubmitting, setSubmitting] = React.useState(false);
const [enteredName, setEnteredName] = React.useState();
const Sess = useSession();
@ -159,7 +161,7 @@ export default function Onboarding(props: OnboardingProps) {
</div>
<div className="w-2/12 text-right">
<Button className="btn-sm" color="secondary" onClick={() => handleAddIntegration(integration.type)}>
Connect
{t("connect")}
</Button>
</div>
</li>
@ -229,14 +231,11 @@ export default function Onboarding(props: OnboardingProps) {
open={isAddCalDavIntegrationDialogOpen}
onOpenChange={(isOpen) => setIsAddCalDavIntegrationDialogOpen(isOpen)}>
<DialogContent>
<DialogHeader
title="Connect to CalDav Server"
subtitle="Your credentials will be stored and encrypted."
/>
<DialogHeader title={t("connect_caldav")} subtitle={t("credentials_stored_and_encrypted")} />
<div className="my-4">
{addCalDavError && (
<p className="text-red-700 text-sm">
<span className="font-bold">Error: </span>
<span className="font-bold">{t("error")}: </span>
{addCalDavError.message}
</p>
)}
@ -250,14 +249,14 @@ export default function Onboarding(props: OnboardingProps) {
type="submit"
form={ADD_CALDAV_INTEGRATION_FORM_TITLE}
className="flex justify-center py-2 px-4 border border-transparent rounded-sm shadow-sm text-sm font-medium text-white bg-neutral-900 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900">
Save
{t("save")}
</button>
<DialogClose
onClick={() => {
setIsAddCalDavIntegrationDialogOpen(false);
}}
asChild>
<Button color="secondary">Cancel</Button>
<Button color="secondary">{t("cancel")}</Button>
</DialogClose>
</div>
</DialogContent>
@ -367,16 +366,15 @@ export default function Onboarding(props: OnboardingProps) {
const steps = [
{
id: "welcome",
title: "Welcome to Cal.com",
description:
"Tell us what to call you and let us know what timezone youre in. Youll be able to edit this later.",
id: t("welcome"),
title: t("welcome_to_calcom"),
description: t("welcome_instructions"),
Component: (
<form className="sm:mx-auto sm:w-full">
<section className="space-y-8">
<fieldset>
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
Full name
{t("full_name")}
</label>
<input
ref={nameRef}
@ -384,7 +382,7 @@ export default function Onboarding(props: OnboardingProps) {
name="name"
id="name"
autoComplete="given-name"
placeholder="Your name"
placeholder={t("your_name")}
defaultValue={props.user.name ?? enteredName}
required
className="mt-1 block w-full border border-gray-300 rounded-sm shadow-sm py-2 px-3 focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm"
@ -394,10 +392,10 @@ export default function Onboarding(props: OnboardingProps) {
<fieldset>
<section className="flex justify-between">
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
Timezone
{t("timezone")}
</label>
<Text variant="caption">
Current time:&nbsp;
{t("current_time")}:&nbsp;
<span className="text-black">{currentTime}</span>
</Text>
</section>
@ -412,9 +410,9 @@ export default function Onboarding(props: OnboardingProps) {
</form>
),
hideConfirm: false,
confirmText: "Continue",
confirmText: t("continue"),
showCancel: true,
cancelText: "Set up later",
cancelText: t("set_up_later"),
onComplete: async () => {
try {
setSubmitting(true);
@ -432,9 +430,8 @@ export default function Onboarding(props: OnboardingProps) {
},
{
id: "connect-calendar",
title: "Connect your calendar",
description:
"Connect your calendar to automatically check for busy times and new events as theyre scheduled.",
title: t("connect_your_calendar"),
description: t("connect_your_calendar_instructions"),
Component: (
<ul className="divide-y divide-gray-200 sm:mx-auto sm:w-full border border-gray-200 rounded-sm">
{props.integrations.map((integration) => {
@ -443,15 +440,14 @@ export default function Onboarding(props: OnboardingProps) {
</ul>
),
hideConfirm: true,
confirmText: "Continue",
confirmText: t("continue"),
showCancel: true,
cancelText: "Continue without calendar",
cancelText: t("continue_without_calendar"),
},
{
id: "set-availability",
title: "Set your availability",
description:
"Define ranges of time when you are available on a recurring basis. You can create more of these later and assign them to different calendars.",
title: t("set_availability"),
description: t("set_availability_instructions"),
Component: (
<>
<section className="bg-white dark:bg-opacity-5 text-black dark:text-white mx-auto max-w-lg">
@ -472,7 +468,7 @@ export default function Onboarding(props: OnboardingProps) {
</section>
<footer className="py-6 sm:mx-auto sm:w-full flex flex-col space-y-6">
<Button className="justify-center" EndIcon={ArrowRightIcon} type="submit" form={SCHEDULE_FORM_ID}>
Continue
{t("continue")}
</Button>
</footer>
</>
@ -482,15 +478,14 @@ export default function Onboarding(props: OnboardingProps) {
},
{
id: "profile",
title: "Nearly there",
description:
"Last thing, a brief description about you and a photo really help you get bookings and let people know who theyre booking with.",
title: t("nearly_there"),
description: t("nearly_there_instructions"),
Component: (
<form className="sm:mx-auto sm:w-full" id="ONBOARDING_STEP_4">
<section className="space-y-4">
<fieldset>
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
Full name
{t("full_name")}
</label>
<input
ref={nameRef}
@ -498,7 +493,7 @@ export default function Onboarding(props: OnboardingProps) {
name="name"
id="name"
autoComplete="given-name"
placeholder="Your name"
placeholder={t("your_name")}
defaultValue={props.user.name || enteredName}
required
className="mt-1 block w-full border border-gray-300 rounded-sm shadow-sm py-2 px-3 focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm"
@ -506,7 +501,7 @@ export default function Onboarding(props: OnboardingProps) {
</fieldset>
<fieldset>
<label htmlFor="bio" className="block text-sm font-medium text-gray-700">
About
{t("about")}
</label>
<input
ref={bioRef}
@ -518,16 +513,16 @@ export default function Onboarding(props: OnboardingProps) {
defaultValue={props.user.bio}
/>
<Text variant="caption" className="mt-2">
A few sentences about yourself. This will appear on your personal url page.
{t("few_sentences_about_yourself")}
</Text>
</fieldset>
</section>
</form>
),
hideConfirm: false,
confirmText: "Finish",
confirmText: t("finish"),
showCancel: true,
cancelText: "Set up later",
cancelText: t("set_up_later"),
onComplete: async () => {
try {
setSubmitting(true);
@ -557,7 +552,7 @@ export default function Onboarding(props: OnboardingProps) {
return (
<div className="bg-black min-h-screen">
<Head>
<title>Cal.com - Getting Started</title>
<title>Cal.com - {t("getting_started")}</title>
<link rel="icon" href="/favicon.ico" />
</Head>

View File

@ -11,6 +11,7 @@ import { useEffect, useState } from "react";
import { asStringOrNull } from "@lib/asStringOrNull";
import { getEventName } from "@lib/event";
import { useLocale } from "@lib/hooks/useLocale";
import useTheme from "@lib/hooks/useTheme";
import { isBrandingHidden } from "@lib/isBrandingHidden";
import prisma from "@lib/prisma";
@ -24,6 +25,7 @@ dayjs.extend(toArray);
dayjs.extend(timezone);
export default function Success(props: inferSSRProps<typeof getServerSideProps>) {
const { t } = useLocale();
const router = useRouter();
const { location, name, reschedule } = router.query;
@ -71,8 +73,8 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
(isReady && (
<div className="h-screen bg-neutral-50 dark:bg-neutral-900">
<HeadSeo
title={`Booking ${needsConfirmation ? "Submitted" : "Confirmed"}`}
description={`Booking ${needsConfirmation ? "Submitted" : "Confirmed"}`}
title={needsConfirmation ? t("booking_submitted") : t("booking_confirmed")}
description={needsConfirmation ? t("booking_submitted") : t("booking_confirmed")}
/>
<main className="py-24 mx-auto max-w-3xl">
<div className="overflow-y-auto fixed inset-0 z-50">
@ -95,21 +97,21 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
<h3
className="text-2xl font-semibold leading-6 dark:text-white text-neutral-900"
id="modal-headline">
{needsConfirmation ? "Submitted" : "This meeting is scheduled"}
{needsConfirmation ? t("submitted") : t("meeting_is_scheduled")}
</h3>
<div className="mt-3">
<p className="text-sm text-neutral-600 dark:text-gray-300">
{needsConfirmation
? props.profile.name !== null
? `${props.profile.name} still needs to confirm or reject the booking.`
: "Your booking still needs to be confirmed or rejected."
: `We emailed you and the other attendees a calendar invitation with all the details.`}
? t("user_needs_to_confirm_or_reject_booking", { user: props.profile.name })
: t("needs_to_be_confirmed_or_rejected")
: t("emailed_you_and_attendees")}
</p>
</div>
<div className="grid grid-cols-3 py-4 mt-4 text-left text-gray-700 border-t border-b dark:text-gray-300 dark:border-gray-900">
<div className="font-medium">What</div>
<div className="font-medium">{t("what")}</div>
<div className="col-span-2 mb-6">{eventName}</div>
<div className="font-medium">When</div>
<div className="font-medium">{t("when")}</div>
<div className="col-span-2 mb-6">
{date.format("dddd, DD MMMM YYYY")}
<br />
@ -120,7 +122,7 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
</div>
{location && (
<>
<div className="font-medium">Where</div>
<div className="font-medium">{t("where")}</div>
<div className="col-span-2">{location}</div>
</>
)}
@ -130,7 +132,7 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
{!needsConfirmation && (
<div className="flex pt-2 mt-5 text-center sm:mt-0 sm:pt-4">
<span className="flex self-center mr-6 font-medium text-gray-700 dark:text-gray-50">
Add to calendar
{t("add_to_calendar")}
</span>
<div className="flex">
<Link
@ -217,7 +219,7 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1000 1000"
className="inline-block -mt-1 mr-1 w-4 h-4">
<title>Other</title>
<title>{t("other")}</title>
<path d="M971.3,154.9c0-34.7-28.2-62.9-62.9-62.9H611.7c-1.3,0-2.6,0.1-3.9,0.2V10L28.7,87.3v823.4L607.8,990v-84.6c1.3,0.1,2.6,0.2,3.9,0.2h296.7c34.7,0,62.9-28.2,62.9-62.9V154.9z M607.8,636.1h44.6v-50.6h-44.6v-21.9h44.6v-50.6h-44.6v-92h277.9v230.2c0,3.8-3.1,7-7,7H607.8V636.1z M117.9,644.7l-50.6-2.4V397.5l50.6-2.2V644.7z M288.6,607.3c17.6,0.6,37.3-2.8,49.1-7.2l9.1,48c-11,5.1-35.6,9.9-66.9,8.3c-85.4-4.3-127.5-60.7-127.5-132.6c0-86.2,57.8-136.7,133.2-140.1c30.3-1.3,53.7,4,64.3,9.2l-12.2,48.9c-12.1-4.9-28.8-9.2-49.5-8.6c-45.3,1.2-79.5,30.1-79.5,87.4C208.8,572.2,237.8,605.7,288.6,607.3z M455.5,665.2c-32.4-1.6-63.7-11.3-79.1-20.5l12.6-50.7c16.8,9.1,42.9,18.5,70.4,19.4c30.1,1,46.3-10.7,46.3-29.3c0-17.8-14-28.1-48.8-40.6c-46.9-16.4-76.8-41.7-76.8-81.5c0-46.6,39.3-84.1,106.8-87.1c33.3-1.5,58.3,4.2,76.5,11.2l-15.4,53.3c-12.1-5.3-33.5-12.8-62.3-12c-28.3,0.8-41.9,13.6-41.9,28.1c0,17.8,16.1,25.5,53.6,39c52.9,18.5,78.4,45.3,78.4,86.4C575.6,629.7,536.2,669.2,455.5,665.2z M935.3,842.7c0,14.9-12.1,27-27,27H611.7c-1.3,0-2.6-0.2-3.9-0.4V686.2h270.9c19.2,0,34.9-15.6,34.9-34.9V398.4c0-19.2-15.6-34.9-34.9-34.9h-47.1v-32.3H808v32.3h-44.8v-32.3h-22.7v32.3h-43.3v-32.3h-22.7v32.3H628v-32.3h-20.2v-203c1.31.2,2.6-0.4,3.9-0.4h296.7c14.9,0,27,12.1,27,27L935.3,842.7L935.3,842.7z" />
</svg>
</a>
@ -227,7 +229,7 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
)}
{!props.hideBranding && (
<div className="pt-4 mt-4 text-xs text-center text-gray-400 border-t dark:border-gray-900 dark:text-white">
<a href="https://cal.com/signup">Create your own booking link with Cal.com</a>
<a href="https://cal.com/signup">{t("create_booking_link_with_calcom")}</a>
<form
onSubmit={(e) => {
@ -245,7 +247,7 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
placeholder="rick.astley@cal.com"
/>
<Button type="submit" className="min-w-max" color="primary">
Try it for free
{t("try_for_free")}
</Button>
</form>
</div>
@ -263,6 +265,7 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
export async function getServerSideProps(context: GetServerSidePropsContext) {
const typeId = parseInt(asStringOrNull(context.query.type) ?? "");
if (isNaN(typeId)) {
return {
notFound: true,

View File

@ -1,4 +1,91 @@
{
"the_username": "The username",
"is_still_available": "is still available.",
"documentation": "Documentation",
"documentation_description": "Learn how to integrate our tools with your app",
"api_reference": "API Reference",
"api_reference_description": "A complete API reference for our libraries",
"blog": "Blog",
"blog_description": "Read our latest news and articles",
"join_our_community": "Join our community",
"claim_username_and_schedule_events": "Claim your username and schedule events",
"popular_pages": "Popular pages",
"register_now": "Register now",
"register": "Register",
"page_doesnt_exist": "This page does not exist.",
"check_spelling_mistakes_or_go_back": "Check for spelling mistakes or go back to the previous page.",
"404_page_not_found": "404: This page could not be found.",
"getting_started": "Getting Started",
"15min_meeting": "15 Min Meeting",
"30min_meeting": "30 Min Meeting",
"secret_meeting": "Secret Meeting",
"login_instead": "Login instead",
"create_account": "Create Account",
"confirm_password": "Confirm password",
"create_your_account": "Create your account",
"sign_up": "Sign up",
"youve_been_logged_out": "You've been logged out",
"hope_to_see_you_soon": "We hope to see you again soon!",
"logged_out": "Logged out",
"please_try_again_and_contact_us": "Please try again and contact us if the issue persists.",
"incorrect_2fa_code": "Two-factor code is incorrect.",
"no_account_exists": "No account exists matching that email address.",
"2fa_enabled_instructions": "Two-factor authentication enabled. Please enter the six-digit code from your authenticator app.",
"create_an_account": "Create an account",
"dont_have_an_account": "Don't have an account?",
"2fa_code": "Two-Factor Code",
"sign_in_account": "Sign in to your account",
"sign_in": "Sign in",
"go_back_login": "Go back to the login page",
"error_during_login": "An error occurred when logging you in. Head back to the login screen and try again.",
"request_password_reset": "Request Password Reset",
"forgot_password": "Forgot Password",
"forgot": "Forgot?",
"done": "Done",
"check_email_reset_password": "Check your email. We sent you a link to reset your password.",
"finish": "Finish",
"few_sentences_about_yourself": "A few sentences about yourself. This will appear on your personal url page.",
"nearly_there": "Nearly there",
"nearly_there_instructions": "Last thing, a brief description about you and a photo really help you get bookings and let people know who theyre booking with.",
"set_availability_instructions": "Define ranges of time when you are available on a recurring basis. You can create more of these later and assign them to different calendars.",
"set_availability": "Set your availability",
"continue_without_calendar": "Continue without calendar",
"connect_your_calendar": "Connect your calendar",
"connect_your_calendar_instructions": "Connect your calendar to automatically check for busy times and new events as theyre scheduled.",
"set_up_later": "Set up later",
"current_time": "Current time",
"welcome": "Welcome",
"welcome_to_calcom": "Welcome to Cal.com",
"welcome_instructions": "Tell us what to call you and let us know what timezone youre in. Youll be able to edit this later.",
"connect_caldav": "Connect to CalDav Server",
"credentials_stored_and_encrypted": "Your credentials will be stored and encrypted.",
"connect": "Connect",
"try_for_free": "Try it for free",
"create_booking_link_with_calcom": "Create your own booking link with Cal.com",
"what": "What",
"when": "When",
"where": "Where",
"add_to_calendar": "Add to calendar",
"other": "Other",
"emailed_you_and_attendees": "We emailed you and the other attendees a calendar invitation with all the details.",
"needs_to_be_confirmed_or_rejected": "Your booking still needs to be confirmed or rejected.",
"user_needs_to_confirm_or_reject_booking": "{{user}} still needs to confirm or reject the booking.",
"meeting_is_scheduled": "This meeting is scheduled",
"submitted": "Submitted",
"booking_submitted": "Booking Submitted",
"booking_confirmed": "Booking Confirmed",
"enter_new_password": "Enter the new password you'd like for your account.",
"reset_password": "Reset Password",
"change_your_password": "Change your password",
"try_again": "Try Again",
"request_is_expired": "That Request is Expired.",
"reset_instructions": "Enter the email address associated with your account and we will send you a link to reset your password.",
"request_is_expired_instructions": "That request is expired. Go back and enter the email associated with your account and we will send you another link to reset your password.",
"whoops": "Whoops",
"login": "Login",
"success": "Success",
"password_has_been_reset_login": "Your password has been reset. You can now login with your newly created password.",
"unexpected_error_try_again": "An unexpected error occurred. Try again.",
"back_to_bookings": "Back to bookings",
"free_to_pick_another_event_type": "Feel free to pick another event anytime.",
"cancelled": "Cancelled",
@ -10,6 +97,7 @@
"error_with_status_code_occured": "An error with status code {{status}} occurred.",
"booking_already_cancelled": "This booking was already cancelled",
"go_back_home": "Go back home",
"or_go_back_home": "Or go back home",
"no_meeting_found": "No Meeting Found",
"no_meeting_found_description": "This meeting does not exist. Contact the meeting owner for an updated link.",
"no_status_bookings_yet": "No {{status}} bookings, yet",
@ -22,7 +110,6 @@
"on": "on",
"and": "and",
"calendar_shows_busy_between": "Your calendar shows you as busy between",
"calendar_no_busy_slots": "Your don't have busy slots in this date.",
"troubleshoot": "Troubleshoot",
"troubleshoot_description": "Understand why certain times are available and others are blocked.",
"overview_of_day": "Here is an overview of your day on",
@ -91,9 +178,9 @@
"password_updated_successfully": "Password updated successfully",
"password_has_been_changed": "Your password has been successfully changed.",
"error_changing_password": "Error changing password",
"something_went_wrong": "Something went wrong",
"something_went_wrong": "Something went wrong.",
"something_doesnt_look_right": "Something doesn't look right?",
"please_try_again": "Please try again",
"please_try_again": "Please try again.",
"super_secure_new_password": "Your super secure new password",
"new_password": "New Password",
"your_old_password": "Your old password",
@ -101,7 +188,7 @@
"change_password": "Change Password",
"new_password_matches_old_password": "New password matches your old password. Please choose a different password.",
"current_incorrect_password": "Current password is incorrect",
"incorrect_password": "Password is incorrect",
"incorrect_password": "Password is incorrect.",
"1_on_1": "1-on-1",
"24_h": "24h",
"use_setting": "Use setting",
@ -162,7 +249,7 @@
"team_updated_successfully": "Team updated successfully",
"your_team_updated_successfully": "Your team has been updated successfully.",
"about": "About",
"team_description": "A few sentences about your team. This will appear on your team&apos;s URL page.",
"team_description": "A few sentences about your team. This will appear on your team's URL page.",
"members": "Members",
"member": "Member",
"owner": "Owner",