From 2b7973df8b540038ebd53d1c144ea54e0872a966 Mon Sep 17 00:00:00 2001 From: James Pine Date: Mon, 20 Apr 2026 03:07:35 -0700 Subject: [PATCH] feat(i18n): localize Effects tab (list, detail, dialogs, toasts) Covers EffectsList (title, "New Preset", section headers, preset cards) and EffectsDetail (header buttons for Save / Save as Custom / Delete, name/description fields, preview section, Save as Custom dialog, all toasts). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../components/EffectsTab/EffectsDetail.tsx | 102 +++++++++--------- app/src/components/EffectsTab/EffectsList.tsx | 25 ++--- app/src/i18n/locales/en/translation.json | 55 ++++++++++ app/src/i18n/locales/zh-CN/translation.json | 55 ++++++++++ 4 files changed, 174 insertions(+), 63 deletions(-) diff --git a/app/src/components/EffectsTab/EffectsDetail.tsx b/app/src/components/EffectsTab/EffectsDetail.tsx index 60c2bf7f..07e650ea 100644 --- a/app/src/components/EffectsTab/EffectsDetail.tsx +++ b/app/src/components/EffectsTab/EffectsDetail.tsx @@ -1,6 +1,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'; import { Loader2, Play, Save, Trash2, Wand2 } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { EffectsChainEditor } from '@/components/Effects/EffectsChainEditor'; import { GenerationPicker } from '@/components/Effects/GenerationPicker'; @@ -25,6 +26,7 @@ import { useEffectsStore } from '@/stores/effectsStore'; import { usePlayerStore } from '@/stores/playerStore'; export function EffectsDetail() { + const { t } = useTranslation(); const selectedPresetId = useEffectsStore((s) => s.selectedPresetId); const isCreatingNew = useEffectsStore((s) => s.isCreatingNew); const workingChain = useEffectsStore((s) => s.workingChain); @@ -115,8 +117,8 @@ export function EffectsDetail() { setAudioWithAutoPlay(url, `preview-${Date.now()}`, null, 'Effects Preview'); } catch (error) { toast({ - title: 'Preview failed', - description: error instanceof Error ? error.message : 'Unknown error', + title: t('effects.toast.previewFailed'), + description: error instanceof Error ? error.message : t('common.unknownError'), variant: 'destructive', }); } finally { @@ -130,7 +132,7 @@ export function EffectsDetail() { async function handleSaveNew() { if (!name.trim()) { - toast({ title: 'Name required', variant: 'destructive' }); + toast({ title: t('effects.toast.nameRequired'), variant: 'destructive' }); return; } setSaving(true); @@ -143,11 +145,14 @@ export function EffectsDetail() { queryClient.invalidateQueries({ queryKey: ['effect-presets'] }); setIsCreatingNew(false); setSelectedPresetId(created.id); - toast({ title: 'Preset saved', description: `"${created.name}" has been created.` }); + toast({ + title: t('effects.toast.saved'), + description: t('effects.toast.createdDescription', { name: created.name }), + }); } catch (error) { toast({ - title: 'Failed to save', - description: error instanceof Error ? error.message : 'Unknown error', + title: t('effects.toast.saveFailed'), + description: error instanceof Error ? error.message : t('common.unknownError'), variant: 'destructive', }); } finally { @@ -166,11 +171,11 @@ export function EffectsDetail() { }); queryClient.invalidateQueries({ queryKey: ['effect-presets'] }); queryClient.invalidateQueries({ queryKey: ['effect-preset', selectedPresetId] }); - toast({ title: 'Preset updated' }); + toast({ title: t('effects.toast.updated') }); } catch (error) { toast({ - title: 'Failed to save', - description: error instanceof Error ? error.message : 'Unknown error', + title: t('effects.toast.saveFailed'), + description: error instanceof Error ? error.message : t('common.unknownError'), variant: 'destructive', }); } finally { @@ -179,15 +184,14 @@ export function EffectsDetail() { } function handleSaveAsNew() { - // Open the dialog with a suggested name based on the current preset - setSaveAsName(`${name} (Copy)`); + setSaveAsName(t('effects.saveAs.suggestedName', { name })); setSaveAsDescription(description); setSaveAsDialogOpen(true); } async function handleSaveAsConfirm() { if (!saveAsName.trim()) { - toast({ title: 'Name required', variant: 'destructive' }); + toast({ title: t('effects.toast.nameRequired'), variant: 'destructive' }); return; } setSaving(true); @@ -200,11 +204,14 @@ export function EffectsDetail() { queryClient.invalidateQueries({ queryKey: ['effect-presets'] }); setSaveAsDialogOpen(false); setSelectedPresetId(created.id); - toast({ title: 'Preset saved', description: `"${created.name}" has been created.` }); + toast({ + title: t('effects.toast.saved'), + description: t('effects.toast.createdDescription', { name: created.name }), + }); } catch (error) { toast({ - title: 'Failed to save', - description: error instanceof Error ? error.message : 'Unknown error', + title: t('effects.toast.saveFailed'), + description: error instanceof Error ? error.message : t('common.unknownError'), variant: 'destructive', }); } finally { @@ -220,11 +227,11 @@ export function EffectsDetail() { queryClient.invalidateQueries({ queryKey: ['effect-presets'] }); setSelectedPresetId(null); setWorkingChain([]); - toast({ title: 'Preset deleted' }); + toast({ title: t('effects.toast.deleted') }); } catch (error) { toast({ - title: 'Failed to delete', - description: error instanceof Error ? error.message : 'Unknown error', + title: t('effects.toast.deleteFailed'), + description: error instanceof Error ? error.message : t('common.unknownError'), variant: 'destructive', }); } finally { @@ -237,7 +244,7 @@ export function EffectsDetail() {
-

Select a preset or create a new one

+

{t('effects.placeholder')}

); @@ -245,10 +252,13 @@ export function EffectsDetail() { return (
- {/* Header */}

- {isCreatingNew ? 'New Preset' : isBuiltIn ? preset?.name : 'Edit Preset'} + {isCreatingNew + ? t('effects.detail.newTitle') + : isBuiltIn + ? preset?.name + : t('effects.detail.editTitle')}

{!isBuiltIn && !isCreatingNew && ( @@ -261,7 +271,7 @@ export function EffectsDetail() { disabled={deleting} > - {deleting ? 'Deleting...' : 'Delete'} + {deleting ? t('effects.detail.deleting') : t('common.delete')} )} @@ -282,7 +292,7 @@ export function EffectsDetail() { disabled={saving || workingChain.length === 0} > - {saving ? 'Saving...' : 'Save Preset'} + {saving ? t('effects.detail.saving') : t('effects.detail.savePreset')} )} {isBuiltIn && ( @@ -294,51 +304,46 @@ export function EffectsDetail() { disabled={saving} > - {saving ? 'Saving...' : 'Save as Custom'} + {saving ? t('effects.detail.saving') : t('effects.detail.saveAsCustom')} )}
- {/* Scrollable content */}
- {/* Name & description */} {(isCreatingNew || !isBuiltIn) && (
- + setName(e.target.value)} - placeholder="My preset..." + placeholder={t('effects.fields.namePlaceholder')} className="h-9" />
- +