Outsourcing languages and adding duplicate detection

This commit is contained in:
koichixD
2026-02-09 23:05:50 +01:00
parent cfec10b1bf
commit b0dac1ec58
2 changed files with 494 additions and 422 deletions
+59 -422
View File
@@ -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() {
</p>
)}
{status && <p className="text-sm mt-1 text-slate-300">{status}</p>}
{duplicateIds.length > 0 && (
<p className="text-sm mt-1 text-amber-300">
{t("duplicatesFound", { count: duplicateIds.length })}: {duplicatePreview.join(", ")}
{duplicateIds.length > duplicatePreview.length
? ` +${duplicateIds.length - duplicatePreview.length}`
: ""}
</p>
)}
<p className="text-xs text-slate-500 mt-2">
Made with by{" "}
Made with love by{" "}
<a
href="https://werksfabrik.net"
onClick={(e) => {
@@ -1214,7 +827,7 @@ export default function App() {
>
koichixD
</a>{" "}
for the Quasar Store community
for the Quasar Store community {"\u2764"}
</p>
</div>
@@ -1261,7 +874,14 @@ export default function App() {
alt={selectedLanguageOption.flagAlt}
className="w-6 h-4 object-cover rounded-sm border border-slate-600"
/>
<span className="text-slate-300 text-xs"></span>
<svg
className="w-3 h-3 text-slate-300"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path d="M5.25 7.5L10 12.25L14.75 7.5H5.25Z" />
</svg>
</button>
{languageMenuOpen && (
<div className="absolute right-0 mt-2 z-50 bg-panel2 border border-slate-700 rounded-xl2 p-2 shadow-soft">
@@ -1338,16 +958,19 @@ export default function App() {
<ul className="space-y-1">
{filteredItems.map((it) => {
{filteredItems.map((it, idx) => {
const imageSrc = getImageSrc(it);
const duplicateCount = duplicateIdCounts[it.id] || 0;
return (
<li
key={it.id}
key={`${it.id}-${idx}`}
onClick={() => setSelectedItemId(it.id)}
className={`cursor-pointer p-2 rounded border transition-colors ${
selectedItemId === it.id
? "bg-slate-700/80 ring-1 ring-slate-600 border-slate-600"
: "border-slate-700 hover:bg-slate-800"
: duplicateCount > 1
? "border-amber-500/70 bg-amber-500/10 hover:bg-amber-500/20"
: "border-slate-700 hover:bg-slate-800"
}`}
>
<div className="flex items-center gap-2">
@@ -1360,6 +983,11 @@ export default function App() {
<div className="text-sm font-medium truncate">{it.label || it.id}</div>
<div className="text-xs text-slate-400 truncate">{it.id}</div>
</div>
{duplicateCount > 1 && (
<span className="ml-auto text-[10px] px-1.5 py-0.5 rounded-full border border-amber-400/70 text-amber-200 bg-amber-500/20">
{duplicateCount}x
</span>
)}
</div>
</li>
);
@@ -1379,6 +1007,14 @@ export default function App() {
{selectedItem.label || selectedItem.id}
</h2>
<p className="text-sm text-slate-400">{selectedItem.id}</p>
{selectedItemDuplicateCount > 1 && (
<p className="text-xs text-amber-300 mt-1">
{t("duplicateSelectedHint", {
id: selectedItem.id,
count: selectedItemDuplicateCount,
})}
</p>
)}
</div>
<button
@@ -1546,3 +1182,4 @@ export default function App() {
</div>
);
}
+435
View File
@@ -0,0 +1,435 @@
export 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: "Espa\u00f1ol", flagSrc: "/flags/es.svg", flagAlt: "ES" },
{ value: "fr", label: "Fran\u00e7ais", flagSrc: "/flags/fr.svg", flagAlt: "FR" },
{ value: "pt-BR", label: "Portugu\u00eas (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: "T\u00fcrk\u00e7e", flagSrc: "/flags/tr.svg", flagAlt: "TR" },
{ value: "ru", label: "\u0420\u0443\u0441\u0441\u043a\u0438\u0439", flagSrc: "/flags/ru.svg", flagAlt: "RU" },
];
export 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\u00e4nk",
selectTemplateTitle: "Template ausw\u00e4hlen",
createItemTitle: "Neues Item bearbeiten",
close: "Schlie\u00dfen",
itemCreated: "Item gespeichert",
imageAdd: "Bild hinzuf\u00fcgen",
imageAddError: ({ message }) => `Bild konnte nicht hinzugef\u00fcgt werden: ${message}`,
imageAdded: "Bild hinzugef\u00fcgt",
duplicatesFound: ({ count }) => `Doppelte Item-IDs gefunden: ${count}`,
duplicateSelectedHint: ({ id, count }) => `Achtung: ID '${id}' ist ${count}x vorhanden.`,
},
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",
duplicatesFound: ({ count }) => `Duplicate item IDs found: ${count}`,
duplicateSelectedHint: ({ id, count }) => `Warning: ID '${id}' exists ${count} times.`,
},
es: {
allItems: "Todos los items",
uncategorized: "Sin asignar",
categories: "Categor\u00edas",
category: "Categor\u00eda",
newCategoryPlaceholder: "Crear nueva categor\u00eda...",
searchPlaceholder: "Buscar item...",
inventoryOpen: "Abrir inventario",
inventoryLabel: "Inventario",
loadedFile: "Cargado",
chooseItemHint: "Elige una categor\u00eda a la izquierda y luego selecciona un item.",
applyChanges: "Aplicar cambios",
selectInventoryTitle: "Seleccionar carpeta de QS inventory",
itemsFileMissing: "No se encontr\u00f3 items.lua. Selecciona la carpeta qs-inventory.",
imagesMissing: "No se encontr\u00f3 la carpeta de im\u00e1genes: qs-inventory\\html\\images",
noItemsDetected:
"No se detectaron items. Aseg\u00farate 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\u00f1adir imagen",
imageAddError: ({ message }) => `No se pudo a\u00f1adir la imagen: ${message}`,
imageAdded: "Imagen a\u00f1adida",
duplicatesFound: ({ count }) => `IDs de item duplicadas encontradas: ${count}`,
duplicateSelectedHint: ({ id, count }) => `Atenci\u00f3n: la ID '${id}' existe ${count} veces.`,
},
fr: {
allItems: "Tous les items",
uncategorized: "Non attribu\u00e9",
categories: "Cat\u00e9gories",
category: "Cat\u00e9gorie",
newCategoryPlaceholder: "Cr\u00e9er une nouvelle cat\u00e9gorie...",
searchPlaceholder: "Rechercher un item...",
inventoryOpen: "Ouvrir l'inventaire",
inventoryLabel: "Inventaire",
loadedFile: "Charg\u00e9",
chooseItemHint: "Choisis une cat\u00e9gorie \u00e0 gauche puis s\u00e9lectionne un item.",
applyChanges: "Appliquer les modifications",
selectInventoryTitle: "S\u00e9lectionner le dossier QS inventory",
itemsFileMissing: "items.lua introuvable. S\u00e9lectionne le dossier qs-inventory.",
imagesMissing: "Dossier d'images introuvable : qs-inventory\\html\\images",
noItemsDetected:
"Aucun item d\u00e9tect\u00e9. V\u00e9rifie que le fichier commence par `ItemList = {` ou `QBShared.Items = {`.",
loadedItems: ({ count }) => `Charg\u00e9 : ${count} items`,
loadError: ({ message }) => `Erreur de chargement : ${message}`,
saved: "Enregistr\u00e9",
saveError: ({ message }) => `Erreur d'enregistrement : ${message}`,
backupError: ({ message }) => `\u00c9chec de la sauvegarde : ${message}`,
language: "Langue",
newItem: "Nouvel item",
template: "Mod\u00e8le",
create: "Cr\u00e9er",
cancel: "Annuler",
itemId: "ID de l'item",
itemLabel: "Libell\u00e9",
idRequired: "Veuillez saisir un ID d'item.",
idExists: ({ id }) => `L'ID de l'item existe d\u00e9j\u00e0 : ${id}`,
templateNormal: "Items normaux",
templateNormalRare: "Items normaux avec raret\u00e9",
templateWeapon: "Armes",
templateFood: "Nourriture",
templateDrink: "Boisson",
selectTemplateTitle: "S\u00e9lectionner un mod\u00e8le",
createItemTitle: "Modifier le nouvel item",
close: "Fermer",
itemCreated: "Item enregistr\u00e9",
imageAdd: "Ajouter une image",
imageAddError: ({ message }) => `Impossible d'ajouter l'image : ${message}`,
imageAdded: "Image ajout\u00e9e",
duplicatesFound: ({ count }) => `IDs d'item dupliqu\u00e9es trouv\u00e9es : ${count}`,
duplicateSelectedHint: ({ id, count }) => `Attention : l'ID '${id}' existe ${count} fois.`,
},
"pt-BR": {
allItems: "Todos os itens",
uncategorized: "N\u00e3o atribu\u00eddo",
categories: "Categorias",
category: "Categoria",
newCategoryPlaceholder: "Criar nova categoria...",
searchPlaceholder: "Buscar item...",
inventoryOpen: "Abrir invent\u00e1rio",
inventoryLabel: "Invent\u00e1rio",
loadedFile: "Carregado",
chooseItemHint: "Escolha uma categoria \u00e0 esquerda e depois selecione um item.",
applyChanges: "Aplicar altera\u00e7\u00f5es",
selectInventoryTitle: "Selecionar pasta do QS inventory",
itemsFileMissing: "items.lua n\u00e3o encontrado. Selecione a pasta qs-inventory.",
imagesMissing: "Pasta de imagens n\u00e3o encontrada: qs-inventory\\html\\images",
noItemsDetected:
"Nenhum item detectado. Verifique se o arquivo come\u00e7a 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: "R\u00f3tulo",
idRequired: "Informe um ID de item.",
idExists: ({ id }) => `ID de item j\u00e1 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",
duplicatesFound: ({ count }) => `IDs de item duplicados encontrados: ${count}`,
duplicateSelectedHint: ({ id, count }) => `Aten\u00e7\u00e3o: o ID '${id}' existe ${count} vezes.`,
},
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 gi\u00e0 esistente: ${id}`,
templateNormal: "Item normali",
templateNormalRare: "Item normali con rarit\u00e0",
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",
duplicatesFound: ({ count }) => `ID oggetto duplicati trovati: ${count}`,
duplicateSelectedHint: ({ id, count }) => `Attenzione: l'ID '${id}' esiste ${count} volte.`,
},
pl: {
allItems: "Wszystkie przedmioty",
uncategorized: "Nieprzypisane",
categories: "Kategorie",
category: "Kategoria",
newCategoryPlaceholder: "Utw\u00f3rz now\u0105 kategori\u0119...",
searchPlaceholder: "Szukaj przedmiotu...",
inventoryOpen: "Otw\u00f3rz ekwipunek",
inventoryLabel: "Ekwipunek",
loadedFile: "Wczytano",
chooseItemHint: "Wybierz kategori\u0119 po lewej stronie, a nast\u0119pnie przedmiot.",
applyChanges: "Zastosuj zmiany",
selectInventoryTitle: "Wybierz folder QS inventory",
itemsFileMissing: "Nie znaleziono items.lua. Wybierz folder qs-inventory.",
imagesMissing: "Nie znaleziono folderu obraz\u00f3w: qs-inventory\\html\\images",
noItemsDetected:
"Nie wykryto przedmiot\u00f3w. Upewnij si\u0119, \u017ce plik zaczyna si\u0119 od `ItemList = {` lub `QBShared.Items = {`.",
loadedItems: ({ count }) => `Wczytano: ${count} przedmiot\u00f3w`,
loadError: ({ message }) => `B\u0142\u0105d wczytywania: ${message}`,
saved: "Zapisano",
saveError: ({ message }) => `B\u0142\u0105d zapisu: ${message}`,
backupError: ({ message }) => `Nie uda\u0142o si\u0119 utworzy\u0107 kopii zapasowej: ${message}`,
language: "J\u0119zyk",
newItem: "Nowy przedmiot",
template: "Szablon",
create: "Utw\u00f3rz",
cancel: "Anuluj",
itemId: "ID przedmiotu",
itemLabel: "Etykieta",
idRequired: "Podaj ID przedmiotu.",
idExists: ({ id }) => `ID przedmiotu ju\u017c istnieje: ${id}`,
templateNormal: "Zwyk\u0142e przedmioty",
templateNormalRare: "Zwyk\u0142e przedmioty z rzadko\u015bci\u0105",
templateWeapon: "Bro\u0144",
templateFood: "Jedzenie",
templateDrink: "Nap\u00f3j",
selectTemplateTitle: "Wybierz szablon",
createItemTitle: "Edytuj nowy przedmiot",
close: "Zamknij",
itemCreated: "Przedmiot zapisany",
imageAdd: "Dodaj obraz",
imageAddError: ({ message }) => `Nie uda\u0142o si\u0119 doda\u0107 obrazu: ${message}`,
imageAdded: "Obraz dodany",
duplicatesFound: ({ count }) => `Znaleziono zduplikowane ID przedmiot\u00f3w: ${count}`,
duplicateSelectedHint: ({ id, count }) => `Uwaga: ID '${id}' wyst\u0119puje ${count} razy.`,
},
tr: {
allItems: "T\u00fcm \u00f6\u011feler",
uncategorized: "Atanmam\u0131\u015f",
categories: "Kategoriler",
category: "Kategori",
newCategoryPlaceholder: "Yeni kategori olu\u015ftur...",
searchPlaceholder: "\u00d6\u011fe ara...",
inventoryOpen: "Envanteri a\u00e7",
inventoryLabel: "Envanter",
loadedFile: "Y\u00fcklendi",
chooseItemHint: "Soldan bir kategori se\u00e7in ve sonra bir \u00f6\u011fe se\u00e7in.",
applyChanges: "De\u011fi\u015fiklikleri uygula",
selectInventoryTitle: "QS inventory klas\u00f6r\u00fcn\u00fc se\u00e7",
itemsFileMissing: "items.lua bulunamad\u0131. L\u00fctfen qs-inventory klas\u00f6r\u00fcn\u00fc se\u00e7in.",
imagesMissing: "G\u00f6rsel klas\u00f6r\u00fc bulunamad\u0131: qs-inventory\\html\\images",
noItemsDetected:
"\u00d6\u011fe bulunamad\u0131. Dosyan\u0131n `ItemList = {` veya `QBShared.Items = {` ile ba\u015flad\u0131\u011f\u0131ndan emin olun.",
loadedItems: ({ count }) => `Y\u00fcklendi: ${count} \u00f6\u011fe`,
loadError: ({ message }) => `Y\u00fckleme hatas\u0131: ${message}`,
saved: "Kaydedildi",
saveError: ({ message }) => `Kaydetme hatas\u0131: ${message}`,
backupError: ({ message }) => `Yedek olu\u015fturulamad\u0131: ${message}`,
language: "Dil",
newItem: "Yeni \u00f6\u011fe",
template: "\u015eablon",
create: "Olu\u015ftur",
cancel: "\u0130ptal",
itemId: "\u00d6\u011fe ID",
itemLabel: "Etiket",
idRequired: "L\u00fctfen bir \u00f6\u011fe ID girin.",
idExists: ({ id }) => `\u00d6\u011fe ID zaten mevcut: ${id}`,
templateNormal: "Normal \u00f6\u011feler",
templateNormalRare: "Nadirlikli normal \u00f6\u011feler",
templateWeapon: "Silahlar",
templateFood: "Yiyecek",
templateDrink: "\u0130\u00e7ecek",
selectTemplateTitle: "\u015eablon se\u00e7",
createItemTitle: "Yeni \u00f6\u011feyi d\u00fczenle",
close: "Kapat",
itemCreated: "\u00d6\u011fe kaydedildi",
imageAdd: "G\u00f6rsel ekle",
imageAddError: ({ message }) => `G\u00f6rsel eklenemedi: ${message}`,
imageAdded: "G\u00f6rsel eklendi",
duplicatesFound: ({ count }) => `Yinelenen \u00f6\u011fe ID'leri bulundu: ${count}`,
duplicateSelectedHint: ({ id, count }) => `Uyar\u0131: '${id}' ID'si ${count} kez bulunuyor.`,
},
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",
duplicatesFound: ({ count }) =>
`\u041d\u0430\u0439\u0434\u0435\u043d\u043e \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432 ID \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043e\u0432: ${count}`,
duplicateSelectedHint: ({ id, count }) =>
`\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435: ID '${id}' \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f ${count} \u0440\u0430\u0437.`,
},
};