diff --git a/.claude/skills/translate-changes/LANGUAGE_CODES.md b/.claude/skills/translate-changes/LANGUAGE_CODES.md index 2fee752d1..4d67714a0 100644 --- a/.claude/skills/translate-changes/LANGUAGE_CODES.md +++ b/.claude/skills/translate-changes/LANGUAGE_CODES.md @@ -2,15 +2,15 @@ ## Supported Languages -| Language | File | Code | Notes | -|----------|------|------|-------| -| English (source) | en.json | en | Source of truth for all translations | -| German | de.json | de | Formal "Sie" form | -| Spanish | es.json | es | Latin American Spanish | -| French | fr.json | fr | Standard French | -| Italian | it.json | it | Standard Italian | -| Russian | ru.json | ru | Includes ICU plural rules | -| Ukrainian | uk.json | uk | Includes ICU plural rules | +| Language | File | Code | Notes | +| ---------------- | ------- | ---- | ------------------------------------ | +| English (source) | en.json | en | Source of truth for all translations | +| German | de.json | de | Formal "Sie" form | +| Spanish | es.json | es | Latin American Spanish | +| French | fr.json | fr | Standard French | +| Italian | it.json | it | Standard Italian | +| Russian | ru.json | ru | Includes ICU plural rules | +| Ukrainian | uk.json | uk | Includes ICU plural rules | ## File Locations @@ -37,6 +37,7 @@ Each language file maintains the same nested key structure as `en.json`: ### HTML Tags Many strings contain HTML for links or formatting: + ```json "signedOut": "You must sign in before..." ``` @@ -46,6 +47,7 @@ Translations MUST preserve the HTML structure exactly. ### Variable Placeholders Strings may contain variables in curly braces: + - `{href}` - URLs - `{n}` - Numbers - `{name}` - Names or identifiers @@ -61,6 +63,7 @@ Some strings use ICU MessageFormat for pluralization: ``` Russian and Ukrainian have complex plural rules: + ```json "labelSelected": "{n, plural, one {# документ} few {# документа} many {# документов} other {# документов}}" ``` @@ -68,30 +71,36 @@ Russian and Ukrainian have complex plural rules: ## Translation Style Guidelines ### German (de.json) + - Use formal "Sie" form - Compound nouns are common - Example: "anmelden" (sign in), "ausführen" (execute) ### Spanish (es.json) + - Latin American Spanish preferred - Use "usted" form (formal) - Example: "iniciar sesión" (sign in), "ejecutar" (execute) ### French (fr.json) + - Use "vous" form (formal) - Elision rules apply (l' before vowels) - Example: "se connecter" (sign in), "exécuter" (execute) ### Italian (it.json) + - Use "Lei" form (formal in some contexts) or informal based on existing patterns - Example: "accedere" (sign in), "eseguire" (execute) ### Russian (ru.json) + - Cyrillic alphabet - Complex case and gender agreement - Example: "войти" (sign in), "запустить" (execute) ### Ukrainian (uk.json) + - Cyrillic alphabet (different from Russian) - Similar grammar to Russian but distinct vocabulary - Example: "увійти" (sign in), "запустити" (execute) @@ -99,6 +108,7 @@ Russian and Ukrainian have complex plural rules: ## Encoding All JSON files use Unicode escape sequences for non-ASCII characters: + - `é` becomes `\u00e9` - `ñ` becomes `\u00f1` - Cyrillic characters are also escaped @@ -108,6 +118,7 @@ The Edit tool handles this automatically - no manual encoding needed. ## Verification Commands ### Check a specific key across all languages + ```bash for file in src/langs/json/{de,es,fr,it,ru,uk}.json; do echo "=== $file ===" @@ -116,6 +127,7 @@ done ``` ### Validate JSON syntax + ```bash for file in src/langs/json/*.json; do echo "Validating $file..." @@ -124,6 +136,7 @@ done ``` ### Compare keys between en.json and another language + ```bash diff \ <(jq -r 'keys | .[]' src/langs/json/en.json) \ @@ -133,15 +146,18 @@ diff \ ## Common Issues ### Missing keys + - Only translate MODIFIED keys, not missing ones - Many existing keys are intentionally untranslated ### Formatting + - JSON must remain valid after edits - Maintain consistent indentation (2 spaces) - Commas must be correct (no trailing comma on last item) ### Context + - Consider the full key path for context - Dialog buttons differ from page headers - Form labels differ from help text diff --git a/.claude/skills/translate-changes/SKILL.md b/.claude/skills/translate-changes/SKILL.md index 140fbbd7c..82f3e5af8 100644 --- a/.claude/skills/translate-changes/SKILL.md +++ b/.claude/skills/translate-changes/SKILL.md @@ -63,18 +63,21 @@ This captures both uncommitted modifications and changes committed in the curren ### Step 2: Parse changes Look for lines starting with `+` in the diff that contain JSON keys: + - Lines like `+ "keyName": "Value"` indicate additions or modifications - Ignore lines that are just structural changes (commas, brackets) ### Step 3: Extract key paths For each modified line, determine: + - The full JSON path (e.g., `addonDispatchDialog.signedOut`) - The new English value ### Step 4: Translate For each modified key: + 1. Generate translations for all 6 languages 2. Maintain the same HTML structure (e.g., `text`) 3. Preserve placeholders like `{href}`, `{n}`, etc. @@ -83,6 +86,7 @@ For each modified key: ### Step 5: Update files For each language file: + 1. Read the current content 2. Locate the key to update using the JSON path 3. Use Edit tool to update the specific key @@ -99,11 +103,13 @@ For each language file: ## Example Translations English: + ```json "signedOut": "You must sign in before dispatching this add-on." ``` Translations should maintain: + - The `` structure - The `{href}` placeholder - Appropriate word order for each language @@ -111,6 +117,7 @@ Translations should maintain: ## Output Format After translating, show: + 1. Summary of changes detected 2. List of keys modified 3. Confirmation of files updated @@ -119,6 +126,7 @@ After translating, show: ## Verification After updates, verify with: + ```bash for file in src/langs/json/{de,es,fr,it,ru,uk}.json; do echo "=== $file ===" @@ -131,4 +139,4 @@ done - JSON files use Unicode escape sequences (e.g., `\u00e9` for é) - The Edit tool will preserve this encoding automatically - Always use Edit tool, not Bash sed/awk, to maintain JSON validity -- Files should remain valid JSON after updates \ No newline at end of file +- Files should remain valid JSON after updates diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..8fdd954df --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 \ No newline at end of file diff --git a/src/lib/components/accounts/Mailkey.svelte b/src/lib/components/accounts/Mailkey.svelte index c032c5d04..24dae73d6 100644 --- a/src/lib/components/accounts/Mailkey.svelte +++ b/src/lib/components/accounts/Mailkey.svelte @@ -55,20 +55,22 @@

