From 02c0b721b900dd2df1c28be9c54060ce591bde33 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Fri, 1 Aug 2025 16:42:31 +0100 Subject: [PATCH] Revert "fix: refactor i18n `loadTranslations` and set timeout to 3s (#22633)" This reverts commit 6ff20b98f46c449087040d71bea4c9a1fc6bde45. --- .../test/lib/handleChildrenEventTypes.test.ts | 7 +- .../ee/billing/credit-service.test.ts | 11 --- .../createOrganizationFromOnboarding.test.ts | 11 --- .../lib/reminders/reminderScheduler.test.ts | 10 --- .../compareReminderBodyToTemplate.test.ts | 13 +--- packages/lib/fetchWithTimeout.ts | 13 ---- packages/lib/server/i18n.ts | 70 +++++++++++++------ packages/lib/server/repository/user.test.ts | 7 +- .../service/userCreationService.test.ts | 7 +- 9 files changed, 53 insertions(+), 96 deletions(-) delete mode 100644 packages/lib/fetchWithTimeout.ts diff --git a/apps/web/test/lib/handleChildrenEventTypes.test.ts b/apps/web/test/lib/handleChildrenEventTypes.test.ts index ef488d7e98..6f24f398cb 100644 --- a/apps/web/test/lib/handleChildrenEventTypes.test.ts +++ b/apps/web/test/lib/handleChildrenEventTypes.test.ts @@ -26,12 +26,7 @@ vi.mock("@calcom/emails/email-manager", () => { vi.mock("@calcom/lib/server/i18n", () => { return { - getTranslation: async (locale: string, namespace: string) => { - const t = (key: string) => key; - t.locale = locale; - t.namespace = namespace; - return t; - }, + getTranslation: (key: string) => key, }; }); diff --git a/packages/features/ee/billing/credit-service.test.ts b/packages/features/ee/billing/credit-service.test.ts index 917b3fae8a..927a7dfb2f 100644 --- a/packages/features/ee/billing/credit-service.test.ts +++ b/packages/features/ee/billing/credit-service.test.ts @@ -25,17 +25,6 @@ vi.mock("@calcom/prisma", () => { vi.mock("stripe"); -vi.mock("@calcom/lib/server/i18n", () => { - return { - getTranslation: async (locale: string, namespace: string) => { - const t = (key: string) => key; - t.locale = locale; - t.namespace = namespace; - return t; - }, - }; -}); - vi.mock("@calcom/lib/constants", async () => { const actual = (await vi.importActual("@calcom/lib/constants")) as typeof import("@calcom/lib/constants"); return { diff --git a/packages/features/ee/organizations/lib/server/createOrganizationFromOnboarding.test.ts b/packages/features/ee/organizations/lib/server/createOrganizationFromOnboarding.test.ts index 4b8d3fb6af..f13fd58ea0 100644 --- a/packages/features/ee/organizations/lib/server/createOrganizationFromOnboarding.test.ts +++ b/packages/features/ee/organizations/lib/server/createOrganizationFromOnboarding.test.ts @@ -43,17 +43,6 @@ vi.mock("@calcom/lib/domainManager/organization", () => ({ createDomain: vi.fn(), })); -vi.mock("@calcom/lib/server/i18n", () => { - return { - getTranslation: async (locale: string, namespace: string) => { - const t = (key: string) => key; - t.locale = locale; - t.namespace = namespace; - return t; - }, - }; -}); - const mockOrganizationOnboarding = { name: "Test Org", slug: "test-org", diff --git a/packages/features/ee/workflows/lib/reminders/reminderScheduler.test.ts b/packages/features/ee/workflows/lib/reminders/reminderScheduler.test.ts index aa8549db6a..d8c8179b7e 100644 --- a/packages/features/ee/workflows/lib/reminders/reminderScheduler.test.ts +++ b/packages/features/ee/workflows/lib/reminders/reminderScheduler.test.ts @@ -17,16 +17,6 @@ vi.mock("@calcom/features/ee/workflows/lib/reminders/providers/emailProvider", ( sendOrScheduleWorkflowEmails: vi.fn(), })); -vi.mock("@calcom/lib/server/i18n", () => { - return { - getTranslation: async (locale: string, namespace: string) => { - const t = (key: string) => key; - t.locale = locale; - t.namespace = namespace; - return t; - }, - }; -}); describe("reminderScheduler", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/packages/features/ee/workflows/lib/test/compareReminderBodyToTemplate.test.ts b/packages/features/ee/workflows/lib/test/compareReminderBodyToTemplate.test.ts index 641cddfb9a..6371b00b8b 100644 --- a/packages/features/ee/workflows/lib/test/compareReminderBodyToTemplate.test.ts +++ b/packages/features/ee/workflows/lib/test/compareReminderBodyToTemplate.test.ts @@ -1,5 +1,5 @@ import { createInstance } from "i18next"; -import { vi, expect, test, describe } from "vitest"; +import { expect, test, describe } from "vitest"; import { getTranslation } from "@calcom/lib/server/i18n"; import { TimeFormat } from "@calcom/lib/timeFormat"; @@ -10,17 +10,6 @@ import { getTemplateBodyForAction } from "../actionHelperFunctions"; import compareReminderBodyToTemplate from "../compareReminderBodyToTemplate"; import plainTextReminderTemplates from "../reminders/templates/plainTextTemplates"; -vi.mock("@calcom/lib/server/i18n", () => { - return { - getTranslation: async (locale: string, namespace: string) => { - const t = (key: string) => key; - t.locale = locale; - t.namespace = namespace; - return t; - }, - }; -}); - const translation = async () => { const _i18n = createInstance(); await _i18n.init({ diff --git a/packages/lib/fetchWithTimeout.ts b/packages/lib/fetchWithTimeout.ts deleted file mode 100644 index ecb0646e26..0000000000 --- a/packages/lib/fetchWithTimeout.ts +++ /dev/null @@ -1,13 +0,0 @@ -export async function fetchWithTimeout(url: string, options: RequestInit = {}, timeoutMs: number) { - const controller = new AbortController(); - const timeout = setTimeout(() => controller.abort(), timeoutMs); - try { - const response = await fetch(url, { - ...options, - signal: controller.signal, - }); - return response; - } finally { - clearTimeout(timeout); - } -} diff --git a/packages/lib/server/i18n.ts b/packages/lib/server/i18n.ts index 06000e2e77..3e092b3309 100644 --- a/packages/lib/server/i18n.ts +++ b/packages/lib/server/i18n.ts @@ -1,14 +1,39 @@ import { createInstance } from "i18next"; -import { i18n } from "@calcom/config/next-i18next.config"; import { WEBAPP_URL } from "@calcom/lib/constants"; -import { fetchWithTimeout } from "../fetchWithTimeout"; -import logger from "../logger"; - const translationCache = new Map>(); const i18nInstanceCache = new Map(); -const SUPPORTED_NAMESPACES = ["common"]; + +/** + * Loads English fallback translations for when requested locale translations fail + * Implements caching to avoid redundant network requests + * @returns {Promise>} English translations object or empty object on failure + */ +async function loadFallbackTranslations() { + const cacheKey = "en-common"; + + if (translationCache.has(cacheKey)) { + return translationCache.get(cacheKey); + } + + try { + const res = await fetch(`${WEBAPP_URL}/static/locales/en/common.json`, { + cache: process.env.NODE_ENV === "production" ? "force-cache" : "no-store", + }); + + if (!res.ok) { + throw new Error(`Failed to fetch fallback translations: ${res.status}`); + } + + const translations = await res.json(); + translationCache.set(cacheKey, translations); + return translations; + } catch (error) { + console.error("Could not fetch fallback translations:", error); + return {}; + } +} /** * Loads translations for a specific locale and namespace with optimized caching @@ -16,33 +41,32 @@ const SUPPORTED_NAMESPACES = ["common"]; * @param {string} ns - The namespace for the translations * @returns {Promise>} Translations object or fallback translations on failure */ -export async function loadTranslations(_locale: string, _ns: string) { - let locale = _locale === "zh" ? "zh-CN" : _locale; - locale = i18n.locales.includes(locale) ? locale : "en"; - const ns = SUPPORTED_NAMESPACES.includes(_ns) ? _ns : "common"; +export async function loadTranslations(_locale: string, ns: string) { + const locale = _locale === "zh" ? "zh-CN" : _locale; const cacheKey = `${locale}-${ns}`; if (translationCache.has(cacheKey)) { return translationCache.get(cacheKey); } - const url = `${WEBAPP_URL}/static/locales/${locale}/${ns}.json`; - const response = await fetchWithTimeout( - url, - { + try { + const url = `${WEBAPP_URL}/static/locales/${locale}/${ns}.json`; + const response = await fetch(url, { cache: process.env.NODE_ENV === "production" ? "force-cache" : "no-store", - }, - 3000 - ); + }); - if (!response.ok) { - logger.error(`Failed to fetch translations: ${response.status}`); - return {}; + if (!response.ok) { + throw new Error(`Failed to fetch translations: ${response.status}`); + } + + const translations = await response.json(); + translationCache.set(cacheKey, translations); + return translations; + } catch (error) { + console.warn(`Failed to load translations for ${locale}/${ns}, falling back to English:`, error); + const fallbackTranslations = await loadFallbackTranslations(); + return fallbackTranslations; } - - const translations = await response.json(); - translationCache.set(cacheKey, translations); - return translations; } /** diff --git a/packages/lib/server/repository/user.test.ts b/packages/lib/server/repository/user.test.ts index 87c19764ce..c4a9ef03b9 100644 --- a/packages/lib/server/repository/user.test.ts +++ b/packages/lib/server/repository/user.test.ts @@ -9,11 +9,8 @@ import { UserRepository } from "./user"; vi.mock("@calcom/lib/server/i18n", () => { return { - getTranslation: async (locale: string, namespace: string) => { - const t = (key: string) => key; - t.locale = locale; - t.namespace = namespace; - return t; + getTranslation: (key: string) => { + return () => key; }, }; }); diff --git a/packages/lib/server/service/userCreationService.test.ts b/packages/lib/server/service/userCreationService.test.ts index 006af47701..098fd7649c 100644 --- a/packages/lib/server/service/userCreationService.test.ts +++ b/packages/lib/server/service/userCreationService.test.ts @@ -11,11 +11,8 @@ import { UserCreationService } from "./userCreationService"; vi.mock("@calcom/lib/server/i18n", () => { return { - getTranslation: async (locale: string, namespace: string) => { - const t = (key: string) => key; - t.locale = locale; - t.namespace = namespace; - return t; + getTranslation: (key: string) => { + return () => key; }, }; });