diff --git a/README.md b/README.md
index e4df1f5..e9e4eda 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,9 @@
-

+
+
+
@@ -73,7 +75,7 @@ flutter run -d ios # or: -d android
| | | | |
| --- | --- | --- | --- |
-|
|
|
|
|
+|
|
|
|
|
## Requirements
diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png
deleted file mode 100644
index 3f1eeb9..0000000
Binary files a/fastlane/metadata/android/en-US/images/icon.png and /dev/null differ
diff --git a/fastlane/metadata/android/en-US/videos/conduit-demo.mp4 b/fastlane/metadata/android/en-US/videos/conduit-demo.mp4
deleted file mode 100644
index de09b88..0000000
Binary files a/fastlane/metadata/android/en-US/videos/conduit-demo.mp4 and /dev/null differ
diff --git a/fastlane/metadata/en-US/description.txt b/fastlane/metadata/en-US/description.txt
deleted file mode 100644
index f8ae6dc..0000000
--- a/fastlane/metadata/en-US/description.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-Conduit is an open-source, native mobile client for Open‑WebUI. Connect to your own server to chat with AI models, manage conversations, and take your self‑hosted AI with you—securely and on the go.
-
-Key Features
-- Real-time streaming chat
-- Model selection
-- Conversation search and management
-- Voice input (speech-to-text)
-- File and image uploads for retrieval-augmented generation (RAG)
-- Vision and multi‑modal support
-- Markdown rendering with syntax highlighting
-- Light, dark, and system themes
-- Secure credential storage
-
-Requirements
-- An existing Open‑WebUI server (Conduit does not host or provide AI models).
-
-Privacy & Permissions
-- Microphone: For voice input
-- Camera and Photos: For image/file attachments
-- Network: To connect to your Open‑WebUI server
-
-Open Source
-Built with Flutter and maintained by the community. Contributions are welcome.
-
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/en-US/full_description.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/full_description.txt
rename to fastlane/metadata/en-US/full_description.txt
diff --git a/fastlane/metadata/en-US/images/conduit-demo.gif b/fastlane/metadata/en-US/images/conduit-demo.gif
new file mode 100644
index 0000000..fda0d94
Binary files /dev/null and b/fastlane/metadata/en-US/images/conduit-demo.gif differ
diff --git a/fastlane/metadata/en-US/images/icon.png b/fastlane/metadata/en-US/images/icon.png
index 72600be..3f1eeb9 100644
Binary files a/fastlane/metadata/en-US/images/icon.png and b/fastlane/metadata/en-US/images/icon.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/en-US/images/phoneScreenshots/1.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
rename to fastlane/metadata/en-US/images/phoneScreenshots/1.png
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png b/fastlane/metadata/en-US/images/phoneScreenshots/2.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
rename to fastlane/metadata/en-US/images/phoneScreenshots/2.png
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png b/fastlane/metadata/en-US/images/phoneScreenshots/3.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
rename to fastlane/metadata/en-US/images/phoneScreenshots/3.png
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png b/fastlane/metadata/en-US/images/phoneScreenshots/4.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
rename to fastlane/metadata/en-US/images/phoneScreenshots/4.png
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png b/fastlane/metadata/en-US/images/phoneScreenshots/5.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
rename to fastlane/metadata/en-US/images/phoneScreenshots/5.png
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_01.png b/fastlane/metadata/en-US/images/phoneScreenshots/flutter_01.png
deleted file mode 100644
index 82baa03..0000000
Binary files a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_01.png and /dev/null differ
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_02.png b/fastlane/metadata/en-US/images/phoneScreenshots/flutter_02.png
deleted file mode 100644
index 1bf2ec7..0000000
Binary files a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_02.png and /dev/null differ
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_03.png b/fastlane/metadata/en-US/images/phoneScreenshots/flutter_03.png
deleted file mode 100644
index d933a39..0000000
Binary files a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_03.png and /dev/null differ
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_04.png b/fastlane/metadata/en-US/images/phoneScreenshots/flutter_04.png
deleted file mode 100644
index 497eae2..0000000
Binary files a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_04.png and /dev/null differ
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_05.png b/fastlane/metadata/en-US/images/phoneScreenshots/flutter_05.png
deleted file mode 100644
index ccac460..0000000
Binary files a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_05.png and /dev/null differ
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_06.png b/fastlane/metadata/en-US/images/phoneScreenshots/flutter_06.png
deleted file mode 100644
index c3a4675..0000000
Binary files a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_06.png and /dev/null differ
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_07.png b/fastlane/metadata/en-US/images/phoneScreenshots/flutter_07.png
deleted file mode 100644
index 56a9ad9..0000000
Binary files a/fastlane/metadata/en-US/images/phoneScreenshots/flutter_07.png and /dev/null differ
diff --git a/fastlane/metadata/en-US/name.txt b/fastlane/metadata/en-US/name.txt
deleted file mode 100644
index a454a94..0000000
--- a/fastlane/metadata/en-US/name.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Conduit
-
diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/en-US/short_description.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/short_description.txt
rename to fastlane/metadata/en-US/short_description.txt
diff --git a/fastlane/metadata/en-US/subtitle.txt b/fastlane/metadata/en-US/subtitle.txt
deleted file mode 100644
index f8cd883..0000000
--- a/fastlane/metadata/en-US/subtitle.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Chat with your self‑hosted AI
-
diff --git a/fastlane/metadata/android/en-US/title.txt b/fastlane/metadata/en-US/title.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/title.txt
rename to fastlane/metadata/en-US/title.txt
diff --git a/lib/features/auth/views/authentication_page.dart b/lib/features/auth/views/authentication_page.dart
index 7e5650d..2ea2ad1 100644
--- a/lib/features/auth/views/authentication_page.dart
+++ b/lib/features/auth/views/authentication_page.dart
@@ -483,7 +483,7 @@ class _AuthenticationPageState extends ConsumerState {
),
]),
obscureText: _obscurePassword,
- semanticLabel: 'Enter your API key',
+ semanticLabel: AppLocalizations.of(context)!.enterApiKey,
prefixIcon: Icon(
Platform.isIOS
? CupertinoIcons.lock_shield
@@ -516,14 +516,14 @@ class _AuthenticationPageState extends ConsumerState {
children: [
AccessibleFormField(
label: AppLocalizations.of(context)!.usernameOrEmail,
- hint: 'Enter your username or email',
+ hint: AppLocalizations.of(context)!.usernameOrEmailHint,
controller: _usernameController,
validator: InputValidationService.combine([
InputValidationService.validateRequired,
(value) => InputValidationService.validateEmailOrUsername(value),
]),
keyboardType: TextInputType.emailAddress,
- semanticLabel: 'Enter your username or email',
+ semanticLabel: AppLocalizations.of(context)!.usernameOrEmailHint,
prefixIcon: Icon(
Platform.isIOS ? CupertinoIcons.person : Icons.person_outline,
color: context.conduitTheme.iconSecondary,
@@ -534,18 +534,18 @@ class _AuthenticationPageState extends ConsumerState {
const SizedBox(height: Spacing.lg),
AccessibleFormField(
label: AppLocalizations.of(context)!.password,
- hint: 'Enter your password',
+ hint: AppLocalizations.of(context)!.passwordHint,
controller: _passwordController,
validator: InputValidationService.combine([
InputValidationService.validateRequired,
(value) => InputValidationService.validateMinLength(
value,
1,
- fieldName: 'Password',
+ fieldName: AppLocalizations.of(context)!.password,
),
]),
obscureText: _obscurePassword,
- semanticLabel: 'Enter your password',
+ semanticLabel: AppLocalizations.of(context)!.passwordHint,
prefixIcon: Icon(
Platform.isIOS ? CupertinoIcons.lock : Icons.lock_outline,
color: context.conduitTheme.iconSecondary,
@@ -576,7 +576,7 @@ class _AuthenticationPageState extends ConsumerState {
child:
ConduitButton(
text: _isSigningIn
- ? 'Signing in...'
+ ? AppLocalizations.of(context)!.signingIn
: _useApiKey
? AppLocalizations.of(context)!.signInWithApiKey
: AppLocalizations.of(context)!.signIn,
diff --git a/lib/features/auth/views/server_connection_page.dart b/lib/features/auth/views/server_connection_page.dart
index 5542448..def2905 100644
--- a/lib/features/auth/views/server_connection_page.dart
+++ b/lib/features/auth/views/server_connection_page.dart
@@ -360,7 +360,7 @@ class _ServerConnectionPageState extends ConsumerState {
Positioned(
bottom: 0,
child: ConduitBadge(
- text: 'Demo',
+ text: AppLocalizations.of(context)!.demoBadge,
backgroundColor: context.conduitTheme.warning.withValues(alpha: 0.15),
textColor: context.conduitTheme.warning,
isCompact: true,
@@ -425,7 +425,7 @@ class _ServerConnectionPageState extends ConsumerState {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
- 'Demo Mode Active',
+ AppLocalizations.of(context)!.demoModeActive,
style: context.conduitTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
color: context.conduitTheme.warning,
@@ -433,7 +433,7 @@ class _ServerConnectionPageState extends ConsumerState {
),
const SizedBox(height: Spacing.xs),
Text(
- 'Skip server setup and try the demo',
+ AppLocalizations.of(context)!.skipServerSetupTryDemo,
style: context.conduitTheme.bodySmall?.copyWith(
color: context.conduitTheme.textSecondary,
),
@@ -445,7 +445,7 @@ class _ServerConnectionPageState extends ConsumerState {
),
const SizedBox(height: Spacing.lg),
ConduitButton(
- text: 'Enter Demo',
+ text: AppLocalizations.of(context)!.enterDemo,
icon: Platform.isIOS ? CupertinoIcons.play_fill : Icons.play_arrow,
onPressed: () {
Navigator.of(context).push(
@@ -470,15 +470,15 @@ class _ServerConnectionPageState extends ConsumerState {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AccessibleFormField(
- label: 'Server URL',
- hint: 'https://your-server.com',
+ label: AppLocalizations.of(context)!.serverUrl,
+ hint: AppLocalizations.of(context)!.serverUrlHint,
controller: _urlController,
validator: InputValidationService.combine([
InputValidationService.validateRequired,
(value) => InputValidationService.validateUrl(value, required: true),
]),
keyboardType: TextInputType.url,
- semanticLabel: 'Enter your server URL or IP address',
+ semanticLabel: AppLocalizations.of(context)!.enterServerUrlSemantic,
onSubmitted: (_) => _connectToServer(),
prefixIcon: Icon(
Platform.isIOS ? CupertinoIcons.globe : Icons.public,
@@ -533,7 +533,7 @@ class _ServerConnectionPageState extends ConsumerState {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
- 'Advanced Settings',
+ AppLocalizations.of(context)!.advancedSettings,
style: context.conduitTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
),
@@ -582,7 +582,7 @@ class _ServerConnectionPageState extends ConsumerState {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
- 'Custom Headers',
+ AppLocalizations.of(context)!.customHeaders,
style: context.conduitTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
),
@@ -600,7 +600,7 @@ class _ServerConnectionPageState extends ConsumerState {
),
const SizedBox(height: Spacing.xs),
Text(
- 'Add custom HTTP headers for authentication, API keys, or special server requirements.',
+ AppLocalizations.of(context)!.customHeadersDescription,
style: context.conduitTheme.bodySmall?.copyWith(
color: context.conduitTheme.textSecondary,
),
@@ -787,13 +787,13 @@ class _ServerConnectionPageState extends ConsumerState {
// Check for duplicates
if (_customHeaders.containsKey(key)) {
- _showHeaderError('Header "$key" already exists. Remove it first to update.');
+ _showHeaderError(AppLocalizations.of(context)!.headerAlreadyExists(key));
return;
}
// Check header count limit
if (_customHeaders.length >= 10) {
- _showHeaderError('Maximum of 10 custom headers allowed. Remove some to add more.');
+ _showHeaderError(AppLocalizations.of(context)!.maxHeadersReachedDetail);
return;
}
@@ -807,47 +807,47 @@ class _ServerConnectionPageState extends ConsumerState {
String? _validateHeaderKey(String key) {
// RFC 7230 compliant header name validation
- if (key.isEmpty) return 'Header name cannot be empty';
- if (key.length > 64) return 'Header name too long (max 64 characters)';
+ if (key.isEmpty) return AppLocalizations.of(context)!.headerNameEmpty;
+ if (key.length > 64) return AppLocalizations.of(context)!.headerNameTooLong;
// Check for valid characters (RFC 7230: token characters)
if (!RegExp(r'^[a-zA-Z0-9!#$&\-^_`|~]+$').hasMatch(key)) {
- return 'Invalid header name. Use only letters, numbers, and these symbols: !#\$&-^_`|~';
+ return AppLocalizations.of(context)!.headerNameInvalidChars;
}
// Check for reserved headers that should not be overridden
final lowerKey = key.toLowerCase();
final reservedHeaders = {
- 'authorization', 'content-type', 'content-length', 'host',
+ 'authorization', 'content-type', 'content-length', 'host',
'user-agent', 'accept', 'accept-encoding', 'connection',
'transfer-encoding', 'upgrade', 'via', 'warning'
};
if (reservedHeaders.contains(lowerKey)) {
- return 'Cannot override reserved header "$key"';
+ return AppLocalizations.of(context)!.headerNameReserved(key);
}
return null;
}
String? _validateHeaderValue(String value) {
- if (value.isEmpty) return 'Header value cannot be empty';
- if (value.length > 1024) return 'Header value too long (max 1024 characters)';
+ if (value.isEmpty) return AppLocalizations.of(context)!.headerValueEmpty;
+ if (value.length > 1024) return AppLocalizations.of(context)!.headerValueTooLong;
// Check for valid characters (no control characters except tab)
for (int i = 0; i < value.length; i++) {
final char = value.codeUnitAt(i);
// Allow printable ASCII (32-126) and tab (9)
if (char != 9 && (char < 32 || char > 126)) {
- return 'Header value contains invalid characters. Use only printable ASCII.';
+ return AppLocalizations.of(context)!.headerValueInvalidChars;
}
}
// Check for security-sensitive patterns
- if (value.toLowerCase().contains('script') ||
- value.contains('<') ||
+ if (value.toLowerCase().contains('script') ||
+ value.contains('<') ||
value.contains('>')) {
- return 'Header value appears to contain potentially unsafe content';
+ return AppLocalizations.of(context)!.headerValueUnsafe;
}
return null;
diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart
index ae04311..284e84a 100644
--- a/lib/l10n/app_localizations.dart
+++ b/lib/l10n/app_localizations.dart
@@ -404,19 +404,43 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Username or Email'**
String get usernameOrEmail;
-
+
/// No description provided for @password.
///
/// In en, this message translates to:
/// **'Password'**
String get password;
+ /// No description provided for @usernameOrEmailHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter your username or email'**
+ String get usernameOrEmailHint;
+
+ /// No description provided for @passwordHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter your password'**
+ String get passwordHint;
+
/// No description provided for @signInWithApiKey.
///
/// In en, this message translates to:
/// **'Sign in with API Key'**
String get signInWithApiKey;
+ /// No description provided for @enterApiKey.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter your API key'**
+ String get enterApiKey;
+
+ /// No description provided for @signingIn.
+ ///
+ /// In en, this message translates to:
+ /// **'Signing in...'**
+ String get signingIn;
+
/// No description provided for @connectToServer.
///
/// In en, this message translates to:
@@ -483,6 +507,84 @@ abstract class AppLocalizations {
/// **'Remove header'**
String get removeHeader;
+ /// No description provided for @advancedSettings.
+ ///
+ /// In en, this message translates to:
+ /// **'Advanced Settings'**
+ String get advancedSettings;
+
+ /// No description provided for @customHeaders.
+ ///
+ /// In en, this message translates to:
+ /// **'Custom Headers'**
+ String get customHeaders;
+
+ /// No description provided for @customHeadersDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'Add custom HTTP headers for authentication, API keys, or special server requirements.'**
+ String get customHeadersDescription;
+
+ /// No description provided for @headerNameEmpty.
+ ///
+ /// In en, this message translates to:
+ /// **'Header name cannot be empty'**
+ String get headerNameEmpty;
+
+ /// No description provided for @headerNameTooLong.
+ ///
+ /// In en, this message translates to:
+ /// **'Header name too long (max 64 characters)'**
+ String get headerNameTooLong;
+
+ /// No description provided for @headerNameInvalidChars.
+ ///
+ /// In en, this message translates to:
+ /// **'Invalid header name. Use only letters, numbers, and these symbols: !#\$&-^_`|~'**
+ String get headerNameInvalidChars;
+
+ /// No description provided for @headerNameReserved.
+ ///
+ /// In en, this message translates to:
+ /// **'Cannot override reserved header "{key}"'**
+ String headerNameReserved(String key);
+
+ /// No description provided for @headerValueEmpty.
+ ///
+ /// In en, this message translates to:
+ /// **'Header value cannot be empty'**
+ String get headerValueEmpty;
+
+ /// No description provided for @headerValueTooLong.
+ ///
+ /// In en, this message translates to:
+ /// **'Header value too long (max 1024 characters)'**
+ String get headerValueTooLong;
+
+ /// No description provided for @headerValueInvalidChars.
+ ///
+ /// In en, this message translates to:
+ /// **'Header value contains invalid characters. Use only printable ASCII.'**
+ String get headerValueInvalidChars;
+
+ /// No description provided for @headerValueUnsafe.
+ ///
+ /// In en, this message translates to:
+ /// **'Header value appears to contain potentially unsafe content'**
+ String get headerValueUnsafe;
+
+ /// No description provided for @headerAlreadyExists.
+ ///
+ /// In en, this message translates to:
+ /// **'Header "{key}" already exists. Remove it first to update.'**
+ String headerAlreadyExists(String key);
+
+ /// No description provided for @maxHeadersReachedDetail.
+ ///
+ /// In en, this message translates to:
+ /// **'Maximum of 10 custom headers allowed. Remove some to add more.'**
+ String get maxHeadersReachedDetail;
+
/// No description provided for @connecting.
///
/// In en, this message translates to:
diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart
index d53df83..fa48259 100644
--- a/lib/l10n/app_localizations_de.dart
+++ b/lib/l10n/app_localizations_de.dart
@@ -177,9 +177,21 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get password => 'Passwort';
+ @override
+ String get usernameOrEmailHint => 'Gib deinen Benutzernamen oder deine E‑Mail ein';
+
+ @override
+ String get passwordHint => 'Gib dein Passwort ein';
+
@override
String get signInWithApiKey => 'Mit API-Schlüssel anmelden';
+ @override
+ String get enterApiKey => 'Gib deinen API-Schlüssel ein';
+
+ @override
+ String get signingIn => 'Anmeldung läuft...';
+
@override
String get connectToServer => 'Mit Server verbinden';
@@ -213,6 +225,51 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get removeHeader => 'Header entfernen';
+ @override
+ String get advancedSettings => 'Erweiterte Einstellungen';
+
+ @override
+ String get customHeaders => 'Benutzerdefinierte Header';
+
+ @override
+ String get customHeadersDescription =>
+ 'Füge benutzerdefinierte HTTP-Header für Authentifizierung, API-Schlüssel oder spezielle Serveranforderungen hinzu.';
+
+ @override
+ String get headerNameEmpty => 'Header-Name darf nicht leer sein';
+
+ @override
+ String get headerNameTooLong => 'Header-Name zu lang (max. 64 Zeichen)';
+
+ @override
+ String get headerNameInvalidChars =>
+ 'Ungültiger Header-Name. Verwende nur Buchstaben, Zahlen und diese Symbole: !#\$&-^_`|~';
+
+ @override
+ String headerNameReserved(String key) => 'Reservierter Header "$key" kann nicht überschrieben werden';
+
+ @override
+ String get headerValueEmpty => 'Header-Wert darf nicht leer sein';
+
+ @override
+ String get headerValueTooLong => 'Header-Wert zu lang (max. 1024 Zeichen)';
+
+ @override
+ String get headerValueInvalidChars =>
+ 'Header-Wert enthält ungültige Zeichen. Verwende nur druckbare ASCII-Zeichen.';
+
+ @override
+ String get headerValueUnsafe =>
+ 'Header-Wert scheint potenziell unsicheren Inhalt zu enthalten';
+
+ @override
+ String headerAlreadyExists(String key) =>
+ 'Header "$key" existiert bereits. Entferne ihn zuerst, um ihn zu aktualisieren.';
+
+ @override
+ String get maxHeadersReachedDetail =>
+ 'Maximal 10 benutzerdefinierte Header erlaubt. Entferne einige, um weitere hinzuzufügen.';
+
@override
String get connecting => 'Verbindung wird hergestellt...';
diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart
index e5cc131..cd5a0b5 100644
--- a/lib/l10n/app_localizations_en.dart
+++ b/lib/l10n/app_localizations_en.dart
@@ -177,9 +177,21 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get password => 'Password';
+ @override
+ String get usernameOrEmailHint => 'Enter your username or email';
+
+ @override
+ String get passwordHint => 'Enter your password';
+
@override
String get signInWithApiKey => 'Sign in with API Key';
+ @override
+ String get enterApiKey => 'Enter your API key';
+
+ @override
+ String get signingIn => 'Signing in...';
+
@override
String get connectToServer => 'Connect to Server';
@@ -213,6 +225,51 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get removeHeader => 'Remove header';
+ @override
+ String get advancedSettings => 'Advanced Settings';
+
+ @override
+ String get customHeaders => 'Custom Headers';
+
+ @override
+ String get customHeadersDescription =>
+ 'Add custom HTTP headers for authentication, API keys, or special server requirements.';
+
+ @override
+ String get headerNameEmpty => 'Header name cannot be empty';
+
+ @override
+ String get headerNameTooLong => 'Header name too long (max 64 characters)';
+
+ @override
+ String get headerNameInvalidChars =>
+ 'Invalid header name. Use only letters, numbers, and these symbols: !#\$&-^_`|~';
+
+ @override
+ String headerNameReserved(String key) => 'Cannot override reserved header "$key"';
+
+ @override
+ String get headerValueEmpty => 'Header value cannot be empty';
+
+ @override
+ String get headerValueTooLong => 'Header value too long (max 1024 characters)';
+
+ @override
+ String get headerValueInvalidChars =>
+ 'Header value contains invalid characters. Use only printable ASCII.';
+
+ @override
+ String get headerValueUnsafe =>
+ 'Header value appears to contain potentially unsafe content';
+
+ @override
+ String headerAlreadyExists(String key) =>
+ 'Header "$key" already exists. Remove it first to update.';
+
+ @override
+ String get maxHeadersReachedDetail =>
+ 'Maximum of 10 custom headers allowed. Remove some to add more.';
+
@override
String get connecting => 'Connecting...';
diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart
index e40f8fc..185db46 100644
--- a/lib/l10n/app_localizations_fr.dart
+++ b/lib/l10n/app_localizations_fr.dart
@@ -177,9 +177,21 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get password => 'Mot de passe';
+ @override
+ String get usernameOrEmailHint => 'Entrez votre nom d\'utilisateur ou e‑mail';
+
+ @override
+ String get passwordHint => 'Entrez votre mot de passe';
+
@override
String get signInWithApiKey => 'Se connecter avec une clé API';
+ @override
+ String get enterApiKey => 'Entrez votre clé API';
+
+ @override
+ String get signingIn => 'Connexion en cours...';
+
@override
String get connectToServer => 'Se connecter au serveur';
@@ -213,6 +225,53 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get removeHeader => 'Supprimer l\'en-tête';
+ @override
+ String get advancedSettings => 'Paramètres avancés';
+
+ @override
+ String get customHeaders => 'En-têtes personnalisés';
+
+ @override
+ String get customHeadersDescription =>
+ 'Ajoutez des en-têtes HTTP personnalisés pour l\'authentification, les clés API ou des exigences spécifiques du serveur.';
+
+ @override
+ String get headerNameEmpty => 'Le nom de l\'en-tête ne peut pas être vide';
+
+ @override
+ String get headerNameTooLong => 'Nom d\'en-tête trop long (max 64 caractères)';
+
+ @override
+ String get headerNameInvalidChars =>
+ 'Nom d\'en-tête invalide. Utilisez uniquement des lettres, des chiffres et ces symboles : !#\$&-^_`|~';
+
+ @override
+ String headerNameReserved(String key) =>
+ 'Impossible d\'écraser l\'en-tête réservé « $key »';
+
+ @override
+ String get headerValueEmpty => 'La valeur de l\'en-tête ne peut pas être vide';
+
+ @override
+ String get headerValueTooLong =>
+ 'Valeur d\'en-tête trop longue (max 1024 caractères)';
+
+ @override
+ String get headerValueInvalidChars =>
+ 'La valeur de l\'en-tête contient des caractères invalides. Utilisez uniquement des caractères ASCII imprimables.';
+
+ @override
+ String get headerValueUnsafe =>
+ 'La valeur de l\'en-tête semble contenir du contenu potentiellement dangereux';
+
+ @override
+ String headerAlreadyExists(String key) =>
+ 'L\'en-tête « $key » existe déjà. Supprimez-le d\'abord pour le modifier.';
+
+ @override
+ String get maxHeadersReachedDetail =>
+ 'Maximum 10 en-têtes personnalisés. Supprimez-en pour en ajouter.';
+
@override
String get connecting => 'Connexion en cours...';
diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart
index ed0db42..5dd5f6c 100644
--- a/lib/l10n/app_localizations_it.dart
+++ b/lib/l10n/app_localizations_it.dart
@@ -177,9 +177,21 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get password => 'Password';
+ @override
+ String get usernameOrEmailHint => 'Inserisci il tuo username o e‑mail';
+
+ @override
+ String get passwordHint => 'Inserisci la password';
+
@override
String get signInWithApiKey => 'Accedi con chiave API';
+ @override
+ String get enterApiKey => 'Inserisci la tua chiave API';
+
+ @override
+ String get signingIn => 'Accesso in corso...';
+
@override
String get connectToServer => 'Connetti al server';
@@ -213,6 +225,52 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get removeHeader => 'Rimuovi header';
+ @override
+ String get advancedSettings => 'Impostazioni avanzate';
+
+ @override
+ String get customHeaders => 'Header personalizzati';
+
+ @override
+ String get customHeadersDescription =>
+ 'Aggiungi header HTTP personalizzati per autenticazione, chiavi API o requisiti speciali del server.';
+
+ @override
+ String get headerNameEmpty => 'Il nome header non può essere vuoto';
+
+ @override
+ String get headerNameTooLong => 'Nome header troppo lungo (max 64 caratteri)';
+
+ @override
+ String get headerNameInvalidChars =>
+ 'Nome header non valido. Usa solo lettere, numeri e questi simboli: !#\$&-^_`|~';
+
+ @override
+ String headerNameReserved(String key) =>
+ 'Impossibile sovrascrivere l\'header riservato "$key"';
+
+ @override
+ String get headerValueEmpty => 'Il valore header non può essere vuoto';
+
+ @override
+ String get headerValueTooLong => 'Valore header troppo lungo (max 1024 caratteri)';
+
+ @override
+ String get headerValueInvalidChars =>
+ 'Il valore header contiene caratteri non validi. Usa solo ASCII stampabile.';
+
+ @override
+ String get headerValueUnsafe =>
+ 'Il valore header sembra contenere contenuto potenzialmente non sicuro';
+
+ @override
+ String headerAlreadyExists(String key) =>
+ 'L\'header "$key" esiste già. Rimuovilo prima per aggiornarlo.';
+
+ @override
+ String get maxHeadersReachedDetail =>
+ 'Massimo 10 header personalizzati consentiti. Rimuovine alcuni per aggiungerne altri.';
+
@override
String get connecting => 'Connessione in corso...';