mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
Refactor localization handling and improve i18n support across the application
- Replaced `safeTranslate` with `translate` in various components for consistent translation handling. - Updated Chinese (Simplified and Traditional) localization files to include new keys and improved translations for model card actions, metadata, and usage tips. - Enhanced the ModelCard, ModelDescription, ModelMetadata, ModelModal, and ModelTags components to utilize the new translation functions. - Improved user feedback messages for actions like copying to clipboard, saving notes, and updating tags with localized strings. - Ensured all UI elements reflect the correct translations based on the user's language preference.
This commit is contained in:
@@ -341,6 +341,66 @@
|
||||
"updated": "Modellbeschreibung aktualisiert",
|
||||
"updateFailed": "Fehler beim Aktualisieren der Modellbeschreibung"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"editModelName": "Modellname bearbeiten",
|
||||
"editFileName": "Dateiname bearbeiten",
|
||||
"editBaseModel": "Basismodell bearbeiten",
|
||||
"viewOnCivitai": "Auf Civitai anzeigen",
|
||||
"viewOnCivitaiText": "Auf Civitai anzeigen",
|
||||
"viewCreatorProfile": "Ersteller-Profil anzeigen"
|
||||
},
|
||||
"metadata": {
|
||||
"version": "Version",
|
||||
"fileName": "Dateiname",
|
||||
"location": "Standort",
|
||||
"baseModel": "Basismodell",
|
||||
"size": "Größe",
|
||||
"unknown": "Unbekannt",
|
||||
"usageTips": "Verwendungstipps",
|
||||
"additionalNotes": "Zusätzliche Notizen",
|
||||
"notesHint": "Enter zum Speichern, Shift+Enter für neue Zeile",
|
||||
"addNotesPlaceholder": "Fügen Sie hier Ihre Notizen hinzu...",
|
||||
"aboutThisVersion": "Über diese Version",
|
||||
"validation": {
|
||||
"nameTooLong": "Modellname ist auf 100 Zeichen begrenzt",
|
||||
"nameEmpty": "Modellname darf nicht leer sein"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "Modellname erfolgreich aktualisiert",
|
||||
"nameUpdateFailed": "Aktualisierung des Modellnamens fehlgeschlagen",
|
||||
"baseModelUpdated": "Basismodell erfolgreich aktualisiert",
|
||||
"baseModelUpdateFailed": "Aktualisierung des Basismodells fehlgeschlagen"
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"saved": "Notizen erfolgreich gespeichert",
|
||||
"saveFailed": "Speichern der Notizen fehlgeschlagen"
|
||||
},
|
||||
"usageTips": {
|
||||
"addPresetParameter": "Voreingestellten Parameter hinzufügen...",
|
||||
"strengthMin": "Stärke Min",
|
||||
"strengthMax": "Stärke Max",
|
||||
"strength": "Stärke",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Wert",
|
||||
"add": "Hinzufügen"
|
||||
},
|
||||
"tags": {
|
||||
"messages": {
|
||||
"updated": "Tags erfolgreich aktualisiert",
|
||||
"updateFailed": "Aktualisierung der Tags fehlgeschlagen"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "Tag sollte 30 Zeichen nicht überschreiten",
|
||||
"maxCount": "Maximal 30 Tags erlaubt",
|
||||
"duplicate": "Dieser Tag existiert bereits"
|
||||
}
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "Keine Rezepte gefunden, die diese LoRA verwenden.",
|
||||
"loadingRecipes": "Rezepte werden geladen...",
|
||||
"errorLoadingRecipes": "Fehler beim Laden der Rezepte. Bitte versuchen Sie es später erneut."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -463,5 +523,38 @@
|
||||
"filter": "Modelle nach verschiedenen Kriterien filtern",
|
||||
"sort": "Modelle nach verschiedenen Attributen sortieren",
|
||||
"backToTop": "Zurück zum Seitenanfang scrollen"
|
||||
},
|
||||
"modelCard": {
|
||||
"actions": {
|
||||
"addToFavorites": "Zu Favoriten hinzufügen",
|
||||
"removeFromFavorites": "Aus Favoriten entfernen",
|
||||
"viewOnCivitai": "Auf Civitai anzeigen",
|
||||
"notAvailableFromCivitai": "Nicht verfügbar auf Civitai",
|
||||
"sendToWorkflow": "An ComfyUI senden (Klick: Anhängen, Shift+Klick: Ersetzen)",
|
||||
"copyLoRASyntax": "LoRA-Syntax kopieren",
|
||||
"checkpointNameCopied": "Checkpoint-Name kopiert",
|
||||
"toggleBlur": "Unschärfe umschalten",
|
||||
"show": "Anzeigen",
|
||||
"openExampleImages": "Beispielbilder-Ordner öffnen"
|
||||
},
|
||||
"nsfw": {
|
||||
"matureContent": "Erwachseneninhalte",
|
||||
"xxxRated": "XXX-bewertete Inhalte",
|
||||
"xRated": "X-bewertete Inhalte",
|
||||
"rRated": "R-bewertete Inhalte"
|
||||
},
|
||||
"favorites": {
|
||||
"added": "Zu Favoriten hinzugefügt",
|
||||
"removed": "Aus Favoriten entfernt",
|
||||
"updateFailed": "Favoriten-Status aktualisierung fehlgeschlagen"
|
||||
},
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "Checkpoint an Workflow senden - Funktion noch zu implementieren"
|
||||
},
|
||||
"exampleImages": {
|
||||
"checkError": "Fehler beim Überprüfen der Beispielbilder",
|
||||
"missingHash": "Fehlende Modell-Hash-Informationen.",
|
||||
"noRemoteImagesAvailable": "Keine Remote-Beispielbilder für dieses Modell auf Civitai verfügbar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
161
locales/en.json
161
locales/en.json
@@ -73,48 +73,39 @@
|
||||
"korean": "한국어",
|
||||
"french": "Français",
|
||||
"spanish": "Español"
|
||||
}
|
||||
},
|
||||
"modelCard": {
|
||||
"actions": {
|
||||
"addToFavorites": "Add to favorites",
|
||||
"removeFromFavorites": "Remove from favorites",
|
||||
"viewOnCivitai": "View on Civitai",
|
||||
"notAvailableFromCivitai": "Not available from Civitai",
|
||||
"sendToWorkflow": "Send to ComfyUI (Click: Append, Shift+Click: Replace)",
|
||||
"copyLoRASyntax": "Copy LoRA Syntax",
|
||||
"checkpointNameCopied": "Checkpoint name copied",
|
||||
"toggleBlur": "Toggle blur",
|
||||
"show": "Show",
|
||||
"openExampleImages": "Open Example Images Folder"
|
||||
},
|
||||
"modelCard": {
|
||||
"favorites": {
|
||||
"added": "Added to favorites",
|
||||
"removed": "Removed from favorites",
|
||||
"updateFailed": "Failed to update favorite status"
|
||||
},
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "Send checkpoint to workflow - feature to be implemented"
|
||||
},
|
||||
"exampleImages": {
|
||||
"checkError": "Error checking for example images",
|
||||
"missingHash": "Missing model hash information."
|
||||
}
|
||||
"nsfw": {
|
||||
"matureContent": "Mature Content",
|
||||
"xxxRated": "XXX-rated Content",
|
||||
"xRated": "X-rated Content",
|
||||
"rRated": "R-rated Content"
|
||||
},
|
||||
"modelTags": {
|
||||
"messages": {
|
||||
"updated": "Tags updated successfully",
|
||||
"updateFailed": "Failed to update tags"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "Tag should not exceed 30 characters",
|
||||
"maxCount": "Maximum 30 tags allowed",
|
||||
"duplicate": "This tag already exists"
|
||||
}
|
||||
"favorites": {
|
||||
"added": "Added to favorites",
|
||||
"removed": "Removed from favorites",
|
||||
"updateFailed": "Failed to update favorite status"
|
||||
},
|
||||
"modelMetadata": {
|
||||
"validation": {
|
||||
"nameTooLong": "Model name is limited to 100 characters",
|
||||
"nameEmpty": "Model name cannot be empty"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "Model name updated successfully",
|
||||
"nameUpdateFailed": "Failed to update model name",
|
||||
"baseModelUpdated": "Base model updated successfully",
|
||||
"baseModelUpdateFailed": "Failed to update base model"
|
||||
}
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "Send checkpoint to workflow - feature to be implemented"
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "No recipes found that use this Lora.",
|
||||
"loadingRecipes": "Loading recipes...",
|
||||
"errorLoadingRecipes": "Failed to load recipes. Please try again later."
|
||||
"exampleImages": {
|
||||
"checkError": "Error checking for example images",
|
||||
"missingHash": "Missing model hash information.",
|
||||
"noRemoteImagesAvailable": "No remote example images available for this model on Civitai"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
@@ -487,6 +478,50 @@
|
||||
"confirmAction": "Confirm Re-link"
|
||||
},
|
||||
"model": {
|
||||
"actions": {
|
||||
"editModelName": "Edit model name",
|
||||
"editFileName": "Edit file name",
|
||||
"editBaseModel": "Edit base model",
|
||||
"viewOnCivitai": "View on Civitai",
|
||||
"viewOnCivitaiText": "View on Civitai",
|
||||
"viewCreatorProfile": "View Creator Profile"
|
||||
},
|
||||
"metadata": {
|
||||
"version": "Version",
|
||||
"fileName": "File Name",
|
||||
"location": "Location",
|
||||
"baseModel": "Base Model",
|
||||
"size": "Size",
|
||||
"unknown": "Unknown",
|
||||
"usageTips": "Usage Tips",
|
||||
"additionalNotes": "Additional Notes",
|
||||
"notesHint": "Press Enter to save, Shift+Enter for new line",
|
||||
"addNotesPlaceholder": "Add your notes here...",
|
||||
"aboutThisVersion": "About this version",
|
||||
"validation": {
|
||||
"nameTooLong": "Model name is limited to 100 characters",
|
||||
"nameEmpty": "Model name cannot be empty"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "Model name updated successfully",
|
||||
"nameUpdateFailed": "Failed to update model name",
|
||||
"baseModelUpdated": "Base model updated successfully",
|
||||
"baseModelUpdateFailed": "Failed to update base model"
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"saved": "Notes saved successfully",
|
||||
"saveFailed": "Failed to save notes"
|
||||
},
|
||||
"usageTips": {
|
||||
"addPresetParameter": "Add preset parameter...",
|
||||
"strengthMin": "Strength Min",
|
||||
"strengthMax": "Strength Max",
|
||||
"strength": "Strength",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Value",
|
||||
"add": "Add"
|
||||
},
|
||||
"description": {
|
||||
"noDescription": "No model description available",
|
||||
"failedToLoad": "Failed to load model description",
|
||||
@@ -509,6 +544,22 @@
|
||||
"description": "Loading model description...",
|
||||
"recipes": "Loading recipes...",
|
||||
"examples": "Loading examples..."
|
||||
},
|
||||
"tags": {
|
||||
"messages": {
|
||||
"updated": "Tags updated successfully",
|
||||
"updateFailed": "Failed to update tags"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "Tag should not exceed 30 characters",
|
||||
"maxCount": "Maximum 30 tags allowed",
|
||||
"duplicate": "This tag already exists"
|
||||
}
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "No recipes found that use this Lora.",
|
||||
"loadingRecipes": "Loading recipes...",
|
||||
"errorLoadingRecipes": "Failed to load recipes. Please try again later."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -622,6 +673,40 @@
|
||||
"keepOne": "Keep only one version (preferably with better metadata/previews) and safely delete the others."
|
||||
}
|
||||
},
|
||||
"uiHelpers": {
|
||||
"clipboard": {
|
||||
"copied": "Copied to clipboard",
|
||||
"copyFailed": "Copy failed"
|
||||
},
|
||||
"lora": {
|
||||
"syntaxCopied": "LoRA syntax copied to clipboard",
|
||||
"syntaxCopiedNoTriggerWords": "LoRA syntax copied to clipboard (no trigger words found)",
|
||||
"syntaxCopiedWithTriggerWords": "LoRA syntax with trigger words copied to clipboard",
|
||||
"syntaxCopiedWithTriggerWordGroups": "LoRA syntax with trigger word groups copied to clipboard"
|
||||
},
|
||||
"workflow": {
|
||||
"noSupportedNodes": "No supported target nodes found in workflow",
|
||||
"communicationFailed": "Failed to communicate with ComfyUI",
|
||||
"recipeReplaced": "Recipe replaced in workflow",
|
||||
"recipeAdded": "Recipe added to workflow",
|
||||
"loraReplaced": "LoRA replaced in workflow",
|
||||
"loraAdded": "LoRA added to workflow",
|
||||
"recipeFailedToSend": "Failed to send recipe to workflow",
|
||||
"loraFailedToSend": "Failed to send LoRA to workflow"
|
||||
},
|
||||
"nodeSelector": {
|
||||
"recipe": "Recipe",
|
||||
"lora": "LoRA",
|
||||
"replace": "Replace",
|
||||
"append": "Append",
|
||||
"selectTargetNode": "Select target node",
|
||||
"sendToAll": "Send to All"
|
||||
},
|
||||
"exampleImages": {
|
||||
"openingFolder": "Opening example images folder",
|
||||
"failedToOpen": "Failed to open example images folder"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"refresh": "Refresh the model list",
|
||||
"bulkOperations": "Select multiple models for batch operations",
|
||||
|
||||
@@ -341,6 +341,66 @@
|
||||
"updated": "Descripción del modelo actualizada",
|
||||
"updateFailed": "Error al actualizar la descripción del modelo"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"editModelName": "Editar nombre del modelo",
|
||||
"editFileName": "Editar nombre del archivo",
|
||||
"editBaseModel": "Editar modelo base",
|
||||
"viewOnCivitai": "Ver en Civitai",
|
||||
"viewOnCivitaiText": "Ver en Civitai",
|
||||
"viewCreatorProfile": "Ver perfil del creador"
|
||||
},
|
||||
"metadata": {
|
||||
"version": "Versión",
|
||||
"fileName": "Nombre del archivo",
|
||||
"location": "Ubicación",
|
||||
"baseModel": "Modelo base",
|
||||
"size": "Tamaño",
|
||||
"unknown": "Desconocido",
|
||||
"usageTips": "Consejos de uso",
|
||||
"additionalNotes": "Notas adicionales",
|
||||
"notesHint": "Presiona Enter para guardar, Shift+Enter para nueva línea",
|
||||
"addNotesPlaceholder": "Añade tus notas aquí...",
|
||||
"aboutThisVersion": "Sobre esta versión",
|
||||
"validation": {
|
||||
"nameTooLong": "El nombre del modelo está limitado a 100 caracteres",
|
||||
"nameEmpty": "El nombre del modelo no puede estar vacío"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "Nombre del modelo actualizado exitosamente",
|
||||
"nameUpdateFailed": "Error al actualizar el nombre del modelo",
|
||||
"baseModelUpdated": "Modelo base actualizado exitosamente",
|
||||
"baseModelUpdateFailed": "Error al actualizar el modelo base"
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"saved": "Notas guardadas exitosamente",
|
||||
"saveFailed": "Error al guardar las notas"
|
||||
},
|
||||
"usageTips": {
|
||||
"addPresetParameter": "Añadir parámetro preestablecido...",
|
||||
"strengthMin": "Fuerza Mín",
|
||||
"strengthMax": "Fuerza Máx",
|
||||
"strength": "Fuerza",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Valor",
|
||||
"add": "Añadir"
|
||||
},
|
||||
"tags": {
|
||||
"messages": {
|
||||
"updated": "Etiquetas actualizadas exitosamente",
|
||||
"updateFailed": "Error al actualizar las etiquetas"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "La etiqueta no debe exceder 30 caracteres",
|
||||
"maxCount": "Máximo 30 etiquetas permitidas",
|
||||
"duplicate": "Esta etiqueta ya existe"
|
||||
}
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "No se encontraron recetas que usen esta LoRA.",
|
||||
"loadingRecipes": "Cargando recetas...",
|
||||
"errorLoadingRecipes": "Error al cargar las recetas. Por favor intenta más tarde."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -463,5 +523,38 @@
|
||||
"filter": "Filtrar modelos por varios criterios",
|
||||
"sort": "Ordenar modelos por diferentes atributos",
|
||||
"backToTop": "Volver al inicio de la página"
|
||||
},
|
||||
"modelCard": {
|
||||
"actions": {
|
||||
"addToFavorites": "Añadir a favoritos",
|
||||
"removeFromFavorites": "Quitar de favoritos",
|
||||
"viewOnCivitai": "Ver en Civitai",
|
||||
"notAvailableFromCivitai": "No disponible en Civitai",
|
||||
"sendToWorkflow": "Enviar a ComfyUI (Clic: Adjuntar, Shift+Clic: Reemplazar)",
|
||||
"copyLoRASyntax": "Copiar sintaxis LoRA",
|
||||
"checkpointNameCopied": "Nombre del checkpoint copiado",
|
||||
"toggleBlur": "Alternar difuminado",
|
||||
"show": "Mostrar",
|
||||
"openExampleImages": "Abrir carpeta de imágenes de ejemplo"
|
||||
},
|
||||
"nsfw": {
|
||||
"matureContent": "Contenido para adultos",
|
||||
"xxxRated": "Contenido XXX",
|
||||
"xRated": "Contenido X",
|
||||
"rRated": "Contenido R"
|
||||
},
|
||||
"favorites": {
|
||||
"added": "Añadido a favoritos",
|
||||
"removed": "Eliminado de favoritos",
|
||||
"updateFailed": "Error al actualizar estado de favorito"
|
||||
},
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "Enviar checkpoint al flujo de trabajo - función por implementar"
|
||||
},
|
||||
"exampleImages": {
|
||||
"checkError": "Error al verificar imágenes de ejemplo",
|
||||
"missingHash": "Falta información de hash del modelo.",
|
||||
"noRemoteImagesAvailable": "No hay imágenes de ejemplo remotas disponibles para este modelo en Civitai"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,6 +341,66 @@
|
||||
"updated": "Description du modèle mise à jour",
|
||||
"updateFailed": "Échec de la mise à jour de la description du modèle"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"editModelName": "Modifier le nom du modèle",
|
||||
"editFileName": "Modifier le nom du fichier",
|
||||
"editBaseModel": "Modifier le modèle de base",
|
||||
"viewOnCivitai": "Voir sur Civitai",
|
||||
"viewOnCivitaiText": "Voir sur Civitai",
|
||||
"viewCreatorProfile": "Voir le profil du créateur"
|
||||
},
|
||||
"metadata": {
|
||||
"version": "Version",
|
||||
"fileName": "Nom du fichier",
|
||||
"location": "Emplacement",
|
||||
"baseModel": "Modèle de base",
|
||||
"size": "Taille",
|
||||
"unknown": "Inconnu",
|
||||
"usageTips": "Conseils d'utilisation",
|
||||
"additionalNotes": "Notes supplémentaires",
|
||||
"notesHint": "Appuyez sur Entrée pour sauvegarder, Shift+Entrée pour nouvelle ligne",
|
||||
"addNotesPlaceholder": "Ajoutez vos notes ici...",
|
||||
"aboutThisVersion": "À propos de cette version",
|
||||
"validation": {
|
||||
"nameTooLong": "Le nom du modèle est limité à 100 caractères",
|
||||
"nameEmpty": "Le nom du modèle ne peut pas être vide"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "Nom du modèle mis à jour avec succès",
|
||||
"nameUpdateFailed": "Échec de la mise à jour du nom du modèle",
|
||||
"baseModelUpdated": "Modèle de base mis à jour avec succès",
|
||||
"baseModelUpdateFailed": "Échec de la mise à jour du modèle de base"
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"saved": "Notes sauvegardées avec succès",
|
||||
"saveFailed": "Échec de la sauvegarde des notes"
|
||||
},
|
||||
"usageTips": {
|
||||
"addPresetParameter": "Ajouter un paramètre prédéfini...",
|
||||
"strengthMin": "Force Min",
|
||||
"strengthMax": "Force Max",
|
||||
"strength": "Force",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Valeur",
|
||||
"add": "Ajouter"
|
||||
},
|
||||
"tags": {
|
||||
"messages": {
|
||||
"updated": "Étiquettes mises à jour avec succès",
|
||||
"updateFailed": "Échec de la mise à jour des étiquettes"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "L'étiquette ne doit pas dépasser 30 caractères",
|
||||
"maxCount": "Maximum 30 étiquettes autorisées",
|
||||
"duplicate": "Cette étiquette existe déjà"
|
||||
}
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "Aucune recette trouvée utilisant cette LoRA.",
|
||||
"loadingRecipes": "Chargement des recettes...",
|
||||
"errorLoadingRecipes": "Échec du chargement des recettes. Veuillez réessayer plus tard."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -463,5 +523,38 @@
|
||||
"filter": "Filtrer les modèles selon divers critères",
|
||||
"sort": "Trier les modèles selon différents attributs",
|
||||
"backToTop": "Remonter en haut de la page"
|
||||
},
|
||||
"modelCard": {
|
||||
"actions": {
|
||||
"addToFavorites": "Ajouter aux favoris",
|
||||
"removeFromFavorites": "Retirer des favoris",
|
||||
"viewOnCivitai": "Voir sur Civitai",
|
||||
"notAvailableFromCivitai": "Non disponible sur Civitai",
|
||||
"sendToWorkflow": "Envoyer vers ComfyUI (Clic: Ajouter, Shift+Clic: Remplacer)",
|
||||
"copyLoRASyntax": "Copier la syntaxe LoRA",
|
||||
"checkpointNameCopied": "Nom du checkpoint copié",
|
||||
"toggleBlur": "Basculer le flou",
|
||||
"show": "Afficher",
|
||||
"openExampleImages": "Ouvrir le dossier d'images d'exemple"
|
||||
},
|
||||
"nsfw": {
|
||||
"matureContent": "Contenu pour adultes",
|
||||
"xxxRated": "Contenu XXX",
|
||||
"xRated": "Contenu X",
|
||||
"rRated": "Contenu R"
|
||||
},
|
||||
"favorites": {
|
||||
"added": "Ajouté aux favoris",
|
||||
"removed": "Retiré des favoris",
|
||||
"updateFailed": "Échec de la mise à jour du statut favori"
|
||||
},
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "Envoyer checkpoint vers workflow - fonctionnalité à implémenter"
|
||||
},
|
||||
"exampleImages": {
|
||||
"checkError": "Erreur lors de la vérification des images d'exemple",
|
||||
"missingHash": "Informations de hachage du modèle manquantes.",
|
||||
"noRemoteImagesAvailable": "Aucune image d'exemple distante disponible pour ce modèle sur Civitai"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,6 +341,66 @@
|
||||
"updated": "モデルの説明を更新しました",
|
||||
"updateFailed": "モデルの説明の更新に失敗しました"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"editModelName": "モデル名を編集",
|
||||
"editFileName": "ファイル名を編集",
|
||||
"editBaseModel": "ベースモデルを編集",
|
||||
"viewOnCivitai": "Civitaiで表示",
|
||||
"viewOnCivitaiText": "Civitaiで表示",
|
||||
"viewCreatorProfile": "クリエイタープロフィールを表示"
|
||||
},
|
||||
"metadata": {
|
||||
"version": "バージョン",
|
||||
"fileName": "ファイル名",
|
||||
"location": "場所",
|
||||
"baseModel": "ベースモデル",
|
||||
"size": "サイズ",
|
||||
"unknown": "不明",
|
||||
"usageTips": "使用のコツ",
|
||||
"additionalNotes": "追加メモ",
|
||||
"notesHint": "Enterで保存、Shift+Enterで改行",
|
||||
"addNotesPlaceholder": "ここにメモを追加...",
|
||||
"aboutThisVersion": "このバージョンについて",
|
||||
"validation": {
|
||||
"nameTooLong": "モデル名は100文字以内に制限されています",
|
||||
"nameEmpty": "モデル名を空にすることはできません"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "モデル名が正常に更新されました",
|
||||
"nameUpdateFailed": "モデル名の更新に失敗しました",
|
||||
"baseModelUpdated": "ベースモデルが正常に更新されました",
|
||||
"baseModelUpdateFailed": "ベースモデルの更新に失敗しました"
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"saved": "メモが正常に保存されました",
|
||||
"saveFailed": "メモの保存に失敗しました"
|
||||
},
|
||||
"usageTips": {
|
||||
"addPresetParameter": "プリセットパラメータを追加...",
|
||||
"strengthMin": "強度最小",
|
||||
"strengthMax": "強度最大",
|
||||
"strength": "強度",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "値",
|
||||
"add": "追加"
|
||||
},
|
||||
"tags": {
|
||||
"messages": {
|
||||
"updated": "タグが正常に更新されました",
|
||||
"updateFailed": "タグの更新に失敗しました"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "タグは30文字を超えてはいけません",
|
||||
"maxCount": "最大30個のタグが許可されています",
|
||||
"duplicate": "このタグは既に存在します"
|
||||
}
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "このLoRAを使用するレシピが見つかりません。",
|
||||
"loadingRecipes": "レシピを読み込み中...",
|
||||
"errorLoadingRecipes": "レシピの読み込みに失敗しました。後でもう一度お試しください。"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -463,5 +523,38 @@
|
||||
"filter": "様々な条件でモデルをフィルター",
|
||||
"sort": "異なる属性でモデルをソート",
|
||||
"backToTop": "ページトップにスクロール"
|
||||
},
|
||||
"modelCard": {
|
||||
"actions": {
|
||||
"addToFavorites": "お気に入りに追加",
|
||||
"removeFromFavorites": "お気に入りから削除",
|
||||
"viewOnCivitai": "Civitaiで表示",
|
||||
"notAvailableFromCivitai": "Civitaiで利用不可",
|
||||
"sendToWorkflow": "ComfyUIに送信(クリック:追加、Shift+クリック:置換)",
|
||||
"copyLoRASyntax": "LoRA構文をコピー",
|
||||
"checkpointNameCopied": "チェックポイント名をコピーしました",
|
||||
"toggleBlur": "ぼかしを切り替え",
|
||||
"show": "表示",
|
||||
"openExampleImages": "サンプル画像フォルダを開く"
|
||||
},
|
||||
"nsfw": {
|
||||
"matureContent": "成人向けコンテンツ",
|
||||
"xxxRated": "XXX指定コンテンツ",
|
||||
"xRated": "X指定コンテンツ",
|
||||
"rRated": "R指定コンテンツ"
|
||||
},
|
||||
"favorites": {
|
||||
"added": "お気に入りに追加しました",
|
||||
"removed": "お気に入りから削除しました",
|
||||
"updateFailed": "お気に入り状態の更新に失敗しました"
|
||||
},
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "チェックポイントをワークフローに送信 - 機能実装予定"
|
||||
},
|
||||
"exampleImages": {
|
||||
"checkError": "サンプル画像の確認でエラーが発生しました",
|
||||
"missingHash": "モデルハッシュ情報が不足しています。",
|
||||
"noRemoteImagesAvailable": "このモデルのリモートサンプル画像はCivitaiで利用できません"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,6 +341,66 @@
|
||||
"updated": "모델 설명이 업데이트되었습니다",
|
||||
"updateFailed": "모델 설명 업데이트에 실패했습니다"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"editModelName": "모델명 편집",
|
||||
"editFileName": "파일명 편집",
|
||||
"editBaseModel": "베이스 모델 편집",
|
||||
"viewOnCivitai": "Civitai에서 보기",
|
||||
"viewOnCivitaiText": "Civitai에서 보기",
|
||||
"viewCreatorProfile": "제작자 프로필 보기"
|
||||
},
|
||||
"metadata": {
|
||||
"version": "버전",
|
||||
"fileName": "파일명",
|
||||
"location": "위치",
|
||||
"baseModel": "베이스 모델",
|
||||
"size": "크기",
|
||||
"unknown": "알 수 없음",
|
||||
"usageTips": "사용 팁",
|
||||
"additionalNotes": "추가 메모",
|
||||
"notesHint": "Enter로 저장, Shift+Enter로 줄바꿈",
|
||||
"addNotesPlaceholder": "여기에 메모를 추가하세요...",
|
||||
"aboutThisVersion": "이 버전에 대해",
|
||||
"validation": {
|
||||
"nameTooLong": "모델명은 100자로 제한됩니다",
|
||||
"nameEmpty": "모델명은 비워둘 수 없습니다"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "모델명이 성공적으로 업데이트됨",
|
||||
"nameUpdateFailed": "모델명 업데이트 실패",
|
||||
"baseModelUpdated": "베이스 모델이 성공적으로 업데이트됨",
|
||||
"baseModelUpdateFailed": "베이스 모델 업데이트 실패"
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"saved": "메모가 성공적으로 저장됨",
|
||||
"saveFailed": "메모 저장 실패"
|
||||
},
|
||||
"usageTips": {
|
||||
"addPresetParameter": "프리셋 매개변수 추가...",
|
||||
"strengthMin": "강도 최소",
|
||||
"strengthMax": "강도 최대",
|
||||
"strength": "강도",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "값",
|
||||
"add": "추가"
|
||||
},
|
||||
"tags": {
|
||||
"messages": {
|
||||
"updated": "태그가 성공적으로 업데이트됨",
|
||||
"updateFailed": "태그 업데이트 실패"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "태그는 30자를 초과할 수 없습니다",
|
||||
"maxCount": "최대 30개의 태그가 허용됩니다",
|
||||
"duplicate": "이 태그는 이미 존재합니다"
|
||||
}
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "이 LoRA를 사용하는 레시피를 찾을 수 없습니다.",
|
||||
"loadingRecipes": "레시피 로딩 중...",
|
||||
"errorLoadingRecipes": "레시피 로딩에 실패했습니다. 나중에 다시 시도해주세요."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -463,5 +523,38 @@
|
||||
"filter": "다양한 기준으로 모델 필터링",
|
||||
"sort": "다양한 속성으로 모델 정렬",
|
||||
"backToTop": "페이지 맨 위로 스크롤"
|
||||
},
|
||||
"modelCard": {
|
||||
"actions": {
|
||||
"addToFavorites": "즐겨찾기에 추가",
|
||||
"removeFromFavorites": "즐겨찾기에서 제거",
|
||||
"viewOnCivitai": "Civitai에서 보기",
|
||||
"notAvailableFromCivitai": "Civitai에서 사용할 수 없음",
|
||||
"sendToWorkflow": "ComfyUI로 전송 (클릭: 추가, Shift+클릭: 교체)",
|
||||
"copyLoRASyntax": "LoRA 문법 복사",
|
||||
"checkpointNameCopied": "체크포인트 이름이 복사됨",
|
||||
"toggleBlur": "흐림 효과 전환",
|
||||
"show": "표시",
|
||||
"openExampleImages": "예제 이미지 폴더 열기"
|
||||
},
|
||||
"nsfw": {
|
||||
"matureContent": "성인 콘텐츠",
|
||||
"xxxRated": "XXX 등급 콘텐츠",
|
||||
"xRated": "X 등급 콘텐츠",
|
||||
"rRated": "R 등급 콘텐츠"
|
||||
},
|
||||
"favorites": {
|
||||
"added": "즐겨찾기에 추가됨",
|
||||
"removed": "즐겨찾기에서 제거됨",
|
||||
"updateFailed": "즐겨찾기 상태 업데이트 실패"
|
||||
},
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "워크플로우로 체크포인트 전송 - 기능 구현 예정"
|
||||
},
|
||||
"exampleImages": {
|
||||
"checkError": "예제 이미지 확인 중 오류 발생",
|
||||
"missingHash": "모델 해시 정보 누락.",
|
||||
"noRemoteImagesAvailable": "이 모델에 대한 원격 예제 이미지가 Civitai에서 사용할 수 없습니다"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,6 +341,66 @@
|
||||
"updated": "Описание модели обновлено",
|
||||
"updateFailed": "Не удалось обновить описание модели"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"editModelName": "Редактировать имя модели",
|
||||
"editFileName": "Редактировать имя файла",
|
||||
"editBaseModel": "Редактировать базовую модель",
|
||||
"viewOnCivitai": "Посмотреть на Civitai",
|
||||
"viewOnCivitaiText": "Посмотреть на Civitai",
|
||||
"viewCreatorProfile": "Посмотреть профиль создателя"
|
||||
},
|
||||
"metadata": {
|
||||
"version": "Версия",
|
||||
"fileName": "Имя файла",
|
||||
"location": "Расположение",
|
||||
"baseModel": "Базовая модель",
|
||||
"size": "Размер",
|
||||
"unknown": "Неизвестно",
|
||||
"usageTips": "Советы по использованию",
|
||||
"additionalNotes": "Дополнительные заметки",
|
||||
"notesHint": "Enter для сохранения, Shift+Enter для новой строки",
|
||||
"addNotesPlaceholder": "Добавьте свои заметки здесь...",
|
||||
"aboutThisVersion": "О данной версии",
|
||||
"validation": {
|
||||
"nameTooLong": "Имя модели ограничено 100 символами",
|
||||
"nameEmpty": "Имя модели не может быть пустым"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "Имя модели успешно обновлено",
|
||||
"nameUpdateFailed": "Не удалось обновить имя модели",
|
||||
"baseModelUpdated": "Базовая модель успешно обновлена",
|
||||
"baseModelUpdateFailed": "Не удалось обновить базовую модель"
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"saved": "Заметки успешно сохранены",
|
||||
"saveFailed": "Не удалось сохранить заметки"
|
||||
},
|
||||
"usageTips": {
|
||||
"addPresetParameter": "Добавить предустановленный параметр...",
|
||||
"strengthMin": "Мин. сила",
|
||||
"strengthMax": "Макс. сила",
|
||||
"strength": "Сила",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "Значение",
|
||||
"add": "Добавить"
|
||||
},
|
||||
"tags": {
|
||||
"messages": {
|
||||
"updated": "Теги успешно обновлены",
|
||||
"updateFailed": "Не удалось обновить теги"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "Тег не должен превышать 30 символов",
|
||||
"maxCount": "Разрешено максимум 30 тегов",
|
||||
"duplicate": "Этот тег уже существует"
|
||||
}
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "Не найдено рецептов, использующих эту LoRA.",
|
||||
"loadingRecipes": "Загрузка рецептов...",
|
||||
"errorLoadingRecipes": "Не удалось загрузить рецепты. Пожалуйста, попробуйте позже."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -463,5 +523,38 @@
|
||||
"filter": "Фильтровать модели по различным критериям",
|
||||
"sort": "Сортировать модели по разным атрибутам",
|
||||
"backToTop": "Прокрутить обратно наверх страницы"
|
||||
},
|
||||
"modelCard": {
|
||||
"actions": {
|
||||
"addToFavorites": "Добавить в избранное",
|
||||
"removeFromFavorites": "Удалить из избранного",
|
||||
"viewOnCivitai": "Посмотреть на Civitai",
|
||||
"notAvailableFromCivitai": "Недоступно на Civitai",
|
||||
"sendToWorkflow": "Отправить в ComfyUI (Клик: Добавить, Shift+Клик: Заменить)",
|
||||
"copyLoRASyntax": "Копировать синтаксис LoRA",
|
||||
"checkpointNameCopied": "Имя чекпоинта скопировано",
|
||||
"toggleBlur": "Переключить размытие",
|
||||
"show": "Показать",
|
||||
"openExampleImages": "Открыть папку с примерами изображений"
|
||||
},
|
||||
"nsfw": {
|
||||
"matureContent": "Контент для взрослых",
|
||||
"xxxRated": "XXX-контент",
|
||||
"xRated": "X-контент",
|
||||
"rRated": "R-контент"
|
||||
},
|
||||
"favorites": {
|
||||
"added": "Добавлено в избранное",
|
||||
"removed": "Удалено из избранного",
|
||||
"updateFailed": "Не удалось обновить статус избранного"
|
||||
},
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "Отправка чекпоинта в рабочий процесс - функция в разработке"
|
||||
},
|
||||
"exampleImages": {
|
||||
"checkError": "Ошибка при проверке примеров изображений",
|
||||
"missingHash": "Отсутствует информация о хэше модели.",
|
||||
"noRemoteImagesAvailable": "Для этой модели нет удалённых примеров изображений на Civitai"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,48 +73,39 @@
|
||||
"korean": "한국어",
|
||||
"french": "Français",
|
||||
"spanish": "Español"
|
||||
}
|
||||
},
|
||||
"modelCard": {
|
||||
"actions": {
|
||||
"addToFavorites": "添加到收藏",
|
||||
"removeFromFavorites": "从收藏中移除",
|
||||
"viewOnCivitai": "在 Civitai 上查看",
|
||||
"notAvailableFromCivitai": "Civitai 上不可用",
|
||||
"sendToWorkflow": "发送到 ComfyUI(点击:追加,Shift+点击:替换)",
|
||||
"copyLoRASyntax": "复制 LoRA 语法",
|
||||
"checkpointNameCopied": "Checkpoint 名称已复制",
|
||||
"toggleBlur": "切换模糊",
|
||||
"show": "显示",
|
||||
"openExampleImages": "打开示例图片文件夹"
|
||||
},
|
||||
"modelCard": {
|
||||
"favorites": {
|
||||
"added": "已添加到收藏",
|
||||
"removed": "已从收藏中移除",
|
||||
"updateFailed": "更新收藏状态失败"
|
||||
},
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "发送 Checkpoint 到工作流 - 功能待实现"
|
||||
},
|
||||
"exampleImages": {
|
||||
"checkError": "检查示例图片时出错",
|
||||
"missingHash": "缺少模型哈希信息。"
|
||||
}
|
||||
"nsfw": {
|
||||
"matureContent": "成人内容",
|
||||
"xxxRated": "XXX 级内容",
|
||||
"xRated": "X 级内容",
|
||||
"rRated": "R 级内容"
|
||||
},
|
||||
"modelTags": {
|
||||
"messages": {
|
||||
"updated": "标签更新成功",
|
||||
"updateFailed": "更新标签失败"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "标签长度不能超过30个字符",
|
||||
"maxCount": "最多允许30个标签",
|
||||
"duplicate": "该标签已存在"
|
||||
}
|
||||
"favorites": {
|
||||
"added": "已添加到收藏",
|
||||
"removed": "已从收藏中移除",
|
||||
"updateFailed": "更新收藏状态失败"
|
||||
},
|
||||
"modelMetadata": {
|
||||
"validation": {
|
||||
"nameTooLong": "模型名称最多100个字符",
|
||||
"nameEmpty": "模型名称不能为空"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "模型名称更新成功",
|
||||
"nameUpdateFailed": "更新模型名称失败",
|
||||
"baseModelUpdated": "基础模型更新成功",
|
||||
"baseModelUpdateFailed": "更新基础模型失败"
|
||||
}
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "发送 Checkpoint 到工作流 - 功能待实现"
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "未找到使用此 LoRA 的配方。",
|
||||
"loadingRecipes": "正在加载配方...",
|
||||
"errorLoadingRecipes": "加载配方失败。请稍后重试。"
|
||||
"exampleImages": {
|
||||
"checkError": "检查示例图片时出错",
|
||||
"missingHash": "缺少模型哈希信息。",
|
||||
"noRemoteImagesAvailable": "该模型在 Civitai 上没有可用的远程示例图片"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
@@ -487,6 +478,50 @@
|
||||
"confirmAction": "确认重新链接"
|
||||
},
|
||||
"model": {
|
||||
"actions": {
|
||||
"editModelName": "编辑模型名称",
|
||||
"editFileName": "编辑文件名",
|
||||
"editBaseModel": "编辑基础模型",
|
||||
"viewOnCivitai": "在 Civitai 上查看",
|
||||
"viewOnCivitaiText": "在 Civitai 上查看",
|
||||
"viewCreatorProfile": "查看创作者资料"
|
||||
},
|
||||
"metadata": {
|
||||
"version": "版本",
|
||||
"fileName": "文件名",
|
||||
"location": "位置",
|
||||
"baseModel": "基础模型",
|
||||
"size": "大小",
|
||||
"unknown": "未知",
|
||||
"usageTips": "使用技巧",
|
||||
"additionalNotes": "附加说明",
|
||||
"notesHint": "按 Enter 保存,Shift+Enter 换行",
|
||||
"addNotesPlaceholder": "在此添加您的说明...",
|
||||
"aboutThisVersion": "关于此版本",
|
||||
"validation": {
|
||||
"nameTooLong": "模型名称最多100个字符",
|
||||
"nameEmpty": "模型名称不能为空"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "模型名称更新成功",
|
||||
"nameUpdateFailed": "更新模型名称失败",
|
||||
"baseModelUpdated": "基础模型更新成功",
|
||||
"baseModelUpdateFailed": "更新基础模型失败"
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"saved": "说明保存成功",
|
||||
"saveFailed": "保存说明失败"
|
||||
},
|
||||
"usageTips": {
|
||||
"addPresetParameter": "添加预设参数...",
|
||||
"strengthMin": "强度最小值",
|
||||
"strengthMax": "强度最大值",
|
||||
"strength": "强度",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "值",
|
||||
"add": "添加"
|
||||
},
|
||||
"description": {
|
||||
"noDescription": "无模型描述信息",
|
||||
"failedToLoad": "加载模型描述失败",
|
||||
@@ -509,6 +544,22 @@
|
||||
"description": "正在加载模型描述...",
|
||||
"recipes": "正在加载配方...",
|
||||
"examples": "正在加载示例..."
|
||||
},
|
||||
"tags": {
|
||||
"messages": {
|
||||
"updated": "标签更新成功",
|
||||
"updateFailed": "更新标签失败"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "标签长度不能超过30个字符",
|
||||
"maxCount": "最多允许30个标签",
|
||||
"duplicate": "该标签已存在"
|
||||
}
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "未找到使用此 LoRA 的配方。",
|
||||
"loadingRecipes": "正在加载配方...",
|
||||
"errorLoadingRecipes": "加载配方失败。请稍后重试。"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -74,6 +74,39 @@
|
||||
"spanish": "Español"
|
||||
}
|
||||
},
|
||||
"modelCard": {
|
||||
"actions": {
|
||||
"addToFavorites": "新增到收藏",
|
||||
"removeFromFavorites": "從收藏中移除",
|
||||
"viewOnCivitai": "在 Civitai 上檢視",
|
||||
"notAvailableFromCivitai": "Civitai 上不可用",
|
||||
"sendToWorkflow": "傳送到 ComfyUI(點擊:附加,Shift+點擊:取代)",
|
||||
"copyLoRASyntax": "複製 LoRA 語法",
|
||||
"checkpointNameCopied": "Checkpoint 名稱已複製",
|
||||
"toggleBlur": "切換模糊",
|
||||
"show": "顯示",
|
||||
"openExampleImages": "開啟範例圖片資料夾"
|
||||
},
|
||||
"nsfw": {
|
||||
"matureContent": "成人內容",
|
||||
"xxxRated": "XXX 級內容",
|
||||
"xRated": "X 級內容",
|
||||
"rRated": "R 級內容"
|
||||
},
|
||||
"favorites": {
|
||||
"added": "已新增到收藏",
|
||||
"removed": "已從收藏中移除",
|
||||
"updateFailed": "更新收藏狀態失敗"
|
||||
},
|
||||
"sendToWorkflow": {
|
||||
"checkpointNotImplemented": "傳送 Checkpoint 到工作流程 - 功能待實作"
|
||||
},
|
||||
"exampleImages": {
|
||||
"checkError": "檢查範例圖片時出錯",
|
||||
"missingHash": "缺少模型雜湊資訊。",
|
||||
"noRemoteImagesAvailable": "該模型在 Civitai 上沒有可用的遠端範例圖片"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"appTitle": "LoRA 管理器",
|
||||
"navigation": {
|
||||
@@ -341,6 +374,77 @@
|
||||
"updated": "模型描述已更新",
|
||||
"updateFailed": "更新模型描述失敗"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"editModelName": "編輯模型名稱",
|
||||
"editFileName": "編輯檔案名稱",
|
||||
"editBaseModel": "編輯基礎模型",
|
||||
"viewOnCivitai": "在 Civitai 上檢視",
|
||||
"viewOnCivitaiText": "在 Civitai 上檢視",
|
||||
"viewCreatorProfile": "檢視創作者資料"
|
||||
},
|
||||
"metadata": {
|
||||
"version": "版本",
|
||||
"fileName": "檔案名稱",
|
||||
"location": "位置",
|
||||
"baseModel": "基礎模型",
|
||||
"size": "大小",
|
||||
"unknown": "未知",
|
||||
"usageTips": "使用技巧",
|
||||
"additionalNotes": "附加說明",
|
||||
"notesHint": "按 Enter 儲存,Shift+Enter 換行",
|
||||
"addNotesPlaceholder": "在此新增您的說明...",
|
||||
"aboutThisVersion": "關於此版本",
|
||||
"validation": {
|
||||
"nameTooLong": "模型名稱最多100個字元",
|
||||
"nameEmpty": "模型名稱不能為空"
|
||||
},
|
||||
"messages": {
|
||||
"nameUpdated": "模型名稱更新成功",
|
||||
"nameUpdateFailed": "更新模型名稱失敗",
|
||||
"baseModelUpdated": "基礎模型更新成功",
|
||||
"baseModelUpdateFailed": "更新基礎模型失敗"
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"saved": "說明儲存成功",
|
||||
"saveFailed": "儲存說明失敗"
|
||||
},
|
||||
"usageTips": {
|
||||
"addPresetParameter": "新增預設參數...",
|
||||
"strengthMin": "強度最小值",
|
||||
"strengthMax": "強度最大值",
|
||||
"strength": "強度",
|
||||
"clipSkip": "Clip Skip",
|
||||
"valuePlaceholder": "值",
|
||||
"add": "新增"
|
||||
},
|
||||
"tabs": {
|
||||
"examples": "範例圖片",
|
||||
"description": "模型描述",
|
||||
"recipes": "配方"
|
||||
},
|
||||
"loading": {
|
||||
"exampleImages": "正在載入範例圖片...",
|
||||
"description": "正在載入模型描述...",
|
||||
"recipes": "正在載入配方...",
|
||||
"examples": "正在載入範例..."
|
||||
},
|
||||
"tags": {
|
||||
"messages": {
|
||||
"updated": "標籤更新成功",
|
||||
"updateFailed": "更新標籤失敗"
|
||||
},
|
||||
"validation": {
|
||||
"maxLength": "標籤長度不能超過30個字元",
|
||||
"maxCount": "最多允許30個標籤",
|
||||
"duplicate": "該標籤已存在"
|
||||
}
|
||||
},
|
||||
"recipeTab": {
|
||||
"noRecipesFound": "未找到使用此 LoRA 的配方。",
|
||||
"loadingRecipes": "正在載入配方...",
|
||||
"errorLoadingRecipes": "載入配方失敗。請稍後重試。"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -464,4 +568,4 @@
|
||||
"sort": "按不同屬性排序模型",
|
||||
"backToTop": "捲動回頁面頂部"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { NSFW_LEVELS } from '../../utils/constants.js';
|
||||
import { MODEL_TYPES } from '../../api/apiConfig.js';
|
||||
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||
import { showDeleteModal } from '../../utils/modalUtils.js';
|
||||
import { safeTranslate } from '../../utils/i18nHelpers.js';
|
||||
import { translate } from '../../utils/i18nHelpers.js';
|
||||
|
||||
// Add global event delegation handlers
|
||||
export function setupModelCardEventDelegation(modelType) {
|
||||
@@ -143,15 +143,15 @@ async function toggleFavorite(card) {
|
||||
});
|
||||
|
||||
if (newFavoriteState) {
|
||||
const addedText = safeTranslate('modelCard.favorites.added', {}, 'Added to favorites');
|
||||
const addedText = translate('modelCard.favorites.added', {}, 'Added to favorites');
|
||||
showToast(addedText, 'success');
|
||||
} else {
|
||||
const removedText = safeTranslate('modelCard.favorites.removed', {}, 'Removed from favorites');
|
||||
const removedText = translate('modelCard.favorites.removed', {}, 'Removed from favorites');
|
||||
showToast(removedText, 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update favorite status:', error);
|
||||
const errorText = safeTranslate('modelCard.favorites.updateFailed', {}, 'Failed to update favorite status');
|
||||
const errorText = translate('modelCard.favorites.updateFailed', {}, 'Failed to update favorite status');
|
||||
showToast(errorText, 'error');
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ function handleSendToWorkflow(card, replaceMode, modelType) {
|
||||
sendLoraToWorkflow(loraSyntax, replaceMode, 'lora');
|
||||
} else {
|
||||
// Checkpoint send functionality - to be implemented
|
||||
const text = safeTranslate('modelCard.sendToWorkflow.checkpointNotImplemented', {}, 'Send checkpoint to workflow - feature to be implemented');
|
||||
const text = translate('modelCard.sendToWorkflow.checkpointNotImplemented', {}, 'Send checkpoint to workflow - feature to be implemented');
|
||||
showToast(text, 'info');
|
||||
}
|
||||
}
|
||||
@@ -175,7 +175,8 @@ function handleCopyAction(card, modelType) {
|
||||
} else if (modelType === MODEL_TYPES.CHECKPOINT) {
|
||||
// Checkpoint copy functionality - copy checkpoint name
|
||||
const checkpointName = card.dataset.file_name;
|
||||
copyToClipboard(checkpointName, 'Checkpoint name copied');
|
||||
const message = translate('modelCard.actions.checkpointNameCopied', {}, 'Checkpoint name copied');
|
||||
copyToClipboard(checkpointName, message);
|
||||
} else if (modelType === MODEL_TYPES.EMBEDDING) {
|
||||
const embeddingName = card.dataset.file_name;
|
||||
copyToClipboard(embeddingName, 'Embedding name copied');
|
||||
@@ -200,7 +201,7 @@ async function handleExampleImagesAccess(card, modelType) {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking for example images:', error);
|
||||
const text = safeTranslate('modelCard.exampleImages.checkError', {}, 'Error checking for example images');
|
||||
const text = translate('modelCard.exampleImages.checkError', {}, 'Error checking for example images');
|
||||
showToast(text, 'error');
|
||||
}
|
||||
}
|
||||
@@ -283,7 +284,7 @@ function showExampleAccessModal(card, modelType) {
|
||||
// Get the model hash
|
||||
const modelHash = card.dataset.sha256;
|
||||
if (!modelHash) {
|
||||
const text = safeTranslate('modelCard.exampleImages.missingHash', {}, 'Missing model hash information.');
|
||||
const text = translate('modelCard.exampleImages.missingHash', {}, 'Missing model hash information.');
|
||||
showToast(text, 'error');
|
||||
return;
|
||||
}
|
||||
@@ -305,7 +306,8 @@ function showExampleAccessModal(card, modelType) {
|
||||
};
|
||||
} else {
|
||||
downloadBtn.classList.add('disabled');
|
||||
downloadBtn.setAttribute('title', 'No remote example images available for this model on Civitai');
|
||||
const noRemoteImagesTitle = translate('modelCard.exampleImages.noRemoteImagesAvailable', {}, 'No remote example images available for this model on Civitai');
|
||||
downloadBtn.setAttribute('title', noRemoteImagesTitle);
|
||||
downloadBtn.onclick = null;
|
||||
}
|
||||
}
|
||||
@@ -436,14 +438,14 @@ export function createModelCard(model, modelType) {
|
||||
const previewUrl = model.preview_url || '/loras_static/images/no-preview.png';
|
||||
const versionedPreviewUrl = version ? `${previewUrl}?t=${version}` : previewUrl;
|
||||
|
||||
// Determine NSFW warning text based on level
|
||||
let nsfwText = "Mature Content";
|
||||
// Determine NSFW warning text based on level with i18n support
|
||||
let nsfwText = translate('modelCard.nsfw.matureContent', {}, 'Mature Content');
|
||||
if (nsfwLevel >= NSFW_LEVELS.XXX) {
|
||||
nsfwText = "XXX-rated Content";
|
||||
nsfwText = translate('modelCard.nsfw.xxxRated', {}, 'XXX-rated Content');
|
||||
} else if (nsfwLevel >= NSFW_LEVELS.X) {
|
||||
nsfwText = "X-rated Content";
|
||||
nsfwText = translate('modelCard.nsfw.xRated', {}, 'X-rated Content');
|
||||
} else if (nsfwLevel >= NSFW_LEVELS.R) {
|
||||
nsfwText = "R-rated Content";
|
||||
nsfwText = translate('modelCard.nsfw.rRated', {}, 'R-rated Content');
|
||||
}
|
||||
|
||||
// Check if autoplayOnHover is enabled for video previews
|
||||
@@ -454,22 +456,36 @@ export function createModelCard(model, modelType) {
|
||||
// Get favorite status from model data
|
||||
const isFavorite = model.favorite === true;
|
||||
|
||||
// Generate action icons based on model type
|
||||
// Generate action icons based on model type with i18n support
|
||||
const favoriteTitle = isFavorite ?
|
||||
translate('modelCard.actions.removeFromFavorites', {}, 'Remove from favorites') :
|
||||
translate('modelCard.actions.addToFavorites', {}, 'Add to favorites');
|
||||
const globeTitle = model.from_civitai ?
|
||||
translate('modelCard.actions.viewOnCivitai', {}, 'View on Civitai') :
|
||||
translate('modelCard.actions.notAvailableFromCivitai', {}, 'Not available from Civitai');
|
||||
const sendTitle = translate('modelCard.actions.sendToWorkflow', {}, 'Send to ComfyUI (Click: Append, Shift+Click: Replace)');
|
||||
const copyTitle = translate('modelCard.actions.copyLoRASyntax', {}, 'Copy LoRA Syntax');
|
||||
|
||||
const actionIcons = `
|
||||
<i class="${isFavorite ? 'fas fa-star favorite-active' : 'far fa-star'}"
|
||||
title="${isFavorite ? 'Remove from favorites' : 'Add to favorites'}">
|
||||
title="${favoriteTitle}">
|
||||
</i>
|
||||
<i class="fas fa-globe"
|
||||
title="${model.from_civitai ? 'View on Civitai' : 'Not available from Civitai'}"
|
||||
title="${globeTitle}"
|
||||
${!model.from_civitai ? 'style="opacity: 0.5; cursor: not-allowed"' : ''}>
|
||||
</i>
|
||||
<i class="fas fa-paper-plane"
|
||||
title="Send to ComfyUI (Click: Append, Shift+Click: Replace)">
|
||||
title="${sendTitle}">
|
||||
</i>
|
||||
<i class="fas fa-copy"
|
||||
title="Copy LoRA Syntax">
|
||||
title="${copyTitle}">
|
||||
</i>`;
|
||||
|
||||
// Generate UI text with i18n support
|
||||
const toggleBlurTitle = translate('modelCard.actions.toggleBlur', {}, 'Toggle blur');
|
||||
const showButtonText = translate('modelCard.actions.show', {}, 'Show');
|
||||
const openExampleImagesTitle = translate('modelCard.actions.openExampleImages', {}, 'Open Example Images Folder');
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="card-preview ${shouldBlur ? 'blurred' : ''}">
|
||||
${isVideo ?
|
||||
@@ -480,7 +496,7 @@ export function createModelCard(model, modelType) {
|
||||
}
|
||||
<div class="card-header">
|
||||
${shouldBlur ?
|
||||
`<button class="toggle-blur-btn" title="Toggle blur">
|
||||
`<button class="toggle-blur-btn" title="${toggleBlurTitle}">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>` : ''}
|
||||
<span class="base-model-label ${shouldBlur ? 'with-toggle' : ''}" title="${model.base_model}">
|
||||
@@ -494,7 +510,7 @@ export function createModelCard(model, modelType) {
|
||||
<div class="nsfw-overlay">
|
||||
<div class="nsfw-warning">
|
||||
<p>${nsfwText}</p>
|
||||
<button class="show-content-btn">Show</button>
|
||||
<button class="show-content-btn">${showButtonText}</button>
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
@@ -505,7 +521,7 @@ export function createModelCard(model, modelType) {
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<i class="fas fa-folder-open"
|
||||
title="Open Example Images Folder">
|
||||
title="${openExampleImagesTitle}">
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { safeTranslate } from '../../utils/i18nHelpers.js';
|
||||
import { translate } from '../../utils/i18nHelpers.js';
|
||||
|
||||
/**
|
||||
* ModelDescription.js
|
||||
@@ -63,7 +63,7 @@ async function loadModelDescription() {
|
||||
const description = await getModelApiClient().fetchModelDescription(filePath);
|
||||
|
||||
// Update content
|
||||
const noDescriptionText = safeTranslate('modals.model.description.noDescription', {}, 'No model description available');
|
||||
const noDescriptionText = translate('modals.model.description.noDescription', {}, 'No model description available');
|
||||
descriptionContent.innerHTML = description || `<div class="no-description">${noDescriptionText}</div>`;
|
||||
descriptionContent.dataset.loaded = 'true';
|
||||
|
||||
@@ -72,7 +72,7 @@ async function loadModelDescription() {
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading model description:', error);
|
||||
const failedText = safeTranslate('modals.model.description.failedToLoad', {}, 'Failed to load model description');
|
||||
const failedText = translate('modals.model.description.failedToLoad', {}, 'Failed to load model description');
|
||||
descriptionContent.innerHTML = `<div class="no-description">${failedText}</div>`;
|
||||
} finally {
|
||||
// Hide loading state
|
||||
@@ -96,7 +96,7 @@ export async function setupModelDescriptionEditing(filePath) {
|
||||
editBtn = document.createElement('button');
|
||||
editBtn.className = 'edit-model-description-btn';
|
||||
// Set title using i18n
|
||||
const editTitle = safeTranslate('modals.model.description.editTitle', {}, 'Edit model description');
|
||||
const editTitle = translate('modals.model.description.editTitle', {}, 'Edit model description');
|
||||
editBtn.title = editTitle;
|
||||
editBtn.innerHTML = '<i class="fas fa-pencil-alt"></i>';
|
||||
descContainer.insertBefore(editBtn, descContent);
|
||||
@@ -154,7 +154,7 @@ export async function setupModelDescriptionEditing(filePath) {
|
||||
}
|
||||
if (!newValue) {
|
||||
this.innerHTML = originalValue;
|
||||
const emptyErrorText = safeTranslate('modals.model.description.validation.cannotBeEmpty', {}, 'Description cannot be empty');
|
||||
const emptyErrorText = translate('modals.model.description.validation.cannotBeEmpty', {}, 'Description cannot be empty');
|
||||
showToast(emptyErrorText, 'error');
|
||||
exitEditMode();
|
||||
return;
|
||||
@@ -163,11 +163,11 @@ export async function setupModelDescriptionEditing(filePath) {
|
||||
// Save to backend
|
||||
const { getModelApiClient } = await import('../../api/modelApiFactory.js');
|
||||
await getModelApiClient().saveModelMetadata(filePath, { modelDescription: newValue });
|
||||
const successText = safeTranslate('modals.model.description.messages.updated', {}, 'Model description updated');
|
||||
const successText = translate('modals.model.description.messages.updated', {}, 'Model description updated');
|
||||
showToast(successText, 'success');
|
||||
} catch (err) {
|
||||
this.innerHTML = originalValue;
|
||||
const errorText = safeTranslate('modals.model.description.messages.updateFailed', {}, 'Failed to update model description');
|
||||
const errorText = translate('modals.model.description.messages.updateFailed', {}, 'Failed to update model description');
|
||||
showToast(errorText, 'error');
|
||||
} finally {
|
||||
exitEditMode();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { BASE_MODELS } from '../../utils/constants.js';
|
||||
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||
import { safeTranslate } from '../../utils/i18nHelpers.js';
|
||||
import { translate } from '../../utils/i18nHelpers.js';
|
||||
|
||||
/**
|
||||
* Set up model name editing functionality
|
||||
@@ -83,7 +83,7 @@ export function setupModelNameEditing(filePath) {
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
|
||||
const text = safeTranslate('modelMetadata.validation.nameTooLong', {}, 'Model name is limited to 100 characters');
|
||||
const text = translate('modelMetadata.validation.nameTooLong', {}, 'Model name is limited to 100 characters');
|
||||
showToast(text, 'warning');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ import { renderCompactTags, setupTagTooltip, formatFileSize } from './utils.js';
|
||||
import { renderTriggerWords, setupTriggerWordsEditMode } from './TriggerWords.js';
|
||||
import { parsePresets, renderPresetTags } from './PresetTags.js';
|
||||
import { loadRecipesForLora } from './RecipeTab.js';
|
||||
import { safeTranslate } from '../../utils/i18nHelpers.js';
|
||||
import { translate } from '../../utils/i18nHelpers.js';
|
||||
|
||||
/**
|
||||
* Display the model modal with the given model data
|
||||
@@ -62,9 +62,9 @@ export async function showModelModal(model, modelType) {
|
||||
}
|
||||
|
||||
// Generate tabs based on model type
|
||||
const examplesText = safeTranslate('modals.model.tabs.examples', {}, 'Examples');
|
||||
const descriptionText = safeTranslate('modals.model.tabs.description', {}, 'Model Description');
|
||||
const recipesText = safeTranslate('modals.model.tabs.recipes', {}, 'Recipes');
|
||||
const examplesText = translate('modals.model.tabs.examples', {}, 'Examples');
|
||||
const descriptionText = translate('modals.model.tabs.description', {}, 'Model Description');
|
||||
const recipesText = translate('modals.model.tabs.recipes', {}, 'Recipes');
|
||||
|
||||
const tabsContent = modelType === 'loras' ?
|
||||
`<button class="tab-btn active" data-tab="showcase">${examplesText}</button>
|
||||
@@ -73,10 +73,10 @@ export async function showModelModal(model, modelType) {
|
||||
`<button class="tab-btn active" data-tab="showcase">${examplesText}</button>
|
||||
<button class="tab-btn" data-tab="description">${descriptionText}</button>`;
|
||||
|
||||
const loadingExampleImagesText = safeTranslate('modals.model.loading.exampleImages', {}, 'Loading example images...');
|
||||
const loadingDescriptionText = safeTranslate('modals.model.loading.description', {}, 'Loading model description...');
|
||||
const loadingRecipesText = safeTranslate('modals.model.loading.recipes', {}, 'Loading recipes...');
|
||||
const loadingExamplesText = safeTranslate('modals.model.loading.examples', {}, 'Loading examples...');
|
||||
const loadingExampleImagesText = translate('modals.model.loading.exampleImages', {}, 'Loading example images...');
|
||||
const loadingDescriptionText = translate('modals.model.loading.description', {}, 'Loading model description...');
|
||||
const loadingRecipesText = translate('modals.model.loading.recipes', {}, 'Loading recipes...');
|
||||
const loadingExamplesText = translate('modals.model.loading.examples', {}, 'Loading examples...');
|
||||
|
||||
const tabPanesContent = modelType === 'loras' ?
|
||||
`<div id="showcase-tab" class="tab-pane active">
|
||||
@@ -122,19 +122,19 @@ export async function showModelModal(model, modelType) {
|
||||
<header class="modal-header">
|
||||
<div class="model-name-header">
|
||||
<h2 class="model-name-content">${modalTitle}</h2>
|
||||
<button class="edit-model-name-btn" title="Edit model name">
|
||||
<button class="edit-model-name-btn" title="${translate('modals.model.actions.editModelName', {}, 'Edit model name')}">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="creator-actions">
|
||||
${modelWithFullData.from_civitai ? `
|
||||
<div class="civitai-view" title="View on Civitai" data-action="view-civitai" data-filepath="${modelWithFullData.file_path}">
|
||||
<i class="fas fa-globe"></i> View on Civitai
|
||||
<div class="civitai-view" title="${translate('modals.model.actions.viewOnCivitai', {}, 'View on Civitai')}" data-action="view-civitai" data-filepath="${modelWithFullData.file_path}">
|
||||
<i class="fas fa-globe"></i> ${translate('modals.model.actions.viewOnCivitaiText', {}, 'View on Civitai')}
|
||||
</div>` : ''}
|
||||
|
||||
${modelWithFullData.civitai?.creator ? `
|
||||
<div class="creator-info" data-username="${modelWithFullData.civitai.creator.username}" data-action="view-creator" title="View Creator Profile">
|
||||
<div class="creator-info" data-username="${modelWithFullData.civitai.creator.username}" data-action="view-creator" title="${translate('modals.model.actions.viewCreatorProfile', {}, 'View Creator Profile')}">
|
||||
${modelWithFullData.civitai.creator.image ?
|
||||
`<div class="creator-avatar">
|
||||
<img src="${modelWithFullData.civitai.creator.image}" alt="${modelWithFullData.civitai.creator.username}" onerror="this.onerror=null; this.src='static/icons/user-placeholder.png';">
|
||||
@@ -154,48 +154,48 @@ export async function showModelModal(model, modelType) {
|
||||
<div class="info-section">
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<label>Version</label>
|
||||
<label>${translate('modals.model.metadata.version', {}, 'Version')}</label>
|
||||
<span>${modelWithFullData.civitai?.name || 'N/A'}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>File Name</label>
|
||||
<label>${translate('modals.model.metadata.fileName', {}, 'File Name')}</label>
|
||||
<div class="file-name-wrapper">
|
||||
<span id="file-name" class="file-name-content">${modelWithFullData.file_name || 'N/A'}</span>
|
||||
<button class="edit-file-name-btn" title="Edit file name">
|
||||
<button class="edit-file-name-btn" title="${translate('modals.model.actions.editFileName', {}, 'Edit file name')}">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item location-size">
|
||||
<div class="location-wrapper">
|
||||
<label>Location</label>
|
||||
<label>${translate('modals.model.metadata.location', {}, 'Location')}</label>
|
||||
<span class="file-path">${modelWithFullData.file_path.replace(/[^/]+$/, '') || 'N/A'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item base-size">
|
||||
<div class="base-wrapper">
|
||||
<label>Base Model</label>
|
||||
<label>${translate('modals.model.metadata.baseModel', {}, 'Base Model')}</label>
|
||||
<div class="base-model-display">
|
||||
<span class="base-model-content">${modelWithFullData.base_model || 'Unknown'}</span>
|
||||
<button class="edit-base-model-btn" title="Edit base model">
|
||||
<span class="base-model-content">${modelWithFullData.base_model || translate('modals.model.metadata.unknown', {}, 'Unknown')}</span>
|
||||
<button class="edit-base-model-btn" title="${translate('modals.model.actions.editBaseModel', {}, 'Edit base model')}">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="size-wrapper">
|
||||
<label>Size</label>
|
||||
<label>${translate('modals.model.metadata.size', {}, 'Size')}</label>
|
||||
<span>${formatFileSize(modelWithFullData.file_size)}</span>
|
||||
</div>
|
||||
</div>
|
||||
${typeSpecificContent}
|
||||
<div class="info-item notes">
|
||||
<label>Additional Notes <i class="fas fa-info-circle notes-hint" title="Press Enter to save, Shift+Enter for new line"></i></label>
|
||||
<label>${translate('modals.model.metadata.additionalNotes', {}, 'Additional Notes')} <i class="fas fa-info-circle notes-hint" title="${translate('modals.model.metadata.notesHint', {}, 'Press Enter to save, Shift+Enter for new line')}"></i></label>
|
||||
<div class="editable-field">
|
||||
<div class="notes-content" contenteditable="true" spellcheck="false">${modelWithFullData.notes || 'Add your notes here...'}</div>
|
||||
<div class="notes-content" contenteditable="true" spellcheck="false">${modelWithFullData.notes || translate('modals.model.metadata.addNotesPlaceholder', {}, 'Add your notes here...')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item full-width">
|
||||
<label>About this version</label>
|
||||
<label>${translate('modals.model.metadata.aboutThisVersion', {}, 'About this version')}</label>
|
||||
<div class="description-text">${modelWithFullData.civitai?.description || 'N/A'}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -259,18 +259,18 @@ export async function showModelModal(model, modelType) {
|
||||
function renderLoraSpecificContent(lora, escapedWords) {
|
||||
return `
|
||||
<div class="info-item usage-tips">
|
||||
<label>Usage Tips</label>
|
||||
<label>${translate('modals.model.metadata.usageTips', {}, 'Usage Tips')}</label>
|
||||
<div class="editable-field">
|
||||
<div class="preset-controls">
|
||||
<select id="preset-selector">
|
||||
<option value="">Add preset parameter...</option>
|
||||
<option value="strength_min">Strength Min</option>
|
||||
<option value="strength_max">Strength Max</option>
|
||||
<option value="strength">Strength</option>
|
||||
<option value="clip_skip">Clip Skip</option>
|
||||
<option value="">${translate('modals.model.usageTips.addPresetParameter', {}, 'Add preset parameter...')}</option>
|
||||
<option value="strength_min">${translate('modals.model.usageTips.strengthMin', {}, 'Strength Min')}</option>
|
||||
<option value="strength_max">${translate('modals.model.usageTips.strengthMax', {}, 'Strength Max')}</option>
|
||||
<option value="strength">${translate('modals.model.usageTips.strength', {}, 'Strength')}</option>
|
||||
<option value="clip_skip">${translate('modals.model.usageTips.clipSkip', {}, 'Clip Skip')}</option>
|
||||
</select>
|
||||
<input type="number" id="preset-value" step="0.01" placeholder="Value" style="display:none;">
|
||||
<button class="add-preset-btn">Add</button>
|
||||
<input type="number" id="preset-value" step="0.01" placeholder="${translate('modals.model.usageTips.valuePlaceholder', {}, 'Value')}" style="display:none;">
|
||||
<button class="add-preset-btn">${translate('modals.model.usageTips.add', {}, 'Add')}</button>
|
||||
</div>
|
||||
<div class="preset-tags">
|
||||
${renderPresetTags(parsePresets(lora.usage_tips))}
|
||||
@@ -438,9 +438,11 @@ async function saveNotes(filePath) {
|
||||
try {
|
||||
await getModelApiClient().saveModelMetadata(filePath, { notes: content });
|
||||
|
||||
showToast('Notes saved successfully', 'success');
|
||||
const successMessage = translate('modals.model.notes.saved', {}, 'Notes saved successfully');
|
||||
showToast(successMessage, 'success');
|
||||
} catch (error) {
|
||||
showToast('Failed to save notes', 'error');
|
||||
const errorMessage = translate('modals.model.notes.saveFailed', {}, 'Failed to save notes');
|
||||
showToast(errorMessage, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||
import { safeTranslate } from '../../utils/i18nHelpers.js';
|
||||
import { translate } from '../../utils/i18nHelpers.js';
|
||||
|
||||
// Preset tag suggestions
|
||||
const PRESET_TAGS = [
|
||||
@@ -217,10 +217,10 @@ async function saveTags() {
|
||||
// Exit edit mode
|
||||
editBtn.click();
|
||||
|
||||
showToast(safeTranslate('modelTags.messages.updated', {}, 'Tags updated successfully'), 'success');
|
||||
showToast(translate('modelTags.messages.updated', {}, 'Tags updated successfully'), 'success');
|
||||
} catch (error) {
|
||||
console.error('Error saving tags:', error);
|
||||
showToast(safeTranslate('modelTags.messages.updateFailed', {}, 'Failed to update tags'), 'error');
|
||||
showToast(translate('modelTags.messages.updateFailed', {}, 'Failed to update tags'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ function addNewTag(tag) {
|
||||
|
||||
// Validation: Check length
|
||||
if (tag.length > 30) {
|
||||
const text = safeTranslate('modelTags.validation.maxLength', {}, 'Tag should not exceed 30 characters');
|
||||
const text = translate('modelTags.validation.maxLength', {}, 'Tag should not exceed 30 characters');
|
||||
showToast(text, 'error');
|
||||
return;
|
||||
}
|
||||
@@ -370,7 +370,7 @@ function addNewTag(tag) {
|
||||
// Validation: Check total number
|
||||
const currentTags = tagsContainer.querySelectorAll('.metadata-item');
|
||||
if (currentTags.length >= 30) {
|
||||
const text = safeTranslate('modelTags.validation.maxCount', {}, 'Maximum 30 tags allowed');
|
||||
const text = translate('modelTags.validation.maxCount', {}, 'Maximum 30 tags allowed');
|
||||
showToast(text, 'error');
|
||||
return;
|
||||
}
|
||||
@@ -378,7 +378,7 @@ function addNewTag(tag) {
|
||||
// Validation: Check for duplicates
|
||||
const existingTags = Array.from(currentTags).map(tag => tag.dataset.tag);
|
||||
if (existingTags.includes(tag)) {
|
||||
const text = safeTranslate('modelTags.validation.duplicate', {}, 'This tag already exists');
|
||||
const text = translate('modelTags.validation.duplicate', {}, 'This tag already exists');
|
||||
showToast(text, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,29 +22,6 @@ export function translate(key, params = {}, fallback = null) {
|
||||
return translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe translation function. Assumes i18n is already ready.
|
||||
* @param {string} key - Translation key
|
||||
* @param {Object} params - Parameters for interpolation
|
||||
* @param {string} fallback - Fallback text if translation fails
|
||||
* @returns {string} Translated text
|
||||
*/
|
||||
export function safeTranslate(key, params = {}, fallback = null) {
|
||||
if (!window.i18n) {
|
||||
console.warn('i18n not available');
|
||||
return fallback || key;
|
||||
}
|
||||
|
||||
const translation = window.i18n.t(key, params);
|
||||
|
||||
// If translation returned the key (meaning not found), use fallback
|
||||
if (translation === key && fallback) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update element text with translation
|
||||
* @param {HTMLElement|string} element - Element or selector
|
||||
@@ -56,7 +33,7 @@ export function updateElementText(element, key, params = {}, fallback = null) {
|
||||
const el = typeof element === 'string' ? document.querySelector(element) : element;
|
||||
if (!el) return;
|
||||
|
||||
const text = safeTranslate(key, params, fallback);
|
||||
const text = translate(key, params, fallback);
|
||||
el.textContent = text;
|
||||
}
|
||||
|
||||
@@ -72,7 +49,7 @@ export function updateElementAttribute(element, attribute, key, params = {}, fal
|
||||
const el = typeof element === 'string' ? document.querySelector(element) : element;
|
||||
if (!el) return;
|
||||
|
||||
const text = safeTranslate(key, params, fallback);
|
||||
const text = translate(key, params, fallback);
|
||||
el.setAttribute(attribute, text);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import { translate } from './i18nHelpers.js';
|
||||
import { state, getCurrentPageState } from '../state/index.js';
|
||||
import { getStorageItem, setStorageItem } from './storageHelpers.js';
|
||||
import { NODE_TYPE_ICONS, DEFAULT_NODE_COLOR } from './constants.js';
|
||||
|
||||
/**
|
||||
* Utility function to copy text to clipboard with fallback for older browsers
|
||||
* @param {string} text - The text to copy to clipboard
|
||||
* @param {string} successMessage - Optional success message to show in toast
|
||||
* @returns {Promise<boolean>} - Promise that resolves to true if copy was successful
|
||||
/**
|
||||
* Utility function to copy text to clipboard with fallback for older browsers
|
||||
* @param {string} text - The text to copy to clipboard
|
||||
* @param {string} successMessage - Optional success message to show in toast
|
||||
* @returns {Promise<boolean>} - Promise that resolves to true if copy was successful
|
||||
*/
|
||||
export async function copyToClipboard(text, successMessage = 'Copied to clipboard') {
|
||||
export async function copyToClipboard(text, successMessage = null) {
|
||||
const defaultSuccessMessage = successMessage || translate('uiHelpers.clipboard.copied', {}, 'Copied to clipboard');
|
||||
|
||||
try {
|
||||
// Modern clipboard API
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
@@ -25,13 +33,14 @@ export async function copyToClipboard(text, successMessage = 'Copied to clipboar
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
|
||||
if (successMessage) {
|
||||
showToast(successMessage, 'success');
|
||||
if (defaultSuccessMessage) {
|
||||
showToast(defaultSuccessMessage, 'success');
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('Copy failed:', err);
|
||||
showToast('Copy failed', 'error');
|
||||
const errorMessage = translate('uiHelpers.clipboard.copyFailed', {}, 'Copy failed');
|
||||
showToast(errorMessage, 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -294,7 +303,8 @@ export function copyLoraSyntax(card) {
|
||||
const includeTriggerWords = state.global.settings.includeTriggerWords;
|
||||
|
||||
if (!includeTriggerWords) {
|
||||
copyToClipboard(baseSyntax, "LoRA syntax copied to clipboard");
|
||||
const message = translate('uiHelpers.lora.syntaxCopied', {}, 'LoRA syntax copied to clipboard');
|
||||
copyToClipboard(baseSyntax, message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -307,10 +317,8 @@ export function copyLoraSyntax(card) {
|
||||
!Array.isArray(trainedWords) ||
|
||||
trainedWords.length === 0
|
||||
) {
|
||||
copyToClipboard(
|
||||
baseSyntax,
|
||||
"LoRA syntax copied to clipboard (no trigger words found)"
|
||||
);
|
||||
const message = translate('uiHelpers.lora.syntaxCopiedNoTriggerWords', {}, 'LoRA syntax copied to clipboard (no trigger words found)');
|
||||
copyToClipboard(baseSyntax, message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -325,10 +333,8 @@ export function copyLoraSyntax(card) {
|
||||
if (triggers.length > 0) {
|
||||
finalSyntax = `${baseSyntax}, ${triggers.join(", ")}`;
|
||||
}
|
||||
copyToClipboard(
|
||||
finalSyntax,
|
||||
"LoRA syntax with trigger words copied to clipboard"
|
||||
);
|
||||
const message = translate('uiHelpers.lora.syntaxCopiedWithTriggerWords', {}, 'LoRA syntax with trigger words copied to clipboard');
|
||||
copyToClipboard(finalSyntax, message);
|
||||
} else {
|
||||
// Multiple groups: format with separators
|
||||
const groups = trainedWords
|
||||
@@ -348,10 +354,8 @@ export function copyLoraSyntax(card) {
|
||||
finalSyntax += `\n${"-".repeat(17)}\n${groups[i]}`;
|
||||
}
|
||||
}
|
||||
copyToClipboard(
|
||||
finalSyntax,
|
||||
"LoRA syntax with trigger word groups copied to clipboard"
|
||||
);
|
||||
const message = translate('uiHelpers.lora.syntaxCopiedWithTriggerWordGroups', {}, 'LoRA syntax with trigger word groups copied to clipboard');
|
||||
copyToClipboard(finalSyntax, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +388,8 @@ export async function sendLoraToWorkflow(loraSyntax, replaceMode = false, syntax
|
||||
// Success case - check node count
|
||||
if (registryData.data.node_count === 0) {
|
||||
// No nodes found - show warning
|
||||
showToast('No supported target nodes found in workflow', 'warning');
|
||||
const message = translate('uiHelpers.workflow.noSupportedNodes', {}, 'No supported target nodes found in workflow');
|
||||
showToast(message, 'warning');
|
||||
return false;
|
||||
} else if (registryData.data.node_count > 1) {
|
||||
// Multiple nodes - show selector
|
||||
@@ -397,7 +402,8 @@ export async function sendLoraToWorkflow(loraSyntax, replaceMode = false, syntax
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get registry:', error);
|
||||
showToast('Failed to communicate with ComfyUI', 'error');
|
||||
const message = translate('uiHelpers.workflow.communicationFailed', {}, 'Failed to communicate with ComfyUI');
|
||||
showToast(message, 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -429,18 +435,31 @@ async function sendToSpecificNode(nodeIds, loraSyntax, replaceMode, syntaxType)
|
||||
if (result.success) {
|
||||
// Use different toast messages based on syntax type
|
||||
if (syntaxType === 'recipe') {
|
||||
showToast(`Recipe ${replaceMode ? 'replaced' : 'added'} to workflow`, 'success');
|
||||
const message = replaceMode ?
|
||||
translate('uiHelpers.workflow.recipeReplaced', {}, 'Recipe replaced in workflow') :
|
||||
translate('uiHelpers.workflow.recipeAdded', {}, 'Recipe added to workflow');
|
||||
showToast(message, 'success');
|
||||
} else {
|
||||
showToast(`LoRA ${replaceMode ? 'replaced' : 'added'} to workflow`, 'success');
|
||||
const message = replaceMode ?
|
||||
translate('uiHelpers.workflow.loraReplaced', {}, 'LoRA replaced in workflow') :
|
||||
translate('uiHelpers.workflow.loraAdded', {}, 'LoRA added to workflow');
|
||||
showToast(message, 'success');
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
showToast(result.error || `Failed to send ${syntaxType === 'recipe' ? 'recipe' : 'LoRA'} to workflow`, 'error');
|
||||
const errorMessage = result.error ||
|
||||
(syntaxType === 'recipe' ?
|
||||
translate('uiHelpers.workflow.recipeFailedToSend', {}, 'Failed to send recipe to workflow') :
|
||||
translate('uiHelpers.workflow.loraFailedToSend', {}, 'Failed to send LoRA to workflow'));
|
||||
showToast(errorMessage, 'error');
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to send to workflow:', error);
|
||||
showToast(`Failed to send ${syntaxType === 'recipe' ? 'recipe' : 'LoRA'} to workflow`, 'error');
|
||||
const message = syntaxType === 'recipe' ?
|
||||
translate('uiHelpers.workflow.recipeFailedToSend', {}, 'Failed to send recipe to workflow') :
|
||||
translate('uiHelpers.workflow.loraFailedToSend', {}, 'Failed to send LoRA to workflow');
|
||||
showToast(message, 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -482,20 +501,26 @@ function showNodeSelector(nodes, loraSyntax, replaceMode, syntaxType) {
|
||||
}).join('');
|
||||
|
||||
// Add header with action mode indicator
|
||||
const actionType = syntaxType === 'recipe' ? 'Recipe' : 'LoRA';
|
||||
const actionMode = replaceMode ? 'Replace' : 'Append';
|
||||
const actionType = syntaxType === 'recipe' ?
|
||||
translate('uiHelpers.nodeSelector.recipe', {}, 'Recipe') :
|
||||
translate('uiHelpers.nodeSelector.lora', {}, 'LoRA');
|
||||
const actionMode = replaceMode ?
|
||||
translate('uiHelpers.nodeSelector.replace', {}, 'Replace') :
|
||||
translate('uiHelpers.nodeSelector.append', {}, 'Append');
|
||||
const selectTargetNodeText = translate('uiHelpers.nodeSelector.selectTargetNode', {}, 'Select target node');
|
||||
const sendToAllText = translate('uiHelpers.nodeSelector.sendToAll', {}, 'Send to All');
|
||||
|
||||
selector.innerHTML = `
|
||||
<div class="node-selector-header">
|
||||
<span class="selector-action-type">${actionMode} ${actionType}</span>
|
||||
<span class="selector-instruction">Select target node</span>
|
||||
<span class="selector-instruction">${selectTargetNodeText}</span>
|
||||
</div>
|
||||
${nodeItems}
|
||||
<div class="node-item send-all-item" data-action="send-all">
|
||||
<div class="node-icon-indicator all-nodes">
|
||||
<i class="fas fa-broadcast-tower"></i>
|
||||
</div>
|
||||
<span>Send to All</span>
|
||||
<span>${sendToAllText}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -654,15 +679,18 @@ export async function openExampleImagesFolder(modelHash) {
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showToast('Opening example images folder', 'success');
|
||||
const message = translate('uiHelpers.exampleImages.openingFolder', {}, 'Opening example images folder');
|
||||
showToast(message, 'success');
|
||||
return true;
|
||||
} else {
|
||||
showToast(result.error || 'Failed to open example images folder', 'error');
|
||||
const message = result.error || translate('uiHelpers.exampleImages.failedToOpen', {}, 'Failed to open example images folder');
|
||||
showToast(message, 'error');
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to open example images folder:', error);
|
||||
showToast('Failed to open example images folder', 'error');
|
||||
const message = translate('uiHelpers.exampleImages.failedToOpen', {}, 'Failed to open example images folder');
|
||||
showToast(message, 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user