Skip to content

Conversation

@Apoorva2394
Copy link

@Apoorva2394 Apoorva2394 commented Jan 24, 2026

Supersedes #1875 (closed after feedback was addressed)

Summary

This PR adds a zero-config React localizer demo under the /community directory.
The demo showcases how lingo.dev automatically localizes UI text and formatting
at build time without translation keys, runtime libraries, or code changes.

What this demo shows

  • Automatic UI text translation
  • Locale switching using setLingoLocale
  • Zero translation keys or t() functions
  • Build-time localization via compiler
  • React 19 compatibility

Testing

  • Installed dependencies locally
  • Ran the demo using npm install and npm run dev
  • Verified language switching works correctly
  • Verified UI text updates without code changes

Visuals

  • Language selector UI with live switching
  • Demo page content translated across locales
    (Screenshots or demo video can be added if required)

Checklist

  • No breaking changes
  • Demo isolated to /community
  • Follows zero-config localization principles
  • Compatible with React 19

Summary by CodeRabbit

  • New Features
    • Added a zero‑config localizer demo app: language switching, interactive build-process visualization, bundle‑size comparison, code transformation inspector, original-source preview, and several example components (forms, dynamic, nested, simple).
  • Documentation
    • Added comprehensive README with quick start, features, translation workflow, troubleshooting, and contribution notes.
  • Chores
    • Added project manifest, build/dev configuration, static demo page, global styles, and repository ignore rules.

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 24, 2026

📝 Walkthrough

Walkthrough

Added a zero‑config localization demo: a Vite + React app integrating the lingo.dev compiler, with demo UI, example components, build tooling, README, and assets demonstrating build‑time translation, pseudotranslation, and per‑locale bundle generation.

Changes

Cohort / File(s) Summary
Project Metadata & Build
community/zero-config-localizer/package.json, community/zero-config-localizer/vite.config.js
New package.json and Vite config wiring the lingo.dev compiler plugin, scripts (dev/build/preview/translate), sourceRoot/sourceLocale/targetLocales, conditional GROQ models, and pseudotranslator/cache build modes.
Repo Ignore
community/zero-config-localizer/.gitignore
Added .gitignore with node_modules, PnP, coverage, build artifacts, env files, logs, editor and OS temp files.
App Shell & Entry
community/zero-config-localizer/index.html, community/zero-config-localizer/src/main.jsx, community/zero-config-localizer/src/index.css
New HTML entry, React bootstrap mounting App inside a Lingo provider wrapper, and global CSS (reset, typography, animation utilities).
Root App
community/zero-config-localizer/src/App.jsx
New default-export App: tabbed UI, locale switching via useLingoLocale, and layout composing demo sections and components.
Compiler / Visualization Components
community/zero-config-localizer/src/components/CompilerVisualization.jsx, .../BundleComparison.jsx, .../CodeInspector.jsx
New interactive components: animated multi-step compiler visualization (interval-driven), bundle size comparison (Recharts), and before/after code transformation inspector. Check interval cleanup and chart responsiveness.
UI Display Component
community/zero-config-localizer/src/components/OriginalCode.jsx
New component rendering an original source code snippet and checklist panel.
Example App Components
community/zero-config-localizer/src/examples/SimpleComponent.jsx, .../FormComponent.jsx, .../DynamicComponent.jsx, .../NestedComponent.jsx
Example components demonstrating static text, form validation, locale-aware date formatting, and nested dynamic content for compiler detection.
Styling & Assets
community/zero-config-localizer/src/index.css, community/zero-config-localizer/index.html
Global styles and basic HTML scaffold for the demo app.
Documentation
community/zero-config-localizer/README.md
Added comprehensive README explaining demo purpose, architecture, quick start, pseudotranslation vs real translations, code transform workflow, and troubleshooting.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant Vite as Vite
    participant Plugin as lingo.dev Compiler Plugin
    participant Parser as JSX Parser
    participant Detector as Content Detector
    participant FP as Fingerprint Generator
    participant API as Translation API
    participant Injector as Lookup Injector
    participant Bundler as Bundler

    Dev->>Vite: start dev/build
    Vite->>Plugin: invoke compiler plugin
    Plugin->>Parser: parse source (JSX → AST)
    Parser->>Detector: detect translatable strings/nodes
    Detector->>FP: generate fingerprints & extract strings
    FP->>API: request translations for target locales
    API-->>FP: return translations
    FP->>Injector: generate lookup code & inject translations
    Injector->>Bundler: emit optimized locale bundles
    Bundler->>Dev: deliver built artifacts
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰
I hop through JSX fields so bright,
I sniff the strings and set them right,
Build‑time carrots, small and sweet,
Bundles lean, translations neat—
Hooray for localized treats! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding a zero-config localizer demo. It directly reflects the primary objective of introducing a new demo under /community.
Description check ✅ Passed The description provides a comprehensive summary, key changes, testing performed, and completed checklist items. It follows the template structure with summary, changes, testing, visuals, and checklist sections, though some template checkboxes are not formally checked.