{$_("mailkey.title")}

-
{@html $_("mailkey.description")}
- {#if message} -

- {@html message} -

- {/if} - - - - +
diff --git a/src/lib/components/forms/ConfirmRedaction.svelte b/src/lib/components/forms/ConfirmRedaction.svelte index 54a4e81d6..1f27a3a38 100644 --- a/src/lib/components/forms/ConfirmRedaction.svelte +++ b/src/lib/components/forms/ConfirmRedaction.svelte @@ -59,7 +59,7 @@ This almost certainly lives in a modal. } -
+

{$_("redact.really")}

diff --git a/src/lib/components/forms/DeleteProject.svelte b/src/lib/components/forms/DeleteProject.svelte index 829bff000..dd9538905 100644 --- a/src/lib/components/forms/DeleteProject.svelte +++ b/src/lib/components/forms/DeleteProject.svelte @@ -20,7 +20,7 @@ Confirm project deletion. $: action = new URL("?/delete", canonicalUrl(project)).href; - +

{$_("projects.delete.really", { values: { project: project.title } })}

@@ -36,9 +36,6 @@ Confirm project deletion. diff --git a/src/lib/components/forms/Edit.svelte b/src/lib/components/forms/Edit.svelte index b527da9de..1f8935565 100644 --- a/src/lib/components/forms/Edit.svelte +++ b/src/lib/components/forms/Edit.svelte @@ -63,7 +63,7 @@ Usually this will be rendered inside a modal, but it doesn't have to be. } - + @@ -133,9 +133,3 @@ Usually this will be rendered inside a modal, but it doesn't have to be. - - diff --git a/src/lib/components/forms/EditAccess.svelte b/src/lib/components/forms/EditAccess.svelte index 68af7ff52..f6579d450 100644 --- a/src/lib/components/forms/EditAccess.svelte +++ b/src/lib/components/forms/EditAccess.svelte @@ -59,7 +59,7 @@ Usually this will be rendered inside a modal, but it doesn't have to be. } -
+ @@ -102,9 +102,3 @@ Usually this will be rendered inside a modal, but it doesn't have to be.
- - diff --git a/src/lib/components/forms/EditData.svelte b/src/lib/components/forms/EditData.svelte index 4ff702594..c260a5702 100644 --- a/src/lib/components/forms/EditData.svelte +++ b/src/lib/components/forms/EditData.svelte @@ -69,7 +69,7 @@ } -
+ @@ -114,11 +114,6 @@ width: 100%; } - form { - width: 100%; - padding: 1rem; - } - th { text-align: start; font-size: var(--font-md); diff --git a/src/lib/components/forms/EditMany.svelte b/src/lib/components/forms/EditMany.svelte index eb8109c57..b7e41fe2c 100644 --- a/src/lib/components/forms/EditMany.svelte +++ b/src/lib/components/forms/EditMany.svelte @@ -71,7 +71,7 @@ Usually this will be rendered inside a modal, but it doesn't have to be. } - + @@ -146,9 +146,3 @@ Usually this will be rendered inside a modal, but it doesn't have to be. - - diff --git a/src/lib/components/forms/EditNote.svelte b/src/lib/components/forms/EditNote.svelte index 5fd6bb974..14c92f982 100644 --- a/src/lib/components/forms/EditNote.svelte +++ b/src/lib/components/forms/EditNote.svelte @@ -46,7 +46,13 @@ Positioning and generating coordinates should happen outside of this form. } - + - + - - diff --git a/src/lib/components/forms/EditSections.svelte b/src/lib/components/forms/EditSections.svelte index 5eac54b45..fc3220584 100644 --- a/src/lib/components/forms/EditSections.svelte +++ b/src/lib/components/forms/EditSections.svelte @@ -36,7 +36,7 @@ This form is entirely client-side. }); -
+
{#if sections.length} @@ -96,11 +96,6 @@ This form is entirely client-side. width: 100%; } - form { - padding: 1rem; - width: 100%; - } - td, th { padding: 0 0.5rem 0.5rem 0; diff --git a/src/lib/components/forms/InviteCollaborator.svelte b/src/lib/components/forms/InviteCollaborator.svelte index 3798f08ed..67bc552a0 100644 --- a/src/lib/components/forms/InviteCollaborator.svelte +++ b/src/lib/components/forms/InviteCollaborator.svelte @@ -43,7 +43,7 @@ Invite a new collaborator to a project } - + diff --git a/src/lib/components/forms/Reprocess.svelte b/src/lib/components/forms/Reprocess.svelte index 170368b6a..884122608 100644 --- a/src/lib/components/forms/Reprocess.svelte +++ b/src/lib/components/forms/Reprocess.svelte @@ -133,7 +133,7 @@ This will mostly be used inside a modal but isn't dependent on one. } - + @@ -244,10 +244,7 @@ This will mostly be used inside a modal but isn't dependent on one. diff --git a/src/lib/components/forms/UserFeedback.svelte b/src/lib/components/forms/UserFeedback.svelte index 2f4067e74..9ca820b3e 100644 --- a/src/lib/components/forms/UserFeedback.svelte +++ b/src/lib/components/forms/UserFeedback.svelte @@ -63,7 +63,7 @@ form { - display: flex; - flex-direction: column; gap: 0.5rem; } header, diff --git a/src/lib/components/forms/stories/AddOnDispatch.stories.svelte b/src/lib/components/forms/stories/AddOnDispatch.stories.svelte index 1d3051ece..d9261e696 100644 --- a/src/lib/components/forms/stories/AddOnDispatch.stories.svelte +++ b/src/lib/components/forms/stories/AddOnDispatch.stories.svelte @@ -66,4 +66,4 @@ }} /> - + diff --git a/src/lib/components/layouts/Error.svelte b/src/lib/components/layouts/Error.svelte index 209a8c5a1..4f71248e3 100644 --- a/src/lib/components/layouts/Error.svelte +++ b/src/lib/components/layouts/Error.svelte @@ -64,9 +64,7 @@ {#if feedbackOpen} (feedbackOpen = false)}> - {#snippet title()} -

{$_("feedback.title")}

- {/snippet} +

{$_("feedback.title")}

@@ -87,22 +94,28 @@ of the $modal store. These are used to set the active modal on any given page. .card { display: flex; flex-direction: column; - gap: 1rem; - max-width: 48rem; + gap: 0 1rem; overflow-x: hidden; overflow-y: auto; position: relative; - padding: 1.5rem; + padding: 0; border-radius: var(--font-md, 1rem); background: var(--white, #fff); box-shadow: 0px 4px 16px 4px #99a8b3; } + .card.fill-viewport { + width: 100%; + height: auto; + } + .card > header { display: flex; align-items: center; justify-content: space-between; flex-direction: row-reverse; + padding: 1rem; + border-bottom: 1px solid var(--gray-2, #d8dee2); } .content { diff --git a/src/lib/components/layouts/stories/Modal.stories.svelte b/src/lib/components/layouts/stories/Modal.stories.svelte index 466ccef50..d43aa9b49 100644 --- a/src/lib/components/layouts/stories/Modal.stories.svelte +++ b/src/lib/components/layouts/stories/Modal.stories.svelte @@ -11,9 +11,9 @@ -

The Ship

-
-

+

The Ship

+
+

In bed we concocted our plans for the morrow. But to my surprise and no small concern, Queequeg now gave me to understand, that he had been diligently consulting Yojo—the name of his black little god—and Yojo had @@ -33,8 +33,8 @@ -

-

+

+

In bed we concocted our plans for the morrow. But to my surprise and no small concern, Queequeg now gave me to understand, that he had been diligently consulting Yojo—the name of his black little god—and Yojo had diff --git a/src/lib/components/notes/Note.svelte b/src/lib/components/notes/Note.svelte index aa82bfb19..5463ac3d4 100644 --- a/src/lib/components/notes/Note.svelte +++ b/src/lib/components/notes/Note.svelte @@ -102,7 +102,12 @@

{#if !embed && shareNoteOpen} - (shareNoteOpen = false)}> + (shareNoteOpen = false)} + fillViewport + maxWidth="66rem" + maxHeight="80vh" + >

{$_("dialog.share")}

diff --git a/src/lib/components/sidebar/DocumentActions.svelte b/src/lib/components/sidebar/DocumentActions.svelte index 5d238fb90..53d53d566 100644 --- a/src/lib/components/sidebar/DocumentActions.svelte +++ b/src/lib/components/sidebar/DocumentActions.svelte @@ -167,7 +167,12 @@ Most actual actions are deferred to their own forms, so this is more of a switch {#if visible} - +

{$_(labels[visible])}

{#if visible === "share"} diff --git a/src/lib/components/sidebar/Documents.svelte b/src/lib/components/sidebar/Documents.svelte index f59856248..d4f4b53c9 100644 --- a/src/lib/components/sidebar/Documents.svelte +++ b/src/lib/components/sidebar/Documents.svelte @@ -97,8 +97,8 @@ {/if} - + {$_("documents.publicDocuments")} - \ No newline at end of file + diff --git a/src/lib/components/sidebar/ViewerActions.svelte b/src/lib/components/sidebar/ViewerActions.svelte index c0274a93e..342212011 100644 --- a/src/lib/components/sidebar/ViewerActions.svelte +++ b/src/lib/components/sidebar/ViewerActions.svelte @@ -149,7 +149,12 @@ {#if visible} - +

{$_(labels[visible])} {#if visible === "revisions"} diff --git a/src/lib/components/viewer/PageActions.svelte b/src/lib/components/viewer/PageActions.svelte index 01912719f..45ee91e89 100644 --- a/src/lib/components/viewer/PageActions.svelte +++ b/src/lib/components/viewer/PageActions.svelte @@ -103,7 +103,12 @@

{#if pageShareOpen} - (pageShareOpen = false)}> + (pageShareOpen = false)} + fillViewport + maxWidth="90vw" + maxHeight="80vh" + >

{$_("dialog.share")}

diff --git a/src/style/kit.css b/src/style/kit.css index b3afc9587..2b0535de4 100644 --- a/src/style/kit.css +++ b/src/style/kit.css @@ -218,6 +218,20 @@ hr.divider { box-shadow: 0px 2px 4px 2px var(--shadow-1, rgba(30, 48, 56, 0.15)); } +/* Modal Form Utilities */ +.modal-form { + width: 100%; + padding: 1rem; +} + +.modal-form--flex { + display: flex; + flex-direction: column; + gap: 0.75rem; + width: 100%; + padding: 1rem; +} + @keyframes spin { 0% { transform: rotate(0deg);