Skip to content

Conversation

hashaam-011
Copy link

@hashaam-011 hashaam-011 commented Aug 14, 2025

🌍 Add Internationalization (i18n) Support
Overview
This PR implements comprehensive internationalization support for the DreamLayer frontend, enabling users to switch between English and French languages throughout the entire application.
✨ Features Added
Complete i18n System: React Context-based translation system with language switching
Language Selector: Added to header for easy language toggling
Comprehensive Translations: Full English and French translations for all UI elements
Real-time Language Switching: Instant translation updates without page refresh
📁 Files Modified
Translation Files:
src/i18n/en.js - Complete English translations
src/i18n/fr.js - Complete French translations
src/i18n/i18nContext.tsx - React Context provider
src/i18n/index.ts - Export module
Components Updated:
src/App.tsx - Wrapped with I18nProvider
src/components/NavBar.tsx - Added language selector
src/components/LanguageSelector.tsx - New language switcher component
All major pages and components now use t() function for translations
🎯 Pages Fully Translated
Txt2Img Page: All generation settings, prompts, and controls
Img2Img Page: Image upload, tools, and settings
Extras Page: Upscaling options, face restoration, batch processing
Models Page: Model management, upload, search, and filtering
Configurations Page: System settings, paths, and preferences
PNG Info Page: Metadata extraction and display
🔧 Technical Implementation
Uses React Context API for global language state management
Translation keys organized by feature/module for maintainability
Fallback to English for missing translations
TypeScript support with proper typing
No breaking changes to existing functionality
🚀 How to Test
Start the frontend application
Look for the language selector in the header (EN/FR)
Toggle between English and French
Verify all UI elements translate correctly
Test language persistence across page refreshes
📝 Translation Coverage
✅ Navigation and headers
✅ All form labels and placeholders
✅ Button text and actions
✅ Error messages and notifications
✅ Settings and configuration options
✅ Help text and descriptions
✅ Modal dialogs and popups
This implementation provides a solid foundation for adding more languages in the future while maintaining a clean, maintainable codebase.

Screenshot 2025-08-14 215906 Screenshot 2025-08-14 215841

Summary by Sourcery

Implement comprehensive internationalization support by introducing a React Context i18n system, full English and French translations, and a header language selector for instant language toggling

New Features:

  • Add React Context-based internationalization system with I18nProvider and useI18n hook
  • Provide complete English and French translation dictionaries for all UI strings
  • Add LanguageSelector component and integrate it into the header for runtime language switching

Summary by CodeRabbit

  • New Features
    • Added multilingual support with English and French translations across the UI.
    • Introduced a language selector dropdown with flags to quickly switch languages.
    • Current language is visibly highlighted for clarity.
    • Built-in fallback displays English when a translation is unavailable.
    • Accessible trigger includes a screen-reader-friendly label.

Copy link
Contributor

sourcery-ai bot commented Aug 14, 2025

Reviewer's Guide

This PR implements a full internationalization layer in the frontend by introducing React Context to manage the current language, providing English and French translation bundles, and integrating a language switcher component into the UI. All UI text paths now use a t() lookup function with an English fallback, and the App is wrapped in I18nProvider to enable real-time language toggling.

Sequence diagram for real-time language switching

sequenceDiagram
  actor User
  participant LanguageSelector
  participant I18nProvider
  participant UIComponent
  User->>LanguageSelector: Clicks language dropdown
  LanguageSelector->>I18nProvider: setLanguage(newLang)
  I18nProvider->>UIComponent: t(key) returns translation for newLang
  UIComponent-->>User: UI text updates instantly
Loading

Class diagram for the LanguageSelector component

classDiagram
  class LanguageSelector {
    + uses useI18n()
    + renders DropdownMenu
    + onClick: setLanguage(lang)
  }
  LanguageSelector --> I18nProvider
Loading

File-Level Changes

Change Details Files
Add translation bundles for English and French
  • Created en.js with a complete mapping of English UI strings
  • Created fr.js with corresponding French translations
src/i18n/en.js
src/i18n/fr.js
Implement React Context-based i18n provider
  • Defined I18nContext with language state and setter
  • Built t() function for nested key lookups and fallback logic
  • Exported provider and hook via index.ts
  • Typed TranslationKeys and LanguageCode in TypeScript
src/i18n/i18nContext.tsx
src/i18n/index.ts
Integrate language selector UI and wrap application
  • Added LanguageSelector component using dropdown menu and flag icons
  • Wrapped in I18nProvider to supply context
  • Updated NavBar to include the language toggle
  • Replaced hard-coded text with t() lookups across major pages
src/components/LanguageSelector.tsx
src/App.tsx
src/components/NavBar.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

coderabbitai bot commented Aug 14, 2025

Walkthrough

Introduces an i18n system with provider, hook, and translations (English and French). Adds a LanguageSelector UI component that uses the i18n context to switch languages. Re-exports provider, hook, and locales via an index module.

Changes

Cohort / File(s) Summary
i18n Context & Hook
dream_layer_frontend/src/i18n/i18nContext.tsx
Adds I18nProvider and useI18n with language state, translation lookup (dot-path), English fallback, and availableLanguages metadata.
Locales
dream_layer_frontend/src/i18n/en.js, dream_layer_frontend/src/i18n/fr.js
Adds English and French translation dictionaries; static nested objects for UI strings.
i18n Re-exports
dream_layer_frontend/src/i18n/index.ts
Re-exports I18nProvider, useI18n, en, fr.
UI Component
dream_layer_frontend/src/components/LanguageSelector.tsx
New dropdown component to list available languages, indicate active, and call setLanguage on selection.

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant LS as LanguageSelector
  participant IH as useI18n Hook
  participant IP as I18nProvider
  participant V as View Components

  U->>LS: Click trigger
  LS->>LS: Open dropdown (languages)
  U->>LS: Select language
  LS->>IH: setLanguage(code)
  IH->>IP: Update language state
  IP-->>V: Provide t() with new language
  V->>IH: t("...keys")
  IH-->>V: Translated strings (with en fallback)
  V->>U: Rerendered UI in selected language
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Poem