✏️ 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

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

🤖 Fix all issues with AI agents
In `@community/zero-config-localizer/README.md`:
- Around line 104-110: Update README.md to satisfy markdownlint: add language
specifier "text" to the fenced code blocks for the "Original Text" block and the
"Pseudotranslation" block (both occurrences referenced as the pseudotranslation
and original text code blocks in README.md), replace the bare URL
"https://console.groq.com" with an inline-autolink <https://console.groq.com>,
and remove `.env.example` from the project tree listing under the
zero-config-localizer file tree so the listed files match the repository tree.

In `@community/zero-config-localizer/src/components/CodeInspector.jsx`:
- Around line 89-101: The selector buttons currently toggle visually but lack
ARIA state and a button type; update the JSX for the elements rendered in the
CodeInspector component (the button elements that call setSelectedExample and
read selectedExample) to include type="button" and aria-pressed={selectedExample
=== index} so assistive tech can observe the pressed state and to prevent
accidental form submissions.

In `@community/zero-config-localizer/vite.config.js`:
- Around line 6-21: The build currently forces real translations by calling
lingoCompiler.vite with models set to 'groq:llama-3.3-70b-versatile' and
buildMode: 'translate', which will fail when GROQ_API_KEY is not present; update
the lingoCompiler.vite invocation to detect process.env.GROQ_API_KEY (or read
from dotenv) and, if it's missing, switch to a safe fallback: set buildMode to
'cache-only' (or change models to a non-Groq/pseudotranslator entry) so
translations are not attempted, otherwise keep the Groq model and buildMode
'translate'; target the lingoCompiler.vite call and the models and buildMode
properties when implementing this conditional.
🧹 Nitpick comments (4)
community/zero-config-localizer/src/examples/FormComponent.jsx (1)

18-33: Add basic a11y wiring for the error state.
This improves screen-reader feedback with minimal change.

♿️ Suggested update
         <input
           id="email"
           type="email"
           placeholder="Enter your email"
           value={email}
           onChange={(e) => setEmail(e.target.value)}
+          aria-invalid={Boolean(error)}
+          aria-describedby={error ? 'email-error' : undefined}
           required
         />
-        {error && <span style={{ color: 'red' }}>{error}</span>}
+        {error && (
+          <span id="email-error" role="alert" style={{ color: 'red' }}>
+            {error}
+          </span>
+        )}
community/zero-config-localizer/src/examples/DynamicComponent.jsx (1)

1-10: Stabilize the creation date to avoid render-time drift.

new Date() in render will change across re-renders (and after locale updates), which makes the “account created on” value shift. Consider memoizing the date or passing it as a prop so the output stays stable.

♻️ Suggested update
-import { useLingoLocale } from 'lingo.dev/react/client';
+import { useMemo } from 'react';
+import { useLingoLocale } from 'lingo.dev/react/client';
   
 export default function DynamicComponent({ userName, messageCount }) {
   const locale = useLingoLocale();
-  const createdOn = new Date().toLocaleDateString(locale ?? undefined);
+  const createdOn = useMemo(() => new Date(), []);
+  const createdOnLabel = useMemo(
+    () => createdOn.toLocaleDateString(locale ?? undefined),
+    [createdOn, locale]
+  );
   return (
     <div>
       <h2>Hello, {userName}!</h2>
       <p>You have {messageCount} new messages</p>
-      <p>Your account was created on {createdOn}</p>
+      <p>Your account was created on {createdOnLabel}</p>
     </div>
   );
 }
community/zero-config-localizer/src/components/BundleComparison.jsx (1)

5-52: Derive card metrics from data to prevent drift.

The stat cards (234 KB, 97 KB, 58.5%) are hard-coded while the chart uses data. If the data changes, the cards can drift. Consider computing the numbers from the same source.

♻️ Suggested update
 export default function BundleComparison() {
   const data = [
     {
       name: 'react-i18next',
       bundle: 145,
       dictionaries: 89,
       total: 234
     },
@@
       total: 97
     }
   ];
