mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 22:45:41 +00:00
ad74578ef6
* feat(playlists): playlist folder model — store + pure grouping core Local, per-server folder layer over the server's flat playlist list (the Subsonic API has no folder concept). Adds: - playlistFolders.ts: shared types + pure groupPlaylistsByFolder (used by every surface), with full unit coverage. - playlistFolderStore.ts: persisted Zustand store (create/rename/delete/ assign/collapse), per-server scoped; deleting a folder drops its assignments so playlists fall back to ungrouped. UI surfaces (sidebar + Playlists page) build on this in following commits. * i18n(playlists): folder strings across all 9 locales Adds the playlists.folders.* namespace (folder names, move/remove, expand/ collapse, group-by-folders toggle, count plurals, and the local-only notice explaining that Navidrome and the Subsonic API have no native folder support). * feat(playlists): folder views, drag-to-folder, move-to-folder menu, view toggle Surfaces the local folder layer on both the Playlists page and the sidebar: - Page: collapsible folder sections + ungrouped remainder, each reusing VirtualCardGrid; flat grid is kept verbatim when no folders exist or the group view is toggled off. - Drag-to-folder via the shared mouse-based psy-drop system (HTML5 DnD is unusable in WebKitGTK); the whole section is the drop zone, and the ungrouped zone appears during a drag so a playlist can always return to root. - "Move to folder" submenu in the playlist context menu (keyboard-accessible path; also creates folders on the fly) — stays available offline. - Header gets a "New folder" action and a "Group by folders" view toggle (both context-aware); a notice surfaces the local-only caveat. - Sidebar renders the same collapsible folder groups. - groupView preference added to the folder store. * docs(changelog): note playlist folders (#1119)
123 lines
5.0 KiB
TypeScript
123 lines
5.0 KiB
TypeScript
export const playlists = {
|
|
title: 'Playlists',
|
|
newPlaylist: 'New Playlist',
|
|
unnamed: 'Unnamed Playlist',
|
|
createName: 'Playlist name…',
|
|
create: 'Create',
|
|
cancel: 'Cancel',
|
|
empty: 'No playlists yet.',
|
|
emptyPlaylist: 'This playlist is empty.',
|
|
addFirstSong: 'Add your first song',
|
|
notFound: 'Playlist not found.',
|
|
songs_one: '{{count}} song',
|
|
songs_other: '{{count}} songs',
|
|
playAll: 'Play All',
|
|
playTooltip: 'Play playlist',
|
|
shuffle: 'Shuffle',
|
|
addToQueue: 'Add to Queue',
|
|
back: 'Back to Playlists',
|
|
deletePlaylist: 'Delete',
|
|
confirmDelete: 'Click again to confirm',
|
|
removeSong: 'Remove from playlist',
|
|
addSongs: 'Add Songs',
|
|
addSongsTooltip: 'Search your library to add tracks',
|
|
searchPlaceholder: 'Search your library…',
|
|
noResults: 'No results.',
|
|
suggestions: 'Suggested Songs',
|
|
noSuggestions: 'No suggestions available.',
|
|
titleBadge: 'Playlist',
|
|
refreshSuggestions: 'New suggestions',
|
|
addSong: 'Add to playlist',
|
|
preview: 'Preview (30s)',
|
|
previewStop: 'Stop preview',
|
|
playNextSuggestion: 'Play next',
|
|
suggestionsHint: 'Double-click a row to add it to the playlist',
|
|
addSelected: 'Add selected',
|
|
cacheOffline: 'Cache playlist offline',
|
|
offlineCached: 'Playlist cached',
|
|
removeOffline: 'Remove from offline cache',
|
|
offlineDownloading: 'Caching… ({{done}}/{{total}} albums)',
|
|
publicLabel: 'Public',
|
|
privateLabel: 'Private',
|
|
editMeta: 'Edit playlist',
|
|
editNamePlaceholder: 'Playlist name…',
|
|
editCommentPlaceholder: 'Add a description…',
|
|
editPublic: 'Public playlist',
|
|
editSave: 'Save',
|
|
editCancel: 'Cancel',
|
|
changeCover: 'Change cover image',
|
|
changeCoverLabel: 'Change photo',
|
|
removeCover: 'Remove photo',
|
|
coverUpdated: 'Cover updated',
|
|
metaSaved: 'Playlist updated',
|
|
downloadZip: 'Download (ZIP)',
|
|
// Toast notifications for multi-select
|
|
addSuccess: '{{count}} songs added to {{playlist}}',
|
|
addPartial: '{{added}} added, {{skipped}} skipped (duplicates)',
|
|
addAllSkipped: 'All skipped ({{count}} duplicates)',
|
|
addedAsDuplicates: '{{count}} songs added to {{playlist}} (as duplicates)',
|
|
duplicateConfirmTitle: 'Already in playlist',
|
|
duplicateConfirmMessage: 'All {{count}} songs are already in "{{playlist}}". Add them again as duplicates?',
|
|
duplicateConfirmAction: 'Add anyway',
|
|
addError: 'Error adding songs',
|
|
createAndAddSuccess: 'Playlist "{{playlist}}" created with {{count}} songs',
|
|
createError: 'Error creating playlist',
|
|
deleteSuccess: '{{count}} playlists deleted',
|
|
deleteFailed: 'Error deleting {{name}}',
|
|
deleteSelected: 'Delete selected',
|
|
deleteSelectedPartial: 'Only {{n}} of {{total}} selected playlists can be deleted (the others belong to someone else).',
|
|
mergeSuccess: '{{count}} songs merged into {{playlist}}',
|
|
mergeNoNewSongs: 'No new songs to add',
|
|
mergeError: 'Error merging playlists',
|
|
mergeInto: 'Merge into',
|
|
selectionCount: '{{count}} selected',
|
|
select: 'Select',
|
|
startSelect: 'Enable selection',
|
|
cancelSelect: 'Cancel',
|
|
loadingAlbums: 'Resolving {{count}} albums…',
|
|
loadingArtists: 'Resolving {{count}} artists…',
|
|
myPlaylists: 'My Playlists',
|
|
noOtherPlaylists: 'No other playlists available',
|
|
addToPlaylistSuccess: '{{count}} songs added to {{playlist}}',
|
|
addToPlaylistNoNew: 'No new songs to add to {{playlist}}',
|
|
addToPlaylistError: 'Error adding to playlist',
|
|
removeSuccess: 'Song removed from playlist',
|
|
removeError: 'Error removing song from playlist',
|
|
importCSV: 'Import CSV',
|
|
importCSVTooltip: 'Import from Spotify CSV',
|
|
csvImportReport: 'CSV Import Report',
|
|
csvImportTotal: 'Total',
|
|
csvImportAdded: 'Added',
|
|
csvImportDuplicates: 'Duplicates',
|
|
csvImportNotFound: 'Not Found',
|
|
csvImportNetworkErrors: 'Network Errors',
|
|
csvImportDuplicatesTitle: 'Duplicate Tracks (Already in Playlist):',
|
|
csvImportNotFoundTitle: 'Tracks Not Found:',
|
|
csvImportNetworkErrorsTitle: 'Network Errors (may retry import):',
|
|
csvImportDownloadReport: 'Download Report',
|
|
csvImportClose: 'Close',
|
|
csvImportNoValidTracks: 'No valid tracks found in CSV file',
|
|
csvImportFailed: 'Failed to import CSV file',
|
|
csvImportToast: '{{added}} added, {{notFound}} not found, {{duplicates}} duplicates',
|
|
csvImportDownloadSuccess: 'Report downloaded successfully',
|
|
csvImportDownloadError: 'Failed to download report',
|
|
// Playlist folders (local, per-server organisation layer)
|
|
folders: {
|
|
newFolder: 'New folder',
|
|
namePlaceholder: 'Folder name…',
|
|
create: 'Create',
|
|
rename: 'Rename',
|
|
delete: 'Delete folder',
|
|
ungrouped: 'Ungrouped',
|
|
moveToFolder: 'Move to folder',
|
|
removeFromFolder: 'Remove from folder',
|
|
expandFolder: 'Expand folder',
|
|
collapseFolder: 'Collapse folder',
|
|
groupByFolders: 'Group by folders',
|
|
count_one: '{{count}} playlist',
|
|
count_other: '{{count}} playlists',
|
|
localOnlyNotice:
|
|
'Folders are saved only in Psysonic on this device. Navidrome and the Subsonic API have no native playlist-folder support, so the structure is not stored on your server or synced to your other devices and apps.',
|
|
},
|
|
};
|