From fc13a22162e282fa178774d4f450842d95c72155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20L=C3=B3pez?= Date: Mon, 16 May 2022 16:06:31 -0600 Subject: [PATCH 1/8] Merge pull request #2772 from calcom/main v1.6 --- .env.appStore.example | 5 + .github/workflows/crowdin.yml | 2 +- apps/web/components/App.tsx | 7 +- apps/web/components/CustomBranding.tsx | 12 +- apps/web/components/ImageUploader.tsx | 5 +- apps/web/components/Loader.tsx | 8 +- apps/web/components/Logo.tsx | 2 + apps/web/components/Shell.tsx | 16 +- apps/web/components/apps/AppCard.tsx | 10 +- apps/web/components/availability/Schedule.tsx | 2 +- .../availability/ScheduleListItem.tsx | 75 ++ .../availability/SkeletonLoader.tsx | 13 + .../web/components/booking/SkeletonLoader.tsx | 7 +- .../booking/pages/AvailabilityPage.tsx | 46 +- .../components/booking/pages/BookingPage.tsx | 29 +- .../dialog/ConfirmationDialogContent.tsx | 15 +- .../dialog/DeleteStripeDialogContent.tsx | 78 ++ .../components/eventtype/CreateEventType.tsx | 5 + .../eventtype/RecurringEventController.tsx | 191 ++--- .../DisconnectStripeIntegration.tsx | 69 ++ .../integrations/IntegrationListItem.tsx | 5 +- .../security/EnableTwoFactorModal.tsx | 5 +- apps/web/components/team/TeamList.tsx | 6 +- apps/web/components/team/TeamListItem.tsx | 16 +- .../team/TeamSettingsRightSidebar.tsx | 1 + apps/web/components/team/screens/Team.tsx | 1 + apps/web/components/ui/AuthContainer.tsx | 4 +- apps/web/components/ui/Avatar.tsx | 1 + apps/web/components/ui/AvatarSSR.tsx | 1 + apps/web/components/ui/PoweredByCal.tsx | 26 +- apps/web/components/ui/WeekdaySelect.tsx | 5 +- apps/web/components/ui/form/CheckedSelect.tsx | 5 +- apps/web/ee/components/stripe/Payment.tsx | 2 + apps/web/ee/components/stripe/PaymentPage.tsx | 3 +- apps/web/ee/components/web3/CryptoSection.tsx | 17 +- apps/web/ee/lib/stripe/server.ts | 9 +- apps/web/ee/pages/payment/[uid].tsx | 1 + apps/web/lib/emails/templates/_base-email.ts | 42 + .../templates/attendee-scheduled-email.ts | 43 +- .../emails/templates/forgot-password-email.ts | 38 +- .../templates/organizer-scheduled-email.ts | 41 +- .../lib/emails/templates/team-invite-email.ts | 38 +- apps/web/lib/getBusyTimes.ts | 58 ++ apps/web/lib/hooks/useSlots.ts | 62 +- apps/web/lib/queries/availability/index.ts | 17 +- apps/web/lib/slots.ts | 29 +- apps/web/lib/telemetry.ts | 2 + apps/web/lib/webhooks/integrationTemplate.tsx | 2 +- apps/web/package.json | 3 +- apps/web/pages/404.tsx | 4 +- apps/web/pages/500.tsx | 7 +- apps/web/pages/[user].tsx | 5 +- apps/web/pages/[user]/book.tsx | 9 +- apps/web/pages/api/app-store/installed.ts | 5 +- apps/web/pages/api/auth/[...nextauth].tsx | 7 + apps/web/pages/api/availability/[user].ts | 20 +- apps/web/pages/api/book/confirm.ts | 17 + apps/web/pages/api/book/event.ts | 124 ++- apps/web/pages/api/cancel.ts | 6 +- apps/web/pages/api/import/savvycal.ts | 2 +- apps/web/pages/api/integrations.ts | 80 ++ .../apps/{[slug].tsx => [slug]/index.tsx} | 2 +- apps/web/pages/apps/[slug]/setup.tsx | 45 ++ apps/web/pages/apps/categories/[category].tsx | 48 +- apps/web/pages/apps/index.tsx | 14 +- apps/web/pages/apps/installed.tsx | 15 + apps/web/pages/apps/setup/[appName].tsx | 38 - apps/web/pages/auth/error.tsx | 2 +- apps/web/pages/auth/forgot-password/[id].tsx | 19 +- apps/web/pages/auth/login.tsx | 2 +- apps/web/pages/auth/logout.tsx | 4 +- apps/web/pages/auth/sso/[provider].tsx | 2 +- apps/web/pages/availability/index.tsx | 62 +- apps/web/pages/availability/troubleshoot.tsx | 2 +- apps/web/pages/bookings/[status].tsx | 5 +- apps/web/pages/event-types/[type].tsx | 146 +++- apps/web/pages/event-types/index.tsx | 88 ++- apps/web/pages/getting-started.tsx | 35 +- apps/web/pages/success.tsx | 42 +- apps/web/pages/video/[uid].tsx | 33 +- apps/web/playwright/app-store.test.ts | 37 +- .../playwright/auth/delete-account.test.ts | 24 +- .../playwright/auth/forgot-password.test.ts | 19 +- apps/web/playwright/booking-pages.test.ts | 87 +- .../playwright/dynamic-booking-pages.test.ts | 34 +- .../playwright/embed-code-generator.test.ts | 6 +- apps/web/playwright/event-types.test.ts | 2 + apps/web/playwright/fixtures/bookings.ts | 88 +++ apps/web/playwright/fixtures/payments.ts | 59 ++ apps/web/playwright/fixtures/users.ts | 98 ++- apps/web/playwright/hash-my-url.test.ts | 41 +- .../playwright/integrations-stripe.test.ts | 59 +- apps/web/playwright/integrations.test.ts | 29 +- .../webhookResponse-chromium.txt | 2 +- apps/web/playwright/lib/dbSetup.ts | 80 -- apps/web/playwright/lib/fixtures.ts | 19 +- apps/web/playwright/lib/teardown.ts | 8 + apps/web/playwright/lib/testUtils.ts | 10 +- apps/web/playwright/login.test.ts | 24 +- apps/web/playwright/onboarding.test.ts | 5 + apps/web/playwright/reschedule.test.ts | 298 +++---- apps/web/public/cal-logo-word-dark.svg | 9 + apps/web/public/static/locales/ar/common.json | 108 ++- apps/web/public/static/locales/ar/vital.json | 1 + apps/web/public/static/locales/cs/common.json | 71 +- apps/web/public/static/locales/cs/vital.json | 1 + apps/web/public/static/locales/de/common.json | 107 ++- apps/web/public/static/locales/de/vital.json | 13 + apps/web/public/static/locales/en/common.json | 8 +- apps/web/public/static/locales/es/common.json | 107 +++ apps/web/public/static/locales/es/vital.json | 22 +- apps/web/public/static/locales/fr/common.json | 54 +- apps/web/public/static/locales/fr/vital.json | 1 + apps/web/public/static/locales/he/common.json | 741 +++++++++++++++++- apps/web/public/static/locales/he/vital.json | 1 + apps/web/public/static/locales/hu/common.json | 51 ++ apps/web/public/static/locales/hu/vital.json | 1 + apps/web/public/static/locales/it/common.json | 106 ++- apps/web/public/static/locales/it/vital.json | 1 + apps/web/public/static/locales/ja/common.json | 460 ++++++----- apps/web/public/static/locales/ja/vital.json | 1 + apps/web/public/static/locales/ko/common.json | 108 ++- apps/web/public/static/locales/ko/vital.json | 1 + apps/web/public/static/locales/nl/common.json | 243 +++++- apps/web/public/static/locales/nl/vital.json | 1 + apps/web/public/static/locales/pl/common.json | 45 +- apps/web/public/static/locales/pl/vital.json | 1 + .../public/static/locales/pt-BR/common.json | 185 ++++- .../public/static/locales/pt-BR/vital.json | 1 + apps/web/public/static/locales/pt/common.json | 109 ++- apps/web/public/static/locales/pt/vital.json | 13 + apps/web/public/static/locales/ro/common.json | 249 +++++- apps/web/public/static/locales/ro/vital.json | 1 + apps/web/public/static/locales/ru/common.json | 108 ++- apps/web/public/static/locales/ru/vital.json | 1 + apps/web/public/static/locales/sr/common.json | 111 ++- apps/web/public/static/locales/sr/vital.json | 1 + apps/web/public/static/locales/sv/common.json | 150 +++- apps/web/public/static/locales/sv/vital.json | 13 + apps/web/public/static/locales/tr/common.json | 737 ++++++++++++++++- apps/web/public/static/locales/tr/vital.json | 1 + apps/web/public/static/locales/uk/common.json | 95 ++- apps/web/public/static/locales/uk/vital.json | 1 + apps/web/public/static/locales/vi/common.json | 54 +- apps/web/public/static/locales/vi/vital.json | 1 + .../public/static/locales/zh-CN/common.json | 114 ++- .../public/static/locales/zh-CN/vital.json | 1 + .../public/static/locales/zh-TW/common.json | 38 +- .../public/static/locales/zh-TW/vital.json | 1 + apps/web/server/routers/viewer.tsx | 6 +- apps/web/server/routers/viewer/eventTypes.tsx | 16 +- apps/web/tailwind.config.js | 2 +- .../_components/DynamicComponent.tsx | 9 + .../_pages/setup/_getStaticProps.tsx | 20 + packages/app-store/_pages/setup/index.tsx | 13 + packages/app-store/giphy/README.mdx | 9 + packages/app-store/giphy/static/GIPHY1.png | Bin 0 -> 57610 bytes packages/app-store/giphy/static/GIPHY2.png | Bin 0 -> 70151 bytes .../googlecalendar/lib/CalendarService.ts | 13 +- packages/app-store/googlevideo/_metadata.ts | 4 +- .../app-store/googlevideo/static/logo.webp | Bin 0 -> 4328 bytes packages/app-store/locations.ts | 1 + packages/app-store/metamask/_metadata.ts | 2 +- .../office365calendar/lib/CalendarService.ts | 18 +- packages/app-store/office365video/api/add.ts | 2 +- .../app-store/office365video/api/callback.ts | 2 +- .../components/AccountDialog.tsx | 4 +- packages/app-store/types.d.ts | 2 + packages/app-store/utils.ts | 3 +- .../app-store/wipemycalother/_metadata.ts | 4 +- packages/app-store/zapier/README.md | 4 +- packages/app-store/zapier/README.mdx | 3 +- packages/app-store/zapier/api/add.ts | 2 +- packages/app-store/zapier/api/index.ts | 1 + .../zapier/api/subscriptions/listBookings.ts | 1 - .../app-store/zapier/api/subscriptions/me.ts | 35 + packages/app-store/zapier/components/index.ts | 1 - .../zapier/pages/setup/_getStaticProps.tsx | 20 + .../zapierSetup.tsx => pages/setup/index.tsx} | 27 +- packages/core/CalendarManager.ts | 21 +- packages/core/EventManager.ts | 15 +- .../embed-core/playwright/lib/testUtils.ts | 6 +- .../migration.sql | 2 + packages/prisma/schema.prisma | 1 + packages/prisma/seed-app-store.ts | 12 +- packages/prisma/zod-utils.ts | 1 + packages/types/Calendar.d.ts | 16 +- packages/types/EventManager.d.ts | 1 + packages/ui/Button.tsx | 13 +- packages/ui/Loader.tsx | 7 + packages/ui/index.tsx | 1 + tests/config/playwright.config.ts | 22 +- turbo.json | 2 +- 193 files changed, 6091 insertions(+), 1500 deletions(-) create mode 100644 apps/web/components/availability/ScheduleListItem.tsx create mode 100644 apps/web/components/dialog/DeleteStripeDialogContent.tsx create mode 100644 apps/web/components/integrations/DisconnectStripeIntegration.tsx create mode 100644 apps/web/lib/emails/templates/_base-email.ts create mode 100644 apps/web/lib/getBusyTimes.ts rename apps/web/pages/apps/{[slug].tsx => [slug]/index.tsx} (99%) create mode 100644 apps/web/pages/apps/[slug]/setup.tsx delete mode 100644 apps/web/pages/apps/setup/[appName].tsx create mode 100644 apps/web/playwright/fixtures/bookings.ts create mode 100644 apps/web/playwright/fixtures/payments.ts delete mode 100644 apps/web/playwright/lib/dbSetup.ts create mode 100644 apps/web/public/cal-logo-word-dark.svg create mode 100644 apps/web/public/static/locales/ar/vital.json create mode 100644 apps/web/public/static/locales/cs/vital.json create mode 100644 apps/web/public/static/locales/de/vital.json create mode 100644 apps/web/public/static/locales/fr/vital.json create mode 100644 apps/web/public/static/locales/he/vital.json create mode 100644 apps/web/public/static/locales/hu/common.json create mode 100644 apps/web/public/static/locales/hu/vital.json create mode 100644 apps/web/public/static/locales/it/vital.json create mode 100644 apps/web/public/static/locales/ja/vital.json create mode 100644 apps/web/public/static/locales/ko/vital.json create mode 100644 apps/web/public/static/locales/nl/vital.json create mode 100644 apps/web/public/static/locales/pl/vital.json create mode 100644 apps/web/public/static/locales/pt-BR/vital.json create mode 100644 apps/web/public/static/locales/pt/vital.json create mode 100644 apps/web/public/static/locales/ro/vital.json create mode 100644 apps/web/public/static/locales/ru/vital.json create mode 100644 apps/web/public/static/locales/sr/vital.json create mode 100644 apps/web/public/static/locales/sv/vital.json create mode 100644 apps/web/public/static/locales/tr/vital.json create mode 100644 apps/web/public/static/locales/uk/vital.json create mode 100644 apps/web/public/static/locales/vi/vital.json create mode 100644 apps/web/public/static/locales/zh-CN/vital.json create mode 100644 apps/web/public/static/locales/zh-TW/vital.json create mode 100644 packages/app-store/_components/DynamicComponent.tsx create mode 100644 packages/app-store/_pages/setup/_getStaticProps.tsx create mode 100644 packages/app-store/_pages/setup/index.tsx create mode 100644 packages/app-store/giphy/README.mdx create mode 100644 packages/app-store/giphy/static/GIPHY1.png create mode 100644 packages/app-store/giphy/static/GIPHY2.png create mode 100644 packages/app-store/googlevideo/static/logo.webp create mode 100644 packages/app-store/zapier/api/subscriptions/me.ts create mode 100644 packages/app-store/zapier/pages/setup/_getStaticProps.tsx rename packages/app-store/zapier/{components/zapierSetup.tsx => pages/setup/index.tsx} (85%) create mode 100644 packages/prisma/migrations/20220503183922_add_external_calendar_id_to_booking_reference/migration.sql create mode 100644 packages/ui/Loader.tsx diff --git a/.env.appStore.example b/.env.appStore.example index 9f6825c32f..667b5bf877 100644 --- a/.env.appStore.example +++ b/.env.appStore.example @@ -81,4 +81,9 @@ VITAL_WEBHOOK_SECRET= VITAL_DEVELOPMENT_MODE="sandbox" # "us" | "eu" VITAL_REGION="us" + +# - ZAPIER +# Used for the Zapier integration +# @see https://github.com/calcom/cal.com/blob/main/packages/app-store/zapier/README.md +ZAPIER_INVITE_LINK="" # ********************************************************************************************************* diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index 4d2dfb2d2c..e6fbff03b7 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v2 - name: crowdin action - uses: crowdin/github-action@1.4.2 + uses: crowdin/github-action@1.4.9 with: upload_translations: true download_translations: true diff --git a/apps/web/components/App.tsx b/apps/web/components/App.tsx index be404f3271..34d9f74983 100644 --- a/apps/web/components/App.tsx +++ b/apps/web/components/App.tsx @@ -81,7 +81,7 @@ export default function App({ } } getInstalledApp(type); - }, []); + }, [type]); return ( <> @@ -94,7 +94,10 @@ export default function App({
- {name} + { + // eslint-disable-next-line @next/next/no-img-element + {name} + }

{name}

diff --git a/apps/web/components/CustomBranding.tsx b/apps/web/components/CustomBranding.tsx index 314443e3ad..8dd83e2235 100644 --- a/apps/web/components/CustomBranding.tsx +++ b/apps/web/components/CustomBranding.tsx @@ -274,7 +274,17 @@ const BrandColor = ({ "--brand-text-color-dark-mode", getContrastingTextColor(darkVal, true) ); - }, [lightVal, darkVal]); + }, [ + embedBrandingColors.highlightColor, + embedBrandingColors.lightestColor, + embedBrandingColors.lighterColor, + embedBrandingColors.lightColor, + embedBrandingColors.medianColor, + embedBrandingColors.darkColor, + embedBrandingColors.darkerColor, + lightVal, + darkVal, + ]); return null; }; diff --git a/apps/web/components/ImageUploader.tsx b/apps/web/components/ImageUploader.tsx index 26aae80621..92c8702466 100644 --- a/apps/web/components/ImageUploader.tsx +++ b/apps/web/components/ImageUploader.tsx @@ -134,7 +134,10 @@ export default function ImageUploader({ {t("no_target", { target })}

)} - {imageSrc && {target}} + {imageSrc && ( + // eslint-disable-next-line @next/next/no-img-element + {target} + )}

)} {result && } diff --git a/apps/web/components/Loader.tsx b/apps/web/components/Loader.tsx index 29ac50d738..1eb4ef8161 100644 --- a/apps/web/components/Loader.tsx +++ b/apps/web/components/Loader.tsx @@ -1,7 +1 @@ -export default function Loader() { - return ( -
- -
- ); -} +export { default } from "@calcom/ui/Loader"; diff --git a/apps/web/components/Logo.tsx b/apps/web/components/Logo.tsx index 40923b4a74..08bc6632b2 100644 --- a/apps/web/components/Logo.tsx +++ b/apps/web/components/Logo.tsx @@ -3,8 +3,10 @@ export default function Logo({ small, icon }: { small?: boolean; icon?: boolean

{icon ? ( + // eslint-disable-next-line @next/next/no-img-element Cal ) : ( + // eslint-disable-next-line @next/next/no-img-element Cal {/* logo icon for tablet */} - + @@ -460,7 +460,6 @@ function UserDropdown({ small }: { small?: boolean }) { }, }); const utils = trpc.useContext(); - return ( @@ -470,11 +469,14 @@ function UserDropdown({ small }: { small?: boolean }) { small ? "h-8 w-8" : "h-10 w-10", "relative flex-shrink-0 rounded-full bg-gray-300 ltr:mr-3 rtl:ml-3" )}> - {user?.username + { + // eslint-disable-next-line @next/next/no-img-element + {user?.username + } {!user?.away && (
)} diff --git a/apps/web/components/apps/AppCard.tsx b/apps/web/components/apps/AppCard.tsx index 2549b5e422..2512e786d8 100644 --- a/apps/web/components/apps/AppCard.tsx +++ b/apps/web/components/apps/AppCard.tsx @@ -16,11 +16,15 @@ interface AppCardProps { export default function AppCard(props: AppCardProps) { return ( - +
- {props.name + { + // eslint-disable-next-line @next/next/no-img-element + {props.name + } + + + +
+ + ); +} diff --git a/apps/web/components/availability/SkeletonLoader.tsx b/apps/web/components/availability/SkeletonLoader.tsx index 2c12aa37c4..d85e129401 100644 --- a/apps/web/components/availability/SkeletonLoader.tsx +++ b/apps/web/components/availability/SkeletonLoader.tsx @@ -31,3 +31,16 @@ function SkeletonItem() { ); } + +export const AvailabilitySelectSkeletonLoader = () => { + return ( +
  • +
    +
    + + +
    +
    +
  • + ); +}; diff --git a/apps/web/components/booking/SkeletonLoader.tsx b/apps/web/components/booking/SkeletonLoader.tsx index 2e50ad0ef9..e38f75a0b3 100644 --- a/apps/web/components/booking/SkeletonLoader.tsx +++ b/apps/web/components/booking/SkeletonLoader.tsx @@ -2,8 +2,6 @@ import React from "react"; import { SkeletonText } from "@calcom/ui"; -import BookingsShell from "@components/BookingsShell"; - function SkeletonLoader() { return (
      @@ -22,10 +20,9 @@ function SkeletonItem() {
      - - + +
      -
      diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index cdd911014b..604a9ace82 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -16,7 +16,7 @@ import dayjs, { Dayjs } from "dayjs"; import customParseFormat from "dayjs/plugin/customParseFormat"; import utc from "dayjs/plugin/utc"; import { useRouter } from "next/router"; -import { useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { FormattedNumber, IntlProvider } from "react-intl"; import { Frequency as RRuleFrequency } from "rrule"; @@ -76,7 +76,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage if (!contracts[(eventType.metadata.smartContractAddress || null) as number]) router.replace(`/${eventOwner.username}`); } - }, [contracts, eventType.metadata.smartContractAddress, router]); + }, [contracts, eventType.metadata.smartContractAddress, eventType.users, router]); const selectedDate = useMemo(() => { const dateString = asStringOrNull(router.query.date); @@ -113,32 +113,42 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage telemetry.withJitsu((jitsu) => jitsu.track( - telemetryEventTypes.pageView, - collectPageParameters("availability", { isTeamBooking: document.URL.includes("team/") }) + top !== window ? telemetryEventTypes.embedView : telemetryEventTypes.pageView, + collectPageParameters("/availability", { isTeamBooking: document.URL.includes("team/") }) ) ); }, [telemetry]); - const changeDate = (newDate: Dayjs) => { - router.replace( - { - query: { - ...router.query, - date: newDate.format("YYYY-MM-DDZZ"), + const changeDate = useCallback( + (newDate: Dayjs) => { + router.replace( + { + query: { + ...router.query, + date: newDate.tz(timeZone(), true).format("YYYY-MM-DDZZ"), + }, }, - }, - undefined, - { - shallow: true, - } - ); - }; + undefined, + { shallow: true } + ); + }, + [router] + ); + + useEffect(() => { + if ( + selectedDate != null && + selectedDate?.utcOffset() !== selectedDate.clone().utcOffset(0).tz(timeZone()).utcOffset() + ) { + changeDate(selectedDate.tz(timeZone(), true)); + } + }, [selectedDate, changeDate]); const handleSelectTimeZone = (selectedTimeZone: string): void => { + timeZone(selectedTimeZone); if (selectedDate) { changeDate(selectedDate.tz(selectedTimeZone, true)); } - timeZone(selectedTimeZone); setIsTimeOptionsOpen(false); }; diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx index 33e8d11256..f479d00e0f 100644 --- a/apps/web/components/booking/pages/BookingPage.tsx +++ b/apps/web/components/booking/pages/BookingPage.tsx @@ -66,6 +66,7 @@ type BookingFormValues = { locationType?: LocationType; guests?: string[]; phone?: string; + hostPhoneNumber?: string; // Maybe come up with a better way to name this to distingish between two types of phone numbers customInputs?: { [key: string]: string; }; @@ -89,6 +90,17 @@ const BookingPage = ({ const { contracts } = useContracts(); const { data: session } = useSession(); const isBackgroundTransparent = useIsBackgroundTransparent(); + const telemetry = useTelemetry(); + + useEffect(() => { + telemetry.withJitsu((jitsu) => + jitsu.track( + top !== window ? telemetryEventTypes.embedView : telemetryEventTypes.pageView, + collectPageParameters("/book", { isTeamBooking: document.URL.includes("team/") }) + ) + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); useEffect(() => { if (eventType.metadata.smartContractAddress) { @@ -98,7 +110,7 @@ const BookingPage = ({ /* @ts-ignore */ router.replace(`/${eventOwner.username}`); } - }, [contracts, eventType.metadata.smartContractAddress, router]); + }, [contracts, eventType.metadata.smartContractAddress, eventType.users, router]); const mutation = useMutation(createBooking, { onSuccess: async (responseData) => { @@ -182,7 +194,7 @@ const BookingPage = ({ const eventTypeDetail = { isWeb3Active: false, ...eventType }; - type Location = { type: LocationType; address?: string; link?: string }; + type Location = { type: LocationType; address?: string; link?: string; hostPhoneNumber?: string }; // it would be nice if Prisma at some point in the future allowed for Json; as of now this is not the case. const locations: Location[] = useMemo( () => (eventType.locations as Location[]) || [], @@ -195,8 +207,6 @@ const BookingPage = ({ } }, [router.query.guest]); - const telemetry = useTelemetry(); - const locationInfo = (type: LocationType) => locations.find((location) => location.type === type); const loggedInIsOwner = eventType?.users[0]?.name === session?.user?.name; const guestListEmails = !isDynamicGroupBooking @@ -259,7 +269,9 @@ const BookingPage = ({ })(), }); - const getLocationValue = (booking: Pick) => { + const getLocationValue = ( + booking: Pick + ) => { const { locationType } = booking; switch (locationType) { case LocationType.Phone: { @@ -271,6 +283,9 @@ const BookingPage = ({ case LocationType.Link: { return locationInfo(locationType)?.link || ""; } + case LocationType.UserPhone: { + return locationInfo(locationType)?.hostPhoneNumber || ""; + } // Catches all other location types, such as Google Meet, Zoom etc. default: return selectedLocation || ""; @@ -294,7 +309,7 @@ const BookingPage = ({ const bookEvent = (booking: BookingFormValues) => { telemetry.withJitsu((jitsu) => jitsu.track( - telemetryEventTypes.bookingConfirmed, + top !== window ? telemetryEventTypes.embedBookingConfirmed : telemetryEventTypes.bookingConfirmed, collectPageParameters("/book", { isTeamBooking: document.URL.includes("team/") }) ) ); @@ -410,7 +425,7 @@ const BookingPage = ({ "main overflow-hidden", isEmbed ? "" : "border border-gray-200", isBackgroundTransparent ? "" : "dark:border-1 bg-white dark:bg-gray-800", - "rounded-md sm:border sm:dark:border-gray-600" + "rounded-md dark:border-gray-600 sm:border" )}>
      diff --git a/apps/web/components/dialog/ConfirmationDialogContent.tsx b/apps/web/components/dialog/ConfirmationDialogContent.tsx index 9487bf885a..6337b41d2d 100644 --- a/apps/web/components/dialog/ConfirmationDialogContent.tsx +++ b/apps/web/components/dialog/ConfirmationDialogContent.tsx @@ -3,15 +3,15 @@ import { CheckIcon } from "@heroicons/react/solid"; import * as DialogPrimitive from "@radix-ui/react-dialog"; import React, { PropsWithChildren, ReactNode } from "react"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Button } from "@calcom/ui/Button"; import { DialogClose, DialogContent } from "@calcom/ui/Dialog"; -import { useLocale } from "@lib/hooks/useLocale"; - export type ConfirmationDialogContentProps = { confirmBtn?: ReactNode; confirmBtnText?: string; cancelBtnText?: string; + isLoading?: boolean; onConfirm?: (event: React.MouseEvent) => void; title: string; variety?: "danger" | "warning" | "success"; @@ -25,6 +25,7 @@ export default function ConfirmationDialogContent(props: PropsWithChildren
      - - {confirmBtn || } + + {confirmBtn || ( + + )} - +
      diff --git a/apps/web/components/dialog/DeleteStripeDialogContent.tsx b/apps/web/components/dialog/DeleteStripeDialogContent.tsx new file mode 100644 index 0000000000..18ab4e49df --- /dev/null +++ b/apps/web/components/dialog/DeleteStripeDialogContent.tsx @@ -0,0 +1,78 @@ +import { ExclamationIcon } from "@heroicons/react/outline"; +import { CheckIcon } from "@heroicons/react/solid"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import React, { PropsWithChildren, ReactNode } from "react"; + +import { Button } from "@calcom/ui/Button"; +import { DialogClose, DialogContent } from "@calcom/ui/Dialog"; + +import { useLocale } from "@lib/hooks/useLocale"; + +export type DeleteStripeDialogContentProps = { + confirmBtn?: ReactNode; + cancelAllBookingsBtnText?: string; + removeBtnText?: string; + cancelBtnText?: string; + onConfirm?: (event: React.MouseEvent) => void; + onRemove?: (event: React.MouseEvent) => void; + title: string; + variety?: "danger" | "warning" | "success"; +}; + +export default function DeleteStripeDialogContent(props: PropsWithChildren) { + const { t } = useLocale(); + const { + title, + variety, + confirmBtn = null, + cancelAllBookingsBtnText, + removeBtnText, + cancelBtnText = t("cancel"), + onConfirm, + onRemove, + children, + } = props; + + return ( + +
      + {variety && ( +
      + {variety === "danger" && ( +
      + +
      + )} + {variety === "warning" && ( +
      + +
      + )} + {variety === "success" && ( +
      + +
      + )} +
      + )} +
      + {title} + + {children} + +
      +
      +
      + + + + + + + + + +
      +
      + ); +} diff --git a/apps/web/components/eventtype/CreateEventType.tsx b/apps/web/components/eventtype/CreateEventType.tsx index 540660ff78..cf531544e6 100644 --- a/apps/web/components/eventtype/CreateEventType.tsx +++ b/apps/web/components/eventtype/CreateEventType.tsx @@ -104,6 +104,11 @@ export default function CreateEventTypeButton(props: Props) { showToast(message, "error"); } + if (err.data?.code === "BAD_REQUEST") { + const message = `${err.data.code}: URL already exists.`; + showToast(message, "error"); + } + if (err.data?.code === "UNAUTHORIZED") { const message = `${err.data.code}: You are not able to create this event`; showToast(message, "error"); diff --git a/apps/web/components/eventtype/RecurringEventController.tsx b/apps/web/components/eventtype/RecurringEventController.tsx index 7034f2fcfe..518805fa96 100644 --- a/apps/web/components/eventtype/RecurringEventController.tsx +++ b/apps/web/components/eventtype/RecurringEventController.tsx @@ -1,18 +1,26 @@ import { Collapsible, CollapsibleContent } from "@radix-ui/react-collapsible"; -import React, { useState } from "react"; +import { useState } from "react"; import { UseFormReturn } from "react-hook-form"; import { Frequency as RRuleFrequency } from "rrule"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { RecurringEvent } from "@calcom/types/Calendar"; +import { Alert } from "@calcom/ui/Alert"; import Select from "@components/ui/form/Select"; -type RecurringEventControllerProps = { recurringEvent: RecurringEvent; formMethods: UseFormReturn }; +type RecurringEventControllerProps = { + recurringEvent: RecurringEvent; + formMethods: UseFormReturn; + paymentEnabled: boolean; + onRecurringEventDefined: Function; +}; export default function RecurringEventController({ recurringEvent, formMethods, + paymentEnabled, + onRecurringEventDefined, }: RecurringEventControllerProps) { const { t } = useLocale(); @@ -39,93 +47,100 @@ export default function RecurringEventController({ {t("recurring_event")}
      -
      -
      -
      - { - setRecurringEventDefined(event?.target.checked); - if (!event?.target.checked) { - formMethods.setValue("recurringEvent", {}); - } else { - formMethods.setValue( - "recurringEvent", - recurringEventDefined - ? recurringEvent - : { - interval: 1, - count: 12, - freq: RRuleFrequency.WEEKLY, - } - ); - } - recurringEvent = formMethods.getValues("recurringEvent"); - }} - type="checkbox" - className="text-primary-600 h-4 w-4 rounded border-gray-300" - defaultChecked={recurringEventDefined} - data-testid="recurring-event-check" - /> -
      -
      -

      {t("recurring_event_description")}

      -
      -
      - setRecurringEventDefined(!recurringEventDefined)}> - -
      -

      {t("repeats_every")}

      - { - setRecurringEventInterval(parseInt(event?.target.value)); - recurringEvent.interval = parseInt(event?.target.value); - formMethods.setValue("recurringEvent", recurringEvent); - }} - /> - { + setRecurringEventDefined(event?.target.checked); + onRecurringEventDefined(event?.target.checked); + if (!event?.target.checked) { + formMethods.setValue("recurringEvent", {}); + } else { + formMethods.setValue( + "recurringEvent", + recurringEventDefined + ? recurringEvent + : { + interval: 1, + count: 12, + freq: RRuleFrequency.WEEKLY, + } + ); + } + recurringEvent = formMethods.getValues("recurringEvent"); + }} + type="checkbox" + className="text-primary-600 h-4 w-4 rounded border-gray-300" + defaultChecked={recurringEventDefined} + data-testid="recurring-event-check" + /> +
      +
      +

      {t("recurring_event_description")}

      +
      -
      -

      {t("max")}

      - { - setRecurringEventCount(parseInt(event?.target.value)); - recurringEvent.count = parseInt(event?.target.value); - formMethods.setValue("recurringEvent", recurringEvent); - }} - /> -

      - {t(`${RRuleFrequency[recurringEventFrequency].toString().toLowerCase()}`, { - count: recurringEventCount, - })} -

      -
      - - + setRecurringEventDefined(!recurringEventDefined)}> + +
      +

      {t("repeats_every")}

      + { + setRecurringEventInterval(parseInt(event?.target.value)); + recurringEvent.interval = parseInt(event?.target.value); + formMethods.setValue("recurringEvent", recurringEvent); + }} + /> + { + setRecurringEventCount(parseInt(event?.target.value)); + recurringEvent.count = parseInt(event?.target.value); + formMethods.setValue("recurringEvent", recurringEvent); + }} + /> +

      + {t(`${RRuleFrequency[recurringEventFrequency].toString().toLowerCase()}`, { + count: recurringEventCount, + })} +

      +
      +
      +
      + + )}

    ); diff --git a/apps/web/components/integrations/DisconnectStripeIntegration.tsx b/apps/web/components/integrations/DisconnectStripeIntegration.tsx new file mode 100644 index 0000000000..d72c946cf5 --- /dev/null +++ b/apps/web/components/integrations/DisconnectStripeIntegration.tsx @@ -0,0 +1,69 @@ +import { useState } from "react"; +import { useMutation } from "react-query"; + +import showToast from "@calcom/lib/notification"; +import { ButtonBaseProps } from "@calcom/ui/Button"; +import { Dialog } from "@calcom/ui/Dialog"; + +import DeleteStripeDialogContent from "@components/dialog/DeleteStripeDialogContent"; + +export default function DisconnectStripeIntegration(props: { + /** Integration credential id */ + id: number; + render: (renderProps: ButtonBaseProps) => JSX.Element; + onOpenChange: (isOpen: boolean) => unknown | Promise; +}) { + const [modalOpen, setModalOpen] = useState(false); + const mutation = useMutation( + async (action: string) => { + const res = await fetch("/api/integrations", { + method: "DELETE", + body: JSON.stringify({ id: props.id, action }), + headers: { + "Content-Type": "application/json", + }, + }); + if (!res.ok) { + throw new Error("Something went wrong"); + } + return res.json(); + }, + { + async onSettled() { + await props.onOpenChange(modalOpen); + }, + onSuccess(data) { + showToast(data.message, "success"); + setModalOpen(false); + }, + } + ); + return ( + <> + + { + mutation.mutate("cancel"); + }} + onRemove={() => { + mutation.mutate("remove"); + }}> + If you have unpaid and unconfirmed bookings, you must choose to cancel them or remove the required + payment field. + + + {props.render({ + onClick() { + setModalOpen(true); + }, + disabled: modalOpen, + loading: mutation.isLoading, + })} + + ); +} diff --git a/apps/web/components/integrations/IntegrationListItem.tsx b/apps/web/components/integrations/IntegrationListItem.tsx index 454a0e4330..39f9a0d88b 100644 --- a/apps/web/components/integrations/IntegrationListItem.tsx +++ b/apps/web/components/integrations/IntegrationListItem.tsx @@ -14,7 +14,10 @@ function IntegrationListItem(props: { return (
    - {props.imageSrc && {props.title}} + { + // eslint-disable-next-line @next/next/no-img-element + props.imageSrc && {props.title} + }
    {props.title} {props.description} diff --git a/apps/web/components/security/EnableTwoFactorModal.tsx b/apps/web/components/security/EnableTwoFactorModal.tsx index e0cf227e50..ffd04c9d40 100644 --- a/apps/web/components/security/EnableTwoFactorModal.tsx +++ b/apps/web/components/security/EnableTwoFactorModal.tsx @@ -150,7 +150,10 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps) <>
    - + { + // eslint-disable-next-line @next/next/no-img-element + + }

    {secret}

    diff --git a/apps/web/components/team/TeamList.tsx b/apps/web/components/team/TeamList.tsx index 8c7bc027a9..f817770042 100644 --- a/apps/web/components/team/TeamList.tsx +++ b/apps/web/components/team/TeamList.tsx @@ -1,6 +1,6 @@ import showToast from "@calcom/lib/notification"; -import { trpc, inferQueryOutput } from "@lib/trpc"; +import { inferQueryOutput, trpc } from "@lib/trpc"; import TeamListItem from "./TeamListItem"; @@ -39,7 +39,9 @@ export default function TeamList(props: Props) { selectAction(action, team?.id as number)}> + onActionSelect={(action: string) => selectAction(action, team?.id as number)} + isLoading={deleteTeamMutation.isLoading} + /> ))}
    diff --git a/apps/web/components/team/TeamListItem.tsx b/apps/web/components/team/TeamListItem.tsx index 776a9139c3..ce05aeb6fe 100644 --- a/apps/web/components/team/TeamListItem.tsx +++ b/apps/web/components/team/TeamListItem.tsx @@ -1,10 +1,10 @@ import { LogoutIcon } from "@heroicons/react/outline"; import { - ExternalLinkIcon, - TrashIcon, - LinkIcon, DotsHorizontalIcon, + ExternalLinkIcon, + LinkIcon, PencilIcon, + TrashIcon, } from "@heroicons/react/solid"; import { MembershipRole } from "@prisma/client"; import Link from "next/link"; @@ -16,14 +16,14 @@ import { Dialog, DialogTrigger } from "@calcom/ui/Dialog"; import Dropdown, { DropdownMenuContent, DropdownMenuItem, - DropdownMenuTrigger, DropdownMenuSeparator, + DropdownMenuTrigger, } from "@calcom/ui/Dropdown"; import { Tooltip } from "@calcom/ui/Tooltip"; import classNames from "@lib/classNames"; import { getPlaceholderAvatar } from "@lib/getPlaceholderAvatar"; -import { trpc, inferQueryOutput } from "@lib/trpc"; +import { inferQueryOutput, trpc } from "@lib/trpc"; import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent"; import Avatar from "@components/ui/Avatar"; @@ -34,6 +34,7 @@ interface Props { team: inferQueryOutput<"viewer.teams.list">[number]; key: number; onActionSelect: (text: string) => void; + isLoading?: boolean; } export default function TeamListItem(props: Props) { @@ -175,7 +176,10 @@ export default function TeamListItem(props: Props) { variety="danger" title={t("disband_team")} confirmBtnText={t("confirm_disband_team")} - onConfirm={() => props.onActionSelect("disband")}> + isLoading={props.isLoading} + onConfirm={() => { + props.onActionSelect("disband"); + }}> {t("disband_team_confirmation_message")} diff --git a/apps/web/components/team/TeamSettingsRightSidebar.tsx b/apps/web/components/team/TeamSettingsRightSidebar.tsx index 57aaffe15b..16bdb93150 100644 --- a/apps/web/components/team/TeamSettingsRightSidebar.tsx +++ b/apps/web/components/team/TeamSettingsRightSidebar.tsx @@ -118,6 +118,7 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers; )}
    {props.team?.id && props.role !== MembershipRole.MEMBER && ( + // eslint-disable-next-line @next/next/link-passhref
    {"View Availability"} diff --git a/apps/web/components/team/screens/Team.tsx b/apps/web/components/team/screens/Team.tsx index a7a26338c3..63405bdd89 100644 --- a/apps/web/components/team/screens/Team.tsx +++ b/apps/web/components/team/screens/Team.tsx @@ -37,6 +37,7 @@ const Team = ({ team }: TeamPageProps) => { ); return ( + // eslint-disable-next-line @next/next/link-passhref
    ) { return (
    -
    +
    {props.showLogo && ( + // eslint-disable-next-line @next/next/no-img-element Cal.com Logo )} {props.heading && ( diff --git a/apps/web/components/ui/Avatar.tsx b/apps/web/components/ui/Avatar.tsx index ecc18d7b70..25febf92e2 100644 --- a/apps/web/components/ui/Avatar.tsx +++ b/apps/web/components/ui/Avatar.tsx @@ -27,6 +27,7 @@ export default function Avatar(props: AvatarProps) { /> {gravatarFallbackMd5 && ( + // eslint-disable-next-line @next/next/no-img-element {alt} )} diff --git a/apps/web/components/ui/AvatarSSR.tsx b/apps/web/components/ui/AvatarSSR.tsx index 13c1e80929..9e0599274b 100644 --- a/apps/web/components/ui/AvatarSSR.tsx +++ b/apps/web/components/ui/AvatarSSR.tsx @@ -28,5 +28,6 @@ export function AvatarSSR(props: AvatarProps) { } else if (user.emailMd5) { imgSrc = defaultAvatarSrc(user.emailMd5); } + // eslint-disable-next-line @next/next/no-img-element return imgSrc ? {alt} : null; } diff --git a/apps/web/components/ui/PoweredByCal.tsx b/apps/web/components/ui/PoweredByCal.tsx index 4ac80352af..9aa32166c4 100644 --- a/apps/web/components/ui/PoweredByCal.tsx +++ b/apps/web/components/ui/PoweredByCal.tsx @@ -12,16 +12,22 @@ const PoweredByCal = () => { {t("powered_by")}{" "} - Cal.com Logo - Cal.com Logo + { + // eslint-disable-next-line @next/next/no-img-element + Cal.com Logo + } + { + // eslint-disable-next-line @next/next/no-img-element + Cal.com Logo + }
    diff --git a/apps/web/components/ui/WeekdaySelect.tsx b/apps/web/components/ui/WeekdaySelect.tsx index 95aec8f4c5..5a56af2e1a 100644 --- a/apps/web/components/ui/WeekdaySelect.tsx +++ b/apps/web/components/ui/WeekdaySelect.tsx @@ -6,6 +6,7 @@ interface WeekdaySelectProps { } export const WeekdaySelect = (props: WeekdaySelectProps) => { + const { onSelect } = props; const [activeDays, setActiveDays] = useState( Array.from(Array(7).keys()).map((v, i) => (props.defaultValue || []).includes(i)) ); @@ -13,8 +14,8 @@ export const WeekdaySelect = (props: WeekdaySelectProps) => { const days = ["S", "M", "T", "W", "T", "F", "S"]; useEffect(() => { - props.onSelect(activeDays.map((v, idx) => (v ? idx : -1)).filter((v) => v !== -1)); - }, [activeDays]); + onSelect(activeDays.map((v, idx) => (v ? idx : -1)).filter((v) => v !== -1)); + }, [onSelect, activeDays]); const toggleDay = (idx: number) => { activeDays[idx] = !activeDays[idx]; diff --git a/apps/web/components/ui/form/CheckedSelect.tsx b/apps/web/components/ui/form/CheckedSelect.tsx index fa5f8aa7a2..2c61aaaa7c 100644 --- a/apps/web/components/ui/form/CheckedSelect.tsx +++ b/apps/web/components/ui/form/CheckedSelect.tsx @@ -24,12 +24,13 @@ export type CheckedSelectProps = { }; export const CheckedSelect = (props: CheckedSelectProps) => { + const { onChange } = props; const [selectedOptions, setSelectedOptions] = useState(props.defaultValue || []); const { t } = useLocale(); useEffect(() => { - props.onChange(selectedOptions); - }, [selectedOptions]); + onChange(selectedOptions); + }, [onChange, selectedOptions]); const options = props.options.map((option) => ({ ...option, diff --git a/apps/web/ee/components/stripe/Payment.tsx b/apps/web/ee/components/stripe/Payment.tsx index e1ae9dd2bb..c6157114b2 100644 --- a/apps/web/ee/components/stripe/Payment.tsx +++ b/apps/web/ee/components/stripe/Payment.tsx @@ -35,6 +35,7 @@ type Props = { eventType: { id: number }; user: { username: string | null }; location?: string | null; + bookingId: number; }; type States = @@ -86,6 +87,7 @@ export default function PaymentComponent(props: Props) { type: props.eventType.id, user: props.user.username, name, + bookingId: props.bookingId, }; if (props.location) { diff --git a/apps/web/ee/components/stripe/PaymentPage.tsx b/apps/web/ee/components/stripe/PaymentPage.tsx index 08606bdd67..58852d28b9 100644 --- a/apps/web/ee/components/stripe/PaymentPage.tsx +++ b/apps/web/ee/components/stripe/PaymentPage.tsx @@ -49,7 +49,7 @@ const PaymentPage: FC = (props) => { embedIframeWidth = e.detail.data.iframeWidth as number; }); } - }, [isEmbed]); + }, [date, isEmbed]); const eventName = props.booking.title; @@ -137,6 +137,7 @@ const PaymentPage: FC = (props) => { eventType={props.eventType} user={props.user} location={props.booking.location} + bookingId={props.booking.id} /> )} diff --git a/apps/web/ee/components/web3/CryptoSection.tsx b/apps/web/ee/components/web3/CryptoSection.tsx index 6e0eb81447..23cbe2abf0 100644 --- a/apps/web/ee/components/web3/CryptoSection.tsx +++ b/apps/web/ee/components/web3/CryptoSection.tsx @@ -89,12 +89,15 @@ const CryptoSection = (props: CryptoSectionProps) => { } return
    ; - }, [props.verified]); + }, [props.pathname, props.verified]); const verifyButton = useMemo(() => { return ( ); @@ -103,7 +106,10 @@ const CryptoSection = (props: CryptoSectionProps) => { const connectButton = useMemo(() => { return ( ); @@ -118,7 +124,10 @@ const CryptoSection = (props: CryptoSectionProps) => { await connectMetamask(); await verifyWallet(); }}> - + { + // eslint-disable-next-line @next/next/no-img-element + MetaMask + } {t("verify_wallet")} ); diff --git a/apps/web/ee/lib/stripe/server.ts b/apps/web/ee/lib/stripe/server.ts index 1302fdea0a..625b985f6a 100644 --- a/apps/web/ee/lib/stripe/server.ts +++ b/apps/web/ee/lib/stripe/server.ts @@ -2,6 +2,7 @@ import { PaymentType, Prisma } from "@prisma/client"; import Stripe from "stripe"; import { v4 as uuidv4 } from "uuid"; +import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug"; import { getErrorFromUnknown } from "@calcom/lib/errors"; import prisma from "@calcom/prisma"; import { createPaymentLink } from "@calcom/stripe/client"; @@ -16,8 +17,8 @@ export type PaymentInfo = { id?: string | null; }; -const paymentFeePercentage = process.env.PAYMENT_FEE_PERCENTAGE!; -const paymentFeeFixed = process.env.PAYMENT_FEE_FIXED!; +let paymentFeePercentage: number | undefined; +let paymentFeeFixed: number | undefined; export async function handlePayment( evt: CalendarEvent, @@ -33,6 +34,10 @@ export async function handlePayment( uid: string; } ) { + const appKeys = await getAppKeysFromSlug("stripe"); + if (typeof appKeys.payment_fee_fixed === "number") paymentFeePercentage = appKeys.payment_fee_fixed; + if (typeof appKeys.payment_fee_percentage === "number") paymentFeeFixed = appKeys.payment_fee_percentage; + const paymentFee = Math.round( selectedEventType.price * parseFloat(`${paymentFeePercentage}`) + parseInt(`${paymentFeeFixed}`) ); diff --git a/apps/web/ee/pages/payment/[uid].tsx b/apps/web/ee/pages/payment/[uid].tsx index 23b44b9003..fd7021b9b8 100644 --- a/apps/web/ee/pages/payment/[uid].tsx +++ b/apps/web/ee/pages/payment/[uid].tsx @@ -21,6 +21,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => bookingId: true, booking: { select: { + id: true, description: true, title: true, startTime: true, diff --git a/apps/web/lib/emails/templates/_base-email.ts b/apps/web/lib/emails/templates/_base-email.ts new file mode 100644 index 0000000000..3cd5b44eea --- /dev/null +++ b/apps/web/lib/emails/templates/_base-email.ts @@ -0,0 +1,42 @@ +import nodemailer from "nodemailer"; + +import { getErrorFromUnknown } from "@calcom/lib/errors"; +import { serverConfig } from "@calcom/lib/serverConfig"; + +export default class BaseEmail { + name: string = ""; + + protected getNodeMailerPayload(): Record { + return {}; + } + public sendEmail() { + if (process.env.NEXT_PUBLIC_IS_E2E) return new Promise((r) => r("Skipped sendEmail for E2E")); + new Promise((resolve, reject) => + nodemailer + .createTransport(this.getMailerOptions().transport) + .sendMail(this.getNodeMailerPayload(), (_err, info) => { + if (_err) { + const err = getErrorFromUnknown(_err); + this.printNodeMailerError(err); + reject(err); + } else { + resolve(info); + } + }) + ).catch((e) => console.error("sendEmail", e)); + return new Promise((resolve) => resolve("send mail async")); + } + + protected getMailerOptions() { + return { + transport: serverConfig.transport, + from: serverConfig.from, + }; + } + + protected printNodeMailerError(error: Error): void { + /** Don't clog the logs with unsent emails in E2E */ + if (process.env.NEXT_PUBLIC_IS_E2E) return; + console.error(`${this.name}_ERROR`, error); + } +} diff --git a/apps/web/lib/emails/templates/attendee-scheduled-email.ts b/apps/web/lib/emails/templates/attendee-scheduled-email.ts index 1a95899074..051aa3273b 100644 --- a/apps/web/lib/emails/templates/attendee-scheduled-email.ts +++ b/apps/web/lib/emails/templates/attendee-scheduled-email.ts @@ -4,22 +4,20 @@ import timezone from "dayjs/plugin/timezone"; import toArray from "dayjs/plugin/toArray"; import utc from "dayjs/plugin/utc"; import { createEvent, DateArray } from "ics"; -import { DatasetJsonLdProps } from "next-seo"; -import nodemailer from "nodemailer"; import rrule from "rrule"; import { getAppName } from "@calcom/app-store/utils"; import { getCancelLink, getRichDescription } from "@calcom/lib/CalEventParser"; -import { getErrorFromUnknown } from "@calcom/lib/errors"; -import { serverConfig } from "@calcom/lib/serverConfig"; -import type { Person, CalendarEvent, RecurringEvent } from "@calcom/types/Calendar"; +import type { CalendarEvent, Person, RecurringEvent } from "@calcom/types/Calendar"; + +import BaseEmail from "@lib/emails/templates/_base-email"; import { - emailHead, - emailSchedulingBodyHeader, emailBodyLogo, + emailHead, emailScheduledBodyHeaderContent, emailSchedulingBodyDivider, + emailSchedulingBodyHeader, linkIcon, } from "./common"; @@ -28,34 +26,19 @@ dayjs.extend(timezone); dayjs.extend(localizedFormat); dayjs.extend(toArray); -export default class AttendeeScheduledEmail { +export default class AttendeeScheduledEmail extends BaseEmail { calEvent: CalendarEvent; attendee: Person; recurringEvent: RecurringEvent; constructor(calEvent: CalendarEvent, attendee: Person, recurringEvent: RecurringEvent) { + super(); + this.name = "SEND_BOOKING_CONFIRMATION"; this.calEvent = calEvent; this.attendee = attendee; this.recurringEvent = recurringEvent; } - public sendEmail() { - new Promise((resolve, reject) => - nodemailer - .createTransport(this.getMailerOptions().transport) - .sendMail(this.getNodeMailerPayload(), (_err, info) => { - if (_err) { - const err = getErrorFromUnknown(_err); - this.printNodeMailerError(err); - reject(err); - } else { - resolve(info); - } - }) - ).catch((e) => console.error("sendEmail", e)); - return new Promise((resolve) => resolve("send mail async")); - } - protected getiCalEventAsString(): string | undefined { // Taking care of recurrence rule beforehand let recurrenceRule: string | undefined = undefined; @@ -115,12 +98,6 @@ export default class AttendeeScheduledEmail { }; } - protected getMailerOptions() { - return { - transport: serverConfig.transport, - from: serverConfig.from, - }; - } protected getDescription(): string { if (!this.calEvent.description) return ""; return ` @@ -144,10 +121,6 @@ ${getRichDescription(this.calEvent)} `.trim(); } - protected printNodeMailerError(error: Error): void { - console.error("SEND_BOOKING_CONFIRMATION_ERROR", this.attendee.email, error); - } - protected getHtmlBody(): string { const headerContent = this.calEvent.attendees[0].language.translate("confirmed_event_type_subject", { eventType: this.calEvent.type, diff --git a/apps/web/lib/emails/templates/forgot-password-email.ts b/apps/web/lib/emails/templates/forgot-password-email.ts index 45cc19d997..cd5c0f2d59 100644 --- a/apps/web/lib/emails/templates/forgot-password-email.ts +++ b/apps/web/lib/emails/templates/forgot-password-email.ts @@ -1,10 +1,8 @@ import { TFunction } from "next-i18next"; -import nodemailer from "nodemailer"; -import { getErrorFromUnknown } from "@calcom/lib/errors"; -import { serverConfig } from "@calcom/lib/serverConfig"; +import BaseEmail from "@lib/emails/templates/_base-email"; -import { emailHead, linkIcon, emailBodyLogo } from "./common"; +import { emailBodyLogo, emailHead, linkIcon } from "./common"; export type PasswordReset = { language: TFunction; @@ -17,37 +15,15 @@ export type PasswordReset = { export const PASSWORD_RESET_EXPIRY_HOURS = 6; -export default class ForgotPasswordEmail { +export default class ForgotPasswordEmail extends BaseEmail { passwordEvent: PasswordReset; constructor(passwordEvent: PasswordReset) { + super(); + this.name = "SEND_PASSWORD_RESET_EMAIL"; this.passwordEvent = passwordEvent; } - public sendEmail() { - new Promise((resolve, reject) => - nodemailer - .createTransport(this.getMailerOptions().transport) - .sendMail(this.getNodeMailerPayload(), (_err, info) => { - if (_err) { - const err = getErrorFromUnknown(_err); - this.printNodeMailerError(err); - reject(err); - } else { - resolve(info); - } - }) - ).catch((e) => console.error("sendEmail", e)); - return new Promise((resolve) => resolve("send mail async")); - } - - protected getMailerOptions() { - return { - transport: serverConfig.transport, - from: serverConfig.from, - }; - } - protected getNodeMailerPayload(): Record { return { to: `${this.passwordEvent.user.name} <${this.passwordEvent.user.email}>`, @@ -58,10 +34,6 @@ export default class ForgotPasswordEmail { }; } - protected printNodeMailerError(error: Error): void { - console.error("SEND_PASSWORD_RESET_EMAIL_ERROR", this.passwordEvent.user.email, error); - } - protected getTextBody(): string { return ` ${this.passwordEvent.language("reset_password_subject")} diff --git a/apps/web/lib/emails/templates/organizer-scheduled-email.ts b/apps/web/lib/emails/templates/organizer-scheduled-email.ts index fe9381a76a..3f51f6f121 100644 --- a/apps/web/lib/emails/templates/organizer-scheduled-email.ts +++ b/apps/web/lib/emails/templates/organizer-scheduled-email.ts @@ -4,21 +4,20 @@ import timezone from "dayjs/plugin/timezone"; import toArray from "dayjs/plugin/toArray"; import utc from "dayjs/plugin/utc"; import { createEvent, DateArray, Person } from "ics"; -import nodemailer from "nodemailer"; import rrule from "rrule"; import { getAppName } from "@calcom/app-store/utils"; import { getCancelLink, getRichDescription } from "@calcom/lib/CalEventParser"; -import { getErrorFromUnknown } from "@calcom/lib/errors"; -import { serverConfig } from "@calcom/lib/serverConfig"; import type { CalendarEvent, RecurringEvent } from "@calcom/types/Calendar"; +import BaseEmail from "@lib/emails/templates/_base-email"; + import { - emailHead, - emailSchedulingBodyHeader, emailBodyLogo, + emailHead, emailScheduledBodyHeaderContent, emailSchedulingBodyDivider, + emailSchedulingBodyHeader, linkIcon, } from "./common"; @@ -27,32 +26,17 @@ dayjs.extend(timezone); dayjs.extend(localizedFormat); dayjs.extend(toArray); -export default class OrganizerScheduledEmail { +export default class OrganizerScheduledEmail extends BaseEmail { calEvent: CalendarEvent; recurringEvent: RecurringEvent; constructor(calEvent: CalendarEvent, recurringEvent: RecurringEvent) { + super(); + this.name = "SEND_BOOKING_CONFIRMATION"; this.calEvent = calEvent; this.recurringEvent = recurringEvent; } - public sendEmail() { - new Promise((resolve, reject) => - nodemailer - .createTransport(this.getMailerOptions().transport) - .sendMail(this.getNodeMailerPayload(), (_err, info) => { - if (_err) { - const err = getErrorFromUnknown(_err); - this.printNodeMailerError(err); - reject(err); - } else { - resolve(info); - } - }) - ).catch((e) => console.error("sendEmail", e)); - return new Promise((resolve) => resolve("send mail async")); - } - protected getiCalEventAsString(): string | undefined { // Taking care of recurrence rule beforehand let recurrenceRule: string | undefined = undefined; @@ -121,13 +105,6 @@ export default class OrganizerScheduledEmail { }; } - protected getMailerOptions() { - return { - transport: serverConfig.transport, - from: serverConfig.from, - }; - } - protected getTextBody(): string { return ` ${this.calEvent.organizer.language.translate( @@ -139,10 +116,6 @@ ${getRichDescription(this.calEvent)} `.trim(); } - protected printNodeMailerError(error: Error): void { - console.error("SEND_BOOKING_CONFIRMATION_ERROR", this.calEvent.organizer.email, error); - } - protected getHtmlBody(): string { const headerContent = this.calEvent.organizer.language.translate("confirmed_event_type_subject", { eventType: this.calEvent.type, diff --git a/apps/web/lib/emails/templates/team-invite-email.ts b/apps/web/lib/emails/templates/team-invite-email.ts index 7d9d91aed2..c15c705376 100644 --- a/apps/web/lib/emails/templates/team-invite-email.ts +++ b/apps/web/lib/emails/templates/team-invite-email.ts @@ -1,10 +1,8 @@ import { TFunction } from "next-i18next"; -import nodemailer from "nodemailer"; -import { getErrorFromUnknown } from "@calcom/lib/errors"; -import { serverConfig } from "@calcom/lib/serverConfig"; +import BaseEmail from "@lib/emails/templates/_base-email"; -import { emailHead, linkIcon, emailBodyLogo } from "./common"; +import { emailBodyLogo, emailHead, linkIcon } from "./common"; export type TeamInvite = { language: TFunction; @@ -14,37 +12,15 @@ export type TeamInvite = { joinLink: string; }; -export default class TeamInviteEmail { +export default class TeamInviteEmail extends BaseEmail { teamInviteEvent: TeamInvite; constructor(teamInviteEvent: TeamInvite) { + super(); + this.name = "SEND_TEAM_INVITE_EMAIL"; this.teamInviteEvent = teamInviteEvent; } - public sendEmail() { - new Promise((resolve, reject) => - nodemailer - .createTransport(this.getMailerOptions().transport) - .sendMail(this.getNodeMailerPayload(), (_err, info) => { - if (_err) { - const err = getErrorFromUnknown(_err); - this.printNodeMailerError(err); - reject(err); - } else { - resolve(info); - } - }) - ).catch((e) => console.error("sendEmail", e)); - return new Promise((resolve) => resolve("send mail async")); - } - - protected getMailerOptions() { - return { - transport: serverConfig.transport, - from: serverConfig.from, - }; - } - protected getNodeMailerPayload(): Record { return { to: this.teamInviteEvent.to, @@ -58,10 +34,6 @@ export default class TeamInviteEmail { }; } - protected printNodeMailerError(error: Error): void { - console.error("SEND_TEAM_INVITE_EMAIL_ERROR", this.teamInviteEvent.to, error); - } - protected getTextBody(): string { return ""; } diff --git a/apps/web/lib/getBusyTimes.ts b/apps/web/lib/getBusyTimes.ts new file mode 100644 index 0000000000..b6e81d07ce --- /dev/null +++ b/apps/web/lib/getBusyTimes.ts @@ -0,0 +1,58 @@ +import { Credential, SelectedCalendar } from "@prisma/client"; + +import { getBusyCalendarTimes } from "@calcom/core/CalendarManager"; +import { getBusyVideoTimes } from "@calcom/core/videoClient"; +import notEmpty from "@calcom/lib/notEmpty"; +import type { EventBusyDate } from "@calcom/types/Calendar"; + +import prisma from "@lib/prisma"; + +async function getBusyTimes(params: { + credentials: Credential[]; + userId: number; + eventTypeId?: number; + startTime: string; + endTime: string; + selectedCalendars: SelectedCalendar[]; +}) { + const { credentials, userId, eventTypeId, startTime, endTime, selectedCalendars } = params; + const busyTimes: EventBusyDate[] = await prisma.booking + .findMany({ + where: { + AND: [ + { + userId, + eventTypeId, + startTime: { gte: new Date(startTime) }, + endTime: { lte: new Date(endTime) }, + }, + { + OR: [ + { + status: "ACCEPTED", + }, + { + status: "PENDING", + }, + ], + }, + ], + }, + select: { + startTime: true, + endTime: true, + }, + }) + .then((bookings) => bookings.map((booking) => ({ end: booking.endTime, start: booking.startTime }))); + + if (credentials) { + const calendarBusyTimes = await getBusyCalendarTimes(credentials, startTime, endTime, selectedCalendars); + busyTimes.push(...calendarBusyTimes); + const videoBusyTimes = (await getBusyVideoTimes(credentials)).filter(notEmpty); + busyTimes.push(...videoBusyTimes); + } + + return busyTimes; +} + +export default getBusyTimes; diff --git a/apps/web/lib/hooks/useSlots.ts b/apps/web/lib/hooks/useSlots.ts index a54e31e599..2b1a28c21d 100644 --- a/apps/web/lib/hooks/useSlots.ts +++ b/apps/web/lib/hooks/useSlots.ts @@ -122,6 +122,31 @@ export const useSlots = (props: UseSlotsProps) => { const dateTo = date.endOf("day").format(); const query = stringify({ dateFrom, dateTo, eventTypeId }); + const handleAvailableSlots = async (res: Response) => { + const responseBody: AvailabilityUserResponse = await res.json(); + const times = getSlots({ + frequency: slotInterval || eventLength, + inviteeDate: date, + workingHours: responseBody.workingHours, + minimumBookingNotice, + eventLength, + }); + const filterTimeProps = { + times, + busy: responseBody.busy, + eventLength, + beforeBufferTime, + afterBufferTime, + }; + const filteredTimes = getFilteredTimes(filterTimeProps); + // temporary + const user = res.url.substring(res.url.lastIndexOf("/") + 1, res.url.indexOf("?")); + return filteredTimes.map((time) => ({ + time, + users: [user], + })); + }; + Promise.all( users.map((user) => fetch(`/api/availability/${user.username}?${query}`).then(handleAvailableSlots)) ) @@ -173,32 +198,17 @@ export const useSlots = (props: UseSlotsProps) => { console.error(e); setError(e); }); - }, [date]); - - const handleAvailableSlots = async (res: Response) => { - const responseBody: AvailabilityUserResponse = await res.json(); - const times = getSlots({ - frequency: slotInterval || eventLength, - inviteeDate: date, - workingHours: responseBody.workingHours, - minimumBookingNotice, - eventLength, - }); - const filterTimeProps = { - times, - busy: responseBody.busy, - eventLength, - beforeBufferTime, - afterBufferTime, - }; - const filteredTimes = getFilteredTimes(filterTimeProps); - // temporary - const user = res.url.substring(res.url.lastIndexOf("/") + 1, res.url.indexOf("?")); - return filteredTimes.map((time) => ({ - time, - users: [user], - })); - }; + }, [ + afterBufferTime, + beforeBufferTime, + eventLength, + minimumBookingNotice, + slotInterval, + eventTypeId, + props.schedulingType, + users, + date, + ]); return { slots, diff --git a/apps/web/lib/queries/availability/index.ts b/apps/web/lib/queries/availability/index.ts index 19a8eeabaa..a3c88c0dec 100644 --- a/apps/web/lib/queries/availability/index.ts +++ b/apps/web/lib/queries/availability/index.ts @@ -1,10 +1,9 @@ import { Prisma } from "@prisma/client"; import dayjs from "dayjs"; -import { getBusyCalendarTimes } from "@calcom/core/CalendarManager"; - import { asStringOrNull } from "@lib/asStringOrNull"; import { getWorkingHours } from "@lib/availability"; +import getBusyTimes from "@lib/getBusyTimes"; import prisma from "@lib/prisma"; export async function getUserAvailability(query: { @@ -60,12 +59,14 @@ export async function getUserAvailability(query: { const { selectedCalendars, ...currentUser } = rawUser; - const busyTimes = await getBusyCalendarTimes( - currentUser.credentials, - dateFrom.format(), - dateTo.format(), - selectedCalendars - ); + const busyTimes = await getBusyTimes({ + credentials: currentUser.credentials, + startTime: dateFrom.format(), + endTime: dateTo.format(), + eventTypeId: query.eventTypeId, + userId: currentUser.id, + selectedCalendars, + }); const bufferedBusyTimes = busyTimes.map((a) => ({ start: dayjs(a.start).subtract(currentUser.bufferTime, "minute").toString(), diff --git a/apps/web/lib/slots.ts b/apps/web/lib/slots.ts index 2ed82d762a..2fb9920b3e 100644 --- a/apps/web/lib/slots.ts +++ b/apps/web/lib/slots.ts @@ -32,7 +32,7 @@ const splitAvailableTime = ( const periodTime = initialTime + frequency; const slotEndTime = initialTime + eventLength; /* - check if the slot end time surpasses availability end time of the user + check if the slot end time surpasses availability end time of the user 1 minute is added to round up the hour mark so that end of the slot is considered in the check instead of x9 eg: if finalization time is 11:59, slotEndTime is 12:00, we ideally want the slot to be available */ @@ -64,9 +64,32 @@ const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours, const slots: Dayjs[] = []; const slotsTimeFrameAvailable = [] as Array; - - // Here we split working hour in chunks for every frequency available that can fit in whole working hour + // Here we split working hour in chunks for every frequency available that can fit in whole working hours + const computedLocalWorkingHours: WorkingHoursTimeFrame[] = []; + let tempComputeTimeFrame: WorkingHoursTimeFrame | undefined; + const computeLength = localWorkingHours.length - 1; + const makeTimeFrame = (item: typeof localWorkingHours[0]): WorkingHoursTimeFrame => ({ + startTime: item.startTime, + endTime: item.endTime, + }); localWorkingHours.forEach((item, index) => { + if (!tempComputeTimeFrame) { + tempComputeTimeFrame = makeTimeFrame(item); + } else { + // please check the comment in splitAvailableTime func for the added 1 minute + if (tempComputeTimeFrame.endTime + 1 === item.startTime) { + // to deal with time that across the day, e.g. from 11:59 to to 12:01 + tempComputeTimeFrame.endTime = item.endTime; + } else { + computedLocalWorkingHours.push(tempComputeTimeFrame); + tempComputeTimeFrame = makeTimeFrame(item); + } + } + if (index == computeLength) { + computedLocalWorkingHours.push(tempComputeTimeFrame); + } + }); + computedLocalWorkingHours.forEach((item, index) => { slotsTimeFrameAvailable.push(...splitAvailableTime(item.startTime, item.endTime, frequency, eventLength)); }); diff --git a/apps/web/lib/telemetry.ts b/apps/web/lib/telemetry.ts index 90093be7db..df787e7f33 100644 --- a/apps/web/lib/telemetry.ts +++ b/apps/web/lib/telemetry.ts @@ -13,6 +13,8 @@ export const telemetryEventTypes = { googleLogin: "google_login", samlLogin: "saml_login", samlConfig: "saml_config", + embedView: "embed_view", + embedBookingConfirmed: "embed_booking_confirmed", }; /** diff --git a/apps/web/lib/webhooks/integrationTemplate.tsx b/apps/web/lib/webhooks/integrationTemplate.tsx index 6c9c9f9c27..a461789cb9 100644 --- a/apps/web/lib/webhooks/integrationTemplate.tsx +++ b/apps/web/lib/webhooks/integrationTemplate.tsx @@ -8,7 +8,7 @@ export const hasTemplateIntegration = (props: WebhookIntegrationProps) => { const ind = supportedWebhookIntegrationList.findIndex((integration) => { return props.url.includes(integration); }); - return ind > -1 ? true : false; + return ind > -1; }; const customTemplate = (props: WebhookIntegrationProps) => { diff --git a/apps/web/package.json b/apps/web/package.json index 4ef3ab481d..a67a702703 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@calcom/web", - "version": "1.5.4", + "version": "1.6.0", "private": true, "scripts": { "analyze": "ANALYZE=true next build", @@ -74,6 +74,7 @@ "ical.js": "^1.4.0", "ics": "^2.31.0", "jimp": "^0.16.1", + "libphonenumber-js": "^1.9.53", "lodash": "^4.17.21", "micro": "^9.3.4", "mime-types": "^2.1.35", diff --git a/apps/web/pages/404.tsx b/apps/web/pages/404.tsx index 45ca57c24f..90a54f04fa 100644 --- a/apps/web/pages/404.tsx +++ b/apps/web/pages/404.tsx @@ -35,7 +35,7 @@ export default function Custom404() { const [url, setUrl] = useState("https://cal.com/signup?username="); useEffect(() => { setUrl(`https://cal.com/signup?username=${username.replace("/", "")}`); - }, [router.query]); + }, [username, router.query]); const isSubpage = router.asPath.includes("/", 2); const isSignup = router.asPath.includes("/signup"); @@ -98,7 +98,7 @@ export default function Custom404() {
    - +
    diff --git a/apps/web/pages/auth/forgot-password/[id].tsx b/apps/web/pages/auth/forgot-password/[id].tsx index b7bae37bc5..6bbdf86a9f 100644 --- a/apps/web/pages/auth/forgot-password/[id].tsx +++ b/apps/web/pages/auth/forgot-password/[id].tsx @@ -63,13 +63,16 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {

    {t("password_has_been_reset_login")}

    - - - + { + // eslint-disable-next-line @next/next/link-passhref + + + + }
    ); @@ -84,7 +87,7 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {

    {t("request_is_expired")}

    {t("request_is_expired_instructions")}

    - +
    diff --git a/apps/web/pages/auth/logout.tsx b/apps/web/pages/auth/logout.tsx index 22673e1cde..70fcb675c6 100644 --- a/apps/web/pages/auth/logout.tsx +++ b/apps/web/pages/auth/logout.tsx @@ -25,7 +25,7 @@ export default function Logout(props: Props) { if (props.query?.survey === "true") { router.push("https://cal.com/cancellation"); } - }, []); + }, [props.query?.survey, router]); const { t } = useLocale(); return ( @@ -43,7 +43,7 @@ export default function Logout(props: Props) { - + diff --git a/apps/web/pages/auth/sso/[provider].tsx b/apps/web/pages/auth/sso/[provider].tsx index 8627edd9a5..1c8e914d41 100644 --- a/apps/web/pages/auth/sso/[provider].tsx +++ b/apps/web/pages/auth/sso/[provider].tsx @@ -38,7 +38,7 @@ export default function Provider(props: SSOProviderPageProps) { } else { signIn(props.provider); } - }, []); + }, [props.isSAMLLoginEnabled, props.product, props.provider, props.tenant, router]); return null; } diff --git a/apps/web/pages/availability/index.tsx b/apps/web/pages/availability/index.tsx index 6de7650716..ef216396c4 100644 --- a/apps/web/pages/availability/index.tsx +++ b/apps/web/pages/availability/index.tsx @@ -1,13 +1,7 @@ import { ClockIcon } from "@heroicons/react/outline"; -import { DotsHorizontalIcon, TrashIcon } from "@heroicons/react/solid"; -import { Availability } from "@prisma/client"; -import Link from "next/link"; -import { availabilityAsString } from "@calcom/lib/availability"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; -import { Button } from "@calcom/ui"; -import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown"; import { withQuery } from "@lib/QueryCell"; import { HttpError } from "@lib/core/http/error"; @@ -16,10 +10,11 @@ import { inferQueryOutput, trpc } from "@lib/trpc"; import EmptyScreen from "@components/EmptyScreen"; import Shell from "@components/Shell"; import { NewScheduleButton } from "@components/availability/NewScheduleButton"; +import { ScheduleListItem } from "@components/availability/ScheduleListItem"; import SkeletonLoader from "@components/availability/SkeletonLoader"; export function AvailabilityList({ schedules }: inferQueryOutput<"viewer.availability.list">) { - const { t, i18n } = useLocale(); + const { t } = useLocale(); const utils = trpc.useContext(); const deleteMutation = trpc.useMutation("viewer.availability.schedule.delete", { onSuccess: async () => { @@ -45,53 +40,12 @@ export function AvailabilityList({ schedules }: inferQueryOutput<"viewer.availab
    diff --git a/apps/web/pages/availability/troubleshoot.tsx b/apps/web/pages/availability/troubleshoot.tsx index 2b2230733a..9d6330ccb2 100644 --- a/apps/web/pages/availability/troubleshoot.tsx +++ b/apps/web/pages/availability/troubleshoot.tsx @@ -48,7 +48,7 @@ const AvailabilityView = ({ user }: { user: User }) => { }); }; fetchAvailability(selectedDate); - }, [selectedDate]); + }, [user.username, selectedDate]); return (
    diff --git a/apps/web/pages/bookings/[status].tsx b/apps/web/pages/bookings/[status].tsx index b6e77cb860..f838a5bcd7 100644 --- a/apps/web/pages/bookings/[status].tsx +++ b/apps/web/pages/bookings/[status].tsx @@ -60,10 +60,7 @@ export default function Bookings() { }; return ( - }> + }>
    diff --git a/apps/web/pages/event-types/[type].tsx b/apps/web/pages/event-types/[type].tsx index 0f6ff47a29..dae70aa507 100644 --- a/apps/web/pages/event-types/[type].tsx +++ b/apps/web/pages/event-types/[type].tsx @@ -21,6 +21,7 @@ import classNames from "classnames"; import dayjs from "dayjs"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; +import { isValidPhoneNumber, parsePhoneNumber } from "libphonenumber-js"; import { GetServerSidePropsContext } from "next"; import { useRouter } from "next/router"; import React, { useEffect, useState } from "react"; @@ -37,6 +38,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; import { StripeData } from "@calcom/stripe/server"; import { RecurringEvent } from "@calcom/types/Calendar"; +import { Alert } from "@calcom/ui/Alert"; import Button from "@calcom/ui/Button"; import { Dialog, DialogContent, DialogTrigger } from "@calcom/ui/Dialog"; import Switch from "@calcom/ui/Switch"; @@ -60,6 +62,7 @@ import { EmbedButton, EmbedDialog } from "@components/Embed"; import Loader from "@components/Loader"; import Shell from "@components/Shell"; import { UpgradeToProDialog } from "@components/UpgradeToProDialog"; +import { AvailabilitySelectSkeletonLoader } from "@components/availability/SkeletonLoader"; import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent"; import RecurringEventController from "@components/eventtype/RecurringEventController"; import CustomInputTypeForm from "@components/pages/eventtypes/CustomInputTypeForm"; @@ -69,6 +72,7 @@ import CheckboxField from "@components/ui/form/CheckboxField"; import CheckedSelect from "@components/ui/form/CheckedSelect"; import { DateRangePicker } from "@components/ui/form/DateRangePicker"; import MinutesField from "@components/ui/form/MinutesField"; +import PhoneInput from "@components/ui/form/PhoneInput"; import Select from "@components/ui/form/Select"; import * as RadioArea from "@components/ui/form/radio-area"; import WebhookListContainer from "@components/webhook/WebhookListContainer"; @@ -164,6 +168,7 @@ const AvailabilitySelect = ({ return ( } success={({ data }) => { const options = data.schedules.map((schedule) => ({ value: schedule.id, @@ -278,8 +283,9 @@ const EventTypePage = (props: inferSSRProps) => { const [advancedSettingsVisible, setAdvancedSettingsVisible] = useState(false); - const [requirePayment, setRequirePayment] = useState( - eventType.price > 0 && eventType.recurringEvent?.count !== undefined + const [requirePayment, setRequirePayment] = useState(eventType.price > 0); + const [recurringEventDefined, setRecurringEventDefined] = useState( + eventType.recurringEvent?.count !== undefined ); const [hashedLinkVisible, setHashedLinkVisible] = useState(!!eventType.hashedLink); @@ -327,6 +333,7 @@ const EventTypePage = (props: inferSSRProps) => { fetchTokens(); !hashedUrl && setHashedUrl(generateHashedLink(eventType.users[0].id)); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); async function deleteEventTypeHandler(event: React.MouseEvent) { @@ -419,6 +426,34 @@ const EventTypePage = (props: inferSSRProps) => {
    ); + case LocationType.UserPhone: + return ( +
    + +
    + location.type === LocationType.UserPhone)?.hostPhoneNumber + } + /> + {locationFormMethods.formState.errors.locationPhoneNumber && ( +

    + {locationFormMethods.formState.errors.locationPhoneNumber.message} +

    + )} +
    +
    + ); case LocationType.Phone: return

    {t("cal_invitee_phone_number_scheduling")}

    ; /* TODO: Render this dynamically from App Store */ @@ -506,7 +541,7 @@ const EventTypePage = (props: inferSSRProps) => { hidden: boolean; hideCalendarNotes: boolean; hashedLink: string | undefined; - locations: { type: LocationType; address?: string; link?: string }[]; + locations: { type: LocationType; address?: string; link?: string; hostPhoneNumber?: string }[]; customInputs: EventTypeCustomInput[]; users: string[]; schedule: number; @@ -539,11 +574,16 @@ const EventTypePage = (props: inferSSRProps) => { const locationFormSchema = z.object({ locationType: z.string(), locationAddress: z.string().optional(), + locationPhoneNumber: z + .string() + .refine((val) => isValidPhoneNumber(val)) + .optional(), locationLink: z.string().url().optional(), // URL validates as new URL() - which requires HTTPS:// In the input field }); const locationFormMethods = useForm<{ locationType: LocationType; + locationPhoneNumber?: string; locationAddress?: string; // TODO: We should validate address or fetch the address from googles api to see if its valid? locationLink?: string; // Currently this only accepts links that are HTTPS:// }>({ @@ -562,7 +602,11 @@ const EventTypePage = (props: inferSSRProps) => { if (e?.value) { const newLocationType: LocationType = e.value; locationFormMethods.setValue("locationType", newLocationType); - if (newLocationType === LocationType.InPerson || newLocationType === LocationType.Link) { + if ( + newLocationType === LocationType.InPerson || + newLocationType === LocationType.Link || + newLocationType === LocationType.UserPhone + ) { openLocationModal(newLocationType); } else { addLocation(newLocationType); @@ -599,6 +643,16 @@ const EventTypePage = (props: inferSSRProps) => { /> )} + {location.type === LocationType.UserPhone && ( +
    + + +
    + )} {location.type === LocationType.Phone && (
    @@ -866,6 +920,7 @@ const EventTypePage = (props: inferSSRProps) => { locationFormMethods.setValue("locationType", location.type); locationFormMethods.unregister("locationLink"); locationFormMethods.unregister("locationAddress"); + locationFormMethods.unregister("locationPhoneNumber"); openLocationModal(location.type); }} aria-label={t("edit")} @@ -911,7 +966,9 @@ const EventTypePage = (props: inferSSRProps) => {

    - {eventType.title} + {formMethods.getValues("title") && formMethods.getValues("title") !== "" + ? formMethods.getValues("title") + : eventType.title}

    @@ -926,6 +983,10 @@ const EventTypePage = (props: inferSSRProps) => { placeholder={t("quick_chat")} {...formMethods.register("title")} defaultValue={eventType.title} + onBlur={() => { + setEditIcon(true); + formMethods.getValues("title") === "" && formMethods.setValue("title", eventType.title); + }} />
    )} @@ -1353,6 +1414,8 @@ const EventTypePage = (props: inferSSRProps) => { /> @@ -1670,7 +1733,7 @@ const EventTypePage = (props: inferSSRProps) => { formMethods={formMethods} eventType={eventType}> - {hasPaymentIntegration && eventType.recurringEvent?.count !== undefined && ( + {hasPaymentIntegration && ( <>
    @@ -1684,40 +1747,44 @@ const EventTypePage = (props: inferSSRProps) => {
    -
    -
    -
    -
    - { - setRequirePayment(event.target.checked); - if (!event.target.checked) { - formMethods.setValue("price", 0); - } - }} - id="requirePayment" - name="requirePayment" - type="checkbox" - className="text-primary-600 h-4 w-4 rounded border-gray-300" - defaultChecked={requirePayment} - /> -
    -
    -

    - {t("require_payment")} (0.5% +{" "} - - - {" "} - {t("commission_per_transaction")}) -

    + {recurringEventDefined ? ( + + ) : ( +
    +
    +
    +
    + { + setRequirePayment(event.target.checked); + if (!event.target.checked) { + formMethods.setValue("price", 0); + } + }} + id="requirePayment" + name="requirePayment" + type="checkbox" + className="text-primary-600 h-4 w-4 rounded border-gray-300" + defaultChecked={requirePayment} + /> +
    +
    +

    + {t("require_payment")} (0.5% +{" "} + + + {" "} + {t("commission_per_transaction")}) +

    +
    -
    + )}
    {requirePayment && (
    @@ -1916,7 +1983,9 @@ const EventTypePage = (props: inferSSRProps) => { if (newLocation === LocationType.Link) { details = { link: values.locationLink }; } - + if (newLocation === LocationType.UserPhone) { + details = { hostPhoneNumber: values.locationPhoneNumber }; + } addLocation(newLocation, details); setShowLocationModal(false); }}> @@ -1937,6 +2006,7 @@ const EventTypePage = (props: inferSSRProps) => { locationFormMethods.setValue("locationType", val.value); locationFormMethods.unregister("locationLink"); locationFormMethods.unregister("locationAddress"); + locationFormMethods.unregister("locationPhoneNumber"); setSelectedLocation(val); } }} diff --git a/apps/web/pages/event-types/index.tsx b/apps/web/pages/event-types/index.tsx index 69846ff3be..8b1b077a7b 100644 --- a/apps/web/pages/event-types/index.tsx +++ b/apps/web/pages/event-types/index.tsx @@ -78,7 +78,10 @@ const Item = ({ type, group, readOnly }: any) => { return (
    (
  • -
    - {types.length > 1 && ( +
    + {types.length > 1 && !type.$disabled && ( <> - + + + +
    {" "} {t("edit")} @@ -294,7 +312,10 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL type="button" color="minimal" size="sm" - className="w-full rounded-none" + className={classNames( + "w-full rounded-none", + type.$disabled && " pointer-events-none cursor-not-allowed opacity-30" + )} data-testid={"event-type-duplicate-" + type.id} StartIcon={DuplicateIcon} onClick={() => openModal(group, type)}> @@ -304,7 +325,10 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL diff --git a/apps/web/pages/getting-started.tsx b/apps/web/pages/getting-started.tsx index e14d6bf74c..b8313674d7 100644 --- a/apps/web/pages/getting-started.tsx +++ b/apps/web/pages/getting-started.tsx @@ -12,7 +12,7 @@ import { NextPageContext } from "next"; import { useSession } from "next-auth/react"; import Head from "next/head"; import { useRouter } from "next/router"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect, useRef, useState, useCallback } from "react"; import { useForm } from "react-hook-form"; import TimezoneSelect from "react-timezone-select"; import * as z from "zod"; @@ -102,21 +102,24 @@ export default function Onboarding(props: inferSSRProps(null); - const updateUser = async (data: Prisma.UserUpdateInput) => { - const res = await fetch(`/api/user/${props.user.id}`, { - method: "PATCH", - body: JSON.stringify({ data: { ...data } }), - headers: { - "Content-Type": "application/json", - }, - }); + const updateUser = useCallback( + async (data: Prisma.UserUpdateInput) => { + const res = await fetch(`/api/user/${props.user.id}`, { + method: "PATCH", + body: JSON.stringify({ data: { ...data } }), + headers: { + "Content-Type": "application/json", + }, + }); - if (!res.ok) { - throw new Error((await res.json()).message); - } - const responseData = await res.json(); - return responseData.data; - }; + if (!res.ok) { + throw new Error((await res.json()).message); + } + const responseData = await res.json(); + return responseData.data; + }, + [props.user.id] + ); const createEventType = async (data: Prisma.EventTypeCreateInput) => { const res = await fetch(`/api/availability/eventtype`, { @@ -288,7 +291,7 @@ export default function Onboarding(props: inferSSRProps { + telemetry.withJitsu((jitsu) => + jitsu.track( + top !== window ? telemetryEventTypes.embedView : telemetryEventTypes.pageView, + collectPageParameters("/success") + ) + ); + }, [telemetry]); useEffect(() => { const users = eventType.users; @@ -190,7 +200,7 @@ export default function Success(props: SuccessProps) { }); setDate(date.tz(localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess())); setIs24h(!!localStorage.getItem("timeOption.is24hClock")); - }, [eventType, needsConfirmation]); + }, [date, eventType, needsConfirmation]); function eventLink(): string { const optional: { location?: string } = {}; @@ -274,7 +284,10 @@ export default function Success(props: SuccessProps) { "mx-auto flex items-center justify-center", !giphyImage ? "h-12 w-12 rounded-full bg-green-100" : "" )}> - {giphyImage && !needsConfirmation && {"Gif} + {giphyImage && !needsConfirmation && ( + // eslint-disable-next-line @next/next/no-img-element + {"Gif + )} {!giphyImage && !needsConfirmation && ( )} @@ -299,14 +312,6 @@ export default function Success(props: SuccessProps) {
    {t("what")}
    {eventName}
    {t("when")}
    - {/*
    - {date.format("MMMM DD, YYYY")} -
    - {date.format("LT")} - {date.add(props.eventType.length, "m").format("LT")}{" "} - - ({localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()}) - -
    */}
    {t("who")}
    -
    +
    {bookingInfo?.user && (

    {bookingInfo.user.name}

    @@ -546,9 +551,9 @@ function RecurringBookings({ {eventType.recurringEvent?.count && recurringBookings.slice(0, 4).map((dateStr, idx) => (
    - {dayjs(dateStr).format("dddd, DD MMMM YYYY")} + {dayjs(dateStr).format("MMMM DD, YYYY")}
    - {dayjs(dateStr).format(is24h ? "H:mm" : "h:mma")} - {eventType.length} mins{" "} + {dayjs(dateStr).format("LT")} - {dayjs(dateStr).add(eventType.length, "m").format("LT")}{" "} ({localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()}) @@ -565,9 +570,9 @@ function RecurringBookings({ {eventType.recurringEvent?.count && recurringBookings.slice(4).map((dateStr, idx) => (
    - {dayjs(dateStr).format("dddd, DD MMMM YYYY")} + {dayjs(dateStr).format("MMMM DD, YYYY")}
    - {dayjs(dateStr).format(is24h ? "H:mm" : "h:mma")} - {eventType.length} mins{" "} + {dayjs(dateStr).format("LT")} - {dayjs(dateStr).add(eventType.length, "m").format("LT")}{" "} ({localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()}) @@ -579,9 +584,9 @@ function RecurringBookings({ ) : !eventType.recurringEvent.freq ? ( <> - {date.format("dddd, DD MMMM YYYY")} + {date.format("MMMM DD, YYYY")}
    - {date.format(is24h ? "H:mm" : "h:mma")} - {eventType.length} mins{" "} + {date.format("LT")} - {date.add(eventType.length, "m").format("LT")}{" "} ({localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()}) @@ -634,7 +639,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { const recurringEventIdQuery = asStringOrNull(context.query.recur); const typeSlug = asStringOrNull(context.query.eventSlug) ?? "15min"; const dynamicEventName = asStringOrNull(context.query.eventName) ?? ""; - const bookingId = parseInt(context.query.bookingId as string); + if (typeof context.query.bookingId !== "string") return { notFound: true } as const; + const bookingId = parseInt(context.query.bookingId); if (isNaN(typeId)) { return { diff --git a/apps/web/pages/video/[uid].tsx b/apps/web/pages/video/[uid].tsx index 488dc0d440..5353ae9bb8 100644 --- a/apps/web/pages/video/[uid].tsx +++ b/apps/web/pages/video/[uid].tsx @@ -96,8 +96,14 @@ export default function JoinCall(props: JoinCallPageProps) { token: props.booking.dailyRef?.dailytoken, }); } - }, []); - + }, [ + emptyBooking, + meetingUnavailable, + props.booking?.dailyRef?.dailytoken, + props.booking?.dailyRef?.dailyurl, + props.booking?.user?.id, + session?.userid, + ]); return ( <> @@ -116,16 +122,19 @@ export default function JoinCall(props: JoinCallPageProps) {
    - - Cal.com Logo + + { + // eslint-disable-next-line @next/next/no-img-element + Cal.com Logo + } {JoinCall}
    diff --git a/apps/web/playwright/app-store.test.ts b/apps/web/playwright/app-store.test.ts index 326d093ce3..515d1088fd 100644 --- a/apps/web/playwright/app-store.test.ts +++ b/apps/web/playwright/app-store.test.ts @@ -1,44 +1,27 @@ +import { expect } from "@playwright/test"; + import { test } from "./lib/fixtures"; +test.describe.configure({ mode: "parallel" }); + test.describe("App Store - Authed", () => { test.use({ storageState: "playwright/artifacts/proStorageState.json" }); test("Browse apple-calendar and try to install", async ({ page }) => { await page.goto("/apps"); - page.click('[data-testid="app-store-category-calendar"]'); - await page.waitForNavigation({ - url: (url) => { - console.log(url, url.pathname); - return url.pathname.includes("apps/categories/calendar"); - }, - }); - page.click('[data-testid="app-store-app-card-apple-calendar"]'); - await page.waitForNavigation({ - url: (url) => { - return url.pathname.includes("apps/apple-calendar"); - }, - }); + await page.click('[data-testid="app-store-category-calendar"]'); + await page.click('[data-testid="app-store-app-card-apple-calendar"]'); await page.click('[data-testid="install-app-button"]'); + await expect(page.locator(`text=Connect to Apple Server`)).toBeVisible(); }); }); test.describe("App Store - Unauthed", () => { test("Browse apple-calendar and try to install", async ({ page }) => { await page.goto("/apps"); - + await page.waitForSelector("[data-testid=dashboard-shell]"); await page.click('[data-testid="app-store-category-calendar"]'); - if (!page.url().includes("apps/categories/calendar")) { - await page.waitForNavigation({ - url: (url) => { - console.log(url, url.pathname); - return url.pathname.includes("apps/categories/calendar"); - }, - }); - } await page.click('[data-testid="app-store-app-card-apple-calendar"]'); - await page.waitForNavigation({ - url: (url) => { - return url.pathname.includes("/auth/login"); - }, - }); + await page.click('[data-testid="install-app-button"]'); + await expect(page.locator(`[data-testid="login-form"]`)).toBeVisible(); }); }); diff --git a/apps/web/playwright/auth/delete-account.test.ts b/apps/web/playwright/auth/delete-account.test.ts index 3ba706388d..2bc1bb7a8c 100644 --- a/apps/web/playwright/auth/delete-account.test.ts +++ b/apps/web/playwright/auth/delete-account.test.ts @@ -1,19 +1,13 @@ -import { expect, test } from "@playwright/test"; +import { expect } from "@playwright/test"; + +import { test } from "../lib/fixtures"; + +test("Can delete user account", async ({ page, users }) => { + const user = await users.create({ + username: "delete-me", + }); + await user.login(); -test("Can delete user account", async ({ page }) => { - //FIXME: This test depends on seed. - // Login to account to delete - await page.goto(`/auth/login`); - // Click input[name="email"] - await page.click('input[name="email"]'); - // Fill input[name="email"] - await page.fill('input[name="email"]', `delete-me@example.com`); - // Press Tab - await page.press('input[name="email"]', "Tab"); - // Fill input[name="password"] - await page.fill('input[name="password"]', "delete-me"); - // Press Enter - await page.press('input[name="password"]', "Enter"); await page.waitForSelector("[data-testid=dashboard-shell]"); await page.goto(`/settings/profile`); diff --git a/apps/web/playwright/auth/forgot-password.test.ts b/apps/web/playwright/auth/forgot-password.test.ts index 289a27bfd5..0e7339806d 100644 --- a/apps/web/playwright/auth/forgot-password.test.ts +++ b/apps/web/playwright/auth/forgot-password.test.ts @@ -1,11 +1,16 @@ -import { expect, test } from "@playwright/test"; +import { expect } from "@playwright/test"; -test("Can reset forgotten password", async ({ page }) => { +import { test } from "../lib/fixtures"; + +test("Can reset forgotten password", async ({ page, users }) => { + const user = await users.create(); + const newPassword = `${user.username!}-123`; // Got to reset password flow await page.goto("/auth/forgot-password"); + await page.waitForSelector("text=Forgot Password"); // Fill [placeholder="john.doe@example.com"] - await page.fill('input[name="email"]', "pro@example.com"); + await page.fill('input[name="email"]', `${user.username}@example.com`); // Press Enter await Promise.all([ @@ -18,7 +23,7 @@ test("Can reset forgotten password", async ({ page }) => { // Wait for page to fully load await page.waitForSelector("text=Reset Password"); // Fill input[name="password"] - await page.fill('input[name="password"]', "pro"); + await page.fill('input[name="password"]', newPassword); // Click text=Submit await page.click('button[type="submit"]'); @@ -33,10 +38,12 @@ test("Can reset forgotten password", async ({ page }) => { await Promise.all([page.waitForNavigation({ url: "/auth/login" }), page.click('button:has-text("Login")')]); // Fill input[name="email"] - await page.fill('input[name="email"]', "pro@example.com"); - await page.fill('input[name="password"]', "pro"); + await page.fill('input[name="email"]', `${user.username}@example.com`); + await page.fill('input[name="password"]', newPassword); await page.press('input[name="password"]', "Enter"); await page.waitForSelector("[data-testid=dashboard-shell]"); await expect(page.locator("[data-testid=dashboard-shell]")).toBeVisible(); + + await users.deleteAll(); }); diff --git a/apps/web/playwright/booking-pages.test.ts b/apps/web/playwright/booking-pages.test.ts index 6c0115b01f..f1bc7090c0 100644 --- a/apps/web/playwright/booking-pages.test.ts +++ b/apps/web/playwright/booking-pages.test.ts @@ -1,27 +1,28 @@ -import { expect, test } from "@playwright/test"; +import { expect } from "@playwright/test"; -import { deleteAllBookingsByEmail } from "./lib/teardown"; +import { test } from "./lib/fixtures"; import { bookFirstEvent, bookTimeSlot, selectFirstAvailableTimeSlotNextMonth, selectSecondAvailableTimeSlotNextMonth, - todo, } from "./lib/testUtils"; +test.describe.configure({ mode: "parallel" }); + test.describe("free user", () => { - test.beforeEach(async ({ page }) => { - await page.goto("/free"); + test.beforeEach(async ({ page, users }) => { + const free = await users.create({ plan: "FREE" }); + await page.goto(`/${free.username}`); + }); + test.afterEach(async ({ users }) => { + await users.deleteAll(); }); - test.afterEach(async () => { - // delete test bookings - await deleteAllBookingsByEmail("free@example.com"); - }); - - test("only one visible event", async ({ page }) => { - await expect(page.locator(`[href="/free/30min"]`)).toBeVisible(); - await expect(page.locator(`[href="/free/60min"]`)).not.toBeVisible(); + test("only one visible event", async ({ page, users }) => { + const [free] = users.get(); + await expect(page.locator(`[href="/${free.username}/${free.eventTypes[0].slug}"]`)).toBeVisible(); + await expect(page.locator(`[href="/${free.username}/${free.eventTypes[1].slug}"]`)).not.toBeVisible(); }); test("cannot book same slot multiple times", async ({ page }) => { @@ -44,11 +45,7 @@ test.describe("free user", () => { await bookTimeSlot(page); // Make sure we're navigated to the success page - await page.waitForNavigation({ - url(url) { - return url.pathname.endsWith("/success"); - }, - }); + await expect(page.locator("[data-testid=success-page]")).toBeVisible(); // return to same time spot booking page await page.goto(bookingUrl); @@ -60,55 +57,44 @@ test.describe("free user", () => { await expect(page.locator("[data-testid=booking-fail]")).toBeVisible(); }); - // Why do we need this test. The previous test is testing /30min booking only ? - todo("`/free/30min` is bookable"); - - test("`/free/60min` is not bookable", async ({ page }) => { + test("Second event type is not bookable", async ({ page, users }) => { + const [free] = users.get(); // Not available in listing - await expect(page.locator('[href="/free/60min"]')).toHaveCount(0); + await expect(page.locator(`[href="/${free.username}/${free.eventTypes[1].slug}"]`)).toHaveCount(0); - await page.goto("/free/60min"); + await page.goto(`/${free.username}/${free.eventTypes[1].slug}`); // Not available on a direct visit to event type page await expect(page.locator('[data-testid="404-page"]')).toBeVisible(); }); }); test.describe("pro user", () => { - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); - - test.beforeEach(async ({ page }) => { - await deleteAllBookingsByEmail("pro@example.com"); - await page.goto("/pro"); + test.beforeEach(async ({ page, users }) => { + const pro = await users.create(); + await page.goto(`/${pro.username}`); }); - - test.afterEach(async () => { - await deleteAllBookingsByEmail("pro@example.com"); + test.afterEach(async ({ users }) => { + await users.deleteAll(); }); test("pro user's page has at least 2 visible events", async ({ page }) => { // await page.pause(); - const $eventTypes = await page.locator("[data-testid=event-types] > *"); + const $eventTypes = page.locator("[data-testid=event-types] > *"); expect(await $eventTypes.count()).toBeGreaterThanOrEqual(2); }); test("book an event first day in next month", async ({ page }) => { - // Click first event type - await page.click('[data-testid="event-type-link"]'); - await selectFirstAvailableTimeSlotNextMonth(page); - await bookTimeSlot(page); - - // Make sure we're navigated to the success page - await page.waitForNavigation({ - url(url) { - return url.pathname.endsWith("/success"); - }, - }); - }); - test("can reschedule a booking", async ({ page }) => { await bookFirstEvent(page); + }); + test("can reschedule a booking", async ({ page, users, bookings }) => { + const [pro] = users.get(); + const [eventType] = pro.eventTypes; + await bookings.create(pro.id, pro.username, eventType.id); + + await pro.login(); await page.goto("/bookings/upcoming"); - await page.locator('[data-testid="reschedule"]').click(); + await page.locator('[data-testid="reschedule"]').nth(0).click(); await page.locator('[data-testid="edit"]').click(); await page.waitForNavigation({ url: (url) => { @@ -126,9 +112,12 @@ test.describe("pro user", () => { }); }); - test("Can cancel the recently created booking and rebook the same timeslot", async ({ page }) => { + test("Can cancel the recently created booking and rebook the same timeslot", async ({ page, users }) => { await bookFirstEvent(page); + const [pro] = users.get(); + await pro.login(); + await page.goto("/bookings/upcoming"); await page.locator('[data-testid="cancel"]').first().click(); await page.waitForNavigation({ @@ -143,7 +132,7 @@ test.describe("pro user", () => { return url.pathname === "/cancel/success"; }, }); - await page.goto("/pro"); + await page.goto(`/${pro.username}`); await bookFirstEvent(page); }); }); diff --git a/apps/web/playwright/dynamic-booking-pages.test.ts b/apps/web/playwright/dynamic-booking-pages.test.ts index ec3a3fb037..b2990654f7 100644 --- a/apps/web/playwright/dynamic-booking-pages.test.ts +++ b/apps/web/playwright/dynamic-booking-pages.test.ts @@ -1,6 +1,6 @@ -import { Page, test } from "@playwright/test"; +import { expect } from "@playwright/test"; -import { deleteAllBookingsByEmail } from "./lib/teardown"; +import { test } from "./lib/fixtures"; import { bookFirstEvent, bookTimeSlot, @@ -8,19 +8,18 @@ import { selectSecondAvailableTimeSlotNextMonth, } from "./lib/testUtils"; -test.describe("dynamic booking", () => { - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); +test.describe.configure({ mode: "parallel" }); - test.beforeEach(async ({ page }) => { - await deleteAllBookingsByEmail("pro@example.com"); - await deleteAllBookingsByEmail("free@example.com"); - await page.goto("/pro+free"); +test.describe("dynamic booking", () => { + test.beforeEach(async ({ page, users }) => { + const pro = await users.create(); + await pro.login(); + const free = await users.create({ plan: "FREE" }); + await page.goto(`/${pro.username}+${free.username}`); }); - test.afterAll(async () => { - // delete test bookings - await deleteAllBookingsByEmail("pro@example.com"); - await deleteAllBookingsByEmail("free@example.com"); + test.afterEach(async ({ page, users }) => { + await users.deleteAll(); }); test("book an event first day in next month", async ({ page }) => { @@ -28,13 +27,7 @@ test.describe("dynamic booking", () => { await page.click('[data-testid="event-type-link"]'); await selectFirstAvailableTimeSlotNextMonth(page); await bookTimeSlot(page); - - // Make sure we're navigated to the success page - await page.waitForNavigation({ - url(url) { - return url.pathname.endsWith("/success"); - }, - }); + await expect(page.locator("[data-testid=success-page]")).toBeVisible(); }); test("can reschedule a booking", async ({ page }) => { @@ -42,7 +35,7 @@ test.describe("dynamic booking", () => { // Logged in await page.goto("/bookings/upcoming"); - await page.locator('[data-testid="reschedule"]').click(); + await page.locator('[data-testid="reschedule"]').nth(0).click(); await page.locator('[data-testid="edit"]').click(); await page.waitForNavigation({ url: (url) => { @@ -58,6 +51,7 @@ test.describe("dynamic booking", () => { return url.pathname === "/success" && url.searchParams.get("reschedule") === "true"; }, }); + await expect(page.locator("[data-testid=success-page]")).toBeVisible(); }); test("Can cancel the recently created booking", async ({ page }) => { diff --git a/apps/web/playwright/embed-code-generator.test.ts b/apps/web/playwright/embed-code-generator.test.ts index 8f8e442c49..57ed102576 100644 --- a/apps/web/playwright/embed-code-generator.test.ts +++ b/apps/web/playwright/embed-code-generator.test.ts @@ -5,6 +5,8 @@ function chooseEmbedType(page: Page, embedType: string) { } async function gotToPreviewTab(page: Page) { + // To prevent early timeouts + await page.waitForTimeout(1000); await page.locator("[data-testid=embed-tabs]").locator("text=Preview").click(); } @@ -79,6 +81,8 @@ async function expectToContainValidPreviewIframe( ); } +test.describe.configure({ mode: "parallel" }); + test.describe("Embed Code Generator Tests", () => { test.use({ storageState: "playwright/artifacts/proStorageState.json" }); @@ -177,7 +181,7 @@ test.describe("Embed Code Generator Tests", () => { embedType: "inline", }); - gotToPreviewTab(page); + await gotToPreviewTab(page); await expectToContainValidPreviewIframe(page, { embedType: "inline", diff --git a/apps/web/playwright/event-types.test.ts b/apps/web/playwright/event-types.test.ts index ff52af67bc..7a4fade9c1 100644 --- a/apps/web/playwright/event-types.test.ts +++ b/apps/web/playwright/event-types.test.ts @@ -3,6 +3,8 @@ import { expect, Locator, test } from "@playwright/test"; import { randomString } from "../lib/random"; import { deleteEventTypeByTitle } from "./lib/teardown"; +test.describe.configure({ mode: "parallel" }); + test.describe("Event Types tests", () => { test.beforeEach(async ({ page }) => { await page.goto("/event-types"); diff --git a/apps/web/playwright/fixtures/bookings.ts b/apps/web/playwright/fixtures/bookings.ts new file mode 100644 index 0000000000..1439962f51 --- /dev/null +++ b/apps/web/playwright/fixtures/bookings.ts @@ -0,0 +1,88 @@ +import type { Page } from "@playwright/test"; +import type { Booking, Prisma } from "@prisma/client"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import short from "short-uuid"; +import { v5 as uuidv5, v4 as uuidv4 } from "uuid"; + +import { prisma } from "@calcom/prisma"; + +dayjs.extend(utc); + +const translator = short(); + +type BookingFixture = ReturnType; + +// creates a user fixture instance and stores the collection +export const createBookingsFixture = (page: Page) => { + let store = { bookings: [], page } as { bookings: BookingFixture[]; page: typeof page }; + return { + create: async ( + userId: number, + username: string | null, + eventTypeId = -1, + { + confirmed = true, + rescheduled = false, + paid = false, + status = "ACCEPTED", + }: Partial = {} + ) => { + const startDate = dayjs().add(1, "day").toDate(); + const seed = `${username}:${dayjs(startDate).utc().format()}:${new Date().getTime()}`; + const uid = translator.fromUUID(uuidv5(seed, uuidv5.URL)); + const booking = await prisma.booking.create({ + data: { + uid: uid, + title: "30min", + startTime: startDate, + endTime: dayjs().add(1, "day").add(30, "minutes").toDate(), + user: { + connect: { + id: userId, + }, + }, + attendees: { + create: { + email: "attendee@example.com", + name: "Attendee Example", + timeZone: "Europe/London", + }, + }, + eventType: { + connect: { + id: eventTypeId, + }, + }, + confirmed, + rescheduled, + paid, + status, + }, + }); + const bookingFixture = createBookingFixture(booking, store.page!); + store.bookings.push(bookingFixture); + return bookingFixture; + }, + get: () => store.bookings, + delete: async (id: number) => { + await prisma.booking.delete({ + where: { id }, + }); + store.bookings = store.bookings.filter((b) => b.id !== id); + }, + }; +}; + +// creates the single user fixture +const createBookingFixture = (booking: Booking, page: Page) => { + const store = { booking, page }; + + // self is a reflective method that return the Prisma object that references this fixture. + return { + id: store.booking.id, + uid: store.booking.uid, + self: async () => (await prisma.booking.findUnique({ where: { id: store.booking.id } }))!, + delete: async () => (await prisma.booking.delete({ where: { id: store.booking.id } }))!, + }; +}; diff --git a/apps/web/playwright/fixtures/payments.ts b/apps/web/playwright/fixtures/payments.ts new file mode 100644 index 0000000000..15fcaaeeb3 --- /dev/null +++ b/apps/web/playwright/fixtures/payments.ts @@ -0,0 +1,59 @@ +import type { Page } from "@playwright/test"; +import type { Payment } from "@prisma/client"; +import { v4 as uuidv4 } from "uuid"; + +import { prisma } from "@calcom/prisma"; + +type PaymentFixture = ReturnType; + +// creates a user fixture instance and stores the collection +export const createPaymentsFixture = (page: Page) => { + let store = { payments: [], page } as { payments: PaymentFixture[]; page: typeof page }; + return { + create: async ( + bookingId: number, + { success = false, refunded = false }: { success?: boolean; refunded?: boolean } = {} + ) => { + const payment = await prisma.payment.create({ + data: { + uid: uuidv4(), + amount: 20000, + fee: 160, + currency: "usd", + success, + refunded, + type: "STRIPE", + data: {}, + externalId: "DEMO_PAYMENT_FROM_DB_" + Date.now(), + booking: { + connect: { + id: bookingId, + }, + }, + }, + }); + const paymentFixture = createPaymentFixture(payment, store.page!); + store.payments.push(paymentFixture); + return paymentFixture; + }, + get: () => store.payments, + delete: async (id: number) => { + await prisma.payment.delete({ + where: { id }, + }); + store.payments = store.payments.filter((b) => b.id !== id); + }, + }; +}; + +// creates the single user fixture +const createPaymentFixture = (payment: Payment, page: Page) => { + const store = { payment, page }; + + // self is a reflective method that return the Prisma object that references this fixture. + return { + id: store.payment.id, + self: async () => (await prisma.payment.findUnique({ where: { id: store.payment.id } }))!, + delete: async () => (await prisma.payment.delete({ where: { id: store.payment.id } }))!, + }; +}; diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts index 796d101632..e3994d41af 100644 --- a/apps/web/playwright/fixtures/users.ts +++ b/apps/web/playwright/fixtures/users.ts @@ -1,6 +1,6 @@ -import type { Page } from "@playwright/test"; -import { UserPlan } from "@prisma/client"; +import type { Page, WorkerInfo } from "@playwright/test"; import type Prisma from "@prisma/client"; +import { Prisma as PrismaType, UserPlan } from "@prisma/client"; import { hashPassword } from "@calcom/lib/auth"; import { prisma } from "@calcom/prisma"; @@ -9,13 +9,42 @@ import { TimeZoneEnum } from "./types"; type UserFixture = ReturnType; +const userIncludes = PrismaType.validator()({ + eventTypes: true, + credentials: true, +}); + +const userWithEventTypes = PrismaType.validator()({ + include: userIncludes, +}); + +type UserWithIncludes = PrismaType.UserGetPayload; + // creates a user fixture instance and stores the collection -export const createUsersFixture = (page: Page) => { +export const createUsersFixture = (page: Page, workerInfo: WorkerInfo) => { let store = { users: [], page } as { users: UserFixture[]; page: typeof page }; return { create: async (opts?: CustomUserOpts) => { - const user = await prisma.user.create({ - data: await createUser(opts), + const _user = await prisma.user.create({ + data: await createUser(workerInfo, opts), + }); + await prisma.eventType.create({ + data: { + users: { + connect: { + id: _user.id, + }, + }, + title: "Paid", + slug: "paid", + length: 30, + price: 1000, + }, + }); + const user = await prisma.user.findUnique({ + rejectOnNotFound: true, + where: { id: _user.id }, + include: userIncludes, }); const userFixture = createUserFixture(user, store.page!); store.users.push(userFixture); @@ -25,25 +54,39 @@ export const createUsersFixture = (page: Page) => { logout: async () => { await page.goto("/auth/logout"); }, + deleteAll: async () => { + const ids = store.users.map((u) => u.id); + await prisma.user.deleteMany({ where: { id: { in: ids } } }); + store.users = []; + }, + delete: async (id: number) => { + await prisma.user.delete({ where: { id } }); + store.users = store.users.filter((b) => b.id !== id); + }, }; }; type JSONValue = string | number | boolean | { [x: string]: JSONValue } | Array; // creates the single user fixture -const createUserFixture = (user: Prisma.User, page: Page) => { +const createUserFixture = (user: UserWithIncludes, page: Page) => { const store = { user, page }; // self is a reflective method that return the Prisma object that references this fixture. - const self = async () => (await prisma.user.findUnique({ where: { id: store.user.id } }))!; + const self = async () => + (await prisma.user.findUnique({ where: { id: store.user.id }, include: { eventTypes: true } }))!; return { id: user.id, + username: user.username, + eventTypes: user.eventTypes!, self, login: async () => login({ ...(await self()), password: user.username }, store.page), + getPaymentCredential: async () => getPaymentCredential(store.page), // ths is for developemnt only aimed to inject debugging messages in the metadata field of the user debug: async (message: string | Record) => { await prisma.user.update({ where: { id: store.user.id }, data: { metadata: { debug: message } } }); }, + delete: async () => (await prisma.user.delete({ where: { id: store.user.id } }))!, }; }; @@ -51,10 +94,14 @@ type CustomUserOptsKeys = "username" | "password" | "plan" | "completedOnboardin type CustomUserOpts = Partial> & { timeZone?: TimeZoneEnum }; // creates the actual user in the db. -const createUser = async (opts?: CustomUserOpts) => { +const createUser = async ( + workerInfo: WorkerInfo, + opts?: CustomUserOpts +): Promise => { // build a unique name for our user - const uname = - (opts?.username ?? opts?.plan?.toLocaleLowerCase() ?? UserPlan.PRO.toLowerCase()) + "-" + Date.now(); + const uname = `${opts?.username ?? opts?.plan?.toLocaleLowerCase() ?? UserPlan.PRO.toLowerCase()}-${ + workerInfo.workerIndex + }-${Date.now()}`; return { username: uname, name: (opts?.username ?? opts?.plan ?? UserPlan.PRO).toUpperCase(), @@ -65,6 +112,13 @@ const createUser = async (opts?: CustomUserOpts) => { completedOnboarding: opts?.completedOnboarding ?? true, timeZone: opts?.timeZone ?? TimeZoneEnum.UK, locale: opts?.locale ?? "en", + eventTypes: { + create: { + title: "30 min", + slug: "30-min", + length: 30, + }, + }, }; }; @@ -74,10 +128,10 @@ export async function login( page: Page ) { // get locators - const loginLocator = await page.locator("[data-testid=login-form]"); - const emailLocator = await loginLocator.locator("#email"); - const passwordLocator = await loginLocator.locator("#password"); - const signInLocator = await loginLocator.locator('[type="submit"]'); + const loginLocator = page.locator("[data-testid=login-form]"); + const emailLocator = loginLocator.locator("#email"); + const passwordLocator = loginLocator.locator("#password"); + const signInLocator = loginLocator.locator('[type="submit"]'); //login await page.goto("/"); @@ -88,3 +142,19 @@ export async function login( // 2 seconds of delay to give the session enough time for a clean load await page.waitForTimeout(2000); } + +export async function getPaymentCredential(page: Page) { + await page.goto("/apps/installed"); + + /** We start the Stripe flow */ + await Promise.all([ + page.waitForNavigation({ url: "https://connect.stripe.com/oauth/v2/authorize?*" }), + page.click('li:has-text("Stripe") >> [data-testid="integration-connection-button"]'), + ]); + + await Promise.all([ + page.waitForNavigation({ url: "/apps/installed" }), + /** We skip filling Stripe forms (testing mode only) */ + page.click('[id="skip-account-app"]'), + ]); +} diff --git a/apps/web/playwright/hash-my-url.test.ts b/apps/web/playwright/hash-my-url.test.ts index a66d41c902..4cdfcf3b90 100644 --- a/apps/web/playwright/hash-my-url.test.ts +++ b/apps/web/playwright/hash-my-url.test.ts @@ -1,23 +1,18 @@ -import { expect, test } from "@playwright/test"; +import { expect } from "@playwright/test"; -import { deleteAllBookingsByEmail } from "./lib/teardown"; +import { test } from "./lib/fixtures"; import { bookTimeSlot, selectFirstAvailableTimeSlotNextMonth } from "./lib/testUtils"; +test.describe.configure({ mode: "parallel" }); + test.describe("hash my url", () => { - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); - let $url = ""; - test.beforeEach(async ({ page }) => { - await deleteAllBookingsByEmail("pro@example.com"); - await page.goto("/event-types"); - // We wait until loading is finished - await page.waitForSelector('[data-testid="event-types"]'); + test.beforeEach(async ({ users }) => { + const user = await users.create(); + await user.login(); }); - - test.afterAll(async () => { - // delete test bookings - await deleteAllBookingsByEmail("pro@example.com"); + test.afterEach(async ({ users }) => { + await users.deleteAll(); }); - test("generate url hash", async ({ page }) => { // await page.pause(); await page.goto("/event-types"); @@ -34,26 +29,22 @@ test.describe("hash my url", () => { !isChecked && (await page.click('//*[@id="hashedLinkCheck"]')); // we wait for the hashedLink setting to load await page.waitForSelector('//*[@data-testid="generated-hash-url"]'); - $url = await page.locator('//*[@data-testid="generated-hash-url"]').inputValue(); + const $url = await page.locator('//*[@data-testid="generated-hash-url"]').inputValue(); // click update await page.focus('//button[@type="submit"]'); await page.keyboard.press("Enter"); - }); - test("book using generated url hash", async ({ page }) => { + // To prevent an early 404 + await page.waitForTimeout(1000); + + // book using generated url hash await page.goto($url); await selectFirstAvailableTimeSlotNextMonth(page); await bookTimeSlot(page); - // Make sure we're navigated to the success page - await page.waitForNavigation({ - url(url) { - return url.pathname.endsWith("/success"); - }, - }); - }); + await expect(page.locator("[data-testid=success-page]")).toBeVisible(); - test("hash regenerates after successful booking", async ({ page }) => { + // hash regenerates after successful booking await page.goto("/event-types"); // We wait until loading is finished await page.waitForSelector('[data-testid="event-types"]'); diff --git a/apps/web/playwright/integrations-stripe.test.ts b/apps/web/playwright/integrations-stripe.test.ts index f642659150..306a895074 100644 --- a/apps/web/playwright/integrations-stripe.test.ts +++ b/apps/web/playwright/integrations-stripe.test.ts @@ -1,53 +1,45 @@ -import { expect, test } from "@playwright/test"; +import { expect } from "@playwright/test"; -import * as teardown from "./lib/teardown"; +import { test } from "./lib/fixtures"; import { selectFirstAvailableTimeSlotNextMonth, todo } from "./lib/testUtils"; +test.describe.configure({ mode: "parallel" }); + const IS_STRIPE_ENABLED = !!( process.env.STRIPE_CLIENT_ID && process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY && process.env.STRIPE_PRIVATE_KEY ); -test.describe.serial("Stripe integration", () => { - test.afterAll(() => { - teardown.deleteAllPaymentsByEmail("pro@example.com"); - teardown.deleteAllBookingsByEmail("pro@example.com"); - teardown.deleteAllPaymentCredentialsByEmail("pro@example.com"); - }); +test.describe("Stripe integration", () => { test.skip(!IS_STRIPE_ENABLED, "It should only run if Stripe is installed"); - test.describe.serial("Stripe integration dashboard", () => { - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); - - test("Can add Stripe integration", async ({ page }) => { + test.describe("Stripe integration dashboard", () => { + test("Can add Stripe integration", async ({ page, users }) => { + const user = await users.create(); + await user.login(); await page.goto("/apps/installed"); - /** We should see the "Connect" button for Stripe */ - await expect( - page.locator(`li:has-text("Stripe") >> [data-testid="integration-connection-button"]`) - ).toContainText("Connect"); - /** We start the Stripe flow */ - await Promise.all([ - page.waitForNavigation({ url: "https://connect.stripe.com/oauth/v2/authorize?*" }), - page.click('li:has-text("Stripe") >> [data-testid="integration-connection-button"]'), - ]); - - await Promise.all([ - page.waitForNavigation({ url: "/apps/installed" }), - /** We skip filling Stripe forms (testing mode only) */ - page.click('[id="skip-account-app"]'), - ]); + await user.getPaymentCredential(); /** If Stripe is added correctly we should see the "Disconnect" button */ await expect( page.locator(`li:has-text("Stripe") >> [data-testid="integration-connection-button"]`) ).toContainText("Disconnect"); + + // Cleanup + await user.delete(); }); }); - test("Can book a paid booking", async ({ page }) => { - await page.goto("/pro/paid"); + test("Can book a paid booking", async ({ page, users }) => { + const user = await users.create(); + const eventType = user.eventTypes.find((e) => e.slug === "paid")!; + await user.login(); + await page.goto("/apps/installed"); + await user.getPaymentCredential(); + + await page.goto(`${user.username}/${eventType.slug}`); await selectFirstAvailableTimeSlotNextMonth(page); // --- fill form await page.fill('[name="name"]', "Stripe Stripeson"); @@ -71,11 +63,10 @@ test.describe.serial("Stripe integration", () => { await page.click('button:has-text("Pay now")'); // Make sure we're navigated to the success page - await page.waitForNavigation({ - url(url) { - return url.pathname.endsWith("/success"); - }, - }); + await expect(page.locator("[data-testid=success-page]")).toBeVisible(); + + // Cleanup + await user.delete(); }); todo("Pending payment booking should not be confirmed by default"); diff --git a/apps/web/playwright/integrations.test.ts b/apps/web/playwright/integrations.test.ts index cf3d2d6d4c..3bee7451de 100644 --- a/apps/web/playwright/integrations.test.ts +++ b/apps/web/playwright/integrations.test.ts @@ -1,20 +1,11 @@ -import { expect, test } from "@playwright/test"; +import { expect } from "@playwright/test"; -import * as teardown from "./lib/teardown"; +import { test } from "./lib/fixtures"; import { createHttpServer, selectFirstAvailableTimeSlotNextMonth, todo, waitFor } from "./lib/testUtils"; -test.describe("integrations", () => { - //teardown - test.afterAll(async () => { - await teardown.deleteAllWebhooksByEmail("pro@example.com"); - await teardown.deleteAllBookingsByEmail("pro@example.com"); - }); - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); - - test.beforeEach(async ({ page }) => { - await page.goto("/apps/installed"); - }); +test.describe.configure({ mode: "parallel" }); +test.describe("Integrations", () => { todo("Can add Zoom integration"); todo("Can add Google Calendar"); @@ -25,8 +16,15 @@ test.describe("integrations", () => { todo("Can add Apple Calendar"); - test("add webhook & test that creating an event triggers a webhook call", async ({ page }, testInfo) => { + test("add webhook & test that creating an event triggers a webhook call", async ({ + page, + users, + }, testInfo) => { const webhookReceiver = createHttpServer(); + const user = await users.create(); + const [eventType] = user.eventTypes; + await user.login(); + await page.goto("/apps/installed"); // --- add webhook await page.click('[data-testid="new_webhook"]'); @@ -43,7 +41,7 @@ test.describe("integrations", () => { expect(page.locator(`text='${webhookReceiver.url}'`)).toBeDefined(); // --- Book the first available day next month in the pro user's "30min"-event - await page.goto(`/pro/30min`); + await page.goto(`/${user.username}/${eventType.slug}`); await selectFirstAvailableTimeSlotNextMonth(page); // --- fill form @@ -69,6 +67,7 @@ test.describe("integrations", () => { attendee.timeZone = dynamic; attendee.language = dynamic; } + body.payload.organizer.email = dynamic; body.payload.organizer.timeZone = dynamic; body.payload.organizer.language = dynamic; body.payload.uid = dynamic; diff --git a/apps/web/playwright/integrations.test.ts-snapshots/webhookResponse-chromium.txt b/apps/web/playwright/integrations.test.ts-snapshots/webhookResponse-chromium.txt index 07fe93f9cc..a1bc2d49b5 100644 --- a/apps/web/playwright/integrations.test.ts-snapshots/webhookResponse-chromium.txt +++ b/apps/web/playwright/integrations.test.ts-snapshots/webhookResponse-chromium.txt @@ -1 +1 @@ -{"triggerEvent":"BOOKING_CREATED","createdAt":"[redacted/dynamic]","payload":{"type":"30min","title":"30min between Pro Example and Test Testson","description":"","additionalNotes":"","startTime":"[redacted/dynamic]","endTime":"[redacted/dynamic]","organizer":{"name":"Pro Example","email":"pro@example.com","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"},"attendees":[{"email":"test@example.com","name":"Test Testson","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"}],"location":"[redacted/dynamic]","destinationCalendar":null,"hideCalendarNotes":false,"uid":"[redacted/dynamic]","metadata":{},"additionInformation":"[redacted/dynamic]"}} \ No newline at end of file +{"triggerEvent":"BOOKING_CREATED","createdAt":"[redacted/dynamic]","payload":{"type":"30 min","title":"30 min between PRO and Test Testson","description":"","additionalNotes":"","startTime":"[redacted/dynamic]","endTime":"[redacted/dynamic]","organizer":{"name":"PRO","email":"[redacted/dynamic]","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"},"attendees":[{"email":"test@example.com","name":"Test Testson","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"}],"location":"[redacted/dynamic]","destinationCalendar":null,"hideCalendarNotes":false,"uid":"[redacted/dynamic]","metadata":{},"additionInformation":"[redacted/dynamic]"}} \ No newline at end of file diff --git a/apps/web/playwright/lib/dbSetup.ts b/apps/web/playwright/lib/dbSetup.ts deleted file mode 100644 index 8837f7855e..0000000000 --- a/apps/web/playwright/lib/dbSetup.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Booking } from "@prisma/client"; -import dayjs from "dayjs"; -import utc from "dayjs/plugin/utc"; -import short from "short-uuid"; -import { v5 as uuidv5, v4 as uuidv4 } from "uuid"; - -dayjs.extend(utc); - -const translator = short(); - -const TestUtilCreateBookingOnUserId = async ( - userId: number, - username: string, - eventTypeId: number, - { confirmed = true, rescheduled = false, paid = false, status = "ACCEPTED" }: Partial -) => { - const startDate = dayjs().add(1, "day").toDate(); - const seed = `${username}:${dayjs(startDate).utc().format()}:${new Date().getTime()}`; - const uid = translator.fromUUID(uuidv5(seed, uuidv5.URL)); - return await prisma?.booking.create({ - data: { - uid: uid, - title: "30min", - startTime: startDate, - endTime: dayjs().add(1, "day").add(30, "minutes").toDate(), - user: { - connect: { - id: userId, - }, - }, - attendees: { - create: { - email: "attendee@example.com", - name: "Attendee Example", - timeZone: "Europe/London", - }, - }, - eventType: { - connect: { - id: eventTypeId, - }, - }, - confirmed, - rescheduled, - paid, - status, - }, - select: { - id: true, - uid: true, - user: true, - }, - }); -}; - -const TestUtilCreatePayment = async ( - bookingId: number, - { success = false, refunded = false }: { success?: boolean; refunded?: boolean } -) => { - return await prisma?.payment.create({ - data: { - uid: uuidv4(), - amount: 20000, - fee: 160, - currency: "usd", - success, - refunded, - type: "STRIPE", - data: {}, - externalId: "DEMO_PAYMENT_FROM_DB", - booking: { - connect: { - id: bookingId, - }, - }, - }, - }); -}; - -export { TestUtilCreateBookingOnUserId, TestUtilCreatePayment }; diff --git a/apps/web/playwright/lib/fixtures.ts b/apps/web/playwright/lib/fixtures.ts index 615347a97c..0287e1c67a 100644 --- a/apps/web/playwright/lib/fixtures.ts +++ b/apps/web/playwright/lib/fixtures.ts @@ -1,14 +1,29 @@ import { test as base } from "@playwright/test"; +import { createBookingsFixture } from "../fixtures/bookings"; +import { createPaymentsFixture } from "../fixtures/payments"; import { createUsersFixture } from "../fixtures/users"; interface Fixtures { users: ReturnType; + bookings: ReturnType; + payments: ReturnType; } +/** + * @see https://playwright.dev/docs/test-fixtures + */ export const test = base.extend({ - users: async ({ page }, use) => { - const usersFixture = createUsersFixture(page); + users: async ({ page }, use, workerInfo) => { + const usersFixture = createUsersFixture(page, workerInfo); await use(usersFixture); }, + bookings: async ({ page }, use) => { + const bookingsFixture = createBookingsFixture(page); + await use(bookingsFixture); + }, + payments: async ({ page }, use) => { + const payemntsFixture = createPaymentsFixture(page); + await use(payemntsFixture); + }, }); diff --git a/apps/web/playwright/lib/teardown.ts b/apps/web/playwright/lib/teardown.ts index 18e6317267..cb6010c47e 100644 --- a/apps/web/playwright/lib/teardown.ts +++ b/apps/web/playwright/lib/teardown.ts @@ -2,6 +2,14 @@ import { Prisma } from "@prisma/client"; import prisma from "@lib/prisma"; +/** + * @deprecated + * DO NOT USE, since test run in parallel this will cause flaky tests. The reason + * being that a set of test may end earlier than other trigger a delete of all bookings + * than other tests may depend on them. The proper ettiquete should be that EACH test + * should cleanup ONLY the booking that we're created in that specific test to se DB + * remains "pristine" after each test + */ export const deleteAllBookingsByEmail = async ( email: string, whereConditional: Prisma.BookingWhereInput = {} diff --git a/apps/web/playwright/lib/testUtils.ts b/apps/web/playwright/lib/testUtils.ts index 88d258170a..136b40b73c 100644 --- a/apps/web/playwright/lib/testUtils.ts +++ b/apps/web/playwright/lib/testUtils.ts @@ -1,4 +1,4 @@ -import { Page, test } from "@playwright/test"; +import { expect, Page, test } from "@playwright/test"; import { createServer, IncomingMessage, ServerResponse } from "http"; export function todo(title: string) { @@ -75,7 +75,7 @@ export async function selectFirstAvailableTimeSlotNextMonth(page: Page) { await page.waitForTimeout(1000); // TODO: Find out why the first day is always booked on tests await page.locator('[data-testid="day"][data-disabled="false"]').nth(1).click(); - await page.click('[data-testid="time"]'); + await page.locator('[data-testid="time"]').nth(0).click(); } export async function selectSecondAvailableTimeSlotNextMonth(page: Page) { @@ -93,10 +93,7 @@ export async function bookFirstEvent(page: Page) { // Click first event type await page.click('[data-testid="event-type-link"]'); await selectFirstAvailableTimeSlotNextMonth(page); - // --- fill form - await page.fill('[name="name"]', "Test Testson"); - await page.fill('[name="email"]', "test@example.com"); - await page.press('[name="email"]', "Enter"); + await bookTimeSlot(page); // Make sure we're navigated to the success page await page.waitForNavigation({ @@ -104,6 +101,7 @@ export async function bookFirstEvent(page: Page) { return url.pathname.endsWith("/success"); }, }); + await expect(page.locator("[data-testid=success-page]")).toBeVisible(); } export const bookTimeSlot = async (page: Page) => { diff --git a/apps/web/playwright/login.test.ts b/apps/web/playwright/login.test.ts index 8674b9c05b..d8c00ef54c 100644 --- a/apps/web/playwright/login.test.ts +++ b/apps/web/playwright/login.test.ts @@ -1,22 +1,25 @@ import { expect } from "@playwright/test"; import { UserPlan } from "@prisma/client"; -import { ErrorCode } from "@lib/auth"; - import { login } from "./fixtures/users"; import { test } from "./lib/fixtures"; import { localize } from "./lib/testUtils"; +test.describe.configure({ mode: "parallel" }); + test.describe("Login and logout tests", () => { + test.afterEach(async ({ users }) => { + await users.deleteAll(); + }); // Test login with all plans const plans = [UserPlan.PRO, UserPlan.FREE, UserPlan.TRIAL]; plans.forEach((plan) => { test(`Should login with a ${plan} account`, async ({ page, users }) => { // Create user and login - const pro = await users.create({ plan }); - await pro.login(); + const user = await users.create({ plan }); + await user.login(); - const shellLocator = await page.locator(`[data-testid=dashboard-shell]`); + const shellLocator = page.locator(`[data-testid=dashboard-shell]`); // expects the home page for an authorized user await page.goto("/"); @@ -25,7 +28,7 @@ test.describe("Login and logout tests", () => { // Asserts to read the tested plan const planLocator = shellLocator.locator(`[data-testid=plan-${plan.toLowerCase()}]`); await expect(planLocator).toBeVisible(); - await await expect(planLocator).toHaveText; + await expect(planLocator).toHaveText(`-${plan}`); // When TRIAL check if the TRIAL banner is visible if (plan === UserPlan.TRIAL) { @@ -34,7 +37,7 @@ test.describe("Login and logout tests", () => { }); }); - test("Should warn when user does not exist", async ({ page, users }) => { + test("Should warn when user does not exist", async ({ page }) => { const alertMessage = (await localize("en"))("no_account_exists"); // Login with a non-existent user @@ -51,7 +54,7 @@ test.describe("Login and logout tests", () => { const pro = await users.create({ username: "pro" }); // login with a wrong password - await login({ username: "pro", password: "wrong" }, page); + await login({ username: pro.username, password: "wrong" }, page); // assert for the visibility of the localized alert message await expect(page.locator(`text=${alertMessage}`)).toBeVisible(); @@ -59,8 +62,7 @@ test.describe("Login and logout tests", () => { test("Should logout", async ({ page, users }) => { const signOutLabel = (await localize("en"))("sign_out"); - const userDropdownDisclose = async () => - (await page.locator("[data-testid=user-dropdown-trigger]")).click(); + const userDropdownDisclose = async () => page.locator("[data-testid=user-dropdown-trigger]").click(); // creates a user and login const pro = await users.create(); @@ -68,7 +70,7 @@ test.describe("Login and logout tests", () => { // disclose and click the sign out button from the user dropdown await userDropdownDisclose(); - const signOutBtn = await page.locator(`text=${signOutLabel}`); + const signOutBtn = page.locator(`text=${signOutLabel}`); await signOutBtn.click(); // 2s of delay to assure the session is cleared diff --git a/apps/web/playwright/onboarding.test.ts b/apps/web/playwright/onboarding.test.ts index a181edc854..d994cd5344 100644 --- a/apps/web/playwright/onboarding.test.ts +++ b/apps/web/playwright/onboarding.test.ts @@ -30,6 +30,11 @@ test.describe("Onboarding", () => { test.describe("Onboarding", () => { test("update onboarding username via localstorage", async ({ page }) => { + /** + * We need to come up with a better test since all test are run in an incognito window. + * Meaning that all localstorage access is null here. + */ + test.fixme(); await page.addInitScript(() => { window.localStorage.setItem("username", "alwaysavailable"); }, {}); diff --git a/apps/web/playwright/reschedule.test.ts b/apps/web/playwright/reschedule.test.ts index c8be2197ac..1b030d256e 100644 --- a/apps/web/playwright/reschedule.test.ts +++ b/apps/web/playwright/reschedule.test.ts @@ -1,9 +1,9 @@ -import { expect, test } from "@playwright/test"; +import { expect } from "@playwright/test"; import { BookingStatus } from "@prisma/client"; -import dayjs from "dayjs"; -import { TestUtilCreateBookingOnUserId, TestUtilCreatePayment } from "./lib/dbSetup"; -import { deleteAllBookingsByEmail } from "./lib/teardown"; +import prisma from "@lib/prisma"; + +import { test } from "./lib/fixtures"; import { selectFirstAvailableTimeSlotNextMonth } from "./lib/testUtils"; const IS_STRIPE_ENABLED = !!( @@ -11,236 +11,146 @@ const IS_STRIPE_ENABLED = !!( process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY && process.env.STRIPE_PRIVATE_KEY ); -const findUserByEmail = async (email: string) => { - return await prisma?.user.findFirst({ - select: { - id: true, - email: true, - username: true, - credentials: true, - }, - where: { - email, - }, - }); -}; + +test.describe.configure({ mode: "parallel" }); + test.describe("Reschedule Tests", async () => { - let currentUser: Awaited>; - // Using logged in state from globalSetup - test.use({ storageState: "playwright/artifacts/proStorageState.json" }); - test.beforeAll(async () => { - currentUser = await findUserByEmail("pro@example.com"); - }); - test.afterEach(async () => { - try { - await deleteAllBookingsByEmail("pro@example.com", { - createdAt: { gte: dayjs().startOf("day").toISOString() }, - }); - } catch (error) { - console.log("Error while trying to delete all bookings from pro user"); - } + test.afterEach(async ({ users }) => { + await users.deleteAll(); }); + test("Should do a booking request reschedule from /bookings", async ({ page, users, bookings }) => { + const user = await users.create(); - test("Should do a booking request reschedule from /bookings", async ({ page }) => { - const user = currentUser; - const eventType = await prisma?.eventType.findFirst({ - where: { - userId: user?.id, - slug: "30min", - }, + const booking = await bookings.create(user.id, user.username, user.eventTypes[0].id!, { + status: BookingStatus.ACCEPTED, }); - let originalBooking; - if (user && user.id && user.username && eventType) { - originalBooking = await TestUtilCreateBookingOnUserId(user?.id, user?.username, eventType?.id, { - status: BookingStatus.ACCEPTED, - }); - } + await user.login(); await page.goto("/bookings/upcoming"); - await page.locator('[data-testid="reschedule"]').click(); + await page.locator('[data-testid="reschedule"]').nth(0).click(); await page.locator('[data-testid="reschedule_request"]').click(); await page.fill('[data-testid="reschedule_reason"]', "I can't longer have it"); await page.locator('button[data-testid="send_request"]').click(); + await expect(page.locator('[id="modal-title"]')).not.toBeVisible(); - await page.goto("/bookings/cancelled"); + const updatedBooking = await booking.self(); - // Find booking that was recently cancelled - const booking = await prisma?.booking.findFirst({ - select: { - id: true, - uid: true, - cancellationReason: true, - status: true, - rescheduled: true, - }, - where: { id: originalBooking?.id }, - }); - - expect(booking?.rescheduled).toBe(true); - expect(booking?.cancellationReason).toBe("I can't longer have it"); - expect(booking?.status).toBe(BookingStatus.CANCELLED); + expect(updatedBooking.rescheduled).toBe(true); + expect(updatedBooking.cancellationReason).toBe("I can't longer have it"); + expect(updatedBooking.status).toBe(BookingStatus.CANCELLED); + await booking.delete(); }); - test("Should display former time when rescheduling availability", async ({ page }) => { - const user = currentUser; - const eventType = await prisma?.eventType.findFirst({ - where: { - userId: user?.id, - slug: "30min", - }, + test("Should display former time when rescheduling availability", async ({ page, users, bookings }) => { + const user = await users.create(); + const booking = await bookings.create(user.id, user.username, user.eventTypes[0].id!, { + status: BookingStatus.CANCELLED, + rescheduled: true, }); - let originalBooking; - if (user && user.id && user.username && eventType) { - originalBooking = await TestUtilCreateBookingOnUserId(user?.id, user?.username, eventType?.id, { - status: BookingStatus.CANCELLED, - rescheduled: true, - }); - } - await page.goto( - `/${originalBooking?.user?.username}/${eventType?.slug}?rescheduleUid=${originalBooking?.uid}` - ); - const formerTimeElement = await page.locator('[data-testid="former_time_p_desktop"]'); + await page.goto(`/${user.username}/${user.eventTypes[0].slug}?rescheduleUid=${booking.uid}`); + const formerTimeElement = page.locator('[data-testid="former_time_p_desktop"]'); await expect(formerTimeElement).toBeVisible(); + await booking.delete(); }); - test("Should display request reschedule send on bookings/cancelled", async ({ page }) => { - const user = currentUser; - const eventType = await prisma?.eventType.findFirst({ - where: { - userId: user?.id, - slug: "30min", - }, + test("Should display request reschedule send on bookings/cancelled", async ({ page, users, bookings }) => { + const user = await users.create(); + const booking = await bookings.create(user.id, user.username, user.eventTypes[0].id, { + status: BookingStatus.CANCELLED, + rescheduled: true, }); - if (user && user.id && user.username && eventType) { - await TestUtilCreateBookingOnUserId(user?.id, user?.username, eventType?.id, { - status: BookingStatus.CANCELLED, - rescheduled: true, - }); - } + await user.login(); await page.goto("/bookings/cancelled"); - const requestRescheduleSentElement = await page.locator('[data-testid="request_reschedule_sent"]').nth(1); + const requestRescheduleSentElement = page.locator('[data-testid="request_reschedule_sent"]').nth(1); await expect(requestRescheduleSentElement).toBeVisible(); + await booking.delete(); }); - test("Should do a reschedule from user owner", async ({ page }) => { - const user = currentUser; + test("Should do a reschedule from user owner", async ({ page, users, bookings }) => { + const user = await users.create(); + const [eventType] = user.eventTypes; + const booking = await bookings.create(user.id, user.username, eventType.id, { + status: BookingStatus.CANCELLED, + rescheduled: true, + }); - const eventType = await prisma?.eventType.findFirst({ - where: { - userId: user?.id, + await page.goto(`/${user.username}/${eventType.slug}?rescheduleUid=${booking.uid}`); + + await selectFirstAvailableTimeSlotNextMonth(page); + + await expect(page.locator('[name="name"]')).toBeDisabled(); + await expect(page.locator('[name="email"]')).toBeDisabled(); + await expect(page.locator('[name="notes"]')).toBeDisabled(); + + await page.locator('[data-testid="confirm-reschedule-button"]').click(); + + await expect(page.locator("[data-testid=success-page]")).toBeVisible(); + + // NOTE: remove if old booking should not be deleted + expect(await booking.self()).toBeNull(); + + const newBooking = await prisma.booking.findFirst({ where: { fromReschedule: booking.uid } }); + expect(newBooking).not.toBeNull(); + await prisma.booking.delete({ where: { id: newBooking?.id } }); + }); + + test("Unpaid rescheduling should go to payment page", async ({ page, users, bookings, payments }) => { + test.skip(!IS_STRIPE_ENABLED, "Skipped as Stripe is not installed"); + const user = await users.create(); + await user.login(); + await user.getPaymentCredential(); + const eventType = user.eventTypes.find((e) => e.slug === "paid")!; + const booking = await bookings.create(user.id, user.username, eventType.id, { + rescheduled: true, + status: BookingStatus.CANCELLED, + paid: false, + }); + + const payment = await payments.create(booking.id); + await page.goto(`/${user.username}/${eventType.slug}?rescheduleUid=${booking.uid}`); + + await selectFirstAvailableTimeSlotNextMonth(page); + + await page.locator('[data-testid="confirm-reschedule-button"]').click(); + + await page.waitForNavigation({ + url(url) { + return url.pathname.indexOf("/payment") > -1; }, }); - if (user?.id && user?.username && eventType?.id) { - const booking = await TestUtilCreateBookingOnUserId(user?.id, user?.username, eventType?.id, { - rescheduled: true, - status: BookingStatus.CANCELLED, - }); - await page.goto(`/${user?.username}/${eventType?.slug}?rescheduleUid=${booking?.uid}`); - - await selectFirstAvailableTimeSlotNextMonth(page); - - await expect(page.locator('[name="name"]')).toBeDisabled(); - await expect(page.locator('[name="email"]')).toBeDisabled(); - await expect(page.locator('[name="notes"]')).toBeDisabled(); - - await page.locator('[data-testid="confirm-reschedule-button"]').click(); - - await page.waitForNavigation({ - url(url) { - return url.pathname.endsWith("/success"); - }, - }); - - await expect(page).toHaveURL(/.*success/); - - // NOTE: remove if old booking should not be deleted - const oldBooking = await prisma?.booking.findFirst({ where: { id: booking?.id } }); - expect(oldBooking).toBeNull(); - - const newBooking = await prisma?.booking.findFirst({ where: { fromReschedule: booking?.uid } }); - expect(newBooking).not.toBeNull(); - } + await expect(page).toHaveURL(/.*payment/); + await payment.delete(); }); - test("Unpaid rescheduling should go to payment page", async ({ page }) => { - let user = currentUser; - - test.skip( - IS_STRIPE_ENABLED && !(user && user.credentials.length > 0), - "Skipped as stripe is not installed and user is missing credentials" - ); - - const eventType = await prisma?.eventType.findFirst({ - where: { - userId: user?.id, - slug: "paid", - }, + test("Paid rescheduling should go to success page", async ({ page, users, bookings, payments }) => { + const user = await users.create(); + await user.login(); + await user.getPaymentCredential(); + await users.logout(); + const eventType = user.eventTypes.find((e) => e.slug === "paid")!; + const booking = await bookings.create(user.id, user.username, eventType.id, { + rescheduled: true, + status: BookingStatus.CANCELLED, + paid: true, }); - if (user?.id && user?.username && eventType?.id) { - const booking = await TestUtilCreateBookingOnUserId(user?.id, user?.username, eventType?.id, { - rescheduled: true, - status: BookingStatus.CANCELLED, - paid: false, - }); - if (booking?.id) { - await TestUtilCreatePayment(booking.id, {}); - await page.goto(`/${user?.username}/${eventType?.slug}?rescheduleUid=${booking?.uid}`); - await selectFirstAvailableTimeSlotNextMonth(page); + const payment = await payments.create(booking.id); + await page.goto(`/${user?.username}/${eventType?.slug}?rescheduleUid=${booking?.uid}`); - await page.locator('[data-testid="confirm-reschedule-button"]').click(); + await selectFirstAvailableTimeSlotNextMonth(page); - await page.waitForNavigation({ - url(url) { - return url.pathname.indexOf("/payment") > -1; - }, - }); + await page.locator('[data-testid="confirm-reschedule-button"]').click(); - await expect(page).toHaveURL(/.*payment/); - } - } - }); + await expect(page).toHaveURL(/.*success/); - test("Paid rescheduling should go to success page", async ({ page }) => { - let user = currentUser; - try { - const eventType = await prisma?.eventType.findFirst({ - where: { - userId: user?.id, - slug: "paid", - }, - }); - if (user?.id && user?.username && eventType?.id) { - const booking = await TestUtilCreateBookingOnUserId(user?.id, user?.username, eventType?.id, { - rescheduled: true, - status: BookingStatus.CANCELLED, - paid: true, - }); - if (booking?.id) { - await TestUtilCreatePayment(booking.id, {}); - await page.goto(`/${user?.username}/${eventType?.slug}?rescheduleUid=${booking?.uid}`); - - await selectFirstAvailableTimeSlotNextMonth(page); - - await page.locator('[data-testid="confirm-reschedule-button"]').click(); - - await expect(page).toHaveURL(/.*success/); - } - } - } catch (error) { - await prisma?.payment.delete({ - where: { - externalId: "DEMO_PAYMENT_FROM_DB", - }, - }); - } + await payment.delete(); }); }); diff --git a/apps/web/public/cal-logo-word-dark.svg b/apps/web/public/cal-logo-word-dark.svg new file mode 100644 index 0000000000..e12d2875c3 --- /dev/null +++ b/apps/web/public/cal-logo-word-dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/static/locales/ar/common.json b/apps/web/public/static/locales/ar/common.json index 8db6b2fdc3..ba6ea9ca0b 100644 --- a/apps/web/public/static/locales/ar/common.json +++ b/apps/web/public/static/locales/ar/common.json @@ -2,6 +2,8 @@ "trial_days_left": "يتبقى لديك $t(day, {\"count\": {{days}} }) في الفترة التجريبية من PRO", "day": "{{count}} أيام", "day_plural": "{{count}} أيام", + "second": "{{count}} ثانية", + "second_plural": "{{count}} ثوانٍ", "upgrade_now": "تحديث الآن", "accept_invitation": "اقبل الدعوة", "calcom_explained": "Cal.com هو بديل Calendly مفتوح المصدر الذي يتيح لك التحكم في بياناتك وسير عملك والمظهر على التطبيق أو موقع الويب.", @@ -60,9 +62,15 @@ "password_reset_instructions": "إذا لم تطلب هذا، يمكنك تجاهل هذا البريد الإلكتروني بأمان ولن يتم تغيير كلمة المرور الخاصة بك.", "event_awaiting_approval_subject": "في انتظار الموافقة: {{eventType}} مع {{name}} في {{date}}", "event_still_awaiting_approval": "الحدث لا يزال في انتظار موافقتك", + "booking_submitted_subject": "تم إرسال الحجز: {{eventType}} مع {{name}} في {{date}}", "your_meeting_has_been_booked": "لقد تم حجز الاجتماع الخاص بك", "event_type_has_been_rescheduled_on_time_date": "تمت إعادة جدولة {{eventType}} لديك مع {{name}} إلى {{time}} ({{timeZone}}) في {{date}}.", "event_has_been_rescheduled": "تم التحديث - تم إعادة جدولة الحدث الخاص بك", + "request_reschedule_title_attendee": "طلب إعادة جدولة الحجز الخاص بك", + "request_reschedule_subtitle": "ألغى {{organizer}} الحجز وطلب منك اختيار حجز في وقت آخر.", + "request_reschedule_title_organizer": "لقد طلبت من {{attendee}} إعادة جدولة موعد", + "request_reschedule_subtitle_organizer": "لقد ألغيت الحجز ويجب أن يختار {{attendee}} وقتًا جديداً للحجز معك.", + "reschedule_reason": "سبب إعادة الجدولة", "hi_user_name": "مرحبا {{name}}", "ics_event_title": "{{eventType}} مع {{name}}", "new_event_subject": "حدث جديد: {{attendeeName}} - {{date}} - {{eventType}}", @@ -81,6 +89,7 @@ "meeting_url": "رابط الاجتماع", "meeting_request_rejected": "تم رفض طلبك للاجتماع", "rescheduled_event_type_subject": "إعادة جدولة: {{eventType}} مع {{name}} في {{date}}", + "requested_to_reschedule_subject_attendee": "الإجراء المطلوب إعادة الجدولة: يُرجى حجز وقت جديد لـ {{eventType}} مع {{name}}", "rejected_event_type_with_organizer": "تم الرفض: {{eventType}} مع {{organizer}} في {{date}}", "hi": "مرحبا", "join_team": "انضم إلى للفريق", @@ -284,6 +293,10 @@ "hover_over_bold_times_tip": "نصيحة: قم بالمرور فوق الأوقات المكتوبة بخط عريض للحصول على الزمن بالكامل", "start_time": "وقت البدء", "end_time": "وقت الانتهاء", + "buffer_time": "وقت التخزين المؤقت", + "before_event": "قبل الحدث", + "after_event": "بعد الحدث", + "event_buffer_default": "لا يوجد وقت للتخزين المؤقت", "buffer": "الفترة بين الحدثين", "your_day_starts_at": "يبدأ يومك في", "your_day_ends_at": "ينتهي يومك في", @@ -299,6 +312,9 @@ "light": "فاتح", "dark": "داكن", "automatically_adjust_theme": "ضبط السمة تلقائيًا استنادًا إلى تفضيلات المدعوين", + "user_dynamic_booking_disabled": "قام بعض المستخدمين في المجموعة حاليًا بتعطيل حجز المجموعات الديناميكي", + "allow_dynamic_booking_tooltip": "روابط الحجز الجماعية التي يمكن إنشاؤها ديناميكيًا عن طريق إضافة أسماء مستخدمين متعددة باستخدام \"+\". مثال: \"cal.com/bailey+peer\"", + "allow_dynamic_booking": "السماح للحضور بالحجز لك عن طريق حجز المجموعات الديناميكي", "email": "البريد الإلكتروني", "email_placeholder": "name@example.com", "full_name": "الاسم بالكامل", @@ -311,9 +327,12 @@ "event_triggers": "مشغلات الحدث", "subscriber_url": "عنوان URL للمشترك", "create_new_webhook": "إنشاء ويب هوك جديد", + "webhooks": "ويبهوكس", + "team_webhooks": "فريق ويبهوكس", "create_new_webhook_to_account": "إنشاء ويب هوك جديد لحسابك", "new_webhook": "ويب هوك جديد", "receive_cal_meeting_data": "تلقَّ بيانات اجتماعات Cal على عنوان URL محدد، وفي الوقت الفعلي، عندما تتم جدولة حدث ما أو يتم إلغاؤه.", + "receive_cal_event_meeting_data": "تلقي بيانات اجتماعات Cal على عنوان URL محدد، وفي الوقت الحقيقي، عندما تتم جدولة حدث ما أو إلغاؤه.", "responsive_fullscreen_iframe": "إطار iframe سريع الاستجابة بكامل الشاشة", "loading": "يجري التحميل...", "standard_iframe": "إطار iframe القياسي", @@ -398,10 +417,12 @@ "booking_confirmation": "قم بتأكيد {{eventTypeTitle}} مع {{profileName}}", "booking_reschedule_confirmation": "أعد جدولة {{eventTypeTitle}} مع {{profileName}}", "in_person_meeting": "الرابط أو الاجتماع الشخصي", + "link_meeting": "رابط الاجتماع", "phone_call": "المكالمة الهاتفية", "phone_number": "رقم الهاتف", "enter_phone_number": "أدخل رقم الهاتف", "reschedule": "إعادة الجدولة", + "reschedule_this": "إعادة الجدولة بدلاً من ذلك", "book_a_team_member": "حجز عضو في الفريق بدلًا من ذلك", "or": "أو", "go_back": "العودة", @@ -435,6 +456,8 @@ "danger_zone": "منطقة الخطر", "back": "عودة", "cancel": "إلغاء", + "apply": "تطبيق", + "cancel_event": "إلغاء هذا الحدث", "continue": "متابعة", "confirm": "تأكيد", "disband_team": "تفكيك الفريق", @@ -504,6 +527,8 @@ "first_day_of_week": "أول يوم في الأسبوع", "single_theme": "سمة واحدة", "brand_color": "لون العلامة التجارية", + "light_brand_color": "لون العلامة التجارية (السمة الفاتحة)", + "dark_brand_color": "لون العلامة التجارية (السمة الداكنة)", "file_not_named": "لم تتم تسمية الملف [user]/[idOrSlug]", "create_team": "إنشاء فريق", "name": "الاسم", @@ -552,6 +577,8 @@ "type": "النوع", "edit": "تعديل", "add_input": "إضافة مُدخَل", + "disable_notes": "إخفاء الملاحظات في التقويم", + "disable_notes_description": "لأسباب تتعلق بالخصوصية، سيتم إخفاء المدخلات والملاحظات الإضافية في إدخال التقويم. سيتم إرسالها إلى بريدك الإلكتروني.", "opt_in_booking": "حجز يتطلب موافقة", "opt_in_booking_description": "يجب تأكيد الحجز يدويًا قبل نقله إلى التكاملات وإرسال رسالة تأكيد عبر البريد الإلكتروني.", "disable_guests": "تعطيل خاصية الضيوف", @@ -561,6 +588,7 @@ "calendar_days": "أيام التقويم", "business_days": "أيام العمل", "set_address_place": "تعيين عنوان أو مكان", + "set_link_meeting": "تعيين رابطًا للاجتماع", "cal_invitee_phone_number_scheduling": "سيطلب Cal من المدعو لديك إدخال رقم الهاتف قبل الجدولة.", "cal_provide_google_meet_location": "سيوفر Cal رابطًا لـ Google Meet.", "cal_provide_zoom_meeting_url": "سيوفر Cal رابطًا للاجتماع عبر Zoom.", @@ -568,6 +596,7 @@ "cal_provide_video_meeting_url": "سيوفر Cal رابطًا للاجتماع عبر الفيديو على Daily.", "cal_provide_jitsi_meeting_url": "سنوفر لك رابطًا للاجتماع عبر Jitsi Meet.", "cal_provide_huddle01_meeting_url": "سيوفر Cal رابطًا للاجتماع عبر الفيديو على Huddle01 web3.", + "cal_provide_teams_meeting_url": "سيوفر Cal عنوان URL لاجتماعات MS Teams. ملاحظة: يجب أن يكون لديك حساب عمل أو حساب مدرسة", "require_payment": "يلزم الدفع", "commission_per_transaction": "عمولة كل معاملة", "event_type_updated_successfully_description": "تم تحديث نوع الحدث لديك بنجاح.", @@ -593,6 +622,9 @@ "confirm_delete_account": "نعم، احذف الحساب", "delete_account_confirmation_message": "هل تريد بالتأكيد حذف حسابك على Cal.com؟ أي شخص شاركت رابط حسابك معه لن يستطيع بعد الآن الحجز باستخدامه، وستفقد أي تفضيلات حفظتها.", "integrations": "التكاملات", + "apps": "التطبيقات", + "app_store": "متجر التطبيقات", + "app_store_description": "الربط بين الناس، والتكنولوجيا، ومكان العمل.", "settings": "الإعدادات", "event_type_moved_successfully": "تم نقل نوع الحدث بنجاح", "next_step": "تخطي الخطوة", @@ -640,6 +672,24 @@ "import_from": "الاستيراد من", "access_token": "Access token", "visit_roadmap": "المخطط", + "popular_categories": "الفئات الشائعة", + "trending_apps": "التطبيقات الرائجة", + "all_apps": "كل التطبيقات", + "installed_apps": "التطبيقات المثبتة", + "manage_your_connected_apps": "إدارة تطبيقاتك المثبتة أو تغيير الإعدادات", + "browse_apps": "استعراض التطبيقات", + "features": "الميزات", + "permissions": "الأذونات", + "terms_and_privacy": "الشروط والخصوصية", + "published_by": "نُشر بواسطة {{author}}", + "subscribe": "اشتراك", + "buy": "شراء", + "install_app": "تثبيت التطبيق", + "categories": "الفئات", + "pricing": "التسعير", + "learn_more": "معرفة المزيد", + "privacy_policy": "سياسة الخصوصية", + "terms_of_service": "شروط الخدمة", "remove": "إزالة", "add": "إضافة", "verify_wallet": "تأكيد المحفظة", @@ -655,7 +705,63 @@ "prisma_studio_tip_description": "تعرّف على كيفية إعداد مستخدمك الأول", "contact_sales": "تواصل مع قسم المبيعات", "error_404": "خطأ 404", + "default": "الافتراضي", + "set_to_default": "تعيين إلى الافتراضي", + "new_schedule_btn": "جدول جديد", + "add_new_schedule": "إضافة جدول جديد", + "delete_schedule": "حذف الجدول", + "schedule_created_successfully": "تم إنشاء جدول {{scheduleName}} بنجاح", "availability_updated_successfully": "تم تحديث الأوقات المتاحة بنجاح", + "schedule_deleted_successfully": "تم حذف الجدول بنجاح", + "default_schedule_name": "ساعات العمل", + "new_schedule_heading": "إنشاء جدول الأوقات المتاحة", + "new_schedule_description": "إن إنشاء جداول الأوقات المتاحة يتيح لك إدارة الأوقات المتاحة عبر أنواع الأحداث. يمكن تطبيقها على نوع أو أكثر من أنواع الأحداث.", "requires_ownership_of_a_token": "يتطلب امتلاك token يخص العنوان التالي:", - "example_name": "فلان الفلاني" + "example_name": "فلان الفلاني", + "time_format": "تنسيق الوقت", + "12_hour": "12 ساعة", + "24_hour": "24 ساعة", + "redirect_success_booking": "إعادة التوجيه عند الحجز ", + "you_are_being_redirected": "ستتم إعادة توجيهك إلى {{ url }} خلال $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "لاستخدام هذه الميزة، تحتاج إلى الترقية إلى حساب Pro.", + "duplicate": "تكرار", + "you_can_manage_your_schedules": "يمكنك إدارة الجداول الخاصة بك في صفحة الأوقات المتاحة.", + "api_keys": "مفاتيح API", + "api_key_modal_subtitle": "تتيح لك مفاتيح API إجراء مكالمات API لحسابك الخاص.", + "api_keys_subtitle": "إنشاء مفاتيح API لاستخدامها للوصول إلى حسابك الخاص.", + "generate_new_api_key": "إنشاء مفتاح API جديد", + "create_api_key": "إنشاء مفتاح API", + "personal_note": "تسمية هذا المفتاح", + "personal_note_placeholder": "على سبيل المثال؛ التطوير", + "api_key_no_note": "مفتاح API بدون اسم", + "api_key_never_expires": "مفتاح API هذا ليس له تاريخ انتهاء صلاحية", + "edit_api_key": "تحرير مفتاح API", + "never_expire_key": "لا تنتهي الصّلاحية أبدًا", + "delete_api_key": "إلغاء مفتاح API", + "success_api_key_created": "تم إنشاء مفتاح API بنجاح", + "success_api_key_edited": "تم تحديث مفتاح API بنجاح", + "create": "إنشاء", + "success_api_key_created_bold_tagline": "حفظ مفتاح API هذا في مكان آمن.", + "you_will_only_view_it_once": "لن تتمكن من مشاهدته مرة أخرى بمجرد إغلاق هذا النموذج.", + "copy_to_clipboard": "نسخ إلى الحافظة", + "confirm_delete_api_key": "إلغاء مفتاح API هذا", + "revoke_api_key": "إلغاء مفتاح API", + "api_key_copied": "تم نسخ مفتاح API!", + "delete_api_key_confirm_title": "هل تريد إزالة مفتاح API هذا بشكل دائم من حسابك؟", + "copy": "نسخ", + "expire_date": "تاريخ انتهاء الصّلاحية", + "expired": "انتهت الصلاحية", + "never_expires": "لا تنتهي الصلاحية أبدًا", + "expires": "تنتهي الصلاحية في", + "request_reschedule_booking": "طلب إعادة جدولة الحجز الخاص بك", + "reason_for_reschedule": "سبب إعادة الجدولة", + "book_a_new_time": "احجز وقتًا جديدًا", + "reschedule_request_sent": "تم إرسال طلب إعادة جدولة", + "reschedule_modal_description": "سيؤدي هذا إلى إلغاء الاجتماع المجدول، قم بإبلاغ صاحب الجدولة وطلب منه اختيار وقت جديد.", + "reason_for_reschedule_request": "سبب طلب إعادة الجدولة", + "send_reschedule_request": "طلب إعادة جدولة ", + "edit_booking": "تحرير الحجز", + "reschedule_booking": "إعادة جدولة الحجز", + "former_time": "الوقت السابق" } diff --git a/apps/web/public/static/locales/ar/vital.json b/apps/web/public/static/locales/ar/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/ar/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/cs/common.json b/apps/web/public/static/locales/cs/common.json index cc014e7ccc..54e5a0e41a 100644 --- a/apps/web/public/static/locales/cs/common.json +++ b/apps/web/public/static/locales/cs/common.json @@ -2,6 +2,8 @@ "trial_days_left": "V PRO verzi na zkoušku vám zbývají $t(day, {\"count\": {{days}} })", "day": "{{count}} den", "day_plural": "{{count}} dny", + "second": "{{count}} s", + "second_plural": "{{count}} s", "upgrade_now": "Přejít na vyšší tarif", "accept_invitation": "Přijmout pozvánku", "calcom_explained": "Cal.com je open source alternativa, která vám dává kontrolu nad vlastními daty, workflow a vzhledem.", @@ -64,6 +66,11 @@ "your_meeting_has_been_booked": "Vaše schůzka byla rezervována", "event_type_has_been_rescheduled_on_time_date": "Váš {{eventType}} s {{name}} byl přesunut na {{time}} ({{timeZone}}) dne {{date}}.", "event_has_been_rescheduled": "Změna - Vaše událost byla přesunuta na jindy", + "request_reschedule_title_attendee": "Žádost o přeplánování rezervace", + "request_reschedule_subtitle": "Organizátor {{organizer}} zrušil rezervaci a požádal vás, abyste si vybrali jiný čas.", + "request_reschedule_title_organizer": "Požádali jste uživatele {{attendee}} o změnu časového plánu", + "request_reschedule_subtitle_organizer": "Zrušili jste rezervaci a uživatel {{attendee}} by si s vámi měl zarezervovat nový čas.", + "reschedule_reason": "Důvod přeplánování", "hi_user_name": "Hezký den, {{name}}", "ics_event_title": "{{eventType}} s {{name}}", "new_event_subject": "Nová událost: {{attendeeName}} - {{date}} - {{eventType}}", @@ -82,6 +89,7 @@ "meeting_url": "URL ke schůzce", "meeting_request_rejected": "Vaše žádost o schůzku byla zamítnuta", "rescheduled_event_type_subject": "Přesunuto: {{eventType}} s {{name}} během {{date}}", + "requested_to_reschedule_subject_attendee": "Akce vyžadovaná k přeplánování: zarezervujte si nový čas pro událost {{eventType}} s osobou {{name}}", "rejected_event_type_with_organizer": "Zamítnuto: {{eventType}} s {{organizer}} během {{date}}", "hi": "Zdravíme", "join_team": "Připojit se k týmu", @@ -304,6 +312,9 @@ "light": "Světlý", "dark": "Tmavý", "automatically_adjust_theme": "Automaticky změň motiv podle preferencí pozvaného", + "user_dynamic_booking_disabled": "Někteří uživatelé ve skupině mají v současné době zakázané dynamické skupinové rezervace", + "allow_dynamic_booking_tooltip": "Odkazy na skupinové rezervace, které lze vytvořit dynamicky přidáním více uživatelských jmen s „+“. příklad: „cal.com/bailey+peer“", + "allow_dynamic_booking": "Povolit účastníkům rezervovat vás prostřednictvím dynamických skupinových rezervací", "email": "E-mailová adresa", "email_placeholder": "vas@email.cz", "full_name": "Jméno a příjmení", @@ -566,6 +577,8 @@ "type": "Typ", "edit": "Upravit", "add_input": "Přidat položku", + "disable_notes": "Skrýt poznámky v kalendáři", + "disable_notes_description": "Z důvodu ochrany soukromí budou v záznamu kalendáře skryty další vstupy a poznámky. Stále budou odeslány na váš e-mail.", "opt_in_booking": "Přidat se k rezervaci", "opt_in_booking_description": "Rezervace musí být ručně potvrzena, než bude odeslána do integrací a dojde k odeslání e-mailu s potvrzením.", "disable_guests": "Zakázat hosty", @@ -583,6 +596,7 @@ "cal_provide_video_meeting_url": "Cal poskytne URL Daily video meetingu.", "cal_provide_jitsi_meeting_url": "Cal poskytne URL Jitsi Meet video meetingu.", "cal_provide_huddle01_meeting_url": "Cal poskytne URL Huddle01 web3 video meetingu.", + "cal_provide_teams_meeting_url": "Cal poskytne URL schůzky aplikace MS Teams. POZNÁMKA: MUSÍ SE JEDNAT O PRACOVNÍ NEBO ŠKOLNÍ ÚČET", "require_payment": "Vyžadovat platbu", "commission_per_transaction": "provize za transakci", "event_type_updated_successfully_description": "Typ události byl úspěšně aktualizován.", @@ -693,12 +707,67 @@ "error_404": "Chyba 404", "default": "Výchozí", "set_to_default": "Nastavit jako výchozí", + "new_schedule_btn": "Nový plán", + "add_new_schedule": "Přidat nový plán", + "delete_schedule": "Odstranit plán", + "schedule_created_successfully": "Plán {{scheduleName}} byl úspěšně vytvořen", "availability_updated_successfully": "Dostupnost byla úspěšně aktualizována", + "schedule_deleted_successfully": "Plán byl úspěšně odstraněn", "default_schedule_name": "Pracovní doba", + "new_schedule_heading": "Vytvořit plán dostupnosti", + "new_schedule_description": "Vytvoření plánů dostupnosti vám umožní spravovat dostupnost mezi typy událostí. Mohou být použity pro jeden nebo více typů událostí.", "requires_ownership_of_a_token": "Vyžaduje vlastnictví tokenu, který patří následující adrese:", "example_name": "Jan Novák", "time_format": "Formát času", "12_hour": "12hodinový", "24_hour": "24hodinový", - "duplicate": "Duplikovat" + "redirect_success_booking": "Přesměrovat při rezervaci ", + "you_are_being_redirected": "Budete přesměrováni na {{ url }} za $t(s, {\"count\": {{s}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Pokud chcete použít tuto funkci, musíte provést aktualizaci na účet Pro.", + "duplicate": "Duplikovat", + "you_can_manage_your_schedules": "Své plány můžete spravovat na stránce Dostupnost.", + "api_keys": "Klíče API", + "api_key_modal_subtitle": "Klíče API vám umožňují provádět volání API pro váš vlastní účet.", + "api_keys_subtitle": "Generovat API klíče pro přístup k vašemu vlastnímu účtu.", + "generate_new_api_key": "Generovat nový API klíč", + "create_api_key": "Vytvořit klíč API", + "personal_note": "Pojmenovat tento klíč", + "personal_note_placeholder": "Například vývoj", + "api_key_no_note": "Bezejmenný klíč API", + "api_key_never_expires": "Tento klíč API nemá datum uplynutí platnosti", + "edit_api_key": "Upravit klíč API", + "never_expire_key": "Platnost nikdy neuplyne", + "delete_api_key": "Odvolat klíč API", + "success_api_key_created": "Klíč API vytvořen", + "success_api_key_edited": "Klíč API aktualizován", + "create": "Vytvořit", + "success_api_key_created_bold_tagline": "Uložte si tento klíč API na nějaké bezpečné místo.", + "you_will_only_view_it_once": "Nebudete to moci znovu zobrazit po zavření tohoto modálu.", + "copy_to_clipboard": "Kopírovat do schránky", + "confirm_delete_api_key": "Odvolat tento klíč API", + "revoke_api_key": "Odvolat klíč API", + "api_key_copied": "Klíč API zkopírován!", + "delete_api_key_confirm_title": "Trvale odebrat tento klíč API z vašeho účtu?", + "copy": "Kopírovat", + "expire_date": "Datum uplynutí platnosti", + "expired": "Platnost uplynula", + "never_expires": "Platnost nikdy neuplyne", + "expires": "Platnost vyprší", + "request_reschedule_booking": "Žádost o přeplánování rezervace", + "reason_for_reschedule": "Důvod přeplánování", + "book_a_new_time": "Rezervovat nový čas", + "reschedule_request_sent": "Žádost o přeplánování byla odeslána", + "reschedule_modal_description": "Tímto zrušíte plánovanou schůzku, oznámíte plánovači a požádáte je o vybrání nového času.", + "reason_for_reschedule_request": "Důvod žádosti o přeplánování", + "send_reschedule_request": "Žádost o přeplánování ", + "edit_booking": "Upravit rezervaci", + "reschedule_booking": "Přeplánovat rezervaci", + "former_time": "Dřívější čas", + "confirmation_page_gif": "Gif pro potvrzovací stránku", + "search": "Hledat", + "impersonate": "Zosobnit", + "impersonate_user_tip": "Všechna použití této funkce jsou kontrolována.", + "impersonating_user_warning": "Zosobnění uživatelského jména „{{user}}“.", + "impersonating_stop_instructions": "<0>Kliknutím zde zastavte." } diff --git a/apps/web/public/static/locales/cs/vital.json b/apps/web/public/static/locales/cs/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/cs/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/de/common.json b/apps/web/public/static/locales/de/common.json index 80c2336d87..c7833333ff 100644 --- a/apps/web/public/static/locales/de/common.json +++ b/apps/web/public/static/locales/de/common.json @@ -12,6 +12,7 @@ "event_declined_subject": "Abgelehnt: {{eventType}} mit {{name}} am {{date}}", "event_cancelled_subject": "Abgebrochen: {{eventType}} mit {{name}} am {{date}}", "event_request_declined": "Ihre Event-Anfrage wurde abgelehnt", + "event_request_declined_recurring": "Ihre wiederkehrende Terminanfrage wurde abgelehnt", "event_request_cancelled": "Ihr geplantes Ereignis wurde abgebrochen", "organizer": "Organisator", "need_to_reschedule_or_cancel": "Möchten Sie neu planen oder absagen?", @@ -23,6 +24,7 @@ "rejection_confirmation": "Buchung ablehnen", "manage_this_event": "Diesen Termin verwalten", "your_event_has_been_scheduled": "Ihr Event wurde geplant", + "your_event_has_been_scheduled_recurring": "Ihr wiederkehrendes Ereignis wurde geplant", "accept_our_license": "Akzeptieren Sie unsere Lizenz, indem Sie die .env-Variable <1>NEXT_PUBLIC_LICENSE_CONSENT auf '{{agree}} ' ändern.", "remove_banner_instructions": "Um diesen Banner zu entfernen, öffnen Sie bitte Ihre .env-Datei und ändern Sie die <1>NEXT_PUBLIC_LICENSE_CONSENT Variable auf '{{agree}}'.", "error_message": "Die Fehlermeldung war: '{{errorMessage}}'", @@ -38,9 +40,9 @@ "refunded": "Erstattet", "pay_later_instructions": "Sie haben auch eine E-Mail mit diesem Link erhalten, wenn Sie später bezahlen möchten.", "payment": "Bezahlung", - "missing_card_fields": "Fehlender Karteneintrag", + "missing_card_fields": "Fehlender Eintrag für Karteninformationen", "pay_now": "Jetzt bezahlen", - "codebase_has_to_stay_opensource": "Die Codebase muss Open Source bleiben, unabhängig davon, ob sie geändert wurde oder nicht", + "codebase_has_to_stay_opensource": "Die Codebasis muss Open Source bleiben, unabhängig davon, ob sie geändert wurde oder nicht", "cannot_repackage_codebase": "Sie dürfen die Codebasis nicht weiter verkaufen", "acquire_license": "Erwerben Sie eine kommerzielle Lizenz per Email, um diese Bedingungen zu entfernen", "terms_summary": "Zusammenfassung der Bedingungen", @@ -57,6 +59,7 @@ "confirm_or_reject_request": "Anfrage bestätigen oder ablehnen", "check_bookings_page_to_confirm_or_reject": "Überprüfen Sie Ihre Buchungsseite, um die Buchung zu bestätigen oder abzulehnen.", "event_awaiting_approval": "Ein neues Event wartet auf Ihre Bestätigung", + "event_awaiting_approval_recurring": "Ein wiederkehrender Termin wartet auf Ihre Genehmigung", "someone_requested_an_event": "Jemand hat eine Buchungsanfrage gestellt.", "someone_requested_password_reset": "Jemand hat einen Link angefordert, um Ihr Passwort zu ändern.", "password_reset_instructions": "Wenn Sie dies nicht angefordert haben, können Sie diese E-Mail sicher ignorieren und Ihr Passwort wird nicht geändert.", @@ -66,6 +69,11 @@ "your_meeting_has_been_booked": "Dein Meeting wurde gebucht", "event_type_has_been_rescheduled_on_time_date": "Ihr {{eventType}} mit {{name}} wurde auf {{time}} ({{timeZone}}) am {{date}} verschoben.", "event_has_been_rescheduled": "Ihr Event wurde verschoben.", + "request_reschedule_title_attendee": "Anfrage zur Neuplanung Ihrer Buchung", + "request_reschedule_subtitle": "{{organizer}} hat die Buchung storniert und Sie gebeten, eine andere Zeit auszuwählen.", + "request_reschedule_title_organizer": "Sie haben {{attendee}} zur Neuplanung aufgefordert", + "request_reschedule_subtitle_organizer": "Sie haben die Buchung storniert und {{attendee}} muss eine neue Buchungszeit mit Ihnen vereinbaren.", + "reschedule_reason": "Grund für die Neuplanung", "hi_user_name": "Hi {{userName}}", "ics_event_title": "{{eventType}} mit {{name}}", "new_event_subject": "Neues Ereignis: {{attendeeName}} - {{date}} - {{eventType}}", @@ -74,6 +82,7 @@ "manage_my_bookings": "Meine Buchungen verwalten", "need_to_make_a_change": "Sie müssen eine Änderung vornehmen?", "new_event_scheduled": "Ein neues Event wurde geplant.", + "new_event_scheduled_recurring": "Ein neues wiederkehrendes Ereignis wurde geplant.", "invitee_email": "Teilnehmer-Email", "invitee_timezone": "Zeitzone der Teilnehmenden", "event_type": "Event-Typ", @@ -84,6 +93,7 @@ "meeting_url": "Meeting-URL", "meeting_request_rejected": "Ihre Meeting Anfrage wurde abgelehnt", "rescheduled_event_type_subject": "Neu geplant: {{eventType}} mit {{name}} am {{date}}", + "requested_to_reschedule_subject_attendee": "Aktion erforderlich - Neuplanung: Bitte buchen Sie eine neue Zeit für „{{eventType}}“ mit {{name}}", "rejected_event_type_with_organizer": "Abgelehnt: {{eventType}} mit {{organizer}} auf {{date}}", "hi": "Hi", "join_team": "Team beitreten", @@ -122,6 +132,7 @@ "ping_test": "Pingtest", "add_to_homescreen": "Fügen Sie diese App Ihrem Startbildschirm für schnelleren Zugriff hinzu.", "upcoming": "Bevorstehend", + "recurring": "Wiederkehrend", "past": "Vergangen", "choose_a_file": "Datei auswählen...", "upload_image": "Bild hochladen", @@ -226,13 +237,20 @@ "add_to_calendar": "Zum Kalender hinzufügen", "other": "Anderes", "emailed_you_and_attendees": "Wir haben Ihnen und den anderen Teilnehmern eine Einladung zum Kalender mit allen Details zugeschickt.", + "emailed_you_and_attendees_recurring": "Wir haben Ihnn und den anderen Teilnemern eine Kalendereinladung für den ersten Termin dieses wiederkehrendem Ereignisses gesendet.", "emailed_you_and_any_other_attendees": "Sie und alle anderen Teilnehmer wurden mit diesen Informationen per E-Mail informiert.", "needs_to_be_confirmed_or_rejected": "Ihre Buchung muss noch bestätigt oder abgelehnt werden.", + "needs_to_be_confirmed_or_rejected_recurring": "Ihr wiederkehrender Termin muss noch bestätigt oder abgelehnt werden.", "user_needs_to_confirm_or_reject_booking": "{{user}} muss die Buchung noch bestätigen oder ablehnen.", + "user_needs_to_confirm_or_reject_booking_recurring": "{{user}} muss noch jede Buchung des wiederkehrenden Meetings bestätigen oder ablehnen.", "meeting_is_scheduled": "Dieses Meeting ist geplant", + "meeting_is_scheduled_recurring": "Ihr wiederkehrendes Ereignis wurde geplant", "submitted": "Ihre Buchung wurde gesendet", + "submitted_recurring": "Ihr wiederkehrendes Meeting wurde gesendet", "booking_submitted": "Ihre Buchung wurde gesendet", + "booking_submitted_recurring": "Ihr wiederkehrendes Meeting wurde gesendet", "booking_confirmed": "Ihre Buchung wurde bestätigt", + "booking_confirmed_recurring": "Ihr wiederkehrendes Meeting wurde bestätigt", "enter_new_password": "Geben Sie das neue Passwort ein.", "reset_password": "Passwort zurücksetzen", "change_your_password": "Passwort ändern", @@ -276,6 +294,7 @@ "bookings": "Buchungen", "bookings_description": "Sehen Sie anstehende und vergangene Veranstaltungen, die über Ihre Events gebucht wurden.", "upcoming_bookings": "Sobald jemand eine Zeit bei Ihnen bucht können Sie das hier sehen.", + "recurring_bookings": "Sobald jemand ein wiederkehrendes Meeting mit Ihnen bucht wird es hier erscheinen.", "past_bookings": "Ihre früheren Buchungen werden hier angezeigt.", "cancelled_bookings": "Ihre stornierten Buchungen werden hier angezeigt.", "on": "auf", @@ -426,6 +445,7 @@ "edit_role": "Rolle bearbeiten", "edit_team": "Team bearbeiten", "reject": "Ablehnen", + "reject_all": "Alle ablehnen", "accept": "Annehmen", "leave": "Verlassen", "profile": "Profil", @@ -454,6 +474,7 @@ "cancel_event": "Diesen Termin stornieren", "continue": "Weiter", "confirm": "Bestätigen", + "confirm_all": "Alle bestätigen", "disband_team": "Team auflösen", "disband_team_confirmation_message": "Bist du sicher, dass du dieses Team auflösen möchtest? Jeder der diesen Team-Link erhalten hat, kann Sie nicht mehr buchen.", "remove_member_confirmation_message": "Sind Sie sicher, dass Sie dieses Mitglied aus dem Team entfernen möchten?", @@ -479,6 +500,7 @@ "user_from_team": "{{user}} von {{team}}", "preview": "Vorschau", "link_copied": "Link kopiert!", + "private_link_copied": "Privater Link kopiert!", "link_shared": "Link geteilt!", "title": "Titel", "description": "Beschreibung", @@ -494,6 +516,7 @@ "url": "URL", "hidden": "Versteckt", "readonly": "Schreibgeschützt", + "one_time_link": "Einmaliger Link", "plan_description": "Sie haben gerade den Tarif: {{plan}}.", "plan_upgrade_invitation": "Aktualisieren Sie Ihr Konto auf den Pro-Plan, um alle Funktionen freizuschalten.", "plan_upgrade": "Sie müssen Ihr Paket upgraden, um mehr als einen aktiven Ereignistyp zu haben.", @@ -519,6 +542,18 @@ "language": "Sprache", "timezone": "Zeitzone", "first_day_of_week": "Erster Tag der Woche", + "repeats_up_to": "Wiederholt sich {{count}} mal", + "repeats_up_to_plural": "Wiederholt sich {{count}} mal", + "every_for_freq": "Alle {{freq}} für", + "repeats_every": "Wiederholt sich alle", + "weekly": "Woche", + "weekly_plural": "Wochen", + "monthly": "Monat", + "monthly_plural": "Monate", + "yearly": "Jahr", + "yearly_plural": "Jahre", + "plus_more": "{{count}} mehr", + "max": "Maximal", "single_theme": "Einzelnes Theme", "brand_color": "Markenfarbe", "light_brand_color": "Brand Farbe (Helles Theme)", @@ -575,8 +610,14 @@ "disable_notes_description": "Aus Datenschutzgründen werden zusätzliche Eingaben und Notizen im Kalendereintrag ausgeblendet. Sie werden trotzdem an Ihre E-Mail gesendet.", "opt_in_booking": "Opt-in Buchung", "opt_in_booking_description": "Die Buchung muss manuell bestätigt werden, bevor sie an die Integrationen weitergeleitet wird und eine Bestätigungsmail gesendet wird.", + "recurring_event": "Wiederkehrender Termin", + "recurring_event_description": "Personen können sich für wiederkehrende Termine anmelden", + "starting": "Beginnen", "disable_guests": "Gäste deaktivieren", "disable_guests_description": "Das Hinzufügen zusätzlicher Gäste deaktivieren.", + "private_link": "Privaten Link generieren", + "copy_private_link": "Privaten Link kopieren", + "private_link_description": "Erzeuge einen privaten Link um einen Link zu teilen, der deinen Cal Benutzername nicht preisgibt", "invitees_can_schedule": "Teilnehmer können folgendes buchen", "date_range": "Datumszeitraum", "calendar_days": "Kalendertage", @@ -720,5 +761,65 @@ "external_redirect_url": "Externe Umleitungs-URL - Beginnt mit https://", "redirect_url_upgrade_description": "Um diese Funktion nutzen zu können, müssen Sie ein Upgrade auf einen Pro-Account durchführen.", "duplicate": "Duplizieren", - "you_can_manage_your_schedules": "Sie können Ihre Zeitpläne bei Ihren Verfügbarkeitszeiten ändern." + "you_can_manage_your_schedules": "Sie können Ihre Zeitpläne bei Ihren Verfügbarkeitszeiten ändern.", + "api_keys": "API-Schlüssel", + "api_key_modal_subtitle": "Mit API-Schlüsseln können Sie API-Aufrufe für Ihren eigenen Account durchführen.", + "api_keys_subtitle": "Generieren Sie API-Schlüssel für den Zugriff auf Ihren eigenen Account.", + "generate_new_api_key": "Neuen API-Schlüssel generieren", + "create_api_key": "API-Schlüssel erstellen", + "personal_note": "Diesen Schlüssel benennen", + "personal_note_placeholder": "z. B. „Entwicklung“", + "api_key_no_note": "API-Schlüssel ohne Namen", + "api_key_never_expires": "Dieser API-Schlüssel hat kein Ablaufdatum", + "edit_api_key": "API-Schlüssel bearbeiten", + "never_expire_key": "Läuft niemals ab", + "delete_api_key": "API-Schlüssel widerrufen", + "success_api_key_created": "API-Schlüssel erfolgreich erstellt", + "success_api_key_edited": "API-Schlüssel erfolgreich aktualisiert", + "create": "Erstellen", + "success_api_key_created_bold_tagline": "Speichern Sie diesen API-Schlüssel an einem sicheren Speicherort ab.", + "you_will_only_view_it_once": "Sie werden ihn nicht mehr anzeigen können, wenn Sie dieses Dialogfenster schließen.", + "copy_to_clipboard": "In Zwischenablage kopieren", + "enabled_after_update": "Nach der Aktualisierung aktiviert", + "enabled_after_update_description": "Der private Link funktioniert nach dem Speichern", + "confirm_delete_api_key": "Diesen API-Schlüssel widerrufen", + "revoke_api_key": "API-Schlüssel widerrufen", + "api_key_copied": "API-Schlüssel kopiert!", + "delete_api_key_confirm_title": "Diesen API-Schlüssel dauerhaft von Ihrem Konto entfernen?", + "copy": "Kopieren", + "expire_date": "Ablaufdatum", + "expired": "Abgelaufen", + "never_expires": "Läuft niemals ab", + "expires": "Läuft ab", + "request_reschedule_booking": "Anfrage zur Neuplanung Ihrer Buchung", + "reason_for_reschedule": "Grund für die Neuplanung", + "book_a_new_time": "Neue Zeit buchen", + "reschedule_request_sent": "Anfrage zur Neuplanung gesendet", + "reschedule_modal_description": "Damit wird das geplante Meeting abgesagt. Benachrichtigen Sie die Person, die das Meeting geplant hat, und bitten Sie sie, eine neue Zeit auszuwählen.", + "reason_for_reschedule_request": "Grund für die Anfrage zur Neuplanung", + "send_reschedule_request": "Neuplanung anfordern ", + "edit_booking": "Buchung bearbeiten", + "reschedule_booking": "Buchung neu planen", + "former_time": "Vorherige Zeit", + "confirmation_page_gif": "Gif für Bestätigungsseite", + "search": "Suche", + "impersonate": "Imitieren", + "impersonate_user_tip": "Alle Verwendungen dieser Funktion werden geprüft.", + "impersonating_user_warning": "Imitiere den User {{user}}.", + "impersonating_stop_instructions": "<0>Klicke hier um zu stoppen .", + "email_validation_error": "Das sieht nicht nach einer E-Mail-Adresse aus", + "place_where_cal_widget_appear": "Kopieren Sie diesen Code in ihr HTML wo ihr cal-Widget erscheinen soll.", + "copy_code": "Code kopieren", + "code_copied": "Code kopiert!", + "how_you_want_add_cal_site": "Wie möchten Sie Cal zu Ihrer Website hinzufügen?", + "choose_ways_put_cal_site": "Wählen Sie eine der folgenden Möglichkeiten, Cal auf Ihrer Website zu integrieren.", + "setting_up_zapier": "Einrichtung Ihrer Zapier-Integration", + "generate_api_key": "Api Key generieren", + "your_unique_api_key": "Ihr eindeutiger API-Key", + "copy_safe_api_key": "Kopieren Sie diesen API-Key und speichern Sie ihn an einem sicheren Ort. Wenn Sie diesen Key verlieren, müssen Sie einen neuen generieren.", + "zapier_setup_instructions": "<0>Gehen Sie zu: <1>Zapier-Einladungs-Link<1>Logge Sie sich in Ihr Zapier-Konto ein und erstelle einen neuen Zap.<2>Wählen Sie Cal.com als ihre Trigger-App aus. Wählen Sie auch ein Auslöserereignis.<3>Wähle Sie ihr Konto aus und geben dann ihren eindeutigen API-Key ein.<4>Teste Sie den Trigger.<5>Alles fertig!", + "install_zapier_app": "Bitte installieren Sie zuerst die Zapier-App im App Store.", + "go_to_app_store": "Zum App Store", + "calendar_error": "Etwas ist schiefgelaufen. Versuche deinen Kalender mit allen notwendigen Berechtigungen erneut zu verbinden", + "calendar_no_busy_slots": "Keine belegten Zeiten vorhanden" } diff --git a/apps/web/public/static/locales/de/vital.json b/apps/web/public/static/locales/de/vital.json new file mode 100644 index 0000000000..5b663f412e --- /dev/null +++ b/apps/web/public/static/locales/de/vital.json @@ -0,0 +1,13 @@ +{ + "connected_vital_app": "Verbunden mit", + "vital_app_sleep_automation": "Schlaf Terminumbuchunsautomatisierung", + "vital_app_automation_description": "Sie können verschiedene Parameter auswählen, um die Umbuchung basierend auf Ihren Schlafmetriken auszulösen.", + "vital_app_parameter": "Parameter", + "vital_app_trigger": "Auslöser kleiner oder gleich", + "vital_app_save_button": "Einstellungen speichern", + "vital_app_total_label": "Gesamt (Gesamt= rem + leichter Schlaf + tiefer Schlaf)", + "vital_app_duration_label": "Dauer (Dauer = Schlafzeitende - Schlafzeit start)", + "vital_app_hours": "Stunden", + "vital_app_save_success": "Erfolgreich Ihre Vital-Konfigurationen wurden Erfolgreich gespeichert", + "vital_app_save_error": "Ein Fehler ist aufgetreten beim Speichern Ihrer Vital Einstellungen" +} diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index f9ab715349..b299b4d5fc 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -251,6 +251,8 @@ "booking_submitted_recurring": "Your recurring meeting has been submitted", "booking_confirmed": "Your booking has been confirmed", "booking_confirmed_recurring": "Your recurring meeting has been confirmed", + "warning_recurring_event_payment": "Payments are not supported with Recurring Events yet", + "warning_payment_recurring_event": "Recurring events are not supported with Payments yet", "enter_new_password": "Enter the new password you'd like for your account.", "reset_password": "Reset Password", "change_your_password": "Change your password", @@ -348,6 +350,7 @@ "receive_cal_event_meeting_data": "Receive Cal meeting data at a specified URL, in real-time, when this event is scheduled or cancelled.", "responsive_fullscreen_iframe": "Responsive full screen iframe", "loading": "Loading...", + "deleting": "Deleting...", "standard_iframe": "Standard iframe", "iframe_embed": "iframe Embed", "embed_calcom": "The easiest way to embed Cal.com on your website.", @@ -433,6 +436,8 @@ "link_meeting": "Link meeting", "phone_call": "Phone call", "phone_number": "Phone Number", + "attendee_phone_number": "Attendee Phone Number", + "host_phone_number": "Your Phone Number", "enter_phone_number": "Enter phone number", "reschedule": "Reschedule", "reschedule_this": "Reschedule instead", @@ -623,6 +628,7 @@ "calendar_days": "calendar days", "business_days": "business days", "set_address_place": "Set an address or place", + "set_your_phone_number": "Set a phone number for the meeting", "set_link_meeting": "Set a link to the meeting", "cal_invitee_phone_number_scheduling": "Cal will ask your invitee to enter a phone number before scheduling.", "cal_provide_google_meet_location": "Cal will provide a Google Meet location.", @@ -817,7 +823,7 @@ "generate_api_key": "Generate Api Key", "your_unique_api_key": "Your unique API key", "copy_safe_api_key": "Copy this API key and save it somewhere safe. If you lose this key you have to generate a new one.", - "zapier_setup_instructions": "<0>Log into your Zapier account and create a new Zap.<1>Select Cal.com as your Trigger app. Also choose a Trigger event.<2>Choose your account and then enter your Unique API Key.<3>Test your Trigger.<4>You're set!", + "zapier_setup_instructions": "<0>Go to: <1>Zapier Invite Link<1>Log into your Zapier account and create a new Zap.<2>Select Cal.com as your Trigger app. Also choose a Trigger event.<3>Choose your account and then enter your Unique API Key.<4>Test your Trigger.<5>You're set!", "install_zapier_app": "Please first install the Zapier App in the app store.", "go_to_app_store": "Go to App Store", "calendar_error": "Something went wrong, try reconnecting your calendar with all necessary permissions", diff --git a/apps/web/public/static/locales/es/common.json b/apps/web/public/static/locales/es/common.json index 7d59283c94..f231efae55 100644 --- a/apps/web/public/static/locales/es/common.json +++ b/apps/web/public/static/locales/es/common.json @@ -62,9 +62,15 @@ "password_reset_instructions": "Si usted no solicitó esto, puede ignorar este correo electrónico de forma segura y su contraseña no se cambiará.", "event_awaiting_approval_subject": "Esperando aprobación: {{eventType}} con {{name}} en {{date}}", "event_still_awaiting_approval": "Un evento aún está esperando su aprobación", + "booking_submitted_subject": "Reserva enviada: {{eventType}} con {{name}} el {{date}}", "your_meeting_has_been_booked": "Su reunión ha sido reservada", "event_type_has_been_rescheduled_on_time_date": "Tu {{eventType}} con {{name}} ha sido reprogramado a {{time}} ({{timeZone}}) el {{date}}.", "event_has_been_rescheduled": "Tu evento ha sido reprogramado.", + "request_reschedule_title_attendee": "Solicitud para reprogramar su reserva", + "request_reschedule_subtitle": "{{organizer}} ha cancelado la reserva y le ha solicitado que elija otra hora.", + "request_reschedule_title_organizer": "Le ha solicitado a {{attendee}} que reprograme", + "request_reschedule_subtitle_organizer": "Has cancelado la reserva y {{attendee}} deberá elegir una nueva hora de reserva contigo.", + "reschedule_reason": "Motivo de la reprogramación", "hi_user_name": "Hola {{userName}}", "ics_event_title": "{{eventType}} con {{name}}", "new_event_subject": "Nuevo Evento: {{attendeeName}} - {{date}} - {{eventType}}", @@ -83,6 +89,7 @@ "meeting_url": "URL de la reunión", "meeting_request_rejected": "Su solicitud de reunión ha sido rechazada", "rescheduled_event_type_subject": "Reprogramado: {{eventType}} con {{name}} en {{date}}", + "requested_to_reschedule_subject_attendee": "Acción requerida Reprogramar: reserva una nueva hora para {{eventType}} con {{name}}", "rejected_event_type_with_organizer": "Rechazado: {{eventType}} con {{organizer}} en {{date}}", "hi": "Hola", "join_team": "Unirse al Equipo", @@ -286,6 +293,10 @@ "hover_over_bold_times_tip": "Consejo: Supera los tiempos en negrita para una marca de tiempo completa", "start_time": "Hora de Inicio", "end_time": "Hora de finalización", + "buffer_time": "Tiempo de búfer", + "before_event": "Antes del evento", + "after_event": "Después del evento", + "event_buffer_default": "Sin tiempo de búfer", "buffer": "Búfer", "your_day_starts_at": "Tu día empieza a las", "your_day_ends_at": "Tu día termina a las", @@ -301,6 +312,9 @@ "light": "Claro", "dark": "Oscuro", "automatically_adjust_theme": "Ajusta automáticamente el tema según las preferencias de los invitados", + "user_dynamic_booking_disabled": "Actualmente, algunos de los usuarios del grupo tienen desactivadas las reservas dinámicas grupales", + "allow_dynamic_booking_tooltip": "Enlaces de reservas grupales que se pueden crear dinámicamente al añadir varios nombres de usuario con un '+'. Ejemplo: 'cal.com/bailey+peer'", + "allow_dynamic_booking": "Permitir que los asistentes lo reserven a través de reservas dinámicas de grupo", "email": "Correo electrónico", "email_placeholder": "nombre@ejemplo.com", "full_name": "Nombre Completo", @@ -313,9 +327,12 @@ "event_triggers": "Activadores de Eventos", "subscriber_url": "Url del suscriptor", "create_new_webhook": "Crear un nuevo Webhook", + "webhooks": "Webhooks", + "team_webhooks": "Webhooks del equipo", "create_new_webhook_to_account": "Crear un nuevo webhook en tu cuenta", "new_webhook": "Nuevo Webhook", "receive_cal_meeting_data": "Reciba datos de reuniones de Cal en una URL específica, en tiempo real, cuando se programe o cancele un evento.", + "receive_cal_event_meeting_data": "Recibir los datos de la reunión de Cal en una URL especificada, en tiempo real, cuando este evento se programe o se cancele.", "responsive_fullscreen_iframe": "Iframe receptivo a pantalla completa", "loading": "Cargando...", "standard_iframe": "Iframe estándar", @@ -400,10 +417,12 @@ "booking_confirmation": "Confirma tu {{eventTypeTitle}} con {{profileName}}", "booking_reschedule_confirmation": "Reprogramar tu {{eventTypeTitle}} con {{profileName}}", "in_person_meeting": "Reunión en Línea o en Persona", + "link_meeting": "Enlazar reunión", "phone_call": "Llamada Telefónica", "phone_number": "Nª de Telefono", "enter_phone_number": "Introducir Nº de Telefono", "reschedule": "Reprogramar", + "reschedule_this": "Reprogramar en su lugar", "book_a_team_member": "Reservar un miembro del equipo en su lugar", "or": "O", "go_back": "Volver", @@ -437,6 +456,8 @@ "danger_zone": "Zona Peligrosa", "back": "Atrás", "cancel": "Cancelar", + "apply": "Aplicar", + "cancel_event": "Cancelar este evento", "continue": "Continuar", "confirm": "Confirmar", "disband_team": "Disolver Equipo", @@ -506,6 +527,8 @@ "first_day_of_week": "Primer dia de la semana", "single_theme": "Tema", "brand_color": "Color de marca", + "light_brand_color": "Color de marca (Tema claro)", + "dark_brand_color": "Color de marca (Tema oscuro)", "file_not_named": "El archivo no se llama [idOrSlug] / [user]", "create_team": "Crear Equipo", "name": "Nombre", @@ -554,6 +577,8 @@ "type": "Tipo", "edit": "Editar", "add_input": "Agregar una entrada", + "disable_notes": "Ocultar notas en el calendario", + "disable_notes_description": "Por razones de privacidad, las entradas y notas adicionales se ocultarán en la entrada del calendario. Seguirán enviándose a su correo electrónico.", "opt_in_booking": "Reserva pedida", "opt_in_booking_description": "La reserva debe confirmarse manualmente antes de que se envíe a las integraciones y se envíe un correo de confirmación.", "disable_guests": "Desactivar Invitados", @@ -563,6 +588,7 @@ "calendar_days": "Días del Calendário", "business_days": "Días Laborales", "set_address_place": "Establecer una dirección o un lugar", + "set_link_meeting": "Establecer un enlace a la reunión", "cal_invitee_phone_number_scheduling": "Cal le pedirá a su invitado que introduzca un número de teléfono antes de programar.", "cal_provide_google_meet_location": "Cal proporcionará una URL de reunión de Google Meet.", "cal_provide_zoom_meeting_url": "Cal proporcionará una URL de reunión de Zoom.", @@ -570,6 +596,7 @@ "cal_provide_video_meeting_url": "Cal proporcionará una URL de reunión de Daily Video.", "cal_provide_jitsi_meeting_url": "Cal proporcionará una URL de reunión de Jitsi Meet.", "cal_provide_huddle01_meeting_url": "Cal proporcionará una URL de reunión de Huddle01 Web3 Video.", + "cal_provide_teams_meeting_url": "Cal proporcionará una URL de reunión de MS Teams. NOTA: DEBE TENER UNA CUENTA DE TRABAJO O DE EDUCACIÓN", "require_payment": "Requiere Pago", "commission_per_transaction": "Comisión por Transacción", "event_type_updated_successfully_description": "Tu Evento fue Actualizado con Éxito.", @@ -595,6 +622,9 @@ "confirm_delete_account": "Sí, eliminar cuenta", "delete_account_confirmation_message": "¿Estás seguro de que quieres eliminar tu cuenta de Cal.com? Cualquier persona con la que hayas compartido el enlace de tu cuenta ya no podrá reservar con ella y cualquier preferencia que hayas guardado se perderá.", "integrations": "Integraciones", + "apps": "Aplicaciones", + "app_store": "App Store", + "app_store_description": "Conectar personas, tecnología y lugar de trabajo.", "settings": "Ajustes", "event_type_moved_successfully": "El tipo de evento se ha movido correctamente", "next_step": "Saltar paso", @@ -642,6 +672,24 @@ "import_from": "Importar desde", "access_token": "Token de acceso", "visit_roadmap": "Hoja de ruta", + "popular_categories": "Categorías populares", + "trending_apps": "Aplicaciones populares", + "all_apps": "Todas las aplicaciones", + "installed_apps": "Aplicaciones instaladas", + "manage_your_connected_apps": "Administra tus aplicaciones instaladas o cambia la configuración", + "browse_apps": "Ver aplicaciones", + "features": "Características", + "permissions": "Permisos", + "terms_and_privacy": "Términos y privacidad", + "published_by": "Publicado por {{author}}", + "subscribe": "Suscribirse", + "buy": "Comprar", + "install_app": "Instalar aplicación", + "categories": "Categorías", + "pricing": "Precios", + "learn_more": "Más información", + "privacy_policy": "Política de privacidad", + "terms_of_service": "Términos de servicio", "remove": "Eliminar", "add": "Añadir", "verify_wallet": "Verificar billetera", @@ -657,9 +705,68 @@ "prisma_studio_tip_description": "Aprende cómo configurar tu primer usuario", "contact_sales": "Contactar con Ventas", "error_404": "Error 404", + "default": "Predeterminado", + "set_to_default": "Establecer como predeterminado", + "new_schedule_btn": "Nuevo horario", + "add_new_schedule": "Añadir un nuevo horario", + "delete_schedule": "Eliminar horario", + "schedule_created_successfully": "Horario {{scheduleName}} creado con éxito", "availability_updated_successfully": "Disponibilidad actualizada correctamente", + "schedule_deleted_successfully": "Horario eliminado correctamente", + "default_schedule_name": "Horas laborables", + "new_schedule_heading": "Crear un horario de disponibilidad", + "new_schedule_description": "Crear horarios de disponibilidad le permite gestionar la disponibilidad a través de tipos de eventos. Pueden aplicarse a uno o más tipos de eventos.", "requires_ownership_of_a_token": "Requiere la propiedad de un token perteneciente a la siguiente dirección:", "example_name": "Juan Pérez", + "time_format": "Formato de hora", + "12_hour": "12 horas", + "24_hour": "24 horas", + "redirect_success_booking": "Redirigir al reservar ", "you_are_being_redirected": "Serás redirigido a {{ url }} en $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Para poder usar esta función, necesita actualizarse a una cuenta Pro.", + "duplicate": "Duplicar", + "you_can_manage_your_schedules": "Puede administrar sus horarios en la página Disponibilidad.", + "api_keys": "Claves API", + "api_key_modal_subtitle": "Las claves API le permiten realizar llamadas API para su propia cuenta.", + "api_keys_subtitle": "Genere claves API que se usarán para acceder a su propia cuenta.", + "generate_new_api_key": "Generar nueva clave API", + "create_api_key": "Crear una clave API", + "personal_note": "Nombre esta clave", + "personal_note_placeholder": "Ejemplo: Desarrollo", + "api_key_no_note": "Clave API sin nombre", + "api_key_never_expires": "Esta clave API no tiene fecha de caducidad", + "edit_api_key": "Editar clave API", + "never_expire_key": "Nunca caduca", + "delete_api_key": "Revocar clave API", + "success_api_key_created": "Clave API creada con éxito", + "success_api_key_edited": "Clave API actualizada con éxito", + "create": "Crear", + "success_api_key_created_bold_tagline": "Guarde esta clave API en algún lugar seguro.", + "you_will_only_view_it_once": "No podrá volver a verla una vez que cierre esta ventana modal.", + "copy_to_clipboard": "Copiar al portapapeles", + "confirm_delete_api_key": "Revocar esta clave API", + "revoke_api_key": "Revocar clave API", + "api_key_copied": "¡Clave API copiada!", + "delete_api_key_confirm_title": "¿Eliminar permanentemente esta clave de API de tu cuenta?", + "copy": "Copiar", + "expire_date": "Fecha de caducidad", + "expired": "Caducó", + "never_expires": "Nunca caduca", + "expires": "Caduca", + "request_reschedule_booking": "Solicitud para reprogramar tu reserva", + "reason_for_reschedule": "Motivo de la reprogramación", + "book_a_new_time": "Reserva una nueva hora", + "reschedule_request_sent": "Solicitud de reprogramación enviada", + "reschedule_modal_description": "Esto cancelará la reunión programada, notificará al planificador y te pedirá que escojas una nueva hora.", + "reason_for_reschedule_request": "Motivo de la solicitud de reprogramación", + "send_reschedule_request": "Solicitar reprogramación ", + "edit_booking": "Editar reserva", + "reschedule_booking": "Reprogramar reserva", + "former_time": "Hora anterior", + "impersonate": "Suplantar", + "impersonate_user_tip": "Todos los usos de esta característica se auditan.", + "impersonating_user_warning": "Suplantando nombre de usuario \"{{user}}\".", + "impersonating_stop_instructions": "<0>Haga clic aquí para detener.", "zapier_setup_instructions": "<0>Inicia sesión en tu cuenta de Zapier y crea un nuevo Zap.<1>Selecciona Cal.com cómo tu aplicación disparadora. Tambien elige tu evento disparador.<2>Elige tu cuenta e ingresa tu Clave API única.<3>Prueba tu disparador.<4>¡Listo!" } diff --git a/apps/web/public/static/locales/es/vital.json b/apps/web/public/static/locales/es/vital.json index d571e02e7c..da797716e2 100644 --- a/apps/web/public/static/locales/es/vital.json +++ b/apps/web/public/static/locales/es/vital.json @@ -1,13 +1,13 @@ { - "connected_vital_app": "Conectado con", - "vital_app_sleep_automation": "Automatización de reagendado en base al patron de sueño", - "vital_app_automation_description": "Puedes seleccionar diferentes parámetros para activar el reagendado automático en base a tus patrones de sueño.", - "vital_app_parameter": "Parámetro", - "vital_app_trigger": "Activar cuando sea igual o menor que", - "vital_app_save_button": "Guardar configuración", - "vital_app_total_label": "Total (total = rem + sueño ligero + sueño profundo)", - "vital_app_duration_label": "Duration (duration = Hora que te levantaste de la cama - Hora que te acostaste en la cama)", - "vital_app_hours": "horas", - "vital_app_save_success": "Fue un éxito el guardado de tus configuraciones de App Vital", - "vital_app_save_error": "Ocurrió un error al intentar guardar tus configuraciones de App Vital" + "connected_vital_app": "Conectado con", + "vital_app_sleep_automation": "Automatización de reagendado en base al patron de sueño", + "vital_app_automation_description": "Puedes seleccionar diferentes parámetros para activar el reagendado automático en base a tus patrones de sueño.", + "vital_app_parameter": "Parámetro", + "vital_app_trigger": "Activar cuando sea igual o menor que", + "vital_app_save_button": "Guardar configuración", + "vital_app_total_label": "Total (total = rem + sueño ligero + sueño profundo)", + "vital_app_duration_label": "Duration (duration = Hora que te levantaste de la cama - Hora que te acostaste en la cama)", + "vital_app_hours": "horas", + "vital_app_save_success": "Fue un éxito el guardado de tus configuraciones de App Vital", + "vital_app_save_error": "Ocurrió un error al intentar guardar tus configuraciones de App Vital" } diff --git a/apps/web/public/static/locales/fr/common.json b/apps/web/public/static/locales/fr/common.json index 0b5920f680..b8c3e219cc 100644 --- a/apps/web/public/static/locales/fr/common.json +++ b/apps/web/public/static/locales/fr/common.json @@ -2,6 +2,8 @@ "trial_days_left": "Il vous reste $t(day, {\"count\": {{days}} }) pour votre essai PRO", "day": "{{count}} jour", "day_plural": "{{count}} jours", + "second": "{{count}} seconde", + "second_plural": "{{count}} secondes", "upgrade_now": "Mettre à niveau maintenant", "accept_invitation": "Accepter l'invitation", "calcom_explained": "Cal.com est l’alternative open source à Calendly vous permettant de contrôler vos propres données, votre flux de travail, et l'apparence.", @@ -64,6 +66,11 @@ "your_meeting_has_been_booked": "Votre réunion a été réservée", "event_type_has_been_rescheduled_on_time_date": "Votre {{eventType}} avec {{name}} a été reporté à {{time}} ({{timeZone}}) le {{date}}.", "event_has_been_rescheduled": "Votre évènement a été reprogrammé.", + "request_reschedule_title_attendee": "Demande de report de votre réservation", + "request_reschedule_subtitle": "{{organizer}} a annulé la réservation et vous a demandé de choisir un autre moment.", + "request_reschedule_title_organizer": "Vous avez demandé à {{attendee}} de reporter", + "request_reschedule_subtitle_organizer": "Vous avez annulé la réservation et {{attendee}} doit choisir un nouvel horaire de réservation avec vous.", + "reschedule_reason": "Raison du report", "hi_user_name": "Bonjour {{userName}}", "ics_event_title": "{{eventType}} avec {{name}}", "new_event_subject": "Nouvel événement : {{attendeeName}} - {{date}} - {{eventType}}", @@ -82,6 +89,7 @@ "meeting_url": "URL de la réunion", "meeting_request_rejected": "Votre demande de réunion a été refusée", "rescheduled_event_type_subject": "Reporté : {{eventType}} avec {{name}} le {{date}}", + "requested_to_reschedule_subject_attendee": "Action requise - Report : veuillez réserver une nouvelle heure pour {{eventType}} avec {{name}}", "rejected_event_type_with_organizer": "Refusé : {{eventType}} avec {{organizer}} le {{date}}", "hi": "Bonjour", "join_team": "Rejoindre l'équipe", @@ -614,6 +622,8 @@ "confirm_delete_account": "Oui, supprimer le compte", "delete_account_confirmation_message": "Êtes-vous sûr de vouloir supprimer votre compte Cal.com ? Toute personne avec qui vous avez partagé le lien de votre compte ne pourra plus réserver en utilisant ce lien et toutes les préférences que vous avez enregistrées seront perdues.", "integrations": "Intégrations", + "apps": "Applications", + "app_store": "App Store", "app_store_description": "Connecter les personnes, la technologie et l'espace de travail.", "settings": "Paramètres", "event_type_moved_successfully": "Le type d'évènement a été déplacé avec succès", @@ -669,6 +679,7 @@ "manage_your_connected_apps": "Gérer vos applications installées ou modifier les paramètres", "browse_apps": "Parcourir les Apps", "features": "Fonctionnalités", + "permissions": "Permissions", "terms_and_privacy": "Conditions et confidentialité", "published_by": "Publié par {{author}}", "subscribe": "S'abonner", @@ -710,6 +721,47 @@ "time_format": "Format horaire", "12_hour": "12 heures", "24_hour": "24 heures", + "redirect_success_booking": "Rediriger vers la réservation ", + "you_are_being_redirected": "Vous êtes redirigé vers {{ url }} dans $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://exemple.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Pour pouvoir utiliser cette fonctionnalité, vous devez passer à un compte Pro.", "duplicate": "Dupliquer", - "you_can_manage_your_schedules": "Vous pouvez gérer vos calendriers sur la page Disponibilité." + "you_can_manage_your_schedules": "Vous pouvez gérer vos calendriers sur la page Disponibilité.", + "api_keys": "Clés API", + "api_key_modal_subtitle": "Les clés API vous permettent d'effectuer des appels API pour votre propre compte.", + "api_keys_subtitle": "Générer des clés API à utiliser pour accéder à votre propre compte.", + "generate_new_api_key": "Générer une nouvelle clé API", + "create_api_key": "Créer une clé API", + "personal_note": "Nommer cette clé", + "personal_note_placeholder": "Par exemple : développement", + "api_key_no_note": "Clé API sans nom", + "api_key_never_expires": "Cette clé API n'a pas de date d'expiration", + "edit_api_key": "Modifier la clé API", + "never_expire_key": "N'expire jamais", + "delete_api_key": "Révoquer la clé API", + "success_api_key_created": "Clé API créée avec succès", + "success_api_key_edited": "Clé API mise à jour avec succès", + "create": "Créer", + "success_api_key_created_bold_tagline": "Enregistrez cette clé API dans un endroit sûr.", + "you_will_only_view_it_once": "Vous ne serez plus en mesure de la voir une fois que vous fermerez ce modal.", + "copy_to_clipboard": "Copier dans le presse-papiers", + "confirm_delete_api_key": "Révoquer cette clé API", + "revoke_api_key": "Révoquer la clé API", + "api_key_copied": "Clé API copiée !", + "delete_api_key_confirm_title": "Supprimer définitivement cette clé API de votre compte ?", + "copy": "Copier", + "expire_date": "Date d'expiration", + "expired": "Expirée", + "never_expires": "N'expire jamais", + "expires": "Expire", + "request_reschedule_booking": "Demande de report de votre réservation", + "reason_for_reschedule": "Raison du report", + "book_a_new_time": "Réserver une nouvelle heure", + "reschedule_request_sent": "Demande de report envoyée", + "reschedule_modal_description": "Cela annulera la réunion prévue, notifiera le planificateur et lui demandera de choisir une nouvelle heure.", + "reason_for_reschedule_request": "Raison de la demande de report", + "send_reschedule_request": "Demander un report ", + "edit_booking": "Modifier la réservation", + "reschedule_booking": "Reporter la réservation", + "former_time": "Ancienne heure" } diff --git a/apps/web/public/static/locales/fr/vital.json b/apps/web/public/static/locales/fr/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/fr/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/he/common.json b/apps/web/public/static/locales/he/common.json index 0538f86da2..89db4515f9 100644 --- a/apps/web/public/static/locales/he/common.json +++ b/apps/web/public/static/locales/he/common.json @@ -1,34 +1,771 @@ { + "trial_days_left": "נותרו לך $t(day, {\"count\": {{days}} }) לשימוש בגרסת הניסיון של גרסת ה-PRO", "day": "{{count}} יום", "day_plural": "{{count}} ימים", + "second": "שנייה {{count}}", + "second_plural": "{{count}} שניות", "upgrade_now": "שדרג עכשיו", + "accept_invitation": "אישור הזמנה", + "calcom_explained": "Cal.com הוא חלופת הקוד הפתוח של Calendly המאפשרת לך לשלוט בנתונים, בתהליך העבודה ובמראה של האתר שלך.", "have_any_questions": "יש לך שאלות? אנחנו כאן כדי לעזור.", + "reset_password_subject": "Cal.com: הנחיות לאיפוס סיסמה", + "event_declined_subject": "נדחה: {{eventType}} עם {{name}} בתאריך {{date}}", + "event_cancelled_subject": "בוטל: {{eventType}} עם {{name}} בתאריך {{date}}", + "event_request_declined": "בקשת האירוע שלך נדחתה", + "event_request_cancelled": "האירוע המתוכנן בוטל", "organizer": "מארגן", + "need_to_reschedule_or_cancel": "רוצה לקבוע מועד חדש או לבטל?", + "cancellation_reason": "הסיבה לביטול", + "cancellation_reason_placeholder": "מדוע בחרת לבטל? (אופציונלי)", + "rejection_reason": "הסיבה לדחייה", + "rejection_reason_title": "לדחות את בקשת ההזמנה?", + "rejection_reason_description": "בטוח שברצונך לדחות את ההזמנה? אנחנו ניידע את האדם שניסה להזמין. יש לך אפשרות להוסיף את הסיבה לדחייה למטה.", + "rejection_confirmation": "לדחות את ההזמנה", "manage_this_event": "נהל האירוע הזה", + "your_event_has_been_scheduled": "נקבע מועד לאירוע שלך", + "accept_our_license": "יש לאשר את תנאי הרישיון שלנו על ידי שינוי המשתנה מסוג ‎.env בשם <1>NEXT_PUBLIC_LICENSE_CONSENT ל-'{{agree}}'.", + "remove_banner_instructions": "להסרת הבאנר, יש לפתוח את קובץ ה-‎.env ולשנות את המשתנה <1>NEXT_PUBLIC_LICENSE_CONSENT ל-'{{agree}}'.", + "error_message": "הודעת השגיאה הייתה: '{{errorMessage}}'", + "refund_failed_subject": "ההחזר הכספי נכשל: {{name}} - {{date}} - {{eventType}}", + "refund_failed": "ההחזר הכספי עבור האירוע {{eventType}} עם {{userName}} בתאריך {{date}} נכשל.", + "check_with_provider_and_user": "יש לברר עם ספק התשלום ועם {{user}} כיצד לטפל בעניין.", + "a_refund_failed": "ההחזר הכספי נכשל", + "awaiting_payment_subject": "בהמתנה לתשלום: {{eventType}} עם {{name}} בתאריך {{date}}", + "meeting_awaiting_payment": "התשלום על הפגישה שלך טרם בוצע", + "help": "עזרה", + "price": "מחיר", + "paid": "שולם", + "refunded": "ההחזר הכספי נשלח", + "pay_later_instructions": "כמו כן, קיבלת הודעת דוא\"ל עם קישור זה, למקרה שתרצה לשלם מאוחר יותר.", + "payment": "תשלום", + "missing_card_fields": "שדות פרטי כרטיס חסרים", "pay_now": "שלם עכשיו", + "codebase_has_to_stay_opensource": "בין אם בוצעו שינויים בבסיס הקוד ובין אם לא, הקוד חייב להישאר פתוח", + "cannot_repackage_codebase": "לא ניתן לארוז מחדש או למכור את בסיס הקוד", + "acquire_license": "כדי להסיר את התנאים האלה, ניתן לקנות רישיון מסחרי על ידי שליחת דוא\"ל", + "terms_summary": "סיכום התנאים", + "open_env": "יש לפתוח את ‎.env ולאשר את הרישיון שלנו", + "env_changed": "שיניתי את ה-‎.env שלי", + "accept_license": "אישור הרישיון", + "still_waiting_for_approval": "אירוע עדיין ממתין לאישור", + "event_is_still_waiting": "בקשת האירוע עדיין בהמתנה: {{attendeeName}} - {{date}} - {{eventType}}", "no_more_results": "אין יותר תוצאות", "load_more_results": "הצג עוד תוצאות", + "integration_meeting_id": "{{integrationName}} מזהה פגישה: {{meetingId}}", + "confirmed_event_type_subject": "אושר: {{eventType}} עם {{name}} בתאריך {{date}}", + "new_event_request": "בקשה חדשה לאירוע: {{attendeeName}} - {{date}} - {{eventType}}", + "confirm_or_reject_request": "נא לאשר או לדחות את הבקשה", + "check_bookings_page_to_confirm_or_reject": "כדי לאשר או לדחות את ההזמנה, יש לעבור לדף ההזמנות.", + "event_awaiting_approval": "אירוע ממתין לאישורך", + "someone_requested_an_event": "מישהו ביקש לקבוע מועד לאירוע בלוח השנה שלך.", + "someone_requested_password_reset": "מישהו ביקש קישור כדי לשנות את הסיסמה שלך.", + "password_reset_instructions": "אם הבקשה לא היתה שלך, ניתן להתעלם מהודעת הדוא\"ל הזו בבטחה, והסיסמה שלך לא תשתנה.", + "event_awaiting_approval_subject": "בהמתנה לאישור: {{eventType}} עם {{name}} בתאריך {{date}}", + "event_still_awaiting_approval": "אירוע עדיין ממתין לאישורך", + "booking_submitted_subject": "ההזמנה נשלחה: {{eventType}} עם {{name}} בתאריך {{date}}", + "your_meeting_has_been_booked": "הפגישה שלך הוזמנה", + "event_type_has_been_rescheduled_on_time_date": "המועד של ה{{eventType}} שלך עם {{name}} הועבר לשעה {{time}} (שעון {{timeZone}}) בתאריך {{date}}.", + "event_has_been_rescheduled": "עודכן – מועד האירוע שלך השתנה", + "request_reschedule_title_attendee": "בקשה לשינוי מועד ההזמנה", + "request_reschedule_subtitle": "{{organizer}} ביטל/ה את ההזמנה וביקש/ה שתבחר/י מועד אחר.", + "request_reschedule_title_organizer": "ביקשת מ{{attendee}} לקבוע מועד חדש", + "request_reschedule_subtitle_organizer": "ביטלת את ההזמנה ו-{{attendee}} צריך/ה לתאם איתך מועד חדש.", + "reschedule_reason": "הסיבה לשינוי המועד", "hi_user_name": "שלום {{name}}", + "ics_event_title": "{{eventType}} עם {{name}}", + "new_event_subject": "אירוע חדש: {{attendeeName}} - {{date}} - {{eventType}}", + "join_by_entrypoint": "ניתן להצטרף עד {{entryPoint}}", "notes": "הערות", "manage_my_bookings": "נהל ההזמנות שלי", "need_to_make_a_change": "צריך לעשות שינוי?", + "new_event_scheduled": "נקבע מועד לאירוע חדש.", "invitee_email": "אימייל המוזמן", + "invitee_timezone": "אזור הזמן של האדם שהוזמן", "event_type": "סוג אירוע", - "enter_meeting": "הצטרף לפגישה", + "enter_meeting": "להצטרף לפגישה", + "video_call_provider": "ספק שיחות וידאו", "meeting_id": "מזהה פגישה", "meeting_password": "סיסמת הפגישה", "meeting_url": "קישור הפגישה", "meeting_request_rejected": "הפגישה שלך בוטלה", + "rescheduled_event_type_subject": "נקבע מועד חדש: {{eventType}} עם {{name}} בתאריך {{date}}", + "requested_to_reschedule_subject_attendee": "הפעולה חייבה קביעת מועד חדש: יש לקבוע מועד חדש עבור {{eventType}} עם {{name}}", + "rejected_event_type_with_organizer": "נדחה: {{eventType}} עם {{organizer}} בתאריך {{date}}", + "hi": "שלום", "join_team": "הצטרף לצוות", "manage_this_team": "נהל הצוות הנוכחי", "team_info": "מידע על הצוות", + "request_another_invitation_email": "אם אתה מעדיף לא להשתמש בכתובת {{toEmail}} ככתובת הדוא\"ל שלך עבור Cal.com או שכבר יש לך חשבון Cal.com, יש לבקש לקבל הזמנה נוספת לכתובת הדוא\"ל הרצויה.", + "you_have_been_invited": "הוזמנת להצטרף לצוות {{teamName}}", + "user_invited_you": "{{user}} הזמין/ה אותך להצטרף לצוות {{team}} ב-Cal.com", + "hidden_team_member_title": "אתה מוסתר בצוות זה", + "hidden_team_member_message": "לא בוצע תשלום עבור המקום שלך. ניתן לשדרג ל-Pro או ליידע את הבעלים של הצוות שהוא או היא יכולים לשלם עבור המקום שלך.", + "hidden_team_owner_message": "נדרש חשבון Pro כדי להשתמש בתכונות הצוותים. תהיה מוסתר עד שתבצע שידרוג.", + "link_expires": "נ.ב. תוקף הקישור יפוג תוך {{expiresIn}} שעות.", + "upgrade_to_per_seat": "שדרג למינוי לפי מקום", + "team_upgrade_seats_details": "לא התבצע תשלום עבור {{unpaidCount}} מקומות מתוך {{memberCount}} חברי הצוות שלך. תמורת ${{seatPrice}} לחודש למקום, עלות המינוי הכוללת המשוערת שלך היא ${{totalCost}} לחודש.", + "team_upgraded_successfully": "הצוות שלך שודרג בהצלחה!", + "use_link_to_reset_password": "נא להשתמש בקישור הבא כדי לאפס את הסיסמה", "hey_there": "שלום לך,", "forgot_your_password_calcom": "שכחתה את הסיסמה? - cal.com", + "event_type_title": "{{eventTypeTitle}} | סוג האירוע", + "delete_webhook_confirmation_message": "בטוח שברצונך למחוק את ה-Webhook הזה? אם אירוע יבוטל או שייקבע עבורו מועד, לא תקבל יותר נתוני פגישות מ-Cal.com בזמן אמת או בכתובת ה-URL שציינת.", + "confirm_delete_webhook": "כן, אני רוצה למחוק את ה-Webhook", "edit_webhook": "ערוך Webhook", + "delete_webhook": "מחיקת Webhook", "webhook_status": "סטטוס Webhook", "webhook_enabled": "וובהוק מופעל", "webhook_disabled": "וובהוק לא פעיל", + "webhook_response": "תגובת Webhook", + "webhook_test": "בדיקת Webhook", + "manage_your_webhook": "ניהול ה-Webhook שלך", + "webhook_created_successfully": "יצירת ה-Webhook בוצעה בהצלחה!", + "webhook_updated_successfully": "עדכון ה-Webhook בוצע בהצלחה!", + "webhook_removed_successfully": "הסרת ה-Webhook בוצעה בהצלחה!", + "payload_template": "תבנית מטען ייעודי (payload)", + "dismiss": "ביטול", + "no_data_yet": "עדיין אין נתונים", + "ping_test": "בדיקת Ping", + "add_to_homescreen": "יש להוסיף את האפליקציה למסך הבית כדי לגשת אליה מהר יותר ולשפר את חוויית השימוש בה.", + "upcoming": "בקרוב", + "past": "בעבר", + "choose_a_file": "בחירת קובץ...", + "upload_image": "העלאת תמונה", + "upload_target": "העלאת {{target}}", + "no_target": "ללא {{target}}", + "slide_zoom_drag_instructions": "נא להחליק כדי להתקרב ולגרור כדי לשנות את המיקום", + "view_notifications": "צפייה בהתראות", + "view_public_page": "צפייה בדף הציבורי", + "sign_out": "יציאה", + "add_another": "הוסף לוח זמנים נוסף", + "until": "עד", + "powered_by": "מופעל על ידי", + "unavailable": "לא זמין", + "set_work_schedule": "הגדרת לוח הזמנים שלך בעבודה", + "change_bookings_availability": "שינוי מועדי הזמינות שלך להזמנות", + "select": "נא לבחור...", + "2fa_confirm_current_password": "כדי להתחיל, יש לאשר את הסיסמה הנוכחית.", + "2fa_scan_image_or_use_code": "סרוק את התמונה הבאה באמצעות אפליקציית האימות (authenticator) בטלפון או הזן את קוד הטקסט ידנית.", + "text": "טקסט", + "multiline_text": "טקסט מרובה שורות", + "number": "מספר", + "checkbox": "תיבת סימון", + "is_required": "נדרש", + "required": "נדרש", + "input_type": "סוג הקלט", + "rejected": "נדחה", + "unconfirmed": "לא אושר", + "guests": "אורחים", + "guest": "אורח", + "web_conferencing_details_to_follow": "פרטי שיחת הוועידה המקוונת, הכלולים בדוא\"ל האישור, שיש לפעול על פיהם.", + "the_username": "שם המשתמש", + "username": "שם משתמש", + "is_still_available": "עדיין זמין.", + "documentation": "תיעוד", + "documentation_description": "למד כיצד ניתן לשלב את הכלים שלנו באפליקציה שלך", + "api_reference": "הפניית ממשק תכנות יישומים (API)", + "api_reference_description": "הפניית ממשק תכנות יישומים (API) מלאה אל הספריות שלנו", + "blog": "בלוג", + "blog_description": "לעיון בחדשות ובמאמרים האחרונים שלנו", + "join_our_community": "הצטרף לקהילה שלנו", + "join_our_slack": "הצטרף לצ׳אט שלנו ב-Slack", + "claim_username_and_schedule_events": "קבל את שם המשתמש שלך והתחל לקבוע מועדים לאירועים", + "popular_pages": "דפים פופולריים", + "register_now": "להירשם עכשיו", + "register": "הרשמה", + "page_doesnt_exist": "דף זה אינו קיים.", + "check_spelling_mistakes_or_go_back": "ודא כי שאין שגיאות איות או חזור/י לדף הקודם.", + "404_page_not_found": "404: לא ניתן למצוא את הדף הזה.", + "getting_started": "תחילת העבודה", + "15min_meeting": "פגישה של 15 דקות", + "30min_meeting": "פגישה של 30 דקות", + "secret_meeting": "פגישה חשאית", + "login_instead": "להיכנס במקום זאת", + "already_have_an_account": "כבר יש לך חשבון?", + "create_account": "צור חשבון", + "confirm_password": "אישור סיסמה", + "create_your_account": "צור את החשבון שלך", + "sign_up": "הרשמה", + "youve_been_logged_out": "יצאת מהמערכת", + "hope_to_see_you_soon": "מקווים לראותך שוב בקרוב!", + "logged_out": "יצאת", + "please_try_again_and_contact_us": "נא לנסות שוב. אם הבעיה נמשכת, ניתן לפנות אלינו.", + "incorrect_2fa_code": "קוד האימות הדו-גורמי שגוי.", + "no_account_exists": "לא קיים חשבון עם כתובת דוא\"ל זו.", + "2fa_enabled_instructions": "אימות דו-גורמי מופעל. הזן את הקוד בן שש הספרות שמוצג באפליקציית האימות (authenticator) שלך.", + "2fa_enter_six_digit_code": "הזן את הקוד בן שש הספרות שמוצג באפליקציית האימות (authenticator) שלך להלן.", + "create_an_account": "יצירת חשבון", "dont_have_an_account": "אין לך חשבון עדיין?", "2fa_code": "קוד אימות דו-שלבי", - "sign_in": "התחבר" + "sign_in_account": "כניסה לחשבון", + "sign_in": "התחבר", + "go_back_login": "חזרה לדף הכניסה", + "error_during_login": "אירעה שגיאה בעת הכניסה. נא לחזור אל מסך הכניסה ולנסות שוב.", + "request_password_reset": "בקשה לאיפוס סיסמה", + "forgot_password": "שכחתי את הסיסמה", + "forgot": "שכחת?", + "done": "בוצע", + "check_email_reset_password": "בדוק/י בתיבת הדוא\"ל – שלחנו לך קישור לאיפוס הסיסמה.", + "finish": "סיום", + "few_sentences_about_yourself": "מספר משפטים אודותיך. המידע הזה יופיע בדף ה-URL האישי שלך.", + "nearly_there": "כמעט סיימנו", + "nearly_there_instructions": "דבר אחרון: תיאור קצר אודותיך בתוספת תמונה ממש עוזרים להשיג הזמנות ומאפשרים לאנשים לדעת עם מי הם עומדים להיפגש.", + "set_availability_instructions": "הגדר טווחי זמן שבהם אתה זמין באופן קבוע. ניתן יהיה ליצור טווחי זמן נוספים מאוחר יותר ולהקצות אותם ללוחות שנה אחרים.", + "set_availability": "ציין את הזמינות שלך", + "continue_without_calendar": "להמשיך בלי לוח שנה", + "connect_your_calendar": "קשר את לוח השנה שלך", + "connect_your_calendar_instructions": "קשר את לוח השנה שלך לצפייה בזמנים עמוסים ובאירועים חדשים באופן אוטומטי ברגע שנקבע מועד עבורם.", + "set_up_later": "להגדיר מאוחר יותר", + "current_time": "השעה הנוכחית", + "welcome": "ברוך הבא", + "welcome_to_calcom": "ברוך הבא אל Cal.com", + "welcome_instructions": "אמור לנו באיזה שם ברצונך שנקרא לך ובאיזה אזור זמן אתה נמצא. ניתן יהיה לערוך את הפרטים האלה מאוחר יותר.", + "connect_caldav": "חיבור לשרת CalDav", + "credentials_stored_and_encrypted": "פרטי הכניסה שלך יישמרו ויוצפנו.", + "connect": "להתחבר", + "try_for_free": "לנסות בחינם", + "create_booking_link_with_calcom": "צור קישור הזמנה משלך ב-Cal.com", + "who": "מי", + "what": "מה", + "when": "מתי", + "where": "היכן", + "add_to_calendar": "הוספה ללוח השנה", + "other": "אחר", + "emailed_you_and_attendees": "שלחנו לך ולמשתתפים האחרים הודעת דוא\"ל עם הזמנה בלוח השנה הכוללת את כל הפרטים.", + "emailed_you_and_any_other_attendees": "שלחנו לך ולמשתתפים האחרים, אם ישנם כאלה, הודעת דוא\"ל עם הפרטים האלה.", + "needs_to_be_confirmed_or_rejected": "ההזמנה שלך עדיין ממתינה לאישור או לדחייה.", + "user_needs_to_confirm_or_reject_booking": "{{user}} עדיין צריך/ה לאשר או לדחות את ההזמנה.", + "meeting_is_scheduled": "נקבע מועד לפגישה זו", + "submitted": "ההזמנה שלך נשלחה", + "booking_submitted": "ההזמנה שלך נשלחה", + "booking_confirmed": "ההזמנה שלך אושרה", + "enter_new_password": "נא להזין את הסיסמה החדשה שבה ברצונך להשתמש עבור החשבון.", + "reset_password": "איפוס סיסמה", + "change_your_password": "שינוי סיסמה", + "try_again": "לנסות שוב", + "request_is_expired": "תוקף הבקשה פג.", + "reset_instructions": "הזן את כתובת הדוא\"ל המשויכת לחשבון שלך, ואנו נשלח לך קישור לאיפוס הסיסמה.", + "request_is_expired_instructions": "תוקף הבקשה פג. נא לחזור אחורה ולהזין את כתובת הדוא\"ל המשויכת לחשבון שלך, ואנו נשלח לך קישור נוסף לאיפוס הסיסמה.", + "whoops": "אופס", + "login": "כניסה", + "success": "הפעולה בוצעה בהצלחה", + "failed": "הפעולה נכשלה", + "password_has_been_reset_login": "הסיסמה שלך אופסה. עכשיו ניתן להיכנס עם הסיסמה החדשה.", + "unexpected_error_try_again": "אירעה שגיאה בלתי צפויה. נא לנסות שוב.", + "sunday_time_error": "שעה לא תקפה ביום ראשון", + "monday_time_error": "שעה לא תקפה ביום שני", + "tuesday_time_error": "שעה לא תקפה ביום שלישי", + "wednesday_time_error": "שעה לא תקפה ביום רביעי", + "thursday_time_error": "שעה לא תקפה ביום חמישי", + "friday_time_error": "שעה לא תקפה ביום שישי", + "saturday_time_error": "שעה לא תקפה ביום שבת", + "error_end_time_before_start_time": "שעת הסיום לא יכולה להיות לפני שעת ההתחלה", + "error_end_time_next_day": "שעת הסיום לא יכולה לחרוג מ-24 שעות", + "back_to_bookings": "בחזרה להזמנות", + "free_to_pick_another_event_type": "ניתן לבחור אירוע אחר בכל עת.", + "cancelled": "בוטלו", + "cancellation_successful": "פעולת הביטול בוצעה בהצלחה", + "really_cancel_booking": "בטוח שברצונך לבטל את ההזמנה?", + "cannot_cancel_booking": "לא ניתן לבטל את ההזמנה הזו", + "reschedule_instead": "במקום זאת, ניתן לקבוע מועד אחר.", + "event_is_in_the_past": "האירוע חל בעבר", + "error_with_status_code_occured": "אירעה שגיאה עם קוד הסטטוס {{status}}.", + "booking_already_cancelled": "ההזמנה הזו כבר בוטלה", + "go_back_home": "חזרה אל מסך הבית", + "or_go_back_home": "או חזור אל מסך הבית", + "no_availability": "לא זמין", + "no_meeting_found": "לא נמצאה פגישה", + "no_meeting_found_description": "פגישה זו אינה קיימת. נא לפנות אל הבעלים של הפגישה לקבלת קישור מעודכן.", + "no_status_bookings_yet": "עדיין אין הזמנות בסטטוס {{status}}", + "no_status_bookings_yet_description": "אין לך הזמנות בסטטוס {{status}}. {{description}}", + "event_between_users": "{{eventName}} בין {{host}} ו-{{attendeeName}}", + "bookings": "הזמנות", + "bookings_description": "צפה באירועים הקרובים ובאירועים שעברו שהוזמנו דרך קישורי סוגי האירועים שלך.", + "upcoming_bookings": "ברגע שמישהו יקבע איתך פגישה, המועד שנקבע יוצג כאן.", + "past_bookings": "ההזמנות הקודמות שלך יוצגו כאן.", + "cancelled_bookings": "ההזמנות המבוטלות שלך יוצגו כאן.", + "on": "בתאריך", + "and": "וגם", + "calendar_shows_busy_between": "בלוח השנה מוצג כי אתה עסוק בין", + "troubleshoot": "פתרון בעיות", + "troubleshoot_description": "גלה מדוע שעות מסוימות מוגדרות כזמינות ואחרות חסומות.", + "overview_of_day": "הנה מבט כולל על היום שלך בתאריך", + "hover_over_bold_times_tip": "טיפ: אם מזיזים את העכבר מעל המועדים המודגשים, ניתן לראות את חותם הזמן השלם", + "start_time": "שעת התחלה", + "end_time": "שעת סיום", + "buffer_time": "זמן המתנה בין פגישות", + "before_event": "לפני האירוע", + "after_event": "לאחר האירוע", + "event_buffer_default": "ללא זמן המתנה בין פגישות", + "buffer": "המתנה בין פגישות", + "your_day_starts_at": "היום שלך מתחיל בשעה", + "your_day_ends_at": "היום שלך מסתיים בשעה", + "launch_troubleshooter": "הפעלת פותר הבעיות", + "troubleshoot_availability": "פתור בעיות בענייני הזמינות שלך כדי לבדוק מדוע חלונות הזמן שלך מוצגים כפי שהם מוצגים.", + "change_available_times": "שינוי המועדים הזמינים", + "change_your_available_times": "שנה את המועדים שבהם אתה זמין", + "change_start_end": "שינוי שעות ההתחלה והסיום של היום שלך", + "change_start_end_buffer": "הגדר את שעות ההתחלה והסיום של היום שלך, וזמן המתנה מינימלי בין פגישות.", + "current_start_date": "נכון לעכשיו, היום שלך מוגדר להתחיל בשעה", + "start_end_changed_successfully": "שעות ההתחלה והסיום של היום שלך שונו בהצלחה.", + "and_end_at": "ומסתיים בשעה", + "light": "בהיר", + "dark": "כהה", + "automatically_adjust_theme": "התאמת ערכת הנושא באופן אוטומטי לפי העדפות המוזמן/ת", + "user_dynamic_booking_disabled": "אצל חלק מהמשתמשים בקבוצה מושבתת כרגע האפשרות של הזמנות קבוצתיות דינמיות", + "allow_dynamic_booking_tooltip": "קישורי הזמנות קבוצתיות שניתן ליצור באופן דינמי על ידי הוספת שמות משתמשים מרובים עם '+', למשל: 'cal.com/bailey+peer'", + "allow_dynamic_booking": "לאפשר למשתתפים לשריין איתך פגישה באמצעות הזמנות קבוצתיות דינמיות", + "email": "דוא\"ל", + "email_placeholder": "iisraeli@example.com", + "full_name": "שם מלא", + "browse_api_documentation": "עיון במסמכי ממשק תכנות היישומים (API) שלנו", + "leverage_our_api": "מומלץ להיעזר בממשק תכנות היישומים (API) שלנו לקבלת שליטה מלאה ויכולת התאמה אישית.", + "create_webhook": "יצירת Webhook", + "booking_cancelled": "ההזמנה בוטלה", + "booking_rescheduled": "מועד ההזמנה השתנה", + "booking_created": "ההזמנה נוצרה", + "event_triggers": "גורמים מפעילים של אירועים", + "subscriber_url": "כתובת URL של המנוי/ה", + "create_new_webhook": "יצירת Webhook חדש", + "webhooks": "רכיבי Webhook", + "team_webhooks": "רכיבי Webhook של הצוות", + "create_new_webhook_to_account": "יצירת Webhook חדש לחשבון", + "new_webhook": "Webhook חדש", + "receive_cal_meeting_data": "קבל נתוני פגישות מ-Cal בכתובת URL שציינת ובזמן אמת, כאשר נקבע מועד לאירוע או כאשר אירוע מבוטל.", + "receive_cal_event_meeting_data": "קבל/י נתוני פגישות מ-Cal בכתובת URL שציינת ובזמן אמת, כאשר נקבע מועד לאירוע זה או כאשר הוא מבוטל.", + "responsive_fullscreen_iframe": "iframe רספונסיבי על מסך מלא", + "loading": "טוען...", + "standard_iframe": "iframe רגיל", + "iframe_embed": "הטמעת iframe", + "embed_calcom": "הדרך הקלה ביותר להטמיע את Cal.com באתר שלך.", + "integrate_using_embed_or_webhooks": "צור אינטגרציה עם אתר האינטרנט שלך באמצעות אפשרויות ההטמעה שלנו' או קבל מידע על הזמנות בזמן אמת באמצעות רכיבי Webhook מותאמים אישית.", + "schedule_a_meeting": "קביעת מועד לפגישה", + "view_and_manage_billing_details": "הצגה וניהול של פרטי החיוב", + "view_and_edit_billing_details": "כאן ניתן להציג ולנהל את פרטי החיוב, וכן לבטל את המינוי.", + "go_to_billing_portal": "מעבר אל פורטל החיובים", + "need_anything_else": "צריך משהו נוסף?", + "further_billing_help": "אם דרוש לך סיוע נוסף בענייני חיוב, צוות התמיכה שלנו כאן כדי לעזור.", + "contact_our_support_team": "יצירת קשר עם צוות התמיכה", + "uh_oh": "אוי, לא!", + "no_event_types_have_been_setup": "משתמש זה עדיין לא הגדיר סוג אירוע.", + "edit_logo": "עריכת לוגו", + "upload_a_logo": "העלאת לוגו", + "remove_logo": "הסרת לוגו", + "enable": "הפעלה", + "code": "קוד", + "code_is_incorrect": "הקוד שגוי.", + "add_an_extra_layer_of_security": "כדאי להוסיף רמת אבטחה נוספת לחשבון, למקרה שהסיסמה שלך תיגנב.", + "2fa": "אימות דו-גורמי", + "enable_2fa": "הפעלת אימות דו-גורמי", + "disable_2fa": "השבתת אימות דו-גורמי", + "disable_2fa_recommendation": "אם עליך להשבית את האימות הדו-גורמי, מומלץ להפעיל אותו שוב בהקדם האפשרי.", + "error_disabling_2fa": "שגיאה בעת השבתת אימות דו-גורמי", + "error_enabling_2fa": "שגיאה בעת הגדרת אימות דו-גורמי", + "security": "אבטחה", + "manage_account_security": "נהל את אבטחת החשבון.", + "password": "סיסמה", + "password_updated_successfully": "עדכון הסיסמה בוצע בהצלחה", + "password_has_been_changed": "שינוי הסיסמה בוצע בהצלחה.", + "error_changing_password": "שגיאה בעת שינוי הסיסמה", + "something_went_wrong": "משהו השתבש.", + "something_doesnt_look_right": "משהו נראה לא כשורה?", + "please_try_again": "נא לנסות שוב.", + "super_secure_new_password": "הסיסמה החדשה והמאובטחת שלך", + "new_password": "סיסמה חדשה", + "your_old_password": "הסיסמה הקודמת", + "current_password": "הסיסמה הנוכחית", + "change_password": "שינוי סיסמה", + "new_password_matches_old_password": "הסיסמה החדשה זהה לישנה. יש לבחור סיסמה אחרת.", + "current_incorrect_password": "הסיסמה הנוכחית שגויה", + "incorrect_password": "הסיסמה שגויה.", + "1_on_1": "אחד על אחד", + "24_h": "24 שעות", + "use_setting": "השתמש בהגדרה", + "am_pm": "לפנה\"צ/אחה\"צ", + "time_options": "אפשרויות זמן", + "january": "ינואר", + "february": "פברואר", + "march": "מרץ", + "april": "אפריל", + "may": "מאי", + "june": "יוני", + "july": "יולי", + "august": "אוגוסט", + "september": "ספטמבר", + "october": "אוקטובר", + "november": "נובמבר", + "december": "דצמבר", + "monday": "יום שני", + "tuesday": "יום שלישי", + "wednesday": "יום רביעי", + "thursday": "יום חמישי", + "friday": "יום שישי", + "saturday": "יום שבת", + "sunday": "יום ראשון", + "all_booked_today": "כל חלונות הזמן להיום הוזמנו.", + "slots_load_fail": "לא ניתן לטעון את חלונות הזמן הזמינים.", + "additional_guests": "+ אורחים נוספים", + "your_name": "שמך", + "email_address": "כתובת דוא\"ל", + "location": "מיקום", + "yes": "כן", + "no": "לא", + "additional_notes": "הערות נוספות", + "booking_fail": "לא ניתן לשריין זמן לפגישה.", + "reschedule_fail": "לא ניתן לשנות את מועד הפגישה.", + "share_additional_notes": "נא לשתף את כל הדברים שעשויים לסייע בהכנה לקראת הפגישה שלנו.", + "booking_confirmation": "אשר את ה{{eventTypeTitle}} עם {{profileName}}", + "booking_reschedule_confirmation": "שנה את המועד של ה{{eventTypeTitle}} עם {{profileName}}", + "in_person_meeting": "פגישה אישית", + "link_meeting": "פגישה עם קישור", + "phone_call": "שיחת טלפון", + "phone_number": "מספר טלפון", + "enter_phone_number": "נא להזין מספר טלפון", + "reschedule": "שינוי המועד שנקבע", + "reschedule_this": "קבע מועד חדש, במקום זאת", + "book_a_team_member": "שריין זמן של חבר/ת צוות, במקום זאת", + "or": "או", + "go_back": "חזרה", + "email_or_username": "דוא\"ל או שם משתמש", + "send_invite_email": "שליחת דוא\"ל הזמנה", + "role": "תפקיד", + "edit_role": "עריכת תפקיד", + "edit_team": "עריכת צוות", + "reject": "דחייה", + "accept": "אישור", + "leave": "יציאה", + "profile": "פרופיל", + "my_team_url": "כתובת ה-URL של הצוות שלי", + "team_name": "שם הצוות", + "your_team_name": "שם הצוות שלך", + "team_updated_successfully": "עדכון הצוות בוצע בהצלחה", + "your_team_updated_successfully": "הצוות שלך עודכן בהצלחה.", + "about": "אודות", + "team_description": "מספר משפטים אודות הצוות. המידע הזה יופיע בדף ה-URL של הצוות.", + "members": "חברים", + "member": "חבר/ת", + "owner": "בעלים", + "admin": "מנהל/ת מערכת", + "new_member": "חבר/ה חדש/ה", + "invite": "להזמין", + "invite_new_member": "להזמין חבר/ה חדש/ה", + "invite_new_team_member": "להזמין מישהו לצוות שלך.", + "change_member_role": "לשנות את תפקיד החבר/ה בצוות", + "disable_cal_branding": "השבת את מיתוג Cal.com", + "disable_cal_branding_description": "הסתר את כל מיתוגי Cal.com מהדפים הציבוריים שלך.", + "danger_zone": "אזור סכנה", + "back": "הקודם", + "cancel": "ביטול", + "apply": "החל", + "cancel_event": "לבטל אירוע זה", + "continue": "המשך", + "confirm": "אישור", + "disband_team": "פיזור הצוות", + "disband_team_confirmation_message": "בטוח שברצונך לפזר את הצוות הזה? כל מי ששיתפת איתו את הקישור לצוות לא יוכל להזמין יותר באמצעותו.", + "remove_member_confirmation_message": "בטוח שברצונך להסיר חבר/ה זה/ו מהצוות?", + "confirm_disband_team": "כן, לפזר את הצוות", + "confirm_remove_member": "כן, להסיר את החבר/ה", + "remove_member": "להסיר את החבר/ה", + "manage_your_team": "נהל/י את הצוות שלך", + "no_teams": "עדיין אין לך צוותים.", + "no_teams_description": "בעזרת צוותים, אנשים אחרים יכולים לקבוע מועד לאירועים המשותפים לעמיתיך.", + "submit": "שליחה", + "delete": "מחיקה", + "update": "עדכון", + "save": "שמירה", + "pending": "בהמתנה", + "open_options": "פתח את האפשרויות", + "copy_link": "העתק את הקישור לאירוע", + "share": "שיתוף", + "share_event": "האם תוכל/י להזמין חלון בלוח השנה שלי או לשלוח לי את הקישור שלך?", + "copy_link_team": "העתק את הקישור לצוות", + "leave_team": "לעזוב את הצוות", + "confirm_leave_team": "כן, לעזוב את הצוות", + "leave_team_confirmation_message": "בטוח שברצונך לעזוב את הצוות הזה? לא תוכל להזמין יותר באמצעותו.", + "user_from_team": "{{user}} מצוות {{team}}", + "preview": "תצוגה מקדימה", + "link_copied": "הקישור הועתק!", + "link_shared": "הקישור שותף!", + "title": "תפקיד", + "description": "תיאור", + "quick_video_meeting": "פגישה מהירה בווידאו.", + "scheduling_type": "סוג התזמון", + "preview_team": "תצוגה מקדימה של הצוות", + "collective": "קבוצתי", + "collective_description": "קבע מועדים לפגישות כאשר כל חברי הצוות שנבחרו זמינים.", + "duration": "משך זמן", + "minutes": "דקות", + "round_robin": "לפי תורות", + "round_robin_description": "פגישות מחזוריות בין חברי צוות מרובים.", + "url": "כתובת URL", + "hidden": "מוסתר", + "readonly": "לקריאה בלבד", + "plan_description": "כרגע אתה משתמש בחבילת {{plan}}.", + "plan_upgrade_invitation": "כדאי לשדרג את החשבון לחבילת Pro כדי ליהנות מכל התכונות שאנחנו מציעים.", + "plan_upgrade": "כדי שיהיה לך יותר מסוג אירוע פעיל אחד, עליך לשדרג את החבילה.", + "plan_upgrade_teams": "כדי ליצור צוות, עליך לשדרג את החבילה.", + "plan_upgrade_instructions": "ניתן <1>לשדרג אותה כאן.", + "event_types_page_title": "סוגי אירועים", + "event_types_page_subtitle": "צור אירועים לשיתוף כדי שאנשים יוכלו להזמין פגישות בלוח השנה שלך.", + "new_event_type_btn": "סוג אירוע חדש", + "new_event_type_heading": "צור את סוג האירוע הראשון שלך", + "new_event_type_description": "סוגי אירועים מאפשרים לך לשתף קישורים המציגים את המועדים הפנויים בלוח השנה שלך ומאפשרים לאנשים לשריין זמן איתך.", + "new_event_title": "הוסף סוג אירוע חדש", + "new_event_subtitle": "צור סוג אירוע בשמך או עבור צוות.", + "new_team_event": "הוסף סוג אירוע חדש עבור הצוות", + "new_event_description": "צור סוג אירוע חדש שאנשים יוכלו לקבוע באמצעותו מועדים.", + "event_type_created_successfully": "יצירת האירוע מסוג {{eventTypeTitle}} בוצעה בהצלחה", + "event_type_updated_successfully": "עדכון האירוע מסוג {{eventTypeTitle}} בוצע בהצלחה", + "event_type_deleted_successfully": "מחיקת סוג האירוע בוצעה בהצלחה", + "web3_metamask_added": "הוספת ה-Metamask בוצעה בהצלחה", + "web3_metamask_disconnected": "ניתוק ה-Metamask בוצע בהצלחה", + "hours": "שעות", + "your_email": "הדוא\"ל שלך", + "change_avatar": "שינוי אווטאר", + "language": "שפה", + "timezone": "אזור זמן", + "first_day_of_week": "היום הראשון בשבוע", + "single_theme": "ערכת נושא בודדת", + "brand_color": "צבע המותג", + "light_brand_color": "צבע המותג (עיצוב בהיר)", + "dark_brand_color": "צבע המותג (עיצוב כהה)", + "file_not_named": "אין לקובץ שם [idOrSlug]/[user]", + "create_team": "יצירת צוות", + "name": "שם", + "create_new_team_description": "צור צוות חדש כדי לשתף פעולה עם המשתמשים.", + "create_new_team": "יצירת צוות חדש", + "open_invitations": "הזמנות פתוחות", + "new_team": "צוות חדש", + "create_first_team_and_invite_others": "צור את הצוות הראשון שלך והזמן משתמשים אחרים לעבוד יחד איתך.", + "create_team_to_get_started": "צור צוות כדי להתחיל", + "teams": "צוותים", + "team_billing": "חיוב צוותים", + "upgrade_to_flexible_pro_title": "שינינו את החיוב בעבור צוותים", + "upgrade_to_flexible_pro_message": "יש בצוות שלך חברים שאין להם מקום. שדרג לחבילת Pro לדאוג למקומות החסרים.", + "changed_team_billing_info": "החל מינואר 2022, התחלנו לגבות תשלום עבור כל חבר צוות שמשתתף בפגישה. חברי צוות שעבדו עם גרסת PRO בחינם, הועברו לגרסת ניסיון למשך 14 יום. ברגע שהתוקף של גרסת הניסיון יסתיים, חברי צוות אלה יוסתרו מהצוות, אלא אם תשדרג כעת.", + "create_manage_teams_collaborative": "צור ונהל צוותים כדי להשתמש בתכונות של שיתוף פעולה.", + "only_available_on_pro_plan": "תכונה זו זמינה רק בחבילת Pro", + "remove_cal_branding_description": "כדי להסיר את המיתוג של Cal מדפי ההזמנות שלך, עליך לשדרג לחשבון Pro.", + "edit_profile_info_description": "ערוך את פרטי הפרופיל שלך שיופיעו בקישור לקביעת מועד.", + "change_email_tip": "ייתכן שיהיה צורך לצאת ולהיכנס שוב כדי לראות את השינויים.", + "little_something_about": "משהו קטן אודותיך.", + "profile_updated_successfully": "עדכון הפרופיל בוצע בהצלחה", + "your_user_profile_updated_successfully": "פרופיל המשתמש שלך עודכן בהצלחה.", + "user_cannot_found_db": "נראה שהמשתמש התחבר אבל לא ניתן למצוא אותו במסד הנתונים", + "embed_and_webhooks": "הטמעה ורכיבי Webhook", + "enabled": "מופעל", + "disabled": "מושבת", + "disable": "השבת", + "billing": "חיוב", + "manage_your_billing_info": "נהל את פרטי החיוב ובטל את המינוי.", + "availability": "זמינות", + "configure_availability": "הגדר מועדים שבהם תהיה זמין להזמנות.", + "change_weekly_schedule": "שינוי לוח הזמנים השבועי שלך", + "logo": "לוגו", + "error": "שגיאה", + "team_logo": "הלוגו של הצוות", + "add_location": "הוסף מיקום", + "attendees": "משתתפים", + "add_attendees": "הוסף משתתפים", + "show_advanced_settings": "הצגת הגדרות מתקדמות", + "event_name": "שם האירוע", + "event_name_tooltip": "השם שיופיע בלוחות השנה", + "meeting_with_user": "פגישה עם {USER}", + "additional_inputs": "אפשרויות קלט נוספות", + "label": "תווית", + "placeholder": "מציין מיקום", + "type": "סוג", + "edit": "עריכה", + "add_input": "הוספת קלט", + "disable_notes": "הסתר הערות בלוח השנה", + "disable_notes_description": "מסיבות פרטיות, אפשרויות קלט נוספות והערות יוסתרו מהרשומה בלוח השנה. הם עדיין יישלחו אליך בדוא\"ל.", + "opt_in_booking": "לאשר את ההזמנה", + "opt_in_booking_description": "יש לאשר את ההזמנה באופן ידני כדי שניתן יהיה להעביר אותה בדחיפה אל השילובים ולשלוח הודעת אישור בדוא\"ל.", + "disable_guests": "השבתת אורחים", + "disable_guests_description": "השבת את האפשרות להוסיף אורחים נוספים בעת ביצוע הזמנה.", + "invitees_can_schedule": "המוזמנים יכולים לקבוע מועד", + "date_range": "טווח תאריכים", + "calendar_days": "ימים קלנדריים", + "business_days": "ימי עסקים", + "set_address_place": "הגדר כתובת או מקום", + "set_link_meeting": "הגדר קישור לפגישה", + "cal_invitee_phone_number_scheduling": "Cal יציג למוזמן/ת בקשה להזין מספר טלפון לפני קביעת מועד.", + "cal_provide_google_meet_location": "Cal יספק מיקום Google Meet.", + "cal_provide_zoom_meeting_url": "Cal יספק כתובת URL לפגישת Zoom.", + "cal_provide_tandem_meeting_url": "Cal יספק כתובת URL לפגישת Tandem.", + "cal_provide_video_meeting_url": "Cal יספק כתובת URL לפגישת וידאו.", + "cal_provide_jitsi_meeting_url": "אנחנו ניצור עבורך כתובת URL לפגישת Jitsi Meet.", + "cal_provide_huddle01_meeting_url": "Cal יספק כתובת URL לפגישת וידאו ב-Huddle01 web3.", + "cal_provide_teams_meeting_url": "Cal יספק כתובת URL לפגישת MS Teams. הערה: חייב להיות חשבון של מקום עבודה או בית ספר", + "require_payment": "דרישת תשלום", + "commission_per_transaction": "עמלה לעסקה", + "event_type_updated_successfully_description": "סוג האירוע שלך עודכן בהצלחה.", + "hide_event_type": "הסתר את סוג האירוע", + "edit_location": "ערוך מיקום", + "into_the_future": "בעתיד", + "within_date_range": "בתוך טווח תאריכים", + "indefinitely_into_future": "בעתיד, במועד לא ידוע", + "this_input_will_shown_booking_this_event": "הקלט יוצג בעת הזמנת אירוע זה", + "add_new_custom_input_field": "הוספת שדה קלט מותאם אישית חדש", + "quick_chat": "צ'אט מהיר", + "add_new_team_event_type": "הוסף סוג אירוע חדש עבור הצוות", + "add_new_event_type": "הוסף סוג אירוע חדש", + "new_event_type_to_book_description": "צור סוג אירוע חדש שאנשים יוכלו לקבוע מועדים באמצעותו.", + "length": "משך זמן", + "minimum_booking_notice": "משך הזמן המינימלי לפני ביצוע הזמנה", + "slot_interval": "מרווחי חלונות זמן", + "slot_interval_default": "השתמש במשך האירוע (ברירת מחדל)", + "delete_event_type_description": "בטוח שברצונך למחוק את סוג האירוע הזה? כל מי ששיתפת איתו את הקישור לא יוכל יותר להזמין באמצעותו.", + "delete_event_type": "מחיקת סוג האירוע", + "confirm_delete_event_type": "כן, למחוק את סוג האירוע", + "delete_account": "מחיקת חשבון", + "confirm_delete_account": "כן, למחוק את החשבון", + "delete_account_confirmation_message": "בטוח שברצונך למחוק את חשבון Cal.com שלך? כל מי ששיתפת איתו את הקישור לחשבון לא יוכל יותר להזמין באמצעותו, וכל ההעדפות ששמרת יאבדו.", + "integrations": "שילובים", + "apps": "אפליקציות", + "app_store": "App Store", + "app_store_description": "מקשרים בין אנשים, טכנולוגיה ומקום העבודה.", + "settings": "הגדרות", + "event_type_moved_successfully": "סוג האירוע הועבר בהצלחה", + "next_step": "לדלג על שלב זה", + "prev_step": "לשלב הקודם", + "installed": "מותקן", + "disconnect": "התנתקות", + "embed_your_calendar": "הטמע את לוח השנה שלך בתוך דף האינטרנט", + "connect_your_favourite_apps": "חבר את האפליקציות האהובות עליך.", + "automation": "אוטומציה", + "configure_how_your_event_types_interact": "קבע איזו אינטראקציה תהיה בין סוגי האירועים שלך לבין לוחות השנה שלך.", + "select_destination_calendar": "יצירת אירועים ב", + "connect_an_additional_calendar": "קשר לוח שנה נוסף", + "conferencing": "שיחות ועידה", + "calendar": "לוח שנה", + "not_installed": "לא מותקן", + "error_password_mismatch": "הסיסמאות אינן זהות.", + "error_required_field": "שדה זה הוא שדה חובה.", + "status": "סטטוס", + "team_view_user_availability": "הצגת זמינות המשתמש", + "team_view_user_availability_disabled": "כדי להציג את זמינות הצוות, המשתמש חייב לאשר את ההזמנה", + "set_as_away": "הגדר/י את הסטטוס שלך כ'לא נמצא/ת'", + "set_as_free": "השבתת הסטטוס 'לא נמצא/ת'", + "user_away": "משתמש זה מוגדר כעת כ'לא נמצא/ת'.", + "user_away_description": "האדם שניסית לשריין איתו חלון זמן הגדיר את עצמו כ'לא נמצא/ת', ולכן הוא אינו מקבל הזמנות חדשות.", + "meet_people_with_the_same_tokens": "לפגוש אנשים עם אותם טוקנים", + "only_book_people_and_allow": "להזמין ולאפשר קבלת הזמנות רק מאנשים שיש להם את אותם הטוקנים, DAO או NFT.", + "saml_config_deleted_successfully": "תצורת SAML נמחקה בהצלחה", + "account_created_with_identity_provider": "החשבון שלך נוצר באמצעות ספק זהויות.", + "account_managed_by_identity_provider": "החשבון שלך מנוהל על ידי {{provider}}", + "account_managed_by_identity_provider_description": "כדי לשנות את כתובת הדוא\"ל ואת הסיסמה, לאפשר אימות דו-גורמי ועוד, יש לעבור אל הגדרות החשבון אצל {{provider}}.", + "signin_with_google": "כניסה עם Google", + "signin_with_saml": "כניסה עם SAML", + "saml_configuration": "תצורת SAML", + "delete_saml_configuration": "מחיקת תצורת SAML", + "delete_saml_configuration_confirmation_message": "בטוח שברצונך למחוק את תצורת SAML? חברי הצוות שלך שנכנסים אמצעות SAML לא יוכלו יותר לגשת ל-Cal.com.", + "confirm_delete_saml_configuration": "כן, אני רוצה למחוק את תצורת SAML", + "saml_not_configured_yet": "SAML עדיין לא הוגדר", + "saml_configuration_description": "יש להדביק את המטא-נתונים של SAML מספק הזהויות בתיבת הטקסט הבאה כדי לעדכן את תצורת SAML.", + "saml_configuration_placeholder": "יש להדביק את המטא-נתונים של SAML מספק הזהויות כאן", + "saml_configuration_update_failed": "עדכון תצורת SAML נכשל", + "saml_configuration_delete_failed": "מחיקת תצורת SAML נכשלה", + "saml_email_required": "הזן כתובת דוא\"ל כדי שנוכל למצוא את ספק זהויות SAML שלך", + "you_will_need_to_generate": "תצטרך ליצור טוקן גישה מהכלי הישן שבו השתמשת לקביעת מועדים.", + "import": "ייבוא", + "import_from": "ייבוא מ", + "access_token": "טוקן גישה", + "visit_roadmap": "מפת דרכים", + "popular_categories": "קטגוריות פופולריות", + "trending_apps": "אפליקציות פופולריות", + "all_apps": "כל האפליקציות", + "installed_apps": "אפליקציות מותקנות", + "manage_your_connected_apps": "ניהול האפליקציות המותקנות שלך או שינוי הגדרות", + "browse_apps": "עיון באפליקציות", + "features": "תכונות", + "permissions": "הרשאות", + "terms_and_privacy": "תנאים ופרטיות", + "published_by": "פורסם על ידי {{author}}", + "subscribe": "הרשמה למינוי", + "buy": "רכישה", + "install_app": "התקנת האפליקציה", + "categories": "קטגוריות", + "pricing": "תמחור", + "learn_more": "מידע נוסף", + "privacy_policy": "מדיניות פרטיות", + "terms_of_service": "תנאי שימוש", + "remove": "הסר", + "add": "הוסף", + "verify_wallet": "אימות הארנק", + "connect_metamask": "קישור Metamask", + "create_events_on": "יצירת אירועים ב:", + "missing_license": "חסר רישיון", + "signup_requires": "נדרש רישיון לשימוש מסחרי", + "signup_requires_description": "בשלב זה, Cal.com, Inc.‎ אינה מציעה גרסת קוד פתוח חינמית של דף ההרשמה. לקבלת גישה מלאה לרכיבי ההרשמה, תצטרך לקנות רישיון לשימוש מסחרי. ליציר חשבונות המיועדים לשימוש אישי, אנחנו ממליצים על Prisma Data Platform או על כל ממשק אחר של Postgres ליצירת חשבונות.", + "next_steps": "השלבים הבאים", + "acquire_commercial_license": "רכישת רישיון לשימוש מסחרי", + "the_infrastructure_plan": "חבילת הבסיס מחויבת לפי שימוש ומוצעת בהנחות למתחילים.", + "prisma_studio_tip": "יצירת חשבון דרך Prisma Studio", + "prisma_studio_tip_description": "למד כיצד ניתן להגדיר את המשתמש הראשון", + "contact_sales": "פנה למחלקת המכירות", + "error_404": "שגיאת 404", + "default": "ברירת מחדל", + "set_to_default": "הגדר לברירת המחדל", + "new_schedule_btn": "תזמון חדש", + "add_new_schedule": "הוסף מועד חדש", + "delete_schedule": "מחק תזמון", + "schedule_created_successfully": "יצירת התזמון {{scheduleName}} בוצעה בהצלחה", + "availability_updated_successfully": "עדכון התזמון {{scheduleName}} בוצע בהצלחה", + "schedule_deleted_successfully": "מחיקת התזמון בוצעה בהצלחה", + "default_schedule_name": "שעות עבודה", + "new_schedule_heading": "יצירת לוח זמנים של מועדים פנויים", + "new_schedule_description": "יצירת לוחות זמנים להצגת זמינות מאפשרת לנהל את הזמינות של סוגי אירועים שונים. ניתן להחיל אותם על סוג אירוע אחד או יותר.", + "requires_ownership_of_a_token": "מחייב בעלות על טוקן ששייך לכתובת הבאה:", + "example_name": "ישראל ישראלי", + "time_format": "פורמט תצוגת זמן", + "12_hour": "12 שעות", + "24_hour": "24 שעות", + "redirect_success_booking": "הפניה מחדש בעת הזמנה ", + "you_are_being_redirected": "אנחנו ננתב אותך אל {{ url }} בעוד $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "כדי להשתמש בתכונה זו, עליך לשדרג לחשבון Pro.", + "duplicate": "כפילות", + "you_can_manage_your_schedules": "ניתן לנהל את לוחות הזמנים בדף 'זמינות'.", + "api_keys": "מפתחות ממשק תכנות יישומים (API)", + "api_key_modal_subtitle": "מפתחות ממשק תכנות יישומים (API) מאפשרים לבצע קריאות לממשק תכנות יישומים עבור החשבון שלך.", + "api_keys_subtitle": "צור מפתחות ממשק תכנות יישומים (API) לקבלת גישה לחשבון שלך.", + "generate_new_api_key": "יצירת מפתח ממשק תכנות יישומים (API) חדש", + "create_api_key": "יצירת מפתח ממשק תכנות יישומים (API)", + "personal_note": "מתן שם למפתח זה", + "personal_note_placeholder": "לדוגמה, 'פיתוח'", + "api_key_no_note": "מפתח ממשק תכנות יישומים (API) ללא שם", + "api_key_never_expires": "למפתח ממשק תכנות יישומים (API) זה אין תאריך פקיעת תוקף", + "edit_api_key": "עריכת מפתח ממשק תכנות יישומים (API)", + "never_expire_key": "התוקף לעולם לא יפוג", + "delete_api_key": "ביטול מפתח ממשק תכנות יישומים (API)", + "success_api_key_created": "יצירת מפתח ממשק תכנות היישומים (API) בוצעה בהצלחה", + "success_api_key_edited": "עדכון מפתח ממשק תכנות היישומים (API) בוצע בהצלחה", + "create": "יצירה", + "success_api_key_created_bold_tagline": "יש לשמור את מפתח ממשק תכנות היישומים (API) הזה במקום בטוח.", + "you_will_only_view_it_once": "לא תוכל לראות אותו שוב ברגע שתסגור את החלון המודאלי הזה.", + "copy_to_clipboard": "העתק ללוח", + "confirm_delete_api_key": "ביטול מפתח ממשק תכנות יישומים (API) זה", + "revoke_api_key": "ביטול מפתח ממשק תכנות יישומים (API)", + "api_key_copied": "מפתח ממשק תכנות היישומים (API) הועתק!", + "delete_api_key_confirm_title": "להסיר את מפתח ממשק תכנות היישומים (API) הזה מהחשבון לצמיתות?", + "copy": "העתק", + "expire_date": "תאריך פקיעת תוקף", + "expired": "התוקף פג", + "never_expires": "התוקף לעולם לא יפוג", + "expires": "פקיעת תוקף", + "request_reschedule_booking": "בקשה לשינוי מועד ההזמנה", + "reason_for_reschedule": "הסיבה לשינוי המועד", + "book_a_new_time": "קביעת מועד חדש", + "reschedule_request_sent": "נשלחה בקשה לשינוי המועד", + "reschedule_modal_description": "פעולה זו תבטל את הפגישה שנקבעה, תיידע את המארגן ותבקש שיבחר מועד חדש.", + "reason_for_reschedule_request": "הסיבה לבקשה לשינוי מועד", + "send_reschedule_request": "בקשה לשינוי המועד ", + "edit_booking": "עריכת ההזמנה", + "reschedule_booking": "שנה/י את מועד ההזמנה", + "former_time": "המועד הקודם", + "impersonate": "התחזות", + "impersonate_user_tip": "כל השימושים בתכונה זו נבדקים.", + "impersonating_user_warning": "בהתחזות לשם המשתמש \"{{user}}\".", + "impersonating_stop_instructions": "<0>לחץ כאן כדי להפסיק." } diff --git a/apps/web/public/static/locales/he/vital.json b/apps/web/public/static/locales/he/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/he/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/hu/common.json b/apps/web/public/static/locales/hu/common.json new file mode 100644 index 0000000000..98b0108348 --- /dev/null +++ b/apps/web/public/static/locales/hu/common.json @@ -0,0 +1,51 @@ +{ + "day": "{{count}} nap", + "day_plural": "{{count}} nap", + "second": "{{count}} másodperc", + "second_plural": "{{count}} másodperc", + "organizer": "Szervező", + "price": "Ár", + "paid": "Fizetett", + "hi": "Szia", + "delete_webhook": "Webhook törlése", + "sign_out": "Kijelentkezés", + "text": "Szöveg", + "number": "Szám", + "sign_up": "Regisztráció", + "logged_out": "Kijelentkezve", + "sign_in": "Bejelentkezés", + "done": "Kész", + "connect": "Csatlakozás", + "who": "Ki", + "what": "Mit", + "when": "Mikor", + "where": "Hol", + "login": "Bejelentkezés", + "and": "és", + "light": "Világos", + "dark": "Sötét", + "email": "Email", + "full_name": "Teljes név", + "new_webhook": "Új Webhook", + "loading": "Betöltés...", + "uh_oh": "Jaj ne!", + "edit_logo": "Logó szerkesztése", + "upload_a_logo": "Logó feltöltése", + "remove_logo": "Logó eltávolítása", + "enable": "Engedélyezés", + "code": "Kód", + "security": "Biztonság", + "password": "Jelszó", + "january": "Január", + "february": "Február", + "march": "Március", + "april": "Április", + "may": "Május", + "june": "Június", + "july": "Július", + "august": "Augusztus", + "september": "Szeptember", + "october": "Október", + "november": "November", + "december": "December" +} diff --git a/apps/web/public/static/locales/hu/vital.json b/apps/web/public/static/locales/hu/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/hu/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/it/common.json b/apps/web/public/static/locales/it/common.json index 5058559d4b..25fbfb69da 100644 --- a/apps/web/public/static/locales/it/common.json +++ b/apps/web/public/static/locales/it/common.json @@ -2,6 +2,8 @@ "trial_days_left": "Ti rimangono $t(day, {\"count\": {{days}} }) di prova con il piano PRO", "day": "{{count}} giorno", "day_plural": "{{count}} giorni", + "second": "{{count}} secondo", + "second_plural": "{{count}} secondi", "upgrade_now": "Cambia piano", "accept_invitation": "Accetta Invito", "calcom_explained": "Cal.com è l'alternativa Calendly open source che ti consente di controllare i tuoi dati, il flusso di lavoro e l'aspetto.", @@ -60,9 +62,14 @@ "password_reset_instructions": "Se non l'hai richiesto, puoi tranquillamente ignorare questa email e la tua password non verrà modificata.", "event_awaiting_approval_subject": "In attesa di approvazione: {{eventType}} con {{name}} il {{date}}", "event_still_awaiting_approval": "Un evento è ancora in attesa della tua approvazione", + "booking_submitted_subject": "Prenotazione inviata: {{eventType}} con {{name}} il {{date}}", "your_meeting_has_been_booked": "Il tuo meeting è stata prenotato", "event_type_has_been_rescheduled_on_time_date": "Il tuo {{eventType}} con {{name}} è stato riprogrammato al {{time}} ({{timeZone}}) il {{date}}.", "event_has_been_rescheduled": "Aggiornato: il tuo evento è stato riprogrammato", + "request_reschedule_title_attendee": "Richiesta di riprogrammare la prenotazione", + "request_reschedule_subtitle": "{{organizer}} ha annullato la prenotazione e ti ha chiesto di scegliere un altro orario.", + "request_reschedule_title_organizer": "Hai richiesto a {{attendee}} di riprogrammare", + "reschedule_reason": "Motivo della riprogrammazione", "hi_user_name": "Ciao {{name}}", "ics_event_title": "{{eventType}} con {{name}}", "new_event_subject": "Richiesta di nuovo evento: {{attendeeName}} - {{date}} - {{eventType}}", @@ -284,6 +291,10 @@ "hover_over_bold_times_tip": "Suggerimento: Passa sui tempi in grassetto per un timestamp completo", "start_time": "Ora di inizio", "end_time": "Ora di fine", + "buffer_time": "Tempo buffer", + "before_event": "Prima dell'evento", + "after_event": "Dopo l'evento", + "event_buffer_default": "Nessun tempo buffer", "buffer": "Buffer", "your_day_starts_at": "La tua giornata inizia alle", "your_day_ends_at": "La tua giornata termina alle", @@ -299,6 +310,9 @@ "light": "Chiaro", "dark": "Scuro", "automatically_adjust_theme": "Regola automaticamente il tema in base alle preferenze dell'invito", + "user_dynamic_booking_disabled": "Alcuni degli utenti del gruppo hanno attualmente disabilitato le prenotazioni di gruppo dinamiche", + "allow_dynamic_booking_tooltip": "È possibile creare dinamicamente collegamenti a prenotazioni di gruppo inserendo più nomi utente separati da '+'. Esempio: 'cal.com/bailey+peer'", + "allow_dynamic_booking": "Consenti ai partecipanti di prenotare per te utilizzando le prenotazioni di gruppo dinamiche", "email": "Email", "email_placeholder": "jdoe@example.com", "full_name": "Nome completo", @@ -311,9 +325,12 @@ "event_triggers": "Attivatori Evento", "subscriber_url": "Url del Subscriber", "create_new_webhook": "Crea un nuovo webhook", + "webhooks": "Webhook", + "team_webhooks": "Webhook del team", "create_new_webhook_to_account": "Crea un nuovo webhook sul tuo account", "new_webhook": "Nuovo Webhook", "receive_cal_meeting_data": "Ricevi i dati di riunione Cal ad un URL specificato, in tempo reale, quando un evento è programmato o annullato.", + "receive_cal_event_meeting_data": "Ricevi i dati della riunione Cal a un URL specificato, in tempo reale, quando questo evento viene programmato o annullato.", "responsive_fullscreen_iframe": "Iframe full screen responsive", "loading": "Caricamento...", "standard_iframe": "Iframe standard", @@ -398,10 +415,12 @@ "booking_confirmation": "Conferma il tuo {{eventTypeTitle}} con {{profileName}}", "booking_reschedule_confirmation": "Riprogramma il tuo {{eventTypeTitle}} con {{profileName}}", "in_person_meeting": "Link o riunione di persona", + "link_meeting": "Collegamento riunione", "phone_call": "Chiamata da telefono", "phone_number": "Numero Di Telefono", "enter_phone_number": "Inserisci numero di telefono", "reschedule": "Riprogramma", + "reschedule_this": "Riprogramma", "book_a_team_member": "Prenota invece un membro del team", "or": "O", "go_back": "Torna indietro", @@ -435,6 +454,8 @@ "danger_zone": "Zona Di Pericolo", "back": "Indietro", "cancel": "Annulla", + "apply": "Applica", + "cancel_event": "Annulla questo evento", "continue": "Continua", "confirm": "Conferma", "disband_team": "Sciogli Squadra", @@ -504,6 +525,8 @@ "first_day_of_week": "Primo giorno della settimana", "single_theme": "Tema Singolo", "brand_color": "Colore del Marchio", + "light_brand_color": "Colore del marchio (tema chiaro)", + "dark_brand_color": "Colore del marchio (tema scuro)", "file_not_named": "Il file non è chiamato [idOrSlug]/[user]", "create_team": "Crea Team", "name": "Nome", @@ -552,6 +575,8 @@ "type": "Tipo", "edit": "Modifica", "add_input": "Aggiungi un input", + "disable_notes": "Nascondi le note nel calendario", + "disable_notes_description": "Per motivi di privacy, gli input e le note aggiuntivi saranno nascosti nella voce del calendario. Verranno comunque inviati alla tua email.", "opt_in_booking": "Prenotazione Opt-in", "opt_in_booking_description": "La prenotazione deve essere confermata manualmente prima di essere spinta alle integrazioni e viene inviata una mail di conferma.", "disable_guests": "Disabilita Ospiti", @@ -561,6 +586,7 @@ "calendar_days": "giorni del calendario", "business_days": "giorni lavorativi", "set_address_place": "Imposta un indirizzo o un luogo", + "set_link_meeting": "Imposta un collegamento alla riunione", "cal_invitee_phone_number_scheduling": "Cal chiederà al tuo invitato di inserire un numero di telefono prima della pianificazione.", "cal_provide_google_meet_location": "Cal fornirà un Google Meet location.", "cal_provide_zoom_meeting_url": "Cal fornirà un URL di riunione Zoom.", @@ -568,6 +594,7 @@ "cal_provide_video_meeting_url": "Cal fornirà un URL di riunione Daily video.", "cal_provide_jitsi_meeting_url": "Cal fornirà un URL di riunione Jitsi Meet.", "cal_provide_huddle01_meeting_url": "Cal fornirà un URL di riunione Huddle01 web3 video.", + "cal_provide_teams_meeting_url": "Cal fornirà un URL per la riunione MS Teams. NOTA: È NECESSARIO POSSEDERE UN ACCOUNT LAVORATIVO O SCOLASTICO", "require_payment": "Richiedi Pagamento", "commission_per_transaction": "commissione per transazione", "event_type_updated_successfully_description": "Il tuo team è stato aggiornato con successo.", @@ -593,6 +620,9 @@ "confirm_delete_account": "Sì, elimina account", "delete_account_confirmation_message": "Sei sicuro di voler eliminare il tuo account Cal.com? Chiunque abbia il link del tuo account non potrà più utilizzarlo per prenotare e le preferenze che hai salvato andranno perse.", "integrations": "Integrazioni", + "apps": "App", + "app_store": "App Store", + "app_store_description": "Collegare persone, tecnologia e luogo di lavoro.", "settings": "Impostazioni", "event_type_moved_successfully": "Il tipo di evento è stato spostato con successo", "next_step": "Salta passo", @@ -640,6 +670,24 @@ "import_from": "Importa da", "access_token": "Token di accesso", "visit_roadmap": "Roadmap", + "popular_categories": "Categorie popolari", + "trending_apps": "App di tendenza", + "all_apps": "Tutte le app", + "installed_apps": "App installate", + "manage_your_connected_apps": "Gestisci le app installate o modifica le impostazioni", + "browse_apps": "Sfoglia app", + "features": "Funzionalità", + "permissions": "Autorizzazioni", + "terms_and_privacy": "Termini e informativa privacy", + "published_by": "Pubblicato da {{author}}", + "subscribe": "Abbonati", + "buy": "Acquista", + "install_app": "Installa app", + "categories": "Categorie", + "pricing": "Prezzi", + "learn_more": "Ulteriori informazioni", + "privacy_policy": "Informativa sulla privacy", + "terms_of_service": "Termini del servizio", "remove": "Rimuovi", "add": "Aggiungi", "verify_wallet": "Verifica Wallet", @@ -655,7 +703,63 @@ "prisma_studio_tip_description": "Scopri come configurare il tuo primo utente", "contact_sales": "Contatta reparto commerciale", "error_404": "Errore 404", + "default": "Predefinito", + "set_to_default": "Impostazione predefinita", + "new_schedule_btn": "Nuovo programma", + "add_new_schedule": "Aggiungi un nuovo programma", + "delete_schedule": "Elimina programma", + "schedule_created_successfully": "Programma {{scheduleName}} creato", "availability_updated_successfully": "Disponibilità aggiornata con successo", + "schedule_deleted_successfully": "Programma eliminato", + "default_schedule_name": "Orario lavorativo", + "new_schedule_heading": "Crea un programma di disponibilità", + "new_schedule_description": "La creazione di programmi di disponibilità consente di gestire la disponibilità per i diversi tipi di eventi. I programmi possono essere applicati a uno o più tipi di evento.", "requires_ownership_of_a_token": "Richiede il possesso di un token appartenente al seguente indirizzo:", - "example_name": "Paolo Rossi" + "example_name": "Paolo Rossi", + "time_format": "Formato dell'orario", + "12_hour": "12 ore", + "24_hour": "24 ore", + "redirect_success_booking": "Reindirizza su prenotazione ", + "you_are_being_redirected": "Verrai reindirizzato a {{ url }} tra $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Per poter utilizzare questa funzionalità, è necessario passare a un account Pro.", + "duplicate": "Duplica", + "you_can_manage_your_schedules": "Puoi gestire i tuoi programmi dalla pagina Disponibilità.", + "api_keys": "Chiavi API", + "api_key_modal_subtitle": "Le chiavi API consentono di effettuare chiamate API per il proprio account.", + "api_keys_subtitle": "Genera chiavi API da usare per accedere al tuo account.", + "generate_new_api_key": "Genera nuova chiave API", + "create_api_key": "Crea una chiave API", + "personal_note": "Assegna un nome a questa chiave", + "personal_note_placeholder": "Ad es. Sviluppo", + "api_key_no_note": "Chiave API senza nome", + "api_key_never_expires": "Questa chiave API non ha data di scadenza", + "edit_api_key": "Modifica chiave API", + "never_expire_key": "Nessuna scadenza", + "delete_api_key": "Revoca chiave API", + "success_api_key_created": "Chiave API creata", + "success_api_key_edited": "Chiave API aggiornata", + "create": "Crea", + "success_api_key_created_bold_tagline": "Salva questa chiave API in un posto sicuro.", + "you_will_only_view_it_once": "Una volta chiusa questa finestra non sarà più possibila visualizzarla.", + "copy_to_clipboard": "Copia negli Appunti", + "confirm_delete_api_key": "Revoca questa chiave API", + "revoke_api_key": "Revoca chiave API", + "api_key_copied": "Chiave API copiata", + "delete_api_key_confirm_title": "Rimuovere definitivamente questa chiave API dal tuo account?", + "copy": "Copia", + "expire_date": "Data di scadenza", + "expired": "Scaduta", + "never_expires": "Nessuna scadenza", + "expires": "Scade", + "request_reschedule_booking": "Richiesta di riprogrammare la prenotazione", + "reason_for_reschedule": "Motivo della riprogrammazione", + "book_a_new_time": "Prenota un nuovo orario", + "reschedule_request_sent": "Richiesta di riprogrammazione inviata", + "reschedule_modal_description": "Questa operazione annullerà la riunione programmata, avviserà il creatore del programma e gli chiederà di scegliere una nuova ora.", + "reason_for_reschedule_request": "Motivo della richiesta di riprogrammazione", + "send_reschedule_request": "Richiedi riprogrammazione ", + "edit_booking": "Modifica prenotazione", + "reschedule_booking": "Riprogramma prenotazione", + "former_time": "Orario precedente" } diff --git a/apps/web/public/static/locales/it/vital.json b/apps/web/public/static/locales/it/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/it/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/ja/common.json b/apps/web/public/static/locales/ja/common.json index 635bd30c2b..58b345f618 100644 --- a/apps/web/public/static/locales/ja/common.json +++ b/apps/web/public/static/locales/ja/common.json @@ -1,142 +1,151 @@ { "trial_days_left": "PRO トライアル版は残り $t(day, {\"count\": {{days}} }) です", "day": "{{count}} 日", - "day_plural": "{{count}} 日間", + "day_plural": "{{count}} 日", + "second": "{{count}} 秒", + "second_plural": "{{count}} 秒", "upgrade_now": "今すぐアップグレード", - "accept_invitation": "招待を承諾する", - "calcom_explained": "Cal.com は、Calendly の代替となるオープンソースのツールです。データ、ワークフロー、外観などを自分自身の手でコントロールすることができます。", + "accept_invitation": "招待を受け入れる", + "calcom_explained": "Cal.com は、Calendly の代替となるオープンソースのツールです。データ、ワークフロー、外観などを自由にコントロールできます。", "have_any_questions": "ご不明な点があれば、お気軽にお問い合わせください。", "reset_password_subject": "Cal.com: パスワードのリセット手順", - "event_declined_subject": "拒否されました: {{name}} による {{eventType}}、{{date}}", - "event_cancelled_subject": "キャンセルされました: {{name}} による {{eventType}}、{{date}}", + "event_declined_subject": "拒否されました: {{date}} の {{name}} との {{eventType}}", + "event_cancelled_subject": "キャンセルされました: {{date}} の {{name}} との {{eventType}}", "event_request_declined": "イベントのリクエストが拒否されました", "event_request_cancelled": "スケジュールされていた予定がキャンセルされました", "organizer": "主催者", "need_to_reschedule_or_cancel": "再スケジュールまたはキャンセルが必要ですか?", "cancellation_reason": "キャンセルの理由", - "cancellation_reason_placeholder": "なぜキャンセルが必要なのですか?(オプション)", + "cancellation_reason_placeholder": "キャンセルが必要な理由を教えてください (任意)", "rejection_reason": "拒否の理由", "rejection_reason_title": "予約のリクエストを拒否しますか?", - "rejection_reason_description": "この予約を本当に拒否してよろしいですか?予約を試みたユーザーに対して通知を行います。以下に理由をご記入いただけます。", + "rejection_reason_description": "この予約を拒否してよろしいですか?予約を試みたユーザーに通知を送ります。拒否の理由は、下にご記入いただけます。", "rejection_confirmation": "予約を拒否する", "manage_this_event": "このイベントを管理する", "your_event_has_been_scheduled": "イベントがスケジュールされました", - "accept_our_license": ".env 変数 <1>NEXT_PUBLIC_LICENSE_CONSENT を '{{agree}} ' に変更することで、ライセンスを承認します。", - "remove_banner_instructions": "このバナーを削除するには、.envファイルを開き、<1>NEXT_PUBLIC_LICENSE_CONSENT変数を「{{agree}}」に変更してください", - "error_message": "エラーメッセージ: '{{errorMessage}}'", - "refund_failed_subject": "返金に失敗しました: {{userName}} - {{date}} - {{eventType}}", - "refund_failed": "{{userName}} の {{date}} の {{eventType}} に関するイベントの返金に失敗しました。", - "check_with_provider_and_user": "支払い業者に確認し、 {{userName}} の処理方法をご確認ください。", + "accept_our_license": ".env 変数 <1>NEXT_PUBLIC_LICENSE_CONSENT を '{{agree}} ' に変更することで、ライセンスに同意してください。", + "remove_banner_instructions": "このバナーを削除するには、.envファイルを開き、<1>NEXT_PUBLIC_LICENSE_CONSENT 変数を「{{agree}}」に変更してください", + "error_message": "エラーメッセージ: '{{errorMessage}}'", + "refund_failed_subject": "返金に失敗しました: {{name}} - {{date}} - {{eventType}}", + "refund_failed": "{{userName}} の {{date}} における {{eventType}} の返金に失敗しました。", + "check_with_provider_and_user": "これを処理する方法については、支払い業者と {{user}} にご確認ください。", "a_refund_failed": "返金に失敗しました", - "awaiting_payment_subject": "お支払いを待機しています: {{name}} による {{eventType}}、{{date}}", + "awaiting_payment_subject": "お支払いを待機しています: {{date}} の {{name}} との {{eventType}}", "meeting_awaiting_payment": "会議の支払いを待っています", "help": "ヘルプ", "price": "料金", "paid": "支払い済み", "refunded": "返金済み", - "pay_later_instructions": "後で支払いたい場合は、このリンクに関するメールも送信済みです。", + "pay_later_instructions": "後からの支払いをご希望でしたら、このリンクをメールでもお送りしていますので、ご自由にお使いください。", "payment": "お支払い", - "missing_card_fields": "カードが見つかりません", + "missing_card_fields": "カード情報のフィールドが未記入です", "pay_now": "今すぐ支払う", - "codebase_has_to_stay_opensource": "コードベースは、変更されたかどうかに関わらず、オープンソースを維持する必要があります", - "cannot_repackage_codebase": "コードベースを再パッケージ化または販売することはできません", + "codebase_has_to_stay_opensource": "コードベースは、変更されたかどうかを問わず、オープンソースでなくてはいけません", + "cannot_repackage_codebase": "コードベースは、再パッケージ化することも、販売することもできません", "acquire_license": "電子メールでこれらの条件を削除するための商用ライセンスを取得します", - "terms_summary": "規約要件", + "terms_summary": "利用規約", "open_env": ".envを開き、ライセンスに同意してください", - "env_changed": "私の.envを変更しました", - "accept_license": "ライセンスを承認する", + "env_changed": "私の .env を変更しました", + "accept_license": "ライセンスに同意する", "still_waiting_for_approval": "イベントはまだ承認待ちです", "event_is_still_waiting": "イベントリクエストはまだ待機中です: {{attendeeName}} - {{date}} - {{eventType}}", - "no_more_results": "これ以上結果はありません", + "no_more_results": "他の結果はありません", "load_more_results": "さらに結果を読み込む", - "integration_meeting_id": "{{integrationName}} ミーティングID: {{meetingId}}", - "confirmed_event_type_subject": "確認済: {{name}} の {{date}} の {{eventType}}", - "new_event_request": "新しいイベントのリクエスト: {{attendeeName}} - {{date}} - {{eventType}}", - "confirm_or_reject_request": "リクエストを承認または拒否する", - "check_bookings_page_to_confirm_or_reject": "予約ページを確認し、予約を確認または拒否してください。", - "event_awaiting_approval": "新しいイベントがあなたの承認を待っています", - "someone_requested_an_event": "別のユーザーがあなたのカレンダーへのイベントの追加をリクエストしています。", + "integration_meeting_id": "{{integrationName}} ミーティング ID: {{meetingId}}", + "confirmed_event_type_subject": "確認済: {{date}} の {{name}} との {{eventType}}", + "new_event_request": "新しいイベントのリクエスト: {{attendeeName}} - {{date}} - {{eventType}}", + "confirm_or_reject_request": "リクエストを確認または拒否する", + "check_bookings_page_to_confirm_or_reject": "予約ページを開いて、予約を確認または拒否してください。", + "event_awaiting_approval": "イベントはあなたの承認待ちです", + "someone_requested_an_event": "別のユーザーがあなたのカレンダーにイベントを予定するリクエストしています。", "someone_requested_password_reset": "別のユーザーがあなたのパスワードを変更するためのリンクをリクエストしています。", - "password_reset_instructions": "リクエストしていない場合には、このメールを無視していただいて構いません。これによりあなたのパスワードが変更されることはありません。", - "event_awaiting_approval_subject": "承認を待機しています: {{name}} による {{eventType}}、{{date}}", - "event_still_awaiting_approval": "承認待機中のイベントがあります", + "password_reset_instructions": "このリクエストをしていない場合は、このメールを無視しても大丈夫です。パスワードも変更されません。", + "event_awaiting_approval_subject": "承認を待っています: {{date}} の {{name}} との {{eventType}}", + "event_still_awaiting_approval": "イベントはまだあなたの承認待ちです", + "booking_submitted_subject": "予約を送信しました: {{date}} の {{name}} との {{eventType}}", "your_meeting_has_been_booked": "ミーティングが予約されました", - "event_type_has_been_rescheduled_on_time_date": "{{name}} と出席する {{eventType}} は ({{timeZone}}) の {{date}} {{time}} にスケジュール変更されました", - "event_has_been_rescheduled": "イベントのスケジュールが変更されました", - "hi_user_name": "こんにちは、 {{userName}}", - "ics_event_title": "{{name}} による {{eventType}}", + "event_type_has_been_rescheduled_on_time_date": "{{name}} との {{eventType}} は {{date}} の {{time}} ({{timeZone}}) にスケジュール変更されました。", + "event_has_been_rescheduled": "更新 - イベントのスケジュールが変更されました", + "request_reschedule_title_attendee": "予約のスケジュール変更をリクエスト", + "request_reschedule_subtitle": "{{organizer}} が予約をキャンセルしました。別の時間を選択することをあなたにリクエストしています。", + "request_reschedule_title_organizer": "{{attendee}} にスケジュールの変更をリクエストしました", + "request_reschedule_subtitle_organizer": "予約をキャンセルしました。{{attendee}} はあなたとの予約の時間を新しく選択する必要があります。", + "reschedule_reason": "スケジュール変更の理由", + "hi_user_name": "こんにちは、{{name}} さん", + "ics_event_title": "{{name}} との {{eventType}}", "new_event_subject": "新規イベント: {{attendeeName}} - {{date}} - {{eventType}}", "join_by_entrypoint": "{{entryPoint}} に参加する", "notes": "メモ", "manage_my_bookings": "予約の管理", "need_to_make_a_change": "変更が必要ですか?", - "new_event_scheduled": "新しいイベントが予定されています。", + "new_event_scheduled": "新しいイベントが予定されました。", "invitee_email": "招待者のメールアドレス", "invitee_timezone": "招待者のタイムゾーン", - "event_type": "イベント種別", + "event_type": "イベントタイプ", "enter_meeting": "ミーティングに入る", - "video_call_provider": "ビデオ通話プロバイダー", - "meeting_id": "ミーティングID", + "video_call_provider": "ビデオコールプロバイダー", + "meeting_id": "ミーティング ID", "meeting_password": "ミーティングパスワード", - "meeting_url": "ミーティングID", + "meeting_url": "ミーティング URL", "meeting_request_rejected": "ミーティングリクエストが拒否されました", - "rescheduled_event_type_subject": "再スケジュールされました: {{name}} による {{eventType}}、{{date}}", - "rejected_event_type_with_organizer": "拒否: {{date}} の{{organizer}} による{{eventType}}", + "rescheduled_event_type_subject": "スケジュール変更されました: {{date}} の {{name}} との {{eventType}}", + "requested_to_reschedule_subject_attendee": "スケジュール変更のアクションが必要です: {{name}} との {{eventType}} に新しい時間を予約してください", + "rejected_event_type_with_organizer": "拒否されたリクエスト: {{date}} の {{organizer}} による{{eventType}}", "hi": "こんにちは", "join_team": "チームに参加", "manage_this_team": "このチームを管理する", "team_info": "チームの情報", - "request_another_invitation_email": "{{toEmail}} を Cal.com のメールアドレスとして使用しない場合、または Cal.com アカウントをすでに持っている場合は、そのメールへの別の招待をリクエストしてください。", - "you_have_been_invited": "チーム {{teamName}} に招待されました。", - "user_invited_you": "{{user}} さんがあなたをチーム {{teamName}} に招待しました", + "request_another_invitation_email": "{{toEmail}} を Cal.com のメールアドレスとして使用しない場合、または Cal.com アカウントをすでに持っている場合は、そのメールへの招待を別にリクエストしてください。", + "you_have_been_invited": "チーム {{teamName}} への参加を招待されました。", + "user_invited_you": "{{user}} さんがあなたにチーム {{team}} への参加を招待しました", "hidden_team_member_title": "あなたはこのチームで非表示にされています", "hidden_team_member_message": "座席料の支払いが完了していません。Pro へとアップグレードするか、チームのオーナーにあなたの座席料の支払いをご依頼ください。", "hidden_team_owner_message": "チームをご利用いただくためには、Pro アカウントが必要です。アップグレードするまでは非表示となります。", - "link_expires": "追記: {{expiresIn}} 時間後に有効期限が切れます。", + "link_expires": "追記: 有効期限は {{expiresIn}} 時間後に切れます。", "upgrade_to_per_seat": "座席ごとにアップグレードする", - "team_upgrade_seats_details": "あなたのチームのメンバー {{memberCount}} 名の内 {{unpaidCount}} 名のユーザーの座席料の支払いが完了していません。1 座席あたりの金額は ${{seatPrice}}、すべての座席の合計金額は ${{totalCost}} です。", + "team_upgrade_seats_details": "あなたのチームに所属する {{memberCount}} 人のメンバーのうち、{{unpaidCount}} 名分のライセンスが未払いとなっています。ライセンスごとの料金は、${{seatPrice}} / 月ですので、あなたのメンバーシップの合計金額は、${{totalCost}} / 月になると予想されます。", "team_upgraded_successfully": "チームのアップグレードが正常に完了しました!", "use_link_to_reset_password": "以下のリンクを使用してパスワードをリセットしてください", "hey_there": "こんにちは!", "forgot_your_password_calcom": "パスワードをお忘れですか? - Cal.com", - "event_type_title": "{{eventTypeTitle}} | イベント種別", - "delete_webhook_confirmation_message": "このウェブフックを削除してもよろしいですか? イベントがスケジュールまたはキャンセルされたときに、指定されたURLでのミーティングデータを受け取れなくなります。", - "confirm_delete_webhook": "はい、Webhookを削除します", - "edit_webhook": "Webhookを編集", - "delete_webhook": "Webhookを削除", - "webhook_status": "Webhookの状態", - "webhook_enabled": "Webhookが有効です", - "webhook_disabled": "Webhookは無効です", - "webhook_response": "Webhook の応答", - "webhook_test": "Webhookのテスト", - "manage_your_webhook": "Webhookの管理", - "webhook_created_successfully": "Webhookの作成に成功しました!", - "webhook_updated_successfully": "Webhookが正常に更新されました!", - "webhook_removed_successfully": "Webhookの削除に成功しました!", + "event_type_title": "{{eventTypeTitle}} | イベントタイプ", + "delete_webhook_confirmation_message": "このウェブフックを削除してもよろしいですか? イベントがスケジュールまたはキャンセルされたときに、指定された URL でのミーティングデータをリアルタイムには受け取れなくなります。", + "confirm_delete_webhook": "はい、ウェブフックを削除します", + "edit_webhook": "ウェブフックを編集", + "delete_webhook": "ウェブフックを削除", + "webhook_status": "ウェブフックの状態", + "webhook_enabled": "ウェブフックが有効です", + "webhook_disabled": "ウェブフックは無効です", + "webhook_response": "ウェブフックの応答", + "webhook_test": "ウェブフックのテスト", + "manage_your_webhook": "ウェブフックの管理", + "webhook_created_successfully": "ウェブフックが正常に作成されました!", + "webhook_updated_successfully": "ウェブフックが正常に更新されました!", + "webhook_removed_successfully": "ウェブフックが正常に削除されました!", "payload_template": "ペイロードのテンプレート", "dismiss": "解除", "no_data_yet": "データがまだありません", - "ping_test": "Pingテスト", - "add_to_homescreen": "このアプリをホーム画面に追加すると、より速くアクセスでき、操作性が向上します。", + "ping_test": "Ping テスト", + "add_to_homescreen": "このアプリをホーム画面に追加すると、アクセスが簡単になり、使用体験が向上します。", "upcoming": "近日公開", "past": "過去", - "choose_a_file": "ファイルを選択してください...", + "choose_a_file": "ファイルを選択する...", "upload_image": "画像をアップロードする", - "upload_target": "{{target}} をアップロード", + "upload_target": "{{target}} をアップロードする", "no_target": "{{target}} はありません", - "slide_zoom_drag_instructions": "スライドしてズームし、ドラッグして位置を変更", - "view_notifications": "通知を表示", - "view_public_page": "公開ページを見る", - "sign_out": "ログアウトする", + "slide_zoom_drag_instructions": "スライドでズーム、ドラッグで位置を変更", + "view_notifications": "通知を表示する", + "view_public_page": "公開ページを表示する", + "sign_out": "ログアウト", "add_another": "さらに追加", "until": "まで", "powered_by": "powered by", "unavailable": "利用不可", "set_work_schedule": "作業スケジュールを設定する", "change_bookings_availability": "予約が可能な場合に変更する", - "select": "選択中...", + "select": "選択する...", "2fa_confirm_current_password": "現在のパスワードを確認して開始します。", - "2fa_scan_image_or_use_code": "以下の画像を携帯電話の認証アプリでスキャンするか、手動でテキストコードを入力してください。", + "2fa_scan_image_or_use_code": "携帯電話の認証アプリで下の画像をスキャンするか、手動でテキストコードを入力してください。", "text": "テキスト", "multiline_text": "複数行のテキスト", "number": "番号", @@ -148,28 +157,28 @@ "unconfirmed": "未確認", "guests": "ゲスト", "guest": "ゲスト", - "web_conferencing_details_to_follow": "ウェブ会議の詳細", + "web_conferencing_details_to_follow": "ウェブ会議の詳細には確認メールでお送りします。", "the_username": "ユーザー名", "username": "ユーザー名", - "is_still_available": "はまだ利用可能です。", + "is_still_available": "まだ利用可能。", "documentation": "文書", - "documentation_description": "私たちのツールをあなたのアプリと統合する方法を学ぶ", - "api_reference": "API参照", - "api_reference_description": "ライブラリの完全なAPIリファレンス", + "documentation_description": "当社のツールとあなたのアプリを連携させる方法について解説します", + "api_reference": "API リファレンス", + "api_reference_description": "当社ライブラリの完全な API リファレンス", "blog": "ブログ", "blog_description": "最新のニュースや記事を読む", "join_our_community": "コミュニティーに参加する", - "join_our_slack": "Slackに参加する", - "claim_username_and_schedule_events": "ユーザー名とイベントのスケジュールを要求する", - "popular_pages": "人気ページ", + "join_our_slack": "Slack に参加する", + "claim_username_and_schedule_events": "ユーザー名を獲得してイベントをスケジュールする", + "popular_pages": "人気のページ", "register_now": "今すぐ登録", "register": "登録", "page_doesnt_exist": "このページは存在しません。", "check_spelling_mistakes_or_go_back": "スペルミスを確認するか、前のページに戻ってください。", - "404_page_not_found": "404: このページは見つかりません。", - "getting_started": "開始", - "15min_meeting": "15分間のミーティング", - "30min_meeting": "30分間のミーティング", + "404_page_not_found": "404: このページは見つかりませんでした。", + "getting_started": "はじめに", + "15min_meeting": "15 分間のミーティング", + "30min_meeting": "30 分間のミーティング", "secret_meeting": "秘密のミーティング", "login_instead": "代わりにログインする", "already_have_an_account": "既にアカウントをお持ちですか?", @@ -209,32 +218,32 @@ "set_up_later": "あとで設定", "current_time": "現在の時刻", "welcome": "ようこそ", - "welcome_to_calcom": "Cal.comへようこそ", - "welcome_instructions": "電話番号をとタイムゾーンを教えてください。これらは後で編集できます。", + "welcome_to_calcom": "Cal.com へようこそ", + "welcome_instructions": "Cal.com でご使用になるお名前とタイムゾーンを教えてください。これらは後から編集できます。", "connect_caldav": "CalDav Serverに接続", "credentials_stored_and_encrypted": "認証情報は保存され暗号化されます。", "connect": "接続", "try_for_free": "無料で試す", - "create_booking_link_with_calcom": "Cal.comで独自の予約リンクを作成する", + "create_booking_link_with_calcom": "Cal.com で独自の予約リンクを作成する", "who": "誰が", - "what": "何", + "what": "何を", "when": "いつ", "where": "どこで", "add_to_calendar": "カレンダーに追加", "other": "その他", - "emailed_you_and_attendees": "あなたと他の出席者にすべての詳細を載せたカレンダーの招待状を電子メールで送信しました.", + "emailed_you_and_attendees": "あなたと他の出席者にすべての詳細を記載したカレンダーの招待状を電子メールで送信しました。", "emailed_you_and_any_other_attendees": "あなたと他の参加者にこの情報を電子メールで送信しました。", - "needs_to_be_confirmed_or_rejected": "予約の確認または拒否が必要です。", + "needs_to_be_confirmed_or_rejected": "予約は依然として確認または拒否が必要です。", "user_needs_to_confirm_or_reject_booking": "{{user}} はまだ予約を承認または拒否する必要があります", "meeting_is_scheduled": "このミーティングは予定されています", - "submitted": "送信済み", + "submitted": "予約が送信されました", "booking_submitted": "予約が送信されました", - "booking_confirmed": "予約の確認", - "enter_new_password": "アカウントに新しいパスワードを入力してください", + "booking_confirmed": "予約が確認されました", + "enter_new_password": "アカウントに使う新しいパスワードを入力してください", "reset_password": "パスワードのリセット", "change_your_password": "パスワードの変更", "try_again": "もう一度試す", - "request_is_expired": "そのリクエストは期限切れです。", + "request_is_expired": "そのリクエストは期限が切れています。", "reset_instructions": "あなたのアカウントに関連付けられているメールアドレスを入力すると、パスワードをリセットするためのリンクが送信されます。", "request_is_expired_instructions": "リクエストの有効期限が切れています。アカウントに関連付けられているメールアドレスを入力すると、パスワードをリセットするための別のリンクが送信されます。", "whoops": "エラー", @@ -242,7 +251,7 @@ "success": "成功", "failed": "失敗", "password_has_been_reset_login": "パスワードがリセットされました。新しく作成したパスワードでログインできるようになりました。", - "unexpected_error_try_again": "予期しないエラーが発生しました。再試行してください。", + "unexpected_error_try_again": "予期しないエラーが発生しました。もう一度お試しください。", "sunday_time_error": "日曜日の時間が無効です", "monday_time_error": "月曜日の時間が無効です", "tuesday_time_error": "火曜日の時間が無効です", @@ -250,15 +259,15 @@ "thursday_time_error": "木曜日の時間が無効です", "friday_time_error": "金曜日の時間が無効です", "saturday_time_error": "土曜日の時間が無効です", - "error_end_time_before_start_time": "終了時刻を開始時刻よりも前の時刻に設定することはできません", + "error_end_time_before_start_time": "終了時刻は開始時刻よりも前の時刻に設定できません", "error_end_time_next_day": "終了時刻に 24 時を超える時刻を設定することはできません", "back_to_bookings": "予約に戻る", - "free_to_pick_another_event_type": "好きな他のイベントを選んでください。", - "cancelled": "キャンセルしました", + "free_to_pick_another_event_type": "他の好きなイベントを選んでください。", + "cancelled": "キャンセル済み", "cancellation_successful": "キャンセルしました", "really_cancel_booking": "本当に予約をキャンセルしますか?", "cannot_cancel_booking": "この予約はキャンセルできません", - "reschedule_instead": "代わりに、それを再スケジュールすることもできます。", + "reschedule_instead": "代わりに、そのスケジュールを変更することもできます。", "event_is_in_the_past": "イベントは過去のものです", "error_with_status_code_occured": "ステータスコード {{status}} に関するエラーが発生しました。", "booking_already_cancelled": "この予約はすでにキャンセルされています", @@ -284,6 +293,10 @@ "hover_over_bold_times_tip": "ヒント:太字の時間にカーソルを合わせると完全なタイムスタンプが表示されます", "start_time": "開始時間", "end_time": "終了時間", + "buffer_time": "バッファ時間", + "before_event": "イベント前", + "after_event": "イベント後", + "event_buffer_default": "バッファ時間なし", "buffer": "バッファ", "your_day_starts_at": "1日の開始は", "your_day_ends_at": "1日の終わりは", @@ -299,6 +312,9 @@ "light": "明るさ", "dark": "暗め", "automatically_adjust_theme": "招待者の設定に基づいてテーマを自動調整する", + "user_dynamic_booking_disabled": "グループ内の一部のユーザーは、現在動的なグループ予約を無効にしています", + "allow_dynamic_booking_tooltip": "\"+\" を使って複数のユーザー名を追加することで動的に作成できるグループ予約リンク。例: \"cal.com/bailey+peer\"", + "allow_dynamic_booking": "出席者が動的なグループ予約を通じてあなたを予約できるようにする", "email": "Eメールアドレス", "email_placeholder": "jdoe@example.com", "full_name": "フルネーム", @@ -309,54 +325,57 @@ "booking_rescheduled": "予約の再スケジュール済", "booking_created": "予約を作成しました", "event_triggers": "イベントトリガー", - "subscriber_url": "購読者のURL", - "create_new_webhook": "新しいWebhookを作成", - "create_new_webhook_to_account": "アカウントに新しいWebhookを作成します", - "new_webhook": "新しい Webhook", - "receive_cal_meeting_data": "イベントがスケジュールまたはキャンセルされた場合、リアルタイムで特定の URL でカルミーティングデータを受信します。", - "responsive_fullscreen_iframe": "レスポンシブフルスクリーンiframe", - "loading": "ロード中...", + "subscriber_url": "購読者の URL", + "create_new_webhook": "新しいウェブフックを作成", + "webhooks": "ウェブフック", + "team_webhooks": "チームのウェブフック", + "create_new_webhook_to_account": "アカウントに新しいウェブフックを作成します", + "new_webhook": "新しいウェブフック", + "receive_cal_meeting_data": "イベントが予定またはキャンセルされた場合、特定の URL で開かれる Cal ミーティングデータをリアルタイムに受信する。", + "receive_cal_event_meeting_data": "このイベントが予定またはキャンセルされた場合、特定の URL で開かれる Cal ミーティングデータをリアルタイムに受信する。", + "responsive_fullscreen_iframe": "レスポンシブフルスクリーン iframe", + "loading": "読み込み中...", "standard_iframe": "標準 iframe", - "iframe_embed": "iframeの埋め込み", - "embed_calcom": "あなたのウェブサイトにCal.comを埋め込む最も簡単な方法。", - "integrate_using_embed_or_webhooks": "埋め込みオプションを使用してウェブサイトと統合するか、カスタムWebhookを使用してリアルタイムの予約情報を取得します。", - "schedule_a_meeting": "ミーティングをスケジュール", - "view_and_manage_billing_details": "請求書の詳細の表示と管理", - "view_and_edit_billing_details": "請求の詳細を表示および編集したり、サブスクリプションをキャンセルしたりできます。", + "iframe_embed": "iframe の埋め込み", + "embed_calcom": "ウェブサイトに Cal.com を埋め込む最も簡単な方法。", + "integrate_using_embed_or_webhooks": "埋め込みオプションを使用してウェブサイトと連携するか、カスタムウェブフックを使用して予約情報をリアルタイムに取得します。", + "schedule_a_meeting": "ミーティングを予定する", + "view_and_manage_billing_details": "請求情報の詳細を表示・管理する", + "view_and_edit_billing_details": "請求情報の詳細を表示・編集、およびサブスクリプションをキャンセルする。", "go_to_billing_portal": "請求ポータルに移動", - "need_anything_else": "他に何か必要なものはありませんか?", - "further_billing_help": "請求に関するお手伝いが必要な場合は、サポートチームがお手伝いします。", - "contact_our_support_team": "サポートチームにお問い合わせ", - "uh_oh": "ええっと!", + "need_anything_else": "他に必要なものはありませんか?", + "further_billing_help": "請求に関するサポートが必要な場合は、サポートチームがお手伝いします。", + "contact_our_support_team": "サポートチームに問い合わせる", + "uh_oh": "おっと!", "no_event_types_have_been_setup": "このユーザーはまだイベントタイプを設定していません。", "edit_logo": "ロゴを編集", "upload_a_logo": "ロゴをアップロード", "remove_logo": "ロゴを削除する", - "enable": "有効", + "enable": "有効にする", "code": "コード", "code_is_incorrect": "コードが正しくありません。", - "add_an_extra_layer_of_security": "パスワードが盗まれた場合に備えて、追加のセキュリティレイヤーをアカウントに追加してください。", - "2fa": "二段階認証", - "enable_2fa": "二段階認証を有効にする", - "disable_2fa": "二段階認証を無効にする", - "disable_2fa_recommendation": "2FAを無効にする必要がある場合は、できるだけ早く有効にすることをお勧めします。", - "error_disabling_2fa": "二段階認証の無効化エラー", - "error_enabling_2fa": "二段階認証の設定エラー", + "add_an_extra_layer_of_security": "パスワードが盗まれる場合に備えて、アカウントのセキュリティを強化します。", + "2fa": "二要素認証", + "enable_2fa": "二要素認証を有効にする", + "disable_2fa": "二要素認証を無効にする", + "disable_2fa_recommendation": "2FA を無効にする必要がある場合は、その必要がなくなった後すぐに有効にしなおすことをおすすめします。", + "error_disabling_2fa": "二要素認証の無効中にエラーが発生しました", + "error_enabling_2fa": "二要素認証の設定中にエラーが発生しました", "security": "セキュリティ", "manage_account_security": "アカウントのセキュリティを管理する", "password": "パスワード", "password_updated_successfully": "パスワードが正常に更新されました", - "password_has_been_changed": "パスワードは正常に変更されました。", - "error_changing_password": "パスワードの変更エラー", + "password_has_been_changed": "パスワードが正常に変更されました。", + "error_changing_password": "パスワードの変更中にエラーが発生しました", "something_went_wrong": "問題が発生しました。", - "something_doesnt_look_right": "何かが正しく見えませんか?", + "something_doesnt_look_right": "何かおかしいことがありますか?", "please_try_again": "もう一度お試しください", "super_secure_new_password": "セキュリティ保護された新しいパスワード", "new_password": "新しいパスワード", - "your_old_password": "以前のパスワード", + "your_old_password": "古いパスワード", "current_password": "現在のパスワード", "change_password": "パスワードの変更", - "new_password_matches_old_password": "新しいパスワードは以前のパスワードと同じです。別のパスワードを選択してください。", + "new_password_matches_old_password": "新しいパスワードは古いパスワードと同じです。別のパスワードを選択してください。", "current_incorrect_password": "現在のパスワードが正しくありません", "incorrect_password": "パスワードが正しくありません。", "1_on_1": "1対1", @@ -398,29 +417,31 @@ "booking_confirmation": "{{profileName}} の{{eventTypeTitle}} を確認", "booking_reschedule_confirmation": "{{profileName}} の {{eventTypeTitle}} をスケジュール変更する", "in_person_meeting": "リンクまたは対面ミーティング", + "link_meeting": "ミーティングをリンク", "phone_call": "電話番号", "phone_number": "電話番号", "enter_phone_number": "電話番号を入力", "reschedule": "スケジュールの変更", + "reschedule_this": "代わりにスケジュールを変更", "book_a_team_member": "代わりにチームメンバーを予約する", "or": "もしくは", "go_back": "戻る", "email_or_username": "メールアドレスまたはユーザー名", "send_invite_email": "招待メールを送信", - "role": "権限", + "role": "ロール", "edit_role": "役割を編集する", "edit_team": "チームを編集", "reject": "拒否", "accept": "許可", - "leave": "退出する", + "leave": "退出", "profile": "プロフィール", - "my_team_url": "チームのURL", + "my_team_url": "チームの URL", "team_name": "チーム名", - "your_team_name": "あなたのチーム名", + "your_team_name": "チーム名", "team_updated_successfully": "チームが正常に更新されました", "your_team_updated_successfully": "チームが正常に更新されました。", "about": "このアプリについて", - "team_description": "あなたのチームについてのいくつかの文章。これはあなたのチームのURLページに表示されます。", + "team_description": "あなたのチームに関する簡潔な情報。あなたのチームの URL ページに表示されます。", "members": "メンバー", "member": "メンバー", "owner": "所有者", @@ -428,17 +449,19 @@ "new_member": "新しいメンバー", "invite": "招待する", "invite_new_member": "新しいメンバーを招待する", - "invite_new_team_member": "チームに誰かを招待します。", + "invite_new_team_member": "チームに誰かを招待する。", "change_member_role": "チームメンバーの役割を変更する", - "disable_cal_branding": "Cal.comのブランディングを無効にする", - "disable_cal_branding_description": "Cal.comのすべてのブランディングを公開ページから非表示にする。", - "danger_zone": "危険なエリア", + "disable_cal_branding": "Cal.com のブランディングを無効にする", + "disable_cal_branding_description": "公開ページに表示される Cal.com のブランディングをすべて非表示にする。", + "danger_zone": "危険なゾーン", "back": "戻る", "cancel": "キャンセル", + "apply": "適用", + "cancel_event": "このイベントをキャンセル", "continue": "続行", "confirm": "確認", "disband_team": "チームを解散する", - "disband_team_confirmation_message": "本当にこのチームを解散しますか? あなた'veがこのチームのリンクを共有した人は、もはやそれを使用して予約することができません。", + "disband_team_confirmation_message": "このチームを解散しますか? あなたがこのチームのリンクを共有した人は、それを使って予約することができなくなります。", "remove_member_confirmation_message": "このメンバーをチームから削除してもよろしいですか?", "confirm_disband_team": "はい、チームを解散します。", "confirm_remove_member": "はい、メンバーを削除します", @@ -504,6 +527,8 @@ "first_day_of_week": "週の最初の日", "single_theme": "シングルテーマ", "brand_color": "ブランドカラー", + "light_brand_color": "ブランドカラー (ライトテーマ)", + "dark_brand_color": "ブランドカラー (ダークテーマ)", "file_not_named": "ファイル名が [idOrSlug]/[user] ではありません", "create_team": "チームを作成", "name": "名前", @@ -525,22 +550,22 @@ "change_email_tip": "変更内容を有効にするには、一度ログアウトしてから再度ログインを行う必要があります。", "little_something_about": "あなた自身につい何か少し。", "profile_updated_successfully": "プロフィールが正常に更新されました", - "your_user_profile_updated_successfully": "ユーザープロフィールの更新が完了しました。", - "user_cannot_found_db": "ユーザーはログインしているようですが、dbで見つかりません", + "your_user_profile_updated_successfully": "ユーザープロフィールが正常に更新されました。", + "user_cannot_found_db": "ユーザーはログインしているようですが、データベースに見つかりません", "embed_and_webhooks": "埋め込みとウェブフック", "enabled": "有効", "disabled": "無効", "disable": "無効", - "billing": "請求書", - "manage_your_billing_info": "支払い情報を管理し、サブスクリプションをキャンセルします。", + "billing": "請求", + "manage_your_billing_info": "請求情報の管理とサブスクリプションのキャンセル。", "availability": "利用可能期間", - "configure_availability": "予約が可能な時間を設定してください。", + "configure_availability": "予約が可能な時間を設定する。", "change_weekly_schedule": "ウィークリースケジュールを変更する", "logo": "ロゴ", "error": "エラー", "team_logo": "チームロゴ", - "add_location": "位置情報を追加", - "attendees": "参加者", + "add_location": "場所を追加", + "attendees": "出席者", "add_attendees": "出席者を追加", "show_advanced_settings": "詳細設定を表示", "event_name": "イベント名", @@ -548,68 +573,75 @@ "meeting_with_user": "{USER} とのミーティング", "additional_inputs": "追加入力", "label": "ラベル", - "placeholder": "代替", - "type": "種類", + "placeholder": "プレースホルダー", + "type": "タイプ", "edit": "編集", "add_input": "入力を追加", - "opt_in_booking": "予約を選択", + "disable_notes": "カレンダーのメモを非表示にする", + "disable_notes_description": "プライバシー上の理由から、追加の入力やメモはカレンダーのエントリで非表示にされます。それらは引き続きあなたのメールアドレスに送信されます。", + "opt_in_booking": "オプトイン予約", "opt_in_booking_description": "連携にプッシュされ確認メールが送信される前に、予約を手動で確認する必要があります。", "disable_guests": "ゲストを無効化", - "disable_guests_description": "予約中は追加ゲストの追加を無効にします。", - "invitees_can_schedule": "招待者はスケジュールできます", + "disable_guests_description": "予約中のゲストの追加を無効にします。", + "invitees_can_schedule": "招待されるメンバーはスケジュールできる", "date_range": "日付の範囲", "calendar_days": "暦日", "business_days": "営業日", "set_address_place": "住所または場所を設定", - "cal_invitee_phone_number_scheduling": "Calは、予約する前に招待者に電話番号を入力するように依頼します。", - "cal_provide_google_meet_location": "CalはGoogleMeetの場所を提供します。", - "cal_provide_zoom_meeting_url": "カルはZoomミーティングURLを提供します。", - "cal_provide_tandem_meeting_url": "カルはTandemミーティングURLを提供します。", - "cal_provide_video_meeting_url": "カルは毎日のビデオミーティングのURLを提供します。", - "cal_provide_jitsi_meeting_url": "カルはJitsi MeetミーティングURLを提供します。", - "cal_provide_huddle01_meeting_url": "カルはHuddle01 Web3ミーティングURLを提供します。", - "require_payment": "お支払いが必要です", - "commission_per_transaction": "取引あたりの手数料", - "event_type_updated_successfully_description": "イベント種別の更新が完了しました。", - "hide_event_type": "イベント種別を非表示", + "set_link_meeting": "ミーティングのリンクを設定", + "cal_invitee_phone_number_scheduling": "招待されるメンバーは予約する前に Cal によって電話番号の入力を求められる。", + "cal_provide_google_meet_location": "Cal は GoogleMeet の場所を提供する。", + "cal_provide_zoom_meeting_url": "Cal は Zoom ミーティングの URLを提供する。", + "cal_provide_tandem_meeting_url": "Cal は Tandem ミーティングの URL を提供する。", + "cal_provide_video_meeting_url": "Cal はビデオミーティングの URL を提供する。", + "cal_provide_jitsi_meeting_url": "Cal は Jitsi Meet の URL を提供する。", + "cal_provide_huddle01_meeting_url": "Cal は Huddle01 web3 ビデオミーティングの URL を提供する。", + "cal_provide_teams_meeting_url": "Cal は、MS Teams のミーティングの URL を提供する。注: 職場や学校のアカウントが必要です。", + "require_payment": "有料", + "commission_per_transaction": "取引あたりのコミッション", + "event_type_updated_successfully_description": "イベントタイプが正常に更新されました。", + "hide_event_type": "イベントタイプを非表示にする", "edit_location": "場所を編集", - "into_the_future": "未来に向けて", + "into_the_future": "将来の日付", "within_date_range": "日付の範囲内で", - "indefinitely_into_future": "無期限で将来へ", + "indefinitely_into_future": "未定の将来の日", "this_input_will_shown_booking_this_event": "この入力は、このイベントを予約する際に表示されます", - "add_new_custom_input_field": "新しいカスタム入力項目を追加", + "add_new_custom_input_field": "新しいカスタム入力フィールドを追加する", "quick_chat": "クイックチャット", - "add_new_team_event_type": "新しいチームイベント種別を追加", - "add_new_event_type": "新しいイベント種別を追加", - "new_event_type_to_book_description": "人々が時間を予約するための新しいイベント種別を作成します。", + "add_new_team_event_type": "新しいチームイベントのタイプを追加する", + "add_new_event_type": "新しいイベントタイプを追加する", + "new_event_type_to_book_description": "ユーザーが時間を予約する際に使う新しいイベントタイプを作成する。", "length": "長さ", "minimum_booking_notice": "最低限の予約通知", "slot_interval": "時間帯の間隔", "slot_interval_default": "イベントの長さを使用する (デフォルト)", - "delete_event_type_description": "このイベント種別を削除してもよろしいですか? あなた'veがこのリンクを共有した人は、もはやそれを使用して予約することはできません。", - "delete_event_type": "イベント種別を削除", - "confirm_delete_event_type": "はい、イベント種別を削除します", + "delete_event_type_description": "このイベントタイプを削除してもよろしいですか? あなたがこのリンクを共有した人は、それを使って予約することができなくなります。", + "delete_event_type": "イベントタイプを削除する", + "confirm_delete_event_type": "はい、イベントタイプを削除します", "delete_account": "アカウントを削除する", "confirm_delete_account": "はい。アカウントを削除します", "delete_account_confirmation_message": "あなたの Cal.com アカウントを本当に削除してもよろしいですか?あなたのアカウントリンクを共有しているユーザーは、そのアカウントを使用して予約を行うことができなくなり、保存していた設定も失われます。", - "integrations": "統合", + "integrations": "連携", + "apps": "アプリ", + "app_store": "App Store", + "app_store_description": "人とテクノロジーと職場をつなぐ。", "settings": "設定", "event_type_moved_successfully": "イベントの種類が正常に移動されました", "next_step": "手順をスキップ", "prev_step": "前の手順", "installed": "インストール済み", "disconnect": "接続を解除", - "embed_your_calendar": "ウェブページにカレンダーを埋め込む", + "embed_your_calendar": "カレンダーをウェブページに埋め込む", "connect_your_favourite_apps": "お気に入りのアプリを接続する", "automation": "自動化", - "configure_how_your_event_types_interact": "イベント種別とカレンダーをどのように連携するかを設定します。", + "configure_how_your_event_types_interact": "イベントタイプとカレンダーの連携の仕方を設定します。", "select_destination_calendar": "以下にイベントを作成する", - "connect_an_additional_calendar": "追加のカレンダーを接続する", + "connect_an_additional_calendar": "別のカレンダーを接続する", "conferencing": "ミーティング中", "calendar": "カレンダー", "not_installed": "インストールされていません", "error_password_mismatch": "パスワードが一致しません。", - "error_required_field": "この項目は必須です。", + "error_required_field": "このフィールドは必須です。", "status": "状態", "team_view_user_availability": "ユーザーの状態を表示する", "team_view_user_availability_disabled": "状態を表示するには、招待を承認する必要があります", @@ -640,6 +672,24 @@ "import_from": "インポート元", "access_token": "アクセストークン", "visit_roadmap": "ロードマップ", + "popular_categories": "人気のカテゴリー", + "trending_apps": "人気のアプリ", + "all_apps": "すべてのアプリ", + "installed_apps": "インストールされているアプリ", + "manage_your_connected_apps": "インストール済みアプリの管理や設定の変更を行う", + "browse_apps": "アプリを閲覧", + "features": "特徴", + "permissions": "アクセス許可", + "terms_and_privacy": "利用規約とプライバシー", + "published_by": "{{author}} が公開", + "subscribe": "サブスクライブ", + "buy": "購入", + "install_app": "アプリをインストール", + "categories": "カテゴリー", + "pricing": "料金設定", + "learn_more": "詳細情報", + "privacy_policy": "プライバシーポリシー", + "terms_of_service": "利用規約", "remove": "削除する", "add": "追加する", "verify_wallet": "ウォレットを確認する", @@ -655,7 +705,63 @@ "prisma_studio_tip_description": "最初のユーザーを設定する方法を学ぶ", "contact_sales": "営業担当に問い合わせる", "error_404": "404 エラー", + "default": "デフォルト", + "set_to_default": "デフォルトに設定", + "new_schedule_btn": "新しいスケジュール", + "add_new_schedule": "新しいスケジュールを追加", + "delete_schedule": "スケジュールを削除", + "schedule_created_successfully": "{{scheduleName}} のスケジュールが正常に作成されました", "availability_updated_successfully": "利用可否の更新に成功しました", + "schedule_deleted_successfully": "スケジュールが正常に削除されました", + "default_schedule_name": "作業時間", + "new_schedule_heading": "空き状況のスケジュールを作成する", + "new_schedule_description": "空き状況のスケジュールを作成すると、各イベントタイプの空き状況を管理できます。空き状況のスケジュールは、イベントの複数のタイプに適用できます。", "requires_ownership_of_a_token": "次のアドレスに属するトークンの所有権が必要となります:", - "example_name": "山田太朗" + "example_name": "山田太朗", + "time_format": "時間の形式", + "12_hour": "12 時間", + "24_hour": "24 時間", + "redirect_success_booking": "予約時にリダイレクト", + "you_are_being_redirected": "$t(second, {\"count\": {{seconds}} }) で {{ url }} にリダイレクトします", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "この機能を利用するには、Pro アカウントへのアップグレードが必要です。", + "duplicate": "複製", + "you_can_manage_your_schedules": "スケジュール管理は、「空き状況」ページで行えます。", + "api_keys": "API キー", + "api_key_modal_subtitle": "API キーを使用すると、自分のアカウントを API で呼び出すことができます。", + "api_keys_subtitle": "自分のアカウントへアクセスする際に使用する API キーを生成します。", + "generate_new_api_key": "新しい API キーを生成", + "create_api_key": "API キーの作成", + "personal_note": "このキーに名前を付ける", + "personal_note_placeholder": "例: 開発", + "api_key_no_note": "無名の API キー", + "api_key_never_expires": "この API キーには有効期限がありません", + "edit_api_key": "API キーの編集", + "never_expire_key": "有効期限なし", + "delete_api_key": "API キーの取り消し", + "success_api_key_created": "API キーが正常に作成されました", + "success_api_key_edited": "API キーが正常に更新されました", + "create": "作成", + "success_api_key_created_bold_tagline": "この API キーを安全な場所に保存します。", + "you_will_only_view_it_once": "このウィンドウを一度閉じてしまうと、再度表示することはできません。", + "copy_to_clipboard": "クリップボードにコピー", + "confirm_delete_api_key": "この API キーを取り消す", + "revoke_api_key": "API キーの取り消し", + "api_key_copied": "API キーをコピーしました!", + "delete_api_key_confirm_title": "この API キーをご利用のアカウントから完全に削除しますか?", + "copy": "コピー", + "expire_date": "有効期限", + "expired": "期限切れ", + "never_expires": "有効期限なし", + "expires": "有効期限あり", + "request_reschedule_booking": "予約のスケジュール変更をリクエスト", + "reason_for_reschedule": "スケジュール変更の理由", + "book_a_new_time": "新しい時間を予約", + "reschedule_request_sent": "スケジュール変更のリクエストが送信されました", + "reschedule_modal_description": "スケジュール設定済みのミーティングをキャンセルし、スケジュールの設定者に通知して新しい時間を選択するように依頼します。", + "reason_for_reschedule_request": "スケジュール変更をリクエストする理由", + "send_reschedule_request": "スケジュールの変更をリクエスト", + "edit_booking": "予約を編集", + "reschedule_booking": "予約のスケジュールを変更", + "former_time": "以前の時間" } diff --git a/apps/web/public/static/locales/ja/vital.json b/apps/web/public/static/locales/ja/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/ja/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/ko/common.json b/apps/web/public/static/locales/ko/common.json index 8193ffb5a2..bb89d3d9a8 100644 --- a/apps/web/public/static/locales/ko/common.json +++ b/apps/web/public/static/locales/ko/common.json @@ -2,6 +2,8 @@ "trial_days_left": "PRO 평가판의 남은 기한 $t(day, {\"count\": {{days}} })", "day": "{{count}}일", "day_plural": "{{count}}일", + "second": "{{count}}초", + "second_plural": "{{count}}초", "upgrade_now": "지금 업그레이드", "accept_invitation": "초대 수락", "calcom_explained": "Cal.com은 오픈 소스 Calendly 대안으로 자신의 데이터, 워크플로 및 형태를 컨트롤할 수 있습니다.", @@ -60,9 +62,15 @@ "password_reset_instructions": "요청하지 않으셨다면 이 이메일을 무시하십시오. 비밀번호는 변경되지 않습니다.", "event_awaiting_approval_subject": "승인을 기다리고 있습니다: {{date}} {{name}} 의 {{eventType}}", "event_still_awaiting_approval": "이벤트가 아직 승인을 기다리고 있습니다.", + "booking_submitted_subject": "예약 제출됨: {{date}}에 {{name}}에 의한 {{eventType}}", "your_meeting_has_been_booked": "회의가 예약되었습니다.", "event_type_has_been_rescheduled_on_time_date": "{{name}} {{eventType}} 가 {{date}} {{time}}({{timeZone}}) 으로 재예약되었습니다.", "event_has_been_rescheduled": "업데이트 - 이벤트 일정이 변경되었습니다.", + "request_reschedule_title_attendee": "예약 일정 변경 요청", + "request_reschedule_subtitle": "{{organizer}}에서 예약을 취소했고 다른 시간을 선택할 것을 요청했습니다.", + "request_reschedule_title_organizer": "{{attendee}}에게 일정 변경을 요청하셨습니다", + "request_reschedule_subtitle_organizer": "예약을 취소하셨기 때문에 {{attendee}} 님이 새 예약 시간을 선택해야 합니다.", + "reschedule_reason": "일정 변경 사유", "hi_user_name": "안녕하세요 {{name}}", "ics_event_title": "{{eventType}}과 {{name}}", "new_event_subject": "새 이벤트: {{attendeeName}} - {{date}} - {{eventType}}", @@ -81,6 +89,7 @@ "meeting_url": "회의 URL", "meeting_request_rejected": "회의 요청이 거절되었습니다.", "rescheduled_event_type_subject": "일정 변경: {{eventType}}의 {{name}}가 {{date}}로 변경되었습니다.", + "requested_to_reschedule_subject_attendee": "일정 변경 조치 필요: {{name}} 님과 함께 새로운 {{eventType}} 시간을 예약하십시오", "rejected_event_type_with_organizer": "거절됨: {{date}} {{organizer}} 의 {{eventType}}", "hi": "안녕하세요", "join_team": "팀에 합류하세요", @@ -284,6 +293,10 @@ "hover_over_bold_times_tip": "팁: 전체 타임스탬프를 보려면 굵게 표시된 시간 위에 마우스를 갖다대세요.", "start_time": "시작 시간", "end_time": "종료 시간", + "buffer_time": "여유 시간", + "before_event": "이벤트 전", + "after_event": "이벤트 후", + "event_buffer_default": "여유 시간 없음", "buffer": "버퍼", "your_day_starts_at": "당신의 하루는 다음 시간에 시작됩니다.", "your_day_ends_at": "당신의 하루는 다음 시간에 끝납니다.", @@ -299,6 +312,9 @@ "light": "밝은", "dark": "어두움", "automatically_adjust_theme": "초대 받은 사람의 선호에 따라 자동으로 테마 조정", + "user_dynamic_booking_disabled": "그룹에서 사용자 중 일부는 현재 동적 그룹 예약을 비활성화했습니다", + "allow_dynamic_booking_tooltip": "'+' 기호로 여러 사용자 이름을 추가하여 동적으로 생성할 수 있는 그룹 예약 링크. 예: 'cal.com/bailey+peer'", + "allow_dynamic_booking": "참석자가 동적 그룹 예약을 통해 귀하를 예약하도록 허용", "email": "이메일", "email_placeholder": "jdoe@example.com", "full_name": "이름", @@ -311,9 +327,12 @@ "event_triggers": "이벤트 트리거", "subscriber_url": "구독자 Url", "create_new_webhook": "새 웹훅 만들기", + "webhooks": "웹훅", + "team_webhooks": "팀 웹훅", "create_new_webhook_to_account": "내 계정에 새 웹훅 만들기", "new_webhook": "새 웹훅", "receive_cal_meeting_data": "일정이 잡히거나 취소될 때 지정된 URL에서 실시간으로 Cal 회의 데이터를 수신합니다.", + "receive_cal_event_meeting_data": "일정이 잡혔거나 취소되었을 때 지정된 URL에서 실시간으로 Cal 회의 데이터를 수신합니다.", "responsive_fullscreen_iframe": "반응형 전체 화면 iframe", "loading": "로드 중", "standard_iframe": "표준 iframe", @@ -398,10 +417,12 @@ "booking_confirmation": "{{profileName}}로 {{eventTypeTitle}} 확인", "booking_reschedule_confirmation": "{{profileName}}을 사용하여 {{eventTypeTitle}} 일정을 변경하세요.", "in_person_meeting": "온라인 또는 오프라인 회의", + "link_meeting": "회의 링크", "phone_call": "전화", "phone_number": "전화 번호", "enter_phone_number": "전화 번호 입력", "reschedule": "일정 변경", + "reschedule_this": "대신 일정 조정하기", "book_a_team_member": "대신 팀원을 예약하세요.", "or": "또는", "go_back": "돌아가기", @@ -435,6 +456,8 @@ "danger_zone": "위험 구역", "back": "뒤로 가기", "cancel": "취소", + "apply": "적용", + "cancel_event": "이 이벤트 취소", "continue": "계속하기", "confirm": "확인", "disband_team": "팀 해체", @@ -504,6 +527,8 @@ "first_day_of_week": "첫 번째 요일", "single_theme": "단일 테마", "brand_color": "브랜드 색상", + "light_brand_color": "브랜드 색상(밝은 테마)", + "dark_brand_color": "브랜드 색상(어두운 테마)", "file_not_named": "파일 이름이 [idOrSlug]/[user]가 아닙니다.", "create_team": "팀 만들기", "name": "이름", @@ -552,6 +577,8 @@ "type": "타입", "edit": "수정", "add_input": "입력 추가", + "disable_notes": "캘린더에서 메모 숨기기", + "disable_notes_description": "개인 정보 보호를 위해, 추가 입력 및 메모는 캘린더 항목에서 숨겨지지만, 여전히 귀하의 이메일로 전송됩니다.", "opt_in_booking": "옵트인 예약", "opt_in_booking_description": "확인 메일이 전송되기 전에 예약을 수동으로 확인하십시오.", "disable_guests": "게스트 비활성화", @@ -561,6 +588,7 @@ "calendar_days": "일", "business_days": "영업일", "set_address_place": "주소 또는 장소 설정", + "set_link_meeting": "회의 링크 설정", "cal_invitee_phone_number_scheduling": "Cal은 일정을 잡기 전에 초대받은 사람에게 전화번호를 요청합니다.", "cal_provide_google_meet_location": "Cal은 Google Meet 위치를 제공합니다.", "cal_provide_zoom_meeting_url": "Cal은 Zoom 회의 URL을 제공합니다.", @@ -568,6 +596,7 @@ "cal_provide_video_meeting_url": "Cal은 일일 화상 회의 URL을 제공합니다.", "cal_provide_jitsi_meeting_url": "Cal은 Jitsi Meet 회의 URL을 제공합니다.", "cal_provide_huddle01_meeting_url": "Cal은 Huddle01 Web3 회의 URL을 제공합니다.", + "cal_provide_teams_meeting_url": "Cal은 MS Teams 회의 URL을 제공합니다. 참고: 직장 또는 학교 계정이 있어야 합니다.", "require_payment": "지불 요청", "commission_per_transaction": "거래당 수수료", "event_type_updated_successfully_description": "이벤트 타입이 성공적으로 업데이트되었습니다.", @@ -593,6 +622,9 @@ "confirm_delete_account": "예, 계정을 삭제합니다", "delete_account_confirmation_message": "Cal.com 계정을 삭제하시겠습니까? 계정 링크를 공유한 사람은 더 이상 이 링크를 사용하여 예약할 수 없으며 저장한 모든 기본 설정이 손실됩니다.", "integrations": "통합", + "apps": "앱", + "app_store": "앱 스토어", + "app_store_description": "사람, 기술, 직장을 연결합니다.", "settings": "설정", "event_type_moved_successfully": "이벤트 타입이 성공적으로 이동되었습니다.", "next_step": "건너뛰기", @@ -640,6 +672,24 @@ "import_from": "가져오기 위치", "access_token": "토큰 액세스", "visit_roadmap": "로드맵", + "popular_categories": "인기 카테고리", + "trending_apps": "트랜드 앱", + "all_apps": "모든 앱", + "installed_apps": "설치된 앱", + "manage_your_connected_apps": "설치된 앱 관리 또는 설정 변경", + "browse_apps": "앱 찾아보기", + "features": "기능", + "permissions": "권한", + "terms_and_privacy": "약관 및 개인정보 보호", + "published_by": "퍼블리셔 {{author}}", + "subscribe": "구독", + "buy": "구매", + "install_app": "앱 설치", + "categories": "카테고리", + "pricing": "가격", + "learn_more": "자세히 알아보기", + "privacy_policy": "개인정보 보호정책", + "terms_of_service": "서비스 약관", "remove": "제거", "add": "추가", "verify_wallet": "지갑 인증", @@ -655,7 +705,63 @@ "prisma_studio_tip_description": "첫 사용자 설정 방법 알아보기", "contact_sales": "영업팀에 문의", "error_404": "오류 404", + "default": "기본값", + "set_to_default": "기본값으로 설정", + "new_schedule_btn": "새 일정", + "add_new_schedule": "새 일정 추가", + "delete_schedule": "일정 삭제", + "schedule_created_successfully": "{{scheduleName}} 일정이 성공적으로 생성되었습니다", "availability_updated_successfully": "사용성이 성공적으로 업데이트되었습니다.", + "schedule_deleted_successfully": "일정이 성공적으로 삭제되었습니다", + "default_schedule_name": "작업 시간", + "new_schedule_heading": "가용성 일정 생성", + "new_schedule_description": "가용성 일정을 생성하면 이벤트 유형 전반에 걸쳐 가용성을 관리할 수 있습니다. 하나 이상의 이벤트 유형에 적용할 수 있습니다.", "requires_ownership_of_a_token": "다음 주소에 속한 토큰의 소유권이 필요합니다:", - "example_name": "홍길동" + "example_name": "홍길동", + "time_format": "시간 형식", + "12_hour": "12시간", + "24_hour": "24시간", + "redirect_success_booking": "예약 시 리디렉션", + "you_are_being_redirected": "$t(second, {\"count\": {{seconds}} }) 후에 {{ url }}(으)로 리디렉션됩니다.", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "이 기능을 사용하려면 Pro 계정으로 업그레이드해야 합니다.", + "duplicate": "복제", + "you_can_manage_your_schedules": "가용성 페이지에서 자신의 일정을 관리할 수 있습니다.", + "api_keys": "API 키", + "api_key_modal_subtitle": "API 키를 사용하면 자신의 계정에 대해 API를 호출할 수 있습니다.", + "api_keys_subtitle": "자신의 계정에 액세스하는 데 사용할 API 키를 생성합니다.", + "generate_new_api_key": "새 API 키 생성", + "create_api_key": "API 키 생성", + "personal_note": "이 키의 이름 지정하기", + "personal_note_placeholder": "예: 개발", + "api_key_no_note": "이름 없는 API 키", + "api_key_never_expires": "이 API 키에는 만료일이 없습니다", + "edit_api_key": "API 키 편집", + "never_expire_key": "만료되지 않음", + "delete_api_key": "API 키 취소", + "success_api_key_created": "API 키가 생성되었습니다", + "success_api_key_edited": "API 키가 업데이트되었습니다", + "create": "생성", + "success_api_key_created_bold_tagline": "이 API 키를 안전한 곳에 보관하십시오.", + "you_will_only_view_it_once": "이 모달을 닫으면 다시 볼 수 없습니다.", + "copy_to_clipboard": "클립보드에 복사", + "confirm_delete_api_key": "이 API 키 취소", + "revoke_api_key": "API 키 취소", + "api_key_copied": "API 키가 복사되었습니다!", + "delete_api_key_confirm_title": "계정에서 이 API 키를 영구적으로 제거하시겠습니까?", + "copy": "복사", + "expire_date": "만료일", + "expired": "만료됨", + "never_expires": "만료되지 않음", + "expires": "만료", + "request_reschedule_booking": "예약 일정 변경 요청", + "reason_for_reschedule": "일정 변경 사유", + "book_a_new_time": "새 시간 예약", + "reschedule_request_sent": "일정 변경 요청 전송됨", + "reschedule_modal_description": "이렇게 하면 예약된 회의가 취소되고 스케줄러에 알림이 보내지며 새로운 시간 선택이 요청됩니다.", + "reason_for_reschedule_request": "일정 변경 요청 사유", + "send_reschedule_request": "일정 변경 요청", + "edit_booking": "예약 편집", + "reschedule_booking": "예약 일정 변경", + "former_time": "이전 시간" } diff --git a/apps/web/public/static/locales/ko/vital.json b/apps/web/public/static/locales/ko/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/ko/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/nl/common.json b/apps/web/public/static/locales/nl/common.json index 98a6f9e364..467c2bc438 100644 --- a/apps/web/public/static/locales/nl/common.json +++ b/apps/web/public/static/locales/nl/common.json @@ -1,9 +1,36 @@ { + "trial_days_left": "U heeft nog $t(day, {\"count\": {{days}} }) over van uw PRO-proefperiode", + "day": "{{count}} dag", + "day_plural": "{{count}} dagen", + "second": "{{count}} seconde", + "second_plural": "{{count}} seconden", + "upgrade_now": "Nu upgraden", + "accept_invitation": "Uitnodiging accepteren", + "calcom_explained": "Cal.com is het open source Calendly-alternatief waarmee u uw eigen gegevens, werkstroom en uiterlijk in de hand houdt.", + "have_any_questions": "Heeft u vragen? We zijn er om te helpen.", + "reset_password_subject": "Cal.com: Instructies voor het opnieuw instellen van uw wachtwoord", + "event_declined_subject": "Afgewezen: {{eventType}} met {{name}} op {{date}}", + "event_cancelled_subject": "Geannuleerd: {{eventType}} met {{name}} op {{date}}", + "event_request_declined": "Uw gebeurtenisverzoek is geweigerd", + "event_request_cancelled": "Uw geplande gebeurtenis is geannuleerd", + "organizer": "Organisator", + "need_to_reschedule_or_cancel": "Moet u verplaatsen of annuleren?", + "cancellation_reason": "Reden voor annulering", + "cancellation_reason_placeholder": "Waarom annuleert u? (optioneel)", + "rejection_reason": "Reden voor weigering", + "rejection_reason_title": "Boekingsverzoek weigeren?", + "rejection_reason_description": "Weet u zeker dat u de boeking wilt weigeren? We informeren de persoon die probeerde te boeken. U kunt hieronder een reden opgeven.", + "rejection_confirmation": "Boeking weigeren", + "manage_this_event": "Deze gebeurtenis beheren", + "your_event_has_been_scheduled": "Uw gebeurtenis is gepland", "accept_our_license": "Accepteer onze gebruiksvoorwaarden door de .env variable <1>NEXT_PUBLIC_LICENSE_CONSENT naar '{{agree}}' te veranderen.", "remove_banner_instructions": "Om deze notificatie te verbergen, open het .env bestand en verander <1>NEXT_PUBLIC_LICENSE_CONSENT naar '{{agree}}'.", "error_message": "De foutmelding was: '{{errorMessage}}'", + "refund_failed_subject": "Terugbetaling mislukt: {{name}} - {{date}} - {{eventType}}", "refund_failed": "De terugbetaling voor het evenement {{eventType}} met {{userName}} op {{date}} is mislukt.", + "check_with_provider_and_user": "Neem contact op met uw betalingsverwerker en {{user}} om dit op te lossen.", "a_refund_failed": "Een terugbetaling is mislukt", + "awaiting_payment_subject": "In afwachting van betaling: {{eventType}} met {{name}} op {{date}}", "meeting_awaiting_payment": "Uw vergadering wacht op betaling", "help": "Help", "price": "Prijs", @@ -25,10 +52,27 @@ "no_more_results": "Geen verdere resultaten", "load_more_results": "Meer resultaten laden", "integration_meeting_id": "{{integrationName}} afspraak ID: {{meetingId}}", + "confirmed_event_type_subject": "Bevestigd: {{eventType}} met {{name}} op {{date}}", "new_event_request": "Nieuwe afspraakaanvraag: {{attendeeName}} - {{date}} - {{eventType}}", + "confirm_or_reject_request": "Bevestig of weiger het verzoek", "check_bookings_page_to_confirm_or_reject": "Controleer uw boekingen om de boeking te bevestigen of te verwerpen.", + "event_awaiting_approval": "Een gebeurtenis wacht op uw goedkeuring", + "someone_requested_an_event": "Iemand heeft verzocht om een gebeurtenis op uw kalender te plannen.", + "someone_requested_password_reset": "Iemand heeft een link verzocht om uw wachtwoord te wijzigen.", + "password_reset_instructions": "Als dit niet heeft verzocht, kunt u deze e-mail gerust negeren en wordt uw wachtwoord niet gewijzigd.", + "event_awaiting_approval_subject": "In afwachting van goedkeuring: {{eventType}} met {{name}} om {{date}}", + "event_still_awaiting_approval": "Een gebeurtenis wacht nog op uw goedkeuring", + "booking_submitted_subject": "Boeking verzonden: {{eventType}} met {{name}} om {{date}}", "your_meeting_has_been_booked": "Uw afspraak is geboekt", "event_type_has_been_rescheduled_on_time_date": "Uw {{eventType}} met {{name}} is verplaatst naar {{time}} ({{timeZone}}) op {{date}}.", + "event_has_been_rescheduled": "Bijgewerkt - Uw gebeurtenis is verplaatst", + "request_reschedule_title_attendee": "Vezoek om uw boeking te verplaatsen", + "request_reschedule_subtitle": "{{organizer}} heeft de boeking geannuleerd en u verzocht een andere tijd te kiezen.", + "request_reschedule_title_organizer": "U heeft {{attendee}} gevraagd om te verplaatsen", + "request_reschedule_subtitle_organizer": "U heeft de boeking geannuleerd en {{attendee}} moet samen met u een nieuwe boekingstijd kiezen.", + "reschedule_reason": "Reden voor verplaatsen", + "hi_user_name": "Hallo {{name}}", + "ics_event_title": "{{eventType}} met {{name}}", "new_event_subject": "Nieuwe afspraak: {{attendeeName}} - {{date}} - {{eventType}}", "join_by_entrypoint": "Word lid via {{entryPoint}}", "notes": "Opmerkingen", @@ -44,12 +88,23 @@ "meeting_password": "Deelneming wachtwoord", "meeting_url": "URL afspraak", "meeting_request_rejected": "Uw afspraak is helaas afgewezen", + "rescheduled_event_type_subject": "Verplaatst: {{eventType}} met {{name}} op {{date}}", + "requested_to_reschedule_subject_attendee": "Actie vereist voor verplaatsing: boek een nieuwe tijd voor {{eventType}} met {{name}}", "rejected_event_type_with_organizer": "Afgewezen: {{eventType}} met {{organizer}} op {{date}}", "hi": "Hallo", "join_team": "Lidverzoek accepteren", + "manage_this_team": "Dit team beheren", + "team_info": "Teaminformatie", "request_another_invitation_email": "Als u {{toEmail}} liever niet als uw Cal.com e-mail wilt gebruiken of al een Cal.com account heeft, vraag dan een andere uitnodiging aan voor die e-mail.", "you_have_been_invited": "U bent uitgenodigd om lid te worden van het team {{teamName}}", + "user_invited_you": "{{user}} heeft u uitgenodigd om lid te worden van het team {{team}} op Cal.com", + "hidden_team_member_title": "U bent verborgen in dit team", + "hidden_team_member_message": "Uw plaats is niet betaald. Upgrade naar Pro of laat de teameigenaar weten dat men uw plaats kan betalen.", + "hidden_team_owner_message": "U heeft een Pro-account nodig om teams te gebruiken, u bent verborgen tot u upgradet.", "link_expires": "p.s. De uitnodiging verloopt over {{expiresIn}} uur.", + "upgrade_to_per_seat": "Upgraden naar per plaats", + "team_upgrade_seats_details": "Van de {{memberCount}} leden in uw team zijn {{unpaidCount}} plaats(en) onbetaald. Voor $ {{seatPrice}}/maand per plaats bedragen de geschatte totale kosten van uw abonnement $ {{totalCost}}/maand.", + "team_upgraded_successfully": "Uw team is geüpgraded!", "use_link_to_reset_password": "Gebruik de onderstaande link om uw wachtwoord te resetten", "hey_there": "Hallo daar,", "forgot_your_password_calcom": "Wachtwoord vergeten? - Cal.com", @@ -67,6 +122,7 @@ "webhook_created_successfully": "Webhook succesvol toegevoegd!", "webhook_updated_successfully": "Webhook succesvol bijgewerkt!", "webhook_removed_successfully": "Webhook succesvol verwijderd!", + "payload_template": "Gegevenssjabloon", "dismiss": "Negeer", "no_data_yet": "Nog geen gegevens", "ping_test": "Ping test", @@ -100,6 +156,7 @@ "rejected": "Geweigerd", "unconfirmed": "Niet-bevestigd", "guests": "Bezoekers", + "guest": "Gast", "web_conferencing_details_to_follow": "Details van de web conferentie zullen volgen.", "the_username": "De gebruikersnaam", "username": "Gebruikersnaam", @@ -124,6 +181,7 @@ "30min_meeting": "30 Minuten afspraak", "secret_meeting": "Geheime Afspraak", "login_instead": "In plaats daarvan inloggen", + "already_have_an_account": "Heeft u al een account?", "create_account": "Account Aanmaken", "confirm_password": "Wachtwoord bevestigen", "create_your_account": "Uw account aanmaken", @@ -194,6 +252,15 @@ "failed": "Mislukt", "password_has_been_reset_login": "Uw wachtwoord is opnieuw ingesteld. U kunt nu inloggen met uw nieuwe wachtwoord.", "unexpected_error_try_again": "Er is een onverwachte fout opgetreden. Probeer het opnieuw.", + "sunday_time_error": "Ongeldige tijd op zondag", + "monday_time_error": "Ongeldige tijd op maandag", + "tuesday_time_error": "Ongeldige tijd op dinsdag", + "wednesday_time_error": "Ongeldige tijd op woensdag", + "thursday_time_error": "Ongeldige tijd op donderdag", + "friday_time_error": "Ongeldige tijd op vrijdag", + "saturday_time_error": "Ongeldige tijd op zaterdag", + "error_end_time_before_start_time": "De eindtijd mag niet voor de begintijd zijn", + "error_end_time_next_day": "De eindtijd mag niet meer dan 24 uur later zijn", "back_to_bookings": "Terug naar afspraken", "free_to_pick_another_event_type": "U kunt op elk moment een andere afspraak maken.", "cancelled": "Geannuleerd", @@ -226,6 +293,10 @@ "hover_over_bold_times_tip": "Tip: Beweeg over de dik gedrukte tijden voor een volledige weergave", "start_time": "Begin tijd", "end_time": "Eind tijd", + "buffer_time": "Buffertijd", + "before_event": "Voor gebeurtenis", + "after_event": "Na gebeurtenis", + "event_buffer_default": "Geen buffertijd", "buffer": "Buffer", "your_day_starts_at": "Uw dag begint om", "your_day_ends_at": "Uw dag eindigt om", @@ -241,6 +312,9 @@ "light": "Licht", "dark": "Donker", "automatically_adjust_theme": "Thema automatisch aanpassen op basis van voorkeur genodigde", + "user_dynamic_booking_disabled": "Sommige gebruikers in de groep hebben momenteel dynamische groepsboekingen uitgeschakeld", + "allow_dynamic_booking_tooltip": "Groepsboekingslinks die dynamisch kunnen worden aangemaakt door meerdere gebruikersnamen toe te voegen met een \"+\", bijvoorbeeld: \"cal.com/bailey+peer", + "allow_dynamic_booking": "Sta deelnemers toe om u te boeken via dynamische groepsboekingen", "email": "E-mailadres", "email_placeholder": "jjansen@voorbeeld.nl", "full_name": "Volledige naam", @@ -253,9 +327,12 @@ "event_triggers": "Webhook activaties", "subscriber_url": "Subscriptie URL", "create_new_webhook": "Nieuwe webhook aanmaken", + "webhooks": "Webhooks", + "team_webhooks": "Teamwebhooks", "create_new_webhook_to_account": "Maak een nieuwe webhook aan voor uw account", "new_webhook": "Maak een Webhook", "receive_cal_meeting_data": "Direct Cal afspraakgegevens ontvangen via een opgegeven URL wanneer een event is gepland of geannuleerd.", + "receive_cal_event_meeting_data": "Ontvang Cal-afspraakgegevens in realtime via een opgegeven URL wanneer een gebeurtenis wordt gepland of geannuleerd.", "responsive_fullscreen_iframe": "Responsieve iframe", "loading": "Laden...", "standard_iframe": "Standaard iframe", @@ -273,6 +350,7 @@ "no_event_types_have_been_setup": "Deze gebruiker heeft nog geen evenementen.", "edit_logo": "Bewerk logo", "upload_a_logo": "Logo uploaden", + "remove_logo": "Logo verwijderen", "enable": "Inschakelen", "code": "Code", "code_is_incorrect": "Code is ongeldig.", @@ -339,16 +417,19 @@ "booking_confirmation": "Bevestig uw {{eventTypeTitle}} met {{profileName}}", "booking_reschedule_confirmation": "Opnieuw plannen van uw {{eventTypeTitle}} met {{profileName}}", "in_person_meeting": "Online of persoonlijk afspreken", + "link_meeting": "Vergadering koppelen", "phone_call": "Telefoon afspraak", "phone_number": "Telefoon nummer", "enter_phone_number": "Telefoonnummer", "reschedule": "Boeking wijzigen", + "reschedule_this": "Verplaatst in plaats daarvan", "book_a_team_member": "Of, reserveer een teamlid", "or": "OF", "go_back": "Ga terug", "email_or_username": "E-mail of gebruikersnaam", "send_invite_email": "Verstuur een uitnodigingsmail", "role": "Functie", + "edit_role": "Rol bewerken", "edit_team": "Bewerk team", "reject": "Weigeren", "accept": "Aanvaarden", @@ -364,15 +445,19 @@ "members": "Leden", "member": "Lid", "owner": "Eigenaar", + "admin": "Beheerder", "new_member": "Nieuw Lid", "invite": "Uitnodigen", "invite_new_member": "Nieuw lid uitnodigen", "invite_new_team_member": "Nodig iemand uit tot uw team.", + "change_member_role": "Rol teamlid wijzigen", "disable_cal_branding": "Cal.com branding uitschakelen", "disable_cal_branding_description": "Verberg alle Cal.com branding op uw openbare pagina's.", "danger_zone": "Gevarenzone", "back": "Terug", "cancel": "Annuleren", + "apply": "Toepassen", + "cancel_event": "Deze gebeurtenis annuleren", "continue": "Doorgaan", "confirm": "Bevestig", "disband_team": "Team ontbinden", @@ -382,6 +467,8 @@ "confirm_remove_member": "Ja, verwijder lid", "remove_member": "Verwijder lid", "manage_your_team": "Beheer uw team", + "no_teams": "U heeft nog geen teams.", + "no_teams_description": "Teams geven anderen de mogelijkheid om gebeurtenissen te boeken die zijn gedeeld tussen uw collega's.", "submit": "Opslaan", "delete": "Verwijderen", "update": "Bijwerken", @@ -389,8 +476,16 @@ "pending": "In afwachting", "open_options": "Opties openen", "copy_link": "Link naar afspraak kopiëren", + "share": "Delen", + "share_event": "Zou u mijn Cal willen boeken of mij uw link willen sturen?", + "copy_link_team": "Link naar team kopiëren", + "leave_team": "Team verlaten", + "confirm_leave_team": "Ja, team verlaten", + "leave_team_confirmation_message": "Weet u zeker dat u dit team wilt verlaten? U kunt er niet meer mee boeken.", + "user_from_team": "{{user}} van {{team}}", "preview": "Voorbeeld", "link_copied": "Link gekopieerd!", + "link_shared": "Link gedeeld!", "title": "Titel", "description": "Beschrijving", "quick_video_meeting": "Een korte videovergadering.", @@ -405,7 +500,11 @@ "url": "Link", "hidden": "Verborgen", "readonly": "Enkel-lezen", + "plan_description": "U heeft momenteel het {{plan}}-abonnement.", + "plan_upgrade_invitation": "Upgrade uw account naar het Pro-abonnement om alle functies te ontgrendelen die we te bieden hebben.", "plan_upgrade": "U moet uw abonnement upgraden om meer dan één actief afspraaktype te hebben.", + "plan_upgrade_teams": "U moet uw abonnement upgraden om een team aan te maken.", + "plan_upgrade_instructions": "U kunt <1>hier upgraden.", "event_types_page_title": "Evenementen", "event_types_page_subtitle": "Maak deelbare evenementen die bezoekers kunnen gebruiken om afspraken in uw agenda te boeken.", "new_event_type_btn": "Nieuw evenement", @@ -418,6 +517,8 @@ "event_type_created_successfully": "{{eventTypeTitle}} evenement met succes aangemaakt", "event_type_updated_successfully": "Evenement is bijgewerkt", "event_type_deleted_successfully": "Evenement is verwijderd", + "web3_metamask_added": "Metamasker toegevoegd", + "web3_metamask_disconnected": "Verbindig met metamasker verbroken", "hours": "Uren", "your_email": "Uw E-mailadres", "change_avatar": "Wijzig avatar", @@ -425,6 +526,9 @@ "timezone": "Tijdzone", "first_day_of_week": "Eerste dag van de week", "single_theme": "Enkel Thema", + "brand_color": "Merkkleur", + "light_brand_color": "Merkkleur (licht thema)", + "dark_brand_color": "Merkkleur (donker thema)", "file_not_named": "Bestandsnaam is niet [idOrSlug]/[user]", "create_team": "Maak Team", "name": "Naam", @@ -435,10 +539,15 @@ "create_first_team_and_invite_others": "Maak uw eerste team en nodig andere gebruikers uit om in groepsverband af te spreken.", "create_team_to_get_started": "Maak een team aan om aan de slag te gaan", "teams": "Teams", + "team_billing": "Teamfacturatie", + "upgrade_to_flexible_pro_title": "We hebben het factureren gewijzigd voor teams", + "upgrade_to_flexible_pro_message": "Er zijn leden in uw team zonder een plaats. Upgrade uw Pro-abonnement om de ontbrekende plaatsen te dekken.", + "changed_team_billing_info": "Vanaf januari 2022 rekenen we per plaats voor teamleden. Leden van uw team die Pro gratis hadden, hebben nu een proefperiode van 14 dagen. Zodra hun proefperiode afloopt, worden deze leden verborgen voor uw team, tenzij u nu upgradet.", "create_manage_teams_collaborative": "Maak en beheer teams om gezamenlijke functies te gebruiken.", "only_available_on_pro_plan": "Deze functie is alleen beschikbaar in het Pro-pakket", "remove_cal_branding_description": "Om de Cal branding van uw boekpagina's te verwijderen, moet u upgraden naar een Pro-account.", "edit_profile_info_description": "Bewerk profielgegevens die worden weergegeven op uw boekpagina.", + "change_email_tip": "U moet zich mogelijk afmelden en weer aanmelden voordat de wijziging ingaat.", "little_something_about": "Een introductie over uzelf.", "profile_updated_successfully": "Profiel bijgewerkt", "your_user_profile_updated_successfully": "Uw profiel is is met succes bijgewerkt.", @@ -460,6 +569,7 @@ "add_attendees": "Deelnemers toevoegen", "show_advanced_settings": "Toon geavanceerde instellingen", "event_name": "Naam van evenement", + "event_name_tooltip": "De naam die in kalenders wordt weergegeven", "meeting_with_user": "Afspraak met {USER}", "additional_inputs": "Extra invoer", "label": "Omschrijving", @@ -467,6 +577,8 @@ "type": "Type", "edit": "Wijzigen", "add_input": "Voeg een invoer toe", + "disable_notes": "Notities verbergen in kalender", + "disable_notes_description": "Om privacyredenen worden extra invoer en notities verborgen in het kalenderitem. Ze worden nog wel naar uw e-mail verzonden.", "opt_in_booking": "Opt-in boeking", "opt_in_booking_description": "De boeking moet handmatig bevestigd worden voordat het definitief is.", "disable_guests": "Gasten uitschakelen", @@ -476,6 +588,7 @@ "calendar_days": "kalender dagen", "business_days": "werkdagen", "set_address_place": "Een adres of plaats instellen", + "set_link_meeting": "Stel een link in naar de vergadering", "cal_invitee_phone_number_scheduling": "Cal zal de genodigde om een telefoonnummer vragen.", "cal_provide_google_meet_location": "Cal zal een Google Meet meeting-URL meegeven in de afspraak bevestiging.", "cal_provide_zoom_meeting_url": "Cal zal een Zoom meeting-URL meegeven in de afspraak bevestiging.", @@ -483,6 +596,7 @@ "cal_provide_video_meeting_url": "Cal zal een Daily meeting-URL meegeven in de afspraak bevestiging.", "cal_provide_jitsi_meeting_url": "Cal zal een Jitsi Meet meeting-URL meegeven in de afspraak bevestiging.", "cal_provide_huddle01_meeting_url": "Cal zal een Huddle01 web3 meeting-URL meegeven in de afspraak bevestiging.", + "cal_provide_teams_meeting_url": "Cal geeft een vergaderings-URL voor MS Teams. OPMERKING: MOET EEN WERK- OF SCHOOLACCOUNT HEBBEN", "require_payment": "Betaling vereisen", "commission_per_transaction": "commissie per transactie", "event_type_updated_successfully_description": "Uw evenement is met succes bijgewerkt.", @@ -504,8 +618,15 @@ "delete_event_type_description": "Weet u zeker dat u dit evenement wilt verwijderen? Iedereen met wie u deze link heeft gedeeld zal hem niet meer kunnen gebruiken om te boeken.", "delete_event_type": "Verwijder Evenement", "confirm_delete_event_type": "Ja, verwijder evenement", + "delete_account": "Account verwijderen", + "confirm_delete_account": "Ja, account verwijderen", + "delete_account_confirmation_message": "Weet u zeker dat u uw Cal.com-account wilt verwijderen? Iedereen met wie u uw accountlink gedeeld heeft, kan er niet meer mee boeken en alle voorkeuren die u opgeslagen heeft gaan verloren.", "integrations": "Integraties", + "apps": "Apps", + "app_store": "App Store", + "app_store_description": "Mensen, technologie en de werkplek verbinden.", "settings": "Instellingen", + "event_type_moved_successfully": "Gebeurtenistype is verplaatst", "next_step": "Stap overslaan", "prev_step": "Vorige stap", "installed": "Geinstalleerd", @@ -514,13 +635,133 @@ "connect_your_favourite_apps": "Koppel je favoriete apps.", "automation": "Automatisering", "configure_how_your_event_types_interact": "Stel in hoe uw evenementen met uw agenda wilt verbinden.", + "select_destination_calendar": "Maak gebeurtenissen aan op", "connect_an_additional_calendar": "Koppel een extra agenda", "conferencing": "Confereren", "calendar": "Agenda", "not_installed": "Niet geïnstalleerd", "error_password_mismatch": "Wachtwoorden komen niet overeen.", "error_required_field": "Dit veld is verplicht.", + "status": "Status", + "team_view_user_availability": "Beschikbaarheid van gebruikers weergeven", + "team_view_user_availability_disabled": "Gebruiker moet de uitnodiging accepteren om beschikbaarheid weer te geven", + "set_as_away": "Zet uzelf op afwezig", + "set_as_free": "Status afwezig uitschakelen", + "user_away": "Deze gebruiker is momenteel afwezig.", + "user_away_description": "De persoon die u probeert te boeken heeft zichzelf op afwezig gezet en accepteert dus geen nieuwe boekingen.", + "meet_people_with_the_same_tokens": "Ontmoet mensen met dezelfde tokens", + "only_book_people_and_allow": "Boek alleen en laat boekingen toe van mensen die dezelfde tokens,, DAO's of NFT's delen.", + "saml_config_deleted_successfully": "SAML-configuratie verwijderd", + "account_created_with_identity_provider": "Uw account is aangemaakt met behulp van een identiteitsprovider.", + "account_managed_by_identity_provider": "Uw account wordt beheerd door {{provider}}", + "account_managed_by_identity_provider_description": "Ga naar uw accountinstellingen bij {{provider}} om uw e-mailadres en wachtwoord te wijzigen, tweestapsverificatie in te schakelen en meer.", + "signin_with_google": "Aanmelden met Google", + "signin_with_saml": "Aanmelden met SAML", + "saml_configuration": "SAML-configuratie", + "delete_saml_configuration": "SAML-configuratie verwijderen", + "delete_saml_configuration_confirmation_message": "Weet u zeker dat u de SAML-configuratie wilt verwijderen? Uw teamleden die SAML-aanmelding gebruiken hebben niet langer toegang tot Cal.com.", + "confirm_delete_saml_configuration": "Ja, SAML-configuratie verwijderen", + "saml_not_configured_yet": "SAML nog niet geconfigureerd", + "saml_configuration_description": "Plak de SAML-metagegevens van uw identiteitsprovider in het tekstvak hieronder om uw SAML-configuratie bij te werken.", + "saml_configuration_placeholder": "Plak de SAML-metagegevens van uw identiteitsprovider hier", + "saml_configuration_update_failed": "Bijwerken van SAML-configuratie mislukt", + "saml_configuration_delete_failed": "Verwijderen van SAML-configuratie mislukt", + "saml_email_required": "Voer een e-mailadres in zodat we uw SAML-identiteitsprovider kunnen vinden", + "you_will_need_to_generate": "U moet een toegangstoken genereren van uw oude planningstool.", + "import": "Importeren", + "import_from": "Importeren van", + "access_token": "Toegangstoken", + "visit_roadmap": "Routekaart", + "popular_categories": "Populaire categorieën", + "trending_apps": "Trending apps", + "all_apps": "Alle apps", + "installed_apps": "Geinstalleerde apps", + "manage_your_connected_apps": "Beheer uw geïnstalleerde apps of wijzig instellingen", + "browse_apps": "Door apps bladeren", + "features": "Functies", + "permissions": "Machtigingen", + "terms_and_privacy": "Voorwaarden en privacy", + "published_by": "Gepubliceerd door {{author}}", + "subscribe": "Abonneren", + "buy": "Kopen", + "install_app": "App installeren", + "categories": "Categorieën", + "pricing": "Prijzen", + "learn_more": "Meer informatie", + "privacy_policy": "Privacybeleid", + "terms_of_service": "Gebruiksvoorwaarden", + "remove": "Verwijderen", + "add": "Toevoegen", + "verify_wallet": "Wallet verifiëren", + "connect_metamask": "Metamasker verbinden", + "create_events_on": "Maak gebeurtenissen aan op:", + "missing_license": "Ontbrekende licentie", + "signup_requires": "Commerciële licentie vereist", + "signup_requires_description": "Cal.com, Inc. biedt momenteel geen gratis opensourceversie van de registratiepagina aan. Om volledige toegang te krijgen tot de registratieonderdelen moet u een commerciële licentie verkrijgen. Voor persoonlijk gebruik raden we aan om accounts aan te maken op het Prisma Data Platform of een andere Postgres-interface.", + "next_steps": "Volgende stappen", + "acquire_commercial_license": "Verkrijg een commerciële licentie", + "the_infrastructure_plan": "Het infrastructuurabonnement is gebaseerd op gebruik en heeft speciale kortingen voor start-ups.", + "prisma_studio_tip": "Maak een account aan via Prisma Studio", + "prisma_studio_tip_description": "Leer hoe u uw eerste gebruiker instelt", + "contact_sales": "Contact met Sales", + "error_404": "Fout 404", "default": "standaard keuze", "set_to_default": "Zet als standaard keuze", - "availability_updated_successfully": "Beschikbaarheid bijgewerkt" + "new_schedule_btn": "Nieuwe planning", + "add_new_schedule": "Voeg een nieuwe planning toe", + "delete_schedule": "Planning verwijderen", + "schedule_created_successfully": "Planning {{scheduleName}} aangemaakt", + "availability_updated_successfully": "Beschikbaarheid bijgewerkt", + "schedule_deleted_successfully": "Planning verwijderd", + "default_schedule_name": "Werktijden", + "new_schedule_heading": "Beschikbaarheidsschema aanmaken", + "new_schedule_description": "Door beschikbaarheidsschema's te maken kunt u de beschikbaarheid beheren voor verschillende gebeurteenistypes. Ze kunnen worden toegepast op één of meer gebeurtenistypes.", + "requires_ownership_of_a_token": "Vereist eigendom van een token dat aan het volgende adres toebehoort:", + "example_name": "Jan Pietersen", + "time_format": "Tijdnotatie", + "12_hour": "12 uur", + "24_hour": "24 uur", + "redirect_success_booking": "Omleiding bij boeking ", + "you_are_being_redirected": "U wordt omgeleid naar {{ url }} over $t(seconde, {\"count\": {{seconden}} }).", + "external_redirect_url": "https://voorbeeld.com/omleiding-naar-mijn-succespagina", + "redirect_url_upgrade_description": "Om deze functie te gebruiken, moet u upgraden naar een Pro-account.", + "duplicate": "Dupliceren", + "you_can_manage_your_schedules": "U kunt uw schema's beheren op de beschikbaarheidspagina.", + "api_keys": "API-sleutels", + "api_key_modal_subtitle": "Met API-sleutels kunt API-aanroepen naar uw eigen account doen.", + "api_keys_subtitle": "Genereer API-sleutels voor toegang tot uw eigen account.", + "generate_new_api_key": "Nieuwe API-sleutel genereren", + "create_api_key": "Maak een API-sleutel aan", + "personal_note": "Geef deze sleutel een naam", + "personal_note_placeholder": "Bijvoorbeeld Ontwikkeling", + "api_key_no_note": "Naamloze API-sleutel", + "api_key_never_expires": "Deze API-sleutel heeft geen verloopdatum", + "edit_api_key": "API-sleutel bewerken", + "never_expire_key": "Verloopt nooit", + "delete_api_key": "API-sleutel intrekken", + "success_api_key_created": "API-sleutel aangemaakt", + "success_api_key_edited": "API-sleutel bijgewerkt", + "create": "Aanmaken", + "success_api_key_created_bold_tagline": "Sla deze API-sleutel ergens veilig op.", + "you_will_only_view_it_once": "U kunt hem niet opnieuw bekijken zodra u dit venster sluit.", + "copy_to_clipboard": "Naar klembord kopiëren", + "confirm_delete_api_key": "Deze API-sleutel intrekken", + "revoke_api_key": "API-sleutel intrekken", + "api_key_copied": "API-sleutel gekopieerd!", + "delete_api_key_confirm_title": "Deze API-sleutel permanent van uw account verwijderen?", + "copy": "Kopiëren", + "expire_date": "Verloopdatum", + "expired": "Verlopen", + "never_expires": "Verloopt nooit", + "expires": "Verloopt", + "request_reschedule_booking": "Aanvraag om uw boeking te verplaatsen", + "reason_for_reschedule": "Reden voor verplaatsen", + "book_a_new_time": "Boek een nieuwe tijd", + "reschedule_request_sent": "Verplaatsingsaanvraag verzonden", + "reschedule_modal_description": "Hierdoor wordt de geplande vergadering geannuleerd, wordt de planner geïnformeerd en wordt hem/haar gevraagd een nieuwe tijd te kiezen.", + "reason_for_reschedule_request": "Reden voor verplaatsingsaanvraag", + "send_reschedule_request": "Verplaatsing aanvragen ", + "edit_booking": "Boeking bewerken", + "reschedule_booking": "Boeking verplaatsen", + "former_time": "Vorige tijd" } diff --git a/apps/web/public/static/locales/nl/vital.json b/apps/web/public/static/locales/nl/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/nl/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/pl/common.json b/apps/web/public/static/locales/pl/common.json index d4ad56e4da..d14e1878d9 100644 --- a/apps/web/public/static/locales/pl/common.json +++ b/apps/web/public/static/locales/pl/common.json @@ -66,6 +66,11 @@ "your_meeting_has_been_booked": "Twoje spotkanie zostało zarezerwowane", "event_type_has_been_rescheduled_on_time_date": "Twój {{eventType}} z {{name}} został przełożony na {{time}} ({{timeZone}}) w {{date}}.", "event_has_been_rescheduled": "Zaktualizowano - Twoje wydarzenie zostało przełożone", + "request_reschedule_title_attendee": "Poproś o przełożenie rezerwacji", + "request_reschedule_subtitle": "{{organizer}} anulował(a) rezerwację i prosi Cię o wybranie innego terminu.", + "request_reschedule_title_organizer": "{{attendee}} poproszono o zmianę harmonogramu", + "request_reschedule_subtitle_organizer": "Rezerwacja została przez Ciebie anulowana i {{attendee}} musi wybrać z Tobą nowy czas rezerwacji.", + "reschedule_reason": "Powód zmiany harmonogramu", "hi_user_name": "Cześć {{name}}", "ics_event_title": "{{eventType}} z {{name}}", "new_event_subject": "Nowe wydarzenie: {{attendeeName}} - {{date}} - {{eventType}}", @@ -84,6 +89,7 @@ "meeting_url": "URL Spotkania", "meeting_request_rejected": "Twoja prośba o spotkanie została odrzucona", "rescheduled_event_type_subject": "Przełożono: {{eventType}} z {{name}} w {{date}}", + "requested_to_reschedule_subject_attendee": "Działanie wymagało zmiany harmonogramu: Zarezerwuj nową godzinę na wydarzenie {{eventType}} z {{name}}", "rejected_event_type_with_organizer": "Odrzucono: {{eventType}} z {{organizer}} w {{date}}", "hi": "Cześć", "join_team": "Dołącz do zespołu", @@ -720,5 +726,42 @@ "external_redirect_url": "https://example.com/przekieruj-do-moja-strona", "redirect_url_upgrade_description": "Aby korzystać z tej funkcji, musisz uaktualnić do konta Pro.", "duplicate": "Duplikat", - "you_can_manage_your_schedules": "Możesz zarządzać swoimi harmonogramami na stronie Dostępności." + "you_can_manage_your_schedules": "Możesz zarządzać swoimi harmonogramami na stronie Dostępności.", + "api_keys": "Klucze API", + "api_key_modal_subtitle": "Klucze API umożliwiają wykonywanie wywołań API dla Twojego konta.", + "api_keys_subtitle": "Wygeneruj klucze API, które wykorzystasz, aby uzyskać dostęp do swojego konta.", + "generate_new_api_key": "Wygeneruj nowy klucz API", + "create_api_key": "Utwórz klucz API", + "personal_note": "Nazwij ten klucz", + "personal_note_placeholder": "Np. rozwój", + "api_key_no_note": "Klucz API bez nazwy", + "api_key_never_expires": "Ten klucz API nie ma daty wygaśnięcia", + "edit_api_key": "Edytuj klucz API", + "never_expire_key": "Nigdy nie wygasa", + "delete_api_key": "Unieważnij klucz API", + "success_api_key_created": "Klucz API został utworzony", + "success_api_key_edited": "Klucz API został zaktualizowany", + "create": "Utwórz", + "success_api_key_created_bold_tagline": "Zapisz ten klucz API w bezpiecznym miejscu.", + "you_will_only_view_it_once": "Po zamknięciu tego okna modalnego nie będziesz w stanie wyświetlić ponownie tego elementu.", + "copy_to_clipboard": "Kopiuj do schowka", + "confirm_delete_api_key": "Unieważnij ten klucz API", + "revoke_api_key": "Unieważnij klucz API", + "api_key_copied": "Skopiowano klucz API!", + "delete_api_key_confirm_title": "Czy chcesz trwale usunąć ten klucz API ze swojego konta?", + "copy": "Kopiuj", + "expire_date": "Data wygaśnięcia", + "expired": "Wygasł", + "never_expires": "Nigdy nie wygasa", + "expires": "Wygasa", + "request_reschedule_booking": "Poproś o przełożenie rezerwacji", + "reason_for_reschedule": "Powód zmiany harmonogramu", + "book_a_new_time": "Zarezerwuj nową godzinę", + "reschedule_request_sent": "Wysłano prośbę o zmianę harmonogramu", + "reschedule_modal_description": "To spowoduje anulowanie zaplanowanego spotkania, powiadomienie osoby planującej i poproszenie jej o wybranie nowego terminu.", + "reason_for_reschedule_request": "Powód prośby o zmianę harmonogramu", + "send_reschedule_request": "Poproś o zmianę harmonogramu ", + "edit_booking": "Edytuj rezerwację", + "reschedule_booking": "Przełóż rezerwację", + "former_time": "Poprzednia godzina" } diff --git a/apps/web/public/static/locales/pl/vital.json b/apps/web/public/static/locales/pl/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/pl/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/pt-BR/common.json b/apps/web/public/static/locales/pt-BR/common.json index 539276ac82..952e6d3ce4 100644 --- a/apps/web/public/static/locales/pt-BR/common.json +++ b/apps/web/public/static/locales/pt-BR/common.json @@ -2,6 +2,8 @@ "trial_days_left": "Você tem $t(day, {\"count\": {{days}} }) restantes no seu período de testes da versão PRO", "day": "{{count}} dia", "day_plural": "{{count}} dias", + "second": "{{count}} segundo", + "second_plural": "{{count}} segundos", "upgrade_now": "Atualizar agora", "accept_invitation": "Aceitar convite", "calcom_explained": "Cal.com é a alternativa de código aberto ao Calendly que coloca você no controle dos seus dados, fluxo de trabalho e aparência.", @@ -13,6 +15,12 @@ "event_request_cancelled": "Seu evento agendado foi cancelado", "organizer": "Organizador", "need_to_reschedule_or_cancel": "Precisa reagendar ou cancelar?", + "cancellation_reason": "Motivo do cancelamento", + "cancellation_reason_placeholder": "Por que você está cancelando? (opcional)", + "rejection_reason": "Motivo da recusa", + "rejection_reason_title": "Recusar a solicitação de reserva?", + "rejection_reason_description": "Tem certeza de que deseja recusar a reserva? Avisaremos a pessoa que tentou reservar. Se quiser, informe um motivo abaixo.", + "rejection_confirmation": "Recusar a reserva", "manage_this_event": "Gerenciar este evento", "your_event_has_been_scheduled": "A sua reunião foi agendada", "accept_our_license": "Aceite a nossa licença alterando a variável no arquivo .env <1>NEXT_PUBLIC_LICENSE_CONSENT para '{{agree}}'.", @@ -54,9 +62,15 @@ "password_reset_instructions": "Se você não fez essa solicitação, você pode ignorar este e-mail e sua senha não será alterada.", "event_awaiting_approval_subject": "Aguardando aprovação: {{eventType}} com {{name}} em {{date}}", "event_still_awaiting_approval": "Um evento está aguardando a sua aprovação", + "booking_submitted_subject": "Reserva enviada: {{eventType}} com {{name}} em {{date}}", "your_meeting_has_been_booked": "A sua reunião foi agendada", "event_type_has_been_rescheduled_on_time_date": "O evento {{eventType}} com {{name}} foi reagendado para as {{time}} ({{timeZone}}) de {{date}}.", "event_has_been_rescheduled": "Atualização - O seu evento foi reagendado", + "request_reschedule_title_attendee": "Solicitar reagendamento da reserva", + "request_reschedule_subtitle": "{{organizer}} cancelou a reserva e solicitou que você escolhesse outro horário.", + "request_reschedule_title_organizer": "Você solicitou que {{attendee}} reagendasse", + "request_reschedule_subtitle_organizer": "Você cancelou a reserva e {{attendee}} deve escolher outro horário de reserva com você.", + "reschedule_reason": "Motivo do reagendamento", "hi_user_name": "Olá {{name}}", "ics_event_title": "{{eventType}} com {{name}}", "new_event_subject": "Novo evento: {{attendeeName}} - {{date}} - {{eventType}}", @@ -75,13 +89,22 @@ "meeting_url": "URL da reunião", "meeting_request_rejected": "A sua solicitação de reunião foi rejeitada", "rescheduled_event_type_subject": "Reagendado: {{eventType}} com {{name}} em {{date}}", + "requested_to_reschedule_subject_attendee": "Ação de reagendamento necessária: reserve outro horário para {{eventType}} com {{name}}", "rejected_event_type_with_organizer": "Rejeitado: {{eventType}} com {{organizer}} em {{date}}", "hi": "Olá", "join_team": "Entrar no time", + "manage_this_team": "Gerenciar esta equipe", + "team_info": "Informações da equipe", "request_another_invitation_email": "Se não quiser utilizar {{toEmail}} como email do Cal.com, ou já tem uma conta em Cal.com, por favor peça outro convite para esse email.", "you_have_been_invited": "Você recebeu um convite para se juntar ao time {{teamName}}", "user_invited_you": "{{user}} convidou você para se juntar ao time {{team}} em Cal.com", + "hidden_team_member_title": "Você está oculto nesta equipe", + "hidden_team_member_message": "Seu assento não é pago, atualize para a versão Pro ou avise o proprietário da equipe de que é possível pagar pelo seu assento.", + "hidden_team_owner_message": "É preciso ter uma conta Pro para usar equipes, você permanecerá oculto até a atualização.", "link_expires": "P.S.: Expira em {{expiresIn}} horas.", + "upgrade_to_per_seat": "Atualize para \"Por assento\"", + "team_upgrade_seats_details": "Dos {{memberCount}} membros da sua equipe, há {{unpaidCount}} assento(s) não pago(s). A ${{seatPrice}}/mês por assento, o custo total estimado da sua assinatura é de ${{totalCost}}/mês.", + "team_upgraded_successfully": "Sua equipe foi atualizada com êxito!", "use_link_to_reset_password": "Use o link abaixo para redefinir a sua senha", "hey_there": "Olá,", "forgot_your_password_calcom": "Esqueceu sua senha? - Cal.com", @@ -158,6 +181,7 @@ "30min_meeting": "Reunião de 30 min", "secret_meeting": "Reunião Secreta", "login_instead": "Em vez disso, faça login", + "already_have_an_account": "Já tem uma conta?", "create_account": "Criar Conta", "confirm_password": "Confirmar senha", "create_your_account": "Crie a sua conta", @@ -228,6 +252,15 @@ "failed": "Falhou", "password_has_been_reset_login": "A sua senha foi redefinida. Agora pode fazer login com a sua senha recém-criada.", "unexpected_error_try_again": "Ocorreu um erro inesperado. Tente novamente.", + "sunday_time_error": "Horário inválido no domingo", + "monday_time_error": "Horário inválido na segunda-feira", + "tuesday_time_error": "Horário inválido na terça-feira", + "wednesday_time_error": "Horário inválido na quarta-feira", + "thursday_time_error": "Horário inválido na quinta-feira", + "friday_time_error": "Horário inválido na sexta-feira", + "saturday_time_error": "Horário inválido no sábado", + "error_end_time_before_start_time": "O horário de término não pode ser anterior ao de início", + "error_end_time_next_day": "O horário de término não pode ser maior do que 24 horas", "back_to_bookings": "Voltar às Reservas", "free_to_pick_another_event_type": "Sinta-se à vontade para escolher outro evento a qualquer hora.", "cancelled": "Canceladas", @@ -260,6 +293,10 @@ "hover_over_bold_times_tip": "Dica: passe o mouse sobre os horários em negrito para um visão completa", "start_time": "Hora de início", "end_time": "Hora de término", + "buffer_time": "Horário de intervalo", + "before_event": "Antes do evento", + "after_event": "Após o evento", + "event_buffer_default": "Sem horário de intervalo", "buffer": "Intervalo", "your_day_starts_at": "O seu dia começa às", "your_day_ends_at": "O seu dia termina às", @@ -275,6 +312,9 @@ "light": "Claro", "dark": "Escuro", "automatically_adjust_theme": "Ajustar tema automaticamente com base nas preferências do usuário", + "user_dynamic_booking_disabled": "Alguns usuários do grupo desativaram as reservas de grupos dinâmicas", + "allow_dynamic_booking_tooltip": "Agrupe links de reserva que podem ser criados dinamicamente ao adicionar vários nomes de usuário com \"+\", por exemplo: \"cal.com/bailey+peer\"", + "allow_dynamic_booking": "Permita que os participantes façam reservas com as reservas de grupo dinâmicas", "email": "Email", "email_placeholder": "nome@exemplo.com", "full_name": "Nome Completo", @@ -287,11 +327,15 @@ "event_triggers": "Triggers de eventos", "subscriber_url": "URL do assinante", "create_new_webhook": "Criar um novo webhook", + "webhooks": "Webhooks", + "team_webhooks": "Webhooks da equipe", "create_new_webhook_to_account": "Criar um webhook para sua conta", "new_webhook": "Novo Webhook", "receive_cal_meeting_data": "Receba dados da reunião Cal em uma, URL especificada, em tempo real, quando um evento for agendado ou cancelado.", + "receive_cal_event_meeting_data": "Receba em tempo real dados da reunião do Cal em um URL especificada quando este evento for agendado ou cancelado.", "responsive_fullscreen_iframe": "Iframe responsivo de tela inteira", "loading": "Carregando...", + "deleting": "Apagando...", "standard_iframe": "Iframe padrão", "iframe_embed": "Incorporar iframe", "embed_calcom": "A maneira mais fácil de incorporar o Cal.com no seu site.", @@ -373,11 +417,13 @@ "share_additional_notes": "Por favor, compartilhe qualquer coisa que nos ajude a nos preparar para nossa reunião.", "booking_confirmation": "Confirme seu {{eventTypeTitle}} com {{profileName}}", "booking_reschedule_confirmation": "Reagende seu {{eventTypeTitle}} com {{profileName}}", - "in_person_meeting": "Link ou reunião presencial", + "in_person_meeting": "Reunião presencial", + "link_meeting": "Vincular reunião", "phone_call": "Ligação telefônica", "phone_number": "Número de Telefone", "enter_phone_number": "Digite o número de telefone", "reschedule": "Reagendar", + "reschedule_this": "Ou reagendar", "book_a_team_member": "Agende um membro da equipe no lugar", "or": "OU", "go_back": "Retornar", @@ -411,15 +457,19 @@ "danger_zone": "Zona Perigosa", "back": "Voltar", "cancel": "Cancelar", + "apply": "Aplicar", + "cancel_event": "Cancelar este evento", "continue": "Continuar", "confirm": "Confirmar", "disband_team": "Desfazer Time", - "disband_team_confirmation_message": "Tem certeza de que deseja desfazer este time? Qualquer pessoa com quem você compartilhou este link do time não será mais capaz de fazer novos agendamentos.", + "disband_team_confirmation_message": "Tem certeza de que deseja desfazer esta equipe? Qualquer pessoa com quem você compartilhou este link da equipe não será mais capaz de fazer novos agendamentos.", "remove_member_confirmation_message": "Tem certeza de que deseja remover este membro do time?", "confirm_disband_team": "Sim, disfazer time", "confirm_remove_member": "Sim, remover membro", "remove_member": "Remover membro", "manage_your_team": "Gerenciar seu time", + "no_teams": "Você ainda não tem nenhuma equipe.", + "no_teams_description": "Equipes permitem que outras pessoas reservem eventos compartilhados entre seus colegas de trabalho.", "submit": "Enviar", "delete": "Apagar", "update": "Atualizar", @@ -427,6 +477,8 @@ "pending": "Pendente", "open_options": "Abrir opções", "copy_link": "Copiar link do evento", + "share": "Compartilhar", + "share_event": "Você se importa de fazer uma reserva para mim no Cal ou enviar seu link?", "copy_link_team": "Copiar link do time", "leave_team": "Sair do time", "confirm_leave_team": "Sim, sair do time", @@ -434,6 +486,7 @@ "user_from_team": "{{user}} do {{team}}", "preview": "Pré-visualizar", "link_copied": "Link copiado!", + "link_shared": "Link compartilhado!", "title": "Título", "description": "Descrição", "quick_video_meeting": "Uma chamada rápida de video.", @@ -448,8 +501,11 @@ "url": "URL", "hidden": "Esconder", "readonly": "Somente leitura", + "plan_description": "Atualmente você está no plano {{plan}}.", + "plan_upgrade_invitation": "Atualize a sua conta para o plano Pro e desbloqueie todos os recursos que temos para oferecer.", "plan_upgrade": "Você precisa fazer um upgrade do seu plano para ter mais de um tipo de evento ativo.", "plan_upgrade_teams": "Você precisa fazer upgrade do seu plano para criar um time.", + "plan_upgrade_instructions": "Você pode <1>atualizar aqui.", "event_types_page_title": "Tipos de Eventos", "event_types_page_subtitle": "Crie eventos para que as pessoas possam fazer agendamentos em seu calendário.", "new_event_type_btn": "Novo tipo de evento", @@ -462,6 +518,8 @@ "event_type_created_successfully": "{{eventTypeTitle}} evento criado com sucesso", "event_type_updated_successfully": "Tipo de evento atualizado com sucesso", "event_type_deleted_successfully": "Tipo de evento removido com sucesso", + "web3_metamask_added": "Metamask adicionado com êxito", + "web3_metamask_disconnected": "Metamask desconectado com êxito", "hours": "Horas", "your_email": "Seu Email", "change_avatar": "Alterar Avatar", @@ -470,6 +528,8 @@ "first_day_of_week": "Primeiro Dia da Semana", "single_theme": "Tema", "brand_color": "Cor da Marca", + "light_brand_color": "Cor da marca (tema claro)", + "dark_brand_color": "Cor da marca (tema escuro)", "file_not_named": "Arquivo não é [idOrSlug]/[user]", "create_team": "Criar Time", "name": "Nome", @@ -480,6 +540,10 @@ "create_first_team_and_invite_others": "Crie o seu primeiro time e convide outros usuários para trabalhar em conjunto.", "create_team_to_get_started": "Criar um time para começar", "teams": "Times", + "team_billing": "Cobrança da equipe", + "upgrade_to_flexible_pro_title": "Alteramos a cobrança para equipes", + "upgrade_to_flexible_pro_message": "Há membros da sua equipe que não têm assento. Atualize para o plano Pro para incluir os assentos faltando.", + "changed_team_billing_info": "A partir de janeiro de 2022, a cobrança dos membros de equipe será realizada por assento. Os membros da sua equipe que tinham o plano PRO gratuito receberam um teste de 14 dias. Ao fim do teste, eles serão ocultos da sua equipe, a não ser que você atualize agora.", "create_manage_teams_collaborative": "Criar e gerenciar times para usar recursos de colaboração.", "only_available_on_pro_plan": "Este recurso só está disponível no plano Pro", "remove_cal_branding_description": "Para remover a marca Cal das suas páginas de reservas, você precisar fazer o upgrade para uma conta Pro.", @@ -506,6 +570,7 @@ "add_attendees": "Adicionar Participantes", "show_advanced_settings": "Mostrar Configurações Avançadas", "event_name": "Nome do Evento", + "event_name_tooltip": "O nome que aparecerá nos calendários", "meeting_with_user": "Reunião com {USER}", "additional_inputs": "Campos adicionais", "label": "Etiqueta", @@ -513,6 +578,8 @@ "type": "Tipo", "edit": "Editar", "add_input": "Adicionar um Campo", + "disable_notes": "Ocultar notas no calendário", + "disable_notes_description": "Por motivo de privacidade, notas e dados adicionais serão ocultos na entrada do calendário. Serão enviadas por e-mail.", "opt_in_booking": "Confirmação da reserva", "opt_in_booking_description": "A reserva precisa ser confirmada manualmente antes de ser enviada para as integrações e ser enviado um email de confirmação.", "disable_guests": "Desativar participantes", @@ -522,13 +589,15 @@ "calendar_days": "dias da agenda", "business_days": "dias úteis", "set_address_place": "Defina um endereço ou local", + "set_link_meeting": "Defina um link para a reunião", "cal_invitee_phone_number_scheduling": "Cal solicitará ao seu convidado que insira um número de telefone antes de agendar.", "cal_provide_google_meet_location": "Cal fornecerá um link para o Google Meet.", "cal_provide_zoom_meeting_url": "Cal fornecerá uma URL de reunião do Zoom.", - "cal_provide_tandem_meeting_url": "Cal fornecerá uma URL de reunião do Tandem.", + "cal_provide_tandem_meeting_url": "Cal fornecerá um URL de reunião do Tandem.", "cal_provide_video_meeting_url": "O Cal irá fornecer um URL de reunião do Daily video.", - "cal_provide_jitsi_meeting_url": "O Cal irá fornecer um URL de reunião do Jitsi Meet.", - "cal_provide_huddle01_meeting_url": "O Cal irá fornecer um URL de reunião do Huddle01 Web3 video.", + "cal_provide_jitsi_meeting_url": "O Cal fornecerá um URL de reunião do Jitsi Meet.", + "cal_provide_huddle01_meeting_url": "O Cal fornecerá um URL de reunião do Huddle01 Web3 video.", + "cal_provide_teams_meeting_url": "O Cal enviará um URL da reunião para o MS Teams. OBSERVAÇÃO: É PRECISO TER UMA CONTA ESCOLAR OU CORPORATIVA", "require_payment": "Requerer Pagamento", "commission_per_transaction": "comissão por transação", "event_type_updated_successfully_description": "O seu tipo de evento foi atualizado com sucesso.", @@ -554,6 +623,9 @@ "confirm_delete_account": "Sim, apagar conta", "delete_account_confirmation_message": "Você tem certeza de que deseja apagar sua conta Cal.com? Qualquer pessoa que você compartilhou seu link de reservas não poderá mais utilizá-lo para agendar novos eventos e todas as suas preferências serão perdidas.", "integrations": "Integrações", + "apps": "Apps", + "app_store": "App Store", + "app_store_description": "Conectando pessoas, tecnologia e o ambiente de trabalho.", "settings": "Configurações", "event_type_moved_successfully": "Tipo de evento atualizado com sucesso", "next_step": "Ignorar passo", @@ -577,7 +649,9 @@ "set_as_away": "Definir-se como indisponível", "set_as_free": "Desativar status indisponível", "user_away": "Este usuário está indisponível no momento.", - "user_away_description": "A pessoa que você está tentando realizar uma reserva está indisponível no momento e portanto não está aceitando novas reservas.", + "user_away_description": "A pessoa que você está tentando realizar uma reserva está indisponível no momento e, portanto, não está aceitando novas reservas.", + "meet_people_with_the_same_tokens": "Conheça pessoas com os mesmos tokens", + "only_book_people_and_allow": "Faça ou permita reservas apenas de pessoas que compartilham os mesmos tokens, DAOs ou NFTs.", "saml_config_deleted_successfully": "Configuração do SAML removida com sucesso", "account_created_with_identity_provider": "Sua conta foi criada através de um provedor de identidade.", "account_managed_by_identity_provider": "Sua conta é gerenciada por {{provider}}", @@ -594,10 +668,105 @@ "saml_configuration_update_failed": "A atualização da configuração SAML falhou", "saml_configuration_delete_failed": "A remoção das configurações do SAML falhou", "saml_email_required": "Por favor insira um endereço de e-mail para que possamos encontrar o seu Provedor de Identidade", - "you_will_need_to_generate": "Você precisa gerar um token de acesso na página da integrações.", + "you_will_need_to_generate": "Você precisa gerar um token de acesso na ferramenta de agendamento antiga.", "import": "Importar", "import_from": "Importar de", "access_token": "Token de acesso", "visit_roadmap": "Roadmap", - "availability_updated_successfully": "Disponibilidade atualizada com sucesso" + "popular_categories": "Categorias populares", + "trending_apps": "Apps em alta", + "all_apps": "Todos os apps", + "installed_apps": "Apps instalados", + "manage_your_connected_apps": "Gerencie os apps instalados ou altere as configurações", + "browse_apps": "Procurar apps", + "features": "Recursos", + "permissions": "Permissões", + "terms_and_privacy": "Termos e privacidade", + "published_by": "Publicado por {{author}}", + "subscribe": "Assinar", + "buy": "Comprar", + "install_app": "Instalar app", + "categories": "Categorias", + "pricing": "Preço", + "learn_more": "Saiba mais", + "privacy_policy": "Política de privacidade", + "terms_of_service": "Termos de serviço", + "remove": "Remover", + "add": "Adicionar", + "verify_wallet": "Verificar carteira", + "connect_metamask": "Conecte o Metamask", + "create_events_on": "Criar eventos em:", + "missing_license": "Falta a licença", + "signup_requires": "Licença comercial obrigatória", + "signup_requires_description": "Atualmente a Cal.com, Inc. não oferece nenhuma versão gratuita de código aberto da página de inscrição. Para obter acesso completo aos componentes da inscrição, você precisa adquirir uma licença comercial. Para uso pessoal, recomendamos o Prisma Data Platform ou outras interfaces em Postgres para criação de contas.", + "next_steps": "Próximos passos", + "acquire_commercial_license": "Adquira uma licença comercial", + "the_infrastructure_plan": "O plano de infraestrutura é baseado no uso e tem descontos acessíveis para novos usuários.", + "prisma_studio_tip": "Crie uma conta com o Prisma Studio", + "prisma_studio_tip_description": "Aprenda a configurar seu primeiro usuário", + "contact_sales": "Fale com a equipe de vendas", + "error_404": "Erro 404", + "default": "Padrão", + "set_to_default": "Definir para padrão", + "new_schedule_btn": "Nova agenda", + "add_new_schedule": "Adicionar nova agenda", + "delete_schedule": "Excluir agenda", + "schedule_created_successfully": "Agenda {{scheduleName}} criada com êxito", + "availability_updated_successfully": "Agenda {{scheduleName}} criada com êxito", + "schedule_deleted_successfully": "Agenda removida com êxito", + "default_schedule_name": "Horas de trabalho", + "new_schedule_heading": "Criar agenda de disponibilidade", + "new_schedule_description": "A criação de agendas de disponibilidade permite gerenciar a disponibilidade em tipos de evento. Podem ser aplicadas a um ou mais tipos de evento.", + "requires_ownership_of_a_token": "Requer que o usuário que agendar tenha um token que pertence ao endereço a seguir:", + "example_name": "João da Silva", + "time_format": "Formato de hora", + "12_hour": "12 horas", + "24_hour": "24 horas", + "redirect_success_booking": "Redirecionar após a reserva ", + "you_are_being_redirected": "Você está sendo redirecionado para {{ url }} em $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Para usar este recurso, atualize para uma conta Pro.", + "duplicate": "Duplicar", + "you_can_manage_your_schedules": "Você pode gerenciar suas agendas na página de Disponibilidade.", + "api_keys": "Chaves de API", + "api_key_modal_subtitle": "Chaves de API permitem criar chamadas de API para a sua própria conta.", + "api_keys_subtitle": "Gere chaves de API e use-as para acessar a sua própria conta.", + "generate_new_api_key": "Gerar nova chave de API", + "create_api_key": "Criar uma chave de API", + "personal_note": "Nomear essa chave", + "personal_note_placeholder": "Exemplo: Desenvolvimento", + "api_key_no_note": "Chave de API sem nome", + "api_key_never_expires": "Esta chave de API não data de vencimento", + "edit_api_key": "Editar chave de API", + "never_expire_key": "Nunca expira", + "delete_api_key": "Revogar chave de API", + "success_api_key_created": "Chave de API criada com êxito", + "success_api_key_edited": "Chave de API atualizada com êxito", + "create": "Criar", + "success_api_key_created_bold_tagline": "Salve esta chave de API em um local seguro.", + "you_will_only_view_it_once": "Não será possível visualizá-la novamente ao fechar esta janela.", + "copy_to_clipboard": "Copiar para a área de transferência", + "confirm_delete_api_key": "Revogar esta chave de API", + "revoke_api_key": "Revogar chave de API", + "api_key_copied": "Chave de API copiada!", + "delete_api_key_confirm_title": "Remover permanentemente esta chave de API da sua conta?", + "copy": "Copiar", + "expire_date": "Data de vencimento", + "expired": "Expirada", + "never_expires": "Nunca expira", + "expires": "Expira", + "request_reschedule_booking": "Solicitar o reagendamento da sua reserva", + "reason_for_reschedule": "Motivo do reagendamento", + "book_a_new_time": "Agendar um novo horário", + "reschedule_request_sent": "Solicitação de reagendamento enviada", + "reschedule_modal_description": "Isso cancelará a reunião agendada, notifique quem agendou e peça que escolha outro horário.", + "reason_for_reschedule_request": "Motivo da solicitação de reagendamento", + "send_reschedule_request": "Solicitar reagendamento ", + "edit_booking": "Editar reserva", + "reschedule_booking": "Reagendar reserva", + "former_time": "Horário anterior", + "impersonate": "Representar", + "impersonate_user_tip": "Todos os usos desse recurso são auditados.", + "impersonating_user_warning": "Representando o nome de usuário \"{{user}}\".", + "impersonating_stop_instructions": "<0>Clique aqui para parar." } diff --git a/apps/web/public/static/locales/pt-BR/vital.json b/apps/web/public/static/locales/pt-BR/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/pt-BR/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/pt/common.json b/apps/web/public/static/locales/pt/common.json index 076789f8cf..7aefe44013 100644 --- a/apps/web/public/static/locales/pt/common.json +++ b/apps/web/public/static/locales/pt/common.json @@ -12,6 +12,7 @@ "event_declined_subject": "Declinado: {{eventType}} com {{name}} em {{date}}", "event_cancelled_subject": "Cancelado: {{eventType}} com {{name}} em {{date}}", "event_request_declined": "O seu pedido de evento foi declinado", + "event_request_declined_recurring": "O seu pedido de evento recorrente foi recusado", "event_request_cancelled": "O seu evento agendado foi cancelado", "organizer": "Organizador", "need_to_reschedule_or_cancel": "Precisa reagendar ou cancelar?", @@ -23,6 +24,7 @@ "rejection_confirmation": "Rejeitar a reserva", "manage_this_event": "Gerir este evento", "your_event_has_been_scheduled": "O seu evento foi agendado", + "your_event_has_been_scheduled_recurring": "O seu evento recorrente foi agendado", "accept_our_license": "Aceite a nossa licença, através de alterar a variável de .env <1>NEXT_PUBLIC_LICENSE_CONSENT para '{{agree}}'.", "remove_banner_instructions": "Para remover este banner, por favor abra seu ficheiro .env e altere a variável <1>NEXT_PUBLIC_LICENSE_CONSENT para '{{agree}}'.", "error_message": "A mensagem de erro: '{{errorMessage}}'", @@ -57,6 +59,7 @@ "confirm_or_reject_request": "Confirmar ou rejeitar o pedido", "check_bookings_page_to_confirm_or_reject": "Consulte a sua página de reservas para confirmar ou rejeitar a reserva.", "event_awaiting_approval": "Um novo evento aguarda a sua aprovação", + "event_awaiting_approval_recurring": "Um evento recorrente aguarda a sua aprovação", "someone_requested_an_event": "Alguém pediu para agendar um evento no seu calendário.", "someone_requested_password_reset": "Alguém pediu uma ligação para alterar a sua senha.", "password_reset_instructions": "Se não fez este pedido, pode ignorar este email com segurança e a sua senha não será alterada.", @@ -66,6 +69,11 @@ "your_meeting_has_been_booked": "A sua reunião foi reservada", "event_type_has_been_rescheduled_on_time_date": "O evento {{eventType}} com {{name}} foi reagendado para as {{time}} ({{timeZone}}) de {{date}}.", "event_has_been_rescheduled": "O seu evento foi reagendado.", + "request_reschedule_title_attendee": "Solicitar o reagendamento da sua reserva", + "request_reschedule_subtitle": "{{organizer}} cancelou a reserva e pediu para que escolhesse outro horário.", + "request_reschedule_title_organizer": "Solicitou um reagendamento a {{attendee}}", + "request_reschedule_subtitle_organizer": "Cancelou a reserva e {{attendee}} deve escolher um novo horário para a reserva consigo.", + "reschedule_reason": "Motivo do reagendamento", "hi_user_name": "Olá {{userName}}", "ics_event_title": "{{eventType}} com {{name}}", "new_event_subject": "Novo evento: {{attendeeName}} - {{date}} - {{eventType}}", @@ -74,6 +82,7 @@ "manage_my_bookings": "Gerir as minhas reservas", "need_to_make_a_change": "Precisa de alterar?", "new_event_scheduled": "Foi agendado um novo evento.", + "new_event_scheduled_recurring": "Foi agendado um novo evento recorrente.", "invitee_email": "Email do convidado", "invitee_timezone": "Fuso horário do convidado", "event_type": "Tipo do evento", @@ -84,6 +93,7 @@ "meeting_url": "URL da reunião", "meeting_request_rejected": "O seu pedido de reunião foi rejeitado", "rescheduled_event_type_subject": "Reagendado: {{eventType}} com {{name}} em {{date}}", + "requested_to_reschedule_subject_attendee": "Acção necessária para reagendamento: Por favor reserve uma nova hora para {{eventType}} com {{name}}", "rejected_event_type_with_organizer": "Rejeitado: {{eventType}} com {{organizer}} em {{date}}", "hi": "Olá", "join_team": "Junte-se à equipa", @@ -122,6 +132,7 @@ "ping_test": "Teste de ping", "add_to_homescreen": "Adicione esta aplicação ao seu ecrã principal para acesso mais rápido e uma melhor experiência.", "upcoming": "Próximas", + "recurring": "Recorrente", "past": "Anteriores", "choose_a_file": "Escolha um ficheiro...", "upload_image": "Carregar imagem", @@ -226,13 +237,20 @@ "add_to_calendar": "Adicionar ao calendário", "other": "Outras", "emailed_you_and_attendees": "Enviámos um email para si e para os outros participantes com um convite de calendário com todos os detalhes.", + "emailed_you_and_attendees_recurring": "Enviámos um email para si e para os outros participantes com um convite de calendário para o primeiro destes eventos recorrentes.", "emailed_you_and_any_other_attendees": "Esta informação foi enviada para si e para todos os outros participantes.", "needs_to_be_confirmed_or_rejected": "A sua reserva ainda precisa de ser confirmada ou rejeitada.", + "needs_to_be_confirmed_or_rejected_recurring": "A sua reunião recorrente ainda precisa de ser confirmada ou rejeitada.", "user_needs_to_confirm_or_reject_booking": "{{user}} ainda tem de confirmar ou rejeitar a reserva.", + "user_needs_to_confirm_or_reject_booking_recurring": "{{user}} ainda precisa de confirmar ou rejeitar cada reserva da reunião recorrente.", "meeting_is_scheduled": "Esta reunião está agendada", + "meeting_is_scheduled_recurring": "Os eventos recorrentes estão agendados", "submitted": "A sua reserva foi enviada", + "submitted_recurring": "A sua reunião recorrente foi submetida", "booking_submitted": "A sua reserva foi enviada", + "booking_submitted_recurring": "A sua reunião recorrente foi submetida", "booking_confirmed": "A sua reserva foi confirmada", + "booking_confirmed_recurring": "A sua reunião recorrente foi confirmada", "enter_new_password": "Insira a nova senha que quer utilizar na sua conta.", "reset_password": "Redefinir senha", "change_your_password": "Altere a sua senha", @@ -276,6 +294,7 @@ "bookings": "Reservas", "bookings_description": "Veja os eventos futuros e passados reservados através das ligações de tipos de eventos.", "upcoming_bookings": "Assim que alguém reservar uma hora, ela aparecerá aqui.", + "recurring_bookings": "Assim que alguém reservar uma reunião recorrente, será mostrada aqui.", "past_bookings": "As suas reservas anteriores aparecerão aqui.", "cancelled_bookings": "As suas reservas canceladas aparecerão aqui.", "on": "em", @@ -329,6 +348,7 @@ "receive_cal_event_meeting_data": "Receba dados da reunião Cal num URL especificado, em tempo real, assim que o evento for agendado ou cancelado.", "responsive_fullscreen_iframe": "Iframe responsivo de ecrã inteiro", "loading": "A carregar...", + "deleting": "A eliminar...", "standard_iframe": "Iframe padrão", "iframe_embed": "Incorporar iframe", "embed_calcom": "A maneira mais fácil de incorporar o Cal.com no seu site.", @@ -414,6 +434,8 @@ "link_meeting": "Ligar reunião", "phone_call": "Chamada Telefónica", "phone_number": "Número de Telefone", + "attendee_phone_number": "Número de telefone do participante", + "host_phone_number": "O seu número de telefone", "enter_phone_number": "Inserir Número do Telefone", "reschedule": "Reagendar", "reschedule_this": "Reagendar em alternativa", @@ -426,6 +448,7 @@ "edit_role": "Editar função", "edit_team": "Editar Equipa", "reject": "Rejeitar", + "reject_all": "Rejeitar tudo", "accept": "Aceitar", "leave": "Sair", "profile": "Perfil", @@ -454,6 +477,7 @@ "cancel_event": "Cancelar este evento", "continue": "Continuar", "confirm": "Confirmar", + "confirm_all": "Confirmar tudo", "disband_team": "Dissolver Equipa", "disband_team_confirmation_message": "De certeza que quer desmantelar esta equipa? Qualquer pessoa com quem tenha partilhado esta ligação de equipa deixará de poder fazer reservas através da respectiva ligação.", "remove_member_confirmation_message": "Tem a certeza de que deseja remover este membro da equipa?", @@ -479,6 +503,7 @@ "user_from_team": "{{user}} de {{team}}", "preview": "Pré-visualizar", "link_copied": "Ligação copiada!", + "private_link_copied": "A ligação privada foi copiada!", "link_shared": "Ligação partilhada!", "title": "Título", "description": "Descrição", @@ -494,6 +519,7 @@ "url": "URL", "hidden": "Oculto", "readonly": "Somente Leitura", + "one_time_link": "Ligação de utilização única", "plan_description": "O seu plano é o {{plan}}.", "plan_upgrade_invitation": "Actualize a sua conta para o plano Pro para desbloquear todas as funcionalidades que temos para oferecer.", "plan_upgrade": "Tem de atualizar o seu plano para ter mais de um tipo de evento ativo.", @@ -519,6 +545,18 @@ "language": "Idioma", "timezone": "Fuso Horário", "first_day_of_week": "Primeiro Dia da Semana", + "repeats_up_to": "Repete-se até {{count}} vez", + "repeats_up_to_plural": "Repete-se até {{count}} vezes", + "every_for_freq": "A cada {{freq}} por", + "repeats_every": "Repete-se a cada", + "weekly": "semana", + "weekly_plural": "semanas", + "monthly": "mês", + "monthly_plural": "meses", + "yearly": "ano", + "yearly_plural": "anos", + "plus_more": "+ {{count}} mais", + "max": "Máximo", "single_theme": "Tema", "brand_color": "Cor da marca", "light_brand_color": "Cor da marca (tema claro)", @@ -575,19 +613,26 @@ "disable_notes_description": "Por razões de privacidade, informações adicionais e notas serão escondidas no registo do calendário. Serão enviadas para o seu email.", "opt_in_booking": "Confirmação da reserva", "opt_in_booking_description": "A reserva precisa ser confirmada manualmente antes de ser enviada para as integrações e ser enviada uma mensagem de confirmação.", + "recurring_event": "Evento recorrente", + "recurring_event_description": "As pessoas podem subscrever para eventos recorrentes", + "starting": "A iniciar", "disable_guests": "Desativar Convidados", "disable_guests_description": "Desativar a adição de convidados adicionais durante a reserva.", + "private_link": "Gerar URL privado", + "copy_private_link": "Copiar ligação privada", + "private_link_description": "Crie um URL privado para partilhar sem expor o seu nome de utilizador do Cal", "invitees_can_schedule": "Os convidados podem agendar", "date_range": "Intervalo de Datas", "calendar_days": "dias da agenda", "business_days": "dias úteis", "set_address_place": "Defina um endereço ou local", + "set_your_phone_number": "Defina um número de telefone para a reunião", "set_link_meeting": "Definir uma ligação para a reunião", "cal_invitee_phone_number_scheduling": "Cal solicitará ao seu convidado que insira um número de telefone antes de agendar.", "cal_provide_google_meet_location": "O Cal irá fornecer uma localização do Google Meet.", "cal_provide_zoom_meeting_url": "O Cal irá fornecer um URL de reunião do Zoom.", "cal_provide_tandem_meeting_url": "O Cal irá fornecer um URL de reunião do Tandem.", - "cal_provide_video_meeting_url": "O Cal irá fornecer um URL de reunião do Daily video.", + "cal_provide_video_meeting_url": "O Cal irá fornecer um URL de reunião da videoconferência.", "cal_provide_jitsi_meeting_url": "Será gerado um URL do Jitsi Meet para si.", "cal_provide_huddle01_meeting_url": "O Cal irá fornecer um URL de reunião vídeo do Huddle01 Web3.", "cal_provide_teams_meeting_url": "O Cal fornecerá um URL de reunião para MS Teams. ATENÇÃO: TEM DE TER UMA CONTA DE TRABALHO OU ESCOLAR", @@ -720,5 +765,65 @@ "external_redirect_url": "https://exemplo.com/redireccionar-para-pagina-de-sucesso", "redirect_url_upgrade_description": "Para usar esta funcionalidade, tem de actualizar para uma conta Pro.", "duplicate": "Duplicar", - "you_can_manage_your_schedules": "Pode gerir os seus horários na página de Disponibilidade." + "you_can_manage_your_schedules": "Pode gerir os seus horários na página de Disponibilidade.", + "api_keys": "Chaves de API", + "api_key_modal_subtitle": "As chaves de API permitem que invoque métodos da API para a sua própria conta.", + "api_keys_subtitle": "Gerar chaves de API a utilizar para aceder à sua própria conta.", + "generate_new_api_key": "Gerar nova chave de API", + "create_api_key": "Criar uma chave de API", + "personal_note": "Atribuir um nome a esta chave", + "personal_note_placeholder": "Por exemplo, Desenvolvimento", + "api_key_no_note": "Chave de API sem nome", + "api_key_never_expires": "Esta chave de API não tem data de validade", + "edit_api_key": "Editar chave de API", + "never_expire_key": "Nunca expira", + "delete_api_key": "Revogar chave de API", + "success_api_key_created": "Chave de API criada com sucesso", + "success_api_key_edited": "Chave de API atualizada com sucesso", + "create": "Criar", + "success_api_key_created_bold_tagline": "Guarde esta chave de API num lugar seguro.", + "you_will_only_view_it_once": "Não será possível ver a mesma novamente depois de fechar esta modal.", + "copy_to_clipboard": "Copiar para área de transferência", + "enabled_after_update": "Activado após actualização", + "enabled_after_update_description": "A ligação privada funcionará depois de guardar", + "confirm_delete_api_key": "Revogar esta chave de API", + "revoke_api_key": "Revogar chave de API", + "api_key_copied": "Chave de API copiada!", + "delete_api_key_confirm_title": "Remover de forma permanente esta chave de API da sua conta?", + "copy": "Copiar", + "expire_date": "Data de expiração", + "expired": "Expirou", + "never_expires": "Nunca expira", + "expires": "Expira", + "request_reschedule_booking": "Solicitar o reagendamento da sua reserva", + "reason_for_reschedule": "Motivo do reagendamento", + "book_a_new_time": "Reservar uma nova hora", + "reschedule_request_sent": "Pedido de reagendamento enviado", + "reschedule_modal_description": "Isto irá cancelar a reunião agendada, notificar o organizador e pedir para escolher uma nova hora.", + "reason_for_reschedule_request": "Motivo para o pedido de reagendamento", + "send_reschedule_request": "Pedir reagendamento ", + "edit_booking": "Editar reserva", + "reschedule_booking": "Reagendar reserva", + "former_time": "Horário anterior", + "confirmation_page_gif": "Gif para a página de confirmação", + "search": "Pesquisar", + "impersonate": "Representar", + "impersonate_user_tip": "Todas as utilizações desta funcionalidade são auditadas.", + "impersonating_user_warning": "Representar o nome de utilizador \"{{user}}\".", + "impersonating_stop_instructions": "<0>Clique aqui para parar.", + "email_validation_error": "Isto não parece ser um endereço de email", + "place_where_cal_widget_appear": "Insira este código no seu HTML onde pretende mostrar o seu widget do Cal.", + "copy_code": "Copiar código", + "code_copied": "Código copiado!", + "how_you_want_add_cal_site": "Como deseja adicionar o Cal ao seu site?", + "choose_ways_put_cal_site": "Escolha uma das seguintes formas de colocar o Cal no seu site.", + "setting_up_zapier": "A configurar a sua integração com Zapier", + "generate_api_key": "Gerar chave de API", + "your_unique_api_key": "A sua chave de API única", + "copy_safe_api_key": "Copie esta chave da API e guarde-a num lugar seguro. Se perder esta chave, terá de gerar uma nova.", + "zapier_setup_instructions": "<0>Vá a:<1>Ligação de convite do Zapier<1>Inicie sessão na sua conta Zapier e crie um novo Zap.<2>Seleccione o Cal.com como aplicação accionadora. Seleccione também um evento accionador.<3>Seleccione a sua conta e insira a sua chave de API única.<4>Teste o seu accionador.<5>Está configurado!", + "install_zapier_app": "Por favor, primeiro instale a aplicação Zapier da App Store.", + "go_to_app_store": "Vá para a App Store", + "calendar_error": "Algo correu mal, tente ligar de novo o seu calendário com todas as permissões necessárias", + "calendar_no_busy_slots": "Não há horários ocupados" } diff --git a/apps/web/public/static/locales/pt/vital.json b/apps/web/public/static/locales/pt/vital.json new file mode 100644 index 0000000000..dfcc798f5b --- /dev/null +++ b/apps/web/public/static/locales/pt/vital.json @@ -0,0 +1,13 @@ +{ + "connected_vital_app": "Ligado a", + "vital_app_sleep_automation": "Reagendar automaticamente durante o sono", + "vital_app_automation_description": "Pode seleccionar diferentes parâmetros para accionar o reagendamento com base nas suas métricas de sono.", + "vital_app_parameter": "Parâmetro", + "vital_app_trigger": "Executar abaixo ou igual a", + "vital_app_save_button": "Guardar configuração", + "vital_app_total_label": "Total (total = rem + sono leve + sono profundo)", + "vital_app_duration_label": "Duração (duração = fim do sono - início do sono)", + "vital_app_hours": "horas", + "vital_app_save_success": "As suas configurações vitais foram guardadas com sucesso", + "vital_app_save_error": "Ocorreu um erro ao guardar as suas configurações vitais" +} diff --git a/apps/web/public/static/locales/ro/common.json b/apps/web/public/static/locales/ro/common.json index 24dbade37c..bc4fe42251 100644 --- a/apps/web/public/static/locales/ro/common.json +++ b/apps/web/public/static/locales/ro/common.json @@ -1,4 +1,28 @@ { + "trial_days_left": "Aveți $t(zi, {\"count\": {{days}} }) rămase din perioada de probă PRO", + "day": "{{count}} zi", + "day_plural": "{{count}} (de) zile", + "second": "{{count}} secundă", + "second_plural": "{{count}} (de) secunde", + "upgrade_now": "Faceți upgrade acum", + "accept_invitation": "Acceptați invitația", + "calcom_explained": "Cal.com este alternativa open-source la Calendly, care vă oferă control asupra propriilor date, asupra fluxului de lucru și asupra aspectului site-ului.", + "have_any_questions": "Aveți întrebări? Suntem aici pentru a vă ajuta.", + "reset_password_subject": "Cal.com: instrucțiuni de resetare a parolei", + "event_declined_subject": "Refuzat: {{eventType}} cu {{name}} în {{date}}", + "event_cancelled_subject": "Anulat: {{eventType}} cu {{name}} în {{date}}", + "event_request_declined": "Solicitarea pentru eveniment a fost refuzată", + "event_request_cancelled": "Evenimentul programat a fost anulat", + "organizer": "Organizator", + "need_to_reschedule_or_cancel": "Trebuie să reprogramați sau să anulați?", + "cancellation_reason": "Motivul anulării", + "cancellation_reason_placeholder": "De ce anulați? (opțional)", + "rejection_reason": "Motivul respingerii", + "rejection_reason_title": "Respingeți solicitarea de rezervare?", + "rejection_reason_description": "Sigur doriți să respingeți rezervarea? Vom anunța persoana care a încercat să rezerve. Puteți specifica un motiv mai jos.", + "rejection_confirmation": "Respingeți rezervarea", + "manage_this_event": "Gestionați acest eveniment", + "your_event_has_been_scheduled": "Evenimentul a fost programat", "accept_our_license": "Acceptați licența noastră schimbând variabila .env <1>URMĂTOAREA_LICENȚĂ_PUBLICĂ în.", "remove_banner_instructions": "Pentru a elimina acest banner, deschideți fișierul .env și modificați variabila <1>URMĂTOAREA_LICENȚĂ_PUBLICĂ în „{{agree}}”.", "error_message": "Mesajul de eroare a fost: '{{errorMessage}}'", @@ -6,6 +30,7 @@ "refund_failed": "Rambursarea pentru eveniment {{eventType}} cu {{userName}} în {{date}} a eșuat.", "check_with_provider_and_user": "Vă rugăm să verificați cu furnizorul dvs. de plăți și cu {{userName}} cum să gestionați această activitate.", "a_refund_failed": "Rambursare eșuată", + "awaiting_payment_subject": "Se așteaptă plata: {{eventType}} cu {{name}} în {{date}}", "meeting_awaiting_payment": "Întâlnirea dvs. așteaptă plata", "help": "Ajutor", "price": "Prețul", @@ -29,12 +54,25 @@ "integration_meeting_id": "{{integrationName}} ședință ID: {{meetingId}}", "confirmed_event_type_subject": "Confirmat: {{eventType}} cu {{name}} pe {{date}}", "new_event_request": "Solicitare de eveniment nou: {{attendeeName}} - {{date}} - {{eventType}}", + "confirm_or_reject_request": "Confirmați sau respingeți solicitarea", "check_bookings_page_to_confirm_or_reject": "Verificați pagina de rezervări pentru a confirma sau a respinge rezervarea.", - "event_awaiting_approval": "Un nou eveniment așteaptă aprobarea dvs", + "event_awaiting_approval": "Un eveniment așteaptă aprobarea dvs.", + "someone_requested_an_event": "Cineva a solicitat să programeze un eveniment în calendar.", + "someone_requested_password_reset": "Cineva a solicitat un link pentru a vă schimba parola.", + "password_reset_instructions": "Dacă nu ați solicitat acest lucru, puteți ignora acest e-mail, iar parola nu va fi schimbată.", + "event_awaiting_approval_subject": "Se așteaptă aprobarea: {{eventType}} cu {{name}} în {{date}}", + "event_still_awaiting_approval": "Un eveniment încă așteaptă aprobarea dvs.", + "booking_submitted_subject": "Rezervare transmisă: {{eventType}} cu {{name}} în {{date}}", "your_meeting_has_been_booked": "Întâlnirea dvs. a fost programată", "event_type_has_been_rescheduled_on_time_date": "{{eventType}} cu {{name}} a fost reprogramat la {{time}} ({{timeZone}}) pe {{date}}.", "event_has_been_rescheduled": "Evenimentul dvs. a fost reprogramat.", + "request_reschedule_title_attendee": "Solicitați reprogramarea rezervării", + "request_reschedule_subtitle": "{{organizer}} a anulat rezervarea și v-a cerut să alegeți o altă dată.", + "request_reschedule_title_organizer": "I-ați cerut lui {{attendee}} să reprogrameze", + "request_reschedule_subtitle_organizer": "Ați anulat rezervarea și {{attendee}} trebuie să aleagă o nouă dată pentru rezervarea cu dvs.", + "reschedule_reason": "Motivul reprogramării", "hi_user_name": "Salut {{userName}}", + "ics_event_title": "{{eventType}} cu {{name}}", "new_event_subject": "Eveniment nou: {{attendeeName}} - {{date}} - {{eventType}}", "join_by_entrypoint": "Alăturați-vă de {{entryPoint}}", "notes": "Notiţe", @@ -50,13 +88,23 @@ "meeting_password": "Parolă ședință", "meeting_url": "URL ședință", "meeting_request_rejected": "Solicitarea dvs. în şedinţă a fost respinsă", + "rescheduled_event_type_subject": "Reprogramat: {{eventType}} cu {{name}} în {{date}}", + "requested_to_reschedule_subject_attendee": "Acțiune necesară de reprogramare: rezervați o nouă dată pentru {{eventType}} cu {{name}}", "rejected_event_type_with_organizer": "Respins: {{eventType}} cu {{organizer}} pe {{date}}", "hi": "Salut", "join_team": "Alătură-te echipei", + "manage_this_team": "Gestionați această echipă", + "team_info": "Informații echipă", "request_another_invitation_email": "Dacă preferați să nu utilizați {{toEmail}} ca e-mail Cal.com sau aveți deja un cont Cal.com, vă rugăm să solicitați o altă invitație la acel e-mail.", "you_have_been_invited": "Ați fost invitat să vă alăturați echipei {{teamName}}", "user_invited_you": "{{user}} v-a invitat să vă alăturați echipei {{teamName}}", + "hidden_team_member_title": "Sunteți ascuns în această echipă", + "hidden_team_member_message": "Licența dvs. nu este plătită. Fie faceți upgrade la Pro, fie anunțați proprietarul echipei că vă poate plăti licența.", + "hidden_team_owner_message": "Aveți nevoie de un cont Pro pentru a utiliza echipe. Sunteți ascuns până când faceți upgrade.", "link_expires": "p.s. Expiră în {{expiresIn}} ore.", + "upgrade_to_per_seat": "Faceți upgrade la nivelul Per licență", + "team_upgrade_seats_details": "Printre cei {{memberCount}} (de) membri din echipa dvs., {{unpaidCount}} (de) locuri nu sunt plătite. La {{seatPrice}} USD/lună/licență, costul total estimat al abonamentului dvs. este de {{totalCost}} USD/lună.", + "team_upgraded_successfully": "Echipa dvs. a fost actualizată cu succes!", "use_link_to_reset_password": "Utilizați linkul de mai jos pentru a vă reseta parola", "hey_there": "Bună,", "forgot_your_password_calcom": "Ți-ai uitat parola? - Cal.com", @@ -74,6 +122,7 @@ "webhook_created_successfully": "Webhook creat cu succes!", "webhook_updated_successfully": "Webhook actualizat cu succes!", "webhook_removed_successfully": "Webhook eliminat cu succes!", + "payload_template": "Șablon payload", "dismiss": "Renunță", "no_data_yet": "Încă nu există date", "ping_test": "Testul de ping", @@ -107,6 +156,7 @@ "rejected": "Respins", "unconfirmed": "Neconfirmat", "guests": "Vizitatori", + "guest": "Oaspete", "web_conferencing_details_to_follow": "Urmează detalii despre conferința web.", "the_username": "Numele de utilizator", "username": "Nume de utilizator", @@ -131,6 +181,7 @@ "30min_meeting": "Ședință de 30min", "secret_meeting": "Întâlnire secretă", "login_instead": "Autentifică-te în schimb", + "already_have_an_account": "Aveți deja un cont?", "create_account": "Creează un cont", "confirm_password": "Confirmă parola", "create_your_account": "Creează-ți contul", @@ -201,6 +252,15 @@ "failed": "Eșuat", "password_has_been_reset_login": "Parola a fost resetată. Acum vă puteţi conecta cu parola nou creată.", "unexpected_error_try_again": "A apărut o eroare neașteptată. Încercați din nou.", + "sunday_time_error": "Oră nevalidă duminică", + "monday_time_error": "Oră nevalidă luni", + "tuesday_time_error": "Oră nevalidă marți", + "wednesday_time_error": "Oră nevalidă miercuri", + "thursday_time_error": "Oră nevalidă joi", + "friday_time_error": "Oră nevalidă vineri", + "saturday_time_error": "Oră nevalidă sâmbătă", + "error_end_time_before_start_time": "Ora de încheiere nu poate fi înainte de ora de începere", + "error_end_time_next_day": "Ora de încheiere nu poate depăși 24 de ore", "back_to_bookings": "Înapoi la rezervări", "free_to_pick_another_event_type": "Puteți alege un alt eveniment oricând.", "cancelled": "Anulat", @@ -233,6 +293,10 @@ "hover_over_bold_times_tip": "Sfat: treceți cu mouse-ul peste orele cu bold pentru a obține o marcare de timp completă", "start_time": "Ora începerii", "end_time": "Ora de încheiere", + "buffer_time": "Timp tampon", + "before_event": "Înainte de eveniment", + "after_event": "După eveniment", + "event_buffer_default": "Fără timp tampon", "buffer": "Buffer", "your_day_starts_at": "Ziua ta începe la", "your_day_ends_at": "Ziua ta se termină la", @@ -248,6 +312,9 @@ "light": "Lumină", "dark": "Întunecat", "automatically_adjust_theme": "Ajustează automat tema în funcție de preferințele invitaților", + "user_dynamic_booking_disabled": "Unii dintre utilizatorii din grup au dezactivat momentan rezervările dinamice în grup", + "allow_dynamic_booking_tooltip": "Linkuri de rezervare în grup care pot fi create dinamic prin adăugarea mai multor nume de utilizator cu „+”. Exemplu: „cal.com/bailey+peer”", + "allow_dynamic_booking": "Permiteți participanților să vă efectueze rezervări dinamice în grup", "email": "E-mail", "email_placeholder": "jdoe@exemplu.com", "full_name": "Numele complet", @@ -260,9 +327,12 @@ "event_triggers": "Declanșatoare eveniment", "subscriber_url": "Url Abonat", "create_new_webhook": "Creează un nou webhook", + "webhooks": "Webhooks", + "team_webhooks": "Webhooks de echipă", "create_new_webhook_to_account": "Creați un nou webhook pentru contul dvs", "new_webhook": "Webhook nou", "receive_cal_meeting_data": "Primiți date Cal ședință la un URL specificat, în timp real, atunci când un eveniment este programat sau anulat.", + "receive_cal_event_meeting_data": "Primiți date despre ședința Cal la un URL specificat, în timp real, atunci când acest eveniment este programat sau anulat.", "responsive_fullscreen_iframe": "Iframe ecran complet Responsive", "loading": "Încărcare...", "standard_iframe": "Iframe standard", @@ -280,6 +350,7 @@ "no_event_types_have_been_setup": "Acest utilizator nu a configurat încă niciun tip de eveniment.", "edit_logo": "Editare logo", "upload_a_logo": "Încărcați o siglă", + "remove_logo": "Eliminați sigla", "enable": "Activare", "code": "Cod", "code_is_incorrect": "Codul este incorect.", @@ -346,16 +417,19 @@ "booking_confirmation": "Confirmă {{eventTypeTitle}} cu {{profileName}}", "booking_reschedule_confirmation": "Reprogramează {{eventTypeTitle}} cu {{profileName}}", "in_person_meeting": "Link sau întâlnire în persoană", + "link_meeting": "Link întâlnire", "phone_call": "Apel telefonic", "phone_number": "Număr de telefon", "enter_phone_number": "Introduceți numărul de telefon", "reschedule": "Reprogrameaza", + "reschedule_this": "Reprogramați în schimb", "book_a_team_member": "Rezervați în schimb un membru al echipei", "or": "SAU", "go_back": "Du-te înapoi", "email_or_username": "E-mail sau nume utilizator", "send_invite_email": "Trimite un e-mail de invitație", "role": "Rol", + "edit_role": "Modificare rol", "edit_team": "Editează echipa", "reject": "Respinge", "accept": "Acceptă", @@ -371,15 +445,19 @@ "members": "Membri", "member": "Membru", "owner": "Proprietar", + "admin": "Admin", "new_member": "Membru Nou", "invite": "Invită", "invite_new_member": "Invită un membru nou", "invite_new_team_member": "Invită pe cineva în echipa ta.", + "change_member_role": "Schimbați rolul de membru al echipei", "disable_cal_branding": "Dezactivează branding Cal.com", "disable_cal_branding_description": "Ascunde tot branding-ul Cal.com din paginile tale publice.", "danger_zone": "Zonă de pericol", "back": "Înapoi", "cancel": "Anulează", + "apply": "Aplicați", + "cancel_event": "Anulați acest eveniment", "continue": "Continuă", "confirm": "Confirmă", "disband_team": "Desfiinţează echipa", @@ -389,6 +467,8 @@ "confirm_remove_member": "Da, elimină membrul", "remove_member": "Elimină membrul", "manage_your_team": "Gestionează-ți echipa", + "no_teams": "Încă nu aveți nicio echipă.", + "no_teams_description": "Echipele permit altora să rezerve evenimente distribuite între colegii dvs.", "submit": "Trimiteți", "delete": "Şterge", "update": "Actualizează", @@ -396,8 +476,16 @@ "pending": "În aşteptare", "open_options": "Opțiuni deschise", "copy_link": "Copiază link-ul la eveniment", + "share": "Distribuire", + "share_event": "Puteți să îmi faceți o rezervare în Cal sau să îmi trimiteți linkul dvs.?", + "copy_link_team": "Copiați linkul în echipă", + "leave_team": "Părăsiți echipa", + "confirm_leave_team": "Da, părăsiți echipa", + "leave_team_confirmation_message": "Sigur doriți să părăsiți această echipă? Nu veți mai putea să rezervați folosind această echipă.", + "user_from_team": "{{user}} din {{team}}", "preview": "Previzualizează", "link_copied": "Link copiat!", + "link_shared": "Link distribuit!", "title": "Titlu", "description": "Descriere", "quick_video_meeting": "O şedinţă video rapidă.", @@ -412,7 +500,12 @@ "url": "URL", "hidden": "Ascuns", "readonly": "Needitabil", + "one_time_link": "Link unic", + "plan_description": "Aveți momentan planul {{plan}}.", + "plan_upgrade_invitation": "Faceți upgrade-ul contului la planul Pro pentru a activa toate avantajele pe care le oferim.", "plan_upgrade": "Trebuie actualizat planul pentru a avea mai mult de un tip de eveniment activ.", + "plan_upgrade_teams": "Trebuie să faceți upgrade-ul planului pentru a crea o echipă.", + "plan_upgrade_instructions": "Puteți face <1>upgrade aici.", "event_types_page_title": "Tipuri de evenimente", "event_types_page_subtitle": "Creează evenimente de împărtășire pentru oameni să facă rezervări pe calendarul tău.", "new_event_type_btn": "Nou tip de eveniment", @@ -425,6 +518,8 @@ "event_type_created_successfully": "{{eventTypeTitle}} tip de eveniment creat cu succes", "event_type_updated_successfully": "{{eventTypeTitle}} tip de eveniment actualizat cu succes", "event_type_deleted_successfully": "Tipul de eveniment șters cu succes", + "web3_metamask_added": "Metamask adăugat cu succes", + "web3_metamask_disconnected": "Metamask deconectat cu succes", "hours": "Ore", "your_email": "E-mailul tău", "change_avatar": "Schimbă avatarul", @@ -432,6 +527,9 @@ "timezone": "Timezone", "first_day_of_week": "Prima Zi a Săptămânii", "single_theme": "Temă unică", + "brand_color": "Culoare marcă", + "light_brand_color": "Culoare marcă (temă luminoasă)", + "dark_brand_color": "Culoare marcă (temă întunecată)", "file_not_named": "Fișierul nu este numit [idOrSlug]/[user]", "create_team": "Creează o echipă", "name": "Nume", @@ -442,10 +540,15 @@ "create_first_team_and_invite_others": "Creează-ți prima echipă și invită-i pe ceilalți utilizatori să lucreze împreună cu tine.", "create_team_to_get_started": "Creează o echipă pentru a începe", "teams": "Echipe", + "team_billing": "Facturare echipă", + "upgrade_to_flexible_pro_title": "Am schimbat facturarea pentru echipe", + "upgrade_to_flexible_pro_message": "Există membri fără licență în echipa dvs. Faceți upgrade la planul Pro pentru a acoperi licențele lipsă.", + "changed_team_billing_info": "Începând din ianuarie 2022, se percepe un preț per licență pentru membrii echipei. Membrii echipei dvs. care au avut gratuit planul PRO sunt acum într-o perioadă de probă de 14 zile. Odată ce perioada de probă expiră, acești membri vor fi ascunși în echipa dvs. dacă nu faceți upgrade acum.", "create_manage_teams_collaborative": "Creați și gestionați echipe pentru a utiliza funcții colaborative.", "only_available_on_pro_plan": "Această caracteristică este disponibilă doar în planul Pro", "remove_cal_branding_description": "Pentru a elimina branding Cal din paginile dvs. de rezervare, trebuie să faceți upgrade la un cont Pro.", "edit_profile_info_description": "Editați informațiile de profil, care sunt afișate pe link-ul dvs. de programare.", + "change_email_tip": "Este posibil să trebuiască să vă deconectați și reconectați pentru ca schimbarea să aibă efect.", "little_something_about": "Un pic despre tine.", "profile_updated_successfully": "Profil actualizat cu succes", "your_user_profile_updated_successfully": "Profilul dvs. de utilizator a fost actualizat cu succes.", @@ -467,6 +570,7 @@ "add_attendees": "Adăugați participanți", "show_advanced_settings": "Afișează setările avansate", "event_name": "Denumirea evenimentului", + "event_name_tooltip": "Numele care va apărea în calendare", "meeting_with_user": "Întâlnire cu {USER}", "additional_inputs": "Date suplimentare", "label": "Eticheta", @@ -474,6 +578,8 @@ "type": "Scrie", "edit": "Editează", "add_input": "Adaugă date", + "disable_notes": "Ascundeți notele din calendar", + "disable_notes_description": "Din motive de confidențialitate, intrările și notele suplimentare vor fi ascunse în intrarea din calendar. Acestea vor fi trimise în continuare pe adresa dvs. de e-mail.", "opt_in_booking": "Rezervare Opt-in", "opt_in_booking_description": "Rezervarea trebuie să fie confirmată manual înainte de a fi împinsă către integrări și înainte de trimiterea unui mail de confirmare.", "disable_guests": "Dezactivează vizitatorii", @@ -483,6 +589,7 @@ "calendar_days": "zile calendaristice", "business_days": "zile lucrătoare", "set_address_place": "Setați o adresă sau un loc", + "set_link_meeting": "Definiți un link pentru ședință", "cal_invitee_phone_number_scheduling": "Cal va cere invitatului dvs. să introducă un număr de telefon înainte de programare.", "cal_provide_google_meet_location": "Cal va oferi o locație Google Meet.", "cal_provide_zoom_meeting_url": "Cal va oferi un URL pentru ședința de Zoom.", @@ -490,6 +597,7 @@ "cal_provide_video_meeting_url": "Cal va oferi un URL pentru ședința de Daily.", "cal_provide_jitsi_meeting_url": "Cal va oferi un URL pentru ședința de Jitsi Meet.", "cal_provide_huddle01_meeting_url": "Cal va oferi un URL pentru ședința de Huddle01 Web3.", + "cal_provide_teams_meeting_url": "Cal va pune la dispoziție un URL pentru ședința prin MS Teams. OBSERVAȚIE: TREBUIE SĂ AVEȚI UN CONT PROFESIONAL SAU ȘCOLAR", "require_payment": "Solicită plata", "commission_per_transaction": "comision per tranzacție", "event_type_updated_successfully_description": "Tipul de eveniment a fost actualizat cu succes.", @@ -506,11 +614,20 @@ "new_event_type_to_book_description": "Creaţi un nou tip de eveniment cu care oamenii să poată rezerva orare.", "length": "Durata", "minimum_booking_notice": "Aviz minim de rezervare", + "slot_interval": "Intervale de timp", + "slot_interval_default": "Folosiți durata evenimentului (implicit)", "delete_event_type_description": "Sunteţi sigur că doriţi să ştergeţi acest tip de eveniment? Oricine cu cine ați distribuit acest link nu va mai putea să facărezervări folosindu-l.", "delete_event_type": "Șterge tipul de eveniment", "confirm_delete_event_type": "Da, șterge tipul de eveniment", + "delete_account": "Ștergeți contul", + "confirm_delete_account": "Da, ștergeți contul", + "delete_account_confirmation_message": "Sigur doriți să vă ștergeți contul Cal.com? Persoanele cărora le-ați transmis linkul contului dvs. nu vor mai putea rezerva folosindu-l și orice preferințe pe care le-ați salvat se vor pierde.", "integrations": "Integrări", + "apps": "Aplicații", + "app_store": "App Store", + "app_store_description": "Creăm conexiuni între oameni, tehnologii și locuri de muncă.", "settings": "Setări", + "event_type_moved_successfully": "Tipul de eveniment a fost mutat cu succes", "next_step": "Sari peste", "prev_step": "Pas anterior", "installed": "Instalat", @@ -519,11 +636,139 @@ "connect_your_favourite_apps": "Conectați aplicațiile dvs. preferate.", "automation": "Automatizare", "configure_how_your_event_types_interact": "Configurați modul în care tipurile de evenimente ar trebui să interacționeze cu calendarele dvs.", + "select_destination_calendar": "Creați evenimente în", "connect_an_additional_calendar": "Conectează un calendar adițional", "conferencing": "Conferință", "calendar": "Calendar", "not_installed": "Nu este instalat", "error_password_mismatch": "Parolele nu se potrivesc.", "error_required_field": "Acest câmp este obligatoriu.", - "availability_updated_successfully": "Disponibilitatea actualizată cu succes" + "status": "Stare", + "team_view_user_availability": "Vizualizați disponibilitatea utilizatorului", + "team_view_user_availability_disabled": "Utilizatorul trebuie să accepte invitația pentru a vizualiza disponibilitatea", + "set_as_away": "Setați starea la Plecat", + "set_as_free": "Dezactivați starea Plecat", + "user_away": "Acest utilizator nu este momentan disponibil.", + "user_away_description": "Persoana pentru care încercați să efectuați rezervare și-a setat starea la Plecat, prin urmare, nu acceptă rezervări noi.", + "meet_people_with_the_same_tokens": "Cunoașteți persoane cu aceleași tokenuri", + "only_book_people_and_allow": "Rezervați și permiteți rezervări doar de la persoane care folosesc aceleași tokenuri: DAO sau NFT.", + "saml_config_deleted_successfully": "Configurația SAML a fost ștearsă cu succes", + "account_created_with_identity_provider": "Contul dvs. a fost creat folosind un furnizor de identitate.", + "account_managed_by_identity_provider": "Contul dvs. este gestionat de {{provider}}", + "account_managed_by_identity_provider_description": "Pentru a vă schimba adresa de e-mail sau parola, a activa autentificarea în doi pași și multe altele, accesați setările contului {{provider}}.", + "signin_with_google": "Conectați-vă cu Google", + "signin_with_saml": "Conectați-vă cu SAML", + "saml_configuration": "Configurație SAML", + "delete_saml_configuration": "Ștergeți configurația SAML", + "delete_saml_configuration_confirmation_message": "Sigur doriți să ștergeți configurația SAML? Membrii echipei dvs. care utilizează conectarea prin SAML nu vor mai putea accesa Cal.com.", + "confirm_delete_saml_configuration": "Da, ștergeți configurația SAML", + "saml_not_configured_yet": "SAML nu este configurat încă", + "saml_configuration_description": "Pentru a actualiza configurația SAML, inserați metadatele SAML generate de furnizorul dvs. de identitate în caseta de text de mai jos.", + "saml_configuration_placeholder": "Inserați aici metadatele SAML generate de furnizorul dvs. de identitate", + "saml_configuration_update_failed": "Actualizarea configurării SAML a eșuat", + "saml_configuration_delete_failed": "Ștergerea configurării SAML a eșuat", + "saml_email_required": "Introduceți un e-mail pentru a vă putea găsi furnizorul de identitate SAML", + "you_will_need_to_generate": "Va trebui să generați un token de acces din vechiul instrument de programare.", + "import": "Importare", + "import_from": "Importați din", + "access_token": "Token de acces", + "visit_roadmap": "Foaie parcurs", + "popular_categories": "Categorii populare", + "trending_apps": "Aplicații populare", + "all_apps": "Toate aplicațiile", + "installed_apps": "Aplicații instalate", + "manage_your_connected_apps": "Gestionați aplicațiile instalate sau modificați setările", + "browse_apps": "Răsfoiți aplicațiile", + "features": "Caracteristici", + "permissions": "Permisiuni", + "terms_and_privacy": "Termeni și confidențialitate", + "published_by": "Publicat de {{author}}", + "subscribe": "Abonare", + "buy": "Cumpărați", + "install_app": "Instalați aplicația", + "categories": "Categorii", + "pricing": "Prețuri", + "learn_more": "Aflați mai multe", + "privacy_policy": "Politica de confidențialitate", + "terms_of_service": "Condiții de utilizare", + "remove": "Eliminați", + "add": "Adăugați", + "verify_wallet": "Verificați portofelul", + "connect_metamask": "Conectați Metamask", + "create_events_on": "Creați evenimente pe:", + "missing_license": "Licență lipsă", + "signup_requires": "Licență comercială necesară", + "signup_requires_description": "În prezent, Cal.com, Inc. nu oferă o versiune open-source gratuită a paginii de înscriere. Pentru a primi acces deplin la componentele de înscriere, trebuie să obțineți o licență comercială. Pentru uz personal, recomandăm Prisma Data Platform sau orice altă interfață Postgres pentru a crea conturi.", + "next_steps": "Pașii următori", + "acquire_commercial_license": "Obțineți o licență comercială", + "the_infrastructure_plan": "Planul de infrastructură este bazat pe utilizare și oferă reduceri atractive companiilor de tip start-up.", + "prisma_studio_tip": "Creați un cont prin intermediul Prisma Studio", + "prisma_studio_tip_description": "Aflați cum să configurați primul utilizator", + "contact_sales": "Contactați departamentul de vânzări", + "error_404": "Eroare 404", + "default": "Implicit", + "set_to_default": "Setare ca implicit", + "new_schedule_btn": "Program nou", + "add_new_schedule": "Adăugați un program nou", + "delete_schedule": "Ștergeți programul", + "schedule_created_successfully": "Programul {{scheduleName}} a fost creat cu succes", + "availability_updated_successfully": "Disponibilitatea actualizată cu succes", + "schedule_deleted_successfully": "Program șters cu succes", + "default_schedule_name": "Program de lucru", + "new_schedule_heading": "Creați un program de disponibilitate", + "new_schedule_description": "Crearea programelor de disponibilitate vă permite să gestionați disponibilitatea în toate tipurile de evenimente. Acestea pot fi aplicate la unul sau mai multe tipuri de evenimente.", + "requires_ownership_of_a_token": "Necesită dreptul de proprietate asupra unui token care aparține următoarei adrese:", + "example_name": "Ion Popescu", + "time_format": "Format oră", + "12_hour": "12 ore", + "24_hour": "24 de ore", + "redirect_success_booking": "Redirecționare la rezervare ", + "you_are_being_redirected": "Sunteți redirecționat către {{ url }} în $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Pentru a utiliza această caracteristică, trebuie să faceți upgrade la un cont Pro.", + "duplicate": "Duplicare", + "you_can_manage_your_schedules": "Vă puteți gestiona programările pe pagina Disponibilitate.", + "api_keys": "Chei API", + "api_key_modal_subtitle": "Cheile API vă permit să efectuați apeluri API pentru propriul dvs. cont.", + "api_keys_subtitle": "Generați chei API pentru accesarea propriului cont.", + "generate_new_api_key": "Generați o cheie API nouă", + "create_api_key": "Creați o cheie API", + "personal_note": "Denumiți această cheie", + "personal_note_placeholder": "Ex. Dezvoltare", + "api_key_no_note": "Cheie API fără denumire", + "api_key_never_expires": "Această cheie API nu are dată de expirare", + "edit_api_key": "Modificați cheia API", + "never_expire_key": "Nu expiră niciodată", + "delete_api_key": "Revocați cheia API", + "success_api_key_created": "Cheie API creată cu succes", + "success_api_key_edited": "Cheie API actualizată cu succes", + "create": "Creare", + "success_api_key_created_bold_tagline": "Salvați această cheie API într-un loc sigur.", + "you_will_only_view_it_once": "Nu o veți mai putea vizualiza din nou odată ce închideți acest modal.", + "copy_to_clipboard": "Copiați în clipboard", + "enabled_after_update": "Activat după actualizare", + "confirm_delete_api_key": "Revocați această cheie API", + "revoke_api_key": "Revocați cheia API", + "api_key_copied": "Cheie API copiată!", + "delete_api_key_confirm_title": "Eliminați definitiv această cheie API din contul dvs.?", + "copy": "Copiere", + "expire_date": "Data expirării", + "expired": "Expirat", + "never_expires": "Nu expiră niciodată", + "expires": "Expiră", + "request_reschedule_booking": "Solicitați reprogramarea rezervării", + "reason_for_reschedule": "Motivul reprogramării", + "book_a_new_time": "Rezervați o nouă oră", + "reschedule_request_sent": "Solicitare de reprogramare trimisă", + "reschedule_modal_description": "Această acțiune va anula ședința programată, va anunța programatorul și îi va cere să aleagă o nouă oră.", + "reason_for_reschedule_request": "Motivul solicitării de reprogramare", + "send_reschedule_request": "Solicitare de reprogramare ", + "edit_booking": "Modificați rezervarea", + "reschedule_booking": "Reprogramați rezervarea", + "former_time": "Orar anterior", + "impersonate": "Reprezentare", + "impersonate_user_tip": "Toate utilizările acestei caracteristici sunt verificate.", + "impersonating_user_warning": "Reprezentare nume de utilizator „{{user}}”.", + "impersonating_stop_instructions": "<0>Faceți clic aici pentru a înceta.", + "calendar_error": "A apărut o eroare. Încercați să reconectați calendarul cu toate permisiunile necesare" } diff --git a/apps/web/public/static/locales/ro/vital.json b/apps/web/public/static/locales/ro/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/ro/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/ru/common.json b/apps/web/public/static/locales/ru/common.json index ee0fcdde92..a13405329c 100644 --- a/apps/web/public/static/locales/ru/common.json +++ b/apps/web/public/static/locales/ru/common.json @@ -2,6 +2,8 @@ "trial_days_left": "У вас $t(day, {\"count\": {{days}} }) осталось на пробной версии PRO", "day": "{{count}} день", "day_plural": "{{count}} дней", + "second": "{{count}} сек.", + "second_plural": "{{count}} сек.", "upgrade_now": "Обновить сейчас", "accept_invitation": "Принять приглашение", "calcom_explained": "Cal.com - это альтернатива Calendly с открытым исходным кодом, позволяющая контролировать собственные данные, рабочий процесс и внешний вид.", @@ -60,9 +62,15 @@ "password_reset_instructions": "Если вы не запрашивали это, проигнорируйте это письмо и пароль не будет изменен.", "event_awaiting_approval_subject": "Ожидает подтверждения: {{eventType}} с {{name}} на {{date}}", "event_still_awaiting_approval": "Событие все еще ждет вашего подтверждения", + "booking_submitted_subject": "Бронирование отправлено: {{eventType}} с {{name}}, {{date}}", "your_meeting_has_been_booked": "Ваша встреча была забронирована", "event_type_has_been_rescheduled_on_time_date": "Ваша {{eventType}} с {{name}} была перенесена на {{time}} ({{timeZone}}) на {{date}}.", "event_has_been_rescheduled": "Ваше событие было перенесено.", + "request_reschedule_title_attendee": "Запросить перенос бронирования", + "request_reschedule_subtitle": "{{organizer}} отменил(-а) бронирование и попросил(-а) вас выбрать другое время.", + "request_reschedule_title_organizer": "Вы запросили у {{attendee}} изменение расписания", + "request_reschedule_subtitle_organizer": "Вы отменили бронирование, и {{attendee}} теперь должен(-на) выбрать новое время бронирования.", + "reschedule_reason": "Причина переноса", "hi_user_name": "Привет {{userName}}", "ics_event_title": "{{eventType}} с {{name}}", "new_event_subject": "Новое событие: {{attendeeName}} - {{date}} - {{eventType}}", @@ -81,6 +89,7 @@ "meeting_url": "URL встречи", "meeting_request_rejected": "Ваш запрос на встречу отклонен", "rescheduled_event_type_subject": "Перенесено: {{eventType}} с {{name}} на {{date}}", + "requested_to_reschedule_subject_attendee": "Требуется перенести бронирование: забронируйте новое время для {{eventType}} с {{name}}", "rejected_event_type_with_organizer": "Отклонено: {{eventType}} с {{organizer}} на {{date}}", "hi": "Привет", "join_team": "Вступить в команду", @@ -284,6 +293,10 @@ "hover_over_bold_times_tip": "Подсказка: Наведите курсор на выделенные часы чтобы увидеть полное время", "start_time": "Время начала", "end_time": "Время окончания", + "buffer_time": "Дополнительное время", + "before_event": "Перед событием", + "after_event": "После события", + "event_buffer_default": "Без дополнительного времени", "buffer": "Буфер", "your_day_starts_at": "Ваш день начинается в", "your_day_ends_at": "Ваш день заканчивается в", @@ -299,6 +312,9 @@ "light": "Светлая", "dark": "Тёмная", "automatically_adjust_theme": "Автоматически изменять тему отображения на основе предпочтений участников", + "user_dynamic_booking_disabled": "Некоторые участники группы отключили функцию динамического группового бронирования", + "allow_dynamic_booking_tooltip": "Ссылки для группового бронирования, которые можно создавать динамически, указывая несколько имен пользователей при помощи знака «+». Пример: «cal.com/bailey+peer»", + "allow_dynamic_booking": "Разрешить участникам бронировать встречи с вами при помощи динамического группового бронирования", "email": "Адрес электронной почты", "email_placeholder": "jdoe@example.com", "full_name": "Имя", @@ -311,9 +327,12 @@ "event_triggers": "Триггеры событий", "subscriber_url": "URL подписчика", "create_new_webhook": "Создать новый веб-хук", + "webhooks": "Вебхуки", + "team_webhooks": "Командные вебхуки", "create_new_webhook_to_account": "Создайте новый вебхук для вашей учетной записи", "new_webhook": "Новый веб-хук", "receive_cal_meeting_data": "Получайте данные встреч от Cal по указанному URL в режиме реального времени при создании или отмене встреч.", + "receive_cal_event_meeting_data": "В реальном времени получайте по указанному URL-адресу данные встречи из Cal, когда происходит бронирование или отмена события.", "responsive_fullscreen_iframe": "Адаптивный полноэкранный iframe", "loading": "Загрузка...", "standard_iframe": "Стандартный iframe", @@ -398,10 +417,12 @@ "booking_confirmation": "Подтвердите вашу встречу «{{eventTypeTitle}}» с {{profileName}}", "booking_reschedule_confirmation": "Перенесите вашу встречу «{{eventTypeTitle}}» с {{profileName}}", "in_person_meeting": "Ссылка или личная встреча", + "link_meeting": "Встреча со ссылкой", "phone_call": "Телефонный звонок", "phone_number": "Номер телефона", "enter_phone_number": "Введите номер телефона", "reschedule": "Перенести", + "reschedule_this": "Перенести вместо отмены", "book_a_team_member": "Забронировать встречу с одним из членов команды", "or": "ИЛИ", "go_back": "Вернуться", @@ -435,6 +456,8 @@ "danger_zone": "Опасная зона", "back": "Назад", "cancel": "Отмена", + "apply": "Применить", + "cancel_event": "Отменить это событие", "continue": "Продолжить", "confirm": "Подтвердить", "disband_team": "Распустить команду", @@ -504,6 +527,8 @@ "first_day_of_week": "Начало недели", "single_theme": "Тема оформления", "brand_color": "Цвет бренда", + "light_brand_color": "Цвет бренда (светлая тема)", + "dark_brand_color": "Цвет бренда (темная тема)", "file_not_named": "Файл должен называться [idOrSlug]/[user]", "create_team": "Создать команду", "name": "Имя", @@ -552,6 +577,8 @@ "type": "Тип", "edit": "Изменить", "add_input": "Добавить вопрос", + "disable_notes": "Скрыть заметки в календаре", + "disable_notes_description": "В целях обеспечения конфиденциальности дополнительные поля и заметки будут скрыты в записи календаря. При этом они будут отправлены на ваш адрес электронной почты.", "opt_in_booking": "Бронирование с подтверждением", "opt_in_booking_description": "Бронирование должно быть подтверждено вручную, прежде чем оно будет передано интеграциям. Также отправляется письмо с подтверждением бронирования.", "disable_guests": "Отключить участников", @@ -561,6 +588,7 @@ "calendar_days": "календарные дни", "business_days": "рабочие дни", "set_address_place": "Укажите адрес или место", + "set_link_meeting": "Установить ссылку на встречу", "cal_invitee_phone_number_scheduling": "Cal попросит участника указать номер телефона перед началом бронирования.", "cal_provide_google_meet_location": "Cal создаст ссылку на встречу в Google Meet.", "cal_provide_zoom_meeting_url": "Cal создаст ссылку на встречу в Zoom.", @@ -568,6 +596,7 @@ "cal_provide_video_meeting_url": "Cal создаст ссылку на встречу в Daily.", "cal_provide_jitsi_meeting_url": "Cal создаст ссылку на встречу в Jitsi Meet.", "cal_provide_huddle01_meeting_url": "Cal создаст ссылку на встречу в Huddle01 Web3.", + "cal_provide_teams_meeting_url": "Cal предоставит URL-адрес встречи в MS Teams. ПРИМЕЧАНИЕ: У ВАС ДОЛЖЕН БЫТЬ КОРПОРАТИВНЫЙ ИЛИ СТУДЕНЧЕСКИЙ АККАУНТ", "require_payment": "Требуется оплата", "commission_per_transaction": "комиссия за сделку", "event_type_updated_successfully_description": "Ваш шаблон события успешно обновлён.", @@ -593,6 +622,9 @@ "confirm_delete_account": "Да, удалить аккаунт", "delete_account_confirmation_message": "Вы уверены, что хотите удалить свой Cal.com-аккаунт? Любой, кто имеет ссылку на вашу учетную запись, больше не сможет забронировать встречу, и сохраненные настройки будут потеряны.", "integrations": "Интеграции", + "apps": "Приложения", + "app_store": "App Store", + "app_store_description": "Соединяя людей, технологии и рабочие места.", "settings": "Настройки", "event_type_moved_successfully": "Тип события был успешно перемещен", "next_step": "Пропустить шаг", @@ -640,6 +672,24 @@ "import_from": "Импортировать из", "access_token": "Токен доступа", "visit_roadmap": "План действий", + "popular_categories": "Популярные категории", + "trending_apps": "Популярные приложения", + "all_apps": "Все приложения", + "installed_apps": "Установленные приложения", + "manage_your_connected_apps": "Управление установленными приложениями и изменение настроек", + "browse_apps": "Обзор приложений", + "features": "Возможности", + "permissions": "Разрешения", + "terms_and_privacy": "Условия использования и конфиденциальность", + "published_by": "Опубликовано {{author}}", + "subscribe": "Подписаться", + "buy": "Купить", + "install_app": "Установить приложение", + "categories": "Категории", + "pricing": "Цены", + "learn_more": "Подробнее", + "privacy_policy": "Политика конфиденциальности", + "terms_of_service": "Условия использования", "remove": "Удалить", "add": "Добавить", "verify_wallet": "Подтвердить кошелек", @@ -655,7 +705,63 @@ "prisma_studio_tip_description": "Узнайте, как настроить первого пользователя", "contact_sales": "Связаться с отделом продаж", "error_404": "Ошибка 404", + "default": "По умолчанию", + "set_to_default": "Использовать по умолчанию", + "new_schedule_btn": "Новое расписание", + "add_new_schedule": "Добавить новое расписание", + "delete_schedule": "Удалить расписание", + "schedule_created_successfully": "Расписание {{scheduleName}} успешно создано", "availability_updated_successfully": "Доступность успешно обновлена", + "schedule_deleted_successfully": "Расписание успешно удалено", + "default_schedule_name": "Часы работы", + "new_schedule_heading": "Создать расписание доступности", + "new_schedule_description": "Расписания доступности позволяют управлять доступностью для различных типов событий. Их можно применять к одному или нескольким типам событий.", "requires_ownership_of_a_token": "Нужно быть владельцем токена, относящегося к следующему адресу:", - "example_name": "Джон Доу" + "example_name": "Джон Доу", + "time_format": "Формат времени", + "12_hour": "12-часовой", + "24_hour": "24-часовой", + "redirect_success_booking": "Перенаправление при бронировании ", + "you_are_being_redirected": "Перенаправление на {{ url }} произойдет через $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Чтобы использовать эту функцию, необходимо перейти на аккаунт Pro.", + "duplicate": "Создать копию", + "you_can_manage_your_schedules": "Расписаниями можно управлять на странице «Доступность».", + "api_keys": "Ключи API", + "api_key_modal_subtitle": "Ключи API позволяют совершать API-вызовы для вашего аккаунта.", + "api_keys_subtitle": "Сгенерируйте ключи API для доступа к своему аккаунту.", + "generate_new_api_key": "Сгенерировать новый ключ API", + "create_api_key": "Создать ключ API", + "personal_note": "Введите название ключа", + "personal_note_placeholder": "Например, «разработка»", + "api_key_no_note": "Безымянный ключ API", + "api_key_never_expires": "Ключ API не имеет срока действия", + "edit_api_key": "Редактировать ключ API", + "never_expire_key": "Нет срока действия", + "delete_api_key": "Отозвать ключ API", + "success_api_key_created": "Ключ API успешно создан", + "success_api_key_edited": "Ключ API успешно обновлен", + "create": "Создать", + "success_api_key_created_bold_tagline": "Сохраните этот ключ API в безопасном месте.", + "you_will_only_view_it_once": "После закрытия окна вы больше не сможете посмотреть ключ.", + "copy_to_clipboard": "Копировать в буфер обмена", + "confirm_delete_api_key": "Отозвать ключ API", + "revoke_api_key": "Отозвать ключ API", + "api_key_copied": "Ключ API скопирован.", + "delete_api_key_confirm_title": "Удалить этот ключ API из вашего аккаунта навсегда?", + "copy": "Копировать", + "expire_date": "Срок действия", + "expired": "Истек", + "never_expires": "Не ограничен", + "expires": "Истекает", + "request_reschedule_booking": "Запросить перенос бронирования", + "reason_for_reschedule": "Причина переноса", + "book_a_new_time": "Забронировать новое время", + "reschedule_request_sent": "Запрос на перенос отправлен", + "reschedule_modal_description": "Запланированная встреча будет отменена, и организатор получит уведомление с просьбой назначить новое время.", + "reason_for_reschedule_request": "Причина запроса на перенос", + "send_reschedule_request": "Запросить перенос ", + "edit_booking": "Редактировать бронирование", + "reschedule_booking": "Перенести бронирование", + "former_time": "Прежнее время" } diff --git a/apps/web/public/static/locales/ru/vital.json b/apps/web/public/static/locales/ru/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/ru/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/sr/common.json b/apps/web/public/static/locales/sr/common.json index c00dbdc8a2..9967d6e0e9 100644 --- a/apps/web/public/static/locales/sr/common.json +++ b/apps/web/public/static/locales/sr/common.json @@ -2,6 +2,8 @@ "trial_days_left": "Preostalo vam je $t(day, {\"count\": {{days}} }) na vašem probnom PRO nalogu", "day": "dan", "day_plural": "dana", + "second": "{{count}} sekunda", + "second_plural": "{{count}} sek.", "upgrade_now": "Pretplati se sad", "accept_invitation": "Prihvati poziv", "calcom_explained": "Cal.com je Calendly alternativa sa otvorenim kodom koja vam omogućava kontrolu nad vašim podacima, procesom rada i izgledom.", @@ -60,9 +62,15 @@ "password_reset_instructions": "Ako niste pokrenuli ovaj zahtev, slobodno ignorišite ovu e-poštu i vaša lozinka se neće promeniti.", "event_awaiting_approval_subject": "Čeka na odobrenje: {{eventType}} sa {{name}} datuma {{date}}", "event_still_awaiting_approval": "Događaj još čeka na vaše odobrenje", + "booking_submitted_subject": "Rezervacija podneta: {{eventType}} sa {{name}} datuma {{date}}", "your_meeting_has_been_booked": "Sastanak je zakazan", "event_type_has_been_rescheduled_on_time_date": "Vaš {{eventType}} sa {{name}} je odložen za {{time}} ({{timeZone}}) datuma {{date}}.", "event_has_been_rescheduled": "Ažurirano - Vaš dogadjaj je odložen", + "request_reschedule_title_attendee": "Zahtev za promenu vremena rezervacije", + "request_reschedule_subtitle": "{{organizer}} je otkazao/la rezervaciju i tražio/la je da odaberete drugo vreme.", + "request_reschedule_title_organizer": "Zatražili ste da {{attendee}} promeni vreme rezervacije", + "request_reschedule_subtitle_organizer": "Otkazali ste rezervaciju i {{attendee}} treba da odabere novo vreme rezervacije sa vama.", + "reschedule_reason": "Razlog za promenu vremena", "hi_user_name": "Zdravo {{name}}", "ics_event_title": "{{eventType}} sa {{name}}", "new_event_subject": "Novi dogadjaj: {{attendeeName}} - {{date}} - {{eventType}}", @@ -81,6 +89,7 @@ "meeting_url": "URL Sastanka", "meeting_request_rejected": "Vaš zahtev za sastanak je odbijen", "rescheduled_event_type_subject": "Odloženo: {{eventType}} sa {{name}} datuma {{date}}", + "requested_to_reschedule_subject_attendee": "Promena vremena zahteva radnju: zakažite novo vreme za {{eventType}} sa {{name}}", "rejected_event_type_with_organizer": "Odbijeno: {{eventType}} sa {{organizer}} datuma {{date}}", "hi": "Zdravo", "join_team": "Pridruži se timu", @@ -284,6 +293,10 @@ "hover_over_bold_times_tip": "Savet: pređite kursorom preko podebljanih termina za punu vremensku oznaku", "start_time": "Vreme početka", "end_time": "Vreme završetka", + "buffer_time": "Dodatno vreme", + "before_event": "Pre događaja", + "after_event": "Nakon događaja", + "event_buffer_default": "Bez dodatnog vremena", "buffer": "Vreme između", "your_day_starts_at": "Vaš dan počinje u", "your_day_ends_at": "Vaš dan završava u", @@ -299,6 +312,9 @@ "light": "Svetla", "dark": "Tamna", "automatically_adjust_theme": "Automatski podesite temu na osnovu šta pozvani korisnik preferira", + "user_dynamic_booking_disabled": "Neki od korisnika u ovoj grupi trenutno su onemogućili dinamičke grupne rezervacije", + "allow_dynamic_booking_tooltip": "Moguće je dinamičko kreiranje linkova za grupne rezervacije navođenjem više korisničkih imena sa znakom „+”. Na primer: „cal.com/bailey+peer”", + "allow_dynamic_booking": "Dozvolite učesnicima da rezervišu termin sa vama putem dinamičkih grupnih rezervacija", "email": "E-pošta", "email_placeholder": "ppetrovic@example.com", "full_name": "Ime i prezime", @@ -311,9 +327,12 @@ "event_triggers": "Okidači Dogadjaja", "subscriber_url": "Url Subscriber-a", "create_new_webhook": "Napravite novi webhook", + "webhooks": "Webhooks", + "team_webhooks": "Timski Webhooks", "create_new_webhook_to_account": "Napravite novi webhook za vaš nalog", "new_webhook": "Novi Webhook", "receive_cal_meeting_data": "Primite podatke o Cal sastancima na odredjenom URL, istovremeno kad je dogadjaj rezervisan ili otkazan.", + "receive_cal_event_meeting_data": "Primite podatke o Cal sastancima na određenoj URL adresi istovremeno sa rezervacijom ili otkazivanjem ovog događaja.", "responsive_fullscreen_iframe": "Prilagodljiv iframe preko celog ekrana", "loading": "Učitavanje...", "standard_iframe": "Standardni iframe", @@ -398,10 +417,12 @@ "booking_confirmation": "Potvrdite vaš {{eventTypeTitle}} sa {{profileName}}", "booking_reschedule_confirmation": "Odložite vaš {{eventTypeTitle}} sa {{profileName}}", "in_person_meeting": "Sastanak uživo ili preko online linka", + "link_meeting": "Link za sastanak", "phone_call": "Telefonski poziv", "phone_number": "Broj Telefona", "enter_phone_number": "Unesite broj telefona", "reschedule": "Odloži", + "reschedule_this": "Ponovo rezerviši umesto toga", "book_a_team_member": "Umesto toga, rezervišite sastanak sa članom tima", "or": "ILI", "go_back": "Idi nazad", @@ -435,6 +456,8 @@ "danger_zone": "Opasna Zona", "back": "Nazad", "cancel": "Otkaži", + "apply": "Primeni", + "cancel_event": "Otkaži ovaj događaj", "continue": "Nastavi", "confirm": "Potvrdi", "disband_team": "Raspusti Tim", @@ -453,6 +476,8 @@ "pending": "Na čekanju", "open_options": "Otvori opcije", "copy_link": "Kopiraj link do događaja", + "share": "Podeli", + "share_event": "Da li biste želeli da rezervišete moj Cal ili da mi pošaljete vaš link?", "copy_link_team": "Kopiraj link do tima", "leave_team": "Napusti tim", "confirm_leave_team": "Da, napusti tim", @@ -460,6 +485,7 @@ "user_from_team": "{{user}} iz {{team}}", "preview": "Pregled", "link_copied": "Link kopiran!", + "link_shared": "Link je podeljen!", "title": "Naziv", "description": "Opis", "quick_video_meeting": "Kratak video poziv.", @@ -501,6 +527,8 @@ "first_day_of_week": "Prvi dan u nedelji", "single_theme": "Tema", "brand_color": "Boja brenda", + "light_brand_color": "Boja brenda (svetla tema)", + "dark_brand_color": "Boja brenda (tamna tema)", "file_not_named": "Datoteka nije imenovana [idOrSlug]/[user]", "create_team": "Napravi Tim", "name": "Ime", @@ -549,6 +577,8 @@ "type": "Tip", "edit": "Uredi", "add_input": "Dodaj unos", + "disable_notes": "Sakrij napomene u kalendaru", + "disable_notes_description": "Iz razloga privatnosti, dodatni unosi i napomene biće skriveni u stavci kalendara. Međutim, ipak će vam se slati na imejl.", "opt_in_booking": "Rezervacija sa potvrdom", "opt_in_booking_description": "Rezervacija mora biti manuelno potvrđena pre nego što završi u integracijama i e-pošta za potvrdu će biti poslata.", "disable_guests": "Onemogući Goste", @@ -558,6 +588,7 @@ "calendar_days": "kalendarska dana", "business_days": "radna dana", "set_address_place": "Postavite adresu ili mesto", + "set_link_meeting": "Postavi link za sastanak", "cal_invitee_phone_number_scheduling": "Cal će pitati pozvane da unesu broj telefona pre rezervisanja.", "cal_provide_google_meet_location": "Cal će obezbediti Google Meet link.", "cal_provide_zoom_meeting_url": "Cal će obezbediti URL Zoom sastanka.", @@ -565,6 +596,7 @@ "cal_provide_video_meeting_url": "Cal će obezbediti Daily video meeting URL.", "cal_provide_jitsi_meeting_url": "Mi ćemo generisati Jitsi Meet URL za vas.", "cal_provide_huddle01_meeting_url": "Cal će obezbediti Huddle01 web3 video meeting URL.", + "cal_provide_teams_meeting_url": "Cal će dostaviti URL adresu MS Teams sastanka. NAPOMENA: NEOPHODAN JE POSLOVNI ILI ŠKOLSKI NALOG", "require_payment": "Obavezno plaćanje", "commission_per_transaction": "provizija po transakciji", "event_type_updated_successfully_description": "Vaš tip događaja je uspešno ažuriran.", @@ -590,6 +622,9 @@ "confirm_delete_account": "Da, izbriši nalog", "delete_account_confirmation_message": "Da li ste sigurni da želite izbrisati svoj Cal.com nalog? S kim god ste podelili link ovog naloga više neće moći da ga koristi za rezervacije i sva podešavanja koja imate neće biti sačuvana.", "integrations": "Integracije", + "apps": "Aplikacije", + "app_store": "App Store", + "app_store_description": "Povezujemo ljude, tehnologiju i radno mesto.", "settings": "Podešavanja", "event_type_moved_successfully": "Tip događaja je uspešno pomeren", "next_step": "Preskoči korak", @@ -637,6 +672,24 @@ "import_from": "Importuj iz", "access_token": "Pristupni token", "visit_roadmap": "Plan razvijanja", + "popular_categories": "Popularne kategorije", + "trending_apps": "Aplikacije u trendu", + "all_apps": "Sve aplikacije", + "installed_apps": "Instalirane aplikacije", + "manage_your_connected_apps": "Upravljajte instaliranim aplikacijama ili promenite postavke", + "browse_apps": "Pregledajte aplikacije", + "features": "Funkcije", + "permissions": "Dozvole", + "terms_and_privacy": "Uslovi i privatnost", + "published_by": "Objavio/la {{author}}", + "subscribe": "Prijavite se", + "buy": "Kupi", + "install_app": "Instaliraj aplikaciju", + "categories": "Kategorije", + "pricing": "Cene", + "learn_more": "Saznajte više", + "privacy_policy": "Pravila o privatnosti", + "terms_of_service": "Uslovi korišćenja", "remove": "Ukloni", "add": "Dodaj", "verify_wallet": "Verifikuj Wallet", @@ -652,7 +705,63 @@ "prisma_studio_tip_description": "Naučite kako da podesite svog prvog korisnika", "contact_sales": "Kontaktirajte tim prodaje", "error_404": "Greška 404", + "default": "Podrazumevano", + "set_to_default": "Postavi na Podrazumevano", + "new_schedule_btn": "Novi raspored", + "add_new_schedule": "Dodaj novi raspored", + "delete_schedule": "Izbriši raspored", + "schedule_created_successfully": "Raspored {{scheduleName}} je uspešno izbrisan", "availability_updated_successfully": "Dostupnost uspešno ažurirana", + "schedule_deleted_successfully": "Raspored uspešno izbrisan", + "default_schedule_name": "Radno vreme", + "new_schedule_heading": "Napravi raspored dostupnosti", + "new_schedule_description": "Kreiranje rasporeda dostupnosti vam omogućava da upravljate dostupnošću za različite tipove događaja. Mogu se primeniti na jedan ili više tipova događaja.", "requires_ownership_of_a_token": "Potrebno je vlasništvo tokena koji pripada sledećoj adresi:", - "example_name": "Petar Petrović" + "example_name": "Petar Petrović", + "time_format": "Format vremena", + "12_hour": "12 sati", + "24_hour": "24 sata", + "redirect_success_booking": "Preusmeri na rezervaciju ", + "you_are_being_redirected": "Preusmeravamo vas na {{ url }} za $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Da biste koristili ovu funkciju, morate izvršiti nadogradnju na Pro nalog.", + "duplicate": "Dupliraj", + "you_can_manage_your_schedules": "Svojim rasporedima možete upravljati na stranici Dostupnost.", + "api_keys": "API ključevi", + "api_key_modal_subtitle": "API ključevi vam omogućavaju da vršite API pozive za svoj nalog.", + "api_keys_subtitle": "Generišite API ključeve za pristupanje svom nalogu.", + "generate_new_api_key": "Generiši novi API ključ", + "create_api_key": "Kreiraj API ključ", + "personal_note": "Imenuj ovaj ključ", + "personal_note_placeholder": "Npr. Razvoj", + "api_key_no_note": "API ključ bez imena", + "api_key_never_expires": "Ovaj API ključ nema datum isteka", + "edit_api_key": "Uredi API ključ", + "never_expire_key": "Nikada ne ističe", + "delete_api_key": "Opozovi API ključ", + "success_api_key_created": "API ključ je uspešno kreiran", + "success_api_key_edited": "API ključ je uspešno ažuriran", + "create": "Kreiraj", + "success_api_key_created_bold_tagline": "Sačuvajte ovaj API ključ na bezbednom mestu.", + "you_will_only_view_it_once": "Nećete moći ponovo da ga vidite nakon zatvaranja ovog modula.", + "copy_to_clipboard": "Kopiraj u privremenu memoriju", + "confirm_delete_api_key": "Opozovi ovaj API ključ", + "revoke_api_key": "Opozovi API ključ", + "api_key_copied": "API ključ je kopiran!", + "delete_api_key_confirm_title": "Želite li da trajno uklonite ovaj API ključ iz svog naloga?", + "copy": "Kopiraj", + "expire_date": "Datum isteka", + "expired": "Istekao", + "never_expires": "Nikada ne ističe", + "expires": "Ističe", + "request_reschedule_booking": "Zahtev za promenu vremena rezervacije", + "reason_for_reschedule": "Razlog za promenu vremena", + "book_a_new_time": "Rezervišite drugo vreme", + "reschedule_request_sent": "Zahtev za promenu vremena rezervacije poslat", + "reschedule_modal_description": "Ovim ćete otkazati zakazani sastanak, obavestiti podnosioca rezervacije i zatražiti da odabere drugo vreme.", + "reason_for_reschedule_request": "Razlog zahteva za promenu vremena", + "send_reschedule_request": "Zatražite promenu vremena ", + "edit_booking": "Uredi rezervaciju", + "reschedule_booking": "Promeni vreme rezervacije", + "former_time": "Prethodno vreme" } diff --git a/apps/web/public/static/locales/sr/vital.json b/apps/web/public/static/locales/sr/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/sr/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/sv/common.json b/apps/web/public/static/locales/sv/common.json index bcf6b0a57a..6f3673fdaa 100644 --- a/apps/web/public/static/locales/sv/common.json +++ b/apps/web/public/static/locales/sv/common.json @@ -2,6 +2,8 @@ "trial_days_left": "Du har $t(day, {\"count\": {{days}} }) kvar på din PRO-provperiod", "day": "{{count}} dag", "day_plural": "{{count}} dagar", + "second": "{{count}} sekund", + "second_plural": "{{count}} sekunder", "upgrade_now": "Uppgradera nu", "accept_invitation": "Acceptera inbjudan", "calcom_explained": "Cal.com är öppen källkods-alternativet till Calendly som ger dig kontroll över din egen data, ditt arbetsflöde och ditt utseende.", @@ -10,6 +12,7 @@ "event_declined_subject": "Avböjd: {{eventType}} med {{name}} vid {{date}}", "event_cancelled_subject": "Inställd: {{eventType}} med {{name}} vid {{date}}", "event_request_declined": "Din bokningsförfrågan har avbjöts", + "event_request_declined_recurring": "Din återkommande händelse har avböjts", "event_request_cancelled": "Din schemalagda bokning ställdes in", "organizer": "Arrangör", "need_to_reschedule_or_cancel": "Behöver du boka om eller avboka?", @@ -21,6 +24,7 @@ "rejection_confirmation": "Avböj bokningen", "manage_this_event": "Hantera denna bokning", "your_event_has_been_scheduled": "Din bokning har schemalagts", + "your_event_has_been_scheduled_recurring": "Din återkommande händelseförfrågan har schemalagts", "accept_our_license": "Acceptera vår licens genom att ändra .env variabeln <1>NEXT_PUBLIC_LICENSE_CONSENT till '{{agree}}'.", "remove_banner_instructions": "För att ta bort denna banner, vänligen öppna din .env fil och ändra <1>NEXT_PUBLIC_LICENSE_CONSENT variabeln till '{{agree}}'.", "error_message": "Felmeddelandet var: '{{errorMessage}}'", @@ -55,14 +59,21 @@ "confirm_or_reject_request": "Bekräfta eller avvisa förfrågan", "check_bookings_page_to_confirm_or_reject": "Kontrollera din bokningssida för att bekräfta eller avvisa bokningen.", "event_awaiting_approval": "En bokning väntar på godkännande", + "event_awaiting_approval_recurring": "En återkommande händelse väntar på ditt godkännande", "someone_requested_an_event": "Någon har begärt att schemalägga en händelse i din kalender.", "someone_requested_password_reset": "Någon har begärt en länk för att ändra ditt lösenord.", "password_reset_instructions": "Om du inte begärde detta kan du ignorera detta e-postmeddelande och ditt lösenord kommer inte att ändras.", "event_awaiting_approval_subject": "Inväntar godkännande: {{eventType}} med {{name}} den {{date}}", "event_still_awaiting_approval": "En bokning väntar fortfarande på ditt godkännande", + "booking_submitted_subject": "Bokning skickad: {{eventType}} med {{name}} {{date}}", "your_meeting_has_been_booked": "Ditt möte har bokats", "event_type_has_been_rescheduled_on_time_date": "Ditt {{eventType}} med {{name}} har blivit ombokad till {{time}} ({{timeZone}}) vid {{date}}.", "event_has_been_rescheduled": "Uppdatering - Din bokning har blivit omplanerad", + "request_reschedule_title_attendee": "Begäran om att boka om din bokning", + "request_reschedule_subtitle": "{{organizer}} har avbokat bokningen och bett dig att välja en annan tid.", + "request_reschedule_title_organizer": "Du har begärt att {{attendee}} ska boka om", + "request_reschedule_subtitle_organizer": "Du har avbokat bokningen och {{attendee}} ska välja en ny bokningstid hos dig.", + "reschedule_reason": "Anledning till ombokning", "hi_user_name": "Hej {{name}}", "ics_event_title": "{{eventType}} med {{name}}", "new_event_subject": "Ny bokning: {{attendeeName}} - {{date}} - {{eventType}}", @@ -71,6 +82,7 @@ "manage_my_bookings": "Hantera mina bokningar", "need_to_make_a_change": "Behöver du göra en ändring?", "new_event_scheduled": "En ny bokning har schemalagts.", + "new_event_scheduled_recurring": "En ny återkommande händelse har schemalagts.", "invitee_email": "Den Inbjudnes Email", "invitee_timezone": "Den Inbjudnes Tidszon", "event_type": "Typ av bokning", @@ -81,6 +93,7 @@ "meeting_url": "Mötes-URL", "meeting_request_rejected": "Din mötesförfrågan har avböjts", "rescheduled_event_type_subject": "Omplanerad: {{eventType}} med {{name}} vid {{date}}", + "requested_to_reschedule_subject_attendee": "Åtgärd som krävs Tidsschema: Boka en ny tid för {{eventType}} hos {{name}}", "rejected_event_type_with_organizer": "Avvisad: {{eventType}} med {{organizer}} vid {{date}}", "hi": "Hej", "join_team": "Anslut till team", @@ -119,6 +132,7 @@ "ping_test": "Ping test", "add_to_homescreen": "Lägg till denna app på din hemskärm för snabbare åtkomst och förbättrad upplevelse.", "upcoming": "Kommande", + "recurring": "Återkommande", "past": "Tidigare", "choose_a_file": "Välj en fil...", "upload_image": "Ladda upp bild", @@ -223,13 +237,20 @@ "add_to_calendar": "Lägg till i kalender", "other": "Annat", "emailed_you_and_attendees": "Vi skickade dig och de andra deltagarna en kalenderinbjudan med alla detaljer.", + "emailed_you_and_attendees_recurring": "Vi skickade dig och de andra deltagarna en kalenderinbjudan via e-post för den första av dessa återkommande händelser.", "emailed_you_and_any_other_attendees": "Du och alla andra deltagare har fått e-post med denna information.", "needs_to_be_confirmed_or_rejected": "Din bokning behöver fortfarande bekräftas eller avböjas.", + "needs_to_be_confirmed_or_rejected_recurring": "Ditt återkommande möte behöver fortfarande bekräftas eller avböjas.", "user_needs_to_confirm_or_reject_booking": "{{user}} måste fortfarande bekräfta eller avböja bokningen.", + "user_needs_to_confirm_or_reject_booking_recurring": "{{user}} måste fortfarande bekräfta eller avvisa varje bokning av det återkommande mötet.", "meeting_is_scheduled": "Mötet är schemalagt", + "meeting_is_scheduled_recurring": "De återkommande händelserna har schemalagts", "submitted": "Din bokning har skickats in", + "submitted_recurring": "Ditt återkommande möte har skickats", "booking_submitted": "Din bokning har skickats in", + "booking_submitted_recurring": "Ditt återkommande möte har skickats", "booking_confirmed": "Din bokning har bekräftats", + "booking_confirmed_recurring": "Ditt återkommande möte har bekräftats", "enter_new_password": "Ange det nya lösenordet för ditt konto.", "reset_password": "Återställ Lösenord", "change_your_password": "Ändra ditt lösenord", @@ -273,6 +294,7 @@ "bookings": "Bokningar", "bookings_description": "Se kommande och tidigare händelser bokade via dina händelsetyp-länkar.", "upcoming_bookings": "Så fort någon bokar en tid med dig kommer den att dyka upp här.", + "recurring_bookings": "Så fort någon bokar ett återkommande möte med dig visas det här.", "past_bookings": "Dina tidigare bokningar kommer att visas här.", "cancelled_bookings": "Dina inställda bokningar kommer att visas här.", "on": "den", @@ -303,6 +325,9 @@ "light": "Ljus", "dark": "Mörk", "automatically_adjust_theme": "Justera temat automatiskt baserat på inbjudningsinställningar", + "user_dynamic_booking_disabled": "Några av användarna i gruppen har för närvarande inaktiverat dynamiska gruppbokningar", + "allow_dynamic_booking_tooltip": "Gruppbokningslänkar som kan skapas dynamiskt genom att lägga till flera användarnamn med ett '+'. Exempel: 'cal.com/bailey+peer'", + "allow_dynamic_booking": "Tillåt deltagare att boka dig via dynamiska gruppbokningar", "email": "E-post", "email_placeholder": "jdoe@exempel.se", "full_name": "Fullständigt namn", @@ -405,6 +430,7 @@ "booking_confirmation": "Bekräfta {{eventTypeTitle}} med {{profileName}}", "booking_reschedule_confirmation": "Omboka {{eventTypeTitle}} med {{profileName}}", "in_person_meeting": "Länk eller fysiskt möte", + "link_meeting": "Länka möte", "phone_call": "Telefonsamtal", "phone_number": "Telefonnummer", "enter_phone_number": "Fyll i telefonnummer", @@ -419,6 +445,7 @@ "edit_role": "Redigera roll", "edit_team": "Redigera team", "reject": "Neka", + "reject_all": "Avvisa alla", "accept": "Acceptera", "leave": "Lämna", "profile": "Profil", @@ -443,9 +470,11 @@ "danger_zone": "Riskabel zon", "back": "Tillbaka", "cancel": "Avbryt", + "apply": "Använd", "cancel_event": "Avbryt denna händelse", "continue": "Fortsätt", "confirm": "Bekräfta", + "confirm_all": "Bekräfta alla", "disband_team": "Ta bort teamet", "disband_team_confirmation_message": "Är du säker på att du vill ta bort detta team? Alla som du har delat den här teamlänken med kommer inte längre att kunna boka med den.", "remove_member_confirmation_message": "Är du säker på att du vill ta bort denna medlem från teamet?", @@ -471,6 +500,7 @@ "user_from_team": "{{user}} från {{team}}", "preview": "Förhandsgranska", "link_copied": "Länk kopierad!", + "private_link_copied": "Privat länk kopierad!", "link_shared": "Länk delad!", "title": "Titel", "description": "Beskrivning", @@ -486,6 +516,7 @@ "url": "Webbadress", "hidden": "Dold", "readonly": "Skrivskyddad", + "one_time_link": "Engångslänk", "plan_description": "Du är för närvarande på {{plan}} planen.", "plan_upgrade_invitation": "Uppgradera ditt konto till pro-planen för att låsa upp alla funktioner vi har att erbjuda.", "plan_upgrade": "Du måste uppgradera din plan för att ha mer än en aktiv händelsetyp.", @@ -511,6 +542,18 @@ "language": "Språk", "timezone": "Tidszon", "first_day_of_week": "Första dagen i veckan", + "repeats_up_to": "Upprepas upp till {{count}} gång", + "repeats_up_to_plural": "Upprepas upp till {{count}} gånger", + "every_for_freq": "Varje {{freq}} för", + "repeats_every": "Upprepas varje", + "weekly": "vecka", + "weekly_plural": "veckor", + "monthly": "månad", + "monthly_plural": "månader", + "yearly": "år", + "yearly_plural": "år", + "plus_more": "+ {{count}} till", + "max": "Max", "single_theme": "Ett tema", "brand_color": "Primär färg", "light_brand_color": "Primär färg (ljust tema)", @@ -563,15 +606,24 @@ "type": "Typ", "edit": "Redigera", "add_input": "Lägg till en inmatning", + "disable_notes": "Dölj anteckningar i kalendern", + "disable_notes_description": "Av sekretessskäl kommer ytterligare indata och anteckningar att döljas i kalenderposten. De kommer fortfarande att skickas till din e-post.", "opt_in_booking": "Opt-in bokning", "opt_in_booking_description": "Bokningen måste bekräftas manuellt innan den flyttas till integrationerna och ett bekräftelsemail skickas.", + "recurring_event": "Återkommande möte", + "recurring_event_description": "Personer kan prenumerera på återkommande händelser", + "starting": "Börjar", "disable_guests": "Inaktivera gäster", "disable_guests_description": "Inaktivera möjligheten att lägga till ytterligare gäster vid bokning.", + "private_link": "Generera privat URL", + "copy_private_link": "Kopiera privat länk", + "private_link_description": "Generera en privat URL att dela utan att exponera ditt Cal-användarnamn", "invitees_can_schedule": "Inbjudna kan schemalägga", "date_range": "Datumintervall", "calendar_days": "kalenderdagar", "business_days": "arbetsdagar", "set_address_place": "Ange adress eller plats", + "set_link_meeting": "Ange en länk till mötet", "cal_invitee_phone_number_scheduling": "Cal kommer be den inbjudna att ange ett telefonnummer innan schemaläggning.", "cal_provide_google_meet_location": "Cal kommer att generera en Google Meet länk.", "cal_provide_zoom_meeting_url": "Cal kommer att generera en länk för Zoom-möte.", @@ -579,6 +631,7 @@ "cal_provide_video_meeting_url": "Cal kommer att generera en URL för Daily video-möte.", "cal_provide_jitsi_meeting_url": "Vi kommer att generera en Jitsi Meet URL för dig.", "cal_provide_huddle01_meeting_url": "Cal kommer att generera en URL för Huddle01 web3-möte.", + "cal_provide_teams_meeting_url": "Cal kommer att tillhandahålla en mötes-URL för MS Teams. OBS! MÅSTE HA ETT ARBETS- ELLER SKOLKONTO", "require_payment": "Begär betalning", "commission_per_transaction": "provision per transaktion", "event_type_updated_successfully_description": "Din händelsetyp har uppdaterats.", @@ -604,6 +657,9 @@ "confirm_delete_account": "Ja, radera konto", "delete_account_confirmation_message": "Är du säker på att du vill radera ditt Cal.com konto? Alla som du har delat din kontolänk med kommer inte längre att kunna boka med hjälp av den och eventuella inställningar du har sparat kommer att gå förlorade.", "integrations": "Integrationer", + "apps": "Appar", + "app_store": "App Store", + "app_store_description": "Kopplar samman människor, teknik och arbetsplats.", "settings": "Inställningar", "event_type_moved_successfully": "Händelsetypen har flyttats", "next_step": "Hoppa över steg", @@ -651,6 +707,24 @@ "import_from": "Importera från", "access_token": "Åtkomst-token", "visit_roadmap": "Färdplan", + "popular_categories": "Populära kategorier", + "trending_apps": "Trendande appar", + "all_apps": "Alla appar", + "installed_apps": "Installerade appar", + "manage_your_connected_apps": "Hantera dina installerade appar eller ändra inställningar", + "browse_apps": "Bläddra bland appar", + "features": "Egenskaper", + "permissions": "Behörigheter", + "terms_and_privacy": "Villkor och sekretess", + "published_by": "Publicerad av {{author}}", + "subscribe": "Prenumerera", + "buy": "Köp", + "install_app": "Installera appen", + "categories": "Kategorier", + "pricing": "Priser", + "learn_more": "Läs mer", + "privacy_policy": "Sekretesspolicy", + "terms_of_service": "Användningsvillkor", "remove": "Radera", "add": "Lägg till", "verify_wallet": "Verifiera plånbok", @@ -666,10 +740,84 @@ "prisma_studio_tip_description": "Lär dig hur du konfigurerar din första användare", "contact_sales": "Kontakta försäljning", "error_404": "404 - Sidan kan inte hittas", + "default": "Standard", + "set_to_default": "Ange som standard", + "new_schedule_btn": "Nytt schema", + "add_new_schedule": "Lägg till ett nytt schema", + "delete_schedule": "Ta bort schema", + "schedule_created_successfully": "{{scheduleName}}-schemat har skapats", "availability_updated_successfully": "Tillgängligheten har uppdaterats", + "schedule_deleted_successfully": "Schemat har tagits bort", + "default_schedule_name": "Arbetstider", + "new_schedule_heading": "Skapa ett tillgänglighetsschema", + "new_schedule_description": "Genom att skapa tillgänglighetsscheman kan du hantera tillgänglighet för olika händelsetyper. De kan tillämpas på en eller flera händelsetyper.", "requires_ownership_of_a_token": "Kräver ägande av en token som tillhör följande adress:", "example_name": "Emil Persson", "time_format": "Tidsformat", "12_hour": "12 timmar", - "24_hour": "24 timmar" + "24_hour": "24 timmar", + "redirect_success_booking": "Omdirigera vid bokning ", + "you_are_being_redirected": "Du omdirigeras till {{ url }} om $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "För att kunna använda den här funktionen måste du uppgradera till ett Pro-konto.", + "duplicate": "Duplicera", + "you_can_manage_your_schedules": "Du kan hantera dina scheman på sidan Tillgänglighet.", + "api_keys": "API-nycklar", + "api_key_modal_subtitle": "Med API-nycklar kan du göra API-samtal för ditt eget konto.", + "api_keys_subtitle": "Generera API-nycklar som du använder för att komma åt ditt eget konto.", + "generate_new_api_key": "Skapa en ny API-nyckel", + "create_api_key": "Skapa en API-nyckel", + "personal_note": "Namnge denna nyckel", + "personal_note_placeholder": "T.ex. Utveckling", + "api_key_no_note": "Namnlös API-nyckel", + "api_key_never_expires": "Denna API-nyckel har inget förfallodatum", + "edit_api_key": "Redigera API-nyckel", + "never_expire_key": "Förfaller aldrig", + "delete_api_key": "Återkalla API-nyckel", + "success_api_key_created": "API-nyckeln har skapats", + "success_api_key_edited": "API-nyckeln har uppdaterats", + "create": "Skapa", + "success_api_key_created_bold_tagline": "Spara den här API-nyckeln på en säker plats.", + "you_will_only_view_it_once": "Du kommer inte att kunna se den igen när du stänger denna modal.", + "copy_to_clipboard": "Kopiera till Urklipp", + "enabled_after_update": "Aktiverad efter uppdatering", + "enabled_after_update_description": "Den privata länken kommer att fungera efter att du sparat", + "confirm_delete_api_key": "Återkalla den här API-nyckeln", + "revoke_api_key": "Återkalla API-nyckel", + "api_key_copied": "API-nyckel kopierad!", + "delete_api_key_confirm_title": "Ta bort denna API-nyckel permanent från ditt konto?", + "copy": "Kopiera", + "expire_date": "Förfallodatum", + "expired": "Förfallen", + "never_expires": "Förfaller aldrig", + "expires": "Förfaller", + "request_reschedule_booking": "Begäran om att boka om din bokning", + "reason_for_reschedule": "Anledning till ombokning", + "book_a_new_time": "Boka en ny tid", + "reschedule_request_sent": "Begäran om ombokning skickad", + "reschedule_modal_description": "Detta kommer att avbryta det planerade mötet. Meddela schemaläggaren och be hen att välja en ny tid.", + "reason_for_reschedule_request": "Orsak till begäran om ombokning", + "send_reschedule_request": "Begär ombokning ", + "edit_booking": "Redigera bokning", + "reschedule_booking": "Boka om bokning", + "former_time": "Tidigare tid", + "confirmation_page_gif": "Gif för bekräftelsesida", + "search": "Sök", + "impersonate": "Imitera", + "impersonate_user_tip": "All användning av denna funktion granskas.", + "impersonating_user_warning": "Imiterar användarnamn \"{{user}}\".", + "impersonating_stop_instructions": "<0>Klicka här för att sluta.", + "email_validation_error": "Det ser inte ut som en e-postadress", + "place_where_cal_widget_appear": "Placera denna kod i din HTML där du vill att din Cal-widget ska visas.", + "copy_code": "Kopiera kod", + "code_copied": "Kod kopierad!", + "how_you_want_add_cal_site": "Hur vill du lägga till Cal till din webbplats?", + "choose_ways_put_cal_site": "Välj ett av följande sätt att lägga Cal på din webbplats.", + "setting_up_zapier": "Ställa in din Zapier-integration", + "generate_api_key": "Generera API-nyckel", + "your_unique_api_key": "Din unika API-nyckel", + "copy_safe_api_key": "Kopiera denna API-nyckel och spara den någonstans säkert. Om du förlorar denna nyckel måste du skapa en ny.", + "install_zapier_app": "Installera först Zapier-appen i appbutiken.", + "go_to_app_store": "Gå till App Store", + "calendar_error": "Något gick fel, försök att återansluta din kalender med alla nödvändiga behörigheter" } diff --git a/apps/web/public/static/locales/sv/vital.json b/apps/web/public/static/locales/sv/vital.json new file mode 100644 index 0000000000..76e0ad6b57 --- /dev/null +++ b/apps/web/public/static/locales/sv/vital.json @@ -0,0 +1,13 @@ +{ + "connected_vital_app": "Ansluten med", + "vital_app_sleep_automation": "Omplanera automatisering av viloläge", + "vital_app_automation_description": "Du kan välja olika parametrar för att utlösa tidsplanen baserat på dina sovmått.", + "vital_app_parameter": "Parameter", + "vital_app_trigger": "Utlösare under eller lika med", + "vital_app_save_button": "Spara konfiguration", + "vital_app_total_label": "Totalt (totalt = rem + lätt sömn + djupsömn)", + "vital_app_duration_label": "Varaktighet (varaktighet = sängdags slut - sängdags start)", + "vital_app_hours": "timmar", + "vital_app_save_success": "Dina grundläggande konfigurationer sparades", + "vital_app_save_error": "Ett fel uppstod när dina vitala konfigurationer skulle sparas" +} diff --git a/apps/web/public/static/locales/tr/common.json b/apps/web/public/static/locales/tr/common.json index fbdc8aff99..99b75121af 100644 --- a/apps/web/public/static/locales/tr/common.json +++ b/apps/web/public/static/locales/tr/common.json @@ -2,31 +2,766 @@ "trial_days_left": "PRO denemenizde $t(day, {\"count\": {{days}} }) gün kaldı", "day": "{{count}} gün", "day_plural": "{{count}} gün", + "second": "{{count}} saniye", + "second_plural": "{{count}} saniye", + "upgrade_now": "Şimdi yükselt", + "accept_invitation": "Daveti Kabul Et", + "calcom_explained": "Cal.com, kendi verilerinizin, iş akışınızın ve web görünümünüzün kontrolünü size veren açık kaynaklı Calendly uygulamasının alternatifidir.", + "have_any_questions": "Sorularınız mı var? Size yardımcı olmak için buradayız.", + "reset_password_subject": "Cal.com: Şifre sıfırlama talimatları", + "event_declined_subject": "Reddedildi: {{date}} tarihinde {{name}} ile {{eventType}}", + "event_cancelled_subject": "İptal edildi: {{date}} tarihinde {{name}} ile {{eventType}}", "event_request_declined": "Etkinlik talebiniz reddedildi", "event_request_cancelled": "Planlanan etkinliğiniz iptal edildi", + "organizer": "Organizatör", + "need_to_reschedule_or_cancel": "Yeniden planlamanız veya iptal etmeniz mi gerekiyor?", + "cancellation_reason": "İptal nedeni", + "cancellation_reason_placeholder": "Neden iptal ediyorsunuz? (isteğe bağlı)", + "rejection_reason": "Ret nedeni", + "rejection_reason_title": "Rezervasyon isteği reddedilsin mi?", "rejection_reason_description": "Rezervasyonu reddetmek istediğinizden emin misiniz? Rezervasyon yaptırmaya çalışan kişiye haber vereceğiz. Aşağıda bir neden sunabilirsiniz.", + "rejection_confirmation": "Rezervasyonu reddet", + "manage_this_event": "Bu etkinliği yönet", "your_event_has_been_scheduled": "Etkinliğiniz planlandı", + "accept_our_license": "<1>NEXT_PUBLIC_LICENSE_CONSENT .env değişkenini '{{agree}}' olarak değiştirerek lisansımızı kabul edin.", + "remove_banner_instructions": "Bu banner'ı kaldırmak için lütfen .env dosyanızı açın ve <1>NEXT_PUBLIC_LICENSE_CONSENT değişkenini '{{agree}}' olarak değiştirin.", + "error_message": "Hata mesajı: '{{errorMessage}}'", + "refund_failed_subject": "İade başarısız: {{name}} - {{date}} - {{eventType}}", + "refund_failed": "{{date}} tarihindeki {{userName}} ile {{eventType}} etkinliğinin iadesi başarısız oldu.", + "check_with_provider_and_user": "Lütfen ödeme sağlayıcınızla ve {{user}} ile bunu nasıl ele alınacağınızı kontrol edin.", + "a_refund_failed": "İade başarısız oldu", + "awaiting_payment_subject": "Ödeme Bekleniyor: {{date}} tarihinde {{name}} ile {{eventType}}", + "meeting_awaiting_payment": "Toplantınız için ödeme bekleniyor", "help": "Yardım", "price": "Fiyat", + "paid": "Ödendi", + "refunded": "İade edildi", + "pay_later_instructions": "Daha sonra ödeme yapmak isterseniz bu ödeme bağlantısını da içeren e-postanızı kontrol edebilirsiniz.", + "payment": "Ödeme", + "missing_card_fields": "Kart alanları eksik", + "pay_now": "Şimdi öde", + "codebase_has_to_stay_opensource": "Kod temeli, değiştirilmiş olsun ya da olmasın açık kaynak olarak kalmalıdır", + "cannot_repackage_codebase": "Kod temelini yeniden paketleyemez veya satamazsınız", + "acquire_license": "E-posta göndererek bu şartları kaldırmak için ticari bir lisans edinin", + "terms_summary": "Koşulların özeti", + "open_env": ".env dosyasını açın ve Lisansımızı kabul edin", + "env_changed": ".env dosyamı değiştirdim", + "accept_license": "Lisansı Kabul Et", + "still_waiting_for_approval": "Bir etkinlik hâlâ onay bekliyor", + "event_is_still_waiting": "Etkinlik isteği hâlâ beklemede: {{attendeeName}} - {{date}} - {{eventType}}", + "no_more_results": "Başka sonuç yok", + "load_more_results": "Daha fazla sonuç yükle", + "integration_meeting_id": "{{integrationName}} toplantı kimliği: {{meetingId}}", + "confirmed_event_type_subject": "Onaylandı: {{date}} tarihinde {{name}} ile {{eventType}}", + "new_event_request": "Yeni etkinlik isteği: {{attendeeName}} - {{date}} - {{eventType}}", + "confirm_or_reject_request": "İsteği onaylayın veya reddedin", "check_bookings_page_to_confirm_or_reject": "Rezervasyonu onaylamak veya reddetmek için rezervasyon sayfanızı kontrol edin.", + "event_awaiting_approval": "Bir etkinlik onayınızı bekliyor", + "someone_requested_an_event": "Birisi takviminizde bir etkinlik planlamak istiyor.", + "someone_requested_password_reset": "Biri şifrenizi değiştirmek için bir bağlantı talebinde bulundu.", + "password_reset_instructions": "Böyle bir talepte bulunmadıysanız bu e-postayı güvenle yok sayabilirsiniz. Şifreniz değiştirilmeyecektir.", + "event_awaiting_approval_subject": "Onay Bekleniyor: {{date}} tarihinde {{name}} ile {{eventType}}", + "event_still_awaiting_approval": "Bir etkinlik hâlâ onayınızı bekliyor", + "booking_submitted_subject": "Rezervasyon Gönderildi: {{date}} tarihinde {{name}} ile {{eventType}}", + "your_meeting_has_been_booked": "Toplantı rezervasyonunuz yapıldı", + "event_type_has_been_rescheduled_on_time_date": "{{name}} ile {{eventType}} toplantınız {{date}} tarihinde {{time}} ({{timeZone}}) olarak yeniden planlandı.", + "event_has_been_rescheduled": "Güncellendi - Etkinliğiniz yeniden planlandı", + "request_reschedule_title_attendee": "Rezervasyonunuz için yeniden planlama isteğinde bulunun", + "request_reschedule_subtitle": "{{organizer}} rezervasyonu iptal etti ve sizden başka bir saat seçmenizi istiyor.", + "request_reschedule_title_organizer": "{{attendee}} adlı kişiye yeniden planlama talebinde bulundunuz", + "request_reschedule_subtitle_organizer": "Rezervasyonu iptal ettiniz ve {{attendee}} isimli kişinin sizinle yeni bir rezervasyon saati belirlemesi gerekiyor.", + "reschedule_reason": "Yeniden planlama nedeni", + "hi_user_name": "Merhaba {{name}}", + "ics_event_title": "{{name}} ile {{eventType}}", + "new_event_subject": "Yeni etkinlik: {{attendeeName}} - {{date}} - {{eventType}}", + "join_by_entrypoint": "{{entryPoint}} ile katıl", + "notes": "Notlar", + "manage_my_bookings": "Rezervasyonlarımı yönet", + "need_to_make_a_change": "Değişiklik mi yapmanız gerekiyor?", + "new_event_scheduled": "Yeni bir etkinlik planlandı.", + "invitee_email": "Davet Edilenin E-postası", + "invitee_timezone": "Davet Edilenin Zaman Dilimi", "event_type": "Etkinlik Tipi", + "enter_meeting": "Toplantıya Gir", + "video_call_provider": "Görüntülü arama sağlayıcısı", + "meeting_id": "Toplantı Kimliği", + "meeting_password": "Toplantı Şifresi", + "meeting_url": "Toplantı URL'si", + "meeting_request_rejected": "Toplantı isteğiniz reddedildi", + "rescheduled_event_type_subject": "Yeniden Planlandı: {{date}} tarihinde {{name}} ile {{eventType}}", + "requested_to_reschedule_subject_attendee": "Yeniden Planlama İşlemi Gerekiyor: Lütfen {{name}} ile {{eventType}} için yeni bir saat belirleyin", + "rejected_event_type_with_organizer": "Reddedildi: {{date}} tarihinde {{organizer}} ile {{eventType}}", + "hi": "Merhaba", + "join_team": "Ekibe katıl", + "manage_this_team": "Bu ekibi yönet", + "team_info": "Ekip Bilgileri", + "request_another_invitation_email": "Cal.com e-posta adresiniz olarak {{toEmail}} adresini kullanmak istemiyorsanız veya zaten bir Cal.com hesabınız varsa lütfen bu e-posta için başka bir davet talebinde bulunun.", + "you_have_been_invited": "{{teamName}} ekibine katılmaya davet edildiniz", + "user_invited_you": "{{user}}, sizi Cal.com'da {{team}} ekibine katılmaya davet etti", + "hidden_team_member_title": "Bu ekipte gizlisiniz", + "hidden_team_member_message": "Yeriniz ücretli değil. Hesabınızı Pro'ya yükseltin veya ekip yöneticisinden yeriniz için ödeme yapmasını isteyin.", + "hidden_team_owner_message": "Ekipler özelliğini kullanmak için bir Pro hesabına ihtiyacınız var. Hesabınızı yükseltene kadar gizli olarak kalacaksınız.", + "link_expires": "Not: Süre {{expiresIn}} saat içinde dolacak.", + "upgrade_to_per_seat": "Koltuk Başına Yükselt", + "team_upgrade_seats_details": "Ekibinizdeki {{memberCount}} üyeden {{unpaidCount}} yer ücretsiz. Alan başına aylık {{seatPrice}} $ ile üyeliğinizin tahmini toplam maliyeti aylık {{totalCost}} $'dır.", + "team_upgraded_successfully": "Ekibiniz başarıyla yükseltildi!", + "use_link_to_reset_password": "Şifrenizi sıfırlamak için aşağıdaki bağlantıyı kullanın", + "hey_there": "Selam,", + "forgot_your_password_calcom": "Şifrenizi mi unuttunuz? - Cal.com", + "event_type_title": "{{eventTypeTitle}} | Etkinlik Türü", "delete_webhook_confirmation_message": "Bu web kancasını silmek istediğinizden emin misiniz? Bir etkinlik planlandığında veya iptal edildiğinde, Cal.com toplantı verilerini gerçek zamanlı olarak belirli bir URL'de almazsınız.", + "confirm_delete_webhook": "Evet, web kancasını sil", + "edit_webhook": "Web kancasını düzenle", + "delete_webhook": "Web kancasını sil", + "webhook_status": "Web Kancası Durumu", + "webhook_enabled": "Web Kancası Etkin", + "webhook_disabled": "Web Kancası Devre Dışı", + "webhook_response": "Web kancası yanıtı", + "webhook_test": "Web kancası testi", + "manage_your_webhook": "Web kancanızı yönetin", + "webhook_created_successfully": "Web kancası başarıyla oluşturuldu!", + "webhook_updated_successfully": "Web kancası başarıyla güncellendi!", + "webhook_removed_successfully": "Web kancası başarıyla kaldırıldı!", + "payload_template": "Veri Şablonu", + "dismiss": "Reddet", + "no_data_yet": "Henüz veri yok", + "ping_test": "Ping testi", + "add_to_homescreen": "Daha hızlı erişim ve daha iyi bir deneyim için bu uygulamayı ana ekranınıza ekleyin.", + "upcoming": "Yaklaşan", + "past": "Geçmiş", + "choose_a_file": "Bir dosya seçin...", + "upload_image": "Resim yükle", + "upload_target": "{{target}} yükle", + "no_target": "{{target}} yok", + "slide_zoom_drag_instructions": "Yakınlaştırmak için kaydırın, yeniden konumlandırmak için sürükleyin", + "view_notifications": "Bildirimleri göster", + "view_public_page": "Herkese açık sayfayı görüntüle", + "sign_out": "Çıkış yap", + "add_another": "Başka bir tane ekle", + "until": "Şu tarihe kadar:", + "powered_by": "Destekleyen:", + "unavailable": "Uygun değil", + "set_work_schedule": "Çalışma planınızı ayarlayın", + "change_bookings_availability": "Rezervasyon için uygun olduğunuz zamanı değiştirin", + "select": "Seç...", + "2fa_confirm_current_password": "Başlamak için mevcut şifrenizi onaylayın.", + "2fa_scan_image_or_use_code": "Aşağıdaki resmi telefonunuzdaki kimlik doğrulama uygulamasını kullanarak tarayın veya bunun yerine metin kodunu manuel olarak girin.", + "text": "Metin", + "multiline_text": "Çok Satırlı Metin", + "number": "Sayı", + "checkbox": "Onay kutusu", + "is_required": "Gereklidir", + "required": "Gerekli", + "input_type": "Giriş türü", "rejected": "Reddedildi", + "unconfirmed": "Onaylanmadı", "guests": "Davetliler", "guest": "Davetli", + "web_conferencing_details_to_follow": "Onay e-postasından izlenecek web konferansı ayrıntıları.", + "the_username": "Kullanıcı adı", + "username": "Kullanıcı adı", + "is_still_available": "hâlâ müsait.", + "documentation": "Belgeler", + "documentation_description": "Araçlarımızı uygulamanızla nasıl entegre edeceğinizi öğrenin", + "api_reference": "API Referansı", + "api_reference_description": "Kütüphanemiz için kapsamlı bir API referansı", "blog": "Blog", + "blog_description": "En yeni haber ve makalelerimizi okuyun", + "join_our_community": "Topluluğumuza katılın", + "join_our_slack": "Slack'imize katılın", + "claim_username_and_schedule_events": "Kullanıcı adınızı alın ve etkinlikler planlayın", + "popular_pages": "Popüler sayfalar", + "register_now": "Hemen kaydol", + "register": "Kaydol", + "page_doesnt_exist": "Bu sayfa mevcut değil.", + "check_spelling_mistakes_or_go_back": "Doğru yazıp yazmadığınızı kontrol edin veya önceki sayfaya dönün.", + "404_page_not_found": "404: Bu sayfa bulunamadı.", + "getting_started": "Başlarken", + "15min_meeting": "15 Dakikalık Toplantı", + "30min_meeting": "30 Dakikalık Toplantı", + "secret_meeting": "Gizli Toplantı", + "login_instead": "Ya da giriş yapın", + "already_have_an_account": "Zaten bir hesabınız var mı?", + "create_account": "Hesap Oluştur", + "confirm_password": "Şifreyi onayla", + "create_your_account": "Hesabınızı oluşturun", + "sign_up": "Kaydol", + "youve_been_logged_out": "Çıkış yaptınız", + "hope_to_see_you_soon": "En kısa sürede tekrar görüşmek dileğiyle!", + "logged_out": "Çıkış yapıldı", + "please_try_again_and_contact_us": "Lütfen tekrar deneyin ve sorunun devam etmesi durumunda bizimle iletişime geçin.", + "incorrect_2fa_code": "İki adımlı kod yanlış.", + "no_account_exists": "Bu e-posta adresiyle eşleşen bir hesap yok.", + "2fa_enabled_instructions": "İki adımlı kimlik doğrulama etkinleştirildi. Lütfen kimlik doğrulama uygulamanızdaki altı haneli kodu girin.", + "2fa_enter_six_digit_code": "Kimlik doğrulama uygulamanızdaki altı haneli kodu aşağıya girin.", + "create_an_account": "Hesap oluştur", + "dont_have_an_account": "Hesabınız yok mu?", + "2fa_code": "İki Adımlı Kod", + "sign_in_account": "Hesabınıza giriş yapın", + "sign_in": "Giriş yap", + "go_back_login": "Giriş sayfasına geri dön", + "error_during_login": "Giriş yapılırken bir hata oluştu. Giriş ekranına geri dönün ve tekrar deneyin.", + "request_password_reset": "Şifre Sıfırlama İsteği Gönderin", + "forgot_password": "Şifremi Unuttum", + "forgot": "Unuttunuz mu?", + "done": "Tamam", + "check_email_reset_password": "E-posta adresinizi kontrol edin. Size şifrenizi sıfırlamanız için bir bağlantı göndereceğiz.", + "finish": "Bitir", + "few_sentences_about_yourself": "Birkaç cümleyle kendinizden bahsedin. Bu, kişisel URL sayfanızda görünecektir.", + "nearly_there": "Neredeyse bitti", + "nearly_there_instructions": "Son bir şey: Kendiniz hakkında kısa bir açıklama yazmak ve bir resim koymak, rezervasyon almanıza ve insanların gerçekte kiminle rezervasyon yaptıklarını bilmelerine yardımcı olur.", + "set_availability_instructions": "Sürekli olarak müsait olduğunuz zaman aralıklarını tanımlayın. Daha sonra başka zaman aralıkları oluşturabilir ve bunları farklı takvimlere atayabilirsiniz.", + "set_availability": "Müsaitlik durumunuzu ayarlayın", + "continue_without_calendar": "Takvim olmadan devam et", "connect_your_calendar": "Takviminizi bağlayın", "connect_your_calendar_instructions": "Yeni etkinlikler planlanırken dolu zamanları otomatik olarak kontrol etmek için takviminizi bağlayın.", + "set_up_later": "Daha sonra ayarla", + "current_time": "Geçerli saat", + "welcome": "Hoş geldiniz", + "welcome_to_calcom": "Cal.com'a hoş geldiniz", + "welcome_instructions": "Bize adınızı ve hangi saat diliminde olduğunuzu söyleyin. Bu bilgileri daha sonra düzenleyebilirsiniz.", + "connect_caldav": "CalDav Sunucusuna Bağlan", + "credentials_stored_and_encrypted": "Kimlik bilgileriniz saklanacak ve şifrelenecektir.", + "connect": "Bağlan", + "try_for_free": "Ücretsiz deneyin", + "create_booking_link_with_calcom": "Cal.com ile kendi rezervasyon bağlantınızı oluşturun", "who": "Kim", + "what": "Ne", + "when": "Ne zaman", + "where": "Nerede", + "add_to_calendar": "Takvime ekle", + "other": "Diğer", + "emailed_you_and_attendees": "Size ve diğer katılımcılara tüm ayrıntıları içeren bir takvim daveti e-postası gönderdik.", + "emailed_you_and_any_other_attendees": "Bu bilgiler size ve diğer katılımcılara e-posta ile gönderildi.", + "needs_to_be_confirmed_or_rejected": "Rezervasyonunuzun hâlâ onaylanması veya reddedilmesi gerekiyor.", + "user_needs_to_confirm_or_reject_booking": "{{user}} isimli kullanıcının hâlâ rezervasyonu onaylaması veya reddetmesi gerekiyor.", + "meeting_is_scheduled": "Toplantı planlandı", + "submitted": "Rezervasyonunuz gönderildi", + "booking_submitted": "Rezervasyonunuz gönderildi", + "booking_confirmed": "Rezervasyonunuz onaylandı", + "enter_new_password": "Hesabınız için istediğiniz yeni şifreyi girin.", + "reset_password": "Şifreyi Sıfırla", + "change_your_password": "Şifrenizi değiştirin", + "try_again": "Tekrar Deneyin", + "request_is_expired": "İsteğin Süresi Doldu.", + "reset_instructions": "Hesabınızla ilişkilendirilen e-posta adresinizi girin. Size şifrenizi sıfırlamanız için bir bağlantı göndereceğiz.", + "request_is_expired_instructions": "Bu istek zaman aşımına uğradı. Geri dönün ve hesabınızla ilişkili e-posta adresini girin. Şifrenizi sıfırlamanız için size başka bir bağlantı göndereceğiz.", "whoops": "Hoppala", + "login": "Giriş", + "success": "Başarılı", + "failed": "Başarısız", + "password_has_been_reset_login": "Şifreniz sıfırlandı. Artık yeni oluşturulan şifrenizle giriş yapabilirsiniz.", + "unexpected_error_try_again": "Beklenmeyen bir hata oluştu. Tekrar deneyin.", + "sunday_time_error": "Pazar günü için geçersiz saat", + "monday_time_error": "Pazartesi günü için geçersiz saat", + "tuesday_time_error": "Salı günü için geçersiz saat", + "wednesday_time_error": "Çarşamba günü için geçersiz saat", + "thursday_time_error": "Perşembe günü için geçersiz saat", + "friday_time_error": "Cuma günü için geçersiz saat", + "saturday_time_error": "Cumartesi günü için geçersiz saat", + "error_end_time_before_start_time": "Bitiş zamanı, başlangıç ​​zamanından önce olamaz", + "error_end_time_next_day": "Bitiş zamanı 24 saatten fazla olamaz", "back_to_bookings": "Rezervasyonlara dön", + "free_to_pick_another_event_type": "İstediğiniz zaman başka bir etkinlik seçmekten çekinmeyin.", "cancelled": "İptal edildi", + "cancellation_successful": "İptal işlemi başarılı", + "really_cancel_booking": "Rezervasyonunuz gerçekten iptal edilsin mi?", + "cannot_cancel_booking": "Bu rezervasyonu iptal edemezsiniz", + "reschedule_instead": "Bunun yerine, yeniden planlayabilirsiniz.", + "event_is_in_the_past": "Etkinlik geçmişte kaldı", + "error_with_status_code_occured": "{{status}} durum koduyla ilgili bir hata oluştu.", + "booking_already_cancelled": "Bu rezervasyon zaten iptal edildi", + "go_back_home": "Ana sayfaya geri dönün", + "or_go_back_home": "Veya ana sayfaya geri dönün", + "no_availability": "Uygun değil", + "no_meeting_found": "Toplantı Bulunamadı", + "no_meeting_found_description": "Bu toplantı mevcut değil. Güncel bir bağlantı için toplantı sahibiyle iletişime geçin.", + "no_status_bookings_yet": "Henüz {{status}} rezervasyonu yok", "no_status_bookings_yet_description": "{{status}} durumunda olan bir rezervasyon bulanamadı. {{description}}", + "event_between_users": "{{host}} ve {{attendeeName}} arasındaki {{eventName}}", "bookings": "Rezervasyonlar", + "bookings_description": "Etkinlik türleri bağlantılarımızla rezervasyon yaptığınız yaklaşan ve geçmiş etkinlikleri görün.", + "upcoming_bookings": "Biri sizinle bir zaman dilimini rezerve ettiğinde bilgiler burada görünür.", "past_bookings": "Geçmiş rezervasyonlarınız burada görünecektir.", + "cancelled_bookings": "İptal ettiğiniz rezervasyonlarınız burada görünecektir.", + "on": "/", + "and": "ve", + "calendar_shows_busy_between": "Takviminiz sizi şu saatler arasında meşgul olarak gösteriyor:", + "troubleshoot": "Sorun giderme", + "troubleshoot_description": "Belirli saatlerin neden uygun olduğunu ve diğerlerinin neden engellendiğini anlayın.", + "overview_of_day": "Şu tarihli gününüze genel bir bakış:", + "hover_over_bold_times_tip": "İpucu: Tam bir zaman damgası için koyu renkli zamanların üzerine gelin", + "start_time": "Başlangıç ​​saati", + "end_time": "Bitiş saati", + "buffer_time": "Ara süre", + "before_event": "Etkinlikten önce", + "after_event": "Etkinlikten sonra", + "event_buffer_default": "Ara süre yok", + "buffer": "Ara süre", + "your_day_starts_at": "Günününüz başlıyor:", + "your_day_ends_at": "Gününüz sona eriyor:", + "launch_troubleshooter": "Sorun gidericiyi başlat", + "troubleshoot_availability": "Neden böyle gösterildiğini keşfetmek için müsaitlik durumunuzla ilgili sorunları giderin.", + "change_available_times": "Müsaitlik saatlerini değiştir", + "change_your_available_times": "Müsait olduğunuz saatleri değiştirin", + "change_start_end": "Gününüzün başlangıç ​​ve bitiş saatlerini değiştirin", + "change_start_end_buffer": "Gününüzün başlangıç ​​ve bitiş saatini ve toplantılarınız arasında minimum bir süre belirleyin.", + "current_start_date": "Şu anda gününüz şu saatte başlayacak", + "start_end_changed_successfully": "Gününüzün başlangıç ​​ve bitiş saatleri başarıyla değiştirildi.", + "and_end_at": "ve şu saatte bitecek şekilde ayarlı:", + "light": "Açık", + "dark": "Koyu", + "automatically_adjust_theme": "Davetli tercihlerine göre temayı otomatik olarak ayarlayın", + "user_dynamic_booking_disabled": "Gruptaki kullanıcılardan bazıları şu anda dinamik grup rezervasyonlarını devre dışı bıraktı", + "allow_dynamic_booking_tooltip": "\"+\" işareti ile birden çok kullanıcı adı eklenerek dinamik olarak oluşturulabilen grup rezervasyon bağlantıları. Örnek: \"cal.com/bailey+peer\"", + "allow_dynamic_booking": "Katılımcıların dinamik grup rezervasyonları aracılığıyla sizin adınıza rezervasyon yapmalarına izin verin", + "email": "E-posta", + "email_placeholder": "derya@example.com", + "full_name": "Tam ad", + "browse_api_documentation": "API belgelerimize göz atın", + "leverage_our_api": "Tam kontrol ve özelleştirilebilirlik için API'mizden yararlanın.", + "create_webhook": "Web kancası oluştur", "booking_cancelled": "Rezervasyon iptal edildi", + "booking_rescheduled": "Rezervasyon Yeniden Planlandı", + "booking_created": "Rezervasyon Oluşturuldu", + "event_triggers": "Etkinlik Tetikleyicileri", + "subscriber_url": "Abone URL'si", + "create_new_webhook": "Yeni bir web kancası oluştur", + "webhooks": "Web kancaları", + "team_webhooks": "Ekip Web Kancaları", + "create_new_webhook_to_account": "Hesabınız için yeni bir web kancası oluşturun", + "new_webhook": "Yeni Web Kancası", "receive_cal_meeting_data": "Bir etkinlik planlandığında veya iptal edildiğinde, Cal toplantı verilerini gerçek zamanlı olarak hedef bir URL'de alın.", + "receive_cal_event_meeting_data": "Bu etkinlik planlandığında veya iptal edildiğinde, Cal toplantı verilerini gerçek zamanlı olarak hedef bir URL'de alın.", + "responsive_fullscreen_iframe": "Duyarlı tam ekran iframe", + "loading": "Yükleniyor...", + "standard_iframe": "Standart iframe", + "iframe_embed": "iframe Yerleştirme", + "embed_calcom": "Cal.com'u web sitenize yerleştirmenin en kolay yolu.", + "integrate_using_embed_or_webhooks": "Yerleştirme seçeneklerimizi kullanarak web sitenize entegre edin veya özel web kancalarını kullanarak gerçek zamanlı rezervasyon bilgileri alın.", + "schedule_a_meeting": "Toplantı planla", + "view_and_manage_billing_details": "Fatura bilgilerinizi görüntüleyin ve yönetin", + "view_and_edit_billing_details": "Fatura bilgilerinizi görüntüleyin ve düzenleyin, ayrıca aboneliğinizi iptal edin.", + "go_to_billing_portal": "Fatura portalına git", + "need_anything_else": "Başka bir şeye ihtiyacınız var mı?", + "further_billing_help": "Faturalandırma konusunda daha fazla yardıma ihtiyacınız olursa destek ekibimiz size yardımcı olmaya hazır.", + "contact_our_support_team": "Destek ekibimizle iletişime geçin", + "uh_oh": "Hay aksi!", + "no_event_types_have_been_setup": "Bu kullanıcı henüz etkinlik türü ayarlamadı.", + "edit_logo": "Logoyu düzenle", + "upload_a_logo": "Logo yükle", + "remove_logo": "Logoyu kaldır", + "enable": "Etkinleştir", + "code": "Kod", + "code_is_incorrect": "Kod hatalı.", + "add_an_extra_layer_of_security": "Şifrenizin çalınmasına karşı hesabınıza ekstra bir güvenlik katmanı ekleyin.", + "2fa": "İki Adımlı Kimlik Doğrulama", + "enable_2fa": "İki adımlı kimlik doğrulamayı etkinleştir", + "disable_2fa": "İki adımlı kimlik doğrulamayı devre dışı bırak", + "disable_2fa_recommendation": "İki adımlı kimlik doğrulamayı devre dışı bırakmanız gerekiyorsa mümkün olan en kısa sürede yeniden etkinleştirmenizi öneririz.", + "error_disabling_2fa": "İki adımlı kimlik doğrulama devre dışı bırakılırken bir hata oluştu", + "error_enabling_2fa": "İki adımlı kimlik doğrulama ayarlanırken bir hata oluştu", + "security": "Güvenlik", + "manage_account_security": "Hesabınızın güvenliğini yönetin.", + "password": "Şifre", + "password_updated_successfully": "Şifre başarıyla güncellendi", + "password_has_been_changed": "Şifreniz başarıyla değiştirildi.", + "error_changing_password": "Şifre değiştirilirken bir hata oluştu", + "something_went_wrong": "Bir şeyler ters gitti.", + "something_doesnt_look_right": "Doğru görünmeyen bir şey mi var?", + "please_try_again": "Lütfen tekrar deneyin.", + "super_secure_new_password": "Süper güvenli yeni şifreniz", + "new_password": "Yeni Şifre", + "your_old_password": "Eski Şifreniz", + "current_password": "Mevcut Şifreniz", + "change_password": "Şifreyi Değiştir", + "new_password_matches_old_password": "Yeni şifre eski şifrenizle aynı. Lütfen farklı bir şifre seçin.", + "current_incorrect_password": "Mevcut şifre hatalı", + "incorrect_password": "Şifre hatalı.", + "1_on_1": "Bire bir", + "24_h": "24 sa", + "use_setting": "Ayarı kullan", + "am_pm": "öö/ös", + "time_options": "Zaman seçenekleri", + "january": "Ocak", + "february": "Şubat", + "march": "Mart", + "april": "Nisan", + "may": "Mayıs", + "june": "Haziran", + "july": "Temmuz", + "august": "Ağustos", + "september": "Eylül", + "october": "Ekim", + "november": "Kasım", + "december": "Aralık", + "monday": "Pazartesi", + "tuesday": "Salı", + "wednesday": "Çarşamba", + "thursday": "Perşembe", + "friday": "Cuma", + "saturday": "Cumartesi", + "sunday": "Pazar", + "all_booked_today": "Bugün tüm zaman aralıkları rezerve edildi.", + "slots_load_fail": "Kullanılabilir zaman aralıkları yüklenemedi.", + "additional_guests": "+ Ek Misafirler", + "your_name": "Adınız", + "email_address": "E-posta adresi", + "location": "Konum", + "yes": "evet", + "no": "hayır", + "additional_notes": "Ek notlar", + "booking_fail": "Toplantı rezervasyonu yapılamadı.", + "reschedule_fail": "Toplantı yeniden planlanamadı.", + "share_additional_notes": "Lütfen toplantımıza hazırlanmamıza yardımcı olacak her şeyi paylaşın.", + "booking_confirmation": "{{profileName}} ile {{eventTypeTitle}} etkinliğinizi onaylayın", + "booking_reschedule_confirmation": "{{profileName}} ile {{eventTypeTitle}} etkinliğinizi yeniden planlayın", + "in_person_meeting": "Yüz yüze görüşme", + "link_meeting": "Bağlantılı toplantı", + "phone_call": "Telefon görüşmesi", + "phone_number": "Telefon Numarası", + "enter_phone_number": "Telefon numarası girin", + "reschedule": "Yeniden planla", + "reschedule_this": "Bunun yerine yeniden planlayın", + "book_a_team_member": "Bunun yerine bir ekip üyesi için rezevasyon yapın", + "or": "VEYA", + "go_back": "Geri dönün", + "email_or_username": "E-posta ya da kullanıcı adı", + "send_invite_email": "Davet e-postası gönder", + "role": "Rol", + "edit_role": "Rolü Düzenle", + "edit_team": "Ekibi düzenle", + "reject": "Reddet", + "accept": "Kabul et", + "leave": "Ayrıl", + "profile": "Profil", + "my_team_url": "Ekibimin URL'si", + "team_name": "Ekip adı", + "your_team_name": "Ekibinizin adı", + "team_updated_successfully": "Ekip başarıyla güncellendi", + "your_team_updated_successfully": "Ekibiniz başarıyla güncellendi.", + "about": "Hakkında", + "team_description": "Birkaç cümleyle ekibinizden bahsedin. Bu, ekibinizin URL sayfasında görünecektir.", + "members": "Üyeler", + "member": "Üye", + "owner": "Sahibi", + "admin": "Yönetici", + "new_member": "Yeni Üye", + "invite": "Davet Et", + "invite_new_member": "Yeni bir üye davet et", + "invite_new_team_member": "Ekibinize birini davet edin.", + "change_member_role": "Ekip üyesi rolünü değiştir", + "disable_cal_branding": "Cal.com markasını devre dışı bırak", + "disable_cal_branding_description": "Herkese açık sayfalarınızdan tüm Cal.com markalarını gizleyin.", + "danger_zone": "Tehlikeli Bölge", + "back": "Geri", "cancel": "İptal", + "apply": "Uygula", + "cancel_event": "Bu etkinliği iptal et", + "continue": "Devam et", + "confirm": "Onayla", + "disband_team": "Ekibi Dağıt", + "disband_team_confirmation_message": "Bu ekibi dağıtmak istediğinizden emin misiniz? Bu ekip bağlantısını paylaştığınız herkes artık bu bağlantıyı kullanarak rezervasyon yapamayacaktır.", + "remove_member_confirmation_message": "Bu üyeyi ekipten çıkarmak istediğinizden emin misiniz?", + "confirm_disband_team": "Evet, ekibi dağıt", + "confirm_remove_member": "Evet, üyeyi kaldır", + "remove_member": "Üyeyi kaldır", + "manage_your_team": "Ekibinizi yönetin", + "no_teams": "Henüz hiç ekibiniz yok.", + "no_teams_description": "Ekipler, iş arkadaşlarınız arasında paylaşılan etkinlik rezervasyonları için diğer kişilere izin verir.", + "submit": "Gönder", + "delete": "Sil", + "update": "Güncelle", + "save": "Kaydet", + "pending": "Bekliyor", + "open_options": "Seçenekleri aç", + "copy_link": "Etkinlik bağlantısını kopyala", + "share": "Paylaş", + "share_event": "Cal hesabım üzerinden rezervasyon yapmak veya bana bağlantınızı göndermek ister misiniz?", + "copy_link_team": "Ekip bağlantısını kopyala", + "leave_team": "Ekipten ayrıl", + "confirm_leave_team": "Evet, ekipten ayrıl", + "leave_team_confirmation_message": "Bu ekipten ayrılmak istediğinizden emin misiniz? Artık bu ekibi kullanarak rezervasyon yapamayacaksınız.", + "user_from_team": "{{team}} ekibinden {{user}}", + "preview": "Ön izle", + "link_copied": "Bağlantı kopyalandı!", + "link_shared": "Bağlantı paylaşıldı!", + "title": "Başlık", + "description": "Açıklama", + "quick_video_meeting": "Hızlı bir görüntülü toplantı.", + "scheduling_type": "Planlama Türü", + "preview_team": "Ekibi ön izle", + "collective": "Toplu", + "collective_description": "Tüm seçili ekip üyeleri müsait olduğunda toplantıları planlayın.", + "duration": "Süre", + "minutes": "Dakika", + "round_robin": "Round Robin", + "round_robin_description": "Toplantıları birden fazla ekip üyesi arasında döndürün.", + "url": "URL", + "hidden": "Gizli", + "readonly": "Salt okunur", + "plan_description": "Şu anda {{plan}} planındasınız.", + "plan_upgrade_invitation": "Sunduğumuz tüm özelliklerin kilidini açmak için hesabınızı profesyonel plana yükseltin.", + "plan_upgrade": "Birden fazla aktif etkinlik türüne sahip olmak için planınızı yükseltmeniz gerekiyor.", + "plan_upgrade_teams": "Bir ekip oluşturmak için planınızı yükseltmeniz gerekiyor.", + "plan_upgrade_instructions": "<1>Buradan yükseltebilirsiniz.", + "event_types_page_title": "Etkinlik Türleri", + "event_types_page_subtitle": "Takviminizde rezervasyon yapacak kişilerle paylaşmak üzere etkinlikler oluşturun.", + "new_event_type_btn": "Yeni etkinlik türü", + "new_event_type_heading": "İlk etkinlik türünüzü oluşturun", + "new_event_type_description": "Etkinlik türleri, takviminizde müsait zamanları gösteren bağlantıları paylaşmanıza ve kişilerin sizinle rezervasyon yapmalarına olanak tanır.", + "new_event_title": "Yeni bir etkinlik türü ekle", + "new_event_subtitle": "Adınızla veya bir ekip altında etkinlik türü oluşturun.", + "new_team_event": "Yeni bir ekip ekinliği türü ekle", + "new_event_description": "İnsanların rezervasyon yapabileceği yeni bir etkinlik türü oluşturun.", + "event_type_created_successfully": "{{eventTypeTitle}} etkinlik türü başarıyla oluşturuldu", + "event_type_updated_successfully": "{{eventTypeTitle}} etkinlik türü başarıyla güncellendi", + "event_type_deleted_successfully": "Etkinlik türü başarıyla silindi", + "web3_metamask_added": "Metamask başarıyla eklendi", + "web3_metamask_disconnected": "Metamask bağlantısı başarıyla kesildi", + "hours": "Saat", + "your_email": "E-postanız", + "change_avatar": "Avatarı değiştir", + "language": "Dil", + "timezone": "Saat dilimi", + "first_day_of_week": "Haftanın İlk Günü", + "single_theme": "Tek Tema", + "brand_color": "Marka Rengi", + "light_brand_color": "Marka Rengi (Açık Tema)", + "dark_brand_color": "Marka Rengi (Koyu Tema)", + "file_not_named": "Dosya adlandırılmamış [idOrSlug]/[user]", + "create_team": "Ekip Oluştur", + "name": "Ad", + "create_new_team_description": "Kullanıcılarla iş birliği yapmak için yeni bir ekip oluşturun.", + "create_new_team": "Yeni ekip oluştur", + "open_invitations": "Açık Davetler", + "new_team": "Yeni Ekip", + "create_first_team_and_invite_others": "İlk ekibinizi oluşturun ve diğer kullanıcıları sizinle birlikte çalışmaya davet edin.", + "create_team_to_get_started": "Başlamak için bir ekip oluşturun", + "teams": "Ekipler", + "team_billing": "Ekip Faturası", + "upgrade_to_flexible_pro_title": "Ekipler için faturayı değiştirdik", + "upgrade_to_flexible_pro_message": "Ekibinizde alanı olmayan üyeler var. Eksik alanları doldurmak için profesyonel plana yükseltin.", + "changed_team_billing_info": "Ocak 2022'den itibaren her ekip üyesi alanı için ücret alıyoruz. Ücretsiz PRO'ya sahip olan ekip üyeleriniz şu anda 14 günlük deneme süresinde. Deneme süreleri sona erdiğinde ve yükseltme yapmadığınızda bu üyeler ekibinizden gizlenecektir.", + "create_manage_teams_collaborative": "İş birliği özelliklerini kullanmak için ekipler oluşturun ve yönetin.", + "only_available_on_pro_plan": "Bu özellik yalnızca Pro planında mevcuttur", + "remove_cal_branding_description": "Cal markasını rezervasyon sayfalarınızdan kaldırmak için bir Pro hesabına yükseltmeniz gerekir.", + "edit_profile_info_description": "Planlama bağlantınızda görünecek olan profil bilgilerinizi düzenleyin.", + "change_email_tip": "Değişikliğin geçerli olduğunu görmek için oturumu kapatıp tekrar açmanız gerekebilir.", + "little_something_about": "Biraz kendinizden bahsedin.", + "profile_updated_successfully": "Profil başarıyla güncellendi", + "your_user_profile_updated_successfully": "Kullanıcı profiliniz başarıyla güncellendi.", + "user_cannot_found_db": "Kullanıcı oturum açmış olarak görünüyor ancak veri tabanında bulunamadı", + "embed_and_webhooks": "Yerleştirme ve Web Kancaları", + "enabled": "Etkinleştirildi", + "disabled": "Devre dışı bırakıldı", + "disable": "Devre dışı", + "billing": "Fatura", + "manage_your_billing_info": "Fatura bilgilerinizi yönetin ve aboneliğinizi iptal edin.", + "availability": "Müsaitlik", + "configure_availability": "Rezervasyon için uygun olduğunuz zamanları yapılandırın.", + "change_weekly_schedule": "Haftalık planınızı değiştirin", + "logo": "Logo", + "error": "Hata", + "team_logo": "Ekip Logosu", + "add_location": "Konum ekle", + "attendees": "Katılımcılar", + "add_attendees": "Katılımcı ekle", + "show_advanced_settings": "Gelişmiş ayarları göster", + "event_name": "Etkinlik Adı", + "event_name_tooltip": "Takvimlerde görünecek isim", + "meeting_with_user": "{USER} ile toplantı", + "additional_inputs": "Ek Girişler", + "label": "Etiket", + "placeholder": "Yer tutucu", + "type": "Tür", + "edit": "Düzenle", + "add_input": "Girdi ekleyin", + "disable_notes": "Takvimdeki notları gizle", + "disable_notes_description": "Gizlilik nedeniyle, takvim girişinde ek girişler ve notlar gizlenecektir. Bu bilgiler yine de e-postanıza gönderilebilecektir.", + "opt_in_booking": "Rezervasyon Onayı", + "opt_in_booking_description": "Rezervasyon, entegrasyonlara iletilmeden ve bir onay e-postası gönderilmeden önce manuel olarak onaylanmalıdır.", + "disable_guests": "Misafirleri Devre Dışı Bırak", + "disable_guests_description": "Rezervasyon sırasında ek misafir eklemeyi devre dışı bırakın.", + "invitees_can_schedule": "Davetliler planlayabilir", + "date_range": "Tarih Aralığı", + "calendar_days": "takvim günü", + "business_days": "iş günü", + "set_address_place": "Bir adres veya yer belirleyin", + "set_link_meeting": "Toplantı için bir bağlantı ayarlayın", + "cal_invitee_phone_number_scheduling": "Cal, planlamadan önce davetlinizden bir telefon numarası girmesini isteyecektir.", + "cal_provide_google_meet_location": "Cal, bir Google Meet konumu sağlayacaktır.", + "cal_provide_zoom_meeting_url": "Cal, bir Zoom toplantı URL'si sağlayacaktır.", + "cal_provide_tandem_meeting_url": "Cal, bir Tandem toplantı URL'si sağlayacaktır.", + "cal_provide_video_meeting_url": "Cal, bir görüntülü toplantı URL'si sağlayacaktır.", + "cal_provide_jitsi_meeting_url": "Sizin için bir Jitsi Meet URL'si oluşturacağız.", + "cal_provide_huddle01_meeting_url": "Cal, bir Huddle01 web3 görüntülü toplantı URL'si sağlayacaktır.", + "cal_provide_teams_meeting_url": "Cal, bir MS Teams toplantı URL'si sağlayacaktır. NOT: İŞ VEYA OKUL HESABINIZIN OLMASI GEREKİR", + "require_payment": "Ödemeyi Gerekli Kılın", + "commission_per_transaction": "işlem başına komisyon", + "event_type_updated_successfully_description": "Etkinlik türünüz başarıyla güncellendi.", + "hide_event_type": "Etkinlik türünü gizle", + "edit_location": "Konumu düzenle", + "into_the_future": "gelecekte", + "within_date_range": "Bir tarih aralığında", + "indefinitely_into_future": "Süresiz olarak gelecekte", + "this_input_will_shown_booking_this_event": "Bu girdi, bu etkinlik için rezervasyon yapılırken gösterilecek", + "add_new_custom_input_field": "Yeni özel veri girdi alanı ekle", + "quick_chat": "Hızlı Sohbet", + "add_new_team_event_type": "Yeni bir ekip ekinliği türü ekle", + "add_new_event_type": "Yeni bir etkinlik türü ekle", + "new_event_type_to_book_description": "İnsanların rezervasyon yapabileceği yeni bir etkinlik türü oluşturun.", + "length": "Uzunluk", + "minimum_booking_notice": "Minimum rezervasyon bildirimi", + "slot_interval": "Zaman dilimi aralıkları", + "slot_interval_default": "Etkinlik uzunluğunu kullan (varsayılan)", "delete_event_type_description": "Bu aktivite türünü silmek istediğinizden emin misiniz? Bu bağlantıyı paylaştığınız herkes artık bu bağlantıyı kullanarak rezervasyon yapamayacaktır.", - "delete_account_confirmation_message": "Cal.com hesabınızı silmek istediğinizden emin misiniz? Hesap bağlantısı paylaştığınız herkes artık bu bağlantıyı kullanarak rezervasyon yapamayacaktır ve kaydettiğiniz tüm tercihler kaybolacaktır." + "delete_event_type": "Etkinlik Türünü Sil", + "confirm_delete_event_type": "Evet, etkinlik türünü sil", + "delete_account": "Hesabı sil", + "confirm_delete_account": "Evet, hesabı sil", + "delete_account_confirmation_message": "Cal.com hesabınızı silmek istediğinizden emin misiniz? Hesap bağlantısı paylaştığınız herkes artık bu bağlantıyı kullanarak rezervasyon yapamayacaktır ve kaydettiğiniz tüm tercihler kaybolacaktır.", + "integrations": "Entegrasyonlar", + "apps": "Uygulamalar", + "app_store": "App Store", + "app_store_description": "İnsanları, teknolojiyi ve çalışma ortamını birbirine bağlıyor.", + "settings": "Ayarlar", + "event_type_moved_successfully": "Etkinlik türü başarıyla taşındı", + "next_step": "Adımı geç", + "prev_step": "Önceki adım", + "installed": "Yüklendi", + "disconnect": "Bağlantıyı kes", + "embed_your_calendar": "Takviminizi web sayfanıza yerleştirin", + "connect_your_favourite_apps": "Favori uygulamalarınızı bağlayın.", + "automation": "Otomasyon", + "configure_how_your_event_types_interact": "Etkinlik türlerinizin takvimlerinizle nasıl etkileşimde bulunması gerektiğini yapılandırın.", + "select_destination_calendar": "Şurada etkinlik oluştur:", + "connect_an_additional_calendar": "Ek bir takvimi bağlayın", + "conferencing": "Konferans", + "calendar": "Takvim", + "not_installed": "Yüklü değil", + "error_password_mismatch": "Şifreler eşleşmiyor.", + "error_required_field": "Bu alan gereklidir.", + "status": "Durum", + "team_view_user_availability": "Kullanıcı müsaitliğini görüntüle", + "team_view_user_availability_disabled": "Müsaitlik durumunu görüntülemek için kullanıcının daveti kabul etmesi gerekiyor", + "set_as_away": "Kendinizi dışarıda olarak ayarlayın", + "set_as_free": "Dışarıda durumunu devre dışı bırak", + "user_away": "Bu kullanıcı şu anda uzakta.", + "user_away_description": "Rezervasyon yapmaya çalıştığınız kişi kendisini \"Uzakta\" olarak ayarladığı için şu anda yeni rezervasyon kabul etmiyor.", + "meet_people_with_the_same_tokens": "Aynı token'lara sahip insanlarla tanışın", + "only_book_people_and_allow": "Yalnızca aynı token'leri (DAO veya NFT) paylaşan kullanıcılardan rezervasyon yapabilir veya rezervasyonlara izin verebilirsiniz.", + "saml_config_deleted_successfully": "SAML yapılandırması başarıyla silindi", + "account_created_with_identity_provider": "Hesabınız bir Kimlik Sağlayıcı kullanılarak oluşturuldu.", + "account_managed_by_identity_provider": "Hesabınız {{provider}} tarafından yönetiliyor", + "account_managed_by_identity_provider_description": "E-posta adresinizi ve şifrenizi değiştirmek, iki adımlı kimlik doğrulamayı etkinleştirmek ve daha fazlası için lütfen {{provider}} hesap ayarlarınızı ziyaret edin.", + "signin_with_google": "Google ile giriş yap", + "signin_with_saml": "SAML ile giriş yap", + "saml_configuration": "SAML yapılandırması", + "delete_saml_configuration": "SAML yapılandırmasını sil", + "delete_saml_configuration_confirmation_message": "SAML yapılandırmasını silmek istediğinizden emin misiniz? SAML girişini kullanan ekip üyeleriniz artık Cal.com'a erişemeyecek.", + "confirm_delete_saml_configuration": "Evet, SAML yapılandırmasını sil", + "saml_not_configured_yet": "SAML henüz yapılandırılmadı", + "saml_configuration_description": "SAML yapılandırmanızı güncellemek için lütfen Kimlik Sağlayıcınızın gönderdiği SAML meta verilerini aşağıdaki metin kutusuna yapıştırın.", + "saml_configuration_placeholder": "Lütfen Kimlik Sağlayıcınızdan gelen SAML meta verilerini buraya yapıştırın", + "saml_configuration_update_failed": "SAML yapılandırma güncellemesi başarısız oldu", + "saml_configuration_delete_failed": "SAML yapılandırması silinemedi", + "saml_email_required": "SAML Kimlik Sağlayıcınızı bulabilmemiz için lütfen bir e-posta girin", + "you_will_need_to_generate": "Entegrasyon sayfasında bir erişim token'ı oluşturmanız gerekir.", + "import": "İçe aktar", + "import_from": "Şuradan içe aktar", + "access_token": "Erişim token'ı", + "visit_roadmap": "Yol Haritası", + "popular_categories": "Popüler Kategoriler", + "trending_apps": "Popüler Uygulamalar", + "all_apps": "Tüm Uygulamalar", + "installed_apps": "Yüklü Uygulamalar", + "manage_your_connected_apps": "Yüklü uygulamalarınızı yönetin veya ayarları değiştirin", + "browse_apps": "Uygulamalara göz at", + "features": "Özellikler", + "permissions": "İzinler", + "terms_and_privacy": "Koşullar ve Gizlilik", + "published_by": "{{author}} tarafınan yayınlandı", + "subscribe": "Abone Ol", + "buy": "Satın Al", + "install_app": "Uygulamayı Yükle", + "categories": "Kategoriler", + "pricing": "Fiyatlandırma", + "learn_more": "Daha fazla bilgi edinin", + "privacy_policy": "Gizlilik Politikası", + "terms_of_service": "Kullanım Koşulları", + "remove": "Kaldır", + "add": "Ekle", + "verify_wallet": "Cüzdanı Doğrula", + "connect_metamask": "MetaMask'i Bağla", + "create_events_on": "Şu tarihte etkinlikler oluşturun:", + "missing_license": "Eksik Lisans", + "signup_requires": "Ticari lisans gerekli", + "signup_requires_description": "Cal.com, Inc. şu anda kayıt sayfasının ücretsiz, açık kaynaklı bir sürümünü sunmamaktadır. Kayıt bileşenlerine tam erişim elde etmek için ticari bir lisans almanız gerekiyor. Kişisel kullanım için hesap oluşturmak üzere Prisma Data veya başka bir Postgres arayüzü öneriyoruz.", + "next_steps": "Sonraki Adımlar", + "acquire_commercial_license": "Ticari bir lisans edinin", + "the_infrastructure_plan": "Altyapı planı kullanıma dayalıdır ve yeni başlayanlar için uygun indirimlere sahiptir.", + "prisma_studio_tip": "Prisma Studio aracılığıyla bir hesap oluşturun", + "prisma_studio_tip_description": "İlk kullanıcınız için nasıl ayar yapacağınızı öğrenin", + "contact_sales": "Satış Ekibiyle İletişime Geçin", + "error_404": "404 Hatası", + "default": "Varsayılan", + "set_to_default": "Varsayılan olarak ayarla", + "new_schedule_btn": "Yeni plan", + "add_new_schedule": "Yeni bir plan ekle", + "delete_schedule": "Planı sil", + "schedule_created_successfully": "{{scheduleName}} planı başarıyla oluşturuldu", + "availability_updated_successfully": "{{scheduleName}} planı başarıyla güncellendi", + "schedule_deleted_successfully": "Plan başarıyla silindi", + "default_schedule_name": "Çalışma Saatleri", + "new_schedule_heading": "Müsaitlik planı oluşturun", + "new_schedule_description": "Müsaitlik planları oluşturmak, farklı etkinlik türleri için müsaitlik durumunuzu yönetmenize olanak tanır. Planlar bir veya daha fazla etkinlik türüne uygulanabilir.", + "requires_ownership_of_a_token": "Aşağıdaki adrese ait bir token'a sahip olmak gereklidir:", + "example_name": "Elif Ertem", + "time_format": "Saat biçimi", + "12_hour": "12 saat", + "24_hour": "24 saat", + "redirect_success_booking": "Rezervasyonu yönlendir ", + "you_are_being_redirected": "$t(second, {\"count\": {{seconds}} }) içinde {{ url }} adresine yönlendirileceksiniz.", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Bu özelliği kullanmak için Pro hesabına geçmeniz gerekiyor.", + "duplicate": "Çoğalt", + "you_can_manage_your_schedules": "Müsaitlik durumu sayfasından planlarınızı yönetebilirsiniz.", + "api_keys": "API Anahtarları", + "api_key_modal_subtitle": "API anahtarları, kendi hesabınız için API aramaları yapmanızı sağlar.", + "api_keys_subtitle": "Kendi hesabınıza erişmek için API anahtarları oluşturun.", + "generate_new_api_key": "Yeni API anahtarı oluştur", + "create_api_key": "API anahtarı oluştur", + "personal_note": "Bu anahtarın adı", + "personal_note_placeholder": "Örn. Geliştirme", + "api_key_no_note": "İsimsiz API anahtarı", + "api_key_never_expires": "Bu API anahtarının son kullanma tarihi yok", + "edit_api_key": "API anahtarını düzenle", + "never_expire_key": "Süresiz kullanım", + "delete_api_key": "API anahtarını iptal et", + "success_api_key_created": "API anahtarı başarıyla oluşturuldu", + "success_api_key_edited": "API anahtarı başarıyla güncellendi", + "create": "Oluştur", + "success_api_key_created_bold_tagline": "Bu API anahtarını güvenli bir yere kaydedin.", + "you_will_only_view_it_once": "Bu modu kapattığınızda bir daha göremezsiniz.", + "copy_to_clipboard": "Panoya kopyala", + "confirm_delete_api_key": "Bu API anahtarını iptal et", + "revoke_api_key": "API anahtarını iptal et", + "api_key_copied": "API anahtarı kopyalandı!", + "delete_api_key_confirm_title": "Bu API anahtarı hesabınızdan kalıcı olarak kaldırılsın mı?", + "copy": "Kopyala", + "expire_date": "Son tarih", + "expired": "Süresi doldu", + "never_expires": "Süresiz kullanım", + "expires": "Son tarih", + "request_reschedule_booking": "Rezervasyonunuz için yeniden planlama isteğinde bulunun", + "reason_for_reschedule": "Yeniden planlama nedeni", + "book_a_new_time": "Yeni bir zaman dilimi için rezervasyon", + "reschedule_request_sent": "Yeniden planlama isteği gönderildi", + "reschedule_modal_description": "Bu işlem, planlanan toplantıyı iptal edecek, planlayıcıyı bilgilendirecek ve sizden yeni bir zaman dilimi seçmenizi isteyecektir.", + "reason_for_reschedule_request": "Yeniden zamanlama isteğinin nedeni", + "send_reschedule_request": "Yeniden planlama isteğinde bulun ", + "edit_booking": "Rezervasyonu düzenle", + "reschedule_booking": "Rezervasyonu yeniden planla", + "former_time": "Önceki tarih" } diff --git a/apps/web/public/static/locales/tr/vital.json b/apps/web/public/static/locales/tr/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/tr/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/uk/common.json b/apps/web/public/static/locales/uk/common.json index 7cfde5ec78..80ef362ced 100644 --- a/apps/web/public/static/locales/uk/common.json +++ b/apps/web/public/static/locales/uk/common.json @@ -2,6 +2,8 @@ "trial_days_left": "У вас ще $t(day, {\"count\": {{days}} }) користування пробною PRO-версією", "day": "{{count}} день", "day_plural": "{{count}} д.", + "second": "{{count}} с", + "second_plural": "{{count}} с", "upgrade_now": "Перейти на PRO", "accept_invitation": "Прийняти запрошення", "calcom_explained": "Cal.com – це альтернатива Calendly з відкритим вихідним кодом, що дає змогу керувати вашими власними даними, робочими процесами й оформленням.", @@ -64,6 +66,11 @@ "your_meeting_has_been_booked": "Вашу нараду заброньовано", "event_type_has_been_rescheduled_on_time_date": "{{eventType}} «{{name}}» перенесено. Новий час: {{time}} ({{timeZone}}), {{date}}.", "event_has_been_rescheduled": "Оновлено – ваш захід перенесено", + "request_reschedule_title_attendee": "Запит на перенесення бронювання", + "request_reschedule_subtitle": "{{organizer}} скасував(-ла) бронювання та попросив(-ла) вас вибрати інший час.", + "request_reschedule_title_organizer": "Ви попросили учасника {{attendee}} перенести бронювання", + "request_reschedule_subtitle_organizer": "Ви скасували бронювання. {{attendee}} має забронювати інший час.", + "reschedule_reason": "Причина перенесення", "hi_user_name": "Привіт, {{name}}!", "ics_event_title": "{{eventType}} «{{name}}»", "new_event_subject": "Новий захід: {{attendeeName}} – {{date}} – {{eventType}}", @@ -82,6 +89,7 @@ "meeting_url": "URL-адреса наради", "meeting_request_rejected": "Ваш запит на нараду відхилено", "rescheduled_event_type_subject": "Перенесено: {{eventType}} «{{name}}», {{date}}", + "requested_to_reschedule_subject_attendee": "Потрібно перенести бронювання: забронюйте новий час для заходу «{{eventType}}» із користувачем {{name}}", "rejected_event_type_with_organizer": "Відхилено: {{eventType}} «{{organizer}}», {{date}}", "hi": "Привіт", "join_team": "Увійти в команду", @@ -304,6 +312,9 @@ "light": "Світла", "dark": "Темна", "automatically_adjust_theme": "Автоматично змінювати тему відповідно до вподобань запрошених користувачів", + "user_dynamic_booking_disabled": "Деякі користувачі групи зараз вимкнули динамічні групові бронювання", + "allow_dynamic_booking_tooltip": "Посилання для групового бронювання, які можна створювати динамічно, додаючи кілька імен користувачів за допомогою «+». Приклад: «cal.com/bailey+peer»", + "allow_dynamic_booking": "Дозволити учасникам бронювати ваш час за допомогою динамічних групових бронювань", "email": "Ел. адреса", "email_placeholder": "vpetrenko@zrazok.com", "full_name": "Ім’я і прізвище", @@ -406,6 +417,7 @@ "booking_confirmation": "Підтвердьте свій захід ({{eventTypeTitle}}) із користувачем {{profileName}}", "booking_reschedule_confirmation": "Перенесіть свій захід ({{eventTypeTitle}}) із користувачем {{profileName}}", "in_person_meeting": "Посилання або особиста зустріч", + "link_meeting": "Посилання на нараду", "phone_call": "Телефонний дзвінок", "phone_number": "Номер телефону", "enter_phone_number": "Введіть номер телефону", @@ -444,6 +456,7 @@ "danger_zone": "Небезпечна зона", "back": "Назад", "cancel": "Скасувати", + "apply": "Застосувати", "cancel_event": "Скасувати цей захід", "continue": "Продовжити", "confirm": "Підтвердити", @@ -564,6 +577,8 @@ "type": "Тип", "edit": "Редагувати", "add_input": "Додати поле введення", + "disable_notes": "Приховати примітки в календарі", + "disable_notes_description": "З міркувань конфіденційності додаткові поля введення та примітки буде приховано в записі календаря. Їх усе одно буде надіслано вам на електронну пошту.", "opt_in_booking": "Погодження бронювання", "opt_in_booking_description": "Бронювання потрібно підтвердити вручну, перш ніж його буде надіслано в інтеграції. Ви отримаєте підтвердження електронною поштою.", "disable_guests": "Вимкнути гостей", @@ -573,6 +588,7 @@ "calendar_days": "календарні дні", "business_days": "робочі дні", "set_address_place": "Укажіть адресу або місце", + "set_link_meeting": "Створити посилання на нараду", "cal_invitee_phone_number_scheduling": "У Cal запрошеним знадобиться ввести номер телефону, перш ніж планувати щось.", "cal_provide_google_meet_location": "Cal надасть розташування Google Meet.", "cal_provide_zoom_meeting_url": "Cal надасть URL-адресу наради Zoom.", @@ -580,6 +596,7 @@ "cal_provide_video_meeting_url": "Cal надасть URL-адресу наради Daily.", "cal_provide_jitsi_meeting_url": "Ми згенеруємо URL-адресу Jitsi Meet.", "cal_provide_huddle01_meeting_url": "Cal надасть URL-адресу відеонаради Huddle01 web3.", + "cal_provide_teams_meeting_url": "Cal надасть URL-адресу наради в MS Teams. ПРИМІТКА: ПОТРІБЕН РОБОЧИЙ АБО НАВЧАЛЬНИЙ ОБЛІКОВИЙ ЗАПИС", "require_payment": "Вимагати оплату", "commission_per_transaction": "комісія за транзакцію", "event_type_updated_successfully_description": "Ваш тип заходу оновлено.", @@ -605,6 +622,9 @@ "confirm_delete_account": "Так, видалити обліковий запис", "delete_account_confirmation_message": "Справді видалити обліковий запис Cal.com? Усі, кому ви надавали посилання на свій обліковий запис, більше не зможуть бронювати ваш час за його допомогою. Усі збережені налаштування буде втрачено.", "integrations": "Інтеграції", + "apps": "Додатки", + "app_store": "Магазин додатків", + "app_store_description": "Спілкування та технології на робочому місці.", "settings": "Параметри", "event_type_moved_successfully": "Тип події переміщено", "next_step": "Пропустити крок", @@ -652,6 +672,24 @@ "import_from": "Імпортувати з", "access_token": "Токен доступу", "visit_roadmap": "Дорожня карта", + "popular_categories": "Популярні категорії", + "trending_apps": "Популярні додатки", + "all_apps": "Усі додатки", + "installed_apps": "Установлені додатки", + "manage_your_connected_apps": "Керуйте встановленими додатками й налаштуваннями", + "browse_apps": "Огляд додатків", + "features": "Функції", + "permissions": "Дозволи", + "terms_and_privacy": "Умови та конфіденційність", + "published_by": "Опублікував(-ла) {{author}}", + "subscribe": "Підписатися", + "buy": "Купити", + "install_app": "Установити додаток", + "categories": "Категорії", + "pricing": "Ціни", + "learn_more": "Докладніше", + "privacy_policy": "Політика конфіденційності", + "terms_of_service": "Умови користування", "remove": "Вилучити", "add": "Додати", "verify_wallet": "Пройдіть перевірку гаманця", @@ -667,10 +705,63 @@ "prisma_studio_tip_description": "Дізнайтеся, як налаштувати першого користувача", "contact_sales": "Зв’язатися з відділом продажів", "error_404": "Помилка 404", - "availability_updated_successfully": "Відомості про доступність оновлено", + "default": "За замовчуванням", + "set_to_default": "Установити за замовчуванням", + "new_schedule_btn": "Створити розклад", + "add_new_schedule": "Додати новий розклад", + "delete_schedule": "Видалити розклад", + "schedule_created_successfully": "Розклад «{{scheduleName}}» створено", + "availability_updated_successfully": "Розклад «{{scheduleName}}» оновлено", + "schedule_deleted_successfully": "Розклад видалено", + "default_schedule_name": "Робочі години", + "new_schedule_heading": "Створення розкладу доступності", + "new_schedule_description": "Розклад доступності дає змогу керувати своєю доступністю для участі в заходах різних типів. Його можна застосовувати як до одного, так і до кількох різних типів заходів.", "requires_ownership_of_a_token": "Потрібен токен, що належить такій адресі:", "example_name": "Василь Петренко", "time_format": "Формат часу", "12_hour": "12-годинний", - "24_hour": "24-годинний" + "24_hour": "24-годинний", + "redirect_success_booking": "Перенаправляти під час бронювання ", + "you_are_being_redirected": "Ви перейдете за адресою {{ url }} через $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "Щоб користуватися цією функцією, потрібен обліковий запис Pro.", + "duplicate": "Дублювати", + "you_can_manage_your_schedules": "Керувати розкладами можна на сторінці «Доступність».", + "api_keys": "Ключі API", + "api_key_modal_subtitle": "Ключі API дають змогу здійснювати виклики API для вашого облікового запису.", + "api_keys_subtitle": "Створюйте ключі API, щоб отримувати доступ до свого облікового запису.", + "generate_new_api_key": "Створити новий ключ API", + "create_api_key": "Створіть ключ API", + "personal_note": "Назвіть цей ключ", + "personal_note_placeholder": "Наприклад, «Розробка»", + "api_key_no_note": "Ключ API без назви", + "api_key_never_expires": "Цей ключ API діє безстроково", + "edit_api_key": "Редагувати ключ API", + "never_expire_key": "Діє безстроково", + "delete_api_key": "Відкликати ключ API", + "success_api_key_created": "Ключ API створено", + "success_api_key_edited": "Ключ API оновлено", + "create": "Створити", + "success_api_key_created_bold_tagline": "Зберігайте цей ключ API у безпечному місці.", + "you_will_only_view_it_once": "Коли це вікно закриється, ви не зможете переглянути його ще раз.", + "copy_to_clipboard": "Скопіювати в буфер обміну", + "confirm_delete_api_key": "Відкликати цей ключ API", + "revoke_api_key": "Відкликати ключ API", + "api_key_copied": "Ключ API скопійовано!", + "delete_api_key_confirm_title": "Видалити цей ключ API із вашого облікового запису остаточно?", + "copy": "Копіювати", + "expire_date": "Термін дії", + "expired": "Термін дії минув", + "never_expires": "Немає терміну дії", + "expires": "Термін дії минає", + "request_reschedule_booking": "Надіслати запит на перенесення бронювання", + "reason_for_reschedule": "Причина перенесення", + "book_a_new_time": "Забронювати новий час", + "reschedule_request_sent": "Запит на перенесення надіслано", + "reschedule_modal_description": "Заплановану нараду буде скасовано, і користувач, який її організовував, отримає сповіщення та запит вибрати новий час.", + "reason_for_reschedule_request": "Причина запиту на перенесення", + "send_reschedule_request": "Надіслати запит на перенесення ", + "edit_booking": "Редагувати бронювання", + "reschedule_booking": "Перенести бронювання", + "former_time": "Попередній час" } diff --git a/apps/web/public/static/locales/uk/vital.json b/apps/web/public/static/locales/uk/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/uk/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/vi/common.json b/apps/web/public/static/locales/vi/common.json index 74b598d5c7..8edf2cdeac 100644 --- a/apps/web/public/static/locales/vi/common.json +++ b/apps/web/public/static/locales/vi/common.json @@ -2,6 +2,8 @@ "trial_days_left": "Bạn có $t(day, {\"count\": {{days}} }) còn lại trên bản thử nghiệm PRO", "day": "{{count}} ngày", "day_plural": "{{count}} ngày", + "second": "{{count}} giây", + "second_plural": "{{count}} giây", "upgrade_now": "Nâng cấp ngay", "accept_invitation": "Chấp Nhận Lời Mời", "calcom_explained": "Cal.com là Calendly mã nguồn mở, giúp bạn quản lý dữ liệu, ngoại hình và cách làm việc của mình.", @@ -64,6 +66,11 @@ "your_meeting_has_been_booked": "Cuộc họp của bạn đã được đặt", "event_type_has_been_rescheduled_on_time_date": "{{eventType}} của bạn với {{name}} đã được đổi lại thành {{time}} ({{timeZone}}) vào {{date}}.", "event_has_been_rescheduled": "Đã cập nhật - Sự kiện của bạn đã được đổi", + "request_reschedule_title_attendee": "Yêu cầu xếp lại lịch đặt chỗ của bạn", + "request_reschedule_subtitle": "{{organizer}} đã huỷ đặt chỗ đó và yêu cầu bạn chọn thời gian khác.", + "request_reschedule_title_organizer": "Bạn đã yêu cầu {{attendee}} xếp lại lịch", + "request_reschedule_subtitle_organizer": "Bạn đã huỷ mục đặt chỗ này và {{attendee}} nên chọn thời gian đặt chỗ mới với bạn.", + "reschedule_reason": "Lý do xếp lại lịch", "hi_user_name": "Xin chào {{name}}", "ics_event_title": "{{eventType}} với {{name}}", "new_event_subject": "Sự kiện mới: {{attendeeName}} - {{date}} - {{eventType}}", @@ -82,6 +89,7 @@ "meeting_url": "URL cuộc họp", "meeting_request_rejected": "Yêu cầu lên cuộc họp của bạn đã bị từ chối", "rescheduled_event_type_subject": "Đã lên lịch lại: {{eventType}} với {{name}} lúc {{date}}", + "requested_to_reschedule_subject_attendee": "Thao tác cần làm để xếp lại lịch: Vui lòng đặt thời gian mới cho {{eventType}} với {{name}}", "rejected_event_type_with_organizer": "Bị từ chối: {{eventType}} với {{organizer}} vào {{date}}", "hi": "Chào", "join_team": "Tham gia nhóm", @@ -304,6 +312,9 @@ "light": "Sáng", "dark": "Tối", "automatically_adjust_theme": "Tự động điều chỉnh chủ đề theo tùy chọn của người được mời", + "user_dynamic_booking_disabled": "Một số người dùng trong nhóm hiện đã tắt chức năng đặt chỗ động cho nhóm", + "allow_dynamic_booking_tooltip": "Những liên kết đặt chỗ nhóm vốn có thể được tạo linh động bằng cách thêm nhiều tên người dùng bằng một dấu '+'. Ví dụ như 'cal.com/bailey+peer'", + "allow_dynamic_booking": "Cho phép người tham gia đặt chỗ cho bạn thông qua chức năng đặt chỗ động cho nhóm", "email": "Email", "email_placeholder": "nvana@vidu.com", "full_name": "Họ và tên", @@ -582,7 +593,7 @@ "cal_provide_google_meet_location": "Cal sẽ cung cấp URL Google Meet.", "cal_provide_zoom_meeting_url": "Cal sẽ cung cấp một URL cuộc họp Zoom.", "cal_provide_tandem_meeting_url": "Cal sẽ cung cấp URL cuộc họp Tandem.", - "cal_provide_video_meeting_url": "Cal sẽ cung cấp URL cuộc họp video Daily.", + "cal_provide_video_meeting_url": "Cal sẽ cung cấp URL cuộc họp video.", "cal_provide_jitsi_meeting_url": "Chúng tôi sẽ tạo một URL Jitsi Meet cho bạn.", "cal_provide_huddle01_meeting_url": "Cal sẽ cung cấp URL cuộc họp video Huddle01 web3.", "cal_provide_teams_meeting_url": "Cal sẽ cung cấp URL cuộc họp MS Teams. LƯU Ý: PHẢI CÓ TÀI KHOẢN CÔNG VIỆC HOẶC TRƯỜNG HỌC", @@ -711,7 +722,46 @@ "12_hour": "12 tiếng", "24_hour": "24 tiếng", "redirect_success_booking": "Chuyển hướng khi đặt lịch hẹn ", + "you_are_being_redirected": "Bạn sẽ được chuyển hướng đến {{ url }} sau $t(second, {\"count\": {{seconds}} }).", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", "redirect_url_upgrade_description": "Để sử dụng tính năng này, bạn cần nâng cấp lên tài khoản Pro.", "duplicate": "Sao chép", - "you_can_manage_your_schedules": "Bạn có thể quản lý lịch biểu của mình trên trang \bLịch khả dụng." + "you_can_manage_your_schedules": "Bạn có thể quản lý lịch biểu của mình trên trang \bLịch khả dụng.", + "api_keys": "Khoá API", + "api_key_modal_subtitle": "Khoá API cho phép bạn thực hiện những cuộc gọi API cho tài khoản của chính mình.", + "api_keys_subtitle": "Tạo khóa API để sử dụng cho việc truy cập tài khoản chính mình.", + "generate_new_api_key": "Tạo khóa API mới", + "create_api_key": "Tạo một khóa API", + "personal_note": "Đặt tên khóa này", + "personal_note_placeholder": "Ví dụ như Phát triển", + "api_key_no_note": "Khoá API vô danh", + "api_key_never_expires": "Khoá API này không có ngày hết hiệu lực", + "edit_api_key": "Sửa khóa API", + "never_expire_key": "Không bao giờ hết hiệu lực", + "delete_api_key": "Thu hồi khóa API", + "success_api_key_created": "Khoá API được tạo thành công", + "success_api_key_edited": "Khoá API được cập nhật thành công", + "create": "Tạo", + "success_api_key_created_bold_tagline": "Lưu khóa API này ở nơi an toàn.", + "you_will_only_view_it_once": "Bạn sẽ không thể xem lại nó sau khi đóng chế độ này lại.", + "copy_to_clipboard": "Sao chép vào bộ nhớ tạm", + "confirm_delete_api_key": "Thu hồi khóa API này", + "revoke_api_key": "Thu hồi khóa API", + "api_key_copied": "Khoá API đã sao chép!", + "delete_api_key_confirm_title": "Xoá bỏ vĩnh viễn khóa API này ra khỏi tài khoản của bạn?", + "copy": "Sao chép", + "expire_date": "Ngày hết hiệu lực", + "expired": "Đã hết hiệu lực", + "never_expires": "Không bao giờ hết hiệu lực", + "expires": "Hết hiệu lực", + "request_reschedule_booking": "Yêu cầu xếp lại lịch đặt chỗ của bạn", + "reason_for_reschedule": "Lý do xếp lại lịch", + "book_a_new_time": "Đặt thời gian mới", + "reschedule_request_sent": "Đã gửi yêu cầu xếp lại lịch", + "reschedule_modal_description": "Thao tác này sẽ huỷ cuộc họp đã được xếp lịch này, báo tin cho người xếp lịch và yêu cầu họ chọn thời gian mới.", + "reason_for_reschedule_request": "Lý do yêu cầu xếp lại lịch", + "send_reschedule_request": "Yêu cầu xếp lại lịch ", + "edit_booking": "Sửa mục đặt chỗ", + "reschedule_booking": "Xếp lại lịch đặt chỗ", + "former_time": "Thời gian cũ" } diff --git a/apps/web/public/static/locales/vi/vital.json b/apps/web/public/static/locales/vi/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/vi/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/zh-CN/common.json b/apps/web/public/static/locales/zh-CN/common.json index 0c62dc9285..d943b3ad4f 100644 --- a/apps/web/public/static/locales/zh-CN/common.json +++ b/apps/web/public/static/locales/zh-CN/common.json @@ -2,6 +2,8 @@ "trial_days_left": "您的 PRO 试用还有 $t(day, {\"count\": {{days}} }) 到期", "day": "{{count}} 天", "day_plural": "{{count}} 天", + "second": "{{count}} 秒", + "second_plural": "{{count}} 秒", "upgrade_now": "立刻升级", "accept_invitation": "接受邀请", "calcom_explained": "Cal.com是Calendly的开源版替代,您能够掌控自己的数据、定制工作流程和外观。", @@ -60,9 +62,15 @@ "password_reset_instructions": "如果您没有这样请求,您可以安全地忽略此邮件,您的密码将不会被更改。", "event_awaiting_approval_subject": "待批准: 和 {{name}} 在 {{date}} 的 {{eventType}}", "event_still_awaiting_approval": "有活动仍在等待您的批准", + "booking_submitted_subject": "预约已提交: 名称为 {{name}} 的 {{eventType}},日期为 {{date}}", "your_meeting_has_been_booked": "您的会议已预定", "event_type_has_been_rescheduled_on_time_date": "您与 {{name}} 的 {{eventType}} 已被重新安排到 {{date}} {{time}} ({{timeZone}})。", "event_has_been_rescheduled": "已更新 - 您的活动已被重新安排", + "request_reschedule_title_attendee": "请求重新安排您的预约", + "request_reschedule_subtitle": "{{organizer}} 取消了预约并要求您选择其他时间。", + "request_reschedule_title_organizer": "您已请求 {{attendee}} 重新安排", + "request_reschedule_subtitle_organizer": "您已取消预约,{{attendee}} 应该与您一起选择新的预约时间。", + "reschedule_reason": "重新安排的原因", "hi_user_name": "您好 {{name}}", "ics_event_title": "与 {{name}} 的 {{eventType}}", "new_event_subject": "新活动: {{attendeeName}} - {{date}} - {{eventType}}", @@ -81,6 +89,7 @@ "meeting_url": "会议链接", "meeting_request_rejected": "您的会议请求已被拒绝", "rescheduled_event_type_subject": "已重新安排: 和 {{name}} 在 {{date}} 的 {{eventType}}", + "requested_to_reschedule_subject_attendee": "需要重新安排操作: 请为名称为 {{name}} 的 {{eventType}} 预约新的时间", "rejected_event_type_with_organizer": "已拒绝: 和 {{organizer}} 在 {{date}} 的 {{eventType}}", "hi": "您好", "join_team": "加入团队", @@ -251,6 +260,7 @@ "friday_time_error": "周五有无效时间", "saturday_time_error": "周六有无效时间", "error_end_time_before_start_time": "结束时间不能早于开始时间", + "error_end_time_next_day": "结束时间不能超过 24 小时", "back_to_bookings": "返回预约", "free_to_pick_another_event_type": "随时可以选择另一个活动。", "cancelled": "已取消", @@ -283,6 +293,10 @@ "hover_over_bold_times_tip": "提示:在加粗的时间上悬停以显示完整的时间戳", "start_time": "开始时间", "end_time": "结束时间", + "buffer_time": "缓冲时间", + "before_event": "活动前", + "after_event": "活动后", + "event_buffer_default": "无缓冲时间", "buffer": "缓冲期", "your_day_starts_at": "您的一天开始于", "your_day_ends_at": "您的一天结束于", @@ -298,6 +312,9 @@ "light": "浅色", "dark": "深色", "automatically_adjust_theme": "跟随受邀者偏好", + "user_dynamic_booking_disabled": "组中的一些用户目前禁用了动态组预约", + "allow_dynamic_booking_tooltip": "可以动态创建组预约链接,方法为使用 '+' 来添加多个用户名。例如: 'cal.com/bailey+peer'", + "allow_dynamic_booking": "允许参与者通过动态组预约来向您预约", "email": "邮箱地址", "email_placeholder": "jdoe@example.com", "full_name": "全名", @@ -310,9 +327,12 @@ "event_triggers": "事件触发", "subscriber_url": "订阅者链接", "create_new_webhook": "创建一个新的 webhook", + "webhooks": "Webhook", + "team_webhooks": "团队 Webhook", "create_new_webhook_to_account": "为您的账户创建一个新的Webhook", "new_webhook": "新建 Webhook", "receive_cal_meeting_data": "当活动被安排或取消时,在指定的链接实时接收Cal会议数据。", + "receive_cal_event_meeting_data": "当此活动被安排或取消后,会在指定的 URL 实时接收 Cal 会议数据。", "responsive_fullscreen_iframe": "全屏响应式iframe", "loading": "加载中...", "standard_iframe": "标准 iframe", @@ -397,10 +417,12 @@ "booking_confirmation": "确认您和 {{profileName}} 的 {{eventTypeTitle}}", "booking_reschedule_confirmation": "重新安排您和 {{profileName}} 的 {{eventTypeTitle}}", "in_person_meeting": "线上或线下会议", + "link_meeting": "线上会议", "phone_call": "打电话", "phone_number": "电话号码", "enter_phone_number": "输入电话号码", "reschedule": "重新安排", + "reschedule_this": "改为重新安排", "book_a_team_member": "预约一个团队成员来替代", "or": "或", "go_back": "后退", @@ -434,6 +456,8 @@ "danger_zone": "危险区域", "back": "后退", "cancel": "取消", + "apply": "应用", + "cancel_event": "取消此活动", "continue": "继续", "confirm": "确认", "disband_team": "解散团队", @@ -452,6 +476,8 @@ "pending": "待定", "open_options": "打开选项", "copy_link": "复制活动链接", + "share": "分享", + "share_event": "请您预约我的日历或将您的链接发送给我好吗?", "copy_link_team": "复制团队链接", "leave_team": "离开团队", "confirm_leave_team": "是,离开团队", @@ -459,6 +485,7 @@ "user_from_team": "来自 {{team}} 的 {{user}}", "preview": "预览", "link_copied": "链接已复制!", + "link_shared": "链接已分享!", "title": "标题", "description": "描述", "quick_video_meeting": "快速视频会议。", @@ -500,6 +527,8 @@ "first_day_of_week": "每周的第一天", "single_theme": "主题", "brand_color": "品牌颜色", + "light_brand_color": "品牌颜色(浅色主题)", + "dark_brand_color": "品牌颜色(深色主题)", "file_not_named": "文件名不是 [idOrSlug]/[user]", "create_team": "新建团队", "name": "名称", @@ -548,6 +577,8 @@ "type": "类型", "edit": "编辑", "add_input": "添加输入框", + "disable_notes": "在日历中隐藏备注", + "disable_notes_description": "出于隐私原因,额外的输入和备注将在日历条目中被隐藏,但它们仍将发送到您的电子邮件中。", "opt_in_booking": "手动确认预约", "opt_in_booking_description": "该预约在推送到集成和发送确认邮件之前需要您手动确认。", "disable_guests": "禁用访客", @@ -557,6 +588,7 @@ "calendar_days": "自然日", "business_days": "工作日", "set_address_place": "设置地址或位置", + "set_link_meeting": "设置会议链接", "cal_invitee_phone_number_scheduling": "Cal 将要求您的受邀者在安排时间之前提供电话号码。", "cal_provide_google_meet_location": "Cal 将提供 Google Meet 位置。", "cal_provide_zoom_meeting_url": "Cal将提供Zoom会议链接", @@ -564,6 +596,7 @@ "cal_provide_video_meeting_url": "Cal将提供Daily视频会议链接", "cal_provide_jitsi_meeting_url": "Cal 将提供Jitsi Meet视频会议链接", "cal_provide_huddle01_meeting_url": "Cal 将提供Huddle01 Web3视频会议链接", + "cal_provide_teams_meeting_url": "Cal 将提供 MS Teams 会议 URL。注意: 必须拥有工作或学校帐户", "require_payment": "需要付款", "commission_per_transaction": "每笔交易的佣金", "event_type_updated_successfully_description": "您的活动类型更新成功。", @@ -589,6 +622,9 @@ "confirm_delete_account": "是,删除账户", "delete_account_confirmation_message": "您确定要删除您的 Cal.com 账户? 任何曾通过您的账户链接与您预约过的人都将无法再使用它和您预约,您保存的任何偏好设置都将丢失。", "integrations": "集成", + "apps": "应用", + "app_store": "应用商店", + "app_store_description": "连接人员、技术和工作场所。", "settings": "设置", "event_type_moved_successfully": "活动类型移动成功", "next_step": "跳过步骤", @@ -636,6 +672,24 @@ "import_from": "导入自", "access_token": "访问令牌", "visit_roadmap": "产品路线图", + "popular_categories": "热门类别", + "trending_apps": "热门应用", + "all_apps": "所有应用", + "installed_apps": "已安装的应用", + "manage_your_connected_apps": "管理已安装的应用或更改设置", + "browse_apps": "浏览应用", + "features": "功能", + "permissions": "权限", + "terms_and_privacy": "条款和隐私", + "published_by": "由 {{author}} 发布", + "subscribe": "订阅", + "buy": "购买", + "install_app": "安装应用", + "categories": "类别", + "pricing": "定价", + "learn_more": "了解更多", + "privacy_policy": "隐私政策", + "terms_of_service": "服务条款", "remove": "移除", "add": "添加", "verify_wallet": "验证钱包", @@ -651,5 +705,63 @@ "prisma_studio_tip_description": "学习如何设置您的第一个用户", "contact_sales": "联系销售", "error_404": "错误 404", - "availability_updated_successfully": "可预约时间更新成功" + "default": "默认值", + "set_to_default": "设置为默认值", + "new_schedule_btn": "新建时间表", + "add_new_schedule": "添加新的时间表", + "delete_schedule": "删除时间表", + "schedule_created_successfully": "{{scheduleName}} 时间表已成功创建", + "availability_updated_successfully": "可预约时间更新成功", + "schedule_deleted_successfully": "已成功删除时间表", + "default_schedule_name": "工作时间", + "new_schedule_heading": "创建可预约时间表", + "new_schedule_description": "通过创建可预约的时间表,可以管理不同活动类型的可预约时间。这些时间表可以应用于一个或多个活动类型。", + "requires_ownership_of_a_token": "需要属于以下地址的令牌的所有权:", + "example_name": "某人", + "time_format": "时间格式", + "12_hour": "12 小时", + "24_hour": "24 小时", + "redirect_success_booking": "预约时重定向", + "you_are_being_redirected": "您将在 $t(second, {\"count\": {{seconds}} }) 秒后重定向到 {{ url }}。", + "external_redirect_url": "https://example.com/redirect-to-my-success-page", + "redirect_url_upgrade_description": "要使用此功能,您需要升级到专业版帐户。", + "duplicate": "复制", + "you_can_manage_your_schedules": "您可以在“可预约时间”页面管理您的时间表。", + "api_keys": "API 密钥", + "api_key_modal_subtitle": "通过 API 密钥可以为您自己的帐户进行 API 调用。", + "api_keys_subtitle": "生成用于访问您自己帐户的 API 密钥。", + "generate_new_api_key": "生成新的 API 密钥", + "create_api_key": "创建 API 密钥", + "personal_note": "为此密钥命名", + "personal_note_placeholder": "例如,开发", + "api_key_no_note": "未命名 API 密钥", + "api_key_never_expires": "此 API 密钥没有过期日期", + "edit_api_key": "编辑 API 密钥", + "never_expire_key": "永不过期", + "delete_api_key": "撤销 API 密钥", + "success_api_key_created": "API 密钥已成功创建", + "success_api_key_edited": "API 密钥已成功更新", + "create": "创建", + "success_api_key_created_bold_tagline": "将此 API 密钥保存在安全的地方。", + "you_will_only_view_it_once": "一旦关闭此模式窗口,就无法再次查看它。", + "copy_to_clipboard": "复制到剪贴板", + "confirm_delete_api_key": "撤销此 API 密钥", + "revoke_api_key": "撤销 API 密钥", + "api_key_copied": "API 密钥已复制!", + "delete_api_key_confirm_title": "是否从您的帐户中永久删除此 API 密钥?", + "copy": "复制", + "expire_date": "过期日期", + "expired": "已过期", + "never_expires": "永不过期", + "expires": "过期", + "request_reschedule_booking": "请求重新安排您的预约", + "reason_for_reschedule": "重新安排的原因", + "book_a_new_time": "预约新时间", + "reschedule_request_sent": "重新安排请求已发送", + "reschedule_modal_description": "这将取消预定的会议,通知安排人员并让他们选择一个新时间。", + "reason_for_reschedule_request": "重新安排请求的原因", + "send_reschedule_request": "请求重新安排", + "edit_booking": "编辑预约", + "reschedule_booking": "重新安排预约", + "former_time": "之前的时间" } diff --git a/apps/web/public/static/locales/zh-CN/vital.json b/apps/web/public/static/locales/zh-CN/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/zh-CN/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/public/static/locales/zh-TW/common.json b/apps/web/public/static/locales/zh-TW/common.json index 4f05eb817b..3d9577d244 100644 --- a/apps/web/public/static/locales/zh-TW/common.json +++ b/apps/web/public/static/locales/zh-TW/common.json @@ -67,10 +67,10 @@ "event_type_has_been_rescheduled_on_time_date": "與 {{name}} 的 {{eventType}} 已經將預約時間改為 {{date}} 的 {{time}} ({{timeZone}})。", "event_has_been_rescheduled": "已更新 - 已經重新預定活動時間", "request_reschedule_title_attendee": "要求重新安排您的預約", - "request_reschedule_subtitle": "{{organizer}} 已取消預約並要求您選擇其他時間。", + "request_reschedule_subtitle": "{{organizer}} 已取消預約,並要求您選擇其他時間。", "request_reschedule_title_organizer": "您已要求 {{attendee}} 重新預約", - "request_reschedule_subtitle_organizer": "您已取消預約,{{attendee}} 應重新挑選新的預約時間。", - "reschedule_reason": "重新預約原因", + "request_reschedule_subtitle_organizer": "您已取消預約,{{attendee}} 應和您重新挑選新的預約時間。", + "reschedule_reason": "重新預約的原因", "hi_user_name": "哈囉 {{name}}", "ics_event_title": "與 {{name}} 的 {{eventType}}", "new_event_subject": "新活動:{{attendeeName}} - {{date}} - {{eventType}}", @@ -89,7 +89,7 @@ "meeting_url": "會議網址", "meeting_request_rejected": "會議請求遭到拒絕", "rescheduled_event_type_subject": "已重新預定:{{date}} 與 {{name}} 的 {{eventType}}", - "requested_to_reschedule_subject_attendee": "此操作需要重新預約:請為與 {{name}} 的 {{eventType}} 預訂新時間", + "requested_to_reschedule_subject_attendee": "需進行重新預約操作:請為與 {{name}} 的 {{eventType}} 預訂新時間", "rejected_event_type_with_organizer": "遭到拒絕:{{date}} 與 {{organizer}} 的 {{eventType}}", "hi": "嗨", "join_team": "加入團隊", @@ -109,7 +109,7 @@ "hey_there": "哈囉,", "forgot_your_password_calcom": "忘記密碼了? - Cal.com", "event_type_title": "{{eventTypeTitle}} | 活動類型", - "delete_webhook_confirmation_message": "確定要刪除這組 Webhook 嗎?之後活動預定或取消的時候,就再也不會即時在特定的網址收到 Cal.com 的會議資料。", + "delete_webhook_confirmation_message": "您確定要刪除這組 Webhook 嗎?之後活動預定或取消的時候,您就不會即時經特定連結收到 Cal.com 的會議資料。", "confirm_delete_webhook": "是的,刪除 Webhook", "edit_webhook": "編輯 Webhook", "delete_webhook": "刪除 Webhook", @@ -162,7 +162,7 @@ "username": "使用者名稱", "is_still_available": "仍可以使用。", "documentation": "說明文件", - "documentation_description": "學習把我們的工具,整合進應用程式的方法", + "documentation_description": "了解如何在您的應用程式中運用我們的工具", "api_reference": "API 參考", "api_reference_description": "我們的函式庫完整 API 參考文件", "blog": "部落格", @@ -312,9 +312,9 @@ "light": "亮色", "dark": "暗色", "automatically_adjust_theme": "根據受邀者的偏好,自動調整主題", - "user_dynamic_booking_disabled": "目前群組中的某些使用者已停用了動態群組預約", - "allow_dynamic_booking_tooltip": "使用 '+' 新增多個使用者名稱,以建立動態群組預約連結。例如:'cal.com/bailey+peer' ", - "allow_dynamic_booking": "允許與會者透過動態群組預約來預約您", + "user_dynamic_booking_disabled": "目前群組中某些使用者已停用了動態群組預約", + "allow_dynamic_booking_tooltip": "使用 '+' 新增多個使用者名稱,以動態建立群組預約連結。例如:'cal.com/bailey+peer'", + "allow_dynamic_booking": "允許與會者透過動態群組向您預約", "email": "電子郵件", "email_placeholder": "jdoe@example.com", "full_name": "完整姓名", @@ -416,8 +416,8 @@ "share_additional_notes": "請分享任何對準備會議有幫助的東西。", "booking_confirmation": "向 {{profileName}} 確認{{eventTypeTitle}}。", "booking_reschedule_confirmation": "向 {{profileName}} 重新預定{{eventTypeTitle}}", - "in_person_meeting": "當面會議", - "link_meeting": "連結會議", + "in_person_meeting": "實體會議", + "link_meeting": "線上會議", "phone_call": "撥打電話", "phone_number": "電話號碼", "enter_phone_number": "輸入電話號碼", @@ -578,7 +578,7 @@ "edit": "編輯", "add_input": "新增輸入欄", "disable_notes": "隱藏行事曆中的備註", - "disable_notes_description": "為了隱私,額外輸入欄及備註將在行事曆中隱藏。他們仍會被傳送至您的電子郵件地址。", + "disable_notes_description": "為保護隱私,行事曆中的其他輸入內容及備註將會隱藏,但仍會傳送至您的電子郵件。", "opt_in_booking": "主動加入的預約", "opt_in_booking_description": "此預約在推送至整合服務前,必須手動確認。確認電子郵件已經寄出。", "disable_guests": "關閉賓客", @@ -596,7 +596,7 @@ "cal_provide_video_meeting_url": "Cal 會提供 Daily 視訊會議網址。", "cal_provide_jitsi_meeting_url": "我們會產生一組 Jitsi 會議網址。", "cal_provide_huddle01_meeting_url": "Cal 會提供 Huddle01 Web3 視訊會議網址。", - "cal_provide_teams_meeting_url": "Cal 會提供 Microsoft Teams 會議網址。提醒:必須擁有工作或學校帳號", + "cal_provide_teams_meeting_url": "Cal 會提供 Microsoft Teams 會議網址。提醒您:需擁有工作或學校帳號", "require_payment": "必須付款", "commission_per_transaction": "每筆交易的佣金", "event_type_updated_successfully_description": "成功更新活動類型。", @@ -624,7 +624,7 @@ "integrations": "整合", "apps": "應用程式", "app_store": "應用程式商店", - "app_store_description": "連接人、技術,與工作區。", + "app_store_description": "連線人員、技術,與工作場所。", "settings": "設定", "event_type_moved_successfully": "成功搬移活動類型", "next_step": "跳過步驟", @@ -676,7 +676,7 @@ "trending_apps": "熱門應用程式", "all_apps": "所有應用程式", "installed_apps": "已安裝的應用程式", - "manage_your_connected_apps": "管理已安裝的應用程式或是更改設定", + "manage_your_connected_apps": "管理已安裝的應用程式或是變更設定", "browse_apps": "瀏覽應用程式", "features": "功能", "permissions": "權限", @@ -721,8 +721,8 @@ "time_format": "時間格式", "12_hour": "12 小時", "24_hour": "24 小時", - "redirect_success_booking": "重新導向於預約 ", - "you_are_being_redirected": "您將在 $t(second, {\"count\": {{seconds}} }) 秒後被重新導向至 {{ url }}。", + "redirect_success_booking": "在預約時重新導向 ", + "you_are_being_redirected": "將於 $t(second, {\"count\": {{seconds}} }) 秒後將您重新導向至 {{ url }}。", "external_redirect_url": "https://example.com/redirect-to-my-success-page", "redirect_url_upgrade_description": "您必須升級至專業版帳號才能使用此功能。", "duplicate": "複製", @@ -735,7 +735,7 @@ "personal_note": "為此金鑰命名", "personal_note_placeholder": "例如:開發", "api_key_no_note": "未命名的 API 金鑰", - "api_key_never_expires": "此 API 無到期日", + "api_key_never_expires": "此 API 金鑰無到期日", "edit_api_key": "編輯 API 金鑰", "never_expire_key": "永不過期", "delete_api_key": "撤銷 API 金鑰", @@ -758,7 +758,7 @@ "reason_for_reschedule": "重新預約原因", "book_a_new_time": "預約新時間", "reschedule_request_sent": "已傳送重新預約請求", - "reschedule_modal_description": "這將取消已預約的會議,通知預約者,並要求他們選擇新時間。", + "reschedule_modal_description": "這將取消已預約的會議,通知預約者,並請他們選擇新時間。", "reason_for_reschedule_request": "重新預約請求的原因", "send_reschedule_request": "要求重新預約 ", "edit_booking": "編輯預約", diff --git a/apps/web/public/static/locales/zh-TW/vital.json b/apps/web/public/static/locales/zh-TW/vital.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/apps/web/public/static/locales/zh-TW/vital.json @@ -0,0 +1 @@ +{} diff --git a/apps/web/server/routers/viewer.tsx b/apps/web/server/routers/viewer.tsx index b5d84e025c..2153fb87d6 100644 --- a/apps/web/server/routers/viewer.tsx +++ b/apps/web/server/routers/viewer.tsx @@ -371,7 +371,6 @@ const loggedInViewerRouter = createProtectedRouter() }; const passedBookingsFilter = bookingListingFilters[bookingListingByStatus]; const orderBy = bookingListingOrderby[bookingListingByStatus]; - const bookingsQuery = await prisma.booking.findMany({ where: { OR: [ @@ -440,7 +439,7 @@ const loggedInViewerRouter = createProtectedRouter() endTime: booking.endTime.toISOString(), }; }); - + const bookingsFetched = bookings.length; const seenBookings: Record = {}; // Remove duplicate recurring bookings for upcoming status. @@ -461,7 +460,8 @@ const loggedInViewerRouter = createProtectedRouter() } let nextCursor: typeof skip | null = skip; - if (bookings.length > take) { + + if (bookingsFetched > take) { bookings.shift(); nextCursor += bookings.length; } else { diff --git a/apps/web/server/routers/viewer/eventTypes.tsx b/apps/web/server/routers/viewer/eventTypes.tsx index dd225c1804..4fb76c6a4e 100644 --- a/apps/web/server/routers/viewer/eventTypes.tsx +++ b/apps/web/server/routers/viewer/eventTypes.tsx @@ -1,11 +1,11 @@ import { EventTypeCustomInput, MembershipRole, PeriodType, Prisma } from "@prisma/client"; +import { PrismaClientKnownRequestError } from "@prisma/client/runtime"; import { z } from "zod"; import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug"; import { _DestinationCalendarModel, _EventTypeCustomInputModel, _EventTypeModel } from "@calcom/prisma/zod"; import { stringOrNumber } from "@calcom/prisma/zod-utils"; import { createEventTypeInput } from "@calcom/prisma/zod/custom/eventtype"; -import { RecurringEvent } from "@calcom/types/Calendar"; import { createProtectedRouter } from "@server/createRouter"; import { viewerRouter } from "@server/routers/viewer"; @@ -150,9 +150,17 @@ export const eventTypesRouter = createProtectedRouter() data.schedulingType = schedulingType; } - const eventType = await ctx.prisma.eventType.create({ data }); - - return { eventType }; + try { + const eventType = await ctx.prisma.eventType.create({ data }); + return { eventType }; + } catch (e) { + if (e instanceof PrismaClientKnownRequestError) { + if (e.code === "P2002" && Array.isArray(e.meta?.target) && e.meta?.target.includes("slug")) { + throw new TRPCError({ code: "BAD_REQUEST", message: "URL Slug already exists for given user." }); + } + } + throw e; + } }, }) // Prevent non-owners to update/delete a team event diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js index 3a93df3593..e1f9b8e7bc 100644 --- a/apps/web/tailwind.config.js +++ b/apps/web/tailwind.config.js @@ -4,6 +4,6 @@ module.exports = { content: [ ...base.content, "../../packages/ui/**/*.{js,ts,jsx,tsx}", - "../../packages/app-store/**/components/*.{js,ts,jsx,tsx}", + "../../packages/app-store/**/{components,pages}/**/*.{js,ts,jsx,tsx}", ], }; diff --git a/packages/app-store/_components/DynamicComponent.tsx b/packages/app-store/_components/DynamicComponent.tsx new file mode 100644 index 0000000000..25ba7288a6 --- /dev/null +++ b/packages/app-store/_components/DynamicComponent.tsx @@ -0,0 +1,9 @@ +export function DynamicComponent>(props: { componentMap: T; slug: string }) { + const { componentMap, slug, ...rest } = props; + + if (!componentMap[slug]) return null; + + const Component = componentMap[slug]; + + return ; +} diff --git a/packages/app-store/_pages/setup/_getStaticProps.tsx b/packages/app-store/_pages/setup/_getStaticProps.tsx new file mode 100644 index 0000000000..0e8b631199 --- /dev/null +++ b/packages/app-store/_pages/setup/_getStaticProps.tsx @@ -0,0 +1,20 @@ +import { GetStaticPropsContext } from "next"; + +export const AppSetupPageMap = { + zapier: import("../../zapier/pages/setup/_getStaticProps"), +}; + +export const getStaticProps = async (ctx: GetStaticPropsContext) => { + const { slug } = ctx.params || {}; + if (typeof slug !== "string") return { notFound: true } as const; + + if (!(slug in AppSetupPageMap)) return { props: {} }; + + const page = await AppSetupPageMap[slug as keyof typeof AppSetupPageMap]; + + if (!page.getStaticProps) return { props: {} }; + + const props = await page.getStaticProps(ctx); + + return props; +}; diff --git a/packages/app-store/_pages/setup/index.tsx b/packages/app-store/_pages/setup/index.tsx new file mode 100644 index 0000000000..b4e14e8139 --- /dev/null +++ b/packages/app-store/_pages/setup/index.tsx @@ -0,0 +1,13 @@ +import dynamic from "next/dynamic"; + +import { DynamicComponent } from "../../_components/DynamicComponent"; + +export const AppSetupMap = { + zapier: dynamic(() => import("../../zapier/pages/setup")), +}; + +export const AppSetupPage = (props: { slug: string }) => { + return componentMap={AppSetupMap} {...props} />; +}; + +export default AppSetupPage; diff --git a/packages/app-store/giphy/README.mdx b/packages/app-store/giphy/README.mdx new file mode 100644 index 0000000000..6ad3e8ff89 --- /dev/null +++ b/packages/app-store/giphy/README.mdx @@ -0,0 +1,9 @@ +--- +items: + - /api/app-store/giphy/GIPHY1.png + - /api/app-store/giphy/GIPHY2.png +--- + + + +An online database and search engine that allows users to search for and share short looping videos with no sound that resemble animated GIF files. GIPHY is your top source for the best & newest GIFs & Animated Stickers online. Find everything from funny GIFs, reaction GIFs, unique GIFs and more to add to your custom booking page. Located under advanced settings in each event type. \ No newline at end of file diff --git a/packages/app-store/giphy/static/GIPHY1.png b/packages/app-store/giphy/static/GIPHY1.png new file mode 100644 index 0000000000000000000000000000000000000000..c188596869c15e718b1abd4ed194d58eb6c6cf71 GIT binary patch literal 57610 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 z

    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 { + async updateEvent(uid: string, event: CalendarEvent, externalCalendarId: string): Promise { return new Promise(async (resolve, reject) => { const auth = await this.auth; const myGoogleAuth = await auth.getToken(); @@ -194,9 +195,7 @@ export default class GoogleCalendarService implements Calendar { calendar.events.update( { auth: myGoogleAuth, - calendarId: event.destinationCalendar?.externalId - ? event.destinationCalendar.externalId - : "primary", + calendarId: externalCalendarId ? externalCalendarId : event.destinationCalendar?.externalId, eventId: uid, sendNotifications: true, sendUpdates: "all", @@ -214,7 +213,7 @@ export default class GoogleCalendarService implements Calendar { }); } - async deleteEvent(uid: string, event: CalendarEvent): Promise { + async deleteEvent(uid: string, event: CalendarEvent, externalCalendarId: string): Promise { return new Promise(async (resolve, reject) => { const auth = await this.auth; const myGoogleAuth = await auth.getToken(); @@ -225,9 +224,7 @@ export default class GoogleCalendarService implements Calendar { calendar.events.delete( { auth: myGoogleAuth, - calendarId: event.destinationCalendar?.externalId - ? event.destinationCalendar.externalId - : "primary", + calendarId: externalCalendarId ? externalCalendarId : event.destinationCalendar?.externalId, eventId: uid, sendNotifications: true, sendUpdates: "all", diff --git a/packages/app-store/googlevideo/_metadata.ts b/packages/app-store/googlevideo/_metadata.ts index 32889bc4fc..aa1e85ee9a 100644 --- a/packages/app-store/googlevideo/_metadata.ts +++ b/packages/app-store/googlevideo/_metadata.ts @@ -12,9 +12,9 @@ export const metadata = { category: "video", type: "google_video", title: "Google Meet", - imageSrc: "https://cdn.iconscout.com/icon/free/png-256/google-meet-2923654-2416657.png", + imageSrc: "/api/app-store/googlevideo/logo.webp", variant: "conferencing", - logo: "https://cdn.iconscout.com/icon/free/png-256/google-meet-2923654-2416657.png", + logo: "/api/app-store/googlevideo/logo.webp", publisher: "Cal.com", rating: 5, reviews: 69, diff --git a/packages/app-store/googlevideo/static/logo.webp b/packages/app-store/googlevideo/static/logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..5443580ff53f3fa018368dc9a85f40ee0d3ee978 GIT binary patch literal 4328 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 { @@ -142,12 +142,14 @@ export default class Office365CalendarService implements Calendar { return responseBody.responses.reduce( (acc: BufferedBusyTime[], subResponse: { body: { value: any[] } }) => acc.concat( - subResponse.body.value.map((evt) => { - return { - start: evt.start.dateTime + "Z", - end: evt.end.dateTime + "Z", - }; - }) + subResponse.body.value + .filter((evt) => evt.showAs !== "free" && evt.showAs !== "workingElsewhere") + .map((evt) => { + return { + start: evt.start.dateTime + "Z", + end: evt.end.dateTime + "Z", + }; + }) ), [] ); diff --git a/packages/app-store/office365video/api/add.ts b/packages/app-store/office365video/api/add.ts index eeb7bf948d..99a8778ba6 100644 --- a/packages/app-store/office365video/api/add.ts +++ b/packages/app-store/office365video/api/add.ts @@ -12,7 +12,7 @@ let client_id = ""; export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === "GET") { - const appKeys = await getAppKeysFromSlug("office365-calendar"); + const appKeys = await getAppKeysFromSlug("msteams"); if (typeof appKeys.client_id === "string") client_id = appKeys.client_id; if (!client_id) return res.status(400).json({ message: "Office 365 client_id missing." }); const state = encodeOAuthState(req); diff --git a/packages/app-store/office365video/api/callback.ts b/packages/app-store/office365video/api/callback.ts index cb3b55e7d8..d64b55a443 100644 --- a/packages/app-store/office365video/api/callback.ts +++ b/packages/app-store/office365video/api/callback.ts @@ -21,7 +21,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return; } - const appKeys = await getAppKeysFromSlug("office365-calendar"); + const appKeys = await getAppKeysFromSlug("msteams"); if (typeof appKeys.client_id === "string") client_id = appKeys.client_id; if (typeof appKeys.client_secret === "string") client_secret = appKeys.client_secret; if (!client_id) return res.status(400).json({ message: "Office 365 client_id missing." }); diff --git a/packages/app-store/office365video/components/AccountDialog.tsx b/packages/app-store/office365video/components/AccountDialog.tsx index 42d5864021..dc99603b78 100644 --- a/packages/app-store/office365video/components/AccountDialog.tsx +++ b/packages/app-store/office365video/components/AccountDialog.tsx @@ -11,12 +11,12 @@ import { import useAddAppMutation from "../../_utils/useAddAppMutation"; export function AccountDialog(props: DialogProps) { - const mutation = useAddAppMutation("office365_calendar"); + const mutation = useAddAppMutation("office365_video"); return (

    diff --git a/packages/app-store/types.d.ts b/packages/app-store/types.d.ts index 8abc2d705a..cc0d97d60a 100644 --- a/packages/app-store/types.d.ts +++ b/packages/app-store/types.d.ts @@ -1,3 +1,5 @@ +import { ButtonBaseProps } from "@calcom/ui/Button"; + export type IntegrationOAuthCallbackState = { returnTo: string; }; diff --git a/packages/app-store/utils.ts b/packages/app-store/utils.ts index 47f24cef16..ebfb043a22 100644 --- a/packages/app-store/utils.ts +++ b/packages/app-store/utils.ts @@ -36,7 +36,8 @@ function translateLocations(locations: OptionTypeBase[], t: TFunction) { const defaultLocations: OptionTypeBase[] = [ { value: LocationType.InPerson, label: "in_person_meeting" }, { value: LocationType.Link, label: "link_meeting" }, - { value: LocationType.Phone, label: "phone_call" }, + { value: LocationType.Phone, label: "attendee_phone_number" }, + { value: LocationType.UserPhone, label: "host_phone_number" }, ]; export function getLocationOptions(integrations: AppMeta, t: TFunction) { diff --git a/packages/app-store/wipemycalother/_metadata.ts b/packages/app-store/wipemycalother/_metadata.ts index 7371fde564..6743230977 100644 --- a/packages/app-store/wipemycalother/_metadata.ts +++ b/packages/app-store/wipemycalother/_metadata.ts @@ -8,8 +8,8 @@ export const metadata = { installed: true, category: "other", // If using static next public folder, can then be referenced from the base URL (/). - imageSrc: "/api/app-store/_example/icon.svg", - logo: "/api/app-store/_example/icon.svg", + imageSrc: "/api/app-store/wipemycalother/icon.svg", + logo: "/api/app-store/wipemycalother/icon.svg", publisher: "Cal.com", rating: 0, reviews: 0, diff --git a/packages/app-store/zapier/README.md b/packages/app-store/zapier/README.md index f00a3752c1..80abd1165f 100644 --- a/packages/app-store/zapier/README.md +++ b/packages/app-store/zapier/README.md @@ -56,9 +56,9 @@ Booking created, Booking rescheduled, Booking cancelled Create the other two triggers (booking rescheduled, booking cancelled) exactly like this one, just use the appropriate naming (e.g. booking_rescheduled instead of booking_created) -### Testing integration +### Set ZAPIER_INVITE_LINK -Use the sharing link under Manage → Sharing to create your first Cal.com trigger in Zapier +The invite link can be found under under Manage → Sharing. ## Localhost diff --git a/packages/app-store/zapier/README.mdx b/packages/app-store/zapier/README.mdx index 52d0240a08..ca51a99e12 100644 --- a/packages/app-store/zapier/README.mdx +++ b/packages/app-store/zapier/README.mdx @@ -2,5 +2,4 @@ Workflow automation for everyone. Use the Cal.com Zapier app to trigger your wor
    **After Installation:** You lost your generated API key? Here you can generate a new key and find all information -on how to use the installed app:
    Zapier App Setup - +on how to use the installed app: Zapier App Setup diff --git a/packages/app-store/zapier/api/add.ts b/packages/app-store/zapier/api/add.ts index 0c01b593cc..9f1cf72ba0 100644 --- a/packages/app-store/zapier/api/add.ts +++ b/packages/app-store/zapier/api/add.ts @@ -35,5 +35,5 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.status(500); } - return res.status(200).json({ url: "/apps/setup/zapier" }); + return res.status(200).json({ url: "/apps/zapier/setup" }); } diff --git a/packages/app-store/zapier/api/index.ts b/packages/app-store/zapier/api/index.ts index f3f3d07ad7..d32a6d07b0 100644 --- a/packages/app-store/zapier/api/index.ts +++ b/packages/app-store/zapier/api/index.ts @@ -2,3 +2,4 @@ export { default as add } from "./add"; export { default as listBookings } from "./subscriptions/listBookings"; export { default as deleteSubscription } from "./subscriptions/deleteSubscription"; export { default as addSubscription } from "./subscriptions/addSubscription"; +export { default as me } from "./subscriptions/me"; diff --git a/packages/app-store/zapier/api/subscriptions/listBookings.ts b/packages/app-store/zapier/api/subscriptions/listBookings.ts index 8751b06bea..161c86c84a 100644 --- a/packages/app-store/zapier/api/subscriptions/listBookings.ts +++ b/packages/app-store/zapier/api/subscriptions/listBookings.ts @@ -29,7 +29,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) endTime: true, title: true, location: true, - cancellationReason: true, attendees: { select: { name: true, diff --git a/packages/app-store/zapier/api/subscriptions/me.ts b/packages/app-store/zapier/api/subscriptions/me.ts new file mode 100644 index 0000000000..1131873042 --- /dev/null +++ b/packages/app-store/zapier/api/subscriptions/me.ts @@ -0,0 +1,35 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +import findValidApiKey from "@calcom/ee/lib/api/findValidApiKey"; +import prisma from "@calcom/prisma"; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const apiKey = req.query.apiKey as string; + + if (!apiKey) { + return res.status(401).json({ message: "No API key provided" }); + } + + const validKey = await findValidApiKey(apiKey, "zapier"); + + if (!validKey) { + return res.status(401).json({ message: "API key not valid" }); + } + + if (req.method === "GET") { + try { + const user = await prisma.user.findFirst({ + where: { + id: validKey.userId, + }, + select: { + username: true, + }, + }); + res.status(201).json(user); + } catch (error) { + console.error(error); + return res.status(500).json({ message: "Unable to get User." }); + } + } +} diff --git a/packages/app-store/zapier/components/index.ts b/packages/app-store/zapier/components/index.ts index 5f2c7965c9..e191e97eec 100644 --- a/packages/app-store/zapier/components/index.ts +++ b/packages/app-store/zapier/components/index.ts @@ -1,3 +1,2 @@ export { default as InstallAppButton } from "./InstallAppButton"; -export { default as ZapierSetup } from "./zapierSetup"; export { default as Icon } from "./icon"; diff --git a/packages/app-store/zapier/pages/setup/_getStaticProps.tsx b/packages/app-store/zapier/pages/setup/_getStaticProps.tsx new file mode 100644 index 0000000000..b73a0786cc --- /dev/null +++ b/packages/app-store/zapier/pages/setup/_getStaticProps.tsx @@ -0,0 +1,20 @@ +import { GetStaticPropsContext } from "next"; + +import getAppKeysFromSlug from "../../../_utils/getAppKeysFromSlug"; + +export interface IZapierSetupProps { + inviteLink: string; +} + +export const getStaticProps = async (ctx: GetStaticPropsContext) => { + if (typeof ctx.params?.slug !== "string") return { notFound: true } as const; + let inviteLink = ""; + const appKeys = await getAppKeysFromSlug("zapier"); + if (typeof appKeys.invite_link === "string") inviteLink = appKeys.invite_link; + + return { + props: { + inviteLink, + }, + }; +}; diff --git a/packages/app-store/zapier/components/zapierSetup.tsx b/packages/app-store/zapier/pages/setup/index.tsx similarity index 85% rename from packages/app-store/zapier/components/zapierSetup.tsx rename to packages/app-store/zapier/pages/setup/index.tsx index 362135c8cf..5c28b2671b 100644 --- a/packages/app-store/zapier/components/zapierSetup.tsx +++ b/packages/app-store/zapier/pages/setup/index.tsx @@ -2,28 +2,31 @@ import { ClipboardCopyIcon } from "@heroicons/react/solid"; import { Trans } from "next-i18next"; import Link from "next/link"; import { useState } from "react"; +import { Toaster } from "react-hot-toast"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; -import { Button } from "@calcom/ui"; -import { Tooltip } from "@calcom/ui/Tooltip"; -import Loader from "@calcom/web/components/Loader"; +import { Button, Loader, Tooltip } from "@calcom/ui"; -import Icon from "./icon"; +/** TODO: Maybe extract this into a package to prevent circular dependencies */ +import { trpc } from "@calcom/web/lib/trpc"; -interface IZapierSetupProps { - trpc: any; +import Icon from "../../components/icon"; + +export interface IZapierSetupProps { + inviteLink: string; } const ZAPIER = "zapier"; export default function ZapierSetup(props: IZapierSetupProps) { - const { trpc } = props; const [newApiKey, setNewApiKey] = useState(""); const { t } = useLocale(); const utils = trpc.useContext(); const integrations = trpc.useQuery(["viewer.integrations"]); + // @ts-ignore const oldApiKey = trpc.useQuery(["viewer.apiKeys.findKeyOfType", { appId: ZAPIER }]); + const deleteApiKey = trpc.useMutation("viewer.apiKeys.delete"); const zapierCredentials: { credentialIds: number[] } | undefined = integrations.data?.other?.items.find( (item: { type: string }) => item.type === "zapier_other" @@ -33,6 +36,7 @@ export default function ZapierSetup(props: IZapierSetupProps) { async function createApiKey() { const event = { note: "Zapier", expiresAt: null, appId: ZAPIER }; + // @ts-ignore const apiKey = await utils.client.mutation("viewer.apiKeys.create", event); if (oldApiKey.data) { deleteApiKey.mutate({ @@ -91,8 +95,14 @@ export default function ZapierSetup(props: IZapierSetupProps) { )} -
      +
        +
      1. + Go to: + + Zapier Invite Link + +
      2. Log into your Zapier account and create a new Zap.
      3. Select Cal.com as your Trigger app. Also choose a Trigger event.
      4. Choose your account and then enter your Unique API Key.
      5. @@ -116,6 +126,7 @@ export default function ZapierSetup(props: IZapierSetupProps) {
    )} +
    ); } diff --git a/packages/core/CalendarManager.ts b/packages/core/CalendarManager.ts index 9159dd6c52..126b21c5d7 100644 --- a/packages/core/CalendarManager.ts +++ b/packages/core/CalendarManager.ts @@ -128,19 +128,24 @@ export const createEvent = async (credential: Credential, calEvent: CalendarEven export const updateEvent = async ( credential: Credential, calEvent: CalendarEvent, - bookingRefUid: string | null + bookingRefUid: string | null, + externalCalendarId: string | null ): Promise => { const uid = getUid(calEvent); const calendar = getCalendar(credential); - let success = true; - + let success = false; + if (bookingRefUid === "") { + log.error("updateEvent failed", "bookingRefUid is empty", calEvent, credential); + } const updatedResult = calendar && bookingRefUid - ? await calendar.updateEvent(bookingRefUid, calEvent).catch((e) => { - log.error("updateEvent failed", e, calEvent); - success = false; - return undefined; - }) + ? await calendar + .updateEvent(bookingRefUid, calEvent, externalCalendarId) + .then(() => (success = true)) + .catch((e) => { + log.error("updateEvent failed", e, calEvent); + return undefined; + }) : undefined; return { diff --git a/packages/core/EventManager.ts b/packages/core/EventManager.ts index fdfb1c0c42..27adf6fa56 100644 --- a/packages/core/EventManager.ts +++ b/packages/core/EventManager.ts @@ -146,6 +146,7 @@ export default class EventManager { meetingId: result.createdEvent?.id.toString(), meetingPassword: result.createdEvent?.password, meetingUrl: result.createdEvent?.url, + externalCalendarId: evt.destinationCalendar?.externalId, }; }); @@ -188,6 +189,7 @@ export default class EventManager { meetingId: true, meetingPassword: true, meetingUrl: true, + externalCalendarId: true, }, }, destinationCalendar: true, @@ -345,11 +347,20 @@ export default class EventManager { booking: PartialBooking ): Promise> { return async.mapLimit(this.calendarCredentials, 5, async (credential: Credential) => { + // HACK: + // Right now if two calendars are connected and a booking is created it has two bookingReferences, one is having uid null and the other is having valid uid. + // I don't know why yet - But we should work on fixing that. But even after the fix as there can be multiple references in an existing booking the following ref.uid check would still be required + // We should ignore the one with uid null, the other one is valid. + // Also, we should store(if not already) that which is the calendarCredential for the valid bookingReference, instead of going through all credentials one by one const bookingRefUid = booking - ? booking.references.filter((ref) => ref.type === credential.type)[0]?.uid + ? booking.references.filter((ref) => ref.type === credential.type && !!ref.uid)[0]?.uid : null; - return updateEvent(credential, event, bookingRefUid); + const bookingExternalCalendarId = booking.references + ? booking.references.filter((ref) => ref.type === credential.type)[0].externalCalendarId + : null; + + return updateEvent(credential, event, bookingRefUid, bookingExternalCalendarId!); }); } diff --git a/packages/embeds/embed-core/playwright/lib/testUtils.ts b/packages/embeds/embed-core/playwright/lib/testUtils.ts index 62797e4fb3..1ab43dad74 100644 --- a/packages/embeds/embed-core/playwright/lib/testUtils.ts +++ b/packages/embeds/embed-core/playwright/lib/testUtils.ts @@ -83,11 +83,7 @@ export async function bookFirstEvent(username: string, frame: Frame, page: Page) const responseObj = await response.json(); const bookingId = responseObj.uid; // Make sure we're navigated to the success page - await frame.waitForNavigation({ - url(url) { - return url.pathname.endsWith("/success"); - }, - }); + await expect(page.locator("[data-testid=success-page]")).toBeVisible(); expect(await page.screenshot()).toMatchSnapshot("success-page.png"); return bookingId; } diff --git a/packages/prisma/migrations/20220503183922_add_external_calendar_id_to_booking_reference/migration.sql b/packages/prisma/migrations/20220503183922_add_external_calendar_id_to_booking_reference/migration.sql new file mode 100644 index 0000000000..66960d7d63 --- /dev/null +++ b/packages/prisma/migrations/20220503183922_add_external_calendar_id_to_booking_reference/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "BookingReference" ADD COLUMN "externalCalendarId" TEXT; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index a6c3cbabf8..664623bc61 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -223,6 +223,7 @@ model BookingReference { meetingUrl String? booking Booking? @relation(fields: [bookingId], references: [id], onDelete: Cascade) bookingId Int? + externalCalendarId String? deleted Boolean? } diff --git a/packages/prisma/seed-app-store.ts b/packages/prisma/seed-app-store.ts index 905eb00907..87e4b3cc10 100644 --- a/packages/prisma/seed-app-store.ts +++ b/packages/prisma/seed-app-store.ts @@ -50,7 +50,10 @@ async function main() { client_id: process.env.MS_GRAPH_CLIENT_ID, client_secret: process.env.MS_GRAPH_CLIENT_SECRET, }); - await createApp("msteams", "office365video", ["video"], "office365_video"); + await createApp("msteams", "office365video", ["video"], "office365_video", { + client_id: process.env.MS_GRAPH_CLIENT_ID, + client_secret: process.env.MS_GRAPH_CLIENT_SECRET, + }); } // Video apps if (process.env.DAILY_API_KEY) { @@ -95,7 +98,12 @@ async function main() { webhook_secret: process.env.VITAL_WEBHOOK_SECRET, }); } - await createApp("zapier", "zapier", ["other"], "zapier_other"); + + if (process.env.ZAPIER_INVITE_LINK) { + await createApp("zapier", "zapier", ["other"], "zapier_other", { + invite_link: process.env.ZAPIER_INVITE_LINK, + }); + } // Web3 apps await createApp("huddle01", "huddle01video", ["web3", "video"], "huddle01_video"); await createApp("metamask", "metamask", ["web3"], "metamask_web3"); diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index dbd964b7dc..9148f54dbc 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -9,6 +9,7 @@ export const eventTypeLocations = z.array( type: z.nativeEnum(LocationType), address: z.string().optional(), link: z.string().url().optional(), + hostPhoneNumber: z.string().optional(), }) ); diff --git a/packages/types/Calendar.d.ts b/packages/types/Calendar.d.ts index 57f8386f88..2ca2636c3f 100644 --- a/packages/types/Calendar.d.ts +++ b/packages/types/Calendar.d.ts @@ -60,7 +60,13 @@ export type BatchResponse = { }; export type SubResponse = { - body: { value: { start: { dateTime: string }; end: { dateTime: string } }[] }; + body: { + value: { + showAs: "free" | "tentative" | "away" | "busy" | "workingElsewhere"; + start: { dateTime: string }; + end: { dateTime: string }; + }[]; + }; }; export interface ConferenceData { @@ -134,9 +140,13 @@ export interface IntegrationCalendar extends Ensure, " export interface Calendar { createEvent(event: CalendarEvent): Promise; - updateEvent(uid: string, event: CalendarEvent): Promise; + updateEvent( + uid: string, + event: CalendarEvent, + externalCalendarId?: string | null + ): Promise; - deleteEvent(uid: string, event: CalendarEvent): Promise; + deleteEvent(uid: string, event: CalendarEvent, externalCalendarId?: string | null): Promise; getAvailability( dateFrom: string, diff --git a/packages/types/EventManager.d.ts b/packages/types/EventManager.d.ts index 27298b822d..4a68f7b968 100644 --- a/packages/types/EventManager.d.ts +++ b/packages/types/EventManager.d.ts @@ -8,6 +8,7 @@ export interface PartialReference { meetingId?: string | null; meetingPassword?: string | null; meetingUrl?: string | null; + externalCalendarId?: string | null; } export interface EventResult { diff --git a/packages/ui/Button.tsx b/packages/ui/Button.tsx index 664a269f57..42800c8131 100644 --- a/packages/ui/Button.tsx +++ b/packages/ui/Button.tsx @@ -6,7 +6,7 @@ import classNames from "@calcom/lib/classNames"; type SVGComponent = React.FunctionComponent>; export type ButtonBaseProps = { - color?: "primary" | "secondary" | "minimal" | "warn"; + color?: "primary" | "secondary" | "minimal" | "warn" | "alert" | "alert2"; size?: "base" | "sm" | "lg" | "fab" | "icon"; loading?: boolean; disabled?: boolean; @@ -70,6 +70,14 @@ export const Button = forwardRef )} diff --git a/packages/ui/Loader.tsx b/packages/ui/Loader.tsx new file mode 100644 index 0000000000..29ac50d738 --- /dev/null +++ b/packages/ui/Loader.tsx @@ -0,0 +1,7 @@ +export default function Loader() { + return ( +
    + +
    + ); +} diff --git a/packages/ui/index.tsx b/packages/ui/index.tsx index c1523e7817..f466516ef2 100644 --- a/packages/ui/index.tsx +++ b/packages/ui/index.tsx @@ -1,6 +1,7 @@ export { default as Button } from "./Button"; export { default as EmptyScreen } from "./EmptyScreen"; export { default as Select } from "./form/Select"; +export { default as Loader } from "./Loader"; export * from "./skeleton"; export { default as Switch } from "./Switch"; export { default as Tooltip } from "./Tooltip"; diff --git a/tests/config/playwright.config.ts b/tests/config/playwright.config.ts index 9534c9ac0a..1f9438372f 100644 --- a/tests/config/playwright.config.ts +++ b/tests/config/playwright.config.ts @@ -1,5 +1,6 @@ -import { PlaywrightTestConfig, devices } from "@playwright/test"; +import { devices, PlaywrightTestConfig } from "@playwright/test"; import { addAliases } from "module-alias"; +import * as os from "os"; import * as path from "path"; // Add aliases for the paths specified in the tsconfig.json file. @@ -14,14 +15,21 @@ addAliases({ "@ee": __dirname + "/apps/web/ee", }); +require("dotenv").config({ path: "../../.env" }); + const outputDir = path.join(__dirname, "..", "..", "test-results"); const testDir = path.join(__dirname, "..", "..", "apps/web/playwright"); +const DEFAULT_NAVIGATION_TIMEOUT = 15000; + +const headless = !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS; + const config: PlaywrightTestConfig = { forbidOnly: !!process.env.CI, retries: 1, - workers: 1, + workers: os.cpus().length, timeout: 60_000, + maxFailures: headless ? 10 : undefined, reporter: [ [process.env.CI ? "github" : "list"], ["html", { outputFolder: "./playwright/reports/playwright-html-report", open: "never" }], @@ -36,16 +44,20 @@ const config: PlaywrightTestConfig = { reuseExistingServer: !process.env.CI, }, use: { - baseURL: "http://localhost:3000", + baseURL: "http://localhost:3000/", locale: "en-US", trace: "retain-on-failure", - headless: !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS, + headless, }, projects: [ { name: "chromium", testDir, - use: { ...devices["Desktop Chrome"] }, + use: { + ...devices["Desktop Chrome"], + /** If navigation takes more than this, then something's wrong, let's fail fast. */ + navigationTimeout: DEFAULT_NAVIGATION_TIMEOUT, + }, }, /* { name: "firefox", diff --git a/turbo.json b/turbo.json index c818c78f0c..e2257f66f9 100644 --- a/turbo.json +++ b/turbo.json @@ -132,7 +132,7 @@ }, "test-e2e": { "cache": false, - "dependsOn": ["@calcom/web#test", "@calcom/web#build", "@calcom/prisma#db-reset"] + "dependsOn": ["@calcom/prisma#db-reset", "@calcom/web#test", "@calcom/web#build"] }, "type-check": { "outputs": [] From 9921c43c3faaac3679e2019d6b8f329a3c24739d Mon Sep 17 00:00:00 2001 From: zomars Date: Mon, 16 May 2022 17:24:29 -0600 Subject: [PATCH 2/8] Update [user].ts --- apps/web/pages/api/availability/[user].ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/pages/api/availability/[user].ts b/apps/web/pages/api/availability/[user].ts index 6e45678cce..324e5f12cc 100644 --- a/apps/web/pages/api/availability/[user].ts +++ b/apps/web/pages/api/availability/[user].ts @@ -16,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const user = asStringOrNull(req.query.user); const dateFrom = dayjs(asStringOrNull(req.query.dateFrom)); const dateTo = dayjs(asStringOrNull(req.query.dateTo)); - const eventTypeId = parseInt(asStringOrNull(req.query.eventTypeId) || ""); + const eventTypeId = typeof req.query.eventTypeId === "string" ? parseInt(req.query.eventTypeId) : undefined; if (!dateFrom.isValid() || !dateTo.isValid()) { return res.status(400).json({ message: "Invalid time range given." }); From 8265c65e4c950f4cc59e516ff323485a5bddb6a1 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 17 May 2022 14:25:16 +0100 Subject: [PATCH 3/8] 2FA submit disabled (#2790) (#2792) Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> --- apps/web/pages/auth/login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/pages/auth/login.tsx b/apps/web/pages/auth/login.tsx index 87b40f98a3..b76c6499c6 100644 --- a/apps/web/pages/auth/login.tsx +++ b/apps/web/pages/auth/login.tsx @@ -159,7 +159,7 @@ export default function Login({
    From 3b1dff3b83d66da4968d1ac4f4294d661f41be50 Mon Sep 17 00:00:00 2001 From: zomars Date: Tue, 17 May 2022 10:52:45 -0600 Subject: [PATCH 4/8] Fixes infinite loop --- apps/web/pages/success.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/pages/success.tsx b/apps/web/pages/success.tsx index a995fd51b3..618806a1bf 100644 --- a/apps/web/pages/success.tsx +++ b/apps/web/pages/success.tsx @@ -200,7 +200,8 @@ export default function Success(props: SuccessProps) { }); setDate(date.tz(localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess())); setIs24h(!!localStorage.getItem("timeOption.is24hClock")); - }, [date, eventType, needsConfirmation]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [eventType, needsConfirmation]); function eventLink(): string { const optional: { location?: string } = {}; From 6771960e9eecbbcd162e3a38aa0c51512a0571f7 Mon Sep 17 00:00:00 2001 From: zomars Date: Tue, 17 May 2022 10:52:45 -0600 Subject: [PATCH 5/8] Fixes infinite loop --- apps/web/ee/components/stripe/PaymentPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/ee/components/stripe/PaymentPage.tsx b/apps/web/ee/components/stripe/PaymentPage.tsx index 58852d28b9..b68fdbbc50 100644 --- a/apps/web/ee/components/stripe/PaymentPage.tsx +++ b/apps/web/ee/components/stripe/PaymentPage.tsx @@ -49,7 +49,8 @@ const PaymentPage: FC = (props) => { embedIframeWidth = e.detail.data.iframeWidth as number; }); } - }, [date, isEmbed]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isEmbed]); const eventName = props.booking.title; From 25c550de3a49591828c3d1552a055b3995b8b528 Mon Sep 17 00:00:00 2001 From: zomars Date: Tue, 17 May 2022 10:52:45 -0600 Subject: [PATCH 6/8] Fixes infinite loop --- apps/web/pages/auth/logout.tsx | 10 +++++----- apps/web/pages/auth/sso/[provider].tsx | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/web/pages/auth/logout.tsx b/apps/web/pages/auth/logout.tsx index 70fcb675c6..1be2a675f6 100644 --- a/apps/web/pages/auth/logout.tsx +++ b/apps/web/pages/auth/logout.tsx @@ -1,14 +1,13 @@ import { CheckIcon } from "@heroicons/react/outline"; import { GetServerSidePropsContext } from "next"; -import { useSession, signOut } from "next-auth/react"; -import { getCookieParser } from "next/dist/server/api-utils"; +import { signOut, useSession } from "next-auth/react"; import Link from "next/link"; import { useRouter } from "next/router"; import { useEffect } from "react"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import Button from "@calcom/ui/Button"; -import { useLocale } from "@lib/hooks/useLocale"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import AuthContainer from "@components/ui/AuthContainer"; @@ -18,14 +17,15 @@ import { ssrInit } from "@server/lib/ssr"; type Props = inferSSRProps; export default function Logout(props: Props) { - const { data: session, status } = useSession(); + const { status } = useSession(); if (status === "authenticated") signOut({ redirect: false }); const router = useRouter(); useEffect(() => { if (props.query?.survey === "true") { router.push("https://cal.com/cancellation"); } - }, [props.query?.survey, router]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.query?.survey]); const { t } = useLocale(); return ( diff --git a/apps/web/pages/auth/sso/[provider].tsx b/apps/web/pages/auth/sso/[provider].tsx index 1c8e914d41..719caf1f3f 100644 --- a/apps/web/pages/auth/sso/[provider].tsx +++ b/apps/web/pages/auth/sso/[provider].tsx @@ -38,7 +38,8 @@ export default function Provider(props: SSOProviderPageProps) { } else { signIn(props.provider); } - }, [props.isSAMLLoginEnabled, props.product, props.provider, props.tenant, router]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return null; } From 80954aa86647db4bbf3c41d033f071f6fb6a5589 Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Wed, 18 May 2022 15:59:23 +0100 Subject: [PATCH 7/8] Add team Id to hash url (#2803) --- apps/web/pages/event-types/[type].tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/pages/event-types/[type].tsx b/apps/web/pages/event-types/[type].tsx index dae70aa507..3c585166eb 100644 --- a/apps/web/pages/event-types/[type].tsx +++ b/apps/web/pages/event-types/[type].tsx @@ -332,7 +332,7 @@ const EventTypePage = (props: inferSSRProps) => { fetchTokens(); - !hashedUrl && setHashedUrl(generateHashedLink(eventType.users[0].id)); + !hashedUrl && setHashedUrl(generateHashedLink(eventType.users[0]?.id ?? team?.id)); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -2182,6 +2182,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => successRedirectUrl: true, team: { select: { + id: true, slug: true, members: { where: { From afafd452232f7ca2b706c5b150f1e6ff5e9cab0d Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Date: Wed, 18 May 2022 11:34:21 -0400 Subject: [PATCH 8/8] Show success page if booking was deleted on calendar (#2808) * Add exception to 410 * Fix type error * Add GoogelCalError type --- packages/app-store/googlecalendar/lib/CalendarService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/app-store/googlecalendar/lib/CalendarService.ts b/packages/app-store/googlecalendar/lib/CalendarService.ts index 8471ab0c6d..842b741c35 100644 --- a/packages/app-store/googlecalendar/lib/CalendarService.ts +++ b/packages/app-store/googlecalendar/lib/CalendarService.ts @@ -18,6 +18,10 @@ import type { PartialReference } from "@calcom/types/EventManager"; import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug"; +interface GoogleCalError extends Error { + code?: number; +} + export default class GoogleCalendarService implements Calendar { private url = ""; private integrationName = ""; @@ -229,8 +233,9 @@ export default class GoogleCalendarService implements Calendar { sendNotifications: true, sendUpdates: "all", }, - function (err, event) { + function (err: GoogleCalError | null, event) { if (err) { + if (err.code === 410) resolve(); console.error("There was an error contacting google calendar service: ", err); return reject(err); }