Skip to content

Conversation

@SylvainChevalier
Copy link
Contributor

@SylvainChevalier SylvainChevalier commented Jan 21, 2026

Adds language selection tracking to PostHog analytics.

Changes

  • Add language property to PostHog user identification in auth_context
  • Track language_changed event when user updates language in footer
  • Track language_changed event when user updates language in settings
  • Update language user property in PostHog on language change

Fixes #4125

Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Added analytics tracking for language preference changes throughout the application to monitor user interactions and better understand language selection behavior patterns.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add language property to PostHog user identification in auth_context
- Track language_changed event when user updates language in footer
- Track language_changed event when user updates language in settings
- Update language user property in PostHog on language change

Fixes #4125

Co-authored-by: Sylvain <SylvainChevalier@users.noreply.github.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

This PR integrates PostHog analytics to track language preference changes across the application. Three files are modified to capture language_changed events when users update their language, update user properties in PostHog, and include language in authentication context identification.

Changes

Cohort / File(s) Summary
Language Selection UI Components
front_end/src/app/(main)/accounts/settings/(general)/components/language_preferences.tsx, front_end/src/app/(main)/components/footer.tsx
Adds PostHog integration to capture language_changed events with previous and new language values; updates PostHog person property when language is modified. Existing language update flow remains intact.
Authentication Context
front_end/src/contexts/auth_context.tsx
Extracts language from initialUser and includes it in posthog.identify payload with fallback to locale, enabling user property tracking at authentication time.

Sequence Diagram

sequenceDiagram
    actor User
    participant Component as Language Component
    participant PostHog
    participant Backend

    User->>Component: Select language
    Component->>PostHog: capture("language_changed",<br/>{previous_language, new_language})
    PostHog->>PostHog: Log event
    Component->>PostHog: setPersonProperties({language})
    PostHog->>PostHog: Update user property
    Component->>Backend: updateLanguagePreference()
    Backend->>Backend: Save preference
    Backend-->>Component: Success
    Component->>Component: router.refresh()
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A language dances through the interface bright,
PostHog now listens, tracking each delight,
From "Bonjour" to "こんにちは" they fly,
Analytics gather data way up high! 🎉
One simple feature, three files aligned,
Language preferences perfectly defined!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title directly and clearly summarizes the main change: adding language selection tracking to PostHog analytics, which is the primary objective across all modified files.
Linked Issues check ✅ Passed The pull request fully implements the requirements from issue #4125: captures language selection events, records user language preferences, and tracks language changes across both footer and settings interfaces.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue #4125 objectives of adding language selection tracking to PostHog analytics; no out-of-scope modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@front_end/src/app/`(main)/accounts/settings/(general)/components/language_preferences.tsx:
- Around line 27-40: The PostHog events are emitted before
updateLanguagePreference completes, which can record a change that may fail;
modify the async handler (the anonymous function that checks
isPendingUpdateLanguage) to await updateLanguagePreference(language) and only
call posthog.capture("language_changed", ...) and posthog.setPersonProperties({
language }) after that promise resolves successfully; keep the existing guard
(isPendingUpdateLanguage) and call logError on rejection so analytics are not
sent on failure and person properties remain consistent with
user.language/currentLocale.

In `@front_end/src/app/`(main)/components/footer.tsx:
- Around line 169-183: The current updateLanguage handler calls posthog.capture
and posthog.setPersonProperties before confirming the preference persisted; move
both analytics calls into the success branch of updateLanguagePreference so they
run only after the promise resolves successfully. Specifically, modify
updateLanguage to call updateLanguagePreference(language, false).then(() => {
posthog.capture("language_changed", { previous_language: selectedLanguage,
new_language: language }); if (user) posthog.setPersonProperties({ language });
router.refresh(); }).catch(logError) so tracking and person property updates
happen after persistence succeeds.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f1d6a1 and 35f9abc.

📒 Files selected for processing (3)
  • front_end/src/app/(main)/accounts/settings/(general)/components/language_preferences.tsx
  • front_end/src/app/(main)/components/footer.tsx
  • front_end/src/contexts/auth_context.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
front_end/src/contexts/auth_context.tsx (1)
front_end/scripts/add_missing_translations.mjs (1)
  • language (112-112)
