-
Notifications
You must be signed in to change notification settings - Fork 199
translation #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
translation #97
Conversation
Reviewer's GuideThis 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 switchingsequenceDiagram
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
Class diagram for the LanguageSelector componentclassDiagram
class LanguageSelector {
+ uses useI18n()
+ renders DropdownMenu
+ onClick: setLanguage(lang)
}
LanguageSelector --> I18nProvider
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
WalkthroughIntroduces 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15 minutes Poem
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 unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
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; | ||
}; |
There was a problem hiding this comment.
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.
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)} |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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
};
There was a problem hiding this 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 languageLanguageSelector 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” capitalizationThe 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 languageProvide 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 contextThe 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 accessibilityReplace 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 anyImporting 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.
📒 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-exportsThe 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 setterEnsure 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.
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)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
// 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" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
// 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.
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)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
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" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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'; |
There was a problem hiding this comment.
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.
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.
interface I18nContextType { | ||
language: LanguageCode; | ||
setLanguage: (lang: LanguageCode) => void; | ||
t: (key: string) => string; | ||
availableLanguages: typeof languages; | ||
} |
There was a problem hiding this comment.
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.
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).
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; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
export const I18nProvider: React.FC<I18nProviderProps> = ({ children }) => { | ||
const [language, setLanguage] = useState<LanguageCode>('en'); | ||
|
||
const translations: Record<LanguageCode, TranslationKeys> = { | ||
en, | ||
fr | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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; | ||
}; |
There was a problem hiding this comment.
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.
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; | |
}; |
🌍 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.
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:
Summary by CodeRabbit