From c66b77dbf848ef629a7b260efd341440900ce0f9 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Sun, 8 Feb 2026 09:56:43 +0100 Subject: [PATCH] feat: add i18n support for Italian, Spanish, Portuguese and Russian --- AGENTS.md | 9 +- docs/DIRECTUS-SCHEMA.md | 7 +- docs/add-category-translations.sh | 300 ++++++++++++++++++++++++++++++ js/components/pages/page-home.js | 15 ++ js/i18n.js | 48 ++++- js/services/categories.js | 7 +- locales/es.json | 295 +++++++++++++++++++++++++++++ locales/it.json | 295 +++++++++++++++++++++++++++++ locales/pt.json | 295 +++++++++++++++++++++++++++++ locales/ru.json | 295 +++++++++++++++++++++++++++++ 10 files changed, 1556 insertions(+), 10 deletions(-) create mode 100755 docs/add-category-translations.sh create mode 100644 locales/es.json create mode 100644 locales/it.json create mode 100644 locales/pt.json create mode 100644 locales/ru.json diff --git a/AGENTS.md b/AGENTS.md index 9948217..d77376b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -102,7 +102,11 @@ tests/ locales/ ├── de.json # Deutsch (Fallback) ├── en.json -└── fr.json +├── fr.json +├── it.json # Italienisch +├── es.json # Spanisch +├── pt.json # Portugiesisch (BR) +└── ru.json # Russisch ``` ## Konventionen @@ -130,7 +134,8 @@ locales/ - Schlüssel: `section.key` (z.B. `home.title`) - Im HTML: `data-i18n="key"` oder `${t('key')}` - Placeholder: `data-i18n-placeholder="key"` -- Neue Texte in **allen 3 Sprachen** hinzufügen +- Neue Texte in **allen 7 Sprachen** hinzufügen (de, en, fr, it, es, pt, ru) +- Frontend nutzt Kurzcodes (`de`), Directus Langcodes (`de-DE`) — Mapping in `i18n.js` (`LOCALE_TO_DIRECTUS`) ## Aktuelle Probleme / Hinweise diff --git a/docs/DIRECTUS-SCHEMA.md b/docs/DIRECTUS-SCHEMA.md index cec4d18..665b3ae 100644 --- a/docs/DIRECTUS-SCHEMA.md +++ b/docs/DIRECTUS-SCHEMA.md @@ -90,7 +90,7 @@ Kategorien mit hierarchischer Struktur. |------|-----|--------------| | `id` | integer | Primary Key | | `categories_id` | UUID | FK → categories | -| `languages_code` | string | Sprachcode (`de`, `en`, `fr`) | +| `languages_code` | string | Sprachcode (`de-DE`, `en-US`, `fr-FR`, `it-IT`, `es-ES`, `pt-BR`, `ru-RU`) | | `name` | string | Übersetzter Name | --- @@ -117,8 +117,9 @@ Verfügbare Sprachen. | Feld | Typ | Beschreibung | |------|-----|--------------| -| `code` | string | Primary Key, z.B. `de`, `en`, `fr` | +| `code` | string | Primary Key, z.B. `de-DE`, `en-US`, `fr-FR`, `it-IT`, `es-ES`, `pt-BR`, `ru-RU` | | `name` | string | Sprachname | +| `direction` | string | Textrichtung: `ltr` oder `rtl` | --- @@ -180,7 +181,7 @@ Zusätzliche Felder für User-Einstellungen. | Feld | Typ | Beschreibung | |------|-----|--------------| | `preferred_currency` | string | Bevorzugte Währung: `USD`, `EUR`, `CHF` (Default: `USD`) | -| `preferred_locale` | string | Bevorzugte Sprache: `de`, `en`, `fr` | +| `preferred_locale` | string | Bevorzugte Sprache: `de-DE`, `en-US`, `fr-FR`, `it-IT`, `es-ES`, `pt-BR`, `ru-RU` | **Hinweis:** Diese Felder müssen in Directus unter Settings → Data Model → directus_users angelegt werden. diff --git a/docs/add-category-translations.sh b/docs/add-category-translations.sh new file mode 100755 index 0000000..ba076d1 --- /dev/null +++ b/docs/add-category-translations.sh @@ -0,0 +1,300 @@ +#!/bin/bash +# Add translations for new languages to existing categories +# Usage: DIRECTUS_TOKEN=your_admin_token bash docs/add-category-translations.sh +# +# This script adds it-IT, es-ES, pt-BR, ru-RU translations to all categories. +# It reads existing categories from the API and creates missing translations. + +API="https://api.dgray.io" +TOKEN="${DIRECTUS_TOKEN:?Set DIRECTUS_TOKEN environment variable}" + +add_translation() { + local category_id="$1" lang_code="$2" name="$3" + + curl -s -X POST "$API/items/categories_translations" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"categories_id\": \"$category_id\", + \"languages_code\": \"$lang_code\", + \"name\": \"$name\" + }" > /dev/null 2>&1 +} + +add_all_translations() { + local category_id="$1" + local name_it="$2" name_es="$3" name_pt="$4" name_ru="$5" + + add_translation "$category_id" "it-IT" "$name_it" + add_translation "$category_id" "es-ES" "$name_es" + add_translation "$category_id" "pt-BR" "$name_pt" + add_translation "$category_id" "ru-RU" "$name_ru" +} + +echo "=== Adding translations (it-IT, es-ES, pt-BR, ru-RU) ===" +echo "" + +# Clean up orphaned translations (categories_id = null) from previous runs +echo "Cleaning up orphaned translations..." +ORPHANS=$(curl -s "$API/items/categories_translations?filter[categories_id][_null]=true&fields=id&limit=-1" \ + -H "Authorization: Bearer $TOKEN") +ORPHAN_IDS=$(echo "$ORPHANS" | grep -o '"id":[0-9]*' | cut -d: -f2 | tr '\n' ',' | sed 's/,$//') +if [ -n "$ORPHAN_IDS" ]; then + curl -s -X DELETE "$API/items/categories_translations" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "[$ORPHAN_IDS]" > /dev/null 2>&1 + echo " ✓ removed orphaned entries" +fi + +# Fetch all categories to get their IDs (no status filter for admin) +echo "Fetching categories..." +CATEGORIES=$(curl -s "$API/items/categories?fields=id,slug&limit=-1" \ + -H "Authorization: Bearer $TOKEN") + +get_id() { + local slug="$1" + echo "$CATEGORIES" | python3 -c " +import json, sys +data = json.load(sys.stdin).get('data', []) +for cat in data: + if cat.get('slug') == '$slug': + print(cat['id']) + break +" +} + +# ── 1. Electronics ── +echo "1/12 Electronics..." +ID=$(get_id "electronics") +add_all_translations "$ID" "Elettronica" "Electrónica" "Eletrônicos" "Электроника" + +ID=$(get_id "phones") +add_all_translations "$ID" "Telefoni e Tablet" "Teléfonos y Tablets" "Celulares e Tablets" "Телефоны и планшеты" + +ID=$(get_id "computers") +add_all_translations "$ID" "Computer e Accessori" "Ordenadores y Accesorios" "Computadores e Acessórios" "Компьютеры и аксессуары" + +ID=$(get_id "tv-audio") +add_all_translations "$ID" "TV, Audio e Video" "TV, Audio y Vídeo" "TV, Áudio e Vídeo" "ТВ, аудио и видео" + +ID=$(get_id "gaming") +add_all_translations "$ID" "Gaming e Console" "Gaming y Consolas" "Games e Consoles" "Игры и консоли" + +ID=$(get_id "appliances") +add_all_translations "$ID" "Elettrodomestici" "Electrodomésticos" "Eletrodomésticos" "Бытовая техника" + +ID=$(get_id "cameras") +add_all_translations "$ID" "Fotocamere e Fotografia" "Cámaras y Fotografía" "Câmeras e Fotografia" "Камеры и фотография" +echo " ✓ done" + +# ── 2. Vehicles ── +echo "2/12 Vehicles..." +ID=$(get_id "vehicles") +add_all_translations "$ID" "Veicoli" "Vehículos" "Veículos" "Транспорт" + +ID=$(get_id "cars") +add_all_translations "$ID" "Auto" "Coches" "Carros" "Автомобили" + +ID=$(get_id "motorcycles") +add_all_translations "$ID" "Moto" "Motos" "Motos" "Мотоциклы" + +ID=$(get_id "bikes") +add_all_translations "$ID" "Biciclette ed E-Bike" "Bicicletas y E-Bikes" "Bicicletas e E-Bikes" "Велосипеды и электровелосипеды" + +ID=$(get_id "vehicle-parts") +add_all_translations "$ID" "Ricambi e Accessori" "Recambios y Accesorios" "Peças e Acessórios" "Запчасти и аксессуары" + +ID=$(get_id "boats") +add_all_translations "$ID" "Barche e Imbarcazioni" "Barcos y Embarcaciones" "Barcos e Embarcações" "Лодки и водный транспорт" +echo " ✓ done" + +# ── 3. Home & Garden ── +echo "3/12 Home & Garden..." +ID=$(get_id "home-garden") +add_all_translations "$ID" "Casa e Giardino" "Hogar y Jardín" "Casa e Jardim" "Дом и сад" + +ID=$(get_id "furniture") +add_all_translations "$ID" "Mobili" "Muebles" "Móveis" "Мебель" + +ID=$(get_id "kitchen") +add_all_translations "$ID" "Cucina" "Cocina" "Cozinha" "Кухня" + +ID=$(get_id "garden") +add_all_translations "$ID" "Giardino e Outdoor" "Jardín y Exterior" "Jardim e Exterior" "Сад и отдых" + +ID=$(get_id "tools") +add_all_translations "$ID" "Utensili e Officina" "Herramientas y Taller" "Ferramentas e Oficina" "Инструменты" + +ID=$(get_id "decoration") +add_all_translations "$ID" "Decorazioni e Arte" "Decoración y Arte" "Decoração e Arte" "Декор и искусство" + +ID=$(get_id "bathroom") +add_all_translations "$ID" "Bagno e Sanitari" "Baño y Sanitarios" "Banheiro" "Ванная" +echo " ✓ done" + +# ── 4. Fashion & Accessories ── +echo "4/12 Fashion..." +ID=$(get_id "fashion") +add_all_translations "$ID" "Moda e Accessori" "Moda y Accesorios" "Moda e Acessórios" "Мода и аксессуары" + +ID=$(get_id "women") +add_all_translations "$ID" "Moda Donna" "Moda Mujer" "Moda Feminina" "Женская одежда" + +ID=$(get_id "men") +add_all_translations "$ID" "Moda Uomo" "Moda Hombre" "Moda Masculina" "Мужская одежда" + +ID=$(get_id "kids-fashion") +add_all_translations "$ID" "Moda Bambini" "Moda Infantil" "Moda Infantil" "Детская одежда" + +ID=$(get_id "shoes") +add_all_translations "$ID" "Scarpe" "Zapatos" "Sapatos" "Обувь" + +ID=$(get_id "watches-jewelry") +add_all_translations "$ID" "Orologi e Gioielli" "Relojes y Joyas" "Relógios e Joias" "Часы и украшения" + +ID=$(get_id "bags") +add_all_translations "$ID" "Borse e Bagagli" "Bolsos y Equipaje" "Bolsas e Bagagem" "Сумки и багаж" +echo " ✓ done" + +# ── 5. Sports & Leisure ── +echo "5/12 Sports..." +ID=$(get_id "sports") +add_all_translations "$ID" "Sport e Tempo Libero" "Deportes y Ocio" "Esportes e Lazer" "Спорт и отдых" + +ID=$(get_id "fitness") +add_all_translations "$ID" "Fitness e Palestra" "Fitness y Gimnasio" "Fitness e Academia" "Фитнес" + +ID=$(get_id "outdoor-sports") +add_all_translations "$ID" "Escursionismo e Outdoor" "Senderismo y Outdoor" "Trilhas e Outdoor" "Туризм и походы" + +ID=$(get_id "winter-sports") +add_all_translations "$ID" "Sport Invernali" "Deportes de Invierno" "Esportes de Inverno" "Зимний спорт" + +ID=$(get_id "water-sports") +add_all_translations "$ID" "Sport Acquatici" "Deportes Acuáticos" "Esportes Aquáticos" "Водный спорт" + +ID=$(get_id "team-sports") +add_all_translations "$ID" "Sport di Squadra" "Deportes de Equipo" "Esportes Coletivos" "Командный спорт" + +ID=$(get_id "cycling") +add_all_translations "$ID" "Accessori Ciclismo" "Equipamiento Ciclismo" "Equipamento Ciclismo" "Велоаксессуары" +echo " ✓ done" + +# ── 6. Family & Kids ── +echo "6/12 Family & Kids..." +ID=$(get_id "family-kids") +add_all_translations "$ID" "Famiglia e Bambini" "Familia y Niños" "Família e Crianças" "Семья и дети" + +ID=$(get_id "toys") +add_all_translations "$ID" "Giocattoli e Giochi" "Juguetes y Juegos" "Brinquedos e Jogos" "Игрушки и игры" + +ID=$(get_id "baby") +add_all_translations "$ID" "Neonati e Prima Infanzia" "Bebé y Primera Infancia" "Bebê e Primeira Infância" "Для малышей" + +ID=$(get_id "school") +add_all_translations "$ID" "Materiale Scolastico" "Material Escolar" "Material Escolar" "Школьные принадлежности" +echo " ✓ done" + +# ── 7. Books & Media ── +echo "7/12 Books & Media..." +ID=$(get_id "books-media") +add_all_translations "$ID" "Libri e Media" "Libros y Medios" "Livros e Mídia" "Книги и медиа" + +ID=$(get_id "fiction") +add_all_translations "$ID" "Narrativa" "Ficción" "Ficção" "Художественная литература" + +ID=$(get_id "nonfiction") +add_all_translations "$ID" "Saggistica e Scienze" "No Ficción y Ciencia" "Não Ficção e Ciência" "Нон-фикшн и наука" + +ID=$(get_id "textbooks") +add_all_translations "$ID" "Libri di Testo e Corsi" "Libros de Texto y Cursos" "Livros Didáticos e Cursos" "Учебники и курсы" + +ID=$(get_id "music-movies") +add_all_translations "$ID" "Musica, Film e Giochi" "Música, Películas y Juegos" "Música, Filmes e Jogos" "Музыка, фильмы и игры" +echo " ✓ done" + +# ── 8. Pets & Animals ── +echo "8/12 Pets..." +ID=$(get_id "pets") +add_all_translations "$ID" "Animali e Accessori" "Mascotas y Animales" "Animais e Acessórios" "Животные" + +ID=$(get_id "pet-supplies") +add_all_translations "$ID" "Accessori per Animali" "Accesorios para Mascotas" "Acessórios para Animais" "Зоотовары" + +ID=$(get_id "pet-adoption") +add_all_translations "$ID" "Adozione" "Adopción" "Adoção" "Усыновление" +echo " ✓ done" + +# ── 9. Jobs & Services ── +echo "9/12 Jobs..." +ID=$(get_id "jobs") +add_all_translations "$ID" "Lavoro e Servizi" "Empleo y Servicios" "Empregos e Serviços" "Работа и услуги" + +ID=$(get_id "full-time") +add_all_translations "$ID" "Tempo Pieno" "Tiempo Completo" "Tempo Integral" "Полная занятость" + +ID=$(get_id "part-time") +add_all_translations "$ID" "Part-Time e Lavoretti" "Tiempo Parcial y Minijobs" "Meio Período" "Частичная занятость" + +ID=$(get_id "freelance") +add_all_translations "$ID" "Freelance e Remoto" "Freelance y Remoto" "Freelance e Remoto" "Фриланс и удалёнка" + +ID=$(get_id "services") +add_all_translations "$ID" "Servizi e Artigianato" "Servicios y Oficios" "Serviços e Artesanato" "Услуги и ремесло" +echo " ✓ done" + +# ── 10. Real Estate ── +echo "10/12 Real Estate..." +ID=$(get_id "real-estate") +add_all_translations "$ID" "Immobili" "Inmuebles" "Imóveis" "Недвижимость" + +ID=$(get_id "apartments") +add_all_translations "$ID" "Appartamenti" "Pisos" "Apartamentos" "Квартиры" + +ID=$(get_id "houses") +add_all_translations "$ID" "Case" "Casas" "Casas" "Дома" + +ID=$(get_id "rooms") +add_all_translations "$ID" "Stanze e Coinquilini" "Habitaciones y Pisos Compartidos" "Quartos e Repúblicas" "Комнаты и совместное проживание" + +ID=$(get_id "commercial") +add_all_translations "$ID" "Commerciale" "Comercial" "Comercial" "Коммерческая недвижимость" +echo " ✓ done" + +# ── 11. Collectibles & Hobbies ── +echo "11/12 Collectibles..." +ID=$(get_id "collectibles") +add_all_translations "$ID" "Collezionismo e Hobby" "Coleccionismo y Hobbies" "Colecionáveis e Hobbies" "Коллекционирование и хобби" + +ID=$(get_id "antiques") +add_all_translations "$ID" "Antiquariato" "Antigüedades" "Antiguidades" "Антиквариат" + +ID=$(get_id "coins-stamps") +add_all_translations "$ID" "Monete e Francobolli" "Monedas y Sellos" "Moedas e Selos" "Монеты и марки" + +ID=$(get_id "models") +add_all_translations "$ID" "Modellismo e Figurine" "Modelismo y Figuras" "Modelismo e Figuras" "Моделизм и фигурки" + +ID=$(get_id "art-crafts") +add_all_translations "$ID" "Arte e Fatto a Mano" "Arte y Hecho a Mano" "Arte e Artesanato" "Искусство и хендмейд" +echo " ✓ done" + +# ── 12. Other ── +echo "12/12 Other..." +ID=$(get_id "other") +add_all_translations "$ID" "Altro" "Otros" "Outros" "Прочее" + +ID=$(get_id "free-stuff") +add_all_translations "$ID" "Gratis" "Gratis" "Grátis" "Бесплатно" + +ID=$(get_id "barter") +add_all_translations "$ID" "Baratto e Scambio" "Trueque e Intercambio" "Troca" "Обмен" + +ID=$(get_id "lost-found") +add_all_translations "$ID" "Oggetti Smarriti" "Objetos Perdidos" "Achados e Perdidos" "Находки" +echo " ✓ done" + +echo "" +echo "=== Translation import complete! ===" +echo "4 languages × 71 categories = 284 translations added." diff --git a/js/components/pages/page-home.js b/js/components/pages/page-home.js index 5ddd7aa..e7290fe 100644 --- a/js/components/pages/page-home.js +++ b/js/components/pages/page-home.js @@ -377,6 +377,21 @@ class PageHome extends HTMLElement { }) } + const priceBtn = this.querySelector('#toggle-price-filter span') + if (priceBtn) priceBtn.textContent = t('search.priceRange') + + const clearBtn = this.querySelector('#clear-filters') + if (clearBtn) clearBtn.textContent = t('search.clearAll') + + const applyBtn = this.querySelector('#apply-price') + if (applyBtn) applyBtn.textContent = t('search.apply') + + const minInput = this.querySelector('#min-price') + if (minInput) minInput.placeholder = t('search.min') + + const maxInput = this.querySelector('#max-price') + if (maxInput) maxInput.placeholder = t('search.max') + const searchBox = this.querySelector('search-box') if (searchBox) searchBox.loadAndRender() } diff --git a/js/i18n.js b/js/i18n.js index 23a787a..4800ed4 100644 --- a/js/i18n.js +++ b/js/i18n.js @@ -5,9 +5,28 @@ */ /** - * @typedef {'de' | 'en' | 'fr'} Locale + * @typedef {'de' | 'en' | 'fr' | 'it' | 'es' | 'pt' | 'ru'} Locale */ +/** + * Mapping from short locale codes (used in frontend) to + * Directus long locale codes (used in categories_translations etc.) + * @type {Object} + */ +const LOCALE_TO_DIRECTUS = { + de: 'de-DE', + en: 'en-US', + fr: 'fr-FR', + it: 'it-IT', + es: 'es-ES', + pt: 'pt-BR', + ru: 'ru-RU' +} + +const DIRECTUS_TO_LOCALE = Object.fromEntries( + Object.entries(LOCALE_TO_DIRECTUS).map(([k, v]) => [v, k]) +) + /** * I18n Service class * @class @@ -21,7 +40,7 @@ class I18n { /** @type {Locale} */ this.fallbackLocale = 'de' /** @type {Locale[]} */ - this.supportedLocales = ['de', 'en', 'fr'] + this.supportedLocales = ['de', 'en', 'fr', 'it', 'es', 'pt', 'ru'] /** @type {Set} */ this.subscribers = new Set() /** @type {boolean} */ @@ -198,12 +217,35 @@ class I18n { const names = { de: 'Deutsch', en: 'English', - fr: 'Français' + fr: 'Français', + it: 'Italiano', + es: 'Español', + pt: 'Português', + ru: 'Русский' } return names[locale] || locale } + + /** + * Get the Directus language code for a frontend locale + * @param {Locale} [locale] - Frontend locale (defaults to current) + * @returns {string} Directus language code (e.g. 'de-DE') + */ + getDirectusLocale(locale) { + return LOCALE_TO_DIRECTUS[locale || this.currentLocale] || LOCALE_TO_DIRECTUS[this.fallbackLocale] + } + + /** + * Get the frontend locale for a Directus language code + * @param {string} directusCode - Directus code (e.g. 'it-IT') + * @returns {Locale} + */ + fromDirectusLocale(directusCode) { + return DIRECTUS_TO_LOCALE[directusCode] || directusCode?.split('-')[0] || this.fallbackLocale + } } export const i18n = new I18n() export const t = (key, params) => i18n.t(key, params) export const getCurrentLanguage = () => i18n.getLocale() +export { LOCALE_TO_DIRECTUS, DIRECTUS_TO_LOCALE } diff --git a/js/services/categories.js b/js/services/categories.js index b6054f1..ab84ad8 100644 --- a/js/services/categories.js +++ b/js/services/categories.js @@ -3,7 +3,7 @@ */ import { directus } from './directus.js' -import { getCurrentLanguage } from '../i18n.js' +import { getCurrentLanguage, LOCALE_TO_DIRECTUS } from '../i18n.js' class CategoriesService { constructor() { @@ -126,10 +126,13 @@ class CategoriesService { getTranslatedName(category, lang = null) { const currentLang = lang || getCurrentLanguage() + const directusCode = LOCALE_TO_DIRECTUS[currentLang] || currentLang if (category.translations && Array.isArray(category.translations)) { const translation = category.translations.find( - t => t.languages_code === currentLang || t.languages_code?.startsWith(currentLang) + t => t.languages_code === directusCode + || t.languages_code === currentLang + || t.languages_code?.startsWith(currentLang) ) if (translation?.name) { return translation.name diff --git a/locales/es.json b/locales/es.json new file mode 100644 index 0000000..e2c0719 --- /dev/null +++ b/locales/es.json @@ -0,0 +1,295 @@ +{ + "header": { + "searchPlaceholder": "¿Qué estás buscando?", + "createListing": "Crear anuncio", + "toggleTheme": "Cambiar tema", + "selectLanguage": "Seleccionar idioma", + "profile": "Perfil" + }, + "footer": { + "rights": "Todos los derechos reservados.", + "about": "Acerca de", + "privacy": "Privacidad", + "terms": "Condiciones", + "contact": "Contacto" + }, + "home": { + "title": "Bienvenido a dgray.io", + "subtitle": "Encuentra grandes ofertas cerca de ti o vende lo que ya no necesitas.", + "browseListings": "Explorar anuncios", + "createListing": "Crear anuncio", + "categories": "Categorías", + "recentListings": "Anuncios recientes", + "placeholderTitle": "Anuncio de ejemplo", + "placeholderLocation": "Ubicación", + "addFavorite": "Añadir a favoritos", + "removeFavorite": "Eliminar de favoritos", + "noListings": "No se encontraron anuncios", + "loadMore": "Cargar más", + "pullToRefresh": "Desliza para actualizar" + }, + "common": { + "loading": "Cargando...", + "error": "Error al cargar", + "close": "Cerrar", + "remove": "Eliminar", + "home": "Inicio" + }, + "error": { + "title": "Algo salió mal", + "retry": "Intentar de nuevo", + "offline": "Sin conexión a internet" + }, + "search": { + "title": "Buscar", + "placeholder": "Introduce un término de búsqueda...", + "allCategories": "Todas las categorías", + "allSubcategories": "Todas las subcategorías", + "currentLocation": "Ubicación actual", + "locating": "Localizando...", + "searchButton": "Buscar", + "loading": "Buscando...", + "enterQuery": "Introduce un término de búsqueda para encontrar anuncios.", + "noResults": "No se encontraron resultados. Prueba con otro término de búsqueda.", + "resultsCount": "{{count}} resultados encontrados", + "allIn": "Todo en", + "clearAll": "Borrar todo", + "radiusAround": "{{radius}} km de radio", + "priceRange": "Precio", + "min": "Mín", + "max": "Máx", + "apply": "Aplicar", + "sortBy": "Ordenar por", + "sortNewest": "Más recientes", + "sortOldest": "Más antiguos", + "sortPriceAsc": "Precio: de menor a mayor", + "sortPriceDesc": "Precio: de mayor a menor", + "sortDistance": "Cercanos" + }, + "countries": { + "ch": "Suiza", + "de": "Alemania", + "at": "Austria", + "fr": "Francia", + "it": "Italia", + "li": "Liechtenstein" + }, + "listing": { + "notFound": "Este anuncio no fue encontrado.", + "backHome": "Volver al inicio", + "description": "Descripción", + "location": "Ubicación", + "seller": "Vendedor", + "anonymousSeller": "Vendedor anónimo", + "memberSince": "Miembro desde", + "postedOn": "Publicado el", + "contactSeller": "Contactar al vendedor", + "paymentInfo": "El pago se realiza directamente a través de Monero (XMR).", + "moneroAddress": "Dirección Monero del vendedor", + "noMoneroAddress": "No se proporcionó dirección Monero", + "copyAddress": "Copiar dirección", + "contactHint": "Copia la dirección y envía el importe con tu monedero Monero.", + "priceOnRequest": "Precio a consultar", + "shippingAvailable": "Envío disponible", + "viewSingular": "visita", + "viewPlural": "visitas", + "share": "Compartir", + "report": "Reportar", + "moreFromSeller": "Más de este vendedor", + "edit": "Editar", + "expired": "Caducado", + "expiresIn1Day": "Queda 1 día", + "expiresInDays": "Quedan {{days}} días" + }, + "chat": { + "title": "Enviar mensaje", + "placeholder": "Escribe un mensaje...", + "encrypted": "Cifrado de extremo a extremo", + "startConversation": "Inicia una conversación con el vendedor.", + "send": "Enviar", + "unavailable": "Chat no disponible" + }, + "create": { + "title": "Crear anuncio", + "editTitle": "Editar anuncio", + "listingTitle": "Título", + "titlePlaceholder": "¿Qué quieres vender?", + "category": "Categoría", + "selectCategory": "Seleccionar categoría", + "condition": "Estado", + "conditionNew": "Nuevo", + "conditionLikeNew": "Como nuevo", + "conditionGood": "Bueno", + "conditionFair": "Aceptable", + "conditionPoor": "Deficiente", + "price": "Precio", + "currency": "Moneda", + "priceMode": "Modo de precio", + "priceModeFiat": "Precio fiat fijo", + "priceModeXmr": "Precio XMR fijo", + "priceModeHint": "Fiat fijo: El importe en fiat se mantiene igual. XMR fijo: El importe en XMR se mantiene igual.", + "shippingAvailable": "Envío disponible", + "shippingCost": "Coste de envío", + "shippingCostPlaceholder": "p. ej. 5,00", + "location": "Ubicación", + "locationPlaceholder": "Ciudad, código postal o dirección", + "locationHint": "Elige la ubicación de tu anuncio", + "description": "Descripción", + "descriptionPlaceholder": "Describe tu artículo en detalle...", + "images": "Imágenes", + "uploadImages": "Subir imágenes (máx. 5)", + "moneroAddress": "Tu dirección Monero", + "moneroPlaceholder": "4... o 8...", + "moneroHint": "Los compradores enviarán el pago directamente a esta dirección.", + "cancel": "Cancelar", + "publish": "Publicar", + "publishing": "Publicando...", + "saveChanges": "Guardar cambios", + "saving": "Guardando...", + "publishFailed": "Error al publicar. Inténtalo de nuevo.", + "invalidMoneroAddress": "Dirección Monero no válida. Comprueba el formato.", + "draftRestored": "Borrador restaurado", + "clearDraft": "Descartar" + }, + "notFound": { + "title": "Página no encontrada", + "message": "La página que buscas no existe.", + "backHome": "Volver al inicio" + }, + "cropper": { + "title": "Recortar imagen", + "preview": "Vista previa:", + "cancel": "Cancelar", + "confirm": "Aplicar", + "aspectRatio": "Proporción:", + "free": "Libre" + }, + "captcha": { + "verify": "No soy un robot", + "verified": "Verificado", + "solving": "Verificando...", + "attempts": "intentos", + "error": "Error - inténtalo de nuevo" + }, + "profile": { + "myListings": "Mis anuncios", + "messages": "Mensajes", + "favorites": "Favoritos", + "settings": "Ajustes" + }, + "auth": { + "login": "Iniciar sesión", + "logout": "Cerrar sesión", + "loggingIn": "Iniciando sesión...", + "yourUuid": "Tu UUID", + "enterUuid": "Introduce tu UUID", + "invalidUuid": "UUID no válido o cuenta no encontrada", + "noAccount": "¿Aún no tienes cuenta?", + "hasAccount": "¿Ya tienes cuenta?", + "createAccount": "Crear cuenta", + "registerInfo": "Se generará un UUID único para ti. Este UUID es tu único acceso. ¡Guárdalo de forma segura!", + "generateUuid": "Generar UUID", + "creating": "Creando...", + "accountCreated": "¡Cuenta creada!", + "important": "¡Importante!", + "saveUuidWarning": "Guarda este UUID de forma segura. Es tu único acceso a tu cuenta. ¡No hay forma de recuperarlo!", + "copy": "Copiar", + "downloadBackup": "Descargar copia de seguridad", + "confirmSaved": "He guardado mi UUID", + "registrationFailed": "Error en el registro", + "loginRequired": "Inicia sesión para continuar" + }, + "favorites": { + "title": "Favoritos", + "subtitle": "Tus anuncios guardados", + "empty": "Sin favoritos", + "emptyHint": "Pulsa el icono de corazón en un anuncio para guardarlo.", + "browse": "Explorar anuncios" + }, + "myListings": { + "title": "Mis anuncios", + "subtitle": "Gestiona tus anuncios", + "empty": "Sin anuncios", + "emptyHint": "Aún no has creado ningún anuncio.", + "create": "Crear anuncio", + "loginRequired": "Inicio de sesión requerido", + "loginHint": "Inicia sesión para ver tus anuncios.", + "login": "Iniciar sesión", + "status": { + "draft": "Borrador", + "archived": "Archivado", + "processing": "Pendiente", + "published": "Publicado", + "expired": "Caducado" + } + }, + "messages": { + "title": "Mensajes", + "subtitle": "Tus conversaciones", + "empty": "Sin mensajes", + "emptyHint": "Contacta a un vendedor para iniciar una conversación.", + "browse": "Explorar anuncios", + "loginRequired": "Inicio de sesión requerido", + "loginHint": "Inicia sesión para ver tus mensajes.", + "login": "Iniciar sesión", + "unknownListing": "Anuncio desconocido", + "today": "Hoy", + "yesterday": "Ayer", + "daysAgo": "Hace {{days}} días" + }, + "settings": { + "title": "Ajustes", + "appearance": "Apariencia", + "theme": "Tema", + "themeLight": "Claro", + "themeDark": "Oscuro", + "themeSystem": "Sistema", + "language": "Idioma", + "account": "Cuenta", + "userId": "ID de usuario", + "logout": "Cerrar sesión", + "login": "Iniciar sesión", + "notLoggedIn": "No has iniciado sesión.", + "loggedOut": "Sesión cerrada correctamente", + "data": "Datos", + "favorites": "Favoritos", + "favoritesHint": "Eliminar favoritos guardados localmente", + "searchHistory": "Historial de búsqueda", + "searchHistoryHint": "Eliminar filtros de búsqueda guardados", + "clear": "Borrar", + "confirmClearFavorites": "¿Eliminar todos los favoritos?", + "confirmClearSearch": "¿Eliminar el historial de búsqueda?", + "favoritesCleared": "Favoritos eliminados", + "searchCleared": "Historial de búsqueda eliminado", + "about": "Acerca de", + "currency": "Moneda", + "currencyChanged": "Moneda cambiada" + }, + "notifications": { + "title": "Notificaciones", + "empty": "Sin notificaciones", + "markAllRead": "Marcar todo como leído", + "listing_created": "Tu anuncio ha sido creado", + "listing_published": "Tu anuncio ha sido publicado", + "listing_expired": "Tu anuncio ha caducado", + "new_message": "Tienes un mensaje nuevo", + "favorite_added": "Alguien ha guardado tu anuncio" + }, + "payment": { + "title": "Pago", + "listingFee": "Tarifa del anuncio", + "feeInfo": "1 anuncio = 1 mes = {{amount}} {{currency}}", + "payNow": "Pagar ahora", + "paying": "Procesando pago...", + "processing": "Pago recibido, esperando confirmación...", + "success": "¡Pago completado! Tu anuncio ya está publicado.", + "expired": "El pago ha caducado. Inténtalo de nuevo.", + "failed": "El pago ha fallado. Inténtalo de nuevo.", + "resume": "Reanudar pago", + "pending": "Pago pendiente", + "required": "Se requiere una tarifa de {{amount}} {{currency}} para publicar.", + "paidViaXmr": "Pagado con Monero (XMR)", + "awaitingConfirmation": "Esperando confirmación en la cadena de bloques", + "awaitingHint": "Tu pago ha sido recibido. El anuncio se publicará automáticamente tras 1 confirmación." + } +} diff --git a/locales/it.json b/locales/it.json new file mode 100644 index 0000000..8e9c542 --- /dev/null +++ b/locales/it.json @@ -0,0 +1,295 @@ +{ + "header": { + "searchPlaceholder": "Cosa stai cercando?", + "createListing": "Crea annuncio", + "toggleTheme": "Cambia tema", + "selectLanguage": "Seleziona lingua", + "profile": "Profilo" + }, + "footer": { + "rights": "Tutti i diritti riservati.", + "about": "Chi siamo", + "privacy": "Privacy", + "terms": "Condizioni", + "contact": "Contatto" + }, + "home": { + "title": "Benvenuto su dgray.io", + "subtitle": "Trova ottime offerte vicino a te o vendi ciò che non ti serve più.", + "browseListings": "Sfoglia annunci", + "createListing": "Crea annuncio", + "categories": "Categorie", + "recentListings": "Annunci recenti", + "placeholderTitle": "Annuncio di esempio", + "placeholderLocation": "Località", + "addFavorite": "Aggiungi ai preferiti", + "removeFavorite": "Rimuovi dai preferiti", + "noListings": "Nessun annuncio trovato", + "loadMore": "Carica altri", + "pullToRefresh": "Trascina per aggiornare" + }, + "common": { + "loading": "Caricamento...", + "error": "Errore di caricamento", + "close": "Chiudi", + "remove": "Rimuovi", + "home": "Home" + }, + "error": { + "title": "Qualcosa è andato storto", + "retry": "Riprova", + "offline": "Nessuna connessione internet" + }, + "search": { + "title": "Ricerca", + "placeholder": "Inserisci un termine di ricerca...", + "allCategories": "Tutte le categorie", + "allSubcategories": "Tutte le sottocategorie", + "currentLocation": "Posizione attuale", + "locating": "Localizzazione...", + "searchButton": "Cerca", + "loading": "Ricerca in corso...", + "enterQuery": "Inserisci un termine per trovare annunci.", + "noResults": "Nessun risultato trovato. Prova con un altro termine.", + "resultsCount": "{{count}} risultati trovati", + "allIn": "Tutti in", + "clearAll": "Cancella tutto", + "radiusAround": "Raggio di {{radius}} km", + "priceRange": "Prezzo", + "min": "Min", + "max": "Max", + "apply": "Applica", + "sortBy": "Ordina per", + "sortNewest": "Più recenti", + "sortOldest": "Meno recenti", + "sortPriceAsc": "Prezzo: dal più basso", + "sortPriceDesc": "Prezzo: dal più alto", + "sortDistance": "Nelle vicinanze" + }, + "countries": { + "ch": "Svizzera", + "de": "Germania", + "at": "Austria", + "fr": "Francia", + "it": "Italia", + "li": "Liechtenstein" + }, + "listing": { + "notFound": "Questo annuncio non è stato trovato.", + "backHome": "Torna alla home", + "description": "Descrizione", + "location": "Località", + "seller": "Venditore", + "anonymousSeller": "Venditore anonimo", + "memberSince": "Membro dal", + "postedOn": "Pubblicato il", + "contactSeller": "Contatta il venditore", + "paymentInfo": "Il pagamento avviene direttamente tramite Monero (XMR).", + "moneroAddress": "Indirizzo Monero del venditore", + "noMoneroAddress": "Nessun indirizzo Monero fornito", + "copyAddress": "Copia indirizzo", + "contactHint": "Copia l'indirizzo e invia l'importo con il tuo portafoglio Monero.", + "priceOnRequest": "Prezzo su richiesta", + "shippingAvailable": "Spedizione disponibile", + "viewSingular": "visualizzazione", + "viewPlural": "visualizzazioni", + "share": "Condividi", + "report": "Segnala", + "moreFromSeller": "Altri annunci di questo venditore", + "edit": "Modifica", + "expired": "Scaduto", + "expiresIn1Day": "1 giorno rimanente", + "expiresInDays": "{{days}} giorni rimanenti" + }, + "chat": { + "title": "Invia messaggio", + "placeholder": "Scrivi un messaggio...", + "encrypted": "Crittografia end-to-end", + "startConversation": "Inizia una conversazione con il venditore.", + "send": "Invia", + "unavailable": "Chat non disponibile" + }, + "create": { + "title": "Crea annuncio", + "editTitle": "Modifica annuncio", + "listingTitle": "Titolo", + "titlePlaceholder": "Cosa vuoi vendere?", + "category": "Categoria", + "selectCategory": "Seleziona categoria", + "condition": "Condizione", + "conditionNew": "Nuovo", + "conditionLikeNew": "Come nuovo", + "conditionGood": "Buono", + "conditionFair": "Discreto", + "conditionPoor": "Scarso", + "price": "Prezzo", + "currency": "Valuta", + "priceMode": "Modalità prezzo", + "priceModeFiat": "Prezzo fiat fisso", + "priceModeXmr": "Prezzo XMR fisso", + "priceModeHint": "Fiat fisso: l'importo in fiat resta invariato. XMR fisso: l'importo in XMR resta invariato.", + "shippingAvailable": "Spedizione disponibile", + "shippingCost": "Costo di spedizione", + "shippingCostPlaceholder": "es. 5.00", + "location": "Località", + "locationPlaceholder": "Città, CAP o indirizzo", + "locationHint": "Scegli la località per il tuo annuncio", + "description": "Descrizione", + "descriptionPlaceholder": "Descrivi il tuo articolo in dettaglio...", + "images": "Immagini", + "uploadImages": "Carica immagini (max. 5)", + "moneroAddress": "Il tuo indirizzo Monero", + "moneroPlaceholder": "4... o 8...", + "moneroHint": "Gli acquirenti invieranno il pagamento direttamente a questo indirizzo.", + "cancel": "Annulla", + "publish": "Pubblica", + "publishing": "Pubblicazione in corso...", + "saveChanges": "Salva modifiche", + "saving": "Salvataggio...", + "publishFailed": "Pubblicazione fallita. Riprova.", + "invalidMoneroAddress": "Indirizzo Monero non valido. Controlla il formato.", + "draftRestored": "Bozza ripristinata", + "clearDraft": "Elimina" + }, + "notFound": { + "title": "Pagina non trovata", + "message": "La pagina che stai cercando non esiste.", + "backHome": "Torna alla home" + }, + "cropper": { + "title": "Ritaglia immagine", + "preview": "Anteprima:", + "cancel": "Annulla", + "confirm": "Applica", + "aspectRatio": "Rapporto:", + "free": "Libero" + }, + "captcha": { + "verify": "Non sono un robot", + "verified": "Verificato", + "solving": "Verifica in corso...", + "attempts": "tentativi", + "error": "Errore - riprova" + }, + "profile": { + "myListings": "I miei annunci", + "messages": "Messaggi", + "favorites": "Preferiti", + "settings": "Impostazioni" + }, + "auth": { + "login": "Accedi", + "logout": "Esci", + "loggingIn": "Accesso in corso...", + "yourUuid": "Il tuo UUID", + "enterUuid": "Inserisci il tuo UUID", + "invalidUuid": "UUID non valido o account non trovato", + "noAccount": "Non hai ancora un account?", + "hasAccount": "Hai già un account?", + "createAccount": "Crea account", + "registerInfo": "Verrà generato un UUID unico per te. Questo UUID è il tuo unico accesso - conservalo in modo sicuro!", + "generateUuid": "Genera UUID", + "creating": "Creazione in corso...", + "accountCreated": "Account creato!", + "important": "Importante!", + "saveUuidWarning": "Conserva questo UUID in modo sicuro. È il tuo unico accesso al tuo account. Non è possibile recuperarlo!", + "copy": "Copia", + "downloadBackup": "Scarica backup", + "confirmSaved": "Ho salvato il mio UUID", + "registrationFailed": "Registrazione fallita", + "loginRequired": "Accedi per continuare" + }, + "favorites": { + "title": "Preferiti", + "subtitle": "I tuoi annunci salvati", + "empty": "Nessun preferito", + "emptyHint": "Clicca l'icona del cuore su un annuncio per salvarlo.", + "browse": "Sfoglia annunci" + }, + "myListings": { + "title": "I miei annunci", + "subtitle": "Gestisci i tuoi annunci", + "empty": "Nessun annuncio", + "emptyHint": "Non hai ancora creato nessun annuncio.", + "create": "Crea annuncio", + "loginRequired": "Accesso richiesto", + "loginHint": "Accedi per vedere i tuoi annunci.", + "login": "Accedi", + "status": { + "draft": "Bozza", + "archived": "Archiviato", + "processing": "In attesa", + "published": "Pubblicato", + "expired": "Scaduto" + } + }, + "messages": { + "title": "Messaggi", + "subtitle": "Le tue conversazioni", + "empty": "Nessun messaggio", + "emptyHint": "Contatta un venditore per iniziare una conversazione.", + "browse": "Sfoglia annunci", + "loginRequired": "Accesso richiesto", + "loginHint": "Accedi per vedere i tuoi messaggi.", + "login": "Accedi", + "unknownListing": "Annuncio sconosciuto", + "today": "Oggi", + "yesterday": "Ieri", + "daysAgo": "{{days}} giorni fa" + }, + "settings": { + "title": "Impostazioni", + "appearance": "Aspetto", + "theme": "Tema", + "themeLight": "Chiaro", + "themeDark": "Scuro", + "themeSystem": "Sistema", + "language": "Lingua", + "account": "Account", + "userId": "ID utente", + "logout": "Esci", + "login": "Accedi", + "notLoggedIn": "Non hai effettuato l'accesso.", + "loggedOut": "Disconnesso con successo", + "data": "Dati", + "favorites": "Preferiti", + "favoritesHint": "Elimina i preferiti salvati localmente", + "searchHistory": "Cronologia ricerche", + "searchHistoryHint": "Elimina i filtri di ricerca salvati", + "clear": "Cancella", + "confirmClearFavorites": "Eliminare tutti i preferiti?", + "confirmClearSearch": "Eliminare la cronologia delle ricerche?", + "favoritesCleared": "Preferiti eliminati", + "searchCleared": "Cronologia ricerche eliminata", + "about": "Informazioni", + "currency": "Valuta", + "currencyChanged": "Valuta cambiata" + }, + "notifications": { + "title": "Notifiche", + "empty": "Nessuna notifica", + "markAllRead": "Segna tutto come letto", + "listing_created": "Il tuo annuncio è stato creato", + "listing_published": "Il tuo annuncio è stato pubblicato", + "listing_expired": "Il tuo annuncio è scaduto", + "new_message": "Hai un nuovo messaggio", + "favorite_added": "Qualcuno ha salvato il tuo annuncio" + }, + "payment": { + "title": "Pagamento", + "listingFee": "Tariffa annuncio", + "feeInfo": "1 annuncio = 1 mese = {{amount}} {{currency}}", + "payNow": "Paga ora", + "paying": "Pagamento in corso...", + "processing": "Pagamento ricevuto, in attesa di conferma...", + "success": "Pagamento riuscito! Il tuo annuncio è ora online.", + "expired": "Pagamento scaduto. Riprova.", + "failed": "Pagamento fallito. Riprova.", + "resume": "Riprendi pagamento", + "pending": "Pagamento in sospeso", + "required": "Per la pubblicazione è richiesta una tariffa di {{amount}} {{currency}}.", + "paidViaXmr": "Pagato tramite Monero (XMR)", + "awaitingConfirmation": "In attesa di conferma sulla blockchain", + "awaitingHint": "Il tuo pagamento è stato ricevuto. L'annuncio verrà pubblicato automaticamente dopo 1 conferma." + } +} diff --git a/locales/pt.json b/locales/pt.json new file mode 100644 index 0000000..90cb03f --- /dev/null +++ b/locales/pt.json @@ -0,0 +1,295 @@ +{ + "header": { + "searchPlaceholder": "O que você está procurando?", + "createListing": "Criar Anúncio", + "toggleTheme": "Alternar tema", + "selectLanguage": "Selecionar idioma", + "profile": "Perfil" + }, + "footer": { + "rights": "Todos os direitos reservados.", + "about": "Sobre", + "privacy": "Privacidade", + "terms": "Termos", + "contact": "Contato" + }, + "home": { + "title": "Bem-vindo ao dgray.io", + "subtitle": "Encontre boas ofertas perto de você ou venda o que não precisa mais.", + "browseListings": "Ver Anúncios", + "createListing": "Criar Anúncio", + "categories": "Categorias", + "recentListings": "Anúncios Recentes", + "placeholderTitle": "Anúncio de Exemplo", + "placeholderLocation": "Localização", + "addFavorite": "Adicionar aos favoritos", + "removeFavorite": "Remover dos favoritos", + "noListings": "Nenhum anúncio encontrado", + "loadMore": "Carregar mais", + "pullToRefresh": "Puxe para atualizar" + }, + "common": { + "loading": "Carregando...", + "error": "Erro ao carregar", + "close": "Fechar", + "remove": "Remover", + "home": "Início" + }, + "error": { + "title": "Algo deu errado", + "retry": "Tentar novamente", + "offline": "Sem conexão com a internet" + }, + "search": { + "title": "Busca", + "placeholder": "Digite o termo de busca...", + "allCategories": "Todas as Categorias", + "allSubcategories": "Todas as Subcategorias", + "currentLocation": "Localização Atual", + "locating": "Localizando...", + "searchButton": "Buscar", + "loading": "Buscando...", + "enterQuery": "Digite um termo de busca para encontrar anúncios.", + "noResults": "Nenhum resultado encontrado. Tente outro termo de busca.", + "resultsCount": "{{count}} resultados encontrados", + "allIn": "Todos em", + "clearAll": "Limpar tudo", + "radiusAround": "Raio de {{radius}} km", + "priceRange": "Preço", + "min": "Mín", + "max": "Máx", + "apply": "Aplicar", + "sortBy": "Ordenar por", + "sortNewest": "Mais recentes", + "sortOldest": "Mais antigos", + "sortPriceAsc": "Preço: menor para maior", + "sortPriceDesc": "Preço: maior para menor", + "sortDistance": "Próximos" + }, + "countries": { + "ch": "Suíça", + "de": "Alemanha", + "at": "Áustria", + "fr": "França", + "it": "Itália", + "li": "Liechtenstein" + }, + "listing": { + "notFound": "Este anúncio não foi encontrado.", + "backHome": "Voltar ao Início", + "description": "Descrição", + "location": "Localização", + "seller": "Vendedor", + "anonymousSeller": "Vendedor Anônimo", + "memberSince": "Membro desde", + "postedOn": "Publicado em", + "contactSeller": "Contatar Vendedor", + "paymentInfo": "O pagamento é feito diretamente via Monero (XMR).", + "moneroAddress": "Endereço Monero do Vendedor", + "noMoneroAddress": "Nenhum endereço Monero fornecido", + "copyAddress": "Copiar endereço", + "contactHint": "Copie o endereço e envie o valor usando sua carteira Monero.", + "priceOnRequest": "Preço sob consulta", + "shippingAvailable": "Envio disponível", + "viewSingular": "visualização", + "viewPlural": "visualizações", + "share": "Compartilhar", + "report": "Denunciar", + "moreFromSeller": "Mais deste vendedor", + "edit": "Editar", + "expired": "Expirado", + "expiresIn1Day": "1 dia restante", + "expiresInDays": "{{days}} dias restantes" + }, + "chat": { + "title": "Enviar Mensagem", + "placeholder": "Escreva uma mensagem...", + "encrypted": "Criptografia de ponta a ponta", + "startConversation": "Inicie uma conversa com o vendedor.", + "send": "Enviar", + "unavailable": "Chat indisponível" + }, + "create": { + "title": "Criar Anúncio", + "editTitle": "Editar Anúncio", + "listingTitle": "Título", + "titlePlaceholder": "O que você quer vender?", + "category": "Categoria", + "selectCategory": "Selecionar categoria", + "condition": "Condição", + "conditionNew": "Novo", + "conditionLikeNew": "Seminovo", + "conditionGood": "Bom", + "conditionFair": "Regular", + "conditionPoor": "Ruim", + "price": "Preço", + "currency": "Moeda", + "priceMode": "Modo de preço", + "priceModeFiat": "Preço fixo em fiat", + "priceModeXmr": "Preço fixo em XMR", + "priceModeHint": "Fiat fixo: O valor em fiat permanece o mesmo. XMR fixo: O valor em XMR permanece o mesmo.", + "shippingAvailable": "Envio disponível", + "shippingCost": "Custo de envio", + "shippingCostPlaceholder": "ex: 5,00", + "location": "Localização", + "locationPlaceholder": "Cidade, CEP ou endereço", + "locationHint": "Escolha a localização do seu anúncio", + "description": "Descrição", + "descriptionPlaceholder": "Descreva seu item em detalhes...", + "images": "Imagens", + "uploadImages": "Enviar imagens (máx. 5)", + "moneroAddress": "Seu Endereço Monero", + "moneroPlaceholder": "4... ou 8...", + "moneroHint": "Os compradores enviarão o pagamento diretamente para este endereço.", + "cancel": "Cancelar", + "publish": "Publicar", + "publishing": "Publicando...", + "saveChanges": "Salvar Alterações", + "saving": "Salvando...", + "publishFailed": "Falha ao publicar. Por favor, tente novamente.", + "invalidMoneroAddress": "Endereço Monero inválido. Verifique o formato.", + "draftRestored": "Rascunho restaurado", + "clearDraft": "Descartar" + }, + "notFound": { + "title": "Página Não Encontrada", + "message": "A página que você procura não existe.", + "backHome": "Voltar ao Início" + }, + "cropper": { + "title": "Cortar imagem", + "preview": "Pré-visualização:", + "cancel": "Cancelar", + "confirm": "Aplicar", + "aspectRatio": "Proporção:", + "free": "Livre" + }, + "captcha": { + "verify": "Não sou um robô", + "verified": "Verificado", + "solving": "Verificando...", + "attempts": "tentativas", + "error": "Erro - tente novamente" + }, + "profile": { + "myListings": "Meus Anúncios", + "messages": "Mensagens", + "favorites": "Favoritos", + "settings": "Configurações" + }, + "auth": { + "login": "Entrar", + "logout": "Sair", + "loggingIn": "Entrando...", + "yourUuid": "Seu UUID", + "enterUuid": "Por favor, insira seu UUID", + "invalidUuid": "UUID inválido ou conta não encontrada", + "noAccount": "Ainda não tem conta?", + "hasAccount": "Já tem uma conta?", + "createAccount": "Criar Conta", + "registerInfo": "Um UUID único será gerado para você. Este UUID é seu único acesso - guarde-o com segurança!", + "generateUuid": "Gerar UUID", + "creating": "Criando...", + "accountCreated": "Conta Criada!", + "important": "Importante!", + "saveUuidWarning": "Guarde este UUID com segurança. Ele é seu único acesso à sua conta. Não há como recuperá-lo!", + "copy": "Copiar", + "downloadBackup": "Baixar Backup", + "confirmSaved": "Eu salvei meu UUID", + "registrationFailed": "Falha no cadastro", + "loginRequired": "Por favor, faça login para continuar" + }, + "favorites": { + "title": "Favoritos", + "subtitle": "Seus anúncios salvos", + "empty": "Sem favoritos", + "emptyHint": "Clique no ícone de coração em um anúncio para salvá-lo.", + "browse": "Ver anúncios" + }, + "myListings": { + "title": "Meus Anúncios", + "subtitle": "Gerencie seus anúncios", + "empty": "Sem anúncios", + "emptyHint": "Você ainda não criou nenhum anúncio.", + "create": "Criar anúncio", + "loginRequired": "Login necessário", + "loginHint": "Faça login para ver seus anúncios.", + "login": "Entrar", + "status": { + "draft": "Rascunho", + "archived": "Arquivado", + "processing": "Pendente", + "published": "Publicado", + "expired": "Expirado" + } + }, + "messages": { + "title": "Mensagens", + "subtitle": "Suas conversas", + "empty": "Sem mensagens", + "emptyHint": "Entre em contato com um vendedor para iniciar uma conversa.", + "browse": "Ver anúncios", + "loginRequired": "Login necessário", + "loginHint": "Faça login para ver suas mensagens.", + "login": "Entrar", + "unknownListing": "Anúncio desconhecido", + "today": "Hoje", + "yesterday": "Ontem", + "daysAgo": "{{days}} dias atrás" + }, + "settings": { + "title": "Configurações", + "appearance": "Aparência", + "theme": "Tema", + "themeLight": "Claro", + "themeDark": "Escuro", + "themeSystem": "Sistema", + "language": "Idioma", + "account": "Conta", + "userId": "ID do Usuário", + "logout": "Sair", + "login": "Entrar", + "notLoggedIn": "Você não está logado.", + "loggedOut": "Desconectado com sucesso", + "data": "Dados", + "favorites": "Favoritos", + "favoritesHint": "Excluir favoritos salvos localmente", + "searchHistory": "Histórico de busca", + "searchHistoryHint": "Excluir filtros de busca salvos", + "clear": "Limpar", + "confirmClearFavorites": "Excluir todos os favoritos?", + "confirmClearSearch": "Excluir histórico de busca?", + "favoritesCleared": "Favoritos excluídos", + "searchCleared": "Histórico de busca excluído", + "about": "Sobre", + "currency": "Moeda", + "currencyChanged": "Moeda alterada" + }, + "notifications": { + "title": "Notificações", + "empty": "Sem notificações", + "markAllRead": "Marcar todas como lidas", + "listing_created": "Seu anúncio foi criado", + "listing_published": "Seu anúncio foi publicado", + "listing_expired": "Seu anúncio expirou", + "new_message": "Você tem uma nova mensagem", + "favorite_added": "Alguém salvou seu anúncio" + }, + "payment": { + "title": "Pagamento", + "listingFee": "Taxa do Anúncio", + "feeInfo": "1 anúncio = 1 mês = {{amount}} {{currency}}", + "payNow": "Pagar Agora", + "paying": "Processando pagamento...", + "processing": "Pagamento recebido, aguardando confirmação...", + "success": "Pagamento realizado! Seu anúncio está no ar.", + "expired": "Pagamento expirado. Por favor, tente novamente.", + "failed": "Pagamento falhou. Por favor, tente novamente.", + "resume": "Retomar pagamento", + "pending": "Pagamento pendente", + "required": "Uma taxa de {{amount}} {{currency}} é necessária para publicar.", + "paidViaXmr": "Pago via Monero (XMR)", + "awaitingConfirmation": "Aguardando confirmação na blockchain", + "awaitingHint": "Seu pagamento foi recebido. O anúncio será publicado automaticamente após 1 confirmação." + } +} diff --git a/locales/ru.json b/locales/ru.json new file mode 100644 index 0000000..6f13ca1 --- /dev/null +++ b/locales/ru.json @@ -0,0 +1,295 @@ +{ + "header": { + "searchPlaceholder": "Что вы ищете?", + "createListing": "Создать объявление", + "toggleTheme": "Переключить тему", + "selectLanguage": "Выбрать язык", + "profile": "Профиль" + }, + "footer": { + "rights": "Все права защищены.", + "about": "О нас", + "privacy": "Конфиденциальность", + "terms": "Условия", + "contact": "Контакты" + }, + "home": { + "title": "Добро пожаловать на dgray.io", + "subtitle": "Находите выгодные предложения рядом или продавайте ненужное.", + "browseListings": "Смотреть объявления", + "createListing": "Создать объявление", + "categories": "Категории", + "recentListings": "Новые объявления", + "placeholderTitle": "Пример объявления", + "placeholderLocation": "Местоположение", + "addFavorite": "Добавить в избранное", + "removeFavorite": "Удалить из избранного", + "noListings": "Объявления не найдены", + "loadMore": "Загрузить ещё", + "pullToRefresh": "Потяните для обновления" + }, + "common": { + "loading": "Загрузка...", + "error": "Ошибка загрузки", + "close": "Закрыть", + "remove": "Удалить", + "home": "Главная" + }, + "error": { + "title": "Что-то пошло не так", + "retry": "Попробовать снова", + "offline": "Нет подключения к интернету" + }, + "search": { + "title": "Поиск", + "placeholder": "Введите поисковый запрос...", + "allCategories": "Все категории", + "allSubcategories": "Все подкатегории", + "currentLocation": "Текущее местоположение", + "locating": "Определение...", + "searchButton": "Найти", + "loading": "Поиск...", + "enterQuery": "Введите запрос для поиска объявлений.", + "noResults": "Ничего не найдено. Попробуйте другой запрос.", + "resultsCount": "Найдено результатов: {{count}}", + "allIn": "Все в", + "clearAll": "Очистить всё", + "radiusAround": "Радиус {{radius}} км", + "priceRange": "Цена", + "min": "Мин", + "max": "Макс", + "apply": "Применить", + "sortBy": "Сортировка", + "sortNewest": "Сначала новые", + "sortOldest": "Сначала старые", + "sortPriceAsc": "Цена: по возрастанию", + "sortPriceDesc": "Цена: по убыванию", + "sortDistance": "Рядом" + }, + "countries": { + "ch": "Швейцария", + "de": "Германия", + "at": "Австрия", + "fr": "Франция", + "it": "Италия", + "li": "Лихтенштейн" + }, + "listing": { + "notFound": "Объявление не найдено.", + "backHome": "На главную", + "description": "Описание", + "location": "Местоположение", + "seller": "Продавец", + "anonymousSeller": "Анонимный продавец", + "memberSince": "Участник с", + "postedOn": "Опубликовано", + "contactSeller": "Связаться с продавцом", + "paymentInfo": "Оплата производится напрямую через Monero (XMR).", + "moneroAddress": "Monero-адрес продавца", + "noMoneroAddress": "Monero-адрес не указан", + "copyAddress": "Копировать адрес", + "contactHint": "Скопируйте адрес и отправьте сумму через ваш Monero-кошелёк.", + "priceOnRequest": "Цена по запросу", + "shippingAvailable": "Доставка возможна", + "viewSingular": "просмотр", + "viewPlural": "просмотров", + "share": "Поделиться", + "report": "Пожаловаться", + "moreFromSeller": "Другие объявления продавца", + "edit": "Редактировать", + "expired": "Истекло", + "expiresIn1Day": "Остался 1 день", + "expiresInDays": "Осталось {{days}} дн." + }, + "chat": { + "title": "Отправить сообщение", + "placeholder": "Написать сообщение...", + "encrypted": "Сквозное шифрование", + "startConversation": "Начните разговор с продавцом.", + "send": "Отправить", + "unavailable": "Чат недоступен" + }, + "create": { + "title": "Создать объявление", + "editTitle": "Редактировать объявление", + "listingTitle": "Название", + "titlePlaceholder": "Что вы хотите продать?", + "category": "Категория", + "selectCategory": "Выберите категорию", + "condition": "Состояние", + "conditionNew": "Новое", + "conditionLikeNew": "Как новое", + "conditionGood": "Хорошее", + "conditionFair": "Удовлетворительное", + "conditionPoor": "Плохое", + "price": "Цена", + "currency": "Валюта", + "priceMode": "Режим цены", + "priceModeFiat": "Фиксированная цена в фиате", + "priceModeXmr": "Фиксированная цена в XMR", + "priceModeHint": "Фиат: сумма в фиате остаётся неизменной. XMR: сумма в XMR остаётся неизменной.", + "shippingAvailable": "Доставка возможна", + "shippingCost": "Стоимость доставки", + "shippingCostPlaceholder": "напр. 5.00", + "location": "Местоположение", + "locationPlaceholder": "Город, индекс или адрес", + "locationHint": "Укажите местоположение для вашего объявления", + "description": "Описание", + "descriptionPlaceholder": "Подробно опишите ваш товар...", + "images": "Изображения", + "uploadImages": "Загрузить изображения (макс. 5)", + "moneroAddress": "Ваш Monero-адрес", + "moneroPlaceholder": "4... или 8...", + "moneroHint": "Покупатели отправят оплату напрямую на этот адрес.", + "cancel": "Отмена", + "publish": "Опубликовать", + "publishing": "Публикация...", + "saveChanges": "Сохранить изменения", + "saving": "Сохранение...", + "publishFailed": "Публикация не удалась. Попробуйте снова.", + "invalidMoneroAddress": "Неверный Monero-адрес. Проверьте формат.", + "draftRestored": "Черновик восстановлен", + "clearDraft": "Удалить черновик" + }, + "notFound": { + "title": "Страница не найдена", + "message": "Запрашиваемая страница не существует.", + "backHome": "На главную" + }, + "cropper": { + "title": "Обрезать изображение", + "preview": "Предпросмотр:", + "cancel": "Отмена", + "confirm": "Применить", + "aspectRatio": "Соотношение:", + "free": "Свободное" + }, + "captcha": { + "verify": "Я не робот", + "verified": "Подтверждено", + "solving": "Проверка...", + "attempts": "попыток", + "error": "Ошибка — попробуйте снова" + }, + "profile": { + "myListings": "Мои объявления", + "messages": "Сообщения", + "favorites": "Избранное", + "settings": "Настройки" + }, + "auth": { + "login": "Войти", + "logout": "Выйти", + "loggingIn": "Вход...", + "yourUuid": "Ваш UUID", + "enterUuid": "Введите ваш UUID", + "invalidUuid": "Неверный UUID или аккаунт не найден", + "noAccount": "Нет аккаунта?", + "hasAccount": "Уже есть аккаунт?", + "createAccount": "Создать аккаунт", + "registerInfo": "Для вас будет сгенерирован уникальный UUID. Этот UUID — ваш единственный доступ, сохраните его надёжно!", + "generateUuid": "Сгенерировать UUID", + "creating": "Создание...", + "accountCreated": "Аккаунт создан!", + "important": "Важно!", + "saveUuidWarning": "Сохраните этот UUID надёжно. Это ваш единственный доступ к аккаунту. Восстановить его невозможно!", + "copy": "Копировать", + "downloadBackup": "Скачать резервную копию", + "confirmSaved": "Я сохранил свой UUID", + "registrationFailed": "Регистрация не удалась", + "loginRequired": "Войдите, чтобы продолжить" + }, + "favorites": { + "title": "Избранное", + "subtitle": "Сохранённые объявления", + "empty": "Нет избранного", + "emptyHint": "Нажмите на сердечко, чтобы сохранить объявление.", + "browse": "Смотреть объявления" + }, + "myListings": { + "title": "Мои объявления", + "subtitle": "Управление объявлениями", + "empty": "Нет объявлений", + "emptyHint": "Вы ещё не создали ни одного объявления.", + "create": "Создать объявление", + "loginRequired": "Необходимо войти", + "loginHint": "Войдите, чтобы увидеть свои объявления.", + "login": "Войти", + "status": { + "draft": "Черновик", + "archived": "В архиве", + "processing": "На рассмотрении", + "published": "Опубликовано", + "expired": "Истекло" + } + }, + "messages": { + "title": "Сообщения", + "subtitle": "Ваши переписки", + "empty": "Нет сообщений", + "emptyHint": "Свяжитесь с продавцом, чтобы начать переписку.", + "browse": "Смотреть объявления", + "loginRequired": "Необходимо войти", + "loginHint": "Войдите, чтобы увидеть свои сообщения.", + "login": "Войти", + "unknownListing": "Неизвестное объявление", + "today": "Сегодня", + "yesterday": "Вчера", + "daysAgo": "{{days}} дн. назад" + }, + "settings": { + "title": "Настройки", + "appearance": "Внешний вид", + "theme": "Тема", + "themeLight": "Светлая", + "themeDark": "Тёмная", + "themeSystem": "Системная", + "language": "Язык", + "account": "Аккаунт", + "userId": "ID пользователя", + "logout": "Выйти", + "login": "Войти", + "notLoggedIn": "Вы не вошли в аккаунт.", + "loggedOut": "Вы вышли из аккаунта", + "data": "Данные", + "favorites": "Избранное", + "favoritesHint": "Удалить локально сохранённое избранное", + "searchHistory": "История поиска", + "searchHistoryHint": "Удалить сохранённые фильтры поиска", + "clear": "Очистить", + "confirmClearFavorites": "Удалить всё избранное?", + "confirmClearSearch": "Удалить историю поиска?", + "favoritesCleared": "Избранное удалено", + "searchCleared": "История поиска удалена", + "about": "О приложении", + "currency": "Валюта", + "currencyChanged": "Валюта изменена" + }, + "notifications": { + "title": "Уведомления", + "empty": "Нет уведомлений", + "markAllRead": "Отметить все как прочитанные", + "listing_created": "Ваше объявление создано", + "listing_published": "Ваше объявление опубликовано", + "listing_expired": "Срок вашего объявления истёк", + "new_message": "У вас новое сообщение", + "favorite_added": "Кто-то сохранил ваше объявление" + }, + "payment": { + "title": "Оплата", + "listingFee": "Стоимость размещения", + "feeInfo": "1 объявление = 1 месяц = {{amount}} {{currency}}", + "payNow": "Оплатить", + "paying": "Обработка платежа...", + "processing": "Платёж получен, ожидание подтверждения...", + "success": "Оплата прошла успешно! Ваше объявление опубликовано.", + "expired": "Время оплаты истекло. Попробуйте снова.", + "failed": "Оплата не удалась. Попробуйте снова.", + "resume": "Продолжить оплату", + "pending": "Ожидание оплаты", + "required": "Для публикации требуется оплата {{amount}} {{currency}}.", + "paidViaXmr": "Оплачено через Monero (XMR)", + "awaitingConfirmation": "Ожидание подтверждения в блокчейне", + "awaitingHint": "Ваш платёж получен. Объявление будет опубликовано автоматически после 1 подтверждения." + } +}