mirror of
https://github.com/koichixD/Quasar_Item_Editor.git
synced 2026-08-28 17:01:03 +00:00
Spanisch & Französisch als UI Sprache hinzugefügt ~ V 0.5.1
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="16" viewBox="0 0 24 16">
|
||||
<rect width="24" height="16" fill="#AA151B"/>
|
||||
<rect y="4" width="24" height="8" fill="#F1BF00"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 191 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="16" viewBox="0 0 24 16">
|
||||
<rect width="8" height="16" fill="#0055A4"/>
|
||||
<rect x="8" width="8" height="16" fill="#FFFFFF"/>
|
||||
<rect x="16" width="8" height="16" fill="#EF4135"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 244 B |
+155
-18
@@ -1,4 +1,4 @@
|
||||
import React, { 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";
|
||||
@@ -38,6 +38,12 @@ 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" },
|
||||
];
|
||||
const translations = {
|
||||
de: {
|
||||
allItems: "Alle Items",
|
||||
@@ -127,6 +133,94 @@ const translations = {
|
||||
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",
|
||||
},
|
||||
};
|
||||
|
||||
const itemTemplates = [
|
||||
@@ -295,7 +389,9 @@ export default function App() {
|
||||
const [newItemDraft, setNewItemDraft] = useState(null);
|
||||
const [newItemTemplate, setNewItemTemplate] = useState(itemTemplates[0]?.key || "basic");
|
||||
const [toastMessage, setToastMessage] = useState("");
|
||||
const [languageMenuOpen, setLanguageMenuOpen] = useState(false);
|
||||
const toastTimerRef = useRef(null);
|
||||
const languageMenuRef = useRef(null);
|
||||
|
||||
const t = (key, vars) => {
|
||||
const entry = translations[language]?.[key] ?? translations.en[key] ?? key;
|
||||
@@ -360,6 +456,24 @@ export default function App() {
|
||||
const selectedItem = useMemo(() => {
|
||||
return items.find((i) => i.id === selectedItemId) || null;
|
||||
}, [items, selectedItemId]);
|
||||
const selectedLanguageOption = useMemo(
|
||||
() => languageOptions.find((option) => option.value === language) || languageOptions[0],
|
||||
[language]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
function handlePointerDown(event) {
|
||||
if (!languageMenuOpen) return;
|
||||
if (!languageMenuRef.current?.contains(event.target)) {
|
||||
setLanguageMenuOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("mousedown", handlePointerDown);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handlePointerDown);
|
||||
};
|
||||
}, [languageMenuOpen]);
|
||||
|
||||
const selectedImageSrc = selectedItem ? getImageSrc(selectedItem) : null;
|
||||
|
||||
@@ -904,27 +1018,50 @@ export default function App() {
|
||||
>
|
||||
{t("inventoryOpen")}
|
||||
</button>
|
||||
<div className="flex items-center gap-1 bg-panel2 border border-slate-700 rounded-full p-1">
|
||||
<div className="relative" ref={languageMenuRef}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLanguage("de")}
|
||||
className={`px-2 py-1 rounded-full text-sm ${
|
||||
language === "de" ? "bg-accent text-black" : "hover:bg-slate-800"
|
||||
}`}
|
||||
title="Deutsch"
|
||||
onClick={() => setLanguageMenuOpen((prev) => !prev)}
|
||||
className="h-10 w-14 flex items-center justify-center gap-1 bg-panel2 border border-slate-700 rounded-xl2 hover:bg-slate-800"
|
||||
title={selectedLanguageOption.label}
|
||||
aria-label={t("language")}
|
||||
>
|
||||
<img src="/flags/de.svg" alt="DE" className="w-5 h-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLanguage("en")}
|
||||
className={`px-2 py-1 rounded-full text-sm ${
|
||||
language === "en" ? "bg-accent text-black" : "hover:bg-slate-800"
|
||||
}`}
|
||||
title="English"
|
||||
>
|
||||
<img src="/flags/us.svg" alt="US" className="w-5 h-4" />
|
||||
<img
|
||||
src={selectedLanguageOption.flagSrc}
|
||||
alt={selectedLanguageOption.flagAlt}
|
||||
className="w-6 h-4 object-cover rounded-sm border border-slate-600"
|
||||
/>
|
||||
<span className="text-slate-300 text-xs">▾</span>
|
||||
</button>
|
||||
{languageMenuOpen && (
|
||||
<div className="absolute right-0 mt-2 z-50 bg-panel2 border border-slate-700 rounded-xl2 p-2 shadow-soft">
|
||||
<div className="flex flex-col gap-1">
|
||||
{languageOptions.map((option) => (
|
||||
<button
|
||||
key={option.value}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setLanguage(option.value);
|
||||
setLanguageMenuOpen(false);
|
||||
}}
|
||||
className={`h-9 w-12 flex items-center justify-center rounded border ${
|
||||
language === option.value
|
||||
? "border-accent bg-slate-800"
|
||||
: "border-slate-700 hover:bg-slate-800"
|
||||
}`}
|
||||
title={option.label}
|
||||
aria-label={option.label}
|
||||
>
|
||||
<img
|
||||
src={option.flagSrc}
|
||||
alt={option.flagAlt}
|
||||
className="w-6 h-4 object-cover rounded-sm border border-slate-600"
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const appConfig = {
|
||||
version: "0.5.0",
|
||||
version: "0.5.1",
|
||||
versionLabelSuffix: "Alpha",
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user