Skip to content

Commit

Permalink
feat: overriding i18n language for event page
Browse files Browse the repository at this point in the history
  • Loading branch information
MehulZR committed Jan 20, 2025
1 parent 2144971 commit 4a2ab78
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 7 deletions.
8 changes: 8 additions & 0 deletions apps/web/server/lib/[user]/[type]/getServerSideProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getBookingForReschedule, getBookingForSeatedEvent } from "@calcom/featu
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import type { getPublicEvent } from "@calcom/features/eventtypes/lib/getPublicEvent";
import { CALCOM_VERSION } from "@calcom/lib/constants";
import { getUsernameList } from "@calcom/lib/defaultEvents";
import { UserRepository } from "@calcom/lib/server/repository/user";
import slugify from "@calcom/lib/slugify";
Expand Down Expand Up @@ -247,6 +248,13 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
: false
: user?.allowSEOIndexing;

if (eventData.userInterfaceLanguage) {
ssr.viewer.public.i18n.prefetch({
locale: eventData.userInterfaceLanguage,
CalComVersion: CALCOM_VERSION,
});
}

const props: Props = {
eventData: {
id: eventData.id,
Expand Down
2 changes: 2 additions & 0 deletions apps/web/server/lib/ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { createContext } from "@calcom/trpc/server/createContext";
import { me } from "@calcom/trpc/server/routers/loggedInViewer/procedures/me";
import { teamsAndUserProfilesQuery } from "@calcom/trpc/server/routers/loggedInViewer/procedures/teamsAndUserProfilesQuery";
import { event } from "@calcom/trpc/server/routers/publicViewer/procedures/event";
import { i18n } from "@calcom/trpc/server/routers/publicViewer/procedures/i18n";
import { session } from "@calcom/trpc/server/routers/publicViewer/procedures/session";
import { get } from "@calcom/trpc/server/routers/viewer/eventTypes/procedures/get";
import { hasTeamPlan } from "@calcom/trpc/server/routers/viewer/teams/procedures/hasTeamPlan";
Expand All @@ -31,6 +32,7 @@ const routerSlice = router({
public: router({
session,
event,
i18n,
}),
teams: router({
hasTeamPlan,
Expand Down
36 changes: 34 additions & 2 deletions packages/features/bookings/Booker/Booker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ import dayjs from "@calcom/dayjs";
import { getQueryParam } from "@calcom/features/bookings/Booker/utils/query-param";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
import classNames from "@calcom/lib/classNames";
import { CALCOM_VERSION } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { localeOptions } from "@calcom/lib/i18n";
import { BookerLayouts } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc";

import { VerifyCodeDialog } from "../components/VerifyCodeDialog";
import { AvailableTimeSlots } from "./components/AvailableTimeSlots";
Expand Down Expand Up @@ -151,12 +155,40 @@ const BookerComponent = ({
}
};

const { i18n } = useLocale();
const eventUILanguage = useMemo(
() => localeOptions.find((locale) => locale.value === event.data?.userInterfaceLanguage)?.value,
[event.data?.userInterfaceLanguage]
);

const i18nOverrideQuery = trpc.viewer.public.i18n.useQuery(
{
locale: eventUILanguage || "en",
CalComVersion: CALCOM_VERSION,
},
{
staleTime: Infinity,
enabled: !!eventUILanguage,
}
);

useEffect(() => {
if (eventUILanguage && i18nOverrideQuery.isSuccess) {
i18n.addResourceBundle(
eventUILanguage,
"common",
i18nOverrideQuery.data.i18n._nextI18Next?.initialI18nStore[eventUILanguage].common
);
i18n.changeLanguage(eventUILanguage);
}
}, [eventUILanguage, i18n, i18nOverrideQuery.data, i18nOverrideQuery.isSuccess]);

useEffect(() => {
if (event.isPending) return setBookerState("loading");
if (event.isPending || (eventUILanguage && i18nOverrideQuery.isPending)) return setBookerState("loading");
if (!selectedDate) return setBookerState("selecting_date");
if (!selectedTimeslot) return setBookerState("selecting_time");
return setBookerState("booking");
}, [event, selectedDate, selectedTimeslot, setBookerState]);
}, [event, selectedDate, selectedTimeslot, setBookerState, i18nOverrideQuery.isPending, eventUILanguage]);

const slot = getQueryParam("slot");
useEffect(() => {
Expand Down
1 change: 1 addition & 0 deletions packages/features/bookings/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type BookerEvent = Pick<
| "instantMeetingParameters"
| "fieldTranslations"
| "autoTranslateDescriptionEnabled"
| "userInterfaceLanguage"
> & { users: BookerEventUser[]; showInstantEventConnectNowModal: boolean } & { profile: BookerEventProfile };

export type ValidationErrors<T extends object> = { key: FieldPath<T>; error: ErrorOption }[];
Expand Down
2 changes: 2 additions & 0 deletions packages/features/eventtypes/lib/getPublicEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ const publicEventSelect = Prisma.validator<Prisma.EventTypeSelect>()({
hidden: true,
assignAllTeamMembers: true,
rescheduleWithSameRoundRobinHost: true,
userInterfaceLanguage: true,
});

export async function isCurrentlyAvailable({
Expand Down Expand Up @@ -460,6 +461,7 @@ export const getPublicEvent = async (
}
return {
...eventWithUserProfiles,
userInterfaceLanguage: event.userInterfaceLanguage,
bookerLayouts: bookerLayoutsSchema.parse(eventMetaData?.bookerLayouts || null),
description: markdownToSafeHTML(eventWithUserProfiles.description),
metadata: eventMetaData,
Expand Down
1 change: 1 addition & 0 deletions packages/lib/defaultEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const commons = {
fieldTranslations: [],
maxLeadThreshold: null,
useEventLevelSelectedCalendars: false,
userInterfaceLanguage: null,
};

export const dynamicEvent = {
Expand Down
7 changes: 2 additions & 5 deletions packages/trpc/server/routers/publicViewer/_router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import publicProcedure from "../../procedures/publicProcedure";
import { importHandler, router } from "../../trpc";
import { slotsRouter } from "../viewer/slots/_router";
import { ZUserEmailVerificationRequiredSchema } from "./checkIfUserEmailVerificationRequired.schema";
import { i18nInputSchema } from "./i18n.schema";
import { ZMarkHostAsNoShowInputSchema } from "./markHostAsNoShow.schema";
import { event } from "./procedures/event";
import { i18n } from "./procedures/i18n";
import { session } from "./procedures/session";
import { ZSamlTenantProductInputSchema } from "./samlTenantProduct.schema";
import { ZStripeCheckoutSessionInputSchema } from "./stripeCheckoutSession.schema";
Expand All @@ -17,10 +17,7 @@ const namespaced = (s: string) => `${NAMESPACE}.${s}`;
// things that unauthenticated users can query about themselves
export const publicViewerRouter = router({
session,
i18n: publicProcedure.input(i18nInputSchema).query(async (opts) => {
const handler = await importHandler(namespaced("i18n"), () => import("./i18n.handler"));
return handler(opts);
}),
i18n: i18n,
countryCode: publicProcedure.query(async (opts) => {
const handler = await importHandler(namespaced("countryCode"), () => import("./countryCode.handler"));
return handler(opts);
Expand Down
11 changes: 11 additions & 0 deletions packages/trpc/server/routers/publicViewer/procedures/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import publicProcedure from "../../../procedures/publicProcedure";
import { importHandler } from "../../../trpc";
import { i18nInputSchema } from "../i18n.schema";

const NAMESPACE = "publicViewer";
const namespaced = (s: string) => `${NAMESPACE}.${s}`;

export const i18n = publicProcedure.input(i18nInputSchema).query(async (opts) => {
const handler = await importHandler(namespaced("i18n"), () => import("../i18n.handler"));
return handler(opts);
});

0 comments on commit 4a2ab78

Please sign in to comment.