diff --git a/messages/de.json b/messages/de.json index f9012e8..e385f79 100644 --- a/messages/de.json +++ b/messages/de.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "FreeSR-Daten erfolgreich importiert", "importDatabaseSuccess": "Datenbank erfolgreich importiert", "getData": "Daten abrufen", + "fetchingData": "Daten werden abgerufen...", "import": "Importieren", "freeSRImport": "FreeSR-Import", "onlySupportFreeSRJsonFile": "Unterstützt nur FreeSR-JSON-Dateien", "pickAFile": "Datei auswählen", + "changeFile": "Datei ändern", + "avatars": "Charaktere", + "noFreeSRDataLoaded": "Keine FreeSR-Daten geladen", + "dropFreeSRJsonHint": "Ziehe deine FreeSR.json-Datei hierher oder klicke, um sie auszuwählen.", + "step1": "SCHRITT 1", + "step2": "SCHRITT 2", + "step3": "SCHRITT 3", + "exportJsonFromFreeSR": "JSON aus FreeSR exportieren", + "dropOrSelectFile": "Datei ablegen oder auswählen", + "syncCharacterBuilds": "Charakter-Builds synchronisieren", + "enterUidPlaceholder": "9-stellige UID eingeben...", + "publicCharacters": "Öffentliche Charaktere", + "fetchFromEnka": "Von Enka.Network abrufen", + "fetchEnkaHint": "Gib oben deine Honkai: Star Rail UID ein, um deine öffentlich ausgestellten Charaktere zu synchronisieren.", + "inGameProfile": "Im Spiel: Trailblazer-Profil", + "displayCharactersPublicly": "Charaktere öffentlich anzeigen", + "enterUidAndSync": "UID eingeben & synchronisieren", "lightConeSetting": "Lichtkegel-Einstellung", "relicMaker": "Relikt-Ersteller", "pleaseSelectAllOptions": "Bitte wähle alle Optionen", @@ -219,10 +237,40 @@ "searchStage": "Phase suchen...", "noStageFound": "Keine Phase gefunden", "searchMonster": "Monster suchen...", - "changeRelic": "Relikt wechseln", + "HEAD": "Kopf", + "HAND": "Handschuhe", + "BODY": "Körper", + "FOOT": "Füße", + "NECK": "Planarsphäre", + "OBJECT": "Verbindungskette", + "changeRelic": "Relikt ändern", "deleteRelic": "Relikt löschen", "deleteRelicConfirm": "Möchtest du das Relikt auf diesem Platz wirklich löschen?", - "setEffects": "Effekte einstellen", + "confirmDelete": "Löschen bestätigen", + "cancel": "Abbrechen", + "relicDeletedSuccessfully": "Relikt erfolgreich gelöscht", + "profileCreatedSuccessfully": "Profil erfolgreich erstellt", + "profileUpdatedSuccessfully": "Profil erfolgreich aktualisiert", + "profileDeletedSuccessfully": "Profil erfolgreich gelöscht", + "profileSwitched": "Profil gewechselt zu", + "deleteProfileConfirm": "Möchtest du das Profil wirklich löschen", + "deleteProfile": "Profil löschen", + "copyText": "Text kopieren", + "copyImage": "Bild kopieren", + "processing": "Verarbeite...", + "saving": "Speichere...", + "buildInfoCopied": "Build-Infos in Zwischenablage kopiert!", + "copyFailed": "Kopieren der Build-Infos fehlgeschlagen!", + "imageCopiedToClipboard": "Bild in Zwischenablage kopiert!", + "clipboardWriteFailed": "Direktes Schreiben in Zwischenablage fehlgeschlagen!", + "imageSavedSuccessfully": "Bild erfolgreich gespeichert!", + "failedToGenerateImage": "Bildgenerierung fehlgeschlagen!", + "errorGeneratingShowcaseCard": "Fehler beim Erstellen der Showcase-Karte!", + "feature": "Feature", + "fix": "Behebung", + "improvement": "Verbesserung", + "version": "Version", + "setEffects": "Set-Effekte einstellen", "details": "Details", "normal": "Standardangriff", "bpskill": "Fähigkeit", @@ -258,7 +306,6 @@ "upRoll": "Aufwertung hoch", "downRoll": "Aufwertung runter", "actions": "Aktionen", - "avatars": "Avatare", "quickView": "Schnellansicht", "extraSetting": "Extra-Einstellungen", "disableCensorship": "Zensur deaktivieren", diff --git a/messages/en.json b/messages/en.json index d6d13b1..6ee5188 100644 --- a/messages/en.json +++ b/messages/en.json @@ -154,10 +154,28 @@ "importFreeSRDataSuccess": "Import FreeSR data success", "importDatabaseSuccess": "Import database success", "getData": "Get Data", + "fetchingData": "Fetching...", "import": "Import", "freeSRImport": "FreeSR Import", "onlySupportFreeSRJsonFile": "Only support FreeSR json file", "pickAFile": "Pick a file", + "changeFile": "Change File", + "avatars": "Avatars", + "noFreeSRDataLoaded": "No FreeSR Data Loaded", + "dropFreeSRJsonHint": "Drop your FreeSR.json file here or click anywhere to select from your device.", + "step1": "STEP 1", + "step2": "STEP 2", + "step3": "STEP 3", + "exportJsonFromFreeSR": "Export JSON from FreeSR", + "dropOrSelectFile": "Drop or select file", + "syncCharacterBuilds": "Sync character builds", + "enterUidPlaceholder": "Enter 9-digit UID...", + "publicCharacters": "Public Characters", + "fetchFromEnka": "Fetch from Enka.Network", + "fetchEnkaHint": "Enter your Honkai: Star Rail UID above to sync your public showcase characters.", + "inGameProfile": "In-game Trailblazer Profile", + "displayCharactersPublicly": "Display characters publicly", + "enterUidAndSync": "Enter UID & Sync", "lightConeSetting": "LightCone Setting", "relicMaker": "Relic Maker", "pleaseSelectAllOptions": "Please select all options", @@ -219,10 +237,39 @@ "addNewWave": "Add New Wave", "searchStage": "Search stage...", "noStageFound": "No stage found", - "searchMonster": "Search monster...", + "HEAD": "Head", + "HAND": "Hands", + "BODY": "Body", + "FOOT": "Feet", + "NECK": "Planar Sphere", + "OBJECT": "Link Rope", "changeRelic": "Change relic", "deleteRelic": "Delete relic", "deleteRelicConfirm": "Are you sure you want to delete relic in slot", + "confirmDelete": "Confirm Delete", + "cancel": "Cancel", + "relicDeletedSuccessfully": "Relic deleted successfully", + "profileCreatedSuccessfully": "Profile created successfully", + "profileUpdatedSuccessfully": "Profile updated successfully", + "profileDeletedSuccessfully": "Profile deleted successfully", + "profileSwitched": "Profile switched to", + "deleteProfileConfirm": "Are you sure you want to delete profile", + "deleteProfile": "Delete Profile", + "copyText": "Copy Text", + "copyImage": "Copy Image", + "processing": "Processing...", + "saving": "Saving...", + "buildInfoCopied": "Build info copied to clipboard!", + "copyFailed": "Failed to copy build info!", + "imageCopiedToClipboard": "Image copied to clipboard!", + "clipboardWriteFailed": "Browser clipboard write failed!", + "imageSavedSuccessfully": "Image saved successfully!", + "failedToGenerateImage": "Failed to generate image!", + "errorGeneratingShowcaseCard": "Error generating showcase card!", + "feature": "Feature", + "fix": "Fix", + "improvement": "Improvement", + "version": "Version", "setEffects": "Set Effects", "details": "Details", "normal": "Basic ATK", @@ -259,7 +306,6 @@ "upRoll": "Up Roll", "downRoll": "Down Roll", "actions": "Actions", - "avatars": "Avatars", "quickView": "Quick View", "extraSetting": "Extra Settings", "disableCensorship": "Disable Censorship", diff --git a/messages/es.json b/messages/es.json index b94b77b..773db8e 100644 --- a/messages/es.json +++ b/messages/es.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "Datos de FreeSR importados con éxito", "importDatabaseSuccess": "Base de datos importada con éxito", "getData": "Obtener Datos", + "fetchingData": "Obteniendo datos...", "import": "Importar", "freeSRImport": "Importar FreeSR", "onlySupportFreeSRJsonFile": "Solo soporta archivos JSON de FreeSR", "pickAFile": "Elegir un archivo", + "changeFile": "Cambiar archivo", + "avatars": "Personajes", + "noFreeSRDataLoaded": "No se han cargado datos de FreeSR", + "dropFreeSRJsonHint": "Arrastra tu archivo FreeSR.json aquí o haz clic para seleccionarlo.", + "step1": "PASO 1", + "step2": "PASO 2", + "step3": "PASO 3", + "exportJsonFromFreeSR": "Exportar JSON de FreeSR", + "dropOrSelectFile": "Soltar o seleccionar archivo", + "syncCharacterBuilds": "Sincronizar builds de personajes", + "enterUidPlaceholder": "Ingresar UID de 9 dígitos...", + "publicCharacters": "Personajes públicos", + "fetchFromEnka": "Obtener de Enka.Network", + "fetchEnkaHint": "Ingresa tu UID de Honkai: Star Rail arriba para sincronizar tus personajes públicos.", + "inGameProfile": "Perfil de Trazacaminos en el juego", + "displayCharactersPublicly": "Mostrar personajes públicamente", + "enterUidAndSync": "Ingresar UID y sincronizar", "lightConeSetting": "Ajuste de Cono de Luz", "relicMaker": "Creador de Artefactos", "pleaseSelectAllOptions": "Por favor selecciona todas las opciones", @@ -219,10 +237,40 @@ "searchStage": "Buscar etapa...", "noStageFound": "No se encontró etapa", "searchMonster": "Buscar monstruo...", + "HEAD": "Cabeza", + "HAND": "Manos", + "BODY": "Cuerpo", + "FOOT": "Pies", + "NECK": "Esfera planar", + "OBJECT": "Cuerda de unión", "changeRelic": "Cambiar artefacto", "deleteRelic": "Borrar artefacto", "deleteRelicConfirm": "¿Seguro que quieres borrar el artefacto en este espacio?", - "setEffects": "Configurar Efectos", + "confirmDelete": "Confirmar eliminación", + "cancel": "Cancelar", + "relicDeletedSuccessfully": "Artefacto eliminado con éxito", + "profileCreatedSuccessfully": "Perfil creado con éxito", + "profileUpdatedSuccessfully": "Perfil actualizado con éxito", + "profileDeletedSuccessfully": "Perfil eliminado con éxito", + "profileSwitched": "Perfil cambiado a", + "deleteProfileConfirm": "¿Seguro que quieres borrar el perfil", + "deleteProfile": "Borrar perfil", + "copyText": "Copiar texto", + "copyImage": "Copiar imagen", + "processing": "Procesando...", + "saving": "Guardando...", + "buildInfoCopied": "¡Información de la build copiada al portapapeles!", + "copyFailed": "¡Error al copiar la información de la build!", + "imageCopiedToClipboard": "¡Imagen copiada al portapapeles!", + "clipboardWriteFailed": "¡Error al escribir en el portapapeles!", + "imageSavedSuccessfully": "¡Imagen guardada con éxito!", + "failedToGenerateImage": "¡Error al generar la imagen!", + "errorGeneratingShowcaseCard": "¡Error al generar la tarjeta showcase!", + "feature": "Función", + "fix": "Corrección", + "improvement": "Mejora", + "version": "Versión", + "setEffects": "Efectos de conjunto", "details": "Detalles", "normal": "Ataque Básico", "bpskill": "Habilidad", @@ -258,7 +306,6 @@ "upRoll": "Subir Mejora", "downRoll": "Bajar Mejora", "actions": "Acciones", - "avatars": "Avatares", "quickView": "Vista Rápida", "extraSetting": "Ajustes Extra", "disableCensorship": "Desactivar Censura", diff --git a/messages/fr.json b/messages/fr.json index b4d48c8..5d74d0c 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "Importation FreeSR réussie", "importDatabaseSuccess": "Importation de base de données réussie", "getData": "Obtenir des données", + "fetchingData": "Récupération...", "import": "Importer", "freeSRImport": "Importation FreeSR", "onlySupportFreeSRJsonFile": "Supporte uniquement les fichiers JSON FreeSR", "pickAFile": "Choisir un fichier", + "changeFile": "Changer de fichier", + "avatars": "Personnages", + "noFreeSRDataLoaded": "Aucune donnée FreeSR chargée", + "dropFreeSRJsonHint": "Déposez votre fichier FreeSR.json ici ou cliquez pour le sélectionner.", + "step1": "ÉTAPE 1", + "step2": "ÉTAPE 2", + "step3": "ÉTAPE 3", + "exportJsonFromFreeSR": "Exporter le JSON depuis FreeSR", + "dropOrSelectFile": "Déposer ou sélectionner le fichier", + "syncCharacterBuilds": "Synchroniser les builds des personnages", + "enterUidPlaceholder": "Entrez l'UID à 9 chiffres...", + "publicCharacters": "Personnages publics", + "fetchFromEnka": "Récupérer depuis Enka.Network", + "fetchEnkaHint": "Entrez votre UID Honkai: Star Rail ci-dessus pour synchroniser vos personnages exposés.", + "inGameProfile": "Profil de Pionnier en jeu", + "displayCharactersPublicly": "Afficher les personnages publiquement", + "enterUidAndSync": "Entrez l'UID et synchronisez", "lightConeSetting": "Paramètre de Cône de Lumière", "relicMaker": "Créateur de Reliques", "pleaseSelectAllOptions": "Veuillez sélectionner toutes les options", @@ -218,11 +236,41 @@ "addNewWave": "Ajouter une nouvelle vague", "searchStage": "Chercher un niveau...", "noStageFound": "Aucun niveau trouvé", - "searchMonster": "Chercher un monstre...", + "searchMonster": "Rechercher un monstre...", + "HEAD": "Tête", + "HAND": "Mains", + "BODY": "Corps", + "FOOT": "Pieds", + "NECK": "Sphère planaire", + "OBJECT": "Corde de liaison", "changeRelic": "Changer de relique", "deleteRelic": "Supprimer la relique", "deleteRelicConfirm": "Êtes-vous sûr de vouloir supprimer la relique de cet emplacement ?", - "setEffects": "Définir les effets", + "confirmDelete": "Confirmer la suppression", + "cancel": "Annuler", + "relicDeletedSuccessfully": "Relique supprimée avec succès", + "profileCreatedSuccessfully": "Profil créé avec succès", + "profileUpdatedSuccessfully": "Profil mis à jour avec succès", + "profileDeletedSuccessfully": "Profil supprimé avec succès", + "profileSwitched": "Profil changé pour", + "deleteProfileConfirm": "Êtes-vous sûr de vouloir supprimer le profil", + "deleteProfile": "Supprimer le profil", + "copyText": "Copier le texte", + "copyImage": "Copier l'image", + "processing": "Traitement...", + "saving": "Sauvegarde...", + "buildInfoCopied": "Informations du build copiées dans le presse-papier !", + "copyFailed": "Échec de la copie des informations du build !", + "imageCopiedToClipboard": "Image copiée dans le presse-papier !", + "clipboardWriteFailed": "Échec de l'écriture dans le presse-papier !", + "imageSavedSuccessfully": "Image enregistrée avec succès !", + "failedToGenerateImage": "Échec de la génération de l'image !", + "errorGeneratingShowcaseCard": "Erreur lors de la génération de la carte showcase !", + "feature": "Fonctionnalité", + "fix": "Correction", + "improvement": "Amélioration", + "version": "Version", + "setEffects": "Effets de panoplie", "details": "Détails", "normal": "Attaque Normale", "bpskill": "Compétence", @@ -258,7 +306,6 @@ "upRoll": "Augmenter l'amélioration", "downRoll": "Diminuer l'amélioration", "actions": "Actions", - "avatars": "Avatars", "quickView": "Aperçu rapide", "extraSetting": "Paramètres supplémentaires", "disableCensorship": "Désactiver la censure", diff --git a/messages/id.json b/messages/id.json index f074106..b1c2c10 100644 --- a/messages/id.json +++ b/messages/id.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "Impor data FreeSR berhasil", "importDatabaseSuccess": "Impor database berhasil", "getData": "Dapatkan Data", + "fetchingData": "Mengambil data...", "import": "Impor", "freeSRImport": "Impor FreeSR", "onlySupportFreeSRJsonFile": "Hanya mendukung file JSON FreeSR", "pickAFile": "Pilih sebuah file", + "changeFile": "Ganti file", + "avatars": "Karakter", + "noFreeSRDataLoaded": "Tidak ada data FreeSR yang dimuat", + "dropFreeSRJsonHint": "Tarik file FreeSR.json Anda ke sini atau klik untuk memilih file.", + "step1": "LANGKAH 1", + "step2": "LANGKAH 2", + "step3": "LANGKAH 3", + "exportJsonFromFreeSR": "Ekspor JSON dari FreeSR", + "dropOrSelectFile": "Tarik atau pilih file", + "syncCharacterBuilds": "Sinkronkan build karakter", + "enterUidPlaceholder": "Masukkan 9 digit UID...", + "publicCharacters": "Karakter Publik", + "fetchFromEnka": "Ambil dari Enka.Network", + "fetchEnkaHint": "Masukkan UID Honkai: Star Rail Anda di atas untuk menyinkronkan karakter publik.", + "inGameProfile": "Profil Trailblazer dalam game", + "displayCharactersPublicly": "Tampilkan karakter secara publik", + "enterUidAndSync": "Masukkan UID & Sinkronkan", "lightConeSetting": "Pengaturan Light Cone", "relicMaker": "Pembuat Relic", "pleaseSelectAllOptions": "Silakan pilih semua opsi", @@ -219,10 +237,40 @@ "searchStage": "Cari tahap...", "noStageFound": "Tahap tidak ditemukan", "searchMonster": "Cari monster...", - "changeRelic": "Ganti relic", + "HEAD": "Kepala", + "HAND": "Tangan", + "BODY": "Badan", + "FOOT": "Kaki", + "NECK": "Bola Planar", + "OBJECT": "Tali Penghubung", + "changeRelic": "Ubah relic", "deleteRelic": "Hapus relic", "deleteRelicConfirm": "Yakin ingin menghapus relic di slot ini?", - "setEffects": "Atur Efek", + "confirmDelete": "Konfirmasi Hapus", + "cancel": "Batal", + "relicDeletedSuccessfully": "Relic berhasil dihapus", + "profileCreatedSuccessfully": "Profil berhasil dibuat", + "profileUpdatedSuccessfully": "Profil berhasil diperbarui", + "profileDeletedSuccessfully": "Profil berhasil dihapus", + "profileSwitched": "Profil beralih ke", + "deleteProfileConfirm": "Yakin ingin menghapus profil", + "deleteProfile": "Hapus profil", + "copyText": "Salin Teks", + "copyImage": "Salin Gambar", + "processing": "Memproses...", + "saving": "Menyimpan...", + "buildInfoCopied": "Info build berhasil disalin ke papan klip!", + "copyFailed": "Gagal menyalin info build!", + "imageCopiedToClipboard": "Gambar berhasil disalin ke papan klip!", + "clipboardWriteFailed": "Gagal menulis ke papan klip browser!", + "imageSavedSuccessfully": "Gambar berhasil disimpan!", + "failedToGenerateImage": "Gagal membuat gambar!", + "errorGeneratingShowcaseCard": "Gagal membuat kartu showcase!", + "feature": "Fitur", + "fix": "Perbaikan", + "improvement": "Peningkatan", + "version": "Versi", + "setEffects": "Efek Set", "details": "Detail", "normal": "Basic ATK", "bpskill": "Skill", @@ -258,7 +306,6 @@ "upRoll": "Up Roll", "downRoll": "Down Roll", "actions": "Aksi", - "avatars": "Avatar", "quickView": "Tampilan Cepat", "extraSetting": "Pengaturan Ekstra", "disableCensorship": "Nonaktifkan Sensor", diff --git a/messages/ja.json b/messages/ja.json index d7b5f3a..29ded5e 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "FreeSR データのインポート成功", "importDatabaseSuccess": "データベースのインポート成功", "getData": "データ取得", + "fetchingData": "データを取得中...", "import": "インポート", "freeSRImport": "FreeSR インポート", "onlySupportFreeSRJsonFile": "FreeSR JSON ファイルのみ対応", "pickAFile": "ファイル選択", + "changeFile": "ファイルを変更", + "avatars": "キャラクター", + "noFreeSRDataLoaded": "FreeSR データが読み込まれていません", + "dropFreeSRJsonHint": "FreeSR.json ファイルをここにドラッグ&ドロップするか、クリックして選択してください。", + "step1": "ステップ 1", + "step2": "ステップ 2", + "step3": "ステップ 3", + "exportJsonFromFreeSR": "FreeSR から JSON をエクスポート", + "dropOrSelectFile": "ファイルをドロップまたは選択", + "syncCharacterBuilds": "キャラのビルドを同期", + "enterUidPlaceholder": "9桁のUIDを入力...", + "publicCharacters": "公開キャラクター", + "fetchFromEnka": "Enka.Network から取得", + "fetchEnkaHint": "上に崩壊:スターレイルのUIDを入力して、公開ショーケースのキャラクターを同期します。", + "inGameProfile": "ゲーム内の開拓者プロフィール", + "displayCharactersPublicly": "キャラクターを公開設定にする", + "enterUidAndSync": "UIDを入力して同期", "lightConeSetting": "ライトコーン設定", "relicMaker": "遺物製作", "pleaseSelectAllOptions": "すべてのオプションを選択してください", @@ -218,10 +236,40 @@ "searchStage": "ステージを検索...", "noStageFound": "ステージが見つかりません", "searchMonster": "敵を検索...", + "HEAD": "頭部", + "HAND": "手部", + "BODY": "胴体", + "FOOT": "脚部", + "NECK": "次元界オーブ", + "OBJECT": "連結縄", "changeRelic": "遺物を変更", "deleteRelic": "遺物を削除", - "deleteRelicConfirm": "この遺物を削除してもよろしいですか?", - "setEffects": "効果を設定", + "deleteRelicConfirm": "このスロットの遺物を削除してもよろしいですか?", + "confirmDelete": "削除の確認", + "cancel": "キャンセル", + "relicDeletedSuccessfully": "遺物を削除しました", + "profileCreatedSuccessfully": "プロファイルを作成しました", + "profileUpdatedSuccessfully": "プロファイルを更新しました", + "profileDeletedSuccessfully": "プロファイルを削除しました", + "profileSwitched": "プロファイルを切り替えました", + "deleteProfileConfirm": "このプロファイルを削除してもよろしいですか", + "deleteProfile": "プロファイルを削除", + "copyText": "テキストをコピー", + "copyImage": "画像をコピー", + "processing": "処理中...", + "saving": "保存中...", + "buildInfoCopied": "ビルド情報をクリップボードにコピーしました!", + "copyFailed": "ビルド情報のコピーに失敗しました!", + "imageCopiedToClipboard": "画像をクリップボードにコピーしました!", + "clipboardWriteFailed": "クリップボードへの書き込みに失敗しました!", + "imageSavedSuccessfully": "画像を保存しました!", + "failedToGenerateImage": "画像の生成に失敗しました!", + "errorGeneratingShowcaseCard": "ショーケースカードの生成中にエラーが発生しました!", + "feature": "機能", + "fix": "修正", + "improvement": "改善", + "version": "バージョン", + "setEffects": "セット効果", "details": "詳細", "normal": "通常攻撃", "bpskill": "戦闘スキル", @@ -257,7 +305,6 @@ "upRoll": "サブステータスを増やす", "downRoll": "サブステータスを減らす", "actions": "アクション", - "avatars": "アバター", "quickView": "クイックビュー", "extraSetting": "追加設定", "disableCensorship": "検閲を無効化", diff --git a/messages/ko.json b/messages/ko.json index a2b8efe..b04a69b 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "FreeSR 데이터 가져오기 성공", "importDatabaseSuccess": "데이터베이스 가져오기 성공", "getData": "데이터 가져오기", + "fetchingData": "데이터 가져오는 중...", "import": "가져오기", "freeSRImport": "FreeSR 가져오기", "onlySupportFreeSRJsonFile": "FreeSR JSON 파일만 지원", "pickAFile": "파일 선택", + "changeFile": "파일 변경", + "avatars": "캐릭터", + "noFreeSRDataLoaded": "FreeSR 데이터가 로드되지 않음", + "dropFreeSRJsonHint": "FreeSR.json 파일을 여기에 끌어다 놓거나 클릭하여 선택하세요.", + "step1": "1단계", + "step2": "2단계", + "step3": "3단계", + "exportJsonFromFreeSR": "FreeSR에서 JSON 내보내기", + "dropOrSelectFile": "파일을 끌어다 놓거나 선택", + "syncCharacterBuilds": "캐릭터 빌드 동기화", + "enterUidPlaceholder": "9자리 UID 입력...", + "publicCharacters": "공개 캐릭터", + "fetchFromEnka": "Enka.Network에서 가져오기", + "fetchEnkaHint": "위에 붕괴: 스타레일 UID를 입력하여 공개 쇼케이스 캐릭터를 동기화하세요.", + "inGameProfile": "인게임 개척자 프로필", + "displayCharactersPublicly": "캐릭터 공개 설정", + "enterUidAndSync": "UID 입력 및 동기화", "lightConeSetting": "라이트콘 설정", "relicMaker": "유물 제작", "pleaseSelectAllOptions": "모든 옵션을 선택해주세요", @@ -217,11 +235,41 @@ "addNewWave": "새 웨이브 추가", "searchStage": "스테이지 검색...", "noStageFound": "스테이지를 찾을 수 없음", - "searchMonster": "적 검색...", + "searchMonster": "몬스터 검색...", + "HEAD": "머리", + "HAND": "손", + "BODY": "몸통", + "FOOT": "발", + "NECK": "차원 구체", + "OBJECT": "연결 매듭", "changeRelic": "유물 변경", "deleteRelic": "유물 삭제", "deleteRelicConfirm": "이 슬롯의 유물을 삭제하시겠습니까?", - "setEffects": "효과 설정", + "confirmDelete": "삭제 확인", + "cancel": "취소", + "relicDeletedSuccessfully": "유물이 성공적으로 삭제되었습니다", + "profileCreatedSuccessfully": "프로필이 성공적으로 생성되었습니다", + "profileUpdatedSuccessfully": "프로필이 성공적으로 수정되었습니다", + "profileDeletedSuccessfully": "프로필이 성공적으로 삭제되었습니다", + "profileSwitched": "프로필 전환됨", + "deleteProfileConfirm": "이 프로필을 삭제하시겠습니까", + "deleteProfile": "프로필 삭제", + "copyText": "텍스트 복사", + "copyImage": "이미지 복사", + "processing": "처리 중...", + "saving": "저장 중...", + "buildInfoCopied": "빌드 정보가 클립보드에 복사되었습니다!", + "copyFailed": "빌드 정보 복사 실패!", + "imageCopiedToClipboard": "이미지가 클립보드에 복사되었습니다!", + "clipboardWriteFailed": "클립보드 쓰기에 실패했습니다!", + "imageSavedSuccessfully": "이미지가 성공적으로 저장되었습니다!", + "failedToGenerateImage": "이미지 생성 실패!", + "errorGeneratingShowcaseCard": "쇼케이스 카드 생성 중 오류가 발생했습니다!", + "feature": "기능", + "fix": "수정", + "improvement": "개선", + "version": "버전", + "setEffects": "세트 효과", "details": "상세", "normal": "기본 공격", "bpskill": "스킬", @@ -257,7 +305,6 @@ "upRoll": "부옵션 추가", "downRoll": "부옵션 감소", "actions": "동작", - "avatars": "아바타", "quickView": "빠른 조회", "extraSetting": "추가 설정", "disableCensorship": "검열 비활성화", diff --git a/messages/pt.json b/messages/pt.json index 32c9bbb..7b1771c 100644 --- a/messages/pt.json +++ b/messages/pt.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "Dados FreeSR importados com sucesso", "importDatabaseSuccess": "Banco de dados importado com sucesso", "getData": "Obter Dados", + "fetchingData": "Buscando dados...", "import": "Importar", "freeSRImport": "Importação FreeSR", "onlySupportFreeSRJsonFile": "Suporta apenas arquivos JSON FreeSR", "pickAFile": "Escolher um arquivo", + "changeFile": "Alterar arquivo", + "avatars": "Personagens", + "noFreeSRDataLoaded": "Nenhum dado do FreeSR carregado", + "dropFreeSRJsonHint": "Arraste seu arquivo FreeSR.json aqui ou clique para selecionar.", + "step1": "PASSO 1", + "step2": "PASSO 2", + "step3": "PASSO 3", + "exportJsonFromFreeSR": "Exportar JSON do FreeSR", + "dropOrSelectFile": "Solte ou selecione o arquivo", + "syncCharacterBuilds": "Sincronizar builds de personagens", + "enterUidPlaceholder": "Insira o UID de 9 dígitos...", + "publicCharacters": "Personagens Públicos", + "fetchFromEnka": "Buscar do Enka.Network", + "fetchEnkaHint": "Insira seu UID de Honkai: Star Rail acima para sincronizar seus personagens públicos.", + "inGameProfile": "Perfil de Desbravador no jogo", + "displayCharactersPublicly": "Exibir personagens publicamente", + "enterUidAndSync": "Insira UID e sincronize", "lightConeSetting": "Configuração de Cone de Luz", "relicMaker": "Criador de Relíquias", "pleaseSelectAllOptions": "Por favor selecione todas as opções", @@ -219,10 +237,40 @@ "searchStage": "Buscar fase...", "noStageFound": "Fase não encontrada", "searchMonster": "Buscar monstro...", - "changeRelic": "Mudar relíquia", + "HEAD": "Cabeça", + "HAND": "Mãos", + "BODY": "Corpo", + "FOOT": "Pés", + "NECK": "Esfera Planar", + "OBJECT": "Corda de Ligação", + "changeRelic": "Alterar relíquia", "deleteRelic": "Deletar relíquia", "deleteRelicConfirm": "Tem certeza que deseja deletar a relíquia neste slot?", - "setEffects": "Definir Efeitos", + "confirmDelete": "Confirmar exclusão", + "cancel": "Cancelar", + "relicDeletedSuccessfully": "Relíquia excluída com sucesso", + "profileCreatedSuccessfully": "Perfil criado com sucesso", + "profileUpdatedSuccessfully": "Perfil atualizado com sucesso", + "profileDeletedSuccessfully": "Perfil excluído com sucesso", + "profileSwitched": "Perfil alterado para", + "deleteProfileConfirm": "Tem certeza que deseja excluir o perfil", + "deleteProfile": "Excluir perfil", + "copyText": "Copiar texto", + "copyImage": "Copiar imagem", + "processing": "Processando...", + "saving": "Salvando...", + "buildInfoCopied": "Informações da build copiadas para a área de transferência!", + "copyFailed": "Falha ao copiar informações da build!", + "imageCopiedToClipboard": "Imagem copiada para a área de transferência!", + "clipboardWriteFailed": "Falha ao gravar na área de transferência!", + "imageSavedSuccessfully": "Imagem salva com sucesso!", + "failedToGenerateImage": "Falha ao gerar imagem!", + "errorGeneratingShowcaseCard": "Erro ao gerar cartão showcase!", + "feature": "Funcionalidade", + "fix": "Correção", + "improvement": "Melhoria", + "version": "Versão", + "setEffects": "Efeitos de Conjunto", "details": "Detalhes", "normal": "Ataque Básico", "bpskill": "Perícia", @@ -258,7 +306,6 @@ "upRoll": "Aumentar Melhoria", "downRoll": "Diminuir Melhoria", "actions": "Ações", - "avatars": "Avatares", "quickView": "Visualização Rápida", "extraSetting": "Configurações Extras", "disableCensorship": "Desativar Censura", diff --git a/messages/ru.json b/messages/ru.json index effbf87..e01aa46 100644 --- a/messages/ru.json +++ b/messages/ru.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "Успешный импорт из FreeSR", "importDatabaseSuccess": "Успешный импорт базы данных", "getData": "Получить данные", + "fetchingData": "Получение данных...", "import": "Импорт", "freeSRImport": "Импорт FreeSR", "onlySupportFreeSRJsonFile": "Поддерживаются только JSON от FreeSR", "pickAFile": "Выберите файл", + "changeFile": "Изменить файл", + "avatars": "Персонажи", + "noFreeSRDataLoaded": "Данные FreeSR не загружены", + "dropFreeSRJsonHint": "Перетащите файл FreeSR.json сюда или нажмите, чтобы выбрать.", + "step1": "ШАГ 1", + "step2": "ШАГ 2", + "step3": "ШАГ 3", + "exportJsonFromFreeSR": "Экспорт JSON из FreeSR", + "dropOrSelectFile": "Перетащите или выберите файл", + "syncCharacterBuilds": "Синхронизировать сборки персонажей", + "enterUidPlaceholder": "Введите 9-значный UID...", + "publicCharacters": "Публичные персонажи", + "fetchFromEnka": "Получить из Enka.Network", + "fetchEnkaHint": "Введите UID Honkai: Star Rail выше, чтобы синхронизировать персонажей.", + "inGameProfile": "Профиль Первопроходца в игре", + "displayCharactersPublicly": "Открыть персонажей в витрине", + "enterUidAndSync": "Введите UID и синхронизируйте", "lightConeSetting": "Настройки конуса", "relicMaker": "Создатель реликвий", "pleaseSelectAllOptions": "Пожалуйста, выберите все параметры", @@ -219,10 +237,40 @@ "searchStage": "Поиск стадии...", "noStageFound": "Стадия не найдена", "searchMonster": "Поиск монстра...", + "HEAD": "Голова", + "HAND": "Руки", + "BODY": "Тело", + "FOOT": "Ноги", + "NECK": "Планарная сфера", + "OBJECT": "Соединительная вязь", "changeRelic": "Изменить реликвию", "deleteRelic": "Удалить реликвию", "deleteRelicConfirm": "Удалить реликвию в этом слоте?", - "setEffects": "Настроить эффекты", + "confirmDelete": "Подтвердить удаление", + "cancel": "Отмена", + "relicDeletedSuccessfully": "Реликвия успешно удалена", + "profileCreatedSuccessfully": "Профиль успешно создан", + "profileUpdatedSuccessfully": "Профиль успешно обновлен", + "profileDeletedSuccessfully": "Профиль успешно удален", + "profileSwitched": "Профиль изменен на", + "deleteProfileConfirm": "Вы уверены, что хотите удалить профиль", + "deleteProfile": "Удалить профиль", + "copyText": "Копировать текст", + "copyImage": "Копировать изображение", + "processing": "Обработка...", + "saving": "Сохранение...", + "buildInfoCopied": "Информация о сборке скопирована в буфер обмена!", + "copyFailed": "Не удалось скопировать информацию о сборке!", + "imageCopiedToClipboard": "Изображение скопировано в буфер обмена!", + "clipboardWriteFailed": "Не удалось записать в буфер обмена!", + "imageSavedSuccessfully": "Изображение успешно сохранено!", + "failedToGenerateImage": "Не удалось создать изображение!", + "errorGeneratingShowcaseCard": "Ошибка генерации карточки showcase!", + "feature": "Функция", + "fix": "Исправление", + "improvement": "Улучшение", + "version": "Версия", + "setEffects": "Эффекты набора", "details": "Детали", "normal": "Базовая атака", "bpskill": "Навык", @@ -258,7 +306,6 @@ "upRoll": "Повысить улучшение", "downRoll": "Понизить улучшение", "actions": "Действия", - "avatars": "Персонажи", "quickView": "Быстрый просмотр", "extraSetting": "Дополнительные настройки", "disableCensorship": "Отключить цензуру", diff --git a/messages/th.json b/messages/th.json index ec8cde5..d119d3f 100644 --- a/messages/th.json +++ b/messages/th.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "นำเข้าข้อมูล FreeSR สำเร็จ", "importDatabaseSuccess": "นำเข้าฐานข้อมูลสำเร็จ", "getData": "รับข้อมูล", + "fetchingData": "กำลังดึงข้อมูล...", "import": "นำเข้า", "freeSRImport": "นำเข้า FreeSR", "onlySupportFreeSRJsonFile": "รองรับเฉพาะไฟล์ JSON ของ FreeSR", "pickAFile": "เลือกไฟล์", + "changeFile": "เปลี่ยนไฟล์", + "avatars": "ตัวละคร", + "noFreeSRDataLoaded": "ไม่ได้โหลดข้อมูล FreeSR", + "dropFreeSRJsonHint": "ลากไฟล์ FreeSR.json ของคุณมาที่นี่หรือคลิกเพื่อเลือกไฟล์", + "step1": "ขั้นตอนที่ 1", + "step2": "ขั้นตอนที่ 2", + "step3": "ขั้นตอนที่ 3", + "exportJsonFromFreeSR": "ส่งออก JSON จาก FreeSR", + "dropOrSelectFile": "ลากหรือเลือกไฟล์", + "syncCharacterBuilds": "ซิงค์บิลด์ตัวละคร", + "enterUidPlaceholder": "ป้อน UID 9 หลัก...", + "publicCharacters": "ตัวละครสาธารณะ", + "fetchFromEnka": "ดึงข้อมูลจาก Enka.Network", + "fetchEnkaHint": "ป้อน UID Honkai: Star Rail ของคุณด้านบนเพื่อซิงค์ตัวละครที่แสดงแบบสาธารณะ", + "inGameProfile": "โปรไฟล์ผู้บุกเบิกในเกม", + "displayCharactersPublicly": "แสดงตัวละครแบบสาธารณะ", + "enterUidAndSync": "ป้อน UID และซิงค์", "lightConeSetting": "การตั้งค่า Light Cone", "relicMaker": "สร้างรีลิกส์", "pleaseSelectAllOptions": "โปรดเลือกตัวเลือกทั้งหมด", @@ -219,10 +237,40 @@ "searchStage": "ค้นหาด่าน...", "noStageFound": "ไม่พบด่าน", "searchMonster": "ค้นหามอนสเตอร์...", + "HEAD": "ส่วนหัว", + "HAND": "ส่วนมือ", + "BODY": "ส่วนลำตัว", + "FOOT": "ส่วนเท้า", + "NECK": "Planar Sphere", + "OBJECT": "Link Rope", "changeRelic": "เปลี่ยนรีลิกส์", "deleteRelic": "ลบรีลิกส์", "deleteRelicConfirm": "คุณแน่ใจหรือไม่ว่าต้องการลบรีลิกส์ในช่องนี้?", - "setEffects": "ตั้งค่าเอฟเฟกต์", + "confirmDelete": "ยืนยันการลบ", + "cancel": "ยกเลิก", + "relicDeletedSuccessfully": "ลบรีลิกส์สำเร็จ", + "profileCreatedSuccessfully": "สร้างโปรไฟล์สำเร็จ", + "profileUpdatedSuccessfully": "อัปเดตโปรไฟล์สำเร็จ", + "profileDeletedSuccessfully": "ลบโปรไฟล์สำเร็จ", + "profileSwitched": "สลับโปรไฟล์เป็น", + "deleteProfileConfirm": "คุณแน่ใจหรือไม่ว่าต้องการลบโปรไฟล์", + "deleteProfile": "ลบโปรไฟล์", + "copyText": "คัดลอกข้อความ", + "copyImage": "คัดลอกรูปภาพ", + "processing": "กำลังประมวลผล...", + "saving": "กำลังบันทึก...", + "buildInfoCopied": "คัดลอกข้อมูลบิลด์ไปยังคลิปบอร์ดแล้ว!", + "copyFailed": "คัดลอกข้อมูลบิลด์ไม่สำเร็จ!", + "imageCopiedToClipboard": "คัดลอกรูปภาพไปยังคลิปบอร์ดแล้ว!", + "clipboardWriteFailed": "ไม่สามารถเขียนลงคลิปบอร์ดได้!", + "imageSavedSuccessfully": "บันทึกรูปภาพสำเร็จ!", + "failedToGenerateImage": "สร้างรูปภาพไม่สำเร็จ!", + "errorGeneratingShowcaseCard": "เกิดข้อผิดพลาดในการสร้างการ์ดโชว์เคส!", + "feature": "ฟีเจอร์", + "fix": "แก้ไข", + "improvement": "ปรับปรุง", + "version": "เวอร์ชัน", + "setEffects": "เอฟเฟกต์เซ็ต", "details": "รายละเอียด", "normal": "โจมตีปกติ", "bpskill": "สกิลต่อสู้", @@ -258,7 +306,6 @@ "upRoll": "เพิ่มอัปเกรด", "downRoll": "ลดอัปเกรด", "actions": "แอ็กชัน", - "avatars": "อวตาร", "quickView": "ดูแบบด่วน", "extraSetting": "การตั้งค่าเพิ่มเติม", "disableCensorship": "ปิดการเซ็นเซอร์", diff --git a/messages/vi.json b/messages/vi.json index 38c7eef..1272733 100644 --- a/messages/vi.json +++ b/messages/vi.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "Nhập dữ liệu FreeSR thành công", "importDatabaseSuccess": "Nhập cơ sở dữ liệu thành công", "getData": "Lấy dữ liệu", + "fetchingData": "Đang lấy dữ liệu...", "import": "Nhập", "freeSRImport": "Nhập FreeSR", "onlySupportFreeSRJsonFile": "Chỉ hỗ trợ tệp JSON từ FreeSR", "pickAFile": "Chọn tệp", + "changeFile": "Đổi file", + "avatars": "Nhân vật", + "noFreeSRDataLoaded": "Chưa có dữ liệu FreeSR", + "dropFreeSRJsonHint": "Kéo thả file FreeSR.json vào đây hoặc nhấn để chọn từ thiết bị của bạn.", + "step1": "BƯỚC 1", + "step2": "BƯỚC 2", + "step3": "BƯỚC 3", + "exportJsonFromFreeSR": "Xuất JSON từ FreeSR", + "dropOrSelectFile": "Kéo thả hoặc chọn file", + "syncCharacterBuilds": "Đồng bộ build nhân vật", + "enterUidPlaceholder": "Nhập UID 9 chữ số...", + "publicCharacters": "Nhân vật công khai", + "fetchFromEnka": "Lấy dữ liệu từ Enka.Network", + "fetchEnkaHint": "Nhập UID Honkai: Star Rail ở trên để đồng bộ các nhân vật trưng bày công khai.", + "inGameProfile": "Hồ sơ Nhà Khai Phá trong game", + "displayCharactersPublicly": "Bật hiển thị nhân vật công khai", + "enterUidAndSync": "Nhập UID & Đồng bộ", "lightConeSetting": "Cài đặt Nón Ánh Sáng", "relicMaker": "Trình tạo Thánh Di Vật", "pleaseSelectAllOptions": "Vui lòng chọn tất cả tùy chọn", @@ -219,10 +237,40 @@ "searchStage": "Tìm màn...", "noStageFound": "Không tìm thấy màn", "searchMonster": "Tìm quái...", + "HEAD": "Đầu", + "HAND": "Tay", + "BODY": "Thân", + "FOOT": "Chân", + "NECK": "Quả Cầu Vị Diện", + "OBJECT": "Dây Liên Kết", "changeRelic": "Thay đổi di vật", "deleteRelic": "Xóa di vật", - "deleteRelicConfirm": "Bạn có chắc chắn muốn xóa di vật trong ô này không?", - "setEffects": "Thiết lập hiệu ứng", + "deleteRelicConfirm": "Bạn có chắc chắn muốn xóa di vật trong ô", + "confirmDelete": "Xác nhận xóa", + "cancel": "Hủy", + "relicDeletedSuccessfully": "Đã xóa di vật thành công", + "profileCreatedSuccessfully": "Đã tạo hồ sơ thành công", + "profileUpdatedSuccessfully": "Đã cập nhật hồ sơ thành công", + "profileDeletedSuccessfully": "Đã xóa hồ sơ thành công", + "profileSwitched": "Đã chuyển sang hồ sơ", + "deleteProfileConfirm": "Bạn có chắc chắn muốn xóa hồ sơ", + "deleteProfile": "Xóa hồ sơ", + "copyText": "Sao chép Text", + "copyImage": "Sao chép Ảnh", + "processing": "Đang xử lý...", + "saving": "Đang lưu...", + "buildInfoCopied": "Đã sao chép thông tin build!", + "copyFailed": "Lỗi khi sao chép thông tin build!", + "imageCopiedToClipboard": "Đã sao chép ảnh vào Clipboard!", + "clipboardWriteFailed": "Trình duyệt không cho phép sao chép ảnh trực tiếp!", + "imageSavedSuccessfully": "Đã lưu ảnh thành công!", + "failedToGenerateImage": "Không thể tạo ảnh!", + "errorGeneratingShowcaseCard": "Lỗi khi tạo ảnh showcase!", + "feature": "Tính năng", + "fix": "Sửa lỗi", + "improvement": "Cải tiến", + "version": "Phiên bản", + "setEffects": "Hiệu ứng bộ", "details": "Chi tiết", "normal": "Đánh thường", "bpskill": "Chiến kỹ", @@ -257,7 +305,6 @@ "upRoll": "Tăng dòng", "downRoll": "Giảm dòng", "actions": "Hành động", - "avatars": "Nhân vật", "quickView": "Xem nhanh", "extraSetting": "Cài đặt bổ sung", "disableCensorship": "Tắt kiểm duyệt", diff --git a/messages/zh.json b/messages/zh.json index 921c662..24dc9b5 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -153,10 +153,28 @@ "importFreeSRDataSuccess": "导入 FreeSR 数据成功", "importDatabaseSuccess": "导入数据库成功", "getData": "获取数据", + "fetchingData": "正在获取数据...", "import": "导入", "freeSRImport": "导入 FreeSR", "onlySupportFreeSRJsonFile": "仅支持 FreeSR JSON 文件", "pickAFile": "选择文件", + "changeFile": "更换文件", + "avatars": "角色", + "noFreeSRDataLoaded": "未加载 FreeSR 数据", + "dropFreeSRJsonHint": "将 FreeSR.json 文件拖放到此处,或点击从设备中选择。", + "step1": "步骤 1", + "step2": "步骤 2", + "step3": "步骤 3", + "exportJsonFromFreeSR": "从 FreeSR 导出 JSON", + "dropOrSelectFile": "拖放或选择文件", + "syncCharacterBuilds": "同步角色配置", + "enterUidPlaceholder": "输入 9 位 UID...", + "publicCharacters": "公开展示角色", + "fetchFromEnka": "从 Enka.Network 获取数据", + "fetchEnkaHint": "在上方输入崩坏:星穹铁道 UID 即可同步公开展示的角色。", + "inGameProfile": "游戏内开拓者个人资料", + "displayCharactersPublicly": "将展示角色设为公开", + "enterUidAndSync": "输入 UID 并同步", "lightConeSetting": "光锥设置", "relicMaker": "遗器制作", "pleaseSelectAllOptions": "请选择所有选项", @@ -218,10 +236,40 @@ "searchStage": "搜索关卡...", "noStageFound": "未找到关卡", "searchMonster": "搜索怪物...", + "HEAD": "头部", + "HAND": "手部", + "BODY": "躯干", + "FOOT": "脚部", + "NECK": "位面球", + "OBJECT": "连结绳", "changeRelic": "更换遗物", "deleteRelic": "删除遗物", "deleteRelicConfirm": "确定要删除插槽中的遗物吗", - "setEffects": "设置效果", + "confirmDelete": "确认删除", + "cancel": "取消", + "relicDeletedSuccessfully": "遗物删除成功", + "profileCreatedSuccessfully": "配置创建成功", + "profileUpdatedSuccessfully": "配置更新成功", + "profileDeletedSuccessfully": "配置删除成功", + "profileSwitched": "已切换至配置", + "deleteProfileConfirm": "确定要删除此配置吗", + "deleteProfile": "删除配置", + "copyText": "复制文本", + "copyImage": "复制图片", + "processing": "处理中...", + "saving": "保存中...", + "buildInfoCopied": "已复制配装信息到剪贴板!", + "copyFailed": "复制配装信息失败!", + "imageCopiedToClipboard": "已复制图片到剪贴板!", + "clipboardWriteFailed": "浏览器不支持直接写入剪贴板!", + "imageSavedSuccessfully": "图片保存成功!", + "failedToGenerateImage": "生成图片失败!", + "errorGeneratingShowcaseCard": "生成展示卡片出错!", + "feature": "新特性", + "fix": "修复", + "improvement": "改进", + "version": "版本", + "setEffects": "套装效果", "details": "详情", "normal": "普通攻击", "bpskill": "技能", @@ -257,7 +305,6 @@ "upRoll": "增加副属性", "downRoll": "减少副属性", "actions": "操作", - "avatars": "头像", "quickView": "快速预览", "extraSetting": "额外设置", "disableCensorship": "禁用审查", diff --git a/src/components/actionBar/index.tsx b/src/components/actionBar/index.tsx index ad3707b..844f612 100644 --- a/src/components/actionBar/index.tsx +++ b/src/components/actionBar/index.tsx @@ -77,6 +77,8 @@ export default function ActionBar() { return avatar?.profileList?.[avatar?.profileSelect] ?? avatar?.profileList?.[0] ?? null; }, [avatarSelected, avatars]); + const [profileToDelete, setProfileToDelete] = useState<{ id: number; name: string } | null>(null); + const listProfile = useMemo(() => { if (!avatarSelected) return []; const avatar = avatars[avatarSelected.ID]; @@ -93,13 +95,13 @@ export default function ActionBar() { }; const newListProfile = [...listProfile, newProfile]; setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: newListProfile.length - 1 }) - toast.success("Profile created successfully") + toast.success(transI18n("profileCreatedSuccessfully")) } else if (formState === "EDIT" && profileCurrent && avatarSelected && profileEdit !== -1) { const newListProfile = listProfile.map((p, idx) => idx === profileEdit ? { ...p, profile_name: profileName } : p ); setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile }) - toast.success("Profile updated successfully") + toast.success(transI18n("profileUpdatedSuccessfully")) } handleCloseModal("update_profile_modal"); }; @@ -129,22 +131,33 @@ export default function ActionBar() { setAvatar({ ...avatars[avatarSelected.ID], profileSelect: profileId }) const selectedProfile = avatars[avatarSelected.ID].profileList?.[profileId]; if (selectedProfile) { - toast.success(`Profile changed to: ${selectedProfile.profile_name}`) + toast.success(`${transI18n("profileSwitched")}: ${selectedProfile.profile_name}`) } } - const handleDeleteProfile = (profileId: number, e: React.MouseEvent) => { + const requestDeleteProfile = (profileId: number, e: React.MouseEvent) => { e.stopPropagation() if (!avatarSelected || profileId === 0 || !avatars[avatarSelected.ID]) return; const targetProfile = avatars[avatarSelected.ID].profileList?.[profileId]; if (!targetProfile) return; - if (window.confirm(`Are you sure you want to delete profile: ${targetProfile.profile_name}?`)) { - const currentSelect = avatars[avatarSelected.ID].profileSelect; - const newListProfile = listProfile.filter((_, idx) => idx !== profileId); - const nextSelect = currentSelect >= profileId ? Math.max(0, currentSelect - 1) : currentSelect; - setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: nextSelect }) - toast.success(`Profile ${targetProfile.profile_name} deleted successfully`) - } + setProfileToDelete({ id: profileId, name: targetProfile.profile_name }); + handleShow("delete_profile_confirm_modal"); + } + + const handleConfirmDeleteProfile = () => { + if (!avatarSelected || !profileToDelete || !avatars[avatarSelected.ID]) return; + const currentSelect = avatars[avatarSelected.ID].profileSelect; + const newListProfile = listProfile.filter((_, idx) => idx !== profileToDelete.id); + const nextSelect = currentSelect >= profileToDelete.id ? Math.max(0, currentSelect - 1) : currentSelect; + setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: nextSelect }) + toast.success(transI18n("profileDeletedSuccessfully")) + handleCloseModal("delete_profile_confirm_modal"); + setProfileToDelete(null); + } + + const handleCancelDeleteProfile = () => { + handleCloseModal("delete_profile_confirm_modal"); + setProfileToDelete(null); } const handleConnectOrSyncPS = async () => { @@ -193,7 +206,7 @@ export default function ActionBar() {
@@ -490,6 +503,7 @@ export default function ActionBar() { {title} + +
+ + +
+ +
+ ); } \ No newline at end of file diff --git a/src/components/card/characterInfoCard.tsx b/src/components/card/characterInfoCard.tsx index 5de4b3e..54e496a 100644 --- a/src/components/card/characterInfoCard.tsx +++ b/src/components/card/characterInfoCard.tsx @@ -8,6 +8,7 @@ import ParseText from '../parseText'; import useDetailDataStore from '@/stores/detailDataStore'; import { getLocaleName } from '@/helper/getName'; import { useShallow } from 'zustand/react/shallow'; +import { Check } from 'lucide-react'; export default function CharacterInfoCard({ character, @@ -35,15 +36,24 @@ export default function CharacterInfoCard({ return (
onCharacterToggle(character)} > - {/* Character Portrait */} -
+ {/* Selection Checkmark Badge */} +
+ +
+ + {/* Character Portrait Area */} +
{avatar && ( <> - {damageType?.[avatar.DamageType]?.Icon && ( -
- {avatar.DamageType.toLowerCase()} -
- )} - {baseType?.[avatar.BaseType]?.Icon && ( -
- {avatar.BaseType.toLowerCase()} -
- )} + {/* Damage & Path Badges */} +
+ {damageType?.[avatar.DamageType]?.Icon && ( +
+ {avatar.DamageType.toLowerCase()} +
+ )} + {baseType?.[avatar.BaseType]?.Icon && ( +
+ {avatar.BaseType.toLowerCase()} +
+ )} +
)}
- {/* Character Name and Level Badge */} -
+ {/* Character Name & Badges */} +
-
- +
+ Lv.{character.level} - + E{character.rank}
- {/* Relics Mini-grid */} + {/* Relics Grid (2 rows of 3, no overflow) */} {character.relics.length > 0 && ( -
+
{character.relics.map((relic, index) => (
-
+
Relic
-
+
+{relic.level}
@@ -126,29 +139,31 @@ export default function CharacterInfoCard({
)} - {/* Light Cone */} + {/* Light Cone Preview (Seamless strip) */} {character.lightcone && character.lightcone.item_id !== 0 && ( -
-
+
+
Lightcone
-
- -
-
- Lv.{character.lightcone.level} - S{character.lightcone.rank} +
+
+ +
+
+ Lv.{character.lightcone.level} + S{character.lightcone.rank} +
)} diff --git a/src/components/card/relicCard.tsx b/src/components/card/relicCard.tsx index f51a33c..b6bb0bb 100644 --- a/src/components/card/relicCard.tsx +++ b/src/components/card/relicCard.tsx @@ -5,6 +5,7 @@ import useUserDataStore from "@/stores/userDataStore"; import { useShallow } from "zustand/react/shallow"; import Image from "next/image"; import { useMemo } from "react"; +import { useTranslations } from "next-intl"; interface RelicCardProps { slot: string @@ -26,36 +27,23 @@ const getRarityColor = (rarity: string) => { } }; -const getSlotIconAndName = (slot: string) => { - const slotMap: Record = { - '1': { icon: '/relics/HEAD.png', name: 'Head' }, - '2': { icon: '/relics/HAND.png', name: 'Hands' }, - '3': { icon: '/relics/BODY.png', name: 'Body' }, - '4': { icon: '/relics/FOOT.png', name: 'Feet' }, - '5': { icon: '/relics/NECK.png', name: 'Planar Sphere' }, - '6': { icon: '/relics/OBJECT.png', name: 'Link Rope' }, - }; - - const current = slotMap[slot]; - if (!current) return null; - - return ( -
- {current.name} - {current.name} -
- ); +const slotIconMap: Record = { + '1': { icon: '/relics/HEAD.png', key: 'HEAD' }, + '2': { icon: '/relics/HAND.png', key: 'HAND' }, + '3': { icon: '/relics/BODY.png', key: 'BODY' }, + '4': { icon: '/relics/FOOT.png', key: 'FOOT' }, + '5': { icon: '/relics/NECK.png', key: 'NECK' }, + '6': { icon: '/relics/OBJECT.png', key: 'OBJECT' }, + 'HEAD': { icon: '/relics/HEAD.png', key: 'HEAD' }, + 'HAND': { icon: '/relics/HAND.png', key: 'HAND' }, + 'BODY': { icon: '/relics/BODY.png', key: 'BODY' }, + 'FOOT': { icon: '/relics/FOOT.png', key: 'FOOT' }, + 'NECK': { icon: '/relics/NECK.png', key: 'NECK' }, + 'OBJECT': { icon: '/relics/OBJECT.png', key: 'OBJECT' }, }; export default function RelicCard({ slot, avatarId }: RelicCardProps) { + const transI18n = useTranslations("DataPage"); const { avatars } = useUserDataStore( useShallow((state) => ({ avatars: state.avatars })) ) @@ -63,6 +51,9 @@ export default function RelicCard({ slot, avatarId }: RelicCardProps) { useShallow((state) => ({ selectedRelicSlot: state.selectedRelicSlot })) ) + const slotInfo = slotIconMap[slot]; + const slotDisplayName = slotInfo ? (transI18n(slotInfo.key) || slotInfo.key) : slot; + const relicDetail = useMemo(() => { const avatar = avatars[avatarId]; if (avatar) { @@ -103,7 +94,20 @@ export default function RelicCard({ slot, avatarId }: RelicCardProps) {
- {getSlotIconAndName(slot)} + {slotInfo && ( +
+ {slotDisplayName} + {slotDisplayName} +
+ )}
) : (
@@ -127,7 +131,20 @@ export default function RelicCard({ slot, avatarId }: RelicCardProps) {
- {getSlotIconAndName(slot)} + {slotInfo && ( +
+ {slotDisplayName} + {slotDisplayName} +
+ )}
)}
diff --git a/src/components/changelog/index.tsx b/src/components/changelog/index.tsx index c87d392..2ec28fd 100644 --- a/src/components/changelog/index.tsx +++ b/src/components/changelog/index.tsx @@ -1,8 +1,10 @@ import useLocaleStore from '@/stores/localeStore'; import { useShallow } from 'zustand/react/shallow'; import { Sparkles, Bug, Zap, Package, Calendar } from 'lucide-react'; +import { useTranslations } from 'next-intl'; export default function ChangelogBar() { + const transI18n = useTranslations("DataPage"); const { changelog } = useLocaleStore( useShallow((state) => ({ changelog: state.changelog })) ) @@ -36,13 +38,13 @@ export default function ChangelogBar() { const getTypeLabel = (type: string) => { switch (type) { case 'feature': - return 'Feature'; + return transI18n("feature") || 'Feature'; case 'fix': - return 'Fix'; + return transI18n("fix") || 'Fix'; case 'improvement': - return 'Improvement'; + return transI18n("improvement") || 'Improvement'; default: - return 'Update'; + return transI18n("update") || 'Update'; } }; @@ -59,7 +61,7 @@ export default function ChangelogBar() { {getTypeLabel(change.type)}

- Version {change.version} + {transI18n("version") || "Version"} {change.version}

diff --git a/src/components/header/index.tsx b/src/components/header/index.tsx index 46144e8..4c2bbba 100644 --- a/src/components/header/index.tsx +++ b/src/components/header/index.tsx @@ -646,8 +646,8 @@ export default function Header() { : 'max-w-[92vw] sm:max-w-[88vw] max-h-[90vh]' } max-h-[90vh] flex flex-col bg-base-100 text-base-content border border-base-content/10 shadow-2xl rounded-2xl p-0 overflow-hidden`}> {/* Modal Header */} -
-

+
+

{title}

- {selectedCharacters.length > 0 && ( - - )} + {/* Top Bar: Title & UID Input */} +
+
+
+ + Enka.Network Live + + Showcase Fetcher +
+

+ HSR UID Showcase +

- {Error && ( -
😭 {Error}
- )} - {/* Player Info */} - {enkaData && ( -
-
Name: {enkaData.detailInfo.nickname}
-
UID: {enkaData.detailInfo.uid}
-
Level: {enkaData.detailInfo.level}
-
- )} - - {/* Selection Info */} -
-
- - {transI18n("selectedCharacters")}: - - - {selectedCharacters.length} - -
-
- - + {/* UID Search Input Form */} +
+
+ + setUidInput(e.target.value)} + onKeyDown={handleKeyDown} + className="w-full h-10 pl-10 pr-9 rounded-xl bg-base-200/90 text-base-content border border-base-content/20 text-sm font-mono focus:border-cyan-500 focus:outline-none focus:ring-1 focus:ring-cyan-500 transition-all placeholder:font-sans placeholder:text-xs placeholder:text-base-content/40" + placeholder={transI18n("enterUidPlaceholder")} + /> + {uidInput && ( + + )}
+
- {isLoading && ( -
-
+ + {/* Error Message Banner */} + {errorMsg && ( +
+ + {errorMsg}
)} + + {/* Trailblazer Profile HUD Banner (when loaded) */} + {enkaData && ( +
+
+
+ +
+
+
+ + {enkaData.detailInfo.nickname} + + + Lv.{enkaData.detailInfo.level} + + {enkaData.detailInfo.worldLevel && ( + + WL {enkaData.detailInfo.worldLevel} + + )} +
+
+ UID: {enkaData.detailInfo.uid} + {enkaData.detailInfo.signature && ( + + “{enkaData.detailInfo.signature}” + + )} +
+
+
+ +
+ {transI18n("publicCharacters")}: + + {validCharacters.length} + +
+
+ )} + + {/* Floating Selection & Actions Bar */} + {enkaData && ( +
+ {/* Left: Counter and Quick Select */} +
+
+ + + {transI18n("selectedCharacters")}: + + + {selectedCharacters.length} / {validCharacters.length} + +
+ +
+ +
+ + +
+
+ + {/* Right: Import CTA */} + +
+ )} + + {/* Loading Shimmer State */} + {isLoading && ( +
+ {[1, 2, 3, 4, 5, 6].map((i) => ( +
+
+
+
+
+ ))} +
+ )} + + {/* Empty State (when no UID fetched yet) */} + {!isLoading && !enkaData && ( +
+ {/* Ambient Glow */} +
+ +
+
+ +
+ +
+

+ {transI18n("fetchFromEnka")} +

+

+ {transI18n("fetchEnkaHint")} +

+
+ + {/* Step hints */} +
+
+ {transI18n("step1")} +

{transI18n("inGameProfile")}

+
+
+ {transI18n("step2")} +

{transI18n("displayCharactersPublicly")}

+
+
+ {transI18n("step3")} +

{transI18n("enterUidAndSync")}

+
+
+
+
+ )} + {/* Character Grid */} -
- {validCharacters.map((character) => ( - - ))} -
+ {!isLoading && enkaData && ( +
+ {validCharacters.map((character) => ( + + ))} +
+ )}
); } + diff --git a/src/components/importBar/freesr.tsx b/src/components/importBar/freesr.tsx index 992b219..3870ef4 100644 --- a/src/components/importBar/freesr.tsx +++ b/src/components/importBar/freesr.tsx @@ -4,7 +4,7 @@ import useFreeSRStore from "@/stores/freesrStore"; import useModelStore from "@/stores/modelStore"; import useUserDataStore from "@/stores/userDataStore"; import { CharacterInfoCardType } from "@/types"; -import { useState } from "react"; +import { useState, useRef } from "react"; import CharacterInfoCard from "../card/characterInfoCard"; import { freeSrJsonSchema } from "@/zod"; import { toast } from "react-toastify"; @@ -13,12 +13,22 @@ import { useTranslations } from "next-intl"; import useDetailDataStore from "@/stores/detailDataStore"; import { useShallow } from "zustand/react/shallow"; import { z } from "zod"; +import { + UploadCloud, + FileJson, + CheckCheck, + RotateCcw, + AlertCircle, + ArrowRight, + Users, + FileCheck +} from "lucide-react"; type FreeSRJsonInput = z.infer; const toCharacterInfoCard = (data: FreeSRJsonInput, character: FreeSRJsonInput["avatars"][string]): CharacterInfoCardType => { - const lightcone = data.lightcones.find((lightcone) => lightcone.equip_avatar === character.avatar_id) - const relics = data.relics.filter((relic) => relic.equip_avatar === character.avatar_id) + const lightcone = data.lightcones?.find((lc) => lc.equip_avatar === character.avatar_id); + const relics = data.relics?.filter((relic) => relic.equip_avatar === character.avatar_id) || []; return { key: character.avatar_id, @@ -35,8 +45,8 @@ const toCharacterInfoCard = (data: FreeSRJsonInput, character: FreeSRJsonInput[" relic_id: relic.relic_id, relic_set_id: relic.relic_set_id, })), - } -} + }; +}; export default function FreeSRImport() { const { avatars, setAvatar } = useUserDataStore( @@ -54,9 +64,14 @@ export default function FreeSRImport() { useShallow((state) => ({ setIsOpenImport: state.setIsOpenImport, })) - ) - const [isLoading, setIsLoading] = useState(false) - const [Error, setError] = useState("") + ); + + const [isLoading, setIsLoading] = useState(false); + const [errorMsg, setErrorMsg] = useState(""); + const [fileName, setFileName] = useState(null); + const [isDragging, setIsDragging] = useState(false); + const fileInputRef = useRef(null); + const { freeSRData, setFreeSRData, selectedCharacters, setSelectedCharacters } = useFreeSRStore( useShallow((state) => ({ freeSRData: state.freeSRData, @@ -64,14 +79,19 @@ export default function FreeSRImport() { selectedCharacters: state.selectedCharacters, setSelectedCharacters: state.setSelectedCharacters, })) - ) - const transI18n = useTranslations("DataPage") - const parsedFreeSRData = freeSrJsonSchema.safeParse(freeSRData) - const validFreeSRData = parsedFreeSRData.success ? parsedFreeSRData.data : null + ); + + const transI18n = useTranslations("DataPage"); + const parsedFreeSRData = freeSrJsonSchema.safeParse(freeSRData); + const validFreeSRData = parsedFreeSRData.success ? parsedFreeSRData.data : null; + + const availableCharacters = validFreeSRData + ? Object.values(validFreeSRData.avatars).filter(it => mapAvatar?.[it.avatar_id]) + : []; const handleCharacterToggle = (character: CharacterInfoCardType) => { - if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) { - setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key)); + if (selectedCharacters.some((selected) => selected.key === character.key)) { + setSelectedCharacters(selectedCharacters.filter((selected) => selected.key !== character.key)); return; } setSelectedCharacters([...selectedCharacters, character]); @@ -82,93 +102,118 @@ export default function FreeSRImport() { }; const selectAll = () => { - const parsed = freeSrJsonSchema.safeParse(freeSRData) - if (parsed.success) { - setSelectedCharacters(Object.values(parsed.data.avatars).filter(it => mapAvatar?.[it.avatar_id]).map((character) => toCharacterInfoCard(parsed.data, character))); + if (validFreeSRData) { + setSelectedCharacters(availableCharacters.map((char) => toCharacterInfoCard(validFreeSRData, char))); } }; - const handlerReadFile = (event: React.ChangeEvent) => { - setIsLoading(true) - const file = event.target.files?.[0]; - if (!file) { - setSelectedCharacters([]) - setFreeSRData(null) - setError(transI18n("pleaseSelectAFile")) - setIsLoading(false) - return - } - if (!file.name.endsWith(".json") || file.type !== "application/json") { - setSelectedCharacters([]) - setFreeSRData(null) - setError(transI18n("fileMustBeAValidJsonFile")) - setIsLoading(false) - return + const processFile = (file: File) => { + setIsLoading(true); + if (!file.name.endsWith(".json") && file.type !== "application/json") { + setSelectedCharacters([]); + setFreeSRData(null); + setFileName(null); + setErrorMsg(transI18n("fileMustBeAValidJsonFile")); + setIsLoading(false); + return; } - if (file) { - - const reader = new FileReader(); - reader.onload = (e) => { - try { - const data = JSON.parse(e.target?.result as string); - const parsed = freeSrJsonSchema.safeParse(data) - if (!parsed.success) { - setSelectedCharacters([]) - setFreeSRData(null) - setError(transI18n("fileMustBeAValidJsonFile")) - return - } - - setFreeSRData(parsed.data) - setError("") - - setSelectedCharacters(Object.values(parsed.data.avatars).filter(it => mapAvatar?.[it.avatar_id]).map((character) => toCharacterInfoCard(parsed.data, character))); - } catch { - - setSelectedCharacters([]) - setFreeSRData(null) - setError(transI18n("fileMustBeAValidJsonFile")) - } finally { - setIsLoading(false) + const reader = new FileReader(); + reader.onload = (e) => { + try { + const raw = JSON.parse(e.target?.result as string); + const parsed = freeSrJsonSchema.safeParse(raw); + if (!parsed.success) { + setSelectedCharacters([]); + setFreeSRData(null); + setFileName(null); + setErrorMsg(transI18n("fileMustBeAValidJsonFile")); + return; } - }; - reader.onerror = () => { - setSelectedCharacters([]) - setFreeSRData(null) - setError(transI18n("fileMustBeAValidJsonFile")) - setIsLoading(false) + + setFreeSRData(parsed.data); + setFileName(file.name); + setErrorMsg(""); + + const validChars = Object.values(parsed.data.avatars) + .filter(it => mapAvatar?.[it.avatar_id]) + .map((char) => toCharacterInfoCard(parsed.data, char)); + setSelectedCharacters(validChars); + } catch { + setSelectedCharacters([]); + setFreeSRData(null); + setFileName(null); + setErrorMsg(transI18n("fileMustBeAValidJsonFile")); + } finally { + setIsLoading(false); } - reader.readAsText(file); + }; + + reader.onerror = () => { + setSelectedCharacters([]); + setFreeSRData(null); + setFileName(null); + setErrorMsg(transI18n("fileMustBeAValidJsonFile")); + setIsLoading(false); + }; + + reader.readAsText(file); + }; + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + processFile(file); } }; + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + const file = e.dataTransfer.files?.[0]; + if (file) { + processFile(file); + } + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = () => { + setIsDragging(false); + }; + const handleImport = () => { if (selectedCharacters.length === 0) { - setError(transI18n("pleaseSelectAtLeastOneCharacter")); + setErrorMsg(transI18n("pleaseSelectAtLeastOneCharacter")); return; } if (!freeSRData) { - setError(transI18n("noDataToImport")); + setErrorMsg(transI18n("noDataToImport")); return; } - setError(""); + setErrorMsg(""); - const parsed = freeSrJsonSchema.safeParse(freeSRData) + const parsed = freeSrJsonSchema.safeParse(freeSRData); if (!parsed.success) { - setError(transI18n("fileMustBeAValidJsonFile")); + setErrorMsg(transI18n("fileMustBeAValidJsonFile")); return; } - const listAvatars = { ...avatars } - const filterData = Object.values(parsed.data.avatars).filter((character) => selectedCharacters.some((selectedCharacter) => selectedCharacter.avatar_id === character.avatar_id)) + const listAvatars = { ...avatars }; + const filterData = Object.values(parsed.data.avatars).filter((character) => + selectedCharacters.some((selected) => selected.avatar_id === character.avatar_id) + ); + filterData.forEach((character) => { - const currentAvatar = listAvatars[character.avatar_id] + const currentAvatar = listAvatars[character.avatar_id]; if (currentAvatar && Object.keys(currentAvatar).length !== 0) { - const newProfileList = [...(currentAvatar.profileList || [])] - const newProfile = converterOneFreeSRDataToAvatarStore(parsed.data, newProfileList.length, character.avatar_id) + const newProfileList = [...(currentAvatar.profileList || [])]; + const newProfile = converterOneFreeSRDataToAvatarStore(parsed.data, newProfileList.length, character.avatar_id); if (newProfile) { - newProfileList.push(newProfile) + newProfileList.push(newProfile); } const newAvatar = { ...currentAvatar, @@ -180,81 +225,218 @@ export default function FreeSRImport() { }, profileList: newProfileList, profileSelect: newProfile ? newProfileList.length - 1 : currentAvatar.profileSelect, - } - setAvatar(newAvatar) + }; + setAvatar(newAvatar); } - }) - setIsOpenImport(false) - toast.success(transI18n("importFreeSRDataSuccess")) - } + }); + + setIsOpenImport(false); + toast.success(transI18n("importFreeSRDataSuccess")); + }; return (
- {/* Header */} -
-

{transI18n("freeSRImport")}

- -
-
- {transI18n("pickAFile")} - - -
- - {selectedCharacters.length > 0 && ( - - )} + {/* Top Bar: Title & Upload Trigger */} +
+
+
+ + FreeSR Protocol + + JSON Importer +
+

+ {transI18n("freeSRImport")} +

- {Error && ( -
😭 {Error}
+ + {/* File Dropzone / Active File Badge */} + + + {!validFreeSRData ? ( +
fileInputRef.current?.click()} + onDrop={handleDrop} + onDragOver={handleDragOver} + onDragLeave={handleDragLeave} + className={`flex items-center gap-3 px-4 py-2.5 rounded-xl border border-dashed cursor-pointer transition-all duration-200 select-none w-full sm:w-auto ${ + isDragging + ? 'bg-primary/15 border-primary shadow-[0_0_20px_rgba(var(--color-primary-rgb,6,182,212),0.3)] scale-[1.02]' + : 'bg-base-200/50 hover:bg-base-200/80 border-white/20 hover:border-primary/50' + }`} + > +
+ +
+
+

+ {transI18n("pickAFile")} + (.json) +

+

+ {transI18n("onlySupportFreeSRJsonFile")} +

+
+
+ ) : ( +
+
+
+ + {fileName || "data.json"} +
+ + {availableCharacters.length} {transI18n("avatars")} + +
+ +
)} - - {/* Selection Info */} -
-
- - {transI18n("selectedCharacters")}: - - - {selectedCharacters.length} - -
-
- - -
-
- {isLoading && ( -
-
+ + {/* Error Message */} + {errorMsg && ( +
+ + {errorMsg}
)} + + {/* Floating Selection & Actions Bar */} + {validFreeSRData && ( +
+ {/* Left: Counter and Quick Select */} +
+
+ + + {transI18n("selectedCharacters")}: + + + {selectedCharacters.length} / {availableCharacters.length} + +
+ +
+ +
+ + +
+
+ + {/* Right: Import CTA */} + +
+ )} + + {/* Loading Shimmer State */} + {isLoading && ( +
+ {[1, 2, 3, 4, 5, 6].map((i) => ( +
+
+
+
+
+ ))} +
+ )} + + {/* Empty State (when no valid data is loaded) */} + {!isLoading && !validFreeSRData && ( +
fileInputRef.current?.click()} + onDrop={handleDrop} + onDragOver={handleDragOver} + onDragLeave={handleDragLeave} + className="relative overflow-hidden py-12 sm:py-16 px-4 sm:px-6 rounded-2xl border border-dashed border-white/15 bg-base-200/20 hover:bg-base-200/40 transition-all duration-300 text-center cursor-pointer group select-none" + > + {/* Ambient Glow */} +
+ +
+
+ +
+ +
+

+ {transI18n("noFreeSRDataLoaded")} +

+

+ {transI18n("dropFreeSRJsonHint")} +

+
+ + {/* Step hints */} +
+
+ {transI18n("step1")} +

{transI18n("exportJsonFromFreeSR")}

+
+
+ {transI18n("step2")} +

{transI18n("dropOrSelectFile")}

+
+
+ {transI18n("step3")} +

{transI18n("syncCharacterBuilds")}

+
+
+
+
+ )} + {/* Character Grid */} -
- {validFreeSRData && Object.values(validFreeSRData.avatars).filter(it => mapAvatar?.[it.avatar_id]).map((character) => { - return ( + {!isLoading && validFreeSRData && ( +
+ {availableCharacters.map((character) => ( - ) - })} -
+ ))} +
+ )}
- ) + ); } + diff --git a/src/components/quickView/index.tsx b/src/components/quickView/index.tsx index 6a6459e..e338900 100644 --- a/src/components/quickView/index.tsx +++ b/src/components/quickView/index.tsx @@ -401,7 +401,8 @@ export default function QuickView() { subAffix, relicEffects, mapRelicSet, - avatarSkillTree + avatarSkillTree, + transI18n ]) return ( diff --git a/src/components/relicsInfo/index.tsx b/src/components/relicsInfo/index.tsx index b910b6b..18b755f 100644 --- a/src/components/relicsInfo/index.tsx +++ b/src/components/relicsInfo/index.tsx @@ -1,6 +1,7 @@ /* eslint-disable react-hooks/exhaustive-deps */ "use client"; -import { useCallback, useEffect, useMemo } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import Image from "next/image"; import RelicMaker from "../relicBar"; import useUserDataStore from "@/stores/userDataStore"; import { useTranslations } from "next-intl"; @@ -15,6 +16,8 @@ import useDetailDataStore from "@/stores/detailDataStore"; import { getLocaleName } from '@/helper'; import useLocaleStore from "@/stores/localeStore"; import { useShallow } from 'zustand/react/shallow'; +import { Trash2 } from "lucide-react"; +import { toast } from "react-toastify"; export default function RelicsInfo() { const { avatars, setAvatar } = useUserDataStore( @@ -78,6 +81,8 @@ export default function RelicsInfo() { })) ) + const [relicSlotToDelete, setRelicSlotToDelete] = useState(null); + const handleShow = (modalId: string) => { const modal = document.getElementById(modalId) as HTMLDialogElement | null; if (modal) { @@ -125,6 +130,25 @@ export default function RelicsInfo() { } }; + const requestDeleteRelic = (slot: string) => { + setRelicSlotToDelete(slot); + handleShow("delete_relic_confirm_modal"); + }; + + const handleConfirmDelete = () => { + if (relicSlotToDelete) { + handlerDeleteRelic(relicSlotToDelete); + toast.success(transI18n("relicDeletedSuccessfully")); + handleCloseModal("delete_relic_confirm_modal"); + setRelicSlotToDelete(null); + } + }; + + const handleCancelDelete = () => { + handleCloseModal("delete_relic_confirm_modal"); + setRelicSlotToDelete(null); + }; + const handlerChangeRelic = (slot: string) => { const relic = getRelic(slot) setSelectedRelicSlot(slot) @@ -217,6 +241,7 @@ export default function RelicsInfo() { for (const item of modalConfigs) { handleCloseModal(item?.id || "") } + handleCloseModal("delete_relic_confirm_modal") } }; @@ -250,23 +275,15 @@ export default function RelicsInfo() {
{/* 6-Slot Grid completely free of box-in-box */} -
+
{["1", "2", "3", "4", "5", "6"].map((item, index) => (
{ - if (item === selectedRelicSlot) { - setSelectedRelicSlot("") - } else { - setSelectedRelicSlot(item) - } - handlerChangeRelic(item) - }} - className={`relative group flex flex-col items-center justify-center p-2 rounded-2xl transition-all duration-200 cursor-pointer ${ - item === selectedRelicSlot - ? "bg-primary/5 rounded-2xl ring-1.5 ring-primary/40" - : "hover:bg-base-content/5" - }`} + onClick={() => setSelectedRelicSlot(item)} + className={`group relative rounded-xl transition-all duration-200 cursor-pointer ${selectedRelicSlot === item + ? "ring-2 ring-primary ring-offset-2 ring-offset-base-100 shadow-md" + : "hover:ring-1 hover:ring-base-content/20" + }`} > @@ -413,6 +432,7 @@ export default function RelicsInfo() { {title}

+ +
+
+
+
+ +
+
); } diff --git a/src/components/showcaseCard/index.tsx b/src/components/showcaseCard/index.tsx index d1acaaf..040e8d9 100644 --- a/src/components/showcaseCard/index.tsx +++ b/src/components/showcaseCard/index.tsx @@ -48,7 +48,7 @@ export default function ShowCaseInfo() { const handleSaveImage = useCallback(async () => { if (cardRef.current === null || !avatarSelected) { - toast.error("Avatar showcase not found!"); + toast.error(transI18n("noAvatarSelected") || "Avatar showcase not found!"); return; } if (isSaving) return; @@ -78,7 +78,7 @@ export default function ShowCaseInfo() { canvas.toBlob((blob) => { if (!blob) { - toast.error("Failed to generate image!"); + toast.error(transI18n("failedToGenerateImage")); setIsSaving(false); return; } @@ -89,10 +89,10 @@ export default function ShowCaseInfo() { link.click(); URL.revokeObjectURL(url); setIsSaving(false); - toast.success(locale === "vi" ? "Đã lưu ảnh thành công!" : "Image saved successfully!"); + toast.success(transI18n("imageSavedSuccessfully")); }, "image/png"); } catch { - toast.error("Error generating showcase card!"); + toast.error(transI18n("errorGeneratingShowcaseCard")); setIsSaving(false); } }, [avatarSelected, locale, transI18n, isSaving]); @@ -638,16 +638,16 @@ export default function ShowCaseInfo() { const textToCopy = lines.join("\n"); navigator.clipboard.writeText(textToCopy) .then(() => { - toast.success(locale === "vi" ? "Đã sao chép thông tin build!" : "Build info copied to clipboard!"); + toast.success(transI18n("buildInfoCopied")); }) .catch(() => { - toast.error(locale === "vi" ? "Lỗi khi sao chép!" : "Failed to copy build info!"); + toast.error(transI18n("copyFailed")); }); }, [avatarSelected, avatarData, avatarProfile, mapLightCone, locale, transI18n, characterStats, relicEffects, mapRelicSet, relicStats]); const handleCopyImage = useCallback(async () => { if (cardRef.current === null || !avatarSelected) { - toast.error("Avatar showcase not found!"); + toast.error(transI18n("noAvatarSelected") || "Avatar showcase not found!"); return; } if (isCopyingImage) return; @@ -677,25 +677,25 @@ export default function ShowCaseInfo() { canvas.toBlob((blob) => { if (!blob) { - toast.error("Failed to create image blob"); + toast.error(transI18n("failedToGenerateImage")); setIsCopyingImage(false); return; } navigator.clipboard.write([ new ClipboardItem({ "image/png": blob }) ]).then(() => { - toast.success(locale === "vi" ? "Đã sao chép ảnh vào Clipboard!" : "Image copied to clipboard!"); + toast.success(transI18n("imageCopiedToClipboard")); }).catch(() => { - toast.error(locale === "vi" ? "Trình duyệt không cho phép sao chép ảnh trực tiếp!" : "Clipboard write failed!"); + toast.error(transI18n("clipboardWriteFailed")); }).finally(() => { setIsCopyingImage(false); }); }, "image/png"); } catch { - toast.error("Error generating showcase image!"); + toast.error(transI18n("errorGeneratingShowcaseCard")); setIsCopyingImage(false); } - }, [avatarSelected, locale, isCopyingImage]); + }, [avatarSelected, isCopyingImage, transI18n]); return (
@@ -712,23 +712,23 @@ export default function ShowCaseInfo() {