-
Projects
+
{t("pageTitle")}
{
disabled={isImporting}
>
- {isImporting ? "Importing..." : "Import..."}
+ {isImporting ? t("importing") : t("importBtn")}
setIsCreating(true)}
>
- Create
+ {t("createBtn")}
diff --git a/messages/de.json b/messages/de.json
new file mode 100644
index 0000000..fa0eecf
--- /dev/null
+++ b/messages/de.json
@@ -0,0 +1,266 @@
+{
+ "navbar": {
+ "screenplay": "Drehbuch",
+ "board": "Board",
+ "titlePage": "Titelseite",
+ "synced": "Mit der Cloud synchronisiert",
+ "noConnection": "Keine Verbindung",
+ "reconnecting": "Verbindung wird hergestellt..."
+ },
+ "common": {
+ "save": "Änderungen speichern",
+ "cancel": "Abbrechen",
+ "loading": "Lädt..."
+ },
+ "sidebar": {
+ "title": "Dashboard",
+ "logOut": "Abmelden",
+ "logIn": "Anmelden"
+ },
+ "appearance": {
+ "theme": "Theme",
+ "themeHelp": {
+ "dark": "Augenschonendes Dunkeltheme für Nachtschwärmer und späte Arbeitsstunden.",
+ "light": "Klares, luftiges Theme – tagsüber natürlich und angenehm.",
+ "latte": "Weiches, cremefarbenes Theme, das Wärme und Lesbarkeit verbindet.",
+ "wonka": "Samtiges, kakaobasiertes Theme mit tiefem Luxusgefühl und augenschonender Wirkung.",
+ "mint": "Erfrischendes Minztheme, das botanische Ruhe mit visueller Balance verbindet.",
+ "blossom": "Zartes Blütentheme, das florale Wärme mit sanfter Optik vereint."
+ },
+ "editor": "Editor",
+ "themedEditor": "Thematisierter Editor",
+ "themedEditorDesc": "Theme-Farben für Editorhintergrund und Text verwenden"
+ },
+ "keybinds": {
+ "screenplayElements": "Drehbuchelemente",
+ "typing": "Eingabe… (Esc zum Abbrechen)",
+ "notSet": "Nicht belegt",
+ "modifiersOnly": "Nur Modifikatoren — bitte auch eine normale Taste drücken",
+ "defaultPrefix": "Standard: {combo}",
+ "reset": "Zurücksetzen",
+ "resetTitle": "Benutzerbelegung löschen",
+ "resetDefaults": "Standardwerte wiederherstellen",
+ "save": "Änderungen speichern"
+ },
+ "language": {
+ "label": "Anzeigesprache",
+ "helpText": "Wähle die Sprache, die in der gesamten App verwendet wird."
+ },
+ "modal": {
+ "groups": {
+ "project": "Projekt",
+ "preferences": "Einstellungen",
+ "account": "Konto"
+ },
+ "tabs": {
+ "General": "Allgemein",
+ "Layout": "Layout",
+ "Export": "Import/Export",
+ "Collaborators": "Mitarbeiter",
+ "Keybinds": "Tastenkürzel",
+ "Appearance": "Erscheinungsbild",
+ "Language": "Sprache",
+ "Profile": "Profil",
+ "Security": "Sicherheit",
+ "Settings": "Einstellungen",
+ "Login": "Anmelden",
+ "About": "Über"
+ }
+ },
+ "dangerZone": {
+ "transferOwnership": "Eigentümerschaft übertragen",
+ "transferDesc": "Übertrage deine Eigentümerrolle an einen anderen Nutzer. Du erhältst anschließend die Editorrolle.",
+ "transferBtn": "Übertragen",
+ "transferPrompt": "Gib die E-Mail-Adresse des neuen Eigentümers ein:",
+ "deleteProject": "Projekt löschen",
+ "deleteProjectDesc": "Ein gelöschtes Projekt kann nicht wiederhergestellt werden. Bitte sei dir sicher.",
+ "deleteBtn": "Löschen",
+ "modalTitle": "Projekt löschen",
+ "modalDesc": "Diese Aktion ist dauerhaft und kann nicht rückgängig gemacht werden. Alle mit diesem Projekt verbundenen Daten gehen verloren.",
+ "deleting": "Wird gelöscht...",
+ "confirmDeleteBtn": "Projekt löschen"
+ },
+ "profile": {
+ "email": "E-Mail",
+ "username": "Benutzername",
+ "usernamePlaceholder": "Anzeigenamen eingeben...",
+ "usernameHelp": "Dieser Name ist für Mitarbeiter sichtbar, wenn du an gemeinsamen Projekten arbeitest.",
+ "color": "Farbe",
+ "customColor": "Benutzerdefinierte Farbe",
+ "selectColor": "Farbe auswählen {color}",
+ "successMessage": "Profil erfolgreich aktualisiert",
+ "failedUpdate": "Profil konnte nicht aktualisiert werden",
+ "errorSaving": "Beim Speichern ist ein Fehler aufgetreten",
+ "saving": "Wird gespeichert...",
+ "dangerZoneTitle": "Gefahrenbereich",
+ "deleteAccount": "Konto löschen",
+ "deleteAccountDesc": "Lösche dauerhaft dein Konto und alle zugehörigen Daten. Diese Aktion ist unwiderruflich.",
+ "deleteBtn": "Löschen",
+ "deleteModalTitle": "Konto löschen",
+ "deleteModalDesc": "Dein Konto und alle zugehörigen Daten werden dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.",
+ "deleteConfirmPhrase": "Ich bestätige die Löschung meines Kontos",
+ "deleteConfirmLabel": "Gib
ein, um zu bestätigen.",
+ "deleting": "Wird gelöscht...",
+ "deleteAccountBtn": "Mein Konto löschen"
+ },
+ "projects": {
+ "untitled": "Ohne Titel",
+ "newProject": "Neues Projekt",
+ "pageTitle": "Projekte",
+ "importBtn": "Importieren...",
+ "importing": "Wird importiert...",
+ "createBtn": "Erstellen",
+ "empty": {
+ "createFirst": "Klicke, um dein erstes Projekt zu erstellen",
+ "or": "oder",
+ "importExisting": "Vorhandenes Skript importieren"
+ },
+ "item": {
+ "posterAlt": "Filmplakat",
+ "localOnly": "Nur lokal",
+ "syncedToCloud": "Mit Cloud synchronisiert",
+ "today": "Heute",
+ "yesterday": "Gestern",
+ "daysAgo": "Vor {days, plural, one {# Tag} other {# Tagen}}",
+ "monthsAgo": "Vor {months, plural, one {# Monat} other {# Monaten}}",
+ "moreThanYearAgo": "Vor mehr als einem Jahr"
+ },
+ "form": {
+ "formTitle": "Projekt erstellen",
+ "titleField": "Titel",
+ "descriptionField": "Beschreibung",
+ "authorField": "Autor",
+ "posterField": "Plakat",
+ "optional": "optional",
+ "submitBtn": "Erstellen",
+ "failedToCreate": "Projekt konnte nicht erstellt werden"
+ }
+ },
+ "editorSidebar": {
+ "scenes": "Szenen",
+ "characters": "Charaktere",
+ "locations": "Schauplätze"
+ },
+ "formatDropdown": {
+ "elements": {
+ "scene": "SZENENÜBERSCHRIFT",
+ "action": "Handlung",
+ "character": "FIGUR",
+ "dialogue": "Dialog",
+ "parenthetical": "(Klammertext)",
+ "transition": "ÜBERGANG:",
+ "section": "Abschnitt",
+ "note": "[[Notiz]]",
+ "none": "Keiner"
+ },
+ "titlePageElements": {
+ "title": "Titel",
+ "author": "Autor",
+ "date": "Datum",
+ "none": "Keiner"
+ }
+ },
+ "search": {
+ "placeholder": "Suchen...",
+ "noMatches": "Keine Treffer",
+ "matchCount": "{current} von {total}",
+ "replacePlaceholder": "Ersetzen durch...",
+ "replace": "Ersetzen",
+ "replaceAll": "Alle ersetzen",
+ "filterByElement": "Nach Element filtern:",
+ "elements": {
+ "scene": "Szenenüberschrift",
+ "action": "Handlung",
+ "character": "Figur",
+ "dialogue": "Dialog",
+ "parenthetical": "Klammertext",
+ "transition": "Übergang",
+ "section": "Abschnitt",
+ "note": "Notiz",
+ "none": "Keiner"
+ }
+ },
+ "collaborators": {
+ "projectTeam": "Projektteam ({count}/{max})",
+ "teamHelp": "Verwalte Teammitglieder und ausstehende Einladungen. Du kannst jeden Nicht-Pro-Nutzer einladen, an deinem Projekt teilzunehmen. Das Projekt bleibt kollaborativ, solange der Eigentümer einen Pro-Plan hat.",
+ "roles": {
+ "owner": "Eigentümer",
+ "admin": "Admin",
+ "editor": "Editor",
+ "viewer": "Betrachter"
+ },
+ "roleDesc": {
+ "owner": "Kann das Projekt löschen und die Eigentümerschaft übertragen",
+ "admin": "Kann Mitarbeiter einladen, befördern, herabstufen und entfernen",
+ "editor": "Kann das Drehbuch und andere Projektinhalte bearbeiten",
+ "viewer": "Nur-Lese-Zugriff. Kann keine Änderungen vornehmen"
+ },
+ "you": "(du)",
+ "leave": "Verlassen",
+ "kick": "Entfernen",
+ "pending": "Ausstehend",
+ "cancel": "Abbrechen",
+ "emailPlaceholder": "E-Mail eingeben...",
+ "invite": "Einladen"
+ },
+ "export": {
+ "importLabel": "Importieren",
+ "selectFile": "Datei auswählen",
+ "selectFileDesc": ".fountain, .fdx, .scriptio oder .txt hochladen",
+ "exportLabel": "Exportieren",
+ "formatOptions": {
+ "pdf": "PDF-Dokument (.pdf)",
+ "fountain": "Fountain (.fountain)",
+ "fdx": "Final Draft (.fdx)",
+ "scriptio": "Scriptio (.scriptio)"
+ },
+ "formatHelp": {
+ "pdf": "Standard-Industrieformat. Ideal zum Teilen und Drucken.",
+ "fountain": "Klartextformat auf Markdown-Basis, ideal für Kompatibilität.",
+ "fdx": "Kompatibel mit der Branchensoftware Final Draft.",
+ "scriptio": "Eigenes Scriptio-Format, um das Projekt lokal zu speichern"
+ },
+ "includeNotes": "Notizen einschließen",
+ "includeNotesDesc": "Inline-Notizen exportieren.",
+ "watermark": "Wasserzeichen",
+ "watermarkDesc": "Den Namen des Autors auf den Seiten einblenden.",
+ "passwordProtection": "Kennwortschutz",
+ "passwordProtectionDesc": "Ein Kennwort zum Öffnen der PDF-Datei erforderlich.",
+ "passwordPlaceholder": "Kennwort eingeben",
+ "exportBtn": "Exportieren",
+ "exporting": "Wird exportiert...",
+ "exportingProgress": "Wird exportiert ({progress}%)"
+ },
+ "layout": {
+ "pageFormat": "Seitenformat",
+ "pageFormatHelp": {
+ "letter": "Standardformat in den USA. Branchenstandard für Hollywood-Drehbücher.",
+ "a4": "Internationales Standardformat. In Europa und den meisten anderen Ländern üblich."
+ },
+ "sceneHeadings": "Szenenüberschriften",
+ "bold": "Fett",
+ "boldDesc": "Szenenüberschriften werden fett dargestellt",
+ "extraSpace": "Zusätzlicher Abstand oben",
+ "extraSpaceDesc": "Zusätzlichen Abstand vor Szenenüberschriften einfügen",
+ "sceneNumbering": "Szenennummerierung",
+ "sceneNumberingDesc": "Szenennummern im linken Rand anzeigen",
+ "duplicateRight": "Im rechten Rand duplizieren",
+ "duplicateRightDesc": "Nummer auf beiden Seiten anzeigen",
+ "continuation": "Fortsetzung",
+ "moreTitle": "Dialogfortsetzungskürzel anwenden (unten auf der Seite)",
+ "contdTitle": "Figurfortsetzungskürzel anwenden (oben auf der Seite)"
+ },
+ "projectSettings": {
+ "titleLabel": "Titel",
+ "titlePlaceholder": "Projektname eingeben...",
+ "authorLabel": "Autor",
+ "authorPlaceholder": "Autorenname...",
+ "descriptionLabel": "Beschreibung",
+ "descriptionPlaceholder": "Wovon handelt dieses Drehbuch?",
+ "posterLabel": "Plakat",
+ "noPoster": "Kein Plakat",
+ "posterHelp": "Empfohlen: 600x900 Pixel (Verhältnis 2:3). Unterstützt PNG und JPG.",
+ "saveChanges": "Änderungen speichern",
+ "dangerZoneTitle": "Gefahrenbereich"
+ }
+}
diff --git a/messages/en.json b/messages/en.json
new file mode 100644
index 0000000..d9846ef
--- /dev/null
+++ b/messages/en.json
@@ -0,0 +1,266 @@
+{
+ "navbar": {
+ "screenplay": "Screenplay",
+ "board": "Board",
+ "titlePage": "Title Page",
+ "synced": "Synced to cloud",
+ "noConnection": "No connection",
+ "reconnecting": "Reconnecting..."
+ },
+ "common": {
+ "save": "Save Changes",
+ "cancel": "Cancel",
+ "loading": "Loading..."
+ },
+ "sidebar": {
+ "title": "Dashboard",
+ "logOut": "Log Out",
+ "logIn": "Log In"
+ },
+ "appearance": {
+ "theme": "Theme",
+ "themeHelp": {
+ "dark": "Cozy, low-glare theme made for night owls and late-hour focus.",
+ "light": "Crisp, airy theme that feels natural and comfortable during the day.",
+ "latte": "Soft, cream-based theme that blends warmth with readability.",
+ "wonka": "Velvety, cocoa-based theme that blends deep luxury with eye-resting focus.",
+ "mint": "Refreshing, mint-infused theme that blends botanical serenity with eye-resting balance.",
+ "blossom": "Gentle, petal-infused theme that blends floral warmth with eye-resting softness."
+ },
+ "editor": "Editor",
+ "themedEditor": "Themed editor",
+ "themedEditorDesc": "Use theme colors for the editor background and text"
+ },
+ "keybinds": {
+ "screenplayElements": "Screenplay Elements",
+ "typing": "Type… (Esc to cancel)",
+ "notSet": "Not set",
+ "modifiersOnly": "Modifiers only — press a regular key",
+ "defaultPrefix": "Default: {combo}",
+ "reset": "Reset",
+ "resetTitle": "Clear user binding",
+ "resetDefaults": "Reset to defaults",
+ "save": "Save changes"
+ },
+ "language": {
+ "label": "Display Language",
+ "helpText": "Choose the language used throughout the app."
+ },
+ "modal": {
+ "groups": {
+ "project": "Project",
+ "preferences": "Preferences",
+ "account": "Account"
+ },
+ "tabs": {
+ "General": "General",
+ "Layout": "Layout",
+ "Export": "Import/Export",
+ "Collaborators": "Collaborators",
+ "Keybinds": "Keybinds",
+ "Appearance": "Appearance",
+ "Language": "Language",
+ "Profile": "Profile",
+ "Security": "Security",
+ "Settings": "Settings",
+ "Login": "Login",
+ "About": "About"
+ }
+ },
+ "dangerZone": {
+ "transferOwnership": "Transfer ownership",
+ "transferDesc": "Transfer your owner role to another user. You will be given editor role.",
+ "transferBtn": "Transfer",
+ "transferPrompt": "Enter the email of the new owner:",
+ "deleteProject": "Delete project",
+ "deleteProjectDesc": "Once you delete a project, there is no going back. Please be certain.",
+ "deleteBtn": "Delete",
+ "modalTitle": "Delete project",
+ "modalDesc": "This action is permanent and cannot be undone. All data associated with this project will be lost.",
+ "deleting": "Deleting...",
+ "confirmDeleteBtn": "Delete project"
+ },
+ "profile": {
+ "email": "Email",
+ "username": "Username",
+ "usernamePlaceholder": "Enter your display name...",
+ "usernameHelp": "This name will be visible to collaborators when you work on shared projects.",
+ "color": "Color",
+ "customColor": "Custom color",
+ "selectColor": "Select color {color}",
+ "successMessage": "Profile updated successfully",
+ "failedUpdate": "Failed to update profile",
+ "errorSaving": "An error occurred while saving",
+ "saving": "Saving...",
+ "dangerZoneTitle": "Danger zone",
+ "deleteAccount": "Delete account",
+ "deleteAccountDesc": "Permanently delete your account and all associated data. This cannot be undone.",
+ "deleteBtn": "Delete",
+ "deleteModalTitle": "Delete account",
+ "deleteModalDesc": "This will permanently delete your account and all associated data. This action cannot be undone.",
+ "deleteConfirmPhrase": "I confirm my account deletion",
+ "deleteConfirmLabel": "Type
to confirm.",
+ "deleting": "Deleting...",
+ "deleteAccountBtn": "Delete my account"
+ },
+ "projects": {
+ "untitled": "Untitled",
+ "newProject": "New Project",
+ "pageTitle": "Projects",
+ "importBtn": "Import...",
+ "importing": "Importing...",
+ "createBtn": "Create",
+ "empty": {
+ "createFirst": "Click to create your first project",
+ "or": "or",
+ "importExisting": "Import an existing script"
+ },
+ "item": {
+ "posterAlt": "Movie poster",
+ "localOnly": "Local only",
+ "syncedToCloud": "Synced to cloud",
+ "today": "Today",
+ "yesterday": "Yesterday",
+ "daysAgo": "{days, plural, one {# day ago} other {# days ago}}",
+ "monthsAgo": "{months, plural, one {# month ago} other {# months ago}}",
+ "moreThanYearAgo": "More than 1 year ago"
+ },
+ "form": {
+ "formTitle": "Create project",
+ "titleField": "Title",
+ "descriptionField": "Description",
+ "authorField": "Author",
+ "posterField": "Poster",
+ "optional": "optional",
+ "submitBtn": "Create",
+ "failedToCreate": "Failed to create project"
+ }
+ },
+ "editorSidebar": {
+ "scenes": "Scenes",
+ "characters": "Characters",
+ "locations": "Locations"
+ },
+ "formatDropdown": {
+ "elements": {
+ "scene": "SCENE HEADING",
+ "action": "Action",
+ "character": "CHARACTER",
+ "dialogue": "Dialogue",
+ "parenthetical": "(Parenthetical)",
+ "transition": "TRANSITION:",
+ "section": "Section",
+ "note": "[[Note]]",
+ "none": "None"
+ },
+ "titlePageElements": {
+ "title": "Title",
+ "author": "Author",
+ "date": "Date",
+ "none": "None"
+ }
+ },
+ "search": {
+ "placeholder": "Search...",
+ "noMatches": "No matches",
+ "matchCount": "{current} of {total}",
+ "replacePlaceholder": "Replace with...",
+ "replace": "Replace",
+ "replaceAll": "Replace All",
+ "filterByElement": "Filter by element:",
+ "elements": {
+ "scene": "Scene Heading",
+ "action": "Action",
+ "character": "Character",
+ "dialogue": "Dialogue",
+ "parenthetical": "Parenthetical",
+ "transition": "Transition",
+ "section": "Section",
+ "note": "Note",
+ "none": "None"
+ }
+ },
+ "collaborators": {
+ "projectTeam": "Project Team ({count}/{max})",
+ "teamHelp": "Manage your team members and pending invitations. You can invite any non-Pro user to be part of your project. The project remains collaborative until owner has Pro plan.",
+ "roles": {
+ "owner": "Owner",
+ "admin": "Admin",
+ "editor": "Editor",
+ "viewer": "Viewer"
+ },
+ "roleDesc": {
+ "owner": "Can delete the project and transfer ownership",
+ "admin": "Can invite, promote, demote, and kick collaborators",
+ "editor": "Can modify screenplay and other project content",
+ "viewer": "Read-only access. Cannot make any changes"
+ },
+ "you": "(you)",
+ "leave": "Leave",
+ "kick": "Kick",
+ "pending": "Pending",
+ "cancel": "Cancel",
+ "emailPlaceholder": "Enter email...",
+ "invite": "Invite"
+ },
+ "export": {
+ "importLabel": "Import",
+ "selectFile": "Select File",
+ "selectFileDesc": "Upload .fountain, .fdx, .scriptio, or .txt",
+ "exportLabel": "Export",
+ "formatOptions": {
+ "pdf": "PDF Document (.pdf)",
+ "fountain": "Fountain (.fountain)",
+ "fdx": "Final Draft (.fdx)",
+ "scriptio": "Scriptio (.scriptio)"
+ },
+ "formatHelp": {
+ "pdf": "Standard industry format. Best for sharing and printing.",
+ "fountain": "Plain text format based on markdown, great for compatibility.",
+ "fdx": "Compatible with Final Draft industry software.",
+ "scriptio": "Scriptio own format, to keep your project local"
+ },
+ "includeNotes": "Include Notes",
+ "includeNotesDesc": "Export inline notes.",
+ "watermark": "Watermark",
+ "watermarkDesc": "Overlay the author's name on pages.",
+ "passwordProtection": "Password Protection",
+ "passwordProtectionDesc": "Require a password to open the PDF.",
+ "passwordPlaceholder": "Enter password",
+ "exportBtn": "Export",
+ "exporting": "Exporting...",
+ "exportingProgress": "Exporting ({progress}%)"
+ },
+ "layout": {
+ "pageFormat": "Page Format",
+ "pageFormatHelp": {
+ "letter": "Standard format used in the United States. Industry standard for Hollywood screenplays.",
+ "a4": "International standard format. Common in Europe and most other countries."
+ },
+ "sceneHeadings": "Scene Headings",
+ "bold": "Bold",
+ "boldDesc": "Scene headings will appear in bold",
+ "extraSpace": "Extra space above",
+ "extraSpaceDesc": "Add extra spacing before scene headings",
+ "sceneNumbering": "Scene numbering",
+ "sceneNumberingDesc": "Show scene numbers in left margin",
+ "duplicateRight": "Duplicate in right margin",
+ "duplicateRightDesc": "Show number on both sides",
+ "continuation": "Continuation",
+ "moreTitle": "Apply dialogue continuation label (bottom of page)",
+ "contdTitle": "Apply character continuation label (top of page)"
+ },
+ "projectSettings": {
+ "titleLabel": "Title",
+ "titlePlaceholder": "Enter project name...",
+ "authorLabel": "Author",
+ "authorPlaceholder": "Author name...",
+ "descriptionLabel": "Description",
+ "descriptionPlaceholder": "What is this screenplay about?",
+ "posterLabel": "Poster",
+ "noPoster": "No Poster",
+ "posterHelp": "Recommended: 600x900 pixels (2:3 ratio). Supports PNG, JPG.",
+ "saveChanges": "Save changes",
+ "dangerZoneTitle": "Danger zone"
+ }
+}
diff --git a/messages/es.json b/messages/es.json
new file mode 100644
index 0000000..4e6418a
--- /dev/null
+++ b/messages/es.json
@@ -0,0 +1,266 @@
+{
+ "navbar": {
+ "screenplay": "Guión",
+ "board": "Tablero",
+ "titlePage": "Portada",
+ "synced": "Sincronizado en la nube",
+ "noConnection": "Sin conexión",
+ "reconnecting": "Reconectando..."
+ },
+ "common": {
+ "save": "Guardar cambios",
+ "cancel": "Cancelar",
+ "loading": "Cargando..."
+ },
+ "sidebar": {
+ "title": "Panel",
+ "logOut": "Cerrar sesión",
+ "logIn": "Iniciar sesión"
+ },
+ "appearance": {
+ "theme": "Tema",
+ "themeHelp": {
+ "dark": "Tema acogedor y de bajo resplandor, perfecto para noctámbulos y noches de concentración.",
+ "light": "Tema nítido y aireado que se siente natural y cómodo durante el día.",
+ "latte": "Tema suave con base crema que combina calidez y legibilidad.",
+ "wonka": "Tema aterciopelado con base de cacao que combina lujo profundo y descanso visual.",
+ "mint": "Tema refrescante con esencia de menta que combina serenidad botánica y equilibrio visual.",
+ "blossom": "Tema delicado con esencia floral que combina calidez de pétalos y suavidad visual."
+ },
+ "editor": "Editor",
+ "themedEditor": "Editor con tema",
+ "themedEditorDesc": "Usar los colores del tema para el fondo y el texto del editor"
+ },
+ "keybinds": {
+ "screenplayElements": "Elementos de guión",
+ "typing": "Escribir… (Esc para cancelar)",
+ "notSet": "No asignado",
+ "modifiersOnly": "Solo modificadores — presiona una tecla normal",
+ "defaultPrefix": "Por defecto: {combo}",
+ "reset": "Restablecer",
+ "resetTitle": "Borrar asignación del usuario",
+ "resetDefaults": "Restablecer predeterminados",
+ "save": "Guardar cambios"
+ },
+ "language": {
+ "label": "Idioma de la interfaz",
+ "helpText": "Elige el idioma que se usa en toda la aplicación."
+ },
+ "modal": {
+ "groups": {
+ "project": "Proyecto",
+ "preferences": "Preferencias",
+ "account": "Cuenta"
+ },
+ "tabs": {
+ "General": "General",
+ "Layout": "Diseño",
+ "Export": "Importar/Exportar",
+ "Collaborators": "Colaboradores",
+ "Keybinds": "Atajos de teclado",
+ "Appearance": "Apariencia",
+ "Language": "Idioma",
+ "Profile": "Perfil",
+ "Security": "Seguridad",
+ "Settings": "Ajustes",
+ "Login": "Iniciar sesión",
+ "About": "Acerca de"
+ }
+ },
+ "dangerZone": {
+ "transferOwnership": "Transferir propiedad",
+ "transferDesc": "Transfiere tu rol de propietario a otro usuario. Pasarás a tener el rol de editor.",
+ "transferBtn": "Transferir",
+ "transferPrompt": "Introduce el correo del nuevo propietario:",
+ "deleteProject": "Eliminar proyecto",
+ "deleteProjectDesc": "Una vez eliminado el proyecto, no hay vuelta atrás. Por favor, asegúrate.",
+ "deleteBtn": "Eliminar",
+ "modalTitle": "Eliminar proyecto",
+ "modalDesc": "Esta acción es permanente y no se puede deshacer. Se perderán todos los datos asociados a este proyecto.",
+ "deleting": "Eliminando...",
+ "confirmDeleteBtn": "Eliminar proyecto"
+ },
+ "profile": {
+ "email": "Correo electrónico",
+ "username": "Nombre de usuario",
+ "usernamePlaceholder": "Introduce tu nombre para mostrar...",
+ "usernameHelp": "Este nombre será visible para los colaboradores cuando trabajes en proyectos compartidos.",
+ "color": "Color",
+ "customColor": "Color personalizado",
+ "selectColor": "Seleccionar color {color}",
+ "successMessage": "Perfil actualizado correctamente",
+ "failedUpdate": "Error al actualizar el perfil",
+ "errorSaving": "Ocurrió un error al guardar",
+ "saving": "Guardando...",
+ "dangerZoneTitle": "Zona de peligro",
+ "deleteAccount": "Eliminar cuenta",
+ "deleteAccountDesc": "Elimina permanentemente tu cuenta y todos los datos asociados. Esto no se puede deshacer.",
+ "deleteBtn": "Eliminar",
+ "deleteModalTitle": "Eliminar cuenta",
+ "deleteModalDesc": "Esto eliminará permanentemente tu cuenta y todos los datos asociados. Esta acción no se puede deshacer.",
+ "deleteConfirmPhrase": "Confirmo la eliminación de mi cuenta",
+ "deleteConfirmLabel": "Escribe
para confirmar.",
+ "deleting": "Eliminando...",
+ "deleteAccountBtn": "Eliminar mi cuenta"
+ },
+ "projects": {
+ "untitled": "Sin título",
+ "newProject": "Nuevo proyecto",
+ "pageTitle": "Proyectos",
+ "importBtn": "Importar...",
+ "importing": "Importando...",
+ "createBtn": "Crear",
+ "empty": {
+ "createFirst": "Haz clic para crear tu primer proyecto",
+ "or": "o",
+ "importExisting": "Importar un guión existente"
+ },
+ "item": {
+ "posterAlt": "Póster de la película",
+ "localOnly": "Solo local",
+ "syncedToCloud": "Sincronizado en la nube",
+ "today": "Hoy",
+ "yesterday": "Ayer",
+ "daysAgo": "Hace {days, plural, one {# día} other {# días}}",
+ "monthsAgo": "Hace {months, plural, one {# mes} other {# meses}}",
+ "moreThanYearAgo": "Hace más de 1 año"
+ },
+ "form": {
+ "formTitle": "Crear proyecto",
+ "titleField": "Título",
+ "descriptionField": "Descripción",
+ "authorField": "Autor",
+ "posterField": "Póster",
+ "optional": "opcional",
+ "submitBtn": "Crear",
+ "failedToCreate": "Error al crear el proyecto"
+ }
+ },
+ "editorSidebar": {
+ "scenes": "Escenas",
+ "characters": "Personajes",
+ "locations": "Localizaciones"
+ },
+ "formatDropdown": {
+ "elements": {
+ "scene": "ENCABEZADO DE ESCENA",
+ "action": "Acción",
+ "character": "PERSONAJE",
+ "dialogue": "Diálogo",
+ "parenthetical": "(Paréntesis)",
+ "transition": "TRANSICIÓN:",
+ "section": "Sección",
+ "note": "[[Nota]]",
+ "none": "Ninguno"
+ },
+ "titlePageElements": {
+ "title": "Título",
+ "author": "Autor",
+ "date": "Fecha",
+ "none": "Ninguno"
+ }
+ },
+ "search": {
+ "placeholder": "Buscar...",
+ "noMatches": "Sin resultados",
+ "matchCount": "{current} de {total}",
+ "replacePlaceholder": "Reemplazar con...",
+ "replace": "Reemplazar",
+ "replaceAll": "Reemplazar todo",
+ "filterByElement": "Filtrar por elemento:",
+ "elements": {
+ "scene": "Encabezado de escena",
+ "action": "Acción",
+ "character": "Personaje",
+ "dialogue": "Diálogo",
+ "parenthetical": "Paréntesis",
+ "transition": "Transición",
+ "section": "Sección",
+ "note": "Nota",
+ "none": "Ninguno"
+ }
+ },
+ "collaborators": {
+ "projectTeam": "Equipo del proyecto ({count}/{max})",
+ "teamHelp": "Gestiona los miembros de tu equipo y las invitaciones pendientes. Puedes invitar a cualquier usuario no Pro a participar en tu proyecto. El proyecto permanece colaborativo mientras el propietario tenga el plan Pro.",
+ "roles": {
+ "owner": "Propietario",
+ "admin": "Administrador",
+ "editor": "Editor",
+ "viewer": "Espectador"
+ },
+ "roleDesc": {
+ "owner": "Puede eliminar el proyecto y transferir la propiedad",
+ "admin": "Puede invitar, ascender, degradar y expulsar colaboradores",
+ "editor": "Puede modificar el guión y otro contenido del proyecto",
+ "viewer": "Acceso de solo lectura. No puede realizar cambios"
+ },
+ "you": "(tú)",
+ "leave": "Salir",
+ "kick": "Expulsar",
+ "pending": "Pendiente",
+ "cancel": "Cancelar",
+ "emailPlaceholder": "Introduce el correo...",
+ "invite": "Invitar"
+ },
+ "export": {
+ "importLabel": "Importar",
+ "selectFile": "Seleccionar archivo",
+ "selectFileDesc": "Sube .fountain, .fdx, .scriptio o .txt",
+ "exportLabel": "Exportar",
+ "formatOptions": {
+ "pdf": "Documento PDF (.pdf)",
+ "fountain": "Fountain (.fountain)",
+ "fdx": "Final Draft (.fdx)",
+ "scriptio": "Scriptio (.scriptio)"
+ },
+ "formatHelp": {
+ "pdf": "Formato estándar de la industria. Ideal para compartir e imprimir.",
+ "fountain": "Formato de texto plano basado en Markdown, ideal para compatibilidad.",
+ "fdx": "Compatible con el software de la industria Final Draft.",
+ "scriptio": "Formato propio de Scriptio, para mantener el proyecto en local"
+ },
+ "includeNotes": "Incluir notas",
+ "includeNotesDesc": "Exportar notas en línea.",
+ "watermark": "Marca de agua",
+ "watermarkDesc": "Superponer el nombre del autor en las páginas.",
+ "passwordProtection": "Protección con contraseña",
+ "passwordProtectionDesc": "Requerir una contraseña para abrir el PDF.",
+ "passwordPlaceholder": "Introduce la contraseña",
+ "exportBtn": "Exportar",
+ "exporting": "Exportando...",
+ "exportingProgress": "Exportando ({progress}%)"
+ },
+ "layout": {
+ "pageFormat": "Formato de página",
+ "pageFormatHelp": {
+ "letter": "Formato estándar de Estados Unidos. Estándar de la industria para guiones de Hollywood.",
+ "a4": "Formato estándar internacional. Común en Europa y la mayoría de los países."
+ },
+ "sceneHeadings": "Encabezados de escena",
+ "bold": "Negrita",
+ "boldDesc": "Los encabezados de escena aparecerán en negrita",
+ "extraSpace": "Espacio extra arriba",
+ "extraSpaceDesc": "Añadir espacio extra antes de los encabezados de escena",
+ "sceneNumbering": "Numeración de escenas",
+ "sceneNumberingDesc": "Mostrar números de escena en el margen izquierdo",
+ "duplicateRight": "Duplicar en margen derecho",
+ "duplicateRightDesc": "Mostrar número en ambos lados",
+ "continuation": "Continuación",
+ "moreTitle": "Aplicar etiqueta de continuación de diálogo (pie de página)",
+ "contdTitle": "Aplicar etiqueta de continuación de personaje (inicio de página)"
+ },
+ "projectSettings": {
+ "titleLabel": "Título",
+ "titlePlaceholder": "Introduce el nombre del proyecto...",
+ "authorLabel": "Autor",
+ "authorPlaceholder": "Nombre del autor...",
+ "descriptionLabel": "Descripción",
+ "descriptionPlaceholder": "¿De qué trata este guión?",
+ "posterLabel": "Póster",
+ "noPoster": "Sin póster",
+ "posterHelp": "Recomendado: 600x900 píxeles (proporción 2:3). Compatible con PNG y JPG.",
+ "saveChanges": "Guardar cambios",
+ "dangerZoneTitle": "Zona de peligro"
+ }
+}
diff --git a/messages/fr.json b/messages/fr.json
new file mode 100644
index 0000000..94b0c83
--- /dev/null
+++ b/messages/fr.json
@@ -0,0 +1,266 @@
+{
+ "navbar": {
+ "screenplay": "Scénario",
+ "board": "Tableau",
+ "titlePage": "Page de titre",
+ "synced": "Synchronisé dans le cloud",
+ "noConnection": "Pas de connexion",
+ "reconnecting": "Reconnexion..."
+ },
+ "common": {
+ "save": "Enregistrer",
+ "cancel": "Annuler",
+ "loading": "Chargement..."
+ },
+ "sidebar": {
+ "title": "Tableau de bord",
+ "logOut": "Se déconnecter",
+ "logIn": "Se connecter"
+ },
+ "appearance": {
+ "theme": "Thème",
+ "themeHelp": {
+ "dark": "Thème sombre et doux, idéal pour les noctambules et les sessions tardives.",
+ "light": "Thème clair et aéré, naturel et agréable en pleine journée.",
+ "latte": "Thème doux aux tons crème, alliant chaleur et lisibilité.",
+ "wonka": "Thème velouté aux tons cacao, entre luxe profond et confort visuel.",
+ "mint": "Thème frais aux notes de menthe, alliant sérénité botanique et équilibre visuel.",
+ "blossom": "Thème délicat aux notes florales, alliant douceur pétale et confort visuel."
+ },
+ "editor": "Éditeur",
+ "themedEditor": "Éditeur thématisé",
+ "themedEditorDesc": "Utiliser les couleurs du thème pour le fond et le texte de l'éditeur"
+ },
+ "keybinds": {
+ "screenplayElements": "Éléments de scénario",
+ "typing": "Saisir… (Échap pour annuler)",
+ "notSet": "Non défini",
+ "modifiersOnly": "Modificateurs uniquement — appuyez sur une touche normale",
+ "defaultPrefix": "Par défaut : {combo}",
+ "reset": "Réinitialiser",
+ "resetTitle": "Effacer le raccourci personnalisé",
+ "resetDefaults": "Rétablir les valeurs par défaut",
+ "save": "Enregistrer les modifications"
+ },
+ "language": {
+ "label": "Langue d'affichage",
+ "helpText": "Choisissez la langue utilisée dans toute l'application."
+ },
+ "modal": {
+ "groups": {
+ "project": "Projet",
+ "preferences": "Préférences",
+ "account": "Compte"
+ },
+ "tabs": {
+ "General": "Général",
+ "Layout": "Mise en page",
+ "Export": "Import/Export",
+ "Collaborators": "Collaborateurs",
+ "Keybinds": "Raccourcis",
+ "Appearance": "Apparence",
+ "Language": "Langue",
+ "Profile": "Profil",
+ "Security": "Sécurité",
+ "Settings": "Paramètres",
+ "Login": "Connexion",
+ "About": "À propos"
+ }
+ },
+ "dangerZone": {
+ "transferOwnership": "Transférer la propriété",
+ "transferDesc": "Transférez votre rôle de propriétaire à un autre utilisateur. Vous obtiendrez le rôle d'éditeur.",
+ "transferBtn": "Transférer",
+ "transferPrompt": "Saisissez l'adresse e-mail du nouveau propriétaire :",
+ "deleteProject": "Supprimer le projet",
+ "deleteProjectDesc": "La suppression d'un projet est irréversible. Veuillez être certain.",
+ "deleteBtn": "Supprimer",
+ "modalTitle": "Supprimer le projet",
+ "modalDesc": "Cette action est permanente et ne peut pas être annulée. Toutes les données associées à ce projet seront perdues.",
+ "deleting": "Suppression...",
+ "confirmDeleteBtn": "Supprimer le projet"
+ },
+ "profile": {
+ "email": "E-mail",
+ "username": "Nom d'utilisateur",
+ "usernamePlaceholder": "Entrez votre nom d'affichage...",
+ "usernameHelp": "Ce nom sera visible par vos collaborateurs lorsque vous travaillerez sur des projets partagés.",
+ "color": "Couleur",
+ "customColor": "Couleur personnalisée",
+ "selectColor": "Sélectionner la couleur {color}",
+ "successMessage": "Profil mis à jour avec succès",
+ "failedUpdate": "Échec de la mise à jour du profil",
+ "errorSaving": "Une erreur est survenue lors de l'enregistrement",
+ "saving": "Enregistrement...",
+ "dangerZoneTitle": "Zone dangereuse",
+ "deleteAccount": "Supprimer le compte",
+ "deleteAccountDesc": "Supprimez définitivement votre compte et toutes les données associées. Cette action est irréversible.",
+ "deleteBtn": "Supprimer",
+ "deleteModalTitle": "Supprimer le compte",
+ "deleteModalDesc": "Cela supprimera définitivement votre compte et toutes les données associées. Cette action ne peut pas être annulée.",
+ "deleteConfirmPhrase": "Je confirme la suppression de mon compte",
+ "deleteConfirmLabel": "Tapez
pour confirmer.",
+ "deleting": "Suppression...",
+ "deleteAccountBtn": "Supprimer mon compte"
+ },
+ "projects": {
+ "untitled": "Sans titre",
+ "newProject": "Nouveau projet",
+ "pageTitle": "Projets",
+ "importBtn": "Importer...",
+ "importing": "Importation...",
+ "createBtn": "Créer",
+ "empty": {
+ "createFirst": "Cliquez pour créer votre premier projet",
+ "or": "ou",
+ "importExisting": "Importer un scénario existant"
+ },
+ "item": {
+ "posterAlt": "Affiche du film",
+ "localOnly": "Local uniquement",
+ "syncedToCloud": "Synchronisé dans le cloud",
+ "today": "Aujourd'hui",
+ "yesterday": "Hier",
+ "daysAgo": "Il y a {days, plural, one {# jour} other {# jours}}",
+ "monthsAgo": "Il y a {months, plural, one {# mois} other {# mois}}",
+ "moreThanYearAgo": "Il y a plus d'un an"
+ },
+ "form": {
+ "formTitle": "Créer un projet",
+ "titleField": "Titre",
+ "descriptionField": "Description",
+ "authorField": "Auteur",
+ "posterField": "Affiche",
+ "optional": "facultatif",
+ "submitBtn": "Créer",
+ "failedToCreate": "Échec de la création du projet"
+ }
+ },
+ "editorSidebar": {
+ "scenes": "Scènes",
+ "characters": "Personnages",
+ "locations": "Lieux"
+ },
+ "formatDropdown": {
+ "elements": {
+ "scene": "EN-TÊTE DE SCÈNE",
+ "action": "Action",
+ "character": "PERSONNAGE",
+ "dialogue": "Dialogue",
+ "parenthetical": "(Parenthèse)",
+ "transition": "TRANSITION :",
+ "section": "Section",
+ "note": "[[Note]]",
+ "none": "Aucun"
+ },
+ "titlePageElements": {
+ "title": "Titre",
+ "author": "Auteur",
+ "date": "Date",
+ "none": "Aucun"
+ }
+ },
+ "search": {
+ "placeholder": "Rechercher...",
+ "noMatches": "Aucun résultat",
+ "matchCount": "{current} sur {total}",
+ "replacePlaceholder": "Remplacer par...",
+ "replace": "Remplacer",
+ "replaceAll": "Tout remplacer",
+ "filterByElement": "Filtrer par élément :",
+ "elements": {
+ "scene": "En-tête de scène",
+ "action": "Action",
+ "character": "Personnage",
+ "dialogue": "Dialogue",
+ "parenthetical": "Parenthèse",
+ "transition": "Transition",
+ "section": "Section",
+ "note": "Note",
+ "none": "Aucun"
+ }
+ },
+ "collaborators": {
+ "projectTeam": "Équipe du projet ({count}/{max})",
+ "teamHelp": "Gérez vos membres et les invitations en attente. Vous pouvez inviter tout utilisateur non Pro à rejoindre votre projet. Le projet reste collaboratif tant que le propriétaire a un plan Pro.",
+ "roles": {
+ "owner": "Propriétaire",
+ "admin": "Admin",
+ "editor": "Éditeur",
+ "viewer": "Lecteur"
+ },
+ "roleDesc": {
+ "owner": "Peut supprimer le projet et transférer la propriété",
+ "admin": "Peut inviter, promouvoir, rétrograder et exclure des collaborateurs",
+ "editor": "Peut modifier le scénario et le contenu du projet",
+ "viewer": "Accès en lecture seule. Ne peut effectuer aucune modification"
+ },
+ "you": "(vous)",
+ "leave": "Quitter",
+ "kick": "Exclure",
+ "pending": "En attente",
+ "cancel": "Annuler",
+ "emailPlaceholder": "Saisir un e-mail...",
+ "invite": "Inviter"
+ },
+ "export": {
+ "importLabel": "Importer",
+ "selectFile": "Sélectionner un fichier",
+ "selectFileDesc": "Importer .fountain, .fdx, .scriptio ou .txt",
+ "exportLabel": "Exporter",
+ "formatOptions": {
+ "pdf": "Document PDF (.pdf)",
+ "fountain": "Fountain (.fountain)",
+ "fdx": "Final Draft (.fdx)",
+ "scriptio": "Scriptio (.scriptio)"
+ },
+ "formatHelp": {
+ "pdf": "Format standard de l'industrie. Idéal pour le partage et l'impression.",
+ "fountain": "Format texte brut basé sur Markdown, idéal pour la compatibilité.",
+ "fdx": "Compatible avec le logiciel professionnel Final Draft.",
+ "scriptio": "Format natif de Scriptio, pour conserver votre projet en local"
+ },
+ "includeNotes": "Inclure les notes",
+ "includeNotesDesc": "Exporter les notes intégrées.",
+ "watermark": "Filigrane",
+ "watermarkDesc": "Superposer le nom de l'auteur sur les pages.",
+ "passwordProtection": "Protection par mot de passe",
+ "passwordProtectionDesc": "Exiger un mot de passe pour ouvrir le PDF.",
+ "passwordPlaceholder": "Entrer le mot de passe",
+ "exportBtn": "Exporter",
+ "exporting": "Exportation...",
+ "exportingProgress": "Exportation ({progress}%)"
+ },
+ "layout": {
+ "pageFormat": "Format de page",
+ "pageFormatHelp": {
+ "letter": "Format standard aux États-Unis. Standard de l'industrie pour les scénarios hollywoodiens.",
+ "a4": "Format international standard. Courant en Europe et dans la plupart des pays."
+ },
+ "sceneHeadings": "En-têtes de scène",
+ "bold": "Gras",
+ "boldDesc": "Les en-têtes de scène apparaîtront en gras",
+ "extraSpace": "Espace supplémentaire au-dessus",
+ "extraSpaceDesc": "Ajouter un espacement supplémentaire avant les en-têtes de scène",
+ "sceneNumbering": "Numérotation des scènes",
+ "sceneNumberingDesc": "Afficher les numéros de scène dans la marge gauche",
+ "duplicateRight": "Dupliquer dans la marge droite",
+ "duplicateRightDesc": "Afficher le numéro des deux côtés",
+ "continuation": "Continuation",
+ "moreTitle": "Appliquer l'étiquette de continuation de dialogue (bas de page)",
+ "contdTitle": "Appliquer l'étiquette de continuation de personnage (haut de page)"
+ },
+ "projectSettings": {
+ "titleLabel": "Titre",
+ "titlePlaceholder": "Entrez le nom du projet...",
+ "authorLabel": "Auteur",
+ "authorPlaceholder": "Nom de l'auteur...",
+ "descriptionLabel": "Description",
+ "descriptionPlaceholder": "De quoi parle ce scénario ?",
+ "posterLabel": "Affiche",
+ "noPoster": "Pas d'affiche",
+ "posterHelp": "Recommandé : 600x900 pixels (ratio 2:3). Supporte PNG et JPG.",
+ "saveChanges": "Enregistrer",
+ "dangerZoneTitle": "Zone dangereuse"
+ }
+}
diff --git a/messages/ja.json b/messages/ja.json
new file mode 100644
index 0000000..41b1144
--- /dev/null
+++ b/messages/ja.json
@@ -0,0 +1,266 @@
+{
+ "navbar": {
+ "screenplay": "脚本",
+ "board": "ボード",
+ "titlePage": "タイトルページ",
+ "synced": "クラウドに同期済み",
+ "noConnection": "接続なし",
+ "reconnecting": "再接続中..."
+ },
+ "common": {
+ "save": "変更を保存",
+ "cancel": "キャンセル",
+ "loading": "読み込み中..."
+ },
+ "sidebar": {
+ "title": "ダッシュボード",
+ "logOut": "ログアウト",
+ "logIn": "ログイン"
+ },
+ "appearance": {
+ "theme": "テーマ",
+ "themeHelp": {
+ "dark": "夜更かしする方や深夜の集中作業に適した、低グレアのダークテーマ。",
+ "light": "日中に自然で快適な、すっきりとした明るいテーマ。",
+ "latte": "温かみと読みやすさを融合した、柔らかなクリーム系テーマ。",
+ "wonka": "深みのある高級感と目への優しさを兼ね備えた、ベルベットカカオ系テーマ。",
+ "mint": "植物の穏やかさと視覚的バランスを融合した、さわやかなミント系テーマ。",
+ "blossom": "花びらの温もりと視覚的な柔らかさを融合した、繊細なブロッサム系テーマ。"
+ },
+ "editor": "エディタ",
+ "themedEditor": "テーマエディタ",
+ "themedEditorDesc": "テーマの色をエディタの背景とテキストに適用する"
+ },
+ "keybinds": {
+ "screenplayElements": "脚本要素",
+ "typing": "入力中…(Escでキャンセル)",
+ "notSet": "未設定",
+ "modifiersOnly": "修飾キーのみ — 通常のキーも押してください",
+ "defaultPrefix": "デフォルト:{combo}",
+ "reset": "リセット",
+ "resetTitle": "ユーザー設定をクリア",
+ "resetDefaults": "デフォルトに戻す",
+ "save": "変更を保存"
+ },
+ "language": {
+ "label": "表示言語",
+ "helpText": "アプリ全体で使用する言語を選択してください。"
+ },
+ "modal": {
+ "groups": {
+ "project": "プロジェクト",
+ "preferences": "環境設定",
+ "account": "アカウント"
+ },
+ "tabs": {
+ "General": "一般",
+ "Layout": "レイアウト",
+ "Export": "インポート/エクスポート",
+ "Collaborators": "コラボレーター",
+ "Keybinds": "ショートカット",
+ "Appearance": "外観",
+ "Language": "言語",
+ "Profile": "プロフィール",
+ "Security": "セキュリティ",
+ "Settings": "設定",
+ "Login": "ログイン",
+ "About": "このアプリについて"
+ }
+ },
+ "dangerZone": {
+ "transferOwnership": "所有権の移転",
+ "transferDesc": "オーナー権限を別のユーザーに移転します。移転後はエディター権限が付与されます。",
+ "transferBtn": "移転",
+ "transferPrompt": "新しいオーナーのメールアドレスを入力してください:",
+ "deleteProject": "プロジェクトを削除",
+ "deleteProjectDesc": "プロジェクトを削除すると元に戻せません。よく確認してください。",
+ "deleteBtn": "削除",
+ "modalTitle": "プロジェクトを削除",
+ "modalDesc": "この操作は永続的で取り消せません。このプロジェクトに関連するすべてのデータが失われます。",
+ "deleting": "削除中...",
+ "confirmDeleteBtn": "プロジェクトを削除"
+ },
+ "profile": {
+ "email": "メールアドレス",
+ "username": "ユーザー名",
+ "usernamePlaceholder": "表示名を入力してください...",
+ "usernameHelp": "共有プロジェクトで共同作業する際、この名前がコラボレーターに表示されます。",
+ "color": "カラー",
+ "customColor": "カスタムカラー",
+ "selectColor": "カラーを選択 {color}",
+ "successMessage": "プロフィールを更新しました",
+ "failedUpdate": "プロフィールの更新に失敗しました",
+ "errorSaving": "保存中にエラーが発生しました",
+ "saving": "保存中...",
+ "dangerZoneTitle": "危険ゾーン",
+ "deleteAccount": "アカウントを削除",
+ "deleteAccountDesc": "アカウントとすべての関連データを完全に削除します。この操作は取り消せません。",
+ "deleteBtn": "削除",
+ "deleteModalTitle": "アカウントを削除",
+ "deleteModalDesc": "アカウントとすべての関連データが完全に削除されます。この操作は取り消せません。",
+ "deleteConfirmPhrase": "アカウントの削除を確認します",
+ "deleteConfirmLabel": "
と入力して確認してください。",
+ "deleting": "削除中...",
+ "deleteAccountBtn": "アカウントを削除する"
+ },
+ "projects": {
+ "untitled": "無題",
+ "newProject": "新しいプロジェクト",
+ "pageTitle": "プロジェクト",
+ "importBtn": "インポート...",
+ "importing": "インポート中...",
+ "createBtn": "作成",
+ "empty": {
+ "createFirst": "クリックして最初のプロジェクトを作成",
+ "or": "または",
+ "importExisting": "既存のスクリプトをインポート"
+ },
+ "item": {
+ "posterAlt": "映画ポスター",
+ "localOnly": "ローカルのみ",
+ "syncedToCloud": "クラウドに同期済み",
+ "today": "今日",
+ "yesterday": "昨日",
+ "daysAgo": "{days}日前",
+ "monthsAgo": "{months}ヶ月前",
+ "moreThanYearAgo": "1年以上前"
+ },
+ "form": {
+ "formTitle": "プロジェクトを作成",
+ "titleField": "タイトル",
+ "descriptionField": "説明",
+ "authorField": "著者",
+ "posterField": "ポスター",
+ "optional": "任意",
+ "submitBtn": "作成",
+ "failedToCreate": "プロジェクトの作成に失敗しました"
+ }
+ },
+ "editorSidebar": {
+ "scenes": "シーン",
+ "characters": "キャラクター",
+ "locations": "場所"
+ },
+ "formatDropdown": {
+ "elements": {
+ "scene": "シーン見出し",
+ "action": "アクション",
+ "character": "キャラクター",
+ "dialogue": "セリフ",
+ "parenthetical": "(ト書き注)",
+ "transition": "トランジション:",
+ "section": "セクション",
+ "note": "【メモ】",
+ "none": "なし"
+ },
+ "titlePageElements": {
+ "title": "タイトル",
+ "author": "著者",
+ "date": "日付",
+ "none": "なし"
+ }
+ },
+ "search": {
+ "placeholder": "検索...",
+ "noMatches": "一致なし",
+ "matchCount": "{total}件中{current}件目",
+ "replacePlaceholder": "置換後の文字列...",
+ "replace": "置換",
+ "replaceAll": "すべて置換",
+ "filterByElement": "要素でフィルター:",
+ "elements": {
+ "scene": "シーン見出し",
+ "action": "アクション",
+ "character": "キャラクター",
+ "dialogue": "セリフ",
+ "parenthetical": "ト書き注",
+ "transition": "トランジション",
+ "section": "セクション",
+ "note": "メモ",
+ "none": "なし"
+ }
+ },
+ "collaborators": {
+ "projectTeam": "プロジェクトチーム ({count}/{max})",
+ "teamHelp": "チームメンバーと保留中の招待を管理します。Proプランを持たないユーザーをプロジェクトに招待できます。オーナーがProプランを持っている間、プロジェクトは共同作業可能な状態が維持されます。",
+ "roles": {
+ "owner": "オーナー",
+ "admin": "管理者",
+ "editor": "編集者",
+ "viewer": "閲覧者"
+ },
+ "roleDesc": {
+ "owner": "プロジェクトの削除と所有権の移転が可能",
+ "admin": "コラボレーターの招待、昇格、降格、削除が可能",
+ "editor": "脚本やその他のプロジェクトコンテンツを編集可能",
+ "viewer": "読み取り専用アクセス。変更を加えることはできません"
+ },
+ "you": "(自分)",
+ "leave": "退出",
+ "kick": "除外",
+ "pending": "保留中",
+ "cancel": "キャンセル",
+ "emailPlaceholder": "メールアドレスを入力...",
+ "invite": "招待"
+ },
+ "export": {
+ "importLabel": "インポート",
+ "selectFile": "ファイルを選択",
+ "selectFileDesc": ".fountain、.fdx、.scriptio、または .txt をアップロード",
+ "exportLabel": "エクスポート",
+ "formatOptions": {
+ "pdf": "PDF ドキュメント (.pdf)",
+ "fountain": "Fountain (.fountain)",
+ "fdx": "Final Draft (.fdx)",
+ "scriptio": "Scriptio (.scriptio)"
+ },
+ "formatHelp": {
+ "pdf": "業界標準フォーマット。共有や印刷に最適。",
+ "fountain": "Markdownベースのプレーンテキスト形式で、互換性に優れています。",
+ "fdx": "業界ソフトウェアFinal Draftと互換。",
+ "scriptio": "Scriptio独自の形式。プロジェクトをローカルに保存するために使用"
+ },
+ "includeNotes": "ノートを含める",
+ "includeNotesDesc": "インラインノートをエクスポートします。",
+ "watermark": "透かし",
+ "watermarkDesc": "ページに著者名を重ねて表示します。",
+ "passwordProtection": "パスワード保護",
+ "passwordProtectionDesc": "PDFを開くためにパスワードが必要です。",
+ "passwordPlaceholder": "パスワードを入力",
+ "exportBtn": "エクスポート",
+ "exporting": "エクスポート中...",
+ "exportingProgress": "エクスポート中 ({progress}%)"
+ },
+ "layout": {
+ "pageFormat": "ページフォーマット",
+ "pageFormatHelp": {
+ "letter": "米国の標準形式。ハリウッド脚本の業界標準。",
+ "a4": "国際標準形式。ヨーロッパやほとんどの国で一般的。"
+ },
+ "sceneHeadings": "シーン見出し",
+ "bold": "太字",
+ "boldDesc": "シーン見出しが太字で表示されます",
+ "extraSpace": "上部に余白を追加",
+ "extraSpaceDesc": "シーン見出しの前に追加のスペースを挿入します",
+ "sceneNumbering": "シーン番号",
+ "sceneNumberingDesc": "左余白にシーン番号を表示",
+ "duplicateRight": "右余白にも複製",
+ "duplicateRightDesc": "両側に番号を表示",
+ "continuation": "継続",
+ "moreTitle": "台詞継続ラベルを適用(ページ下部)",
+ "contdTitle": "キャラクター継続ラベルを適用(ページ上部)"
+ },
+ "projectSettings": {
+ "titleLabel": "タイトル",
+ "titlePlaceholder": "プロジェクト名を入力...",
+ "authorLabel": "著者",
+ "authorPlaceholder": "著者名...",
+ "descriptionLabel": "説明",
+ "descriptionPlaceholder": "この脚本はどんな内容ですか?",
+ "posterLabel": "ポスター",
+ "noPoster": "ポスターなし",
+ "posterHelp": "推奨: 600x900ピクセル (2:3比率)。PNG、JPG対応。",
+ "saveChanges": "変更を保存",
+ "dangerZoneTitle": "危険ゾーン"
+ }
+}
diff --git a/messages/ko.json b/messages/ko.json
new file mode 100644
index 0000000..99d9eed
--- /dev/null
+++ b/messages/ko.json
@@ -0,0 +1,266 @@
+{
+ "navbar": {
+ "screenplay": "시나리오",
+ "board": "보드",
+ "titlePage": "타이틀 페이지",
+ "synced": "클라우드에 동기화됨",
+ "noConnection": "연결 없음",
+ "reconnecting": "재연결 중..."
+ },
+ "common": {
+ "save": "변경사항 저장",
+ "cancel": "취소",
+ "loading": "불러오는 중..."
+ },
+ "sidebar": {
+ "title": "대시보드",
+ "logOut": "로그아웃",
+ "logIn": "로그인"
+ },
+ "appearance": {
+ "theme": "테마",
+ "themeHelp": {
+ "dark": "야행성 사용자와 늦은 밤 집중을 위한 낮은 눈부심의 다크 테마.",
+ "light": "낮 동안 자연스럽고 편안한 밝고 상쾌한 테마.",
+ "latte": "따뜻함과 가독성을 조화롭게 결합한 부드러운 크림 계열 테마.",
+ "wonka": "깊은 고급스러움과 눈 편안함을 결합한 벨벳 카카오 계열 테마.",
+ "mint": "식물의 평온함과 눈의 균형을 결합한 상쾌한 민트 계열 테마.",
+ "blossom": "꽃잎의 따뜻함과 눈의 부드러움을 결합한 섬세한 블로섬 테마."
+ },
+ "editor": "에디터",
+ "themedEditor": "테마 에디터",
+ "themedEditorDesc": "에디터 배경과 텍스트에 테마 색상 적용"
+ },
+ "keybinds": {
+ "screenplayElements": "시나리오 요소",
+ "typing": "입력 중… (Esc로 취소)",
+ "notSet": "미설정",
+ "modifiersOnly": "수정키만 입력됨 — 일반 키를 함께 누르세요",
+ "defaultPrefix": "기본값: {combo}",
+ "reset": "초기화",
+ "resetTitle": "사용자 설정 지우기",
+ "resetDefaults": "기본값으로 재설정",
+ "save": "변경사항 저장"
+ },
+ "language": {
+ "label": "표시 언어",
+ "helpText": "앱 전체에서 사용할 언어를 선택하세요."
+ },
+ "modal": {
+ "groups": {
+ "project": "프로젝트",
+ "preferences": "환경설정",
+ "account": "계정"
+ },
+ "tabs": {
+ "General": "일반",
+ "Layout": "레이아웃",
+ "Export": "가져오기/내보내기",
+ "Collaborators": "협업자",
+ "Keybinds": "단축키",
+ "Appearance": "외관",
+ "Language": "언어",
+ "Profile": "프로필",
+ "Security": "보안",
+ "Settings": "설정",
+ "Login": "로그인",
+ "About": "정보"
+ }
+ },
+ "dangerZone": {
+ "transferOwnership": "소유권 이전",
+ "transferDesc": "소유자 역할을 다른 사용자에게 이전합니다. 이전 후 편집자 역할이 부여됩니다.",
+ "transferBtn": "이전",
+ "transferPrompt": "새 소유자의 이메일을 입력하세요:",
+ "deleteProject": "프로젝트 삭제",
+ "deleteProjectDesc": "프로젝트를 삭제하면 되돌릴 수 없습니다. 신중하게 결정하세요.",
+ "deleteBtn": "삭제",
+ "modalTitle": "프로젝트 삭제",
+ "modalDesc": "이 작업은 영구적이며 취소할 수 없습니다. 이 프로젝트와 관련된 모든 데이터가 삭제됩니다.",
+ "deleting": "삭제 중...",
+ "confirmDeleteBtn": "프로젝트 삭제"
+ },
+ "profile": {
+ "email": "이메일",
+ "username": "사용자 이름",
+ "usernamePlaceholder": "표시 이름을 입력하세요...",
+ "usernameHelp": "공유 프로젝트에서 협업할 때 이 이름이 다른 협업자에게 표시됩니다.",
+ "color": "색상",
+ "customColor": "사용자 정의 색상",
+ "selectColor": "색상 선택 {color}",
+ "successMessage": "프로필이 성공적으로 업데이트되었습니다",
+ "failedUpdate": "프로필 업데이트에 실패했습니다",
+ "errorSaving": "저장 중 오류가 발생했습니다",
+ "saving": "저장 중...",
+ "dangerZoneTitle": "위험 구역",
+ "deleteAccount": "계정 삭제",
+ "deleteAccountDesc": "계정과 모든 관련 데이터를 영구적으로 삭제합니다. 이 작업은 취소할 수 없습니다.",
+ "deleteBtn": "삭제",
+ "deleteModalTitle": "계정 삭제",
+ "deleteModalDesc": "계정과 모든 관련 데이터가 영구적으로 삭제됩니다. 이 작업은 취소할 수 없습니다.",
+ "deleteConfirmPhrase": "계정 삭제를 확인합니다",
+ "deleteConfirmLabel": "
를 입력하여 확인하세요.",
+ "deleting": "삭제 중...",
+ "deleteAccountBtn": "내 계정 삭제"
+ },
+ "projects": {
+ "untitled": "제목 없음",
+ "newProject": "새 프로젝트",
+ "pageTitle": "프로젝트",
+ "importBtn": "가져오기...",
+ "importing": "가져오는 중...",
+ "createBtn": "만들기",
+ "empty": {
+ "createFirst": "클릭하여 첫 번째 프로젝트를 만드세요",
+ "or": "또는",
+ "importExisting": "기존 스크립트 가져오기"
+ },
+ "item": {
+ "posterAlt": "영화 포스터",
+ "localOnly": "로컬 전용",
+ "syncedToCloud": "클라우드에 동기화됨",
+ "today": "오늘",
+ "yesterday": "어제",
+ "daysAgo": "{days}일 전",
+ "monthsAgo": "{months}개월 전",
+ "moreThanYearAgo": "1년 이상 전"
+ },
+ "form": {
+ "formTitle": "프로젝트 만들기",
+ "titleField": "제목",
+ "descriptionField": "설명",
+ "authorField": "작가",
+ "posterField": "포스터",
+ "optional": "선택사항",
+ "submitBtn": "만들기",
+ "failedToCreate": "프로젝트 생성에 실패했습니다"
+ }
+ },
+ "editorSidebar": {
+ "scenes": "장면",
+ "characters": "캐릭터",
+ "locations": "장소"
+ },
+ "formatDropdown": {
+ "elements": {
+ "scene": "씬 헤딩",
+ "action": "액션",
+ "character": "캐릭터",
+ "dialogue": "대사",
+ "parenthetical": "(괄호)",
+ "transition": "전환:",
+ "section": "섹션",
+ "note": "[[메모]]",
+ "none": "없음"
+ },
+ "titlePageElements": {
+ "title": "제목",
+ "author": "작가",
+ "date": "날짜",
+ "none": "없음"
+ }
+ },
+ "search": {
+ "placeholder": "검색...",
+ "noMatches": "결과 없음",
+ "matchCount": "{total}개 중 {current}번째",
+ "replacePlaceholder": "바꿀 내용...",
+ "replace": "바꾸기",
+ "replaceAll": "모두 바꾸기",
+ "filterByElement": "요소로 필터:",
+ "elements": {
+ "scene": "씬 헤딩",
+ "action": "액션",
+ "character": "캐릭터",
+ "dialogue": "대사",
+ "parenthetical": "괄호",
+ "transition": "전환",
+ "section": "섹션",
+ "note": "메모",
+ "none": "없음"
+ }
+ },
+ "collaborators": {
+ "projectTeam": "프로젝트 팀 ({count}/{max})",
+ "teamHelp": "팀 멤버와 대기 중인 초대를 관리하세요. Pro 플랜이 없는 사용자를 프로젝트에 초대할 수 있습니다. 소유자가 Pro 플랜을 보유하는 동안 프로젝트는 협업 가능 상태를 유지합니다.",
+ "roles": {
+ "owner": "소유자",
+ "admin": "관리자",
+ "editor": "편집자",
+ "viewer": "뷰어"
+ },
+ "roleDesc": {
+ "owner": "프로젝트를 삭제하고 소유권을 이전할 수 있습니다",
+ "admin": "협업자를 초대, 승격, 강등, 내보낼 수 있습니다",
+ "editor": "시나리오 및 기타 프로젝트 콘텐츠를 수정할 수 있습니다",
+ "viewer": "읽기 전용 접근. 변경할 수 없습니다"
+ },
+ "you": "(나)",
+ "leave": "나가기",
+ "kick": "내보내기",
+ "pending": "대기 중",
+ "cancel": "취소",
+ "emailPlaceholder": "이메일 입력...",
+ "invite": "초대"
+ },
+ "export": {
+ "importLabel": "가져오기",
+ "selectFile": "파일 선택",
+ "selectFileDesc": ".fountain, .fdx, .scriptio 또는 .txt 업로드",
+ "exportLabel": "내보내기",
+ "formatOptions": {
+ "pdf": "PDF 문서 (.pdf)",
+ "fountain": "Fountain (.fountain)",
+ "fdx": "Final Draft (.fdx)",
+ "scriptio": "Scriptio (.scriptio)"
+ },
+ "formatHelp": {
+ "pdf": "업계 표준 형식. 공유 및 인쇄에 최적화.",
+ "fountain": "마크다운 기반 일반 텍스트 형식, 호환성이 뛰어납니다.",
+ "fdx": "Final Draft 업계 소프트웨어와 호환.",
+ "scriptio": "Scriptio 고유 형식, 프로젝트를 로컬에 보관"
+ },
+ "includeNotes": "노트 포함",
+ "includeNotesDesc": "인라인 노트를 내보냅니다.",
+ "watermark": "워터마크",
+ "watermarkDesc": "페이지에 작가 이름을 오버레이합니다.",
+ "passwordProtection": "비밀번호 보호",
+ "passwordProtectionDesc": "PDF를 열 때 비밀번호가 필요합니다.",
+ "passwordPlaceholder": "비밀번호 입력",
+ "exportBtn": "내보내기",
+ "exporting": "내보내는 중...",
+ "exportingProgress": "내보내는 중 ({progress}%)"
+ },
+ "layout": {
+ "pageFormat": "페이지 형식",
+ "pageFormatHelp": {
+ "letter": "미국 표준 형식. 할리우드 시나리오의 업계 표준.",
+ "a4": "국제 표준 형식. 유럽 및 대부분의 국가에서 통용됩니다."
+ },
+ "sceneHeadings": "씬 헤딩",
+ "bold": "굵게",
+ "boldDesc": "씬 헤딩이 굵게 표시됩니다",
+ "extraSpace": "위 추가 간격",
+ "extraSpaceDesc": "씬 헤딩 앞에 추가 간격을 삽입합니다",
+ "sceneNumbering": "씬 번호 매기기",
+ "sceneNumberingDesc": "왼쪽 여백에 씬 번호 표시",
+ "duplicateRight": "오른쪽 여백에 복제",
+ "duplicateRightDesc": "양쪽에 번호 표시",
+ "continuation": "이어지기",
+ "moreTitle": "대사 이어지기 레이블 적용 (페이지 하단)",
+ "contdTitle": "캐릭터 이어지기 레이블 적용 (페이지 상단)"
+ },
+ "projectSettings": {
+ "titleLabel": "제목",
+ "titlePlaceholder": "프로젝트 이름 입력...",
+ "authorLabel": "작가",
+ "authorPlaceholder": "작가 이름...",
+ "descriptionLabel": "설명",
+ "descriptionPlaceholder": "이 시나리오는 어떤 내용인가요?",
+ "posterLabel": "포스터",
+ "noPoster": "포스터 없음",
+ "posterHelp": "권장: 600x900픽셀 (2:3 비율). PNG, JPG 지원.",
+ "saveChanges": "변경사항 저장",
+ "dangerZoneTitle": "위험 구역"
+ }
+}
diff --git a/messages/pl.json b/messages/pl.json
new file mode 100644
index 0000000..c88d38f
--- /dev/null
+++ b/messages/pl.json
@@ -0,0 +1,266 @@
+{
+ "navbar": {
+ "screenplay": "Scenariusz",
+ "board": "Tablica",
+ "titlePage": "Strona tytułowa",
+ "synced": "Zsynchronizowano z chmurą",
+ "noConnection": "Brak połączenia",
+ "reconnecting": "Łączenie..."
+ },
+ "common": {
+ "save": "Zapisz zmiany",
+ "cancel": "Anuluj",
+ "loading": "Ładowanie..."
+ },
+ "sidebar": {
+ "title": "Panel",
+ "logOut": "Wyloguj się",
+ "logIn": "Zaloguj się"
+ },
+ "appearance": {
+ "theme": "Motyw",
+ "themeHelp": {
+ "dark": "Ciemny motyw o niskim olśnieniu, stworzony dla nocnych marków i późnonocnej koncentracji.",
+ "light": "Jasny, przewiewny motyw, naturalny i komfortowy w ciągu dnia.",
+ "latte": "Miękki, kremowy motyw łączący ciepło z czytelnością.",
+ "wonka": "Aksamitny, kakaowy motyw łączący głęboki luksus z komfortem dla oczu.",
+ "mint": "Orzeźwiający, miętowy motyw łączący botaniczny spokój z wizualną równowagą.",
+ "blossom": "Delikatny, kwiatowy motyw łączący ciepło płatków z miękkością dla oczu."
+ },
+ "editor": "Edytor",
+ "themedEditor": "Edytor z motywem",
+ "themedEditorDesc": "Użyj kolorów motywu dla tła i tekstu edytora"
+ },
+ "keybinds": {
+ "screenplayElements": "Elementy scenariusza",
+ "typing": "Wpisz… (Esc aby anulować)",
+ "notSet": "Nieustawiony",
+ "modifiersOnly": "Tylko modyfikatory — naciśnij też zwykły klawisz",
+ "defaultPrefix": "Domyślny: {combo}",
+ "reset": "Resetuj",
+ "resetTitle": "Wyczyść skrót użytkownika",
+ "resetDefaults": "Przywróć domyślne",
+ "save": "Zapisz zmiany"
+ },
+ "language": {
+ "label": "Język wyświetlania",
+ "helpText": "Wybierz język używany w całej aplikacji."
+ },
+ "modal": {
+ "groups": {
+ "project": "Projekt",
+ "preferences": "Preferencje",
+ "account": "Konto"
+ },
+ "tabs": {
+ "General": "Ogólne",
+ "Layout": "Układ",
+ "Export": "Import/Eksport",
+ "Collaborators": "Współpracownicy",
+ "Keybinds": "Skróty klawiszowe",
+ "Appearance": "Wygląd",
+ "Language": "Język",
+ "Profile": "Profil",
+ "Security": "Bezpieczeństwo",
+ "Settings": "Ustawienia",
+ "Login": "Logowanie",
+ "About": "O aplikacji"
+ }
+ },
+ "dangerZone": {
+ "transferOwnership": "Przekaż własność",
+ "transferDesc": "Przekaż rolę właściciela innemu użytkownikowi. Otrzymasz rolę edytora.",
+ "transferBtn": "Przekaż",
+ "transferPrompt": "Wprowadź adres e-mail nowego właściciela:",
+ "deleteProject": "Usuń projekt",
+ "deleteProjectDesc": "Po usunięciu projektu nie ma możliwości jego przywrócenia. Upewnij się przed kontynuacją.",
+ "deleteBtn": "Usuń",
+ "modalTitle": "Usuń projekt",
+ "modalDesc": "Ta operacja jest trwała i nieodwracalna. Wszystkie dane powiązane z tym projektem zostaną utracone.",
+ "deleting": "Usuwanie...",
+ "confirmDeleteBtn": "Usuń projekt"
+ },
+ "profile": {
+ "email": "E-mail",
+ "username": "Nazwa użytkownika",
+ "usernamePlaceholder": "Wprowadź swoją nazwę wyświetlaną...",
+ "usernameHelp": "Ta nazwa będzie widoczna dla współpracowników podczas pracy nad wspólnymi projektami.",
+ "color": "Kolor",
+ "customColor": "Kolor niestandardowy",
+ "selectColor": "Wybierz kolor {color}",
+ "successMessage": "Profil zaktualizowany pomyślnie",
+ "failedUpdate": "Nie udało się zaktualizować profilu",
+ "errorSaving": "Wystąpił błąd podczas zapisywania",
+ "saving": "Zapisywanie...",
+ "dangerZoneTitle": "Strefa niebezpieczna",
+ "deleteAccount": "Usuń konto",
+ "deleteAccountDesc": "Trwale usuń swoje konto i wszystkie powiązane dane. Tej operacji nie można cofnąć.",
+ "deleteBtn": "Usuń",
+ "deleteModalTitle": "Usuń konto",
+ "deleteModalDesc": "Twoje konto i wszystkie powiązane dane zostaną trwale usunięte. Tej operacji nie można cofnąć.",
+ "deleteConfirmPhrase": "Potwierdzam usunięcie mojego konta",
+ "deleteConfirmLabel": "Wpisz
, aby potwierdzić.",
+ "deleting": "Usuwanie...",
+ "deleteAccountBtn": "Usuń moje konto"
+ },
+ "projects": {
+ "untitled": "Bez tytułu",
+ "newProject": "Nowy projekt",
+ "pageTitle": "Projekty",
+ "importBtn": "Importuj...",
+ "importing": "Importowanie...",
+ "createBtn": "Utwórz",
+ "empty": {
+ "createFirst": "Kliknij, aby utworzyć pierwszy projekt",
+ "or": "lub",
+ "importExisting": "Importuj istniejący skrypt"
+ },
+ "item": {
+ "posterAlt": "Plakat filmowy",
+ "localOnly": "Tylko lokalnie",
+ "syncedToCloud": "Zsynchronizowano z chmurą",
+ "today": "Dzisiaj",
+ "yesterday": "Wczoraj",
+ "daysAgo": "{days, plural, one {# dzień temu} few {# dni temu} many {# dni temu} other {# dni temu}}",
+ "monthsAgo": "{months, plural, one {# miesiąc temu} few {# miesiące temu} many {# miesięcy temu} other {# miesięcy temu}}",
+ "moreThanYearAgo": "Ponad rok temu"
+ },
+ "form": {
+ "formTitle": "Utwórz projekt",
+ "titleField": "Tytuł",
+ "descriptionField": "Opis",
+ "authorField": "Autor",
+ "posterField": "Plakat",
+ "optional": "opcjonalnie",
+ "submitBtn": "Utwórz",
+ "failedToCreate": "Nie udało się utworzyć projektu"
+ }
+ },
+ "editorSidebar": {
+ "scenes": "Sceny",
+ "characters": "Postacie",
+ "locations": "Lokacje"
+ },
+ "formatDropdown": {
+ "elements": {
+ "scene": "NAGŁÓWEK SCENY",
+ "action": "Akcja",
+ "character": "POSTAĆ",
+ "dialogue": "Dialog",
+ "parenthetical": "(Nawias)",
+ "transition": "PRZEJŚCIE:",
+ "section": "Sekcja",
+ "note": "[[Uwaga]]",
+ "none": "Brak"
+ },
+ "titlePageElements": {
+ "title": "Tytuł",
+ "author": "Autor",
+ "date": "Data",
+ "none": "Brak"
+ }
+ },
+ "search": {
+ "placeholder": "Szukaj...",
+ "noMatches": "Brak wyników",
+ "matchCount": "{current} z {total}",
+ "replacePlaceholder": "Zastąp przez...",
+ "replace": "Zastąp",
+ "replaceAll": "Zastąp wszystko",
+ "filterByElement": "Filtruj według elementu:",
+ "elements": {
+ "scene": "Nagłówek sceny",
+ "action": "Akcja",
+ "character": "Postać",
+ "dialogue": "Dialog",
+ "parenthetical": "Nawias",
+ "transition": "Przejście",
+ "section": "Sekcja",
+ "note": "Uwaga",
+ "none": "Brak"
+ }
+ },
+ "collaborators": {
+ "projectTeam": "Zespół projektu ({count}/{max})",
+ "teamHelp": "Zarządzaj członkami zespołu i oczekującymi zaproszeniami. Możesz zaprosić dowolnego użytkownika bez planu Pro do udziału w projekcie. Projekt pozostaje współdzielony, dopóki właściciel ma plan Pro.",
+ "roles": {
+ "owner": "Właściciel",
+ "admin": "Admin",
+ "editor": "Edytor",
+ "viewer": "Obserwator"
+ },
+ "roleDesc": {
+ "owner": "Może usunąć projekt i przekazać własność",
+ "admin": "Może zapraszać, awansować, degradować i usuwać współpracowników",
+ "editor": "Może modyfikować scenariusz i inne treści projektu",
+ "viewer": "Dostęp tylko do odczytu. Nie może wprowadzać żadnych zmian"
+ },
+ "you": "(ty)",
+ "leave": "Opuść",
+ "kick": "Wyrzuć",
+ "pending": "Oczekuje",
+ "cancel": "Anuluj",
+ "emailPlaceholder": "Wpisz adres e-mail...",
+ "invite": "Zaproś"
+ },
+ "export": {
+ "importLabel": "Importuj",
+ "selectFile": "Wybierz plik",
+ "selectFileDesc": "Prześlij .fountain, .fdx, .scriptio lub .txt",
+ "exportLabel": "Eksportuj",
+ "formatOptions": {
+ "pdf": "Dokument PDF (.pdf)",
+ "fountain": "Fountain (.fountain)",
+ "fdx": "Final Draft (.fdx)",
+ "scriptio": "Scriptio (.scriptio)"
+ },
+ "formatHelp": {
+ "pdf": "Standardowy format branżowy. Najlepszy do udostępniania i drukowania.",
+ "fountain": "Format zwykłego tekstu oparty na Markdown, doskonały dla zgodności.",
+ "fdx": "Kompatybilny z oprogramowaniem branżowym Final Draft.",
+ "scriptio": "Własny format Scriptio, aby zachować projekt lokalnie"
+ },
+ "includeNotes": "Uwzględnij notatki",
+ "includeNotesDesc": "Eksportuj wbudowane notatki.",
+ "watermark": "Znak wodny",
+ "watermarkDesc": "Nakładanie nazwy autora na strony.",
+ "passwordProtection": "Ochrona hasłem",
+ "passwordProtectionDesc": "Wymagaj hasła do otwarcia pliku PDF.",
+ "passwordPlaceholder": "Wprowadź hasło",
+ "exportBtn": "Eksportuj",
+ "exporting": "Eksportowanie...",
+ "exportingProgress": "Eksportowanie ({progress}%)"
+ },
+ "layout": {
+ "pageFormat": "Format strony",
+ "pageFormatHelp": {
+ "letter": "Format standardowy w USA. Branżowy standard dla scenariuszy hollywoodzkich.",
+ "a4": "Międzynarodowy format standardowy. Powszechny w Europie i większości krajów."
+ },
+ "sceneHeadings": "Nagłówki scen",
+ "bold": "Pogrubienie",
+ "boldDesc": "Nagłówki scen będą wyświetlane pogrubione",
+ "extraSpace": "Dodatkowe miejsce powyżej",
+ "extraSpaceDesc": "Dodaj dodatkowe odstępy przed nagłówkami scen",
+ "sceneNumbering": "Numerowanie scen",
+ "sceneNumberingDesc": "Wyświetlaj numery scen na lewym marginesie",
+ "duplicateRight": "Powiel na prawym marginesie",
+ "duplicateRightDesc": "Wyświetlaj numer po obu stronach",
+ "continuation": "Kontynuacja",
+ "moreTitle": "Zastosuj etykietę kontynuacji dialogu (dół strony)",
+ "contdTitle": "Zastosuj etykietę kontynuacji postaci (góra strony)"
+ },
+ "projectSettings": {
+ "titleLabel": "Tytuł",
+ "titlePlaceholder": "Wprowadź nazwę projektu...",
+ "authorLabel": "Autor",
+ "authorPlaceholder": "Imię i nazwisko autora...",
+ "descriptionLabel": "Opis",
+ "descriptionPlaceholder": "O czym jest ten scenariusz?",
+ "posterLabel": "Plakat",
+ "noPoster": "Brak plakatu",
+ "posterHelp": "Zalecane: 600x900 pikseli (proporcje 2:3). Obsługuje PNG i JPG.",
+ "saveChanges": "Zapisz zmiany",
+ "dangerZoneTitle": "Strefa niebezpieczna"
+ }
+}
diff --git a/messages/zh.json b/messages/zh.json
new file mode 100644
index 0000000..dab5759
--- /dev/null
+++ b/messages/zh.json
@@ -0,0 +1,266 @@
+{
+ "navbar": {
+ "screenplay": "剧本",
+ "board": "版块",
+ "titlePage": "标题页",
+ "synced": "已同步至云端",
+ "noConnection": "无连接",
+ "reconnecting": "正在重新连接..."
+ },
+ "common": {
+ "save": "保存更改",
+ "cancel": "取消",
+ "loading": "加载中..."
+ },
+ "sidebar": {
+ "title": "控制台",
+ "logOut": "退出登录",
+ "logIn": "登录"
+ },
+ "appearance": {
+ "theme": "主题",
+ "themeHelp": {
+ "dark": "低眩光深色主题,专为夜猫子和深夜专注而设计。",
+ "light": "清爽明亮的主题,白天使用自然舒适。",
+ "latte": "柔和奶油色调主题,兼顾温暖感与可读性。",
+ "wonka": "丝绒可可色调主题,深邃奢华与护眼舒适并重。",
+ "mint": "清新薄荷主题,融合自然宁静与视觉平衡。",
+ "blossom": "淡雅花瓣主题,花卉温柔与视觉柔和相得益彰。"
+ },
+ "editor": "编辑器",
+ "themedEditor": "主题化编辑器",
+ "themedEditorDesc": "将主题颜色应用于编辑器背景和文字"
+ },
+ "keybinds": {
+ "screenplayElements": "剧本元素",
+ "typing": "按键中…(Esc 取消)",
+ "notSet": "未设置",
+ "modifiersOnly": "仅修饰键 — 请同时按一个普通按键",
+ "defaultPrefix": "默认:{combo}",
+ "reset": "重置",
+ "resetTitle": "清除自定义绑定",
+ "resetDefaults": "恢复默认值",
+ "save": "保存更改"
+ },
+ "language": {
+ "label": "显示语言",
+ "helpText": "选择整个应用程序使用的语言。"
+ },
+ "modal": {
+ "groups": {
+ "project": "项目",
+ "preferences": "偏好设置",
+ "account": "账户"
+ },
+ "tabs": {
+ "General": "常规",
+ "Layout": "布局",
+ "Export": "导入/导出",
+ "Collaborators": "协作者",
+ "Keybinds": "快捷键",
+ "Appearance": "外观",
+ "Language": "语言",
+ "Profile": "个人资料",
+ "Security": "安全",
+ "Settings": "设置",
+ "Login": "登录",
+ "About": "关于"
+ }
+ },
+ "dangerZone": {
+ "transferOwnership": "转让所有权",
+ "transferDesc": "将您的所有者角色转让给其他用户,您将获得编辑者角色。",
+ "transferBtn": "转让",
+ "transferPrompt": "请输入新所有者的邮箱地址:",
+ "deleteProject": "删除项目",
+ "deleteProjectDesc": "项目一旦删除,无法恢复,请确认后再操作。",
+ "deleteBtn": "删除",
+ "modalTitle": "删除项目",
+ "modalDesc": "此操作不可逆,项目的所有相关数据将永久丢失。",
+ "deleting": "删除中...",
+ "confirmDeleteBtn": "删除项目"
+ },
+ "profile": {
+ "email": "邮箱",
+ "username": "用户名",
+ "usernamePlaceholder": "输入您的显示名称...",
+ "usernameHelp": "在共享项目中协作时,此名称对其他协作者可见。",
+ "color": "颜色",
+ "customColor": "自定义颜色",
+ "selectColor": "选择颜色 {color}",
+ "successMessage": "个人资料更新成功",
+ "failedUpdate": "个人资料更新失败",
+ "errorSaving": "保存时发生错误",
+ "saving": "保存中...",
+ "dangerZoneTitle": "危险区域",
+ "deleteAccount": "删除账户",
+ "deleteAccountDesc": "永久删除您的账户及所有关联数据,此操作不可撤销。",
+ "deleteBtn": "删除",
+ "deleteModalTitle": "删除账户",
+ "deleteModalDesc": "此操作将永久删除您的账户及所有关联数据,且无法撤销。",
+ "deleteConfirmPhrase": "我确认删除我的账户",
+ "deleteConfirmLabel": "输入
以确认。",
+ "deleting": "删除中...",
+ "deleteAccountBtn": "删除我的账户"
+ },
+ "projects": {
+ "untitled": "无标题",
+ "newProject": "新建项目",
+ "pageTitle": "项目",
+ "importBtn": "导入...",
+ "importing": "导入中...",
+ "createBtn": "创建",
+ "empty": {
+ "createFirst": "点击以创建您的第一个项目",
+ "or": "或",
+ "importExisting": "导入现有剧本"
+ },
+ "item": {
+ "posterAlt": "电影海报",
+ "localOnly": "仅本地",
+ "syncedToCloud": "已同步至云端",
+ "today": "今天",
+ "yesterday": "昨天",
+ "daysAgo": "{days} 天前",
+ "monthsAgo": "{months} 个月前",
+ "moreThanYearAgo": "一年多前"
+ },
+ "form": {
+ "formTitle": "创建项目",
+ "titleField": "标题",
+ "descriptionField": "描述",
+ "authorField": "作者",
+ "posterField": "海报",
+ "optional": "可选",
+ "submitBtn": "创建",
+ "failedToCreate": "项目创建失败"
+ }
+ },
+ "editorSidebar": {
+ "scenes": "场景",
+ "characters": "角色",
+ "locations": "地点"
+ },
+ "formatDropdown": {
+ "elements": {
+ "scene": "场景标题",
+ "action": "动作",
+ "character": "角色",
+ "dialogue": "对白",
+ "parenthetical": "(旁注)",
+ "transition": "转场:",
+ "section": "章节",
+ "note": "【注释】",
+ "none": "无"
+ },
+ "titlePageElements": {
+ "title": "标题",
+ "author": "作者",
+ "date": "日期",
+ "none": "无"
+ }
+ },
+ "search": {
+ "placeholder": "搜索...",
+ "noMatches": "无结果",
+ "matchCount": "{total} 条中第 {current} 条",
+ "replacePlaceholder": "替换为...",
+ "replace": "替换",
+ "replaceAll": "全部替换",
+ "filterByElement": "按元素筛选:",
+ "elements": {
+ "scene": "场景标题",
+ "action": "动作",
+ "character": "角色",
+ "dialogue": "对白",
+ "parenthetical": "旁注",
+ "transition": "转场",
+ "section": "章节",
+ "note": "注释",
+ "none": "无"
+ }
+ },
+ "collaborators": {
+ "projectTeam": "项目团队 ({count}/{max})",
+ "teamHelp": "管理您的团队成员和待处理的邀请。您可以邀请任何非 Pro 用户加入您的项目。只要项目所有者拥有 Pro 计划,项目就可以协作。",
+ "roles": {
+ "owner": "所有者",
+ "admin": "管理员",
+ "editor": "编辑",
+ "viewer": "查看者"
+ },
+ "roleDesc": {
+ "owner": "可以删除项目并转让所有权",
+ "admin": "可以邀请、晋升、降级和踢出协作者",
+ "editor": "可以修改剧本和其他项目内容",
+ "viewer": "只读权限,无法进行任何更改"
+ },
+ "you": "(你)",
+ "leave": "离开",
+ "kick": "踢出",
+ "pending": "待处理",
+ "cancel": "取消",
+ "emailPlaceholder": "输入邮箱...",
+ "invite": "邀请"
+ },
+ "export": {
+ "importLabel": "导入",
+ "selectFile": "选择文件",
+ "selectFileDesc": "上传 .fountain、.fdx、.scriptio 或 .txt",
+ "exportLabel": "导出",
+ "formatOptions": {
+ "pdf": "PDF 文档 (.pdf)",
+ "fountain": "Fountain (.fountain)",
+ "fdx": "Final Draft (.fdx)",
+ "scriptio": "Scriptio (.scriptio)"
+ },
+ "formatHelp": {
+ "pdf": "行业标准格式,最适合分享和打印。",
+ "fountain": "基于 Markdown 的纯文本格式,兼容性佳。",
+ "fdx": "兼容 Final Draft 行业软件。",
+ "scriptio": "Scriptio 专属格式,用于本地保存项目"
+ },
+ "includeNotes": "包含注释",
+ "includeNotesDesc": "导出内联注释。",
+ "watermark": "水印",
+ "watermarkDesc": "在页面上叠加作者姓名。",
+ "passwordProtection": "密码保护",
+ "passwordProtectionDesc": "需要密码才能打开 PDF。",
+ "passwordPlaceholder": "输入密码",
+ "exportBtn": "导出",
+ "exporting": "导出中...",
+ "exportingProgress": "导出中 ({progress}%)"
+ },
+ "layout": {
+ "pageFormat": "页面格式",
+ "pageFormatHelp": {
+ "letter": "美国标准格式,好莱坞剧本的行业标准。",
+ "a4": "国际标准格式,欧洲及大多数国家通用。"
+ },
+ "sceneHeadings": "场景标题",
+ "bold": "加粗",
+ "boldDesc": "场景标题将以粗体显示",
+ "extraSpace": "上方额外空间",
+ "extraSpaceDesc": "在场景标题前添加额外间距",
+ "sceneNumbering": "场景编号",
+ "sceneNumberingDesc": "在左边距显示场景编号",
+ "duplicateRight": "在右边距重复",
+ "duplicateRightDesc": "两侧均显示编号",
+ "continuation": "续接",
+ "moreTitle": "应用对话续接标签(页面底部)",
+ "contdTitle": "应用角色续接标签(页面顶部)"
+ },
+ "projectSettings": {
+ "titleLabel": "标题",
+ "titlePlaceholder": "输入项目名称...",
+ "authorLabel": "作者",
+ "authorPlaceholder": "作者姓名...",
+ "descriptionLabel": "描述",
+ "descriptionPlaceholder": "这个剧本讲的是什么?",
+ "posterLabel": "海报",
+ "noPoster": "无海报",
+ "posterHelp": "推荐尺寸:600x900 像素(2:3 比例),支持 PNG、JPG。",
+ "saveChanges": "保存更改",
+ "dangerZoneTitle": "危险区域"
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index a228856..c7643aa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -45,6 +45,7 @@
"jspdf": "^4.2.0",
"lucide-react": "^0.562.0",
"next": "^16.1.6",
+ "next-intl": "^4.8.3",
"next-themes": "^0.2.0",
"nodemailer": "^7.0.12",
"patch-package": "^8.0.1",
@@ -3663,9 +3664,9 @@
}
},
"node_modules/@floating-ui/core": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
- "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz",
+ "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -3723,6 +3724,58 @@
"integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==",
"license": "MIT"
},
+ "node_modules/@formatjs/ecma402-abstract": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-3.1.1.tgz",
+ "integrity": "sha512-jhZbTwda+2tcNrs4kKvxrPLPjx8QsBCLCUgrrJ/S+G9YrGHWLhAyFMMBHJBnBoOwuLHd7L14FgYudviKaxkO2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/fast-memoize": "3.1.0",
+ "@formatjs/intl-localematcher": "0.8.1",
+ "decimal.js": "^10.6.0",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@formatjs/fast-memoize": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.0.tgz",
+ "integrity": "sha512-b5mvSWCI+XVKiz5WhnBCY3RJ4ZwfjAidU0yVlKa3d3MSgKmH1hC3tBGEAtYyN5mqL7N0G5x0BOUYyO8CEupWgg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@formatjs/icu-messageformat-parser": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.1.tgz",
+ "integrity": "sha512-sSDmSvmmoVQ92XqWb499KrIhv/vLisJU8ITFrx7T7NZHUmMY7EL9xgRowAosaljhqnj/5iufG24QrdzB6X3ItA==",
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "3.1.1",
+ "@formatjs/icu-skeleton-parser": "2.1.1",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@formatjs/icu-skeleton-parser": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.1.tgz",
+ "integrity": "sha512-PSFABlcNefjI6yyk8f7nyX1DC7NHmq6WaCHZLySEXBrXuLOB2f935YsnzuPjlz+ibhb9yWTdPeVX1OVcj24w2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "3.1.1",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@formatjs/intl-localematcher": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.1.tgz",
+ "integrity": "sha512-xwEuwQFdtSq1UKtQnyTZWC+eHdv7Uygoa+H2k/9uzBVQjDyp9r20LNDNKedWXll7FssT3GRHvqsdJGYSUWqYFA==",
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/fast-memoize": "3.1.0",
+ "tslib": "^2.8.1"
+ }
+ },
"node_modules/@formkit/auto-animate": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.7.0.tgz",
@@ -5227,10 +5280,8 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
- "dev": true,
"hasInstallScript": true,
"license": "MIT",
- "optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
@@ -5267,7 +5318,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5288,7 +5338,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5309,7 +5358,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5330,7 +5378,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5351,7 +5398,6 @@
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5372,7 +5418,6 @@
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5393,7 +5438,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5414,7 +5458,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5435,7 +5478,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5456,7 +5498,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5477,7 +5518,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5498,7 +5538,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5519,7 +5558,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -5537,9 +5575,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
- "dev": true,
"license": "Apache-2.0",
- "optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
@@ -5551,9 +5587,7 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
- "dev": true,
- "license": "MIT",
- "optional": true
+ "license": "MIT"
},
"node_modules/@phc/format": {
"version": "1.0.0",
@@ -5704,6 +5738,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@schummar/icu-type-parser": {
+ "version": "1.21.5",
+ "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz",
+ "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==",
+ "license": "MIT"
+ },
"node_modules/@sindresorhus/is": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz",
@@ -6725,6 +6765,172 @@
"url": "https://github.com/sponsors/gregberge"
}
},
+ "node_modules/@swc/core-darwin-arm64": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.13.tgz",
+ "integrity": "sha512-ztXusRuC5NV2w+a6pDhX13CGioMLq8CjX5P4XgVJ21ocqz9t19288Do0y8LklplDtwcEhYGTNdMbkmUT7+lDTg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-darwin-x64": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.13.tgz",
+ "integrity": "sha512-cVifxQUKhaE7qcO/y9Mq6PEhoyvN9tSLzCnnFZ4EIabFHBuLtDDO6a+vLveOy98hAs5Qu1+bb5Nv0oa1Pihe3Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm-gnueabihf": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.13.tgz",
+ "integrity": "sha512-t+xxEzZ48enl/wGGy7SRYd7kImWQ/+wvVFD7g5JZo234g6/QnIgZ+YdfIyjHB+ZJI3F7a2IQHS7RNjxF29UkWw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-gnu": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.13.tgz",
+ "integrity": "sha512-VndeGvKmTXFn6AGwjy0Kg8i7HccOCE7Jt/vmZwRxGtOfNZM1RLYRQ7MfDLo6T0h1Bq6eYzps3L5Ma4zBmjOnOg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-musl": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.13.tgz",
+ "integrity": "sha512-SmZ9m+XqCB35NddHCctvHFLqPZDAs5j8IgD36GoutufDJmeq2VNfgk5rQoqNqKmAK3Y7iFdEmI76QoHIWiCLyw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-gnu": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.13.tgz",
+ "integrity": "sha512-5rij+vB9a29aNkHq72EXI2ihDZPszJb4zlApJY4aCC/q6utgqFA6CkrfTfIb+O8hxtG3zP5KERETz8mfFK6A0A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-musl": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.13.tgz",
+ "integrity": "sha512-OlSlaOK9JplQ5qn07WiBLibkOw7iml2++ojEXhhR3rbWrNEKCD7sd8+6wSavsInyFdw4PhLA+Hy6YyDBIE23Yw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-arm64-msvc": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.13.tgz",
+ "integrity": "sha512-zwQii5YVdsfG8Ti9gIKgBKZg8qMkRZxl+OlYWUT5D93Jl4NuNBRausP20tfEkQdAPSRrMCSUZBM6FhW7izAZRg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-ia32-msvc": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.13.tgz",
+ "integrity": "sha512-hYXvyVVntqRlYoAIDwNzkS3tL2ijP3rxyWQMNKaxcCxxkCDto/w3meOK/OB6rbQSkNw0qTUcBfU9k+T0ptYdfQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-x64-msvc": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.13.tgz",
+ "integrity": "sha512-XTzKs7c/vYCcjmcwawnQvlHHNS1naJEAzcBckMI5OJlnrcgW8UtcX9NHFYvNjGtXuKv0/9KvqL4fuahdvlNGKw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
+ "license": "Apache-2.0"
+ },
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@@ -6734,6 +6940,15 @@
"tslib": "^2.8.0"
}
},
+ "node_modules/@swc/types": {
+ "version": "0.1.25",
+ "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz",
+ "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/counter": "^0.1.3"
+ }
+ },
"node_modules/@tauri-apps/api": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz",
@@ -9277,6 +9492,12 @@
}
}
},
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "license": "MIT"
+ },
"node_modules/deep-equal": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz",
@@ -11261,6 +11482,21 @@
"postcss": "^8.1.0"
}
},
+ "node_modules/icu-minify": {
+ "version": "4.8.3",
+ "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.8.3.tgz",
+ "integrity": "sha512-65Av7FLosNk7bPbmQx5z5XG2Y3T2GFppcjiXh4z1idHeVgQxlDpAmkGoYI0eFzAvrOnjpWTL5FmPDhsdfRMPEA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/amannn"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/icu-messageformat-parser": "^3.4.0"
+ }
+ },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -11413,6 +11649,18 @@
"node": ">= 0.4"
}
},
+ "node_modules/intl-messageformat": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.1.2.tgz",
+ "integrity": "sha512-ucSrQmZGAxfiBHfBRXW/k7UC8MaGFlEj4Ry1tKiDcmgwQm1y3EDl40u+4VNHYomxJQMJi9NEI3riDRlth96jKg==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "3.1.1",
+ "@formatjs/fast-memoize": "3.1.0",
+ "@formatjs/icu-messageformat-parser": "3.5.1",
+ "tslib": "^2.8.1"
+ }
+ },
"node_modules/iobuffer": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
@@ -11641,7 +11889,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -11687,7 +11934,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -12823,6 +13069,15 @@
"node": ">= 4.4.x"
}
},
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/next": {
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz",
@@ -12877,6 +13132,82 @@
}
}
},
+ "node_modules/next-intl": {
+ "version": "4.8.3",
+ "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.8.3.tgz",
+ "integrity": "sha512-PvdBDWg+Leh7BR7GJUQbCDVVaBRn37GwDBWc9sv0rVQOJDQ5JU1rVzx9EEGuOGYo0DHAl70++9LQ7HxTawdL7w==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/amannn"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/intl-localematcher": "^0.8.1",
+ "@parcel/watcher": "^2.4.1",
+ "@swc/core": "^1.15.2",
+ "icu-minify": "^4.8.3",
+ "negotiator": "^1.0.0",
+ "next-intl-swc-plugin-extractor": "^4.8.3",
+ "po-parser": "^2.1.1",
+ "use-intl": "^4.8.3"
+ },
+ "peerDependencies": {
+ "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0",
+ "typescript": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next-intl-swc-plugin-extractor": {
+ "version": "4.8.3",
+ "resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.8.3.tgz",
+ "integrity": "sha512-YcaT+R9z69XkGhpDarVFWUprrCMbxgIQYPUaXoE6LGVnLjGdo8hu3gL6bramDVjNKViYY8a/pXPy7Bna0mXORg==",
+ "license": "MIT"
+ },
+ "node_modules/next-intl/node_modules/@swc/core": {
+ "version": "1.15.13",
+ "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.13.tgz",
+ "integrity": "sha512-0l1gl/72PErwUZuavcRpRAQN9uSst+Nk++niC5IX6lmMWpXoScYx3oq/narT64/sKv/eRiPTaAjBFGDEQiWJIw==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/counter": "^0.1.3",
+ "@swc/types": "^0.1.25"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/swc"
+ },
+ "optionalDependencies": {
+ "@swc/core-darwin-arm64": "1.15.13",
+ "@swc/core-darwin-x64": "1.15.13",
+ "@swc/core-linux-arm-gnueabihf": "1.15.13",
+ "@swc/core-linux-arm64-gnu": "1.15.13",
+ "@swc/core-linux-arm64-musl": "1.15.13",
+ "@swc/core-linux-x64-gnu": "1.15.13",
+ "@swc/core-linux-x64-musl": "1.15.13",
+ "@swc/core-win32-arm64-msvc": "1.15.13",
+ "@swc/core-win32-ia32-msvc": "1.15.13",
+ "@swc/core-win32-x64-msvc": "1.15.13"
+ },
+ "peerDependencies": {
+ "@swc/helpers": ">=0.5.17"
+ },
+ "peerDependenciesMeta": {
+ "@swc/helpers": {
+ "optional": true
+ }
+ }
+ },
"node_modules/next-themes": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz",
@@ -13634,6 +13965,12 @@
"node": ">=14.19.0"
}
},
+ "node_modules/po-parser": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.1.1.tgz",
+ "integrity": "sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==",
+ "license": "MIT"
+ },
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@@ -15802,6 +16139,27 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-intl": {
+ "version": "4.8.3",
+ "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.8.3.tgz",
+ "integrity": "sha512-nLxlC/RH+le6g3amA508Itnn/00mE+J22ui21QhOWo5V9hCEC43+WtnRAITbJW0ztVZphev5X9gvOf2/Dk9PLA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/amannn"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/fast-memoize": "^3.1.0",
+ "@schummar/icu-type-parser": "1.21.5",
+ "icu-minify": "^4.8.3",
+ "intl-messageformat": "^11.1.0"
+ },
+ "peerDependencies": {
+ "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
+ }
+ },
"node_modules/use-sync-external-store": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
diff --git a/package.json b/package.json
index 7b80acf..797386f 100644
--- a/package.json
+++ b/package.json
@@ -58,6 +58,7 @@
"jspdf": "^4.2.0",
"lucide-react": "^0.562.0",
"next": "^16.1.6",
+ "next-intl": "^4.8.3",
"next-themes": "^0.2.0",
"nodemailer": "^7.0.12",
"patch-package": "^8.0.1",
diff --git a/src/app/api/users/settings/route.ts b/src/app/api/users/settings/route.ts
index 157de3e..6d23acd 100644
--- a/src/app/api/users/settings/route.ts
+++ b/src/app/api/users/settings/route.ts
@@ -17,7 +17,7 @@ import { NextRequest } from "next/server";
const UpdateSettingsBodySchema = z.object({
keybinds: z.record(z.string(), z.string()).optional(),
theme: z.enum(["light", "dark", "latte", "wonka", "mint", "blossom"]).optional(),
- language: z.literal("en").optional(),
+ language: z.enum(["en", "es", "fr", "zh", "ko", "ja", "de", "pl"]).optional(),
themedEditor: z.boolean().optional(),
});
diff --git a/src/app/providers.tsx b/src/app/providers.tsx
index 07a4f75..1f431c0 100644
--- a/src/app/providers.tsx
+++ b/src/app/providers.tsx
@@ -2,9 +2,11 @@
import { ReactNode, useEffect } from "react";
import { ThemeProvider } from "next-themes";
+import { NextIntlClientProvider } from "next-intl";
import { SWRConfig } from "swr";
import { UserContextProvider } from "@src/context/UserContext";
import { DashboardContextProvider } from "@src/context/DashboardContext";
+import { LocaleContextProvider, useLocale } from "@src/context/LocaleContext";
import { useSettings } from "@src/lib/utils/hooks";
import fetcher from "@src/lib/fetcher";
@@ -24,6 +26,31 @@ function EditorThemeSync() {
return null;
}
+/**
+ * Reads locale/messages from LocaleContext and feeds NextIntlClientProvider.
+ * Needed because useLocale() must be called inside LocaleContextProvider.
+ */
+function IntlBridge({ children }: { children: ReactNode }) {
+ const { locale, messages } = useLocale();
+ return (
+
+ {children}
+
+ );
+}
+
+/**
+ * Syncs attribute to the active locale.
+ * Mirrors the EditorThemeSync pattern.
+ */
+function LocaleSync() {
+ const { locale } = useLocale();
+ useEffect(() => {
+ document.documentElement.lang = locale;
+ }, [locale]);
+ return null;
+}
+
export function Providers({ children }: { children: ReactNode }) {
return (
-
-
- {children}
-
+
+
+
+
+
+ {children}
+
+
+
diff --git a/src/context/LocaleContext.tsx b/src/context/LocaleContext.tsx
new file mode 100644
index 0000000..e505eaf
--- /dev/null
+++ b/src/context/LocaleContext.tsx
@@ -0,0 +1,72 @@
+"use client";
+
+import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from "react";
+import { UserLanguage } from "@src/lib/utils/types";
+import { useSettings } from "@src/lib/utils/hooks";
+import { editUserSettings } from "@src/lib/utils/requests";
+
+import enMessages from "../../messages/en.json";
+import esMessages from "../../messages/es.json";
+import frMessages from "../../messages/fr.json";
+import zhMessages from "../../messages/zh.json";
+import koMessages from "../../messages/ko.json";
+import jaMessages from "../../messages/ja.json";
+import deMessages from "../../messages/de.json";
+import plMessages from "../../messages/pl.json";
+
+const LOCALE_KEY = "scriptio-locale";
+const DEFAULT_LOCALE: UserLanguage = "en";
+const MESSAGES: Record
= { en: enMessages, es: esMessages, fr: frMessages, zh: zhMessages, ko: koMessages, ja: jaMessages, de: deMessages, pl: plMessages };
+
+interface LocaleContextValue {
+ locale: UserLanguage;
+ messages: typeof enMessages;
+ setLanguage: (lang: UserLanguage) => void;
+}
+
+const LocaleContext = createContext({
+ locale: DEFAULT_LOCALE,
+ messages: enMessages,
+ setLanguage: () => {},
+});
+
+export function LocaleContextProvider({ children }: { children: ReactNode }) {
+ const [locale, setLocale] = useState(() => {
+ if (typeof window === "undefined") return DEFAULT_LOCALE;
+ const stored = window.localStorage.getItem(LOCALE_KEY) as UserLanguage | null;
+ return stored && stored in MESSAGES ? stored : DEFAULT_LOCALE;
+ });
+
+ const { settings, mutate } = useSettings();
+
+ // Server-wins sync: if settings differ from local state (e.g. changed on another device)
+ useEffect(() => {
+ if (settings?.language && settings.language !== locale) {
+ setLocale(settings.language);
+ window.localStorage.setItem(LOCALE_KEY, settings.language);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [settings?.language]);
+
+ const setLanguage = useCallback(
+ (lang: UserLanguage) => {
+ if (lang === locale) return;
+ setLocale(lang);
+ window.localStorage.setItem(LOCALE_KEY, lang);
+ if (settings) mutate({ ...settings, language: lang }, false);
+ editUserSettings({ language: lang });
+ },
+ [locale, settings, mutate],
+ );
+
+ const value = useMemo(
+ () => ({ locale, messages: MESSAGES[locale], setLanguage }),
+ [locale, setLanguage],
+ );
+
+ return {children} ;
+}
+
+export function useLocale() {
+ return useContext(LocaleContext);
+}
diff --git a/src/lib/utils/types.ts b/src/lib/utils/types.ts
index 4903721..33a594b 100644
--- a/src/lib/utils/types.ts
+++ b/src/lib/utils/types.ts
@@ -38,7 +38,7 @@ export interface UserSettings {
themedEditor: boolean;
}
-export type UserLanguage = "en";
+export type UserLanguage = "en" | "es" | "fr" | "zh" | "ko" | "ja" | "de" | "pl";
export type UserTheme = "light" | "dark" | "latte" | "wonka" | "mint" | "blossom";
export interface UserOnlineSettings {
diff --git a/styles/scriptio.css b/styles/scriptio.css
index fbbdd46..af1584c 100644
--- a/styles/scriptio.css
+++ b/styles/scriptio.css
@@ -94,10 +94,6 @@
}
/* Screenplay elements */
- .is-empty > br.ProseMirror-trailingBreak {
- display: none;
- }
-
.is-empty {
cursor: text;
background-color: transparent; /* Catches the click */
@@ -210,6 +206,21 @@
content: ")" !important;
}
+ /* When empty, creates a second line box that pushes ) to the next line.
+ Clip it with max-height (overflow: hidden already set on .is-empty),
+ and merge both parens into ::before so () appears together on the first line. */
+ .parenthetical.is-empty {
+ max-height: 1lh;
+ }
+
+ .parenthetical.is-empty::before {
+ content: "()" !important;
+ }
+
+ .parenthetical.is-empty::after {
+ content: "" !important;
+ }
+
/* Transition */
.transition {
position: relative;