I tickle the globe with a twitch of my ear,
Flags in a dropdown—bonjour, hello, my dear!
With hops between keys and dots on the trail,
If French goes missing, English won’t fail.
New words bloom bright—what a multilingual year! 🐇🌍

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • Persist the selected language (e.g. in localStorage or cookies) so user choice survives page refreshes and new sessions.
  • Consider memoizing or caching getNestedValue lookups to improve performance when calling t() repeatedly in renders.
  • Strengthen type safety by defining a literal union of translation keys for t() instead of using a raw string, catching typos at compile time.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Persist the selected language (e.g. in localStorage or cookies) so user choice survives page refreshes and new sessions.
- Consider memoizing or caching getNestedValue lookups to improve performance when calling t() repeatedly in renders.
- Strengthen type safety by defining a literal union of translation keys for t() instead of using a raw string, catching typos at compile time.

## Individual Comments

### Comment 1
<location> `dream_layer_frontend/src/i18n/i18nContext.tsx:62` </location>
<code_context>
+    fr
+  };
+
+  const t = (key: string): string => {
+    const currentTranslations = translations[language];
+    const value = getNestedValue(currentTranslations, key);
+
+    // If the value is the same as the key, try English as fallback
+    if (value === key && language !== 'en') {
+      const fallbackValue = getNestedValue(translations.en, key);
+      return typeof fallbackValue === 'string' ? fallbackValue : key;
+    }
+
+    return value;
+  };
+
</code_context>

<issue_to_address>
The translation function t does not support variable interpolation for dynamic values.

Several translation keys contain placeholders (e.g., {count}, {total}), but the current function does not handle their interpolation. Please update the translation function to support passing a values object for dynamic replacement.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
  const t = (key: string): string => {
    const currentTranslations = translations[language];
    const value = getNestedValue(currentTranslations, key);

    // If the value is the same as the key, try English as fallback
    if (value === key && language !== 'en') {
      const fallbackValue = getNestedValue(translations.en, key);
      return typeof fallbackValue === 'string' ? fallbackValue : key;
    }

    return value;
  };
=======
  const t = (key: string, values?: Record<string, string | number>): string => {
    const currentTranslations = translations[language];
    let value = getNestedValue(currentTranslations, key);

    // If the value is the same as the key, try English as fallback
    if (value === key && language !== 'en') {
      const fallbackValue = getNestedValue(translations.en, key);
      value = typeof fallbackValue === 'string' ? fallbackValue : key;
    }

    // Interpolate variables if values are provided and value is a string
    if (typeof value === 'string' && values) {
      Object.entries(values).forEach(([placeholder, replacement]) => {
        value = value.replace(new RegExp(`{${placeholder}}`, 'g'), String(replacement));
      });
    }

    return value;
  };
>>>>>>> REPLACE

</suggested_fix>

