From 43305878e4cff4144550f6910e68d2954fa12cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralf=20Kr=C3=BCmmel?= Date: Sat, 18 Oct 2025 22:15:15 +0200 Subject: [PATCH 1/4] feat: add localization and electron packaging --- .gitignore | 10 ++ App.tsx | 58 ++++++----- components/FileUpload.tsx | 6 +- components/LanguageSelector.tsx | 30 ++++++ components/OptionsPanel.tsx | 49 ++++++---- components/OutputDisplay.tsx | 18 ++-- electron/main.cjs | 53 ++++++++++ electron/preload.cjs | 5 + i18n.tsx | 166 ++++++++++++++++++++++++++++++++ index.tsx | 5 +- package-lock.json | 3 + package.json | 37 ++++++- 12 files changed, 386 insertions(+), 54 deletions(-) create mode 100644 .gitignore create mode 100644 components/LanguageSelector.tsx create mode 100644 electron/main.cjs create mode 100644 electron/preload.cjs create mode 100644 i18n.tsx diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a9b1d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +node_modules/ +dist/ +release/ +.DS_Store +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.idea/ +.vscode/ diff --git a/App.tsx b/App.tsx index e273224..cc0590a 100644 --- a/App.tsx +++ b/App.tsx @@ -1,11 +1,12 @@ import React, { useState, useCallback, useMemo } from 'react'; import { DumpOptions, Preset } from './types'; -import { PRESETS } from './constants'; import { createCodeDump } from './services/zipProcessor'; import { OptionsPanel } from './components/OptionsPanel'; import { FileUpload } from './components/FileUpload'; import { OutputDisplay } from './components/OutputDisplay'; +import { useTranslation } from './i18n'; +import { LanguageSelector } from './components/LanguageSelector'; const defaultOptions: DumpOptions = { include: ['**/*'], @@ -17,19 +18,23 @@ const defaultOptions: DumpOptions = { strictText: false, }; -const LoadingIndicator: React.FC<{ progress: number }> = ({ progress }) => ( -
- - - - -

Processing ZIP file...

-
-
+const LoadingIndicator: React.FC<{ progress: number }> = ({ progress }) => { + const { t } = useTranslation(); + + return ( +
+ + + + +

{t('loading.processing')}

+
+
+
+

{t('loading.percentComplete', { percent: progress })}

-

{progress}% complete

-
-); + ); +}; function App() { @@ -38,6 +43,7 @@ function App() { const [output, setOutput] = useState(null); const [isLoading, setIsLoading] = useState(false); const [progress, setProgress] = useState(0); + const { t } = useTranslation(); const handlePresetChange = useCallback((preset: Preset) => { const newOptions: DumpOptions = { @@ -57,13 +63,13 @@ function App() { if (file.type === 'application/zip' || file.type === 'application/x-zip-compressed') { setZipFile(file); } else { - alert('Please select a valid ZIP file.'); + alert(t('app.invalidZip')); } - }, []); + }, [t]); const handleGenerate = async () => { if (!zipFile) { - alert('Please select a ZIP file first.'); + alert(t('app.noZip')); return; } setIsLoading(true); @@ -75,7 +81,8 @@ function App() { setOutput(markdownContent); } catch (error) { console.error('Failed to process ZIP file:', error); - alert(`An error occurred: ${error instanceof Error ? error.message : 'Unknown error'}`); + const message = error instanceof Error ? error.message : 'Unknown error'; + alert(t('app.error', { message })); setOutput(null); } finally { setIsLoading(false); @@ -101,9 +108,14 @@ function App() { return (
-
-

CodeDump from ZIP

-

Generate a comprehensive Markdown file from your project's ZIP archive.

+
+
+
+

{t('app.title')}

+

{t('app.subtitle')}

+
+ +
@@ -122,12 +134,12 @@ function App() {
{zipFile && (
-

Selected: {zipFile.name}

-
)} diff --git a/components/FileUpload.tsx b/components/FileUpload.tsx index 1875db9..6c9c596 100644 --- a/components/FileUpload.tsx +++ b/components/FileUpload.tsx @@ -1,6 +1,7 @@ import React, { useState, useCallback } from 'react'; import { Card } from './ui/Card'; +import { useTranslation } from '../i18n'; interface FileUploadProps { onFileSelect: (file: File) => void; @@ -16,6 +17,7 @@ const UploadIcon = () => ( export const FileUpload: React.FC = ({ onFileSelect, disabled }) => { const [isDragging, setIsDragging] = useState(false); + const { t } = useTranslation(); const handleDrag = useCallback((e: React.DragEvent) => { e.preventDefault(); @@ -55,9 +57,9 @@ export const FileUpload: React.FC = ({ onFileSelect, disabled }
diff --git a/components/LanguageSelector.tsx b/components/LanguageSelector.tsx new file mode 100644 index 0000000..14605ba --- /dev/null +++ b/components/LanguageSelector.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { useTranslation } from '../i18n'; +import { Select } from './ui/Select'; + +export const LanguageSelector: React.FC = () => { + const { language, setLanguage, t } = useTranslation(); + + const handleChange = (event: React.ChangeEvent) => { + setLanguage(event.target.value as typeof language); + }; + + return ( +
+ +
+ +
+ +
+ ); +}; diff --git a/components/OptionsPanel.tsx b/components/OptionsPanel.tsx index 5e86c13..cedf700 100644 --- a/components/OptionsPanel.tsx +++ b/components/OptionsPanel.tsx @@ -8,6 +8,9 @@ import { Select } from './ui/Select'; import { Input } from './ui/Input'; import { Textarea } from './ui/Textarea'; import { Checkbox } from './ui/Checkbox'; +import { useTranslation, TranslationKey } from '../i18n'; + +type PresetTranslationKey = `presets.${typeof PRESETS[number]['name']}`; interface OptionsPanelProps { options: DumpOptions; @@ -17,6 +20,7 @@ interface OptionsPanelProps { } export const OptionsPanel: React.FC = ({ options, setOptions, onPresetChange, disabled }) => { + const { t } = useTranslation(); const handleOptionChange = (key: K, value: DumpOptions[K]) => { setOptions(prev => ({ ...prev, [key]: value })); @@ -36,78 +40,81 @@ export const OptionsPanel: React.FC = ({ options, setOptions, return (
-

Configuration

+

{t('options.configuration')}

- +
- +