From b5890dd1891c37357aece026897cf1b449382e95 Mon Sep 17 00:00:00 2001 From: Hugo Bois Date: Fri, 27 Feb 2026 01:24:09 +0100 Subject: [PATCH] added translations, fixed orphan-prevention extension --- components/board/BoardCanvas.tsx | 16 ++--- components/board/BoardCard.tsx | 8 ++- components/editor/sidebar/ContextMenu.tsx | 32 +++++----- components/navbar/ProjectNavbar.module.css | 26 +++++++-- components/navbar/ProjectNavbar.tsx | 32 +++++----- components/navbar/dropdown/DropdownItem.tsx | 2 +- components/popup/PopupCharacterItem.tsx | 38 ++++++------ components/popup/PopupImportFile.tsx | 12 ++-- components/popup/PopupSceneItem.tsx | 14 +++-- messages/de.json | 58 +++++++++++++++++++ messages/en.json | 58 +++++++++++++++++++ messages/es.json | 58 +++++++++++++++++++ messages/fr.json | 58 +++++++++++++++++++ messages/ja.json | 58 +++++++++++++++++++ messages/ko.json | 58 +++++++++++++++++++ messages/pl.json | 58 +++++++++++++++++++ messages/zh.json | 58 +++++++++++++++++++ .../extensions/orphan-prevention-extension.ts | 11 ++-- 18 files changed, 578 insertions(+), 77 deletions(-) diff --git a/components/board/BoardCanvas.tsx b/components/board/BoardCanvas.tsx index cf7dd8b5..f707bef4 100644 --- a/components/board/BoardCanvas.tsx +++ b/components/board/BoardCanvas.tsx @@ -7,6 +7,7 @@ import BoardCard, { BoardCardData } from "./BoardCard"; import styles from "./BoardCanvas.module.css"; import { v4 as uuidv4 } from "uuid"; import { Trash2, Plus, Minus, Copy } from "lucide-react"; +import { useTranslations } from "next-intl"; const GRID_SIZE = 20; const MIN_SCALE = 0.25; @@ -42,6 +43,7 @@ interface ArrowContextMenuState { const BoardCanvas = ({ isVisible }: { isVisible: boolean }) => { const { repository, isYjsReady } = useContext(ProjectContext); + const t = useTranslations("board"); const ydoc = repository?.getState(); const containerRef = useRef(null); const canvasRef = useRef(null); @@ -928,14 +930,14 @@ const BoardCanvas = ({ isVisible }: { isVisible: boolean }) => { onClick={() => handleDuplicateCard(cardContextMenu.card)} > -

Duplicate

+

{t("duplicate")}

handleDeleteCard(cardContextMenu.card.id)} > -

Delete

+

{t("delete")}

)} @@ -954,7 +956,7 @@ const BoardCanvas = ({ isVisible }: { isVisible: boolean }) => { onClick={() => handleDeleteArrow(arrowContextMenu.arrow.id)} > -

Delete

+

{t("delete")}

)} @@ -970,10 +972,10 @@ const BoardCanvas = ({ isVisible }: { isVisible: boolean }) => {
- Middle-click to pan - Drag to select cards - Double-click to create card - Hold Shift to move freely + {t("hints.pan")} + {t("hints.select")} + {t("hints.create")} + {t("hints.move")}
diff --git a/components/board/BoardCard.tsx b/components/board/BoardCard.tsx index 946adf27..ee25f0ee 100644 --- a/components/board/BoardCard.tsx +++ b/components/board/BoardCard.tsx @@ -2,6 +2,7 @@ import { useRef, useState, useCallback, useEffect } from "react"; import styles from "./BoardCanvas.module.css"; +import { useTranslations } from "next-intl"; export interface BoardCardData { id: string; @@ -39,6 +40,7 @@ const BoardCard = ({ isConnecting, isSelected, }: BoardCardProps) => { + const t = useTranslations("board"); const cardRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [isEditing, setIsEditing] = useState(false); @@ -263,12 +265,12 @@ const BoardCard = ({ onKeyDown={handleTitleKeyDown} onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} - placeholder="Title" + placeholder={t("titlePlaceholder")} autoFocus /> ) : ( - {card.title || "Untitled"} + {card.title || t("untitled")} )} @@ -290,7 +292,7 @@ const BoardCard = ({ onKeyDown={handleDescriptionKeyDown} onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} - placeholder="Description" + placeholder={t("descriptionPlaceholder")} autoFocus /> )} diff --git a/components/editor/sidebar/ContextMenu.tsx b/components/editor/sidebar/ContextMenu.tsx index 3cdb28e8..35b313cc 100644 --- a/components/editor/sidebar/ContextMenu.tsx +++ b/components/editor/sidebar/ContextMenu.tsx @@ -11,6 +11,7 @@ import { copyText, cutText, focusOnPosition, pasteText, selectTextInEditor } fro import { addCharacterPopup, editCharacterPopup, editScenePopup } from "@src/lib/screenplay/popup"; import { ProjectContext } from "@src/context/ProjectContext"; import { useUser } from "@src/lib/utils/hooks"; +import { useTranslations } from "next-intl"; import { ArrowDownRight, ClipboardPaste, @@ -69,6 +70,7 @@ export type SceneContextProps = { }; const SceneItemMenu = (props: any) => { + const t = useTranslations("contextMenu"); const userCtx = useContext(UserContext); const { editor } = useContext(ProjectContext); const scene: Scene = props.props.scene; @@ -76,18 +78,18 @@ const SceneItemMenu = (props: any) => { return ( <> focusOnPosition(editor!, scene.position)} /> - editScenePopup(scene, userCtx)} /> + editScenePopup(scene, userCtx)} /> cutText(editor!, scene.position, scene.nextPosition)} /> selectTextInEditor(editor!, scene.position, scene.nextPosition)} /> @@ -115,6 +117,7 @@ export type CharacterContextProps = { }; const CharacterItemMenu = (props: any) => { + const t = useTranslations("contextMenu"); const userCtx = useContext(UserContext); const projectCtx = useContext(ProjectContext); const { toggleCharacterHighlight } = projectCtx; @@ -122,15 +125,15 @@ const CharacterItemMenu = (props: any) => { return ( <> - editCharacterPopup(character, userCtx)} /> - deleteCharacter(character.name, projectCtx)} /> + editCharacterPopup(character, userCtx)} /> + deleteCharacter(character.name, projectCtx)} /> pasteText(projectCtx.editor!, character.name)} /> toggleCharacterHighlight(character.name)} /> @@ -139,8 +142,9 @@ const CharacterItemMenu = (props: any) => { }; const CharacterListMenu = (props: any) => { + const t = useTranslations("contextMenu"); const userCtx = useContext(UserContext); - return addCharacterPopup(userCtx)} />; + return addCharacterPopup(userCtx)} />; }; /* ======================== */ @@ -152,15 +156,16 @@ export type LocationContextProps = { }; const LocationItemMenu = (props: any) => { + const t = useTranslations("contextMenu"); const projectCtx = useContext(ProjectContext); const location: LocationData = props.props.location; return ( <> - deleteLocation(location.name, projectCtx)} /> + deleteLocation(location.name, projectCtx)} /> pasteText(projectCtx.editor!, location.name)} /> @@ -177,6 +182,7 @@ export type EditorSelectionContextProps = { }; const EditorSelectionMenu = (props: any) => { + const t = useTranslations("contextMenu"); const projectCtx = useContext(ProjectContext); const { repository, editor, setActiveCommentId } = projectCtx; const { from, to } = props.props as EditorSelectionContextProps; @@ -207,8 +213,8 @@ const EditorSelectionMenu = (props: any) => { return ( <> - - + + ); }; diff --git a/components/navbar/ProjectNavbar.module.css b/components/navbar/ProjectNavbar.module.css index 02442a1f..58544169 100644 --- a/components/navbar/ProjectNavbar.module.css +++ b/components/navbar/ProjectNavbar.module.css @@ -144,16 +144,35 @@ background-color: var(--secondary-hover); } +.title_wrapper { + display: inline-grid; + align-items: center; +} + +.title_wrapper::after { + content: attr(data-value) " "; + visibility: hidden; + white-space: pre; + grid-area: 1 / 1; + font-size: 0.9rem; + font-family: inherit; + letter-spacing: inherit; + padding: 4px 8px; +} + .title_box { border: none; background: none; font-size: 0.9rem; + font-family: inherit; + letter-spacing: inherit; border-radius: 6px; padding: 4px 8px; color: var(--primary-text); - width: auto; - max-width: 200px; - padding-right: 8px; + box-sizing: border-box; + grid-area: 1 / 1; + width: 100%; + min-width: 0; } .title_box:focus { @@ -165,7 +184,6 @@ background-color: var(--secondary-hover); } - .notlogged_btns { display: flex; flex-direction: row; diff --git a/components/navbar/ProjectNavbar.tsx b/components/navbar/ProjectNavbar.tsx index 718099ac..34aa5b4e 100644 --- a/components/navbar/ProjectNavbar.tsx +++ b/components/navbar/ProjectNavbar.tsx @@ -217,21 +217,23 @@ const ProjectNavbar = () => {
- { - isLocalEdit.current = true; - setProjectTitle(e.target.value); - setContextTitle(e.target.value); - deferredTitleUpdate(projectId, e.target.value); - }} - onBlur={() => { - isLocalEdit.current = false; - }} - value={projectTitle} - /> +
+ { + isLocalEdit.current = true; + setProjectTitle(e.target.value); + setContextTitle(e.target.value); + deferredTitleUpdate(projectId, e.target.value); + }} + onBlur={() => { + isLocalEdit.current = false; + }} + value={projectTitle} + /> +
{(hasScreenplay || hasTitlePage) && (
diff --git a/components/navbar/dropdown/DropdownItem.tsx b/components/navbar/dropdown/DropdownItem.tsx index 89deba0a..b394c4b3 100644 --- a/components/navbar/dropdown/DropdownItem.tsx +++ b/components/navbar/dropdown/DropdownItem.tsx @@ -1,6 +1,6 @@ "use client"; -import { ForwardedRef, MutableRefObject, RefObject, forwardRef } from "react"; +import { ForwardedRef, forwardRef } from "react"; import dropdown from "./DropdownItem.module.css"; type Props = { diff --git a/components/popup/PopupCharacterItem.tsx b/components/popup/PopupCharacterItem.tsx index dc3345ae..57e9343f 100644 --- a/components/popup/PopupCharacterItem.tsx +++ b/components/popup/PopupCharacterItem.tsx @@ -16,6 +16,7 @@ import { UserContext } from "@src/context/UserContext"; import { PopupCharacterData, PopupData, PopupType, closePopup } from "@src/lib/screenplay/popup"; import { countOccurrences } from "@src/lib/screenplay/screenplay"; import { ColorPicker } from "@components/utils/ColorPicker"; +import { useTranslations } from "next-intl"; import CloseSVG from "@public/images/close.svg"; @@ -30,33 +31,32 @@ type NewNameWarningProps = { nameOccurrences: number; oldName: string; newName: string; + t: any; }; const NewNameWarning = (props: NewNameWarningProps) => { return (

- Are you sure you want to update {props.nameOccurrences} occurrences of word {props.oldName} to{" "} - {props.newName}? Take extra care of common words whose update might be unwated. + {props.t("updateOccurrences", { count: props.nameOccurrences, oldName: props.oldName, newName: props.newName })}

); }; -const TakenNameError = (newName: string) => { +const TakenNameError = (newName: string, t: any) => { return (

- A character with the name {newName} already exists. Please choose a different name or edit the existing - character instead. + {t("takenNameError", { newName })}

); @@ -66,6 +66,7 @@ export const PopupCharacterItem = ({ type, data: { character } }: PopupData(false); const [takenNameError, setTakenNameError] = useState(false); @@ -168,7 +169,7 @@ export const PopupCharacterItem = ({ type, data: { character } }: PopupData closePopup(userCtx)} alt="Close icon" />
- {takenNameError && TakenNameError(newName)} + {takenNameError && TakenNameError(newName, t)} {newNameWarning && NewNameWarning({ setNewNameWarning, @@ -205,10 +206,11 @@ export const PopupCharacterItem = ({ type, data: { character } }: PopupData
-

Name

+

{t("name")}

-

Gender

+

{t("gender")}


-

Color

+

{t("color")}


-

Synopsis

+

{t("synopsis")}