diff --git a/locales/de.json b/locales/de.json index 874673c9..064b7306 100644 --- a/locales/de.json +++ b/locales/de.json @@ -40,6 +40,53 @@ "tb": "TB" } }, + "onboarding": { + "languageSelection": { + "title": "Willkommen beim LoRA Manager", + "continue": "Weiter", + "changeFailed": "Fehler beim Ändern der Sprache: {message}" + }, + "tutorial": { + "skipTutorial": "Tutorial überspringen", + "back": "Zurück", + "next": "Weiter", + "finish": "Fertigstellen" + }, + "steps": { + "fetch": { + "title": "Modelldaten abrufen", + "content": "Klicken Sie auf die Schaltfläche Abrufen, um Modelldaten und Vorschaubilder von Civitai herunterzuladen." + }, + "download": { + "title": "Neue Modelle herunterladen", + "content": "Verwenden Sie die Schaltfläche Herunterladen, um Modelle direkt über Civitai-URLs herunterzuladen." + }, + "bulk": { + "title": "Massenoperationen", + "content": "Wechseln Sie in den Massenmodus, indem Sie auf diese Schaltfläche klicken oder B drücken. Wählen Sie mehrere Modelle aus und führen Sie Stapeloperationen durch. Mit Strg+A können Sie alle sichtbaren Modelle auswählen." + }, + "searchOptions": { + "title": "Suchoptionen", + "content": "Klicken Sie auf diese Schaltfläche, um festzulegen, in welchen Feldern gesucht werden soll: Dateiname, Modellname, Tags oder Erstellername. Passen Sie den Suchbereich an." + }, + "filter": { + "title": "Modelle filtern", + "content": "Verwenden Sie Filter, um Modelle nach Basis-Modelltyp (SD1.5, SDXL, Flux usw.) oder bestimmten Tags einzugrenzen." + }, + "breadcrumb": { + "title": "Breadcrumb-Navigation", + "content": "Die Breadcrumb-Navigation zeigt Ihren aktuellen Pfad und ermöglicht eine schnelle Navigation zwischen Ordnern. Klicken Sie auf einen Ordnernamen, um direkt dorthin zu springen." + }, + "modelCards": { + "title": "Modellkarten", + "content": "Einfachklick auf eine Modellkarte zeigt detaillierte Informationen und ermöglicht das Bearbeiten von Metadaten. Suchen Sie nach dem Stiftsymbol, wenn Sie über bearbeitbare Felder fahren." + }, + "contextMenu": { + "title": "Kontextmenü", + "content": "Rechtsklick auf eine Modellkarte öffnet ein Kontextmenü mit weiteren Aktionen." + } + } + }, "modelCard": { "actions": { "addToFavorites": "Zu Favoriten hinzufügen", diff --git a/locales/en.json b/locales/en.json index fc609e15..c518574a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -40,6 +40,53 @@ "tb": "TB" } }, + "onboarding": { + "languageSelection": { + "title": "Welcome to LoRA Manager", + "continue": "Continue", + "changeFailed": "Failed to change language: {message}" + }, + "tutorial": { + "skipTutorial": "Skip Tutorial", + "back": "Back", + "next": "Next", + "finish": "Finish" + }, + "steps": { + "fetch": { + "title": "Fetch Models Metadata", + "content": "Click the Fetch button to download model metadata and preview images from Civitai." + }, + "download": { + "title": "Download New Models", + "content": "Use the Download button to download models directly from Civitai URLs." + }, + "bulk": { + "title": "Bulk Operations", + "content": "Enter bulk mode by clicking this button or pressing B. Select multiple models and perform batch operations. Use Ctrl+A to select all visible models." + }, + "searchOptions": { + "title": "Search Options", + "content": "Click this button to configure what fields to search in: filename, model name, tags, or creator name. Customize your search scope." + }, + "filter": { + "title": "Filter Models", + "content": "Use filters to narrow down models by base model type (SD1.5, SDXL, Flux, etc.) or by specific tags." + }, + "breadcrumb": { + "title": "Breadcrumb Navigation", + "content": "The breadcrumb navigation shows your current path and allows quick navigation between folders. Click any folder name to jump directly there." + }, + "modelCards": { + "title": "Model Cards", + "content": "Single-click a model card to view detailed information and edit metadata. Look for the pencil icon when hovering over editable fields." + }, + "contextMenu": { + "title": "Context Menu", + "content": "Right-click any model card for a context menu with additional actions." + } + } + }, "modelCard": { "actions": { "addToFavorites": "Add to favorites", diff --git a/locales/es.json b/locales/es.json index 9ca8a1aa..db682c59 100644 --- a/locales/es.json +++ b/locales/es.json @@ -40,6 +40,53 @@ "tb": "TB" } }, + "onboarding": { + "languageSelection": { + "title": "Bienvenido a LoRA Manager", + "continue": "Continuar", + "changeFailed": "Error al cambiar el idioma: {message}" + }, + "tutorial": { + "skipTutorial": "Saltar tutorial", + "back": "Atrás", + "next": "Siguiente", + "finish": "Finalizar" + }, + "steps": { + "fetch": { + "title": "Obtener metadatos de modelos", + "content": "Haz clic en el botón Obtener para descargar metadatos y vistas previas de modelos desde Civitai." + }, + "download": { + "title": "Descargar nuevos modelos", + "content": "Usa el botón Descargar para descargar modelos directamente desde URLs de Civitai." + }, + "bulk": { + "title": "Operaciones masivas", + "content": "Entra en modo masivo haciendo clic en este botón o presionando B. Selecciona varios modelos y realiza operaciones por lotes. Usa Ctrl+A para seleccionar todos los modelos visibles." + }, + "searchOptions": { + "title": "Opciones de búsqueda", + "content": "Haz clic en este botón para configurar en qué campos buscar: nombre de archivo, nombre del modelo, etiquetas o nombre del creador. Personaliza el alcance de tu búsqueda." + }, + "filter": { + "title": "Filtrar modelos", + "content": "Utiliza los filtros para reducir los modelos por tipo de modelo base (SD1.5, SDXL, Flux, etc.) o por etiquetas específicas." + }, + "breadcrumb": { + "title": "Navegación por rutas", + "content": "La navegación por rutas muestra tu ruta actual y permite una navegación rápida entre carpetas. Haz clic en cualquier nombre de carpeta para ir directamente allí." + }, + "modelCards": { + "title": "Tarjetas de modelo", + "content": "Clic único en una tarjeta de modelo para ver información detallada y editar metadatos. Busca el icono de lápiz al pasar el ratón sobre campos editables." + }, + "contextMenu": { + "title": "Menú contextual", + "content": "Clic derecho en cualquier tarjeta de modelo para ver un menú contextual con acciones adicionales." + } + } + }, "modelCard": { "actions": { "addToFavorites": "Añadir a favoritos", diff --git a/locales/fr.json b/locales/fr.json index 399a3fcf..5739e7cf 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -40,6 +40,53 @@ "tb": "To" } }, + "onboarding": { + "languageSelection": { + "title": "Bienvenue dans LoRA Manager", + "continue": "Continuer", + "changeFailed": "Échec du changement de langue : {message}" + }, + "tutorial": { + "skipTutorial": "Passer le tutoriel", + "back": "Retour", + "next": "Suivant", + "finish": "Terminer" + }, + "steps": { + "fetch": { + "title": "Récupérer les métadonnées des modèles", + "content": "Cliquez sur le bouton Récupérer pour télécharger les métadonnées et les images d'aperçu des modèles depuis Civitai." + }, + "download": { + "title": "Télécharger de nouveaux modèles", + "content": "Utilisez le bouton Télécharger pour télécharger des modèles directement à partir des URLs Civitai." + }, + "bulk": { + "title": "Opérations groupées", + "content": "Activez le mode groupé en cliquant sur ce bouton ou en appuyant sur B. Sélectionnez plusieurs modèles et effectuez des opérations par lot. Utilisez Ctrl+A pour sélectionner tous les modèles visibles." + }, + "searchOptions": { + "title": "Options de recherche", + "content": "Cliquez sur ce bouton pour configurer les champs à rechercher : nom de fichier, nom du modèle, tags ou nom du créateur. Personnalisez la portée de votre recherche." + }, + "filter": { + "title": "Filtrer les modèles", + "content": "Utilisez les filtres pour affiner les modèles par type de modèle de base (SD1.5, SDXL, Flux, etc.) ou par tags spécifiques." + }, + "breadcrumb": { + "title": "Navigation par fil d'Ariane", + "content": "La navigation par fil d'Ariane affiche votre chemin actuel et permet une navigation rapide entre les dossiers. Cliquez sur le nom d'un dossier pour y accéder directement." + }, + "modelCards": { + "title": "Cartes de modèles", + "content": "Cliquez une fois sur une carte de modèle pour afficher les informations détaillées et modifier les métadonnées. Cherchez l'icône crayon lors du survol des champs modifiables." + }, + "contextMenu": { + "title": "Menu contextuel", + "content": "Clic droit sur une carte de modèle pour accéder à un menu contextuel avec des actions supplémentaires." + } + } + }, "modelCard": { "actions": { "addToFavorites": "Ajouter aux favoris", @@ -77,7 +124,7 @@ "appTitle": "LoRA Manager", "navigation": { "loras": "LoRAs", - "recipes": "Recettes", + "recipes": "Recipes", "checkpoints": "Checkpoints", "embeddings": "Embeddings", "statistics": "Statistiques" @@ -86,7 +133,7 @@ "placeholder": "Rechercher...", "placeholders": { "loras": "Rechercher des LoRAs...", - "recipes": "Rechercher des recettes...", + "recipes": "Rechercher des recipes...", "checkpoints": "Rechercher des checkpoints...", "embeddings": "Rechercher des embeddings..." }, @@ -98,7 +145,7 @@ "modelname": "Nom du modèle", "tags": "Tags", "creator": "Créateur", - "title": "Titre de la recette", + "title": "Titre de la recipe", "loraName": "Nom de fichier LoRA", "loraModel": "Nom du modèle LoRA" } @@ -284,7 +331,7 @@ "relinkCivitai": "Relier à nouveau à Civitai", "copySyntax": "Copier la syntaxe LoRA", "copyFilename": "Copier le nom de fichier du modèle", - "copyRecipeSyntax": "Copier la syntaxe de la recette", + "copyRecipeSyntax": "Copier la syntaxe de la recipe", "sendToWorkflowAppend": "Envoyer vers le workflow (Ajouter)", "sendToWorkflowReplace": "Envoyer vers le workflow (Remplacer)", "openExamples": "Ouvrir le dossier d'exemples", @@ -294,33 +341,33 @@ "moveToFolder": "Déplacer vers un dossier", "excludeModel": "Exclure le modèle", "deleteModel": "Supprimer le modèle", - "shareRecipe": "Partager la recette", + "shareRecipe": "Partager la recipe", "viewAllLoras": "Voir tous les LoRAs", "downloadMissingLoras": "Télécharger les LoRAs manquants", - "deleteRecipe": "Supprimer la recette" + "deleteRecipe": "Supprimer la recipe" } }, "recipes": { - "title": "Recettes LoRA", + "title": "LoRA Recipes", "controls": { "import": { "action": "Importer", - "title": "Importer une recette depuis une image ou une URL", + "title": "Importer une recipe depuis une image ou une URL", "urlLocalPath": "URL / Chemin local", "uploadImage": "Téléverser une image", - "urlSectionDescription": "Saisissez une URL d'image Civitai ou un chemin de fichier local pour l'importer comme recette.", + "urlSectionDescription": "Saisissez une URL d'image Civitai ou un chemin de fichier local pour l'importer comme recipe.", "imageUrlOrPath": "URL d'image ou chemin de fichier :", "urlPlaceholder": "https://civitai.com/images/... ou C:/chemin/vers/image.png", "fetchImage": "Récupérer l'image", - "uploadSectionDescription": "Téléversez une image avec des métadonnées LoRA pour l'importer comme recette.", + "uploadSectionDescription": "Téléversez une image avec des métadonnées LoRA pour l'importer comme recipe.", "selectImage": "Sélectionner une image", - "recipeName": "Nom de la recette", - "recipeNamePlaceholder": "Entrez le nom de la recette", + "recipeName": "Nom de la recipe", + "recipeNamePlaceholder": "Entrez le nom de la recipe", "tagsOptional": "Tags (optionnel)", "addTagPlaceholder": "Ajouter un tag", "addTag": "Ajouter", "noTagsAdded": "Aucun tag ajouté", - "lorasInRecipe": "LoRAs dans cette recette", + "lorasInRecipe": "LoRAs dans cette recipe", "downloadLocationPreview": "Aperçu de l'emplacement de téléchargement :", "useDefaultPath": "Utiliser le chemin par défaut", "useDefaultPathTooltip": "Lorsque activé, les fichiers sont automatiquement organisés selon les modèles de chemin configurés", @@ -330,14 +377,14 @@ "createNewFolder": "Créer un nouveau dossier", "root": "Racine", "browseFolders": "Parcourir les dossiers :", - "downloadAndSaveRecipe": "Télécharger et sauvegarder la recette", + "downloadAndSaveRecipe": "Télécharger et sauvegarder la recipe", "downloadMissingLoras": "Télécharger les LoRAs manquants", - "saveRecipe": "Sauvegarder la recette", + "saveRecipe": "Sauvegarder la recipe", "loraCountInfo": "({existing}/{total} dans la bibliothèque)", "processingInput": "Traitement de l'entrée...", "analyzingMetadata": "Analyse des métadonnées de l'image...", "downloadingLoras": "Téléchargement des LoRAs...", - "savingRecipe": "Sauvegarde de la recette...", + "savingRecipe": "Sauvegarde de la recipe...", "startingDownload": "Début du téléchargement pour le LoRA {current}/{total}", "deletedFromCivitai": "Supprimé de Civitai", "inLibrary": "Dans la bibliothèque", @@ -346,12 +393,12 @@ "earlyAccessEnds": "L'accès anticipé se termine le {date}.", "earlyAccess": "Accès anticipé", "verifyEarlyAccess": "Vérifiez que vous avez acheté l'accès anticipé avant de télécharger.", - "duplicateRecipesFound": "{count} recette(s) identique(s) trouvée(s) dans votre bibliothèque", - "duplicateRecipesDescription": "Ces recettes contiennent les mêmes LoRAs avec des poids identiques.", + "duplicateRecipesFound": "{count} recipe(s) identique(s) trouvée(s) dans votre bibliothèque", + "duplicateRecipesDescription": "Ces recipes contiennent les mêmes LoRAs avec des poids identiques.", "showDuplicates": "Afficher les doublons", "hideDuplicates": "Masquer les doublons", "loraCount": "{count} LoRAs", - "recipePreviewAlt": "Aperçu de la recette", + "recipePreviewAlt": "Aperçu de la recipe", "loraPreviewAlt": "Aperçu LoRA", "errors": { "selectImageFile": "Veuillez sélectionner un fichier image", @@ -360,7 +407,7 @@ } }, "refresh": { - "title": "Actualiser la liste des recettes" + "title": "Actualiser la liste des recipes" }, "filteredByLora": "Filtré par LoRA" }, @@ -371,20 +418,20 @@ }, "contextMenu": { "copyRecipe": { - "missingId": "Impossible de copier la recette : ID de recette manquant", - "failed": "Échec de la copie de la syntaxe de la recette" + "missingId": "Impossible de copier la recipe : ID de recipe manquant", + "failed": "Échec de la copie de la syntaxe de la recipe" }, "sendRecipe": { - "missingId": "Impossible d'envoyer la recette : ID de recette manquant", - "failed": "Échec de l'envoi de la recette vers le workflow" + "missingId": "Impossible d'envoyer la recipe : ID de recipe manquant", + "failed": "Échec de l'envoi de la recipe vers le workflow" }, "viewLoras": { - "missingId": "Impossible de voir les LoRAs : ID de recette manquant", - "noLorasFound": "Aucun LoRA trouvé dans cette recette", - "loadError": "Erreur lors du chargement des LoRAs de la recette : {message}" + "missingId": "Impossible de voir les LoRAs : ID de recipe manquant", + "noLorasFound": "Aucun LoRA trouvé dans cette recipe", + "loadError": "Erreur lors du chargement des LoRAs de la recipe : {message}" }, "downloadMissing": { - "missingId": "Impossible de télécharger les LoRAs : ID de recette manquant", + "missingId": "Impossible de télécharger les LoRAs : ID de recipe manquant", "noMissingLoras": "Aucun LoRA manquant à télécharger", "getInfoFailed": "Échec de l'obtention des informations pour les LoRAs manquants", "prepareError": "Erreur lors de la préparation des LoRAs pour le téléchargement : {message}" @@ -504,9 +551,9 @@ "message": "Êtes-vous sûr de vouloir exclure ce modèle ? Les modèles exclus n'apparaîtront pas dans les recherches ou listes de modèles." }, "deleteDuplicateRecipes": { - "title": "Supprimer les recettes dupliquées", - "message": "Êtes-vous sûr de vouloir supprimer les recettes dupliquées sélectionnées ?", - "countMessage": "recettes seront définitivement supprimées." + "title": "Supprimer les recipes dupliquées", + "message": "Êtes-vous sûr de vouloir supprimer les recipes dupliquées sélectionnées ?", + "countMessage": "recipes seront définitivement supprimées." }, "deleteDuplicateModels": { "title": "Supprimer les modèles dupliqués", @@ -637,12 +684,12 @@ "tabs": { "examples": "Exemples", "description": "Description du modèle", - "recipes": "Recettes" + "recipes": "Recipes" }, "loading": { "exampleImages": "Chargement des images d'exemple...", "description": "Chargement de la description du modèle...", - "recipes": "Chargement des recettes...", + "recipes": "Chargement des recipes...", "examples": "Chargement des exemples..." } } @@ -685,8 +732,8 @@ "message": "Scan et construction du cache embedding. Cela peut prendre quelques minutes..." }, "recipes": { - "title": "Initialisation du gestionnaire de recettes", - "message": "Chargement et traitement des recettes. Cela peut prendre quelques minutes..." + "title": "Initialisation du gestionnaire de recipes", + "message": "Chargement et traitement des recipes. Cela peut prendre quelques minutes..." }, "statistics": { "title": "Initialisation des statistiques", @@ -705,9 +752,9 @@ "alt": "Téléchargement Civitai" }, "recipes": { - "title": "Sauvegarder les recettes", - "description": "Créez des recettes pour sauvegarder vos combinaisons de modèles préférées pour une utilisation future.", - "alt": "Recettes" + "title": "Sauvegarder les recipes", + "description": "Créez des recipes pour sauvegarder vos combinaisons de modèles préférées pour une utilisation future.", + "alt": "Recipes" }, "filter": { "title": "Filtrage rapide", @@ -748,12 +795,12 @@ "loraAdded": "LoRA ajouté au workflow", "loraReplaced": "LoRA remplacé dans le workflow", "loraFailedToSend": "Échec de l'envoi du LoRA au workflow", - "recipeAdded": "Recette ajoutée au workflow", - "recipeReplaced": "Recette remplacée dans le workflow", - "recipeFailedToSend": "Échec de l'envoi de la recette au workflow" + "recipeAdded": "Recipe ajoutée au workflow", + "recipeReplaced": "Recipe remplacée dans le workflow", + "recipeFailedToSend": "Échec de l'envoi de la recipe au workflow" }, "nodeSelector": { - "recipe": "Recette", + "recipe": "Recipe", "lora": "LoRA", "replace": "Remplacer", "append": "Ajouter", @@ -787,7 +834,7 @@ "general": "Général", "troubleshooting": "Dépannage", "modelManagement": "Gestion des modèles", - "recipes": "Recettes", + "recipes": "Recipes", "settings": "Paramètres & Configuration", "extensions": "Extensions", "newBadge": "NOUVEAU" @@ -879,42 +926,42 @@ "downloadCompleted": "Téléchargement terminé avec succès" }, "recipes": { - "fetchFailed": "Échec de la récupération des recettes : {message}", + "fetchFailed": "Échec de la récupération des recipes : {message}", "reloadFailed": "Échec du rechargement des {modelType}s : {message}", "loadFailed": "Échec du chargement des {modelType}s : {message}", "refreshComplete": "Actualisation terminée", - "refreshFailed": "Échec de l'actualisation des recettes : {message}", - "updateFailed": "Échec de la mise à jour de la recette : {error}", - "updateError": "Erreur lors de la mise à jour de la recette : {message}", - "nameSaved": "Recette \"{name}\" sauvegardée avec succès", - "nameUpdated": "Nom de la recette mis à jour avec succès", - "tagsUpdated": "Tags de la recette mis à jour avec succès", + "refreshFailed": "Échec de l'actualisation des recipes : {message}", + "updateFailed": "Échec de la mise à jour de la recipe : {error}", + "updateError": "Erreur lors de la mise à jour de la recipe : {message}", + "nameSaved": "Recipe \"{name}\" sauvegardée avec succès", + "nameUpdated": "Nom de la recipe mis à jour avec succès", + "tagsUpdated": "Tags de la recipe mis à jour avec succès", "sourceUrlUpdated": "URL source mise à jour avec succès", - "noRecipeId": "Aucun ID de recette disponible", - "copyFailed": "Erreur lors de la copie de la syntaxe de la recette : {message}", + "noRecipeId": "Aucun ID de recipe disponible", + "copyFailed": "Erreur lors de la copie de la syntaxe de la recipe : {message}", "noMissingLoras": "Aucun LoRA manquant à télécharger", "missingLorasInfoFailed": "Échec de l'obtention des informations pour les LoRAs manquants", "preparingForDownloadFailed": "Erreur lors de la préparation des LoRAs pour le téléchargement", "enterLoraName": "Veuillez entrer un nom ou une syntaxe LoRA", "reconnectedSuccessfully": "LoRA reconnecté avec succès", "reconnectFailed": "Erreur lors de la reconnexion du LoRA : {message}", - "cannotSend": "Impossible d'envoyer la recette : ID de recette manquant", - "sendFailed": "Échec de l'envoi de la recette vers le workflow", - "sendError": "Erreur lors de l'envoi de la recette vers le workflow", - "cannotDelete": "Impossible de supprimer la recette : ID de recette manquant", + "cannotSend": "Impossible d'envoyer la recipe : ID de recipe manquant", + "sendFailed": "Échec de l'envoi de la recipe vers le workflow", + "sendError": "Erreur lors de l'envoi de la recipe vers le workflow", + "cannotDelete": "Impossible de supprimer la recipe : ID de recipe manquant", "deleteConfirmationError": "Erreur lors de l'affichage de la confirmation de suppression", - "deletedSuccessfully": "Recette supprimée avec succès", - "deleteFailed": "Erreur lors de la suppression de la recette : {message}", - "cannotShare": "Impossible de partager la recette : ID de recette manquant", - "preparingForSharing": "Préparation de la recette pour le partage...", - "downloadStarted": "Téléchargement de la recette démarré", - "shareError": "Erreur lors du partage de la recette : {message}", - "sharePreparationError": "Erreur lors de la préparation de la recette pour le partage", + "deletedSuccessfully": "Recipe supprimée avec succès", + "deleteFailed": "Erreur lors de la suppression de la recipe : {message}", + "cannotShare": "Impossible de partager la recipe : ID de recipe manquant", + "preparingForSharing": "Préparation de la recipe pour le partage...", + "downloadStarted": "Téléchargement de la recipe démarré", + "shareError": "Erreur lors du partage de la recipe : {message}", + "sharePreparationError": "Erreur lors de la préparation de la recipe pour le partage", "selectImageFirst": "Veuillez d'abord sélectionner une image", - "enterRecipeName": "Veuillez entrer un nom de recette", + "enterRecipeName": "Veuillez entrer un nom de recipe", "processingError": "Erreur de traitement : {message}", "folderBrowserError": "Erreur lors du chargement du navigateur de dossiers : {message}", - "recipeSaveFailed": "Échec de la sauvegarde de la recette : {error}", + "recipeSaveFailed": "Échec de la sauvegarde de la recipe : {error}", "importFailed": "Échec de l'importation : {message}", "folderTreeFailed": "Échec du chargement de l'arborescence des dossiers", "folderTreeError": "Erreur lors du chargement de l'arborescence des dossiers" diff --git a/locales/ja.json b/locales/ja.json index f531101e..d1fc6bd5 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -40,6 +40,53 @@ "tb": "TB" } }, + "onboarding": { + "languageSelection": { + "title": "LoRA Managerへようこそ", + "continue": "続行", + "changeFailed": "言語の変更に失敗しました:{message}" + }, + "tutorial": { + "skipTutorial": "チュートリアルをスキップ", + "back": "戻る", + "next": "次へ", + "finish": "完了" + }, + "steps": { + "fetch": { + "title": "モデルメタデータの取得", + "content": "取得ボタンをクリックして、Civitaiからモデルのメタデータとプレビュー画像をダウンロードします。" + }, + "download": { + "title": "新しいモデルのダウンロード", + "content": "ダウンロードボタンを使って、CivitaiのURLから直接モデルをダウンロードできます。" + }, + "bulk": { + "title": "一括操作", + "content": "このボタンをクリックするか、Bキーを押して一括モードに入ります。複数のモデルを選択して一括操作が可能です。Ctrl+Aで表示中のモデルをすべて選択できます。" + }, + "searchOptions": { + "title": "検索オプション", + "content": "このボタンをクリックして、検索対象フィールド(ファイル名、モデル名、タグ、作成者名)を設定できます。検索範囲をカスタマイズしましょう。" + }, + "filter": { + "title": "モデルのフィルタ", + "content": "フィルタを使って、ベースモデルタイプ(SD1.5、SDXL、Fluxなど)や特定のタグでモデルを絞り込めます。" + }, + "breadcrumb": { + "title": "パンくずナビゲーション", + "content": "パンくずナビゲーションは現在のパスを表示し、フォルダ間を素早く移動できます。フォルダ名をクリックすると直接ジャンプできます。" + }, + "modelCards": { + "title": "モデルカード", + "content": "モデルカードをクリックすると詳細情報の表示やメタデータの編集ができます。編集可能な項目はホバー時に鉛筆アイコンが表示されます。" + }, + "contextMenu": { + "title": "コンテキストメニュー", + "content": "モデルカードを右クリックすると追加の操作ができるコンテキストメニューが表示されます。" + } + } + }, "modelCard": { "actions": { "addToFavorites": "お気に入りに追加", diff --git a/locales/ko.json b/locales/ko.json index 2ea2fab2..5d1b5447 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -40,6 +40,53 @@ "tb": "TB" } }, + "onboarding": { + "languageSelection": { + "title": "LoRA Manager에 오신 것을 환영합니다", + "continue": "계속", + "changeFailed": "언어 변경 실패: {message}" + }, + "tutorial": { + "skipTutorial": "튜토리얼 건너뛰기", + "back": "뒤로", + "next": "다음", + "finish": "완료" + }, + "steps": { + "fetch": { + "title": "모델 메타데이터 가져오기", + "content": "가져오기 버튼을 클릭하여 Civitai에서 모델 메타데이터와 미리보기 이미지를 다운로드하세요." + }, + "download": { + "title": "새 모델 다운로드", + "content": "다운로드 버튼을 사용하여 Civitai URL에서 모델을 직접 다운로드하세요." + }, + "bulk": { + "title": "일괄 작업", + "content": "이 버튼을 클릭하거나 B 키를 눌러 일괄 모드로 진입하세요. 여러 모델을 선택하여 일괄 작업을 수행할 수 있습니다. Ctrl+A로 모든 표시된 모델을 선택하세요." + }, + "searchOptions": { + "title": "검색 옵션", + "content": "이 버튼을 클릭하여 검색할 필드를 설정하세요: 파일명, 모델명, 태그, 제작자명 등. 검색 범위를 자유롭게 지정할 수 있습니다." + }, + "filter": { + "title": "모델 필터링", + "content": "필터를 사용하여 베이스 모델 유형(SD1.5, SDXL, Flux 등)이나 특정 태그로 모델을 좁혀보세요." + }, + "breadcrumb": { + "title": "경로(브레드크럼) 내비게이션", + "content": "브레드크럼 내비게이션은 현재 경로를 보여주며 폴더 간 빠른 이동을 지원합니다. 폴더 이름을 클릭하면 해당 위치로 바로 이동합니다." + }, + "modelCards": { + "title": "모델 카드", + "content": "한 번 클릭하면 모델 카드의 상세 정보와 메타데이터를 볼 수 있습니다. 편집 가능한 필드는 마우스를 올리면 연필 아이콘이 표시됩니다." + }, + "contextMenu": { + "title": "컨텍스트 메뉴", + "content": "오른쪽 클릭으로 모델 카드의 추가 작업 메뉴를 사용할 수 있습니다." + } + } + }, "modelCard": { "actions": { "addToFavorites": "즐겨찾기에 추가", diff --git a/locales/ru.json b/locales/ru.json index d3d9eb96..d3d13607 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -40,6 +40,53 @@ "tb": "ТБ" } }, + "onboarding": { + "languageSelection": { + "title": "Добро пожаловать в LoRA Manager", + "continue": "Продолжить", + "changeFailed": "Не удалось изменить язык: {message}" + }, + "tutorial": { + "skipTutorial": "Пропустить обучение", + "back": "Назад", + "next": "Далее", + "finish": "Завершить" + }, + "steps": { + "fetch": { + "title": "Получить метаданные моделей", + "content": "Нажмите кнопку Получить, чтобы загрузить метаданные моделей и изображения превью с Civitai." + }, + "download": { + "title": "Загрузить новые модели", + "content": "Используйте кнопку Загрузить для скачивания моделей напрямую по URL Civitai." + }, + "bulk": { + "title": "Массовые операции", + "content": "Войдите в массовый режим, нажав эту кнопку или клавишу B. Выберите несколько моделей и выполните пакетные операции. Используйте Ctrl+A для выбора всех видимых моделей." + }, + "searchOptions": { + "title": "Опции поиска", + "content": "Нажмите эту кнопку, чтобы настроить поля для поиска: имя файла, название модели, теги или имя автора. Настройте область поиска." + }, + "filter": { + "title": "Фильтрация моделей", + "content": "Используйте фильтры для сужения списка моделей по типу базовой модели (SD1.5, SDXL, Flux и др.) или по конкретным тегам." + }, + "breadcrumb": { + "title": "Навигация по папкам", + "content": "Навигация по папкам показывает ваш текущий путь и позволяет быстро переходить между папками. Нажмите на имя папки для перехода." + }, + "modelCards": { + "title": "Карточки моделей", + "content": "Одиночный клик по карточке модели откроет подробную информацию и позволит редактировать метаданные. Ищите значок карандаша при наведении на редактируемые поля." + }, + "contextMenu": { + "title": "Контекстное меню", + "content": "Правый клик по карточке модели откроет контекстное меню с дополнительными действиями." + } + } + }, "modelCard": { "actions": { "addToFavorites": "Добавить в избранное", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 28e2e551..c843ed9d 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -40,6 +40,53 @@ "tb": "TB" } }, + "onboarding": { + "languageSelection": { + "title": "欢迎使用 LoRA 管理器", + "continue": "继续", + "changeFailed": "切换语言失败:{message}" + }, + "tutorial": { + "skipTutorial": "跳过教程", + "back": "返回", + "next": "下一步", + "finish": "完成" + }, + "steps": { + "fetch": { + "title": "获取模型元数据", + "content": "点击 获取 按钮,从 Civitai 下载模型元数据和预览图片。" + }, + "download": { + "title": "下载新模型", + "content": "使用 下载 按钮,可直接通过 Civitai URL 下载模型。" + }, + "bulk": { + "title": "批量操作", + "content": "点击此按钮或按 B 进入批量模式。可多选模型并进行批量操作。使用 Ctrl+A 全选所有可见模型。" + }, + "searchOptions": { + "title": "搜索选项", + "content": "点击此按钮可配置搜索字段:文件名、模型名称、标签或创作者名称。自定义你的搜索范围。" + }, + "filter": { + "title": "筛选模型", + "content": "使用筛选器按基础模型类型(SD1.5、SDXL、Flux 等)或特定标签缩小模型范围。" + }, + "breadcrumb": { + "title": "路径导航", + "content": "路径导航显示你当前所在位置,并可快速在文件夹间跳转。点击任意文件夹名称即可直接跳转。" + }, + "modelCards": { + "title": "模型卡片", + "content": "单击模型卡片可查看详细信息并编辑元数据。悬停在可编辑字段时会显示铅笔图标。" + }, + "contextMenu": { + "title": "右键菜单", + "content": "右键点击任意模型卡片可打开更多操作菜单。" + } + } + }, "modelCard": { "actions": { "addToFavorites": "添加到收藏", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 4b2b564f..c04f39ae 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -40,6 +40,53 @@ "tb": "TB" } }, + "onboarding": { + "languageSelection": { + "title": "歡迎使用 LoRA 管理器", + "continue": "繼續", + "changeFailed": "切換語言失敗:{message}" + }, + "tutorial": { + "skipTutorial": "跳過教學", + "back": "返回", + "next": "下一步", + "finish": "完成" + }, + "steps": { + "fetch": { + "title": "取得模型 metadata", + "content": "點擊 取得 按鈕,從 Civitai 下載模型 metadata 與預覽圖片。" + }, + "download": { + "title": "下載新模型", + "content": "使用 下載 按鈕,直接從 Civitai 網址下載模型。" + }, + "bulk": { + "title": "批次操作", + "content": "點擊此按鈕或按下 B 進入批次模式。可選取多個模型並執行批量操作。使用 Ctrl+A 選取所有可見模型。" + }, + "searchOptions": { + "title": "搜尋選項", + "content": "點擊此按鈕可設定搜尋欄位:檔案名稱、模型名稱、標籤或創作者名稱。自訂搜尋範圍。" + }, + "filter": { + "title": "篩選模型", + "content": "使用篩選器依基礎模型類型(SD1.5、SDXL、Flux 等)或特定標籤縮小模型範圍。" + }, + "breadcrumb": { + "title": "麵包屑導覽", + "content": "麵包屑導覽顯示目前路徑,並可快速在資料夾間切換。點擊任一資料夾名稱即可直接跳轉。" + }, + "modelCards": { + "title": "模型卡片", + "content": "單擊模型卡片可檢視詳細資訊並編輯 metadata。滑鼠懸停於可編輯欄位時會顯示鉛筆圖示。" + }, + "contextMenu": { + "title": "右鍵選單", + "content": "右鍵點擊任一模型卡片可開啟更多操作選單。" + } + } + }, "modelCard": { "actions": { "addToFavorites": "加入收藏", diff --git a/static/css/onboarding.css b/static/css/onboarding.css new file mode 100644 index 00000000..88e1ece0 --- /dev/null +++ b/static/css/onboarding.css @@ -0,0 +1,252 @@ +/* Onboarding Tutorial Styles */ +.onboarding-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); + z-index: var(--z-overlay); + display: none; + /* Use mask to create cutout for highlighted element */ + mask-composite: subtract; + -webkit-mask-composite: subtract; +} + +.onboarding-overlay.active { + display: block; +} + +.onboarding-spotlight { + position: absolute; + background: transparent; + border: 3px solid var(--lora-accent); + border-radius: var(--border-radius-base); + z-index: calc(var(--z-overlay) + 1); + pointer-events: none; + transition: all 0.3s ease; + /* Add glow effect */ + box-shadow: + 0 0 0 2px rgba(24, 144, 255, 0.3), + 0 0 20px rgba(24, 144, 255, 0.2), + inset 0 0 0 1px rgba(255, 255, 255, 0.1); +} + +/* Target element highlighting */ +.onboarding-target-highlight { + position: relative; + z-index: calc(var(--z-overlay) + 2) !important; + pointer-events: auto !important; +} + +/* Ensure highlighted elements are interactive */ +.onboarding-target-highlight * { + pointer-events: auto !important; +} + +.onboarding-popup { + position: absolute; + background: var(--lora-surface); + border: 1px solid var(--lora-border); + border-radius: var(--border-radius-base); + padding: var(--space-3); + min-width: 320px; + max-width: 400px; + z-index: calc(var(--z-overlay) + 3); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + backdrop-filter: blur(10px); +} + +.onboarding-popup h3 { + margin: 0 0 var(--space-2) 0; + color: var(--lora-accent); + font-size: 1.2em; + font-weight: 600; +} + +.onboarding-popup p { + margin: 0 0 var(--space-3) 0; + color: var(--text-color); + line-height: 1.5; +} + +.onboarding-controls { + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--space-2); +} + +.onboarding-progress { + display: flex; + align-items: center; + gap: var(--space-1); + font-size: 0.85em; + color: var(--text-muted); +} + +.onboarding-actions { + display: flex; + gap: var(--space-2); +} + +.onboarding-btn { + padding: var(--space-1) var(--space-2); + border: 1px solid var(--lora-border); + border-radius: var(--border-radius-sm); + background: var(--card-bg); + color: var(--text-color); + cursor: pointer; + font-size: 0.9em; + transition: all 0.2s ease; +} + +.onboarding-btn:hover { + background: var(--lora-accent); + color: var(--lora-text); + border-color: var(--lora-accent); +} + +.onboarding-btn.primary { + background: var(--lora-accent); + color: var(--lora-text); + border-color: var(--lora-accent); +} + +.onboarding-btn.primary:hover { + opacity: 0.9; +} + +/* Language Selection Modal */ +.language-selection-modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.9); + display: flex; + align-items: center; + justify-content: center; + z-index: calc(var(--z-overlay) + 10); +} + +.language-selection-content { + background: var(--lora-surface); + border: 1px solid var(--lora-border); + border-radius: var(--border-radius-base); + padding: var(--space-3); + min-width: 510px; + text-align: center; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); + backdrop-filter: blur(10px); +} + +.language-selection-content h2 { + margin: 0 0 var(--space-2) 0; + color: var(--lora-accent); + font-size: 1.5em; +} + +.language-selection-content p { + margin: 0 0 var(--space-3) 0; + color: var(--text-color); + line-height: 1.5; +} + +.language-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--space-2); + margin-bottom: var(--space-3); +} + +.language-option { + padding: var(--space-2); + border: 2px solid var(--lora-border); + border-radius: var(--border-radius-sm); + background: var(--card-bg); + cursor: pointer; + transition: all 0.2s ease; + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-1); +} + +.language-option:hover { + border-color: var(--lora-accent); + background: var(--lora-surface); +} + +.language-option.selected { + border-color: var(--lora-accent); + background: var(--lora-accent); + color: var(--lora-text); +} + +.language-flag { + font-size: 1.5em; +} + +.language-name { + font-size: 0.9em; + font-weight: 500; +} + +.language-actions { + display: flex; + gap: var(--space-2); + justify-content: center; +} + +/* Shortcut Key Highlighting */ +.onboarding-shortcut { + display: inline-block; + background: var(--shortcut-bg); + border: 1px solid var(--shortcut-border); + border-radius: var(--border-radius-xs); + padding: 2px 6px; + font-size: 0.8em; + font-weight: 600; + color: var(--shortcut-text); + margin: 0 2px; +} + +/* Animation for highlighting elements */ +.onboarding-highlight { + animation: onboarding-pulse 2s infinite; +} + +@keyframes onboarding-pulse { + 0%, 100% { + box-shadow: + 0 0 0 2px rgba(24, 144, 255, 0.4), + 0 0 20px rgba(24, 144, 255, 0.3), + inset 0 0 0 1px rgba(255, 255, 255, 0.1); + } + 50% { + box-shadow: + 0 0 0 4px rgba(24, 144, 255, 0.6), + 0 0 30px rgba(24, 144, 255, 0.4), + inset 0 0 0 1px rgba(255, 255, 255, 0.2); + } +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .onboarding-popup { + min-width: 280px; + max-width: calc(100vw - 40px); + padding: var(--space-2); + } + + .language-grid { + grid-template-columns: repeat(2, 1fr); + } + + .language-selection-content { + min-width: calc(100vw - 40px); + max-width: 400px; + } +} diff --git a/static/js/components/SidebarManager.js b/static/js/components/SidebarManager.js index 637959ab..674fe825 100644 --- a/static/js/components/SidebarManager.js +++ b/static/js/components/SidebarManager.js @@ -173,7 +173,7 @@ export class SidebarManager { if (!sidebar || !hoverArea) return; // Get stored pin state - const isPinned = getStorageItem(`${this.pageType}_sidebarPinned`, false); + const isPinned = getStorageItem(`${this.pageType}_sidebarPinned`, true); this.isPinned = isPinned; // Sidebar starts hidden by default (CSS handles this) @@ -922,7 +922,7 @@ export class SidebarManager { } restoreSidebarState() { - const isPinned = getStorageItem(`${this.pageType}_sidebarPinned`, false); + const isPinned = getStorageItem(`${this.pageType}_sidebarPinned`, true); const expandedPaths = getStorageItem(`${this.pageType}_expandedNodes`, []); const displayMode = getStorageItem(`${this.pageType}_displayMode`, 'tree'); // 'tree' or 'list', default to 'tree' diff --git a/static/js/core.js b/static/js/core.js index eba47200..1a250aca 100644 --- a/static/js/core.js +++ b/static/js/core.js @@ -14,6 +14,7 @@ import { initTheme, initBackToTop } from './utils/uiHelpers.js'; import { initializeInfiniteScroll } from './utils/infiniteScroll.js'; import { migrateStorageItems } from './utils/storageHelpers.js'; import { i18n } from './i18n/index.js'; +import { onboardingManager } from './managers/OnboardingManager.js'; // Core application class export class AppCore { @@ -65,6 +66,14 @@ export class AppCore { // Mark as initialized this.initialized = true; + // Start onboarding if needed (after everything is initialized) + setTimeout(() => { + // Do not show onboarding if version-mismatch banner is visible + if (!bannerService.isBannerVisible('version-mismatch')) { + onboardingManager.start(); + } + }, 1000); // Small delay to ensure all elements are rendered + // Return the core instance for chaining return this; } diff --git a/static/js/managers/BannerService.js b/static/js/managers/BannerService.js index c2226fce..808bfb12 100644 --- a/static/js/managers/BannerService.js +++ b/static/js/managers/BannerService.js @@ -171,6 +171,16 @@ class BannerService { } } + /** + * Check if a banner is currently rendered and visible + * @param {string} bannerId + * @returns {boolean} + */ + isBannerVisible(bannerId) { + const el = document.querySelector(`[data-banner-id="${bannerId}"]`); + return !!el && el.offsetParent !== null; + } + /** * Update container visibility based on active banners */ diff --git a/static/js/managers/OnboardingManager.js b/static/js/managers/OnboardingManager.js new file mode 100644 index 00000000..ccbb971f --- /dev/null +++ b/static/js/managers/OnboardingManager.js @@ -0,0 +1,488 @@ +import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js'; +import { state } from '../state/index.js'; +import { translate } from '../utils/i18nHelpers.js'; +import { showToast } from '../utils/uiHelpers.js'; + +export class OnboardingManager { + constructor() { + this.isActive = false; + this.currentStep = 0; + this.selectedLanguage = 'en'; // Will be updated from state + this.overlay = null; + this.spotlight = null; + this.popup = null; + this.currentTarget = null; // Track current highlighted element + + // Available languages with SVG flags (using flag-icons) + this.languages = [ + { code: 'en', name: 'English', flag: 'us' }, + { code: 'zh-CN', name: '简体中文', flag: 'cn' }, + { code: 'zh-TW', name: '繁體中文', flag: 'hk' }, + { code: 'ja', name: '日本語', flag: 'jp' }, + { code: 'ko', name: '한국어', flag: 'kr' }, + { code: 'es', name: 'Español', flag: 'es' }, + { code: 'fr', name: 'Français', flag: 'fr' }, + { code: 'de', name: 'Deutsch', flag: 'de' }, + { code: 'ru', name: 'Русский', flag: 'ru' } + ]; + + // Tutorial steps configuration + this.steps = [ + { + target: '.controls .action-buttons [data-action="fetch"]', + title: () => translate('onboarding.steps.fetch.title', {}, 'Fetch Models Metadata'), + content: () => translate('onboarding.steps.fetch.content', {}, 'Click the Fetch button to download model metadata and preview images from Civitai.'), + position: 'bottom' + }, + { + target: '.controls .action-buttons [data-action="download"]', + title: () => translate('onboarding.steps.download.title', {}, 'Download New Models'), + content: () => translate('onboarding.steps.download.content', {}, 'Use the Download button to download models directly from Civitai URLs.'), + position: 'bottom' + }, + { + target: '.controls .action-buttons [data-action="bulk"]', + title: () => translate('onboarding.steps.bulk.title', {}, 'Bulk Operations'), + content: () => translate('onboarding.steps.bulk.content', {}, 'Enter bulk mode by clicking this button or pressing B. Select multiple models and perform batch operations. Use Ctrl+A to select all visible models.'), + position: 'bottom' + }, + { + target: '#searchOptionsToggle', + title: () => translate('onboarding.steps.searchOptions.title', {}, 'Search Options'), + content: () => translate('onboarding.steps.searchOptions.content', {}, 'Click this button to configure what fields to search in: filename, model name, tags, or creator name. Customize your search scope.'), + position: 'bottom' + }, + { + target: '#filterButton', + title: () => translate('onboarding.steps.filter.title', {}, 'Filter Models'), + content: () => translate('onboarding.steps.filter.content', {}, 'Use filters to narrow down models by base model type (SD1.5, SDXL, Flux, etc.) or by specific tags.'), + position: 'bottom' + }, + { + target: '#breadcrumbContainer', + title: () => translate('onboarding.steps.breadcrumb.title', {}, 'Breadcrumb Navigation'), + content: () => translate('onboarding.steps.breadcrumb.content', {}, 'The breadcrumb navigation shows your current path and allows quick navigation between folders. Click any folder name to jump directly there.'), + position: 'bottom' + }, + { + target: '.card-grid', + title: () => translate('onboarding.steps.modelCards.title', {}, 'Model Cards'), + content: () => translate('onboarding.steps.modelCards.content', {}, 'Single-click a model card to view detailed information and edit metadata. Look for the pencil icon when hovering over editable fields.'), + position: 'top', + customPosition: { top: '20%', left: '50%' } + }, + { + target: '.card-grid', + title: () => translate('onboarding.steps.contextMenu.title', {}, 'Context Menu'), + content: () => translate('onboarding.steps.contextMenu.content', {}, 'Right-click any model card for a context menu with additional actions.'), + position: 'top', + customPosition: { top: '20%', left: '50%' } + } + ]; + } + + // Check if user should see onboarding + shouldShowOnboarding() { + const completed = getStorageItem('onboarding_completed'); + const skipped = getStorageItem('onboarding_skipped'); + return !completed && !skipped; + } + + // Start the onboarding process + async start() { + if (!this.shouldShowOnboarding()) { + return; + } + + // If language has already been set, skip language selection + if (getStorageItem('onboarding_language_set')) { + this.startTutorial(); + return; + } + + // Show language selection first + await this.showLanguageSelection(); + } + + // Show language selection modal + showLanguageSelection() { + return new Promise((resolve) => { + // Initialize selected language from current settings + this.selectedLanguage = state.global.settings.language || 'en'; + + const modal = document.createElement('div'); + modal.className = 'language-selection-modal'; + modal.innerHTML = ` +
Choose Your Language / 选择语言 / 言語を選択
+${typeof step.content === 'function' ? step.content() : step.content}
+