feat: implement MariaDB export wizard and add localization support

This commit is contained in:
Ümit Tunç
2026-04-01 23:55:38 +03:00
parent 3722942ea0
commit a8c3b4a824
10 changed files with 948 additions and 319 deletions
+181 -193
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'
import { useState, useEffect, useRef, useMemo } from 'react'
import logo from './assets/logo.svg'
import {
Box,
@@ -72,6 +72,7 @@ import {
} from '@mui/icons-material'
import Swal from 'sweetalert2'
import { useTranslation } from 'react-i18next'
import { supportedLngs } from './i18n'
import MariaDbImportWizard from './components/MariaDbImportWizard'
import MariaDbExportWizard from './components/MariaDbExportWizard'
import NginxCodeEditor from './components/NginxCodeEditor'
@@ -346,10 +347,10 @@ function App(): JSX.Element {
if (!result.success) {
Swal.fire({
title: 'Hata!',
title: t('common.error'),
text: result.message,
icon: 'error',
confirmButtonText: 'Tamam',
confirmButtonText: t('common.ok'),
background: '#1e1e1e',
color: '#fff',
confirmButtonColor: '#153E5E',
@@ -374,12 +375,12 @@ function App(): JSX.Element {
const handleResetData = async (service: string) => {
const confirmResult = await Swal.fire({
title: 'Emin misiniz?',
text: `${service} verilerini sıfırlamak istiyor musunuz? Bu işlem tüm verileri siler ve temiz kurulum yapar.`,
title: t('common.reset_confirm_title'),
text: t('common.reset_confirm_text', { service }),
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Evet, Sıfırla',
cancelButtonText: 'Vazgeç',
confirmButtonText: t('common.reset_btn'),
cancelButtonText: t('common.cancel'),
background: '#1e1e1e',
color: '#fff',
confirmButtonColor: '#d33',
@@ -389,7 +390,7 @@ function App(): JSX.Element {
if (confirmResult.isConfirmed) {
const result = await window.api.invoke('services:reset-data', service)
Swal.fire({
title: result.success ? 'Başarılı!' : 'Hata!',
title: result.success ? t('common.success') : t('common.error'),
text: result.message,
icon: result.success ? 'success' : 'error',
background: '#1e1e1e',
@@ -403,14 +404,15 @@ function App(): JSX.Element {
if (window.api && window.api.saveSettings) {
const result = await window.api.saveSettings(settings)
if (result.success) {
setNotification({ open: true, message: 'Ayarlar kaydedildi.', severity: 'success' })
setNotification({ open: true, message: t('settings.saved'), severity: 'success' })
}
}
}
const toggleLanguage = () => {
const newLang = i18n.language === 'tr' ? 'en' : i18n.language === 'en' ? 'de' : 'tr'
const handleLanguageChange = (event: any) => {
const newLang = event.target.value
i18n.changeLanguage(newLang)
setSettings(prev => ({ ...prev, language: newLang }))
}
const handleStartAll = async () => {
@@ -437,7 +439,7 @@ function App(): JSX.Element {
])
fetchStatus()
setNotification({ open: true, message: 'Tüm servisler başlatıldı.', severity: 'success' })
setNotification({ open: true, message: t('dashboard.all_started'), severity: 'success' })
}
const handleStopAll = async () => {
@@ -454,12 +456,12 @@ function App(): JSX.Element {
])
fetchStatus()
setNotification({ open: true, message: 'Tüm servisler durduruldu.', severity: 'success' })
setNotification({ open: true, message: t('dashboard.all_stopped'), severity: 'success' })
}
const getStatusChip = (s: string) => {
if (s === 'starting') {
return <Chip label="BAŞLATILIYOR..." color="warning" size="small" sx={{ fontWeight: 'bold' }} icon={<CircularProgress size={14} color="inherit" />} />
return <Chip label={t('common.starting')} color="warning" size="small" sx={{ fontWeight: 'bold' }} icon={<CircularProgress size={14} color="inherit" />} />
}
const color = s === 'running' ? 'success' : s === 'stopped' ? 'error' : 'warning'
return <Chip label={s.toUpperCase()} color={color} size="small" sx={{ fontWeight: 'bold' }} />
@@ -474,28 +476,28 @@ function App(): JSX.Element {
}
setIsProjectDialogOpen(false)
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' })
setNotification({ open: true, message: newProject.id ? 'Proje güncellendi.' : 'Proje başarıyla eklendi.', severity: 'success' })
setNotification({ open: true, message: newProject.id ? t('projects.updated') : t('projects.added'), severity: 'success' })
fetchProjects()
}
const handleRemoveProject = async (id: string) => {
if (!window.api) return
const result = await Swal.fire({
title: 'Emin misiniz?',
text: "Bu projeyi silmek istediğinize emin misiniz? Nginx ayarları otomatik olarak güncellenecektir.",
title: t('common.are_you_sure'),
text: t('projects.delete_confirm'),
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#ff4444',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Evet, sil!',
cancelButtonText: 'İptal',
confirmButtonText: t('common.yes_delete'),
cancelButtonText: t('common.cancel'),
background: '#1e1e1e',
color: '#fff'
})
if (result.isConfirmed) {
await window.api.removeProject(id)
setNotification({ open: true, message: 'Proje silindi. Nginx yeniden yüklendi.', severity: 'success' })
setNotification({ open: true, message: t('projects.deleted'), severity: 'success' })
fetchProjects()
}
}
@@ -512,7 +514,7 @@ function App(): JSX.Element {
if (window.api && window.api.invoke) {
const result = await window.api.invoke('nginx:config:save', nginxConfig)
if (result.success) {
setNotification({ open: true, message: 'Nginx yapılandırması kaydedildi ve yeniden yüklendi.', severity: 'success' })
setNotification({ open: true, message: t('server.nginx.config_saved'), severity: 'success' })
setIsNginxConfigOpen(false)
}
}
@@ -530,7 +532,7 @@ function App(): JSX.Element {
if (window.api && window.api.invoke) {
const result = await window.api.invoke('apache:config:save', apacheConfig)
if (result.success) {
setNotification({ open: true, message: 'Apache yapılandırması kaydedildi ve yeniden yüklendi.', severity: 'success' })
setNotification({ open: true, message: t('server.apache.config_saved'), severity: 'success' })
setIsApacheConfigOpen(false)
}
}
@@ -577,15 +579,15 @@ function App(): JSX.Element {
${errorInfo?.host ? `
<div style="margin-top: 15px; text-align: center;">
<p style="font-size: 0.9rem; color: #ffb142;">
<b>Sorunlu Proje:</b> ${errorInfo.host} (Satır: ${errorInfo.line})
<b>{t('common.error')}:</b> ${errorInfo.host} (Satır: ${errorInfo.line})
</p>
</div>
` : ''}
`,
icon: result.success ? 'success' : 'error',
showCancelButton: !result.success && !!errorInfo?.host,
confirmButtonText: !result.success && errorInfo?.host ? 'Düzeltmeye Git' : 'Tamam',
cancelButtonText: 'Kapat',
confirmButtonText: !result.success && errorInfo?.host ? t('common.go_to_fix') : t('common.ok'),
cancelButtonText: t('common.close'),
background: '#1e1e1e',
color: '#fff',
width: '600px'
@@ -611,7 +613,7 @@ function App(): JSX.Element {
if (window.api && window.api.invoke) {
const result = await window.api.invoke('project:nginx:save', selectedProjectHost, projectNginxConfig)
if (result.success) {
setNotification({ open: true, message: `${selectedProjectHost} için Nginx yapılandırması kaydedildi.`, severity: 'success' })
setNotification({ open: true, message: t('server.nginx.config_saved'), severity: 'success' })
setIsProjectNginxConfigOpen(false)
}
}
@@ -668,10 +670,9 @@ function App(): JSX.Element {
try {
const success = await window.api.pmaSetup()
if (success) {
setPmaInstalled(true)
setNotification({ open: true, message: 'phpMyAdmin başarıyla kuruldu.', severity: 'success' })
setNotification({ open: true, message: t('server.phpmyadmin.installed_success'), severity: 'success' })
} else {
setNotification({ open: true, message: 'phpMyAdmin kurulumu sırasında hata oluştu.', severity: 'error' })
setNotification({ open: true, message: t('server.phpmyadmin.installed_error'), severity: 'error' })
}
} finally {
setPmaLoading(false)
@@ -718,12 +719,28 @@ function App(): JSX.Element {
backdropFilter: 'blur(10px)'
}}
>
<Toolbar sx={{ justifyContent: 'flex-end' }}>
<Tooltip title="Dil Değiştir">
<IconButton onClick={toggleLanguage} color="inherit">
<LanguageIcon />
</IconButton>
</Tooltip>
<Toolbar sx={{ gap: 2 }}>
<FormControl size="small" variant="outlined" sx={{ minWidth: 120 }}>
<Select
value={i18n.language}
onChange={handleLanguageChange}
displayEmpty
IconComponent={LanguageIcon}
sx={{
color: 'white',
'.MuiOutlinedInput-notchedOutline': { borderColor: 'rgba(255,255,255,0.3)' },
'&:hover .MuiOutlinedInput-notchedOutline': { borderColor: 'rgba(255,255,255,0.5)' },
'&.Mui-focused .MuiOutlinedInput-notchedOutline': { borderColor: 'primary.main' },
'.MuiSelect-icon': { color: 'white' }
}}
>
{supportedLngs.map(lang => (
<MenuItem key={lang} value={lang}>
{lang.toUpperCase()}
</MenuItem>
))}
</Select>
</FormControl>
<IconButton color="inherit" onClick={() => setActiveTab('settings')}>
<SettingsIcon />
</IconButton>
@@ -747,19 +764,19 @@ function App(): JSX.Element {
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'dashboard'} onClick={() => setActiveTab('dashboard')}>
<ListItemIcon><DashboardIcon color={activeTab === 'dashboard' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary="Dashboard" />
<ListItemText primary={t('dashboard.title')} />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'projects'} onClick={() => setActiveTab('projects')}>
<ListItemIcon><ProjectIcon color={activeTab === 'projects' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary="Projeler" />
<ListItemText primary={t('projects.title')} />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'tools'} onClick={() => setActiveTab('tools')}>
<ListItemIcon><ToolsIcon color={activeTab === 'tools' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary="Araçlar" />
<ListItemText primary={t('common.tools')} />
</ListItemButton>
</ListItem>
</List>
@@ -768,7 +785,7 @@ function App(): JSX.Element {
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'settings'} onClick={() => setActiveTab('settings')}>
<ListItemIcon><SettingsIcon color={activeTab === 'settings' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary="Genel Ayarlar" />
<ListItemText primary={t('settings.general')} />
</ListItemButton>
</ListItem>
</List>
@@ -788,19 +805,19 @@ function App(): JSX.Element {
<Tab label="MariaDB" icon={<DbIcon />} iconPosition="start" />
<Tab label="Nginx" icon={<WebIcon />} iconPosition="start" />
<Tab label="Apache" icon={<ServerIcon />} iconPosition="start" />
<Tab label="Sunucu Ayarları" icon={<ServerIcon />} iconPosition="start" />
<Tab label={t('settings.server_settings')} icon={<ServerIcon />} iconPosition="start" />
</Tabs>
</Box>
{settingsTab === 0 && (
<Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
<Box sx={{ p: 2, borderBottom: '1px solid rgba(255, 255, 255, 0.05)' }}>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>PHP Sürümleri</Typography>
<Typography variant="body2" color="text.secondary">Gereken PHP sürümlerini buradan indirebilir ve yönetebilirsiniz.</Typography>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>{t('settings.php_versions')}</Typography>
<Typography variant="body2" color="text.secondary">{t('settings.php_versions_desc')}</Typography>
</Box>
<List sx={{ maxHeight: 500, overflow: 'auto' }}>
{phpVersions.length === 0 && (
<ListItem><ListItemText primary="Yükleniyor veya liste boş..." /></ListItem>
<ListItem><ListItemText primary={t('common.loading')} /></ListItem>
)}
{phpVersions.map((v) => (
<ListItem key={v.id} sx={{ py: 2 }}>
@@ -809,13 +826,13 @@ function App(): JSX.Element {
</ListItemIcon>
<ListItemText
primary={`PHP ${v.version}`}
secondary={v.status === 'downloading' ? `İndiriliyor... %${Math.round(v.progress)}` : v.status === 'installed' ? 'Kurulu' : 'İndirilebilir'}
secondary={v.status === 'downloading' ? `${t('common.loading')} %${Math.round(v.progress)}` : v.status === 'installed' ? t('php.extensions.active') : t('common.available')}
sx={{ flexGrow: 1 }}
/>
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
{v.status === 'available' && (
<Button variant="outlined" size="small" startIcon={<DownloadIcon />} onClick={() => handleDownloadPhp(v.id)}>
İNDİR
{t('common.download')}
</Button>
)}
{v.status === 'downloading' && (
@@ -826,9 +843,9 @@ function App(): JSX.Element {
{v.status === 'installed' && (
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<Button variant="outlined" size="small" startIcon={<ToolsIcon />} onClick={() => handleOpenPhpExtensions(v.version)}>
EKLENTİLER
{t('php.extensions.title', { version: '' }).replace(' ', ' ')}
</Button>
<Chip label="HAZIR" color="success" variant="outlined" size="small" />
<Chip label={t('common.ok').toUpperCase()} color="success" variant="outlined" size="small" />
</Box>
)}
</Box>
@@ -841,12 +858,12 @@ function App(): JSX.Element {
{settingsTab === 1 && (
<Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
<Box sx={{ p: 2, borderBottom: '1px solid rgba(255, 255, 255, 0.05)' }}>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>MariaDB Sürümleri</Typography>
<Typography variant="body2" color="text.secondary">Farklı MariaDB sürümlerini indirebilir ve sunucunuzda kullanabilirsiniz.</Typography>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>{t('settings.mariadb_versions')}</Typography>
<Typography variant="body2" color="text.secondary">{t('settings.mariadb_versions_desc')}</Typography>
</Box>
<List sx={{ maxHeight: 500, overflow: 'auto' }}>
{mariaDbVersions.length === 0 && (
<ListItem><ListItemText primary="Yükleniyor veya liste boş..." /></ListItem>
<ListItem><ListItemText primary={t('common.loading')} /></ListItem>
)}
{mariaDbVersions.map((v) => (
<ListItem key={v.id} sx={{ py: 2 }}>
@@ -854,15 +871,8 @@ function App(): JSX.Element {
{v.status === 'installed' ? <InstalledIcon color="success" /> : <AvailableIcon color="action" />}
</ListItemIcon>
<ListItemText
primary={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{`MariaDB ${v.version} (${v.cycle})`}
{v.description && (
<Chip label={v.description} size="small" variant="outlined" sx={{ fontSize: '0.7rem', height: 20 }} />
)}
</Box>
}
secondary={v.status === 'downloading' ? `İndiriliyor... %${Math.round(v.progress)}` : v.status === 'installed' ? 'Kurulu' : 'İndirilebilir'}
primary={`MariaDB ${v.version}`}
secondary={v.status === 'downloading' ? `${t('common.loading')} %${Math.round(v.progress)}` : v.status === 'installed' ? t('php.extensions.active') : t('common.available')}
sx={{ flexGrow: 1 }}
/>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
@@ -883,7 +893,7 @@ function App(): JSX.Element {
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
{v.status === 'available' && (
<Button variant="outlined" size="small" startIcon={<DownloadIcon />} onClick={() => handleDownloadMariaDb(v.id)}>
İNDİR
{t('common.download')}
</Button>
)}
{v.status === 'downloading' && (
@@ -892,7 +902,7 @@ function App(): JSX.Element {
</Box>
)}
{v.status === 'installed' && (
<Chip label="HAZIR" color="success" variant="outlined" size="small" />
<Chip label={t('common.ok').toUpperCase()} color="success" variant="outlined" size="small" />
)}
</Box>
</Box>
@@ -905,24 +915,27 @@ function App(): JSX.Element {
{settingsTab === 2 && (
<Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
<Box sx={{ p: 2, borderBottom: '1px solid rgba(255, 255, 255, 0.05)' }}>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>Nginx Web Sunucusu</Typography>
<Typography variant="body2" color="text.secondary">Sunucu için gerekli Nginx binary dosyalarını buradan indirebilirsiniz.</Typography>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>{t('settings.nginx_versions')}</Typography>
<Typography variant="body2" color="text.secondary">{t('settings.nginx_versions_desc')}</Typography>
</Box>
<List>
<List sx={{ maxHeight: 500, overflow: 'auto' }}>
{nginxVersions.length === 0 && (
<ListItem><ListItemText primary={t('common.loading')} /></ListItem>
)}
{nginxVersions.map((v) => (
<ListItem key={v.id} sx={{ py: 2 }}>
<ListItemIcon>
{v.status === 'installed' ? <InstalledIcon color="success" /> : <AvailableIcon color="action" />}
</ListItemIcon>
<ListItemText
primary={`Nginx ${v.version} (Stable)`}
secondary={v.status === 'downloading' ? `İndiriliyor... %${Math.round(v.progress)}` : v.status === 'installed' ? 'Kurulu' : 'İndirilebilir'}
primary={`Nginx ${v.version}`}
secondary={v.status === 'downloading' ? `${t('common.loading')} %${Math.round(v.progress)}` : v.status === 'installed' ? t('php.extensions.active') : t('common.available')}
sx={{ flexGrow: 1 }}
/>
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
{v.status === 'available' && (
<Button variant="outlined" size="small" startIcon={<DownloadIcon />} onClick={() => handleDownloadNginx(v.id)}>
İNDİR
{t('common.download')}
</Button>
)}
{v.status === 'downloading' && (
@@ -931,7 +944,7 @@ function App(): JSX.Element {
</Box>
)}
{v.status === 'installed' && (
<Chip label="HAZIR" color="success" variant="outlined" size="small" />
<Chip label={t('common.ok').toUpperCase()} color="success" variant="outlined" size="small" />
)}
</Box>
</ListItem>
@@ -943,10 +956,13 @@ function App(): JSX.Element {
{settingsTab === 3 && (
<Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
<Box sx={{ p: 2, borderBottom: '1px solid rgba(255, 255, 255, 0.05)' }}>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>Apache Web Sunucusu</Typography>
<Typography variant="body2" color="text.secondary">Apache HTTP Server binary dosyalarını buradan yönetebilirsiniz.</Typography>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>{t('settings.apache_versions')}</Typography>
<Typography variant="body2" color="text.secondary">{t('settings.apache_versions_desc')}</Typography>
</Box>
<List>
<List sx={{ maxHeight: 500, overflow: 'auto' }}>
{apacheVersions.length === 0 && (
<ListItem><ListItemText primary={t('common.loading')} /></ListItem>
)}
{apacheVersions.map((v) => (
<ListItem key={v.id} sx={{ py: 2 }}>
<ListItemIcon>
@@ -954,13 +970,13 @@ function App(): JSX.Element {
</ListItemIcon>
<ListItemText
primary={`Apache ${v.version}`}
secondary={v.status === 'downloading' ? `İndiriliyor... %${Math.round(v.progress)}` : v.status === 'installed' ? 'Kurulu' : 'İndirilebilir'}
secondary={v.status === 'downloading' ? `${t('common.loading')} %${Math.round(v.progress)}` : v.status === 'installed' ? t('php.extensions.active') : t('common.available')}
sx={{ flexGrow: 1 }}
/>
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
{v.status === 'available' && (
<Button variant="outlined" size="small" startIcon={<DownloadIcon />} onClick={() => handleDownloadApache(v.id)}>
İNDİR
{t('common.download')}
</Button>
)}
{v.status === 'downloading' && (
@@ -971,9 +987,9 @@ function App(): JSX.Element {
{v.status === 'installed' && (
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<Button variant="outlined" size="small" startIcon={<SettingsIcon />} onClick={handleOpenApacheConfig}>
YAPILANDIRMA
{t('common.settings').toUpperCase()}
</Button>
<Chip label="HAZIR" color="success" variant="outlined" size="small" />
<Chip label={t('common.ok').toUpperCase()} color="success" variant="outlined" size="small" />
</Box>
)}
</Box>
@@ -984,78 +1000,50 @@ function App(): JSX.Element {
)}
{settingsTab === 4 && (
<Box component={Stack} spacing={3}>
<Paper sx={{ p: 3, bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
<Typography variant="h6" gutterBottom>Port Ayarları</Typography>
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 3, mt: 2 }}>
<FormControl variant="filled" size="small" fullWidth>
<InputLabel>Varsayılan Web Sunucusu</InputLabel>
<Paper sx={{ p: 4, bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
<Typography variant="h6" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<SettingsIcon color="primary" />
{t('settings.server')}
</Typography>
<Grid container spacing={3} sx={{ mt: 1 }}>
<Grid item xs={12} sm={6}>
<TextField variant="outlined" fullWidth label={t('settings.nginx_port')} value={settings.nginxPort} onChange={(e) => setSettings({ ...settings, nginxPort: e.target.value })} />
</Grid>
<Grid item xs={12} sm={6}>
<TextField variant="outlined" fullWidth label={t('settings.apache_port')} value={settings.apachePort} onChange={(e) => setSettings({ ...settings, apachePort: e.target.value })} />
</Grid>
<Grid item xs={12} sm={6}>
<TextField variant="outlined" fullWidth label={t('settings.php_port')} value={settings.phpPort} onChange={(e) => setSettings({ ...settings, phpPort: e.target.value })} />
</Grid>
<Grid item xs={12} sm={6}>
<TextField variant="outlined" fullWidth label={t('settings.mariadb_port')} value={settings.mariaDbPort} onChange={(e) => setSettings({ ...settings, mariaDbPort: e.target.value })} />
</Grid>
<Grid item xs={12}>
<FormControl fullWidth>
<InputLabel>{t('settings.server_type_select')}</InputLabel>
<Select
value={settings.serverType || 'nginx'}
value={settings.serverType}
label={t('settings.server_type_select')}
onChange={(e) => setSettings({ ...settings, serverType: e.target.value as any })}
>
<MenuItem value="nginx">Nginx (Performans)</MenuItem>
<MenuItem value="apache">Apache (htaccess Desteği)</MenuItem>
<MenuItem value="nginx">Nginx</MenuItem>
<MenuItem value="apache">Apache</MenuItem>
</Select>
</FormControl>
<TextField
label="Nginx Portu"
value={settings.nginxPort}
onChange={(e) => setSettings({ ...settings, nginxPort: e.target.value })}
variant="filled"
size="small"
helperText="Varsayılan: 80"
</Grid>
<Grid item xs={12}>
<FormControlLabel
control={<Switch checked={settings.allowRemoteAccess} onChange={(e) => setSettings({ ...settings, allowRemoteAccess: e.target.checked })} />}
label={t('settings.remote_access')}
/>
<TextField
label="Apache Portu"
value={settings.apachePort}
onChange={(e) => setSettings({ ...settings, apachePort: e.target.value })}
variant="filled"
size="small"
helperText="Varsayılan: 8080"
/>
<TextField
label="PHP FastCGI Portu"
value={settings.phpPort}
onChange={(e) => setSettings({ ...settings, phpPort: e.target.value })}
variant="filled"
size="small"
helperText="Varsayılan: 9001"
/>
<TextField
label="MariaDB Portu"
value={settings.mariaDbPort}
onChange={(e) => setSettings({ ...settings, mariaDbPort: e.target.value })}
variant="filled"
size="small"
helperText="Varsayılan: 3306"
/>
</Box>
<Divider sx={{ my: 2, bgcolor: 'rgba(255,255,255,0.05)' }} />
<FormControlLabel
control={
<Switch
checked={settings.allowRemoteAccess}
onChange={(e) => {
const newSettings = { ...settings, allowRemoteAccess: e.target.checked }
setSettings(newSettings)
}}
/>
}
label="Dış Erişime İzin Ver (0.0.0.0)"
/>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block' }}>
{settings.allowRemoteAccess
? "Sunucu yerel ağdaki diğer cihazlardan erişilebilir."
: "Sunucu sadece bu bilgisayardan (127.0.0.1) erişilebilir."}
</Typography>
</Paper>
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button variant="contained" startIcon={<SaveIcon />} size="large" onClick={handleSaveSettings}>
Ayarları Kaydet
</Button>
</Box>
</Box>
</Grid>
<Grid item xs={12} sx={{ mt: 2 }}>
<Button variant="contained" color="primary" size="large" startIcon={<SaveIcon />} onClick={handleSaveSettings} fullWidth>
{t('common.save')}
</Button>
</Grid>
</Grid>
</Paper>
)}
</Box>
)}
@@ -1094,12 +1082,12 @@ function App(): JSX.Element {
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>Port: {settings.nginxPort}</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', gap: 1 }}>
<IconButton onClick={() => handleOpenLogs('nginx')} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
<IconButton onClick={() => handleOpenLogs('nginx')} title={t('diagnostics.logs')} size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
<TerminalIcon fontSize="small" />
</IconButton>
<IconButton
onClick={handleOpenNginxConfig}
title="Nginx Yapılandırması"
title={t('server.nginx.config')}
size="small"
sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'primary.main' } }}
>
@@ -1111,7 +1099,7 @@ function App(): JSX.Element {
setNotification({ open: true, message: res.message, severity: res.success ? 'success' : 'error' })
})
}}
title="Yapılandırmayı Yeniden Yükle"
title={t('server.nginx.save_reload')}
size="small"
disabled={status.nginx !== 'running'}
sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'primary.main' } }}
@@ -1148,12 +1136,12 @@ function App(): JSX.Element {
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>Port: {settings.apachePort}</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', gap: 1 }}>
<IconButton onClick={() => handleOpenLogs('apache')} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
<IconButton onClick={() => handleOpenLogs('apache')} title={t('diagnostics.logs')} size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
<TerminalIcon fontSize="small" />
</IconButton>
<IconButton
onClick={handleOpenApacheConfig}
title="Apache Yapılandırması"
title={t('server.apache.config')}
size="small"
sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'primary.main' } }}
>
@@ -1220,10 +1208,10 @@ function App(): JSX.Element {
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>Port: {settings.mariaDbPorts?.[v.id] || v.port}</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', gap: 1 }}>
<IconButton onClick={() => handleOpenLogs(`mariadb:${v.version}`)} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
<IconButton onClick={() => handleOpenLogs(`mariadb:${v.version}`)} title={t('diagnostics.logs')} size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
<TerminalIcon fontSize="small" />
</IconButton>
<IconButton onClick={() => handleResetData(`mariadb:${v.version}`)} title="Verileri Sıfırla" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'error.main' } }}>
<IconButton onClick={() => handleResetData(`mariadb:${v.version}`)} title={t('common.reset')} size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'error.main' } }}>
<ResetIcon fontSize="small" />
</IconButton>
</Box>
@@ -1262,7 +1250,7 @@ function App(): JSX.Element {
<Box sx={{ flexGrow: 1 }}>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>phpMyAdmin</Typography>
<Typography variant="body2" color="text.secondary">
{pmaInstalled ? 'Veritabanı yönetimi için phpMyAdmin hazır.' : 'Veritabanlarını web üzerinden yönetmek için phpMyAdmin kurun.'}
{pmaInstalled ? t('pma.ready_desc') : t('pma.install_desc')}
</Typography>
</Box>
<Box>
@@ -1275,7 +1263,7 @@ function App(): JSX.Element {
startIcon={pmaLoading ? <CircularProgress size={20} /> : <DownloadIcon />}
sx={{ borderRadius: 2 }}
>
{pmaLoading ? 'KURULUYOR...' : 'KURULUMU BAŞLAT'}
{pmaLoading ? t('pma.installing') : t('pma.install_button')}
</Button>
) : (
<Button
@@ -1285,7 +1273,7 @@ function App(): JSX.Element {
startIcon={<OpenIcon />}
sx={{ borderRadius: 2 }}
>
{t('pma.open')}
</Button>
)}
</Box>
@@ -1294,7 +1282,7 @@ function App(): JSX.Element {
<Box sx={{ mt: 3 }}>
<LinearProgress variant="determinate" value={pmaProgress} sx={{ height: 8, borderRadius: 4 }} />
<Typography variant="caption" sx={{ mt: 1, display: 'block', textAlign: 'center', color: 'primary.main', fontWeight: 'bold' }}>
İndiriliyor... %{Math.round(pmaProgress)}
{t('pma.progress', { progress: Math.round(pmaProgress) })}
</Typography>
</Box>
)}
@@ -1306,9 +1294,9 @@ function App(): JSX.Element {
<DbIcon color="primary" sx={{ fontSize: 32 }} />
</Box>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MariaDB Veri İçe Aktar</Typography>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>{t('mariadb.import.title')}</Typography>
<Typography variant="body2" color="text.secondary">
Seçtiğiniz bir .sql veya arşiv dosyasını hızlıca veritabanına yükleyin.
{t('mariadb.import.desc')}
</Typography>
</Box>
<Box>
@@ -1319,7 +1307,7 @@ function App(): JSX.Element {
startIcon={<DbIcon />}
sx={{ borderRadius: 2 }}
>
İÇE AKTAR
{t('mariadb.import.button')}
</Button>
</Box>
</Box>
@@ -1331,9 +1319,9 @@ function App(): JSX.Element {
<DbIcon color="secondary" sx={{ fontSize: 32 }} />
</Box>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MariaDB Veri Dışa Aktar</Typography>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>{t('mariadb.export.title')}</Typography>
<Typography variant="body2" color="text.secondary">
Veritabanlarını .sql veya .sql.gz olarak güvenle yedekleyin.
{t('mariadb.export.desc')}
</Typography>
</Box>
<Box>
@@ -1345,7 +1333,7 @@ function App(): JSX.Element {
startIcon={<DbIcon />}
sx={{ borderRadius: 2 }}
>
DIŞA AKTAR
{t('mariadb.export.button')}
</Button>
</Box>
</Box>
@@ -1358,16 +1346,16 @@ function App(): JSX.Element {
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Typography variant="h5" sx={{ fontWeight: 'medium' }}>
Projelerim
{t('projects.title')}
</Typography>
<Button variant="contained" startIcon={<ProjectIcon />} onClick={() => setIsProjectDialogOpen(true)}>
Yeni Proje Ekle
{t('projects.add_new')}
</Button>
</Box>
{projects.length === 0 ? (
<Paper sx={{ p: 4, textAlign: 'center', bgcolor: 'rgba(255, 255, 255, 0.03)' }}>
<Typography variant="body1" color="text.secondary">Henüz bir proje eklenmemiş.</Typography>
<Typography variant="body1" color="text.secondary">{t('projects.empty')}</Typography>
</Paper>
) : (
<Grid container spacing={3}>
@@ -1405,7 +1393,7 @@ function App(): JSX.Element {
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 0.5, flexShrink: 0 }}>
<Tooltip title="Düzenle">
<Tooltip title={t('common.edit')}>
<IconButton size="small" onClick={() => {
setNewProject(project)
setIsProjectDialogOpen(true)
@@ -1414,7 +1402,7 @@ function App(): JSX.Element {
</IconButton>
</Tooltip>
{project.serverType !== 'apache' && (
<Tooltip title="Nginx Ayarları">
<Tooltip title={t('server.nginx.config')}>
<IconButton size="small" onClick={() => handleOpenProjectNginxConfig(project.host)} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}>
<TerminalIcon fontSize="small" />
</IconButton>
@@ -1474,7 +1462,7 @@ function App(): JSX.Element {
'&:hover': { bgcolor: 'rgba(57, 167, 255, 0.1)' }
}}
>
Gözat
{t('projects.browse')}
</Button>
</Paper>
</Stack>
@@ -1614,17 +1602,17 @@ function App(): JSX.Element {
setIsProjectDialogOpen(false)
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost', serverType: 'nginx' })
}} fullWidth maxWidth="sm">
<DialogTitle>{newProject.id ? 'Proje Düzenle' : 'Yeni Proje Ekle'}</DialogTitle>
<DialogTitle>{newProject.id ? t('projects.edit') : t('projects.add_new')}</DialogTitle>
<DialogContent>
<Box sx={{ mt: 2, display: 'flex', flexDirection: 'column', gap: 2 }}>
<TextField
label="Proje Adı"
label={t('projects.name')}
fullWidth
value={newProject.name}
onChange={(e) => setNewProject({ ...newProject, name: e.target.value })}
/>
<TextField
label="Dosya Yolu"
label={t('projects.path')}
fullWidth
value={newProject.path}
InputProps={{
@@ -1639,17 +1627,17 @@ function App(): JSX.Element {
onChange={(e) => setNewProject({ ...newProject, path: e.target.value })}
/>
<TextField
label="Hostname (örn: local.test)"
label={t('projects.hostname_placeholder')}
fullWidth
value={newProject.host}
onChange={(e) => setNewProject({ ...newProject, host: e.target.value })}
/>
<FormControl fullWidth disabled={installedPhpVersions.length === 0}>
<InputLabel id="php-version-label">PHP Sürümü</InputLabel>
<InputLabel id="php-version-label">{t('projects.php_version')}</InputLabel>
<Select
labelId="php-version-label"
value={newProject.phpVersion}
label="PHP Sürümü"
label={t('projects.php_version')}
onChange={(e) => setNewProject({ ...newProject, phpVersion: e.target.value })}
>
{installedPhpVersions.length > 0 ? (
@@ -1657,20 +1645,20 @@ function App(): JSX.Element {
<MenuItem key={v.id} value={v.version}>PHP {v.version}</MenuItem>
))
) : (
<MenuItem disabled value="">Önce PHP sürümü indirmelisiniz</MenuItem>
<MenuItem disabled value="">{t('projects.no_php_warning')}</MenuItem>
)}
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel id="server-type-label">Web Sunucusu</InputLabel>
<InputLabel id="server-type-label">{t('projects.server_type')}</InputLabel>
<Select
labelId="server-type-label"
value={newProject.serverType || 'nginx'}
label="Web Sunucusu"
label={t('projects.server_type')}
onChange={(e) => setNewProject({ ...newProject, serverType: e.target.value as any })}
>
<MenuItem value="nginx">Nginx</MenuItem>
<MenuItem value="apache">Apache (htaccess desteği)</MenuItem>
<MenuItem value="apache">{t('projects.apache_htaccess')}</MenuItem>
</Select>
</FormControl>
</Box>
@@ -1679,13 +1667,13 @@ function App(): JSX.Element {
<Button onClick={() => {
setIsProjectDialogOpen(false)
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost', serverType: 'nginx' })
}}>İptal</Button>
}}>{t('common.cancel')}</Button>
<Button
onClick={handleAddProject}
variant="contained"
disabled={!newProject.name || !newProject.path || installedPhpVersions.length === 0}
>
{newProject.id ? 'Güncelle' : 'Ekle'}
{newProject.id ? t('common.save') : t('common.save')}
</Button>
</DialogActions>
</Dialog>
@@ -1717,14 +1705,14 @@ function App(): JSX.Element {
}}
>
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="h6">Nginx Yapılandırması (nginx.conf)</Typography>
<Typography variant="h6">{t('server.nginx.config_title')}</Typography>
<IconButton onClick={() => setIsNginxConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.5)' }}>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent dividers sx={{ borderColor: 'rgba(255,255,255,0.1)' }}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.4)', mb: 2, display: 'block' }}>
Dikkat: Hatalı yapılandırma Nginx'in çalışmasını durdurabilir. Değişiklik yapmadan önce test edin.
{t('server.nginx.config_warning')}
</Typography>
<NginxCodeEditor
value={nginxConfig}
@@ -1738,17 +1726,17 @@ function App(): JSX.Element {
startIcon={<RefreshIcon />}
sx={{ color: '#fff', borderColor: 'rgba(255,255,255,0.3)' }}
>
Yapılandırmayı Test Et
{t('server.nginx.test_config')}
</Button>
<Button onClick={() => setIsNginxConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.6)' }}>
İptal
{t('common.cancel')}
</Button>
<Button
onClick={handleSaveNginxConfig}
variant="contained"
startIcon={<SaveIcon />}
>
Kaydet ve Yeniden Yükle
{t('server.nginx.save_reload')}
</Button>
</DialogActions>
</Dialog>
@@ -1768,14 +1756,14 @@ function App(): JSX.Element {
}}
>
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="h6">{selectedProjectHost} Nginx Yapılandırması</Typography>
<Typography variant="h6">{t('server.project_nginx.title', { host: selectedProjectHost })}</Typography>
<IconButton onClick={() => setIsProjectNginxConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.5)' }}>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent dividers sx={{ borderColor: 'rgba(255,255,255,0.1)' }}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.4)', mb: 2, display: 'block' }}>
İpucu: Bu yapılandırma sadece {selectedProjectHost} projesi için geçerlidir.
{t('server.project_nginx.tip', { host: selectedProjectHost })}
</Typography>
<NginxCodeEditor
value={projectNginxConfig}
@@ -1798,7 +1786,7 @@ function App(): JSX.Element {
.replace(/successful/g, '<span style="color: #33d9b2;">successful</span>')
Swal.fire({
title: result.success ? 'Başarılı' : 'Yapılandırma Hatası',
title: result.success ? t('common.success') : t('server.nginx.test_failed'),
html: `
<div class="nginx-error-container">
${formattedMessage}
@@ -1806,7 +1794,7 @@ function App(): JSX.Element {
${!result.success && errorInfo ? `
<div style="margin-top: 15px; text-align: center;">
<p style="font-size: 0.9rem; color: #ffb142;">
<b>Hata Satırı:</b> ${errorInfo.line}
<b>{t('server.project_nginx.error_line', { line: errorInfo.line })}</b>
</p>
</div>
` : ''}
@@ -1822,17 +1810,17 @@ function App(): JSX.Element {
startIcon={<RefreshIcon />}
sx={{ color: '#fff', borderColor: 'rgba(255,255,255,0.3)' }}
>
Yapılandırmayı Test Et
{t('server.nginx.test_config')}
</Button>
<Button onClick={() => setIsProjectNginxConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.6)' }}>
İptal
{t('common.cancel')}
</Button>
<Button
onClick={handleSaveProjectNginxConfig}
variant="contained"
startIcon={<SaveIcon />}
>
Kaydet ve Yeniden Yükle
{t('server.nginx.save_reload')}
</Button>
</DialogActions>
</Dialog>
@@ -1851,14 +1839,14 @@ function App(): JSX.Element {
}}
>
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="h6">Apache (httpd.conf) Yapılandırması</Typography>
<Typography variant="h6">{t('server.apache.config_title')}</Typography>
<IconButton onClick={() => setIsApacheConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.5)' }}>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent dividers sx={{ borderColor: 'rgba(255,255,255,0.1)' }}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.4)', mb: 2, display: 'block' }}>
Düzenlediğiniz bu dosya Apache sunucusunun ana (global) yapılandırma dosyasıdır.
{t('server.apache.config_desc')}
</Typography>
<ApacheCodeEditor
value={apacheConfig}
@@ -1872,13 +1860,13 @@ function App(): JSX.Element {
startIcon={<RefreshIcon />}
sx={{ color: '#fff', borderColor: 'rgba(255,255,255,0.3)' }}
>
Yapılandırmayı Test Et
{t('server.apache.test_config')}
</Button>
<Button onClick={() => setIsApacheConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.6)' }}>
İptal
{t('common.cancel')}
</Button>
<Button onClick={handleSaveApacheConfig} variant="contained" color="primary" startIcon={<SaveIcon />}>
Kaydet ve Yeniden Yükle
{t('server.apache.save_reload')}
</Button>
</DialogActions>
</Dialog>
@@ -29,6 +29,7 @@ import {
Save as SaveIcon
} from '@mui/icons-material'
import Swal from 'sweetalert2'
import { useTranslation } from 'react-i18next'
interface MariaDbExportWizardProps {
open: boolean
@@ -36,14 +37,15 @@ interface MariaDbExportWizardProps {
mariaDbVersions: any[]
}
const steps = [
'Sunucu Seçimi',
'Veritabanı Seçimi',
'Dışa Aktarma Ayarları',
'Özet ve İşlem'
]
const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose, mariaDbVersions }) => {
const { t } = useTranslation()
const steps = [
t('mariadb.export.step1'),
t('mariadb.export.step2'),
t('mariadb.export.step3'),
t('mariadb.export.step4')
]
const [activeStep, setActiveStep] = useState(0)
const [selectedVersion, setSelectedVersion] = useState('')
const [databases, setDatabases] = useState<string[]>([])
@@ -79,7 +81,7 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
const handleStep1Next = async () => {
if (!selectedVersion) {
Swal.fire('Hata', 'Lütfen bir MariaDB sunucusu seçin.', 'error')
Swal.fire(t('common.error'), t('mariadb.export.step1_desc'), 'error')
return
}
setLoading(true)
@@ -88,7 +90,7 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
setDatabases(dbs)
nextStep()
} catch (err: any) {
Swal.fire('Hata', 'Veritabanları listelenemedi: ' + err.message, 'error')
Swal.fire(t('common.error'), t('mariadb.export.list_error') + err.message, 'error')
} finally {
setLoading(false)
}
@@ -103,7 +105,7 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
const startExport = async () => {
setLoading(true)
setLogs(['[Sistem] Dışa aktarma işlemi başlatılıyor...'])
setLogs([t('mariadb.export.starting')])
const removeListener = window.api.onMariaDbExportLog((msg: string) => {
setLogs(prev => [...prev, msg])
@@ -131,12 +133,12 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
case 0:
return (
<Box sx={{ mt: 2 }}>
<Typography variant="body1" gutterBottom>Veritabanın hangi MariaDB sunucusunda olduğunu seçin.</Typography>
<Typography variant="body1" gutterBottom>{t('mariadb.export.step1_desc')}</Typography>
<FormControl fullWidth sx={{ mt: 2 }}>
<InputLabel>MariaDB Sunucusu</InputLabel>
<InputLabel>{t('mariadb.import.server')}</InputLabel>
<Select
value={selectedVersion}
label="MariaDB Sunucusu"
label={t('mariadb.import.server')}
onChange={(e) => setSelectedVersion(e.target.value)}
>
{mariaDbVersions.filter(v => v.status === 'installed').map(v => (
@@ -147,25 +149,25 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
</Select>
</FormControl>
<TextField
label="Root Şifresi (Eğer varsa)"
label={t('mariadb.import.root_pass')}
fullWidth
sx={{ mt: 3 }}
value={rootPass}
type="password"
onChange={(e) => setRootPass(e.target.value)}
helperText="MariaDB 'root' kullanıcısının şifresi. Boşsa boş bırakın."
helperText={t('mariadb.import.root_pass_helper')}
/>
</Box>
)
case 1:
return (
<Box sx={{ mt: 2 }}>
<Typography variant="body1" gutterBottom>Dışa aktarmak istediğiniz veritabanını seçin.</Typography>
<Typography variant="body1" gutterBottom>{t('mariadb.export.step2_desc')}</Typography>
<FormControl fullWidth sx={{ mt: 2 }}>
<InputLabel>Veritabanı</InputLabel>
<InputLabel>{t('mariadb.export.db')}</InputLabel>
<Select
value={selectedDb}
label="Veritabanı"
label={t('mariadb.export.db')}
onChange={(e) => setSelectedDb(e.target.value)}
>
{databases.map(db => (
@@ -175,7 +177,7 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
</FormControl>
{databases.length === 0 && (
<Typography variant="caption" color="error" sx={{ mt: 1, display: 'block' }}>
Seçili sunucuda erişilebilir veritabanı bulunamadı.
{t('mariadb.export.no_db_found')}
</Typography>
)}
</Box>
@@ -183,17 +185,17 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
case 2:
return (
<Box sx={{ mt: 2, display: 'flex', flexDirection: 'column', gap: 3 }}>
<Typography variant="body1">Dosyanın kaydedileceği yeri ve formatını belirleyin.</Typography>
<Typography variant="body1">{t('mariadb.export.step3_desc')}</Typography>
<FormControlLabel
control={<Checkbox checked={compress} onChange={(e) => setCompress(e.target.checked)} />}
label="Gzip ile Sıkıştır (.sql.gz)"
label={t('mariadb.export.compress')}
/>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
<TextField
fullWidth
variant="outlined"
value={savePath}
placeholder="Kaydedilecek dosya..."
placeholder={t('mariadb.export.placeholder')}
disabled
InputProps={{
startAdornment: (
@@ -203,7 +205,7 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
),
}}
/>
<Button variant="contained" onClick={handleSelectSavePath} sx={{ minWidth: '120px' }}>SEÇ</Button>
<Button variant="contained" onClick={handleSelectSavePath} sx={{ minWidth: '120px' }}>{t('common.select')}</Button>
</Box>
</Box>
)
@@ -213,13 +215,13 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
{!exportResult ? (
<>
<Paper sx={{ p: 2, bgcolor: 'rgba(255,255,255,0.05)', borderRadius: 2 }}>
<Typography variant="subtitle2" color="primary" sx={{ fontWeight: 'bold' }}>Özet Bilgiler</Typography>
<Typography variant="subtitle2" color="primary" sx={{ fontWeight: 'bold' }}>{t('mariadb.import.summary')}</Typography>
<Divider sx={{ my: 1 }} />
<Stack spacing={1}>
<Typography variant="body2"><strong>Sunucu:</strong> {selectedVersion}</Typography>
<Typography variant="body2"><strong>Veritabanı:</strong> {selectedDb}</Typography>
<Typography variant="body2"><strong>Hedef:</strong> {savePath}</Typography>
<Typography variant="body2"><strong>Sıkıştırma:</strong> {compress ? 'Açık (.gz)' : 'Kapalı (.sql)'}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.server')}:</strong> {selectedVersion}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.db')}:</strong> {selectedDb}</Typography>
<Typography variant="body2"><strong>{t('mariadb.export.target')}:</strong> {savePath}</Typography>
<Typography variant="body2"><strong>{t('mariadb.export.compression')}:</strong> {compress ? t('mariadb.export.on') : t('mariadb.export.off')}</Typography>
</Stack>
</Paper>
{loading && (
@@ -238,20 +240,20 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
{exportResult.success ? (
<>
<SuccessIcon color="success" sx={{ fontSize: 60 }} />
<Typography variant="h6" sx={{ mt: 2 }}>İşlem Başarılı!</Typography>
<Typography variant="h6" sx={{ mt: 2 }}>{t('mariadb.import.success_title')}</Typography>
<Typography variant="body2" color="text.secondary">{exportResult.message}</Typography>
<Button
variant="outlined"
sx={{ mt: 3 }}
onClick={() => window.api.invoke('shell:open-path', savePath.substring(0, savePath.lastIndexOf('\\')))}
>
DOSYAYI GÖSTER
{t('mariadb.export.btn_show_file')}
</Button>
</>
) : (
<>
<ErrorIcon color="error" sx={{ fontSize: 60 }} />
<Typography variant="h6" sx={{ mt: 2 }}>Hata Oluştu!</Typography>
<Typography variant="h6" sx={{ mt: 2 }}>{t('mariadb.import.error_title')}</Typography>
<Typography variant="body2" color="error">{exportResult.message}</Typography>
</>
)}
@@ -260,13 +262,13 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
</Box>
)
default:
return 'Bilinmeyen adım'
return t('common.error')
}
}
return (
<Dialog open={open} onClose={loading ? undefined : onClose} maxWidth="sm" fullWidth>
<DialogTitle>MariaDB Veritabanı Dışa Aktarma Sihirbazı</DialogTitle>
<DialogTitle>{t('mariadb.export.wizard_title')}</DialogTitle>
<DialogContent>
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
{steps.map((label) => (
@@ -280,25 +282,25 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
</DialogContent>
<DialogActions>
<Button onClick={onClose} disabled={loading}>KAPAT</Button>
<Button onClick={onClose} disabled={loading}>{t('common.close')}</Button>
<Box sx={{ flexGrow: 1 }} />
{activeStep > 0 && activeStep < 3 && !exportResult && (
<Button onClick={prevStep} disabled={loading}>GERİ</Button>
<Button onClick={prevStep} disabled={loading}>{t('common.back')}</Button>
)}
{activeStep === 0 && (
<Button variant="contained" onClick={handleStep1Next} disabled={loading}>
{loading ? <CircularProgress size={24} /> : 'İLERİ'}
{loading ? <CircularProgress size={24} /> : t('common.next')}
</Button>
)}
{activeStep === 1 && (
<Button variant="contained" onClick={nextStep} disabled={!selectedDb}>İLERİ</Button>
<Button variant="contained" onClick={nextStep} disabled={!selectedDb}>{t('common.next')}</Button>
)}
{activeStep === 2 && (
<Button variant="contained" onClick={nextStep} disabled={!savePath}>İLERİ</Button>
<Button variant="contained" onClick={nextStep} disabled={!savePath}>{t('common.next')}</Button>
)}
{activeStep === 3 && !exportResult && (
<Button variant="contained" color="primary" onClick={startExport} disabled={loading}>
{loading ? <CircularProgress size={24} /> : 'EXPORTU BAŞLAT'}
{loading ? <CircularProgress size={24} /> : t('mariadb.export.btn_start')}
</Button>
)}
</DialogActions>
@@ -29,6 +29,7 @@ import {
Error as ErrorIcon
} from '@mui/icons-material'
import Swal from 'sweetalert2'
import { useTranslation } from 'react-i18next'
interface MariaDbImportWizardProps {
open: boolean
@@ -36,14 +37,15 @@ interface MariaDbImportWizardProps {
mariaDbVersions: any[]
}
const steps = [
'Veri Seçimi',
'Sunucu Seçimi',
'Veritabanı Yapılandırması',
'Özet ve İşlem'
]
const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose, mariaDbVersions }) => {
const { t } = useTranslation()
const steps = [
t('mariadb.import.step1'),
t('mariadb.import.step2'),
t('mariadb.import.step3'),
t('mariadb.import.step4')
]
const [activeStep, setActiveStep] = useState(0)
const [filePath, setFilePath] = useState('')
const [selectedVersion, setSelectedVersion] = useState('')
@@ -103,7 +105,7 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
const handleStep2Next = () => {
if (!selectedVersion) {
Swal.fire('Hata', 'Lütfen bir MariaDB sunucusu seçin.', 'error')
Swal.fire(t('common.error'), t('mariadb.import.step2_desc'), 'error')
return
}
nextStep()
@@ -111,7 +113,7 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
const handleStep3Next = async () => {
if (!dbConfig.dbName || !dbConfig.user) {
Swal.fire('Hata', 'Lütfen veritabanı adı ve kullanıcıyı doldurun.', 'error')
Swal.fire(t('common.error'), t('mariadb.import.step3_desc'), 'error')
return
}
@@ -120,19 +122,19 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
const { exists } = await window.api.checkMariaDbDb(selectedVersion, dbConfig.dbName, dbConfig.rootPass)
if (exists) {
const result = await Swal.fire({
title: 'Veritabanı Mevcut!',
text: `'${dbConfig.dbName}' veritabanı zaten var. Üzerine yazmak istiyor musunuz? Mevcut tüm veriler silinecektir!`,
title: t('mariadb.import.overwrite_title'),
text: t('mariadb.import.overwrite_desc', { dbName: dbConfig.dbName }),
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Evet, Üzerine Yaz',
cancelButtonText: 'Hayır, Vazgeç',
confirmButtonText: t('common.yes_delete'),
cancelButtonText: t('common.cancel'),
confirmButtonColor: '#d33'
})
if (!result.isConfirmed) return
}
nextStep()
} catch (err: any) {
Swal.fire('Hata', err.message, 'error')
Swal.fire(t('common.error'), err.message, 'error')
} finally {
setLoading(false)
}
@@ -140,7 +142,7 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
const startImport = async () => {
setLoading(true)
setLogs(['[Sistem] İşlem başlatılıyor...'])
setLogs([t('mariadb.import.starting')])
const removeListener = window.api.onMariaDbImportLog((msg: string) => {
setLogs(prev => [...prev, msg])
@@ -170,13 +172,13 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
case 0:
return (
<Box sx={{ mt: 2, textAlign: 'center' }}>
<Typography variant="body1" gutterBottom>Lütfen yüklenecek olan .sql dosyasını veya arşivini (.zip, .tar.gz) seçin.</Typography>
<Typography variant="body1" gutterBottom>{t('mariadb.import.step1_desc')}</Typography>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', mt: 3 }}>
<TextField
fullWidth
variant="outlined"
value={filePath}
placeholder="Dosya seçilmedi..."
placeholder={t('mariadb.import.no_file')}
disabled
InputProps={{
startAdornment: (
@@ -186,19 +188,19 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
),
}}
/>
<Button variant="contained" onClick={handleSelectFile} sx={{ minWidth: '120px' }}>SEÇ</Button>
<Button variant="contained" onClick={handleSelectFile} sx={{ minWidth: '120px' }}>{t('common.select')}</Button>
</Box>
</Box>
)
case 1:
return (
<Box sx={{ mt: 2 }}>
<Typography variant="body1" gutterBottom>Dosyanın hangi MariaDB sunucusuna yükleneceğini seçin.</Typography>
<Typography variant="body1" gutterBottom>{t('mariadb.import.step2_desc')}</Typography>
<FormControl fullWidth sx={{ mt: 2 }}>
<InputLabel>MariaDB Sunucusu</InputLabel>
<InputLabel>{t('mariadb.import.server')}</InputLabel>
<Select
value={selectedVersion}
label="MariaDB Sunucusu"
label={t('mariadb.import.server')}
onChange={(e) => setSelectedVersion(e.target.value)}
>
{mariaDbVersions.filter(v => v.status === 'installed').map(v => (
@@ -209,34 +211,34 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
</Select>
</FormControl>
<TextField
label="Root Şifresi (Eğer varsa)"
label={t('mariadb.import.root_pass')}
fullWidth
sx={{ mt: 3 }}
value={dbConfig.rootPass}
type="password"
onChange={(e) => setDbConfig({ ...dbConfig, rootPass: e.target.value })}
helperText="MariaDB 'root' kullanıcısının mevcut şifresini girin. Boşsa boş bırakın."
helperText={t('mariadb.import.root_pass_helper')}
/>
</Box>
)
case 2:
return (
<Box sx={{ mt: 2, display: 'flex', flexDirection: 'column', gap: 3 }}>
<Typography variant="body1">Veritabanı ve kullanıcı bilgilerini yapılandırın.</Typography>
<Typography variant="body1">{t('mariadb.import.step3_desc')}</Typography>
<TextField
label="Veritabanı Adı"
label={t('mariadb.import.db_name')}
fullWidth
value={dbConfig.dbName}
onChange={(e) => setDbConfig({ ...dbConfig, dbName: e.target.value })}
/>
<TextField
label="Veritabanı Kullanıcısı"
label={t('mariadb.import.db_user')}
fullWidth
value={dbConfig.user}
onChange={(e) => setDbConfig({ ...dbConfig, user: e.target.value })}
/>
<TextField
label="Kullanıcı Şifresi"
label={t('mariadb.import.db_pass')}
fullWidth
value={dbConfig.pass}
type="text"
@@ -259,13 +261,13 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
{!importResult ? (
<>
<Paper sx={{ p: 2, bgcolor: 'rgba(255,255,255,0.05)', borderRadius: 2 }}>
<Typography variant="subtitle2" color="primary" sx={{ fontWeight: 'bold' }}>Özet Bilgiler</Typography>
<Typography variant="subtitle2" color="primary" sx={{ fontWeight: 'bold' }}>{t('mariadb.import.summary')}</Typography>
<Divider sx={{ my: 1 }} />
<Stack spacing={1}>
<Typography variant="body2"><strong>Dosya:</strong> {filePath.split(/[/\\]/).pop()}</Typography>
<Typography variant="body2"><strong>Sunucu:</strong> {selectedVersion}</Typography>
<Typography variant="body2"><strong>Veritabanı:</strong> {dbConfig.dbName}</Typography>
<Typography variant="body2"><strong>Kullanıcı:</strong> {dbConfig.user}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.file')}:</strong> {filePath.split(/[/\\]/).pop()}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.server')}:</strong> {selectedVersion}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.db')}:</strong> {dbConfig.dbName}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.user')}:</strong> {dbConfig.user}</Typography>
</Stack>
</Paper>
{loading && (
@@ -284,20 +286,20 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
{importResult.success ? (
<>
<SuccessIcon color="success" sx={{ fontSize: 60 }} />
<Typography variant="h6" sx={{ mt: 2 }}>İşlem Başarılı!</Typography>
<Typography variant="h6" sx={{ mt: 2 }}>{t('mariadb.import.success_title')}</Typography>
<Typography variant="body2" color="text.secondary">{importResult.message}</Typography>
<Paper sx={{ p: 2, mt: 3, textAlign: 'left', bgcolor: 'rgba(0,180,0,0.1)' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>Erişim Bilgileri:</Typography>
<Typography variant="body2"><strong>Host:</strong> localhost</Typography>
<Typography variant="body2"><strong>DB:</strong> {dbConfig.dbName}</Typography>
<Typography variant="body2"><strong>User:</strong> {dbConfig.user}</Typography>
<Typography variant="body2"><strong>Pass:</strong> {dbConfig.pass}</Typography>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>{t('mariadb.import.access_info')}:</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.host')}:</strong> localhost</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.db')}:</strong> {dbConfig.dbName}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.user')}:</strong> {dbConfig.user}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.db_pass')}:</strong> {dbConfig.pass}</Typography>
</Paper>
</>
) : (
<>
<ErrorIcon color="error" sx={{ fontSize: 60 }} />
<Typography variant="h6" sx={{ mt: 2 }}>Hata Oluştu!</Typography>
<Typography variant="h6" sx={{ mt: 2 }}>{t('mariadb.import.error_title')}</Typography>
<Typography variant="body2" color="error">{importResult.message}</Typography>
<Box sx={{ mt: 2, textAlign: 'left', bgcolor: '#000', p: 1, maxHeight: 150, overflowY: 'auto' }}>
{logs.slice(-5).map((l, i) => <Typography key={i} variant="caption" sx={{ color: '#fff', display: 'block' }}>{l}</Typography>)}
@@ -309,13 +311,13 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
</Box>
)
default:
return 'Bilinmeyen adım'
return t('common.error')
}
}
return (
<Dialog open={open} onClose={loading ? undefined : onClose} maxWidth="sm" fullWidth>
<DialogTitle>MariaDB Hızlı Dump/Import Sihirbazı</DialogTitle>
<DialogTitle>{t('mariadb.import.wizard_title')}</DialogTitle>
<DialogContent>
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
{steps.map((label) => (
@@ -329,25 +331,25 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
</DialogContent>
<DialogActions>
<Button onClick={onClose} disabled={loading}>KAPAT</Button>
<Button onClick={onClose} disabled={loading}>{t('common.close')}</Button>
<Box sx={{ flexGrow: 1 }} />
{activeStep > 0 && activeStep < 3 && !importResult && (
<Button onClick={prevStep} disabled={loading}>GERİ</Button>
<Button onClick={prevStep} disabled={loading}>{t('common.back')}</Button>
)}
{activeStep === 0 && (
<Button variant="contained" onClick={nextStep} disabled={!filePath}>İLERİ</Button>
<Button variant="contained" onClick={nextStep} disabled={!filePath}>{t('common.next')}</Button>
)}
{activeStep === 1 && (
<Button variant="contained" onClick={handleStep2Next}>İLERİ</Button>
<Button variant="contained" onClick={handleStep2Next}>{t('common.next')}</Button>
)}
{activeStep === 2 && (
<Button variant="contained" onClick={handleStep3Next} disabled={loading}>
{loading ? <CircularProgress size={24} /> : 'ÖZETİ GÖR'}
{loading ? <CircularProgress size={24} /> : t('mariadb.import.btn_summary')}
</Button>
)}
{activeStep === 3 && !importResult && (
<Button variant="contained" color="primary" onClick={startImport} disabled={loading}>
{loading ? <CircularProgress size={24} /> : 'IMPORTU BAŞLAT'}
{loading ? <CircularProgress size={24} /> : t('mariadb.import.btn_start')}
</Button>
)}
</DialogActions>
@@ -19,6 +19,7 @@ import {
} from '@mui/material'
import { Search as SearchIcon, Close as CloseIcon, Refresh as RefreshIcon, EditNote as RawIcon, Save as SaveIcon } from '@mui/icons-material'
import Swal from 'sweetalert2'
import { useTranslation } from 'react-i18next'
import PhpIniCodeEditor from './PhpIniCodeEditor'
interface PhpExtension {
@@ -34,6 +35,7 @@ interface PhpExtensionManagerProps {
}
const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose, phpVersion }) => {
const { t } = useTranslation()
const [extensions, setExtensions] = useState<PhpExtension[]>([])
const [loading, setLoading] = useState(false)
const [searchTerm, setSearchTerm] = useState('')
@@ -72,8 +74,8 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
const handleSaveRaw = async () => {
setIsSavingRaw(true)
Swal.fire({
title: 'Yapılandırma Kaydediliyor',
text: 'PHP servisi yeniden başlatılıyor...',
title: t('php.extensions.saving'),
text: t('php.extensions.restarting'),
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading()
@@ -89,8 +91,8 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
await window.api.invoke('services:start', `php:${phpVersion}`)
Swal.fire({
title: 'Başarılı',
text: 'Yapılandırma kaydedildi ve servis yeniden başlatıldı.',
title: t('common.success'),
text: t('php.extensions.success_msg'),
icon: 'success',
timer: 2000,
showConfirmButton: false,
@@ -121,8 +123,11 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
if (wasRunning) {
Swal.fire({
title: 'Eklenti Ayarlanıyor',
text: `${extName} ${enabled ? 'aktif ediliyor' : 'devre dışı bırakılıyor'} ve PHP yeniden başlatılıyor...`,
title: t('php.extensions.adjusting'),
text: t('php.extensions.adjusting_desc', {
extName,
mode: enabled ? t('common.starting').toLowerCase() : t('common.stop').toLowerCase()
}),
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading()
@@ -142,10 +147,10 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
Swal.close()
}
} else {
Swal.fire({ title: 'Hata', text: result.message, icon: 'error', background: '#1e1e1e', color: '#fff' })
Swal.fire({ title: t('common.error'), text: result.message, icon: 'error', background: '#1e1e1e', color: '#fff' })
}
} catch (error: any) {
Swal.fire({ title: 'Hata', text: error.message, icon: 'error', background: '#1e1e1e', color: '#fff' })
Swal.fire({ title: t('common.error'), text: error.message, icon: 'error', background: '#1e1e1e', color: '#fff' })
} finally {
setSaving(null)
}
@@ -175,8 +180,8 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
>
<DialogTitle sx={{ m: 0, p: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Box>
<Typography variant="h6">PHP {phpVersion} Eklentileri</Typography>
<Typography variant="caption" color="text.secondary">php.ini üzerinden eklentileri yönetin</Typography>
<Typography variant="h6">{t('php.extensions.title', { version: phpVersion })}</Typography>
<Typography variant="caption" color="text.secondary">{t('php.extensions.desc')}</Typography>
</Box>
<IconButton onClick={onClose} sx={{ color: 'text.secondary' }}>
<CloseIcon />
@@ -187,7 +192,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
<TextField
fullWidth
size="small"
placeholder="Eklenti ara... (mysql, gd, mbstring...)"
placeholder={t('php.extensions.search_placeholder')}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
InputProps={{
@@ -205,7 +210,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
onClick={fetchRawConfig}
sx={{ whiteSpace: 'nowrap', borderColor: 'rgba(255,255,255,0.2)', color: '#fff' }}
>
RAW EDİTÖR
{t('php.extensions.raw_editor')}
</Button>
</Box>
@@ -218,7 +223,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
{filteredExtensions.length === 0 ? (
<ListItem>
<ListItemText
primary={searchTerm ? "Eklenti bulunamadı." : "Hiç eklenti yok."}
primary={searchTerm ? t('php.extensions.not_found') : t('php.extensions.none')}
sx={{ textAlign: 'center', color: 'text.secondary' }}
/>
</ListItem>
@@ -245,7 +250,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
primary={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{ext.name}
{ext.enabled && <Chip label="Aktif" size="small" color="success" sx={{ height: 18, fontSize: '0.65rem' }} />}
{ext.enabled && <Chip label={t('php.extensions.active')} size="small" color="success" sx={{ height: 18, fontSize: '0.65rem' }} />}
</Box>
}
secondary={ext.dll}
@@ -259,10 +264,10 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
</DialogContent>
<DialogActions sx={{ p: 2, bgcolor: 'rgba(255,255,255,0.02)' }}>
<Button onClick={fetchExtensions} startIcon={<RefreshIcon />} sx={{ mr: 'auto' }}>
YENİLE
{t('common.refresh').toUpperCase()}
</Button>
<Button onClick={onClose} variant="contained" color="primary">
KAPAT
{t('common.close').toUpperCase()}
</Button>
</DialogActions>
</Dialog>
@@ -282,7 +287,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
>
<DialogTitle sx={{ display: 'flex', flexDirection: 'column', gap: 0.5, p: 2 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="h6">php.ini Düzenle ({phpVersion})</Typography>
<Typography variant="h6">{t('php.extensions.ini_edit', { version: phpVersion })}</Typography>
<IconButton onClick={() => setIsRawEditorOpen(false)} sx={{ color: 'rgba(255,255,255,0.5)' }}>
<CloseIcon />
</IconButton>
@@ -296,7 +301,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
'&:hover': { textDecoration: 'underline', color: 'primary.light' }
}}
onClick={() => window.api.invoke('shell:open-path', configPath)}
title="Dosyayı sistemde aç"
title={t('php.extensions.file_open_tip')}
>
{configPath}
</Typography>
@@ -305,7 +310,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
<TextField
fullWidth
size="small"
placeholder="Dosya içerisinde ara..."
placeholder={t('php.extensions.search_file_placeholder')}
value={rawSearchTerm}
onChange={(e) => setRawSearchTerm(e.target.value)}
InputProps={{
@@ -331,7 +336,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
</DialogContent>
<DialogActions sx={{ p: 2, bgcolor: 'rgba(0,0,0,0.2)' }}>
<Button onClick={() => setIsRawEditorOpen(false)} sx={{ color: 'rgba(255,255,255,0.5)' }}>
İPTAL
{t('common.cancel').toUpperCase()}
</Button>
<Button
onClick={handleSaveRaw}
@@ -340,7 +345,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
startIcon={isSavingRaw ? <CircularProgress size={18} color="inherit" /> : <SaveIcon />}
disabled={isSavingRaw}
>
{isSavingRaw ? 'KAYDEDİLİYOR...' : 'KAYDET VE SUNUCUYU DURDUR'}
{isSavingRaw ? t('php.extensions.saving_btn') : t('php.extensions.save_restart_btn')}
</Button>
</DialogActions>
</Dialog>
@@ -5,6 +5,7 @@ import 'prismjs/components/prism-ini'
import 'prismjs/themes/prism-tomorrow.css'
import { IconButton, Box, Typography, Tooltip } from '@mui/material'
import { KeyboardArrowDown as DownIcon, KeyboardArrowUp as UpIcon } from '@mui/icons-material'
import { useTranslation } from 'react-i18next'
interface PhpIniCodeEditorProps {
value: string
@@ -13,6 +14,7 @@ interface PhpIniCodeEditorProps {
}
const PhpIniCodeEditor: React.FC<PhpIniCodeEditorProps> = ({ value, onChange, searchTerm }) => {
const { t } = useTranslation()
const editorRef = useRef<HTMLDivElement>(null)
const [matches, setMatches] = useState<number[]>([])
const [currentIndex, setCurrentIndex] = useState(0)
@@ -114,12 +116,12 @@ const PhpIniCodeEditor: React.FC<PhpIniCodeEditorProps> = ({ value, onChange, se
<Typography variant="caption" sx={{ color: '#aaa', mr: 1, minWidth: '40px', textAlign: 'center', userSelect: 'none' }}>
{matches.length > 0 ? `${currentIndex + 1} / ${matches.length}` : '0 / 0'}
</Typography>
<Tooltip title="Önceki (Shift+Enter)">
<Tooltip title={t('php.extensions.prev_match')}>
<IconButton size="small" onClick={handlePrev} sx={{ color: '#fff' }}>
<UpIcon fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title="Sonraki (Enter)">
<Tooltip title={t('php.extensions.next_match')}>
<IconButton size="small" onClick={handleNext} sx={{ color: '#fff' }}>
<DownIcon fontSize="small" />
</IconButton>
+17 -8
View File
@@ -1,22 +1,31 @@
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import tr from './locales/tr.json'
import en from './locales/en.json'
import de from './locales/de.json'
// Dynamically import all json files in the locales directory
const modules = import.meta.glob('./locales/*.json', { eager: true })
const resources = {}
const supportedLngs: string[] = []
for (const path in modules) {
const key = path.split('/').pop()?.replace('.json', '')
if (key) {
resources[key] = { translation: (modules[path] as any).default || modules[path] }
supportedLngs.push(key)
}
}
i18n
.use(initReactI18next)
.init({
resources: {
tr: { translation: tr },
en: { translation: en },
de: { translation: de }
},
resources,
lng: 'tr',
fallbackLng: 'en',
supportedLngs,
interpolation: {
escapeValue: false
}
})
export { supportedLngs }
export default i18n
+210 -4
View File
@@ -2,22 +2,228 @@
"common": {
"start": "Starten",
"stop": "Stoppen",
"restart": "Neustart",
"restart": "Neustarten",
"settings": "Einstellungen",
"tools": "Werkzeuge",
"close": "Schließen",
"quit": "Beenden",
"save": "Speichern",
"port": "Port",
"all": "Alle"
"all": "Alle",
"export": "Exportieren",
"import": "Importieren",
"ok": "OK",
"cancel": "Abbrechen",
"error": "Fehler!",
"success": "Erfolgreich!",
"warning": "Warnung",
"info": "Info",
"delete": "Löschen",
"edit": "Bearbeiten",
"refresh": "Aktualisieren",
"search": "Suchen",
"loading": "Laden...",
"starting": "Wird gestartet",
"are_you_sure": "Sind Sie sicher?",
"yes_delete": "Ja, Löschen",
"go_to_fix": "Zur Korrektur gehen",
"select": "AUSWÄHLEN",
"next": "WEITER",
"back": "ZURÜCK",
"finish": "FERTIGSTELLEN",
"download": "HERUNTERLADEN",
"available": "Verfügbar",
"reset": "Zurücksetzen",
"copy": "Kopieren",
"browse": "Durchsuchen",
"reset_confirm_title": "Sind Sie sicher?",
"reset_confirm_text": "Möchten Sie die {{service}}-Daten zurücksetzen? Dieser Vorgang löscht alle Daten und führt eine Neuinstallation durch.",
"reset_btn": "Ja, Zurücksetzen"
},
"dashboard": {
"title": "Trunçgil Multi-PHP Server",
"status": "Serverstatus",
"services": "Dienste",
"active_version": "Aktive PHP-Version",
"quick_look": "Schnellansicht",
"quick_look": "Kurzübersicht",
"start_all": "Alle starten",
"stop_all": "Alle stoppen"
"stop_all": "Alle stoppen",
"all_started": "Alle Dienste wurden gestartet.",
"all_stopped": "Alle Dienste wurden gestoppt."
},
"projects": {
"title": "Meine Projekte",
"add_new": "Neues Projekt hinzufügen",
"edit": "Projekt bearbeiten",
"empty": "Noch keine Projekte hinzugefügt.",
"name": "Projektname",
"path": "Projektpfad",
"host": "Host / Domain",
"hostname_placeholder": "Hostname (z.B. local.test)",
"php_version": "PHP-Version",
"mariadb_version": "MariaDB-Version",
"server_type": "Webserver",
"apache_htaccess": "Apache (htaccess-Unterstützung)",
"actions": "Aktionen",
"no_php_warning": "Sie müssen zuerst eine PHP-Version herunterladen",
"added": "Projekt erfolgreich hinzugefügt.",
"updated": "Projekt erfolgreich aktualisiert.",
"deleted": "Projekt wurde gelöscht.",
"delete_confirm": "Sind Sie sicher, dass Sie dieses Projekt löschen möchten? Ihre Dateien werden nicht gelöscht, nur aus der Liste entfernt."
},
"settings": {
"general": "Allgemeine Einstellungen",
"server": "Server-Einstellungen",
"saved": "Einstellungen gespeichert.",
"save_failed": "Einstellungen konnten nicht gespeichert werden.",
"language": "Sprache",
"nginx_port": "Nginx-Port",
"apache_port": "Apache-Port",
"php_port": "PHP-FPM-Port",
"mariadb_port": "MariaDB-Port",
"remote_access": "Fernzugriff zulassen",
"server_type_select": "Standard-Servertyp",
"php_versions": "PHP-Versionen",
"mariadb_versions": "MariaDB-Versionen",
"nginx_versions": "Nginx-Versionen",
"apache_versions": "Apache-Versionen",
"php_versions_desc": "Laden Sie hier benötigte PHP-Versionen herunter und verwalten Sie sie.",
"mariadb_versions_desc": "MariaDB-Versionen hier verwalten.",
"nginx_versions_desc": "Nginx-Versionen hier verwalten.",
"apache_versions_desc": "Apache-Versionen hier verwalten.",
"server_settings": "Servereinstellungen",
"nginx_port_desc": "Port, auf dem Nginx läuft (Standard: 80)"
},
"server": {
"nginx": {
"config": "Nginx-Konfiguration",
"config_title": "Nginx-Konfiguration (nginx.conf)",
"config_warning": "Warnung: Fehlerhafte Konfiguration kann Nginx stoppen. Vor Änderungen testen.",
"test": "Konfiguration testen",
"save_reload": "Speichern und neu laden",
"config_saved": "Nginx-Konfiguration erfolgreich gespeichert.",
"title": "Nginx",
"test_failed": "Konfigurationsfehler",
"test_config": "Konfiguration testen"
},
"apache": {
"config": "Apache-Konfiguration",
"config_title": "Apache (httpd.conf) Konfiguration",
"config_desc": "Dies ist die globale Konfigurationsdatei für den Apache-Server.",
"test": "Konfiguration testen",
"save_reload": "Speichern und neu laden",
"config_saved": "Apache-Konfiguration gespeichert.",
"title": "Apache",
"test_config": "Konfiguration testen"
},
"project_nginx": {
"title": "{{host}} Nginx-Konfiguration",
"tip": "Tipp: Diese Konfiguration gilt nur für das {{host}}-Projekt.",
"error_line": "Fehlerzeile: {{line}}"
}
},
"pma": {
"ready_desc": "phpMyAdmin ist bereit für die Datenbankverwaltung.",
"install_desc": "phpMyAdmin installieren, um Datenbanken über das Web zu verwalten.",
"installing": "INSTALLATION...",
"install_button": "INSTALLATION STARTEN",
"open": "ÖFFNEN",
"progress": "Downloading... %{{progress}}"
},
"mariadb": {
"import": {
"title": "MariaDB Daten importieren",
"desc": "Schnelles Laden einer .sql- oder Archivdatei in die Datenbank.",
"button": "IMPORTIEREN",
"wizard_title": "MariaDB-Schnellimport-Assistent",
"step1": "Datenauswahl",
"step2": "Serverauswahl",
"step3": "Datenbankkonfiguration",
"step4": "Zusammenfassung & Prozess",
"step1_desc": "Bitte wählen Sie die .sql-Datei oder das Archiv (.zip, .tar.gz) aus, das hochgeladen werden soll.",
"step2_desc": "Wählen Sie den MariaDB-Server aus, auf den die Datei hochgeladen werden soll.",
"step3_desc": "Konfigurieren Sie Datenbank- und Benutzerinformationen.",
"no_file": "Keine Datei ausgewählt...",
"root_pass": "Root-Passwort (falls vorhanden)",
"root_pass_helper": "Geben Sie das vorhandene Passwort für den MariaDB-Benutzer 'root' ein. Leer lassen, wenn kein Passwort vorhanden ist.",
"db_name": "Datenbankname",
"db_user": "Datenbankbenutzer",
"db_pass": "Benutzerpasswort",
"summary": "Zusammenfassungsinformationen",
"file": "Datei",
"server": "Server",
"db": "Datenbank",
"user": "Benutzer",
"success_title": "Vorgang erfolgreich!",
"error_title": "Ein Fehler ist aufgetreten!",
"access_info": "Zugriffsinformationen",
"host": "Host",
"starting": "[System] Vorgang wird gestartet...",
"overwrite_title": "Datenbank existiert bereits!",
"overwrite_desc": "Die Datenbank '{{dbName}}' existiert bereits. Möchten Sie sie überschreiben? Alle vorhandenen Daten werden gelöscht!",
"btn_summary": "ZUSAMMENFASSUNG ANZEIGEN",
"btn_start": "IMPORT STARTEN"
},
"export": {
"title": "MariaDB Daten exportieren",
"desc": "Datenbanken sicher als .sql oder .sql.gz sichern.",
"button": "EXPORTIEREN",
"wizard_title": "MariaDB-Datenbank-Export-Assistent",
"step1": "Serverauswahl",
"step2": "Datenbankauswahl",
"step3": "Exporteinstellungen",
"step4": "Zusammenfassung & Prozess",
"step1_desc": "Wählen Sie den MariaDB-Server aus, auf dem sich die Datenbank befindet.",
"step2_desc": "Wählen Sie die Datenbank aus, die Sie exportieren möchten.",
"step3_desc": "Bestimmen Sie den Speicherort und das Format für die gespeicherte Datei.",
"placeholder": "Zu speichernde Datei...",
"compress": "Mit Gzip komprimieren (.sql.gz)",
"target": "Ziel",
"compression": "Komprimierung",
"on": "Ein (.gz)",
"off": "Aus (.sql)",
"btn_show_file": "DATEI ANZEIGEN",
"btn_start": "EXPORT STARTEN",
"starting": "[System] Exportvorgang wird gestartet...",
"no_db_found": "Auf dem ausgewählten Server wurde keine zugängliche Datenbank gefunden.",
"list_error": "Datenbanken konnten nicht aufgelistet werden: "
},
"wizard": {
"import_title": "MariaDB-Daten-Import-Assistent",
"export_title": "MariaDB-Daten-Export-Assistent",
"select_version": "MariaDB-Version wählen",
"select_db": "Datenbank wählen",
"select_file": "Datei wählen",
"import_success": "Datenerfolg beim Importieren.",
"export_success": "Datenerfolg beim Exportieren."
}
},
"diagnostics": {
"logs": "Fehlerprotokolle",
"noLogs": "Noch keine Protokolle aufgezeichnet."
},
"php": {
"extensions": {
"title": "PHP {{version}} Erweiterungen",
"desc": "Erweiterungen über php.ini verwalten",
"search_placeholder": "Erweiterung suchen... (mysql, gd, mbstring...)",
"search_file_placeholder": "In Datei suchen...",
"raw_editor": "RAW EDITOR",
"none": "Keine Erweiterungen gefunden.",
"not_found": "Erweiterung nicht gefunden.",
"active": "Aktiv",
"ini_edit": "php.ini bearbeiten ({{version}})",
"file_open_tip": "Datei im System öffnen",
"saving": "Konfiguration wird gespeichert",
"restarting": "PHP-Dienst wird neu gestartet...",
"adjusting": "Erweiterung wird angepasst",
"adjusting_desc": "{{extName}} wird {{mode}} und PHP wird neu gestartet...",
"save_restart_btn": "SPEICHERN UND NEU STARTEN",
"saving_btn": "SPEICHERN...",
"save_error": "Fehler beim Speichern der Erweiterungen.",
"success_msg": "Konfiguration gespeichert und Dienst neu gestartet.",
"prev_match": "Vorheriges (Shift+Enter)",
"next_match": "Nächstes (Enter)"
}
}
}
+214 -2
View File
@@ -9,7 +9,39 @@
"quit": "Quit",
"save": "Save",
"port": "Port",
"all": "All"
"all": "All",
"export": "Export",
"import": "Import",
"ok": "OK",
"cancel": "Cancel",
"error": "Error!",
"success": "Success!",
"warning": "Warning",
"info": "Info",
"delete": "Delete",
"edit": "Edit",
"refresh": "Refresh",
"search": "Search",
"loading": "Loading...",
"starting": "Starting",
"are_you_sure": "Are you sure?",
"yes_delete": "Yes, delete!",
"go_to_fix": "Go to Fix",
"select": "SELECT",
"next": "NEXT",
"back": "BACK",
"finish": "FINISH",
"download": "DOWNLOAD",
"available": "Available",
"reset": "Reset",
"copy": "Copy",
"browse": "Browse",
"delete_confirm": "Are you sure you want to delete this project?",
"yes_delete_btn": "Yes, Delete",
"stopping": "Stopping",
"reset_confirm_title": "Are you sure?",
"reset_confirm_text": "Do you want to reset {{service}} data? This operation deletes all data and performs a clean installation.",
"reset_btn": "Yes, Reset"
},
"dashboard": {
"title": "Trunçgil Multi-PHP Server",
@@ -18,6 +50,186 @@
"active_version": "Active PHP Version",
"quick_look": "Quick Look",
"start_all": "Start All",
"stop_all": "Stop All"
"stop_all": "Stop All",
"all_started": "All services started.",
"all_stopped": "All services stopped."
},
"projects": {
"title": "My Projects",
"add_new": "Add New Project",
"edit": "Edit Project",
"empty": "No projects added yet.",
"name": "Project Name",
"path": "Project Path",
"host": "Host / Domain",
"hostname_placeholder": "Hostname (e.g., local.test)",
"php_version": "PHP Version",
"mariadb_version": "MariaDB Version",
"server_type": "Web Server",
"apache_htaccess": "Apache (htaccess support)",
"actions": "Actions",
"no_php_warning": "You must download a PHP version first",
"added": "Project added successfully.",
"updated": "Project updated successfully.",
"deleted": "Project deleted.",
"delete_confirm": "Are you sure you want to delete this project? Your files will not be deleted, only removed from the list."
},
"settings": {
"general": "General Settings",
"server": "Server Settings",
"saved": "Settings saved.",
"save_failed": "Failed to save settings.",
"language": "Language",
"nginx_port": "Nginx Port",
"apache_port": "Apache Port",
"php_port": "PHP-FPM Port",
"mariadb_port": "MariaDB Port",
"remote_access": "Allow Remote Access",
"server_type_select": "Default Server Type",
"php_versions": "PHP Versions",
"mariadb_versions": "MariaDB Versions",
"nginx_versions": "Nginx Versions",
"apache_versions": "Apache Versions",
"php_versions_desc": "Download and manage required PHP versions here.",
"mariadb_versions_desc": "Manage MariaDB versions here.",
"nginx_versions_desc": "Manage Nginx versions here.",
"apache_versions_desc": "Manage Apache versions here.",
"server_settings": "Server Settings",
"nginx_port_desc": "Port Nginx will run on (Default: 80)"
},
"server": {
"nginx": {
"config": "Nginx Settings",
"config_title": "Nginx Configuration (nginx.conf)",
"config_warning": "Warning: Incorrect configuration may stop Nginx. Test before making changes.",
"test_config": "Test Configuration",
"save_reload": "Save and Reload",
"config_saved": "Nginx configuration saved successfully.",
"test_failed": "Configuration Error"
},
"apache": {
"title": "Apache",
"config": "Apache Settings",
"config_title": "Apache (httpd.conf) Configuration",
"config_desc": "This is the main (global) configuration file for the Apache server.",
"test_config": "Test Configuration",
"save_reload": "Save and Reload",
"config_saved": "Apache configuration saved."
},
"project_nginx": {
"title": "{{host}} Nginx Configuration",
"tip": "Tip: This configuration applies only to the {{host}} project.",
"error_line": "Error Line: {{line}}"
}
},
"pma": {
"ready_desc": "phpMyAdmin is ready for database management.",
"install_desc": "Install phpMyAdmin to manage databases via web.",
"installing": "INSTALLING...",
"install_button": "START INSTALLATION",
"open": "OPEN",
"progress": "Downloading... %{{progress}}",
"title": "phpMyAdmin",
"desc": "Web-based database management tool",
"open_pma": "Open phpMyAdmin",
"install": "Install phpMyAdmin",
"installed_success": "phpMyAdmin installed successfully.",
"installed_error": "An error occurred during phpMyAdmin installation."
},
"mariadb": {
"import": {
"title": "MariaDB Import Data",
"desc": "Quickly load a .sql or archive file into the database.",
"button": "IMPORT",
"wizard_title": "MariaDB Quick Dump/Import Wizard",
"step1": "Data Selection",
"step2": "Server Selection",
"step3": "Database Configuration",
"step4": "Summary & Process",
"step1_desc": "Please select the .sql file or archive (.zip, .tar.gz) to be uploaded.",
"step2_desc": "Select the MariaDB server where the file will be uploaded.",
"step3_desc": "Configure database and user information.",
"no_file": "No file selected...",
"root_pass": "Root Password (If any)",
"root_pass_helper": "Enter the existing password for the MariaDB 'root' user. Leave blank if empty.",
"db_name": "Database Name",
"db_user": "Database User",
"db_pass": "User Password",
"summary": "Summary Information",
"file": "File",
"server": "Server",
"db": "Database",
"user": "User",
"success_title": "Operation Successful!",
"error_title": "An Error Occurred!",
"access_info": "Access Information",
"host": "Host",
"starting": "[System] Operation is starting...",
"overwrite_title": "Database Exists!",
"overwrite_desc": "Database '{{dbName}}' already exists. Do you want to overwrite it? All existing data will be deleted!",
"btn_summary": "VIEW SUMMARY",
"btn_start": "START IMPORT"
},
"export": {
"title": "MariaDB Export Data",
"desc": "Securely backup databases as .sql or .sql.gz.",
"button": "EXPORT",
"wizard_title": "MariaDB Database Export Wizard",
"step1": "Server Selection",
"step2": "Database Selection",
"step3": "Export Settings",
"step4": "Summary & Process",
"step1_desc": "Select the MariaDB server where the database is located.",
"step2_desc": "Select the database you want to export.",
"step3_desc": "Determine the location and format for the saved file.",
"placeholder": "File to be saved...",
"compress": "Compress with Gzip (.sql.gz)",
"target": "Target",
"compression": "Compression",
"on": "On (.gz)",
"off": "Off (.sql)",
"btn_show_file": "SHOW FILE",
"btn_start": "START EXPORT",
"starting": "[System] Export operation is starting...",
"no_db_found": "No accessible database found on the selected server.",
"list_error": "Databases could not be listed: "
},
"wizard": {
"import_title": "MariaDB Data Import Wizard",
"export_title": "MariaDB Data Export Wizard",
"select_version": "Select MariaDB Version",
"select_db": "Select Database",
"select_file": "Select File",
"import_success": "Data imported successfully.",
"export_success": "Data exported successfully."
}
},
"diagnostics": {
"logs": "Error Logs",
"noLogs": "No logs recorded yet."
},
"php": {
"extensions": {
"title": "PHP {{version}} Extensions",
"desc": "Manage extensions via php.ini",
"search_placeholder": "Search extension... (mysql, gd, mbstring...)",
"search_file_placeholder": "Search inside file...",
"raw_editor": "RAW EDITOR",
"none": "No extensions found.",
"not_found": "Extension not found.",
"active": "Active",
"ini_edit": "Edit php.ini ({{version}})",
"file_open_tip": "Open file in system",
"saving": "Configuration Saving",
"restarting": "PHP service is restarting...",
"adjusting": "Adjusting Extension",
"adjusting_desc": "{{extName}} is being {{mode}} and PHP is restarting...",
"save_restart_btn": "SAVE AND RESTART",
"saving_btn": "SAVING...",
"save_error": "Error occurred while saving extensions.",
"success_msg": "Configuration saved and service restarted.",
"prev_match": "Previous (Shift+Enter)",
"next_match": "Next (Enter)"
}
}
}
+205 -2
View File
@@ -11,7 +11,36 @@
"port": "Port",
"all": "Tümü",
"export": "Dışa Aktar",
"import": "İçe Aktar"
"import": "İçe Aktar",
"ok": "Tamam",
"cancel": "İptal",
"error": "Hata!",
"success": "Başarılı!",
"warning": "Uyarı",
"info": "Bilgi",
"delete": "Sil",
"edit": "Düzenle",
"refresh": "Yenile",
"search": "Ara",
"loading": "Yükleniyor...",
"starting": "Başlatılıyor",
"are_you_sure": "Emin misiniz?",
"yes_delete": "Evet, Sil",
"go_to_fix": "Sorunu Düzeltmeye Git",
"select": "SEÇ",
"next": "İLERİ",
"back": "GERİ",
"finish": "BİTİR",
"download": "İNDİR",
"available": "İndirilebilir",
"reset": "Sıfırla",
"copy": "Kopyala",
"browse": "Gözat",
"delete_confirm": "Bu projeyi silmek istediğinize emin misiniz?",
"stop_status": "Durduruluyor",
"reset_confirm_title": "Emin misiniz?",
"reset_confirm_text": "{{service}} verilerini sıfırlamak istiyor musunuz? Bu işlem tüm verileri siler ve temiz kurulum yapar.",
"reset_btn": "Evet, Sıfırla"
},
"dashboard": {
"title": "Trunçgil Multi-PHP Server",
@@ -20,6 +49,180 @@
"active_version": "Aktif PHP Sürümü",
"quick_look": "Hızlı Bakış",
"start_all": "Tümünü Başlat",
"stop_all": "Tümünü Durdur"
"stop_all": "Tümünü Durdur",
"all_started": "Tüm servisler başlatıldı.",
"all_stopped": "Tüm servisler durduruldu."
},
"projects": {
"title": "Projelerim",
"add_new": "Yeni Proje Ekle",
"edit": "Proje Düzenle",
"empty": "Henüz bir proje eklenmemiş.",
"name": "Proje Adı",
"path": "Dosya Yolu",
"host": "Host / Domain",
"hostname_placeholder": "Hostname (örn: local.test)",
"php_version": "PHP Sürümü",
"mariadb_version": "MariaDB Sürümü",
"server_type": "Web Sunucusu",
"apache_htaccess": "Apache (htaccess desteği)",
"actions": "Aksiyonlar",
"no_php_warning": "Önce PHP sürümü indirmelisiniz",
"added": "Proje başarıyla eklendi.",
"updated": "Proje başarıyla güncellendi.",
"deleted": "Proje silindi.",
"delete_confirm": "Bu projeyi silmek istediğinize emin misiniz? Dosyalarınız silinmez, sadece listeden kaldırılır."
},
"settings": {
"general": "Genel Ayarlar",
"server_settings": "Sunucu Ayarları",
"saved": "Ayarlar kaydedildi.",
"save_failed": "Ayarlar kaydedilemedi.",
"language": "Dil",
"nginx_port": "Nginx Portu",
"apache_port": "Apache Portu",
"php_port": "PHP-FPM Portu",
"mariadb_port": "MariaDB Portu",
"remote_access": "Dış Erişime İzin Ver",
"server_type_select": "Varsayılan Sunucu Tipi",
"php_versions": "PHP Sürümleri",
"mariadb_versions": "MariaDB Sürümleri",
"nginx_versions": "Nginx Sürümleri",
"apache_versions": "Apache Sürümleri",
"php_versions_desc": "Gereken PHP sürümlerini buradan indirebilir ve yönetebilirsiniz.",
"mariadb_versions_desc": "MariaDB sürümlerini buradan yönetebilirsiniz.",
"nginx_versions_desc": "Nginx sürümlerini buradan yönetebilirsiniz.",
"apache_versions_desc": "Apache sürümlerini buradan yönetebilirsiniz.",
"nginx_port_desc": "Nginx'in çalışacağı port (Varsayılan: 80)"
},
"server": {
"nginx": {
"title": "Nginx",
"config": "Nginx Ayarları",
"config_title": "Nginx Yapılandırması (nginx.conf)",
"config_warning": "Dikkat: Hatalı yapılandırma Nginx'in çalışmasını durdurabilir. Değişiklik yapmadan önce test edin.",
"test_config": "Yapılandırmayı Test Et",
"save_reload": "Kaydet ve Yeniden Yükle",
"config_saved": "Nginx yapılandırması başarıyla kaydedildi.",
"test_failed": "Yapılandırma Hatası"
},
"apache": {
"title": "Apache",
"config": "Apache Ayarları",
"config_title": "Apache (httpd.conf) Yapılandırması",
"config_desc": "Düzenlediğiniz bu dosya Apache sunucusunun ana (global) yapılandırma dosyasıdır.",
"test_config": "Yapılandırmayı Test Et",
"save_reload": "Kaydet ve Yeniden Yükle",
"config_saved": "Apache yapılandırması kaydedildi."
},
"project_nginx": {
"title": "{{host}} Nginx Yapılandırması",
"tip": "İpucu: Bu yapılandırma sadece {{host}} projesi için geçerlidir.",
"error_line": "Hata Satırı: {{line}}"
}
},
"pma": {
"ready_desc": "Veritabanı yönetimi için phpMyAdmin hazır.",
"install_desc": "Veritabanlarını web üzerinden yönetmek için phpMyAdmin kurun.",
"installing": "KURULUYOR...",
"install_button": "KURULUMU BAŞLAT",
"open": "AÇ",
"progress": "İndiriliyor... %{{progress}}"
},
"mariadb": {
"import": {
"title": "MariaDB Veri İçe Aktar",
"desc": "Seçtiğiniz bir .sql veya arşiv dosyasını hızlıca veritabanına yükleyin.",
"button": "İÇE AKTAR",
"wizard_title": "MariaDB Hızlı Dump/Import Sihirbazı",
"step1": "Veri Seçimi",
"step2": "Sunucu Seçimi",
"step3": "Veritabanı Yapılandırması",
"step4": "Özet ve İşlem",
"step1_desc": "Lütfen yüklenecek olan .sql dosyasını veya arşivini (.zip, .tar.gz) seçin.",
"step2_desc": "Dosyanın hangi MariaDB sunucusuna yükleneceğini seçin.",
"step3_desc": "Veritabanı ve kullanıcı bilgilerini yapılandırın.",
"no_file": "Dosya seçilmedi...",
"root_pass": "Root Şifresi (Eğer varsa)",
"root_pass_helper": "MariaDB 'root' kullanıcısının mevcut şifresini girin. Boşsa boş bırakın.",
"db_name": "Veritabanı Adı",
"db_user": "Veritabanı Kullanıcısı",
"db_pass": "Kullanıcı Şifresi",
"summary": "Özet Bilgiler",
"file": "Dosya",
"server": "Sunucu",
"db": "Veritabanı",
"user": "Kullanıcı",
"success_title": "İşlem Başarılı!",
"error_title": "Hata Oluştu!",
"access_info": "Erişim Bilgileri",
"host": "Host",
"starting": "[Sistem] İşlem başlatılıyor...",
"overwrite_title": "Veritabanı Mevcut!",
"overwrite_desc": "'{{dbName}}' veritabanı zaten var. Üzerine yazmak istiyor musunuz? Mevcut tüm veriler silinecektir!",
"btn_summary": "ÖZETİ GÖR",
"btn_start": "IMPORTU BAŞLAT"
},
"export": {
"title": "MariaDB Veri Dışa Aktar",
"desc": "Veritabanlarını .sql veya .sql.gz olarak güvenle yedekleyin.",
"button": "DIŞA AKTAR",
"wizard_title": "MariaDB Veritabanı Dışa Aktarma Sihirbazı",
"step1": "Sunucu Seçimi",
"step2": "Veritabanı Seçimi",
"step3": "Dışa Aktarma Ayarları",
"step4": "Özet ve İşlem",
"step1_desc": "Veritabanın hangi MariaDB sunucusunda olduğunu seçin.",
"step2_desc": "Dışa aktarmak istediğiniz veritabanını seçin.",
"step3_desc": "Dosyanın kaydedileceği yeri ve formatını belirleyin.",
"placeholder": "Kaydedilecek dosya...",
"compress": "Gzip ile Sıkıştır (.sql.gz)",
"target": "Hedef",
"compression": "Sıkıştırma",
"on": "Açık (.gz)",
"off": "Kapalı (.sql)",
"btn_show_file": "DOSYAYI GÖSTER",
"btn_start": "EXPORTU BAŞLAT",
"starting": "[Sistem] Dışa aktarma işlemi başlatılıyor...",
"no_db_found": "Seçili sunucuda erişilebilir veritabanı bulunamadı.",
"list_error": "Veritabanları listelenemedi: "
},
"wizard": {
"import_title": "MariaDB Veri İçe Aktarma Sihirbazı",
"export_title": "MariaDB Veri Dışa Aktarma Sihirbazı",
"select_version": "MariaDB Sürümü Seçin",
"select_db": "Veritabanı Seçin",
"select_file": "Dosya Seçin",
"import_success": "Veri başarıyla içe aktarıldı.",
"export_success": "Veri başarıyla dışa aktarıldı."
}
},
"diagnostics": {
"logs": "Hata Logları",
"noLogs": "Henüz log kaydı bulunmuyor."
},
"php": {
"extensions": {
"title": "PHP {{version}} Eklentileri",
"desc": "php.ini üzerinden eklentileri yönetin",
"search_placeholder": "Eklenti ara... (mysql, gd, mbstring...)",
"search_file_placeholder": "Dosya içerisinde ara...",
"raw_editor": "RAW EDİTÖR",
"none": "Hiç eklenti yok.",
"not_found": "Eklenti bulunamadı.",
"active": "Aktif",
"ini_edit": "php.ini Düzenle ({{version}})",
"file_open_tip": "Dosyayı sistemde aç",
"saving": "Yapılandırma Kaydediliyor",
"restarting": "PHP servisi yeniden başlatılıyor...",
"adjusting": "Eklenti Ayarlanıyor",
"adjusting_desc": "{{extName}} {{mode}} ve PHP yeniden başlatılıyor...",
"save_restart_btn": "KAYDET VE YENİDEN BAŞLAT",
"saving_btn": "KAYDEDİLİYOR...",
"save_error": "Eklentiler kaydedilirken hata oluştu.",
"success_msg": "Yapılandırma kaydedildi ve servis yeniden başlatıldı.",
"prev_match": "Önceki (Shift+Enter)",
"next_match": "Sonraki (Enter)"
}
}
}
+1 -1
View File
@@ -50,7 +50,7 @@ class ErrorBoundary extends React.Component<{ children: React.ReactNode }, { has
if (this.state.hasError) {
return (
<div style={{ padding: 20, color: 'white' }}>
<h1>Görüntüleme Hatası Oluştu</h1>
<h1>{i18n.t('common.error')}</h1>
<pre style={{ whiteSpace: 'pre-wrap' }}>{this.state.error?.toString()}</pre>
</div>
)