+  const traditionalTotal =
+    data.find((d) => d.name === 'react-i18next')?.total ?? data[0]?.total ?? 0;
+  const lingoTotal =
+    data.find((d) => d.name === 'Lingo.dev')?.total ?? data[data.length - 1]?.total ?? 0;
+  const sizeReduction = traditionalTotal
+    ? ((1 - lingoTotal / traditionalTotal) * 100).toFixed(1)
+    : '0.0';

   return (
     <div>
@@
         <StatCard
           icon={Package}
           title="Traditional i18n"
-          value="234 KB"
+          value={`${traditionalTotal} KB`}
           description="Runtime library + translation files"
           color="#ef4444"
         />
         <StatCard
           icon={Package}
           title="Lingo.dev Compiler"
-          value="97 KB"
+          value={`${lingoTotal} KB`}
           description="No runtime overhead"
           color="#10b981"
         />
         <StatCard
           icon={TrendingDown}
           title="Size Reduction"
-          value="58.5%"
+          value={`${sizeReduction}%`}
           description="Smaller bundles, faster loads"
           color="#667eea"
         />
community/zero-config-localizer/src/main.jsx (1)

7-11: Consider adding a small Suspense fallback while dictionaries load.
Line 9 renders LingoProviderWrapper without a fallback, so the app can be blank during initial dictionary fetch. A lightweight fallback improves UX for the demo.

♻️ Possible tweak
-    <LingoProviderWrapper loadDictionary={(locale) => loadDictionary(locale)}>
+    <LingoProviderWrapper
+      loadDictionary={(locale) => loadDictionary(locale)}
+      fallback={<div style={{ padding: 24, color: '#94a3b8' }}>Loading translations…</div>}
+    >

Comment on lines 104 to 110
```
Welcome to our platform
```

**Pseudotranslation:**
```
[Ŵêƚçôɱê ţô ôûŕ þƚàţƒôŕɱ]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix markdownlint violations and sync the project tree.
Line 104/109/178 need fenced languages (MD040). Line 127 has a bare URL (MD034). Line 185 lists .env.example; if it was removed, the tree should be updated.

✅ Suggested doc fixes
-**Original Text:**
-```
+**Original Text:**
+```text
 Welcome to our platform

-Pseudotranslation:
- +**Pseudotranslation:** +text
[Ŵêƚçôɱê ţô ôûŕ þƚàţƒôŕɱ]


-1. Visit https://console.groq.com
+1. Visit <https://console.groq.com>

-```
+```text
zero-config-localizer/
├── README.md
├── package.json
├── vite.config.js
├── index.html
├── .gitignore
-├── .env.example
├── src/
│   ├── main.jsx

Also applies to: 127-127, 178-186

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

104-104: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


109-109: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@community/zero-config-localizer/README.md` around lines 104 - 110, Update
README.md to satisfy markdownlint: add language specifier "text" to the fenced
code blocks for the "Original Text" block and the "Pseudotranslation" block
(both occurrences referenced as the pseudotranslation and original text code
blocks in README.md), replace the bare URL "https://console.groq.com" with an
inline-autolink <https://console.groq.com>, and remove `.env.example` from the
project tree listing under the zero-config-localizer file tree so the listed
files match the repository tree.

@Apoorva2394
Copy link
Author

Hi! I’ve addressed all feedback from CodeRabbit and updated the PR accordingly.
All checks are now passing on my side.

Noted that remaining workflow approvals require maintainer permissions.
Thanks for taking a look!

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: 3

🤖 Fix all issues with AI agents
In `@community/zero-config-localizer/src/examples/DynamicComponent.jsx`:
- Around line 11-16: The displayed "created on" date uses new Date() so
createdOn always shows today's date; change DynamicComponent to accept a
createdAt prop (e.g., createdAt) and compute createdOn with new
Date(createdAt).toLocaleDateString(locale ?? undefined) in place of new Date(),
or alternatively update the copy to something like "Today's date" if you don't
want to add a prop; update references to createdOn in the JSX (and document the
new createdAt prop) so the UI shows a real/mocked creation date instead of the
current date.

In `@community/zero-config-localizer/src/examples/NestedComponent.jsx`:
- Line 20: The JSX currently renders a hardcoded dollar sign around
product.price which breaks localization; update the rendering in NestedComponent
(where product.price is used) to format the price with Intl.NumberFormat (e.g.,
new Intl.NumberFormat(locale, { style: 'currency', currency })) using a
locale/currency prop or context, or if intentionally left as a limitation add a
clear inline comment above the JSX noting the hardcoded $ is deliberate for the
example; ensure you reference the product.price usage and the component name
NestedComponent when making the change.
- Around line 7-12: The NestedComponent currently accesses product.name and
product.sku without guarding against product being null/undefined; update the
component (NestedComponent) to defensively handle a missing product by either
providing a default value in the parameter (e.g., product = {}), adding an early
guard/conditional render (e.g., return null or a placeholder when product is
falsy), or using optional chaining when reading properties (product?.name,
product?.sku) so no TypeError is thrown.
♻️ Duplicate comments (1)
community/zero-config-localizer/src/components/CodeInspector.jsx (1)

99-114: Add type="button" and aria-pressed for accessibility.

The selector buttons visually toggle but lack ARIA state for assistive technologies, and missing type="button" could cause unintended form submissions if nested.

🧩 Suggested fix
          <button
+           type="button"
+           aria-pressed={selectedExample === index}
            key={index}
            onClick={() => setSelectedExample(index)}
            style={{
🧹 Nitpick comments (5)
community/zero-config-localizer/src/components/OriginalCode.jsx (1)

1-4: Remove extra blank lines at the start of the file.

Lines 1-2 are unnecessary blank lines before the imports.

🧹 Suggested cleanup
-
-
 import { Code } from 'lucide-react';
community/zero-config-localizer/src/components/CodeInspector.jsx (1)

147-158: Hide decorative arrow from assistive technologies.

The arrow is purely decorative and should be hidden from screen readers to avoid confusion.

🧩 Suggested fix
        <div style={{
          width: 48,
          height: 48,
          borderRadius: '50%',
          background: 'linear-gradient(135deg, `#667eea` 0%, `#764ba2` 100%)',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0
-       }}>
+       }}
+         aria-hidden="true"
+       >
          <ArrowRight size={24} />
        </div>
community/zero-config-localizer/src/examples/DynamicComponent.jsx (2)

7-19: Inconsistent indentation throughout the file.

The import statement on line 7 has no indentation, but the function definition (lines 9-19) is indented by 2 spaces. This appears to be an accidental formatting issue.

🛠️ Suggested fix
 import { useLingoLocale } from 'lingo.dev/react/client';
-  
-  export default function DynamicComponent({ userName, messageCount }) {
-    const locale = useLingoLocale();
-    const createdOn = new Date().toLocaleDateString(locale ?? undefined);
-    return (
-      <div>
-        <h2>Hello, {userName}!</h2>
-        <p>You have {messageCount} new messages</p>
-        <p>Your account was created on {createdOn}</p>
-      </div>
-    );
-  }
-  
-  
+
+export default function DynamicComponent({ userName, messageCount }) {
+  const locale = useLingoLocale();
+  const createdOn = new Date().toLocaleDateString(locale ?? undefined);
+  return (
+    <div>
+      <h2>Hello, {userName}!</h2>
+      <p>You have {messageCount} new messages</p>
+      <p>Your account was created on {createdOn}</p>
+    </div>
+  );
+}

1-5: JSDoc is incomplete—missing @param tags for props.

The JSDoc documents the return type but omits the userName and messageCount props. For a demo component meant to showcase patterns, complete documentation would be helpful.

📝 Suggested fix
 /**
- * React component example.
+ * Dynamic component example demonstrating locale-aware date formatting.
  *
+ * `@param` {Object} props - Component props
+ * `@param` {string} props.userName - The user's display name
+ * `@param` {number} props.messageCount - Number of new messages
  * `@returns` {JSX.Element}
  */
community/zero-config-localizer/src/examples/NestedComponent.jsx (1)

1-5: JSDoc is missing @param for the product prop.

Consider documenting the expected shape of the product object to help consumers of this component.

📝 Suggested documentation improvement
 /**
  * React component example.
  *
+ * `@param` {Object} props - Component props
+ * `@param` {Object} props.product - Product data object
+ * `@param` {string} props.product.name - Product name
+ * `@param` {string} props.product.sku - Product SKU
+ * `@param` {string} props.product.description - Product description
+ * `@param` {number} props.product.price - Product price
+ * `@param` {number} props.product.stock - Available stock count
  * `@returns` {JSX.Element}
  */

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: 1

🤖 Fix all issues with AI agents
In `@community/zero-config-localizer/src/examples/DynamicComponent.jsx`:
- Around line 13-16: The createdOn calculation in DynamicComponent uses new
Date(createdAt) which treats 'YYYY-MM-DD' as UTC and can cause timezone drift;
detect if createdAt is a date-only ISO string (e.g., /^\d{4}-\d{2}-\d{2}$/) and,
if so, parse into a local Date using components (new Date(year, month-1, day))
before calling toLocaleDateString; otherwise continue to use new Date(createdAt)
as-is, and keep useLingoLocale() for formatting.
🧹 Nitpick comments (1)
community/zero-config-localizer/src/examples/NestedComponent.jsx (1)

11-14: Consider using useLingoLocale() for consistency with other demo components.

The locale prop approach works, but DynamicComponent.jsx uses the useLingoLocale() hook directly. For a cohesive demo showcasing the lingo.dev compiler's zero-config philosophy, consider whether all components should follow the same pattern.

If intentional to demonstrate multiple approaches, a brief inline comment would clarify this design choice.

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.

1 participant