diff --git a/App.tsx b/App.tsx index 0f8bdfd..137f936 100644 --- a/App.tsx +++ b/App.tsx @@ -15,13 +15,17 @@ import { Song, GenerationParams, View, Playlist } from './types'; import { generateApi, songsApi, playlistsApi, getAudioUrl } from './services/api'; import { useAuth } from './context/AuthContext'; import { useResponsive } from './context/ResponsiveContext'; +import { I18nProvider, useI18n } from './context/I18nContext'; import { List } from 'lucide-react'; import { PlaylistDetail } from './components/PlaylistDetail'; import { Toast, ToastType } from './components/Toast'; import { SearchPage } from './components/SearchPage'; -export default function App() { +function AppContent() { + // i18n + const { t } = useI18n(); + // Responsive const { isMobile, isDesktop } = useResponsive(); @@ -464,9 +468,9 @@ export default function App() { if (audio.error && audio.error.code !== 1) { console.error("Audio playback error:", audio.error); if (audio.error.code === 4) { - showToast('This song is no longer available.', 'error'); + showToast(t('songNotAvailable'), 'error'); } else { - showToast('Unable to play this song.', 'error'); + showToast(t('unableToPlay'), 'error'); } } setIsPlaying(false); @@ -502,7 +506,7 @@ export default function App() { if (err instanceof Error && err.name !== 'AbortError') { console.error("Playback failed:", err); if (err.name === 'NotSupportedError') { - showToast('This song is no longer available.', 'error'); + showToast(t('songNotAvailable'), 'error'); } setIsPlaying(false); } @@ -631,7 +635,7 @@ export default function App() { } else if (status.status === 'failed') { cleanupJob(jobId, tempId); console.error(`Job ${jobId} failed:`, status.error); - showToast(`Generation failed: ${status.error || 'Unknown error'}`, 'error'); + showToast(`${t('generationFailed')}: ${status.error || 'Unknown error'}`, 'error'); } } catch (pollError) { console.error(`Polling error for job ${jobId}:`, pollError); @@ -646,7 +650,7 @@ export default function App() { if (activeJobsRef.current.has(jobId)) { console.warn(`Job ${jobId} timed out`); cleanupJob(jobId, tempId); - showToast('Generation timed out', 'error'); + showToast(t('generationTimedOut'), 'error'); } }, 600000); }, [token, cleanupJob, refreshSongsList]); @@ -762,7 +766,7 @@ export default function App() { if (activeJobsRef.current.size === 0) { setIsGenerating(false); } - showToast('Generation failed. Please try again.', 'error'); + showToast(t('generationFailed'), 'error'); } }; @@ -946,10 +950,10 @@ export default function App() { // Remove from play queue if present setPlayQueue(prev => prev.filter(s => s.id !== song.id)); - showToast('Song deleted successfully'); + showToast(t('songDeleted')); } catch (error) { console.error('Failed to delete song:', error); - showToast('Failed to delete song', 'error'); + showToast(t('failedToDeleteSong'), 'error'); } }; @@ -1001,9 +1005,9 @@ export default function App() { } if (failed.length > 0) { - showToast(`Deleted ${succeeded.length}/${songsToDelete.length} songs`, 'error'); + showToast(t('songsDeletedPartial').replace('{succeeded}', String(succeeded.length)).replace('{total}', String(songsToDelete.length)), 'error'); } else { - showToast('Songs deleted successfully'); + showToast(t('songsDeletedSuccess')); } }; @@ -1020,10 +1024,10 @@ export default function App() { throw new Error('Failed to delete upload'); } setReferenceTracks(prev => prev.filter(track => track.id !== trackId)); - showToast('Upload deleted successfully'); + showToast(t('songDeleted')); } catch (error) { console.error('Failed to delete upload:', error); - showToast('Failed to delete upload', 'error'); + showToast(t('failedToDeleteSong'), 'error'); } }; @@ -1038,10 +1042,10 @@ export default function App() { setSongToAddToPlaylist(null); playlistsApi.getMyPlaylists(token).then(r => setPlaylists(r.playlists)); } - showToast('Playlist created successfully!'); + showToast(t('playlistCreated')); } catch (error) { console.error('Create playlist error:', error); - showToast('Failed to create playlist', 'error'); + showToast(t('failedToCreatePlaylist'), 'error'); } }; @@ -1055,11 +1059,11 @@ export default function App() { try { await playlistsApi.addSong(playlistId, songToAddToPlaylist.id, token); setSongToAddToPlaylist(null); - showToast('Song added to playlist'); + showToast(t('songAddedToPlaylist')); playlistsApi.getMyPlaylists(token).then(r => setPlaylists(r.playlists)); } catch (error) { console.error('Add song error:', error); - showToast('Failed to add song to playlist', 'error'); + showToast(t('failedToAddSong'), 'error'); } }; @@ -1286,7 +1290,7 @@ export default function App() { onClick={() => setMobileShowList(!mobileShowList)} className="bg-zinc-800 text-white px-4 py-2 rounded-full shadow-lg border border-white/10 flex items-center gap-2 text-sm font-bold" > - {mobileShowList ? 'Create Song' : 'View List'} + {mobileShowList ? t('createSong') : t('viewList')} @@ -1415,3 +1419,11 @@ export default function App() { ); } + +export default function App() { + return ( + + + + ); +} diff --git a/I18N_USAGE.md b/I18N_USAGE.md new file mode 100644 index 0000000..e362ecb --- /dev/null +++ b/I18N_USAGE.md @@ -0,0 +1,61 @@ +# ACE-Step UI — Internationalization Guide + +## Overview + +The project supports 4 languages: English (default), Chinese, Japanese, and Korean. + +## Architecture + +``` +ace-step-ui/ +├── i18n/ +│ └── translations.ts # All translation strings +├── context/ +│ └── I18nContext.tsx # React context + useI18n hook +└── components/ # i18n-enabled components +``` + +## Usage + +### 1. Use translations in a component + +```tsx +import { useI18n } from '../context/I18nContext'; + +function YourComponent() { + const { t } = useI18n(); + return
{t('yourTranslationKey')}
; +} +``` + +### 2. Switch language + +Users can switch language in Settings. Programmatically: + +```tsx +const { language, setLanguage } = useI18n(); +setLanguage('en'); // 'en' | 'zh' | 'ja' | 'ko' +``` + +### 3. Add a new translation key + +Add the key to all 4 languages in `i18n/translations.ts`: + +```typescript +export const translations = { + en: { yourNewKey: 'English text' }, + zh: { yourNewKey: '中文文本' }, + ja: { yourNewKey: '日本語テキスト' }, + ko: { yourNewKey: '한국어 텍스트' }, +}; +``` + +## Language Persistence + +The selected language is stored in `localStorage` and restored on next visit. Default is English. + +## Notes + +- All keys must exist in every language object +- TypeScript's `TranslationKey` type enforces key safety +- If a key is missing, the raw key name is returned as fallback diff --git a/components/SettingsModal.tsx b/components/SettingsModal.tsx index 0f61b8c..e884e1d 100644 --- a/components/SettingsModal.tsx +++ b/components/SettingsModal.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; -import { X, User as UserIcon, Palette, Info, Edit3, ExternalLink, Github } from 'lucide-react'; +import { X, User as UserIcon, Palette, Info, Edit3, ExternalLink, Globe, ChevronDown, Github } from 'lucide-react'; import { useAuth } from '../context/AuthContext'; +import { useI18n } from '../context/I18nContext'; import { EditProfileModal } from './EditProfileModal'; interface SettingsModalProps { @@ -13,6 +14,7 @@ interface SettingsModalProps { export const SettingsModal: React.FC = ({ isOpen, onClose, theme, onToggleTheme, onNavigateToProfile }) => { const { user } = useAuth(); + const { t, language, setLanguage } = useI18n(); const [isEditProfileOpen, setIsEditProfileOpen] = useState(false); if (!isOpen || !user) { @@ -29,14 +31,14 @@ export const SettingsModal: React.FC = ({ isOpen, onClose, t } return ( -
+
e.stopPropagation()} > {/* Header */}
-

Settings

+

{t('settings')}

