Add internationalization support with English and Dutch translations

- Integrated i18next for language detection and translation support.
- Added English and Dutch JSON translation files for various app sections.
- Updated TweaksPanel and Workspace components to use translated strings.
- Imported i18n configuration in main entry file.
This commit is contained in:
LaurensBartelds committed 2026-04-26 21:19:47 +02:00
1 parent c64cdcd9e3
commit 6277826472
19 files changed
+1193 -369

No files matched your search

+30 -30
View File
@@ -1,4 +1,5 @@
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { setToken } from '../utils/api'
import * as setupApi from '../api/setupApi'
@@ -7,6 +8,7 @@ interface Props {
}
export default function SetupWizard({ onComplete }: Readonly<Props>) {
const { t } = useTranslation()
const [step, setStep] = useState(1)
const [email, setEmail] = useState('')
const [displayName, setDisplayName] = useState('')
@@ -24,19 +26,19 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
const validateStep1 = () => {
if (!email.trim() || !email.includes('@')) {
setError('Please enter a valid email address')
setError(t('setup.validEmailRequired'))
return false
}
if (!displayName.trim()) {
setError('Display name is required')
setError(t('setup.displayNameRequired'))
return false
}
if (password.length < 8) {
setError('Password must be at least 8 characters')
setError(t('setup.passwordMinLength'))
return false
}
if (password !== confirmPassword) {
setError('Passwords do not match')
setError(t('setup.passwordsDoNotMatch'))
return false
}
return true
@@ -68,7 +70,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
onComplete()
} catch (err) {
setError(
err instanceof Error ? err.message : 'Setup failed. Please try again.',
err instanceof Error ? err.message : t('setup.setupFailed'),
)
} finally {
setSubmitting(false)
@@ -79,20 +81,20 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
<div className="setup-page">
<div className="setup-card">
<div className="brand-mark" style={{ margin: '0 auto 16px' }} />
<h1 className="setup-title">Welcome to BrainWave</h1>
<h1 className="setup-title">{t('setup.welcome')}</h1>
<p className="setup-subtitle">
Let's get your instance set up. This only takes a minute.
{t('setup.subtitle')}
</p>
<div className="setup-steps">
<div className={`setup-step ${step >= 1 ? 'active' : ''}`}>
<span className="setup-step-num">1</span>
<span className="setup-step-label">Admin Account</span>
<span className="setup-step-label">{t('setup.adminAccount')}</span>
</div>
<div className="setup-step-line" />
<div className={`setup-step ${step >= 2 ? 'active' : ''}`}>
<span className="setup-step-num">2</span>
<span className="setup-step-label">Email Settings</span>
<span className="setup-step-label">{t('setup.emailSettings')}</span>
</div>
</div>
@@ -102,7 +104,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
<>
<div className="auth-field">
<label className="auth-label" htmlFor="setup-email">
Email
{t('setup.email')}
</label>
<input
id="setup-email"
@@ -117,7 +119,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
</div>
<div className="auth-field">
<label className="auth-label" htmlFor="setup-name">
Display Name
{t('setup.displayName')}
</label>
<input
id="setup-name"
@@ -132,7 +134,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
</div>
<div className="auth-field">
<label className="auth-label" htmlFor="setup-password">
Password
{t('setup.password')}
</label>
<input
id="setup-password"
@@ -148,7 +150,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
</div>
<div className="auth-field">
<label className="auth-label" htmlFor="setup-confirm">
Confirm Password
{t('setup.confirmPassword')}
</label>
<input
id="setup-confirm"
@@ -167,7 +169,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
onClick={handleNext}
style={{ marginTop: 8 }}
>
Continue →
{t('setup.continue')}
</button>
</>
)}
@@ -175,17 +177,15 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
{step === 2 && (
<form onSubmit={(e) => void handleSubmit(e)}>
<p className="setup-hint">
Configure your outgoing email server so BrainWave can send
verification and password-reset emails.
{t('setup.setupHint1')}
</p>
<p className="setup-hint">
You can leave these blank and configure them later in the admin
dashboard.
{t('setup.setupHint2')}
</p>
<div className="auth-field">
<label className="auth-label" htmlFor="smtp-host">
SMTP Host
{t('setup.smtpHost')}
</label>
<input
id="smtp-host"
@@ -198,7 +198,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
</div>
<div className="auth-field">
<label className="auth-label" htmlFor="smtp-port">
SMTP Port
{t('setup.smtpPort')}
</label>
<input
id="smtp-port"
@@ -211,7 +211,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
</div>
<div className="auth-field">
<label className="auth-label" htmlFor="smtp-user">
SMTP Username
{t('setup.smtpUsername')}
</label>
<input
id="smtp-user"
@@ -224,7 +224,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
</div>
<div className="auth-field">
<label className="auth-label" htmlFor="smtp-pass">
SMTP Password
{t('setup.smtpPassword')}
</label>
<input
id="smtp-pass"
@@ -237,7 +237,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
</div>
<div className="auth-field">
<label className="auth-label" htmlFor="smtp-from">
From Address
{t('setup.fromAddress')}
</label>
<input
id="smtp-from"
@@ -250,7 +250,7 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
</div>
<div className="auth-field">
<label className="auth-label" htmlFor="smtp-secure">
Security
{t('setup.security')}
</label>
<select
id="smtp-secure"
@@ -258,14 +258,14 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
value={smtpSecure}
onChange={(e) => setSmtpSecure(e.target.value)}
>
<option value="auto">Auto (use SSL on port 465)</option>
<option value="true">SSL/TLS</option>
<option value="false">None / STARTTLS</option>
<option value="auto">{t('setup.autoSsl')}</option>
<option value="true">{t('setup.sslTls')}</option>
<option value="false">{t('setup.noneStarttls')}</option>
</select>
</div>
<div className="auth-field">
<label className="auth-label" htmlFor="app-url">
App URL
{t('setup.appUrl')}
</label>
<input
id="app-url"
@@ -284,14 +284,14 @@ export default function SetupWizard({ onComplete }: Readonly<Props>) {
className="btn btn-ghost"
onClick={() => setStep(1)}
>
← Back
{t('setup.back')}
</button>
<button
className="btn btn-primary auth-submit"
type="submit"
disabled={submitting}
>
{submitting ? 'Setting up…' : 'Complete Setup'}
{submitting ? t('setup.settingUp') : t('setup.completeSetup')}
</button>
</div>
</form>