Skip to content

Commit 8d17ca5

Browse files
authored
perf: bundle i18n json into code (calcom#23303)
* bundle i18n jsons into code * refactor * use absolute url
1 parent 3fa7b30 commit 8d17ca5

1 file changed

Lines changed: 40 additions & 17 deletions

File tree

packages/lib/server/i18n.ts

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,23 @@ import logger from "../logger";
77

88
/* eslint-disable @typescript-eslint/no-var-requires */
99
const { i18n } = require("@calcom/config/next-i18next.config");
10+
const log = logger.getSubLogger({ prefix: ["[i18n]"] });
11+
12+
// Import only English translations directly to avoid HTTP requests
13+
// Other languages will be loaded dynamically to minimize bundle size
14+
const englishTranslations: Record<
15+
string,
16+
string
17+
> = require("../../../apps/web/public/static/locales/en/common.json");
1018

1119
const translationCache = new Map<string, Record<string, string>>();
1220
const i18nInstanceCache = new Map<string, any>();
1321
const SUPPORTED_NAMESPACES = ["common"];
1422

1523
/**
1624
* Loads translations for a specific locale and namespace with optimized caching
25+
* English translations are bundled as englishTranslations for reliability,
26+
* other languages use dynamic imports with HTTP fallback to minimize bundle size
1727
* @param {string} _locale - The locale code (e.g., 'en', 'fr', 'zh')
1828
* @param {string} ns - The namespace for the translations
1929
* @returns {Promise<Record<string, string>>} Translations object or fallback translations on failure
@@ -28,28 +38,41 @@ export async function loadTranslations(_locale: string, _ns: string) {
2838
return translationCache.get(cacheKey);
2939
}
3040

31-
const url = `${WEBAPP_URL}/static/locales/${locale}/${ns}.json`;
41+
if (locale === "en") {
42+
translationCache.set(cacheKey, englishTranslations);
43+
return englishTranslations;
44+
}
45+
3246
try {
33-
const response = await fetchWithTimeout(
34-
url,
35-
{
36-
cache: "no-store",
37-
},
38-
process.env.NODE_ENV === "development" ? 30000 : 3000
47+
const { default: localeTranslations } = await import(
48+
`../../../apps/web/public/static/locales/${locale}/${ns}.json`
3949
);
4050

41-
if (!response.ok) {
42-
logger.error(`Failed to fetch translations: ${response.status}`);
43-
return {};
44-
}
51+
translationCache.set(cacheKey, localeTranslations);
52+
return localeTranslations;
53+
} catch (dynamicImportErr) {
54+
log.warn(`Dynamic import failed for locale ${locale}:`, dynamicImportErr);
4555

46-
const translations = await response.json();
47-
translationCache.set(cacheKey, translations);
48-
return translations;
49-
} catch (err) {
50-
console.error("loadTranslations Error:", err);
56+
// Try HTTP fallback as second option
57+
try {
58+
const response = await fetchWithTimeout(
59+
`${WEBAPP_URL}/static/locales/${locale}/${ns}.json`,
60+
{
61+
cache: "no-store",
62+
},
63+
3000
64+
);
65+
if (response.ok) {
66+
const httpTranslations = await response.json();
67+
translationCache.set(cacheKey, httpTranslations);
68+
return httpTranslations;
69+
}
70+
} catch (httpErr) {
71+
log.error(`HTTP fallback also failed for locale ${locale}:`, httpErr);
72+
}
5173

52-
return {};
74+
log.info(`Falling back to English for locale: ${locale}`);
75+
return englishTranslations;
5376
}
5477
}
5578

0 commit comments

Comments
 (0)