diff --git a/src/lib/Installer.svelte b/src/lib/Installer.svelte index 0d85412..6dab851 100644 --- a/src/lib/Installer.svelte +++ b/src/lib/Installer.svelte @@ -4,6 +4,7 @@ import { getCurrentWindow } from '@tauri-apps/api/window'; import { onMount } from 'svelte'; import iconUrl from '../assets/icon.png'; + import { t } from './utils/i18n.js'; let installing = $state(false); let error = $state(''); @@ -65,7 +66,7 @@ error = e.toString(); installing = false; if (error.includes('Access is denied') && (isInstalled ? installedAllUsers : allUsers)) { - error = 'Access denied. Please run as Administrator.'; + error = t('installer.accessDenied'); } } } @@ -97,7 +98,7 @@
-
@@ -105,15 +106,15 @@
App Icon -

Markdown Viewer

+

{t('installer.markdownViewer')}

{#if isInstalled}
- Current: v{installedVersion} + {t('installer.current')} v{installedVersion} - Target: v{installerVersion} + {t('installer.target')} v{installerVersion}
{:else} -

A simple markdown viewer v{installerVersion}

+

{t('installer.simpleMarkdownViewer')} v{installerVersion}

{/if}
@@ -123,8 +124,8 @@
{#if !isInstalled}
- - + +
{/if} @@ -134,39 +135,39 @@
{:else}

- Installed for: {installedAllUsers ? 'All Users' : 'Current User'} + {t('installer.installedFor')} {installedAllUsers ? t('installer.allUsers') : 'Current User'}

@@ -181,25 +182,25 @@
{#if isInstalled} - - + + {:else} {/if}
{#if allUsers || (isInstalled && installedAllUsers)} -

Requires Administrator privileges

+

{t('installer.requiresAdmin')}

{/if}
{:else}
-

{isInstalled ? 'Updating' : 'Installing'} Markpad...

+

{t(isInstalled ? 'installer.updating' : 'installer.installing')} {t('installer.markpad')}

{/if}
diff --git a/src/lib/MarkdownViewer.svelte b/src/lib/MarkdownViewer.svelte index 97cf2f3..202b3c0 100644 --- a/src/lib/MarkdownViewer.svelte +++ b/src/lib/MarkdownViewer.svelte @@ -24,8 +24,9 @@ import { processMarkdownHtml } from './utils/markdown'; import DOMPurify from 'dompurify'; import HomePage from './components/HomePage.svelte'; - import { tabManager } from './stores/tabs.svelte.js'; - import { settings } from './stores/settings.svelte.js'; +import { tabManager } from './stores/tabs.svelte.js'; +import { settings } from './stores/settings.svelte.js'; +import { t } from './utils/i18n.js'; // syntax highlighting & latex let hljs: any = $state(null); @@ -40,6 +41,12 @@ import { processMarkdownHtml } from './utils/markdown'; let showSettings = $state(false); + let uiLanguage = $state(settings.language); + + $effect(() => { + uiLanguage = settings.language; + }); + let recentFiles = $state([]); let isFocused = $state(true); @@ -238,8 +245,8 @@ import { processMarkdownHtml } from './utils/markdown'; if (settings.restoreStateOnReopen) { const hasUnsaved = tabManager.tabs.some((t) => t.isDirty || (t.path === '' && t.rawContent.trim() !== '')); if (hasUnsaved) { - const response = await askCustom(`Are you sure you want to exit? All unsaved tabs and local history will be lost.`, { - title: 'Confirm Exit', + const response = await askCustom(t('modal.exit.unsaved.message'), { + title: t('modal.exit.unsaved.title'), kind: 'warning', showSave: false, }); @@ -1048,8 +1055,8 @@ import { processMarkdownHtml } from './utils/markdown'; if (!tab.isDirty) return true; - const response = await askCustom(`You have unsaved changes in "${tab.title}". Do you want to save them before closing?`, { - title: 'Unsaved Changes', + const response = await askCustom(t('modal.youHaveUnsavedChanges', settings.language).replace('{title}', tab.title), { + title: t('modal.unsavedChanges.title'), kind: 'warning', showSave: true, }); @@ -1070,14 +1077,14 @@ import { processMarkdownHtml } from './utils/markdown'; // Switch back to view if (tab.isDirty && tab.path !== '') { if (autoSave) { - const success = await saveContent(); - if (!success) return; // If save fails, stay in edit mode? - } else { - const response = await askCustom('You have unsaved changes. Do you want to save them before returning to view mode?', { - title: 'Unsaved Changes', - kind: 'warning', - showSave: true, - }); + const success = await saveContent(); + if (!success) return; // If save fails, stay in edit mode? + } else { + const response = await askCustom(t('modal.unsavedChanges.viewMode.message'), { + title: t('modal.unsavedChanges.title'), + kind: 'warning', + showSave: true, + }); if (response === 'cancel') return; if (response === 'save') { @@ -1319,7 +1326,7 @@ import { processMarkdownHtml } from './utils/markdown'; const filename = tab?.path ? tab.path.split(/[/\\]/).pop()?.replace(/\.[^.]+$/, '') || '' : ''; const ref = filename ? `[[${filename}#${text}]]` : `#${text}`; copyRefItem = [ - { label: 'Copy Reference', onClick: () => invoke('clipboard_write_text', { text: ref }) }, + { label: t('menu.copyReference', uiLanguage), onClick: () => invoke('clipboard_write_text', { text: ref }) }, { separator: true }, ]; } @@ -1328,7 +1335,7 @@ import { processMarkdownHtml } from './utils/markdown'; let mediaItems: any[] = []; if (img) { mediaItems = [ - { label: 'Save Image As...', onClick: () => saveImageAs(img.src) }, + { label: t('menu.saveImageAs', uiLanguage), onClick: () => saveImageAs(img.src) }, { separator: true } ]; } @@ -1336,7 +1343,7 @@ import { processMarkdownHtml } from './utils/markdown'; const mermaidDiag = (e.target as HTMLElement).closest('.mermaid-diagram'); if (mermaidDiag) { mediaItems = [ - { label: 'Save Diagram As SVG...', onClick: () => saveDiagramAs(mermaidDiag as HTMLElement) }, + { label: t('menu.saveDiagramAsSvg', uiLanguage), onClick: () => saveDiagramAs(mermaidDiag as HTMLElement) }, { separator: true } ]; } @@ -1350,16 +1357,16 @@ import { processMarkdownHtml } from './utils/markdown'; ...mediaItems, ...(isEditing && isInsideEditor ? [ - { label: 'Undo', shortcut: 'Ctrl+Z', onClick: () => editorPane?.undo() }, - { label: 'Redo', shortcut: 'Ctrl+Y', onClick: () => editorPane?.redo() }, + { label: t('menu.undo', uiLanguage), shortcut: 'Ctrl+Z', onClick: () => editorPane?.undo() }, + { label: t('menu.redo', uiLanguage), shortcut: 'Ctrl+Y', onClick: () => editorPane?.redo() }, { separator: true } ] : []), - ...(hasSelection ? [{ label: 'Copy', onClick: () => { + ...(hasSelection ? [{ label: t('menu.copy', uiLanguage), onClick: () => { const selection = window.getSelection()?.toString(); if (selection) invoke('clipboard_write_text', { text: selection }); } }] : []), - { label: 'Select All', onClick: () => { + { label: t('menu.selectAll', uiLanguage), onClick: () => { if (!markdownBody) return; const range = document.createRange(); range.selectNodeContents(markdownBody); @@ -1368,10 +1375,10 @@ import { processMarkdownHtml } from './utils/markdown'; selection?.addRange(range); } }, { separator: true }, - { label: 'Open File Location', onClick: openFileLocation, disabled: !currentFile }, - { label: 'Edit', onClick: () => toggleEdit() }, + { label: t('menu.openLocation', uiLanguage), onClick: openFileLocation, disabled: !currentFile }, + { label: t('menu.edit', uiLanguage), onClick: () => toggleEdit() }, { separator: true }, - { label: 'Close File', onClick: closeFile }, + { label: t('menu.closeFile', uiLanguage), onClick: closeFile }, ], }; } @@ -1521,8 +1528,8 @@ import { processMarkdownHtml } from './utils/markdown'; const success = await saveContent(); if (!success) return; } else { - const response = await askCustom('You have unsaved changes. Do you want to save them before closing split view?', { - title: 'Unsaved Changes', + const response = await askCustom(t('modal.unsavedChanges.splitView.message'), { + title: t('modal.unsavedChanges.title'), kind: 'warning', showSave: true, }); @@ -1797,7 +1804,7 @@ import { processMarkdownHtml } from './utils/markdown'; const tab = tabManager.tabs.find((t) => t.id === tabId); if (!tab || !tab.path) return; - const newName = window.prompt('Rename file:', tab.title); + const newName = window.prompt(t('menu.renameFile', settings.language), tab.title); if (newName && newName !== tab.title) { const oldPath = tab.path; const newPath = oldPath.replace(/[/\\][^/\\]+$/, (m) => m.charAt(0) + newName); @@ -1865,12 +1872,12 @@ import { processMarkdownHtml } from './utils/markdown'; console.log('Dirty tabs:', dirtyTabs.length); if (dirtyTabs.length > 0) { console.log('Preventing default close'); - event.preventDefault(); - const response = await askCustom(`You have ${dirtyTabs.length} unsaved file(s). Do you want to save your changes?`, { - title: 'Unsaved Changes', - kind: 'warning', - showSave: true, - }); + event.preventDefault(); + const response = await askCustom(t('modal.youHaveUnsavedFiles', settings.language).replace('{{count}}', dirtyTabs.length.toString()), { + title: t('modal.unsavedChanges', settings.language), + kind: 'warning', + showSave: true, + }); if (response === 'save') { // Attempt to save all dirty tabs @@ -1940,8 +1947,8 @@ import { processMarkdownHtml } from './utils/markdown'; if (ext && ['md', 'markdown', 'txt'].includes(ext)) { loadMarkdown(path); } else { - const filename = path.split(/[/\\]/).pop() || 'File'; - addToast(`Unsupported file type: ${filename}`, 'error'); + const filename = path.split(/[\/\\]/).pop() || 'File'; + addToast(t('toast.unsupportedFile').replace('{{filename}}', filename), 'error'); } }); } @@ -2191,7 +2198,7 @@ import { processMarkdownHtml } from './utils/markdown'; y: e.clientY, items: [ { - label: 'Copy Reference', + label: t('menu.copyReference', uiLanguage), onClick: () => { const tab = tabManager.activeTab; const fn = tab?.path ? tab.path.split(/[/\\]/).pop()?.replace(/\.[^.]+$/, '') || '' : ''; @@ -2272,17 +2279,17 @@ import { processMarkdownHtml } from './utils/markdown';
{#if isSplit || isEditing}
-
- Drop to Embed -
-
+
+ {t('dragAndDrop.embed')} +
+
{/if} {#if isSplit || !isEditing}
-
- Drop to Open -
-
+
+ {t('dragAndDrop.open')} +
+ {/if} diff --git a/src/lib/Uninstaller.svelte b/src/lib/Uninstaller.svelte index 0b3f7db..1d65f8f 100644 --- a/src/lib/Uninstaller.svelte +++ b/src/lib/Uninstaller.svelte @@ -2,6 +2,7 @@ import { invoke } from '@tauri-apps/api/core'; import { getCurrentWindow } from '@tauri-apps/api/window'; import iconUrl from '../assets/icon.png'; + import { t } from './utils/i18n.js'; let uninstalling = $state(false); let error = $state(''); @@ -27,7 +28,7 @@
-
@@ -35,8 +36,8 @@
App Icon -

Uninstall Markpad?

-

This will remove the application and all its shortcuts.

+

{t('uninstaller.uninstallMarkpad')}

+

{t('uninstaller.removeApplication')}

{#if !uninstalling} @@ -46,14 +47,14 @@ {/if}
- - + +
{:else}
-

Removing Markpad...

+

{t('uninstaller.removingMarkpad')}

{/if}
diff --git a/src/lib/components/Editor.svelte b/src/lib/components/Editor.svelte index be08bb8..e4f7b35 100644 --- a/src/lib/components/Editor.svelte +++ b/src/lib/components/Editor.svelte @@ -2,6 +2,7 @@ import { onMount, onDestroy } from "svelte"; import { tabManager } from "../stores/tabs.svelte.js"; import { settings } from "../stores/settings.svelte.js"; + import { t } from '../utils/i18n.js'; import * as monaco from "monaco-editor"; import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker"; @@ -68,6 +69,11 @@ let wordCount = $state(0); let currentLanguage = $state("markdown"); let currentTabId = $state(tabManager.activeTabId); + let uiLanguage = $state(settings.language); + + $effect(() => { + uiLanguage = settings.language; + }); self.MonacoEnvironment = { getWorker: function (_moduleId: any, label: string) { @@ -218,7 +224,7 @@ editor.addAction({ id: "toggle-minimap", - label: "Toggle Minimap", + label: t('settings.minimap', uiLanguage), run: () => { settings.toggleMinimap(); }, @@ -226,7 +232,7 @@ editor.addAction({ id: "toggle-word-wrap", - label: "Toggle Word Wrap", + label: t('settings.wordWrap', uiLanguage), run: () => { settings.toggleWordWrap(); }, @@ -234,7 +240,7 @@ editor.addAction({ id: "toggle-line-numbers", - label: "Toggle Line Numbers", + label: t('settings.lineNumbers', uiLanguage), run: () => { settings.toggleLineNumbers(); }, @@ -242,7 +248,7 @@ editor.addAction({ id: "toggle-vim-mode", - label: "Toggle Vim Mode", + label: t('settings.vimMode', uiLanguage), run: () => { settings.toggleVimMode(); }, @@ -250,7 +256,7 @@ editor.addAction({ id: "toggle-status-bar", - label: "Toggle Status Bar", + label: t('settings.statusBar', uiLanguage), run: () => { settings.toggleStatusBar(); }, @@ -258,7 +264,7 @@ editor.addAction({ id: "toggle-word-count", - label: "Toggle Word Count", + label: t('settings.wordCount', uiLanguage), run: () => { settings.toggleWordCount(); }, @@ -266,7 +272,7 @@ editor.addAction({ id: "toggle-line-highlight", - label: "Toggle Line Highlight", + label: t('settings.lineHighlight', uiLanguage), run: () => { settings.toggleLineHighlight(); }, @@ -274,7 +280,7 @@ editor.addAction({ id: "toggle-occurrences-highlight", - label: "Toggle Occurrences Highlight", + label: t('settings.showWhitespace', uiLanguage), run: () => { settings.toggleOccurrencesHighlight(); }, @@ -282,7 +288,7 @@ editor.addAction({ id: "toggle-whitespace", - label: "Toggle Show Whitespace", + label: t('settings.showWhitespace', uiLanguage), run: () => { settings.toggleShowWhitespace(); }, @@ -290,7 +296,7 @@ editor.addAction({ id: "toggle-tabs", - label: "Toggle Tabs", + label: t('settings.showTabs', uiLanguage), keybindings: [ monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyB, ], @@ -301,7 +307,7 @@ editor.addAction({ id: "toggle-zen-mode", - label: "Toggle Zen Mode", + label: t('settings.zenMode', uiLanguage), keybindings: [ monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyZ, ], @@ -431,28 +437,28 @@ editor.addAction({ id: "fmt-bold", - label: "Format: Bold", + label: t('menu.bold', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyB], run: () => toggleFormat("**"), }); editor.addAction({ id: "fmt-italic", - label: "Format: Italic", + label: t('menu.italic', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyI], run: () => toggleFormat("*"), }); editor.addAction({ id: "fmt-underline", - label: "Format: Underline", + label: t('menu.underline', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyU], run: () => toggleFormat("|", "tag"), }); editor.addAction({ id: "insert-table-simple", - label: "Insert Table", + label: t('menu.insertTable', uiLanguage), keybindings: [ monaco.KeyMod.chord( monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, @@ -485,7 +491,7 @@ editor.addAction({ id: "file-new", - label: "New File", + label: t('menu.newFile', uiLanguage), keybindings: [ monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyN, monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyT, @@ -495,28 +501,28 @@ editor.addAction({ id: "file-open", - label: "Open File", + label: t('menu.openFile', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyO], run: () => onopen?.(), }); editor.addAction({ id: "file-save", - label: "Save File", + label: t('menu.save', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS], run: () => onsave?.(), }); editor.addAction({ id: "file-close", - label: "Close File", + label: t('menu.closeFile', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyW], run: () => onclose?.(), }); editor.addAction({ id: "file-reveal", - label: "Open File Location", + label: t('menu.openLocation', uiLanguage), keybindings: [ monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyR, ], @@ -525,35 +531,35 @@ editor.addAction({ id: "view-toggle-edit", - label: "Toggle Edit Mode", + label: t('menu.toggleEditMode', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyE], run: () => ontoggleEdit?.(), }); editor.addAction({ id: "view-toggle-live", - label: "Toggle Live Mode", + label: t('menu.toggleLiveMode', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyL], run: () => ontoggleLive?.(), }); editor.addAction({ id: "view-toggle-split", - label: "Toggle Split View", + label: t('menu.toggleSplitView', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyH], run: () => ontoggleSplit?.(), }); editor.addAction({ id: "tab-next", - label: "Next Tab", + label: t('menu.nextTab', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Tab], run: () => onnextTab?.(), }); editor.addAction({ id: "tab-prev", - label: "Previous Tab", + label: t('menu.previousTab', uiLanguage), keybindings: [ monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.Tab, ], @@ -562,7 +568,7 @@ editor.addAction({ id: "tab-undo-close", - label: "Undo Close Tab", + label: t('menu.undoCloseTab', uiLanguage), keybindings: [ monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyT, ], @@ -571,7 +577,7 @@ editor.addAction({ id: "app-command-palette", - label: "Command Palette", + label: t('menu.commandPalette', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyP], run: (ed) => { ed.trigger("keyboard", "editor.action.quickCommand", {}); @@ -678,7 +684,7 @@ // clipboard handling: override Ctrl+C and Ctrl+V to use Rust backend editor.addAction({ id: "custom-copy", - label: "Copy", + label: t('menu.copy', uiLanguage), keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyC], run: async (ed) => { const selection = ed.getSelection(); @@ -1124,20 +1130,20 @@ {#if settings.statusBar}
- Ln {cursorPosition?.lineNumber ?? 1}, Col {cursorPosition?.column ?? 1} -
+ {t('editor.status.lineCol', settings.language).replace('{{line}}', (cursorPosition?.lineNumber ?? 1).toString()).replace('{{col}}', (cursorPosition?.column ?? 1).toString())} +
{#if selectionCount > 0}
- {selectionCount} selected + {t('editor.status.selected', settings.language).replace('{{count}}', selectionCount.toString())}
{:else if cursorCount > 1}
- {cursorCount} selections + {t('editor.status.selections', settings.language).replace('{{count}}', cursorCount.toString())}
{/if} {#if settings.wordCount}
- {wordCount} words + {t('editor.status.words', settings.language).replace('{{count}}', wordCount.toString())}
{/if}
@@ -1146,8 +1152,8 @@
{currentLanguage}
-
CRLF
-
UTF-8
+
{t('editor.status.crlf')}
+
{t('editor.status.utf8')}
{/if} diff --git a/src/lib/components/HomePage.svelte b/src/lib/components/HomePage.svelte index cf04197..0f043fd 100644 --- a/src/lib/components/HomePage.svelte +++ b/src/lib/components/HomePage.svelte @@ -1,6 +1,8 @@
-

Open a Markdown file

+

{t('home.welcomeToMarkpad', settings.language)}

-

Recent Files

+

{t('home.recentFiles', settings.language)}

{#if recentFiles.length > 0}
{#each recentFiles as file} @@ -103,7 +105,7 @@ {/each}
{:else} -

Your recently opened files will appear here.

+

{t('home.noRecentFiles', settings.language)}

{/if}
diff --git a/src/lib/components/Modal.svelte b/src/lib/components/Modal.svelte index feb3c03..a4c45ce 100644 --- a/src/lib/components/Modal.svelte +++ b/src/lib/components/Modal.svelte @@ -1,5 +1,7 @@