@@ -91,21 +93,47 @@ export const SettingsModal: React.FC = ({ isOpen, onClose, t
-

Account

+

{t('account')}

- +

@{user.username}

+ {/* Language Section */} +
+
+ +

{t('language')}

+
+
+
+ + +
+
+
+ {/* Theme Section */}
-

Appearance

+

{t('appearance')}

@@ -116,7 +144,7 @@ export const SettingsModal: React.FC = ({ isOpen, onClose, t : 'border-zinc-300 dark:border-zinc-700 hover:border-zinc-400 dark:hover:border-zinc-600' }`} > - Light + {t('light')}
@@ -135,41 +163,70 @@ export const SettingsModal: React.FC = ({ isOpen, onClose, t
-

About

+

{t('about')}

-

Version 1.0.0

-

ACE-Step UI - Local AI Music Generator

+

{t('version')} 2.0.0

+

ACE-Step UI - {t('localAIMusicGenerator')}

- Powered by ACE-Step 1.5. Open source and free to use. + {t('poweredBy')}

-
-

Created by Ambsd

-
- - - - - Follow @AmbsdOP - - - - GitHub Repo - +
+
+

{t('createdBy')}

+ +

+ Report issues or request features on GitHub +

+
+ -

- Report issues or request features on GitHub -

@@ -181,7 +238,7 @@ export const SettingsModal: React.FC = ({ isOpen, onClose, t onClick={onClose} className="px-6 py-2 bg-zinc-900 dark:bg-white text-white dark:text-black font-semibold rounded-lg hover:bg-zinc-800 dark:hover:bg-zinc-200 transition-colors" > - Done + {t('done')}
diff --git a/context/I18nContext.tsx b/context/I18nContext.tsx new file mode 100644 index 0000000..f8f1065 --- /dev/null +++ b/context/I18nContext.tsx @@ -0,0 +1,40 @@ +import React, { createContext, useContext, useState, ReactNode } from 'react'; +import { translations, Language, TranslationKey } from '../i18n/translations'; + +interface I18nContextType { + language: Language; + setLanguage: (lang: Language) => void; + t: (key: TranslationKey) => string; +} + +const I18nContext = createContext(undefined); + +export const I18nProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + const [language, setLanguage] = useState(() => { + const stored = localStorage.getItem('language') as Language; + return stored === 'zh' || stored === 'en' || stored === 'ja' || stored === 'ko' ? stored : 'en'; + }); + + const handleSetLanguage = (lang: Language) => { + setLanguage(lang); + localStorage.setItem('language', lang); + }; + + const t = (key: TranslationKey): string => { + return translations[language][key] || key; + }; + + return ( + + {children} + + ); +}; + +export const useI18n = () => { + const context = useContext(I18nContext); + if (!context) { + throw new Error('useI18n must be used within I18nProvider'); + } + return context; +}; diff --git a/i18n/translations.ts b/i18n/translations.ts new file mode 100644 index 0000000..a94c795 --- /dev/null +++ b/i18n/translations.ts @@ -0,0 +1,2361 @@ +export type Language = 'en' | 'zh' | 'ja' | 'ko'; + +export const translations = { + en: { + // Navigation + create: 'Create', + library: 'Library', + search: 'Search', + training: 'Training', + + // Theme + lightMode: 'Light Mode', + darkMode: 'Dark Mode', + + // Auth + signIn: 'Sign In', + signOut: 'Sign Out', + settings: 'Settings', + + // Username Modal + welcomeTitle: 'Welcome to ACE-Step UI', + welcomeSubtitle: 'Enter your name to get started creating AI music', + yourName: 'Your Name', + enterYourName: 'Enter your name', + getStarted: 'Get Started', + gettingStarted: 'Getting Started...', + yourMusicYourWay: 'Your music, your way. Create unlimited AI music for free.', + usernameMinLength: 'Username must be at least 2 characters', + usernameInvalidChars: 'Username can only contain letters, numbers, underscores, and dashes', + failedToSetUsername: 'Failed to set username', + + // Toasts + songNotAvailable: 'This song is no longer available.', + unableToPlay: 'Unable to play this song.', + generationTimedOut: 'Generation timed out', + generationFailed: 'Generation failed. Please try again.', + playlistCreated: 'Playlist created successfully!', + failedToCreatePlaylist: 'Failed to create playlist', + songAddedToPlaylist: 'Song added to playlist', + failedToAddSong: 'Failed to add song to playlist', + songDeleted: 'Song deleted successfully', + failedToDeleteSong: 'Failed to delete song', + + // Sidebar + aceStepUI: 'ACE-Step UI', + + // Common + cancel: 'Cancel', + save: 'Save', + delete: 'Delete', + edit: 'Edit', + share: 'Share', + download: 'Download', + play: 'Play', + pause: 'Pause', + normalSpeed: 'Normal', + next: 'Next', + previous: 'Previous', + + // Placeholders + searchPlaceholder: 'Search...', + + // Settings + language: 'Language', + english: 'English', + chinese: 'Chinese', + japaneseLanguage: 'Japanese', + koreanLanguage: 'Korean', + appearance: 'Appearance', + profile: 'Profile', + viewProfile: 'View Profile', + editProfile: 'Edit Profile', + account: 'Account', + username: 'Username', + memberSince: 'Member since', + about: 'About', + version: 'Version', + localAIMusicGenerator: 'Local AI Music Generator', + poweredBy: 'Powered by ACE-Step 1.5. Open source and free to use.', + createdBy: 'Created by Ambsd', + localizedBy: 'Localization & Training by bdsqlsz', + follow: 'Follow', + done: 'Done', + light: 'Light', + dark: 'Dark', + + // Mobile + createSong: 'Create Song', + viewList: 'View List', + + // CreatePanel - Modes + simple: 'Simple', + custom: 'Custom', + + // CreatePanel - Sections + audio: 'AUDIO', + lyrics: 'LYRICS', + styleOfMusic: 'STYLE OF MUSIC', + title: 'Title', + musicParameters: 'Music Parameters', + advancedSettings: 'Advanced Settings', + + // CreatePanel - LoRA + loraPath: 'LoRA Path', + loraPathPlaceholder: 'Enter LoRA model path...', + browseFiles: 'Browse files', + loraLoaded: 'LoRA Loaded', + loraUnloaded: 'LoRA Unloaded', + loraLoad: 'Load', + loraUnload: 'Unload', + loraScale: 'LoRA Scale', + loraScaleDescription: 'Adjust the influence strength of the LoRA model', + + // CreatePanel - Audio + reference: 'Reference', + cover: 'Cover', + fromLibrary: 'From library', + upload: 'Upload', + + // CreatePanel - Lyrics + lyricsPlaceholder: 'Your lyrics here...', + leaveLyricsEmpty: 'Leave empty for instrumental or toggle below', + vocal: 'Vocal', + instrumental: 'Instrumental', + vocalLanguage: 'Vocal Language', + autoInstrumental: 'Auto / Instrumental', + vocalArabic: 'Arabic', + vocalAzerbaijani: 'Azerbaijani', + vocalBulgarian: 'Bulgarian', + vocalBengali: 'Bengali', + vocalCatalan: 'Catalan', + vocalCzech: 'Czech', + vocalDanish: 'Danish', + vocalGerman: 'German', + vocalGreek: 'Greek', + vocalEnglish: 'English', + vocalSpanish: 'Spanish', + vocalPersian: 'Persian', + vocalFinnish: 'Finnish', + vocalFrench: 'French', + vocalHebrew: 'Hebrew', + vocalHindi: 'Hindi', + vocalCroatian: 'Croatian', + vocalHaitianCreole: 'Haitian Creole', + vocalHungarian: 'Hungarian', + vocalIndonesian: 'Indonesian', + vocalIcelandic: 'Icelandic', + vocalItalian: 'Italian', + vocalJapanese: 'Japanese', + vocalKorean: 'Korean', + vocalLatin: 'Latin', + vocalLithuanian: 'Lithuanian', + vocalMalay: 'Malay', + vocalNepali: 'Nepali', + vocalDutch: 'Dutch', + vocalNorwegian: 'Norwegian', + vocalPunjabi: 'Punjabi', + vocalPolish: 'Polish', + vocalPortuguese: 'Portuguese', + vocalRomanian: 'Romanian', + vocalRussian: 'Russian', + vocalSanskrit: 'Sanskrit', + vocalSlovak: 'Slovak', + vocalSerbian: 'Serbian', + vocalSwedish: 'Swedish', + vocalSwahili: 'Swahili', + vocalTamil: 'Tamil', + vocalTelugu: 'Telugu', + vocalThai: 'Thai', + vocalTagalog: 'Tagalog', + vocalTurkish: 'Turkish', + vocalUkrainian: 'Ukrainian', + vocalUrdu: 'Urdu', + vocalVietnamese: 'Vietnamese', + vocalCantonese: 'Cantonese', + vocalChineseMandarin: 'Chinese (Mandarin)', + + // CreatePanel - Style + stylePlaceholder: 'e.g. upbeat pop rock, emotional ballad, 90s hip hop', + genreMoodInstruments: 'Genre, mood, instruments, vibe', + refreshGenres: 'Refresh genre suggestions', + mainGenre: 'Main Genre', + subGenre: 'Sub Genre', + nameSong: 'Name your song', + + // CreatePanel - Parameters + bpm: 'BPM', + auto: 'Auto', + duration: 'Duration', + seconds: 's', + keyScale: 'Key & Scale', + timeSignature: 'Time Signature', + + // CreatePanel - Advanced + randomSeed: 'Random Seed', + thinking: 'Thinking', + audioFormat: 'Audio Format', + inferenceSteps: 'Inference Steps', + guidanceScale: 'Guidance Scale', + inferMethod: 'Inference Method', + shift: 'Shift', + timestepShiftForBase: 'Timestep shift for base models (not effective for turbo)', + expertControls: 'EXPERT CONTROLS', + lmParameters: 'LM Parameters', + controlLyricGeneration: 'Control lyric generation + creativity', + transform: 'TRANSFORM', + controlSourceAudio: 'Control how much the model follows the source audio', + audioCodes: 'Audio Codes', + optionalAudioCodes: 'Optional audio codes payload', + taskType: 'Task Type', + textToMusic: 'Text → Music', + audioCoverStrength: 'Audio Cover Strength', + repaintingStart: 'Repainting Start', + repaintingEnd: 'Repainting End', + instruction: 'Instruction', + lmTemperature: 'LM Temperature', + higherMoreRandom: 'Higher = more random (0-2)', + lmCfgScale: 'LM CFG Scale', + noCfgScale: '1.0 = no CFG (1-3)', + topK: 'Top-K', + topP: 'Top-P', + lmNegativePrompt: 'LM Negative Prompt', + noUserInput: 'NO USER INPUT', + thingsToAvoid: 'Things to avoid...', + useWhenCfgScaleGreater: 'Use when LM CFG Scale > 1.0', + lmTopK: 'LM Top-K', + lmTopP: 'LM Top-P', + + // Guidance section + guidance: 'GUIDANCE', + advancedCfgScheduling: 'Advanced CFG scheduling controls.', + cfgIntervalStart: 'CFG Interval Start', + cfgIntervalEnd: 'CFG Interval End', + customTimesteps: 'Custom Timesteps', + timestepsPlaceholder: 'e.g. 1,3,5,7', + scoreScale: 'Score Scale', + lmBatchChunkSize: 'LM Batch Chunk Size', + trackName: 'Track Name', + optionalTrackName: 'Optional track name', + completeTrackClasses: 'Complete Track Classes', + trackClassesPlaceholder: 'class-a, class-b', + useAdg: 'Use ADG', + allowLmBatch: 'Allow LM Batch', + useCotMetas: 'Use CoT Metas', + useCotCaption: 'Use CoT Caption', + useCotLanguage: 'Use CoT Language', + autogen: 'Autogen', + constrainedDecodingDebug: 'Constrained Decoding Debug', + formatCaption: 'Format Caption', + getScores: 'Get Scores', + getLrcLyrics: 'Get LRC (Lyrics)', + batchSize: 'Batch Size (Variations)', + variations: 'Variations', + bulkGenerate: 'Bulk Generate', + job: 'job', + jobs: 'jobs', + queueMultipleJobs: 'Queue multiple independent generation jobs with same settings', + numberOfVariations: 'Number of song variations to generate', + moreStepsBetterQuality: 'More steps = better quality, slower (8 recommended for turbo)', + howCloselyFollowPrompt: 'How closely to follow the prompt', + seed: 'Seed', + randomSeedRecommended: 'Randomized every run (recommended)', + fixedSeedReproducible: 'Fixed seed for reproducible results', + enterFixedSeed: 'Enter fixed seed', + thinkingCot: 'Thinking (CoT)', + mp3Smaller: 'MP3 (smaller)', + flacLossless: 'FLAC (lossless)', + odeDeterministic: 'ODE (deterministic)', + sdeStochastic: 'SDE (stochastic)', + min: 'min', + + // CreatePanel - Drag & Drop + dropToUseAudio: 'Drop to use audio', + dropToUpload: 'Drop to upload', + usingAsReference: 'Using as Reference', + usingAsCover: 'Using as Cover', + uploadingAsReference: 'Uploading as Reference', + uploadingAsCover: 'Uploading as Cover', + + // CreatePanel - Vocal Gender + vocalGender: 'Vocal Gender', + male: 'Male', + female: 'Female', + + // CreatePanel - LM Backend & Model + lmBackendLabel: 'LM Backend', + lmBackendPt: 'PT (~1.6 GB VRAM)', + lmBackendVllm: 'VLLM (~9.2 GB VRAM)', + lmBackendHint: 'PT uses less VRAM, VLLM may be faster on powerful GPUs', + lmModelLabel: 'LM Model', + lmModel06B: '0.6B (Lightest, ~0.5 GB VRAM)', + lmModel17B: '1.7B (Balanced, ~1.5 GB VRAM)', + lmModel4B: '4B (Best quality, ~4 GB VRAM)', + lmModelHint: 'Controls the LLM used for lyrics/style enhancement. Auto-downloads if not present.', + + // CreatePanel - Transcription + transcribing: 'Transcribing...', + transcribingWithWhisper: 'Transcribing with Whisper…', + + // CreatePanel - Library Tabs + uploaded: 'Uploaded', + createdTab: 'Created', + useTrack: 'Use', + noCreatedSongsYet: 'No created songs yet', + generateSongsToReuse: 'Generate songs to reuse them as cover or reference', + + // CreatePanel - Actions + generate: 'Generate', + generating: 'Generating...', + createButton: 'Create', + formatLyrics: 'Format Lyrics', + formatting: 'Formatting...', + clearAll: 'Clear All', + + // Simple Mode + describeYourSong: 'DESCRIBE YOUR SONG', + songDescriptionPlaceholder: 'A happy pop song about summer adventures with friends...', + quickSettings: 'QUICK SETTINGS', + key: 'Key', + time: 'Time', + + // CreatePanel - Errors + loginToGenerate: 'Please login to generate music', + uploadFailed: 'Upload failed', + formatFailed: 'Format failed', + formatFailedDetail: 'Format failed. Make sure the LLM is initialized.', + formatFailedLlmUnavailable: 'Format failed. The LLM may not be available.', + deleteSongConfirm: 'Are you sure you want to delete "{title}"? This action cannot be undone.', + profileUpdateFailed: 'Failed to update profile', + ffmpegLoadFailed: 'Failed to load video encoder. Please refresh and try again.', + videoRenderFailed: 'Video rendering failed. Please try again.', + + // SongList / Filters + searchYourSongs: 'Search your songs...', + filters: 'Filters', + refineBy: 'REFINE BY', + liked: 'Liked', + public: 'Public', + private: 'Private', + generatingStatus: 'Generating', + selectSongToView: 'Select a song to view details', + noSongsMatchFilters: 'No songs match your filters.', + clearFilters: 'Clear filters', + + // RightSidebar - Song Details + songDetails: 'Song Details', + created: 'Created', + styleTags: 'STYLE & TAGS', + lyricsSection: 'LYRICS', + copyLyrics: 'Copy', + + // Languages + japanese: 'Japanese', + spanish: 'Spanish', + german: 'German', + french: 'French', + portuguese: 'Portuguese', + russian: 'Russian', + italian: 'Italian', + dutch: 'Dutch', + polish: 'Polish', + turkish: 'Turkish', + vietnamese: 'Vietnamese', + czech: 'Czech', + persian: 'Persian', + arabic: 'Arabic', + korean: 'Korean', + thai: 'Thai', + hindi: 'Hindi', + indonesian: 'Indonesian', + ukrainian: 'Ukrainian', + hungarian: 'Hungarian', + swedish: 'Swedish', + romanian: 'Romanian', + greek: 'Greek', + + // Task Types + audio2audio: 'Audio → Audio', + coverTask: 'Cover', + repaintTask: 'Repaint', + + // Search Page + searchSongsPlaceholder: 'Search for songs, playlists, creators, or genres', + songsMatching: 'Songs matching', + featuredSongs: 'Featured Songs', + noSongsFound: 'No songs found matching', + creatorsMatching: 'Creators matching', + featuredCreators: 'Featured Creators', + noCreatorsFound: 'No creators found matching', + noCreatorsYet: 'No creators yet. Be the first to share your music!', + playlistsMatching: 'Playlists matching', + featuredPlaylists: 'Featured Playlists', + noPlaylistsFound: 'No playlists found matching', + noPlaylistsYet: 'No public playlists yet. Create one and share your favorites!', + genres: 'Genres', + songs: 'songs', + + // Library View + yourLibrary: 'Your Library', + newPlaylist: 'New Playlist', + likedSongs: 'Liked Songs', + playlists: 'Playlists', + playlist: 'Playlist', + byYou: 'By You', + + // User Profile + back: 'Back', + loadingProfile: 'Loading profile...', + userNotFound: 'User not found', + goBack: 'Go Back', + likes: 'Likes', + plays: 'Plays', + recent: 'Recent', + top: 'Top', + noPublicSongsYet: 'No public songs yet', + seeMore: 'See More', + joined: 'Joined', + supportingSince: 'Supporting since', + avatarImage: 'Avatar Image', + uploadAvatar: 'Upload Avatar', + avatarFormats: 'JPG, PNG, WebP, GIF • Max 5MB', + bannerImage: 'Banner Image', + clickToUploadBanner: 'Click to upload banner', + bannerFormats: 'Recommended: 1500x500px • JPG, PNG, WebP, GIF • Max 5MB', + bio: 'Bio', + bioPlaceholder: 'Tell us about yourself...', + uploadingAvatar: 'Uploading Avatar...', + uploadingBanner: 'Uploading Banner...', + saving: 'Saving...', + saveChanges: 'Save Changes', + + // Playlist Modals + createPlaylist: 'Create Playlist', + playlistName: 'Name', + playlistNamePlaceholder: 'My Awesome Playlist', + playlistDescription: 'Description', + descriptionPlaceholder: 'Vibes for coding...', + addToPlaylist: 'Add to Playlist', + createNewPlaylist: 'Create New Playlist', + noExistingPlaylists: 'No existing playlists.', + + // Edit Profile Modal (Settings) + usernameLabel: 'Username', + usernamePlaceholder: 'username', + usernameMinLengthError: 'Username must be at least 2 characters', + usernameTakenError: 'Username is already taken', + usernameUpdateFailedError: 'Failed to update username', + usernameRequirements: 'Letters, numbers, underscores, and hyphens only', + + // Song Details (RightSidebar) + more: 'more', + copy: 'Copy', + copied: 'Copied!', + copyAllTags: 'Copy all tags', + + // Song Dropdown Menu + createVideo: 'Create Video', + editAudio: 'Edit Audio', + extractStems: 'Extract Stems', + reusePrompt: 'Reuse Prompt', + deleteSong: 'Delete Song', + useAsReference: 'Use as Reference', + coverSong: 'Cover Song', + confirmDeleteTitle: 'Delete Song', + confirmDeleteManyTitle: 'Delete Songs', + deleteSongsConfirm: 'Delete {count} songs? This action cannot be undone.', + songsDeletedSuccess: 'Songs deleted successfully', + songsDeletedPartial: 'Deleted {succeeded}/{total} songs', + deleteUploadConfirm: 'Delete this upload? This action cannot be undone.', + cannotBeUndone: 'This action cannot be undone.', + + // Player + nowPlaying: 'Now Playing', + downloadAudio: 'Download Audio', + openInEditor: 'Open in Editor', + anonymous: 'Anonymous', + + // PlaylistDetail + loadingPlaylist: 'Loading playlist...', + playlistNotFound: 'Playlist not found', + deletePlaylistConfirm: 'Are you sure you want to delete this playlist?', + dateAdded: 'Date Added', + justNow: 'Just now', + unknown: 'Unknown', + + // SongProfile + loadingSong: 'Loading song...', + songNotFound: 'Song not found', + similar: 'Similar', + by: 'By', + + // ShareModal + shareSong: 'Share Song', + shareOnX: 'Share on X', + shareOnFacebook: 'Share on Facebook', + shareOnWhatsApp: 'Share on WhatsApp', + shareOnTelegram: 'Share on Telegram', + shareOnReddit: 'Share on Reddit', + shareOnLinkedIn: 'Share on LinkedIn', + shareViaEmail: 'Share via Email', + copyLink: 'Copy Link', + emailSubject: 'Check out this AI song', + emailBody: 'Hey!\n\nI created this AI-generated song and thought you\'d love it:\n\n"{title}" by {creator}\n{style}\n\nListen here: {url}\n\nMade with ACE-Step UI - free and open source local AI music generation!', + + // Reference Modal + referenceModalTitle: 'Reference', + coverModalTitle: 'Cover', + referenceModalDescription: 'Create songs inspired by a reference track', + coverModalDescription: 'Transform an existing track into a new version', + uploadAudio: 'Upload audio', + uploadingAudio: 'Uploading...', + audioFormats: 'MP3, WAV, FLAC', + mine: 'Mine', + loadingTracks: 'Loading tracks...', + noTracksYet: 'No tracks yet', + uploadAudioFilesAsReferences: 'Upload audio files to use them as references', + + // Training Module + loraTraining: 'LoRA Training', + trainingDescription: 'Build datasets from your audio files and train custom LoRA adapters', + datasetBuilder: 'Dataset Builder', + trainLora: 'Train LoRA', + quickStart: 'Quick Start', + chooseOneOption: 'Choose one: Load existing dataset OR Scan new directory', + loadExistingDataset: 'Load Existing Dataset', + loadDataset: 'Load', + scanNewDirectory: 'Scan New Directory', + scanDataset: 'Scan', + datasetSettings: 'Dataset Settings', + datasetName: 'Dataset Name', + customActivationTag: 'Custom Activation Tag', + allInstrumental: 'All Instrumental', + tagPosition: 'Tag Position', + tagPrepend: 'Prepend (tag, caption)', + tagAppend: 'Append (caption, tag)', + tagReplace: 'Replace caption', + genreRatio: 'Genre Ratio', + genreRatioHint: '0% = all Caption, 100% = all Genre', + autoLabelWithAI: 'Auto-Label with AI', + autoLabelDescription: 'Generate metadata for audio files: Caption, BPM, Key, Time Signature', + skipMetas: 'Skip BPM/Key/Time Signature', + onlyUnlabeled: 'Only Unlabeled', + autoLabelAll: 'Auto-Label All', + filename: 'Filename', + labeled: 'Labeled', + saveDataset: 'Save Dataset', + preprocessToTensors: 'Preprocess to Tensors', + preprocessDescription: 'Converts dataset to pre-computed tensors for fast training (requires model loaded)', + preprocess: 'Preprocess', + preprocessedDataset: 'Preprocessed Dataset', + loraSettings: 'LoRA Settings', + loraRank: 'LoRA Rank', + loraAlpha: 'LoRA Alpha', + dropout: 'Dropout', + trainingParameters: 'Training Parameters', + learningRate: 'Learning Rate', + maxEpochs: 'Max Epochs', + gradientAccumulation: 'Gradient Accumulation', + saveEvery: 'Save Every', + epochs: 'epochs', + outputDirectory: 'Output Directory', + useFP8: 'Use FP8 Quantization', + fp8Description: 'Reduces VRAM usage', + startTraining: 'Start Training', + stopTraining: 'Stop Training', + trainingProgress: 'Training Progress', + trainingLog: 'Training Log', + trainingStatus: 'Training Status', + tensorboardView: 'TensorBoard View', + showTensorboard: 'Show TensorBoard', + hideTensorboard: 'Hide TensorBoard', + openInNewTab: 'Open in New Tab', + + // Training Status Messages + scanning: 'Scanning...', + autoLabeling: 'Auto-labeling...', + savingDataset: 'Saving...', + preprocessing: 'Preprocessing...', + loadingDataset: 'Loading...', + startingTraining: 'Starting...', + error: 'Error', + datasetInfo: 'Dataset: {name}, Samples: {samples}, Labeled: {labeled}', + trainingStep: 'Step {step} - Loss: {loss}', + editSample: 'Edit Sample', + caption: 'Caption', + genre: 'Genre', + promptOverride: 'Prompt Override', + musicDescription: 'Music description...', + overrideGlobalRatio: 'Override global ratio for this sample', + editableUsedForTraining: 'editable, used for training', + useGlobalRatio: 'Use Global Ratio', + thisSample: 'this sample', + }, + zh: { + // Navigation + create: '创作', + library: '音乐库', + search: '搜索', + training: '训练', + + // Theme + lightMode: '浅色模式', + darkMode: '深色模式', + + // Auth + signIn: '登录', + signOut: '退出', + settings: '设置', + + // Username Modal + welcomeTitle: '欢迎使用 ACE-Step UI', + welcomeSubtitle: '输入您的名字开始创作 AI 音乐', + yourName: '您的名字', + enterYourName: '请输入您的名字', + getStarted: '开始使用', + gettingStarted: '正在启动...', + yourMusicYourWay: '您的音乐,您的方式。免费创作无限 AI 音乐。', + usernameMinLength: '用户名至少需要 2 个字符', + usernameInvalidChars: '用户名只能包含字母、数字、下划线和破折号', + failedToSetUsername: '设置用户名失败', + + // Toasts + songNotAvailable: '该歌曲已不可用。', + unableToPlay: '无法播放该歌曲。', + generationTimedOut: '生成超时', + generationFailed: '生成失败,请重试。', + playlistCreated: '歌单创建成功!', + failedToCreatePlaylist: '创建歌单失败', + songAddedToPlaylist: '已添加到歌单', + failedToAddSong: '添加歌曲失败', + songDeleted: '歌曲删除成功', + failedToDeleteSong: '删除歌曲失败', + + // Sidebar + aceStepUI: 'ACE-Step UI', + + // Common + cancel: '取消', + save: '保存', + delete: '删除', + edit: '编辑', + share: '分享', + download: '下载', + play: '播放', + pause: '暂停', + normalSpeed: '正常', + next: '下一首', + previous: '上一首', + + // Placeholders + searchPlaceholder: '搜索...', + + // Settings + language: '语言', + english: 'English', + chinese: '简体中文', + japaneseLanguage: '日本語', + koreanLanguage: '한국어', + appearance: '外观', + profile: '个人资料', + viewProfile: '查看个人资料', + editProfile: '编辑资料', + account: '账户', + username: '用户名', + memberSince: '加入时间', + about: '关于', + version: '版本', + localAIMusicGenerator: '本地 AI 音乐生成器', + poweredBy: '由 ACE-Step 1.5 驱动。开源免费使用。', + createdBy: '开发者 Ambsd', + localizedBy: '本地化翻译 & 训练 青龙圣者', + follow: '关注', + done: '完成', + light: '浅色', + dark: '深色', + + // Mobile + createSong: '创作歌曲', + viewList: '查看列表', + + // CreatePanel - Modes + simple: '简单', + custom: '自定义', + + // CreatePanel - Sections + audio: '音频', + lyrics: '歌词', + styleOfMusic: '音乐风格', + title: '标题', + musicParameters: '音乐参数', + advancedSettings: '高级设置', + + // CreatePanel - LoRA + loraPath: 'LoRA 路径', + loraPathPlaceholder: '输入 LoRA 模型路径...', + browseFiles: '浏览文件', + loraLoaded: 'LoRA 已加载', + loraUnloaded: 'LoRA 未加载', + loraLoad: '加载', + loraUnload: '卸载', + loraScale: 'LoRA 强度', + loraScaleDescription: '调整 LoRA 模型的影响强度', + + // CreatePanel - Audio + reference: '参考', + cover: '翻唱', + fromLibrary: '从库中选择', + upload: '上传', + + // CreatePanel - Lyrics + lyricsPlaceholder: '在此输入歌词...', + leaveLyricsEmpty: '留空表示纯音乐,或使用下方开关', + vocal: '人声', + instrumental: '纯音乐', + vocalLanguage: '人声语言', + autoInstrumental: '自动 / 纯音乐', + vocalArabic: '阿拉伯语', + vocalAzerbaijani: '阿塞拜疆语', + vocalBulgarian: '保加利亚语', + vocalBengali: '孟加拉语', + vocalCatalan: '加泰罗尼亚语', + vocalCzech: '捷克语', + vocalDanish: '丹麦语', + vocalGerman: '德语', + vocalGreek: '希腊语', + vocalEnglish: '英语', + vocalSpanish: '西班牙语', + vocalPersian: '波斯语', + vocalFinnish: '芬兰语', + vocalFrench: '法语', + vocalHebrew: '希伯来语', + vocalHindi: '印地语', + vocalCroatian: '克罗地亚语', + vocalHaitianCreole: '海地克里奥尔语', + vocalHungarian: '匈牙利语', + vocalIndonesian: '印尼语', + vocalIcelandic: '冰岛语', + vocalItalian: '意大利语', + vocalJapanese: '日语', + vocalKorean: '韩语', + vocalLatin: '拉丁语', + vocalLithuanian: '立陶宛语', + vocalMalay: '马来语', + vocalNepali: '尼泊尔语', + vocalDutch: '荷兰语', + vocalNorwegian: '挪威语', + vocalPunjabi: '旁遮普语', + vocalPolish: '波兰语', + vocalPortuguese: '葡萄牙语', + vocalRomanian: '罗马尼亚语', + vocalRussian: '俄语', + vocalSanskrit: '梵语', + vocalSlovak: '斯洛伐克语', + vocalSerbian: '塞尔维亚语', + vocalSwedish: '瑞典语', + vocalSwahili: '斯瓦希里语', + vocalTamil: '泰米尔语', + vocalTelugu: '泰卢固语', + vocalThai: '泰语', + vocalTagalog: '他加禄语', + vocalTurkish: '土耳其语', + vocalUkrainian: '乌克兰语', + vocalUrdu: '乌尔都语', + vocalVietnamese: '越南语', + vocalCantonese: '粤语', + vocalChineseMandarin: '中文(普通话)', + + // CreatePanel - Style + stylePlaceholder: '例如:欢快流行摇滚、抒情民谣、90年代嘻哈', + genreMoodInstruments: '曲风、情绪、乐器、氛围', + refreshGenres: '刷新风格建议', + mainGenre: '大类风格', + subGenre: '小类风格', + nameSong: '为歌曲命名', + + // CreatePanel - Parameters + bpm: '曲速*(BPM)', + auto: '自动', + duration: '时长', + seconds: '秒', + keyScale: '调式', + timeSignature: '拍号', + + // CreatePanel - Advanced + randomSeed: '随机种子', + thinking: '思考模式', + audioFormat: '音频格式', + inferenceSteps: '推理步数', + guidanceScale: '引导强度(cfg)', + inferMethod: '推理方法', + shift: '偏移(shift)', + timestepShiftForBase: '基础模型的时间步偏移(turbo 模式无效)', + expertControls: '专家控制', + lmParameters: 'LM 参数', + controlLyricGeneration: '控制歌词生成和创造力', + transform: '音频转换', + controlSourceAudio: '控制模型遵循源音频的程度', + audioCodes: '音频编码', + optionalAudioCodes: '可选的音频编码载荷', + taskType: '任务类型', + textToMusic: '文本 → 音乐', + audioCoverStrength: '音频翻唱强度', + repaintingStart: '重绘开始', + repaintingEnd: '重绘结束', + instruction: '指令', + lmTemperature: 'LM 温度', + higherMoreRandom: '值越高越随机(0-2)', + lmCfgScale: 'LM CFG 强度', + noCfgScale: '1.0 = 无 CFG(1-3)', + topK: 'Top-K', + topP: 'Top-P', + lmNegativePrompt: 'LM 负面提示词', + noUserInput: '无用户输入', + thingsToAvoid: '要避免的内容...', + useWhenCfgScaleGreater: '当 LM CFG 强度 > 1.0 时使用', + lmTopK: 'LM Top-K', + lmTopP: 'LM Top-P', + + // Guidance section + guidance: '引导控制(guidance)', + advancedCfgScheduling: '高级 CFG 调度控制。', + cfgIntervalStart: 'CFG 区间开始', + cfgIntervalEnd: 'CFG 区间结束', + customTimesteps: '自定义时间步', + timestepsPlaceholder: '例如:1,3,5,7', + scoreScale: '分数缩放', + lmBatchChunkSize: 'LM 批次块大小', + trackName: '音轨名称', + optionalTrackName: '可选的音轨名称', + completeTrackClasses: '完整音轨类别', + trackClassesPlaceholder: '类别-a, 类别-b', + useAdg: '使用 ADG', + allowLmBatch: '允许 LM 批处理', + useCotMetas: '使用 CoT 元数据', + useCotCaption: '使用 CoT 字幕', + useCotLanguage: '使用 CoT 语言', + autogen: '自动生成', + constrainedDecodingDebug: '约束解码调试', + formatCaption: '格式化字幕', + getScores: '获取分数', + getLrcLyrics: '获取 LRC(歌词)', + batchSize: '批量大小(变体数)', + variations: '变体', + bulkGenerate: '批量生成', + job: '任务', + jobs: '任务', + queueMultipleJobs: '使用相同设置排队多个独立生成任务', + numberOfVariations: '要生成的歌曲变体数量', + moreStepsBetterQuality: '步数越多质量越好,速度越慢(turbo 推荐 8)', + howCloselyFollowPrompt: '多严格遵循提示词', + seed: '种子', + randomSeedRecommended: '每次运行随机(推荐)', + fixedSeedReproducible: '固定种子以获得可重现结果', + enterFixedSeed: '输入固定种子', + thinkingCot: '思考模式(CoT)', + mp3Smaller: 'MP3(较小)', + flacLossless: 'FLAC(无损)', + odeDeterministic: 'ODE(确定性)', + sdeStochastic: 'SDE(随机性)', + min: '分钟', + + // CreatePanel - Drag & Drop + dropToUseAudio: '拖放以使用音频', + dropToUpload: '拖放以上传', + usingAsReference: '用作参考', + usingAsCover: '用作翻唱', + uploadingAsReference: '上传为参考', + uploadingAsCover: '上传为翻唱', + + // CreatePanel - Vocal Gender + vocalGender: '人声性别', + male: '男声', + female: '女声', + + // CreatePanel - LM Backend & Model + lmBackendLabel: 'LM 后端', + lmBackendPt: 'PT(~1.6 GB 显存)', + lmBackendVllm: 'VLLM(~9.2 GB 显存)', + lmBackendHint: 'PT 显存占用更少,VLLM 在高性能 GPU 上可能更快', + lmModelLabel: 'LM 模型', + lmModel06B: '0.6B(最轻量,~0.5 GB 显存)', + lmModel17B: '1.7B(均衡,~1.5 GB 显存)', + lmModel4B: '4B(最佳质量,~4 GB 显存)', + lmModelHint: '控制用于歌词/风格增强的 LLM。如不存在将自动下载。', + + // CreatePanel - Transcription + transcribing: '转录中...', + transcribingWithWhisper: '正在使用 Whisper 转录…', + + // CreatePanel - Library Tabs + uploaded: '已上传', + createdTab: '已创建', + useTrack: '使用', + noCreatedSongsYet: '暂无已创建歌曲', + generateSongsToReuse: '生成歌曲后可作为翻唱或参考使用', + + // CreatePanel - Actions + generate: '生成', + generating: '生成中...', + createButton: '创建', + formatLyrics: '格式化歌词', + formatting: '格式化中...', + clearAll: '清空全部', + + // Simple Mode + describeYourSong: '描述你的歌曲', + songDescriptionPlaceholder: '一首关于夏日和朋友冒险的快乐流行歌曲...', + quickSettings: '快速设置', + key: '调式', + time: '拍号', + + // CreatePanel - Errors + loginToGenerate: '请登录以生成音乐', + uploadFailed: '上传失败', + formatFailed: '格式化失败', + formatFailedDetail: '格式化失败。请确保 LLM 已初始化。', + formatFailedLlmUnavailable: '格式化失败。LLM 可能不可用。', + deleteSongConfirm: '确定要删除"{title}"吗?此操作无法撤销。', + profileUpdateFailed: '更新个人资料失败', + ffmpegLoadFailed: '加载视频编码器失败。请刷新后重试。', + videoRenderFailed: '视频渲染失败。请重试。', + + // SongList / Filters + searchYourSongs: '搜索你的歌曲...', + filters: '筛选', + refineBy: '筛选条件', + liked: '已喜欢', + public: '公开', + private: '私密', + generatingStatus: '生成中', + selectSongToView: '选择一首歌曲查看详情', + noSongsMatchFilters: '没有歌曲符合你的筛选条件。', + clearFilters: '清除筛选', + + // RightSidebar - Song Details + songDetails: '歌曲详情', + created: '创建于', + styleTags: '风格与标签', + lyricsSection: '歌词', + copyLyrics: '复制', + + // Languages + japanese: '日语', + spanish: '西班牙语', + german: '德语', + french: '法语', + portuguese: '葡萄牙语', + russian: '俄语', + italian: '意大利语', + dutch: '荷兰语', + polish: '波兰语', + turkish: '土耳其语', + vietnamese: '越南语', + czech: '捷克语', + persian: '波斯语', + arabic: '阿拉伯语', + korean: '韩语', + thai: '泰语', + hindi: '印地语', + indonesian: '印尼语', + ukrainian: '乌克兰语', + hungarian: '匈牙利语', + swedish: '瑞典语', + romanian: '罗马尼亚语', + greek: '希腊语', + + // Task Types + audio2audio: '音频 → 音频', + coverTask: '翻唱', + repaintTask: '重绘', + + // Search Page + searchSongsPlaceholder: '搜索歌曲、播放列表、创作者或风格', + songsMatching: '匹配的歌曲', + featuredSongs: '精选歌曲', + noSongsFound: '未找到匹配的歌曲', + creatorsMatching: '匹配的创作者', + featuredCreators: '精选创作者', + noCreatorsFound: '未找到匹配的创作者', + noCreatorsYet: '暂无创作者。成为第一个分享音乐的人!', + playlistsMatching: '匹配的播放列表', + featuredPlaylists: '精选播放列表', + noPlaylistsFound: '未找到匹配的播放列表', + noPlaylistsYet: '暂无公开播放列表。创建一个并分享你的最爱!', + genres: '风格', + songs: '首歌曲', + + // Library View + yourLibrary: '我的音乐库', + newPlaylist: '新建播放列表', + likedSongs: '喜欢的歌曲', + playlists: '播放列表', + playlist: '播放列表', + byYou: '由你创建', + + // User Profile + back: '返回', + loadingProfile: '加载中...', + userNotFound: '用户不存在', + goBack: '返回', + likes: '喜欢', + plays: '播放', + recent: '最新', + top: '热门', + noPublicSongsYet: '暂无公开歌曲', + seeMore: '查看更多', + joined: '加入于', + supportingSince: '支持者自', + avatarImage: '头像', + uploadAvatar: '上传头像', + avatarFormats: 'JPG、PNG、WebP、GIF • 最大 5MB', + bannerImage: '横幅图片', + clickToUploadBanner: '点击上传横幅', + bannerFormats: '推荐尺寸:1500x500px • JPG、PNG、WebP、GIF • 最大 5MB', + bio: '个人简介', + bioPlaceholder: '介绍一下你自己...', + uploadingAvatar: '上传头像中...', + uploadingBanner: '上传横幅中...', + saving: '保存中...', + saveChanges: '保存更改', + + // Playlist Modals + createPlaylist: '创建播放列表', + playlistName: '名称', + playlistNamePlaceholder: '我的超棒播放列表', + playlistDescription: '描述', + descriptionPlaceholder: '编程时的氛围音乐...', + addToPlaylist: '添加到播放列表', + createNewPlaylist: '创建新播放列表', + noExistingPlaylists: '暂无播放列表。', + + // Edit Profile Modal (Settings) + usernameLabel: '用户名', + usernamePlaceholder: '用户名', + usernameMinLengthError: '用户名至少需要 2 个字符', + usernameTakenError: '用户名已被占用', + usernameUpdateFailedError: '更新用户名失败', + usernameRequirements: '仅允许字母、数字、下划线和短横线', + + // Song Details (RightSidebar) + more: '更多', + copy: '复制', + copied: '已复制!', + copyAllTags: '复制所有标签', + + // Song Dropdown Menu + createVideo: '创建视频', + editAudio: '编辑音频', + extractStems: '提取音轨', + reusePrompt: '重用提示词', + deleteSong: '删除歌曲', + useAsReference: '用作参考', + coverSong: '翻唱歌曲', + confirmDeleteTitle: '删除歌曲', + confirmDeleteManyTitle: '删除歌曲', + deleteSongsConfirm: '确定要删除 {count} 首歌曲吗?此操作无法撤销。', + songsDeletedSuccess: '歌曲删除成功', + songsDeletedPartial: '已删除 {succeeded}/{total} 首歌曲', + deleteUploadConfirm: '确定要删除此上传文件吗?此操作无法撤销。', + cannotBeUndone: '此操作无法撤销。', + + // Player + nowPlaying: '正在播放', + downloadAudio: '下载音频', + openInEditor: '在编辑器中打开', + anonymous: '匿名用户', + + // PlaylistDetail + loadingPlaylist: '加载播放列表中...', + playlistNotFound: '播放列表未找到', + deletePlaylistConfirm: '确定要删除这个播放列表吗?', + dateAdded: '添加日期', + justNow: '刚刚', + unknown: '未知', + + // SongProfile + loadingSong: '加载歌曲中...', + songNotFound: '歌曲未找到', + similar: '相似', + by: '由', + + // ShareModal + shareSong: '分享歌曲', + shareOnX: '分享到 X', + shareOnFacebook: '分享到 Facebook', + shareOnWhatsApp: '分享到 WhatsApp', + shareOnTelegram: '分享到 Telegram', + shareOnReddit: '分享到 Reddit', + shareOnLinkedIn: '分享到 LinkedIn', + shareViaEmail: '通过邮件分享', + copyLink: '复制链接', + emailSubject: '来听听这首AI歌曲', + emailBody: '嘿!\n\n我创作了这首AI生成的歌曲,觉得你会喜欢:\n\n《{title}》- {creator}\n{style}\n\n点击收听:{url}\n\n使用 ACE-Step UI 制作 - 免费开源的本地AI音乐生成工具!', + + // Reference Modal + referenceModalTitle: '参考音频', + coverModalTitle: '翻唱音频', + referenceModalDescription: '基于参考音轨创作歌曲', + coverModalDescription: '将现有音轨转换为新版本', + uploadAudio: '上传音频', + uploadingAudio: '上传中...', + audioFormats: 'MP3、WAV、FLAC', + mine: '我的', + loadingTracks: '加载中...', + noTracksYet: '暂无音轨', + uploadAudioFilesAsReferences: '上传音频文件作为参考', + + // Training Module + loraTraining: 'LoRA 训练', + trainingDescription: '从音频文件构建数据集并训练自定义 LoRA 适配器', + datasetBuilder: '数据集构建器', + trainLora: '训练 LoRA', + quickStart: '快速开始', + chooseOneOption: '选择一项:加载现有数据集 或 扫描新目录', + loadExistingDataset: '加载现有数据集', + loadDataset: '加载', + scanNewDirectory: '扫描新目录', + scanDataset: '扫描', + datasetSettings: '数据集设置', + datasetName: '数据集名称', + customActivationTag: '自定义激活标签', + allInstrumental: '全部纯音乐', + tagPosition: '标签位置', + tagPrepend: '前置(标签,描述)', + tagAppend: '后置(描述,标签)', + tagReplace: '替换描述', + genreRatio: '风格比例', + genreRatioHint: '0% = 全部描述,100% = 全部风格', + autoLabelWithAI: 'AI 自动标注', + autoLabelDescription: '为音频文件生成元数据:描述、BPM、调式、拍号', + skipMetas: '跳过 BPM/调式/拍号', + onlyUnlabeled: '仅未标注', + autoLabelAll: '自动标注全部', + filename: '文件名', + labeled: '已标注', + saveDataset: '保存数据集', + preprocessToTensors: '预处理为张量', + preprocessDescription: '将数据集转换为预计算张量以加速训练(需要加载模型)', + preprocess: '预处理', + preprocessedDataset: '预处理数据集', + loraSettings: 'LoRA 设置', + loraRank: 'LoRA Rank', + loraAlpha: 'LoRA Alpha', + dropout: 'Dropout', + trainingParameters: '训练参数', + learningRate: '学习率', + maxEpochs: '最大轮数', + gradientAccumulation: '梯度累积', + saveEvery: '保存间隔', + epochs: '轮', + outputDirectory: '输出目录', + useFP8: '使用 FP8 量化', + fp8Description: '减少显存占用', + startTraining: '开始训练', + stopTraining: '停止训练', + trainingProgress: '训练进度', + trainingLog: '训练日志', + trainingStatus: '训练状态', + tensorboardView: 'TensorBoard 视图', + showTensorboard: '显示 TensorBoard', + hideTensorboard: '隐藏 TensorBoard', + openInNewTab: '在新标签页打开', + + // Training Status Messages + scanning: '扫描中...', + autoLabeling: '自动标注中...', + savingDataset: '保存中...', + preprocessing: '预处理中...', + loadingDataset: '加载中...', + startingTraining: '启动中...', + error: '错误', + datasetInfo: '数据集:{name},样本数:{samples},已标注:{labeled}', + trainingStep: '步骤 {step} - 损失:{loss}', + editSample: '编辑样本', + caption: '描述', + genre: '风格', + promptOverride: '提示词覆盖', + musicDescription: '音乐描述...', + overrideGlobalRatio: '覆盖此样本的全局比例', + editableUsedForTraining: '可编辑,用于训练', + useGlobalRatio: '使用全局比例', + thisSample: '此样本', + }, + ja: { + // Navigation + create: '作成', + library: 'ライブラリ', + search: '検索', + training: 'トレーニング', + + // Theme + lightMode: 'ライトモード', + darkMode: 'ダークモード', + + // Auth + signIn: 'サインイン', + signOut: 'サインアウト', + settings: '設定', + + // Username Modal + welcomeTitle: 'ACE-Step UIへようこそ', + welcomeSubtitle: 'AI音楽作成を始めるために名前を入力してください', + yourName: 'お名前', + enterYourName: '名前を入力', + getStarted: '始める', + gettingStarted: '開始中...', + yourMusicYourWay: 'あなたの音楽、あなたのスタイルで。無料で無制限にAI音楽を作成。', + usernameMinLength: 'ユーザー名は2文字以上である必要があります', + usernameInvalidChars: 'ユーザー名は文字、数字、アンダースコア、ハイフンのみ使用できます', + failedToSetUsername: 'ユーザー名の設定に失敗しました', + + // Toasts + songNotAvailable: 'この曲は利用できません。', + unableToPlay: 'この曲を再生できません。', + generationTimedOut: '生成がタイムアウトしました', + generationFailed: '生成に失敗しました。もう一度お試しください。', + playlistCreated: 'プレイリストを作成しました!', + failedToCreatePlaylist: 'プレイリストの作成に失敗しました', + songAddedToPlaylist: 'プレイリストに追加しました', + failedToAddSong: '曲の追加に失敗しました', + songDeleted: '曲を削除しました', + failedToDeleteSong: '曲の削除に失敗しました', + + // Sidebar + aceStepUI: 'ACE-Step UI', + + // Common + cancel: 'キャンセル', + save: '保存', + delete: '削除', + edit: '編集', + share: '共有', + download: 'ダウンロード', + play: '再生', + pause: '一時停止', + normalSpeed: '通常', + next: '次へ', + previous: '前へ', + + // Placeholders + searchPlaceholder: '検索...', + + // Settings + language: '言語', + english: 'English', + chinese: '简体中文', + japaneseLanguage: '日本語', + koreanLanguage: '한국어', + appearance: '外観', + profile: 'プロフィール', + viewProfile: 'プロフィールを表示', + editProfile: 'プロフィールを編集', + account: 'アカウント', + username: 'ユーザー名', + memberSince: '登録日', + about: 'について', + version: 'バージョン', + localAIMusicGenerator: 'ローカルAI音楽ジェネレーター', + poweredBy: 'ACE-Step 1.5を搭載。オープンソースで無料で使用できます。', + createdBy: '開発者 Ambsd', + localizedBy: 'ローカライゼーション&二次開発 bdsqlsz', + follow: 'フォロー', + done: '完了', + light: 'ライト', + dark: 'ダーク', + + // Mobile + createSong: '曲を作成', + viewList: 'リストを表示', + + // CreatePanel - Modes + simple: 'シンプル', + custom: 'カスタム', + + // CreatePanel - Sections + audio: 'オーディオ', + lyrics: '歌詞', + styleOfMusic: '音楽スタイル', + title: 'タイトル', + musicParameters: '音楽パラメータ', + advancedSettings: '詳細設定', + + // CreatePanel - LoRA + loraPath: 'LoRA パス', + loraPathPlaceholder: 'LoRA モデルパスを入力...', + browseFiles: 'ファイルを参照', + loraLoaded: 'LoRA 読み込み済み', + loraUnloaded: 'LoRA 未読み込み', + loraLoad: '読み込む', + loraUnload: 'アンロード', + loraScale: 'LoRA スケール', + loraScaleDescription: 'LoRA モデルの影響強度を調整', + + // CreatePanel - Audio + reference: '参照', + cover: 'カバー', + fromLibrary: 'ライブラリから', + upload: 'アップロード', + + // CreatePanel - Lyrics + lyricsPlaceholder: 'ここに歌詞を入力...', + leaveLyricsEmpty: 'インストゥルメンタルの場合は空のままにするか、下のトグルを使用', + vocal: 'ボーカル', + instrumental: 'インストゥルメンタル', + vocalLanguage: 'ボーカル言語', + autoInstrumental: '自動 / インストゥルメンタル', + vocalArabic: 'アラビア語', + vocalAzerbaijani: 'アゼルバイジャン語', + vocalBulgarian: 'ブルガリア語', + vocalBengali: 'ベンガル語', + vocalCatalan: 'カタルーニャ語', + vocalCzech: 'チェコ語', + vocalDanish: 'デンマーク語', + vocalGerman: 'ドイツ語', + vocalGreek: 'ギリシャ語', + vocalEnglish: '英語', + vocalSpanish: 'スペイン語', + vocalPersian: 'ペルシャ語', + vocalFinnish: 'フィンランド語', + vocalFrench: 'フランス語', + vocalHebrew: 'ヘブライ語', + vocalHindi: 'ヒンディー語', + vocalCroatian: 'クロアチア語', + vocalHaitianCreole: 'ハイチ・クレオール語', + vocalHungarian: 'ハンガリー語', + vocalIndonesian: 'インドネシア語', + vocalIcelandic: 'アイスランド語', + vocalItalian: 'イタリア語', + vocalJapanese: '日本語', + vocalKorean: '韓国語', + vocalLatin: 'ラテン語', + vocalLithuanian: 'リトアニア語', + vocalMalay: 'マレー語', + vocalNepali: 'ネパール語', + vocalDutch: 'オランダ語', + vocalNorwegian: 'ノルウェー語', + vocalPunjabi: 'パンジャーブ語', + vocalPolish: 'ポーランド語', + vocalPortuguese: 'ポルトガル語', + vocalRomanian: 'ルーマニア語', + vocalRussian: 'ロシア語', + vocalSanskrit: 'サンスクリット語', + vocalSlovak: 'スロバキア語', + vocalSerbian: 'セルビア語', + vocalSwedish: 'スウェーデン語', + vocalSwahili: 'スワヒリ語', + vocalTamil: 'タミル語', + vocalTelugu: 'テルグ語', + vocalThai: 'タイ語', + vocalTagalog: 'タガログ語', + vocalTurkish: 'トルコ語', + vocalUkrainian: 'ウクライナ語', + vocalUrdu: 'ウルドゥー語', + vocalVietnamese: 'ベトナム語', + vocalCantonese: '広東語', + vocalChineseMandarin: '中国語(標準語)', + + // CreatePanel - Style + stylePlaceholder: '例:アップビートなポップロック、エモーショナルなバラード、90年代ヒップホップ', + genreMoodInstruments: 'ジャンル、ムード、楽器、雰囲気', + refreshGenres: 'ジャンル提案を更新', + mainGenre: 'メインジャンル', + subGenre: 'サブジャンル', + nameSong: '曲名を付ける', + + // CreatePanel - Parameters + bpm: 'BPM', + auto: '自動', + duration: '長さ', + seconds: '秒', + keyScale: 'キーとスケール', + timeSignature: '拍子', + + // CreatePanel - Advanced + randomSeed: 'ランダムシード', + thinking: '思考', + audioFormat: 'オーディオ形式', + inferenceSteps: '推論ステップ', + guidanceScale: 'ガイダンススケール', + inferMethod: '推論方法', + shift: 'シフト', + timestepShiftForBase: 'ベースモデルのタイムステップシフト(turboでは無効)', + expertControls: 'エキスパートコントロール', + lmParameters: 'LMパラメータ', + controlLyricGeneration: '歌詞生成と創造性を制御', + transform: '変換', + controlSourceAudio: 'モデルがソースオーディオに従う程度を制御', + audioCodes: 'オーディオコード', + optionalAudioCodes: 'オプションのオーディオコードペイロード', + taskType: 'タスクタイプ', + textToMusic: 'テキスト → 音楽', + audioCoverStrength: 'オーディオカバー強度', + repaintingStart: 'リペイント開始', + repaintingEnd: 'リペイント終了', + instruction: '指示', + lmTemperature: 'LM温度', + higherMoreRandom: '高いほどランダム(0-2)', + lmCfgScale: 'LM CFGスケール', + noCfgScale: '1.0 = CFGなし(1-3)', + topK: 'Top-K', + topP: 'Top-P', + lmNegativePrompt: 'LMネガティブプロンプト', + noUserInput: 'ユーザー入力なし', + thingsToAvoid: '避けるべきこと...', + useWhenCfgScaleGreater: 'LM CFGスケール > 1.0の場合に使用', + lmTopK: 'LM Top-K', + lmTopP: 'LM Top-P', + + // Guidance section + guidance: 'ガイダンス', + advancedCfgScheduling: '高度なCFGスケジューリング制御。', + cfgIntervalStart: 'CFG間隔開始', + cfgIntervalEnd: 'CFG間隔終了', + customTimesteps: 'カスタムタイムステップ', + timestepsPlaceholder: '例:1,3,5,7', + scoreScale: 'スコアスケール', + lmBatchChunkSize: 'LMバッチチャンクサイズ', + trackName: 'トラック名', + optionalTrackName: 'オプションのトラック名', + completeTrackClasses: '完全なトラッククラス', + trackClassesPlaceholder: 'クラス-a、クラス-b', + useAdg: 'ADGを使用', + allowLmBatch: 'LMバッチを許可', + useCotMetas: 'CoTメタを使用', + useCotCaption: 'CoTキャプションを使用', + useCotLanguage: 'CoT言語を使用', + autogen: '自動生成', + constrainedDecodingDebug: '制約付きデコーディングデバッグ', + formatCaption: 'キャプションをフォーマット', + getScores: 'スコアを取得', + getLrcLyrics: 'LRC(歌詞)を取得', + batchSize: 'バッチサイズ(バリエーション)', + variations: 'バリエーション', + bulkGenerate: '一括生成', + job: 'ジョブ', + jobs: 'ジョブ', + queueMultipleJobs: '同じ設定で複数の独立した生成ジョブをキューに追加', + numberOfVariations: '生成する曲のバリエーション数', + moreStepsBetterQuality: 'ステップが多いほど品質が高く、遅くなります(turboには8を推奨)', + howCloselyFollowPrompt: 'プロンプトにどれだけ忠実に従うか', + seed: 'シード', + randomSeedRecommended: '毎回ランダム化(推奨)', + fixedSeedReproducible: '再現可能な結果のための固定シード', + enterFixedSeed: '固定シードを入力', + thinkingCot: '思考(CoT)', + mp3Smaller: 'MP3(小さい)', + flacLossless: 'FLAC(ロスレス)', + odeDeterministic: 'ODE(決定論的)', + sdeStochastic: 'SDE(確率的)', + min: '分', + + // CreatePanel - Drag & Drop + dropToUseAudio: 'ドロップしてオーディオを使用', + dropToUpload: 'ドロップしてアップロード', + usingAsReference: '参照として使用', + usingAsCover: 'カバーとして使用', + uploadingAsReference: '参照としてアップロード', + uploadingAsCover: 'カバーとしてアップロード', + + // CreatePanel - Vocal Gender + vocalGender: 'ボーカルの性別', + male: '男性', + female: '女性', + + // CreatePanel - LM Backend & Model + lmBackendLabel: 'LMバックエンド', + lmBackendPt: 'PT(〜1.6 GB VRAM)', + lmBackendVllm: 'VLLM(〜9.2 GB VRAM)', + lmBackendHint: 'PTはVRAMが少なく、VLLMは高性能GPUで高速', + lmModelLabel: 'LMモデル', + lmModel06B: '0.6B(最軽量、〜0.5 GB VRAM)', + lmModel17B: '1.7B(バランス、〜1.5 GB VRAM)', + lmModel4B: '4B(最高品質、〜4 GB VRAM)', + lmModelHint: '歌詞/スタイル強化に使用するLLMを制御。未インストールの場合自動ダウンロード。', + + // CreatePanel - Transcription + transcribing: '文字起こし中...', + transcribingWithWhisper: 'Whisperで文字起こし中…', + + // CreatePanel - Library Tabs + uploaded: 'アップロード済み', + createdTab: '作成済み', + useTrack: '使用', + noCreatedSongsYet: '作成した曲がまだありません', + generateSongsToReuse: '曲を生成してカバーや参照として再利用', + + // CreatePanel - Actions + generate: '生成', + generating: '生成中...', + createButton: '作成', + formatLyrics: '歌詞をフォーマット', + formatting: 'フォーマット中...', + clearAll: 'すべてクリア', + + // Simple Mode + describeYourSong: '曲を説明', + songDescriptionPlaceholder: '友達との夏の冒険についての明るいポップソング...', + quickSettings: 'クイック設定', + key: 'キー', + time: '拍子', + + // CreatePanel - Errors + loginToGenerate: '音楽を生成するにはログインしてください', + uploadFailed: 'アップロードに失敗しました', + formatFailed: 'フォーマットに失敗しました', + formatFailedDetail: 'フォーマットに失敗しました。LLMが初期化されていることを確認してください。', + formatFailedLlmUnavailable: 'フォーマットに失敗しました。LLMが利用できない可能性があります。', + deleteSongConfirm: '「{title}」を削除しますか?この操作は元に戻せません。', + profileUpdateFailed: 'プロフィールの更新に失敗しました', + ffmpegLoadFailed: 'ビデオエンコーダーの読み込みに失敗しました。更新してもう一度お試しください。', + videoRenderFailed: 'ビデオレンダリングに失敗しました。もう一度お試しください。', + + // SongList / Filters + searchYourSongs: '曲を検索...', + filters: 'フィルター', + refineBy: '絞り込み', + liked: 'いいね', + public: '公開', + private: '非公開', + generatingStatus: '生成中', + selectSongToView: '詳細を表示する曲を選択', + noSongsMatchFilters: 'フィルターに一致する曲がありません。', + clearFilters: 'フィルターをクリア', + + // RightSidebar - Song Details + songDetails: '曲の詳細', + created: '作成日', + styleTags: 'スタイルとタグ', + lyricsSection: '歌詞', + copyLyrics: 'コピー', + + // Languages + japanese: '日本語', + spanish: 'スペイン語', + german: 'ドイツ語', + french: 'フランス語', + portuguese: 'ポルトガル語', + russian: 'ロシア語', + italian: 'イタリア語', + dutch: 'オランダ語', + polish: 'ポーランド語', + turkish: 'トルコ語', + vietnamese: 'ベトナム語', + czech: 'チェコ語', + persian: 'ペルシャ語', + arabic: 'アラビア語', + korean: '韓国語', + thai: 'タイ語', + hindi: 'ヒンディー語', + indonesian: 'インドネシア語', + ukrainian: 'ウクライナ語', + hungarian: 'ハンガリー語', + swedish: 'スウェーデン語', + romanian: 'ルーマニア語', + greek: 'ギリシャ語', + + // Task Types + audio2audio: 'オーディオ → オーディオ', + coverTask: 'カバー', + repaintTask: 'リペイント', + + // Search Page + searchSongsPlaceholder: '曲、プレイリスト、クリエイター、スタイルを検索', + songsMatching: '一致する曲', + featuredSongs: '注目の曲', + noSongsFound: '一致する曲が見つかりません', + creatorsMatching: '一致するクリエイター', + featuredCreators: '注目のクリエイター', + noCreatorsFound: '一致するクリエイターが見つかりません', + noCreatorsYet: 'まだクリエイターがいません。最初に音楽を共有しましょう!', + playlistsMatching: '一致するプレイリスト', + featuredPlaylists: '注目のプレイリスト', + noPlaylistsFound: '一致するプレイリストが見つかりません', + noPlaylistsYet: '公開プレイリストがまだありません。作成してお気に入りを共有しましょう!', + genres: 'ジャンル', + songs: '曲', + + // Library View + yourLibrary: 'マイライブラリ', + newPlaylist: '新しいプレイリスト', + likedSongs: 'いいねした曲', + playlists: 'プレイリスト', + playlist: 'プレイリスト', + byYou: 'あなたが作成', + + // User Profile + back: '戻る', + loadingProfile: '読み込み中...', + userNotFound: 'ユーザーが見つかりません', + goBack: '戻る', + likes: 'いいね', + plays: '再生回数', + recent: '最新', + top: '人気', + noPublicSongsYet: '公開曲がまだありません', + seeMore: 'もっと見る', + joined: '参加日', + supportingSince: 'サポート開始', + avatarImage: 'アバター画像', + uploadAvatar: 'アバターをアップロード', + avatarFormats: 'JPG、PNG、WebP、GIF • 最大5MB', + bannerImage: 'バナー画像', + clickToUploadBanner: 'クリックしてバナーをアップロード', + bannerFormats: '推奨サイズ:1500x500px • JPG、PNG、WebP、GIF • 最大5MB', + bio: '自己紹介', + bioPlaceholder: '自己紹介を書いてください...', + uploadingAvatar: 'アバターをアップロード中...', + uploadingBanner: 'バナーをアップロード中...', + saving: '保存中...', + saveChanges: '変更を保存', + + // Playlist Modals + createPlaylist: 'プレイリストを作成', + playlistName: '名前', + playlistNamePlaceholder: '私の素晴らしいプレイリスト', + playlistDescription: '説明', + descriptionPlaceholder: 'コーディング時の雰囲気...', + addToPlaylist: 'プレイリストに追加', + createNewPlaylist: '新しいプレイリストを作成', + noExistingPlaylists: 'プレイリストがまだありません。', + + // Edit Profile Modal (Settings) + usernameLabel: 'ユーザー名', + usernamePlaceholder: 'ユーザー名', + usernameMinLengthError: 'ユーザー名は2文字以上である必要があります', + usernameTakenError: 'このユーザー名は既に使用されています', + usernameUpdateFailedError: 'ユーザー名の更新に失敗しました', + usernameRequirements: '文字、数字、アンダースコア、ハイフンのみ使用可能', + + // Song Details (RightSidebar) + more: 'もっと', + copy: 'コピー', + copied: 'コピーしました!', + copyAllTags: 'すべてのタグをコピー', + + // Song Dropdown Menu + createVideo: 'ビデオを作成', + editAudio: 'オーディオを編集', + extractStems: 'ステムを抽出', + reusePrompt: 'プロンプトを再利用', + deleteSong: '曲を削除', + useAsReference: '参照として使用', + coverSong: 'カバーソング', + confirmDeleteTitle: '曲を削除', + confirmDeleteManyTitle: '曲を削除', + deleteSongsConfirm: '{count}曲を削除しますか?この操作は元に戻せません。', + songsDeletedSuccess: '曲を削除しました', + songsDeletedPartial: '{succeeded}/{total}曲を削除しました', + deleteUploadConfirm: 'このアップロードを削除しますか?この操作は元に戻せません。', + cannotBeUndone: 'この操作は元に戻せません。', + + // Player + nowPlaying: '再生中', + downloadAudio: 'オーディオをダウンロード', + openInEditor: 'エディターで開く', + anonymous: '匿名ユーザー', + + // PlaylistDetail + loadingPlaylist: 'プレイリストを読み込み中...', + playlistNotFound: 'プレイリストが見つかりません', + deletePlaylistConfirm: 'このプレイリストを削除してもよろしいですか?', + dateAdded: '追加日', + justNow: 'たった今', + unknown: '不明', + + // SongProfile + loadingSong: '曲を読み込み中...', + songNotFound: '曲が見つかりません', + similar: '類似', + by: '作成者', + + // ShareModal + shareSong: '曲を共有', + shareOnX: 'Xで共有', + shareOnFacebook: 'Facebookで共有', + shareOnWhatsApp: 'WhatsAppで共有', + shareOnTelegram: 'Telegramで共有', + shareOnReddit: 'Redditで共有', + shareOnLinkedIn: 'LinkedInで共有', + shareViaEmail: 'メールで共有', + copyLink: 'リンクをコピー', + emailSubject: 'このAI曲をチェックしてください', + emailBody: 'こんにちは!\n\n私がこのAI生成の曲を作りました。気に入ってもらえると思います:\n\n「{title}」by {creator}\n{style}\n\nここで聴いてください:{url}\n\nACE-Step UIで作成 - 無料でオープンソースのローカルAI音楽生成!', + + // Reference Modal + referenceModalTitle: 'リファレンス', + coverModalTitle: 'カバー', + referenceModalDescription: 'リファレンストラックに触発された曲を作成', + coverModalDescription: '既存のトラックを新しいバージョンに変換', + uploadAudio: 'オーディオをアップロード', + uploadingAudio: 'アップロード中...', + audioFormats: 'MP3、WAV、FLAC', + mine: 'マイトラック', + loadingTracks: '読み込み中...', + noTracksYet: 'トラックがまだありません', + uploadAudioFilesAsReferences: 'リファレンスとして使用するオーディオファイルをアップロード', + + // Training Module + loraTraining: 'LoRAトレーニング', + trainingDescription: 'オーディオファイルからデータセットを構築し、カスタムLoRAアダプターをトレーニング', + datasetBuilder: 'データセットビルダー', + trainLora: 'LoRAをトレーニング', + quickStart: 'クイックスタート', + chooseOneOption: '選択:既存のデータセットを読み込む または 新しいディレクトリをスキャン', + loadExistingDataset: '既存のデータセットを読み込む', + loadDataset: '読み込む', + scanNewDirectory: '新しいディレクトリをスキャン', + scanDataset: 'スキャン', + datasetSettings: 'データセット設定', + datasetName: 'データセット名', + customActivationTag: 'カスタム活性化タグ', + allInstrumental: '全てインストゥルメンタル', + tagPosition: 'タグの位置', + tagPrepend: '前置(タグ、キャプション)', + tagAppend: '後置(キャプション、タグ)', + tagReplace: 'キャプションを置換', + genreRatio: 'ジャンル比率', + genreRatioHint: '0% = 全てキャプション、100% = 全てジャンル', + autoLabelWithAI: 'AIで自動ラベル付け', + autoLabelDescription: 'オーディオファイルのメタデータを生成:キャプション、BPM、キー、拍子', + skipMetas: 'BPM/キー/拍子をスキップ', + onlyUnlabeled: 'ラベルなしのみ', + autoLabelAll: '全て自動ラベル付け', + filename: 'ファイル名', + labeled: 'ラベル付き', + saveDataset: 'データセットを保存', + preprocessToTensors: 'テンソルに前処理', + preprocessDescription: 'データセットを事前計算されたテンソルに変換して高速トレーニング(モデルのロードが必要)', + preprocess: '前処理', + preprocessedDataset: '前処理済みデータセット', + loraSettings: 'LoRA設定', + loraRank: 'LoRAランク', + loraAlpha: 'LoRA Alpha', + dropout: 'ドロップアウト', + trainingParameters: 'トレーニングパラメータ', + learningRate: '学習率', + maxEpochs: '最大エポック数', + gradientAccumulation: '勾配累積', + saveEvery: '保存間隔', + epochs: 'エポック', + outputDirectory: '出力ディレクトリ', + useFP8: 'FP8量子化を使用', + fp8Description: 'VRAMを削減', + startTraining: 'トレーニング開始', + stopTraining: 'トレーニング停止', + trainingProgress: 'トレーニング進行状況', + trainingLog: 'トレーニングログ', + trainingStatus: 'トレーニングステータス', + tensorboardView: 'TensorBoardビュー', + showTensorboard: 'TensorBoardを表示', + hideTensorboard: 'TensorBoardを非表示', + openInNewTab: '新しいタブで開く', + + // Training Status Messages + scanning: 'スキャン中...', + autoLabeling: '自動ラベル付け中...', + savingDataset: '保存中...', + preprocessing: '前処理中...', + loadingDataset: '読み込み中...', + startingTraining: '開始中...', + error: 'エラー', + datasetInfo: 'データセット: {name}、サンプル数: {samples}、ラベル付き: {labeled}', + trainingStep: 'ステップ {step} - 損失: {loss}', + editSample: 'サンプルを編集', + caption: 'キャプション', + genre: 'ジャンル', + promptOverride: 'プロンプト上書き', + musicDescription: '音楽の説明...', + overrideGlobalRatio: 'このサンプルのグローバル比率を上書き', + editableUsedForTraining: '編集可能、トレーニングに使用', + useGlobalRatio: 'グローバル比率を使用', + thisSample: 'このサンプル', + }, + ko: { + // Navigation + create: '생성', + library: '라이브러리', + search: '검색', + training: '훈련', + + // Theme + lightMode: '라이트 모드', + darkMode: '다크 모드', + + // Auth + signIn: '로그인', + signOut: '로그아웃', + settings: '설정', + + // Username Modal + welcomeTitle: 'ACE-Step UI에 오신 것을 환영합니다', + welcomeSubtitle: 'AI 음악 제작을 시작하려면 이름을 입력하세요', + yourName: '이름', + enterYourName: '이름 입력', + getStarted: '시작하기', + gettingStarted: '시작 중...', + yourMusicYourWay: '당신의 음악, 당신만의 방식으로. 무료로 무제한 AI 음악 제작.', + usernameMinLength: '사용자 이름은 최소 2자 이상이어야 합니다', + usernameInvalidChars: '사용자 이름은 문자, 숫자, 밑줄, 하이픈만 사용할 수 있습니다', + failedToSetUsername: '사용자 이름 설정에 실패했습니다', + + // Toasts + songNotAvailable: '이 곡은 더 이상 사용할 수 없습니다.', + unableToPlay: '이 곡을 재생할 수 없습니다.', + generationTimedOut: '생성 시간 초과', + generationFailed: '생성에 실패했습니다. 다시 시도해주세요.', + playlistCreated: '재생목록이 생성되었습니다!', + failedToCreatePlaylist: '재생목록 생성에 실패했습니다', + songAddedToPlaylist: '재생목록에 추가되었습니다', + failedToAddSong: '곡 추가에 실패했습니다', + songDeleted: '곡이 삭제되었습니다', + failedToDeleteSong: '곡 삭제에 실패했습니다', + + // Sidebar + aceStepUI: 'ACE-Step UI', + + // Common + cancel: '취소', + save: '저장', + delete: '삭제', + edit: '편집', + share: '공유', + download: '다운로드', + play: '재생', + pause: '일시정지', + normalSpeed: '보통', + next: '다음', + previous: '이전', + + // Placeholders + searchPlaceholder: '검색...', + + // Settings + language: '언어', + english: 'English', + chinese: '简体中文', + japaneseLanguage: '日本語', + koreanLanguage: '한국어', + appearance: '외관', + profile: '프로필', + viewProfile: '프로필 보기', + editProfile: '프로필 편집', + account: '계정', + username: '사용자 이름', + memberSince: '가입일', + about: '정보', + version: '버전', + localAIMusicGenerator: '로컬 AI 음악 생성기', + poweredBy: 'ACE-Step 1.5 기반. 오픈 소스이며 무료로 사용 가능합니다.', + createdBy: '개발자 Ambsd', + localizedBy: '현지화 및 2차 개발 bdsqlsz', + follow: '팔로우', + done: '완료', + light: '라이트', + dark: '다크', + + // Mobile + createSong: '곡 만들기', + viewList: '목록 보기', + + // CreatePanel - Modes + simple: '간단', + custom: '사용자 정의', + + // CreatePanel - Sections + audio: '오디오', + lyrics: '가사', + styleOfMusic: '음악 스타일', + title: '제목', + musicParameters: '음악 매개변수', + advancedSettings: '고급 설정', + + // CreatePanel - LoRA + loraPath: 'LoRA 경로', + loraPathPlaceholder: 'LoRA 모델 경로 입력...', + browseFiles: '파일 찾아보기', + loraLoaded: 'LoRA 로드됨', + loraUnloaded: 'LoRA 언로드됨', + loraLoad: '로드', + loraUnload: '언로드', + loraScale: 'LoRA 강도', + loraScaleDescription: 'LoRA 모델의 영향 강도 조절', + + // CreatePanel - Audio + reference: '참조', + cover: '커버', + fromLibrary: '라이브러리에서', + upload: '업로드', + + // CreatePanel - Lyrics + lyricsPlaceholder: '여기에 가사를 입력하세요...', + leaveLyricsEmpty: '연주곡의 경우 비워두거나 아래 토글 사용', + vocal: '보컬', + instrumental: '연주곡', + vocalLanguage: '보컬 언어', + autoInstrumental: '자동 / 연주곡', + vocalArabic: '아랍어', + vocalAzerbaijani: '아제르바이잔어', + vocalBulgarian: '불가리아어', + vocalBengali: '벵골어', + vocalCatalan: '카탈루냐어', + vocalCzech: '체코어', + vocalDanish: '덴마크어', + vocalGerman: '독일어', + vocalGreek: '그리스어', + vocalEnglish: '영어', + vocalSpanish: '스페인어', + vocalPersian: '페르시아어', + vocalFinnish: '핀란드어', + vocalFrench: '프랑스어', + vocalHebrew: '히브리어', + vocalHindi: '힌디어', + vocalCroatian: '크로아티아어', + vocalHaitianCreole: '아이티 크레올어', + vocalHungarian: '헝가리어', + vocalIndonesian: '인도네시아어', + vocalIcelandic: '아이슬란드어', + vocalItalian: '이탈리아어', + vocalJapanese: '일본어', + vocalKorean: '한국어', + vocalLatin: '라틴어', + vocalLithuanian: '리투아니아어', + vocalMalay: '말레이어', + vocalNepali: '네팔어', + vocalDutch: '네덜란드어', + vocalNorwegian: '노르웨이어', + vocalPunjabi: '펀자브어', + vocalPolish: '폴란드어', + vocalPortuguese: '포르투갈어', + vocalRomanian: '루마니아어', + vocalRussian: '러시아어', + vocalSanskrit: '산스크리트어', + vocalSlovak: '슬로바키아어', + vocalSerbian: '세르비아어', + vocalSwedish: '스웨덴어', + vocalSwahili: '스와힐리어', + vocalTamil: '타밀어', + vocalTelugu: '텔루구어', + vocalThai: '태국어', + vocalTagalog: '타갈로그어', + vocalTurkish: '터키어', + vocalUkrainian: '우크라이나어', + vocalUrdu: '우르두어', + vocalVietnamese: '베트남어', + vocalCantonese: '광둥어', + vocalChineseMandarin: '중국어(표준어)', + + // CreatePanel - Style + stylePlaceholder: '예: 경쾌한 팝 록, 감성적인 발라드, 90년대 힙합', + genreMoodInstruments: '장르, 분위기, 악기, 느낌', + refreshGenres: '장르 제안 새로고침', + mainGenre: '주요 장르', + subGenre: '하위 장르', + nameSong: '곡 이름 짓기', + + // CreatePanel - Parameters + bpm: 'BPM', + auto: '자동', + duration: '길이', + seconds: '초', + keyScale: '키와 스케일', + timeSignature: '박자', + + // CreatePanel - Advanced + randomSeed: '랜덤 시드', + thinking: '생각', + audioFormat: '오디오 형식', + inferenceSteps: '추론 단계', + guidanceScale: '가이던스 스케일', + inferMethod: '추론 방법', + shift: '시프트', + timestepShiftForBase: '베이스 모델의 타임스텝 시프트 (turbo에서는 무효)', + expertControls: '전문가 제어', + lmParameters: 'LM 매개변수', + controlLyricGeneration: '가사 생성과 창의성 제어', + transform: '변환', + controlSourceAudio: '모델이 소스 오디오를 따르는 정도를 제어', + audioCodes: '오디오 코드', + optionalAudioCodes: '선택적 오디오 코드 페이로드', + taskType: '작업 유형', + textToMusic: '텍스트 → 음악', + audioCoverStrength: '오디오 커버 강도', + repaintingStart: '리페인팅 시작', + repaintingEnd: '리페인팅 종료', + instruction: '지시', + lmTemperature: 'LM 온도', + higherMoreRandom: '높을수록 랜덤 (0-2)', + lmCfgScale: 'LM CFG 스케일', + noCfgScale: '1.0 = CFG 없음 (1-3)', + topK: 'Top-K', + topP: 'Top-P', + lmNegativePrompt: 'LM 네거티브 프롬프트', + noUserInput: '사용자 입력 없음', + thingsToAvoid: '피해야 할 것들...', + useWhenCfgScaleGreater: 'LM CFG 스케일 > 1.0일 때 사용', + lmTopK: 'LM Top-K', + lmTopP: 'LM Top-P', + + // Guidance section + guidance: '가이던스', + advancedCfgScheduling: '고급 CFG 스케줄링 제어.', + cfgIntervalStart: 'CFG 간격 시작', + cfgIntervalEnd: 'CFG 간격 종료', + customTimesteps: '사용자 정의 타임스텝', + timestepsPlaceholder: '예: 1,3,5,7', + scoreScale: '스코어 스케일', + lmBatchChunkSize: 'LM 배치 청크 크기', + trackName: '트랙 이름', + optionalTrackName: '선택적 트랙 이름', + completeTrackClasses: '완전한 트랙 클래스', + trackClassesPlaceholder: '클래스-a, 클래스-b', + useAdg: 'ADG 사용', + allowLmBatch: 'LM 배치 허용', + useCotMetas: 'CoT 메타 사용', + useCotCaption: 'CoT 캡션 사용', + useCotLanguage: 'CoT 언어 사용', + autogen: '자동 생성', + constrainedDecodingDebug: '제약 디코딩 디버그', + formatCaption: '캡션 포맷', + getScores: '점수 가져오기', + getLrcLyrics: 'LRC(가사) 가져오기', + batchSize: '배치 크기 (변형)', + variations: '변형', + bulkGenerate: '대량 생성', + job: '작업', + jobs: '작업', + queueMultipleJobs: '동일한 설정으로 여러 독립 생성 작업 대기열에 추가', + numberOfVariations: '생성할 곡 변형 수', + moreStepsBetterQuality: '단계가 많을수록 품질이 높고 느립니다 (turbo는 8 권장)', + howCloselyFollowPrompt: '프롬프트를 얼마나 엄격히 따를지', + seed: '시드', + randomSeedRecommended: '매 실행마다 무작위 (권장)', + fixedSeedReproducible: '재현 가능한 결과를 위한 고정 시드', + enterFixedSeed: '고정 시드 입력', + thinkingCot: '생각 (CoT)', + mp3Smaller: 'MP3 (작음)', + flacLossless: 'FLAC (무손실)', + odeDeterministic: 'ODE (결정론적)', + sdeStochastic: 'SDE (확률적)', + min: '분', + + // CreatePanel - Drag & Drop + dropToUseAudio: '드롭하여 오디오 사용', + dropToUpload: '드롭하여 업로드', + usingAsReference: '참조로 사용', + usingAsCover: '커버로 사용', + uploadingAsReference: '참조로 업로드', + uploadingAsCover: '커버로 업로드', + + // CreatePanel - Vocal Gender + vocalGender: '보컬 성별', + male: '남성', + female: '여성', + + // CreatePanel - LM Backend & Model + lmBackendLabel: 'LM 백엔드', + lmBackendPt: 'PT (~1.6 GB VRAM)', + lmBackendVllm: 'VLLM (~9.2 GB VRAM)', + lmBackendHint: 'PT는 VRAM이 적고, VLLM은 고성능 GPU에서 더 빠름', + lmModelLabel: 'LM 모델', + lmModel06B: '0.6B (가장 가벼움, ~0.5 GB VRAM)', + lmModel17B: '1.7B (균형, ~1.5 GB VRAM)', + lmModel4B: '4B (최고 품질, ~4 GB VRAM)', + lmModelHint: '가사/스타일 향상에 사용되는 LLM을 제어. 없으면 자동 다운로드.', + + // CreatePanel - Transcription + transcribing: '전사 중...', + transcribingWithWhisper: 'Whisper로 전사 중…', + + // CreatePanel - Library Tabs + uploaded: '업로드됨', + createdTab: '생성됨', + useTrack: '사용', + noCreatedSongsYet: '생성된 곡이 아직 없습니다', + generateSongsToReuse: '곡을 생성하여 커버나 참조로 재사용', + + // CreatePanel - Actions + generate: '생성', + generating: '생성 중...', + createButton: '만들기', + formatLyrics: '가사 포맷', + formatting: '포맷 중...', + clearAll: '모두 지우기', + + // Simple Mode + describeYourSong: '곡 설명', + songDescriptionPlaceholder: '친구들과 여름 모험에 대한 밝은 팝송...', + quickSettings: '빠른 설정', + key: '키', + time: '박자', + + // CreatePanel - Errors + loginToGenerate: '음악을 생성하려면 로그인하세요', + uploadFailed: '업로드 실패', + formatFailed: '포맷 실패', + formatFailedDetail: '포맷 실패. LLM이 초기화되었는지 확인하세요.', + formatFailedLlmUnavailable: '포맷 실패. LLM을 사용할 수 없을 수 있습니다.', + deleteSongConfirm: '「{title}」을(를) 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.', + profileUpdateFailed: '프로필 업데이트에 실패했습니다', + ffmpegLoadFailed: '비디오 인코더 로드에 실패했습니다. 새로고침 후 다시 시도하세요.', + videoRenderFailed: '비디오 렌더링에 실패했습니다. 다시 시도하세요.', + + // SongList / Filters + searchYourSongs: '곡 검색...', + filters: '필터', + refineBy: '세분화', + liked: '좋아요', + public: '공개', + private: '비공개', + generatingStatus: '생성 중', + selectSongToView: '세부 정보를 볼 곡 선택', + noSongsMatchFilters: '필터와 일치하는 곡이 없습니다.', + clearFilters: '필터 지우기', + + // RightSidebar - Song Details + songDetails: '곡 상세정보', + created: '생성일', + styleTags: '스타일 및 태그', + lyricsSection: '가사', + copyLyrics: '복사', + + // Languages + japanese: '일본어', + spanish: '스페인어', + german: '독일어', + french: '프랑스어', + portuguese: '포르투갈어', + russian: '러시아어', + italian: '이탈리아어', + dutch: '네덜란드어', + polish: '폴란드어', + turkish: '터키어', + vietnamese: '베트남어', + czech: '체코어', + persian: '페르시아어', + arabic: '아랍어', + korean: '한국어', + thai: '태국어', + hindi: '힌디어', + indonesian: '인도네시아어', + ukrainian: '우크라이나어', + hungarian: '헝가리어', + swedish: '스웨덴어', + romanian: '루마니아어', + greek: '그리스어', + + // Task Types + audio2audio: '오디오 → 오디오', + coverTask: '커버', + repaintTask: '리페인트', + + // Search Page + searchSongsPlaceholder: '곡, 재생목록, 제작자 또는 스타일 검색', + songsMatching: '일치하는 곡', + featuredSongs: '추천 곡', + noSongsFound: '일치하는 곡을 찾을 수 없습니다', + creatorsMatching: '일치하는 제작자', + featuredCreators: '추천 제작자', + noCreatorsFound: '일치하는 제작자를 찾을 수 없습니다', + noCreatorsYet: '아직 제작자가 없습니다. 음악을 공유하는 첫 번째 사람이 되세요!', + playlistsMatching: '일치하는 재생목록', + featuredPlaylists: '추천 재생목록', + noPlaylistsFound: '일치하는 재생목록을 찾을 수 없습니다', + noPlaylistsYet: '공개 재생목록이 아직 없습니다. 만들고 즐겨찾기를 공유하세요!', + genres: '장르', + songs: '곡', + + // Library View + yourLibrary: '내 라이브러리', + newPlaylist: '새 재생목록', + likedSongs: '좋아하는 곡', + playlists: '재생목록', + playlist: '재생목록', + byYou: '내가 만든', + + // User Profile + back: '뒤로', + loadingProfile: '로딩 중...', + userNotFound: '사용자를 찾을 수 없습니다', + goBack: '뒤로 가기', + likes: '좋아요', + plays: '재생 수', + recent: '최신', + top: '인기', + noPublicSongsYet: '공개 곡이 아직 없습니다', + seeMore: '더 보기', + joined: '가입일', + supportingSince: '후원 시작일', + avatarImage: '아바타 이미지', + uploadAvatar: '아바타 업로드', + avatarFormats: 'JPG, PNG, WebP, GIF • 최대 5MB', + bannerImage: '배너 이미지', + clickToUploadBanner: '클릭하여 배너 업로드', + bannerFormats: '권장 크기: 1500x500px • JPG, PNG, WebP, GIF • 최대 5MB', + bio: '자기소개', + bioPlaceholder: '자신에 대해 소개하세요...', + uploadingAvatar: '아바타 업로드 중...', + uploadingBanner: '배너 업로드 중...', + saving: '저장 중...', + saveChanges: '변경 사항 저장', + + // Playlist Modals + createPlaylist: '재생목록 만들기', + playlistName: '이름', + playlistNamePlaceholder: '내 멋진 재생목록', + playlistDescription: '설명', + descriptionPlaceholder: '코딩할 때 듣는 음악...', + addToPlaylist: '재생목록에 추가', + createNewPlaylist: '새 재생목록 만들기', + noExistingPlaylists: '재생목록이 아직 없습니다.', + + // Edit Profile Modal (Settings) + usernameLabel: '사용자 이름', + usernamePlaceholder: '사용자 이름', + usernameMinLengthError: '사용자 이름은 최소 2자 이상이어야 합니다', + usernameTakenError: '이 사용자 이름은 이미 사용 중입니다', + usernameUpdateFailedError: '사용자 이름 업데이트에 실패했습니다', + usernameRequirements: '문자, 숫자, 밑줄, 하이픈만 허용됨', + + // Song Details (RightSidebar) + more: '더보기', + copy: '복사', + copied: '복사됨!', + copyAllTags: '모든 태그 복사', + + // Song Dropdown Menu + createVideo: '비디오 만들기', + editAudio: '오디오 편집', + extractStems: '스템 추출', + reusePrompt: '프롬프트 재사용', + deleteSong: '곡 삭제', + useAsReference: '참조로 사용', + coverSong: '커버 곡', + confirmDeleteTitle: '곡 삭제', + confirmDeleteManyTitle: '곡 삭제', + deleteSongsConfirm: '{count}곡을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.', + songsDeletedSuccess: '곡이 삭제되었습니다', + songsDeletedPartial: '{succeeded}/{total}곡 삭제됨', + deleteUploadConfirm: '이 업로드를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.', + cannotBeUndone: '이 작업은 되돌릴 수 없습니다.', + + // Player + nowPlaying: '재생 중', + downloadAudio: '오디오 다운로드', + openInEditor: '편집기에서 열기', + anonymous: '익명 사용자', + + // PlaylistDetail + loadingPlaylist: '재생목록 로딩 중...', + playlistNotFound: '재생목록을 찾을 수 없습니다', + deletePlaylistConfirm: '이 재생목록을 삭제하시겠습니까?', + dateAdded: '추가일', + justNow: '방금', + unknown: '알 수 없음', + + // SongProfile + loadingSong: '곡 로딩 중...', + songNotFound: '곡을 찾을 수 없습니다', + similar: '유사', + by: '제작자', + + // ShareModal + shareSong: '곡 공유', + shareOnX: 'X에서 공유', + shareOnFacebook: 'Facebook에서 공유', + shareOnWhatsApp: 'WhatsApp에서 공유', + shareOnTelegram: 'Telegram에서 공유', + shareOnReddit: 'Reddit에서 공유', + shareOnLinkedIn: 'LinkedIn에서 공유', + shareViaEmail: '이메일로 공유', + copyLink: '링크 복사', + emailSubject: '이 AI 곡을 들어보세요', + emailBody: '안녕하세요!\n\n제가 만든 이 AI 생성 곡을 들어보세요. 마음에 드실 거예요:\n\n"{title}" - {creator}\n{style}\n\n여기서 들으세요: {url}\n\nACE-Step UI로 제작 - 무료 오픈 소스 로컬 AI 음악 생성!', + + // Reference Modal + referenceModalTitle: '참조 음악', + coverModalTitle: '커버 음악', + referenceModalDescription: '참조 트랙에서 영감을 받은 곡 만들기', + coverModalDescription: '기존 트랙을 새로운 버전으로 변환', + uploadAudio: '오디오 업로드', + uploadingAudio: '업로드 중...', + audioFormats: 'MP3, WAV, FLAC', + mine: '내 트랙', + loadingTracks: '로딩 중...', + noTracksYet: '트랙이 없습니다', + uploadAudioFilesAsReferences: '참조로 사용할 오디오 파일 업로드', + + // Training Module + loraTraining: 'LoRA 훈련', + trainingDescription: '오디오 파일에서 데이터셋을 구축하고 커스텀 LoRA 어댑터를 훈련', + datasetBuilder: '데이터셋 빌더', + trainLora: 'LoRA 훈련', + quickStart: '빠른 시작', + chooseOneOption: '선택: 기존 데이터셋 로드 또는 새 디렉토리 스캔', + loadExistingDataset: '기존 데이터셋 로드', + loadDataset: '로드', + scanNewDirectory: '새 디렉토리 스캔', + scanDataset: '스캔', + datasetSettings: '데이터셋 설정', + datasetName: '데이터셋 이름', + customActivationTag: '커스텀 활성화 태그', + allInstrumental: '모두 인스트루멘탈', + tagPosition: '태그 위치', + tagPrepend: '앞에 추가 (태그, 캡션)', + tagAppend: '뒤에 추가 (캡션, 태그)', + tagReplace: '캡션 대체', + genreRatio: '장르 비율', + genreRatioHint: '0% = 모두 캡션, 100% = 모두 장르', + autoLabelWithAI: 'AI로 자동 라벨링', + autoLabelDescription: '오디오 파일의 메타데이터 생성: 캡션, BPM, 키, 박자', + skipMetas: 'BPM/키/박자 건너뛰기', + onlyUnlabeled: '라벨 없는 것만', + autoLabelAll: '모두 자동 라벨링', + filename: '파일명', + labeled: '라벨됨', + saveDataset: '데이터셋 저장', + preprocessToTensors: '텐서로 전처리', + preprocessDescription: '빠른 훈련을 위해 데이터셋을 사전 계산된 텐서로 변환 (모델 로드 필요)', + preprocess: '전처리', + preprocessedDataset: '전처리된 데이터셋', + loraSettings: 'LoRA 설정', + loraRank: 'LoRA 랭크', + loraAlpha: 'LoRA Alpha', + dropout: '드롭아웃', + trainingParameters: '훈련 파라미터', + learningRate: '학습률', + maxEpochs: '최대 에포크', + gradientAccumulation: '그래디언트 누적', + saveEvery: '저장 간격', + epochs: '에포크', + outputDirectory: '출력 디렉토리', + useFP8: 'FP8 양자화 사용', + fp8Description: 'VRAM 사용량 감소', + startTraining: '훈련 시작', + stopTraining: '훈련 중지', + trainingProgress: '훈련 진행', + trainingLog: '훈련 로그', + + // Training Status Messages + scanning: '스캔 중...', + autoLabeling: '자동 라벨링 중...', + savingDataset: '저장 중...', + preprocessing: '전처리 중...', + loadingDataset: '로드 중...', + startingTraining: '시작 중...', + error: '오류', + datasetInfo: '데이터셋: {name}, 샘플: {samples}, 라벨됨: {labeled}', + trainingStep: '스텝 {step} - 손실: {loss}', + editSample: '샘플 편집', + caption: '캡션', + genre: '장르', + promptOverride: '프롬프트 재정의', + musicDescription: '음악 설명...', + overrideGlobalRatio: '이 샘플의 글로벌 비율 재정의', + editableUsedForTraining: '편집 가능, 훈련에 사용', + useGlobalRatio: '글로벌 비율 사용', + thisSample: '이 샘플', + } +}; + +export type TranslationKey = keyof typeof translations.en;