Skip to content

Conversation

@AnthonyRonning
Copy link
Contributor

@AnthonyRonning AnthonyRonning commented Nov 11, 2025

Overview

Added lingo.dev for multilingual support with Spanish translation.

Changes

  • Installed lingo.dev compiler and React provider
  • Configured Vite build to compile translations
  • Added LingoProviderWrapper to main.tsx
  • Added LocaleSwitcher to TopNav (desktop + mobile)

⚠️ CRITICAL ISSUE - DO NOT MERGE

Lingo breaks Radix UI components! The text wrapping causes ref forwarding to fail in:

  • Dialog components (Profile dialog won't open)
  • DropdownMenu components
  • AlertDialog components
  • Any component that uses refs internally

This makes critical UI features unusable.

Possible Solutions

  1. Find exclusion mechanism - Need way to exclude Radix components from translation
  2. Switch to different i18n - Use react-i18next or similar that doesn't wrap text
  3. Accept limitations - Manually mark problem components with data-lingo-skip

Testing

Run bun run dev and try to:

  • ❌ Open profile dialog from sidebar (fails)
  • ❌ Use dropdown menus (may fail)
  • ✅ Language switcher works (visible in top nav)

This PR is for documentation purposes to show the Lingo integration attempt and its issues.

Summary by CodeRabbit

  • New Features
    • Added language switcher to top navigation for selecting between English and Spanish locales. Language switcher appears on desktop and mobile navigation menus.

Added lingo.dev for multilingual support with Spanish translation:
- Installed lingo.dev compiler and React provider
- Configured Vite build to compile translations
- Added LingoProviderWrapper to main.tsx
- Added LocaleSwitcher to TopNav (desktop + mobile)

KNOWN ISSUE: Lingo wraps text nodes which breaks Radix UI components
that use refs internally (Dialog, DropdownMenu, AlertDialog). This
causes the profile dialog and other interactive components to not work.

Need to either:
1. Find a way to exclude Radix components from translation
2. Use a different i18n solution
3. Accept the limitations and manually mark problem components

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@cloudflare-workers-and-pages
Copy link

Deploying maple with  Cloudflare Pages  Cloudflare Pages

Latest commit: d6708b4
Status: ✅  Deploy successful!
Preview URL: https://5a32055e.maple-ca8.pages.dev
Branch Preview URL: https://lingo.maple-ca8.pages.dev

View logs

@coderabbitai
Copy link

coderabbitai bot commented Nov 11, 2025

Walkthrough

Adding internationalization support through the lingo.dev library. Changes include adding the dependency, wrapping the App component with LingoProviderWrapper, integrating a LocaleSwitcher UI component into navigation, and configuring the Vite compiler for locale management targeting English and Spanish.

Changes

Cohort / File(s) Summary
Dependency Addition
frontend/package.json
Added lingo.dev v0.114.5 to dependencies.
Localization Provider Integration
frontend/src/main.tsx
Imported LingoProviderWrapper and loadDictionary from lingo.dev/react/client. Wrapped App component with LingoProviderWrapper, configuring loadDictionary callback for locale handling.
UI Component Enhancement
frontend/src/components/TopNav.tsx
Imported LocaleSwitcher component from lingo.dev/react/client. Added LocaleSwitcher instances in both desktop navigation (hidden on small screens) and mobile navigation panel, configured with locales ["en", "es"].
Build Configuration
frontend/vite.config.ts
Imported lingoCompiler from lingo.dev/compiler. Converted static config export to function-wrapped config using lingoCompiler.vite() with sourceRoot, targetLocales, and models parameters.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant App as App Component
    participant LingoProvider as LingoProviderWrapper
    participant Dictionary as Dictionary Loader
    participant LocaleSwitcher as LocaleSwitcher UI

    Browser->>App: Initialize application
    App->>LingoProvider: Wrap App with provider
    LingoProvider->>Dictionary: Load initial locale (en)
    Dictionary-->>LingoProvider: Dictionary loaded
    LingoProvider-->>App: Locale context ready
    LocaleSwitcher->>LingoProvider: User selects locale (es)
    LingoProvider->>Dictionary: loadDictionary(es)
    Dictionary-->>LingoProvider: Spanish dictionary loaded
    LingoProvider-->>App: Update locale context
    App->>Browser: Re-render with new locale
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Vite config function-wrapping pattern should be verified for build compatibility
  • Ensure lingo.dev dependency version compatibility with existing project configuration
  • Confirm locale handling in LingoProviderWrapper callback integrates correctly with app state management

Poem

🐰 A dash of lingo.dev, locales now flow,
Spanish and English in harmony glow,
The rabbit hops between tongues with delight,
NavBars now switch with elegant might,
Global connections, translations take flight! 🌍✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding Lingo i18n support with a note about breaking changes. It directly relates to the primary objective of integrating multilingual support.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch lingo

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

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 11, 2025

Greptile Overview

Greptile Summary

This PR adds multilingual support using lingo.dev with Spanish translations. The implementation includes:

  • Lingo compiler integration in Vite build configuration
  • LingoProviderWrapper wrapping the entire app in main.tsx
  • LocaleSwitcher component added to TopNav for language selection
  • Auto-generated translation dictionary and metadata

Critical Issue (Acknowledged by Author):

The Lingo text-wrapping approach fundamentally breaks Radix UI components. When Lingo wraps text nodes, it interferes with ref forwarding that Radix components (Dialog, DropdownMenu, AlertDialog) depend on for positioning and functionality. This makes critical UI features like the profile dialog and dropdown menus unusable.

Recommendation:

As noted in the PR description, this should NOT be merged in its current state. Consider these alternatives:

  1. Switch to react-i18next - Uses a non-wrapping API that won't interfere with component refs
  2. Use next-intl or similar - More mature i18n solutions with better React ecosystem compatibility
  3. Manual exclusions - Add data-lingo-skip to all Radix components (tedious and error-prone)

The integration is technically clean otherwise, but the core architectural incompatibility with Radix UI makes this approach non-viable for production.

Confidence Score: 0/5

  • This PR cannot be safely merged - it breaks critical UI functionality across the application
  • Score of 0 reflects that the PR introduces a breaking change that makes essential user interactions (opening dialogs, using dropdowns) completely non-functional. The author has correctly marked this as "DO NOT MERGE" and documented the issue. While the Lingo integration itself is implemented correctly, the fundamental incompatibility with Radix UI means this approach is not production-ready.
  • frontend/src/main.tsx requires immediate attention - the LingoProviderWrapper must be removed or replaced with a Radix-compatible i18n solution before any merge consideration

Important Files Changed

File Analysis

Filename Score Overview
frontend/package.json 4/5 Added lingo.dev dependency (^0.114.5) for i18n support
frontend/vite.config.ts 3/5 Integrated Lingo compiler plugin, configured for Spanish translation - may impact build performance
frontend/src/main.tsx 2/5 Wrapped app with LingoProviderWrapper - breaks Radix UI components due to text wrapping interfering with ref forwarding
frontend/src/components/TopNav.tsx 4/5 Added LocaleSwitcher component for language switching in both desktop and mobile navigation

Sequence Diagram

sequenceDiagram
    participant User
    participant Browser
    participant Main as main.tsx
    participant Lingo as LingoProviderWrapper
    participant App as App Component
    participant TopNav
    participant Switcher as LocaleSwitcher
    participant Dict as dictionary.js

    User->>Browser: Load Application
    Browser->>Main: Initialize App
    Main->>Lingo: Wrap with LingoProviderWrapper
    Lingo->>Dict: loadDictionary(locale)
    Dict-->>Lingo: Return translations
    Lingo->>App: Render with translation context
    App->>TopNav: Render navigation
    TopNav->>Switcher: Display LocaleSwitcher(en, es)
    
    Note over Lingo,App: ⚠️ Lingo wraps all text nodes<br/>Breaking Radix UI refs
    
    User->>Switcher: Click language option
    Switcher->>Lingo: Change locale to 'es'
    Lingo->>Dict: loadDictionary('es')
    Dict-->>Lingo: Spanish translations
    Lingo->>App: Re-render with Spanish
    
    User->>App: Try to open Dialog/Dropdown
    App->>App: ❌ Ref forwarding fails
    Note over App: Components don't open due to<br/>text wrapping interference
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +10 to +14
lingoCompiler.vite({
sourceRoot: "src",
targetLocales: ["es"],
models: "lingo.dev"
})({
Copy link
Contributor

Choose a reason for hiding this comment

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

style: the Lingo compiler wraps the entire Vite config, which may complicate debugging and maintenance

Consider a more explicit composition pattern:

Suggested change
lingoCompiler.vite({
sourceRoot: "src",
targetLocales: ["es"],
models: "lingo.dev"
})({
export default defineConfig(() => {
const baseConfig = {
plugins: [TanStackRouterVite(), react(), derPlugin()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src")
}
}
};
return lingoCompiler.vite({
sourceRoot: "src",
targetLocales: ["es"],
models: "lingo.dev"
})(baseConfig);
})
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/vite.config.ts
Line: 10:14

Comment:
**style:** the Lingo compiler wraps the entire Vite config, which may complicate debugging and maintenance

Consider a more explicit composition pattern:

```suggestion
export default defineConfig(() => {
  const baseConfig = {
    plugins: [TanStackRouterVite(), react(), derPlugin()],
    resolve: {
      alias: {
        "@": path.resolve(__dirname, "./src")
      }
    }
  };
  
  return lingoCompiler.vite({
    sourceRoot: "src",
    targetLocales: ["es"],
    models: "lingo.dev"
  })(baseConfig);
})
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +19 to 22
<LingoProviderWrapper loadDictionary={(locale) => loadDictionary(locale)}>
<App />
</LingoProviderWrapper>
</StrictMode>
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: wrapping text content breaks Radix UI's ref forwarding mechanism - Dialog, DropdownMenu, and AlertDialog components will fail to open

The PR description acknowledges this critical issue. Lingo wraps all text nodes which interferes with components that rely on refs for positioning and functionality.

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/main.tsx
Line: 19:22

Comment:
**logic:** wrapping text content breaks Radix UI's ref forwarding mechanism - Dialog, DropdownMenu, and AlertDialog components will fail to open

The PR description acknowledges this critical issue. Lingo wraps all text nodes which interferes with components that rely on refs for positioning and functionality.

How can I resolve this? If you propose a fix, please make it concise.

<div className="flex items-center gap-4">
{/* Language Switcher */}
<div className="hidden sm:block">
<LocaleSwitcher locales={["en", "es"]} />
Copy link
Contributor

Choose a reason for hiding this comment

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

style: hardcoded locale list limits scalability

Suggested change
<LocaleSwitcher locales={["en", "es"]} />
<LocaleSwitcher locales={["en", "es", "fr", "de"]} />

Consider moving supported locales to a config file for easier maintenance as more languages are added.

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/components/TopNav.tsx
Line: 71:71

Comment:
**style:** hardcoded locale list limits scalability

```suggestion
              <LocaleSwitcher locales={["en", "es", "fr", "de"]} />
```

Consider moving supported locales to a config file for easier maintenance as more languages are added.

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@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: 0

🧹 Nitpick comments (1)
frontend/src/main.tsx (1)

19-21: Simplify the loadDictionary prop.

The arrow function wrapper (locale) => loadDictionary(locale) is redundant. You can pass the function directly.

Apply this diff to simplify:

-        <LingoProviderWrapper loadDictionary={(locale) => loadDictionary(locale)}>
+        <LingoProviderWrapper loadDictionary={loadDictionary}>
           <App />
         </LingoProviderWrapper>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24002d3 and d6708b4.

⛔ Files ignored due to path filters (1)
  • frontend/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • frontend/package.json (1 hunks)
  • frontend/src/components/TopNav.tsx (3 hunks)
  • frontend/src/main.tsx (2 hunks)
  • frontend/vite.config.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use 2-space indentation, double quotes, and a 100-character line limit for formatting
Use camelCase for variable and function names
Use try/catch with specific error types for error handling

Files:

  • frontend/src/components/TopNav.tsx
  • frontend/vite.config.ts
  • frontend/src/main.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript typing and avoid any when possible

Files:

  • frontend/src/components/TopNav.tsx
  • frontend/vite.config.ts
  • frontend/src/main.tsx
🧠 Learnings (1)
📚 Learning: 2025-07-19T21:31:44.925Z
Learnt from: CR
Repo: OpenSecretCloud/Maple PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T21:31:44.925Z
Learning: Applies to src/**/*.{tsx} : Use React context for global state management when needed

Applied to files:

  • frontend/src/components/TopNav.tsx
🧬 Code graph analysis (2)
frontend/vite.config.ts (1)
frontend/vite-der-plugin.ts (1)
  • derPlugin (3-23)
frontend/src/main.tsx (1)
frontend/src/app.tsx (1)
  • App (50-112)
⏰ 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). (5)
  • GitHub Check: build-linux
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: build-android
  • GitHub Check: build-ios
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (7)
frontend/package.json (1)

45-45: LGTM!

The dependency addition is correctly formatted and positioned.

frontend/src/main.tsx (1)

5-5: LGTM!

The import statement is correct for Lingo React client integration.

frontend/src/components/TopNav.tsx (4)

7-7: LGTM!

The import statement is correct for the Lingo LocaleSwitcher component.


69-72: LocaleSwitcher configuration looks correct, but verify with Vite config.

The desktop LocaleSwitcher is properly positioned and configured with locales={["en", "es"]}. However, ensure this aligns with the targetLocales configuration in vite.config.ts (currently only ["es"]).


125-127: Mobile LocaleSwitcher implementation is correct, but verify with Vite config.

The mobile LocaleSwitcher implementation follows the same pattern as the desktop version and includes appropriate styling. Ensure the locales={["en", "es"]} configuration aligns with vite.config.ts.


1-134: Lingo integration carries unmitigated Radix UI compatibility risks—verify AccountMenu and nested dialogs.

Lingo 0.114.5 is active in the codebase, but no workarounds (e.g., data-lingo-skip) are implemented. While TopNav.tsx's LocaleSwitcher integration itself is syntactically sound, the broader codebase has severe exposure:

  • AccountMenu nests AlertDialog > Dialog > DropdownMenu (all Radix primitives)—highest risk for ref forwarding failures
  • 20+ Dialog components, 6+ DropdownMenu, 10+ AlertDialog components across the app depend on proper ref handling

The PR objectives document that Lingo breaks these components' text wrapping and ref forwarding. Before merging, either:

  1. Implement data-lingo-skip on all Radix UI wrapper components
  2. Switch to an alternative i18n library
  3. Test AccountMenu and nested modals thoroughly to confirm no breakage
frontend/vite.config.ts (1)

10-13: Review comment is incorrect—configuration is properly aligned.

The Lingo compiler configuration is actually correct. According to Lingo documentation, sourceLocale is the source language (default "en") and targetLocales is the array of locale codes you want generated.

The current setup:

  • sourceLocale: "en" (implicit default)
  • targetLocales: ["es"] (Spanish translations to be generated)
  • LocaleSwitcher locales={["en", "es"]} (users can select source or target language)

This is the intended design: the source locale ("en") is not included in targetLocales because source strings don't require compilation—only target languages do. Users can still switch to English via LocaleSwitcher since it's the active source locale. No runtime issues will occur.

Likely an incorrect or invalid review comment.

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.

2 participants