>
) => {
+const { t } = useTranslation();
+
const { slug, name, teamName, t } = props;
return (
@@ -24,20 +28,19 @@ export const SlugReplacementEmail = (
title={t("event_replaced_notice")}>
<>
- Hi {name}
+ {t('hi-{name}', { name })}
,
-
- An administrator on the {teamName} team has replaced your event type{" "}
- /{slug} with a managed event type that they control.
-
+ {teamName}>, slug }}
+components={{"0": , "1":
+ }}
+/>
-
- Your link will continue to work but some settings for it may have changed. You can review it in
- event types.
-
+ {t('link-will-continue-to-work-settings-may-have-changed-review-it-in-event-types')}
-
- If you have any questions about the event type, please reach out to your administrator.
-
-
- Happy scheduling,
- The Cal.com team
-
+ {t('questions-about-the-event-type-reach-out-to-your-administrator')}
+
{t('happy-scheduling')}
{t('the-cal-com-team')}
{/*
<>{t("email_body_slug_replacement_suggestion")}>
diff --git a/packages/emails/src/templates/TeamInviteEmail.tsx b/packages/emails/src/templates/TeamInviteEmail.tsx
index 77ee69c4b0b894..12a8ef5c5d1d3b 100644
--- a/packages/emails/src/templates/TeamInviteEmail.tsx
+++ b/packages/emails/src/templates/TeamInviteEmail.tsx
@@ -1,3 +1,4 @@
+import { useTranslation, Trans } from "react-i18next";
import type { TFunction } from "next-i18next";
import { Trans } from "next-i18next";
@@ -24,6 +25,8 @@ type TeamInvite = {
export const TeamInviteEmail = (
props: TeamInvite & Partial>
) => {
+const { t } = useTranslation();
+
const typeOfInvite = getTypeOfInvite(props);
const heading = getHeading();
@@ -46,7 +49,7 @@ export const TeamInviteEmail = (
? `${WEBAPP_URL}/emails/calendar-email-hero.png`
: "http://localhost:3000/emails/calendar-email-hero.png"
}
- alt=""
+ alt={t('empty-string')}
/>
{autoJoinType == "added" ? (
<>
-
- {invitedBy} has added you to the {teamName} organization.
- {" "}
+ {teamName}> }}
+components={{"0": }}
+/>{" "}
- Your link has been changed from {prevLinkWithoutProtocol} to{" "}
- {newLinkWithoutProtocol} but don't worry, all previous
- links still work and redirect appropriately.
-
-
- Please note: All of your personal event types have been moved into the{" "}
- {teamName} organisation, which may also include potential personal link.
-
-
- Please log in and make sure you have no private events on your new organisational account.
-
-
- For personal events we recommend creating a new account with a personal email address.
-
-
- Enjoy your new clean link:{" "}
- {newLinkWithoutProtocol}
+ values={{ prevLink: props.prevLink, newLink: props.newLink, teamName: props.teamName }}>{prevLinkWithoutProtocol}>, _newLinkWithoutProtocol_: <>{newLinkWithoutProtocol}> }}
+components={{"0": , "1":
+ }}
+/>
+
{teamName}> }}
+components={{"0":
+ }}
+/>
+
{t('log-in-check-private-events')}
+
{t('recommend-creating-new-account')}
+
{newLinkWithoutProtocol}> }}
+components={{"0":
+ }}
+/>
>
) : (
<>
-
- {invitedBy} has invited you to join the {teamName} organization.
- {" "}
+ {teamName}> }}
+components={{"0": }}
+/>{" "}
- On accepting the invite, your link will change to your organization domain but don't
- worry, all previous links will still work and redirect appropriately.
-
-
- Please note: All of your personal event types will be moved into the{" "}
- {teamName} organisation, which may also include potential personal link.
-
-
- For personal events we recommend creating a new account with a personal email address.
-
+ values={{ prevLink: props.prevLink, newLink: props.newLink, teamName: props.teamName }}>{t('accepting-invite-link-change')}
+
{teamName}> }}
+components={{"0":
+ }}
+/>
+
{t('recommend-creating-new-account-2')}
>
)}
>
@@ -208,18 +214,19 @@ export const TeamInviteEmail = (
return (
<>
{autoJoinType === "added" ? (
-
- {invitedBy} has added you to the {teamName} organization.
-
+ {teamName}> }}
+components={{"0": }}
+/>
) : (
-
- {invitedBy} has invited you to join the {teamName} organization.
-
+ {teamName}> }}
+components={{"0": }}
+/>
)}{" "}
-
- {appName} is the event-juggling scheduler that enables you and your team to schedule meetings
- without the email tennis.
-
+ {t('app-name-event-juggling-scheduler', { appName })}
>
);
}
@@ -228,20 +235,21 @@ export const TeamInviteEmail = (
return (
<>
{autoJoinType === "added" ? (
-
- {invitedBy} has added you to the team {teamName} in their organization{" "}
- {parentTeamName}.
-
+ {teamName}>, _parentTeamName_: <>{parentTeamName}> }}
+components={{"0": , "1":
+ }}
+/>
) : (
-
- {invitedBy} has invited you to the team {teamName} in their organization{" "}
- {parentTeamName}.
-
+ {teamName}>, _parentTeamName_: <>{parentTeamName}> }}
+components={{"0": , "1":
+ }}
+/>
)}{" "}
-
- {appName} is the event-juggling scheduler that enables you and your team to schedule meetings
- without the email tennis.
-
+ {t('app-name-event-juggling-scheduler-2', { appName })}
>
);
}
diff --git a/packages/emails/src/templates/locales/en.json b/packages/emails/src/templates/locales/en.json
new file mode 100644
index 00000000000000..f0246ae2e2c958
--- /dev/null
+++ b/packages/emails/src/templates/locales/en.json
@@ -0,0 +1,71 @@
+{
+ "event-location-changed": "\"event_location_changed\"",
+ "view-all-stats": "\"View all stats\"",
+ "event-has-been-rescheduled": "\"event_has_been_rescheduled\"",
+ "empty-string": "\"\"",
+ "note-download-link-valid": "Note: The download link is valid only for 12 hours. You can generate new download link by following instructions<0>here0>",
+ "no-longer-attending": "\"no_longer_attending\"",
+ "could-not-add-meeting-link": "We could not add the<0>{{_props_location_}}0>meeting link to your scheduled event. Contact your invitees or update your calendar event to add the details. You can either <1>change your location on the event type1>\n or try <2>removing and adding the app again.2>",
+ "could-not-update-calendar": "We could not update your<0>{{_props_calendar_}}0>. <1>Please check your calendar settings or remove and add your calendar again1>",
+ "event-still-awaiting-approval": "\"event_still_awaiting_approval\"",
+ "payment": "Payment",
+ "new-guests-added": "\"new_guests_added\"",
+ "meeting-awaiting-payment-method": "\"meeting_awaiting_payment_method\"",
+ "meeting-awaiting-payment": "\"meeting_awaiting_payment\"",
+ "event-has-been-updated": "\"event_has_been_updated\"",
+ "event-request-cancelled": "\"event_request_cancelled\"",
+ "added-as-owner": "You have been added as an owner of the organization. To publish your new organization, visit <0>{{WEBAPP_URL}}/upgrade0>",
+ "new-organization-link": "Enjoy your new organization link:<0>{{_newLinkWithoutProtocol_}}0>",
+ "link-changed-notification": "Your link has been changed from<0>{{_prevLinkWithoutProtocol_}}0>to <1>{{_newLinkWithoutProtocol_}}1>but don't worry, all previous links still work and redirect appropriately.",
+ "personal-event-types-moved": "Please note: All of your personal event types have been moved into the<0>{{_teamName_}}0> organisation, which may also include potential personal link.",
+ "check-private-events": "Please log in and make sure you have no private events on your new organisational account.",
+ "recommend-new-account": "For personal events we recommend creating a new account with a personal email address.",
+ "new-clean-link": "Enjoy your new clean link:<0>{{_newLinkWithoutProtocol_}}0>",
+ "event-request-reassigned": "\"event_request_reassigned\"",
+ "an-organization-with-slug": "An organization with slug",
+ "was-created": "was created.",
+ "please-configure-dns-registry": "Please be sure to configure your DNS registry to point the subdomain corresponding to the new organization to where the main app is running. Otherwise the organization will not work.",
+ "basic-options-to-configure-subdomain": "Here are just the very basic options to configure a subdomain to point to their app so it loads the organization profile page.",
+ "a-record-configuration": "You can do it either with the A Record:",
+ "request-reschedule-booking": "\"request_reschedule_booking\"",
+ "book-a-new-time": "\"Book a new time\"",
+ "username": "\"Username\"",
+ "email": "\"Email\"",
+ "rating": "\"Rating\"",
+ "comment": "\"Comment\"",
+ "hi-{name}": "Hi{{name}}",
+ "administrator-on-the-{team-name}-team-replaced-your-event-type-{slug}": "An administrator on the<0>{{_teamName_}}0>team has replaced your event type <1>/{{slug}}1>with a managed event type that they control.",
+ "link-will-continue-to-work-settings-may-have-changed-review-it-in-event-types": "Your link will continue to work but some settings for it may have changed. You can review it in event types.",
+ "questions-about-the-event-type-reach-out-to-your-administrator": "If you have any questions about the event type, please reach out to your administrator.",
+ "happy-scheduling": "Happy scheduling,",
+ "the-cal-com-team": "The Cal.com team",
+ "empty-string-fragment": "\"\"",
+ "transcript-fragment": "Transcript",
+ "attendee-no-longer-attending": "\"attendee_no_longer_attending\"",
+ "hello-organization-admins": "Hello Organization Admins,",
+ "please-note-it-has-been-brought-to-our-attention-that": "Please note: It has been brought to our attention that",
+ "has-not-had-any-availability-when-a-user-has-visited": "has not had any availability when a user has visited",
+ "slash": "/",
+ "period": ".",
+ "start-time": "Start time:",
+ "end-time": "End time:",
+ "theres-a-few-reasons-why-this-could-be-happening": "There’s a few reasons why this could be happening:",
+ "the-user-does-not-have-any-calendars-connected": "The user does not have any calendars connected",
+ "their-schedules-attached-to-this-event-are-not-enabled": "Their schedules attached to this event are not enabled",
+ "invited-by-added-to-team-organization": "{{invitedBy}}has added you to the<0>{{_teamName_}}0>organization.",
+ "link-changed-from-to": "Your link has been changed from<0>{{_prevLinkWithoutProtocol_}}0>to <1>{{_newLinkWithoutProtocol_}}1>but don't worry, all previous links still work and redirect appropriately.",
+ "personal-event-types-moved-to-organization": "Please note: All of your personal event types have been moved into the <0>{{_teamName_}}0>organisation, which may also include potential personal link.",
+ "log-in-check-private-events": "Please log in and make sure you have no private events on your new organisational account.",
+ "recommend-creating-new-account": "For personal events we recommend creating a new account with a personal email address.",
+ "enjoy-new-clean-link": "Enjoy your new clean link: <0>{{_newLinkWithoutProtocol_}}0>",
+ "invited-by-invited-to-join-organization": "{{invitedBy}}has invited you to join the<0>{{_teamName_}}0>organization.",
+ "accepting-invite-link-change": "On accepting the invite, your link will change to your organization domain but don't worry, all previous links will still work and redirect appropriately.",
+ "personal-event-types-moved-organization": "Please note: All of your personal event types will be moved into the <0>{{_teamName_}}0>organisation, which may also include potential personal link.",
+ "recommend-creating-new-account-2": "For personal events we recommend creating a new account with a personal email address.",
+ "invited-by-added-to-team-organization-2": "{{invitedBy}}has added you to the<0>{{_teamName_}}0>organization.",
+ "invited-by-invited-to-team-organization": "{{invitedBy}}has invited you to join the<0>{{_teamName_}}0>organization.",
+ "app-name-event-juggling-scheduler": "{{appName}}is the event-juggling scheduler that enables you and your team to schedule meetings without the email tennis.",
+ "invited-by-added-to-team-in-organization": "{{invitedBy}}has added you to the team<0>{{_teamName_}}0>in their organization <1>{{_parentTeamName_}}1>.",
+ "invited-by-invited-to-team-in-organization": "{{invitedBy}}has invited you to the team<0>{{_teamName_}}0>in their organization <1>{{_parentTeamName_}}1>.",
+ "app-name-event-juggling-scheduler-2": "{{appName}}is the event-juggling scheduler that enables you and your team to schedule meetings without the email tennis."
+}
diff --git a/packages/platform/examples/base/src/components/Navbar/index.tsx b/packages/platform/examples/base/src/components/Navbar/index.tsx
index 051ad00697595a..3e2d318f6112b0 100644
--- a/packages/platform/examples/base/src/components/Navbar/index.tsx
+++ b/packages/platform/examples/base/src/components/Navbar/index.tsx
@@ -1,42 +1,43 @@
+import { useTranslation } from "react-i18next";
import { Poppins } from "next/font/google";
import Link from "next/link";
const poppins = Poppins({ subsets: ["latin"], weight: ["400", "800"] });
export function Navbar({ username }: { username?: string }) {
+const { t } = useTranslation();
+
return (
{username && 👤 {username}
}
-
- Calendar
+ {t('calendar')}
-
- Availability
+ {t('availability')}
-
- EventTypes
+ {t('event-types')}
-
- Book Me
+ {t('book-me')}
-
- My Bookings
+ {t('my-bookings')}
-
- Embed
+ {t('embed')}
-
- Conferencing Apps
+ {t('conferencing-apps')}
diff --git a/packages/platform/examples/base/src/components/Navbar/locales/en.json b/packages/platform/examples/base/src/components/Navbar/locales/en.json
new file mode 100644
index 00000000000000..cf181cf1de4895
--- /dev/null
+++ b/packages/platform/examples/base/src/components/Navbar/locales/en.json
@@ -0,0 +1,10 @@
+{
+ "cal-sync": "CalSync",
+ "calendar": "Calendar",
+ "availability": "Availability",
+ "event-types": "EventTypes",
+ "book-me": "Book Me",
+ "my-bookings": "My Bookings",
+ "embed": "Embed",
+ "conferencing-apps": "Conferencing Apps"
+}
diff --git a/packages/platform/examples/base/src/pages/[bookingUid].tsx b/packages/platform/examples/base/src/pages/[bookingUid].tsx
index b0729bba2a9322..17f6529f4ae7e9 100644
--- a/packages/platform/examples/base/src/pages/[bookingUid].tsx
+++ b/packages/platform/examples/base/src/pages/[bookingUid].tsx
@@ -1,3 +1,4 @@
+import { useTranslation } from "react-i18next";
import { Navbar } from "@/components/Navbar";
import { Inter } from "next/font/google";
// eslint-disable-next-line @calcom/eslint/deprecated-imports-next-router
@@ -10,6 +11,8 @@ import { Icon } from "@calcom/ui";
const inter = Inter({ subsets: ["latin"] });
export default function Bookings(props: { calUsername: string; calEmail: string }) {
+const { t } = useTranslation();
+
const router = useRouter();
const { isLoading, data: booking, refetch } = useBooking((router.query.bookingUid as string) ?? "");
@@ -32,7 +35,7 @@ export default function Bookings(props: { calUsername: string; calEmail: string
- {isLoading && Loading...
}
+ {isLoading && {t('loading')}
}
{!isLoading && booking && (
- This meeting is scheduled
- We sent an email with a calendar invitation with the details to everyone.
+ {t('meeting-scheduled')}
+ {t('email-calendar-invitation-details')}