diff --git a/apps/web/src/components/language-switcher.tsx b/apps/web/src/components/language-switcher.tsx index 93a33e4..1b50120 100644 --- a/apps/web/src/components/language-switcher.tsx +++ b/apps/web/src/components/language-switcher.tsx @@ -7,6 +7,7 @@ import { useEffect, useRef, useState } from "react"; const LOCALES = [ { value: "en", short: "EN", label: "English" }, { value: "de", short: "DE", label: "Deutsch" }, + { value: "hu", short: "HU", label: "Magyar" }, ] as const; export function LanguageSwitcher({ locale }: { locale: string }) { diff --git a/apps/web/src/i18n/dictionaries.ts b/apps/web/src/i18n/dictionaries.ts index b009312..7bf437d 100644 --- a/apps/web/src/i18n/dictionaries.ts +++ b/apps/web/src/i18n/dictionaries.ts @@ -1,4 +1,4 @@ -export type Locale = "en" | "de"; +export type Locale = "en" | "de" | "hu"; const en = { header: { login: "Log in", dashboard: "Dashboard", logout: "Log out", toggleTheme: "Toggle theme", language: "Language" }, @@ -386,4 +386,196 @@ const de: Dictionary = { authError: "Anmeldung fehlgeschlagen. Bitte versuche es erneut.", }; -export const dictionaries: Record = { en, de }; +const hu: Dictionary = { + header: { login: "Bejelentkezés", dashboard: "Vezérlőpult", logout: "Kijelentkezés", toggleTheme: "Téma váltása", language: "Nyelv" }, + footer: { + tagline: "Jelentkezési űrlapok valódi állapotkövetéssel, a MSK Scripts-től.", + product: "Termék", ecosystem: "Ökoszisztéma", legal: "Jogi információk", + demoForm: "Demo űrlap", documentation: "Dokumentáció", github: "GitHub", + imprint: "Impresszum", privacy: "Adatvédelem", terms: "Felhasználási feltételek", + rights: "Minden jog fenntartva.", + }, + hero: { + badge: "Jelentkezési platform", + headPre: "Jelentkezési űrlapok valódi ", headAccent: "állapotkövetéssel", headPost: ".", + sub: "Hozz létre űrlapot, oszd meg a linket, és a jelentkezők élőben követhetik az állapotukat. Discord bottal bármelyik szerver meghívhatja.", + loginDiscord: "Bejelentkezés Discorddal", demo: "Demo űrlap megtekintése", openDashboard: "Vezérlőpult megnyitása", + inviteBot: "Bot meghívása", + }, + features: { + title: "Amit a legtöbb űrlapeszköz kihagy.", + sub: "Egy űrlap egyszerű. A nehéz rész elmondani az embereknek, mi történt a beküldés után. Pontosan erről szól a MSK Forms.", + uspTitle: "A jelentkezők mindig tudják, hol tartanak.", + uspBody: + "Minden beküldés privát linket kap. Az állapot frissül, amint egy bíráló intézkedik – ugyanazon az oldalon, ahol jelentkeztek. Nincs több „láttad a jelentkezésem?”", + sSubmitted: "Beküldve", sInReview: "Elbírálás alatt", sAccepted: "Elfogadva", + botTitle: "Meghívható bot", + botBody: "Tedd ki az űrlapokat egy csatornába, és küldd az állapotfrissítéseket közvetlenül a jelentkezőknek privát üzenetben.", + buildTitle: "Építsd kód nélkül", + buildBody: "Állítsd össze az űrlapokat típusos mezőkből: szöveg, választás, dátum, hozzájárulás. Másodpercek alatt kész.", + }, + steps: { + title: "Nulláról élesbe három lépésben.", + build: "Építés", buildBody: "Húzd be a szükséges mezőket, és állítsd be, ki küldhet be.", + share: "Megosztás", shareBody: "Tedd közzé a linket, vagy posztold az űrlapot Discordra a bottal.", + review: "Elbírálás", reviewBody: "Fogadd el, utasítsd el, vagy kérj többet. A jelentkezők élőben látják.", + }, + cta: { + title: "Kezdj el olyan jelentkezéseket gyűjteni, amelyeket valóban követni lehet.", + openDashboard: "Vezérlőpult megnyitása", loginDiscord: "Bejelentkezés Discorddal", + inviteBot: "Discord bot meghívása", + }, + form: { + notAccepting: "Ez az űrlap jelenleg nem fogad válaszokat.", + needLogin: "Az űrlap kitöltéséhez be kell jelentkezned.", + loginDiscord: "Bejelentkezés Discorddal", + accessRestricted: "Ez az űrlap olyan hozzáférési korlátozást használ, amely még nem támogatott.", + submit: "Beküldés", submitting: "Beküldés…", required: "Ez a mező kötelező.", + submitFailed: "A beküldés sikertelen.", + captchaRequired: "Kérjük, töltsd ki a captchát.", + fileUploading: "Feltöltés…", fileRemove: "Eltávolítás", uploadFailed: "A feltöltés sikertelen.", + signatureClear: "Törlés", + next: "Tovább", back: "Vissza", step: "Lépés", + }, + status: { + yourSubmission: "A beküldésed", activity: "Tevékenység", yourAnswers: "A válaszaid", + submitted: "Beküldve", notAnswered: "Nincs megválaszolva", statusChangedTo: "Az állapot megváltozott erre:", update: "Frissítés", + yes: "Igen", no: "Nem", + yourData: "Az adataid", + withdraw: "Beküldés visszavonása", + withdrawConfirm: "Visszavonod ezt a beküldést? Lehet, hogy nem tudsz azonnal újra jelentkezni.", + exportData: "Adataim letöltése (JSON)", + deleteData: "Beküldésem törlése", + deleteConfirm: "Véglegesen törlöd ezt a beküldést és a feltöltött fájljait? Ez nem vonható vissza.", + deleted: "A beküldésed törölve lett.", + actionFailed: "A művelet sikertelen. Kérjük, próbáld újra.", + cancel: "Mégse", + }, + preview: { + submission: "A beküldésed", title: "Whitelist jelentkezés", inReview: "Elbírálás alatt", + submitted: "Beküldve", pickedUp: "Egy bíráló átvette", decision: "Döntés", + pending: "függőben", reviewerNote: "Bírálói megjegyzés", + note: "Jól néz ki. Megerősítjük a játékbeli neved, és bent vagy.", + }, + dashboard: { + guilds: "Szerverek", mySubmissions: "Beküldéseim", formsTab: "Űrlapok", submissionsTab: "Beküldések", boardTab: "Tábla", brandingTab: "Arculat", botTab: "Bot", statusesTab: "Állapotok", + board: { + title: "Tábla", + moveTo: "Áthelyezés ide", + columnEmpty: "Itt nincs semmi.", + saveFailed: "Az állapot nem frissíthető.", + noPerm: "Nincs jogosultságod a beküldések elbírálásához.", + }, + bulk: { selected: "kiválasztva", apply: "Alkalmaz", applying: "Alkalmazás…", failed: "A tömeges frissítés sikertelen." }, + statuses: { + title: "Egyéni állapotok", + intro: "Adj hozzá saját bírálati állapotokat a beépített folyamat mellé. A bírálók bármelyikre áthelyezhetik a beküldéseket.", + defaultsTitle: "Beépített állapotok", + key: "Kulcs", label: "Címke", color: "Szín", terminal: "Végállapot", visible: "Látható a jelentkezőnek", + add: "Állapot hozzáadása", remove: "Eltávolítás", + save: "Állapotok mentése", saving: "Mentés…", saved: "Mentve!", + empty: "Még nincsenek egyéni állapotok.", + errSave: "Az állapotok mentése sikertelen.", + noPerm: "Nincs jogosultságod az állapotok kezeléséhez.", + }, + yourGuilds: "A szervereid", + noGuilds: "Még nem kezelsz egyetlen szervert sem.", + noGuildsHint: + "Egy szerver összekapcsolása a Discord bot meghívásán keresztül történik. Ez a folyamat egy későbbi szakaszban érkezik. Egyelőre egy adminisztrátor hozzáadhat egy szerverhez.", + countForm: "űrlap", countForms: "űrlap", countSubmissions: "beküldés", + newForm: "Új űrlap", noForms: "Még nincsenek űrlapok. Hozd létre az elsőt.", edit: "Szerkesztés", exportCsv: "CSV exportálás", + deleteForm: "Törlés", deleteFormTitle: "Törlöd az űrlapot?", deleteFormConfirm: "Törlöd ezt az űrlapot? Az összes beküldése, fájlja és tevékenysége véglegesen törlődik. Ez nem vonható vissza.", deleteFormFailed: "Az űrlap törlése sikertelen.", cancel: "Mégse", + colApplicant: "Jelentkező", colForm: "Űrlap", colDate: "Dátum", colStatus: "Állapot", + anonymous: "Névtelen", noSubmissions: "Még nincsenek beküldések.", open: "Megnyitás", + mySubmissionsTitle: "Beküldéseim", noMySubmissions: "Még nem küldtél be egyetlen űrlapot sem.", + newFormTitle: "Új űrlap", editFormTitle: "Űrlap szerkesztése", + noPermCreate: "Nincs jogosultságod űrlapok létrehozásához.", + noPermEdit: "Nincs jogosultságod űrlapok szerkesztéséhez.", + }, + review: { + back: "Vissza a beküldésekhez", + applicant: "Jelentkező", anonymous: "Névtelen", submitted: "Beküldve", + answers: "Válaszok", timeline: "Tevékenység", noEvents: "Még nincs tevékenység.", + actions: "Bírálati műveletek", + changeStatus: "Állapot módosítása", apply: "Alkalmaz", + noteLabel: "Belső megjegyzés", noteHint: "Csak a csapatod látja.", + notePlaceholder: "Adj hozzá megjegyzést a csapatnak…", saveNote: "Megjegyzés mentése", + messageLabel: "Üzenet a jelentkezőnek", messageHint: "Megjelenik a jelentkező állapotoldalán.", + messagePlaceholder: "Írj üzenetet a jelentkezőnek…", sendMessage: "Üzenet küldése", + internal: "Belső", public: "Nyilvános", system: "Rendszer", + statusChanged: "állapotot módosított", noteAdded: "belső megjegyzést adott hozzá", messageSent: "üzenetet küldött", + to: "erre:", saving: "Mentés…", actionFailed: "A művelet sikertelen.", + noPermReview: "Nincs jogosultságod a beküldések elbírálásához.", + }, + branding: { + title: "Arculat", + accentColor: "Kiemelő szín", + accentHint: "A nyilvános űrlap- és állapotoldalakon jelenik meg.", + save: "Mentés", saving: "Mentés…", saved: "Mentve.", reset: "Visszaállítás", + errSave: "Az arculat mentése sikertelen.", + noPerm: "Nincs jogosultságod az arculat szerkesztéséhez.", + logo: "Logó", + logoHint: "PNG, JPEG vagy WebP, legfeljebb 1 MB. A nyilvános űrlap- és állapotoldalakon jelenik meg.", + logoUploading: "Feltöltés…", logoRemove: "Logó eltávolítása", logoFailed: "A logó frissítése sikertelen.", + }, + botConfig: { + title: "Bot beállítások", + reviewChannel: "Bírálati csatorna azonosítója", + reviewChannelHint: "Az új beküldések ide kerülnek, hogy a csapatod elbírálhassa.", + acceptedRole: "Elfogadási szerep azonosítója", + acceptedRoleHint: "Alapértelmezett szerep elfogadáskor. Minden űrlap felülírhatja a saját beállításaiban.", + idHint: "Kapcsold be a Fejlesztői módot a Discordban (Beállítások → Speciális), majd jobb klikk egy csatornára/szerepre → Azonosító másolása.", + postName: "Megjelenítendő név posztoláskor", + postNameHint: "Ha be van állítva, a bot ezzel a névvel és a logóddal posztolja az űrlapokat és bírálati beágyazásokat (webhookon keresztül). Szükséges a Webhookok kezelése jogosultság. Hagyd üresen, hogy botként posztoljon.", + save: "Mentés", saving: "Mentés…", saved: "Mentve.", + errSave: "A bot beállítások mentése sikertelen.", + noPerm: "Nincs jogosultságod a bot beállítások szerkesztéséhez.", + }, + builder: { + title: "Cím", description: "Leírás", slug: "Slug", slugHint: "Nyilvános URL: /f/", auto: "Auto", + status: "Állapot", visibility: "Láthatóság", + statusDraft: "Vázlat", statusLive: "Élő", statusClosed: "Lezárt", statusArchived: "Archivált", + visPublic: "Nyilvános", visAuth: "Bejelentkezés szükséges", + acceptedRole: "Elfogadási szerep azonosítója", acceptedRoleHint: "Elfogadáskor ehhez az űrlaphoz adva (felülírja a szerver alapértelmezését a Bot beállításokban). Hagyd üresen az alapértelmezéshez.", + addField: "Mező hozzáadása", add: "Hozzáadás", + page: "Oldal", addPage: "Oldal hozzáadása", removePage: "Oldal eltávolítása", pageTitlePh: "Oldal címe (opcionális)…", + saving: "Mentés…", saveChanges: "Módosítások mentése", createForm: "Űrlap létrehozása", cancel: "Mégse", + errTitle: "A cím kötelező.", errSlug: "A slug kötelező.", errFields: "Adj hozzá legalább egy mezőt.", + errSave: "Az űrlap mentése sikertelen.", + fieldLabel: "Címke", fieldText: "Szöveg", helpText: "Súgószöveg", helpHint: "Opcionális, a címke alatt jelenik meg.", + options: "Opciók", optionPh: "Opció", required: "Kötelező", addOption: "Opció hozzáadása", + moveUp: "Mozgatás felfelé", moveDown: "Mozgatás lefelé", remove: "Eltávolítás", removeOption: "Opció eltávolítása", + labelPh: "Kérdés címkéje…", headingPh: "Szakasz címe…", + stars: "Csillagok", min: "Min", max: "Max", step: "Lépés", + columns: "Oszlopok", rows: "Sorok", rowPh: "Sor", addRow: "Sor hozzáadása", removeRow: "Sor eltávolítása", + cond: { + title: "Logika", + addRule: "Szabály hozzáadása", + removeRule: "Szabály eltávolítása", + noFields: "Előbb adj hozzá más mezőket a logikához.", + valuePh: "Érték", + whenField: "mező", + actShow: "Megjelenítés, ha", actHide: "Elrejtés, ha", actRequire: "Kötelező, ha", + opEquals: "egyenlő", opNotEquals: "nem egyenlő", opContains: "tartalmazza", + opGreater: "nagyobb mint", opLess: "kisebb mint", + opEmpty: "üres", opNotEmpty: "nem üres", opInList: "listában (vesszővel elválasztva)", + }, + ft: { + short_text: "Rövid szöveg", long_text: "Hosszú szöveg", email: "E-mail", number: "Szám", phone: "Telefon", url: "URL", + single_choice: "Egy választás", dropdown: "Legördülő", multi_choice: "Több választás", yes_no: "Igen / Nem", + rating_stars: "Csillagos értékelés", nps: "NPS (0–10)", slider: "Csúszka", emoji_scale: "Emoji skála", matrix: "Mátrix", + date: "Dátum", file_upload: "Fájlfeltöltés", image_upload: "Képfeltöltés", signature: "Aláírás", + consent: "Hozzájárulás", heading: "Címsor", paragraph: "Bekezdés", divider: "Elválasztó", + }, + }, + share: { + share: "Megosztás", + link: "Nyilvános link", + copy: "Másolás", copied: "Másolva!", + qrCode: "QR-kód", + embedCode: "Beágyazási kód", + }, + authError: "A bejelentkezés sikertelen. Kérjük, próbáld újra.", +}; + +export const dictionaries: Record = { en, de, hu }; diff --git a/apps/web/src/i18n/index.ts b/apps/web/src/i18n/index.ts index 8398351..bbe0112 100644 --- a/apps/web/src/i18n/index.ts +++ b/apps/web/src/i18n/index.ts @@ -4,14 +4,14 @@ import { dictionaries, type Dictionary, type Locale } from "./dictionaries"; export type { Dictionary, Locale } from "./dictionaries"; -export const locales: Locale[] = ["en", "de"]; +export const locales: Locale[] = ["en", "de", "hu"]; export const defaultLocale: Locale = "en"; export const LOCALE_COOKIE = "NEXT_LOCALE"; /** Current locale from the NEXT_LOCALE cookie (default en). */ export async function getLocale(): Promise { const value = (await cookies()).get(LOCALE_COOKIE)?.value; - return value === "de" || value === "en" ? value : defaultLocale; + return locales.includes(value as Locale) ? (value as Locale) : defaultLocale; } /** Dictionary for the current locale. */