fix: pass in an i18next instance by using initReactI18next#23607
fix: pass in an i18next instance by using initReactI18next#23607Vibgitcode27 wants to merge 7 commits intocalcom:mainfrom
Conversation
WalkthroughThis PR updates i18n setup across the codebase. It introduces a global lazy-initialized i18next singleton in useLocale.ts with initReactI18next and config-driven defaults. Server and platform i18n utilities now await i18n.init, add interpolation.escapeValue: false, and implement per-locale/namespace i18n instance caching via a Map. Imports are adjusted to include initReactI18next and config loading. The useLocale hook’s return type updates i18n to typeof i18next. No public API changes in server/platform i18n helpers. Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
|
Hey there and thank you for opening this pull request! 👋🏼 We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted. Details: |
|
@Vibgitcode27 is attempting to deploy a commit to the cal Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/lib/hooks/useLocale.ts (1)
47-47: Fix readiness check.
Object.keys(i18n).lengthis not a valid readiness signal and will almost always be truthy. UseisInitialized(and optionally presence of the bundle).- const isLocaleReady = Object.keys(i18n).length > 0; + const activeNs = Array.isArray(namespace) ? namespace[0] : namespace || "common"; + const isLocaleReady = i18n.isInitialized && + (!!i18n.language) && + (typeof i18n.hasResourceBundle === "function" + ? i18n.hasResourceBundle(i18n.language, activeNs) + : true);
🧹 Nitpick comments (7)
packages/platform/libraries/i18n.ts (2)
11-12: Type the caches instead of suppressing ESLint.Avoid
anyand give both caches precise i18next types; also type the function return asTFunctionand the translations asResourceLanguage.Apply:
-import { createInstance } from "i18next"; +import { createInstance } from "i18next"; +import type { i18n as I18n, TFunction, ResourceLanguage } from "i18next"; -const translationCache = new Map<string, Record<string, string>>(); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const i18nInstanceCache = new Map<string, any>(); +const translationCache = new Map<string, ResourceLanguage>(); +const i18nInstanceCache = new Map<string, I18n>(); -export async function loadTranslations(_locale: string, _ns: string) { +export async function loadTranslations(_locale: string, _ns: string): Promise<ResourceLanguage> { -export const getTranslation = async (locale: string, ns: string) => { +export const getTranslation = async (locale: string, ns: string): Promise<TFunction> => {
63-66: Optional: dedupe concurrent inits for same locale-ns.Two parallel calls can initialize duplicate instances. Consider an “in-flight”
Map<string, Promise<I18n>>to coalesce.Also applies to: 84-86
packages/lib/server/i18n.ts (2)
20-21: Type safety: use i18next types for caches.Mirror platform changes: type translation cache and instance cache; type
getTranslationto returnTFunction.-import { createInstance } from "i18next"; +import { createInstance } from "i18next"; +import type { i18n as I18n, TFunction, ResourceLanguage } from "i18next"; -const translationCache = new Map<string, Record<string, string>>(); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const i18nInstanceCache = new Map<string, any>(); +const translationCache = new Map<string, ResourceLanguage>(); +const i18nInstanceCache = new Map<string, I18n>(); -export const getTranslation = async (locale: string, ns: string) => { +export const getTranslation = async (locale: string, ns: string): Promise<TFunction> => {
85-90: Optional: prevent duplicate inits under load.Add an “in-flight” promise cache to avoid initializing the same locale/ns multiple times concurrently.
Also applies to: 107-110
packages/lib/hooks/useLocale.ts (3)
13-35: Gate global i18next init to the browser.Initializing the global singleton server-side is unnecessary (server path creates its own instance) and can add overhead in SSR.
-// Initialize global i18next instance if not already initialized -if (!i18next.isInitialized) { +// Initialize global i18next instance on the client only +if (typeof window !== "undefined" && !i18next.isInitialized) { i18next.use(initReactI18next).init({ lng: config?.defaultLocale || "en", fallbackLng: "en", debug: process.env.NODE_ENV === "development",
58-58: Strongly type the server instances cache.Improves safety at the return site.
-const serverI18nInstances = new Map(); +const serverI18nInstances = new Map< + string, + { t: TFunction; isLocaleReady: boolean; i18n: typeof i18next } +>();
71-86: Server instance: add fallback language for parity.Add
fallbackLng: "en"to match other modules and ensure predictable fallback.i18n.init({ lng: locale, resources: { [locale]: { [ns]: translations, }, }, + fallbackLng: "en", interpolation: { escapeValue: false, }, });
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/lib/hooks/useLocale.ts(2 hunks)packages/lib/server/i18n.ts(2 hunks)packages/platform/libraries/i18n.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
**/*.ts: For Prisma queries, only select data you need; never useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
packages/lib/hooks/useLocale.tspackages/lib/server/i18n.tspackages/platform/libraries/i18n.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()in hot paths like loops
Files:
packages/lib/hooks/useLocale.tspackages/lib/server/i18n.tspackages/platform/libraries/i18n.ts
**/*.{ts,tsx,js,jsx}
⚙️ CodeRabbit configuration file
Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.
Files:
packages/lib/hooks/useLocale.tspackages/lib/server/i18n.tspackages/platform/libraries/i18n.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.
🧬 Code graph analysis (1)
packages/lib/hooks/useLocale.ts (1)
packages/config/next-i18next.config.d.ts (1)
i18n(2-5)
🔇 Additional comments (3)
packages/platform/libraries/i18n.ts (1)
71-82: Good fix: init awaited and escape disabled for React.Awaiting
initand settinginterpolation.escapeValue: falsematches react-i18next guidance and prevents the console warning.packages/lib/server/i18n.ts (1)
94-105: LGTM on awaiting init and adding interpolation config.This aligns server utilities with client behavior and removes the init warning.
packages/lib/hooks/useLocale.ts (1)
15-35: Confirms the core fix.Using
initReactI18nextresolves “You will need to pass in an i18next instance” and aligns with react-i18next expectations.
| // Initialize global i18next instance if not already initialized | ||
| if (!i18next.isInitialized) { | ||
| i18next.use(initReactI18next).init({ | ||
| lng: config?.defaultLocale || "en", | ||
| fallbackLng: "en", | ||
| debug: process.env.NODE_ENV === "development", | ||
|
|
||
| interpolation: { | ||
| escapeValue: false, | ||
| }, | ||
|
|
||
| resources: { | ||
| // Start with empty resources, will be populated dynamically | ||
| }, | ||
|
|
||
| react: { | ||
| useSuspense: false, | ||
| }, | ||
|
|
||
| ns: ["common"], | ||
| defaultNS: "common", | ||
| }); | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify CAL-6371 is addressed (Description label focus).
Separate from i18n, please confirm the form label fix landed in this PR. Quick repo scan:
🏁 Script executed:
#!/bin/bash
# Find Description label associations and the textarea id they're targeting
rg -nP -C2 --type=tsx '(htmlFor\s*=\s*["\']description["\']|for="description")|(<textarea[^>]*id\s*=\s*["\']description["\'])|Description</label>' \
apps packages
# Also check for missing label/textarea linkage in "New Event Type" UI
rg -nP -C3 --type=tsx '(NewEventType|EventType|Description)' apps packagesLength of output: 116
🏁 Script executed:
#!/bin/bash
# Find Description label associations and the textarea id they're targeting
rg -nP -C2 --type=tsx "(htmlFor\s*=\s*['\"]description['\"]|for=\"description\")|(<textarea[^>]*id\s*=\s*['\"]description['\"])|Description</label>" apps packages
# Also check for missing label/textarea linkage in "New Event Type" UI
rg -nP -C3 --type=tsx "(NewEventType|EventType|Description)" apps packagesLength of output: 303
🏁 Script executed:
#!/bin/bash
# Search .tsx files for Description label associations and textarea linkage
rg -nP -C2 '(htmlFor\s*=\s*["\']description["\']|for="description")|(<textarea[^>]*id\s*=\s*["\']description["\'])|Description</label>' -g '*.tsx' apps packages || echo "No matches for Description label associations"
# Search .tsx files in New Event Type UI for missing label/textarea linkage
rg -nP -C3 '(NewEventType|EventType|Description)' -g '*.tsx' apps packages || echo "No matches for NewEventType linkage"Length of output: 116
🏁 Script executed:
#!/bin/bash
# Search .tsx files for Description label associations and textarea linkage
grep -R -P -n -C2 'htmlFor\s*=\s*['"'"']description['"'"']|for="description"|<textarea[^>]*id\s*=\s*['"'"']description['"'"']|Description</label>' apps packages --include='*.tsx' || echo "No matches for Description label associations"
# Search .tsx files in New Event Type UI for missing label/textarea linkage
grep -R -P -n -C3 '(NewEventType|EventType|Description)' apps packages --include='*.tsx' || echo "No matches for NewEventType linkage"Length of output: 262557
🏁 Script executed:
#!/bin/bash
# Locate CreateEventTypeDialog component in codebase
grep -R -n 'CreateEventTypeDialog' apps packages --include='*.tsx' || echo 'CreateEventTypeDialog not found'Length of output: 501
🏁 Script executed:
#!/bin/bash
sed -n '1,200p' packages/features/eventtypes/components/CreateEventTypeDialog.tsxLength of output: 5302
Ensure Description label is properly linked
No htmlFor="description" on the Description <label> nor matching id="description" on its <textarea> in CreateEventTypeForm.tsx and TeamEventTypeForm.tsx—add the label/input linkage to satisfy CAL-6371.
🤖 Prompt for AI Agents
In packages/lib/hooks/useLocale.ts around lines 13 to 35: the review notes
missing label/input linkage for the Description field in CreateEventTypeForm.tsx
and TeamEventTypeForm.tsx; update both components so the Description <label>
includes htmlFor="description" and the matching <textarea> has id="description"
(use unique ids if multiple forms render on the same page, e.g.,
description-createEventType or description-teamEventType), and ensure any form
libraries or aria attributes reference the same id to satisfy CAL-6371.
|
This PR is being marked as stale due to inactivity. |
|
@Devanshusharma2005 Can you review this? |
|
This PR is being marked as stale due to inactivity. |
|
@cubic-dev-ai give review again |
@pallava-joshi I've started the AI code review. It'll take a few minutes to complete. |
There was a problem hiding this comment.
1 issue found across 3 files
Prompt for AI agents (all 1 issues)
Understand the root cause of the following 1 issues and fix them.
<file name="packages/platform/libraries/i18n.ts">
<violation number="1" location="packages/platform/libraries/i18n.ts:15">
Please keep the default "en-common" seed in translationCache; without it every English lookup waits for fetchWithTimeout to fail, causing multi-second delays whenever WEBAPP_URL is unreachable. Restoring the seed avoids the regression.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
pallava-joshi
left a comment
There was a problem hiding this comment.
can you address the changes suggested by cubic
|
@cubic-dev-ai give review again |
@Vibgitcode27 I've started the AI code review. It'll take a few minutes to complete. |
|
@pallava-joshi Addressed cubic suggestions. PR is ready for review. |
There was a problem hiding this comment.
1 issue found across 3 files
Prompt for AI agents (all 1 issues)
Understand the root cause of the following 1 issues and fix them.
<file name="packages/lib/hooks/useLocale.ts">
<violation number="1" location="packages/lib/hooks/useLocale.ts:17">
The new global i18next initialisation hard-codes `fallbackLng: "en"`, overriding the configured zh→zh-CN fallback in next-i18next.config.js. When the hook falls back to the shared client instance, zh locales will now fall back to English instead of zh-CN, breaking the intended localisation behaviour.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
| if (!i18next.isInitialized) { | ||
| i18next.use(initReactI18next).init({ | ||
| lng: config?.defaultLocale || "en", | ||
| fallbackLng: "en", |
There was a problem hiding this comment.
The new global i18next initialisation hard-codes fallbackLng: "en", overriding the configured zh→zh-CN fallback in next-i18next.config.js. When the hook falls back to the shared client instance, zh locales will now fall back to English instead of zh-CN, breaking the intended localisation behaviour.
Prompt for AI agents
Address the following comment on packages/lib/hooks/useLocale.ts at line 17:
<comment>The new global i18next initialisation hard-codes `fallbackLng: "en"`, overriding the configured zh→zh-CN fallback in next-i18next.config.js. When the hook falls back to the shared client instance, zh locales will now fall back to English instead of zh-CN, breaking the intended localisation behaviour.</comment>
<file context>
@@ -1,14 +1,41 @@
+if (!i18next.isInitialized) {
+ i18next.use(initReactI18next).init({
+ lng: config?.defaultLocale || "en",
+ fallbackLng: "en",
+ debug: process.env.NODE_ENV === "development",
+
</file context>
What does this PR do?
Visual Demo (For contributors especially)
A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).
Video Demo (if applicable):
N/A
Image Demo (if applicable)
if NODE_ENV is development
OR
You won't see any log if NODE_ENV is production
Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
Testing Steps:
Environment Setup:
Test Steps:
cd apps/web yarn dev visit localhost:3000Checklist