### Comment 2
<location> `dream_layer_frontend/src/components/LanguageSelector.tsx:27` </location>
<code_context>
+        {Object.entries(availableLanguages).map(([code, lang]) => (
+          <DropdownMenuItem
+            key={code}
+            onClick={() => setLanguage(code as keyof typeof availableLanguages)}
+            className={language === code ? 'bg-accent' : ''}
+          >
</code_context>

<issue_to_address>
The LanguageSelector does not provide visual feedback for the currently selected language beyond background color.

Consider adding a checkmark or icon to indicate the selected language, which will improve accessibility for users with color vision deficiencies.

Suggested implementation:

```typescript
import { Globe, Check } from "lucide-react";

```

```typescript
        {Object.entries(availableLanguages).map(([code, lang]) => (
          <DropdownMenuItem
            key={code}
            onClick={() => setLanguage(code as keyof typeof availableLanguages)}
            className={language === code ? 'bg-accent' : ''}
          >
            <span className="mr-2">{lang.flag}</span>
            {lang.name}
            {language === code && (
              <Check
                className="ml-2 h-4 w-4 text-primary"
                aria-label="Selected language"
              />
            )}
          </DropdownMenuItem>
        ))}

```
</issue_to_address>

### Comment 3
<location> `dream_layer_frontend/src/i18n/i18nContext.tsx:55` </location>
<code_context>
+}
+
+export const I18nProvider: React.FC<I18nProviderProps> = ({ children }) => {
+  const [language, setLanguage] = useState<LanguageCode>('en');
+
+  const translations: Record<LanguageCode, TranslationKeys> = {
</code_context>

<issue_to_address>
Language selection is not persisted across page reloads.

Persist the selected language in localStorage or a similar mechanism to maintain user preference after reloads.

Suggested implementation:

```typescript
export const I18nProvider: React.FC<I18nProviderProps> = ({ children }) => {
  const getInitialLanguage = (): LanguageCode => {
    const saved = localStorage.getItem('language');
    return saved === 'en' || saved === 'fr' ? saved as LanguageCode : 'en';
  };

  const [language, setLanguage] = useState<LanguageCode>(getInitialLanguage());

```

```typescript
  useEffect(() => {
    localStorage.setItem('language', language);
  }, [language]);

  const translations: Record<LanguageCode, TranslationKeys> = {
    en,
    fr
  };

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +62 to +73
const t = (key: string): string => {
const currentTranslations = translations[language];
const value = getNestedValue(currentTranslations, key);

// If the value is the same as the key, try English as fallback
if (value === key && language !== 'en') {
const fallbackValue = getNestedValue(translations.en, key);
return typeof fallbackValue === 'string' ? fallbackValue : key;
}

return value;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: The translation function t does not support variable interpolation for dynamic values.

Several translation keys contain placeholders (e.g., {count}, {total}), but the current function does not handle their interpolation. Please update the translation function to support passing a values object for dynamic replacement.

Suggested change
const t = (key: string): string => {
const currentTranslations = translations[language];
const value = getNestedValue(currentTranslations, key);
// If the value is the same as the key, try English as fallback
if (value === key && language !== 'en') {
const fallbackValue = getNestedValue(translations.en, key);
return typeof fallbackValue === 'string' ? fallbackValue : key;
}
return value;
};
const t = (key: string, values?: Record<string, string | number>): string => {
const currentTranslations = translations[language];
let value = getNestedValue(currentTranslations, key);
// If the value is the same as the key, try English as fallback
if (value === key && language !== 'en') {
const fallbackValue = getNestedValue(translations.en, key);
value = typeof fallbackValue === 'string' ? fallbackValue : key;
}
// Interpolate variables if values are provided and value is a string
if (typeof value === 'string' && values) {
Object.entries(values).forEach(([placeholder, replacement]) => {
value = value.replace(new RegExp(`{${placeholder}}`, 'g'), String(replacement));
});
}
return value;
};

{Object.entries(availableLanguages).map(([code, lang]) => (
<DropdownMenuItem
key={code}
onClick={() => setLanguage(code as keyof typeof availableLanguages)}
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: The LanguageSelector does not provide visual feedback for the currently selected language beyond background color.

Consider adding a checkmark or icon to indicate the selected language, which will improve accessibility for users with color vision deficiencies.

Suggested implementation:

import { Globe, Check } from "lucide-react";
        {Object.entries(availableLanguages).map(([code, lang]) => (
          <DropdownMenuItem
            key={code}
            onClick={() => setLanguage(code as keyof typeof availableLanguages)}
            className={language === code ? 'bg-accent' : ''}
          >
            <span className="mr-2">{lang.flag}</span>
            {lang.name}
            {language === code && (
              <Check
                className="ml-2 h-4 w-4 text-primary"
                aria-label="Selected language"
              />
            )}
          </DropdownMenuItem>
        ))}

}

export const I18nProvider: React.FC<I18nProviderProps> = ({ children }) => {
const [language, setLanguage] = useState<LanguageCode>('en');
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Language selection is not persisted across page reloads.

Persist the selected language in localStorage or a similar mechanism to maintain user preference after reloads.

Suggested implementation:

export const I18nProvider: React.FC<I18nProviderProps> = ({ children }) => {
  const getInitialLanguage = (): LanguageCode => {
    const saved = localStorage.getItem('language');
    return saved === 'en' || saved === 'fr' ? saved as LanguageCode : 'en';
  };

  const [language, setLanguage] = useState<LanguageCode>(getInitialLanguage());
  useEffect(() => {
    localStorage.setItem('language', language);
  }, [language]);

  const translations: Record<LanguageCode, TranslationKeys> = {
    en,
    fr
  };

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

🧹 Nitpick comments (7)
dream_layer_frontend/src/i18n/fr.js (1)

426-431: Consider adding an accessible label for toggling language

LanguageSelector currently hardcodes an English sr-only label. Add a localized label in this locale to support accessibility.

Apply this diff to add a toggle label:

   language: {
     english: "English",
-    french: "Français"
+    french: "Français",
+    toggle: "Changer de langue"
   }
dream_layer_frontend/src/i18n/en.js (2)

42-45: Consistency: “LoRA” capitalization

The term is typically styled as “LoRA” elsewhere; here it’s “Lora”.

Apply this diff for consistency:

-    lora: "Lora",
+    lora: "LoRA",

426-431: Add localized accessible label for toggling language

Provide a localized label used by the UI for the language toggle’s sr-only text.

Apply this diff:

   language: {
     english: "English",
-    french: "Français"
+    french: "Français",
+    toggle: "Toggle language"
   }
dream_layer_frontend/src/components/LanguageSelector.tsx (3)

12-14: Localize the accessible label and expose t from context

The sr-only label is currently hardcoded in English. Pull t from the context and use a localized string.

Apply this diff:

-  const { language, setLanguage, availableLanguages } = useI18n();
+  const { language, setLanguage, availableLanguages, t } = useI18n();

18-21: Use localized label for accessibility

Replace hardcoded “Toggle language” with a localized string.

Apply this diff:

-        <Button variant="ghost" size="sm" className="h-8 w-8 p-0">
+        <Button variant="ghost" size="sm" className="h-8 w-8 p-0" aria-label={t('language.toggle')}>
           <Globe className="h-4 w-4" />
-          <span className="sr-only">Toggle language</span>
+          <span className="sr-only">{t('language.toggle')}</span>
         </Button>

25-33: Prefer onSelect over onClick for dropdown items (shadcn/ui idiom)

DropdownMenuItem is typically wired with onSelect for better keyboard support and to prevent unexpected behavior.

Apply this diff:

-          <DropdownMenuItem
+          <DropdownMenuItem
             key={code}
-            onClick={() => setLanguage(code as keyof typeof availableLanguages)}
+            onSelect={() => setLanguage(code as keyof typeof availableLanguages)}
             className={language === code ? 'bg-accent' : ''}
           >
dream_layer_frontend/src/i18n/i18nContext.tsx (1)

2-3: Type safety note: en/fr are .js; TranslationKeys likely resolves to any

Importing from .js means typeof en is probably any, weakening type safety. To realize the “TypeScript typing support” claim, consider converting en.js and fr.js to .ts and exporting with const assertions, then derive a KeyPath union for t.

Suggested follow-up (outside this hunk):

  • Rename en.js → en.ts and fr.js → fr.ts.
  • Export with “as const”.
  • Optionally define a KeyPath type to strongly type t’s key parameter.

Example:

// en.ts
export const en = { /* ... */ } as const;

// i18nContext.tsx
type Translation = typeof en;
type DotPaths<T, P extends string = ''> =
  T extends string ? P :
  { [K in keyof T & string]: DotPaths<T[K], `${P}${P extends '' ? '' : '.'}${K}`> }[keyof T & string];

type TranslationKey = DotPaths<Translation>;
// then: t: (key: TranslationKey, params?: Record<string, string | number>) => string
📜 Review details

Configuration used: CodeRabbit UI
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 settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a51a673 and eae695b.

📒 Files selected for processing (5)
  • dream_layer_frontend/src/components/LanguageSelector.tsx (1 hunks)
  • dream_layer_frontend/src/i18n/en.js (1 hunks)
  • dream_layer_frontend/src/i18n/fr.js (1 hunks)
  • dream_layer_frontend/src/i18n/i18nContext.tsx (1 hunks)
  • dream_layer_frontend/src/i18n/index.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
dream_layer_frontend/src/i18n/en.js (1)
dream_layer_frontend/src/i18n/index.ts (1)
  • en (2-2)
dream_layer_frontend/src/i18n/fr.js (1)
dream_layer_frontend/src/i18n/index.ts (1)
  • fr (3-3)
dream_layer_frontend/src/components/LanguageSelector.tsx (1)
dream_layer_frontend/src/i18n/i18nContext.tsx (1)
  • useI18n (90-96)
🪛 Biome (2.1.2)
dream_layer_frontend/src/i18n/en.js

[error] 248-249: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)


[error] 282-283: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)

dream_layer_frontend/src/i18n/fr.js

[error] 252-253: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)


[error] 287-287: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)

🔇 Additional comments (2)
dream_layer_frontend/src/i18n/index.ts (1)

1-3: LGTM: Clear public re-exports

The i18n API surface is cleanly re-exported and straightforward to consume.

dream_layer_frontend/src/i18n/i18nContext.tsx (1)

75-80: Context value references updated setter

Ensure the context value references the new setLanguage wrapper that persists to localStorage.

No code change required if you applied the previous diffs, but please double-check that the exported setLanguage in the context value is the wrapper and not the state setter.

Comment on lines +248 to +257
modelType: "Model Type",
fileSize: "File Size",
lastModified: "Last Modified",
// Model Manager Page
uploadOrganizeManage: "Upload, organize, and manage your AI models",
hideUpload: "Hide Upload",
showUpload: "Show Upload",
uploadNewModel: "Upload New Model",
modelType: "Model Type",
baseModelCheckpoint: "Base Model (Checkpoint)",
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

Duplicate object key "modelType" overwrites earlier value

Inside modelManager, modelType is declared twice. Only the last definition survives.

Apply this diff to remove the duplicate later key:

     uploadNewModel: "Upload New Model",
-    modelType: "Model Type",
     baseModelCheckpoint: "Base Model (Checkpoint)",
📝 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
modelType: "Model Type",
fileSize: "File Size",
lastModified: "Last Modified",
// Model Manager Page
uploadOrganizeManage: "Upload, organize, and manage your AI models",
hideUpload: "Hide Upload",
showUpload: "Show Upload",
uploadNewModel: "Upload New Model",
modelType: "Model Type",
baseModelCheckpoint: "Base Model (Checkpoint)",
modelType: "Model Type",
fileSize: "File Size",
lastModified: "Last Modified",
// Model Manager Page
uploadOrganizeManage: "Upload, organize, and manage your AI models",
hideUpload: "Hide Upload",
showUpload: "Show Upload",
uploadNewModel: "Upload New Model",
baseModelCheckpoint: "Base Model (Checkpoint)",
🧰 Tools
🪛 Biome (2.1.2)

[error] 248-249: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)

🤖 Prompt for AI Agents
In dream_layer_frontend/src/i18n/en.js around lines 248 to 257 there is a
duplicate object key "modelType" (declared twice) which causes the earlier value
to be overwritten; remove the second occurrence (the later "modelType" entry) so
only the first definition remains and ensure surrounding commas/formatting
remain valid.

Comment on lines +279 to +321
// Extras
extras: {
title: "Extras",
upscale: "Upscale",
faceRestoration: "Face Restoration",
imageToImage: "Image to Image",
batchFromDirectory: "Batch from Directory",
batchFromDirectoryInput: "Input Directory",
batchFromDirectoryOutput: "Output Directory",
batchFromDirectoryOutputDir: "Output Directory",
batchFromDirectoryOutputDirPlaceholder: "Select output directory...",
// Extras Page
imagePostProcessing: "Image Post-Processing",
singleImage: "Single Image",
upscalingOptions: "Upscaling Options",
processedImageWillDisplay: "Processed image will display here",
processing: "Processing...",
batchOptions: "Batch Options",
inputDirectory: "Input Directory - Batch image processing",
browseBatchFiles: "Browse Batch Files",
specifyFolder: "Specify a folder containing reference images",
batchOperation: "Batch Operation:",
resizeAll: "Resize All",
upscaleAll: "Upscale All",
convertFormat: "Convert Format",
enhanceAll: "Enhance All",
clickGenerateToProcess: "Click Generate to process the image",
// Extras specific strings
dragAndDropImageHere: "Drag & drop an image here or click to browse",
fileTypesSupported: "PNG, JPG, WEBP or GIF up to 10MB",
setUpscaleSize: "a) Set Upscale Size:",
upscaleBy: "Upscale by",
resizeTo: "Resize to",
upscale: "Upscale",
upscalingModel1: "b) Upscaling Model #1",
upscalingModel2: "c) Upscaling Model #2",
aboutTheUpscaler: "About The Upscaler:",
upscalerDescription: "Delivers extreme sharpness, great for textures and fine detail, but may exaggerate edges. Speed: Medium.",
upscaler2Visibility: "d) Upscaler 2 visibility | Optimal Level: 0.25",
gfpganVisibility: "e) GFPGAN visibility | Optimal Level: 0.8",
codeformerVisibility: "f) CodeFormer visibility | Optimal Level: 0.7",
codeformerWeight: "g) CodeFormer weight | Optimal Level: 0.2"
},
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

Duplicate object key "upscale" overwrites earlier value

In extras, the key upscale appears twice (Line 282 and Line 312). Remove the later duplicate.

Apply this diff:

     resizeTo: "Resize to",
-    upscale: "Upscale",
     upscalingModel1: "b) Upscaling Model #1",
📝 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
// Extras
extras: {
title: "Extras",
upscale: "Upscale",
faceRestoration: "Face Restoration",
imageToImage: "Image to Image",
batchFromDirectory: "Batch from Directory",
batchFromDirectoryInput: "Input Directory",
batchFromDirectoryOutput: "Output Directory",
batchFromDirectoryOutputDir: "Output Directory",
batchFromDirectoryOutputDirPlaceholder: "Select output directory...",
// Extras Page
imagePostProcessing: "Image Post-Processing",
singleImage: "Single Image",
upscalingOptions: "Upscaling Options",
processedImageWillDisplay: "Processed image will display here",
processing: "Processing...",
batchOptions: "Batch Options",
inputDirectory: "Input Directory - Batch image processing",
browseBatchFiles: "Browse Batch Files",
specifyFolder: "Specify a folder containing reference images",
batchOperation: "Batch Operation:",
resizeAll: "Resize All",
upscaleAll: "Upscale All",
convertFormat: "Convert Format",
enhanceAll: "Enhance All",
clickGenerateToProcess: "Click Generate to process the image",
// Extras specific strings
dragAndDropImageHere: "Drag & drop an image here or click to browse",
fileTypesSupported: "PNG, JPG, WEBP or GIF up to 10MB",
setUpscaleSize: "a) Set Upscale Size:",
upscaleBy: "Upscale by",
resizeTo: "Resize to",
upscale: "Upscale",
upscalingModel1: "b) Upscaling Model #1",
upscalingModel2: "c) Upscaling Model #2",
aboutTheUpscaler: "About The Upscaler:",
upscalerDescription: "Delivers extreme sharpness, great for textures and fine detail, but may exaggerate edges. Speed: Medium.",
upscaler2Visibility: "d) Upscaler 2 visibility | Optimal Level: 0.25",
gfpganVisibility: "e) GFPGAN visibility | Optimal Level: 0.8",
codeformerVisibility: "f) CodeFormer visibility | Optimal Level: 0.7",
codeformerWeight: "g) CodeFormer weight | Optimal Level: 0.2"
},
// Extras
extras: {
title: "Extras",
upscale: "Upscale",
faceRestoration: "Face Restoration",
imageToImage: "Image to Image",
batchFromDirectory: "Batch from Directory",
batchFromDirectoryInput: "Input Directory",
batchFromDirectoryOutput: "Output Directory",
batchFromDirectoryOutputDir: "Output Directory",
batchFromDirectoryOutputDirPlaceholder: "Select output directory...",
// Extras Page
imagePostProcessing: "Image Post-Processing",
singleImage: "Single Image",
upscalingOptions: "Upscaling Options",
processedImageWillDisplay: "Processed image will display here",
processing: "Processing...",
batchOptions: "Batch Options",
inputDirectory: "Input Directory - Batch image processing",
browseBatchFiles: "Browse Batch Files",
specifyFolder: "Specify a folder containing reference images",
batchOperation: "Batch Operation:",
resizeAll: "Resize All",
upscaleAll: "Upscale All",
convertFormat: "Convert Format",
enhanceAll: "Enhance All",
clickGenerateToProcess: "Click Generate to process the image",
// Extras specific strings
dragAndDropImageHere: "Drag & drop an image here or click to browse",
fileTypesSupported: "PNG, JPG, WEBP or GIF up to 10MB",
setUpscaleSize: "a) Set Upscale Size:",
upscaleBy: "Upscale by",
resizeTo: "Resize to",
upscalingModel1: "b) Upscaling Model #1",
upscalingModel2: "c) Upscaling Model #2",
aboutTheUpscaler: "About The Upscaler:",
upscalerDescription: "Delivers extreme sharpness, great for textures and fine detail, but may exaggerate edges. Speed: Medium.",
upscaler2Visibility: "d) Upscaler 2 visibility | Optimal Level: 0.25",
gfpganVisibility: "e) GFPGAN visibility | Optimal Level: 0.8",
codeformerVisibility: "f) CodeFormer visibility | Optimal Level: 0.7",
codeformerWeight: "g) CodeFormer weight | Optimal Level: 0.2"
},
🧰 Tools
🪛 Biome (2.1.2)

[error] 282-283: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)

🤖 Prompt for AI Agents
In dream_layer_frontend/src/i18n/en.js around lines 279 to 321 there is a
duplicate object key "upscale" (first at ~line 282 and again at ~line 312) which
overwrites the earlier value; remove the later duplicate entry (or rename it if
a distinct label was intended) so the extras object has a single unique
"upscale" key, and verify surrounding comma placement and linting pass.

Comment on lines +248 to +257
modelType: "Type de Modèle",
fileSize: "Taille du Fichier",
lastModified: "Dernière Modification",
// Model Manager Page
uploadOrganizeManage: "Télécharger, organiser et gérer vos modèles IA",
hideUpload: "Masquer le Téléchargement",
showUpload: "Afficher le Téléchargement",
uploadNewModel: "Télécharger un Nouveau Modèle",
modelType: "Type de Modèle",
baseModelCheckpoint: "Modèle de Base (Point de Contrôle)",
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

Duplicate object key "modelType" overwrites earlier value

Within modelManager, modelType is defined twice. Only the last one will take effect, and linters flag this. Remove one occurrence to avoid confusion and linter errors.

Apply this diff to remove the duplicate later key:

     uploadNewModel: "Télécharger un Nouveau Modèle",
-    modelType: "Type de Modèle",
     baseModelCheckpoint: "Modèle de Base (Point de Contrôle)",
📝 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
modelType: "Type de Modèle",
fileSize: "Taille du Fichier",
lastModified: "Dernière Modification",
// Model Manager Page
uploadOrganizeManage: "Télécharger, organiser et gérer vos modèles IA",
hideUpload: "Masquer le Téléchargement",
showUpload: "Afficher le Téléchargement",
uploadNewModel: "Télécharger un Nouveau Modèle",
modelType: "Type de Modèle",
baseModelCheckpoint: "Modèle de Base (Point de Contrôle)",
modelType: "Type de Modèle",
fileSize: "Taille du Fichier",
lastModified: "Dernière Modification",
// Model Manager Page
uploadOrganizeManage: "Télécharger, organiser et gérer vos modèles IA",
hideUpload: "Masquer le Téléchargement",
showUpload: "Afficher le Téléchargement",
uploadNewModel: "Télécharger un Nouveau Modèle",
baseModelCheckpoint: "Modèle de Base (Point de Contrôle)",
🧰 Tools
🪛 Biome (2.1.2)

[error] 252-253: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)

🤖 Prompt for AI Agents
In dream_layer_frontend/src/i18n/fr.js around lines 248 to 257 there is a
duplicate key "modelType" defined twice; remove the later occurrence (the second
modelType: "Type de Modèle") so only a single modelType entry remains to avoid
overwriting and linter errors, keeping the original placement and comma
formatting intact.

Comment on lines +282 to +320
upscale: "Agrandir",
faceRestoration: "Restauration de Visage",
imageToImage: "Image vers Image",
batchFromDirectory: "Lot depuis le Répertoire",
batchFromDirectoryInput: "Répertoire d'Entrée",
batchFromDirectoryOutput: "Répertoire de Sortie",
batchFromDirectoryOutputDir: "Répertoire de Sortie",
batchFromDirectoryOutputDirPlaceholder: "Sélectionner le répertoire de sortie...",
// Extras Page
imagePostProcessing: "Post-Traitement d'Image",
singleImage: "Image Unique",
upscalingOptions: "Options d'Agrandissement",
processedImageWillDisplay: "L'image traitée s'affichera ici",
processing: "Traitement...",
batchOptions: "Options de Lot",
inputDirectory: "Répertoire d'Entrée - Traitement d'images par lot",
browseBatchFiles: "Parcourir les Fichiers de Lot",
specifyFolder: "Spécifier un dossier contenant des images de référence",
batchOperation: "Opération de Lot:",
resizeAll: "Redimensionner Tout",
upscaleAll: "Agrandir Tout",
convertFormat: "Convertir Format",
enhanceAll: "Améliorer Tout",
clickGenerateToProcess: "Cliquez sur Générer pour traiter l'image",
// Extras specific strings
dragAndDropImageHere: "Glissez et déposez une image ici ou cliquez pour parcourir",
fileTypesSupported: "PNG, JPG, WEBP ou GIF jusqu'à 10MB",
setUpscaleSize: "a) Définir la taille d'agrandissement :",
upscaleBy: "Agrandir par",
resizeTo: "Redimensionner à",
upscale: "Agrandir",
upscalingModel1: "b) Modèle d'agrandissement #1",
upscalingModel2: "c) Modèle d'agrandissement #2",
aboutTheUpscaler: "À propos de l'agrandisseur :",
upscalerDescription: "Offre une netteté extrême, idéal pour les textures et les détails fins, mais peut exagérer les bords. Vitesse : Moyenne.",
upscaler2Visibility: "d) Visibilité de l'agrandisseur 2 | Niveau optimal : 0,25",
gfpganVisibility: "e) Visibilité GFPGAN | Niveau optimal : 0,8",
codeformerVisibility: "f) Visibilité CodeFormer | Niveau optimal : 0,7",
codeformerWeight: "g) Poids CodeFormer | Niveau optimal : 0,2"
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

Duplicate object key "upscale" overwrites earlier value

Inside extras, the key upscale appears twice (Line 282 and Line 312). Only the last one will take effect.

Apply this diff to remove the later duplicate:

     resizeTo: "Redimensionner à",
-    upscale: "Agrandir",
     upscalingModel1: "b) Modèle d'agrandissement #1",
📝 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
upscale: "Agrandir",
faceRestoration: "Restauration de Visage",
imageToImage: "Image vers Image",
batchFromDirectory: "Lot depuis le Répertoire",
batchFromDirectoryInput: "Répertoire d'Entrée",
batchFromDirectoryOutput: "Répertoire de Sortie",
batchFromDirectoryOutputDir: "Répertoire de Sortie",
batchFromDirectoryOutputDirPlaceholder: "Sélectionner le répertoire de sortie...",
// Extras Page
imagePostProcessing: "Post-Traitement d'Image",
singleImage: "Image Unique",
upscalingOptions: "Options d'Agrandissement",
processedImageWillDisplay: "L'image traitée s'affichera ici",
processing: "Traitement...",
batchOptions: "Options de Lot",
inputDirectory: "Répertoire d'Entrée - Traitement d'images par lot",
browseBatchFiles: "Parcourir les Fichiers de Lot",
specifyFolder: "Spécifier un dossier contenant des images de référence",
batchOperation: "Opération de Lot:",
resizeAll: "Redimensionner Tout",
upscaleAll: "Agrandir Tout",
convertFormat: "Convertir Format",
enhanceAll: "Améliorer Tout",
clickGenerateToProcess: "Cliquez sur Générer pour traiter l'image",
// Extras specific strings
dragAndDropImageHere: "Glissez et déposez une image ici ou cliquez pour parcourir",
fileTypesSupported: "PNG, JPG, WEBP ou GIF jusqu'à 10MB",
setUpscaleSize: "a) Définir la taille d'agrandissement :",
upscaleBy: "Agrandir par",
resizeTo: "Redimensionner à",
upscale: "Agrandir",
upscalingModel1: "b) Modèle d'agrandissement #1",
upscalingModel2: "c) Modèle d'agrandissement #2",
aboutTheUpscaler: "À propos de l'agrandisseur :",
upscalerDescription: "Offre une netteté extrême, idéal pour les textures et les détails fins, mais peut exagérer les bords. Vitesse : Moyenne.",
upscaler2Visibility: "d) Visibilité de l'agrandisseur 2 | Niveau optimal : 0,25",
gfpganVisibility: "e) Visibilité GFPGAN | Niveau optimal : 0,8",
codeformerVisibility: "f) Visibilité CodeFormer | Niveau optimal : 0,7",
codeformerWeight: "g) Poids CodeFormer | Niveau optimal : 0,2"
upscale: "Agrandir",
faceRestoration: "Restauration de Visage",
imageToImage: "Image vers Image",
batchFromDirectory: "Lot depuis le Répertoire",
batchFromDirectoryInput: "Répertoire d'Entrée",
batchFromDirectoryOutput: "Répertoire de Sortie",
batchFromDirectoryOutputDir: "Répertoire de Sortie",
batchFromDirectoryOutputDirPlaceholder: "Sélectionner le répertoire de sortie...",
// Extras Page
imagePostProcessing: "Post-Traitement d'Image",
singleImage: "Image Unique",
upscalingOptions: "Options d'Agrandissement",
processedImageWillDisplay: "L'image traitée s'affichera ici",
processing: "Traitement...",
batchOptions: "Options de Lot",
inputDirectory: "Répertoire d'Entrée - Traitement d'images par lot",
browseBatchFiles: "Parcourir les Fichiers de Lot",
specifyFolder: "Spécifier un dossier contenant des images de référence",
batchOperation: "Opération de Lot:",
resizeAll: "Redimensionner Tout",
upscaleAll: "Agrandir Tout",
convertFormat: "Convertir Format",
enhanceAll: "Améliorer Tout",
clickGenerateToProcess: "Cliquez sur Générer pour traiter l'image",
// Extras specific strings
dragAndDropImageHere: "Glissez et déposez une image ici ou cliquez pour parcourir",
fileTypesSupported: "PNG, JPG, WEBP ou GIF jusqu'à 10MB",
setUpscaleSize: "a) Définir la taille d'agrandissement :",
upscaleBy: "Agrandir par",
resizeTo: "Redimensionner à",
upscalingModel1: "b) Modèle d'agrandissement #1",
upscalingModel2: "c) Modèle d'agrandissement #2",
aboutTheUpscaler: "À propos de l'agrandisseur :",
upscalerDescription: "Offre une netteté extrême, idéal pour les textures et les détails fins, mais peut exagérer les bords. Vitesse : Moyenne.",
upscaler2Visibility: "d) Visibilité de l'agrandisseur 2 | Niveau optimal : 0,25",
gfpganVisibility: "e) Visibilité GFPGAN | Niveau optimal : 0,8",
codeformerVisibility: "f) Visibilité CodeFormer | Niveau optimal : 0,7",
codeformerWeight: "g) Poids CodeFormer | Niveau optimal : 0,2"
🧰 Tools
🪛 Biome (2.1.2)

[error] 287-287: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)

🤖 Prompt for AI Agents
In dream_layer_frontend/src/i18n/fr.js around lines 282 to 320, there's a
duplicate key "upscale" (first at ~282 and again at ~312) so the latter
overwrites the former; remove the second "upscale" entry (the one inside the
Extras-specific strings block) so only the original "upscale" key remains, or if
different wording was intended, rename the duplicate key to the correct unique
identifier and adjust any references accordingly.

@@ -0,0 +1,96 @@
import React, { createContext, useContext, useState, ReactNode } from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Import useCallback for stable setters

We’ll introduce a memoized setLanguage; add useCallback to the import.

Apply this diff:

-import React, { createContext, useContext, useState, ReactNode } from 'react';
+import React, { createContext, useContext, useState, ReactNode, useCallback } from 'react';
📝 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
import React, { createContext, useContext, useState, ReactNode } from 'react';
import React, { createContext, useContext, useState, ReactNode, useCallback } from 'react';
🤖 Prompt for AI Agents
In dream_layer_frontend/src/i18n/i18nContext.tsx around line 1, the file
currently imports React hooks but misses useCallback; update the import to
include useCallback and then wrap the setLanguage setter in useCallback to
produce a stable, memoized setter (e.g., const setLanguage = useCallback((lang)
=> setLang(lang), [])), ensuring dependencies are correct so consumers relying
on reference stability don't re-render unnecessarily.

Comment on lines +17 to +22
interface I18nContextType {
language: LanguageCode;
setLanguage: (lang: LanguageCode) => void;
t: (key: string) => string;
availableLanguages: typeof languages;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Extend t to support interpolation parameters

Strings such as “Showing {count} of {total} models” require variable replacement. Expand t’s signature now to accept params.

Apply this diff:

 interface I18nContextType {
   language: LanguageCode;
   setLanguage: (lang: LanguageCode) => void;
-  t: (key: string) => string;
+  t: (key: string, params?: Record<string, string | number>) => string;
   availableLanguages: typeof languages;
 }
📝 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
interface I18nContextType {
language: LanguageCode;
setLanguage: (lang: LanguageCode) => void;
t: (key: string) => string;
availableLanguages: typeof languages;
}
interface I18nContextType {
language: LanguageCode;
setLanguage: (lang: LanguageCode) => void;
t: (key: string, params?: Record<string, string | number>) => string;
availableLanguages: typeof languages;
}
🤖 Prompt for AI Agents
In dream_layer_frontend/src/i18n/i18nContext.tsx around lines 17 to 22, the
I18nContextType.t signature only accepts a key and must be extended to accept
interpolation parameters; change t to accept an optional params object (e.g.
params?: Record<string, string | number | boolean>) so callers can pass values
like {count, total}, and update any implementations to accept the params and
perform variable replacement (keeping params optional so existing calls remain
valid).

Comment on lines +28 to +47
const getNestedValue = (obj: any, key: string): string => {
const keys = key.split('.');
let value = obj;

for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k];
} else {
// Fallback to English if key not found
const fallbackValue = getNestedValue(en, key);
if (fallbackValue && typeof fallbackValue === 'object' && k in fallbackValue) {
value = fallbackValue[k];
} else {
return key; // Return the key if not found anywhere
}
}
}

return typeof value === 'string' ? value : key;
};
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

Fix fallback logic and simplify nested key resolution

Current getNestedValue recursively calls itself with the full key inside the loop, which is unnecessary and can never satisfy the object check, effectively degrading to a no-op fallback. Simplify it to a pure lookup; let t handle language fallback.

Apply this diff:

-// Translation function that handles nested keys
-const getNestedValue = (obj: any, key: string): string => {
-  const keys = key.split('.');
-  let value = obj;
-
-  for (const k of keys) {
-    if (value && typeof value === 'object' && k in value) {
-      value = value[k];
-    } else {
-      // Fallback to English if key not found
-      const fallbackValue = getNestedValue(en, key);
-      if (fallbackValue && typeof fallbackValue === 'object' && k in fallbackValue) {
-        value = fallbackValue[k];
-      } else {
-        return key; // Return the key if not found anywhere
-      }
-    }
-  }
-
-  return typeof value === 'string' ? value : key;
-};
+// Resolve nested keys like "section.sub.key"; returns undefined if not found
+const getNestedValue = (obj: Record<string, any>, key: string): string | undefined => {
+  return key.split('.').reduce<any>((acc, k) => {
+    if (acc && typeof acc === 'object' && k in acc) return acc[k];
+    return undefined;
+  }, obj);
+};
📝 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
const getNestedValue = (obj: any, key: string): string => {
const keys = key.split('.');
let value = obj;
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k];
} else {
// Fallback to English if key not found
const fallbackValue = getNestedValue(en, key);
if (fallbackValue && typeof fallbackValue === 'object' && k in fallbackValue) {
value = fallbackValue[k];
} else {
return key; // Return the key if not found anywhere
}
}
}
return typeof value === 'string' ? value : key;
};
// Resolve nested keys like "section.sub.key"; returns undefined if not found
const getNestedValue = (obj: Record<string, any>, key: string): string | undefined => {
return key.split('.').reduce<any>((acc, k) => {
if (acc && typeof acc === 'object' && k in acc) return acc[k];
return undefined;
}, obj);
};
🤖 Prompt for AI Agents
In dream_layer_frontend/src/i18n/i18nContext.tsx around lines 28 to 47, the
current getNestedValue function calls itself with the full key inside the loop
which is incorrect and unnecessary; change it to a simple two-step lookup: first
traverse the provided obj by splitting the key and walking its properties,
returning the value if it's a string; if traversal fails, then traverse the
English fallback object (en) the same way and return its string if found; if
neither yields a string, return the original key. Ensure no recursive calls
inside the loop and keep traversal logic pure and identical for both attempts.

Comment on lines +54 to +60
export const I18nProvider: React.FC<I18nProviderProps> = ({ children }) => {
const [language, setLanguage] = useState<LanguageCode>('en');

const translations: Record<LanguageCode, TranslationKeys> = {
en,
fr
};
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

Persist language across refreshes (as claimed in PR) with SSR-safe localStorage

Language currently defaults to 'en' on every load; persistence is missing, conflicting with the PR objectives. Initialize from localStorage and persist on change.

Apply this diff:

-export const I18nProvider: React.FC<I18nProviderProps> = ({ children }) => {
-  const [language, setLanguage] = useState<LanguageCode>('en');
+export const I18nProvider: React.FC<I18nProviderProps> = ({ children }) => {
+  const [language, setLanguageState] = useState<LanguageCode>(() => {
+    if (typeof window !== 'undefined') {
+      const stored = window.localStorage.getItem('i18n.language') as LanguageCode | null;
+      if (stored && stored in languages) return stored;
+    }
+    return 'en';
+  });
+
+  const setLanguage = useCallback((lang: LanguageCode) => {
+    setLanguageState(lang);
+    try {
+      if (typeof window !== 'undefined') {
+        window.localStorage.setItem('i18n.language', lang);
+      }
+    } catch {
+      // no-op
+    }
+  }, []);
 
   const translations: Record<LanguageCode, TranslationKeys> = {
     en,
     fr
   };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In dream_layer_frontend/src/i18n/i18nContext.tsx around lines 54 to 60 the
language state always defaults to 'en' and does not persist across refreshes as
claimed; update initialization to read a saved language from localStorage in an
SSR-safe way (check typeof window !== 'undefined' before accessing localStorage)
and use a lazy initializer for useState to return that value or 'en' if
missing/invalid, then add a useEffect that writes the language to localStorage
whenever it changes (also guarding for window existence) so changes persist
across reloads.

Comment on lines +62 to +73
const t = (key: string): string => {
const currentTranslations = translations[language];
const value = getNestedValue(currentTranslations, key);

// If the value is the same as the key, try English as fallback
if (value === key && language !== 'en') {
const fallbackValue = getNestedValue(translations.en, key);
return typeof fallbackValue === 'string' ? fallbackValue : key;
}

return value;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement English fallback and parameter interpolation in t()

Add proper fallback to English and replace placeholders like {count} or {total}.

Apply this diff:

-  const t = (key: string): string => {
-    const currentTranslations = translations[language];
-    const value = getNestedValue(currentTranslations, key);
-
-    // If the value is the same as the key, try English as fallback
-    if (value === key && language !== 'en') {
-      const fallbackValue = getNestedValue(translations.en, key);
-      return typeof fallbackValue === 'string' ? fallbackValue : key;
-    }
-
-    return value;
-  };
+  const t = (key: string, params?: Record<string, string | number>): string => {
+    const currentTranslations = translations[language];
+    let value = getNestedValue(currentTranslations, key);
+    if (typeof value !== 'string' && language !== 'en') {
+      value = getNestedValue(translations.en, key);
+    }
+    if (typeof value !== 'string') return key;
+    if (params) {
+      value = value.replace(/\{(\w+)\}/g, (_, p1) =>
+        p1 in params ? String(params[p1]) : `{${p1}}`
+      );
+    }
+    return value;
+  };
📝 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
const t = (key: string): string => {
const currentTranslations = translations[language];
const value = getNestedValue(currentTranslations, key);
// If the value is the same as the key, try English as fallback
if (value === key && language !== 'en') {
const fallbackValue = getNestedValue(translations.en, key);
return typeof fallbackValue === 'string' ? fallbackValue : key;
}
return value;
};
const t = (key: string, params?: Record<string, string | number>): string => {
const currentTranslations = translations[language];
let value = getNestedValue(currentTranslations, key);
if (typeof value !== 'string' && language !== 'en') {
value = getNestedValue(translations.en, key);
}
if (typeof value !== 'string') return key;
if (params) {
value = value.replace(/\{(\w+)\}/g, (_, p1) =>
p1 in params ? String(params[p1]) : `{${p1}}`
);
}
return value;
};

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