chore: create standalone i18n utility for platform libraries (#23386)

* chore: create i18n for platform libraries

* fixup! chore: create i18n for platform libraries

* refactor: use previous version of i18n

* Revert "refactor: use previous version of i18n"

This reverts commit 0181ae1018d99ea6d5455d6dde5f3727e70422e1.

* refactor: use previous version of i18n

---------

Co-authored-by: supalarry <laurisskraucis@gmail.com>
This commit is contained in:
Morgan
2025-08-27 10:55:57 -03:00
committed by GitHub
co-authored by supalarry
parent 5324832f13
commit f18ebc4d67
3 changed files with 91 additions and 1 deletions
+83
View File
@@ -0,0 +1,83 @@
import { createInstance } from "i18next";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { fetchWithTimeout } from "@calcom/lib/fetchWithTimeout";
import logger from "@calcom/lib/logger";
/* eslint-disable @typescript-eslint/no-var-requires */
const { i18n } = require("@calcom/config/next-i18next.config");
const translationCache = new Map<string, Record<string, string>>();
const i18nInstanceCache = new Map<string, any>();
const SUPPORTED_NAMESPACES = ["common"];
/**
* 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) {
let locale = _locale === "zh" ? "zh-CN" : _locale;
locale = i18n.locales.includes(locale) ? locale : "en";
const ns = SUPPORTED_NAMESPACES.includes(_ns) ? _ns : "common";
const cacheKey = `${locale}-${ns}`;
if (translationCache.has(cacheKey)) {
return translationCache.get(cacheKey);
}
const url = `${WEBAPP_URL}/static/locales/${locale}/${ns}.json`;
try {
const response = await fetchWithTimeout(
url,
{
cache: "no-store",
},
process.env.NODE_ENV === "development" ? 30000 : 3000
);
if (!response.ok) {
logger.error(`Failed to fetch translations: ${response.status}`);
return {};
}
const translations = await response.json();
translationCache.set(cacheKey, translations);
return translations;
} catch (err) {
console.error("loadTranslations Error:", err);
return {};
}
}
/**
* 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}`;
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);
};
+5 -1
View File
@@ -8,7 +8,11 @@
"types": ["jest"],
"outDir": "./dist",
"emitDecoratorMetadata": true,
"experimentalDecorators": true
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@calcom/lib/server/i18n": ["./i18n.ts"]
}
},
"include": [".", "../../types/*.d.ts"],
"exclude": [
@@ -187,6 +187,9 @@ export default defineConfig({
plugins: [react(), dts()],
resolve: {
alias: {
"@calcom/lib/server/i18n": path.resolve(__dirname, "./i18n.ts"),
"./server/i18n": path.resolve(__dirname, "./i18n.ts"),
"../server/i18n": path.resolve(__dirname, "./i18n.ts"),
"@": path.resolve(__dirname, "./src"),
"@calcom/lib": path.resolve(__dirname, "../../lib"),
"@calcom/trpc": resolve("../../trpc"),