* refactor: replace i18n HTTP requests with build-time bundling - Create translationBundler.ts for build-time translation loading - Replace HTTP fetch in loadTranslations with file system reads - Add CalComVersion cache invalidation to prevent stale translations - Fix TypeScript errors in booking page components - Eliminate 60s timeout issues by removing network dependency Resolves translation timeout issues by bundling translations at build time instead of making runtime HTTP requests to /static/locales/ endpoints. Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: move i18n files back to packages/lib/server with proper imports - Move i18n.ts and translationBundler.ts back to packages/lib/server/ - Replace all relative imports with @calcom/lib/server/i18n pattern - Fix LOCALES_PATH to point to correct directory - Maintain optimized serverless-friendly translation loading Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * refactor: move locale files to packages/lib/server to eliminate circular deps - Move all locale files from apps/web/public/static/locales to packages/lib/server/locales - Create copy-locales-static.js script to copy files during build - Update all references to use new location for build-time access - Maintain public folder copying for Next.js runtime access - Update platform atoms, scripts, and config files - Fix copy script relative path issue Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: update test imports after locale refactor - Fix import paths in test files updated by pre-commit hooks - Ensure all tests use correct locale import paths Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: revert import paths from @calcom/web/lib/i18n to @calcom/lib/server/i18n - Revert all test file imports back to @calcom/lib/server/i18n as requested - Addresses GitHub comment feedback to stick with packages/lib/server location - Fixes import paths in 6 test files that were incorrectly changed Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: update vite config alias to use new locale path - Update @calcom/web/public/static/locales/en/common.json to @calcom/lib/server/locales/en/common.json - Addresses GitHub comment about updating platform atoms vite config - Maintains correct path resolution after locale files moved to packages/lib/server Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: update translationBundler path resolution for production builds - Use process.cwd() instead of __dirname for locale file path resolution - Ensures locale files can be found in both development and production environments - Fixes E2E test failures caused by missing locale files in .next/server/chunks/ Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: use relative path resolution for locale files in translationBundler - Change from process.cwd() to __dirname with relative paths - Ensures locale files can be found in both development and production environments - Fixes E2E test failures caused by incorrect path resolution in Next.js builds Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: use process.cwd() for locale path resolution in production builds - Change from __dirname to process.cwd() with relative paths - Ensures locale files can be found when bundled into Next.js server chunks - Fixes E2E test failures caused by incorrect path resolution in production environment - Follows same pattern used in getStaticProps.tsx for cross-package file access Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: use path.resolve pattern from sendVerificationRequest for locale access - Change from process.cwd() to path.resolve(process.cwd(), '..', '..', 'packages/lib/server/locales') - Follows same pattern used in sendVerificationRequest.ts for cross-environment file access - Should resolve E2E test failures by ensuring locale files can be found when bundled into Next.js server chunks - Pattern navigates up from current working directory to reach packages directory consistently Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: use dynamic monorepo root detection for locale path resolution - Replace relative path resolution with dynamic monorepo root finder - Ensures locale files can be found from any working directory (root, apps/web, apps/api/v2) - Update API v2 i18n config to use new locale path - Fixes remaining E2E test failures in API v2 and E2E (1/4) test suites Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: use require.resolve(__filename) for robust path resolution in all contexts - Replace __dirname with require.resolve(__filename) in monorepo root detection - Ensures locale files can be found when running from any working directory - Fixes E2E API v2 test failures where __dirname resolves to '.' instead of actual file path Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * Revert "fix: use require.resolve(__filename) for robust path resolution in all contexts" This reverts commit b37d8226000da8f7d5fb98b83dd0e95a53d45372. * fix: update copied locale files after translationBundler path resolution fix - Copy script updated all locale files in public directory - Ensures E2E tests have access to latest locale files - Fixes regression where all E2E tests were failing Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * feat: remove existing locale files to establish copy script as single source of truth - Delete all common.json files from apps/web/public/static/locales/ - Eliminates developer confusion about which files are authoritative - copy-locales-static.js script now clearly the only mechanism for populating public folder - packages/lib/server/locales/ remains the definitive source of truth for translations Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * add comment * refactor: simplify findMonorepoRoot by removing redundant fallback loop - Remove unnecessary second while loop using process.cwd() - The first loop from __dirname will always find the monorepo root - Add clear error message for fail-fast behavior if repo structure is corrupted - Improves code clarity and maintainability Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * feat: integrate updated translations from main branch - Restore all common.json files from main branch to apps/web/public/static/locales/ - Overwrite packages/lib/server/locales/ with up-to-date translation content - Resolve merge conflicts using Benny's safer 2-step approach - Ensure translation source of truth remains in packages/lib/server/locales/ - Complete safer conflict resolution to eliminate merge conflicts on PR #22422 Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * chore: update package.json and yarn.lock after translation integration - Update dependencies after ts-node installation for pre-commit hooks - Ensure yarn.lock reflects current dependency state - Complete translation integration process Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * feat: remove duplicate locale files from apps/web to establish single source of truth - Delete all common.json files from apps/web/public/static/locales/ - Maintain packages/lib/server/locales/ as the single source of truth for translations - copy-locales-static.js script will populate public folder during build process - Complete Benny's safer 2-step approach: restore from main, then remove duplicates - Resolve merge conflicts and eliminate developer confusion about translation file locations Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: hbjORbj <sldisek783@gmail.com>
96 lines
3.2 KiB
TypeScript
96 lines
3.2 KiB
TypeScript
import { createInstance } from "i18next";
|
|
|
|
import { CALCOM_VERSION } from "@calcom/lib/constants";
|
|
|
|
const translationCache = new Map<string, Record<string, string>>();
|
|
const i18nInstanceCache = new Map<string, any>();
|
|
|
|
/**
|
|
* Loads English fallback translations for when requested locale translations fail
|
|
* Implements caching to avoid redundant network requests
|
|
* @returns {Promise<Record<string, string>>} English translations object or empty object on failure
|
|
*/
|
|
async function loadFallbackTranslations(): Promise<Record<string, string>> {
|
|
const cacheKey = `en-common-${CALCOM_VERSION}`;
|
|
|
|
if (translationCache.has(cacheKey)) {
|
|
return translationCache.get(cacheKey)!;
|
|
}
|
|
|
|
try {
|
|
const { getBundledTranslations } = await import("./translationBundler");
|
|
const translations = getBundledTranslations("en", "common");
|
|
|
|
if (Object.keys(translations).length === 0) {
|
|
throw new Error("No English fallback translations found");
|
|
}
|
|
|
|
translationCache.set(cacheKey, translations);
|
|
return translations;
|
|
} catch (error) {
|
|
console.error("Could not load fallback translations:", error);
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads translations for a specific locale and namespace with optimized caching
|
|
* @param {string} _locale - The locale code (e.g., 'en', 'fr', 'zh')
|
|
* @param {string} ns - The namespace for the translations
|
|
* @returns {Promise<Record<string, string>>} Translations object or fallback translations on failure
|
|
*/
|
|
export async function loadTranslations(_locale: string, ns: string): Promise<Record<string, string>> {
|
|
const locale = _locale === "zh" ? "zh-CN" : _locale;
|
|
const cacheKey = `${locale}-${ns}-${CALCOM_VERSION}`;
|
|
|
|
if (translationCache.has(cacheKey)) {
|
|
return translationCache.get(cacheKey)!;
|
|
}
|
|
|
|
try {
|
|
const { getBundledTranslations } = await import("./translationBundler");
|
|
const translations = getBundledTranslations(locale, ns);
|
|
|
|
if (Object.keys(translations).length === 0) {
|
|
throw new Error(`No translations found for ${locale}/${ns}`);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates or retrieves a cached i18next translation function for the specified locale and namespace
|
|
* @param {string} locale - The locale code (e.g., 'en', 'fr')
|
|
* @param {string} ns - The namespace for the translations
|
|
* @returns {Promise<Function>} A translation function bound to the specified locale and namespace
|
|
*/
|
|
export const getTranslation = async (locale: string, ns: string) => {
|
|
const cacheKey = `${locale}-${ns}-${CALCOM_VERSION}`;
|
|
if (i18nInstanceCache.has(cacheKey)) {
|
|
return i18nInstanceCache.get(cacheKey).getFixedT(locale, ns);
|
|
}
|
|
|
|
const resources = await loadTranslations(locale, ns);
|
|
|
|
const _i18n = createInstance();
|
|
_i18n.init({
|
|
lng: locale,
|
|
resources: {
|
|
[locale]: {
|
|
[ns]: resources,
|
|
},
|
|
},
|
|
fallbackLng: "en",
|
|
});
|
|
|
|
// Cache the i18n instance
|
|
i18nInstanceCache.set(cacheKey, _i18n);
|
|
return _i18n.getFixedT(locale, ns);
|
|
};
|