diff --git a/src/App.jsx b/src/App.jsx index 743799e..9283dc4 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useRef, useState } from "react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; import { convertFileSrc } from "@tauri-apps/api/core"; import { basename, join } from "@tauri-apps/api/path"; import { open } from "@tauri-apps/plugin-dialog"; @@ -7,6 +7,7 @@ import { copyFile, exists, readTextFile, writeTextFile } from "@tauri-apps/plugi import { parseLuaItems, buildLuaFromItems } from "./parser/luaParser.js"; import pkg from "../package.json"; import appConfig from "./config/appConfig.js"; +import { languageOptions, translations } from "./i18n/translations.js"; const categoryIcons = { All: "/icons/all.png", @@ -38,421 +39,6 @@ const ammoOptions = [ const ALL_CATEGORY = "__ALL__"; const UNCATEGORIZED = "Nicht zugeordnet"; -const languageOptions = [ - { value: "de", label: "Deutsch", flagSrc: "/flags/de.svg", flagAlt: "DE" }, - { value: "en", label: "English", flagSrc: "/flags/us.svg", flagAlt: "US" }, - { value: "es", label: "Espanol", flagSrc: "/flags/es.svg", flagAlt: "ES" }, - { value: "fr", label: "Francais", flagSrc: "/flags/fr.svg", flagAlt: "FR" }, - { value: "pt-BR", label: "Portugues (BR)", flagSrc: "/flags/pt-br.svg", flagAlt: "BR" }, - { value: "it", label: "Italiano", flagSrc: "/flags/it.svg", flagAlt: "IT" }, - { value: "pl", label: "Polski", flagSrc: "/flags/pl.svg", flagAlt: "PL" }, - { value: "tr", label: "Turkce", flagSrc: "/flags/tr.svg", flagAlt: "TR" }, - { value: "ru", label: "Russkiy", flagSrc: "/flags/ru.svg", flagAlt: "RU" }, -]; -const translations = { - de: { - allItems: "Alle Items", - uncategorized: "Nicht zugeordnet", - categories: "Kategorien", - category: "Kategorie", - newCategoryPlaceholder: "Neue Kategorie anlegen...", - searchPlaceholder: "Item suchen...", - inventoryOpen: "Inventory \u00f6ffnen", - inventoryLabel: "Inventory", - loadedFile: "Geladen", - chooseItemHint: "W\u00e4hle links eine Kategorie und dann ein Item aus.", - applyChanges: "\u00c4nderungen \u00fcbernehmen", - selectInventoryTitle: "QS Inventory Ordner ausw\u00e4hlen", - itemsFileMissing: "items.lua nicht gefunden. Bitte den qs-inventory Ordner w\u00e4hlen.", - imagesMissing: "Bilderordner nicht gefunden: qs-inventory\\html\\images", - noItemsDetected: - "Keine Items erkannt. Stelle sicher, dass die Datei mit `ItemList = {` oder `QBShared.Items = {` beginnt.", - loadedItems: ({ count }) => `Geladen: ${count} Items`, - loadError: ({ message }) => `Fehler beim Laden: ${message}`, - saved: "Gespeichert", - saveError: ({ message }) => `Fehler beim Speichern: ${message}`, - backupError: ({ message }) => `Backup konnte nicht erstellt werden: ${message}`, - language: "Sprache", - newItem: "Neues Item", - template: "Template", - create: "Erstellen", - cancel: "Abbrechen", - itemId: "Item ID", - itemLabel: "Label", - idRequired: "Bitte eine Item-ID angeben.", - idExists: ({ id }) => `Item-ID existiert bereits: ${id}`, - templateNormal: "Normale Items", - templateNormalRare: "Normale Items mit Rarit\u00e4t", - templateWeapon: "Waffen", - templateFood: "Nahrungsmittel", - templateDrink: "Getränk", - selectTemplateTitle: "Template auswählen", - createItemTitle: "Neues Item bearbeiten", - close: "Schließen", - itemCreated: "Item gespeichert", - imageAdd: "Bild hinzufügen", - imageAddError: ({ message }) => `Bild konnte nicht hinzugefügt werden: ${message}`, - imageAdded: "Bild hinzugefügt", - }, - en: { - allItems: "All Items", - uncategorized: "Unassigned", - categories: "Categories", - category: "Category", - newCategoryPlaceholder: "Create new category...", - searchPlaceholder: "Search items...", - inventoryOpen: "Open inventory", - inventoryLabel: "Inventory", - loadedFile: "Loaded", - chooseItemHint: "Choose a category on the left and then select an item.", - applyChanges: "Apply changes", - selectInventoryTitle: "Select QS inventory folder", - itemsFileMissing: "items.lua not found. Please select the qs-inventory folder.", - imagesMissing: "Images folder not found: qs-inventory\\html\\images", - noItemsDetected: - "No items detected. Ensure the file starts with `ItemList = {` or `QBShared.Items = {`.", - loadedItems: ({ count }) => `Loaded: ${count} items`, - loadError: ({ message }) => `Load error: ${message}`, - saved: "Saved", - saveError: ({ message }) => `Save error: ${message}`, - backupError: ({ message }) => `Backup failed: ${message}`, - language: "Language", - newItem: "New Item", - template: "Template", - create: "Create", - cancel: "Cancel", - itemId: "Item ID", - itemLabel: "Label", - idRequired: "Please provide an item ID.", - idExists: ({ id }) => `Item ID already exists: ${id}`, - templateNormal: "Normal Items", - templateNormalRare: "Normal Items with Rarity", - templateWeapon: "Weapons", - templateFood: "Food", - templateDrink: "Drink", - selectTemplateTitle: "Select template", - createItemTitle: "Edit new item", - close: "Close", - itemCreated: "Item saved", - imageAdd: "Add image", - imageAddError: ({ message }) => `Failed to add image: ${message}`, - imageAdded: "Image added", - }, - es: { - allItems: "Todos los items", - uncategorized: "Sin asignar", - categories: "Categorías", - category: "Categoría", - newCategoryPlaceholder: "Crear nueva categoría...", - searchPlaceholder: "Buscar item...", - inventoryOpen: "Abrir inventario", - inventoryLabel: "Inventario", - loadedFile: "Cargado", - chooseItemHint: "Elige una categoría a la izquierda y luego selecciona un item.", - applyChanges: "Aplicar cambios", - selectInventoryTitle: "Seleccionar carpeta de QS inventory", - itemsFileMissing: "No se encontró items.lua. Selecciona la carpeta qs-inventory.", - imagesMissing: "No se encontró la carpeta de imágenes: qs-inventory\\html\\images", - noItemsDetected: - "No se detectaron items. Asegúrate de que el archivo empiece con `ItemList = {` o `QBShared.Items = {`.", - loadedItems: ({ count }) => `Cargado: ${count} items`, - loadError: ({ message }) => `Error al cargar: ${message}`, - saved: "Guardado", - saveError: ({ message }) => `Error al guardar: ${message}`, - backupError: ({ message }) => `No se pudo crear la copia de seguridad: ${message}`, - language: "Idioma", - newItem: "Nuevo item", - template: "Plantilla", - create: "Crear", - cancel: "Cancelar", - itemId: "ID del item", - itemLabel: "Etiqueta", - idRequired: "Introduce un ID de item.", - idExists: ({ id }) => `El ID del item ya existe: ${id}`, - templateNormal: "Items normales", - templateNormalRare: "Items normales con rareza", - templateWeapon: "Armas", - templateFood: "Comida", - templateDrink: "Bebida", - selectTemplateTitle: "Seleccionar plantilla", - createItemTitle: "Editar nuevo item", - close: "Cerrar", - itemCreated: "Item guardado", - imageAdd: "Añadir imagen", - imageAddError: ({ message }) => `No se pudo añadir la imagen: ${message}`, - imageAdded: "Imagen añadida", - }, - fr: { - allItems: "Tous les items", - uncategorized: "Non attribué", - categories: "Catégories", - category: "Catégorie", - newCategoryPlaceholder: "Créer une nouvelle catégorie...", - searchPlaceholder: "Rechercher un item...", - inventoryOpen: "Ouvrir l'inventaire", - inventoryLabel: "Inventaire", - loadedFile: "Chargé", - chooseItemHint: "Choisis une catégorie à gauche puis sélectionne un item.", - applyChanges: "Appliquer les modifications", - selectInventoryTitle: "Sélectionner le dossier QS inventory", - itemsFileMissing: "items.lua introuvable. Sélectionne le dossier qs-inventory.", - imagesMissing: "Dossier d'images introuvable : qs-inventory\\html\\images", - noItemsDetected: - "Aucun item détecté. Vérifie que le fichier commence par `ItemList = {` ou `QBShared.Items = {`.", - loadedItems: ({ count }) => `Chargé : ${count} items`, - loadError: ({ message }) => `Erreur de chargement : ${message}`, - saved: "Enregistré", - saveError: ({ message }) => `Erreur d'enregistrement : ${message}`, - backupError: ({ message }) => `Échec de la sauvegarde : ${message}`, - language: "Langue", - newItem: "Nouvel item", - template: "Modèle", - create: "Créer", - cancel: "Annuler", - itemId: "ID de l'item", - itemLabel: "Libellé", - idRequired: "Veuillez saisir un ID d'item.", - idExists: ({ id }) => `L'ID de l'item existe déjà : ${id}`, - templateNormal: "Items normaux", - templateNormalRare: "Items normaux avec rareté", - templateWeapon: "Armes", - templateFood: "Nourriture", - templateDrink: "Boisson", - selectTemplateTitle: "Sélectionner un modèle", - createItemTitle: "Modifier le nouvel item", - close: "Fermer", - itemCreated: "Item enregistré", - imageAdd: "Ajouter une image", - imageAddError: ({ message }) => `Impossible d'ajouter l'image : ${message}`, - imageAdded: "Image ajoutée", - }, - "pt-BR": { - allItems: "Todos os itens", - uncategorized: "Nao atribuido", - categories: "Categorias", - category: "Categoria", - newCategoryPlaceholder: "Criar nova categoria...", - searchPlaceholder: "Buscar item...", - inventoryOpen: "Abrir inventario", - inventoryLabel: "Inventario", - loadedFile: "Carregado", - chooseItemHint: "Escolha uma categoria a esquerda e depois selecione um item.", - applyChanges: "Aplicar alteracoes", - selectInventoryTitle: "Selecionar pasta do QS inventory", - itemsFileMissing: "items.lua nao encontrado. Selecione a pasta qs-inventory.", - imagesMissing: "Pasta de imagens nao encontrada: qs-inventory\\html\\images", - noItemsDetected: - "Nenhum item detectado. Verifique se o arquivo comeca com `ItemList = {` ou `QBShared.Items = {`.", - loadedItems: ({ count }) => `Carregado: ${count} itens`, - loadError: ({ message }) => `Erro ao carregar: ${message}`, - saved: "Salvo", - saveError: ({ message }) => `Erro ao salvar: ${message}`, - backupError: ({ message }) => `Falha ao criar backup: ${message}`, - language: "Idioma", - newItem: "Novo item", - template: "Modelo", - create: "Criar", - cancel: "Cancelar", - itemId: "ID do item", - itemLabel: "Rotulo", - idRequired: "Informe um ID de item.", - idExists: ({ id }) => `ID de item ja existe: ${id}`, - templateNormal: "Itens normais", - templateNormalRare: "Itens normais com raridade", - templateWeapon: "Armas", - templateFood: "Comida", - templateDrink: "Bebida", - selectTemplateTitle: "Selecionar modelo", - createItemTitle: "Editar novo item", - close: "Fechar", - itemCreated: "Item salvo", - imageAdd: "Adicionar imagem", - imageAddError: ({ message }) => `Falha ao adicionar imagem: ${message}`, - imageAdded: "Imagem adicionada", - }, - it: { - allItems: "Tutti gli item", - uncategorized: "Non assegnato", - categories: "Categorie", - category: "Categoria", - newCategoryPlaceholder: "Crea una nuova categoria...", - searchPlaceholder: "Cerca item...", - inventoryOpen: "Apri inventario", - inventoryLabel: "Inventario", - loadedFile: "Caricato", - chooseItemHint: "Scegli una categoria a sinistra e poi seleziona un item.", - applyChanges: "Applica modifiche", - selectInventoryTitle: "Seleziona cartella QS inventory", - itemsFileMissing: "items.lua non trovato. Seleziona la cartella qs-inventory.", - imagesMissing: "Cartella immagini non trovata: qs-inventory\\html\\images", - noItemsDetected: - "Nessun item rilevato. Assicurati che il file inizi con `ItemList = {` o `QBShared.Items = {`.", - loadedItems: ({ count }) => `Caricati: ${count} item`, - loadError: ({ message }) => `Errore di caricamento: ${message}`, - saved: "Salvato", - saveError: ({ message }) => `Errore di salvataggio: ${message}`, - backupError: ({ message }) => `Backup non riuscito: ${message}`, - language: "Lingua", - newItem: "Nuovo item", - template: "Modello", - create: "Crea", - cancel: "Annulla", - itemId: "ID item", - itemLabel: "Etichetta", - idRequired: "Inserisci un ID item.", - idExists: ({ id }) => `ID item gia esistente: ${id}`, - templateNormal: "Item normali", - templateNormalRare: "Item normali con rarita", - templateWeapon: "Armi", - templateFood: "Cibo", - templateDrink: "Bevanda", - selectTemplateTitle: "Seleziona modello", - createItemTitle: "Modifica nuovo item", - close: "Chiudi", - itemCreated: "Item salvato", - imageAdd: "Aggiungi immagine", - imageAddError: ({ message }) => `Impossibile aggiungere l'immagine: ${message}`, - imageAdded: "Immagine aggiunta", - }, - pl: { - allItems: "Wszystkie przedmioty", - uncategorized: "Nieprzypisane", - categories: "Kategorie", - category: "Kategoria", - newCategoryPlaceholder: "Utworz nowa kategorie...", - searchPlaceholder: "Szukaj przedmiotu...", - inventoryOpen: "Otworz ekwipunek", - inventoryLabel: "Ekwipunek", - loadedFile: "Wczytano", - chooseItemHint: "Wybierz kategorie po lewej stronie, a nastepnie przedmiot.", - applyChanges: "Zastosuj zmiany", - selectInventoryTitle: "Wybierz folder QS inventory", - itemsFileMissing: "Nie znaleziono items.lua. Wybierz folder qs-inventory.", - imagesMissing: "Nie znaleziono folderu obrazow: qs-inventory\\html\\images", - noItemsDetected: - "Nie wykryto przedmiotow. Upewnij sie, ze plik zaczyna sie od `ItemList = {` lub `QBShared.Items = {`.", - loadedItems: ({ count }) => `Wczytano: ${count} przedmiotow`, - loadError: ({ message }) => `Blad wczytywania: ${message}`, - saved: "Zapisano", - saveError: ({ message }) => `Blad zapisu: ${message}`, - backupError: ({ message }) => `Nie udalo sie utworzyc kopii zapasowej: ${message}`, - language: "Jezyk", - newItem: "Nowy przedmiot", - template: "Szablon", - create: "Utworz", - cancel: "Anuluj", - itemId: "ID przedmiotu", - itemLabel: "Etykieta", - idRequired: "Podaj ID przedmiotu.", - idExists: ({ id }) => `ID przedmiotu juz istnieje: ${id}`, - templateNormal: "Zwykle przedmioty", - templateNormalRare: "Zwykle przedmioty z rzadkoscia", - templateWeapon: "Bron", - templateFood: "Jedzenie", - templateDrink: "Napoj", - selectTemplateTitle: "Wybierz szablon", - createItemTitle: "Edytuj nowy przedmiot", - close: "Zamknij", - itemCreated: "Przedmiot zapisany", - imageAdd: "Dodaj obraz", - imageAddError: ({ message }) => `Nie udalo sie dodac obrazu: ${message}`, - imageAdded: "Obraz dodany", - }, - tr: { - allItems: "Tum ogeler", - uncategorized: "Atanmamis", - categories: "Kategoriler", - category: "Kategori", - newCategoryPlaceholder: "Yeni kategori olustur...", - searchPlaceholder: "Oge ara...", - inventoryOpen: "Envanteri ac", - inventoryLabel: "Envanter", - loadedFile: "Yuklendi", - chooseItemHint: "Soldan bir kategori secin ve sonra bir oge secin.", - applyChanges: "Degisiklikleri uygula", - selectInventoryTitle: "QS inventory klasorunu sec", - itemsFileMissing: "items.lua bulunamadi. Lutfen qs-inventory klasorunu secin.", - imagesMissing: "Gorsel klasoru bulunamadi: qs-inventory\\html\\images", - noItemsDetected: - "Oge bulunamadi. Dosyanin `ItemList = {` veya `QBShared.Items = {` ile basladigindan emin olun.", - loadedItems: ({ count }) => `Yuklendi: ${count} oge`, - loadError: ({ message }) => `Yukleme hatasi: ${message}`, - saved: "Kaydedildi", - saveError: ({ message }) => `Kaydetme hatasi: ${message}`, - backupError: ({ message }) => `Yedek olusturulamadi: ${message}`, - language: "Dil", - newItem: "Yeni oge", - template: "Sablon", - create: "Olustur", - cancel: "Iptal", - itemId: "Oge ID", - itemLabel: "Etiket", - idRequired: "Lutfen bir oge ID girin.", - idExists: ({ id }) => `Oge ID zaten mevcut: ${id}`, - templateNormal: "Normal ogeler", - templateNormalRare: "Nadirlikli normal ogeler", - templateWeapon: "Silahlar", - templateFood: "Yiyecek", - templateDrink: "Icecek", - selectTemplateTitle: "Sablon sec", - createItemTitle: "Yeni ogeyi duzenle", - close: "Kapat", - itemCreated: "Oge kaydedildi", - imageAdd: "Gorsel ekle", - imageAddError: ({ message }) => `Gorsel eklenemedi: ${message}`, - imageAdded: "Gorsel eklendi", - }, - ru: { - allItems: "\u0412\u0441\u0435 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u044b", - uncategorized: "\u0411\u0435\u0437 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438", - categories: "\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438", - category: "\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f", - newCategoryPlaceholder: "\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e...", - searchPlaceholder: "\u041f\u043e\u0438\u0441\u043a \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430...", - inventoryOpen: "\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0438\u043d\u0432\u0435\u043d\u0442\u0430\u0440\u044c", - inventoryLabel: "\u0418\u043d\u0432\u0435\u043d\u0442\u0430\u0440\u044c", - loadedFile: "\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e", - chooseItemHint: - "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e \u0441\u043b\u0435\u0432\u0430, \u0437\u0430\u0442\u0435\u043c \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u0435\u0434\u043c\u0435\u0442.", - applyChanges: "\u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f", - selectInventoryTitle: "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u043f\u043a\u0443 QS inventory", - itemsFileMissing: - "\u0424\u0430\u0439\u043b items.lua \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d. \u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u043f\u043a\u0443 qs-inventory.", - imagesMissing: - "\u041f\u0430\u043f\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430: qs-inventory\\html\\images", - noItemsDetected: - "\u041f\u0440\u0435\u0434\u043c\u0435\u0442\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 `ItemList = {` \u0438\u043b\u0438 `QBShared.Items = {`.", - loadedItems: ({ count }) => `\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e: ${count} \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043e\u0432`, - loadError: ({ message }) => `\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438: ${message}`, - saved: "\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e", - saveError: ({ message }) => `\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f: ${message}`, - backupError: ({ message }) => - `\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u0443\u044e \u043a\u043e\u043f\u0438\u044e: ${message}`, - language: "\u042f\u0437\u044b\u043a", - newItem: "\u041d\u043e\u0432\u044b\u0439 \u043f\u0440\u0435\u0434\u043c\u0435\u0442", - template: "\u0428\u0430\u0431\u043b\u043e\u043d", - create: "\u0421\u043e\u0437\u0434\u0430\u0442\u044c", - cancel: "\u041e\u0442\u043c\u0435\u043d\u0430", - itemId: "ID \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430", - itemLabel: "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - idRequired: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 ID \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430.", - idExists: ({ id }) => `ID \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442: ${id}`, - templateNormal: "\u041e\u0431\u044b\u0447\u043d\u044b\u0435 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u044b", - templateNormalRare: "\u041e\u0431\u044b\u0447\u043d\u044b\u0435 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u044b \u0441 \u0440\u0435\u0434\u043a\u043e\u0441\u0442\u044c\u044e", - templateWeapon: "\u041e\u0440\u0443\u0436\u0438\u0435", - templateFood: "\u0415\u0434\u0430", - templateDrink: "\u041d\u0430\u043f\u0438\u0442\u043e\u043a", - selectTemplateTitle: "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u0430\u0431\u043b\u043e\u043d", - createItemTitle: "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430", - close: "\u0417\u0430\u043a\u0440\u044b\u0442\u044c", - itemCreated: "\u041f\u0440\u0435\u0434\u043c\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d", - imageAdd: "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435", - imageAddError: ({ message }) => - `\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435: ${message}`, - imageAdded: "\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e", - }, -}; - const itemTemplates = [ { key: "normal", @@ -663,6 +249,24 @@ export default function App() { () => categories.filter((c) => c !== ALL_CATEGORY), [categories] ); + const duplicateIdCounts = useMemo(() => { + const counts = {}; + for (const item of items) { + const id = item?.id; + if (!id) continue; + counts[id] = (counts[id] || 0) + 1; + } + const duplicates = {}; + for (const [id, count] of Object.entries(counts)) { + if (count > 1) duplicates[id] = count; + } + return duplicates; + }, [items]); + const duplicateIds = useMemo( + () => Object.keys(duplicateIdCounts).sort((a, b) => a.localeCompare(b)), + [duplicateIdCounts] + ); + const duplicatePreview = useMemo(() => duplicateIds.slice(0, 6), [duplicateIds]); const filteredItems = useMemo(() => { let list = items; @@ -686,6 +290,7 @@ export default function App() { const selectedItem = useMemo(() => { return items.find((i) => i.id === selectedItemId) || null; }, [items, selectedItemId]); + const selectedItemDuplicateCount = selectedItem ? duplicateIdCounts[selectedItem.id] || 0 : 0; const selectedLanguageOption = useMemo( () => languageOptions.find((option) => option.value === language) || languageOptions[0], [language] @@ -1202,8 +807,16 @@ export default function App() {

)} {status &&

{status}

} + {duplicateIds.length > 0 && ( +

+ {t("duplicatesFound", { count: duplicateIds.length })}: {duplicatePreview.join(", ")} + {duplicateIds.length > duplicatePreview.length + ? ` +${duplicateIds.length - duplicatePreview.length}` + : ""} +

+ )}

- Made with ❤️ by{" "} + Made with love by{" "} { @@ -1214,7 +827,7 @@ export default function App() { > koichixD {" "} - for the Quasar Store community ✨ + for the Quasar Store community {"\u2764"}

@@ -1261,7 +874,14 @@ export default function App() { alt={selectedLanguageOption.flagAlt} className="w-6 h-4 object-cover rounded-sm border border-slate-600" /> - + {languageMenuOpen && (
@@ -1338,16 +958,19 @@ export default function App() {
); @@ -1379,6 +1007,14 @@ export default function App() { {selectedItem.label || selectedItem.id}

{selectedItem.id}

+ {selectedItemDuplicateCount > 1 && ( +

+ {t("duplicateSelectedHint", { + id: selectedItem.id, + count: selectedItemDuplicateCount, + })} +

+ )}