front_end/src/app/(main)/accounts/settings/(general)/components/language_preferences.tsx (1)
front_end/src/hooks/use_server_action.ts (1)
  • useServerAction (3-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Docker Image
  • GitHub Check: integration-tests
  • GitHub Check: Frontend Checks
  • GitHub Check: Backend Checks
🔇 Additional comments (1)
front_end/src/contexts/auth_context.tsx (1)

31-39: The code is correct as-is. The CurrentUser.language type is string | null (not allowing empty strings as sentinel values), and APP_LANGUAGES defines only actual locale codes: "en", "cs", "es", "pt", "zh", "zh-TW", and "original" (for untranslated content). Language values are never empty strings, so the || fallback to locale is appropriate and functions correctly. Using ?? instead would have no practical impact since empty strings are not used in the codebase.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 27 to 40
async (language: string) => {
if (!isPendingUpdateLanguage) {
const previousLanguage = user.language || currentLocale;

// Track language change in PostHog
posthog.capture("language_changed", {
previous_language: previousLanguage,
new_language: language,
});

// Update user property for language
posthog.setPersonProperties({ language });

updateLanguagePreference(language).catch(logError);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's check if the file exists and view the relevant lines
if [ -f "front_end/src/app/(main)/accounts/settings/(general)/components/language_preferences.tsx" ]; then
  echo "=== File found. Checking lines 20-50 for context ==="
  sed -n '20,50p' "front_end/src/app/(main)/accounts/settings/(general)/components/language_preferences.tsx"
else
  echo "File not found at exact path. Searching for language_preferences file..."
  find . -name "language_preferences.tsx" -type f 2>/dev/null | head -5
fi

Repository: Metaculus/metaculus

Length of output: 1021


🏁 Script executed:

# Also search for the updateLanguagePreference function to understand its behavior
rg "updateLanguagePreference" --type tsx --type ts -A 10 -B 2

Repository: Metaculus/metaculus

Length of output: 90


🏁 Script executed:

# Search for updateLanguagePreference without problematic file type flags
rg "updateLanguagePreference" -A 10 -B 2 | head -100

Repository: Metaculus/metaculus

Length of output: 8295


🏁 Script executed:

# Also check imports in the language_preferences file
head -20 "front_end/src/app/(main)/accounts/settings/(general)/components/language_preferences.tsx"

Repository: Metaculus/metaculus

Length of output: 785


🏁 Script executed:

# Get the full updateLanguagePreference function definition
sed -n '/export async function updateLanguagePreference/,/^}/p' "front_end/src/app/(main)/accounts/profile/actions.tsx" | head -30

Repository: Metaculus/metaculus

Length of output: 504


Emit PostHog updates only after the preference save succeeds.

The event and person property update happen before updateLanguagePreference completes. If the server action fails, PostHog will record a language change that didn't persist, and the person property will drift from reality. Move analytics into the success callback.

✅ Suggested sequencing
-        // Track language change in PostHog
-        posthog.capture("language_changed", {
-          previous_language: previousLanguage,
-          new_language: language,
-        });
-
-        // Update user property for language
-        posthog.setPersonProperties({ language });
-
-        updateLanguagePreference(language).catch(logError);
+        updateLanguagePreference(language)
+          .then(() => {
+            // Track language change in PostHog
+            posthog.capture("language_changed", {
+              previous_language: previousLanguage,
+              new_language: language,
+            });
+
+            // Update user property for language
+            posthog.setPersonProperties({ language });
+          })
+          .catch(logError);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async (language: string) => {
if (!isPendingUpdateLanguage) {
const previousLanguage = user.language || currentLocale;
// Track language change in PostHog
posthog.capture("language_changed", {
previous_language: previousLanguage,
new_language: language,
});
// Update user property for language
posthog.setPersonProperties({ language });
updateLanguagePreference(language).catch(logError);
async (language: string) => {
if (!isPendingUpdateLanguage) {
const previousLanguage = user.language || currentLocale;
updateLanguagePreference(language)
.then(() => {
// Track language change in PostHog
posthog.capture("language_changed", {
previous_language: previousLanguage,
new_language: language,
});
// Update user property for language
posthog.setPersonProperties({ language });
})
.catch(logError);
🤖 Prompt for AI Agents
In
`@front_end/src/app/`(main)/accounts/settings/(general)/components/language_preferences.tsx
around lines 27 - 40, The PostHog events are emitted before
updateLanguagePreference completes, which can record a change that may fail;
modify the async handler (the anonymous function that checks
isPendingUpdateLanguage) to await updateLanguagePreference(language) and only
call posthog.capture("language_changed", ...) and posthog.setPersonProperties({
language }) after that promise resolves successfully; keep the existing guard
(isPendingUpdateLanguage) and call logError on rejection so analytics are not
sent on failure and person properties remain consistent with
user.language/currentLocale.

Comment on lines 169 to 183
const updateLanguage = (language: string) => {
// Track language change in PostHog
posthog.capture("language_changed", {
previous_language: selectedLanguage,
new_language: language,
});

// Update user property for language
if (user) {
posthog.setPersonProperties({ language });
}

updateLanguagePreference(language, false)
.then(() => router.refresh())
.catch(logError);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check if the footer.tsx file exists and read the relevant lines
fd "footer.tsx" --type f

Repository: Metaculus/metaculus

Length of output: 182


🏁 Script executed:

# Search for the updateLanguage function in footer.tsx
rg -A 20 "const updateLanguage" front_end/src/app/\(main\)/components/footer.tsx

Repository: Metaculus/metaculus

Length of output: 637


🏁 Script executed:

# Look for the updateLanguagePreference implementation
rg -B 5 -A 15 "updateLanguagePreference" front_end/src/app/\(main\)/components/footer.tsx

Repository: Metaculus/metaculus

Length of output: 1634


🏁 Script executed:

# Search for similar PostHog analytics patterns in the settings component mentioned in scratchpad
find front_end -name "*settings*" -type f | head -20

Repository: Metaculus/metaculus

Length of output: 391


🏁 Script executed:

# Find and read the updateLanguagePreference function
fd "profile" -type d front_end/src/app/\(main\)/accounts/ | head -5

Repository: Metaculus/metaculus

Length of output: 233


🏁 Script executed:

# Search for the updateLanguagePreference implementation
rg -B 5 -A 20 "export.*updateLanguagePreference" front_end/src/app/\(main\)/accounts/

Repository: Metaculus/metaculus

Length of output: 1785


🏁 Script executed:

# Check the settings_header component for similar PostHog patterns
rg -B 5 -A 15 "posthog.capture\|posthog.setPersonProperties" front_end/src/app/\(main\)/accounts/settings/components/settings_header.tsx

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Search for PostHog analytics patterns in settings components more broadly
rg "posthog.capture|posthog.setPersonProperties" front_end/src/app/\(main\)/accounts/settings/ -B 3 -A 3

Repository: Metaculus/metaculus

Length of output: 1634


🏁 Script executed:

# Check if there are any error handling patterns in similar components
rg -B 10 -A 5 "updateLanguagePreference" front_end/src --type tsx | head -60

Repository: Metaculus/metaculus

Length of output: 90


🏁 Script executed:

# Look at the full profile actions file to understand error scenarios
cat front_end/src/app/\(main\)/accounts/profile/actions.tsx | head -100

Repository: Metaculus/metaculus

Length of output: 2375


🏁 Script executed:

# Read more of the profile actions to see error handling
cat front_end/src/app/\(main\)/accounts/profile/actions.tsx

Repository: Metaculus/metaculus

Length of output: 3379


🏁 Script executed:

# Check the language_preferences settings component to see how it handles errors
cat front_end/src/app/\(main\)/accounts/settings/\(general\)/components/language_preferences.tsx

Repository: Metaculus/metaculus

Length of output: 3466


Defer PostHog tracking until the language update succeeds.

The footer's language change handler sends language_changed and updates person properties before verifying the preference save succeeded. If updateLanguagePreference fails, analytics will be incorrect—claiming a change that never persisted. Move PostHog tracking into the success path after the preference is saved.

✅ Suggested sequencing
-    // Track language change in PostHog
-    posthog.capture("language_changed", {
-      previous_language: selectedLanguage,
-      new_language: language,
-    });
-
-    // Update user property for language
-    if (user) {
-      posthog.setPersonProperties({ language });
-    }
-
-    updateLanguagePreference(language, false)
-      .then(() => router.refresh())
-      .catch(logError);
+    updateLanguagePreference(language, false)
+      .then(() => {
+        // Track language change in PostHog
+        posthog.capture("language_changed", {
+          previous_language: selectedLanguage,
+          new_language: language,
+        });
+
+        // Update user property for language
+        if (user) {
+          posthog.setPersonProperties({ language });
+        }
+
+        router.refresh();
+      })
+      .catch(logError);
🤖 Prompt for AI Agents
In `@front_end/src/app/`(main)/components/footer.tsx around lines 169 - 183, The
current updateLanguage handler calls posthog.capture and
posthog.setPersonProperties before confirming the preference persisted; move
both analytics calls into the success branch of updateLanguagePreference so they
run only after the promise resolves successfully. Specifically, modify
updateLanguage to call updateLanguagePreference(language, false).then(() => {
posthog.capture("language_changed", { previous_language: selectedLanguage,
new_language: language }); if (user) posthog.setPersonProperties({ language });
router.refresh(); }).catch(logError) so tracking and person property updates
happen after persistence succeeds.

@github-actions
Copy link
Contributor

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-4127-claude-issue-4125-20260121-133.fly.dev
📦 Docker Image ghcr.io/metaculus/metaculus:claude-issue-4125-20260121-133-169790b
🗄️ PostgreSQL NeonDB branch preview/pr-4127-claude-issue-4125-20260121-133
Redis Fly Redis mtc-redis-pr-4127-claude-issue-4125-20260121-133

Details

  • Commit: 169790b644ecc19cdbac23e68248bd41f12ada2f
  • Branch: claude/issue-4125-20260121-1335
  • Fly App: metaculus-pr-4127-claude-issue-4125-20260121-133

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add language selection to posthog analytics

2 participants