more strings extractions (#963)

This commit is contained in:
Mihai C 2021-10-15 13:53:42 +03:00 committed by GitHub
parent e1f4ba06d8
commit c4e2b6b458
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 201 additions and 112 deletions

View file

@ -1,7 +1,10 @@
import { XIcon } from "@heroicons/react/outline"; import { XIcon } from "@heroicons/react/outline";
import { useState } from "react"; import { useState } from "react";
import { useLocale } from "@lib/hooks/useLocale";
export default function AddToHomescreen() { export default function AddToHomescreen() {
const { t } = useLocale();
const [closeBanner, setCloseBanner] = useState(false); const [closeBanner, setCloseBanner] = useState(false);
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
@ -27,9 +30,7 @@ export default function AddToHomescreen() {
</svg> </svg>
</span> </span>
<p className="ml-3 text-xs font-medium text-white"> <p className="ml-3 text-xs font-medium text-white">
<span className="inline"> <span className="inline">{t("add_to_homescreen")}</span>
Add this app to your home screen for faster access and improved experience.
</span>
</p> </p>
</div> </div>
@ -38,7 +39,7 @@ export default function AddToHomescreen() {
onClick={() => setCloseBanner(true)} onClick={() => setCloseBanner(true)}
type="button" type="button"
className="-mr-1 flex p-2 rounded-md hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white"> className="-mr-1 flex p-2 rounded-md hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white">
<span className="sr-only">Dismiss</span> <span className="sr-only">{t("dismiss")}</span>
<XIcon className="h-6 w-6 text-white" aria-hidden="true" /> <XIcon className="h-6 w-6 text-white" aria-hidden="true" />
</button> </button>
</div> </div>

View file

@ -1,19 +1,22 @@
import React from "react"; import React from "react";
import { useLocale } from "@lib/hooks/useLocale";
import NavTabs from "./NavTabs"; import NavTabs from "./NavTabs";
export default function BookingsShell({ children }: { children: React.ReactNode }) { export default function BookingsShell({ children }: { children: React.ReactNode }) {
const { t } = useLocale();
const tabs = [ const tabs = [
{ {
name: "Upcoming", name: t("upcoming"),
href: "/bookings/upcoming", href: "/bookings/upcoming",
}, },
{ {
name: "Past", name: t("past"),
href: "/bookings/past", href: "/bookings/past",
}, },
{ {
name: "Cancelled", name: t("cancelled"),
href: "/bookings/cancelled", href: "/bookings/cancelled",
}, },
]; ];

View file

@ -3,6 +3,7 @@ import Cropper from "react-easy-crop";
import { Area, getCroppedImg } from "@lib/cropImage"; import { Area, getCroppedImg } from "@lib/cropImage";
import { useFileReader } from "@lib/hooks/useFileReader"; import { useFileReader } from "@lib/hooks/useFileReader";
import { useLocale } from "@lib/hooks/useLocale";
import { DialogClose, DialogTrigger, Dialog, DialogContent } from "@components/Dialog"; import { DialogClose, DialogTrigger, Dialog, DialogContent } from "@components/Dialog";
import Slider from "@components/Slider"; import Slider from "@components/Slider";
@ -24,6 +25,7 @@ function CropContainer({
imageSrc: string; imageSrc: string;
onCropComplete: (croppedAreaPixels: Area) => void; onCropComplete: (croppedAreaPixels: Area) => void;
}) { }) {
const { t } = useLocale();
const [crop, setCrop] = useState({ x: 0, y: 0 }); const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1); const [zoom, setZoom] = useState(1);
@ -49,7 +51,7 @@ function CropContainer({
min={1} min={1}
max={3} max={3}
step={0.1} step={0.1}
label="Slide to zoom, drag to reposition" label={t("slide_zoom_drag_instructions")}
changeHandler={handleZoomSliderChange} changeHandler={handleZoomSliderChange}
/> />
</div> </div>
@ -63,6 +65,7 @@ export default function ImageUploader({
handleAvatarChange, handleAvatarChange,
...props ...props
}: ImageUploaderProps) { }: ImageUploaderProps) {
const { t } = useLocale();
const [imageSrc, setImageSrc] = useState<string | null>(); const [imageSrc, setImageSrc] = useState<string | null>();
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(); const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>();
@ -110,7 +113,7 @@ export default function ImageUploader({
<div className="sm:flex sm:items-start mb-4"> <div className="sm:flex sm:items-start mb-4">
<div className="mt-3 text-center sm:mt-0 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:text-left">
<h3 className="font-cal text-lg leading-6 font-bold text-gray-900" id="modal-title"> <h3 className="font-cal text-lg leading-6 font-bold text-gray-900" id="modal-title">
Upload {target} {t("upload_target", { target })}
</h3> </h3>
</div> </div>
</div> </div>
@ -118,7 +121,11 @@ export default function ImageUploader({
<div className="cropper mt-6 flex flex-col justify-center items-center p-8 bg-gray-50"> <div className="cropper mt-6 flex flex-col justify-center items-center p-8 bg-gray-50">
{!result && ( {!result && (
<div className="flex justify-start items-center bg-gray-500 max-h-20 h-20 w-20 rounded-full"> <div className="flex justify-start items-center bg-gray-500 max-h-20 h-20 w-20 rounded-full">
{!imageSrc && <p className="sm:text-xs text-sm text-white w-full text-center">No {target}</p>} {!imageSrc && (
<p className="sm:text-xs text-sm text-white w-full text-center">
{t("no_target", { target })}
</p>
)}
{imageSrc && <img className="h-20 w-20 rounded-full" src={imageSrc} alt={target} />} {imageSrc && <img className="h-20 w-20 rounded-full" src={imageSrc} alt={target} />}
</div> </div>
)} )}
@ -128,20 +135,20 @@ export default function ImageUploader({
onInput={onInputFile} onInput={onInputFile}
type="file" type="file"
name={id} name={id}
placeholder="Upload image" placeholder={t("upload_image")}
className="mt-4 pointer-events-none opacity-0 absolute" className="mt-4 pointer-events-none opacity-0 absolute"
accept="image/*" accept="image/*"
/> />
Choose a file... {t("choose_a_file")}
</label> </label>
</div> </div>
</div> </div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse gap-x-2"> <div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse gap-x-2">
<DialogClose asChild> <DialogClose asChild>
<Button onClick={() => showCroppedImage(croppedAreaPixels)}>Save</Button> <Button onClick={() => showCroppedImage(croppedAreaPixels)}>{t("save")}</Button>
</DialogClose> </DialogClose>
<DialogClose asChild> <DialogClose asChild>
<Button color="secondary">Cancel</Button> <Button color="secondary">{t("cancel")}</Button>
</DialogClose> </DialogClose>
</div> </div>
</DialogContent> </DialogContent>

View file

@ -232,7 +232,7 @@ export default function Shell(props: {
</Link> </Link>
<div className="flex items-center self-center gap-3"> <div className="flex items-center self-center gap-3">
<button className="p-2 text-gray-400 bg-white rounded-full hover:text-gray-500 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black"> <button className="p-2 text-gray-400 bg-white rounded-full hover:text-gray-500 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black">
<span className="sr-only">View notifications</span> <span className="sr-only">{t("view_notifications")}</span>
<Link href="/settings/profile"> <Link href="/settings/profile">
<a> <a>
<CogIcon className="w-6 h-6" aria-hidden="true" /> <CogIcon className="w-6 h-6" aria-hidden="true" />
@ -294,6 +294,7 @@ export default function Shell(props: {
} }
function UserDropdown({ small }: { small?: boolean }) { function UserDropdown({ small }: { small?: boolean }) {
const { t } = useLocale();
const query = useMeQuery(); const query = useMeQuery();
const user = query.data; const user = query.data;
@ -331,7 +332,7 @@ function UserDropdown({ small }: { small?: boolean }) {
rel="noopener noreferrer" rel="noopener noreferrer"
href={`${process.env.NEXT_PUBLIC_APP_URL}/${user?.username || ""}`} href={`${process.env.NEXT_PUBLIC_APP_URL}/${user?.username || ""}`}
className="flex px-4 py-2 text-sm text-neutral-500"> className="flex px-4 py-2 text-sm text-neutral-500">
View public page <ExternalLinkIcon className="w-3 h-3 mt-1 ml-1 text-neutral-400" /> {t("view_public_page")} <ExternalLinkIcon className="w-3 h-3 mt-1 ml-1 text-neutral-400" />
</a> </a>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" /> <DropdownMenuSeparator className="h-px bg-gray-200" />
@ -363,7 +364,7 @@ function UserDropdown({ small }: { small?: boolean }) {
fill="#9BA6B6"></path> fill="#9BA6B6"></path>
</g> </g>
</svg> </svg>
Join our Slack {t("join_our_slack")}
</a> </a>
</DropdownMenuItem> </DropdownMenuItem>
<HelpMenuItemDynamic /> <HelpMenuItemDynamic />
@ -379,7 +380,7 @@ function UserDropdown({ small }: { small?: boolean }) {
)} )}
aria-hidden="true" aria-hidden="true"
/> />
Sign out {t("sign_out")}
</a> </a>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>

View file

@ -4,6 +4,7 @@ import dayjs from "dayjs";
import { useMutation } from "react-query"; import { useMutation } from "react-query";
import { HttpError } from "@lib/core/http/error"; import { HttpError } from "@lib/core/http/error";
import { useLocale } from "@lib/hooks/useLocale";
import { inferQueryOutput, trpc } from "@lib/trpc"; import { inferQueryOutput, trpc } from "@lib/trpc";
import TableActions, { ActionType } from "@components/ui/TableActions"; import TableActions, { ActionType } from "@components/ui/TableActions";
@ -11,6 +12,7 @@ import TableActions, { ActionType } from "@components/ui/TableActions";
type BookingItem = inferQueryOutput<"viewer.bookings">[number]; type BookingItem = inferQueryOutput<"viewer.bookings">[number];
function BookingListItem(booking: BookingItem) { function BookingListItem(booking: BookingItem) {
const { t } = useLocale();
const utils = trpc.useContext(); const utils = trpc.useContext();
const mutation = useMutation( const mutation = useMutation(
async (confirm: boolean) => { async (confirm: boolean) => {
@ -37,14 +39,14 @@ function BookingListItem(booking: BookingItem) {
const pendingActions: ActionType[] = [ const pendingActions: ActionType[] = [
{ {
id: "reject", id: "reject",
label: "Reject", label: t("reject"),
onClick: () => mutation.mutate(false), onClick: () => mutation.mutate(false),
icon: BanIcon, icon: BanIcon,
disabled: mutation.isLoading, disabled: mutation.isLoading,
}, },
{ {
id: "confirm", id: "confirm",
label: "Confirm", label: t("confirm"),
onClick: () => mutation.mutate(true), onClick: () => mutation.mutate(true),
icon: CheckIcon, icon: CheckIcon,
disabled: mutation.isLoading, disabled: mutation.isLoading,
@ -55,13 +57,13 @@ function BookingListItem(booking: BookingItem) {
const bookedActions: ActionType[] = [ const bookedActions: ActionType[] = [
{ {
id: "cancel", id: "cancel",
label: "Cancel", label: t("cancel"),
href: `/cancel/${booking.uid}`, href: `/cancel/${booking.uid}`,
icon: XIcon, icon: XIcon,
}, },
{ {
id: "reschedule", id: "reschedule",
label: "Reschedule", label: t("reschedule"),
href: `/reschedule/${booking.uid}`, href: `/reschedule/${booking.uid}`,
icon: ClockIcon, icon: ClockIcon,
}, },
@ -78,7 +80,7 @@ function BookingListItem(booking: BookingItem) {
</div> </div>
{!booking.confirmed && !booking.rejected && ( {!booking.confirmed && !booking.rejected && (
<span className="mb-2 inline-flex items-center px-1.5 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800"> <span className="mb-2 inline-flex items-center px-1.5 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800">
Unconfirmed {t("unconfirmed")}
</span> </span>
)} )}
</td> </td>
@ -86,7 +88,7 @@ function BookingListItem(booking: BookingItem) {
<div className="sm:hidden"> <div className="sm:hidden">
{!booking.confirmed && !booking.rejected && ( {!booking.confirmed && !booking.rejected && (
<span className="mb-2 inline-flex items-center px-1.5 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800"> <span className="mb-2 inline-flex items-center px-1.5 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800">
Unconfirmed {t("unconfirmed")}
</span> </span>
)} )}
<div className="text-sm text-gray-900 font-medium"> <div className="text-sm text-gray-900 font-medium">
@ -117,7 +119,9 @@ function BookingListItem(booking: BookingItem) {
<> <>
{!booking.confirmed && !booking.rejected && <TableActions actions={pendingActions} />} {!booking.confirmed && !booking.rejected && <TableActions actions={pendingActions} />}
{booking.confirmed && !booking.rejected && <TableActions actions={bookedActions} />} {booking.confirmed && !booking.rejected && <TableActions actions={bookedActions} />}
{!booking.confirmed && booking.rejected && <div className="text-sm text-gray-500">Rejected</div>} {!booking.confirmed && booking.rejected && (
<div className="text-sm text-gray-500">{t("rejected")}</div>
)}
</> </>
) : null} ) : null}
</td> </td>

View file

@ -151,7 +151,7 @@ const BookingPage = (props: BookingPageProps) => {
if (payload["location"]) { if (payload["location"]) {
if (payload["location"].includes("integration")) { if (payload["location"].includes("integration")) {
params.location = "Web conferencing details to follow."; params.location = t("web_conferencing_details_to_follow");
} else { } else {
params.location = payload["location"]; params.location = payload["location"];
} }
@ -398,7 +398,7 @@ const BookingPage = (props: BookingPageProps) => {
<label <label
htmlFor="guests" htmlFor="guests"
className="block mb-1 text-sm font-medium text-gray-700 dark:text-white"> className="block mb-1 text-sm font-medium text-gray-700 dark:text-white">
Guests {t("guests")}
</label> </label>
<ReactMultiEmail <ReactMultiEmail
className="relative" className="relative"

View file

@ -3,12 +3,7 @@ import React, { FC } from "react";
import { Controller, SubmitHandler, useForm, useWatch } from "react-hook-form"; import { Controller, SubmitHandler, useForm, useWatch } from "react-hook-form";
import Select, { OptionTypeBase } from "react-select"; import Select, { OptionTypeBase } from "react-select";
const inputOptions: OptionTypeBase[] = [ import { useLocale } from "@lib/hooks/useLocale";
{ value: EventTypeCustomInputType.TEXT, label: "Text" },
{ value: EventTypeCustomInputType.TEXTLONG, label: "Multiline Text" },
{ value: EventTypeCustomInputType.NUMBER, label: "Number" },
{ value: EventTypeCustomInputType.BOOL, label: "Checkbox" },
];
interface Props { interface Props {
onSubmit: SubmitHandler<IFormInput>; onSubmit: SubmitHandler<IFormInput>;
@ -19,6 +14,13 @@ interface Props {
type IFormInput = EventTypeCustomInput; type IFormInput = EventTypeCustomInput;
const CustomInputTypeForm: FC<Props> = (props) => { const CustomInputTypeForm: FC<Props> = (props) => {
const { t } = useLocale();
const inputOptions: OptionTypeBase[] = [
{ value: EventTypeCustomInputType.TEXT, label: t("text") },
{ value: EventTypeCustomInputType.TEXTLONG, label: t("multiline_text") },
{ value: EventTypeCustomInputType.NUMBER, label: t("number") },
{ value: EventTypeCustomInputType.BOOL, label: t("checkbox") },
];
const { selectedCustomInput } = props; const { selectedCustomInput } = props;
const defaultValues = selectedCustomInput || { type: inputOptions[0].value }; const defaultValues = selectedCustomInput || { type: inputOptions[0].value };
const { register, control, handleSubmit } = useForm<IFormInput>({ const { register, control, handleSubmit } = useForm<IFormInput>({
@ -35,7 +37,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
<form onSubmit={handleSubmit(props.onSubmit)}> <form onSubmit={handleSubmit(props.onSubmit)}>
<div className="mb-2"> <div className="mb-2">
<label htmlFor="type" className="block text-sm font-medium text-gray-700"> <label htmlFor="type" className="block text-sm font-medium text-gray-700">
Input type {t("input_type")}
</label> </label>
<Controller <Controller
name="type" name="type"
@ -57,7 +59,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
</div> </div>
<div className="mb-2"> <div className="mb-2">
<label htmlFor="label" className="block text-sm font-medium text-gray-700"> <label htmlFor="label" className="block text-sm font-medium text-gray-700">
Label {t("label")}
</label> </label>
<div className="mt-1"> <div className="mt-1">
<input <input
@ -74,7 +76,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
selectedInputType === EventTypeCustomInputType.TEXTLONG) && ( selectedInputType === EventTypeCustomInputType.TEXTLONG) && (
<div className="mb-2"> <div className="mb-2">
<label htmlFor="placeholder" className="block text-sm font-medium text-gray-700"> <label htmlFor="placeholder" className="block text-sm font-medium text-gray-700">
Placeholder {t("placeholder")}
</label> </label>
<div className="mt-1"> <div className="mt-1">
<input <input
@ -96,7 +98,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
{...register("required")} {...register("required")}
/> />
<label htmlFor="required" className="block text-sm font-medium text-gray-700"> <label htmlFor="required" className="block text-sm font-medium text-gray-700">
Is required {t("is_required")}
</label> </label>
</div> </div>
<input <input
@ -113,10 +115,10 @@ const CustomInputTypeForm: FC<Props> = (props) => {
/> />
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> <div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button type="submit" className="btn btn-primary"> <button type="submit" className="btn btn-primary">
Save {t("save")}
</button> </button>
<button onClick={onCancel} type="button" className="mr-2 btn btn-white"> <button onClick={onCancel} type="button" className="mr-2 btn btn-white">
Cancel {t("cancel")}
</button> </button>
</div> </div>
</form> </form>

View file

@ -79,7 +79,7 @@ const EventTypeList = ({ readOnly, types, profile }: Props): JSX.Element => {
}))} }))}
/> />
)} )}
<Tooltip content="Preview"> <Tooltip content={t("preview")}>
<a <a
href={`${process.env.NEXT_PUBLIC_APP_URL}/${profile.slug}/${type.slug}`} href={`${process.env.NEXT_PUBLIC_APP_URL}/${profile.slug}/${type.slug}`}
target="_blank" target="_blank"
@ -89,10 +89,10 @@ const EventTypeList = ({ readOnly, types, profile }: Props): JSX.Element => {
</a> </a>
</Tooltip> </Tooltip>
<Tooltip content="Copy link"> <Tooltip content={t("copy_link")}>
<button <button
onClick={() => { onClick={() => {
showToast("Link copied!", "success"); showToast(t("link_copied"), "success");
navigator.clipboard.writeText( navigator.clipboard.writeText(
`${process.env.NEXT_PUBLIC_APP_URL}/${profile.slug}/${type.slug}` `${process.env.NEXT_PUBLIC_APP_URL}/${profile.slug}/${type.slug}`
); );

View file

@ -27,13 +27,6 @@ enum SetupStep {
EnterTotpCode, EnterTotpCode,
} }
const setupDescriptions = {
[SetupStep.ConfirmPassword]: "Confirm your current password to get started.",
[SetupStep.DisplayQrCode]:
"Scan the image below with the authenticator app on your phone or manually enter the text code instead.",
[SetupStep.EnterTotpCode]: "Enter the six-digit code from your authenticator app below.",
};
const WithStep = ({ const WithStep = ({
step, step,
current, current,
@ -47,6 +40,12 @@ const WithStep = ({
}; };
const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps) => { const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps) => {
const { t } = useLocale();
const setupDescriptions = {
[SetupStep.ConfirmPassword]: t("2fa_confirm_current_password"),
[SetupStep.DisplayQrCode]: t("2fa_scan_image_or_use_code"),
[SetupStep.EnterTotpCode]: t("2fa_enter_six_digit_code"),
};
const [step, setStep] = useState(SetupStep.ConfirmPassword); const [step, setStep] = useState(SetupStep.ConfirmPassword);
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [totpCode, setTotpCode] = useState(""); const [totpCode, setTotpCode] = useState("");
@ -54,7 +53,6 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
const [secret, setSecret] = useState(""); const [secret, setSecret] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null); const [errorMessage, setErrorMessage] = useState<string | null>(null);
const { t } = useLocale();
async function handleSetup(e: SyntheticEvent) { async function handleSetup(e: SyntheticEvent) {
e.preventDefault(); e.preventDefault();

View file

@ -100,7 +100,7 @@ export default function TeamListItem(props: {
<span className="self-center h-6 px-3 py-1 text-xs text-gray-700 capitalize rounded-md bg-gray-50"> <span className="self-center h-6 px-3 py-1 text-xs text-gray-700 capitalize rounded-md bg-gray-50">
{t("owner")} {t("owner")}
</span> </span>
<Tooltip content="Copy link"> <Tooltip content={t("copy_link")}>
<Button <Button
onClick={() => { onClick={() => {
navigator.clipboard.writeText( navigator.clipboard.writeText(
@ -155,7 +155,7 @@ export default function TeamListItem(props: {
</DialogTrigger> </DialogTrigger>
<ConfirmationDialogContent <ConfirmationDialogContent
variety="danger" variety="danger"
title="Disband Team" title={t("disband_team")}
confirmBtnText={t("confirm_disband_team")} confirmBtnText={t("confirm_disband_team")}
onConfirm={() => props.onActionSelect("disband")}> onConfirm={() => props.onActionSelect("disband")}>
{t("disband_team_confirmation_message")} {t("disband_team_confirmation_message")}

View file

@ -1,23 +1,28 @@
import Link from "next/link"; import Link from "next/link";
const PoweredByCal = () => ( import { useLocale } from "@lib/hooks/useLocale";
<div className="text-xs text-center sm:text-right p-1">
<Link href={`https://cal.com?utm_source=embed&utm_medium=powered-by-button`}> const PoweredByCal = () => {
<a target="_blank" className="dark:text-white text-gray-500 opacity-50 hover:opacity-100"> const { t } = useLocale();
powered by{" "} return (
<img <div className="text-xs text-center sm:text-right p-1">
className="dark:hidden w-auto inline h-[10px] relative -mt-px" <Link href={`https://cal.com?utm_source=embed&utm_medium=powered-by-button`}>
src="https://cal.com/logo.svg" <a target="_blank" className="dark:text-white text-gray-500 opacity-50 hover:opacity-100">
alt="Cal.com Logo" {t("powered_by")}{" "}
/> <img
<img className="dark:hidden w-auto inline h-[10px] relative -mt-px"
className="hidden dark:inline w-auto h-[10px] relativ -mt-px" src="https://cal.com/logo.svg"
src="https://cal.com/logo-white.svg" alt="Cal.com Logo"
alt="Cal.com Logo" />
/> <img
</a> className="hidden dark:inline w-auto h-[10px] relativ -mt-px"
</Link> src="https://cal.com/logo-white.svg"
</div> alt="Cal.com Logo"
); />
</a>
</Link>
</div>
);
};
export default PoweredByCal; export default PoweredByCal;

View file

@ -3,6 +3,8 @@ import classnames from "classnames";
import dayjs, { Dayjs } from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import React from "react"; import React from "react";
import { useLocale } from "@lib/hooks/useLocale";
import Text from "@components/ui/Text"; import Text from "@components/ui/Text";
export const SCHEDULE_FORM_ID = "SCHEDULE_FORM_ID"; export const SCHEDULE_FORM_ID = "SCHEDULE_FORM_ID";
@ -79,6 +81,7 @@ type Props = {
}; };
const SchedulerForm = ({ schedule = DEFAULT_SCHEDULE, onSubmit }: Props) => { const SchedulerForm = ({ schedule = DEFAULT_SCHEDULE, onSubmit }: Props) => {
const { t } = useLocale();
const ref = React.useRef<HTMLFormElement>(null); const ref = React.useRef<HTMLFormElement>(null);
const transformElementsToSchedule = (elements: HTMLFormControlsCollection): Schedule => { const transformElementsToSchedule = (elements: HTMLFormControlsCollection): Schedule => {
@ -299,7 +302,7 @@ const SchedulerForm = ({ schedule = DEFAULT_SCHEDULE, onSubmit }: Props) => {
)) ))
) : ( ) : (
<Text key={`${day}`} variant="caption"> <Text key={`${day}`} variant="caption">
Unavailable {t("unavailable")}
</Text> </Text>
)} )}
</div> </div>

View file

@ -6,6 +6,8 @@ import utc from "dayjs/plugin/utc";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import TimezoneSelect from "react-timezone-select"; import TimezoneSelect from "react-timezone-select";
import { useLocale } from "@lib/hooks/useLocale";
import { WeekdaySelect } from "./WeekdaySelect"; import { WeekdaySelect } from "./WeekdaySelect";
import SetTimesModal from "./modal/SetTimesModal"; import SetTimesModal from "./modal/SetTimesModal";
@ -24,6 +26,7 @@ export const Scheduler = ({
timeZone: selectedTimeZone, timeZone: selectedTimeZone,
setTimeZone, setTimeZone,
}: Props) => { }: Props) => {
const { t } = useLocale();
const [editSchedule, setEditSchedule] = useState(-1); const [editSchedule, setEditSchedule] = useState(-1);
const [dateOverrides, setDateOverrides] = useState([]); const [dateOverrides, setDateOverrides] = useState([]);
const [openingHours, setOpeningHours] = useState([]); const [openingHours, setOpeningHours] = useState([]);
@ -81,7 +84,7 @@ export const Scheduler = ({
.startOf("day") .startOf("day")
.add(item.startTime, "minutes") .add(item.startTime, "minutes")
.format(item.startTime % 60 === 0 ? "ha" : "h:mma")} .format(item.startTime % 60 === 0 ? "ha" : "h:mma")}
&nbsp;until&nbsp; &nbsp;{t("until")}&nbsp;
{dayjs() {dayjs()
.startOf("day") .startOf("day")
.add(item.endTime, "minutes") .add(item.endTime, "minutes")
@ -103,7 +106,7 @@ export const Scheduler = ({
<div className="w-full"> <div className="w-full">
<div> <div>
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700"> <label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
Timezone {t("timezone")}
</label> </label>
<div className="mt-1"> <div className="mt-1">
<TimezoneSelect <TimezoneSelect
@ -120,7 +123,7 @@ export const Scheduler = ({
))} ))}
</ul> </ul>
<button type="button" onClick={addNewSchedule} className="btn-white btn-sm mt-2"> <button type="button" onClick={addNewSchedule} className="btn-white btn-sm mt-2">
Add another {t("add_another")}
</button> </button>
</div> </div>
</div> </div>

View file

@ -3,6 +3,7 @@ import { DotsHorizontalIcon } from "@heroicons/react/solid";
import React, { FC, Fragment } from "react"; import React, { FC, Fragment } from "react";
import classNames from "@lib/classNames"; import classNames from "@lib/classNames";
import { useLocale } from "@lib/hooks/useLocale";
import { SVGComponent } from "@lib/types/SVGComponent"; import { SVGComponent } from "@lib/types/SVGComponent";
import Button from "./Button"; import Button from "./Button";
@ -20,6 +21,7 @@ interface Props {
} }
const TableActions: FC<Props> = ({ actions }) => { const TableActions: FC<Props> = ({ actions }) => {
const { t } = useLocale();
return ( return (
<> <>
<div className="space-x-2 hidden lg:block"> <div className="space-x-2 hidden lg:block">
@ -41,7 +43,7 @@ const TableActions: FC<Props> = ({ actions }) => {
<> <>
<div> <div>
<Menu.Button className="text-neutral-400 mt-1 p-2 border border-transparent hover:border-gray-200"> <Menu.Button className="text-neutral-400 mt-1 p-2 border border-transparent hover:border-gray-200">
<span className="sr-only">Open options</span> <span className="sr-only">{t("open_options")}</span>
<DotsHorizontalIcon className="h-5 w-5" aria-hidden="true" /> <DotsHorizontalIcon className="h-5 w-5" aria-hidden="true" />
</Menu.Button> </Menu.Button>
</div> </div>

View file

@ -1,6 +1,8 @@
import { CheckIcon, XIcon } from "@heroicons/react/outline"; import { CheckIcon, XIcon } from "@heroicons/react/outline";
import React, { ForwardedRef, useEffect, useState } from "react"; import React, { ForwardedRef, useEffect, useState } from "react";
import { useLocale } from "@lib/hooks/useLocale";
import Avatar from "@components/ui/Avatar"; import Avatar from "@components/ui/Avatar";
import Select from "@components/ui/form/Select"; import Select from "@components/ui/form/Select";
@ -21,6 +23,7 @@ export type CheckedSelectProps = {
export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: ForwardedRef<unknown>) => { export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: ForwardedRef<unknown>) => {
const [selectedOptions, setSelectedOptions] = useState<CheckedSelectValue>(props.defaultValue || []); const [selectedOptions, setSelectedOptions] = useState<CheckedSelectValue>(props.defaultValue || []);
const { t } = useLocale();
useEffect(() => { useEffect(() => {
props.onChange(selectedOptions); props.onChange(selectedOptions);
@ -66,12 +69,12 @@ export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: F
}), }),
}} }}
name={props.name} name={props.name}
placeholder={props.placeholder || "Select..."} placeholder={props.placeholder || t("select")}
isSearchable={false} isSearchable={false}
formatOptionLabel={formatOptionLabel} formatOptionLabel={formatOptionLabel}
options={options} options={options}
isMulti isMulti
value={props.placeholder || "Select..."} value={props.placeholder || t("select")}
onChange={changeHandler} onChange={changeHandler}
/> />
{selectedOptions.map((option) => ( {selectedOptions.map((option) => (

View file

@ -3,6 +3,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/r
import React from "react"; import React from "react";
import classNames from "@lib/classNames"; import classNames from "@lib/classNames";
import { useLocale } from "@lib/hooks/useLocale";
import { RadioArea, RadioAreaGroup } from "@components/ui/form/radio-area/RadioAreaGroup"; import { RadioArea, RadioAreaGroup } from "@components/ui/form/radio-area/RadioAreaGroup";
@ -15,10 +16,11 @@ type RadioAreaSelectProps = React.SelectHTMLAttributes<HTMLSelectElement> & {
}; };
export const Select = function RadioAreaSelect(props: RadioAreaSelectProps) { export const Select = function RadioAreaSelect(props: RadioAreaSelectProps) {
const { t } = useLocale();
const { const {
options, options,
disabled = !options.length, // if not explicitly disabled and the options length is empty, disable anyway disabled = !options.length, // if not explicitly disabled and the options length is empty, disable anyway
placeholder = "Select...", placeholder = t("select"),
} = props; } = props;
const getLabel = (value: string | ReadonlyArray<string> | number) => const getLabel = (value: string | ReadonlyArray<string> | number) =>

View file

@ -1,7 +1,10 @@
import { ClockIcon } from "@heroicons/react/outline"; import { ClockIcon } from "@heroicons/react/outline";
import { useRef } from "react"; import { useRef } from "react";
import { useLocale } from "@lib/hooks/useLocale";
export default function SetTimesModal(props) { export default function SetTimesModal(props) {
const { t } = useLocale();
const [startHours, startMinutes] = [Math.floor(props.startTime / 60), props.startTime % 60]; const [startHours, startMinutes] = [Math.floor(props.startTime / 60), props.startTime % 60];
const [endHours, endMinutes] = [Math.floor(props.endTime / 60), props.endTime % 60]; const [endHours, endMinutes] = [Math.floor(props.endTime / 60), props.endTime % 60];
@ -48,18 +51,18 @@ export default function SetTimesModal(props) {
</div> </div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title"> <h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
Change when you are available for bookings {t("change_bookings_availability")}
</h3> </h3>
<div> <div>
<p className="text-sm text-gray-500">Set your work schedule</p> <p className="text-sm text-gray-500">{t("set_work_schedule")}</p>
</div> </div>
</div> </div>
</div> </div>
<div className="flex mb-4"> <div className="flex mb-4">
<label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">Start time</label> <label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">{t("start_time")}</label>
<div> <div>
<label htmlFor="startHours" className="sr-only"> <label htmlFor="startHours" className="sr-only">
Hours {t("hours")}
</label> </label>
<input <input
ref={startHoursRef} ref={startHoursRef}
@ -77,7 +80,7 @@ export default function SetTimesModal(props) {
<span className="mx-2 pt-1">:</span> <span className="mx-2 pt-1">:</span>
<div> <div>
<label htmlFor="startMinutes" className="sr-only"> <label htmlFor="startMinutes" className="sr-only">
Minutes {t("minutes")}
</label> </label>
<input <input
ref={startMinsRef} ref={startMinsRef}
@ -95,10 +98,10 @@ export default function SetTimesModal(props) {
</div> </div>
</div> </div>
<div className="flex"> <div className="flex">
<label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">End time</label> <label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">{t("end_time")}</label>
<div> <div>
<label htmlFor="endHours" className="sr-only"> <label htmlFor="endHours" className="sr-only">
Hours {t("hours")}
</label> </label>
<input <input
ref={endHoursRef} ref={endHoursRef}
@ -116,7 +119,7 @@ export default function SetTimesModal(props) {
<span className="mx-2 pt-1">:</span> <span className="mx-2 pt-1">:</span>
<div> <div>
<label htmlFor="endMinutes" className="sr-only"> <label htmlFor="endMinutes" className="sr-only">
Minutes {t("minutes")}
</label> </label>
<input <input
ref={endMinsRef} ref={endMinsRef}
@ -135,10 +138,10 @@ export default function SetTimesModal(props) {
</div> </div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> <div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button onClick={updateStartEndTimesHandler} type="submit" className="btn btn-primary"> <button onClick={updateStartEndTimesHandler} type="submit" className="btn btn-primary">
Save {t("save")}
</button> </button>
<button onClick={props.onExit} type="button" className="btn btn-white mr-2"> <button onClick={props.onExit} type="button" className="btn btn-white mr-2">
Cancel {t("cancel")}
</button> </button>
</div> </div>
</div> </div>

View file

@ -1,6 +1,7 @@
import { ArrowLeftIcon } from "@heroicons/react/solid"; import { ArrowLeftIcon } from "@heroicons/react/solid";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useLocale } from "@lib/hooks/useLocale";
import showToast from "@lib/notification"; import showToast from "@lib/notification";
import { Webhook } from "@lib/webhook"; import { Webhook } from "@lib/webhook";
@ -8,6 +9,7 @@ import Button from "@components/ui/Button";
import Switch from "@components/ui/Switch"; import Switch from "@components/ui/Switch";
export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => void }) { export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => void }) {
const { t } = useLocale();
const [bookingCreated, setBookingCreated] = useState( const [bookingCreated, setBookingCreated] = useState(
props.webhook.eventTriggers.includes("booking_created") props.webhook.eventTriggers.includes("booking_created")
); );
@ -58,7 +60,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
}) })
.then(handleErrors) .then(handleErrors)
.then(() => { .then(() => {
showToast("Webhook updated successfully!", "success"); showToast(t("webhook_updated_successfully"), "success");
setBtnLoading(false); setBtnLoading(false);
}); });
}; };
@ -74,12 +76,12 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
loading={btnLoading} loading={btnLoading}
StartIcon={ArrowLeftIcon} StartIcon={ArrowLeftIcon}
onClick={() => props.onCloseEdit()}> onClick={() => props.onCloseEdit()}>
Back {t("back")}
</Button> </Button>
</div> </div>
<div> <div>
<div className="pb-5 pr-4 sm:pb-6"> <div className="pb-5 pr-4 sm:pb-6">
<h3 className="text-lg font-bold leading-6 text-gray-900">Manage your webhook</h3> <h3 className="text-lg font-bold leading-6 text-gray-900">{t("manage_your_webhook")}</h3>
</div> </div>
</div> </div>
<hr className="mt-2" /> <hr className="mt-2" />
@ -87,7 +89,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
<div className="my-4"> <div className="my-4">
<div className="mb-4"> <div className="mb-4">
<label htmlFor="subUrl" className="block text-sm font-medium text-gray-700"> <label htmlFor="subUrl" className="block text-sm font-medium text-gray-700">
Subscriber Url {t("subscriber_url")}
</label> </label>
<input <input
ref={subUrlRef} ref={subUrlRef}
@ -99,11 +101,14 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
required required
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm" className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm"
/> />
<legend className="block pt-4 mb-2 text-sm font-medium text-gray-700"> Event Triggers </legend> <legend className="block pt-4 mb-2 text-sm font-medium text-gray-700">
{" "}
{t("event_triggers")}{" "}
</legend>
<div className="p-2 bg-white border border-gray-300 rounded-sm"> <div className="p-2 bg-white border border-gray-300 rounded-sm">
<div className="flex p-2"> <div className="flex p-2">
<div className="w-10/12"> <div className="w-10/12">
<h2 className="text-sm text-gray-800">Booking Created</h2> <h2 className="text-sm text-gray-800">{t("booking_created")}</h2>
</div> </div>
<div className="flex items-center justify-end w-2/12 text-right"> <div className="flex items-center justify-end w-2/12 text-right">
<Switch <Switch
@ -116,7 +121,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
</div> </div>
<div className="flex px-2 py-1"> <div className="flex px-2 py-1">
<div className="w-10/12"> <div className="w-10/12">
<h2 className="text-sm text-gray-800">Booking Rescheduled</h2> <h2 className="text-sm text-gray-800">{t("booking_rescheduled")}</h2>
</div> </div>
<div className="flex items-center justify-end w-2/12 text-right"> <div className="flex items-center justify-end w-2/12 text-right">
<Switch <Switch
@ -129,7 +134,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
</div> </div>
<div className="flex p-2"> <div className="flex p-2">
<div className="w-10/12"> <div className="w-10/12">
<h2 className="text-sm text-gray-800">Booking Cancelled</h2> <h2 className="text-sm text-gray-800">{t("booking_cancelled")}</h2>
</div> </div>
<div className="flex items-center justify-end w-2/12 text-right"> <div className="flex items-center justify-end w-2/12 text-right">
<Switch <Switch
@ -141,11 +146,14 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
</div> </div>
</div> </div>
</div> </div>
<legend className="block pt-4 mb-2 text-sm font-medium text-gray-700"> Webhook Status </legend> <legend className="block pt-4 mb-2 text-sm font-medium text-gray-700">
{" "}
{t("webhook_status")}{" "}
</legend>
<div className="p-2 bg-white border border-gray-300 rounded-sm"> <div className="p-2 bg-white border border-gray-300 rounded-sm">
<div className="flex p-2"> <div className="flex p-2">
<div className="w-10/12"> <div className="w-10/12">
<h2 className="text-sm text-gray-800">Webhook Enabled</h2> <h2 className="text-sm text-gray-800">{t("webhook_enabled")}</h2>
</div> </div>
<div className="flex items-center justify-end w-2/12 text-right"> <div className="flex items-center justify-end w-2/12 text-right">
<Switch <Switch
@ -160,7 +168,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
</div> </div>
<div className="gap-2 mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> <div className="gap-2 mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button type="submit" color="primary" className="ml-2" loading={btnLoading}> <Button type="submit" color="primary" className="ml-2" loading={btnLoading}>
Save {t("save")}
</Button> </Button>
</div> </div>
</div> </div>

View file

@ -1,5 +1,6 @@
import { TrashIcon, PencilAltIcon } from "@heroicons/react/outline"; import { TrashIcon, PencilAltIcon } from "@heroicons/react/outline";
import { useLocale } from "@lib/hooks/useLocale";
import showToast from "@lib/notification"; import showToast from "@lib/notification";
import { Webhook } from "@lib/webhook"; import { Webhook } from "@lib/webhook";
@ -14,6 +15,7 @@ export default function WebhookListItem(props: {
webhook: Webhook; webhook: Webhook;
onEditWebhook: () => void; onEditWebhook: () => void;
}) { }) {
const { t } = useLocale();
const handleErrors = async (resp: Response) => { const handleErrors = async (resp: Response) => {
if (!resp.ok) { if (!resp.ok) {
const err = await resp.json(); const err = await resp.json();
@ -31,7 +33,7 @@ export default function WebhookListItem(props: {
}) })
.then(handleErrors) .then(handleErrors)
.then(() => { .then(() => {
showToast("Webhook removed successfully!", "success"); showToast(t("webhook_removed_successfully"), "success");
props.onChange(); props.onChange();
}); });
}; };
@ -43,7 +45,7 @@ export default function WebhookListItem(props: {
<span className="flex flex-col space-y-2 text-xs"> <span className="flex flex-col space-y-2 text-xs">
{props.webhook.eventTriggers.map((eventTrigger, ind) => ( {props.webhook.eventTriggers.map((eventTrigger, ind) => (
<span key={ind} className="px-1 text-xs text-blue-700 rounded-md w-max bg-blue-50"> <span key={ind} className="px-1 text-xs text-blue-700 rounded-md w-max bg-blue-50">
{eventTrigger} {t(`${eventTrigger}`)}
</span> </span>
))} ))}
</span> </span>
@ -56,16 +58,16 @@ export default function WebhookListItem(props: {
<div className="flex"> <div className="flex">
{!props.webhook.active && ( {!props.webhook.active && (
<span className="self-center h-6 px-3 py-1 text-xs text-red-700 capitalize rounded-md bg-red-50"> <span className="self-center h-6 px-3 py-1 text-xs text-red-700 capitalize rounded-md bg-red-50">
Disabled {t("disabled")}
</span> </span>
)} )}
{!!props.webhook.active && ( {!!props.webhook.active && (
<span className="self-center h-6 px-3 py-1 text-xs text-green-700 capitalize rounded-md bg-green-50"> <span className="self-center h-6 px-3 py-1 text-xs text-green-700 capitalize rounded-md bg-green-50">
Enabled {t("enabled")}
</span> </span>
)} )}
<Tooltip content="Edit Webhook"> <Tooltip content={t("edit_webhook")}>
<Button <Button
onClick={() => props.onEditWebhook()} onClick={() => props.onEditWebhook()}
color="minimal" color="minimal"
@ -74,7 +76,7 @@ export default function WebhookListItem(props: {
className="self-center w-full p-2 ml-4"></Button> className="self-center w-full p-2 ml-4"></Button>
</Tooltip> </Tooltip>
<Dialog> <Dialog>
<Tooltip content="Delete Webhook"> <Tooltip content={t("delete_webhook")}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
onClick={(e) => { onClick={(e) => {
@ -88,14 +90,13 @@ export default function WebhookListItem(props: {
</Tooltip> </Tooltip>
<ConfirmationDialogContent <ConfirmationDialogContent
variety="danger" variety="danger"
title="Delete Webhook" title={t("delete_webhook")}
confirmBtnText="Yes, delete webhook" confirmBtnText={t("confirm_delete_webhook")}
cancelBtnText="Cancel" cancelBtnText={t("cancel")}
onConfirm={() => { onConfirm={() => {
deleteWebhook(props.webhook.id); deleteWebhook(props.webhook.id);
}}> }}>
Are you sure you want to delete this webhook? You will no longer receive Cal.com meeting data at {t("delete_webhook_confirmation_message")}
a specified URL, in real-time, when an event is scheduled or canceled .
</ConfirmationDialogContent> </ConfirmationDialogContent>
</Dialog> </Dialog>
</div> </div>

View file

@ -1,5 +1,46 @@
{ {
"delete_webhook_confirmation_message": "Are you sure you want to delete this webhook? You will no longer receive Cal.com meeting data at a specified URL, in real-time, when an event is scheduled or canceled.",
"confirm_delete_webhook": "Yes, delete webhook",
"edit_webhook": "Edit Webhook",
"delete_webhook": "Delete Webhook",
"webhook_status": "Webhook Status",
"webhook_enabled": "Webhook Enabled",
"manage_your_webhook": "Manage your webhook",
"webhook_updated_successfully": "Webhook updated successfully!",
"webhook_removed_successfully": "Webhook removed successfully!",
"dismiss": "Dismiss",
"add_to_homescreen": "Add this app to your home screen for faster access and improved experience.",
"upcoming": "Upcoming",
"past": "Past",
"choose_a_file": "Choose a file...",
"upload_image": "Upload image",
"upload_target": "Upload {{target}}",
"no_target": "No {{target}}",
"slide_zoom_drag_instructions": "Slide to zoom, drag to reposition",
"view_notifications": "View notifications",
"view_public_page": "View public page",
"sign_out": "Sign out",
"add_another": "Add another",
"until": "until",
"powered_by": "powered by",
"unavailable": "Unavailable",
"set_work_schedule": "Set your work schedule",
"change_bookings_availability": "Change when you are available for bookings",
"select": "Select...",
"2fa_confirm_current_password": "Confirm your current password to get started.",
"2fa_scan_image_or_use_code": "Scan the image below with the authenticator app on your phone or manually enter the text code instead.",
"text": "Text",
"multiline_text": "Multiline Text",
"number": "Number",
"checkbox": "Checkbox",
"is_required": "Is required",
"input_type": "Input type",
"rejected": "Rejected",
"unconfirmed": "Unconfirmed",
"guests": "Guests",
"web_conferencing_details_to_follow": "Web conferencing details to follow.",
"the_username": "The username", "the_username": "The username",
"username": "Username",
"is_still_available": "is still available.", "is_still_available": "is still available.",
"documentation": "Documentation", "documentation": "Documentation",
"documentation_description": "Learn how to integrate our tools with your app", "documentation_description": "Learn how to integrate our tools with your app",
@ -8,6 +49,7 @@
"blog": "Blog", "blog": "Blog",
"blog_description": "Read our latest news and articles", "blog_description": "Read our latest news and articles",
"join_our_community": "Join our community", "join_our_community": "Join our community",
"join_our_slack": "Join our Slack",
"claim_username_and_schedule_events": "Claim your username and schedule events", "claim_username_and_schedule_events": "Claim your username and schedule events",
"popular_pages": "Popular pages", "popular_pages": "Popular pages",
"register_now": "Register now", "register_now": "Register now",
@ -31,6 +73,7 @@
"incorrect_2fa_code": "Two-factor code is incorrect.", "incorrect_2fa_code": "Two-factor code is incorrect.",
"no_account_exists": "No account exists matching that email address.", "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.", "2fa_enabled_instructions": "Two-factor authentication enabled. Please enter the six-digit code from your authenticator app.",
"2fa_enter_six_digit_code": "Enter the six-digit code from your authenticator app below.",
"create_an_account": "Create an account", "create_an_account": "Create an account",
"dont_have_an_account": "Don't have an account?", "dont_have_an_account": "Don't have an account?",
"2fa_code": "Two-Factor Code", "2fa_code": "Two-Factor Code",
@ -139,7 +182,7 @@
"booking_cancelled": "Booking Cancelled", "booking_cancelled": "Booking Cancelled",
"booking_rescheduled": "Booking Rescheduled", "booking_rescheduled": "Booking Rescheduled",
"booking_created": "Booking Created", "booking_created": "Booking Created",
"event_triggers": "Event triggers", "event_triggers": "Event Triggers",
"subscriber_url": "Subscriber Url", "subscriber_url": "Subscriber Url",
"create_new_webhook": "Create a new webhook", "create_new_webhook": "Create a new webhook",
"create_new_webhook_to_account": "Create a new webhook to your account", "create_new_webhook_to_account": "Create a new webhook to your account",