1
+ import React , { useState , useEffect } from 'react' ;
2
+ import { SettingsService , SETTINGS_CHANGE_EVENT } from '../../services/settings-service' ;
3
+ import { Orbit , Languages , ArrowRight } from 'lucide-react' ;
4
+ import { useTranslation } from 'react-i18next' ;
5
+
6
+ export const TranslationPage : React . FC = ( ) => {
7
+ const { t } = useTranslation ( ) ;
8
+ const [ sourceText , setSourceText ] = useState ( '' ) ;
9
+ const [ translatedText , setTranslatedText ] = useState ( '' ) ;
10
+ const [ isTranslating , setIsTranslating ] = useState ( false ) ;
11
+ const [ sourceLanguage , setSourceLanguage ] = useState ( 'auto' ) ;
12
+ const [ targetLanguage , setTargetLanguage ] = useState ( 'en' ) ;
13
+ const [ error , setError ] = useState < Error | null > ( null ) ;
14
+ const [ isApiKeyMissing , setIsApiKeyMissing ] = useState ( true ) ;
15
+
16
+ // Check if API key is available
17
+ useEffect ( ( ) => {
18
+ setIsApiKeyMissing ( ! SettingsService . getInstance ( ) . getApiKey ( ) ) ;
19
+
20
+ const handleSettingsChange = ( ) => {
21
+ setIsApiKeyMissing ( ! SettingsService . getInstance ( ) . getApiKey ( ) ) ;
22
+ } ;
23
+
24
+ window . addEventListener ( SETTINGS_CHANGE_EVENT , handleSettingsChange ) ;
25
+
26
+ return ( ) => {
27
+ window . removeEventListener ( SETTINGS_CHANGE_EVENT , handleSettingsChange ) ;
28
+ } ;
29
+ } , [ ] ) ;
30
+
31
+ // Handle translation
32
+ const handleTranslate = async ( ) => {
33
+ if ( ! sourceText . trim ( ) ) return ;
34
+
35
+ setIsTranslating ( true ) ;
36
+ setError ( null ) ;
37
+
38
+ try {
39
+ // This would be replaced with actual translation logic
40
+ // using a language model API
41
+ setTimeout ( ( ) => {
42
+ setTranslatedText ( sourceText ) ;
43
+ setIsTranslating ( false ) ;
44
+ } , 1000 ) ;
45
+ } catch ( err ) {
46
+ setError ( err as Error ) ;
47
+ setIsTranslating ( false ) ;
48
+ }
49
+ } ;
50
+
51
+ // Languages for dropdown
52
+ const languages = [
53
+ { code : 'auto' , name : t ( 'translation.autoDetect' ) } ,
54
+ { code : 'en' , name : t ( 'translation.english' ) } ,
55
+ { code : 'es' , name : t ( 'translation.spanish' ) } ,
56
+ { code : 'fr' , name : t ( 'translation.french' ) } ,
57
+ { code : 'de' , name : t ( 'translation.german' ) } ,
58
+ { code : 'it' , name : t ( 'translation.italian' ) } ,
59
+ { code : 'pt' , name : t ( 'translation.portuguese' ) } ,
60
+ { code : 'ru' , name : t ( 'translation.russian' ) } ,
61
+ { code : 'zh' , name : t ( 'translation.chinese' ) } ,
62
+ { code : 'ja' , name : t ( 'translation.japanese' ) } ,
63
+ { code : 'ko' , name : t ( 'translation.korean' ) } ,
64
+ ] ;
65
+
66
+ return (
67
+ < div className = "flex flex-col w-full h-full bg-white" >
68
+ { isApiKeyMissing && (
69
+ < div className = "p-2 text-sm text-center text-yellow-800 bg-yellow-100" >
70
+ { t ( 'translation.apiKeyMissing' ) }
71
+ </ div >
72
+ ) }
73
+
74
+ < div className = "flex flex-row h-full" >
75
+ { /* Left side - Source */ }
76
+ < div className = "flex-1 p-6 overflow-hidden frame-right-border" >
77
+ < div className = "flex flex-col h-full" >
78
+ < div className = "flex items-center justify-between gap-4 mb-4" >
79
+
80
+ { /* Language selector */ }
81
+ < div className = "relative flex items-center gap-4" >
82
+ < h1 className = "text-2xl font-semibold" >
83
+ { t ( 'translation.title' ) }
84
+ </ h1 >
85
+
86
+ < select
87
+ value = { sourceLanguage }
88
+ onChange = { ( e ) => setSourceLanguage ( e . target . value ) }
89
+ className = "px-4 py-2 input-box"
90
+ >
91
+ { languages . map ( ( lang ) => (
92
+ < option key = { `source-${ lang . code } ` } value = { lang . code } >
93
+ { lang . name }
94
+ </ option >
95
+ ) ) }
96
+ </ select >
97
+ </ div >
98
+
99
+ < button
100
+ onClick = { handleTranslate }
101
+ disabled = { isTranslating || ! sourceText . trim ( ) || isApiKeyMissing }
102
+ className = "px-8 py-2.5 text-white confirm-btn flex items-center"
103
+ >
104
+ { isTranslating ? (
105
+ < >
106
+ < Orbit size = { 18 } className = "mr-2 animate-spin" />
107
+ { t ( 'translation.translating' ) }
108
+ </ >
109
+ ) : (
110
+ < >
111
+ < Languages size = { 18 } className = "mr-2" />
112
+ { t ( 'translation.translateButton' ) }
113
+ </ >
114
+ ) }
115
+ </ button >
116
+ </ div >
117
+
118
+ { /* Source text input */ }
119
+ < textarea
120
+ value = { sourceText }
121
+ onChange = { ( e ) => setSourceText ( e . target . value ) }
122
+ placeholder = { t ( 'translation.inputPlaceholder' ) }
123
+ className = "flex-1 w-full p-3 mb-4 form-textarea-border input-box"
124
+ style = { { minHeight : '200px' , resize : 'none' } }
125
+ />
126
+
127
+ { /* Translation button */ }
128
+ { /* <div className="flex justify-center">
129
+ <button
130
+ onClick={handleTranslate}
131
+ disabled={isTranslating || !sourceText.trim() || isApiKeyMissing}
132
+ className="px-8 py-2.5 text-white confirm-btn flex items-center"
133
+ >
134
+ {isTranslating ? (
135
+ <>
136
+ <Orbit size={18} className="mr-2 animate-spin" />
137
+ {t('translation.translating')}
138
+ </>
139
+ ) : (
140
+ <>
141
+ <Languages size={18} className="mr-2" />
142
+ {t('translation.translateButton')}
143
+ </>
144
+ )}
145
+ </button>
146
+ </div> */ }
147
+ </ div >
148
+ </ div >
149
+
150
+ { /* Right side - Translation result */ }
151
+ < div className = "flex-1 p-6 overflow-hidden" >
152
+ < div className = "flex flex-col h-full" >
153
+ < div className = "flex items-center gap-4 mb-4" >
154
+ < h2 className = "flex items-center text-xl font-medium" >
155
+ < ArrowRight size = { 24 } />
156
+ </ h2 >
157
+
158
+ { /* Target language selector */ }
159
+ < div className = "relative" >
160
+ < select
161
+ value = { targetLanguage }
162
+ onChange = { ( e ) => setTargetLanguage ( e . target . value ) }
163
+ className = "px-4 py-2 input-box"
164
+ >
165
+ { languages . filter ( lang => lang . code !== 'auto' ) . map ( ( lang ) => (
166
+ < option key = { `target-${ lang . code } ` } value = { lang . code } >
167
+ { lang . name }
168
+ </ option >
169
+ ) ) }
170
+ </ select >
171
+ </ div >
172
+ </ div >
173
+
174
+ { /* Error message */ }
175
+ { error && (
176
+ < div className = "p-3 mb-4 text-red-700 bg-red-100 rounded-lg" >
177
+ { t ( 'common.error' ) } : { error . message }
178
+ </ div >
179
+ ) }
180
+
181
+ { /* Translation result */ }
182
+ < div className = "flex-1 p-3 overflow-auto form-textarea-border major-area-bg-color" >
183
+ { isTranslating ? (
184
+ < div className = "flex items-center justify-center h-full" >
185
+ < div className = "w-8 h-8 border-4 rounded-full border-primary-300 border-t-primary-600 animate-spin" > </ div >
186
+ </ div >
187
+ ) : (
188
+ < div className = "h-full" >
189
+ { translatedText ? (
190
+ < p className = "text-primary-800" > { translatedText } </ p >
191
+ ) : (
192
+ < div className = "flex items-center justify-center h-full" >
193
+ < p className = "text-surface-400" > { t ( 'translation.resultPlaceholder' ) } </ p >
194
+ </ div >
195
+ ) }
196
+ </ div >
197
+ ) }
198
+ </ div >
199
+ </ div >
200
+ </ div >
201
+ </ div >
202
+ </ div >
203
+ ) ;
204
+ } ;
205
+
206
+ export default TranslationPage ;
0 commit comments