62 files changed
+19585
-908
.gitignore
+3
-1
@@ -48,7 +48,9 @@ app.*.map.json
48
.fvm/
49
playstore_keys
50
.agent/
51
+.agents/
52
.claude/
53
.dart_skills/
54
private/
54
-plans/
\ No newline at end of file
55
+plans/
56
+skills-lock.json
android/.kotlin/sessions/kotlin-compiler-1529230861625166061.salive
l10n.yaml
new
+3
@@ -0,0 +1,3 @@
1
+arb-dir: lib/l10n
2
+template-arb-file: app_en.arb
3
+output-localization-file: app_localizations.dart
lib/app.dart
+28
@@ -1,7 +1,9 @@
1
import 'package:flutter/material.dart';
2
+import 'package:flutter_localizations/flutter_localizations.dart';
3
import 'package:flutter_riverpod/flutter_riverpod.dart';
4
import 'package:go_router/go_router.dart';
5
import 'package:shadcn_ui/shadcn_ui.dart';
6
+import 'l10n/app_localizations.dart';
7
8
import 'core/models/enums.dart';
9
import 'core/providers/app_providers.dart';
@@ -152,6 +154,7 @@ class App extends ConsumerWidget {
154
Widget build(BuildContext context, WidgetRef ref) {
155
final router = ref.watch(routerProvider);
156
final appThemeType = ref.watch(themeModeProvider);
157
+ final localeCode = ref.watch(settingsProvider.select((s) => s.localeCode));
158
159
ThemeData theme = AppTheme.lightTheme;
160
ThemeData darkTheme = AppTheme.darkTheme;
@@ -195,6 +198,31 @@ class App extends ConsumerWidget {
198
darkTheme: darkTheme,
199
themeMode: themeMode,
200
routerConfig: router,
201
+ localizationsDelegates: const [
202
+ AppLocalizations.delegate,
203
+ GlobalMaterialLocalizations.delegate,
204
+ GlobalWidgetsLocalizations.delegate,
205
+ GlobalCupertinoLocalizations.delegate,
206
+ ],
207
+ supportedLocales: const [
208
+ Locale('en'),
209
+ Locale('ar'),
210
+ Locale('bn'),
211
+ Locale('zh'),
212
+ Locale('es'),
213
+ Locale('hi'),
214
+ ],
215
+ locale: localeCode != null ? Locale(localeCode) : null,
216
+ localeResolutionCallback: (locale, supportedLocales) {
217
+ if (localeCode != null) {
218
+ final langLocale = Locale(localeCode);
219
+ if (supportedLocales.contains(langLocale)) return langLocale;
220
+ }
221
+ for (final l in supportedLocales) {
222
+ if (l.languageCode == locale?.languageCode) return l;
223
+ }
224
+ return const Locale('en');
225
+ },
226
builder: (context, child) {
227
return ShadAppBuilder(child: child!);
228
},
lib/bootstrap/bootstrap_host.dart
+13
@@ -1,3 +1,5 @@
1
+import 'dart:convert';
2
+
3
import 'package:flutter/material.dart';
4
import 'package:flutter_riverpod/flutter_riverpod.dart';
5
import 'package:shared_preferences/shared_preferences.dart';
@@ -23,6 +25,7 @@ class BootstrapHost extends StatefulWidget {
25
class _BootstrapHostState extends State<BootstrapHost> {
26
BootstrapState _state = const BootstrapState();
27
ProviderContainer? _container;
28
+ Locale? _savedLocale;
29
30
@override
31
void initState() {
@@ -45,6 +48,15 @@ class _BootstrapHostState extends State<BootstrapHost> {
48
49
_updateStage(BootstrapStage.preparingApp, 'Preparing app...');
50
51
+ try {
52
+ final settingsJson = prefs.getString('appSettings');
53
+ if (settingsJson != null) {
54
+ final settings = json.decode(settingsJson) as Map<String, dynamic>;
55
+ final code = settings['localeCode'] as String?;
56
+ if (code != null) _savedLocale = Locale(code);
57
+ }
58
+ } catch (_) {}
59
+
60
final container = ProviderContainer(
61
overrides: [
62
sharedPreferencesProvider.overrideWithValue(prefs),
@@ -110,6 +122,7 @@ class _BootstrapHostState extends State<BootstrapHost> {
122
123
return BootstrapScreen(
124
state: _state,
125
+ locale: _savedLocale,
126
onRetry: _state.stage == BootstrapStage.error ? _runBootstrap : null,
127
);
128
}
lib/bootstrap/bootstrap_screen.dart
+124
-84
@@ -1,108 +1,148 @@
1
import 'package:flutter/material.dart';
2
+import 'package:flutter_localizations/flutter_localizations.dart';
3
import 'package:loading_animation_widget/loading_animation_widget.dart';
4
import '../core/theme/colors.dart';
5
+import '../l10n/app_localizations.dart';
6
import 'bootstrap_state.dart';
7
8
class BootstrapScreen extends StatelessWidget {
9
final BootstrapState state;
10
final VoidCallback? onRetry;
11
+ final Locale? locale;
12
10
- const BootstrapScreen({super.key, required this.state, this.onRetry});
13
+ const BootstrapScreen({
14
+ super.key,
15
+ required this.state,
16
+ this.onRetry,
17
+ this.locale,
18
+ });
19
+
20
+ String _stageMessage(AppLocalizations l10n) {
21
+ switch (state.stage) {
22
+ case BootstrapStage.initializing:
23
+ return l10n.initializing;
24
+ case BootstrapStage.preparingApp:
25
+ return l10n.preparing_app;
26
+ case BootstrapStage.initializingServices:
27
+ return l10n.initializing_services;
28
+ case BootstrapStage.configuringServer:
29
+ return l10n.configuring_server;
30
+ case BootstrapStage.done:
31
+ return l10n.ready;
32
+ case BootstrapStage.error:
33
+ return l10n.startup_failed;
34
+ }
35
+ }
36
37
@override
38
Widget build(BuildContext context) {
39
final brightness =
40
WidgetsBinding.instance.platformDispatcher.platformBrightness;
41
final isDark = brightness == Brightness.dark;
42
+ final locale = this.locale ??
43
+ WidgetsBinding.instance.platformDispatcher.locale;
44
18
- return Directionality(
19
- textDirection: TextDirection.ltr,
20
- child: Material(
21
- color: isDark ? AppColors.darkBackground : AppColors.lightBackground,
22
- child: SafeArea(
23
- child: Center(
24
- child: Padding(
25
- padding: const EdgeInsets.symmetric(horizontal: 32),
26
- child: Column(
27
- mainAxisSize: MainAxisSize.min,
28
- children: [
29
- ClipRRect(
30
- borderRadius: BorderRadius.circular(12),
31
- child: Image.asset(
32
- 'assets/images/logo.webp',
33
- width: 72,
34
- height: 72,
35
- ),
36
- ),
37
- const SizedBox(height: 24),
38
- Text(
39
- 'LocalMind',
40
- style: TextStyle(
41
- fontSize: 28,
42
- fontWeight: FontWeight.bold,
43
- color: isDark
44
- ? AppColors.darkPrimaryText
45
- : AppColors.lightPrimaryText,
46
- letterSpacing: 1,
47
- ),
48
- ),
49
- const SizedBox(height: 48),
50
- if (state.stage != BootstrapStage.error) ...[
51
- LoadingAnimationWidget.fourRotatingDots(
52
- size: 32,
53
- color: isDark
54
- ? AppColors.darkPrimaryText
55
- : AppColors.lightPrimaryText,
56
- ),
57
- const SizedBox(height: 24),
58
- Text(
59
- state.statusMessage,
60
- textAlign: TextAlign.center,
61
- style: TextStyle(
62
- fontSize: 14,
63
- color: isDark
64
- ? AppColors.darkMutedText
65
- : AppColors.lightMutedText,
45
+ return Localizations(
46
+ locale: locale,
47
+ delegates: [
48
+ AppLocalizations.delegate,
49
+ GlobalMaterialLocalizations.delegate,
50
+ GlobalWidgetsLocalizations.delegate,
51
+ ],
52
+ child: Builder(builder: (context) {
53
+ final l10n = AppLocalizations.of(context)!;
54
+ return Directionality(
55
+ textDirection: locale.languageCode.startsWith('ar')
56
+ ? TextDirection.rtl
57
+ : TextDirection.ltr,
58
+ child: Material(
59
+ color: isDark ? AppColors.darkBackground : AppColors.lightBackground,
60
+ child: SafeArea(
61
+ child: Center(
62
+ child: Padding(
63
+ padding: const EdgeInsets.symmetric(horizontal: 32),
64
+ child: Column(
65
+ mainAxisSize: MainAxisSize.min,
66
+ children: [
67
+ ClipRRect(
68
+ borderRadius: BorderRadius.circular(12),
69
+ child: Image.asset(
70
+ 'assets/images/logo.webp',
71
+ width: 72,
72
+ height: 72,
73
+ ),
74
),
67
- ),
68
- ] else ...[
69
- Icon(Icons.error_outline, size: 40, color: AppColors.error),
70
- const SizedBox(height: 16),
71
- Text(
72
- 'Something went wrong',
73
- style: TextStyle(
74
- fontSize: 16,
75
- fontWeight: FontWeight.w600,
76
- color: isDark
77
- ? AppColors.darkPrimaryText
78
- : AppColors.lightPrimaryText,
75
+ const SizedBox(height: 24),
76
+ Text(
77
+ l10n.app_name,
78
+ style: TextStyle(
79
+ fontSize: 28,
80
+ fontWeight: FontWeight.bold,
81
+ color: isDark
82
+ ? AppColors.darkPrimaryText
83
+ : AppColors.lightPrimaryText,
84
+ letterSpacing: 1,
85
+ ),
86
),
80
- ),
81
- const SizedBox(height: 8),
82
- Text(
83
- state.error?.toString() ?? 'Unknown error',
84
- textAlign: TextAlign.center,
85
- style: TextStyle(
86
- fontSize: 13,
87
- color: isDark
88
- ? AppColors.darkMutedText
89
- : AppColors.lightMutedText,
90
- ),
91
- ),
92
- const SizedBox(height: 24),
93
- if (onRetry != null)
94
- ElevatedButton.icon(
95
- onPressed: onRetry,
96
- icon: const Icon(Icons.refresh, size: 18),
97
- label: const Text('Retry'),
98
- ),
99
- ],
100
- ],
87
+ const SizedBox(height: 48),
88
+ if (state.stage != BootstrapStage.error) ...[
89
+ LoadingAnimationWidget.fourRotatingDots(
90
+ size: 32,
91
+ color: isDark
92
+ ? AppColors.darkPrimaryText
93
+ : AppColors.lightPrimaryText,
94
+ ),
95
+ const SizedBox(height: 24),
96
+ Text(
97
+ _stageMessage(l10n),
98
+ textAlign: TextAlign.center,
99
+ style: TextStyle(
100
+ fontSize: 14,
101
+ color: isDark
102
+ ? AppColors.darkMutedText
103
+ : AppColors.lightMutedText,
104
+ ),
105
+ ),
106
+ ] else ...[
107
+ Icon(Icons.error_outline, size: 40, color: AppColors.error),
108
+ const SizedBox(height: 16),
109
+ Text(
110
+ l10n.something_went_wrong,
111
+ style: TextStyle(
112
+ fontSize: 16,
113
+ fontWeight: FontWeight.w600,
114
+ color: isDark
115
+ ? AppColors.darkPrimaryText
116
+ : AppColors.lightPrimaryText,
117
+ ),
118
+ ),
119
+ const SizedBox(height: 8),
120
+ Text(
121
+ state.error?.toString() ?? l10n.unknown_error,
122
+ textAlign: TextAlign.center,
123
+ style: TextStyle(
124
+ fontSize: 13,
125
+ color: isDark
126
+ ? AppColors.darkMutedText
127
+ : AppColors.lightMutedText,
128
+ ),
129
+ ),
130
+ const SizedBox(height: 24),
131
+ if (onRetry != null)
132
+ ElevatedButton.icon(
133
+ onPressed: onRetry,
134
+ icon: const Icon(Icons.refresh, size: 18),
135
+ label: Text(l10n.retry),
136
+ ),
137
+ ],
138
+ ],
139
+ ),
140
+ ),
141
),
142
),
143
),
104
- ),
105
- ),
144
+ );
145
+ }),
146
);
147
}
148
}
lib/core/providers/app_providers.dart
+2
@@ -96,6 +96,8 @@ class SettingsNotifier extends Notifier<AppSettings> {
96
_update(state.copyWith(kittenTtsModelVariant: value));
97
void setAutoSpeakEnabled(bool value) =>
98
_update(state.copyWith(autoSpeakEnabled: value));
99
+ void setLocaleCode(String? value) =>
100
+ _update(state.copyWith(localeCode: value));
101
102
Future<void> _update(AppSettings updated) async {
103
state = updated;
lib/features/chat/views/chat_screen.dart
+81
-73
@@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
4
import 'package:go_router/go_router.dart';
5
import 'package:shadcn_ui/shadcn_ui.dart';
6
import 'package:hugeicons/hugeicons.dart';
7
+import 'package:localmind/l10n/app_localizations.dart';
8
import '../../servers/views/components/server_icon_picker.dart';
9
import '../../servers/data/models/server.dart';
10
import '../../../core/models/enums.dart';
@@ -36,11 +37,11 @@ class ChatScreen extends ConsumerStatefulWidget {
37
class _ChatScreenState extends ConsumerState<ChatScreen> {
38
final ScrollController _scrollController = ScrollController();
39
39
- static const List<String> _quickPrompts = [
40
- 'Help me write a function',
41
- 'Explain this code',
42
- 'Debug this for me',
43
- 'How do I use async/await?',
40
+ List<String> _quickPrompts(AppLocalizations l10n) => [
41
+ l10n.quick_write,
42
+ l10n.quick_explain,
43
+ l10n.quick_debug,
44
+ l10n.quick_async,
45
];
46
47
@override
@@ -56,6 +57,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
57
58
@override
59
Widget build(BuildContext context) {
60
+ final l10n = AppLocalizations.of(context)!;
61
ref.watch(autoSelectFirstLoadedModelProvider);
62
63
final chatState = ref.watch(chatProvider);
@@ -70,9 +72,6 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
72
final theme = Theme.of(context);
73
final isDark = theme.brightness == Brightness.dark;
74
73
- // Autoscroll disabled as per user request
74
- // ref.listen(chatProvider, (previous, next) { ... });
75
-
75
return Column(
76
children: [
77
_ModelTopBar(
@@ -104,7 +103,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
103
const SizedBox(width: 8),
104
],
105
Text(
107
- activeServer?.name ?? 'LocalMind',
106
+ activeServer?.name ?? l10n.chat_title,
107
style: const TextStyle(
108
fontSize: 18,
109
fontWeight: FontWeight.bold,
@@ -142,11 +141,11 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
141
context,
142
initialTab: 'parameters',
143
),
145
- tooltip: 'Chat Parameters',
144
+ tooltip: l10n.chat_parameters_tooltip,
145
),
147
- Positioned(
146
+ PositionedDirectional(
147
top: 4,
149
- right: 0,
148
+ end: 0,
149
child: Container(
150
padding: const EdgeInsets.all(2),
151
decoration: BoxDecoration(
@@ -168,11 +167,11 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
167
icon: const Icon(Icons.more_vert),
168
onSelected: (value) => _handleMenuAction(value, context),
169
itemBuilder: (context) => [
171
- const PopupMenuItem(
170
+ PopupMenuItem(
171
value: 'new_chat',
172
child: ListTile(
174
- leading: Icon(Icons.add),
175
- title: Text('New Chat'),
173
+ leading: const Icon(Icons.add),
174
+ title: Text(l10n.nav_new_chat),
175
contentPadding: EdgeInsets.zero,
176
),
177
),
@@ -185,25 +184,25 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
184
: Icons.smart_toy_outlined,
185
),
186
title: Text(
188
- persona != null ? 'Change Persona' : 'Set Persona',
187
+ persona != null ? l10n.change_persona : l10n.set_persona,
188
),
189
contentPadding: EdgeInsets.zero,
190
),
191
),
192
if (persona != null)
194
- const PopupMenuItem(
193
+ PopupMenuItem(
194
value: 'remove_persona',
195
child: ListTile(
197
- leading: Icon(Icons.person_remove_outlined),
198
- title: Text('Remove Persona'),
196
+ leading: const Icon(Icons.person_remove_outlined),
197
+ title: Text(l10n.remove_persona),
198
contentPadding: EdgeInsets.zero,
199
),
200
),
202
- const PopupMenuItem(
201
+ PopupMenuItem(
202
value: 'clear',
203
child: ListTile(
205
- leading: Icon(Icons.delete_outline),
206
- title: Text('Clear Conversation'),
204
+ leading: const Icon(Icons.delete_outline),
205
+ title: Text(l10n.clear_conversation),
206
contentPadding: EdgeInsets.zero,
207
),
208
),
@@ -248,7 +247,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
247
_EmptyState(
248
onQuickPrompt: (prompt) =>
249
ref.read(chatProvider.notifier).sendMessage(prompt),
251
- quickPrompts: _quickPrompts,
250
+ quickPrompts: _quickPrompts(l10n),
251
recentConversations: ref.watch(
252
conv.recentConversationsProvider,
253
),
@@ -348,25 +347,26 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
347
case 'clear':
348
showDialog(
349
context: context,
351
- builder: (context) => AlertDialog(
352
- title: const Text('Clear conversation?'),
353
- content: const Text(
354
- 'This will delete all messages in this conversation.',
355
- ),
356
- actions: [
357
- TextButton(
358
- onPressed: () => Navigator.pop(context),
359
- child: const Text('Cancel'),
360
- ),
361
- TextButton(
362
- onPressed: () {
363
- Navigator.pop(context);
364
- ref.read(chatProvider.notifier).clearConversation();
365
- },
366
- child: const Text('Clear'),
367
- ),
368
- ],
369
- ),
350
+ builder: (context) {
351
+ final dlgL10n = AppLocalizations.of(context)!;
352
+ return AlertDialog(
353
+ title: Text(dlgL10n.clear_conversation_title),
354
+ content: Text(dlgL10n.clear_conversation_body),
355
+ actions: [
356
+ TextButton(
357
+ onPressed: () => Navigator.pop(context),
358
+ child: Text(dlgL10n.cancel),
359
+ ),
360
+ TextButton(
361
+ onPressed: () {
362
+ Navigator.pop(context);
363
+ ref.read(chatProvider.notifier).clearConversation();
364
+ },
365
+ child: Text(dlgL10n.clear),
366
+ ),
367
+ ],
368
+ );
369
+ },
370
);
371
break;
372
}
@@ -383,6 +383,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
383
context: context,
384
isScrollControlled: true,
385
builder: (ctx) {
386
+ final sheetL10n = AppLocalizations.of(context)!;
387
return SafeArea(
388
child: Container(
389
padding: const EdgeInsets.symmetric(vertical: 16),
@@ -401,7 +402,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
402
),
403
const SizedBox(height: 16),
404
Text(
404
- 'Select Persona',
405
+ sheetL10n.select_persona,
406
style: TextStyle(
407
fontSize: 18,
408
fontWeight: FontWeight.bold,
@@ -483,6 +484,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
484
context: context,
485
isScrollControlled: true,
486
builder: (ctx) {
487
+ final sheetL10n = AppLocalizations.of(context)!;
488
return SafeArea(
489
child: Container(
490
padding: const EdgeInsets.symmetric(vertical: 16),
@@ -501,7 +503,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
503
),
504
const SizedBox(height: 16),
505
Text(
504
- 'Select Persona',
506
+ sheetL10n.select_persona,
507
style: TextStyle(
508
fontSize: 18,
509
fontWeight: FontWeight.bold,
@@ -590,6 +592,7 @@ class _ModelTopBar extends StatelessWidget {
592
593
@override
594
Widget build(BuildContext context) {
595
+ final l10n = AppLocalizations.of(context)!;
596
final theme = Theme.of(context);
597
final isDark = theme.brightness == Brightness.dark;
598
@@ -609,7 +612,7 @@ class _ModelTopBar extends StatelessWidget {
612
children: [
613
Flexible(
614
child: Text(
612
- selectedModel?.displayName ?? 'Select Model',
615
+ selectedModel?.displayName ?? l10n.select_model,
616
style: TextStyle(
617
fontSize: 13,
618
fontWeight: FontWeight.w500,
@@ -642,6 +645,7 @@ class _ConnectionBanner extends StatelessWidget {
645
646
@override
647
Widget build(BuildContext context) {
648
+ final l10n = AppLocalizations.of(context)!;
649
final isError = status == ConnectionStatus.error;
650
651
return Container(
@@ -661,8 +665,8 @@ class _ConnectionBanner extends StatelessWidget {
665
Expanded(
666
child: Text(
667
isError
664
- ? 'Connection error. Check your server.'
665
- : 'Disconnected from server.',
668
+ ? l10n.connection_error
669
+ : l10n.disconnected,
670
style: TextStyle(
671
fontSize: 13,
672
color: isError ? Colors.red : Colors.orange[700],
@@ -674,7 +678,7 @@ class _ConnectionBanner extends StatelessWidget {
678
context.push(AppRoutes.servers);
679
},
680
child: Text(
677
- 'Configure',
681
+ l10n.configure,
682
style: TextStyle(
683
fontSize: 13,
684
color: isError ? Colors.red : Colors.orange[700],
@@ -766,6 +770,7 @@ class _EmptyStateState extends State<_EmptyState>
770
771
@override
772
Widget build(BuildContext context) {
773
+ final l10n = AppLocalizations.of(context)!;
774
final theme = Theme.of(context);
775
final isDark = theme.brightness == Brightness.dark;
776
@@ -819,7 +824,7 @@ class _EmptyStateState extends State<_EmptyState>
824
Flexible(
825
child: Text(
826
widget.selectedModel?.displayName ??
822
- 'Select Model',
827
+ l10n.select_model,
828
style: TextStyle(
829
fontSize: 15,
830
fontWeight: FontWeight.w600,
@@ -886,7 +891,7 @@ class _EmptyStateState extends State<_EmptyState>
891
const SizedBox(width: 8),
892
Flexible(
893
child: Text(
889
- 'Select Persona',
894
+ l10n.select_persona,
895
style: TextStyle(
896
fontSize: 15,
897
fontWeight: FontWeight.w600,
@@ -919,7 +924,7 @@ class _EmptyStateState extends State<_EmptyState>
924
return Opacity(opacity: value, child: child);
925
},
926
child: Text(
922
- 'Start a conversation',
927
+ l10n.start_conversation,
928
style: TextStyle(
929
fontSize: 16,
930
fontWeight: FontWeight.w500,
@@ -974,7 +979,7 @@ class _EmptyStateState extends State<_EmptyState>
979
mainAxisAlignment: MainAxisAlignment.center,
980
children: [
981
Text(
977
- 'Recent chats',
982
+ l10n.recent_chats,
983
style: TextStyle(
984
fontSize: 14,
985
fontWeight: FontWeight.w500,
@@ -987,7 +992,7 @@ class _EmptyStateState extends State<_EmptyState>
992
GestureDetector(
993
onTap: widget.onSeeAll,
994
child: Text(
990
- 'See all',
995
+ l10n.see_all,
996
style: TextStyle(
997
fontSize: 14,
998
fontWeight: FontWeight.w500,
@@ -1093,7 +1098,9 @@ class _RecentConversationItem extends ConsumerWidget {
1098
),
1099
),
1100
Icon(
1096
- Icons.chevron_right,
1101
+ Directionality.of(context) == TextDirection.rtl
1102
+ ? Icons.chevron_left
1103
+ : Icons.chevron_right,
1104
size: 18,
1105
color: isDark ? const Color(0xFF666666) : const Color(0xFF999999),
1106
),
@@ -1275,7 +1282,7 @@ class _SmartReplyChipsState extends ConsumerState<_SmartReplyChips>
1282
child: SizedBox(
1283
height: 40,
1284
child: ListView.separated(
1278
- padding: EdgeInsets.only(left: 8),
1285
+ padding: EdgeInsetsDirectional.only(start: 8),
1286
scrollDirection: Axis.horizontal,
1287
itemCount: suggestions.length,
1288
separatorBuilder: (_, _) => const SizedBox(width: 6),
@@ -1349,6 +1356,7 @@ class _PersonaIndicator extends StatelessWidget {
1356
1357
@override
1358
Widget build(BuildContext context) {
1359
+ final l10n = AppLocalizations.of(context)!;
1360
final theme = Theme.of(context);
1361
final isDark = theme.brightness == Brightness.dark;
1362
@@ -1396,7 +1404,7 @@ class _PersonaIndicator extends StatelessWidget {
1404
onPressed: onRemove,
1405
padding: EdgeInsets.zero,
1406
constraints: const BoxConstraints(),
1399
- tooltip: 'Remove Persona',
1407
+ tooltip: l10n.remove_persona,
1408
color: isDark ? Colors.white38 : Colors.black38,
1409
),
1410
],
@@ -1417,13 +1425,12 @@ class _CorruptedChatState extends ConsumerWidget {
1425
final VoidCallback onStartNewChat;
1426
1427
void _showDebugInfo(BuildContext context, WidgetRef ref) {
1428
+ final debugL10n = AppLocalizations.of(context)!;
1429
showShadDialog(
1430
context: context,
1431
builder: (context) => ShadDialog(
1423
- title: const Text('Technical Details'),
1424
- description: const Text(
1425
- 'Diagnostic information to help identify synchronization issues.',
1426
- ),
1432
+ title: Text(debugL10n.technical_details),
1433
+ description: Text(debugL10n.debug_dialog_desc),
1434
actions: [
1435
ShadButton.outline(
1436
onPressed: () {
@@ -1436,14 +1443,14 @@ Error: $errorMessage
1443
''';
1444
Clipboard.setData(ClipboardData(text: data));
1445
ScaffoldMessenger.of(context).showSnackBar(
1439
- const SnackBar(content: Text('Copied to clipboard')),
1446
+ SnackBar(content: Text(debugL10n.copied_to_clipboard)),
1447
);
1448
},
1442
- child: const Text('Copy Info'),
1449
+ child: Text(debugL10n.copy_info),
1450
),
1451
ShadButton(
1452
onPressed: () => Navigator.of(context).pop(),
1446
- child: const Text('Close'),
1453
+ child: Text(debugL10n.close),
1454
),
1455
],
1456
child: Container(
@@ -1453,13 +1460,13 @@ Error: $errorMessage
1460
crossAxisAlignment: CrossAxisAlignment.start,
1461
mainAxisSize: MainAxisSize.min,
1462
children: [
1456
- _DebugRow(label: 'Conversation ID', value: conversation.id),
1463
+ _DebugRow(label: debugL10n.conversation_id, value: conversation.id),
1464
_DebugRow(
1458
- label: 'Created At',
1465
+ label: debugL10n.created_at,
1466
value: conversation.createdAt.toIso8601String(),
1467
),
1468
_DebugRow(
1462
- label: 'Expected Messages',
1469
+ label: debugL10n.expected_messages,
1470
value: '${conversation.messageCount}',
1471
),
1472
if (errorMessage != null)
@@ -1468,9 +1475,9 @@ Error: $errorMessage
1475
child: Column(
1476
crossAxisAlignment: CrossAxisAlignment.start,
1477
children: [
1471
- const Text(
1472
- 'Last Error:',
1473
- style: TextStyle(
1478
+ Text(
1479
+ debugL10n.last_error,
1480
+ style: const TextStyle(
1481
fontSize: 12,
1482
fontWeight: FontWeight.bold,
1483
color: Colors.red,
@@ -1503,6 +1510,7 @@ Error: $errorMessage
1510
1511
@override
1512
Widget build(BuildContext context, WidgetRef ref) {
1513
+ final l10n = AppLocalizations.of(context)!;
1514
final theme = Theme.of(context);
1515
final isDark = theme.brightness == Brightness.dark;
1516
@@ -1526,7 +1534,7 @@ Error: $errorMessage
1534
),
1535
const SizedBox(height: 24),
1536
Text(
1529
- 'History Missing',
1537
+ l10n.history_missing_title,
1538
style: TextStyle(
1539
fontSize: 20,
1540
fontWeight: FontWeight.bold,
@@ -1535,7 +1543,7 @@ Error: $errorMessage
1543
),
1544
const SizedBox(height: 12),
1545
Text(
1538
- 'Either the messages in this chat were deleted or the history record is corrupted.',
1546
+ l10n.history_missing_desc,
1547
textAlign: TextAlign.center,
1548
style: TextStyle(
1549
fontSize: 15,
@@ -1549,12 +1557,12 @@ Error: $errorMessage
1557
ShadButton(
1558
onPressed: onStartNewChat,
1559
leading: const Icon(Icons.add_rounded, size: 20),
1552
- child: const Text('Start New Chat'),
1560
+ child: Text(l10n.start_new_chat),
1561
),
1562
const SizedBox(width: 12),
1563
ShadButton.outline(
1564
onPressed: () => _showDebugInfo(context, ref),
1557
- child: const Text('Technical Details'),
1565
+ child: Text(l10n.technical_details),
1566
),
1567
],
1568
),
lib/features/chat/views/components/chat_bubble.dart
+25
-17
@@ -1,6 +1,7 @@
1
import 'dart:io';
2
import 'package:flutter/material.dart';
3
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
4
+import 'package:localmind/l10n/app_localizations.dart';
5
import '../../../../core/models/enums.dart';
6
import '../../data/models/message.dart';
7
import 'code_block.dart';
@@ -36,7 +37,7 @@ class ChatBubble extends StatelessWidget {
37
switch (message.role) {
38
case MessageRole.user:
39
return _AnimatedBubble(
39
- alignment: Alignment.centerRight,
40
+ alignment: AlignmentDirectional.centerEnd,
41
child: _UserBubble(
42
message: message,
43
onCopy: onCopy,
@@ -46,7 +47,7 @@ class ChatBubble extends StatelessWidget {
47
);
48
case MessageRole.assistant:
49
return _AnimatedBubble(
49
- alignment: Alignment.centerLeft,
50
+ alignment: AlignmentDirectional.centerStart,
51
child: _AssistantBubble(
52
message: message,
53
onCopy: onCopy,
@@ -57,12 +58,12 @@ class ChatBubble extends StatelessWidget {
58
);
59
case MessageRole.system:
60
return _AnimatedBubble(
60
- alignment: Alignment.center,
61
+ alignment: AlignmentDirectional.center,
62
child: _SystemBubble(message: message),
63
);
64
case MessageRole.tool:
65
return _AnimatedBubble(
65
- alignment: Alignment.centerLeft,
66
+ alignment: AlignmentDirectional.centerStart,
67
child: _ToolBubble(message: message),
68
);
69
}
@@ -73,13 +74,14 @@ class _AnimatedBubble extends StatelessWidget {
74
const _AnimatedBubble({required this.child, required this.alignment});
75
76
final Widget child;
76
- final Alignment alignment;
77
+ final AlignmentDirectional alignment;
78
79
@override
80
Widget build(BuildContext context) {
80
- final offset = alignment == Alignment.centerRight
81
+ final resolvedAlignment = alignment.resolve(Directionality.of(context));
82
+ final offset = resolvedAlignment == Alignment.centerRight
83
? const Offset(0.15, 0.0)
82
- : alignment == Alignment.centerLeft
84
+ : resolvedAlignment == Alignment.centerLeft
85
? const Offset(-0.15, 0.0)
86
: const Offset(0.0, 0.1);
87
@@ -123,7 +125,7 @@ class _UserBubble extends StatelessWidget {
125
final isDark = theme.brightness == Brightness.dark;
126
127
return Align(
126
- alignment: Alignment.centerRight,
128
+ alignment: AlignmentDirectional.centerEnd,
129
child: Column(
130
crossAxisAlignment: CrossAxisAlignment.end,
131
children: [
@@ -131,13 +133,16 @@ class _UserBubble extends StatelessWidget {
133
constraints: BoxConstraints(
134
maxWidth: MediaQuery.of(context).size.width * 0.75,
135
),
134
- margin: const EdgeInsets.only(left: 48, right: 8, top: 4, bottom: 2),
136
+ margin: const EdgeInsetsDirectional.only(start: 48, end: 8, top: 4, bottom: 2),
137
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
138
decoration: BoxDecoration(
139
color: isDark ? const Color(0xFF3B82F6) : const Color(0xFF2563EB),
138
- borderRadius: BorderRadius.circular(
139
- 18,
140
- ).copyWith(bottomRight: const Radius.circular(4)),
140
+ borderRadius: BorderRadiusDirectional.only(
141
+ topStart: Radius.circular(18),
142
+ topEnd: Radius.circular(18),
143
+ bottomStart: Radius.circular(18),
144
+ bottomEnd: const Radius.circular(4),
145
+ ),
146
),
147
child: Column(
148
crossAxisAlignment: CrossAxisAlignment.end,
@@ -163,7 +168,7 @@ class _UserBubble extends StatelessWidget {
168
),
169
),
170
Padding(
166
- padding: const EdgeInsets.only(right: 12, bottom: 4),
171
+ padding: const EdgeInsetsDirectional.only(end: 12, bottom: 4),
172
child: Row(
173
mainAxisSize: MainAxisSize.min,
174
children: [
@@ -357,7 +362,7 @@ class _MarkdownContent extends StatelessWidget {
362
),
363
),
364
),
360
- blockquotePadding: const EdgeInsets.only(left: 16),
365
+ blockquotePadding: EdgeInsetsDirectional.only(start: 16).resolve(Directionality.of(context)),
366
listBullet: TextStyle(color: isDark ? Colors.white : Colors.black),
367
tableHead: TextStyle(
368
color: isDark ? Colors.white : Colors.black,
@@ -442,16 +447,17 @@ class _ToolBubble extends StatelessWidget {
447
448
@override
449
Widget build(BuildContext context) {
450
+ final l10n = AppLocalizations.of(context)!;
451
final theme = Theme.of(context);
452
final isDark = theme.brightness == Brightness.dark;
453
454
return Align(
449
- alignment: Alignment.centerLeft,
455
+ alignment: AlignmentDirectional.centerStart,
456
child: Container(
457
constraints: BoxConstraints(
458
maxWidth: MediaQuery.of(context).size.width * 0.8,
459
),
454
- margin: const EdgeInsets.only(left: 8, right: 48, top: 4, bottom: 4),
460
+ margin: const EdgeInsetsDirectional.only(start: 8, end: 48, top: 4, bottom: 4),
461
padding: const EdgeInsets.all(12),
462
decoration: BoxDecoration(
463
color: isDark ? const Color(0xFF1F2937) : const Color(0xFFEEF2FF),
@@ -474,7 +480,9 @@ class _ToolBubble extends StatelessWidget {
480
),
481
const SizedBox(width: 4),
482
Text(
477
- 'Tool: ${message.toolCallId ?? "Unknown"}',
483
+ message.toolCallId != null
484
+ ? l10n.tool_label(message.toolCallId!)
485
+ : l10n.tool_unknown,
486
style: TextStyle(
487
fontSize: 12,
488
fontWeight: FontWeight.w500,
lib/features/chat/views/components/chat_input_bar.dart
+8
-18
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
5
import 'package:flutter_riverpod/flutter_riverpod.dart';
6
import 'package:haptic_feedback/haptic_feedback.dart';
7
import 'package:hugeicons/hugeicons.dart';
8
+import 'package:localmind/l10n/app_localizations.dart';
9
10
import '../../../../core/models/enums.dart';
11
import '../../../servers/providers/server_providers.dart';
@@ -90,7 +91,7 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
91
}
92
93
Widget _buildActionButton(bool canSend, bool isDark) {
93
- // Action button theme from image: circular, high contrast (black in light, white in dark)
94
+ final l10n = AppLocalizations.of(context)!;
95
final backgroundColor = isDark ? Colors.white : Colors.black;
96
final iconColor = isDark ? Colors.black : Colors.white;
97
@@ -134,7 +135,7 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
135
onPressed: widget.isStreaming
136
? _handleStop
137
: (canSend ? _handleSubmit : null),
137
- tooltip: widget.isStreaming ? 'Stop generation' : 'Send message',
138
+ tooltip: widget.isStreaming ? l10n.stop_generation_tooltip : l10n.send_message_tooltip,
139
),
140
),
141
),
@@ -143,6 +144,7 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
144
145
@override
146
Widget build(BuildContext context) {
147
+ final l10n = AppLocalizations.of(context)!;
148
final theme = Theme.of(context);
149
final isDark = theme.brightness == Brightness.dark;
150
final connectionStatus = ref.watch(connectionStatusProvider);
@@ -176,7 +178,6 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
178
child: Column(
179
mainAxisSize: MainAxisSize.min,
180
children: [
179
- // Attachment Preview Area
181
AnimatedSize(
182
duration: const Duration(milliseconds: 200),
183
curve: Curves.easeOut,
@@ -218,9 +219,9 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
219
),
220
),
221
),
221
- Positioned(
222
+ PositionedDirectional(
223
top: -4,
223
- right: -4,
224
+ end: -4,
225
child: GestureDetector(
226
onTap: () => setState(
227
() => _attachedFiles.removeAt(index),
@@ -246,10 +247,8 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
247
),
248
),
249
),
249
- // Input Main Bar
250
Row(
251
children: [
252
- // Add Attachment Button
252
IconButton(
253
icon: HugeIcon(
254
icon: HugeIcons.strokeRoundedPlusSign,
@@ -257,10 +256,9 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
256
size: 22,
257
),
258
onPressed: isConnected ? _handleAttach : null,
260
- tooltip: 'Attach images',
259
+ tooltip: l10n.attach_images_tooltip,
260
),
261
const SizedBox(width: 4),
263
- // Text Field
262
Expanded(
263
child: TextField(
264
controller: _controller,
@@ -278,7 +276,7 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
276
color: isDark ? Colors.white : Colors.black87,
277
),
278
decoration: InputDecoration(
281
- hintText: 'Ask anything',
279
+ hintText: l10n.chat_input_hint,
280
hintStyle: TextStyle(
281
color: isDark ? Colors.white38 : Colors.black38,
282
fontSize: 15,
@@ -291,14 +289,6 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
289
),
290
),
291
const SizedBox(width: 8),
294
- // Mic Icon (Visual only as per image)
295
- // HugeIcon(
296
- // icon: HugeIcons.strokeRoundedMic01,
297
- // color: isDark ? Colors.white38 : Colors.black38,
298
- // size: 22,
299
- // ),
300
- // const SizedBox(width: 8),
301
- // Action Button (Send/Stop)
292
_buildActionButton(canSend, isDark),
293
],
294
),
lib/features/chat/views/components/chat_settings_sheet.dart
+33
-28
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:shadcn_ui/shadcn_ui.dart';
4
import 'package:hugeicons/hugeicons.dart';
5
+import 'package:localmind/l10n/app_localizations.dart';
6
import '../../../../core/providers/app_providers.dart';
7
import '../../../conversations/providers/conversation_providers.dart' as conv;
8
import '../../providers/chat_mcp_providers.dart';
@@ -28,10 +29,10 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
29
30
@override
31
Widget build(BuildContext context) {
32
+ final l10n = AppLocalizations.of(context)!;
33
final settings = ref.watch(settingsProvider);
34
final activeConv = ref.watch(conv.activeConversationProvider);
35
final mcpConfig = ref.watch(chatMcpConfigProvider);
34
- // final theme = ShadTheme.of(context);
36
final isDark = Theme.of(context).brightness == Brightness.dark;
37
38
// Parameters
@@ -81,7 +82,7 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
82
Row(
83
children: [
84
Text(
84
- 'Chat Settings',
85
+ l10n.chat_settings_title,
86
style: TextStyle(
87
fontSize: 20,
88
fontWeight: FontWeight.bold,
@@ -93,7 +94,7 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
94
ShadButton.ghost(
95
onPressed: () => _resetToDefaults(ref, activeConv?.id),
96
leading: const Icon(Icons.restore, size: 16),
96
- child: const Text('Reset Defaults'),
97
+ child: Text(l10n.reset_defaults),
98
),
99
],
100
),
@@ -105,6 +106,7 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
106
value: 'parameters',
107
content: _buildParametersTab(
108
context,
109
+ l10n,
110
temperature,
111
topP,
112
maxTokens,
@@ -112,11 +114,11 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
114
activeConv?.id,
115
isDark,
116
),
115
- child: const Row(
117
+ child: Row(
118
children: [
117
- HugeIcon(icon: HugeIcons.strokeRoundedSlidersHorizontal, size: 16),
118
- SizedBox(width: 8),
119
- Text('Parameters'),
119
+ const HugeIcon(icon: HugeIcons.strokeRoundedSlidersHorizontal, size: 16),
120
+ const SizedBox(width: 8),
121
+ Text(l10n.parameters_tab),
122
],
123
),
124
),
@@ -124,15 +126,16 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
126
value: 'mcp',
127
content: _buildMcpTab(
128
context,
129
+ l10n,
130
mcpConfig,
131
isGloballyEnabled,
132
isDark,
133
),
131
- child: const Row(
134
+ child: Row(
135
children: [
133
- HugeIcon(icon: HugeIcons.strokeRoundedPuzzle, size: 16),
134
- SizedBox(width: 8),
135
- Text('MCP'),
136
+ const HugeIcon(icon: HugeIcons.strokeRoundedPuzzle, size: 16),
137
+ const SizedBox(width: 8),
138
+ Text(l10n.mcp_tab),
139
],
140
),
141
),
@@ -146,6 +149,7 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
149
150
Widget _buildParametersTab(
151
BuildContext context,
152
+ AppLocalizations l10n,
153
double temperature,
154
double topP,
155
int maxTokens,
@@ -158,23 +162,23 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
162
child: Column(
163
children: [
164
_ParamSlider(
161
- label: 'Temperature',
165
+ label: l10n.temperature,
166
value: temperature,
167
min: 0,
168
max: 2,
169
divisions: 20,
166
- description: 'Controls randomness: Higher = Creative, Lower = Focused',
170
+ description: l10n.temperature_desc,
171
onChanged: (v) => _updateParam(ref, conversationId, temperature: v),
172
isDark: isDark,
173
),
174
const SizedBox(height: 24),
175
_ParamSlider(
172
- label: 'Top P',
176
+ label: l10n.top_p,
177
value: topP,
178
min: 0,
179
max: 1,
180
divisions: 10,
177
- description: 'Nucleus sampling threshold',
181
+ description: l10n.top_p_desc,
182
onChanged: (v) => _updateParam(ref, conversationId, topP: v),
183
isDark: isDark,
184
),
@@ -183,9 +187,9 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
187
children: [
188
Expanded(
189
child: _ParamInput(
186
- label: 'Max Tokens',
190
+ label: l10n.max_tokens,
191
value: maxTokens,
188
- description: 'Response limit',
192
+ description: l10n.max_tokens_desc,
193
onChanged: (v) => _updateParam(ref, conversationId, maxTokens: v),
194
isDark: isDark,
195
),
@@ -193,9 +197,9 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
197
const SizedBox(width: 16),
198
Expanded(
199
child: _ParamInput(
196
- label: 'Context Length',
200
+ label: l10n.context_length,
201
value: contextLength,
198
- description: 'History window',
202
+ description: l10n.context_length_desc,
203
onChanged: (v) => _updateParam(ref, conversationId, contextLength: v),
204
isDark: isDark,
205
),
@@ -209,6 +213,7 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
213
214
Widget _buildMcpTab(
215
BuildContext context,
216
+ AppLocalizations l10n,
217
ChatMcpConfig mcpConfig,
218
bool isGloballyEnabled,
219
bool isDark,
@@ -232,10 +237,10 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
237
children: [
238
const Icon(Icons.warning_amber_rounded, color: Colors.orange, size: 20),
239
const SizedBox(width: 12),
235
- const Expanded(
240
+ Expanded(
241
child: Text(
237
- 'MCP is disabled globally. Enable it in Settings to use these features.',
238
- style: TextStyle(fontSize: 13, color: Colors.orange),
242
+ l10n.mcp_disabled_warning,
243
+ style: const TextStyle(fontSize: 13, color: Colors.orange),
244
),
245
),
246
],
@@ -255,7 +260,7 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
260
}
261
}
262
: null,
258
- label: const Text('Enable MCP for this chat'),
263
+ label: Text(l10n.mcp_enable_chat),
264
),
265
const SizedBox(height: 16),
266
if (mcpConfig.enabled && isGloballyEnabled) ...[
@@ -263,11 +268,11 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
268
value: mcpConfig.autoExecuteTools,
269
onChanged: (v) =>
270
ref.read(chatMcpConfigProvider.notifier).toggleAutoExecute(),
266
- label: const Text('Auto-execute tools'),
271
+ label: Text(l10n.auto_execute_tools),
272
),
273
const SizedBox(height: 24),
274
Text(
270
- 'Add Ephemeral MCP Server',
275
+ l10n.add_ephemeral_mcp,
276
style: theme.textTheme.list,
277
),
278
const SizedBox(height: 12),
@@ -276,7 +281,7 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
281
Expanded(
282
child: ShadInput(
283
controller: _serverLabelController,
279
- placeholder: const Text('Label'),
284
+ placeholder: Text(l10n.mcp_label_placeholder),
285
),
286
),
287
const SizedBox(width: 8),
@@ -284,7 +289,7 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
289
flex: 2,
290
child: ShadInput(
291
controller: _serverUrlController,
287
- placeholder: const Text('URL (https://...)'),
292
+ placeholder: Text(l10n.mcp_url_placeholder),
293
keyboardType: TextInputType.url,
294
),
295
),
@@ -298,7 +303,7 @@ class _ChatSettingsSheetState extends ConsumerState<ChatSettingsSheet> {
303
const SizedBox(height: 24),
304
if (mcpConfig.integrations.isNotEmpty) ...[
305
Text(
301
- 'Active Integrations',
306
+ l10n.active_integrations,
307
style: theme.textTheme.list,
308
),
309
const SizedBox(height: 12),
lib/features/chat/views/components/code_block.dart
+3
-1
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
2
import 'package:flutter/services.dart';
3
import 'package:flutter_riverpod/flutter_riverpod.dart';
4
import 'package:syntax_highlight/syntax_highlight.dart';
5
+import 'package:localmind/l10n/app_localizations.dart';
6
import '../../../../core/providers/highlighter_provider.dart';
7
8
class CodeBlock extends ConsumerWidget {
@@ -207,6 +208,7 @@ class _CopyButtonState extends State<_CopyButton> {
208
209
@override
210
Widget build(BuildContext context) {
211
+ final l10n = AppLocalizations.of(context)!;
212
return GestureDetector(
213
onTap: _copyToClipboard,
214
child: Row(
@@ -223,7 +225,7 @@ class _CopyButtonState extends State<_CopyButton> {
225
),
226
const SizedBox(width: 4),
227
Text(
226
- _copied ? 'Copied!' : 'Copy',
228
+ _copied ? l10n.copied : l10n.copy,
229
style: TextStyle(
230
fontSize: 11,
231
color: _copied
lib/features/chat/views/components/edit_message_dialog.dart
+6
-4
@@ -1,5 +1,6 @@
1
import 'package:flutter/material.dart';
2
import 'package:shadcn_ui/shadcn_ui.dart';
3
+import 'package:localmind/l10n/app_localizations.dart';
4
5
class EditMessageDialog extends StatefulWidget {
6
const EditMessageDialog({super.key, required this.initialContent});
@@ -55,6 +56,7 @@ class _EditMessageDialogState extends State<EditMessageDialog> {
56
57
@override
58
Widget build(BuildContext context) {
59
+ final l10n = AppLocalizations.of(context)!;
60
final isDark = Theme.of(context).brightness == Brightness.dark;
61
final hintColor = isDark
62
? const Color(0xFF888888)
@@ -99,7 +101,7 @@ class _EditMessageDialogState extends State<EditMessageDialog> {
101
),
102
const SizedBox(height: 16),
103
Text(
102
- 'Edit message',
104
+ l10n.edit_message,
105
style: TextStyle(
106
fontSize: 20,
107
fontWeight: FontWeight.bold,
@@ -120,7 +122,7 @@ class _EditMessageDialogState extends State<EditMessageDialog> {
122
),
123
const SizedBox(height: 8),
124
Text(
123
- 'Saving will remove the assistant response below and regenerate.',
125
+ l10n.edit_message_desc,
126
style: TextStyle(fontSize: 12, color: hintColor),
127
),
128
const SizedBox(height: 16),
@@ -129,12 +131,12 @@ class _EditMessageDialogState extends State<EditMessageDialog> {
131
children: [
132
ShadButton.outline(
133
onPressed: () => Navigator.of(context).pop(),
132
- child: const Text('Cancel'),
134
+ child: Text(l10n.cancel),
135
),
136
const SizedBox(width: 8),
137
ShadButton(
138
onPressed: _save,
137
- child: const Text('Save & regenerate'),
139
+ child: Text(l10n.save_regenerate),
140
),
141
],
142
),
lib/features/chat/views/components/message_action_bar.dart
+25
-21
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
2
import 'package:flutter/services.dart';
3
import 'package:flutter_riverpod/flutter_riverpod.dart';
4
import 'package:shadcn_ui/shadcn_ui.dart';
5
+import 'package:localmind/l10n/app_localizations.dart';
6
import '../../../tts/providers/tts_providers.dart' as tts;
7
8
class MessageActionBar extends ConsumerStatefulWidget {
@@ -47,6 +48,7 @@ class _MessageActionBarState extends ConsumerState<MessageActionBar> {
48
49
@override
50
Widget build(BuildContext context) {
51
+ final l10n = AppLocalizations.of(context)!;
52
final ttsState = ref.watch(tts.ttsProvider);
53
final isThisPlaying = ttsState.playingContent == widget.content && ttsState.isSpeaking;
54
final isThisInitializing = ttsState.playingContent == widget.content && ttsState.isInitializing;
@@ -56,15 +58,15 @@ class _MessageActionBarState extends ConsumerState<MessageActionBar> {
58
children: [
59
_ActionButton(
60
icon: Icons.copy,
59
- label: 'Copy',
61
+ label: l10n.copy,
62
onTap: () async {
63
await Clipboard.setData(ClipboardData(text: widget.content));
64
widget.onCopy?.call();
65
if (context.mounted) {
66
ScaffoldMessenger.of(context).showSnackBar(
65
- const SnackBar(
66
- content: Text('Copied to clipboard'),
67
- duration: Duration(seconds: 2),
67
+ SnackBar(
68
+ content: Text(l10n.copied_to_clipboard),
69
+ duration: const Duration(seconds: 2),
70
),
71
);
72
}
@@ -74,19 +76,19 @@ class _MessageActionBarState extends ConsumerState<MessageActionBar> {
76
const SizedBox(width: 4),
77
_ActionButton(
78
icon: Icons.refresh,
77
- label: 'Retry',
79
+ label: l10n.retry,
80
onTap: widget.onRetry,
81
),
82
],
83
if (widget.onEdit != null) ...[
84
const SizedBox(width: 4),
83
- _ActionButton(icon: Icons.edit, label: 'Edit', onTap: widget.onEdit),
85
+ _ActionButton(icon: Icons.edit, label: l10n.edit, onTap: widget.onEdit),
86
],
87
if (widget.onDelete != null) ...[
88
const SizedBox(width: 4),
89
_ActionButton(
90
icon: Icons.delete_outline,
89
- label: 'Delete',
91
+ label: l10n.delete,
92
onTap: () => _showDeleteConfirmation(context),
93
isDestructive: true,
94
),
@@ -95,7 +97,7 @@ class _MessageActionBarState extends ConsumerState<MessageActionBar> {
97
const SizedBox(width: 4),
98
_ActionButton(
99
icon: Icons.ios_share,
98
- label: 'Share',
100
+ label: l10n.share,
101
onTap: widget.onShare,
102
),
103
],
@@ -105,14 +107,14 @@ class _MessageActionBarState extends ConsumerState<MessageActionBar> {
107
? Icons.stop_circle
108
: (isThisInitializing ? Icons.hourglass_top : Icons.volume_up),
109
label: isThisPlaying
108
- ? 'Stop'
109
- : (isThisInitializing ? 'Loading TTS...' : 'Read Aloud'),
110
+ ? l10n.stop
111
+ : (isThisInitializing ? l10n.initializing : l10n.read_aloud),
112
onTap: isThisInitializing ? null : _toggleTts,
113
),
114
const SizedBox(width: 4),
115
_ActionButton(
116
icon: Icons.more_horiz,
115
- label: 'More',
117
+ label: l10n.more,
118
onTap: () => _showMoreOptions(context),
119
),
120
],
@@ -120,18 +122,19 @@ class _MessageActionBarState extends ConsumerState<MessageActionBar> {
122
}
123
124
void _showDeleteConfirmation(BuildContext context) {
125
+ final dlgL10n = AppLocalizations.of(context)!;
126
showShadDialog(
127
context: context,
128
builder: (context) => ShadDialog(
126
- title: const Text('Delete message?'),
127
- description: const Text('This action cannot be undone.'),
129
+ title: Text(dlgL10n.delete_message_title),
130
+ description: Text(dlgL10n.cannot_undo),
131
actions: [
132
ShadButton(
130
- child: const Text('Cancel'),
133
+ child: Text(dlgL10n.cancel),
134
onPressed: () => Navigator.of(context).pop(),
135
),
136
ShadButton(
134
- child: const Text('Delete'),
137
+ child: Text(dlgL10n.delete),
138
onPressed: () {
139
Navigator.of(context).pop();
140
widget.onDelete?.call();
@@ -143,30 +146,31 @@ class _MessageActionBarState extends ConsumerState<MessageActionBar> {
146
}
147
148
void _showMoreOptions(BuildContext context) {
149
+ final sheetL10n = AppLocalizations.of(context)!;
150
final ttsState = ref.read(tts.ttsProvider);
151
final isThisPlaying = ttsState.playingContent == widget.content && ttsState.isSpeaking;
152
153
showShadSheet(
154
context: context,
155
builder: (context) => ShadSheet(
152
- title: const Text('Message options'),
156
+ title: Text(sheetL10n.message_options),
157
child: Column(
158
mainAxisSize: MainAxisSize.min,
159
children: [
160
ListTile(
161
leading: const Icon(Icons.code),
158
- title: const Text('Copy as Markdown'),
162
+ title: Text(sheetL10n.copy_markdown),
163
onTap: () {
164
Navigator.of(context).pop();
165
Clipboard.setData(ClipboardData(text: widget.content));
166
ScaffoldMessenger.of(context).showSnackBar(
163
- const SnackBar(content: Text('Copied as Markdown')),
167
+ SnackBar(content: Text(sheetL10n.copied_markdown)),
168
);
169
},
170
),
171
ListTile(
172
leading: Icon(isThisPlaying ? Icons.stop : Icons.volume_up),
169
- title: Text(isThisPlaying ? 'Stop Reading' : 'Read Aloud'),
173
+ title: Text(isThisPlaying ? sheetL10n.stop_reading : sheetL10n.read_aloud),
174
onTap: () {
175
Navigator.of(context).pop();
176
_toggleTts();
@@ -175,7 +179,7 @@ class _MessageActionBarState extends ConsumerState<MessageActionBar> {
179
if (widget.onShare != null)
180
ListTile(
181
leading: const Icon(Icons.ios_share),
178
- title: const Text('Share'),
182
+ title: Text(sheetL10n.share),
183
onTap: () {
184
Navigator.of(context).pop();
185
widget.onShare?.call();
@@ -183,7 +187,7 @@ class _MessageActionBarState extends ConsumerState<MessageActionBar> {
187
),
188
ListTile(
189
leading: const Icon(Icons.text_fields),
186
- title: Text('${widget.content.length} characters'),
190
+ title: Text(sheetL10n.character_count(widget.content.length)),
191
enabled: false,
192
),
193
],
lib/features/chat/views/components/notification_permission_banner.dart
+6
-5
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:hugeicons/hugeicons.dart';
4
import 'package:shadcn_ui/shadcn_ui.dart';
5
+import 'package:localmind/l10n/app_localizations.dart';
6
7
import '../../../../core/providers/app_providers.dart';
8
import '../../../on_device/providers/on_device_providers.dart';
@@ -34,7 +35,6 @@ class _NotificationPermissionBannerState
35
if (!granted) {
36
setState(() => _isVisible = true);
37
} else {
37
- // If already granted somehow, mark as asked
38
await ref
39
.read(settingsProvider.notifier)
40
.updateSettings(settings.copyWith(hasAskedForNotifications: true));
@@ -57,6 +57,7 @@ class _NotificationPermissionBannerState
57
58
@override
59
Widget build(BuildContext context) {
60
+ final l10n = AppLocalizations.of(context)!;
61
if (!_isVisible) return const SizedBox.shrink();
62
63
final theme = Theme.of(context);
@@ -92,13 +93,13 @@ class _NotificationPermissionBannerState
93
crossAxisAlignment: CrossAxisAlignment.start,
94
children: [
95
Text(
95
- 'Enable Notifications',
96
+ l10n.enable_notifications,
97
style: theme.textTheme.labelLarge?.copyWith(
98
fontWeight: FontWeight.bold,
99
),
100
),
101
Text(
101
- 'Get notified when models finish downloading.',
102
+ l10n.enable_notifications_desc,
103
style: theme.textTheme.bodySmall?.copyWith(
104
color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
105
),
@@ -110,13 +111,13 @@ class _NotificationPermissionBannerState
111
ShadButton.outline(
112
size: ShadButtonSize.sm,
113
onPressed: () => _dismiss(true),
113
- child: const Text('Not Now'),
114
+ child: Text(l10n.not_now),
115
),
116
const SizedBox(width: 8),
117
ShadButton(
118
size: ShadButtonSize.sm,
119
onPressed: _requestPermission,
119
- child: const Text('Enable'),
120
+ child: Text(l10n.enable),
121
),
122
],
123
),
lib/features/chat/views/components/processing_indicator.dart
+3
-1
@@ -1,10 +1,12 @@
1
import 'package:flutter/material.dart';
2
+import 'package:localmind/l10n/app_localizations.dart';
3
4
class ProcessingIndicator extends StatelessWidget {
5
const ProcessingIndicator({super.key});
6
7
@override
8
Widget build(BuildContext context) {
9
+ final l10n = AppLocalizations.of(context)!;
10
final isDark = Theme.of(context).brightness == Brightness.dark;
11
12
return Row(
@@ -22,7 +24,7 @@ class ProcessingIndicator extends StatelessWidget {
24
),
25
const SizedBox(width: 8),
26
Text(
25
- 'Processing...',
27
+ l10n.processing,
28
style: TextStyle(
29
color: isDark ? Colors.grey[400] : Colors.grey[600],
30
fontSize: 14,
lib/features/chat/views/components/reasoning_widget.dart
+3
-1
@@ -1,4 +1,5 @@
1
import 'package:flutter/material.dart';
2
+import 'package:localmind/l10n/app_localizations.dart';
3
4
class ReasoningWidget extends StatefulWidget {
5
final String? reasoningContent;
@@ -45,6 +46,7 @@ class _ReasoningWidgetState extends State<ReasoningWidget> {
46
47
@override
48
Widget build(BuildContext context) {
49
+ final l10n = AppLocalizations.of(context)!;
50
final theme = Theme.of(context);
51
final isDark = theme.brightness == Brightness.dark;
52
final hasContent = widget.reasoningContent?.isNotEmpty == true;
@@ -96,7 +98,7 @@ class _ReasoningWidgetState extends State<ReasoningWidget> {
98
),
99
const SizedBox(width: 8),
100
Text(
99
- 'Thinking',
101
+ l10n.thinking,
102
style: TextStyle(
103
fontSize: 12,
104
fontWeight: FontWeight.w500,
lib/features/chat/views/components/typing_indicator.dart
+1
-1
@@ -122,7 +122,7 @@ class _StreamingIndicatorState extends State<StreamingIndicator>
122
child: Container(
123
width: 2,
124
height: 18,
125
- margin: const EdgeInsets.only(left: 2),
125
+ margin: const EdgeInsetsDirectional.only(start: 2),
126
color: cursorColor,
127
),
128
);
lib/features/conversations/views/chat_history_screen.dart
+4
-6
@@ -1,6 +1,6 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
-
3
+import 'package:localmind/l10n/app_localizations.dart';
4
import '../providers/conversation_providers.dart';
5
import 'components/conversation_empty_state.dart';
6
import 'components/conversation_list.dart';
@@ -11,6 +11,7 @@ class ChatHistoryScreen extends ConsumerWidget {
11
12
@override
13
Widget build(BuildContext context, WidgetRef ref) {
14
+ final l10n = AppLocalizations.of(context)!;
15
final theme = Theme.of(context);
16
final isDark = theme.brightness == Brightness.dark;
17
@@ -21,7 +22,6 @@ class ChatHistoryScreen extends ConsumerWidget {
22
return SafeArea(
23
child: Column(
24
children: [
24
- // Header
25
Container(
26
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 12),
27
decoration: BoxDecoration(
@@ -44,7 +44,7 @@ class ChatHistoryScreen extends ConsumerWidget {
44
),
45
const SizedBox(width: 8),
46
Text(
47
- 'Chat History',
47
+ l10n.chat_history_title,
48
style: TextStyle(
49
fontSize: 20,
50
fontWeight: FontWeight.bold,
@@ -56,10 +56,8 @@ class ChatHistoryScreen extends ConsumerWidget {
56
),
57
),
58
59
- // Search Bar
59
const ConversationSearchBar(),
60
62
- // List
61
Expanded(
62
child: groupedConversations.when(
63
data: (grouped) => grouped.isEmpty
@@ -73,7 +71,7 @@ class ChatHistoryScreen extends ConsumerWidget {
71
),
72
error: (err, stack) => Center(
73
child: Text(
76
- 'Error: $err',
74
+ l10n.error_with_message(err.toString()),
75
style: TextStyle(color: theme.colorScheme.error),
76
),
77
),
lib/features/conversations/views/components/conversation_empty_state.dart
+5
-3
@@ -1,4 +1,5 @@
1
import 'package:flutter/material.dart';
2
+import 'package:localmind/l10n/app_localizations.dart';
3
4
class ConversationEmptyState extends StatelessWidget {
5
const ConversationEmptyState({
@@ -10,6 +11,7 @@ class ConversationEmptyState extends StatelessWidget {
11
12
@override
13
Widget build(BuildContext context) {
14
+ final l10n = AppLocalizations.of(context)!;
15
final theme = Theme.of(context);
16
final isDark = theme.brightness == Brightness.dark;
17
@@ -26,7 +28,7 @@ class ConversationEmptyState extends StatelessWidget {
28
),
29
const SizedBox(height: 16),
30
Text(
29
- isSearching ? 'No results found' : 'No conversations yet',
31
+ isSearching ? l10n.no_results_found : l10n.no_conversations_yet,
32
style: TextStyle(
33
fontSize: 16,
34
fontWeight: FontWeight.w500,
@@ -36,8 +38,8 @@ class ConversationEmptyState extends StatelessWidget {
38
const SizedBox(height: 8),
39
Text(
40
isSearching
39
- ? 'Try a different search term'
40
- : 'Start a new conversation',
41
+ ? l10n.try_different_search
42
+ : l10n.start_new_conversation,
43
style: TextStyle(
44
fontSize: 14,
45
color: isDark
lib/features/conversations/views/components/conversation_list.dart
+24
-22
@@ -1,6 +1,7 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
+import 'package:localmind/l10n/app_localizations.dart';
5
import '../../../../core/routes/app_routes.dart';
6
import '../../../chat/providers/chat_providers.dart';
7
import '../../data/models/conversation.dart';
@@ -20,13 +21,14 @@ class ConversationList extends ConsumerWidget {
21
22
@override
23
Widget build(BuildContext context, WidgetRef ref) {
24
+ final l10n = AppLocalizations.of(context)!;
25
final sectionOrder = [
24
- 'PINNED',
25
- 'TODAY',
26
- 'YESTERDAY',
27
- 'PREVIOUS 7 DAYS',
28
- 'PREVIOUS 30 DAYS',
29
- 'OLDER',
26
+ l10n.pinned_section,
27
+ l10n.today_section,
28
+ l10n.yesterday_section,
29
+ l10n.previous_7_days,
30
+ l10n.previous_30_days,
31
+ l10n.older_section,
32
];
33
final sortedSections = groupedConversations.keys.toList()
34
..sort((a, b) {
@@ -42,7 +44,7 @@ class ConversationList extends ConsumerWidget {
44
final section = sortedSections[sectionIndex];
45
final conversations = groupedConversations[section]!;
46
45
- if (section == 'PINNED' && conversations.isEmpty) {
47
+ if (section == l10n.pinned_section && conversations.isEmpty) {
48
return const SizedBox.shrink();
49
}
50
@@ -59,12 +61,12 @@ class ConversationList extends ConsumerWidget {
61
.read(chatProvider.notifier)
62
.loadConversation(conversation);
63
if (Scaffold.maybeOf(context)?.isDrawerOpen ?? false) {
62
- Navigator.pop(context); // Close drawer if it was a drawer
64
+ Navigator.pop(context);
65
}
66
context.go(AppRoutes.home);
67
},
68
onRename: () {
67
- _showRenameDialog(context, ref, conversation);
69
+ _showRenameDialog(context, ref, l10n, conversation);
70
},
71
onTogglePin: () {
72
ref
@@ -72,7 +74,7 @@ class ConversationList extends ConsumerWidget {
74
.togglePin(conversation.id);
75
},
76
onDelete: () {
75
- _showDeleteConfirmation(context, ref, conversation);
77
+ _showDeleteConfirmation(context, ref, l10n, conversation);
78
},
79
);
80
}),
@@ -85,6 +87,7 @@ class ConversationList extends ConsumerWidget {
87
void _showRenameDialog(
88
BuildContext context,
89
WidgetRef ref,
90
+ AppLocalizations l10n,
91
Conversation conversation,
92
) {
93
final controller = TextEditingController(text: conversation.title);
@@ -93,19 +96,19 @@ class ConversationList extends ConsumerWidget {
96
context: context,
97
builder: (context) {
98
return AlertDialog(
96
- title: const Text('Rename conversation'),
99
+ title: Text(l10n.rename_conversation),
100
content: TextField(
101
controller: controller,
102
autofocus: true,
100
- decoration: const InputDecoration(
101
- hintText: 'Enter new title',
102
- border: OutlineInputBorder(),
103
+ decoration: InputDecoration(
104
+ hintText: l10n.enter_new_title,
105
+ border: const OutlineInputBorder(),
106
),
107
),
108
actions: [
109
TextButton(
110
onPressed: () => Navigator.pop(context),
108
- child: const Text('Cancel'),
111
+ child: Text(l10n.cancel),
112
),
113
TextButton(
114
onPressed: () {
@@ -117,7 +120,7 @@ class ConversationList extends ConsumerWidget {
120
}
121
Navigator.pop(context);
122
},
120
- child: const Text('Rename'),
123
+ child: Text(l10n.rename),
124
),
125
],
126
);
@@ -128,20 +131,19 @@ class ConversationList extends ConsumerWidget {
131
void _showDeleteConfirmation(
132
BuildContext context,
133
WidgetRef ref,
134
+ AppLocalizations l10n,
135
Conversation conversation,
136
) {
137
showDialog(
138
context: context,
139
builder: (context) {
140
return AlertDialog(
137
- title: const Text('Delete conversation?'),
138
- content: Text(
139
- 'Are you sure you want to delete "${conversation.title}"? This cannot be undone.',
140
- ),
141
+ title: Text(l10n.delete_conversation_title),
142
+ content: Text(l10n.delete_conversation_body(conversation.title)),
143
actions: [
144
TextButton(
145
onPressed: () => Navigator.pop(context),
144
- child: const Text('Cancel'),
146
+ child: Text(l10n.cancel),
147
),
148
TextButton(
149
onPressed: () {
@@ -151,7 +153,7 @@ class ConversationList extends ConsumerWidget {
153
Navigator.pop(context);
154
},
155
style: TextButton.styleFrom(foregroundColor: Colors.red),
154
- child: const Text('Delete'),
156
+ child: Text(l10n.delete),
157
),
158
],
159
);
lib/features/conversations/views/components/conversation_search_bar.dart
+3
-1
@@ -1,5 +1,6 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
+import 'package:localmind/l10n/app_localizations.dart';
4
import '../../providers/conversation_providers.dart';
5
6
class ConversationSearchBar extends ConsumerStatefulWidget {
@@ -21,6 +22,7 @@ class _ConversationSearchBarState extends ConsumerState<ConversationSearchBar> {
22
23
@override
24
Widget build(BuildContext context) {
25
+ final l10n = AppLocalizations.of(context)!;
26
final theme = Theme.of(context);
27
final isDark = theme.brightness == Brightness.dark;
28
@@ -43,7 +45,7 @@ class _ConversationSearchBarState extends ConsumerState<ConversationSearchBar> {
45
color: isDark ? Colors.white : Colors.black,
46
),
47
decoration: InputDecoration(
46
- hintText: 'Search conversations...',
48
+ hintText: l10n.search_hint,
49
hintStyle: TextStyle(
50
fontSize: 14,
51
color: isDark ? const Color(0xFF666666) : const Color(0xFF999999),
lib/features/conversations/views/components/conversation_tile.dart
+21
-19
@@ -1,5 +1,6 @@
1
import 'package:flutter/material.dart';
2
import 'package:hugeicons/hugeicons.dart';
3
+import 'package:localmind/l10n/app_localizations.dart';
4
import '../../data/models/conversation.dart';
5
6
class ConversationTile extends StatelessWidget {
@@ -20,27 +21,28 @@ class ConversationTile extends StatelessWidget {
21
final VoidCallback onTogglePin;
22
final VoidCallback onDelete;
23
23
- String _formatTimestamp(DateTime dateTime) {
24
+ String _formatTimestamp(AppLocalizations l10n, DateTime dateTime) {
25
final now = DateTime.now();
26
final diff = now.difference(dateTime);
27
28
if (diff.inMinutes < 1) {
28
- return 'Just now';
29
+ return l10n.conversation_just_now;
30
} else if (diff.inHours < 1) {
30
- return '${diff.inMinutes}m ago';
31
+ return l10n.conversation_minutes_ago(diff.inMinutes);
32
} else if (diff.inHours < 24) {
32
- return '${diff.inHours}h ago';
33
+ return l10n.conversation_hours_ago(diff.inHours);
34
} else if (diff.inDays == 1) {
34
- return 'Yesterday';
35
+ return l10n.conversation_yesterday;
36
} else if (diff.inDays < 7) {
36
- return '${diff.inDays}d ago';
37
+ return l10n.conversation_days_ago(diff.inDays);
38
} else {
38
- return '${dateTime.month}/${dateTime.day}/${dateTime.year}';
39
+ return l10n.conversation_date(dateTime.month, dateTime.day, dateTime.year);
40
}
41
}
42
43
@override
44
Widget build(BuildContext context) {
45
+ final l10n = AppLocalizations.of(context)!;
46
final theme = Theme.of(context);
47
final isDark = theme.brightness == Brightness.dark;
48
@@ -48,8 +50,8 @@ class ConversationTile extends StatelessWidget {
50
key: Key(conversation.id),
51
direction: DismissDirection.endToStart,
52
background: Container(
51
- alignment: Alignment.centerRight,
52
- padding: const EdgeInsets.only(right: 16),
53
+ alignment: AlignmentDirectional.centerEnd,
54
+ padding: const EdgeInsetsDirectional.only(end: 16),
55
color: Colors.red,
56
child: const Icon(Icons.delete, color: Colors.white),
57
),
@@ -65,7 +67,7 @@ class ConversationTile extends StatelessWidget {
67
: Colors.transparent,
68
child: InkWell(
69
onTap: onTap,
68
- onLongPress: () => _showContextMenu(context, isDark),
70
+ onLongPress: () => _showContextMenu(context, l10n, isDark),
71
child: Container(
72
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
73
child: Row(
@@ -119,7 +121,7 @@ class ConversationTile extends StatelessWidget {
121
),
122
const SizedBox(width: 8),
123
Text(
122
- _formatTimestamp(conversation.updatedAt),
124
+ _formatTimestamp(l10n, conversation.updatedAt),
125
style: TextStyle(
126
fontSize: 12,
127
color: isDark
@@ -129,7 +131,7 @@ class ConversationTile extends StatelessWidget {
131
),
132
const SizedBox(width: 8),
133
IconButton(
132
- onPressed: () => _showContextMenu(context, isDark),
134
+ onPressed: () => _showContextMenu(context, l10n, isDark),
135
icon: HugeIcon(
136
icon: HugeIcons.strokeRoundedMoreVertical,
137
size: 18,
@@ -140,7 +142,7 @@ class ConversationTile extends StatelessWidget {
142
padding: EdgeInsets.zero,
143
constraints: const BoxConstraints(),
144
visualDensity: VisualDensity.compact,
143
- tooltip: 'Options',
145
+ tooltip: l10n.options_tooltip,
146
),
147
],
148
),
@@ -150,7 +152,7 @@ class ConversationTile extends StatelessWidget {
152
);
153
}
154
153
- void _showContextMenu(BuildContext context, bool isDark) {
155
+ void _showContextMenu(BuildContext context, AppLocalizations l10n, bool isDark) {
156
showModalBottomSheet(
157
context: context,
158
builder: (ctx) {
@@ -164,7 +166,7 @@ class ConversationTile extends StatelessWidget {
166
? Icons.push_pin_outlined
167
: Icons.push_pin,
168
),
167
- title: Text(conversation.isPinned ? 'Unpin' : 'Pin'),
169
+ title: Text(conversation.isPinned ? l10n.unpin : l10n.pin),
170
onTap: () {
171
Navigator.pop(ctx);
172
onTogglePin();
@@ -172,7 +174,7 @@ class ConversationTile extends StatelessWidget {
174
),
175
ListTile(
176
leading: const Icon(Icons.edit_outlined),
175
- title: const Text('Rename'),
177
+ title: Text(l10n.rename),
178
onTap: () {
179
Navigator.pop(ctx);
180
onRename();
@@ -180,9 +182,9 @@ class ConversationTile extends StatelessWidget {
182
),
183
ListTile(
184
leading: const Icon(Icons.delete_outline, color: Colors.red),
183
- title: const Text(
184
- 'Delete',
185
- style: TextStyle(color: Colors.red),
185
+ title: Text(
186
+ l10n.delete,
187
+ style: const TextStyle(color: Colors.red),
188
),
189
onTap: () {
190
Navigator.pop(ctx);
lib/features/conversations/views/components/new_chat_button.dart
+3
-1
@@ -1,6 +1,7 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
+import 'package:localmind/l10n/app_localizations.dart';
5
6
import '../../../../core/routes/app_routes.dart';
7
import '../../../chat/providers/chat_providers.dart';
@@ -10,6 +11,7 @@ class NewChatButton extends ConsumerWidget {
11
12
@override
13
Widget build(BuildContext context, WidgetRef ref) {
14
+ final l10n = AppLocalizations.of(context)!;
15
return Container(
16
padding: const EdgeInsets.symmetric(horizontal: 16),
17
child: SizedBox(
@@ -23,7 +25,7 @@ class NewChatButton extends ConsumerWidget {
25
}
26
},
27
icon: const Icon(Icons.add, size: 18),
26
- label: const Text('New Chat'),
28
+ label: Text(l10n.nav_new_chat),
29
),
30
),
31
);
lib/features/models/screens/model_picker_sheet.dart
+55
-42
@@ -1,5 +1,6 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
+import 'package:localmind/l10n/app_localizations.dart';
4
import '../../chat/providers/chat_providers.dart';
5
import '../data/models/model_info.dart';
6
import '../../servers/providers/server_providers.dart';
@@ -30,6 +31,7 @@ class ModelPickerSheet extends ConsumerWidget {
31
32
@override
33
Widget build(BuildContext context, WidgetRef ref) {
34
+ final l10n = AppLocalizations.of(context)!;
35
final theme = Theme.of(context);
36
final isDark = theme.brightness == Brightness.dark;
37
final activeServer = ref.watch(activeServerProvider);
@@ -68,7 +70,7 @@ class ModelPickerSheet extends ConsumerWidget {
70
Row(
71
children: [
72
Text(
71
- 'Select Model',
73
+ l10n.select_model_title,
74
style: TextStyle(
75
fontSize: 20,
76
fontWeight: FontWeight.bold,
@@ -88,7 +90,7 @@ class ModelPickerSheet extends ConsumerWidget {
90
crossAxisAlignment: CrossAxisAlignment.start,
91
children: [
92
Text(
91
- 'Loading ${modelLoading.modelId ?? "model"}...',
93
+ l10n.loading_model(modelLoading.modelId ?? 'model'),
94
style: TextStyle(
95
fontSize: 12,
96
color: isDark
@@ -116,7 +118,7 @@ class ModelPickerSheet extends ConsumerWidget {
118
ref.invalidate(availableModelsProvider(activeServer.id));
119
ref.invalidate(loadedModelsProvider(activeServer));
120
},
119
- tooltip: 'Refresh models',
121
+ tooltip: l10n.refresh_models,
122
),
123
],
124
),
@@ -136,7 +138,7 @@ class ModelPickerSheet extends ConsumerWidget {
138
const SizedBox(height: 12),
139
TextField(
140
decoration: InputDecoration(
139
- hintText: 'Search models...',
141
+ hintText: l10n.search_models_hint,
142
prefixIcon: const Icon(Icons.search, size: 20),
143
isDense: true,
144
contentPadding: const EdgeInsets.symmetric(
@@ -180,6 +182,7 @@ class _NoServerState extends StatelessWidget {
182
183
@override
184
Widget build(BuildContext context) {
185
+ final l10n = AppLocalizations.of(context)!;
186
return Center(
187
child: Column(
188
mainAxisAlignment: MainAxisAlignment.center,
@@ -191,7 +194,7 @@ class _NoServerState extends StatelessWidget {
194
),
195
const SizedBox(height: 16),
196
Text(
194
- 'No server connected',
197
+ l10n.no_server_connected,
198
style: TextStyle(
199
fontSize: 16,
200
fontWeight: FontWeight.w500,
@@ -200,7 +203,7 @@ class _NoServerState extends StatelessWidget {
203
),
204
const SizedBox(height: 4),
205
Text(
203
- 'Add a server first to see available models.',
206
+ l10n.add_server_first,
207
style: TextStyle(
208
fontSize: 13,
209
color: isDark ? const Color(0xFF888888) : const Color(0xFF999999),
@@ -227,6 +230,7 @@ class _ModelList extends ConsumerWidget {
230
231
@override
232
Widget build(BuildContext context, WidgetRef ref) {
233
+ final l10n = AppLocalizations.of(context)!;
234
final serversAsync = ref.watch(serversProvider);
235
final servers = serversAsync.value ?? [];
236
final currentServer = servers.where((s) => s.id == serverId).firstOrNull;
@@ -249,7 +253,7 @@ class _ModelList extends ConsumerWidget {
253
Icon(Icons.error_outline, size: 48, color: Colors.red[400]),
254
const SizedBox(height: 16),
255
Text(
252
- 'Failed to load models',
256
+ l10n.failed_load_models,
257
style: TextStyle(
258
fontSize: 16,
259
fontWeight: FontWeight.w500,
@@ -272,7 +276,7 @@ class _ModelList extends ConsumerWidget {
276
onPressed: () =>
277
ref.invalidate(availableModelsProvider(serverId)),
278
icon: const Icon(Icons.refresh),
275
- label: const Text('Retry'),
279
+ label: Text(l10n.retry),
280
),
281
],
282
),
@@ -307,8 +311,8 @@ class _ModelList extends ConsumerWidget {
311
return Center(
312
child: Text(
313
searchQuery.isEmpty
310
- ? 'No models available'
311
- : 'No models match "$searchQuery"',
314
+ ? l10n.no_models_available
315
+ : l10n.no_models_match(searchQuery),
316
style: TextStyle(
317
fontSize: 14,
318
color: isDark
@@ -352,7 +356,7 @@ class _ModelList extends ConsumerWidget {
356
if (context.mounted) {
357
ScaffoldMessenger.of(context).showSnackBar(
358
SnackBar(
355
- content: Text('Failed to load model: $e'),
359
+ content: Text(l10n.model_load_failed(e.toString())),
360
backgroundColor: Colors.red,
361
),
362
);
@@ -383,8 +387,8 @@ class _ModelList extends ConsumerWidget {
387
388
if (context.mounted) {
389
final message = activeServer.type == ServerType.ollama
386
- ? '${model.name} will be unloaded once the keep-alive time passes'
387
- : '${model.name} unloaded successfully';
390
+ ? l10n.model_unloaded_ollama(model.name)
391
+ : l10n.model_unloaded_success(model.name);
392
ScaffoldMessenger.of(
393
context,
394
).showSnackBar(SnackBar(content: Text(message)));
@@ -393,7 +397,7 @@ class _ModelList extends ConsumerWidget {
397
if (context.mounted) {
398
ScaffoldMessenger.of(context).showSnackBar(
399
SnackBar(
396
- content: Text('Failed to unload model: $e'),
400
+ content: Text(l10n.model_unload_failed(e.toString())),
401
backgroundColor: Colors.red,
402
),
403
);
@@ -427,6 +431,7 @@ class _ModelTile extends StatelessWidget {
431
432
@override
433
Widget build(BuildContext context) {
434
+ final l10n = AppLocalizations.of(context)!;
435
final accent = isDark ? const Color(0xFF3B82F6) : const Color(0xFF2563EB);
436
437
return InkWell(
@@ -490,7 +495,7 @@ class _ModelTile extends StatelessWidget {
495
),
496
if (model.contextLength != null)
497
_MetadataChip(
493
- label: '${model.contextLength} ctx',
498
+ label: l10n.context_chip(model.contextLength.toString()),
499
isDark: isDark,
500
),
501
],
@@ -518,7 +523,7 @@ class _ModelTile extends StatelessWidget {
523
onPressed: onUnload,
524
padding: EdgeInsets.zero,
525
constraints: const BoxConstraints(),
521
- tooltip: 'Unload from server',
526
+ tooltip: l10n.unload_from_server,
527
),
528
const SizedBox(width: 8),
529
],
@@ -568,6 +573,7 @@ class _ThinkingIndicatorState extends State<_ThinkingIndicator>
573
574
@override
575
Widget build(BuildContext context) {
576
+ final l10n = AppLocalizations.of(context)!;
577
return Row(
578
mainAxisSize: MainAxisSize.min,
579
children: [
@@ -596,7 +602,7 @@ class _ThinkingIndicatorState extends State<_ThinkingIndicator>
602
),
603
const SizedBox(width: 4),
604
Text(
599
- 'Thinking',
605
+ l10n.thinking,
606
style: TextStyle(
607
fontSize: 12,
608
color: widget.isDark
@@ -647,6 +653,7 @@ class _OnDeviceModelList extends ConsumerWidget {
653
654
@override
655
Widget build(BuildContext context, WidgetRef ref) {
656
+ final l10n = AppLocalizations.of(context)!;
657
final curatedModels = ref.watch(onDeviceModelsProvider);
658
final downloadedAsync = ref.watch(downloadedModelsProvider);
659
final engineState = ref.watch(onDeviceEngineProvider);
@@ -662,7 +669,7 @@ class _OnDeviceModelList extends ConsumerWidget {
669
Icon(Icons.error_outline, size: 48, color: Colors.red[400]),
670
const SizedBox(height: 16),
671
Text(
665
- 'Error loading models',
672
+ l10n.failed_load_models,
673
style: TextStyle(
674
fontSize: 16,
675
fontWeight: FontWeight.w500,
@@ -673,7 +680,7 @@ class _OnDeviceModelList extends ConsumerWidget {
680
OutlinedButton.icon(
681
onPressed: () => ref.invalidate(downloadedModelsProvider),
682
icon: const Icon(Icons.refresh),
676
- label: const Text('Retry'),
683
+ label: Text(l10n.retry),
684
),
685
],
686
),
@@ -693,8 +700,8 @@ class _OnDeviceModelList extends ConsumerWidget {
700
return Center(
701
child: Text(
702
searchQuery.isEmpty
696
- ? 'No models available'
697
- : 'No models match "$searchQuery"',
703
+ ? l10n.no_models_available
704
+ : l10n.no_models_match(searchQuery),
705
style: TextStyle(
706
fontSize: 14,
707
color: isDark
@@ -753,6 +760,7 @@ class _OnDeviceModelTile extends ConsumerWidget {
760
761
@override
762
Widget build(BuildContext context, WidgetRef ref) {
763
+ final l10n = AppLocalizations.of(context)!;
764
final accent = isDark ? const Color(0xFF3B82F6) : const Color(0xFF2563EB);
765
final downloadProgress = ref.watch(
766
foregroundDownloadNotifierProvider,
@@ -813,7 +821,7 @@ class _OnDeviceModelTile extends ConsumerWidget {
821
borderRadius: BorderRadius.circular(4),
822
),
823
child: Text(
816
- 'REC',
824
+ l10n.recommended,
825
style: TextStyle(
826
fontSize: 9,
827
fontWeight: FontWeight.bold,
@@ -831,7 +839,7 @@ class _OnDeviceModelTile extends ConsumerWidget {
839
const Icon(Icons.warning_amber_rounded, color: Colors.orange, size: 12),
840
const SizedBox(width: 4),
841
Text(
834
- 'May be too large',
842
+ l10n.may_be_large,
843
style: TextStyle(
844
fontSize: 10,
845
color: Colors.orange,
@@ -854,8 +862,8 @@ class _OnDeviceModelTile extends ConsumerWidget {
862
const SizedBox(height: 2),
863
Text(
864
isPaused
857
- ? 'Paused - ${((downloadProgress?.progress ?? 0) * 100).toStringAsFixed(0)}%'
858
- : 'Downloading - ${((downloadProgress?.progress ?? 0) * 100).toStringAsFixed(0)}%',
865
+ ? l10n.paused_progress(((downloadProgress?.progress ?? 0) * 100).toStringAsFixed(0))
866
+ : '${l10n.downloading_status} ${((downloadProgress?.progress ?? 0) * 100).toStringAsFixed(0)}%',
867
style: TextStyle(
868
fontSize: 10,
869
color: isDark ? const Color(0xFF888888) : const Color(0xFF999999),
@@ -903,13 +911,13 @@ class _OnDeviceModelTile extends ConsumerWidget {
911
size: 18,
912
color: Colors.red[400],
913
),
906
- tooltip: 'Unload model',
914
+ tooltip: l10n.unload,
915
onPressed: () => _unloadModel(context, ref),
916
),
917
] else if (isDownloaded) ...[
918
_IconButton(
919
icon: Icon(Icons.play_arrow, size: 20, color: accent),
912
- tooltip: 'Load model',
920
+ tooltip: l10n.load,
921
onPressed: () => _loadModel(context, ref),
922
),
923
const SizedBox(width: 4),
@@ -919,7 +927,7 @@ class _OnDeviceModelTile extends ConsumerWidget {
927
size: 18,
928
color: isDark ? const Color(0xFF666666) : const Color(0xFF999999),
929
),
922
- tooltip: 'Delete model',
930
+ tooltip: l10n.delete,
931
onPressed: () => _deleteModel(context, ref),
932
),
933
] else if (isDownloading) ...[
@@ -929,7 +937,7 @@ class _OnDeviceModelTile extends ConsumerWidget {
937
size: 16,
938
color: isDark ? const Color(0xFF666666) : const Color(0xFF999999),
939
),
932
- tooltip: 'Cancel download',
940
+ tooltip: l10n.cancel,
941
onPressed: () => ref.read(foregroundDownloadNotifierProvider.notifier).cancelDownload(model.id),
942
),
943
] else ...[
@@ -939,7 +947,7 @@ class _OnDeviceModelTile extends ConsumerWidget {
947
size: 18,
948
color: isDark ? const Color(0xFF555555) : const Color(0xFF999999),
949
),
942
- tooltip: 'Download model',
950
+ tooltip: l10n.download,
951
onPressed: () {
952
ref
953
.read(foregroundDownloadNotifierProvider.notifier)
@@ -948,7 +956,7 @@ class _OnDeviceModelTile extends ConsumerWidget {
956
),
957
const SizedBox(width: 4),
958
Text(
951
- 'Not downloaded',
959
+ l10n.not_downloaded,
960
style: TextStyle(
961
fontSize: 11,
962
color: isDark ? const Color(0xFF666666) : const Color(0xFF999999),
@@ -999,28 +1007,32 @@ class _OnDeviceModelTile extends ConsumerWidget {
1007
}
1008
1009
Future<bool> _showRamWarning(BuildContext context) async {
1010
+ final l10n = AppLocalizations.of(context)!;
1011
final result = await showDialog<bool>(
1012
context: context,
1013
builder: (ctx) => AlertDialog(
1005
- title: const Row(
1014
+ title: Row(
1015
children: [
1007
- Icon(Icons.warning_amber_rounded, color: Colors.orange),
1008
- SizedBox(width: 8),
1009
- Text('RAM Warning'),
1016
+ const Icon(Icons.warning_amber_rounded, color: Colors.orange),
1017
+ const SizedBox(width: 8),
1018
+ Text(l10n.ram_warning),
1019
],
1020
),
1021
content: Text(
1013
- 'Your device has ${deviceMemory!.availableMemoryFormatted} available RAM, but this model recommends at least ${model.minRamMb ~/ 1024 + 1} GB. Loading it might fail or cause instability.',
1022
+ l10n.ram_warning_body_load(
1023
+ deviceMemory!.availableMemoryFormatted,
1024
+ '${model.minRamMb ~/ 1024 + 1}',
1025
+ ),
1026
),
1027
actions: [
1028
TextButton(
1029
onPressed: () => Navigator.pop(ctx, false),
1018
- child: const Text('Cancel'),
1030
+ child: Text(l10n.cancel),
1031
),
1032
TextButton(
1033
onPressed: () => Navigator.pop(ctx, true),
1034
style: TextButton.styleFrom(foregroundColor: Colors.orange),
1023
- child: const Text('Proceed Anyway'),
1035
+ child: Text(l10n.proceed_anyway),
1036
),
1037
],
1038
),
@@ -1055,19 +1067,20 @@ class _OnDeviceModelTile extends ConsumerWidget {
1067
}
1068
1069
void _deleteModel(BuildContext context, WidgetRef ref) async {
1070
+ final l10n = AppLocalizations.of(context)!;
1071
final confirm = await showDialog<bool>(
1072
context: context,
1073
builder: (ctx) => AlertDialog(
1061
- title: const Text('Delete Model'),
1062
- content: Text('Are you sure you want to delete ${model.name}?'),
1074
+ title: Text(l10n.delete_model_title),
1075
+ content: Text(l10n.delete_model_body(model.name)),
1076
actions: [
1077
TextButton(
1078
onPressed: () => Navigator.pop(ctx, false),
1066
- child: const Text('Cancel'),
1079
+ child: Text(l10n.cancel),
1080
),
1081
TextButton(
1082
onPressed: () => Navigator.pop(ctx, true),
1070
- child: const Text('Delete'),
1083
+ child: Text(l10n.delete),
1084
),
1085
],
1086
),
lib/features/on_device/views/backend_selector.dart
+14
-11
@@ -2,6 +2,7 @@ import 'dart:io';
2
3
import 'package:flutter/material.dart';
4
import 'package:flutter_riverpod/flutter_riverpod.dart';
5
+import 'package:localmind/l10n/app_localizations.dart';
6
import 'package:shadcn_ui/shadcn_ui.dart';
7
8
import '../../../core/models/enums.dart';
@@ -12,6 +13,7 @@ class BackendSelector extends ConsumerWidget {
13
14
@override
15
Widget build(BuildContext context, WidgetRef ref) {
16
+ final l10n = AppLocalizations.of(context)!;
17
final settings = ref.watch(settingsProvider);
18
final currentBackend = settings.preferredBackend;
19
final isAndroid = Platform.isAndroid;
@@ -20,7 +22,7 @@ class BackendSelector extends ConsumerWidget {
22
crossAxisAlignment: CrossAxisAlignment.start,
23
children: [
24
Text(
23
- 'Inference Backend',
25
+ l10n.inference_backend,
26
style: Theme.of(
27
context,
28
).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.bold),
@@ -35,14 +37,14 @@ class BackendSelector extends ConsumerWidget {
37
borderRadius: BorderRadius.circular(8),
38
border: Border.all(color: Colors.orange),
39
),
38
- child: const Row(
40
+ child: Row(
41
children: [
40
- Icon(Icons.info_outline, color: Colors.orange, size: 20),
41
- SizedBox(width: 8),
42
+ const Icon(Icons.info_outline, color: Colors.orange, size: 20),
43
+ const SizedBox(width: 8),
44
Expanded(
45
child: Text(
44
- 'Only CPU backend is available on iOS.',
45
- style: TextStyle(color: Colors.orange),
46
+ l10n.backend_ios_notice,
47
+ style: const TextStyle(color: Colors.orange),
48
),
49
),
50
],
@@ -85,7 +87,7 @@ class BackendSelector extends ConsumerWidget {
87
style: const TextStyle(fontWeight: FontWeight.w600),
88
),
89
Text(
88
- _backendDescription(backend),
90
+ _backendDescription(backend, context),
91
style: Theme.of(context).textTheme.bodySmall
92
?.copyWith(
93
color: Theme.of(context).colorScheme.onSurface
@@ -105,14 +107,15 @@ class BackendSelector extends ConsumerWidget {
107
);
108
}
109
108
- String _backendDescription(LiteLmBackendType backend) {
110
+ String _backendDescription(LiteLmBackendType backend, BuildContext context) {
111
+ final l10n = AppLocalizations.of(context)!;
112
switch (backend) {
113
case LiteLmBackendType.cpu:
111
- return 'Works on all devices. Most compatible.';
114
+ return l10n.backend_cpu_desc;
115
case LiteLmBackendType.gpu:
113
- return 'OpenCL acceleration. Faster on supported devices.';
116
+ return l10n.backend_gpu_desc;
117
case LiteLmBackendType.npu:
115
- return 'Vendor NPU (Qualcomm/MediaTek). Fastest inference.';
118
+ return l10n.backend_npu_desc;
119
}
120
}
121
}
lib/features/on_device/views/model_manager_screen.dart
+63
-45
@@ -2,6 +2,7 @@ import 'dart:io';
2
3
import 'package:flutter/material.dart';
4
import 'package:flutter_riverpod/flutter_riverpod.dart';
5
+import 'package:localmind/l10n/app_localizations.dart';
6
import '../../sidebar/sidebar_widget.dart';
7
import 'package:shadcn_ui/shadcn_ui.dart';
8
@@ -20,6 +21,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
21
22
@override
23
Widget build(BuildContext context, WidgetRef ref) {
24
+ final l10n = AppLocalizations.of(context)!;
25
final theme = Theme.of(context);
26
final models = ref.watch(onDeviceModelsProvider);
27
final downloadedAsync = ref.watch(downloadedModelsProvider);
@@ -30,7 +32,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
32
33
return Scaffold(
34
drawer: const SidebarWidget(),
33
- appBar: AppBar(title: const Text('On-Device Models')),
35
+ appBar: AppBar(title: Text(l10n.on_device_models_title)),
36
body: ListView(
37
padding: const EdgeInsets.all(16),
38
children: [
@@ -43,14 +45,14 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
45
borderRadius: BorderRadius.circular(8),
46
border: Border.all(color: Colors.orange),
47
),
46
- child: const Row(
48
+ child: Row(
49
children: [
48
- Icon(Icons.info_outline, color: Colors.orange, size: 20),
49
- SizedBox(width: 8),
50
+ const Icon(Icons.info_outline, color: Colors.orange, size: 20),
51
+ const SizedBox(width: 8),
52
Expanded(
53
child: Text(
52
- 'On-device inference is available on Android only.',
53
- style: TextStyle(color: Colors.orange),
54
+ l10n.on_device_android_only,
55
+ style: const TextStyle(color: Colors.orange),
56
),
57
),
58
],
@@ -72,7 +74,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
74
const SizedBox(width: 8),
75
Expanded(
76
child: Text(
75
- 'Model loaded: ${engineState.loadedModelId ?? "Unknown"} (${engineState.backend?.name ?? "CPU"})',
77
+ l10n.model_loaded(engineState.loadedModelId ?? 'Unknown', engineState.backend?.name ?? 'CPU'),
78
style: const TextStyle(color: Colors.green),
79
),
80
),
@@ -94,12 +96,12 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
96
border: Border.all(color: Colors.red),
97
),
98
child: Text(
97
- engineState.error ?? 'Engine error',
99
+ engineState.error ?? l10n.unknown_error,
100
style: const TextStyle(color: Colors.red),
101
),
102
),
103
Text(
102
- 'Available Models',
104
+ l10n.available_models,
105
style: theme.textTheme.titleMedium?.copyWith(
106
fontWeight: FontWeight.bold,
107
),
@@ -126,6 +128,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
128
WidgetRef ref,
129
AsyncValue<DeviceMemoryInfo> memoryAsync,
130
) {
131
+ final l10n = AppLocalizations.of(context)!;
132
return memoryAsync.when(
133
data: (info) {
134
if (info.totalMemoryMb == 0) return const SizedBox.shrink();
@@ -135,13 +138,13 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
138
139
// Memory health logic
140
Color statusColor = Colors.green;
138
- String statusText = 'Healthy';
141
+ String statusText = l10n.memory_healthy;
142
if (info.availableMemoryMb < 1024) {
143
statusColor = Colors.red;
141
- statusText = 'Critical';
144
+ statusText = l10n.memory_critical;
145
} else if (info.availableMemoryMb < 2048) {
146
statusColor = Colors.orange;
144
- statusText = 'Low';
147
+ statusText = l10n.memory_low;
148
}
149
150
return Container(
@@ -170,7 +173,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
173
child: Column(
174
children: [
175
Padding(
173
- padding: const EdgeInsets.fromLTRB(20, 16, 12, 8),
176
+ padding: const EdgeInsetsDirectional.only(start: 20, top: 16, end: 12, bottom: 8),
177
child: Row(
178
mainAxisAlignment: MainAxisAlignment.spaceBetween,
179
children: [
@@ -183,7 +186,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
186
),
187
const SizedBox(width: 8),
188
Text(
186
- 'Device Memory',
189
+ l10n.device_memory,
190
style: theme.textTheme.titleMedium?.copyWith(
191
fontWeight: FontWeight.bold,
192
letterSpacing: -0.5,
@@ -207,7 +210,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
210
mainAxisAlignment: MainAxisAlignment.spaceBetween,
211
children: [
212
Text(
210
- 'RAM Usage',
213
+ l10n.ram_usage,
214
style: theme.textTheme.labelMedium?.copyWith(
215
color: theme.colorScheme.onSurface.withValues(alpha: 0.6),
216
),
@@ -268,7 +271,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
271
mainAxisAlignment: MainAxisAlignment.spaceBetween,
272
children: [
273
Text(
271
- '${(usagePercent * 100).toStringAsFixed(0)}% used',
274
+ l10n.ram_used((usagePercent * 100).toStringAsFixed(0)),
275
style: theme.textTheme.labelSmall?.copyWith(
276
fontWeight: FontWeight.w600,
277
),
@@ -292,7 +295,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
295
children: [
296
Expanded(
297
child: _MemoryStat(
295
- label: 'Available RAM',
298
+ label: l10n.available_ram,
299
value: info.availableMemoryFormatted,
300
icon: Icons.speed_rounded,
301
color: statusColor,
@@ -306,7 +309,7 @@ class OnDeviceModelManagerScreen extends ConsumerWidget {
309
),
310
Expanded(
311
child: _MemoryStat(
309
- label: 'Total Capacity',
312
+ label: l10n.total_capacity,
313
value: info.totalMemoryFormatted,
314
icon: Icons.storage_rounded,
315
alignment: CrossAxisAlignment.end,
@@ -415,6 +418,7 @@ class _ModelCard extends ConsumerWidget {
418
419
@override
420
Widget build(BuildContext context, WidgetRef ref) {
421
+ final l10n = AppLocalizations.of(context)!;
422
final isDownloaded = downloadedAsync.when(
423
data: (set) => set.contains(model.id),
424
loading: () => false,
@@ -472,7 +476,7 @@ class _ModelCard extends ConsumerWidget {
476
borderRadius: BorderRadius.circular(4),
477
),
478
child: Text(
475
- 'RECOMMENDED',
479
+ l10n.recommended,
480
style: theme.textTheme.labelSmall?.copyWith(
481
color: theme.colorScheme.primary,
482
fontWeight: FontWeight.bold,
@@ -497,7 +501,7 @@ class _ModelCard extends ConsumerWidget {
501
const Icon(Icons.warning_amber_rounded, color: Colors.orange, size: 14),
502
const SizedBox(width: 6),
503
Text(
500
- 'May be too large for this device',
504
+ l10n.may_be_large,
505
style: theme.textTheme.labelSmall?.copyWith(
506
color: Colors.orange,
507
fontWeight: FontWeight.bold,
@@ -525,7 +529,7 @@ class _ModelCard extends ConsumerWidget {
529
Text(model.license, style: theme.textTheme.labelMedium),
530
const SizedBox(width: 12),
531
Text(
528
- '${model.minRamMb ~/ 1024 + 1} GB RAM min',
532
+ l10n.ram_min_required('${model.minRamMb ~/ 1024 + 1}'),
533
style: theme.textTheme.labelMedium?.copyWith(
534
color: theme.colorScheme.onSurface.withValues(alpha: 0.5),
535
),
@@ -555,6 +559,7 @@ class _ModelCard extends ConsumerWidget {
559
bool isDownloading,
560
bool isPaused,
561
) {
562
+ final l10n = AppLocalizations.of(context)!;
563
final isLoaded = engineState.loadedModelId == model.id;
564
565
if (isDownloaded) {
@@ -567,7 +572,7 @@ class _ModelCard extends ConsumerWidget {
572
),
573
const SizedBox(width: 4),
574
Text(
570
- isLoaded ? 'Loaded' : 'Downloaded',
575
+ isLoaded ? l10n.loaded_status : l10n.downloaded,
576
style: theme.textTheme.bodySmall?.copyWith(
577
color: isLoaded ? Colors.green : Colors.grey,
578
fontWeight: FontWeight.w600,
@@ -584,19 +589,19 @@ class _ModelCard extends ConsumerWidget {
589
ShadButton.outline(
590
size: ShadButtonSize.sm,
591
onPressed: () => _unloadModel(ref),
587
- child: const Text('Unload'),
592
+ child: Text(l10n.unload),
593
)
594
else
595
ShadButton.outline(
596
size: ShadButtonSize.sm,
597
onPressed: () => _loadModel(context, ref),
593
- child: const Text('Load'),
598
+ child: Text(l10n.load),
599
),
600
const SizedBox(width: 8),
601
ShadButton.outline(
602
size: ShadButtonSize.sm,
603
onPressed: () => _deleteModel(context, ref),
599
- child: const Text('Delete'),
604
+ child: Text(l10n.delete),
605
),
606
],
607
);
@@ -621,13 +626,16 @@ class _ModelCard extends ConsumerWidget {
626
mainAxisAlignment: MainAxisAlignment.spaceBetween,
627
children: [
628
Text(
624
- '${((downloadProgress?.progress ?? 0) * 100).toStringAsFixed(1)}% • ${downloadProgress?.speedFormatted ?? '0 B/s'}',
629
+ l10n.download_progress(
630
+ ((downloadProgress?.progress ?? 0) * 100).toStringAsFixed(1),
631
+ downloadProgress?.speedFormatted ?? '0 B/s',
632
+ ),
633
style: theme.textTheme.labelSmall?.copyWith(
634
fontWeight: FontWeight.w600,
635
),
636
),
637
Text(
630
- 'ETA: ${downloadProgress?.etaFormatted ?? 'Calculating...'}',
638
+ l10n.eta_label(downloadProgress?.etaFormatted ?? l10n.calculating),
639
style: theme.textTheme.labelSmall,
640
),
641
],
@@ -649,7 +657,7 @@ class _ModelCard extends ConsumerWidget {
657
onPressed: () => ref
658
.read(foregroundDownloadNotifierProvider.notifier)
659
.pauseDownload(model.id),
652
- child: const Text('Pause'),
660
+ child: Text(l10n.pause),
661
),
662
],
663
),
@@ -662,7 +670,7 @@ class _ModelCard extends ConsumerWidget {
670
children: [
671
Expanded(
672
child: Text(
665
- downloadProgress?.error ?? 'Download failed',
673
+ downloadProgress?.error ?? l10n.download_failed,
674
style: theme.textTheme.bodySmall?.copyWith(color: Colors.red),
675
),
676
),
@@ -672,7 +680,7 @@ class _ModelCard extends ConsumerWidget {
680
onPressed: () => ref
681
.read(foregroundDownloadNotifierProvider.notifier)
682
.retryDownload(model.id),
675
- child: const Text('Retry'),
683
+ child: Text(l10n.retry),
684
),
685
],
686
);
@@ -682,14 +690,14 @@ class _ModelCard extends ConsumerWidget {
690
return Row(
691
children: [
692
Text(
685
- 'Paused - ${((downloadProgress?.progress ?? 0) * 100).toStringAsFixed(0)}%',
693
+ l10n.paused_progress(((downloadProgress?.progress ?? 0) * 100).toStringAsFixed(0)),
694
style: theme.textTheme.bodySmall,
695
),
696
const Spacer(),
697
ShadButton.outline(
698
size: ShadButtonSize.sm,
699
onPressed: () => _startDownload(context, ref),
692
- child: const Text('Resume'),
700
+ child: Text(l10n.resume),
701
),
702
],
703
);
@@ -701,18 +709,22 @@ class _ModelCard extends ConsumerWidget {
709
ShadButton.outline(
710
size: ShadButtonSize.sm,
711
onPressed: () => _startDownload(context, ref),
704
- child: const Text('Download'),
712
+ child: Text(l10n.download),
713
),
714
],
715
);
716
}
717
718
Future<void> _startDownload(BuildContext context, WidgetRef ref) async {
719
+ final l10n = AppLocalizations.of(context)!;
720
if (deviceMemory != null) {
721
if (deviceMemory!.isOversized(model.minRamMb)) {
722
final proceed = await _showRamWarning(
723
context,
715
- 'This model requires at least ${model.minRamMb ~/ 1024 + 1} GB RAM, but your device has ${deviceMemory!.totalMemoryFormatted}. It may not run correctly or could cause the app to crash.',
724
+ l10n.ram_warning_body_download(
725
+ '${model.minRamMb ~/ 1024 + 1}',
726
+ deviceMemory!.totalMemoryFormatted,
727
+ ),
728
);
729
if (!proceed) return;
730
}
@@ -724,11 +736,15 @@ class _ModelCard extends ConsumerWidget {
736
}
737
738
Future<void> _loadModel(BuildContext context, WidgetRef ref) async {
739
+ final l10n = AppLocalizations.of(context)!;
740
if (deviceMemory != null) {
741
if (!deviceMemory!.hasEnoughRam(model.minRamMb)) {
742
final proceed = await _showRamWarning(
743
context,
731
- 'Your device has ${deviceMemory!.availableMemoryFormatted} available RAM, but this model recommends at least ${model.minRamMb ~/ 1024 + 1} GB. Loading it might fail or cause instability.',
744
+ l10n.ram_warning_body_load(
745
+ deviceMemory!.availableMemoryFormatted,
746
+ '${model.minRamMb ~/ 1024 + 1}',
747
+ ),
748
);
749
if (!proceed) return;
750
}
@@ -740,26 +756,27 @@ class _ModelCard extends ConsumerWidget {
756
}
757
758
Future<bool> _showRamWarning(BuildContext context, String message) async {
759
+ final l10n = AppLocalizations.of(context)!;
760
final result = await showDialog<bool>(
761
context: context,
762
builder: (ctx) => AlertDialog(
746
- title: const Row(
763
+ title: Row(
764
children: [
748
- Icon(Icons.warning_amber_rounded, color: Colors.orange),
749
- SizedBox(width: 8),
750
- Text('RAM Warning'),
765
+ const Icon(Icons.warning_amber_rounded, color: Colors.orange),
766
+ const SizedBox(width: 8),
767
+ Text(l10n.ram_warning),
768
],
769
),
770
content: Text(message),
771
actions: [
772
TextButton(
773
onPressed: () => Navigator.pop(ctx, false),
757
- child: const Text('Cancel'),
774
+ child: Text(l10n.cancel),
775
),
776
TextButton(
777
onPressed: () => Navigator.pop(ctx, true),
778
style: TextButton.styleFrom(foregroundColor: Colors.orange),
762
- child: const Text('Proceed Anyway'),
779
+ child: Text(l10n.proceed_anyway),
780
),
781
],
782
),
@@ -772,19 +789,20 @@ class _ModelCard extends ConsumerWidget {
789
}
790
791
Future<void> _deleteModel(BuildContext context, WidgetRef ref) async {
792
+ final l10n = AppLocalizations.of(context)!;
793
final confirm = await showDialog<bool>(
794
context: context,
795
builder: (ctx) => AlertDialog(
778
- title: const Text('Delete Model'),
779
- content: Text('Are you sure you want to delete ${model.name}?'),
796
+ title: Text(l10n.delete_model_title),
797
+ content: Text(l10n.delete_model_body(model.name)),
798
actions: [
799
TextButton(
800
onPressed: () => Navigator.pop(ctx, false),
783
- child: const Text('Cancel'),
801
+ child: Text(l10n.cancel),
802
),
803
TextButton(
804
onPressed: () => Navigator.pop(ctx, true),
787
- child: const Text('Delete'),
805
+ child: Text(l10n.delete),
806
),
807
],
808
),
lib/features/onboarding/screens/onboarding_model_download_screen.dart
+57
-44
@@ -8,6 +8,7 @@ import 'package:shadcn_ui/shadcn_ui.dart';
8
9
import '../../../core/models/enums.dart';
10
import '../../../core/routes/app_routes.dart';
11
+import '../../../l10n/app_localizations.dart';
12
import '../../on_device/data/models/on_device_model.dart';
13
import '../../on_device/data/models/download_status.dart';
14
import '../../on_device/data/models/download_progress_info.dart';
@@ -32,6 +33,7 @@ class _OnboardingModelDownloadScreenState
33
34
@override
35
Widget build(BuildContext context) {
36
+ final l10n = AppLocalizations.of(context)!;
37
final theme = Theme.of(context);
38
final models = ref.watch(onDeviceModelsProvider);
39
final downloadedModelsAsync = ref.watch(downloadedModelsProvider);
@@ -40,13 +42,13 @@ class _OnboardingModelDownloadScreenState
42
final deviceMemoryAsync = ref.watch(deviceMemoryProvider);
43
44
return Scaffold(
43
- appBar: AppBar(title: const Text('Download a Model')),
45
+ appBar: AppBar(title: Text(l10n.download_model_title)),
46
body: SafeArea(
47
child: ListView(
48
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
49
children: [
50
Text(
49
- 'Choose a model to download.\nIt will run locally on your device.',
51
+ l10n.download_model_desc,
52
style: theme.textTheme.bodyMedium?.copyWith(
53
color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
54
),
@@ -61,14 +63,14 @@ class _OnboardingModelDownloadScreenState
63
borderRadius: BorderRadius.circular(8),
64
border: Border.all(color: Colors.orange),
65
),
64
- child: const Row(
66
+ child: Row(
67
children: [
66
- Icon(Icons.info_outline, color: Colors.orange, size: 20),
67
- SizedBox(width: 8),
68
+ const Icon(Icons.info_outline, color: Colors.orange, size: 20),
69
+ const SizedBox(width: 8),
70
Expanded(
71
child: Text(
70
- 'On-device inference is currently available on Android only.',
71
- style: TextStyle(color: Colors.orange),
72
+ l10n.on_device_android_only,
73
+ style: const TextStyle(color: Colors.orange),
74
),
75
),
76
],
@@ -101,9 +103,9 @@ class _OnboardingModelDownloadScreenState
103
ShadButton(
104
width: double.infinity,
105
onPressed: _canContinue() ? _createOnDeviceServer : null,
104
- child: const Text(
105
- 'Continue',
106
- style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
106
+ child: Text(
107
+ l10n.continue_action,
108
+ style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
109
),
110
),
111
],
@@ -145,7 +147,7 @@ class _OnboardingModelDownloadScreenState
147
if (mounted) {
148
ScaffoldMessenger.of(
149
context,
148
- ).showSnackBar(SnackBar(content: Text('Error: ${e.toString()}')));
150
+ ).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.error_with_message(e.toString()))));
151
}
152
} finally {
153
if (mounted) {
@@ -159,6 +161,7 @@ class _OnboardingModelDownloadScreenState
161
WidgetRef ref,
162
AsyncValue<DeviceMemoryInfo> memoryAsync,
163
) {
164
+ final l10n = AppLocalizations.of(context)!;
165
return memoryAsync.when(
166
data: (info) {
167
if (info.totalMemoryMb == 0) return const SizedBox.shrink();
@@ -174,13 +177,13 @@ class _OnboardingModelDownloadScreenState
177
child: Row(
178
children: [
179
_MemoryStat(
177
- label: 'Total RAM',
180
+ label: l10n.total_ram,
181
value: info.totalMemoryFormatted,
182
icon: Icons.memory,
183
),
184
const SizedBox(width: 24),
185
_MemoryStat(
183
- label: 'Available',
186
+ label: l10n.available,
187
value: info.availableMemoryFormatted,
188
icon: Icons.event_available,
189
color: info.availableMemoryMb < 1024 ? Colors.orange : null,
@@ -255,6 +258,7 @@ class _ModelCard extends ConsumerWidget {
258
259
@override
260
Widget build(BuildContext context, WidgetRef ref) {
261
+ final l10n = AppLocalizations.of(context)!;
262
final isDownloading =
263
progressInfo?.status == DownloadStatus.running ||
264
progressInfo?.status == DownloadStatus.pending;
@@ -298,7 +302,7 @@ class _ModelCard extends ConsumerWidget {
302
borderRadius: BorderRadius.circular(4),
303
),
304
child: Text(
301
- 'RECOMMENDED',
305
+ l10n.recommended,
306
style: theme.textTheme.labelSmall?.copyWith(
307
color: theme.colorScheme.primary,
308
fontWeight: FontWeight.bold,
@@ -315,7 +319,7 @@ class _ModelCard extends ConsumerWidget {
319
const Icon(Icons.warning_amber_rounded, color: Colors.orange, size: 14),
320
const SizedBox(width: 4),
321
Text(
318
- 'May be too large for this device',
322
+ l10n.may_be_large,
323
style: theme.textTheme.labelSmall?.copyWith(
324
color: Colors.orange,
325
fontWeight: FontWeight.bold,
@@ -339,7 +343,7 @@ class _ModelCard extends ConsumerWidget {
343
Text(model.license, style: theme.textTheme.labelMedium),
344
const SizedBox(width: 12),
345
Text(
342
- '${model.minRamMb ~/ 1024 + 1} GB RAM min',
346
+ l10n.ram_min_required('${model.minRamMb ~/ 1024 + 1}'),
347
style: theme.textTheme.labelMedium?.copyWith(
348
color: theme.colorScheme.onSurface.withValues(alpha: 0.5),
349
),
@@ -353,7 +357,7 @@ class _ModelCard extends ConsumerWidget {
357
const Icon(Icons.check_circle, color: Colors.green, size: 18),
358
const SizedBox(width: 4),
359
Text(
356
- 'Downloaded',
360
+ l10n.downloaded,
361
style: theme.textTheme.bodySmall?.copyWith(
362
color: Colors.green,
363
fontWeight: FontWeight.w600,
@@ -374,11 +378,14 @@ class _ModelCard extends ConsumerWidget {
378
mainAxisAlignment: MainAxisAlignment.spaceBetween,
379
children: [
380
Text(
377
- '${((progressInfo?.progress ?? 0) * 100).toStringAsFixed(0)}% • ${progressInfo?.speedFormatted ?? "0 B/s"}',
381
+ l10n.download_progress(
382
+ '${((progressInfo?.progress ?? 0) * 100).toStringAsFixed(0)}%',
383
+ progressInfo?.speedFormatted ?? '0 B/s',
384
+ ),
385
style: theme.textTheme.labelSmall,
386
),
387
Text(
381
- 'ETA: ${progressInfo?.etaFormatted ?? "..."}',
388
+ l10n.eta_label(progressInfo?.etaFormatted ?? l10n.calculating),
389
style: theme.textTheme.labelSmall,
390
),
391
],
@@ -392,7 +399,7 @@ class _ModelCard extends ConsumerWidget {
399
onPressed: () => ref
400
.read(foregroundDownloadNotifierProvider.notifier)
401
.pauseDownload(model.id),
395
- child: const Text('Pause'),
402
+ child: Text(l10n.pause),
403
),
404
],
405
)
@@ -400,14 +407,14 @@ class _ModelCard extends ConsumerWidget {
407
Row(
408
children: [
409
Text(
403
- 'Paused - ${((progressInfo?.progress ?? 0) * 100).toStringAsFixed(0)}%',
410
+ l10n.paused_progress('${((progressInfo?.progress ?? 0) * 100).toStringAsFixed(0)}%'),
411
style: theme.textTheme.bodySmall,
412
),
413
const Spacer(),
414
ShadButton.outline(
415
size: ShadButtonSize.sm,
416
onPressed: () => _startDownload(context, ref),
410
- child: const Text('Resume'),
417
+ child: Text(l10n.resume),
418
),
419
],
420
)
@@ -415,7 +422,7 @@ class _ModelCard extends ConsumerWidget {
422
ShadButton.outline(
423
size: ShadButtonSize.sm,
424
onPressed: () => _startDownload(context, ref),
418
- child: const Text('Download'),
425
+ child: Text(l10n.download),
426
),
427
],
428
),
@@ -438,29 +445,35 @@ class _ModelCard extends ConsumerWidget {
445
Future<bool> _showRamWarning(BuildContext context) async {
446
final result = await showDialog<bool>(
447
context: context,
441
- builder: (ctx) => AlertDialog(
442
- title: const Row(
443
- children: [
444
- Icon(Icons.warning_amber_rounded, color: Colors.orange),
445
- SizedBox(width: 8),
446
- Text('RAM Warning'),
447
- ],
448
- ),
449
- content: Text(
450
- 'This model requires at least ${model.minRamMb ~/ 1024 + 1} GB RAM, but your device has ${deviceMemory!.totalMemoryFormatted}. It may not run correctly or could cause the app to crash.',
451
- ),
452
- actions: [
453
- TextButton(
454
- onPressed: () => Navigator.pop(ctx, false),
455
- child: const Text('Cancel'),
448
+ builder: (ctx) {
449
+ final dialogL10n = AppLocalizations.of(ctx)!;
450
+ return AlertDialog(
451
+ title: Row(
452
+ children: [
453
+ const Icon(Icons.warning_amber_rounded, color: Colors.orange),
454
+ const SizedBox(width: 8),
455
+ Text(dialogL10n.ram_warning),
456
+ ],
457
),
457
- TextButton(
458
- onPressed: () => Navigator.pop(ctx, true),
459
- style: TextButton.styleFrom(foregroundColor: Colors.orange),
460
- child: const Text('Proceed Anyway'),
458
+ content: Text(
459
+ dialogL10n.ram_warning_body_download(
460
+ '${model.minRamMb ~/ 1024 + 1}',
461
+ deviceMemory!.totalMemoryFormatted,
462
+ ),
463
),
462
- ],
463
- ),
464
+ actions: [
465
+ TextButton(
466
+ onPressed: () => Navigator.pop(ctx, false),
467
+ child: Text(dialogL10n.cancel),
468
+ ),
469
+ TextButton(
470
+ onPressed: () => Navigator.pop(ctx, true),
471
+ style: TextButton.styleFrom(foregroundColor: Colors.orange),
472
+ child: Text(dialogL10n.proceed_anyway),
473
+ ),
474
+ ],
475
+ );
476
+ },
477
);
478
return result ?? false;
479
}
lib/features/onboarding/screens/onboarding_notification_permission_screen.dart
+11
-9
@@ -6,6 +6,7 @@ import 'package:shadcn_ui/shadcn_ui.dart';
6
7
import '../../../core/providers/app_providers.dart';
8
import '../../../core/routes/app_routes.dart';
9
+import '../../../l10n/app_localizations.dart';
10
import '../../on_device/providers/on_device_providers.dart';
11
12
class OnboardingNotificationPermissionScreen extends ConsumerStatefulWidget {
@@ -51,6 +52,7 @@ class _OnboardingNotificationPermissionScreenState
52
53
@override
54
Widget build(BuildContext context) {
55
+ final l10n = AppLocalizations.of(context)!;
56
final theme = Theme.of(context);
57
58
return Scaffold(
@@ -78,7 +80,7 @@ class _OnboardingNotificationPermissionScreenState
80
),
81
const SizedBox(height: 48),
82
Text(
81
- 'Stay Updated',
83
+ l10n.stay_updated,
84
style: theme.textTheme.displaySmall?.copyWith(
85
fontWeight: FontWeight.bold,
86
),
@@ -86,7 +88,7 @@ class _OnboardingNotificationPermissionScreenState
88
),
89
const SizedBox(height: 16),
90
Text(
89
- 'Get notified when your AI models finish downloading or when long-running tasks complete.',
91
+ l10n.stay_updated_desc,
92
style: theme.textTheme.titleMedium?.copyWith(
93
color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
94
),
@@ -97,19 +99,19 @@ class _OnboardingNotificationPermissionScreenState
99
_buildBenefitRow(
100
context,
101
HugeIcons.strokeRoundedDownload01,
100
- 'Model download progress',
102
+ l10n.notification_benefit_downloads,
103
),
104
const SizedBox(height: 16),
105
_buildBenefitRow(
106
context,
107
HugeIcons.strokeRoundedAiChat01,
106
- 'Generation completions',
108
+ l10n.notification_benefit_completions,
109
),
110
const SizedBox(height: 16),
111
_buildBenefitRow(
112
context,
113
HugeIcons.strokeRoundedClock01,
112
- 'Background tasks status',
114
+ l10n.notification_benefit_background,
115
),
116
const Spacer(),
117
const SizedBox(height: 32),
@@ -127,9 +129,9 @@ class _OnboardingNotificationPermissionScreenState
129
valueColor: AlwaysStoppedAnimation(Colors.white),
130
),
131
)
130
- : const Text(
131
- 'Allow Notifications',
132
- style: TextStyle(
132
+ : Text(
133
+ l10n.allow_notifications,
134
+ style: const TextStyle(
135
fontSize: 16,
136
fontWeight: FontWeight.w600,
137
),
@@ -139,7 +141,7 @@ class _OnboardingNotificationPermissionScreenState
141
ShadButton.ghost(
142
onPressed: _isProcessing ? null : _completeOnboarding,
143
child: Text(
142
- 'Not Now',
144
+ l10n.not_now,
145
style: TextStyle(
146
color: theme.colorScheme.onSurface.withValues(
147
alpha: 0.6,
lib/features/onboarding/screens/onboarding_server_setup_screen.dart
+22
-19
@@ -9,6 +9,7 @@ import '../../../core/constants/app_constants.dart';
9
import '../../../core/models/enums.dart';
10
import '../../../core/providers/service_providers.dart';
11
import '../../../core/routes/app_routes.dart';
12
+import '../../../l10n/app_localizations.dart';
13
import '../../servers/data/models/server.dart';
14
import '../../servers/providers/server_providers.dart';
15
@@ -111,6 +112,7 @@ class _OnboardingServerSetupScreenState
112
_testSuccess = false;
113
});
114
115
+ final l10n = AppLocalizations.of(context)!;
116
final apiService = ref.read(serverApiServiceProvider);
117
final testServer = _buildServer();
118
@@ -119,13 +121,13 @@ class _OnboardingServerSetupScreenState
121
setState(() {
122
_testSuccess = isConnected;
123
_testResult = isConnected
122
- ? 'Connection successful!'
123
- : 'Connection failed. Check your settings.';
124
+ ? l10n.connection_successful
125
+ : l10n.connection_failed;
126
});
127
} catch (e) {
128
setState(() {
129
_testSuccess = false;
128
- _testResult = 'Error: ${e.toString()}';
130
+ _testResult = l10n.error_with_message(e.toString());
131
});
132
} finally {
133
setState(() {
@@ -154,7 +156,7 @@ class _OnboardingServerSetupScreenState
156
if (mounted) {
157
ScaffoldMessenger.of(context).showSnackBar(
158
SnackBar(
157
- content: Text('Error: ${e.toString()}'),
159
+ content: Text(AppLocalizations.of(context)!.error_with_message(e.toString())),
160
backgroundColor: Colors.red,
161
),
162
);
@@ -170,11 +172,12 @@ class _OnboardingServerSetupScreenState
172
173
@override
174
Widget build(BuildContext context) {
175
+ final l10n = AppLocalizations.of(context)!;
176
final theme = Theme.of(context);
177
final isCloud = widget.selectedType == ServerType.openRouter;
178
179
return Scaffold(
177
- appBar: AppBar(title: const Text('Setup Connection')),
180
+ appBar: AppBar(title: Text(l10n.setup_connection)),
181
body: SafeArea(
182
child: Form(
183
key: _formKey,
@@ -185,7 +188,7 @@ class _OnboardingServerSetupScreenState
188
),
189
children: [
190
Text(
188
- 'Configure your ${widget.selectedType.name} server to start chatting.',
191
+ l10n.setup_connection_desc(widget.selectedType.name),
192
style: theme.textTheme.titleMedium?.copyWith(
193
color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
194
),
@@ -195,12 +198,12 @@ class _OnboardingServerSetupScreenState
198
TextFormField(
199
controller: _nameController,
200
decoration: InputDecoration(
198
- labelText: 'Server Name',
201
+ labelText: l10n.server_name,
202
filled: true,
203
fillColor: theme.colorScheme.surface,
204
),
205
validator: (val) =>
203
- val == null || val.trim().isEmpty ? 'Name required' : null,
206
+ val == null || val.trim().isEmpty ? l10n.name_required : null,
207
),
208
const SizedBox(height: 16),
209
@@ -208,12 +211,12 @@ class _OnboardingServerSetupScreenState
211
TextFormField(
212
controller: _hostController,
213
decoration: InputDecoration(
211
- labelText: 'Host / IP Address',
214
+ labelText: l10n.host_label,
215
filled: true,
216
fillColor: theme.colorScheme.surface,
217
),
218
validator: (val) => val == null || val.trim().isEmpty
216
- ? 'Host required'
219
+ ? l10n.host_required
220
: null,
221
),
222
const SizedBox(height: 16),
@@ -221,16 +224,16 @@ class _OnboardingServerSetupScreenState
224
TextFormField(
225
controller: _portController,
226
decoration: InputDecoration(
224
- labelText: 'Port',
227
+ labelText: l10n.port_label,
228
filled: true,
229
fillColor: theme.colorScheme.surface,
230
),
231
validator: (val) {
232
if (val == null || val.trim().isEmpty) {
230
- return 'Port required';
233
+ return l10n.port_required;
234
} else {
235
if (int.tryParse(val) == null) {
233
- return 'Must be a number';
236
+ return l10n.port_invalid;
237
} else {
238
return null;
239
}
@@ -243,13 +246,13 @@ class _OnboardingServerSetupScreenState
246
TextFormField(
247
controller: _apiKeyController,
248
decoration: InputDecoration(
246
- labelText: isCloud ? 'API Key *' : 'API Key (Optional)',
249
+ labelText: isCloud ? l10n.api_key_required : l10n.api_key_optional,
250
filled: true,
251
fillColor: theme.colorScheme.surface,
252
),
253
validator: (val) {
254
if (isCloud && (val == null || val.trim().isEmpty)) {
252
- return 'API Key required for OpenRouter';
255
+ return l10n.api_key_required_openrouter;
256
}
257
return null;
258
},
@@ -298,7 +301,7 @@ class _OnboardingServerSetupScreenState
301
height: 20,
302
child: CircularProgressIndicator(strokeWidth: 2),
303
)
301
- : const Text('Test Connection'),
304
+ : Text(l10n.test_connection),
305
),
306
const SizedBox(height: 16),
307
@@ -314,9 +317,9 @@ class _OnboardingServerSetupScreenState
317
color: Colors.white,
318
),
319
)
317
- : const Text(
318
- 'Save & Continue',
319
- style: TextStyle(
320
+ : Text(
321
+ l10n.save_continue,
322
+ style: const TextStyle(
323
fontSize: 16,
324
fontWeight: FontWeight.w600,
325
),
lib/features/onboarding/screens/onboarding_server_type_screen.dart
+22
-20
@@ -7,6 +7,7 @@ import 'package:shadcn_ui/shadcn_ui.dart';
7
import '../../../core/models/enums.dart';
8
import '../../../core/providers/app_providers.dart';
9
import '../../../core/routes/app_routes.dart';
10
+import '../../../l10n/app_localizations.dart';
11
12
class OnboardingServerTypeScreen extends ConsumerStatefulWidget {
13
const OnboardingServerTypeScreen({super.key});
@@ -28,6 +29,7 @@ class _OnboardingServerTypeScreenState
29
30
@override
31
Widget build(BuildContext context) {
32
+ final l10n = AppLocalizations.of(context)!;
33
final theme = Theme.of(context);
34
35
return Scaffold(
@@ -42,9 +44,9 @@ class _OnboardingServerTypeScreenState
44
height: 24,
45
),
46
),
45
- const Text(
46
- 'LOCALMIND',
47
- style: TextStyle(letterSpacing: 2, fontSize: 14),
47
+ Text(
48
+ l10n.onboarding_localmind,
49
+ style: const TextStyle(letterSpacing: 2, fontSize: 14),
50
),
51
],
52
),
@@ -52,7 +54,7 @@ class _OnboardingServerTypeScreenState
54
actions: [
55
TextButton.icon(
56
onPressed: () => _skipOnboarding(),
55
- label: const Text('Skip'),
57
+ label: Text(l10n.skip),
58
icon: HugeIcon(icon: HugeIcons.strokeRoundedNext),
59
),
60
],
@@ -68,7 +70,7 @@ class _OnboardingServerTypeScreenState
70
children: [
71
const SizedBox(height: 24),
72
Text(
71
- 'Connect Your\nServer',
73
+ l10n.onboarding_connect_server,
74
textAlign: TextAlign.center,
75
style: theme.textTheme.headlineLarge?.copyWith(
76
fontWeight: FontWeight.w800,
@@ -77,7 +79,7 @@ class _OnboardingServerTypeScreenState
79
),
80
const SizedBox(height: 16),
81
Text(
80
- 'Connect to LM Studio, Ollama, or\nOpenRouter to start your private AI\nexperience.',
82
+ l10n.onboarding_connect_desc,
83
textAlign: TextAlign.center,
84
style: theme.textTheme.bodySmall?.copyWith(
85
color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
@@ -87,8 +89,8 @@ class _OnboardingServerTypeScreenState
89
const SizedBox(height: 24),
90
_buildServerCard(
91
type: ServerType.onDevice,
90
- title: 'On-Device',
91
- subtitle: 'NO SERVER NEEDED',
92
+ title: l10n.server_type_on_device,
93
+ subtitle: l10n.server_type_on_device_sub,
94
iconWidget: Icon(
95
Icons.phone_android_rounded,
96
color: _selectedType == ServerType.onDevice
@@ -100,8 +102,8 @@ class _OnboardingServerTypeScreenState
102
const SizedBox(height: 16),
103
_buildServerCard(
104
type: ServerType.lmStudio,
103
- title: 'LM Studio',
104
- subtitle: 'LOCAL API',
105
+ title: l10n.server_type_lm_studio,
106
+ subtitle: l10n.server_type_lm_studio_sub,
107
iconWidget: Icon(
108
Icons.terminal_rounded,
109
color: _selectedType == ServerType.lmStudio
@@ -113,8 +115,8 @@ class _OnboardingServerTypeScreenState
115
const SizedBox(height: 16),
116
_buildServerCard(
117
type: ServerType.ollama,
116
- title: 'Ollama',
117
- subtitle: 'CLI ENGINE',
118
+ title: l10n.server_type_ollama,
119
+ subtitle: l10n.server_type_ollama_sub,
120
iconWidget: Icon(
121
Icons.smart_toy_rounded,
122
color: _selectedType == ServerType.ollama
@@ -126,8 +128,8 @@ class _OnboardingServerTypeScreenState
128
const SizedBox(height: 16),
129
_buildServerCard(
130
type: ServerType.openRouter,
129
- title: 'OpenRouter',
130
- subtitle: 'UNIFIED CLOUD',
131
+ title: l10n.server_type_openrouter,
132
+ subtitle: l10n.server_type_openrouter_sub,
133
iconWidget: HugeIcon(
134
icon: HugeIcons.strokeRoundedCloudServer,
135
color: _selectedType == ServerType.openRouter
@@ -164,8 +166,8 @@ class _OnboardingServerTypeScreenState
166
const SizedBox(width: 8),
167
Text(
168
_selectedType != null
167
- ? 'READY TO CONTINUE'
168
- : 'WAITING FOR SELECTION',
169
+ ? l10n.ready_continue
170
+ : l10n.waiting_selection,
171
style: theme.textTheme.labelSmall?.copyWith(
172
color: theme.colorScheme.onSurface.withValues(
173
alpha: 0.5,
@@ -183,7 +185,7 @@ class _OnboardingServerTypeScreenState
185
onPressed: () {
186
if (_selectedType != null) {
187
if (_selectedType == ServerType.onDevice) {
186
- _completeOnboarding();
188
+ context.push(AppRoutes.onboardingModelDownload);
189
} else {
190
context.push(
191
AppRoutes.onboardingSetup,
@@ -192,9 +194,9 @@ class _OnboardingServerTypeScreenState
194
}
195
}
196
},
195
- child: const Text(
196
- 'Continue',
197
- style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
197
+ child: Text(
198
+ l10n.continue_action,
199
+ style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
200
),
201
),
202
SizedBox(
lib/features/onboarding/screens/onboarding_theme_screen.dart
+15
-13
@@ -7,6 +7,7 @@ import 'package:shadcn_ui/shadcn_ui.dart';
7
import '../../../core/providers/app_providers.dart';
8
import '../../../core/routes/app_routes.dart';
9
import '../../../core/theme/app_theme.dart';
10
+import '../../../l10n/app_localizations.dart';
11
12
class OnboardingThemeScreen extends ConsumerStatefulWidget {
13
const OnboardingThemeScreen({super.key});
@@ -19,12 +20,13 @@ class OnboardingThemeScreen extends ConsumerStatefulWidget {
20
class _OnboardingThemeScreenState extends ConsumerState<OnboardingThemeScreen> {
21
@override
22
Widget build(BuildContext context) {
23
+ final l10n = AppLocalizations.of(context)!;
24
final theme = Theme.of(context);
25
final currentTheme = ref.watch(themeModeProvider);
26
27
return Scaffold(
28
appBar: AppBar(
27
- title: const Text('Choose Theme'),
29
+ title: Text(l10n.choose_theme),
30
leading: IconButton(
31
icon: const Icon(Icons.arrow_back),
32
onPressed: () {
@@ -39,7 +41,7 @@ class _OnboardingThemeScreenState extends ConsumerState<OnboardingThemeScreen> {
41
crossAxisAlignment: CrossAxisAlignment.stretch,
42
children: [
43
Text(
42
- 'Personalize the app appearance. You can always change this later in settings.',
44
+ l10n.choose_theme_desc,
45
style: theme.textTheme.titleMedium?.copyWith(
46
color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
47
),
@@ -51,8 +53,8 @@ class _OnboardingThemeScreenState extends ConsumerState<OnboardingThemeScreen> {
53
children: [
54
_buildThemeCard(
55
type: AppThemeType.system,
54
- title: 'System',
55
- subtitle: 'Matches your device settings',
56
+ title: l10n.theme_card_system,
57
+ subtitle: l10n.theme_card_system_sub,
58
iconWidget: HugeIcon(
59
icon: HugeIcons.strokeRoundedSettings01,
60
color: currentTheme == AppThemeType.system
@@ -65,8 +67,8 @@ class _OnboardingThemeScreenState extends ConsumerState<OnboardingThemeScreen> {
67
const SizedBox(height: 16),
68
_buildThemeCard(
69
type: AppThemeType.light,
68
- title: 'Light',
69
- subtitle: 'Clean and bright',
70
+ title: l10n.theme_card_light,
71
+ subtitle: l10n.theme_card_light_sub,
72
iconWidget: HugeIcon(
73
icon: HugeIcons.strokeRoundedSun01,
74
color: currentTheme == AppThemeType.light
@@ -79,8 +81,8 @@ class _OnboardingThemeScreenState extends ConsumerState<OnboardingThemeScreen> {
81
const SizedBox(height: 16),
82
_buildThemeCard(
83
type: AppThemeType.dark,
82
- title: 'Dark',
83
- subtitle: 'Easy on the eyes',
84
+ title: l10n.theme_card_dark,
85
+ subtitle: l10n.theme_card_dark_sub,
86
iconWidget: HugeIcon(
87
icon: HugeIcons.strokeRoundedMoon02,
88
color: currentTheme == AppThemeType.dark
@@ -93,8 +95,8 @@ class _OnboardingThemeScreenState extends ConsumerState<OnboardingThemeScreen> {
95
const SizedBox(height: 16),
96
_buildThemeCard(
97
type: AppThemeType.claude,
96
- title: 'Claude',
97
- subtitle: 'A warm, peach-tinted theme',
98
+ title: l10n.theme_card_claude,
99
+ subtitle: l10n.theme_card_claude_sub,
100
iconWidget: HugeIcon(
101
icon: HugeIcons.strokeRoundedPaintBrush02,
102
color: currentTheme == AppThemeType.claude
@@ -116,9 +118,9 @@ class _OnboardingThemeScreenState extends ConsumerState<OnboardingThemeScreen> {
118
context.push(AppRoutes.onboardingNotifications);
119
}
120
},
119
- child: const Text(
120
- 'Finish Setup',
121
- style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
121
+ child: Text(
122
+ l10n.finish_setup,
123
+ style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
124
),
125
),
126
],
lib/features/personas/views/create_persona_screen.dart
+38
-32
@@ -1,6 +1,7 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
+import 'package:localmind/l10n/app_localizations.dart';
5
import '../data/models/persona.dart';
6
import '../providers/personas_providers.dart';
7
@@ -139,17 +140,21 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
140
}
141
142
if (mounted) {
143
+ final l10n = AppLocalizations.of(context)!;
144
context.pop();
145
ScaffoldMessenger.of(context).showSnackBar(
146
SnackBar(
145
- content: Text(_isEditing ? 'Persona updated' : 'Persona created'),
147
+ content: Text(_isEditing ? l10n.persona_updated : l10n.persona_created),
148
),
149
);
150
}
151
} catch (e) {
152
if (mounted) {
153
ScaffoldMessenger.of(context).showSnackBar(
152
- SnackBar(content: Text('Error: $e'), backgroundColor: Colors.red),
154
+ SnackBar(
155
+ content: Text(AppLocalizations.of(context)!.error_with_message(e.toString())),
156
+ backgroundColor: Colors.red,
157
+ ),
158
);
159
}
160
} finally {
@@ -159,12 +164,13 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
164
165
@override
166
Widget build(BuildContext context) {
167
+ final l10n = AppLocalizations.of(context)!;
168
final theme = Theme.of(context);
169
final isDark = theme.brightness == Brightness.dark;
170
171
return Scaffold(
172
appBar: AppBar(
167
- title: Text(_isEditing ? 'Edit Persona' : 'Create Persona'),
173
+ title: Text(_isEditing ? l10n.edit_persona : l10n.create_persona),
174
leading: IconButton(
175
icon: const Icon(Icons.close),
176
onPressed: () => context.pop(),
@@ -178,7 +184,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
184
height: 16,
185
child: CircularProgressIndicator(strokeWidth: 2),
186
)
181
- : Text(_isEditing ? 'Save' : 'Create'),
187
+ : Text(_isEditing ? l10n.save : l10n.create_persona_button),
188
),
189
],
190
),
@@ -188,7 +194,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
194
padding: const EdgeInsets.all(16),
195
children: [
196
Text(
191
- 'Emoji',
197
+ l10n.emoji_label,
198
style: TextStyle(
199
fontSize: 14,
200
fontWeight: FontWeight.w500,
@@ -248,13 +254,13 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
254
255
TextFormField(
256
controller: _nameController,
251
- decoration: const InputDecoration(
252
- labelText: 'Name',
253
- hintText: 'My Persona',
257
+ decoration: InputDecoration(
258
+ labelText: l10n.name_label,
259
+ hintText: l10n.my_persona_hint,
260
),
261
validator: (v) {
256
- if (v == null || v.trim().isEmpty) return 'Name is required';
257
- if (v.trim().length > 50) return 'Max 50 characters';
262
+ if (v == null || v.trim().isEmpty) return l10n.name_required;
263
+ if (v.trim().length > 50) return l10n.name_max_50;
264
return null;
265
},
266
textInputAction: TextInputAction.next,
@@ -264,7 +270,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
270
271
DropdownButtonFormField<String>(
272
initialValue: _selectedCategory,
267
- decoration: const InputDecoration(labelText: 'Category'),
273
+ decoration: InputDecoration(labelText: l10n.category_label),
274
items: _categories
275
.map((c) => DropdownMenuItem(value: c, child: Text(c)))
276
.toList(),
@@ -274,9 +280,9 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
280
281
TextFormField(
282
controller: _descriptionController,
277
- decoration: const InputDecoration(
278
- labelText: 'Description (optional)',
279
- hintText: 'What this persona does...',
283
+ decoration: InputDecoration(
284
+ labelText: l10n.description_optional,
285
+ hintText: l10n.description_hint,
286
),
287
maxLength: 200,
288
textInputAction: TextInputAction.next,
@@ -287,7 +293,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
293
mainAxisAlignment: MainAxisAlignment.spaceBetween,
294
children: [
295
Text(
290
- 'System Prompt',
296
+ l10n.system_prompt,
297
style: TextStyle(
298
fontSize: 14,
299
fontWeight: FontWeight.w500,
@@ -297,7 +303,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
303
Row(
304
children: [
305
Text(
300
- '${_promptController.text.length}/4000',
306
+ l10n.character_count_max(_promptController.text.length),
307
style: TextStyle(
308
fontSize: 12,
309
color: _promptController.text.length > 4000
@@ -313,7 +319,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
319
_showPreview ? Icons.edit : Icons.visibility,
320
size: 18,
321
),
316
- tooltip: _showPreview ? 'Edit' : 'Preview',
322
+ tooltip: _showPreview ? l10n.edit : l10n.preview,
323
onPressed: () =>
324
setState(() => _showPreview = !_showPreview),
325
),
@@ -339,7 +345,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
345
),
346
child: Text(
347
_promptController.text.isEmpty
342
- ? 'No prompt yet...'
348
+ ? l10n.no_prompt_placeholder
349
: _promptController.text,
350
style: TextStyle(
351
fontSize: 14,
@@ -354,8 +360,8 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
360
else
361
TextFormField(
362
controller: _promptController,
357
- decoration: const InputDecoration(
358
- hintText: 'You are a helpful assistant...',
363
+ decoration: InputDecoration(
364
+ hintText: l10n.prompt_hint,
365
alignLabelWithHint: true,
366
),
367
maxLines: 8,
@@ -363,9 +369,9 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
369
maxLength: 4000,
370
validator: (v) {
371
if (v == null || v.trim().isEmpty) {
366
- return 'System prompt is required';
372
+ return l10n.prompt_required;
373
}
368
- if (v.trim().length > 4000) return 'Max 4000 characters';
374
+ if (v.trim().length > 4000) return l10n.prompt_max_chars;
375
return null;
376
},
377
onChanged: (_) => setState(() {}),
@@ -384,7 +390,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
390
),
391
const SizedBox(width: 4),
392
Text(
387
- 'Advanced Settings',
393
+ l10n.advanced_settings,
394
style: TextStyle(
395
fontSize: 14,
396
fontWeight: FontWeight.w500,
@@ -403,9 +409,9 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
409
Expanded(
410
child: TextFormField(
411
controller: _tempController,
406
- decoration: const InputDecoration(
407
- labelText: 'Temperature (0.0–2.0)',
408
- hintText: '0.7',
412
+ decoration: InputDecoration(
413
+ labelText: l10n.temperature_label,
414
+ hintText: l10n.temp_hint,
415
),
416
keyboardType: const TextInputType.numberWithOptions(
417
decimal: true,
@@ -413,7 +419,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
419
validator: (v) {
420
if (v == null || v.trim().isEmpty) return null;
421
final val = double.tryParse(v.trim());
416
- if (val == null || val < 0 || val > 2) return '0.0–2.0';
422
+ if (val == null || val < 0 || val > 2) return l10n.range_0_2;
423
return null;
424
},
425
),
@@ -422,9 +428,9 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
428
Expanded(
429
child: TextFormField(
430
controller: _topPController,
425
- decoration: const InputDecoration(
426
- labelText: 'Top P (0.0–1.0)',
427
- hintText: '0.9',
431
+ decoration: InputDecoration(
432
+ labelText: l10n.top_p_label,
433
+ hintText: l10n.top_p_hint,
434
),
435
keyboardType: const TextInputType.numberWithOptions(
436
decimal: true,
@@ -432,7 +438,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
438
validator: (v) {
439
if (v == null || v.trim().isEmpty) return null;
440
final val = double.tryParse(v.trim());
435
- if (val == null || val < 0 || val > 1) return '0.0–1.0';
441
+ if (val == null || val < 0 || val > 1) return l10n.range_0_1;
442
return null;
443
},
444
),
@@ -449,7 +455,7 @@ class _CreatePersonaScreenState extends ConsumerState<CreatePersonaScreen> {
455
height: 20,
456
child: CircularProgressIndicator(strokeWidth: 2),
457
)
452
- : Text(_isEditing ? 'Save Changes' : 'Create Persona'),
458
+ : Text(_isEditing ? l10n.save_changes : l10n.create_persona_button),
459
),
460
],
461
),
lib/features/personas/views/persona_list_screen.dart
+54
-44
@@ -1,7 +1,7 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
-
4
+import 'package:localmind/l10n/app_localizations.dart';
5
import '../../../core/routes/app_routes.dart';
6
import '../data/models/persona.dart';
7
import '../providers/personas_providers.dart';
@@ -11,6 +11,7 @@ class PersonaListScreen extends ConsumerWidget {
11
12
@override
13
Widget build(BuildContext context, WidgetRef ref) {
14
+ final l10n = AppLocalizations.of(context)!;
15
final filteredPersonas = ref.watch(filteredPersonasProvider);
16
final builtIn = filteredPersonas.where((p) => p.isBuiltIn).toList();
17
final userCreated = filteredPersonas.where((p) => !p.isBuiltIn).toList();
@@ -47,7 +48,7 @@ class PersonaListScreen extends ConsumerWidget {
48
),
49
const SizedBox(width: 8),
50
Text(
50
- 'Personas',
51
+ l10n.personas_title,
52
style: TextStyle(
53
fontSize: 20,
54
fontWeight: FontWeight.bold,
@@ -64,14 +65,14 @@ class PersonaListScreen extends ConsumerWidget {
65
padding: const EdgeInsets.symmetric(horizontal: 12),
66
children:
67
[
67
- 'All',
68
- 'General',
69
- 'Coding',
70
- 'Education',
71
- 'Creative',
68
+ l10n.all,
69
+ l10n.persona_category_general,
70
+ l10n.persona_category_coding,
71
+ l10n.persona_category_education,
72
+ l10n.persona_category_creative,
73
].map((cat) {
74
final isActive =
74
- selectedCategory == (cat == 'All' ? null : cat);
75
+ selectedCategory == (cat == l10n.all ? null : cat);
76
return Padding(
77
padding: const EdgeInsets.symmetric(
78
horizontal: 4,
@@ -96,7 +97,7 @@ class PersonaListScreen extends ConsumerWidget {
97
onSelected: (_) {
98
ref
99
.read(personaCategoryFilterProvider.notifier)
99
- .setCategory(cat == 'All' ? null : cat);
100
+ .setCategory(cat == l10n.all ? null : cat);
101
},
102
selectedColor: isDark
103
? const Color(0xFF3B82F6)
@@ -127,7 +128,7 @@ class PersonaListScreen extends ConsumerWidget {
128
),
129
Expanded(
130
child: filteredPersonas.isEmpty
130
- ? _EmptyState(isDark: isDark)
131
+ ? _EmptyState(isDark: isDark, l10n: l10n)
132
: ListView(
133
padding: const EdgeInsets.symmetric(
134
horizontal: 16,
@@ -135,11 +136,12 @@ class PersonaListScreen extends ConsumerWidget {
136
),
137
children: [
138
if (builtIn.isNotEmpty) ...[
138
- _SectionLabel(label: 'BUILT-IN', isDark: isDark),
139
+ _SectionLabel(label: l10n.persona_builtin_section, isDark: isDark),
140
...builtIn.map(
141
(p) => _PersonaCard(
142
persona: p,
143
isDark: isDark,
144
+ l10n: l10n,
145
onLongPress: () => _showActions(
146
context,
147
ref,
@@ -151,11 +153,12 @@ class PersonaListScreen extends ConsumerWidget {
153
const SizedBox(height: 12),
154
],
155
if (userCreated.isNotEmpty) ...[
154
- _SectionLabel(label: 'MY PERSONAS', isDark: isDark),
156
+ _SectionLabel(label: l10n.persona_my_section, isDark: isDark),
157
...userCreated.map(
158
(p) => _PersonaCard(
159
persona: p,
160
isDark: isDark,
161
+ l10n: l10n,
162
onTap: () => context.push(
163
AppRoutes.createPersona,
164
extra: p,
@@ -175,9 +178,9 @@ class PersonaListScreen extends ConsumerWidget {
178
),
179
],
180
),
178
- Positioned(
181
+ PositionedDirectional(
182
bottom: 24,
180
- right: 24,
183
+ end: 24,
184
child: FloatingActionButton(
185
onPressed: () => context.push(AppRoutes.createPersona),
186
child: const Icon(Icons.add),
@@ -197,6 +200,7 @@ class PersonaListScreen extends ConsumerWidget {
200
showModalBottomSheet(
201
context: context,
202
builder: (ctx) {
203
+ final sheetL10n = AppLocalizations.of(ctx)!;
204
return SafeArea(
205
child: Column(
206
mainAxisSize: MainAxisSize.min,
@@ -204,7 +208,7 @@ class PersonaListScreen extends ConsumerWidget {
208
if (!isBuiltIn) ...[
209
ListTile(
210
leading: const Icon(Icons.edit),
207
- title: const Text('Edit'),
211
+ title: Text(sheetL10n.edit),
212
onTap: () {
213
Navigator.pop(ctx);
214
context.push(AppRoutes.createPersona, extra: persona);
@@ -212,43 +216,46 @@ class PersonaListScreen extends ConsumerWidget {
216
),
217
ListTile(
218
leading: const Icon(Icons.delete_outline, color: Colors.red),
215
- title: const Text(
216
- 'Delete',
217
- style: TextStyle(color: Colors.red),
219
+ title: Text(
220
+ sheetL10n.delete,
221
+ style: const TextStyle(color: Colors.red),
222
),
223
onTap: () {
224
Navigator.pop(ctx);
225
showDialog(
226
context: context,
223
- builder: (dCtx) => AlertDialog(
224
- title: Text('Delete "${persona.name}"?'),
225
- content: const Text('This cannot be undone.'),
226
- actions: [
227
- TextButton(
228
- onPressed: () => Navigator.pop(dCtx),
229
- child: const Text('Cancel'),
230
- ),
231
- TextButton(
232
- onPressed: () {
233
- ref
234
- .read(personasNotifierProvider.notifier)
235
- .deletePersona(persona.id);
236
- Navigator.pop(dCtx);
237
- },
238
- style: TextButton.styleFrom(
239
- foregroundColor: Colors.red,
227
+ builder: (dCtx) {
228
+ final dlgL10n = AppLocalizations.of(dCtx)!;
229
+ return AlertDialog(
230
+ title: Text(dlgL10n.delete_persona_title(persona.name)),
231
+ content: Text(dlgL10n.delete_persona_body),
232
+ actions: [
233
+ TextButton(
234
+ onPressed: () => Navigator.pop(dCtx),
235
+ child: Text(dlgL10n.cancel),
236
),
241
- child: const Text('Delete'),
242
- ),
243
- ],
244
- ),
237
+ TextButton(
238
+ onPressed: () {
239
+ ref
240
+ .read(personasNotifierProvider.notifier)
241
+ .deletePersona(persona.id);
242
+ Navigator.pop(dCtx);
243
+ },
244
+ style: TextButton.styleFrom(
245
+ foregroundColor: Colors.red,
246
+ ),
247
+ child: Text(dlgL10n.delete),
248
+ ),
249
+ ],
250
+ );
251
+ },
252
);
253
},
254
),
255
],
256
ListTile(
257
leading: const Icon(Icons.copy),
251
- title: const Text('Clone & Edit'),
258
+ title: Text(sheetL10n.clone_edit),
259
onTap: () {
260
Navigator.pop(ctx);
261
final clone = ref
@@ -295,12 +302,14 @@ class _PersonaCard extends StatelessWidget {
302
const _PersonaCard({
303
required this.persona,
304
required this.isDark,
305
+ required this.l10n,
306
this.onTap,
307
this.onLongPress,
308
});
309
310
final Persona persona;
311
final bool isDark;
312
+ final AppLocalizations l10n;
313
final VoidCallback? onTap;
314
final VoidCallback? onLongPress;
315
@@ -373,7 +382,7 @@ class _PersonaCard extends StatelessWidget {
382
borderRadius: BorderRadius.circular(4),
383
),
384
child: Text(
376
- 'Built-in',
385
+ l10n.builtin_badge,
386
style: TextStyle(
387
fontSize: 10,
388
fontWeight: FontWeight.w600,
@@ -442,8 +451,9 @@ class _PersonaCard extends StatelessWidget {
451
}
452
453
class _EmptyState extends StatelessWidget {
445
- const _EmptyState({required this.isDark});
454
+ const _EmptyState({required this.isDark, required this.l10n});
455
final bool isDark;
456
+ final AppLocalizations l10n;
457
458
@override
459
Widget build(BuildContext context) {
@@ -460,7 +470,7 @@ class _EmptyState extends StatelessWidget {
470
),
471
const SizedBox(height: 16),
472
Text(
463
- 'No personas found',
473
+ l10n.no_personas_found,
474
style: TextStyle(
475
fontSize: 18,
476
fontWeight: FontWeight.w500,
@@ -469,7 +479,7 @@ class _EmptyState extends StatelessWidget {
479
),
480
const SizedBox(height: 8),
481
Text(
472
- 'Create your first persona to customize AI behavior.',
482
+ l10n.no_personas_desc,
483
style: TextStyle(
484
fontSize: 14,
485
color: isDark
lib/features/servers/views/add_server_screen.dart
+42
-35
@@ -3,6 +3,7 @@ import 'package:hugeicons/hugeicons.dart';
3
import 'package:flutter_riverpod/flutter_riverpod.dart';
4
import 'package:go_router/go_router.dart';
5
import 'package:shadcn_ui/shadcn_ui.dart';
6
+import 'package:localmind/l10n/app_localizations.dart';
7
import '../../../core/constants/app_constants.dart';
8
import '../data/models/server.dart';
9
import '../../../core/models/enums.dart';
@@ -93,12 +94,12 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
94
final isConnected = await apiService.testConnection(testServer);
95
setState(() {
96
_testResult = isConnected
96
- ? 'Connection successful!'
97
- : 'Connection failed. Check your settings.';
97
+ ? AppLocalizations.of(context)!.connection_successful
98
+ : AppLocalizations.of(context)!.connection_failed;
99
});
100
} catch (e) {
101
setState(() {
101
- _testResult = 'Error: ${e.toString()}';
102
+ _testResult = AppLocalizations.of(context)!.error_with_message(e.toString());
103
});
104
} finally {
105
setState(() {
@@ -146,10 +147,11 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
147
}
148
149
if (mounted) {
150
+ final l10n = AppLocalizations.of(context)!;
151
context.pop();
152
ScaffoldMessenger.of(context).showSnackBar(
153
SnackBar(
152
- content: Text(_isEditing ? 'Server updated' : 'Server added'),
154
+ content: Text(_isEditing ? l10n.server_updated : l10n.server_added),
155
duration: const Duration(seconds: 2),
156
),
157
);
@@ -158,7 +160,7 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
160
if (mounted) {
161
ScaffoldMessenger.of(context).showSnackBar(
162
SnackBar(
161
- content: Text('Error: ${e.toString()}'),
163
+ content: Text(AppLocalizations.of(context)!.error_with_message(e.toString())),
164
backgroundColor: Colors.red,
165
),
166
);
@@ -173,48 +175,53 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
175
}
176
177
String? _validateName(String? value) {
178
+ final context = this.context;
179
+ final l10n = AppLocalizations.of(context)!;
180
if (value == null || value.trim().isEmpty) {
177
- return 'Name is required';
181
+ return l10n.name_required;
182
}
183
if (value.trim().length > 50) {
180
- return 'Name must be 50 characters or less';
184
+ return l10n.name_length_validation;
185
}
186
return null;
187
}
188
189
String? _validateHost(String? value) {
190
if (_selectedType == ServerType.openRouter) return null;
191
+ final l10n = AppLocalizations.of(context)!;
192
193
if (value == null || value.trim().isEmpty) {
189
- return 'Host is required';
194
+ return l10n.host_required;
195
}
196
final hostPattern = RegExp(r'^[a-zA-Z0-9]([a-zA-Z0-9\-\.]*[a-zA-Z0-9])?$');
197
if (!hostPattern.hasMatch(value.trim())) {
193
- return 'Enter a valid hostname or IP address';
198
+ return l10n.host_valid;
199
}
200
return null;
201
}
202
203
String? _validatePort(String? value) {
204
if (_selectedType == ServerType.openRouter) return null;
205
+ final l10n = AppLocalizations.of(context)!;
206
207
if (value == null || value.trim().isEmpty) {
202
- return 'Port is required';
208
+ return l10n.port_required;
209
}
210
final port = int.tryParse(value.trim());
211
if (port == null || port < 1 || port > 65535) {
206
- return 'Enter a valid port (1-65535)';
212
+ return l10n.port_range;
213
}
214
return null;
215
}
216
217
String? _validateApiKey(String? value) {
218
if (_selectedType == ServerType.openRouter) {
219
+ final l10n = AppLocalizations.of(context)!;
220
if (value == null || value.trim().isEmpty) {
214
- return 'API key is required for OpenRouter';
221
+ return l10n.api_key_required_openrouter;
222
}
223
if (!value.trim().startsWith('sk-')) {
217
- return 'OpenRouter API keys start with sk-';
224
+ return l10n.api_key_format;
225
}
226
}
227
return null;
@@ -237,11 +244,12 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
244
245
@override
246
Widget build(BuildContext context) {
247
+ final l10n = AppLocalizations.of(context)!;
248
final theme = Theme.of(context);
249
250
return Scaffold(
251
appBar: AppBar(
244
- title: Text(_isEditing ? 'Edit Server' : 'Add Server'),
252
+ title: Text(_isEditing ? l10n.edit_server : l10n.add_server_title),
253
leading: IconButton(
254
icon: const Icon(Icons.close),
255
onPressed: () => context.pop(),
@@ -252,7 +260,7 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
260
child: ListView(
261
padding: const EdgeInsets.all(16),
262
children: [
255
- Text('Server Type', style: theme.textTheme.titleSmall),
263
+ Text(l10n.server_type_label, style: theme.textTheme.titleSmall),
264
const SizedBox(height: 8),
265
ServerTypeSelector(
266
selectedType: _selectedType,
@@ -260,7 +268,7 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
268
),
269
const SizedBox(height: 24),
270
263
- Text('Server Icon', style: theme.textTheme.titleSmall),
271
+ Text(l10n.server_icon_label, style: theme.textTheme.titleSmall),
272
const SizedBox(height: 8),
273
GestureDetector(
274
onTap: _showIconPicker,
@@ -276,7 +284,6 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
284
width: 40,
285
height: 40,
286
decoration: BoxDecoration(
279
- // color: theme.colorScheme.primaryContainer,
287
borderRadius: BorderRadius.circular(8),
288
),
289
child: HugeIcon(
@@ -293,7 +300,7 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
300
const SizedBox(width: 12),
301
Expanded(
302
child: Text(
296
- _selectedIconName ?? 'Default icon',
303
+ _selectedIconName ?? l10n.default_icon,
304
style: theme.textTheme.bodyMedium,
305
),
306
),
@@ -310,9 +317,9 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
317
318
TextFormField(
319
controller: _nameController,
313
- decoration: const InputDecoration(
314
- labelText: 'Name',
315
- hintText: 'My Server',
320
+ decoration: InputDecoration(
321
+ labelText: l10n.name_label,
322
+ hintText: l10n.my_server_hint,
323
),
324
validator: _validateName,
325
textInputAction: TextInputAction.next,
@@ -322,8 +329,8 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
329
if (_selectedType != ServerType.openRouter) ...[
330
TextFormField(
331
controller: _hostController,
325
- decoration: const InputDecoration(
326
- labelText: 'Host / IP Address',
332
+ decoration: InputDecoration(
333
+ labelText: l10n.host_label,
334
hintText: '192.168.1.100',
335
),
336
validator: _validateHost,
@@ -335,7 +342,7 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
342
TextFormField(
343
controller: _portController,
344
decoration: InputDecoration(
338
- labelText: 'Port',
345
+ labelText: l10n.port_label,
346
hintText: _selectedType == ServerType.lmStudio
347
? AppConstants.lmStudioDefaultPort.toString()
348
: AppConstants.ollamaDefaultPort.toString(),
@@ -351,11 +358,11 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
358
controller: _apiKeyController,
359
decoration: InputDecoration(
360
labelText: _selectedType == ServerType.openRouter
354
- ? 'API Key *'
355
- : 'API Key (optional)',
361
+ ? l10n.api_key_required
362
+ : l10n.api_key_optional,
363
hintText: _selectedType == ServerType.openRouter
357
- ? 'sk-...'
358
- : 'For authenticated servers',
364
+ ? l10n.api_key_hint_openrouter
365
+ : l10n.api_key_hint_generic,
366
),
367
validator: _validateApiKey,
368
obscureText: true,
@@ -367,12 +374,12 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
374
Container(
375
padding: const EdgeInsets.all(12),
376
decoration: BoxDecoration(
370
- color: _testResult!.contains('successful')
377
+ color: _testResult!.contains(l10n.connection_successful.split('!')[0])
378
? Colors.green.withAlpha(25)
379
: Colors.red.withAlpha(25),
380
borderRadius: BorderRadius.circular(8),
381
border: Border.all(
375
- color: _testResult!.contains('successful')
382
+ color: _testResult!.contains(l10n.connection_successful.split('!')[0])
383
? Colors.green
384
: Colors.red,
385
),
@@ -380,10 +387,10 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
387
child: Row(
388
children: [
389
Icon(
383
- _testResult!.contains('successful')
390
+ _testResult!.contains(l10n.connection_successful.split('!')[0])
391
? Icons.check_circle
392
: Icons.error,
386
- color: _testResult!.contains('successful')
393
+ color: _testResult!.contains(l10n.connection_successful.split('!')[0])
394
? Colors.green
395
: Colors.red,
396
),
@@ -392,7 +399,7 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
399
child: Text(
400
_testResult!,
401
style: TextStyle(
395
- color: _testResult!.contains('successful')
402
+ color: _testResult!.contains(l10n.connection_successful.split('!')[0])
403
? Colors.green
404
: Colors.red,
405
),
@@ -413,7 +420,7 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
420
child: CircularProgressIndicator(strokeWidth: 2),
421
)
422
: const Icon(Icons.network_check),
416
- label: Text(_isTesting ? 'Testing...' : 'Test Connection'),
423
+ label: Text(_isTesting ? l10n.testing : l10n.test_connection),
424
),
425
const SizedBox(height: 24),
426
@@ -425,7 +432,7 @@ class _AddServerScreenState extends ConsumerState<AddServerScreen> {
432
height: 20,
433
child: CircularProgressIndicator(strokeWidth: 2),
434
)
428
- : Text(_isEditing ? 'Update Server' : 'Save Server'),
435
+ : Text(_isEditing ? l10n.update_server : l10n.save_server),
436
),
437
],
438
),
lib/features/servers/views/components/server_card.dart
+35
-34
@@ -1,6 +1,6 @@
1
import 'package:flutter/material.dart';
2
import 'package:hugeicons/hugeicons.dart';
3
-
3
+import 'package:localmind/l10n/app_localizations.dart';
4
import '../../../../core/models/enums.dart';
5
import '../../data/models/server.dart';
6
import 'connection_status_indicator.dart';
@@ -31,33 +31,34 @@ class ServerCard extends StatelessWidget {
31
return getDefaultServerIcon(server.type.name);
32
}
33
34
- String get _serverTypeName {
34
+ String _serverTypeName(AppLocalizations l10n) {
35
switch (server.type) {
36
case ServerType.lmStudio:
37
- return 'LM Studio';
37
+ return l10n.server_type_lm_studio_display;
38
case ServerType.openAICompatible:
39
- return 'OpenAI Compatible';
39
+ return l10n.server_type_openai_display;
40
case ServerType.ollama:
41
- return 'Ollama';
41
+ return l10n.server_type_ollama_display;
42
case ServerType.openRouter:
43
- return 'OpenRouter';
43
+ return l10n.server_type_openrouter_display;
44
case ServerType.onDevice:
45
- return 'On-Device';
45
+ return l10n.server_type_on_device_display;
46
}
47
}
48
49
- String get _serverAddress {
49
+ String _serverAddress(AppLocalizations l10n) {
50
if (server.type == ServerType.openRouter) {
51
- return 'openrouter.ai';
51
+ return l10n.server_address_openrouter;
52
}
53
if (server.type == ServerType.onDevice) {
54
- return 'Local inference';
54
+ return l10n.server_address_on_device;
55
}
56
- return '${server.host}:${server.port}';
56
+ return l10n.server_address_format(server.host, server.port.toString());
57
}
58
59
@override
60
Widget build(BuildContext context) {
61
+ final l10n = AppLocalizations.of(context)!;
62
final theme = Theme.of(context);
63
64
final activeGreen = const Color(0xFF22C55E);
@@ -81,17 +82,17 @@ class ServerCard extends StatelessWidget {
82
child: Stack(
83
children: [
84
if (isActive)
84
- Positioned(
85
- left: 0,
85
+ PositionedDirectional(
86
+ start: 0,
87
top: 0,
88
bottom: 0,
89
width: 4,
90
child: Container(
91
decoration: BoxDecoration(
92
color: activeGreen,
92
- borderRadius: const BorderRadius.only(
93
- topRight: Radius.circular(4),
94
- bottomRight: Radius.circular(4),
93
+ borderRadius: const BorderRadiusDirectional.only(
94
+ topEnd: Radius.circular(4),
95
+ bottomEnd: Radius.circular(4),
96
),
97
),
98
),
@@ -153,13 +154,13 @@ class ServerCard extends StatelessWidget {
154
horizontal: 8,
155
vertical: 2,
156
),
156
- margin: const EdgeInsets.only(right: 4),
157
+ margin: const EdgeInsetsDirectional.only(end: 4),
158
decoration: BoxDecoration(
159
color: activeGreen,
160
borderRadius: BorderRadius.circular(4),
161
),
162
child: Text(
162
- 'Active',
163
+ l10n.active,
164
style: theme.textTheme.labelSmall?.copyWith(
165
color: Colors.white,
166
fontWeight: FontWeight.bold,
@@ -186,7 +187,7 @@ class ServerCard extends StatelessWidget {
187
),
188
),
189
child: Text(
189
- 'Default',
190
+ l10n.default_badge,
191
style: theme.textTheme.labelSmall?.copyWith(
192
color: isActive
193
? activeGreen
@@ -200,14 +201,14 @@ class ServerCard extends StatelessWidget {
201
Row(
202
children: [
203
Text(
203
- _serverTypeName,
204
+ _serverTypeName(l10n),
205
style: theme.textTheme.bodySmall,
206
),
207
const SizedBox(width: 8),
207
- Text('•', style: theme.textTheme.bodySmall),
208
+ Text('\u2022', style: theme.textTheme.bodySmall),
209
const SizedBox(width: 8),
210
Text(
210
- _serverAddress,
211
+ _serverAddress(l10n),
212
style: theme.textTheme.bodySmall,
213
),
214
],
@@ -232,34 +233,34 @@ class ServerCard extends StatelessWidget {
233
}
234
},
235
itemBuilder: (context) => [
235
- const PopupMenuItem(
236
+ PopupMenuItem(
237
value: 'edit',
238
child: Row(
239
children: [
239
- Icon(Icons.edit, size: 18),
240
- SizedBox(width: 8),
241
- Text('Edit'),
240
+ const Icon(Icons.edit, size: 18),
241
+ const SizedBox(width: 8),
242
+ Text(l10n.edit),
243
],
244
),
245
),
246
if (!server.isDefault)
246
- const PopupMenuItem(
247
+ PopupMenuItem(
248
value: 'setDefault',
249
child: Row(
250
children: [
250
- Icon(Icons.star, size: 18),
251
- SizedBox(width: 8),
252
- Text('Set as Default'),
251
+ const Icon(Icons.star, size: 18),
252
+ const SizedBox(width: 8),
253
+ Text(l10n.set_as_default),
254
],
255
),
256
),
256
- const PopupMenuItem(
257
+ PopupMenuItem(
258
value: 'delete',
259
child: Row(
260
children: [
260
- Icon(Icons.delete, size: 18, color: Colors.red),
261
- SizedBox(width: 8),
262
- Text('Delete', style: TextStyle(color: Colors.red)),
261
+ const Icon(Icons.delete, size: 18, color: Colors.red),
262
+ const SizedBox(width: 8),
263
+ Text(l10n.delete, style: const TextStyle(color: Colors.red)),
264
],
265
),
266
),
lib/features/servers/views/components/server_icon_picker.dart
+7
-5
@@ -1,6 +1,7 @@
1
import 'package:flutter/material.dart';
2
import 'package:hugeicons/hugeicons.dart';
3
import 'package:shadcn_ui/shadcn_ui.dart';
4
+import 'package:localmind/l10n/app_localizations.dart';
5
6
class HugeIconData {
7
final String name;
@@ -110,11 +111,12 @@ class _ServerIconPickerState extends State<ServerIconPicker> {
111
112
@override
113
Widget build(BuildContext context) {
114
+ final l10n = AppLocalizations.of(context)!;
115
final theme = Theme.of(context);
116
117
return ShadSheet(
116
- title: const Text('Select Icon'),
117
- description: const Text('Choose an icon for your server'),
118
+ title: Text(l10n.select_icon),
119
+ description: Text(l10n.select_icon_desc),
120
child: SizedBox(
121
width: double.maxFinite,
122
height: MediaQuery.of(context).size.height * 0.6,
@@ -124,9 +126,9 @@ class _ServerIconPickerState extends State<ServerIconPicker> {
126
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
127
child: ShadInput(
128
controller: _searchController,
127
- placeholder: const Text('Search icons...'),
129
+ placeholder: Text(l10n.search_icons_hint),
130
leading: const Padding(
129
- padding: EdgeInsets.only(left: 8),
131
+ padding: EdgeInsetsDirectional.only(start: 8),
132
child: Icon(Icons.search, size: 18),
133
),
134
onChanged: _filterIcons,
@@ -197,7 +199,7 @@ class _ServerIconPickerState extends State<ServerIconPicker> {
199
padding: const EdgeInsets.all(16),
200
child: ShadButton(
201
onPressed: () => Navigator.of(context).pop(),
200
- child: const Text('Done'),
202
+ child: Text(l10n.done),
203
),
204
),
205
],
lib/features/servers/views/components/server_type_selector.dart
+5
-4
@@ -1,6 +1,6 @@
1
import 'package:flutter/material.dart';
2
import 'package:hugeicons/hugeicons.dart';
3
-
3
+import 'package:localmind/l10n/app_localizations.dart';
4
import '../../../../core/models/enums.dart';
5
6
class ServerTypeSelector extends StatelessWidget {
@@ -15,13 +15,14 @@ class ServerTypeSelector extends StatelessWidget {
15
16
@override
17
Widget build(BuildContext context) {
18
+ final l10n = AppLocalizations.of(context)!;
19
final theme = Theme.of(context);
20
final colorScheme = theme.colorScheme;
21
22
final types = [
23
(
24
ServerType.lmStudio,
24
- 'LM Studio',
25
+ l10n.server_type_lm_studio,
26
HugeIcons.strokeRoundedAiComputer,
27
Colors.blue,
28
),
@@ -33,13 +34,13 @@ class ServerTypeSelector extends StatelessWidget {
34
),
35
(
36
ServerType.ollama,
36
- 'Ollama',
37
+ l10n.server_type_ollama,
38
HugeIcons.strokeRoundedPencilEdit02,
39
Colors.orange,
40
),
41
(
42
ServerType.openRouter,
42
- 'OpenRouter',
43
+ l10n.server_type_openrouter,
44
HugeIcons.strokeRoundedCloud,
45
Colors.purple,
46
),
lib/features/servers/views/server_list_screen.dart
+37
-42
@@ -1,7 +1,7 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
-
4
+import 'package:localmind/l10n/app_localizations.dart';
5
import '../../../core/models/enums.dart';
6
import '../../../core/providers/service_providers.dart';
7
import '../../../core/routes/app_routes.dart';
@@ -13,6 +13,7 @@ class ServerListScreen extends ConsumerWidget {
13
14
@override
15
Widget build(BuildContext context, WidgetRef ref) {
16
+ final l10n = AppLocalizations.of(context)!;
17
final servers = ref.watch(serversProvider);
18
final activeServer = ref.watch(activeServerProvider);
19
final theme = Theme.of(context);
@@ -47,7 +48,7 @@ class ServerListScreen extends ConsumerWidget {
48
),
49
const SizedBox(width: 8),
50
Text(
50
- 'Servers',
51
+ l10n.servers_title,
52
style: TextStyle(
53
fontSize: 20,
54
fontWeight: FontWeight.bold,
@@ -61,7 +62,7 @@ class ServerListScreen extends ConsumerWidget {
62
Expanded(
63
child: servers.when(
64
data: (serverList) => serverList.isEmpty
64
- ? _buildEmptyState(context, isDark, theme)
65
+ ? _buildEmptyState(context, l10n, theme)
66
: RefreshIndicator(
67
onRefresh: () async {
68
for (final server in serverList) {
@@ -92,7 +93,7 @@ class ServerListScreen extends ConsumerWidget {
93
ScaffoldMessenger.of(context).showSnackBar(
94
SnackBar(
95
content: Text(
95
- 'Switched to ${server.name}',
96
+ l10n.switched_to_server(server.name),
97
),
98
duration: const Duration(seconds: 2),
99
),
@@ -100,18 +101,10 @@ class ServerListScreen extends ConsumerWidget {
101
},
102
onEdit: isOnDevice
103
? null
103
- : () => _showEditDialog(
104
- context,
105
- ref,
106
- server,
107
- ),
104
+ : () => _showEditDialog(context, ref, server),
105
onDelete: isOnDevice
106
? null
110
- : () => _showDeleteConfirmation(
111
- context,
112
- ref,
113
- server,
114
- ),
107
+ : () => _showDeleteConfirmation(context, ref, l10n, server),
108
onSetDefault: () {
109
ref
110
.read(serversProvider.notifier)
@@ -134,10 +127,10 @@ class ServerListScreen extends ConsumerWidget {
127
size: 48,
128
),
129
const SizedBox(height: 16),
137
- Text('Error loading servers: $err'),
130
+ Text(l10n.error_with_message(err.toString())),
131
TextButton(
132
onPressed: () => ref.invalidate(serversProvider),
140
- child: const Text('Retry'),
133
+ child: Text(l10n.retry),
134
),
135
],
136
),
@@ -146,9 +139,9 @@ class ServerListScreen extends ConsumerWidget {
139
),
140
],
141
),
149
- Positioned(
142
+ PositionedDirectional(
143
bottom: 24,
151
- right: 24,
144
+ end: 24,
145
child: FloatingActionButton(
146
onPressed: () => context.push(AppRoutes.addServer),
147
child: const Icon(Icons.add),
@@ -159,7 +152,7 @@ class ServerListScreen extends ConsumerWidget {
152
);
153
}
154
162
- Widget _buildEmptyState(BuildContext context, bool isDark, ThemeData theme) {
155
+ Widget _buildEmptyState(BuildContext context, AppLocalizations l10n, ThemeData theme) {
156
return Center(
157
child: Padding(
158
padding: const EdgeInsets.all(32),
@@ -168,10 +161,10 @@ class ServerListScreen extends ConsumerWidget {
161
children: [
162
Icon(Icons.computer, size: 80, color: theme.colorScheme.outline),
163
const SizedBox(height: 24),
171
- Text('No Servers Yet', style: theme.textTheme.headlineSmall),
164
+ Text(l10n.no_servers_yet, style: theme.textTheme.headlineSmall),
165
const SizedBox(height: 8),
166
Text(
174
- 'Add your first server to start chatting with AI models.',
167
+ l10n.no_servers_desc,
168
style: theme.textTheme.bodyMedium?.copyWith(
169
color: theme.colorScheme.outline,
170
),
@@ -181,7 +174,7 @@ class ServerListScreen extends ConsumerWidget {
174
ElevatedButton.icon(
175
onPressed: () => context.push(AppRoutes.addServer),
176
icon: const Icon(Icons.add),
184
- label: const Text('Add Server'),
177
+ label: Text(l10n.add_server),
178
),
179
],
180
),
@@ -196,30 +189,32 @@ class ServerListScreen extends ConsumerWidget {
189
void _showDeleteConfirmation(
190
BuildContext context,
191
WidgetRef ref,
192
+ AppLocalizations l10n,
193
dynamic server,
194
) {
195
showDialog(
196
context: context,
203
- builder: (context) => AlertDialog(
204
- title: const Text('Delete Server'),
205
- content: Text(
206
- 'Are you sure you want to delete "${server.name}"? This cannot be undone.',
207
- ),
208
- actions: [
209
- TextButton(
210
- onPressed: () => Navigator.pop(context),
211
- child: const Text('Cancel'),
212
- ),
213
- TextButton(
214
- onPressed: () {
215
- ref.read(serversProvider.notifier).deleteServer(server.id);
216
- Navigator.pop(context);
217
- },
218
- style: TextButton.styleFrom(foregroundColor: Colors.red),
219
- child: const Text('Delete'),
220
- ),
221
- ],
222
- ),
197
+ builder: (context) {
198
+ final dlgL10n = AppLocalizations.of(context)!;
199
+ return AlertDialog(
200
+ title: Text(dlgL10n.delete_server_title),
201
+ content: Text(dlgL10n.delete_server_body(server.name)),
202
+ actions: [
203
+ TextButton(
204
+ onPressed: () => Navigator.pop(context),
205
+ child: Text(dlgL10n.cancel),
206
+ ),
207
+ TextButton(
208
+ onPressed: () {
209
+ ref.read(serversProvider.notifier).deleteServer(server.id);
210
+ Navigator.pop(context);
211
+ },
212
+ style: TextButton.styleFrom(foregroundColor: Colors.red),
213
+ child: Text(dlgL10n.delete),
214
+ ),
215
+ ],
216
+ );
217
+ },
218
);
219
}
220
}
lib/features/settings/data/models/app_settings.dart
+8
@@ -36,6 +36,7 @@ class AppSettings {
36
final double ttsSpeed;
37
final KittenTtsModelVariant kittenTtsModelVariant;
38
final bool autoSpeakEnabled;
39
+ final String? localeCode;
40
41
AppSettings({
42
this.temperature = 0.7,
@@ -64,6 +65,7 @@ class AppSettings {
65
this.ttsSpeed = 1.0,
66
this.kittenTtsModelVariant = KittenTtsModelVariant.nanoInt8,
67
this.autoSpeakEnabled = false,
68
+ this.localeCode,
69
});
70
71
AppSettings copyWith({
@@ -93,6 +95,7 @@ class AppSettings {
95
double? ttsSpeed,
96
KittenTtsModelVariant? kittenTtsModelVariant,
97
bool? autoSpeakEnabled,
98
+ Object? localeCode = _unset,
99
}) {
100
return AppSettings(
101
temperature: temperature ?? this.temperature,
@@ -127,6 +130,9 @@ class AppSettings {
130
kittenTtsModelVariant:
131
kittenTtsModelVariant ?? this.kittenTtsModelVariant,
132
autoSpeakEnabled: autoSpeakEnabled ?? this.autoSpeakEnabled,
133
+ localeCode: identical(localeCode, _unset)
134
+ ? this.localeCode
135
+ : localeCode as String?,
136
);
137
}
138
@@ -158,6 +164,7 @@ class AppSettings {
164
'ttsSpeed': ttsSpeed,
165
'kittenTtsModelVariant': kittenTtsModelVariant.name,
166
'autoSpeakEnabled': autoSpeakEnabled,
167
+ 'localeCode': localeCode,
168
};
169
}
170
@@ -189,6 +196,7 @@ class AppSettings {
196
ttsSpeed: map['ttsSpeed']?.toDouble() ?? 1.0,
197
kittenTtsModelVariant: _parseKittenVariant(map['kittenTtsModelVariant']),
198
autoSpeakEnabled: map['autoSpeakEnabled'] ?? false,
199
+ localeCode: map['localeCode'] as String?,
200
);
201
}
202
lib/features/settings/views/settings_screen.dart
+204
-88
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
import 'package:localmind/features/sidebar/sidebar_widget.dart';
5
+import 'package:localmind/l10n/app_localizations.dart';
6
import '../../../core/providers/app_providers.dart';
7
import '../../../core/models/enums.dart';
8
import '../../../core/routes/app_routes.dart';
@@ -17,47 +18,54 @@ class SettingsViews extends ConsumerWidget {
18
19
@override
20
Widget build(BuildContext context, WidgetRef ref) {
21
+ final l10n = AppLocalizations.of(context)!;
22
final settings = ref.watch(settingsProvider);
23
final theme = Theme.of(context);
24
final isDark = theme.brightness == Brightness.dark;
25
26
return Scaffold(
25
- appBar: AppBar(title: const Text('Settings')),
27
+ appBar: AppBar(title: Text(l10n.settings_title)),
28
drawer: SidebarWidget(),
29
body: ListView(
30
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
31
children: [
30
- _SectionHeader(title: 'Appearance'),
32
+ _SectionHeader(title: l10n.settings_appearance),
33
_ThemeToggle(current: settings.themeMode, ref: ref),
34
+ _LanguageSetting(
35
+ current: settings.localeCode,
36
+ onChanged: (v) =>
37
+ ref.read(settingsProvider.notifier).setLocaleCode(v),
38
+ isDark: isDark,
39
+ ),
40
_SliderSetting(
33
- label: 'Font Size',
41
+ label: l10n.font_size,
42
value: settings.fontSize,
43
min: 12.0,
44
max: 24.0,
45
divisions: 12,
38
- description: 'Adjust text size in chat.',
46
+ description: l10n.font_size_desc,
47
onChanged: (v) =>
48
ref.read(settingsProvider.notifier).setFontSize(v),
49
isDark: isDark,
50
valueFormat: (v) => v.toStringAsFixed(0),
43
- previewText: 'The quick brown fox jumps over the lazy dog.',
51
+ previewText: l10n.font_preview,
52
),
53
_CodeThemeDropdown(
46
- label: 'Code Theme (Dark)',
54
+ label: l10n.code_theme_dark,
55
current: settings.codeThemeDark,
56
onChanged: (v) =>
57
ref.read(settingsProvider.notifier).setCodeThemeDark(v),
58
isDark: isDark,
59
),
60
_CodeThemeDropdown(
53
- label: 'Code Theme (Light)',
61
+ label: l10n.code_theme_light,
62
current: settings.codeThemeLight,
63
onChanged: (v) =>
64
ref.read(settingsProvider.notifier).setCodeThemeLight(v),
65
isDark: isDark,
66
),
67
const Divider(height: 32),
60
- _SectionHeader(title: 'Text-to-Speech'),
68
+ _SectionHeader(title: l10n.settings_tts),
69
_EngineDropdown(
70
current: settings.ttsEngine,
71
onChanged: (v) =>
@@ -69,7 +77,7 @@ class SettingsViews extends ConsumerWidget {
77
child: OutlinedButton.icon(
78
onPressed: () => context.push(AppRoutes.ttsModels),
79
icon: const Icon(Icons.record_voice_over, size: 18),
72
- label: const Text('Manage TTS Models'),
80
+ label: Text(l10n.manage_tts_models),
81
style: OutlinedButton.styleFrom(
82
minimumSize: const Size(double.infinity, 44),
83
shape: RoundedRectangleBorder(
@@ -88,12 +96,12 @@ class SettingsViews extends ConsumerWidget {
96
isDark: isDark,
97
),
98
_SliderSetting(
91
- label: 'TTS Speed',
99
+ label: l10n.tts_speed,
100
value: settings.ttsSpeed,
101
min: 0.5,
102
max: 2.0,
103
divisions: 15,
96
- description: 'Adjust the playback rate.',
104
+ description: l10n.tts_speed_desc,
105
onChanged: (v) =>
106
ref.read(settingsProvider.notifier).setTtsSpeed(v),
107
isDark: isDark,
@@ -101,44 +109,44 @@ class SettingsViews extends ConsumerWidget {
109
),
110
],
111
const Divider(height: 32),
104
- _SectionHeader(title: 'Behavior'),
112
+ _SectionHeader(title: l10n.settings_behavior),
113
_ToggleSetting(
106
- label: 'Streaming Responses',
114
+ label: l10n.streaming_responses,
115
value: settings.streamingEnabled,
116
onChanged: (v) =>
117
ref.read(settingsProvider.notifier).setStreamingEnabled(v),
118
isDark: isDark,
119
),
120
_ToggleSetting(
113
- label: 'Auto-generate Titles',
121
+ label: l10n.auto_generate_titles,
122
value: settings.autoGenerateTitle,
123
onChanged: (v) =>
124
ref.read(settingsProvider.notifier).setAutoGenerateTitle(v),
125
isDark: isDark,
126
),
127
_ToggleSetting(
120
- label: 'Send on Enter',
128
+ label: l10n.send_on_enter,
129
value: settings.sendOnEnter,
130
onChanged: (v) =>
131
ref.read(settingsProvider.notifier).setSendOnEnter(v),
132
isDark: isDark,
133
),
134
_ToggleSetting(
127
- label: 'Show System Messages',
135
+ label: l10n.show_system_messages,
136
value: settings.showSystemMessages,
137
onChanged: (v) =>
138
ref.read(settingsProvider.notifier).setShowSystemMessages(v),
139
isDark: isDark,
140
),
141
_ToggleSetting(
134
- label: 'Haptic Feedback',
142
+ label: l10n.haptic_feedback,
143
value: settings.hapticFeedbackEnabled,
144
onChanged: (v) =>
145
ref.read(settingsProvider.notifier).setHapticFeedback(v),
146
isDark: isDark,
147
),
148
_ToggleSetting(
141
- label: 'Enable MCP',
149
+ label: l10n.enable_mcp,
150
value: settings.mcpEnabled,
151
onChanged: (v) =>
152
ref.read(settingsProvider.notifier).setMcpEnabled(v),
@@ -146,20 +154,20 @@ class SettingsViews extends ConsumerWidget {
154
),
155
if (settings.mcpEnabled)
156
_ToggleSetting(
149
- label: 'New Chat MCP Default',
157
+ label: l10n.new_chat_mcp_default,
158
value: settings.newChatMcpEnabled,
159
onChanged: (v) =>
160
ref.read(settingsProvider.notifier).setNewChatMcpEnabled(v),
161
isDark: isDark,
162
),
163
const Divider(height: 32),
156
- _SectionHeader(title: 'On-Device Inference'),
164
+ _SectionHeader(title: l10n.settings_on_device),
165
Padding(
166
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
167
child: OutlinedButton.icon(
168
onPressed: () => context.push(AppRoutes.onDeviceModels),
169
icon: const Icon(Icons.phone_android, size: 18),
162
- label: const Text('Manage On-Device Models'),
170
+ label: Text(l10n.manage_on_device_models),
171
style: OutlinedButton.styleFrom(
172
minimumSize: const Size(double.infinity, 44),
173
shape: RoundedRectangleBorder(
@@ -170,9 +178,9 @@ class SettingsViews extends ConsumerWidget {
178
),
179
const _OnDeviceOnDeviceEngineStatus(),
180
const Divider(height: 32),
173
- _SectionHeader(title: 'Default Server'),
181
+ _SectionHeader(title: l10n.settings_default_server),
182
_DropdownSetting(
175
- label: 'Default Server',
183
+ label: l10n.settings_default_server,
184
currentValue: settings.defaultServerId,
185
items: (ref.watch(serversProvider).value ?? [])
186
.map((s) => (s.id, s.name))
@@ -183,9 +191,9 @@ class SettingsViews extends ConsumerWidget {
191
icon: Icons.computer,
192
),
193
const Divider(height: 32),
186
- _SectionHeader(title: 'Default Persona'),
194
+ _SectionHeader(title: l10n.settings_default_persona),
195
_DropdownSetting(
188
- label: 'Default Persona',
196
+ label: l10n.settings_default_persona,
197
currentValue: settings.defaultPersonaId,
198
items: (ref.watch(personasNotifierProvider).value ?? [])
199
.map((p) => (p.id, '${p.emoji} ${p.name}'))
@@ -196,18 +204,18 @@ class SettingsViews extends ConsumerWidget {
204
icon: Icons.smart_toy_outlined,
205
),
206
const Divider(height: 32),
199
- _SectionHeader(title: 'Privacy'),
207
+ _SectionHeader(title: l10n.settings_privacy),
208
_ToggleSetting(
201
- label: 'Show Data Indicator',
209
+ label: l10n.show_data_indicator,
210
value: settings.showDataIndicator,
211
onChanged: (v) =>
212
ref.read(settingsProvider.notifier).setShowDataIndicator(v),
213
isDark: isDark,
214
),
215
Padding(
208
- padding: const EdgeInsets.only(left: 16, bottom: 8),
216
+ padding: const EdgeInsetsDirectional.only(start: 16, bottom: 8),
217
child: Text(
210
- '"LocalMind never sees your data"',
218
+ l10n.privacy_info,
219
style: TextStyle(
220
fontSize: 12,
221
color: isDark
@@ -218,27 +226,27 @@ class SettingsViews extends ConsumerWidget {
226
),
227
),
228
const Divider(height: 32),
221
- _SectionHeader(title: 'Data Management'),
229
+ _SectionHeader(title: l10n.settings_data_management),
230
_DangerousAction(
223
- label: 'Delete All Conversations',
231
+ label: l10n.delete_all_conversations,
232
icon: Icons.delete_outline,
233
onConfirm: () =>
234
ref.read(conversationsProvider.notifier).deleteAll(),
235
isDark: isDark,
236
),
237
_DangerousAction(
230
- label: 'Reset Settings to Defaults',
238
+ label: l10n.reset_settings_defaults,
239
icon: Icons.restore,
240
onConfirm: () =>
241
ref.read(settingsProvider.notifier).resetToDefaults(),
242
isDark: isDark,
243
),
244
const Divider(height: 32),
237
- _SectionHeader(title: 'About'),
245
+ _SectionHeader(title: l10n.settings_about),
246
Padding(
247
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
248
child: Text(
241
- 'LocalMind v1.0.0',
249
+ '${l10n.app_name} v${l10n.app_version}',
250
style: TextStyle(
251
fontSize: 14,
252
fontWeight: FontWeight.w500,
@@ -247,9 +255,9 @@ class SettingsViews extends ConsumerWidget {
255
),
256
),
257
Padding(
250
- padding: const EdgeInsets.only(left: 16, bottom: 8),
258
+ padding: const EdgeInsetsDirectional.only(start: 16, bottom: 8),
259
child: Text(
252
- 'Your AI. Your Device. Your Rules.',
260
+ l10n.app_tagline,
261
style: TextStyle(
262
fontSize: 13,
263
color: isDark
@@ -287,6 +295,111 @@ class _SectionHeader extends StatelessWidget {
295
}
296
}
297
298
+class _LanguageSetting extends StatelessWidget {
299
+ const _LanguageSetting({
300
+ required this.current,
301
+ required this.onChanged,
302
+ required this.isDark,
303
+ });
304
+
305
+ static const _localeItems = <(String?, String)>[
306
+ ('en', 'English'),
307
+ ('ar', 'العربية'),
308
+ ('bn', 'বাংলা'),
309
+ ('zh', '中文'),
310
+ ('es', 'Español'),
311
+ ('hi', 'हिन्दी'),
312
+ ];
313
+
314
+ final String? current;
315
+ final ValueChanged<String?> onChanged;
316
+ final bool isDark;
317
+
318
+ @override
319
+ Widget build(BuildContext context) {
320
+ final l10n = AppLocalizations.of(context)!;
321
+ return Padding(
322
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
323
+ child: Column(
324
+ crossAxisAlignment: CrossAxisAlignment.start,
325
+ children: [
326
+ Text(
327
+ l10n.settings_language,
328
+ style: TextStyle(
329
+ fontSize: 14,
330
+ fontWeight: FontWeight.w500,
331
+ color: isDark ? Colors.white : Colors.black,
332
+ ),
333
+ ),
334
+ const SizedBox(height: 8),
335
+ Container(
336
+ decoration: BoxDecoration(
337
+ color: isDark ? const Color(0xFF1F1F1F) : const Color(0xFFF5F5F5),
338
+ borderRadius: BorderRadius.circular(8),
339
+ border: Border.all(
340
+ color: isDark
341
+ ? const Color(0xFF3A3A3A)
342
+ : const Color(0xFFE5E5E5),
343
+ ),
344
+ ),
345
+ child: DropdownButtonHideUnderline(
346
+ child: DropdownButton<String?>(
347
+ value: current,
348
+ isExpanded: true,
349
+ padding: const EdgeInsets.symmetric(horizontal: 12),
350
+ borderRadius: BorderRadius.circular(8),
351
+ dropdownColor:
352
+ isDark ? const Color(0xFF1F1F1F) : Colors.white,
353
+ items: [
354
+ DropdownMenuItem(
355
+ value: null,
356
+ child: Row(
357
+ children: [
358
+ Icon(
359
+ Icons.settings_suggest,
360
+ size: 16,
361
+ color: isDark
362
+ ? const Color(0xFF888888)
363
+ : const Color(0xFF999999),
364
+ ),
365
+ const SizedBox(width: 8),
366
+ Text(
367
+ l10n.language_system_default,
368
+ style: TextStyle(
369
+ fontSize: 14,
370
+ color: isDark ? Colors.white : Colors.black,
371
+ ),
372
+ ),
373
+ ],
374
+ ),
375
+ ),
376
+ ..._localeItems.map((item) => DropdownMenuItem(
377
+ value: item.$1,
378
+ child: Row(
379
+ children: [
380
+ const Icon(Icons.language, size: 16),
381
+ const SizedBox(width: 8),
382
+ Text(
383
+ item.$2,
384
+ style: TextStyle(
385
+ fontSize: 14,
386
+ color: isDark ? Colors.white : Colors.black,
387
+ ),
388
+ ),
389
+ ],
390
+ ),
391
+ )),
392
+ ],
393
+ onChanged: onChanged,
394
+ ),
395
+ ),
396
+ ),
397
+ ],
398
+ ),
399
+ );
400
+ }
401
+}
402
+
403
class _SliderSetting extends StatelessWidget {
404
const _SliderSetting({
405
required this.label,
@@ -529,6 +642,7 @@ class _ThemeToggle extends StatelessWidget {
642
643
@override
644
Widget build(BuildContext context) {
645
+ final l10n = AppLocalizations.of(context)!;
646
final isDark = Theme.of(context).brightness == Brightness.dark;
647
final currentType = ref.watch(themeModeProvider);
648
@@ -538,7 +652,7 @@ class _ThemeToggle extends StatelessWidget {
652
crossAxisAlignment: CrossAxisAlignment.start,
653
children: [
654
Text(
541
- 'Theme',
655
+ l10n.theme,
656
style: TextStyle(
657
fontSize: 14,
658
fontWeight: FontWeight.w500,
@@ -549,7 +663,7 @@ class _ThemeToggle extends StatelessWidget {
663
Row(
664
children: [
665
_ThemeOption(
552
- label: 'System',
666
+ label: l10n.theme_system,
667
icon: Icons.brightness_auto,
668
isSelected: currentType == AppThemeType.system,
669
onTap: () => ref
@@ -559,7 +673,7 @@ class _ThemeToggle extends StatelessWidget {
673
),
674
const SizedBox(width: 8),
675
_ThemeOption(
562
- label: 'Light',
676
+ label: l10n.theme_light,
677
icon: Icons.light_mode,
678
isSelected: currentType == AppThemeType.light,
679
onTap: () => ref
@@ -569,7 +683,7 @@ class _ThemeToggle extends StatelessWidget {
683
),
684
const SizedBox(width: 8),
685
_ThemeOption(
572
- label: 'Dark',
686
+ label: l10n.theme_dark,
687
icon: Icons.dark_mode,
688
isSelected: currentType == AppThemeType.dark,
689
onTap: () => ref
@@ -579,7 +693,7 @@ class _ThemeToggle extends StatelessWidget {
693
),
694
const SizedBox(width: 8),
695
_ThemeOption(
582
- label: 'Claude',
696
+ label: l10n.theme_claude,
697
icon: Icons.auto_awesome,
698
isSelected: currentType == AppThemeType.claude,
699
onTap: () => ref
@@ -683,6 +797,7 @@ class _DropdownSetting extends StatelessWidget {
797
798
@override
799
Widget build(BuildContext context) {
800
+ final l10n = AppLocalizations.of(context)!;
801
return Padding(
802
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
803
child: Container(
@@ -701,7 +816,7 @@ class _DropdownSetting extends StatelessWidget {
816
borderRadius: BorderRadius.circular(8),
817
dropdownColor: isDark ? const Color(0xFF1F1F1F) : Colors.white,
818
hint: Text(
704
- 'None selected',
819
+ l10n.none_selected,
820
style: TextStyle(
821
fontSize: 14,
822
color: isDark
@@ -713,7 +828,7 @@ class _DropdownSetting extends StatelessWidget {
828
DropdownMenuItem(
829
value: null,
830
child: Text(
716
- 'None',
831
+ l10n.none,
832
style: TextStyle(
833
fontSize: 14,
834
color: isDark
@@ -768,17 +883,9 @@ class _CodeThemeDropdown extends StatelessWidget {
883
final ValueChanged<SyntaxThemeName> onChanged;
884
final bool isDark;
885
771
- String _getDisplayName(SyntaxThemeName theme) {
772
- switch (theme) {
773
- case SyntaxThemeName.light:
774
- return 'Light';
775
- case SyntaxThemeName.dark:
776
- return 'Dark';
777
- }
778
- }
779
-
886
@override
887
Widget build(BuildContext context) {
888
+ final l10n = AppLocalizations.of(context)!;
889
return Padding(
890
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
891
child: Column(
@@ -811,10 +918,14 @@ class _CodeThemeDropdown extends StatelessWidget {
918
borderRadius: BorderRadius.circular(8),
919
dropdownColor: isDark ? const Color(0xFF1F1F1F) : Colors.white,
920
items: SyntaxThemeName.values.map((theme) {
921
+ final displayName = switch (theme) {
922
+ SyntaxThemeName.light => l10n.theme_light,
923
+ SyntaxThemeName.dark => l10n.theme_dark,
924
+ };
925
return DropdownMenuItem(
926
value: theme,
927
child: Text(
817
- _getDisplayName(theme),
928
+ displayName,
929
style: TextStyle(
930
fontSize: 14,
931
color: isDark ? Colors.white : Colors.black,
@@ -830,7 +941,7 @@ class _CodeThemeDropdown extends StatelessWidget {
941
),
942
const SizedBox(height: 4),
943
Text(
833
- 'Choose syntax highlighting theme for code blocks.',
944
+ l10n.code_theme_desc,
945
style: TextStyle(
946
fontSize: 12,
947
color: isDark ? const Color(0xFF666666) : const Color(0xFF999999),
@@ -857,35 +968,37 @@ class _DangerousAction extends StatelessWidget {
968
969
@override
970
Widget build(BuildContext context) {
971
+ final l10n = AppLocalizations.of(context)!;
972
return Padding(
973
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
974
child: OutlinedButton.icon(
975
onPressed: () {
976
showDialog(
977
context: context,
866
- builder: (ctx) => AlertDialog(
867
- title: Text(label),
868
- content: const Text(
869
- 'Are you sure? This action cannot be undone.',
870
- ),
871
- actions: [
872
- TextButton(
873
- onPressed: () => Navigator.pop(ctx),
874
- child: const Text('Cancel'),
875
- ),
876
- TextButton(
877
- onPressed: () {
878
- Navigator.pop(ctx);
879
- onConfirm();
880
- ScaffoldMessenger.of(
881
- context,
882
- ).showSnackBar(SnackBar(content: Text('$label completed')));
883
- },
884
- style: TextButton.styleFrom(foregroundColor: Colors.red),
885
- child: const Text('Confirm'),
886
- ),
887
- ],
888
- ),
978
+ builder: (ctx) {
979
+ final dlgL10n = AppLocalizations.of(ctx)!;
980
+ return AlertDialog(
981
+ title: Text(label),
982
+ content: Text(dlgL10n.cannot_undo),
983
+ actions: [
984
+ TextButton(
985
+ onPressed: () => Navigator.pop(ctx),
986
+ child: Text(dlgL10n.cancel),
987
+ ),
988
+ TextButton(
989
+ onPressed: () {
990
+ Navigator.pop(ctx);
991
+ onConfirm();
992
+ ScaffoldMessenger.of(
993
+ context,
994
+ ).showSnackBar(SnackBar(content: Text(l10n.label_completed(label))));
995
+ },
996
+ style: TextButton.styleFrom(foregroundColor: Colors.red),
997
+ child: Text(dlgL10n.confirm),
998
+ ),
999
+ ],
1000
+ );
1001
+ },
1002
);
1003
},
1004
icon: Icon(icon, color: Colors.red, size: 18),
@@ -913,13 +1026,14 @@ class _EngineDropdown extends StatelessWidget {
1026
1027
@override
1028
Widget build(BuildContext context) {
1029
+ final l10n = AppLocalizations.of(context)!;
1030
return Padding(
1031
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
1032
child: Column(
1033
crossAxisAlignment: CrossAxisAlignment.start,
1034
children: [
1035
Text(
922
- 'TTS Engine',
1036
+ l10n.tts_engine,
1037
style: TextStyle(
1038
fontSize: 14,
1039
fontWeight: FontWeight.w500,
@@ -947,12 +1061,12 @@ class _EngineDropdown extends StatelessWidget {
1061
items: [
1062
_engineItem(
1063
EngineId.system,
950
- 'System TTS',
1064
+ l10n.tts_engine_system,
1065
Icons.record_voice_over,
1066
),
1067
_engineItem(
1068
EngineId.kitten,
955
- 'Kitten TTS',
1069
+ l10n.tts_engine_kitten,
1070
Icons.auto_awesome,
1071
),
1072
],
@@ -1007,6 +1121,7 @@ class _VoiceSelector extends StatelessWidget {
1121
1122
@override
1123
Widget build(BuildContext context) {
1124
+ final l10n = AppLocalizations.of(context)!;
1125
final voices = voicesForEngine(engine);
1126
final resolvedCurrentVoiceId = voiceFromSettings(
1127
currentVoiceId,
@@ -1027,7 +1142,7 @@ class _VoiceSelector extends StatelessWidget {
1142
crossAxisAlignment: CrossAxisAlignment.start,
1143
children: [
1144
Text(
1030
- 'Voice',
1145
+ l10n.voice,
1146
style: TextStyle(
1147
fontSize: 14,
1148
fontWeight: FontWeight.w500,
@@ -1037,7 +1152,7 @@ class _VoiceSelector extends StatelessWidget {
1152
const SizedBox(height: 8),
1153
if (femaleVoices.isNotEmpty) ...[
1154
Text(
1040
- 'Female',
1155
+ l10n.voice_female,
1156
style: TextStyle(
1157
fontSize: 11,
1158
color: isDark
@@ -1058,7 +1173,7 @@ class _VoiceSelector extends StatelessWidget {
1173
],
1174
if (maleVoices.isNotEmpty) ...[
1175
Text(
1061
- 'Male',
1176
+ l10n.voice_male,
1177
style: TextStyle(
1178
fontSize: 11,
1179
color: isDark
@@ -1079,7 +1194,7 @@ class _VoiceSelector extends StatelessWidget {
1194
if (otherVoices.isNotEmpty) ...[
1195
const SizedBox(height: 8),
1196
Text(
1082
- 'Other',
1197
+ l10n.voice_other,
1198
style: TextStyle(
1199
fontSize: 11,
1200
color: isDark
@@ -1195,6 +1310,7 @@ class _OnDeviceOnDeviceEngineStatus extends ConsumerWidget {
1310
1311
@override
1312
Widget build(BuildContext context, WidgetRef ref) {
1313
+ final l10n = AppLocalizations.of(context)!;
1314
final engineState = ref.watch(onDeviceEngineProvider);
1315
final isDark = Theme.of(context).brightness == Brightness.dark;
1316
@@ -1202,7 +1318,7 @@ class _OnDeviceOnDeviceEngineStatus extends ConsumerWidget {
1318
return Padding(
1319
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
1320
child: Text(
1205
- 'No model loaded. Tap "Manage On-Device Models" to download and load a model.',
1321
+ l10n.no_model_loaded,
1322
style: TextStyle(
1323
fontSize: 12,
1324
color: isDark ? const Color(0xFF666666) : const Color(0xFF999999),
@@ -1249,10 +1365,10 @@ class _OnDeviceOnDeviceEngineStatus extends ConsumerWidget {
1365
Expanded(
1366
child: Text(
1367
engineState.status == OnDeviceEngineStatus.loaded
1252
- ? 'Loaded: ${engineState.loadedModelId ?? "unknown"} (${engineState.backend?.name ?? "CPU"})'
1368
+ ? l10n.model_loaded(engineState.loadedModelId ?? "unknown", engineState.backend?.name ?? "CPU")
1369
: engineState.status == OnDeviceEngineStatus.loading
1254
- ? 'Loading model...'
1255
- : 'Error: ${engineState.error ?? "Unknown error"}',
1370
+ ? l10n.loading
1371
+ : l10n.error_with_message(engineState.error ?? l10n.unknown_error),
1372
style: TextStyle(
1373
fontSize: 13,
1374
color: engineState.status == OnDeviceEngineStatus.loaded
lib/features/sidebar/components/active_server_indicator.dart
+14
-11
@@ -6,6 +6,7 @@ import 'package:shadcn_ui/shadcn_ui.dart';
6
7
import '../../../core/models/enums.dart';
8
import '../../../core/routes/app_routes.dart';
9
+import '../../../l10n/app_localizations.dart';
10
import '../../servers/providers/server_providers.dart';
11
import '../../servers/views/components/server_icon_picker.dart';
12
@@ -25,21 +26,23 @@ class ActiveServerIndicator extends ConsumerWidget {
26
}
27
}
28
28
- String _getStatusText(ConnectionStatus status) {
29
+ String _getStatusText(BuildContext context, ConnectionStatus status) {
30
+ final l10n = AppLocalizations.of(context)!;
31
switch (status) {
32
case ConnectionStatus.connected:
31
- return 'Online';
33
+ return l10n.online;
34
case ConnectionStatus.error:
33
- return 'Error';
35
+ return l10n.error;
36
case ConnectionStatus.checking:
35
- return 'Checking...';
37
+ return l10n.testing;
38
case ConnectionStatus.disconnected:
37
- return 'Offline';
39
+ return l10n.offline;
40
}
41
}
42
43
@override
44
Widget build(BuildContext context, WidgetRef ref) {
45
+ final l10n = AppLocalizations.of(context)!;
46
final theme = Theme.of(context);
47
final colorScheme = theme.colorScheme;
48
final isDark = theme.brightness == Brightness.dark;
@@ -67,7 +70,7 @@ class ActiveServerIndicator extends ConsumerWidget {
70
const SizedBox(width: 10),
71
Expanded(
72
child: Text(
70
- 'No server selected',
73
+ l10n.no_server_selected,
74
style: theme.textTheme.bodySmall?.copyWith(
75
color: colorScheme.onSurfaceVariant,
76
),
@@ -129,14 +132,14 @@ class ActiveServerIndicator extends ConsumerWidget {
132
crossAxisAlignment: CrossAxisAlignment.start,
133
children: [
134
Text(
132
- 'Switch Server',
135
+ l10n.switch_server,
136
style: theme.textTheme.titleLarge?.copyWith(
137
fontWeight: FontWeight.bold,
138
),
139
),
140
const SizedBox(height: 4),
141
Text(
139
- 'Choose a server to connect to',
142
+ l10n.switch_server_subtitle,
143
style: theme.textTheme.bodyMedium?.copyWith(
144
color: colorScheme.onSurfaceVariant,
145
),
@@ -261,7 +264,7 @@ class ActiveServerIndicator extends ConsumerWidget {
264
),
265
Text(
266
isCurrentlyActive
264
- ? 'Active'
267
+ ? l10n.active
268
: (server.host.isEmpty
269
? 'Local'
270
: server.host),
@@ -310,7 +313,7 @@ class ActiveServerIndicator extends ConsumerWidget {
313
Navigator.pop(context);
314
}
315
},
313
- child: const Text('Manage Servers'),
316
+ child: Text(l10n.manage_servers),
317
),
318
],
319
),
@@ -370,7 +373,7 @@ class ActiveServerIndicator extends ConsumerWidget {
373
),
374
const SizedBox(width: 6),
375
Text(
373
- _getStatusText(connectionStatus),
376
+ _getStatusText(context, connectionStatus),
377
style: theme.textTheme.labelSmall?.copyWith(
378
color: statusColor.withAlpha(200),
379
fontSize: 10,
lib/features/sidebar/components/conversation_drawer_header.dart
+4
-1
@@ -1,11 +1,14 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
4
+import '../../../l10n/app_localizations.dart';
5
+
6
class ConversationDrawerHeader extends ConsumerWidget {
7
const ConversationDrawerHeader({super.key});
8
9
@override
10
Widget build(BuildContext context, WidgetRef ref) {
11
+ final l10n = AppLocalizations.of(context)!;
12
final theme = Theme.of(context);
13
final isDark = theme.brightness == Brightness.dark;
14
@@ -24,7 +27,7 @@ class ConversationDrawerHeader extends ConsumerWidget {
27
),
28
const SizedBox(width: 12),
29
Text(
27
- 'LocalMind',
30
+ l10n.app_name,
31
style: TextStyle(
32
fontSize: 18,
33
fontWeight: FontWeight.bold,
lib/features/sidebar/components/github_repo_card.dart
+6
-3
@@ -3,6 +3,8 @@ import 'package:hugeicons/hugeicons.dart';
3
import 'package:shadcn_ui/shadcn_ui.dart';
4
import 'package:url_launcher/url_launcher.dart';
5
6
+import '../../../l10n/app_localizations.dart';
7
+
8
class GitHubRepoCard extends StatelessWidget {
9
const GitHubRepoCard({super.key});
10
@@ -17,6 +19,7 @@ class GitHubRepoCard extends StatelessWidget {
19
20
@override
21
Widget build(BuildContext context) {
22
+ final l10n = AppLocalizations.of(context)!;
23
final theme = Theme.of(context);
24
final isDark = theme.brightness == Brightness.dark;
25
@@ -41,7 +44,7 @@ class GitHubRepoCard extends StatelessWidget {
44
),
45
const SizedBox(width: 8),
46
Text(
44
- 'Open Source',
47
+ l10n.open_source,
48
style: TextStyle(
49
fontSize: 14,
50
fontWeight: FontWeight.w600,
@@ -53,7 +56,7 @@ class GitHubRepoCard extends StatelessWidget {
56
description: Padding(
57
padding: const EdgeInsets.only(top: 4),
58
child: Text(
56
- 'LocalMind is open source. Follow our progress or contribute on GitHub.',
59
+ l10n.open_source_desc,
60
style: TextStyle(
61
fontSize: 12,
62
color: isDark ? const Color(0xFFA0A0A0) : const Color(0xFF666666),
@@ -71,7 +74,7 @@ class GitHubRepoCard extends StatelessWidget {
74
mainAxisAlignment: MainAxisAlignment.center,
75
children: [
76
Text(
74
- 'Star on GitHub',
77
+ l10n.star_on_github,
78
style: TextStyle(
79
fontSize: 12,
80
fontWeight: FontWeight.w600,
lib/features/sidebar/sidebar_widget.dart
+12
-37
@@ -4,9 +4,8 @@ import 'package:go_router/go_router.dart';
4
import 'package:hugeicons/hugeicons.dart';
5
import 'package:shadcn_ui/shadcn_ui.dart';
6
7
-import '../../core/providers/app_providers.dart';
7
import '../../core/routes/app_routes.dart';
9
-import '../../core/theme/app_theme.dart';
8
+import '../../l10n/app_localizations.dart';
9
import '../chat/providers/chat_providers.dart';
10
import '../tts/views/components/tts_player_bar.dart';
11
import 'components/active_server_indicator.dart';
@@ -20,13 +19,13 @@ class SidebarWidget extends ConsumerWidget {
19
20
@override
21
Widget build(BuildContext context, WidgetRef ref) {
22
+ final l10n = AppLocalizations.of(context)!;
23
final theme = Theme.of(context);
24
final location = GoRouterState.of(context).uri.toString();
25
26
final isHistory = location.startsWith(AppRoutes.chatHistory);
27
final isServers = location.startsWith(AppRoutes.servers);
28
final isPersonas = location.startsWith(AppRoutes.personas);
29
- final themeMode = ref.watch(themeModeProvider);
29
final isLocalModels = location.startsWith(AppRoutes.onDeviceModels);
30
final isTtsModels = location.startsWith(AppRoutes.ttsModels);
31
final isSettings = location.startsWith(AppRoutes.settings);
@@ -35,7 +34,9 @@ class SidebarWidget extends ConsumerWidget {
34
width: 300,
35
decoration: BoxDecoration(
36
color: theme.scaffoldBackgroundColor,
38
- border: Border(right: BorderSide(color: theme.colorScheme.outline)),
37
+ border: Directionality.of(context) == TextDirection.rtl
38
+ ? Border(left: BorderSide(color: theme.colorScheme.outline))
39
+ : Border(right: BorderSide(color: theme.colorScheme.outline)),
40
),
41
child: SafeArea(
42
child: Column(
@@ -55,7 +56,7 @@ class SidebarWidget extends ConsumerWidget {
56
Navigator.pop(context);
57
}
58
},
58
- child: const Text('New Chat'),
59
+ child: Text(l10n.nav_new_chat),
60
),
61
),
62
@@ -71,7 +72,7 @@ class SidebarWidget extends ConsumerWidget {
72
children: [
73
DrawerNavItem(
74
iconData: HugeIcons.strokeRoundedChatting01,
74
- label: 'History',
75
+ label: l10n.nav_history,
76
isSelected: isHistory,
77
onTap: () {
78
if (Scaffold.maybeOf(context)?.isDrawerOpen ?? false) {
@@ -82,7 +83,7 @@ class SidebarWidget extends ConsumerWidget {
83
),
84
DrawerNavItem(
85
iconData: HugeIcons.strokeRoundedServerStack01,
85
- label: 'Servers',
86
+ label: l10n.nav_servers,
87
isSelected: isServers,
88
onTap: () {
89
if (Scaffold.maybeOf(context)?.isDrawerOpen ?? false) {
@@ -93,7 +94,7 @@ class SidebarWidget extends ConsumerWidget {
94
),
95
DrawerNavItem(
96
iconData: HugeIcons.strokeRoundedSmartPhone01,
96
- label: 'Local Models',
97
+ label: l10n.nav_local_models,
98
isSelected: isLocalModels,
99
onTap: () {
100
if (Scaffold.maybeOf(context)?.isDrawerOpen ?? false) {
@@ -104,7 +105,7 @@ class SidebarWidget extends ConsumerWidget {
105
),
106
DrawerNavItem(
107
iconData: HugeIcons.strokeRoundedVoice,
107
- label: 'Text To Speech',
108
+ label: l10n.nav_tts,
109
isSelected: isTtsModels,
110
onTap: () {
111
if (Scaffold.maybeOf(context)?.isDrawerOpen ?? false) {
@@ -115,7 +116,7 @@ class SidebarWidget extends ConsumerWidget {
116
),
117
DrawerNavItem(
118
iconData: HugeIcons.strokeRoundedCompass01,
118
- label: 'Personas',
119
+ label: l10n.nav_personas,
120
isSelected: isPersonas,
121
onTap: () {
122
if (Scaffold.maybeOf(context)?.isDrawerOpen ?? false) {
@@ -129,7 +130,7 @@ class SidebarWidget extends ConsumerWidget {
130
const SizedBox(height: 8),
131
DrawerNavItem(
132
iconData: HugeIcons.strokeRoundedSettings01,
132
- label: 'Settings',
133
+ label: l10n.nav_settings,
134
isSelected: isSettings,
135
onTap: () {
136
if (Scaffold.maybeOf(context)?.isDrawerOpen ?? false) {
@@ -157,30 +158,4 @@ class SidebarWidget extends ConsumerWidget {
158
),
159
);
160
}
160
-
161
- List<List<dynamic>> _getThemeIcon(AppThemeType mode) {
162
- switch (mode) {
163
- case AppThemeType.system:
164
- return HugeIcons.strokeRoundedSettings01;
165
- case AppThemeType.light:
166
- return HugeIcons.strokeRoundedSun01;
167
- case AppThemeType.dark:
168
- return HugeIcons.strokeRoundedMoon02;
169
- case AppThemeType.claude:
170
- return HugeIcons.strokeRoundedPaintBrush02;
171
- }
172
- }
173
-
174
- String _getThemeLabel(AppThemeType mode) {
175
- switch (mode) {
176
- case AppThemeType.system:
177
- return 'System';
178
- case AppThemeType.light:
179
- return 'Light';
180
- case AppThemeType.dark:
181
- return 'Dark';
182
- case AppThemeType.claude:
183
- return 'Claude';
184
- }
185
- }
161
}
lib/features/tts/views/components/tts_player_bar.dart
+5
-3
@@ -1,5 +1,6 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
+import 'package:localmind/l10n/app_localizations.dart';
4
import '../../providers/tts_providers.dart' as tts;
5
6
/// Mini TTS player bar shown when speech is playing or
@@ -14,6 +15,7 @@ class TtsPlayerBar extends ConsumerWidget {
15
16
@override
17
Widget build(BuildContext context, WidgetRef ref) {
18
+ final l10n = AppLocalizations.of(context)!;
19
final ttsState = ref.watch(tts.ttsProvider);
20
ref.listen(tts.ttsProvider, (prev, next) {
21
if (next.error != null && prev?.error != next.error) {
@@ -60,7 +62,7 @@ class TtsPlayerBar extends ConsumerWidget {
62
children: [
63
Text(
64
preview ??
63
- (ttsState.isInitializing ? 'Loading...' : 'Playing'),
65
+ (ttsState.isInitializing ? l10n.loading : 'Playing'),
66
style: TextStyle(
67
fontSize: 12,
68
fontWeight: FontWeight.w500,
@@ -71,7 +73,7 @@ class TtsPlayerBar extends ConsumerWidget {
73
),
74
if (ttsState.isInitializing)
75
Text(
74
- 'Initializing TTS...',
76
+ l10n.initializing,
77
style: TextStyle(
78
fontSize: 10,
79
color: isDark ? Colors.white38 : Colors.black45,
@@ -84,7 +86,7 @@ class TtsPlayerBar extends ConsumerWidget {
86
GestureDetector(
87
onTap: () => ref.read(tts.ttsProvider.notifier).stop(),
88
child: Tooltip(
87
- message: 'Stop',
89
+ message: l10n.stop,
90
child: Container(
91
padding: const EdgeInsets.all(4),
92
decoration: BoxDecoration(
lib/features/tts/views/tts_model_manager_screen.dart
+57
-49
@@ -2,6 +2,7 @@ import 'dart:async';
2
3
import 'package:flutter/material.dart';
4
import 'package:flutter_riverpod/flutter_riverpod.dart';
5
+import 'package:localmind/l10n/app_localizations.dart';
6
import 'package:shadcn_ui/shadcn_ui.dart';
7
8
import '../../../core/models/enums.dart';
@@ -18,11 +19,12 @@ class TtsModelManagerScreen extends ConsumerWidget {
19
20
@override
21
Widget build(BuildContext context, WidgetRef ref) {
22
+ final l10n = AppLocalizations.of(context)!;
23
final settings = ref.watch(settingsProvider);
24
25
return Scaffold(
26
drawer: const SidebarWidget(),
25
- appBar: AppBar(title: const Text('Text To Speech Models')),
27
+ appBar: AppBar(title: Text(l10n.tts_models_title)),
28
body: ListView(
29
padding: const EdgeInsets.all(16),
30
children: [
@@ -47,6 +49,7 @@ class _SystemEngineCard extends ConsumerWidget {
49
50
@override
51
Widget build(BuildContext context, WidgetRef ref) {
52
+ final l10n = AppLocalizations.of(context)!;
53
final meta = EngineMeta.system;
54
55
return _EngineCard(
@@ -54,7 +57,7 @@ class _SystemEngineCard extends ConsumerWidget {
57
meta: meta,
58
engine: EngineId.system,
59
installed: true,
57
- statusText: 'Always available',
60
+ statusText: l10n.always_available,
61
trailing: Row(
62
mainAxisSize: MainAxisSize.min,
63
children: [
@@ -64,7 +67,7 @@ class _SystemEngineCard extends ConsumerWidget {
67
onPressed: () => ref
68
.read(settingsProvider.notifier)
69
.setTtsEngine(EngineId.system),
67
- child: const Text('Select'),
70
+ child: Text(l10n.select),
71
),
72
],
73
),
@@ -76,11 +79,11 @@ class _SystemEngineCard extends ConsumerWidget {
79
class _SystemVoicesList extends ConsumerWidget {
80
@override
81
Widget build(BuildContext context, WidgetRef ref) {
82
+ final l10n = AppLocalizations.of(context)!;
83
return Padding(
84
padding: const EdgeInsets.only(top: 12),
85
child: Text(
82
- 'Uses your device\'s built-in text-to-speech engine.\n'
83
- 'No downloads required. Voice selection uses your device\'s system settings.',
86
+ l10n.tts_system_desc,
87
style: Theme.of(context).textTheme.bodySmall?.copyWith(
88
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
89
),
@@ -103,6 +106,7 @@ class _KittenEngineCard extends ConsumerWidget {
106
107
@override
108
Widget build(BuildContext context, WidgetRef ref) {
109
+ final l10n = AppLocalizations.of(context)!;
110
final settings = ref.watch(settingsProvider);
111
final meta = EngineMeta.kitten;
112
final downloadedAsync = ref.watch(downloadedKittenTtsVariantsProvider);
@@ -123,10 +127,10 @@ class _KittenEngineCard extends ConsumerWidget {
127
engine: EngineId.kitten,
128
installed: isInstalled,
129
statusText: isInstalled
126
- ? 'Installed'
130
+ ? l10n.installed
131
: isDownloading
128
- ? 'Downloading...'
129
- : 'Not installed',
132
+ ? l10n.downloading_status
133
+ : l10n.not_installed,
134
trailing: _buildAction(context, ref, isInstalled, isDownloading, model),
135
child: Column(
136
crossAxisAlignment: CrossAxisAlignment.start,
@@ -134,8 +138,7 @@ class _KittenEngineCard extends ConsumerWidget {
138
Padding(
139
padding: const EdgeInsets.only(top: 12),
140
child: Text(
137
- 'Lightning-fast neural TTS with 8 expressive voices.\n'
138
- 'Requires ${_formatSize(model.totalSizeBytes)} download.',
141
+ l10n.tts_kitten_desc(_formatSize(model.totalSizeBytes)),
142
style: Theme.of(context).textTheme.bodySmall?.copyWith(
143
color: Theme.of(
144
context,
@@ -162,12 +165,13 @@ class _KittenEngineCard extends ConsumerWidget {
165
bool isDownloading,
166
KittenTtsModel model,
167
) {
168
+ final l10n = AppLocalizations.of(context)!;
169
final notifier = ref.read(ttsDownloadProgressProvider.notifier);
170
if (isDownloading) {
171
return ShadButton.outline(
172
size: ShadButtonSize.sm,
173
onPressed: () => notifier.cancelDownload(model.variant),
170
- child: const Text('Cancel'),
174
+ child: Text(l10n.cancel),
175
);
176
}
177
if (isInstalled) {
@@ -180,7 +184,7 @@ class _KittenEngineCard extends ConsumerWidget {
184
onPressed: () => ref
185
.read(settingsProvider.notifier)
186
.setTtsEngine(EngineId.kitten),
183
- child: const Text('Select'),
187
+ child: Text(l10n.select),
188
),
189
const SizedBox(width: 8),
190
ShadIconButton.ghost(
@@ -194,7 +198,7 @@ class _KittenEngineCard extends ConsumerWidget {
198
return ShadButton.outline(
199
size: ShadButtonSize.sm,
200
onPressed: () => notifier.startDownload(model),
197
- child: const Text('Install'),
201
+ child: Text(l10n.install),
202
);
203
}
204
@@ -203,19 +207,18 @@ class _KittenEngineCard extends ConsumerWidget {
207
WidgetRef ref,
208
KittenTtsModel model,
209
) {
210
+ final l10n = AppLocalizations.of(context)!;
211
showDialog(
212
context: context,
213
builder: (context) => AlertDialog(
209
- title: const Text('Delete Model'),
214
+ title: Text(l10n.delete_model_title),
215
content: Text(
211
- 'Are you sure you want to delete ${model.displayName}? This will '
212
- 'free up approximately ${_formatSize(model.totalSizeBytes)} of '
213
- 'space.\n\nYou can download this model again later if needed.',
216
+ l10n.delete_model_body_with_size(model.displayName, _formatSize(model.totalSizeBytes)),
217
),
218
actions: [
219
TextButton(
220
onPressed: () => Navigator.of(context).pop(),
218
- child: const Text('Cancel'),
221
+ child: Text(l10n.cancel),
222
),
223
TextButton(
224
onPressed: () {
@@ -225,7 +228,7 @@ class _KittenEngineCard extends ConsumerWidget {
228
Navigator.of(context).pop();
229
},
230
style: TextButton.styleFrom(foregroundColor: Colors.red),
228
- child: const Text('Delete'),
231
+ child: Text(l10n.delete),
232
),
233
],
234
),
@@ -302,6 +305,7 @@ class _PiperEngineCard extends ConsumerWidget {
305
306
@override
307
Widget build(BuildContext context, WidgetRef ref) {
308
+ final l10n = AppLocalizations.of(context)!;
309
final settings = ref.watch(settingsProvider);
310
final meta = EngineMeta.piper;
311
final downloadedAsync = ref.watch(downloadedPiperTtsVariantsProvider);
@@ -332,10 +336,10 @@ class _PiperEngineCard extends ConsumerWidget {
336
engine: EngineId.piper,
337
installed: installedVoices.isNotEmpty,
338
statusText: isInstalled
335
- ? 'Installed'
339
+ ? l10n.installed
340
: isDownloading
337
- ? 'Downloading...'
338
- : 'Not installed',
341
+ ? l10n.downloading_status
342
+ : l10n.not_installed,
343
trailing: _buildAction(
344
context,
345
ref,
@@ -350,8 +354,7 @@ class _PiperEngineCard extends ConsumerWidget {
354
Padding(
355
padding: const EdgeInsets.only(top: 12),
356
child: Text(
353
- 'Fast offline Piper voices with 2 expressive voices.\n'
354
- 'Requires ${_formatSize(selectedVariant.totalSizeBytes)} download per voice.',
357
+ l10n.tts_piper_desc(_formatSize(selectedVariant.totalSizeBytes)),
358
style: Theme.of(context).textTheme.bodySmall?.copyWith(
359
color: Theme.of(
360
context,
@@ -405,12 +408,13 @@ class _PiperEngineCard extends ConsumerWidget {
408
bool isInstalled,
409
bool isDownloading,
410
) {
411
+ final l10n = AppLocalizations.of(context)!;
412
final notifier = ref.read(piperTtsDownloadProgressProvider.notifier);
413
if (isDownloading) {
414
return ShadButton.outline(
415
size: ShadButtonSize.sm,
416
onPressed: () => notifier.cancelDownload(variant),
413
- child: const Text('Cancel'),
417
+ child: Text(l10n.cancel),
418
);
419
}
420
if (isInstalled) {
@@ -426,7 +430,7 @@ class _PiperEngineCard extends ConsumerWidget {
430
.read(settingsProvider.notifier)
431
.setTtsEngine(EngineId.piper);
432
},
429
- child: const Text('Select'),
433
+ child: Text(l10n.select),
434
),
435
const SizedBox(width: 8),
436
ShadIconButton.ghost(
@@ -440,7 +444,7 @@ class _PiperEngineCard extends ConsumerWidget {
444
return ShadButton.outline(
445
size: ShadButtonSize.sm,
446
onPressed: () => notifier.startDownload(variant),
443
- child: const Text('Install'),
447
+ child: Text(l10n.install),
448
);
449
}
450
@@ -449,19 +453,18 @@ class _PiperEngineCard extends ConsumerWidget {
453
WidgetRef ref,
454
PiperTtsModelVariant variant,
455
) {
456
+ final l10n = AppLocalizations.of(context)!;
457
showDialog(
458
context: context,
459
builder: (context) => AlertDialog(
455
- title: const Text('Delete Voice'),
460
+ title: Text(l10n.delete_voice_title),
461
content: Text(
457
- 'Are you sure you want to delete ${variant.displayName}? This will '
458
- 'free up approximately ${_formatSize(variant.totalSizeBytes)} of '
459
- 'space.\n\nYou can download this voice again later if needed.',
462
+ l10n.delete_voice_body(variant.displayName, _formatSize(variant.totalSizeBytes)),
463
),
464
actions: [
465
TextButton(
466
onPressed: () => Navigator.of(context).pop(),
464
- child: const Text('Cancel'),
467
+ child: Text(l10n.cancel),
468
),
469
TextButton(
470
onPressed: () {
@@ -471,7 +474,7 @@ class _PiperEngineCard extends ConsumerWidget {
474
Navigator.of(context).pop();
475
},
476
style: TextButton.styleFrom(foregroundColor: Colors.red),
474
- child: const Text('Delete'),
477
+ child: Text(l10n.delete),
478
),
479
],
480
),
@@ -546,6 +549,7 @@ class _PiperVariantChip extends StatelessWidget {
549
550
@override
551
Widget build(BuildContext context) {
552
+ final l10n = AppLocalizations.of(context)!;
553
final theme = Theme.of(context);
554
final accent = Color(EngineMeta.piper.accentColor);
555
@@ -581,7 +585,7 @@ class _PiperVariantChip extends StatelessWidget {
585
),
586
const SizedBox(width: 6),
587
Text(
584
- variant.id.contains('ryan') ? 'Male' : 'Female',
588
+ variant.id.contains('ryan') ? l10n.voice_male : l10n.voice_female,
589
style: theme.textTheme.labelSmall?.copyWith(
590
color: theme.colorScheme.onSurface.withValues(alpha: 0.5),
591
fontWeight: FontWeight.w500,
@@ -621,6 +625,7 @@ class _EngineCard extends StatelessWidget {
625
626
@override
627
Widget build(BuildContext context) {
628
+ final l10n = AppLocalizations.of(context)!;
629
final theme = Theme.of(context);
630
final accentColor = Color(meta.accentColor);
631
@@ -692,7 +697,7 @@ class _EngineCard extends StatelessWidget {
697
),
698
const SizedBox(height: 2),
699
Text(
695
- '${meta.sizeMb} MB · ${meta.ramMb} MB RAM · ${meta.voiceCount} voices',
700
+ l10n.engine_spec(meta.sizeMb.toString(), meta.ramMb.toString(), meta.voiceCount),
701
style: theme.textTheme.labelSmall?.copyWith(
702
color: theme.colorScheme.onSurface.withValues(alpha: 0.4),
703
fontSize: 10,
@@ -719,7 +724,7 @@ class _EngineCard extends StatelessWidget {
724
),
725
),
726
child: Text(
722
- 'Active',
727
+ l10n.active,
728
style: theme.textTheme.labelSmall?.copyWith(
729
color: accentColor,
730
fontWeight: FontWeight.bold,
@@ -751,6 +756,7 @@ class _VoiceChipsState extends ConsumerState<_VoiceChips> {
756
757
@override
758
Widget build(BuildContext context) {
759
+ final l10n = AppLocalizations.of(context)!;
760
final theme = Theme.of(context);
761
final settings = ref.watch(settingsProvider);
762
final selectedVoiceId = widget.engine == settings.ttsEngine
@@ -767,7 +773,7 @@ class _VoiceChipsState extends ConsumerState<_VoiceChips> {
773
children: [
774
if (males.isNotEmpty) ...[
775
Text(
770
- 'Male',
776
+ l10n.voice_male,
777
style: theme.textTheme.labelSmall?.copyWith(
778
color: theme.colorScheme.onSurface.withValues(alpha: 0.5),
779
),
@@ -779,8 +785,8 @@ class _VoiceChipsState extends ConsumerState<_VoiceChips> {
785
children: males
786
.map(
787
(v) => Padding(
782
- padding: const EdgeInsets.only(right: 8),
783
- child: _voiceChip(v, theme, accent, selectedVoiceId),
788
+ padding: const EdgeInsetsDirectional.only(end: 8),
789
+ child: _voiceChip(context, v, theme, accent, selectedVoiceId),
790
),
791
)
792
.toList(),
@@ -790,7 +796,7 @@ class _VoiceChipsState extends ConsumerState<_VoiceChips> {
796
],
797
if (females.isNotEmpty) ...[
798
Text(
793
- 'Female',
799
+ l10n.voice_female,
800
style: theme.textTheme.labelSmall?.copyWith(
801
color: theme.colorScheme.onSurface.withValues(alpha: 0.5),
802
),
@@ -802,8 +808,8 @@ class _VoiceChipsState extends ConsumerState<_VoiceChips> {
808
children: females
809
.map(
810
(v) => Padding(
805
- padding: const EdgeInsets.only(right: 8),
806
- child: _voiceChip(v, theme, accent, selectedVoiceId),
811
+ padding: const EdgeInsetsDirectional.only(end: 8),
812
+ child: _voiceChip(context, v, theme, accent, selectedVoiceId),
813
),
814
)
815
.toList(),
@@ -816,6 +822,7 @@ class _VoiceChipsState extends ConsumerState<_VoiceChips> {
822
}
823
824
Widget _voiceChip(
825
+ BuildContext context,
826
Voice voice,
827
ThemeData theme,
828
Color accent,
@@ -848,13 +855,13 @@ class _VoiceChipsState extends ConsumerState<_VoiceChips> {
855
children: [
856
InkWell(
857
onTap: () => _selectVoice(voice),
851
- borderRadius: const BorderRadius.horizontal(
852
- left: Radius.circular(20),
853
- ),
858
+ borderRadius: BorderRadiusDirectional.horizontal(
859
+ start: const Radius.circular(20),
860
+ ).resolve(Directionality.of(context)),
861
child: Padding(
855
- padding: const EdgeInsets.only(
856
- left: 12,
857
- right: 8,
862
+ padding: const EdgeInsetsDirectional.only(
863
+ start: 12,
864
+ end: 8,
865
top: 8,
866
bottom: 8,
867
),
@@ -924,6 +931,7 @@ class _VoiceChipsState extends ConsumerState<_VoiceChips> {
931
}
932
933
Future<void> _previewVoice(Voice voice) async {
934
+ final l10n = AppLocalizations.of(context)!;
935
if (_playingVoice == voice) {
936
await ref.read(tts.ttsProvider.notifier).stop();
937
if (mounted) setState(() => _playingVoice = null);
@@ -939,7 +947,7 @@ class _VoiceChipsState extends ConsumerState<_VoiceChips> {
947
if (mounted) {
948
ScaffoldMessenger.of(
949
context,
942
- ).showSnackBar(SnackBar(content: Text('Preview failed: $e')));
950
+ ).showSnackBar(SnackBar(content: Text(l10n.preview_failed(e.toString()))));
951
}
952
}
953
}
lib/l10n/app_ar.arb
new
+928
@@ -0,0 +1,928 @@
1
+{
2
+ "@@locale": "ar",
3
+
4
+ "app_name": "LocalMind",
5
+
6
+ "app_tagline": "ذكاؤك. جهازك. قواعدك.",
7
+
8
+ "app_version": "1.0.0",
9
+
10
+ "cancel": "إلغاء",
11
+
12
+ "confirm": "تأكيد",
13
+
14
+ "delete": "حذف",
15
+
16
+ "save": "حفظ",
17
+
18
+ "retry": "إعادة المحاولة",
19
+
20
+ "close": "إغلاق",
21
+
22
+ "done": "تم",
23
+
24
+ "continue_action": "متابعة",
25
+
26
+ "skip": "تخطي",
27
+
28
+ "install": "تثبيت",
29
+
30
+ "download": "تنزيل",
31
+
32
+ "resume": "استئناف",
33
+
34
+ "pause": "إيقاف مؤقت",
35
+
36
+ "stop": "إيقاف",
37
+
38
+ "edit": "تعديل",
39
+
40
+ "preview": "معاينة",
41
+
42
+ "unload": "تفريغ",
43
+
44
+ "load": "تحميل",
45
+
46
+ "rename": "إعادة تسمية",
47
+
48
+ "pin": "تثبيت",
49
+
50
+ "unpin": "إلغاء التثبيت",
51
+
52
+ "share": "مشاركة",
53
+
54
+ "copy": "نسخ",
55
+
56
+ "copied": "تم النسخ!",
57
+
58
+ "copied_to_clipboard": "تم النسخ إلى الحافظة",
59
+
60
+ "select": "اختيار",
61
+
62
+ "active": "نشط",
63
+
64
+ "all": "الكل",
65
+
66
+ "none": "لا شيء",
67
+
68
+ "none_selected": "لم يتم اختيار شيء",
69
+
70
+ "online": "متصل",
71
+
72
+ "offline": "غير متصل",
73
+
74
+ "error": "خطأ",
75
+
76
+ "unknown_error": "خطأ غير معروف",
77
+
78
+ "not_now": "ليس الآن",
79
+
80
+ "enable": "تفعيل",
81
+
82
+ "proceed_anyway": "متابعة على أي حال",
83
+
84
+ "test_connection": "اختبار الاتصال",
85
+
86
+ "testing": "جارٍ الاختبار...",
87
+
88
+ "connection_successful": "تم الاتصال بنجاح!",
89
+
90
+ "connection_failed": "فشل الاتصال. تحقق من إعداداتك.",
91
+
92
+ "save_continue": "حفظ ومتابعة",
93
+
94
+ "save_changes": "حفظ التغييرات",
95
+
96
+ "finish_setup": "إنهاء الإعداد",
97
+
98
+ "start_new_chat": "بدء محادثة جديدة",
99
+
100
+ "cannot_undo": "لا يمكن التراجع عن هذا الإجراء.",
101
+
102
+ "ram_warning": "تحذير الذاكرة",
103
+
104
+ "recommended": "موصى به",
105
+
106
+ "may_be_large": "قد يكون كبيراً جداً لهذا الجهاز",
107
+
108
+ "calculating": "جارٍ الحساب...",
109
+
110
+ "download_failed": "فشل التنزيل",
111
+
112
+ "downloaded": "تم التنزيل",
113
+
114
+ "not_downloaded": "لم يتم التنزيل",
115
+
116
+ "installed": "مثبت",
117
+
118
+ "not_installed": "غير مثبت",
119
+
120
+ "loading": "جارٍ التحميل...",
121
+
122
+ "thinking": "تفكير",
123
+
124
+ "processing": "جارٍ المعالجة...",
125
+
126
+ "initializing": "جارٍ التهيئة...",
127
+
128
+ "ready": "جاهز",
129
+
130
+ "preparing_app": "جارٍ تجهيز التطبيق...",
131
+
132
+ "initializing_services": "جارٍ تهيئة الخدمات...",
133
+
134
+ "configuring_server": "جارٍ إعداد الخادم...",
135
+
136
+ "startup_failed": "فشل بدء التشغيل",
137
+
138
+ "something_went_wrong": "حدث خطأ ما",
139
+
140
+ "delete_model_title": "حذف النموذج",
141
+
142
+ "delete_model_body": "هل أنت متأكد من حذف {name}؟",
143
+
144
+ "delete_model_body_with_size": "هل أنت متأكد من حذف {name}؟ سيؤدي ذلك إلى تحرير مساحة تقارب {size}.\n\nيمكنك تنزيل هذا النموذج مرة أخرى لاحقاً إذا احتجت إليه.",
145
+
146
+ "delete_voice_title": "حذف الصوت",
147
+
148
+ "delete_voice_body": "هل أنت متأكد من حذف {name}؟ سيؤدي ذلك إلى تحرير مساحة تقارب {size}.\n\nيمكنك تنزيل هذا الصوت مرة أخرى لاحقاً إذا احتجت إليه.",
149
+
150
+ "delete_server_title": "حذف الخادم",
151
+
152
+ "delete_server_body": "هل أنت متأكد من حذف \"{name}\"؟ لا يمكن التراجع عن هذا.",
153
+
154
+ "delete_conversation_title": "حذف المحادثة؟",
155
+
156
+ "delete_conversation_body": "هل أنت متأكد من حذف \"{title}\"؟ لا يمكن التراجع عن هذا.",
157
+
158
+ "delete_message_title": "حذف الرسالة؟",
159
+
160
+ "delete_persona_title": "حذف \"{name}\"؟",
161
+
162
+ "delete_persona_body": "لا يمكن التراجع عن هذا.",
163
+
164
+ "clear_conversation_title": "مسح المحادثة؟",
165
+
166
+ "clear_conversation_body": "سيؤدي هذا إلى حذف جميع الرسائل في هذه المحادثة.",
167
+
168
+ "clear": "مسح",
169
+
170
+ "label_completed": "اكتمل {label}",
171
+
172
+ "error_with_message": "خطأ: {error}",
173
+
174
+ "preview_failed": "فشلت المعاينة: {error}",
175
+
176
+ "loading_model": "جارٍ تحميل {modelId}...",
177
+
178
+ "model_loaded": "تم تحميل النموذج: {modelId} ({backend})",
179
+
180
+ "no_model_loaded": "لم يتم تحميل أي نموذج. اضغط على \"إدارة النماذج المحلية\" لتنزيل وتحميل نموذج.",
181
+
182
+ "loading_model_error": "خطأ: {error}",
183
+
184
+ "delete_conversation": "حذف المحادثة؟",
185
+
186
+ "nav_history": "السجل",
187
+
188
+ "nav_servers": "الخوادم",
189
+
190
+ "nav_local_models": "النماذج المحلية",
191
+
192
+ "nav_tts": "تحويل النص إلى كلام",
193
+
194
+ "nav_personas": "الشخصيات",
195
+
196
+ "nav_settings": "الإعدادات",
197
+
198
+ "nav_new_chat": "محادثة جديدة",
199
+
200
+ "search_hint": "البحث في المحادثات...",
201
+
202
+ "no_server_selected": "لم يتم تحديد خادم",
203
+
204
+ "switch_server": "تغيير الخادم",
205
+
206
+ "switch_server_subtitle": "اختر خادماً للاتصال به",
207
+
208
+ "manage_servers": "إدارة الخوادم",
209
+
210
+ "open_source": "مفتوح المصدر",
211
+
212
+ "open_source_desc": "LocalMind هو تطبيق مفتوح المصدر. تابع تقدمنا أو ساهم على GitHub.",
213
+
214
+ "star_on_github": "نجمة على GitHub",
215
+
216
+ "settings_title": "الإعدادات",
217
+
218
+ "settings_appearance": "المظهر",
219
+
220
+ "settings_language": "اللغة",
221
+
222
+ "language_system_default": "إعدادات النظام",
223
+
224
+ "settings_tts": "تحويل النص إلى كلام",
225
+
226
+ "settings_behavior": "السلوك",
227
+
228
+ "settings_on_device": "الاستدلال المحلي",
229
+
230
+ "settings_default_server": "الخادم الافتراضي",
231
+
232
+ "settings_default_persona": "الشخصية الافتراضية",
233
+
234
+ "settings_privacy": "الخصوصية",
235
+
236
+ "settings_data_management": "إدارة البيانات",
237
+
238
+ "settings_about": "حول",
239
+
240
+ "theme": "السمة",
241
+
242
+ "theme_system": "النظام",
243
+
244
+ "theme_light": "فاتح",
245
+
246
+ "theme_dark": "داكن",
247
+
248
+ "theme_claude": "Claude",
249
+
250
+ "font_size": "حجم الخط",
251
+
252
+ "font_size_desc": "ضبط حجم النص في المحادثة.",
253
+
254
+ "font_preview": "النص التجريبي لمعاينة حجم الخط الجديد.",
255
+
256
+ "code_theme_dark": "سمة الكود (داكن)",
257
+
258
+ "code_theme_light": "سمة الكود (فاتح)",
259
+
260
+ "code_theme_desc": "اختر سمة تمييز الصياغة لكتل الأكواد.",
261
+
262
+ "tts_engine": "محرك تحويل النص إلى كلام",
263
+
264
+ "tts_engine_system": "محرك النظام",
265
+
266
+ "tts_engine_kitten": "Kitten TTS",
267
+
268
+ "voice": "الصوت",
269
+
270
+ "voice_female": "أنثى",
271
+
272
+ "voice_male": "ذكر",
273
+
274
+ "voice_other": "آخر",
275
+
276
+ "tts_speed": "سرعة القراءة",
277
+
278
+ "tts_speed_desc": "ضبط معدل التشغيل.",
279
+
280
+ "manage_tts_models": "إدارة نماذج TTS",
281
+
282
+ "manage_on_device_models": "إدارة النماذج المحلية",
283
+
284
+ "streaming_responses": "الردود المتدفقة",
285
+
286
+ "auto_generate_titles": "إنشاء العناوين تلقائياً",
287
+
288
+ "send_on_enter": "إرسال عند الضغط على Enter",
289
+
290
+ "show_system_messages": "إظهار رسائل النظام",
291
+
292
+ "haptic_feedback": "الاستجابة اللمسية",
293
+
294
+ "enable_mcp": "تفعيل MCP",
295
+
296
+ "new_chat_mcp_default": "تفعيل MCP افتراضياً في المحادثات الجديدة",
297
+
298
+ "show_data_indicator": "إظهار مؤشر البيانات",
299
+
300
+ "privacy_info": "\"LocalMind لا يطلع على بياناتك أبداً\"",
301
+
302
+ "delete_all_conversations": "حذف جميع المحادثات",
303
+
304
+ "reset_settings_defaults": "إعادة الإعدادات إلى الوضع الافتراضي",
305
+
306
+ "chat_title": "LocalMind",
307
+
308
+ "chat_parameters_tooltip": "معلمات المحادثة",
309
+
310
+ "change_persona": "تغيير الشخصية",
311
+
312
+ "set_persona": "تعيين شخصية",
313
+
314
+ "remove_persona": "إزالة الشخصية",
315
+
316
+ "clear_conversation": "مسح المحادثة",
317
+
318
+ "connection_error": "خطأ في الاتصال. تحقق من الخادم الخاص بك.",
319
+
320
+ "disconnected": "تم قطع الاتصال بالخادم.",
321
+
322
+ "configure": "إعداد",
323
+
324
+ "select_model": "اختيار نموذج",
325
+
326
+ "select_persona": "اختيار شخصية",
327
+
328
+ "start_conversation": "ابدأ محادثة",
329
+
330
+ "recent_chats": "المحادثات الأخيرة",
331
+
332
+ "see_all": "عرض الكل",
333
+
334
+ "quick_write": "ساعدني في كتابة دالة",
335
+
336
+ "quick_explain": "اشرح هذا الكود",
337
+
338
+ "quick_debug": "صحح لي هذا الخطأ",
339
+
340
+ "quick_async": "كيف أستخدم async/await؟",
341
+
342
+ "history_missing_title": "السجل مفقود",
343
+
344
+ "history_missing_desc": "إما أن رسائل هذه المحادثة قد حُذفت أو أن سجل المحادثة تالف.",
345
+
346
+ "technical_details": "التفاصيل التقنية",
347
+
348
+ "last_error": "آخر خطأ:",
349
+
350
+ "copy_info": "نسخ المعلومات",
351
+
352
+ "conversation_id": "معرف المحادثة",
353
+
354
+ "created_at": "تاريخ الإنشاء",
355
+
356
+ "expected_messages": "الرسائل المتوقعة",
357
+
358
+ "debug_dialog_desc": "معلومات تشخيصية للمساعدة في تحديد مشاكل المزامنة.",
359
+
360
+ "chat_input_hint": "اسأل أي شيء",
361
+
362
+ "send_message_tooltip": "إرسال الرسالة",
363
+
364
+ "stop_generation_tooltip": "إيقاف التوليد",
365
+
366
+ "attach_images_tooltip": "إرفاق صور",
367
+
368
+ "tool_label": "أداة: {toolCallId}",
369
+
370
+ "tool_unknown": "أداة: غير معروفة",
371
+
372
+ "message_options": "خيارات الرسالة",
373
+
374
+ "copy_markdown": "نسخ كـ Markdown",
375
+
376
+ "copied_markdown": "تم النسخ كـ Markdown",
377
+
378
+ "read_aloud": "القراءة بصوت عالٍ",
379
+
380
+ "stop_reading": "إيقاف القراءة",
381
+
382
+ "more": "المزيد",
383
+
384
+ "character_count": "{length} حرف",
385
+
386
+ "edit_message": "تعديل الرسالة",
387
+
388
+ "edit_message_desc": "سيؤدي الحفظ إلى إزالة رد المساعد أدناه وإعادة التوليد.",
389
+
390
+ "save_regenerate": "حفظ وإعادة التوليد",
391
+
392
+ "chat_settings_title": "إعدادات المحادثة",
393
+
394
+ "reset_defaults": "إعادة الإعدادات الافتراضية",
395
+
396
+ "parameters_tab": "المعلمات",
397
+
398
+ "mcp_tab": "MCP",
399
+
400
+ "temperature": "درجة الحرارة",
401
+
402
+ "temperature_desc": "التحكم في العشوائية: أعلى = إبداعي، أقل = مركز",
403
+
404
+ "top_p": "Top P",
405
+
406
+ "top_p_desc": "حد أخذ العينات النووي",
407
+
408
+ "max_tokens": "الحد الأقصى للرموز",
409
+
410
+ "max_tokens_desc": "حد الاستجابة",
411
+
412
+ "context_length": "طول السياق",
413
+
414
+ "context_length_desc": "نافذة السجل",
415
+
416
+ "mcp_disabled_warning": "MCP معطل عالمياً. فعّله في الإعدادات لاستخدام هذه الميزات.",
417
+
418
+ "mcp_enable_chat": "تفعيل MCP لهذه المحادثة",
419
+
420
+ "auto_execute_tools": "تشغيل الأدوات تلقائياً",
421
+
422
+ "add_ephemeral_mcp": "إضافة خادم MCP مؤقت",
423
+
424
+ "mcp_label_placeholder": "التسمية",
425
+
426
+ "mcp_url_placeholder": "الرابط (https://...)",
427
+
428
+ "active_integrations": "التكاملات النشطة",
429
+
430
+ "enable_notifications": "تفعيل الإشعارات",
431
+
432
+ "enable_notifications_desc": "احصل على إشعار عند اكتمال تنزيل النماذج.",
433
+
434
+ "chat_history_title": "سجل المحادثات",
435
+
436
+ "conversation_just_now": "الآن",
437
+
438
+ "conversation_minutes_ago": "منذ {minutes} دقيقة",
439
+
440
+ "conversation_hours_ago": "منذ {hours} ساعة",
441
+
442
+ "conversation_yesterday": "أمس",
443
+
444
+ "conversation_days_ago": "منذ {days} يوم",
445
+
446
+ "conversation_date": "{year}/{month}/{day}",
447
+
448
+ "options_tooltip": "خيارات",
449
+
450
+ "no_results_found": "لا توجد نتائج",
451
+
452
+ "no_conversations_yet": "لا توجد محادثات بعد",
453
+
454
+ "try_different_search": "جرّب مصطلح بحث مختلف",
455
+
456
+ "start_new_conversation": "ابدأ محادثة جديدة",
457
+
458
+ "rename_conversation": "إعادة تسمية المحادثة",
459
+
460
+ "enter_new_title": "أدخل عنواناً جديداً",
461
+
462
+ "pinned_section": "مثبت",
463
+
464
+ "today_section": "اليوم",
465
+
466
+ "yesterday_section": "أمس",
467
+
468
+ "previous_7_days": "آخر 7 أيام",
469
+
470
+ "previous_30_days": "آخر 30 يوماً",
471
+
472
+ "older_section": "الأقدم",
473
+
474
+ "onboarding_localmind": "LOCALMIND",
475
+
476
+ "onboarding_connect_server": "اتصل\nبخادمك",
477
+
478
+ "onboarding_connect_desc": "اتصل بـ LM Studio أو Ollama أو\nOpenRouter لبدء تجربة الذكاء\nالاصطناعي الخاصة بك.",
479
+
480
+ "server_type_on_device": "محلي",
481
+
482
+ "server_type_on_device_sub": "لا يحتاج خادماً",
483
+
484
+ "server_type_lm_studio": "LM Studio",
485
+
486
+ "server_type_lm_studio_sub": "API محلي",
487
+
488
+ "server_type_ollama": "Ollama",
489
+
490
+ "server_type_ollama_sub": "محرك أوامر",
491
+
492
+ "server_type_openrouter": "OpenRouter",
493
+
494
+ "server_type_openrouter_sub": "سحابة موحدة",
495
+
496
+ "ready_continue": "جاهز للمتابعة",
497
+
498
+ "waiting_selection": "بانتظار الاختيار",
499
+
500
+ "setup_connection": "إعداد الاتصال",
501
+
502
+ "setup_connection_desc": "قم بإعداد خادم {server} لبدء المحادثة.",
503
+
504
+ "server_name": "اسم الخادم",
505
+
506
+ "name_required": "الاسم مطلوب",
507
+
508
+ "name_max_50": "الحد الأقصى 50 حرفاً",
509
+
510
+ "host_label": "المضيف / عنوان IP",
511
+
512
+ "host_required": "المضيف مطلوب",
513
+
514
+ "port_label": "المنفذ",
515
+
516
+ "port_required": "المنفذ مطلوب",
517
+
518
+ "port_invalid": "يجب أن يكون رقماً",
519
+
520
+ "port_range": "أدخل منفذاً صالحاً (1-65535)",
521
+
522
+ "api_key_required": "مفتاح API *",
523
+
524
+ "api_key_optional": "مفتاح API (اختياري)",
525
+
526
+ "api_key_required_openrouter": "مفتاح API مطلوب لـ OpenRouter",
527
+
528
+ "api_key_format": "مفاتيح OpenRouter API تبدأ بـ sk-",
529
+
530
+ "my_server_hint": "خادمي",
531
+
532
+ "name_length_validation": "يجب ألا يتجاوز الاسم 50 حرفاً",
533
+
534
+ "host_valid": "أدخل اسماً مضيفاً أو عنوان IP صالحاً",
535
+
536
+ "api_key_hint_openrouter": "sk-...",
537
+
538
+ "api_key_hint_generic": "للخوادم الموثقة",
539
+
540
+ "update_server": "تحديث الخادم",
541
+
542
+ "save_server": "حفظ الخادم",
543
+
544
+ "server_updated": "تم تحديث الخادم",
545
+
546
+ "server_added": "تمت إضافة الخادم",
547
+
548
+ "download_model_title": "تنزيل نموذج",
549
+
550
+ "download_model_desc": "اختر نموذجاً للتنزيل.\nسيتم تشغيله محلياً على جهازك.",
551
+
552
+ "on_device_android_only": "الاستدلال المحلي متاح حالياً على Android فقط.",
553
+
554
+ "total_ram": "إجمالي الذاكرة",
555
+
556
+ "available": "متاح",
557
+
558
+ "ram_min_required": "{fileSize} جيجابايت ذاكرة كحد أدنى",
559
+
560
+ "download_progress": "{percent}% • {speed}",
561
+
562
+ "eta_label": "الوقت المتبقي: {eta}",
563
+
564
+ "paused_progress": "متوقف مؤقتاً - {percent}%",
565
+
566
+ "ram_warning_body_download": "هذا النموذج يتطلب {ram} جيجابايت ذاكرة على الأقل، ولكن جهازك يحتوي على {totalMemory}. قد لا يعمل بشكل صحيح أو قد يتسبب في تعطل التطبيق.",
567
+
568
+ "ram_warning_body_load": "جهازك يحتوي على {availableRAM} ذاكرة متاحة، لكن هذا النموذج يوصي بـ {ram} جيجابايت على الأقل. قد يفشل تحميله أو يسبب عدم استقرار.",
569
+
570
+ "choose_theme": "اختر السمة",
571
+
572
+ "choose_theme_desc": "خصص مظهر التطبيق. يمكنك دائماً تغيير هذا لاحقاً في الإعدادات.",
573
+
574
+ "theme_card_system": "النظام",
575
+
576
+ "theme_card_system_sub": "يطابق إعدادات جهازك",
577
+
578
+ "theme_card_light": "فاتح",
579
+
580
+ "theme_card_light_sub": "نظيف ومشرق",
581
+
582
+ "theme_card_dark": "داكن",
583
+
584
+ "theme_card_dark_sub": "مريح للعينين",
585
+
586
+ "theme_card_claude": "Claude",
587
+
588
+ "theme_card_claude_sub": "سمة دافئة بلون الخوخ",
589
+
590
+ "stay_updated": "ابق على اطلاع",
591
+
592
+ "stay_updated_desc": "احصل على إشعارات عند اكتمال تنزيل نماذج الذكاء الاصطناعي أو عند انتهاء المهام الطويلة.",
593
+
594
+ "notification_benefit_downloads": "تقدم تنزيل النماذج",
595
+
596
+ "notification_benefit_completions": "اكتمال التوليد",
597
+
598
+ "notification_benefit_background": "حالة المهام الخلفية",
599
+
600
+ "allow_notifications": "السماح بالإشعارات",
601
+
602
+ "servers_title": "الخوادم",
603
+
604
+ "no_servers_yet": "لا توجد خوادم بعد",
605
+
606
+ "no_servers_desc": "أضف خادمك الأول لبدء المحادثة مع نماذج الذكاء الاصطناعي.",
607
+
608
+ "add_server": "إضافة خادم",
609
+
610
+ "switched_to_server": "تم التبديل إلى {name}",
611
+
612
+ "edit_server": "تعديل الخادم",
613
+
614
+ "add_server_title": "إضافة خادم",
615
+
616
+ "server_type_label": "نوع الخادم",
617
+
618
+ "server_icon_label": "أيقونة الخادم",
619
+
620
+ "default_icon": "الأيقونة الافتراضية",
621
+
622
+ "server_type_lm_studio_display": "LM Studio",
623
+
624
+ "server_type_openai_display": "متوافق مع OpenAI",
625
+
626
+ "server_type_ollama_display": "Ollama",
627
+
628
+ "server_type_openrouter_display": "OpenRouter",
629
+
630
+ "server_type_on_device_display": "محلي",
631
+
632
+ "server_address_openrouter": "openrouter.ai",
633
+
634
+ "server_address_on_device": "استدلال محلي",
635
+
636
+ "server_address_format": "{host}:{port}",
637
+
638
+ "default_badge": "افتراضي",
639
+
640
+ "set_as_default": "تعيين كافتراضي",
641
+
642
+ "select_icon": "اختيار أيقونة",
643
+
644
+ "select_icon_desc": "اختر أيقونة لخادمك",
645
+
646
+ "search_icons_hint": "البحث في الأيقونات...",
647
+
648
+ "server_icon_stack": "Server Stack",
649
+ "server_icon_stack2": "Server Stack 02",
650
+ "server_icon_stack3": "Server Stack 03",
651
+ "server_icon_cloud": "Cloud",
652
+ "server_icon_cloud_server": "Cloud Server",
653
+ "server_icon_mcp": "MCP Server",
654
+ "server_icon_database": "Database",
655
+ "server_icon_database1": "Database 01",
656
+ "server_icon_database2": "Database 02",
657
+ "server_icon_cpu": "CPU",
658
+ "server_icon_chip": "Chip",
659
+ "server_icon_chip2": "Chip 02",
660
+ "server_icon_computer": "Computer",
661
+ "server_icon_laptop": "Laptop",
662
+ "server_icon_terminal": "Computer Terminal",
663
+ "server_icon_code": "Code",
664
+ "server_icon_ai_brain": "AI Brain",
665
+ "server_icon_ai_brain2": "AI Brain 02",
666
+ "server_icon_ai_cloud": "AI Cloud",
667
+ "server_icon_ai_network": "AI Network",
668
+ "server_icon_ai_chat": "AI Chat",
669
+ "server_icon_cellular": "Cellular Network",
670
+ "server_icon_plug1": "Plug 01",
671
+ "server_icon_plug2": "Plug 02",
672
+ "server_icon_bot": "Bot",
673
+ "server_icon_bot2": "Bot 02",
674
+ "server_icon_robotic": "Robotic",
675
+ "server_icon_rocket": "Rocket",
676
+ "server_icon_star": "Star",
677
+ "server_icon_settings1": "Settings 01",
678
+ "server_icon_settings2": "Settings 02",
679
+ "server_icon_home1": "Home 01",
680
+ "server_icon_home2": "Home 02",
681
+ "server_icon_folder1": "Folder 01",
682
+ "server_icon_folder2": "Folder 02",
683
+ "server_icon_file1": "File 01",
684
+ "server_icon_lock": "Lock",
685
+ "server_icon_key": "Key 01",
686
+ "server_icon_link": "Link 01",
687
+ "server_icon_globe": "Globe",
688
+ "server_icon_api": "API",
689
+ "server_icon_arrow_right": "Arrow Right 01",
690
+ "server_icon_check": "Check Circle",
691
+ "server_icon_alert": "Alert Circle",
692
+ "server_icon_info": "Info Circle",
693
+ "server_icon_zap": "Zap",
694
+ "server_icon_cloud_upload": "Cloud Upload",
695
+ "server_icon_cloud_download": "Cloud Download",
696
+ "server_icon_refresh": "Refresh",
697
+ "server_icon_hard_drive": "Hard Drive",
698
+ "server_icon_drive": "Drive",
699
+
700
+ "personas_title": "الشخصيات",
701
+
702
+ "persona_category_general": "عام",
703
+
704
+ "persona_category_coding": "برمجة",
705
+
706
+ "persona_category_education": "تعليم",
707
+
708
+ "persona_category_creative": "إبداعي",
709
+
710
+ "persona_builtin_section": "مضمنة",
711
+
712
+ "persona_my_section": "شخصياتي",
713
+
714
+ "clone_edit": "نسخ وتعديل",
715
+
716
+ "builtin_badge": "مضمنة",
717
+
718
+ "no_personas_found": "لم يتم العثور على شخصيات",
719
+
720
+ "no_personas_desc": "أنشئ شخصيتك الأولى لتخصيص سلوك الذكاء الاصطناعي.",
721
+
722
+ "edit_persona": "تعديل الشخصية",
723
+
724
+ "create_persona": "إنشاء شخصية",
725
+
726
+ "create_persona_button": "إنشاء",
727
+
728
+ "emoji_label": "رمز تعبيري",
729
+
730
+ "name_label": "الاسم",
731
+
732
+ "my_persona_hint": "شخصيتي",
733
+
734
+ "category_label": "الفئة",
735
+
736
+ "description_optional": "الوصف (اختياري)",
737
+
738
+ "description_hint": "ماذا تفعل هذه الشخصية...",
739
+
740
+ "system_prompt": "موجه النظام",
741
+
742
+ "character_count_max": "{currentLen}/4000",
743
+
744
+ "no_prompt_placeholder": "لا يوجد موجه بعد...",
745
+
746
+ "prompt_hint": "أنت مساعد مفيد...",
747
+
748
+ "prompt_required": "موجه النظام مطلوب",
749
+
750
+ "prompt_max_chars": "الحد الأقصى 4000 حرف",
751
+
752
+ "advanced_settings": "الإعدادات المتقدمة",
753
+
754
+ "temperature_label": "درجة الحرارة (0.0-2.0)",
755
+
756
+ "top_p_label": "Top P (0.0-1.0)",
757
+
758
+ "temp_hint": "0.7",
759
+
760
+ "top_p_hint": "0.9",
761
+
762
+ "range_0_2": "0.0-2.0",
763
+
764
+ "range_0_1": "0.0-1.0",
765
+
766
+ "persona_updated": "تم تحديث الشخصية",
767
+
768
+ "persona_created": "تم إنشاء الشخصية",
769
+
770
+ "tts_models_title": "نماذج تحويل النص إلى كلام",
771
+
772
+ "always_available": "متاح دائماً",
773
+
774
+ "tts_system_desc": "يستخدم محرك تحويل النص إلى كلام المدمج في جهازك.\nلا يتطلب أي تنزيلات. اختيار الصوت يستخدم إعدادات نظام جهازك.",
775
+
776
+ "downloading_status": "جارٍ التنزيل...",
777
+
778
+ "tts_kitten_desc": "TTS عصبي فائق السرعة مع 8 أصوات معبرة.\nيتطلب تنزيل {size}.",
779
+
780
+ "tts_piper_desc": "أصوات Piper سريعة دون اتصال مع صوتين معبرين.\nيتطلب تنزيل {size} لكل صوت.",
781
+
782
+ "engine_spec": "{sizeMb} ميجابايت · {ramMb} ميجابايت ذاكرة · {voiceCount} أصوات",
783
+
784
+ "on_device_models_title": "النماذج المحلية",
785
+
786
+ "available_models": "النماذج المتاحة",
787
+
788
+ "device_memory": "ذاكرة الجهاز",
789
+
790
+ "ram_usage": "استخدام الذاكرة",
791
+
792
+ "memory_healthy": "جيد",
793
+
794
+ "memory_critical": "حرج",
795
+
796
+ "memory_low": "منخفض",
797
+
798
+ "ram_used": "{percent}% مستخدم",
799
+
800
+ "available_ram": "الذاكرة المتاحة",
801
+
802
+ "total_capacity": "السعة الإجمالية",
803
+
804
+ "loaded_status": "تم التحميل",
805
+
806
+ "inference_backend": "محطة الاستدلال",
807
+
808
+ "backend_ios_notice": "محطة CPU فقط متاحة على iOS.",
809
+
810
+ "backend_cpu_desc": "يعمل على جميع الأجهزة. الأكثر توافقاً.",
811
+
812
+ "backend_gpu_desc": "تسريع OpenCL. أسرع على الأجهزة المدعومة.",
813
+
814
+ "backend_npu_desc": "NPU من الشركة المصنعة (Qualcomm/MediaTek). أسرع استدلال.",
815
+
816
+ "select_model_title": "اختيار نموذج",
817
+
818
+ "refresh_models": "تحديث النماذج",
819
+
820
+ "search_models_hint": "البحث في النماذج...",
821
+
822
+ "no_server_connected": "لا يوجد خادم متصل",
823
+
824
+ "add_server_first": "أضف خادماً أولاً لرؤية النماذج المتاحة.",
825
+
826
+ "failed_load_models": "فشل تحميل النماذج",
827
+
828
+ "no_models_available": "لا توجد نماذج متاحة",
829
+
830
+ "no_models_match": "لا توجد نماذج تطابق \"{searchQuery}\"",
831
+
832
+ "model_load_failed": "فشل تحميل النموذج: {error}",
833
+
834
+ "model_unloaded_ollama": "سيتم تفريغ {name} بعد انتهاء وقت الاحتفاظ",
835
+
836
+ "model_unloaded_success": "تم تفريغ {name} بنجاح",
837
+
838
+ "model_unload_failed": "فشل تفريغ النموذج: {error}",
839
+
840
+ "unload_from_server": "تفريغ من الخادم",
841
+
842
+ "context_chip": "{ctx} سياق",
843
+
844
+ "download_notification_title": "جارٍ تنزيل {modelName}...",
845
+
846
+ "download_complete_notification": "اكتمل التنزيل!",
847
+
848
+ "download_complete_body": "تم تنزيل {modelName} بنجاح.",
849
+
850
+ "download_failed_notification": "فشل التنزيل: {error}",
851
+
852
+ "download_failed_body": "فشل تنزيل {modelName}.",
853
+
854
+ "engine_name_system": "TTS النظام",
855
+
856
+ "engine_tagline_system": "محرك الجهاز المدمج",
857
+
858
+ "engine_name_kitten": "Kitten TTS",
859
+
860
+ "engine_tagline_kitten": "TTS عصبي عالي السرعة",
861
+
862
+ "engine_name_sherpa": "Sherpa ONNX VITS",
863
+
864
+ "engine_tagline_sherpa": "أصوات Piper دون اتصال",
865
+
866
+ "voice_jasper": "Jasper",
867
+ "voice_bella": "Bella",
868
+ "voice_bruno": "Bruno",
869
+ "voice_luna": "Luna",
870
+ "voice_hugo": "Hugo",
871
+ "voice_rosie": "Rosie",
872
+ "voice_leo": "Leo",
873
+ "voice_kiki": "Kiki",
874
+ "voice_lessac": "Lessac (أمريكي)",
875
+ "voice_ryan": "Ryan (أمريكي)",
876
+
877
+ "model_qwen_3": "Qwen 3 0.6B",
878
+
879
+ "model_qwen_3_desc": "أصغر نموذج محادثة عام. استجابات سريعة، استخدام منخفض للذاكرة.",
880
+
881
+ "model_license_apache": "Apache-2.0",
882
+
883
+ "model_qwen_25": "Qwen 2.5 1.5B Instruct",
884
+
885
+ "model_qwen_25_desc": "جودة وحجم متوازنان. مناسب للمحادثة العامة.",
886
+
887
+ "model_deepseek": "DeepSeek R1 Distill Qwen 1.5B",
888
+
889
+ "model_deepseek_desc": "نموذج استدلال وسلسلة أفكار. الأفضل للمهام المنطقية.",
890
+
891
+ "model_license_mit": "MIT",
892
+
893
+ "model_gemma": "Gemma 4 E2B Instruct",
894
+
895
+ "model_gemma_desc": "نموذج Google الرائد. أعلى جودة، يتطلب ذاكرة أكبر.",
896
+
897
+ "export_header": "*تم التصدير من LocalMind — {date}*",
898
+
899
+ "export_role_user": "## 👤 المستخدم",
900
+
901
+ "export_role_assistant": "## 🤖 المساعد",
902
+
903
+ "export_role_system": "## ⚙️ النظام",
904
+
905
+ "export_role_tool": "## 🔧 الأداة",
906
+
907
+ "export_text_user": "[المستخدم]",
908
+
909
+ "export_text_assistant": "[المساعد]",
910
+
911
+ "export_text_system": "[النظام]",
912
+
913
+ "export_text_tool": "[الأداة]",
914
+
915
+ "export_label_user": "المستخدم",
916
+
917
+ "export_label_assistant": "المساعد",
918
+
919
+ "export_label_system": "النظام",
920
+
921
+ "export_label_tool": "الأداة",
922
+
923
+ "select_model_hint": "اختر نموذجاً لبدء المحادثة",
924
+
925
+ "test_notification_title": "إشعار تجريبي",
926
+
927
+ "test_notification_body": "هذا إشعار تجريبي لتقدم تنزيل النموذج."
928
+}
lib/l10n/app_bn.arb
new
+495
@@ -0,0 +1,495 @@
1
+{
2
+ "@@locale": "bn",
3
+ "active": "সক্রিয়",
4
+ "active_integrations": "সক্রিয় ইন্টিগ্রেশনসমূহ",
5
+ "add_ephemeral_mcp": "সাময়িক MCP সার্ভার যুক্ত করুন",
6
+ "add_server": "সার্ভার যুক্ত করুন",
7
+ "add_server_first": "উপলব্ধ মডেলগুলো দেখতে প্রথমে একটি সার্ভার যুক্ত করুন।",
8
+ "add_server_title": "সার্ভার যুক্ত করুন",
9
+ "advanced_settings": "উন্নত সেটিংস",
10
+ "all": "সব",
11
+ "allow_notifications": "নোটিফিকেশন অনুমোদন করুন",
12
+ "always_available": "সর্বদা উপলব্ধ",
13
+ "api_key_format": "OpenRouter API কি sk- দিয়ে শুরু হয়",
14
+ "api_key_hint_generic": "প্রমাণিত সার্ভারের জন্য",
15
+ "api_key_hint_openrouter": "sk-...",
16
+ "api_key_optional": "এপিআই কি (ঐচ্ছিক)",
17
+ "api_key_required": "এপিআই কি *",
18
+ "api_key_required_openrouter": "OpenRouter-এর জন্য এপিআই কি প্রয়োজন",
19
+ "app_name": "LocalMind",
20
+ "app_tagline": "আপনার এআই। আপনার ডিভাইস। আপনার নিয়ম।",
21
+ "app_version": "1.0.0",
22
+ "attach_images_tooltip": "ছবি সংযুক্ত করুন",
23
+ "auto_execute_tools": "স্বয়ংক্রিয়ভাবে টুলস নির্বাহ করুন",
24
+ "auto_generate_titles": "স্বয়ংক্রিয় শিরোনাম তৈরি করুন",
25
+ "available": "উপলব্ধ",
26
+ "available_models": "উপলব্ধ মডেলসমূহ",
27
+ "available_ram": "উপলব্ধ র্যাম (RAM)",
28
+ "backend_cpu_desc": "সব ডিভাইসে কাজ করে। সবচেয়ে বেশি সামঞ্জস্যপূর্ণ।",
29
+ "backend_gpu_desc": "OpenCL ত্বরণ। সমর্থিত ডিভাইসে দ্রুততর।",
30
+ "backend_ios_notice": "iOS-এ শুধুমাত্র CPU ব্যাকএন্ড উপলব্ধ।",
31
+ "backend_npu_desc": "ভেন্ডর NPU (Qualcomm/MediaTek)। দ্রুততম ইনফারেন্স।",
32
+ "builtin_badge": "বিল্ট-ইন",
33
+ "calculating": "গণনা করা হচ্ছে...",
34
+ "cancel": "বাতিল করুন",
35
+ "cannot_undo": "এই কাজটি আর পূর্বাবস্থায় ফিরিয়ে আনা যাবে না।",
36
+ "category_label": "বিভাগ",
37
+ "change_persona": "পার্সোনা পরিবর্তন করুন",
38
+ "character_count": "{length}টি অক্ষর",
39
+ "character_count_max": "{currentLen}/৪০০০",
40
+ "chat_history_title": "চ্যাটের ইতিহাস",
41
+ "chat_input_hint": "যেকোনো কিছু জিজ্ঞাসা করুন",
42
+ "chat_parameters_tooltip": "চ্যাটের প্যারামিটারসমূহ",
43
+ "chat_settings_title": "চ্যাট সেটিংস",
44
+ "chat_title": "LocalMind",
45
+ "choose_theme": "থিম নির্বাচন করুন",
46
+ "choose_theme_desc": "অ্যাপের চেহারা ব্যক্তিগতকৃত করুন। আপনি পরবর্তীতে সেটিংস থেকে এটি যেকোনো সময় পরিবর্তন করতে পারবেন।",
47
+ "clear": "মুছুন",
48
+ "clear_conversation": "কথোপকথন মুছুন",
49
+ "clear_conversation_body": "এটি এই কথোপকথনের সমস্ত বার্তা মুছে ফেলবে।",
50
+ "clear_conversation_title": "কথোপকথন মুছে ফেলবেন?",
51
+ "clone_edit": "ক্লোন ও সম্পাদনা করুন",
52
+ "close": "বন্ধ করুন",
53
+ "code_theme_dark": "কোড থিম (ডার্ক)",
54
+ "code_theme_desc": "কোড ব্লকের জন্য সিনট্যাক্স হাইলাইটিং থিম নির্বাচন করুন।",
55
+ "code_theme_light": "কোড থিম (লাইট)",
56
+ "configure": "কনফিগার করুন",
57
+ "configuring_server": "সার্ভার কনফিগার করা হচ্ছে...",
58
+ "confirm": "নিশ্চিত করুন",
59
+ "connection_error": "সংযোগ ত্রুটি। আপনার সার্ভার পরীক্ষা করুন।",
60
+ "connection_failed": "সংযোগ ব্যর্থ হয়েছে। আপনার সেটিংস পরীক্ষা করুন।",
61
+ "connection_successful": "সংযোগ সফল হয়েছে!",
62
+ "context_chip": "{ctx} ctx",
63
+ "context_length": "কনটেক্সট দৈর্ঘ্য",
64
+ "context_length_desc": "ইতিহাসের উইন্ডো",
65
+ "continue_action": "চালিয়ে যান",
66
+ "conversation_date": "{month}/{day}/{year}",
67
+ "conversation_days_ago": "{days}দিন আগে",
68
+ "conversation_hours_ago": "{hours}ঘণ্টা আগে",
69
+ "conversation_id": "কথোপকথন আইডি",
70
+ "conversation_just_now": "এইমাত্র",
71
+ "conversation_minutes_ago": "{minutes}মিনিট আগে",
72
+ "conversation_yesterday": "গতকাল",
73
+ "copied": "কপি করা হয়েছে!",
74
+ "copied_markdown": "মার্কডাউন হিসেবে কপি করা হয়েছে",
75
+ "copied_to_clipboard": "ক্লিপবোর্ডে কপি করা হয়েছে",
76
+ "copy": "কপি করুন",
77
+ "copy_info": "তথ্য কপি করুন",
78
+ "copy_markdown": "মার্কডাউন হিসেবে কপি করুন",
79
+ "create_persona": "পার্সোনা তৈরি করুন",
80
+ "create_persona_button": "তৈরি করুন",
81
+ "created_at": "তৈরির সময়",
82
+ "debug_dialog_desc": "সিঙ্ক্রোনাইজেশন সমস্যাগুলো সনাক্ত করতে সাহায্য করার জন্য ডায়াগনস্টিক তথ্য।",
83
+ "default_badge": "ডিফল্ট",
84
+ "default_icon": "ডিফল্ট আইকন",
85
+ "delete": "মুছে ফেলুন",
86
+ "delete_all_conversations": "সমস্ত কথোপকথন মুছে ফেলুন",
87
+ "delete_conversation": "কথোপকথন মুছে ফেলবেন?",
88
+ "delete_conversation_body": "আপনি কি নিশ্চিত যে আপনি \"{title}\" মুছে ফেলতে চান? এটি আর পূর্বাবস্থায় ফিরিয়ে আনা যাবে না।",
89
+ "delete_conversation_title": "কথোপকথন মুছে ফেলবেন?",
90
+ "delete_message_title": "বার্তা মুছে ফেলবেন?",
91
+ "delete_model_body": "আপনি কি নিশ্চিত যে আপনি {name} মুছে ফেলতে চান?",
92
+ "delete_model_body_with_size": "আপনি কি নিশ্চিত যে আপনি {name} মুছে ফেলতে চান? এতে প্রায় {size} খালি জায়গা তৈরি হবে।\n\nপ্রয়োজন হলে আপনি পরবর্তীতে এই মডেলটি আবার ডাউনলোড করতে পারেন।",
93
+ "delete_model_title": "মডেল মুছে ফেলুন",
94
+ "delete_persona_body": "এটি আর পূর্বাবস্থায় ফিরিয়ে আনা যাবে না।",
95
+ "delete_persona_title": "\"{name}\" মুছে ফেলবেন?",
96
+ "delete_server_body": "আপনি কি নিশ্চিত যে আপনি \"{name}\" মুছে ফেলতে চান? এটি আর পূর্বাবস্থায় ফিরিয়ে আনা যাবে না।",
97
+ "delete_server_title": "সার্ভার মুছে ফেলুন",
98
+ "delete_voice_body": "আপনি কি নিশ্চিত যে আপনি {name} মুছে ফেলতে চান? এতে প্রায় {size} খালি জায়গা তৈরি হবে।\n\nপ্রয়োজন হলে আপনি পরবর্তীতে এই ভয়েসটি আবার ডাউনলোড করতে পারেন।",
99
+ "delete_voice_title": "ভয়েস মুছে ফেলুন",
100
+ "description_hint": "এই পার্সোনাটি কী কাজ করে...",
101
+ "description_optional": "বিবরণ (ঐচ্ছিক)",
102
+ "device_memory": "ডিভাইসের মেমরি",
103
+ "disconnected": "সার্ভার থেকে সংযোগ বিচ্ছিন্ন হয়েছে।",
104
+ "done": "সম্পন্ন",
105
+ "download": "ডাউনলোড করুন",
106
+ "download_complete_body": "{modelName} সফলভাবে ডাউনলোড করা হয়েছে।",
107
+ "download_complete_notification": "ডাউনলোড সম্পন্ন হয়েছে!",
108
+ "download_failed": "ডাউনলোড ব্যর্থ হয়েছে",
109
+ "download_failed_body": "{modelName} ডাউনলোড করতে ব্যর্থ হয়েছে।",
110
+ "download_failed_notification": "ডাউনলোড ব্যর্থ হয়েছে: {error}",
111
+ "download_model_desc": "ডাউনলোড করার জন্য একটি মডেল চয়ন করুন।\nএটি আপনার ডিভাইসে স্থানীয়ভাবে চলবে।",
112
+ "download_model_title": "মডেল ডাউনলোড করুন",
113
+ "download_notification_title": "{modelName} ডাউনলোড করা হচ্ছে...",
114
+ "download_progress": "{percent}% • {speed}",
115
+ "downloaded": "ডাউনলোড করা হয়েছে",
116
+ "downloading_status": "ডাউনলোড করা হচ্ছে...",
117
+ "edit": "সম্পাদনা করুন",
118
+ "edit_message": "বার্তা সম্পাদনা করুন",
119
+ "edit_message_desc": "সংরক্ষণ করলে নিচের অ্যাসিস্ট্যান্টের প্রতিক্রিয়াটি মুছে যাবে এবং পুনরায় জেনারেট হবে।",
120
+ "edit_persona": "পার্সোনা সম্পাদনা করুন",
121
+ "edit_server": "সার্ভার সম্পাদনা করুন",
122
+ "emoji_label": "ইমোজি",
123
+ "enable": "সক্ষম করুন",
124
+ "enable_mcp": "MCP সক্ষম করুন",
125
+ "enable_notifications": "নোটিফিকেশন সক্ষম করুন",
126
+ "enable_notifications_desc": "মডেল ডাউনলোডিং শেষ হলে নোটিফিকেশন পান।",
127
+ "engine_name_kitten": "Kitten TTS",
128
+ "engine_name_sherpa": "Sherpa ONNX VITS",
129
+ "engine_name_system": "System TTS",
130
+ "engine_spec": "{sizeMb} MB · {ramMb} MB RAM · {voiceCount}টি ভয়েস",
131
+ "engine_tagline_kitten": "উচ্চ-গতির নিউরাল টিটিএস (TTS)",
132
+ "engine_tagline_sherpa": "অফলাইন পাইপার (Piper) ভয়েসসমূহ",
133
+ "engine_tagline_system": "ডিভাইসের বিল্ট-ইন ইঞ্জিন",
134
+ "enter_new_title": "নতুন শিরোনাম লিখুন",
135
+ "error": "ত্রুটি",
136
+ "error_with_message": "ত্রুটি: {error}",
137
+ "eta_label": "অবশিষ্ট সময় (ETA): {eta}",
138
+ "expected_messages": "প্রত্যাশিত বার্তাসমূহ",
139
+ "export_header": "*LocalMind থেকে রপ্তানি করা হয়েছে — {date}*",
140
+ "export_label_assistant": "অ্যাসিস্ট্যান্ট",
141
+ "export_label_system": "সিস্টেম",
142
+ "export_label_tool": "টুল",
143
+ "export_label_user": "ব্যবহারকারী",
144
+ "export_role_assistant": "## 🤖 অ্যাসিস্ট্যান্ট",
145
+ "export_role_system": "## ⚙️ সিস্টেম",
146
+ "export_role_tool": "## 🔧 টুল",
147
+ "export_role_user": "## 👤 ব্যবহারকারী",
148
+ "export_text_assistant": "[অ্যাসিস্ট্যান্ট]",
149
+ "export_text_system": "[সিস্টেম]",
150
+ "export_text_tool": "[টুল]",
151
+ "export_text_user": "[ব্যবহারকারী]",
152
+ "failed_load_models": "মডেল লোড করতে ব্যর্থ হয়েছে",
153
+ "finish_setup": "সেটআপ সম্পন্ন করুন",
154
+ "font_preview": "যেকোনো লেখার সাইজ কেমন দেখাবে তা এখানে দেখা যাবে।",
155
+ "font_size": "ফন্টের সাইজ",
156
+ "font_size_desc": "চ্যাটে লেখার সাইজ সামঞ্জস্য করুন।",
157
+ "haptic_feedback": "হ্যাপটিক ফিডব্যাক",
158
+ "history_missing_desc": "হয় এই চ্যাটের বার্তাগুলো মুছে ফেলা হয়েছে অথবা চ্যাটের ইতিহাসটি দূষিত (corrupted) হয়েছে।",
159
+ "history_missing_title": "চ্যাটের ইতিহাস পাওয়া যায়নি",
160
+ "host_label": "হোস্ট / আইপি (IP) ঠিকানা",
161
+ "host_required": "হোস্টের ঠিকানা আবশ্যক",
162
+ "host_valid": "একটি বৈধ হোস্টনেম বা আইপি ঠিকানা লিখুন",
163
+ "inference_backend": "ইনফারেন্স ব্যাকএন্ড",
164
+ "initializing": "আরম্ভ করা হচ্ছে...",
165
+ "initializing_services": "সার্ভিসসমূহ আরম্ভ করা হচ্ছে...",
166
+ "install": "ইনস্টল করুন",
167
+ "installed": "ইনস্টল করা হয়েছে",
168
+ "label_completed": "{label} সম্পন্ন হয়েছে",
169
+ "language_system_default": "সিস্টেম ডিফল্ট",
170
+ "last_error": "শেষ ত্রুটি:",
171
+ "load": "লোড করুন",
172
+ "loaded_status": "লোড করা হয়েছে",
173
+ "loading": "লোড হচ্ছে...",
174
+ "loading_model": "{modelId} লোড করা হচ্ছে...",
175
+ "loading_model_error": "ত্রুটি: {error}",
176
+ "manage_on_device_models": "অন-ডিভাইস মডেল পরিচালনা করুন",
177
+ "manage_servers": "সার্ভার পরিচালনা করুন",
178
+ "manage_tts_models": "টিটিএস (TTS) মডেল পরিচালনা করুন",
179
+ "max_tokens": "সর্বোচ্চ টোকেন",
180
+ "max_tokens_desc": "প্রতিক্রিয়ার সীমা",
181
+ "may_be_large": "এই ডিভাইসের জন্য অনেক বড় হতে পারে",
182
+ "mcp_disabled_warning": "MCP বিশ্বব্যাপী নিষ্ক্রিয় করা আছে। এই বৈশিষ্ট্যগুলো ব্যবহার করতে সেটিংসে এটি সক্ষম করুন।",
183
+ "mcp_enable_chat": "এই চ্যাটের জন্য MCP সক্ষম করুন",
184
+ "mcp_label_placeholder": "লেবেল",
185
+ "mcp_tab": "MCP",
186
+ "mcp_url_placeholder": "ইউআরএল (https://...)",
187
+ "memory_critical": "সংকটপূর্ণ",
188
+ "memory_healthy": "স্বাস্থ্যকর",
189
+ "memory_low": "কম",
190
+ "message_options": "বার্তার বিকল্পসমূহ",
191
+ "model_deepseek": "DeepSeek R1 Distill Qwen 1.5B",
192
+ "model_deepseek_desc": "রিজনিং এবং চেইন-অফ-থট মডেল। যৌক্তিক কাজের জন্য সেরা।",
193
+ "model_gemma": "Gemma 4 E2B Instruct",
194
+ "model_gemma_desc": "গুগলের ফ্ল্যাগশিপ মডেল। সর্বোচ্চ মানসম্পন্ন, অধিক র্যাম প্রয়োজন।",
195
+ "model_license_apache": "Apache-2.0",
196
+ "model_license_mit": "MIT",
197
+ "model_load_failed": "মডেল লোড করতে ব্যর্থ হয়েছে: {error}",
198
+ "model_loaded": "মডেল লোড করা হয়েছে: {modelId} ({backend})",
199
+ "model_qwen_25": "Qwen 2.5 1.5B Instruct",
200
+ "model_qwen_25_desc": "মান এবং আকারের চমৎকার ভারসাম্য। সাধারণ কথোপকথনের জন্য ভালো।",
201
+ "model_qwen_3": "Qwen 3 0.6B",
202
+ "model_qwen_3_desc": "সবচেয়ে ছোট সাধারণ ব্যবহারের চ্যাট মডেল। দ্রুত প্রতিক্রিয়া, কম মেমরি ব্যবহার।",
203
+ "model_unload_failed": "মডেল আনলোড করতে ব্যর্থ হয়েছে: {error}",
204
+ "model_unloaded_ollama": "কিপ-অ্যালাইভ (keep-alive) সময় পার হলে {name} আনলোড করা হবে",
205
+ "model_unloaded_success": "{name} সফলভাবে আনলোড করা হয়েছে",
206
+ "more": "আরও",
207
+ "my_persona_hint": "আমার পার্সোনা",
208
+ "my_server_hint": "আমার সার্ভার",
209
+ "name_label": "নাম",
210
+ "name_length_validation": "নাম অবশ্যই ৫০ অক্ষরের বা তার কম হতে হবে",
211
+ "name_max_50": "সর্বোচ্চ ৫০টি অক্ষর",
212
+ "name_required": "নাম আবশ্যক",
213
+ "nav_history": "ইতিহাস",
214
+ "nav_local_models": "লোকাল মডেলসমূহ",
215
+ "nav_new_chat": "নতুন চ্যাট",
216
+ "nav_personas": "পার্সোনা",
217
+ "nav_servers": "সার্ভারসমূহ",
218
+ "nav_settings": "সেটিংস",
219
+ "nav_tts": "টেক্সট টু স্পিচ",
220
+ "new_chat_mcp_default": "নতুন চ্যাটে ডিফল্ট MCP",
221
+ "no_conversations_yet": "এখনও কোনো কথোপকথন নেই",
222
+ "no_model_loaded": "কোনো মডেল লোড করা নেই। একটি মডেল ডাউনলোড এবং লোড করতে \"অন-ডিভাইস মডেল পরিচালনা করুন\" ট্যাপ করুন।",
223
+ "no_models_available": "কোনো মডেল উপলব্ধ নেই",
224
+ "no_models_match": "\"{searchQuery}\"-এর সাথে কোনো মডেল মেলেনি",
225
+ "no_personas_desc": "এআই আচরণ কাস্টমাইজ করতে আপনার প্রথম পার্সোনা তৈরি করুন।",
226
+ "no_personas_found": "কোনো পার্সোনা পাওয়া যায়নি",
227
+ "no_prompt_placeholder": "এখনও কোনো প্রম্পট নেই...",
228
+ "no_results_found": "কোনো ফলাফল পাওয়া যায়নি",
229
+ "no_server_connected": "কোনো সার্ভার সংযুক্ত নেই",
230
+ "no_server_selected": "কোনো সার্ভার নির্বাচিত নেই",
231
+ "no_servers_desc": "এআই মডেলগুলোর সাথে চ্যাট করা শুরু করতে আপনার প্রথম সার্ভার যুক্ত করুন।",
232
+ "no_servers_yet": "এখনও কোনো সার্ভার নেই",
233
+ "none": "কোনোটিই নয়",
234
+ "none_selected": "কোনোটিই নির্বাচন করা হয়নি",
235
+ "not_downloaded": "ডাউনলোড করা হয়নি",
236
+ "not_installed": "ইনস্টল করা হয়নি",
237
+ "not_now": "এখন নয়",
238
+ "notification_benefit_background": "ব্যাকগ্রাউন্ড কাজের অবস্থা",
239
+ "notification_benefit_completions": "জেনারেশন সম্পন্ন হওয়ার তথ্য",
240
+ "notification_benefit_downloads": "মডেল ডাউনলোডের অগ্রগতি",
241
+ "offline": "অফলাইন",
242
+ "older_section": "পূর্বের",
243
+ "on_device_android_only": "অন-ডিভাইস ইনফারেন্স বর্তমানে শুধুমাত্র অ্যান্ড্রয়েডেই উপলব্ধ।",
244
+ "on_device_models_title": "অন-ডিভাইস মডেলসমূহ",
245
+ "onboarding_connect_desc": "আপনার নিজস্ব ব্যক্তিগত এআই অভিজ্ঞতা শুরু করতে\nLM Studio, Ollama, অথবা OpenRouter-এর সাথে\nসংযোগ করুন।",
246
+ "onboarding_connect_server": "আপনার সার্ভার\nসংযোগ করুন",
247
+ "onboarding_localmind": "LOCALMIND",
248
+ "online": "অনলাইন",
249
+ "open_source": "ওপেন সোর্স",
250
+ "open_source_desc": "LocalMind একটি ওপেন সোর্স প্রকল্প। গিটহাবে (GitHub) আমাদের অগ্রগতি অনুসরণ করুন বা অবদান রাখুন।",
251
+ "options_tooltip": "বিকল্পসমূহ",
252
+ "parameters_tab": "প্যারামিটারসমূহ",
253
+ "pause": "বিরতি দিন",
254
+ "paused_progress": "সাময়িক বিরতি - {percent}%",
255
+ "persona_builtin_section": "বিল্ট-ইন",
256
+ "persona_category_coding": "কোডিং",
257
+ "persona_category_creative": "সৃজনশীল",
258
+ "persona_category_education": "শিক্ষা",
259
+ "persona_category_general": "সাধারণ",
260
+ "persona_created": "পার্সোনা তৈরি করা হয়েছে",
261
+ "persona_my_section": "আমার পার্সোনা",
262
+ "persona_updated": "পার্সোনা আপডেট করা হয়েছে",
263
+ "personas_title": "পার্সোনা",
264
+ "pin": "পিন করুন",
265
+ "pinned_section": "পিন করা কথোপকথন",
266
+ "port_invalid": "অবশ্যই একটি সংখ্যা হতে হবে",
267
+ "port_label": "পোর্ট (Port)",
268
+ "port_range": "একটি বৈধ পোর্ট নম্বর লিখুন (১-৬৫৫৩৫)",
269
+ "port_required": "পোর্ট আবশ্যক",
270
+ "preparing_app": "অ্যাপ প্রস্তুত করা হচ্ছে...",
271
+ "preview": "প্রিভিউ",
272
+ "preview_failed": "প্রিভিউ ব্যর্থ হয়েছে: {error}",
273
+ "previous_30_days": "বিগত ৩০ দিন",
274
+ "previous_7_days": "বিগত ৭ দিন",
275
+ "privacy_info": "\"LocalMind আপনার ডেটা কখনোই দেখে না\"",
276
+ "proceed_anyway": "তবুও এগিয়ে যান",
277
+ "processing": "প্রসেস করা হচ্ছে...",
278
+ "prompt_hint": "আপনি একজন সহায়ক অ্যাসিস্ট্যান্ট...",
279
+ "prompt_max_chars": "সর্বোচ্চ ৪০০০ অক্ষর",
280
+ "prompt_required": "সিস্টেম প্রম্পট আবশ্যক",
281
+ "quick_async": "আমি কীভাবে async/await ব্যবহার করব?",
282
+ "quick_debug": "এটি আমার জন্য ডিবাগ করে দিন",
283
+ "quick_explain": "এই কোডটি ব্যাখ্যা করুন",
284
+ "quick_write": "আমাকে একটি ফাংশন লিখতে সাহায্য করুন",
285
+ "ram_min_required": "সর্বনিম্ন {fileSize} GB র্যাম (RAM) প্রয়োজন",
286
+ "ram_usage": "র্যাম ব্যবহার",
287
+ "ram_used": "{percent}% ব্যবহৃত",
288
+ "ram_warning": "র্যাম সংক্রান্ত সতর্কবার্তা",
289
+ "ram_warning_body_download": "এই মডেলটির জন্য অন্তত {ram} GB র্যাম প্রয়োজন, কিন্তু আপনার ডিভাইসে {totalMemory} রয়েছে। এটি সঠিকভাবে নাও চলতে পারে অথবা অ্যাপটি ক্র্যাশ করতে পারে।",
290
+ "ram_warning_body_load": "আপনার ডিভাইসে {availableRAM} খালি র্যাম রয়েছে, কিন্তু এই মডেলটির জন্য অন্তত {ram} GB প্রয়োজন। এটি লোড করতে ব্যর্থ হতে পারে অথবা সিস্টেমে অস্থিরতা দেখা দিতে পারে।",
291
+ "range_0_1": "0.0-1.0",
292
+ "range_0_2": "0.0-2.0",
293
+ "read_aloud": "জোরে পড়ুন",
294
+ "ready": "প্রস্তুত",
295
+ "ready_continue": "এগিয়ে যাওয়ার জন্য প্রস্তুত",
296
+ "recent_chats": "সাম্প্রতিক চ্যাটসমূহ",
297
+ "recommended": "সুপারিশকৃত",
298
+ "refresh_models": "মডেল রিফ্রেশ করুন",
299
+ "remove_persona": "পার্সোনা সরিয়ে ফেলুন",
300
+ "rename": "নাম পরিবর্তন করুন",
301
+ "rename_conversation": "কথোপকথন পুনর্নামকরণ করুন",
302
+ "reset_defaults": "ডিফল্ট মানে রিসেট করুন",
303
+ "reset_settings_defaults": "সেটিংস ডিফল্ট মানে রিসেট করুন",
304
+ "resume": "পুনরায় শুরু করুন",
305
+ "retry": "আবার চেষ্টা করুন",
306
+ "save": "সংরক্ষণ করুন",
307
+ "save_changes": "পরিবর্তনগুলো সংরক্ষণ করুন",
308
+ "save_continue": "সংরক্ষণ করুন এবং এগিয়ে যান",
309
+ "save_regenerate": "সংরক্ষণ করুন এবং পুনরায় জেনারেট করুন",
310
+ "save_server": "সার্ভার সংরক্ষণ করুন",
311
+ "search_hint": "কথোপকথন খুঁজুন...",
312
+ "search_icons_hint": "আইকন খুঁজুন...",
313
+ "search_models_hint": "মডেল খুঁজুন...",
314
+ "see_all": "সব দেখুন",
315
+ "select": "নির্বাচন করুন",
316
+ "select_icon": "আইকন নির্বাচন করুন",
317
+ "select_icon_desc": "আপনার সার্ভারের জন্য একটি আইকন চয়ন করুন",
318
+ "select_model": "মডেল নির্বাচন করুন",
319
+ "select_model_hint": "চ্যাট শুরু করতে একটি মডেল নির্বাচন করুন",
320
+ "select_model_title": "মডেল নির্বাচন করুন",
321
+ "select_persona": "পার্সোনা নির্বাচন করুন",
322
+ "send_message_tooltip": "বার্তা পাঠান",
323
+ "send_on_enter": "এন্টার (Enter) চাপলে পাঠান",
324
+ "server_added": "সার্ভার যুক্ত করা হয়েছে",
325
+ "server_address_format": "{host}:{port}",
326
+ "server_address_on_device": "স্থানীয় ইনফারেন্স (Local Inference)",
327
+ "server_address_openrouter": "openrouter.ai",
328
+ "server_icon_ai_brain": "এআই ব্রেইন (AI Brain)",
329
+ "server_icon_ai_brain2": "এআই ব্রেইন ০২ (AI Brain 02)",
330
+ "server_icon_ai_chat": "এআই চ্যাট (AI Chat)",
331
+ "server_icon_ai_cloud": "এআই ক্লাউড (AI Cloud)",
332
+ "server_icon_ai_network": "এআই নেটওয়ার্ক (AI Network)",
333
+ "server_icon_alert": "সতর্কীকরণ বৃত্ত",
334
+ "server_icon_api": "এপিআই (API)",
335
+ "server_icon_arrow_right": "ডানমুখী তীর ০১",
336
+ "server_icon_bot": "বট (Bot)",
337
+ "server_icon_bot2": "বট ০২",
338
+ "server_icon_cellular": "সেলুলার নেটওয়ার্ক",
339
+ "server_icon_check": "টিকচিহ্ন বৃত্ত",
340
+ "server_icon_chip": "চিপ (Chip)",
341
+ "server_icon_chip2": "চিপ ০২",
342
+ "server_icon_cloud": "ক্লাউড",
343
+ "server_icon_cloud_download": "ক্লাউড ডাউনলোড",
344
+ "server_icon_cloud_server": "ক্লাউড সার্ভার",
345
+ "server_icon_cloud_upload": "ক্লাউড আপলোড",
346
+ "server_icon_code": "কোড",
347
+ "server_icon_computer": "কম্পিউটার",
348
+ "server_icon_cpu": "সিপিইউ (CPU)",
349
+ "server_icon_database": "ডেটাবেস",
350
+ "server_icon_database1": "ডেটাবেস ০১",
351
+ "server_icon_database2": "ডেটাবেস ০২",
352
+ "server_icon_drive": "ড্রাইভ",
353
+ "server_icon_file1": "ফাইল ০১",
354
+ "server_icon_folder1": "ফোল্ডার ০১",
355
+ "server_icon_folder2": "ফোল্ডার ০২",
356
+ "server_icon_globe": "গ্লোব",
357
+ "server_icon_hard_drive": "হার্ড ড্রাইভ",
358
+ "server_icon_home1": "হোম ০১",
359
+ "server_icon_home2": "হোম ০২",
360
+ "server_icon_info": "তথ্য বৃত্ত",
361
+ "server_icon_key": "কি ০১",
362
+ "server_icon_label": "সার্ভার আইকন",
363
+ "server_icon_laptop": "ল্যাপটপ",
364
+ "server_icon_link": "লিঙ্ক ০১",
365
+ "server_icon_lock": "লক",
366
+ "server_icon_mcp": "MCP সার্ভার",
367
+ "server_icon_plug1": "প্লাগ ০১",
368
+ "server_icon_plug2": "প্লাগ ০২",
369
+ "server_icon_refresh": "রিফ্রেশ",
370
+ "server_icon_robotic": "রোবোটিক",
371
+ "server_icon_rocket": "রকেট",
372
+ "server_icon_settings1": "সেটিংস ০১",
373
+ "server_icon_settings2": "সেটিংস ০২",
374
+ "server_icon_stack": "সার্ভার স্ট্যাক",
375
+ "server_icon_stack2": "সার্ভার স্ট্যাক ০২",
376
+ "server_icon_stack3": "সার্ভার স্ট্যাক ০৩",
377
+ "server_icon_star": "তারা",
378
+ "server_icon_terminal": "কম্পিউটার টার্মিনাল",
379
+ "server_icon_zap": "জ্যাপ",
380
+ "server_name": "সার্ভারের নাম",
381
+ "server_type_label": "সার্ভারের ধরন",
382
+ "server_type_lm_studio": "LM Studio",
383
+ "server_type_lm_studio_display": "LM Studio",
384
+ "server_type_lm_studio_sub": "লোকাল এপিআই",
385
+ "server_type_ollama": "Ollama",
386
+ "server_type_ollama_display": "Ollama",
387
+ "server_type_ollama_sub": "সিএলআই ইঞ্জিন",
388
+ "server_type_on_device": "অন-ডিভাইস (On-Device)",
389
+ "server_type_on_device_display": "অন-ডিভাইস (On-Device)",
390
+ "server_type_on_device_sub": "কোনো সার্ভারের প্রয়োজন নেই",
391
+ "server_type_openai_display": "ওপেনএআই সামঞ্জস্যপূর্ণ",
392
+ "server_type_openrouter": "OpenRouter",
393
+ "server_type_openrouter_display": "OpenRouter",
394
+ "server_type_openrouter_sub": "ইউনিফাইড ক্লাউড",
395
+ "server_updated": "সার্ভার আপডেট করা হয়েছে",
396
+ "servers_title": "সার্ভারসমূহ",
397
+ "set_as_default": "ডিফল্ট হিসেবে সেট করুন",
398
+ "set_persona": "পার্সোনা সেট করুন",
399
+ "settings_about": "পরিচিতি",
400
+ "settings_appearance": "চেহারা ও থিম",
401
+ "settings_behavior": "আচরণ",
402
+ "settings_data_management": "ডেটা পরিচালনা",
403
+ "settings_default_persona": "ডিফল্ট পার্সোনা",
404
+ "settings_default_server": "ডিফল্ট সার্ভার",
405
+ "settings_language": "ভাষা",
406
+ "settings_on_device": "অন-ডিভাইস ইনফারেন্স",
407
+ "settings_privacy": "গোপনীয়তা",
408
+ "settings_title": "সেটিংস",
409
+ "settings_tts": "টেক্সট-টু-স্পিচ",
410
+ "setup_connection": "সংযোগ সেটআপ করুন",
411
+ "setup_connection_desc": "চ্যাট শুরু করতে আপনার {server} সার্ভার কনফিগার করুন।",
412
+ "share": "শেয়ার করুন",
413
+ "show_data_indicator": "ডেটা নির্দেশক দেখান",
414
+ "show_system_messages": "সিস্টেম বার্তাসমূহ দেখান",
415
+ "skip": "এড়িয়ে যান",
416
+ "something_went_wrong": "কিছু একটা ভুল হয়েছে",
417
+ "star_on_github": "গিটহাবে স্টার দিন",
418
+ "start_conversation": "কথোপকথন শুরু করুন",
419
+ "start_new_chat": "নতুন চ্যাট শুরু করুন",
420
+ "start_new_conversation": "নতুন কথোপকথন শুরু করুন",
421
+ "startup_failed": "স্টার্টআপ ব্যর্থ হয়েছে",
422
+ "stay_updated": "আপ-টু-ডেট থাকুন",
423
+ "stay_updated_desc": "আপনার এআই মডেলগুলোর ডাউনলোড সম্পন্ন হলে অথবা দীর্ঘস্থায়ী কাজগুলো শেষ হলে নোটিফিকেশন পান।",
424
+ "stop": "বন্ধ করুন",
425
+ "stop_generation_tooltip": "জেনারেশন বন্ধ করুন",
426
+ "stop_reading": "পড়া বন্ধ করুন",
427
+ "streaming_responses": "স্ট্রিমিং প্রতিক্রিয়া",
428
+ "switch_server": "সার্ভার পরিবর্তন করুন",
429
+ "switch_server_subtitle": "সংযোগ করার জন্য একটি সার্ভার চয়ন করুন",
430
+ "switched_to_server": "{name}-এ স্থানান্তরিত হয়েছে",
431
+ "system_prompt": "সিস্টেম প্রম্পট",
432
+ "technical_details": "প্রযুক্তিগত বিবরণ",
433
+ "temp_hint": "0.7",
434
+ "temperature": "টেম্পারেচার (Temperature)",
435
+ "temperature_desc": "র্যান্ডমনেস নিয়ন্ত্রণ করে: উচ্চমান = সৃজনশীল, নিম্নমান = নিবদ্ধ",
436
+ "temperature_label": "টেম্পারেচার (০.০-২.০)",
437
+ "test_connection": "সংযোগ পরীক্ষা করুন",
438
+ "test_notification_body": "এটি মডেল ডাউনলোডের অগ্রগতির জন্য একটি পরীক্ষা নোটিফিকেশন।",
439
+ "test_notification_title": "পরীক্ষা নোটিফিকেশন",
440
+ "testing": "পরীক্ষা করা হচ্ছে...",
441
+ "theme": "থিম",
442
+ "theme_card_claude": "Claude",
443
+ "theme_card_claude_sub": "একটি উষ্ণ, পিচ রঙের থিম",
444
+ "theme_card_dark": "ডার্ক",
445
+ "theme_card_dark_sub": "চোখের জন্য আরামদায়ক",
446
+ "theme_card_light": "লাইট",
447
+ "theme_card_light_sub": "পরিষ্কার এবং উজ্জ্বল",
448
+ "theme_card_system": "সিস্টেম",
449
+ "theme_card_system_sub": "আপনার ডিভাইসের সেটিংসের সাথে মিলে যায়",
450
+ "theme_claude": "Claude",
451
+ "theme_dark": "ডার্ক",
452
+ "theme_light": "লাইট",
453
+ "theme_system": "সিস্টেম",
454
+ "thinking": "চিন্তা করছে",
455
+ "today_section": "আজ",
456
+ "tool_label": "টুল: {toolCallId}",
457
+ "tool_unknown": "টুল: অজানা",
458
+ "top_p": "টপ পি (Top P)",
459
+ "top_p_desc": "নিউক্লিয়াস স্যাম্পলিং থ্রেশহোল্ড",
460
+ "top_p_hint": "0.9",
461
+ "top_p_label": "টপ পি (০.০-১.০)",
462
+ "total_capacity": "মোট ক্ষমতা",
463
+ "total_ram": "মোট র্যাম",
464
+ "try_different_search": "অন্য কোনো অনুসন্ধান শব্দ দিয়ে চেষ্টা করুন",
465
+ "tts_engine": "টিটিএস (TTS) ইঞ্জিন",
466
+ "tts_engine_kitten": "Kitten TTS",
467
+ "tts_engine_system": "System TTS",
468
+ "tts_kitten_desc": "৮টি অভিব্যক্তিপূর্ণ ভয়েসসহ বিদ্যুৎ-গতির নিউরাল টিটিএস।\nএটি ডাউনলোড করতে {size} প্রয়োজন।",
469
+ "tts_models_title": "টেক্সট টু স্পিচ মডেলসমূহ",
470
+ "tts_piper_desc": "২টি অভিব্যক্তিপূর্ণ ভয়েসসহ দ্রুত অফলাইন পাইপার ভয়েস।\nভয়েস প্রতি ডাউনলোড করতে {size} প্রয়োজন।",
471
+ "tts_speed": "টিটিএস স্পিড",
472
+ "tts_speed_desc": "প্লেব্যাকের গতি সামঞ্জস্য করুন।",
473
+ "tts_system_desc": "আপনার ডিভাইসের বিল্ট-ইন টেক্সট-টু-স্পিচ ইঞ্জিন ব্যবহার করে।\nকোনো ডাউনলোডের প্রয়োজন নেই। ভয়েস নির্বাচন আপনার ডিভাইসের সিস্টেম সেটিংস ব্যবহার করে।",
474
+ "unknown_error": "অজানা ত্রুটি",
475
+ "unload": "আনলোড করুন",
476
+ "unload_from_server": "সার্ভার থেকে আনলোড করুন",
477
+ "unpin": "আনপিন করুন",
478
+ "update_server": "সার্ভার আপডেট করুন",
479
+ "voice": "ভয়েস (Voice)",
480
+ "voice_bella": "Bella",
481
+ "voice_bruno": "Bruno",
482
+ "voice_female": "নারী",
483
+ "voice_hugo": "Hugo",
484
+ "voice_jasper": "Jasper",
485
+ "voice_kiki": "Kiki",
486
+ "voice_leo": "Leo",
487
+ "voice_lessac": "Lessac (US)",
488
+ "voice_luna": "Luna",
489
+ "voice_male": "পুরুষ",
490
+ "voice_other": "অন্যান্য",
491
+ "voice_rosie": "Rosie",
492
+ "voice_ryan": "Ryan (US)",
493
+ "waiting_selection": "নির্বাচনের জন্য অপেক্ষা করা হচ্ছে",
494
+ "yesterday_section": "গতকাল"
495
+}
\ No newline at end of file
lib/l10n/app_en.arb
new
+2765
@@ -0,0 +1,2765 @@
1
+{
2
+ "@@locale": "en",
3
+
4
+ "app_name": "LocalMind",
5
+ "@app_name": {
6
+ "description": "Application display name"
7
+ },
8
+
9
+ "app_tagline": "Your AI. Your Device. Your Rules.",
10
+ "@app_tagline": {
11
+ "description": "Application tagline shown in settings and splash"
12
+ },
13
+
14
+ "app_version": "1.0.0",
15
+ "@app_version": {
16
+ "description": "Application version number"
17
+ },
18
+
19
+ "cancel": "Cancel",
20
+ "@cancel": {
21
+ "description": "Generic cancel button label"
22
+ },
23
+
24
+ "confirm": "Confirm",
25
+ "@confirm": {
26
+ "description": "Generic confirm button label"
27
+ },
28
+
29
+ "delete": "Delete",
30
+ "@delete": {
31
+ "description": "Generic delete button label"
32
+ },
33
+
34
+ "save": "Save",
35
+ "@save": {
36
+ "description": "Generic save button label"
37
+ },
38
+
39
+ "retry": "Retry",
40
+ "@retry": {
41
+ "description": "Generic retry button label"
42
+ },
43
+
44
+ "close": "Close",
45
+ "@close": {
46
+ "description": "Generic close button label"
47
+ },
48
+
49
+ "done": "Done",
50
+ "@done": {
51
+ "description": "Generic done button label"
52
+ },
53
+
54
+ "continue_action": "Continue",
55
+ "@continue_action": {
56
+ "description": "Generic continue button label"
57
+ },
58
+
59
+ "skip": "Skip",
60
+ "@skip": {
61
+ "description": "Generic skip button label"
62
+ },
63
+
64
+ "install": "Install",
65
+ "@install": {
66
+ "description": "Install action button"
67
+ },
68
+
69
+ "download": "Download",
70
+ "@download": {
71
+ "description": "Download action button"
72
+ },
73
+
74
+ "resume": "Resume",
75
+ "@resume": {
76
+ "description": "Resume download button"
77
+ },
78
+
79
+ "pause": "Pause",
80
+ "@pause": {
81
+ "description": "Pause download button"
82
+ },
83
+
84
+ "stop": "Stop",
85
+ "@stop": {
86
+ "description": "Stop action button"
87
+ },
88
+
89
+ "edit": "Edit",
90
+ "@edit": {
91
+ "description": "Edit action label"
92
+ },
93
+
94
+ "preview": "Preview",
95
+ "@preview": {
96
+ "description": "Preview action label"
97
+ },
98
+
99
+ "unload": "Unload",
100
+ "@unload": {
101
+ "description": "Unload model action"
102
+ },
103
+
104
+ "load": "Load",
105
+ "@load": {
106
+ "description": "Load model action"
107
+ },
108
+
109
+ "rename": "Rename",
110
+ "@rename": {
111
+ "description": "Rename action label"
112
+ },
113
+
114
+ "pin": "Pin",
115
+ "@pin": {
116
+ "description": "Pin conversation action"
117
+ },
118
+
119
+ "unpin": "Unpin",
120
+ "@unpin": {
121
+ "description": "Unpin conversation action"
122
+ },
123
+
124
+ "share": "Share",
125
+ "@share": {
126
+ "description": "Share action label"
127
+ },
128
+
129
+ "copy": "Copy",
130
+ "@copy": {
131
+ "description": "Copy action label"
132
+ },
133
+
134
+ "copied": "Copied!",
135
+ "@copied": {
136
+ "description": "Copied confirmation text"
137
+ },
138
+
139
+ "copied_to_clipboard": "Copied to clipboard",
140
+ "@copied_to_clipboard": {
141
+ "description": "Snackbar message after copy"
142
+ },
143
+
144
+ "select": "Select",
145
+ "@select": {
146
+ "description": "Select action button"
147
+ },
148
+
149
+ "active": "Active",
150
+ "@active": {
151
+ "description": "Active status badge"
152
+ },
153
+
154
+ "all": "All",
155
+ "@all": {
156
+ "description": "All filter label"
157
+ },
158
+
159
+ "none": "None",
160
+ "@none": {
161
+ "description": "No selection label"
162
+ },
163
+
164
+ "none_selected": "None selected",
165
+ "@none_selected": {
166
+ "description": "Dropdown placeholder when nothing selected"
167
+ },
168
+
169
+ "online": "Online",
170
+ "@online": {
171
+ "description": "Connection status online"
172
+ },
173
+
174
+ "offline": "Offline",
175
+ "@offline": {
176
+ "description": "Connection status offline"
177
+ },
178
+
179
+ "error": "Error",
180
+ "@error": {
181
+ "description": "Error status text"
182
+ },
183
+
184
+ "unknown_error": "Unknown error",
185
+ "@unknown_error": {
186
+ "description": "Fallback unknown error message"
187
+ },
188
+
189
+ "not_now": "Not Now",
190
+ "@not_now": {
191
+ "description": "Dismiss action for notifications"
192
+ },
193
+
194
+ "enable": "Enable",
195
+ "@enable": {
196
+ "description": "Enable action button"
197
+ },
198
+
199
+ "proceed_anyway": "Proceed Anyway",
200
+ "@proceed_anyway": {
201
+ "description": "Proceed despite warning"
202
+ },
203
+
204
+ "test_connection": "Test Connection",
205
+ "@test_connection": {
206
+ "description": "Button to test server connection"
207
+ },
208
+
209
+ "testing": "Testing...",
210
+ "@testing": {
211
+ "description": "Button label while testing connection"
212
+ },
213
+
214
+ "connection_successful": "Connection successful!",
215
+ "@connection_successful": {
216
+ "description": "Connection test success message"
217
+ },
218
+
219
+ "connection_failed": "Connection failed. Check your settings.",
220
+ "@connection_failed": {
221
+ "description": "Connection test failure message"
222
+ },
223
+
224
+ "save_continue": "Save & Continue",
225
+ "@save_continue": {
226
+ "description": "Save and proceed button"
227
+ },
228
+
229
+ "save_changes": "Save Changes",
230
+ "@save_changes": {
231
+ "description": "Save changes button"
232
+ },
233
+
234
+ "finish_setup": "Finish Setup",
235
+ "@finish_setup": {
236
+ "description": "Complete onboarding button"
237
+ },
238
+
239
+ "start_new_chat": "Start New Chat",
240
+ "@start_new_chat": {
241
+ "description": "Button to start a new chat"
242
+ },
243
+
244
+ "cannot_undo": "This action cannot be undone.",
245
+ "@cannot_undo": {
246
+ "description": "Warning for destructive actions"
247
+ },
248
+
249
+ "ram_warning": "RAM Warning",
250
+ "@ram_warning": {
251
+ "description": "RAM warning dialog title"
252
+ },
253
+
254
+ "recommended": "RECOMMENDED",
255
+ "@recommended": {
256
+ "description": "Recommended badge on model cards"
257
+ },
258
+
259
+ "may_be_large": "May be too large for this device",
260
+ "@may_be_large": {
261
+ "description": "Warning when model may exceed device RAM"
262
+ },
263
+
264
+ "calculating": "Calculating...",
265
+ "@calculating": {
266
+ "description": "ETA placeholder during download"
267
+ },
268
+
269
+ "download_failed": "Download failed",
270
+ "@download_failed": {
271
+ "description": "Download failure status"
272
+ },
273
+
274
+ "downloaded": "Downloaded",
275
+ "@downloaded": {
276
+ "description": "Download completed status"
277
+ },
278
+
279
+ "not_downloaded": "Not downloaded",
280
+ "@not_downloaded": {
281
+ "description": "Model not yet downloaded"
282
+ },
283
+
284
+ "installed": "Installed",
285
+ "@installed": {
286
+ "description": "Installed status"
287
+ },
288
+
289
+ "not_installed": "Not installed",
290
+ "@not_installed": {
291
+ "description": "Not installed status"
292
+ },
293
+
294
+ "loading": "Loading...",
295
+ "@loading": {
296
+ "description": "Generic loading indicator text"
297
+ },
298
+
299
+ "thinking": "Thinking",
300
+ "@thinking": {
301
+ "description": "Model thinking indicator"
302
+ },
303
+
304
+ "processing": "Processing...",
305
+ "@processing": {
306
+ "description": "Processing indicator text"
307
+ },
308
+
309
+ "initializing": "Initializing...",
310
+ "@initializing": {
311
+ "description": "Initializing status"
312
+ },
313
+
314
+ "ready": "Ready",
315
+ "@ready": {
316
+ "description": "Ready status text"
317
+ },
318
+
319
+ "preparing_app": "Preparing app...",
320
+ "@preparing_app": {
321
+ "description": "Bootstrap stage message"
322
+ },
323
+
324
+ "initializing_services": "Initializing services...",
325
+ "@initializing_services": {
326
+ "description": "Bootstrap stage message"
327
+ },
328
+
329
+ "configuring_server": "Configuring server...",
330
+ "@configuring_server": {
331
+ "description": "Bootstrap stage message"
332
+ },
333
+
334
+ "startup_failed": "Startup failed",
335
+ "@startup_failed": {
336
+ "description": "Bootstrap error message"
337
+ },
338
+
339
+ "something_went_wrong": "Something went wrong",
340
+ "@something_went_wrong": {
341
+ "description": "Generic error heading on bootstrap screen"
342
+ },
343
+
344
+ "delete_model_title": "Delete Model",
345
+ "@delete_model_title": {
346
+ "description": "Delete model dialog title"
347
+ },
348
+
349
+ "delete_model_body": "Are you sure you want to delete {name}?",
350
+ "@delete_model_body": {
351
+ "description": "Delete model confirmation dialog body",
352
+ "placeholders": {
353
+ "name": {
354
+ "type": "String",
355
+ "example": "ModelName"
356
+ }
357
+ }
358
+ },
359
+
360
+ "delete_model_body_with_size": "Are you sure you want to delete {name}? This will free up approximately {size} of space.\n\nYou can download this model again later if needed.",
361
+ "@delete_model_body_with_size": {
362
+ "description": "Delete model dialog body with size info",
363
+ "placeholders": {
364
+ "name": {
365
+ "type": "String",
366
+ "example": "ModelName"
367
+ },
368
+ "size": {
369
+ "type": "String",
370
+ "example": "250 MB"
371
+ }
372
+ }
373
+ },
374
+
375
+ "delete_voice_title": "Delete Voice",
376
+ "@delete_voice_title": {
377
+ "description": "Delete voice confirmation dialog title"
378
+ },
379
+
380
+ "delete_voice_body": "Are you sure you want to delete {name}? This will free up approximately {size} of space.\n\nYou can download this voice again later if needed.",
381
+ "@delete_voice_body": {
382
+ "description": "Delete voice confirmation dialog body",
383
+ "placeholders": {
384
+ "name": {
385
+ "type": "String",
386
+ "example": "VoiceName"
387
+ },
388
+ "size": {
389
+ "type": "String",
390
+ "example": "100 MB"
391
+ }
392
+ }
393
+ },
394
+
395
+ "delete_server_title": "Delete Server",
396
+ "@delete_server_title": {
397
+ "description": "Delete server confirmation dialog title"
398
+ },
399
+
400
+ "delete_server_body": "Are you sure you want to delete \"{name}\"? This cannot be undone.",
401
+ "@delete_server_body": {
402
+ "description": "Delete server confirmation body",
403
+ "placeholders": {
404
+ "name": {
405
+ "type": "String",
406
+ "example": "My Server"
407
+ }
408
+ }
409
+ },
410
+
411
+ "delete_conversation_title": "Delete conversation?",
412
+ "@delete_conversation_title": {
413
+ "description": "Delete conversation dialog title"
414
+ },
415
+
416
+ "delete_conversation_body": "Are you sure you want to delete \"{title}\"? This cannot be undone.",
417
+ "@delete_conversation_body": {
418
+ "description": "Delete conversation dialog body",
419
+ "placeholders": {
420
+ "title": {
421
+ "type": "String",
422
+ "example": "Chat about Flutter"
423
+ }
424
+ }
425
+ },
426
+
427
+ "delete_message_title": "Delete message?",
428
+ "@delete_message_title": {
429
+ "description": "Delete message dialog title"
430
+ },
431
+
432
+ "delete_persona_title": "Delete \"{name}\"?",
433
+ "@delete_persona_title": {
434
+ "description": "Delete persona dialog title",
435
+ "placeholders": {
436
+ "name": {
437
+ "type": "String",
438
+ "example": "HelperBot"
439
+ }
440
+ }
441
+ },
442
+
443
+ "delete_persona_body": "This cannot be undone.",
444
+ "@delete_persona_body": {
445
+ "description": "Delete persona dialog body"
446
+ },
447
+
448
+ "clear_conversation_title": "Clear conversation?",
449
+ "@clear_conversation_title": {
450
+ "description": "Clear conversation dialog title"
451
+ },
452
+
453
+ "clear_conversation_body": "This will delete all messages in this conversation.",
454
+ "@clear_conversation_body": {
455
+ "description": "Clear conversation confirmation body"
456
+ },
457
+
458
+ "clear": "Clear",
459
+ "@clear": {
460
+ "description": "Clear action button"
461
+ },
462
+
463
+ "label_completed": "{label} completed",
464
+ "@label_completed": {
465
+ "description": "Completion status message",
466
+ "placeholders": {
467
+ "label": {
468
+ "type": "String",
469
+ "example": "Download"
470
+ }
471
+ }
472
+ },
473
+
474
+ "error_with_message": "Error: {error}",
475
+ "@error_with_message": {
476
+ "description": "Error display with message",
477
+ "placeholders": {
478
+ "error": {
479
+ "type": "String",
480
+ "example": "Connection refused"
481
+ }
482
+ }
483
+ },
484
+
485
+ "preview_failed": "Preview failed: {error}",
486
+ "@preview_failed": {
487
+ "description": "Audio preview failed message",
488
+ "placeholders": {
489
+ "error": {
490
+ "type": "String",
491
+ "example": "Audio error"
492
+ }
493
+ }
494
+ },
495
+
496
+ "loading_model": "Loading {modelId}...",
497
+ "@loading_model": {
498
+ "description": "Model loading status",
499
+ "placeholders": {
500
+ "modelId": {
501
+ "type": "String",
502
+ "example": "gpt-4"
503
+ }
504
+ }
505
+ },
506
+
507
+ "model_loaded": "Model loaded: {modelId} ({backend})",
508
+ "@model_loaded": {
509
+ "description": "Model loaded status in settings",
510
+ "placeholders": {
511
+ "modelId": {
512
+ "type": "String",
513
+ "example": "qwen-3"
514
+ },
515
+ "backend": {
516
+ "type": "String",
517
+ "example": "CPU"
518
+ }
519
+ }
520
+ },
521
+
522
+ "no_model_loaded": "No model loaded. Tap \"Manage On-Device Models\" to download and load a model.",
523
+ "@no_model_loaded": {
524
+ "description": "Status when no on-device model is loaded"
525
+ },
526
+
527
+ "loading_model_error": "Error: {error}",
528
+ "@loading_model_error": {
529
+ "description": "Model loading error status",
530
+ "placeholders": {
531
+ "error": {
532
+ "type": "String",
533
+ "example": "Out of memory"
534
+ }
535
+ }
536
+ },
537
+
538
+ "delete_conversation": "Delete conversation?",
539
+ "@delete_conversation": {
540
+ "description": "Delete conversation dialog title"
541
+ },
542
+
543
+ "nav_history": "History",
544
+ "@nav_history": {
545
+ "description": "Navigation item for chat history"
546
+ },
547
+
548
+ "nav_servers": "Servers",
549
+ "@nav_servers": {
550
+ "description": "Navigation item for servers"
551
+ },
552
+
553
+ "nav_local_models": "Local Models",
554
+ "@nav_local_models": {
555
+ "description": "Navigation item for local on-device models"
556
+ },
557
+
558
+ "nav_tts": "Text To Speech",
559
+ "@nav_tts": {
560
+ "description": "Navigation item for TTS models"
561
+ },
562
+
563
+ "nav_personas": "Personas",
564
+ "@nav_personas": {
565
+ "description": "Navigation item for personas"
566
+ },
567
+
568
+ "nav_settings": "Settings",
569
+ "@nav_settings": {
570
+ "description": "Navigation item for settings"
571
+ },
572
+
573
+ "nav_new_chat": "New Chat",
574
+ "@nav_new_chat": {
575
+ "description": "Button to start a new chat"
576
+ },
577
+
578
+ "search_hint": "Search conversations...",
579
+ "@search_hint": {
580
+ "description": "Search field placeholder for conversations"
581
+ },
582
+
583
+ "no_server_selected": "No server selected",
584
+ "@no_server_selected": {
585
+ "description": "Placeholder when no server is selected"
586
+ },
587
+
588
+ "switch_server": "Switch Server",
589
+ "@switch_server": {
590
+ "description": "Switch server sheet title"
591
+ },
592
+
593
+ "switch_server_subtitle": "Choose a server to connect to",
594
+ "@switch_server_subtitle": {
595
+ "description": "Switch server sheet subtitle"
596
+ },
597
+
598
+ "manage_servers": "Manage Servers",
599
+ "@manage_servers": {
600
+ "description": "Button to manage servers list"
601
+ },
602
+
603
+ "open_source": "Open Source",
604
+ "@open_source": {
605
+ "description": "GitHub repo card title"
606
+ },
607
+
608
+ "open_source_desc": "LocalMind is open source. Follow our progress or contribute on GitHub.",
609
+ "@open_source_desc": {
610
+ "description": "GitHub repo card description"
611
+ },
612
+
613
+ "star_on_github": "Star on GitHub",
614
+ "@star_on_github": {
615
+ "description": "Button to star repo on GitHub"
616
+ },
617
+
618
+ "settings_title": "Settings",
619
+ "@settings_title": {
620
+ "description": "Settings screen title"
621
+ },
622
+
623
+ "settings_appearance": "Appearance",
624
+ "@settings_appearance": {
625
+ "description": "Settings section header for appearance"
626
+ },
627
+
628
+ "settings_language": "Language",
629
+ "@settings_language": {
630
+ "description": "Settings section label for language selection"
631
+ },
632
+
633
+ "language_system_default": "System Default",
634
+ "@language_system_default": {
635
+ "description": "Option to use the device's system language"
636
+ },
637
+
638
+ "settings_tts": "Text-to-Speech",
639
+ "@settings_tts": {
640
+ "description": "Settings section header for TTS"
641
+ },
642
+
643
+ "settings_behavior": "Behavior",
644
+ "@settings_behavior": {
645
+ "description": "Settings section header for behavior"
646
+ },
647
+
648
+ "settings_on_device": "On-Device Inference",
649
+ "@settings_on_device": {
650
+ "description": "Settings section header for on-device"
651
+ },
652
+
653
+ "settings_default_server": "Default Server",
654
+ "@settings_default_server": {
655
+ "description": "Settings section header for default server"
656
+ },
657
+
658
+ "settings_default_persona": "Default Persona",
659
+ "@settings_default_persona": {
660
+ "description": "Settings section header for default persona"
661
+ },
662
+
663
+ "settings_privacy": "Privacy",
664
+ "@settings_privacy": {
665
+ "description": "Settings section header for privacy"
666
+ },
667
+
668
+ "settings_data_management": "Data Management",
669
+ "@settings_data_management": {
670
+ "description": "Settings section header for data management"
671
+ },
672
+
673
+ "settings_about": "About",
674
+ "@settings_about": {
675
+ "description": "Settings section header for about"
676
+ },
677
+
678
+ "theme": "Theme",
679
+ "@theme": {
680
+ "description": "Theme selection label"
681
+ },
682
+
683
+ "theme_system": "System",
684
+ "@theme_system": {
685
+ "description": "System theme option"
686
+ },
687
+
688
+ "theme_light": "Light",
689
+ "@theme_light": {
690
+ "description": "Light theme option"
691
+ },
692
+
693
+ "theme_dark": "Dark",
694
+ "@theme_dark": {
695
+ "description": "Dark theme option"
696
+ },
697
+
698
+ "theme_claude": "Claude",
699
+ "@theme_claude": {
700
+ "description": "Claude theme option"
701
+ },
702
+
703
+ "font_size": "Font Size",
704
+ "@font_size": {
705
+ "description": "Font size slider label"
706
+ },
707
+
708
+ "font_size_desc": "Adjust text size in chat.",
709
+ "@font_size_desc": {
710
+ "description": "Font size setting description"
711
+ },
712
+
713
+ "font_preview": "The quick brown fox jumps over the lazy dog.",
714
+ "@font_preview": {
715
+ "description": "Font preview sample text"
716
+ },
717
+
718
+ "code_theme_dark": "Code Theme (Dark)",
719
+ "@code_theme_dark": {
720
+ "description": "Dark code theme dropdown label"
721
+ },
722
+
723
+ "code_theme_light": "Code Theme (Light)",
724
+ "@code_theme_light": {
725
+ "description": "Light code theme dropdown label"
726
+ },
727
+
728
+ "code_theme_desc": "Choose syntax highlighting theme for code blocks.",
729
+ "@code_theme_desc": {
730
+ "description": "Code theme setting description"
731
+ },
732
+
733
+ "tts_engine": "TTS Engine",
734
+ "@tts_engine": {
735
+ "description": "TTS engine dropdown label"
736
+ },
737
+
738
+ "tts_engine_system": "System TTS",
739
+ "@tts_engine_system": {
740
+ "description": "System TTS engine option"
741
+ },
742
+
743
+ "tts_engine_kitten": "Kitten TTS",
744
+ "@tts_engine_kitten": {
745
+ "description": "Kitten TTS engine option"
746
+ },
747
+
748
+ "voice": "Voice",
749
+ "@voice": {
750
+ "description": "Voice selection label"
751
+ },
752
+
753
+ "voice_female": "Female",
754
+ "@voice_female": {
755
+ "description": "Female voice gender label"
756
+ },
757
+
758
+ "voice_male": "Male",
759
+ "@voice_male": {
760
+ "description": "Male voice gender label"
761
+ },
762
+
763
+ "voice_other": "Other",
764
+ "@voice_other": {
765
+ "description": "Other voice gender label"
766
+ },
767
+
768
+ "tts_speed": "TTS Speed",
769
+ "@tts_speed": {
770
+ "description": "TTS speed slider label"
771
+ },
772
+
773
+ "tts_speed_desc": "Adjust the playback rate.",
774
+ "@tts_speed_desc": {
775
+ "description": "TTS speed setting description"
776
+ },
777
+
778
+ "manage_tts_models": "Manage TTS Models",
779
+ "@manage_tts_models": {
780
+ "description": "Button to manage TTS models"
781
+ },
782
+
783
+ "manage_on_device_models": "Manage On-Device Models",
784
+ "@manage_on_device_models": {
785
+ "description": "Button to manage on-device models"
786
+ },
787
+
788
+ "streaming_responses": "Streaming Responses",
789
+ "@streaming_responses": {
790
+ "description": "Toggle for streaming responses"
791
+ },
792
+
793
+ "auto_generate_titles": "Auto-generate Titles",
794
+ "@auto_generate_titles": {
795
+ "description": "Toggle for auto-generating conversation titles"
796
+ },
797
+
798
+ "send_on_enter": "Send on Enter",
799
+ "@send_on_enter": {
800
+ "description": "Toggle for send on enter behavior"
801
+ },
802
+
803
+ "show_system_messages": "Show System Messages",
804
+ "@show_system_messages": {
805
+ "description": "Toggle for system messages visibility"
806
+ },
807
+
808
+ "haptic_feedback": "Haptic Feedback",
809
+ "@haptic_feedback": {
810
+ "description": "Toggle for haptic feedback"
811
+ },
812
+
813
+ "enable_mcp": "Enable MCP",
814
+ "@enable_mcp": {
815
+ "description": "Toggle for MCP feature"
816
+ },
817
+
818
+ "new_chat_mcp_default": "New Chat MCP Default",
819
+ "@new_chat_mcp_default": {
820
+ "description": "Toggle for MCP default in new chats"
821
+ },
822
+
823
+ "show_data_indicator": "Show Data Indicator",
824
+ "@show_data_indicator": {
825
+ "description": "Toggle for data indicator visibility"
826
+ },
827
+
828
+ "privacy_info": "\"LocalMind never sees your data\"",
829
+ "@privacy_info": {
830
+ "description": "Privacy information text"
831
+ },
832
+
833
+ "delete_all_conversations": "Delete All Conversations",
834
+ "@delete_all_conversations": {
835
+ "description": "Dangerous action button to clear all conversations"
836
+ },
837
+
838
+ "reset_settings_defaults": "Reset Settings to Defaults",
839
+ "@reset_settings_defaults": {
840
+ "description": "Button to reset all settings"
841
+ },
842
+
843
+ "chat_title": "LocalMind",
844
+ "@chat_title": {
845
+ "description": "Fallback chat screen title"
846
+ },
847
+
848
+ "chat_parameters_tooltip": "Chat Parameters",
849
+ "@chat_parameters_tooltip": {
850
+ "description": "Tooltip for chat parameters button"
851
+ },
852
+
853
+ "change_persona": "Change Persona",
854
+ "@change_persona": {
855
+ "description": "Menu item to change persona"
856
+ },
857
+
858
+ "set_persona": "Set Persona",
859
+ "@set_persona": {
860
+ "description": "Menu item to set persona"
861
+ },
862
+
863
+ "remove_persona": "Remove Persona",
864
+ "@remove_persona": {
865
+ "description": "Menu item to remove persona"
866
+ },
867
+
868
+ "clear_conversation": "Clear Conversation",
869
+ "@clear_conversation": {
870
+ "description": "Menu item to clear conversation"
871
+ },
872
+
873
+ "connection_error": "Connection error. Check your server.",
874
+ "@connection_error": {
875
+ "description": "Connection error banner text"
876
+ },
877
+
878
+ "disconnected": "Disconnected from server.",
879
+ "@disconnected": {
880
+ "description": "Disconnected banner text"
881
+ },
882
+
883
+ "configure": "Configure",
884
+ "@configure": {
885
+ "description": "Configure action button"
886
+ },
887
+
888
+ "select_model": "Select Model",
889
+ "@select_model": {
890
+ "description": "Model selection placeholder"
891
+ },
892
+
893
+ "select_persona": "Select Persona",
894
+ "@select_persona": {
895
+ "description": "Persona selection sheet title"
896
+ },
897
+
898
+ "start_conversation": "Start a conversation",
899
+ "@start_conversation": {
900
+ "description": "Empty state heading"
901
+ },
902
+
903
+ "recent_chats": "Recent chats",
904
+ "@recent_chats": {
905
+ "description": "Recent chats section label"
906
+ },
907
+
908
+ "see_all": "See all",
909
+ "@see_all": {
910
+ "description": "See all link text"
911
+ },
912
+
913
+ "quick_write": "Help me write a function",
914
+ "@quick_write": {
915
+ "description": "Quick prompt chip text"
916
+ },
917
+
918
+ "quick_explain": "Explain this code",
919
+ "@quick_explain": {
920
+ "description": "Quick prompt chip text"
921
+ },
922
+
923
+ "quick_debug": "Debug this for me",
924
+ "@quick_debug": {
925
+ "description": "Quick prompt chip text"
926
+ },
927
+
928
+ "quick_async": "How do I use async/await?",
929
+ "@quick_async": {
930
+ "description": "Quick prompt chip text"
931
+ },
932
+
933
+ "history_missing_title": "History Missing",
934
+ "@history_missing_title": {
935
+ "description": "Corrupted chat error title"
936
+ },
937
+
938
+ "history_missing_desc": "Either the messages in this chat were deleted or the history record is corrupted.",
939
+ "@history_missing_desc": {
940
+ "description": "Corrupted chat error description"
941
+ },
942
+
943
+ "technical_details": "Technical Details",
944
+ "@technical_details": {
945
+ "description": "Button for debug details"
946
+ },
947
+
948
+ "last_error": "Last Error:",
949
+ "@last_error": {
950
+ "description": "Diagnostic label"
951
+ },
952
+
953
+ "copy_info": "Copy Info",
954
+ "@copy_info": {
955
+ "description": "Button to copy debug info"
956
+ },
957
+
958
+ "conversation_id": "Conversation ID",
959
+ "@conversation_id": {
960
+ "description": "Debug row label"
961
+ },
962
+
963
+ "created_at": "Created At",
964
+ "@created_at": {
965
+ "description": "Debug row label"
966
+ },
967
+
968
+ "expected_messages": "Expected Messages",
969
+ "@expected_messages": {
970
+ "description": "Debug row label"
971
+ },
972
+
973
+ "debug_dialog_desc": "Diagnostic information to help identify synchronization issues.",
974
+ "@debug_dialog_desc": {
975
+ "description": "Debug dialog description"
976
+ },
977
+
978
+ "chat_input_hint": "Ask anything",
979
+ "@chat_input_hint": {
980
+ "description": "Chat input field hint"
981
+ },
982
+
983
+ "send_message_tooltip": "Send message",
984
+ "@send_message_tooltip": {
985
+ "description": "Tooltip for send button"
986
+ },
987
+
988
+ "stop_generation_tooltip": "Stop generation",
989
+ "@stop_generation_tooltip": {
990
+ "description": "Tooltip for stop button"
991
+ },
992
+
993
+ "attach_images_tooltip": "Attach images",
994
+ "@attach_images_tooltip": {
995
+ "description": "Tooltip for attach button"
996
+ },
997
+
998
+ "tool_label": "Tool: {toolCallId}",
999
+ "@tool_label": {
1000
+ "description": "Tool call label in chat bubble",
1001
+ "placeholders": {
1002
+ "toolCallId": {
1003
+ "type": "String",
1004
+ "example": "call_abc123"
1005
+ }
1006
+ }
1007
+ },
1008
+
1009
+ "tool_unknown": "Tool: Unknown",
1010
+ "@tool_unknown": {
1011
+ "description": "Unknown tool call fallback"
1012
+ },
1013
+
1014
+ "message_options": "Message options",
1015
+ "@message_options": {
1016
+ "description": "Message actions sheet title"
1017
+ },
1018
+
1019
+ "copy_markdown": "Copy as Markdown",
1020
+ "@copy_markdown": {
1021
+ "description": "Copy as markdown option"
1022
+ },
1023
+
1024
+ "copied_markdown": "Copied as Markdown",
1025
+ "@copied_markdown": {
1026
+ "description": "Copied as markdown snackbar"
1027
+ },
1028
+
1029
+ "read_aloud": "Read Aloud",
1030
+ "@read_aloud": {
1031
+ "description": "Read aloud action"
1032
+ },
1033
+
1034
+ "stop_reading": "Stop Reading",
1035
+ "@stop_reading": {
1036
+ "description": "Stop reading action"
1037
+ },
1038
+
1039
+ "more": "More",
1040
+ "@more": {
1041
+ "description": "More actions label"
1042
+ },
1043
+
1044
+ "character_count": "{length} characters",
1045
+ "@character_count": {
1046
+ "description": "Character count display",
1047
+ "placeholders": {
1048
+ "length": {
1049
+ "type": "int",
1050
+ "example": "150"
1051
+ }
1052
+ }
1053
+ },
1054
+
1055
+ "edit_message": "Edit message",
1056
+ "@edit_message": {
1057
+ "description": "Edit message dialog title"
1058
+ },
1059
+
1060
+ "edit_message_desc": "Saving will remove the assistant response below and regenerate.",
1061
+ "@edit_message_desc": {
1062
+ "description": "Edit message dialog description"
1063
+ },
1064
+
1065
+ "save_regenerate": "Save & regenerate",
1066
+ "@save_regenerate": {
1067
+ "description": "Save and regenerate button"
1068
+ },
1069
+
1070
+ "chat_settings_title": "Chat Settings",
1071
+ "@chat_settings_title": {
1072
+ "description": "Chat settings sheet title"
1073
+ },
1074
+
1075
+ "reset_defaults": "Reset Defaults",
1076
+ "@reset_defaults": {
1077
+ "description": "Reset to defaults button"
1078
+ },
1079
+
1080
+ "parameters_tab": "Parameters",
1081
+ "@parameters_tab": {
1082
+ "description": "Chat settings parameters tab"
1083
+ },
1084
+
1085
+ "mcp_tab": "MCP",
1086
+ "@mcp_tab": {
1087
+ "description": "Chat settings MCP tab"
1088
+ },
1089
+
1090
+ "temperature": "Temperature",
1091
+ "@temperature": {
1092
+ "description": "Temperature slider label"
1093
+ },
1094
+
1095
+ "temperature_desc": "Controls randomness: Higher = Creative, Lower = Focused",
1096
+ "@temperature_desc": {
1097
+ "description": "Temperature description"
1098
+ },
1099
+
1100
+ "top_p": "Top P",
1101
+ "@top_p": {
1102
+ "description": "Top P slider label"
1103
+ },
1104
+
1105
+ "top_p_desc": "Nucleus sampling threshold",
1106
+ "@top_p_desc": {
1107
+ "description": "Top P description"
1108
+ },
1109
+
1110
+ "max_tokens": "Max Tokens",
1111
+ "@max_tokens": {
1112
+ "description": "Max tokens input label"
1113
+ },
1114
+
1115
+ "max_tokens_desc": "Response limit",
1116
+ "@max_tokens_desc": {
1117
+ "description": "Max tokens description"
1118
+ },
1119
+
1120
+ "context_length": "Context Length",
1121
+ "@context_length": {
1122
+ "description": "Context length input label"
1123
+ },
1124
+
1125
+ "context_length_desc": "History window",
1126
+ "@context_length_desc": {
1127
+ "description": "Context length description"
1128
+ },
1129
+
1130
+ "mcp_disabled_warning": "MCP is disabled globally. Enable it in Settings to use these features.",
1131
+ "@mcp_disabled_warning": {
1132
+ "description": "Warning when MCP is disabled"
1133
+ },
1134
+
1135
+ "mcp_enable_chat": "Enable MCP for this chat",
1136
+ "@mcp_enable_chat": {
1137
+ "description": "Toggle to enable MCP for current chat"
1138
+ },
1139
+
1140
+ "auto_execute_tools": "Auto-execute tools",
1141
+ "@auto_execute_tools": {
1142
+ "description": "Toggle for auto-executing tools"
1143
+ },
1144
+
1145
+ "add_ephemeral_mcp": "Add Ephemeral MCP Server",
1146
+ "@add_ephemeral_mcp": {
1147
+ "description": "Section label for adding temp MCP server"
1148
+ },
1149
+
1150
+ "mcp_label_placeholder": "Label",
1151
+ "@mcp_label_placeholder": {
1152
+ "description": "MCP label field placeholder"
1153
+ },
1154
+
1155
+ "mcp_url_placeholder": "URL (https://...)",
1156
+ "@mcp_url_placeholder": {
1157
+ "description": "MCP URL field placeholder"
1158
+ },
1159
+
1160
+ "active_integrations": "Active Integrations",
1161
+ "@active_integrations": {
1162
+ "description": "Active MCP integrations section"
1163
+ },
1164
+
1165
+ "enable_notifications": "Enable Notifications",
1166
+ "@enable_notifications": {
1167
+ "description": "Notification banner title"
1168
+ },
1169
+
1170
+ "enable_notifications_desc": "Get notified when models finish downloading.",
1171
+ "@enable_notifications_desc": {
1172
+ "description": "Notification banner subtitle"
1173
+ },
1174
+
1175
+ "chat_history_title": "Chat History",
1176
+ "@chat_history_title": {
1177
+ "description": "Chat history screen title"
1178
+ },
1179
+
1180
+ "conversation_just_now": "Just now",
1181
+ "@conversation_just_now": {
1182
+ "description": "Recent conversation timestamp"
1183
+ },
1184
+
1185
+ "conversation_minutes_ago": "{minutes}m ago",
1186
+ "@conversation_minutes_ago": {
1187
+ "description": "Minutes ago timestamp",
1188
+ "placeholders": {
1189
+ "minutes": {
1190
+ "type": "int",
1191
+ "example": "5"
1192
+ }
1193
+ }
1194
+ },
1195
+
1196
+ "conversation_hours_ago": "{hours}h ago",
1197
+ "@conversation_hours_ago": {
1198
+ "description": "Hours ago timestamp",
1199
+ "placeholders": {
1200
+ "hours": {
1201
+ "type": "int",
1202
+ "example": "3"
1203
+ }
1204
+ }
1205
+ },
1206
+
1207
+ "conversation_yesterday": "Yesterday",
1208
+ "@conversation_yesterday": {
1209
+ "description": "Yesterday timestamp"
1210
+ },
1211
+
1212
+ "conversation_days_ago": "{days}d ago",
1213
+ "@conversation_days_ago": {
1214
+ "description": "Days ago timestamp",
1215
+ "placeholders": {
1216
+ "days": {
1217
+ "type": "int",
1218
+ "example": "7"
1219
+ }
1220
+ }
1221
+ },
1222
+
1223
+ "conversation_date": "{month}/{day}/{year}",
1224
+ "@conversation_date": {
1225
+ "description": "Full date timestamp",
1226
+ "placeholders": {
1227
+ "month": {
1228
+ "type": "int",
1229
+ "example": "1"
1230
+ },
1231
+ "day": {
1232
+ "type": "int",
1233
+ "example": "15"
1234
+ },
1235
+ "year": {
1236
+ "type": "int",
1237
+ "example": "2025"
1238
+ }
1239
+ }
1240
+ },
1241
+
1242
+ "options_tooltip": "Options",
1243
+ "@options_tooltip": {
1244
+ "description": "Options menu tooltip"
1245
+ },
1246
+
1247
+ "no_results_found": "No results found",
1248
+ "@no_results_found": {
1249
+ "description": "Search empty state"
1250
+ },
1251
+
1252
+ "no_conversations_yet": "No conversations yet",
1253
+ "@no_conversations_yet": {
1254
+ "description": "Empty conversations state"
1255
+ },
1256
+
1257
+ "try_different_search": "Try a different search term",
1258
+ "@try_different_search": {
1259
+ "description": "Search empty state subtitle"
1260
+ },
1261
+
1262
+ "start_new_conversation": "Start a new conversation",
1263
+ "@start_new_conversation": {
1264
+ "description": "Empty state subtitle when no conversations"
1265
+ },
1266
+
1267
+ "rename_conversation": "Rename conversation",
1268
+ "@rename_conversation": {
1269
+ "description": "Rename dialog title"
1270
+ },
1271
+
1272
+ "enter_new_title": "Enter new title",
1273
+ "@enter_new_title": {
1274
+ "description": "Rename text field hint"
1275
+ },
1276
+
1277
+ "pinned_section": "PINNED",
1278
+ "@pinned_section": {
1279
+ "description": "Pinned conversations section header"
1280
+ },
1281
+
1282
+ "today_section": "TODAY",
1283
+ "@today_section": {
1284
+ "description": "Today section header"
1285
+ },
1286
+
1287
+ "yesterday_section": "YESTERDAY",
1288
+ "@yesterday_section": {
1289
+ "description": "Yesterday section header"
1290
+ },
1291
+
1292
+ "previous_7_days": "PREVIOUS 7 DAYS",
1293
+ "@previous_7_days": {
1294
+ "description": "Previous 7 days section header"
1295
+ },
1296
+
1297
+ "previous_30_days": "PREVIOUS 30 DAYS",
1298
+ "@previous_30_days": {
1299
+ "description": "Previous 30 days section header"
1300
+ },
1301
+
1302
+ "older_section": "OLDER",
1303
+ "@older_section": {
1304
+ "description": "Older conversations section header"
1305
+ },
1306
+
1307
+ "onboarding_localmind": "LOCALMIND",
1308
+ "@onboarding_localmind": {
1309
+ "description": "Onboarding screen branding text"
1310
+ },
1311
+
1312
+ "onboarding_connect_server": "Connect Your\nServer",
1313
+ "@onboarding_connect_server": {
1314
+ "description": "Onboarding heading for server connection"
1315
+ },
1316
+
1317
+ "onboarding_connect_desc": "Connect to LM Studio, Ollama, or\nOpenRouter to start your private AI\nexperience.",
1318
+ "@onboarding_connect_desc": {
1319
+ "description": "Onboarding server connection description"
1320
+ },
1321
+
1322
+ "server_type_on_device": "On-Device",
1323
+ "@server_type_on_device": {
1324
+ "description": "On-device server type card title"
1325
+ },
1326
+
1327
+ "server_type_on_device_sub": "NO SERVER NEEDED",
1328
+ "@server_type_on_device_sub": {
1329
+ "description": "On-device server type card subtitle"
1330
+ },
1331
+
1332
+ "server_type_lm_studio": "LM Studio",
1333
+ "@server_type_lm_studio": {
1334
+ "description": "LM Studio server type card title"
1335
+ },
1336
+
1337
+ "server_type_lm_studio_sub": "LOCAL API",
1338
+ "@server_type_lm_studio_sub": {
1339
+ "description": "LM Studio server type card subtitle"
1340
+ },
1341
+
1342
+ "server_type_ollama": "Ollama",
1343
+ "@server_type_ollama": {
1344
+ "description": "Ollama server type card title"
1345
+ },
1346
+
1347
+ "server_type_ollama_sub": "CLI ENGINE",
1348
+ "@server_type_ollama_sub": {
1349
+ "description": "Ollama server type card subtitle"
1350
+ },
1351
+
1352
+ "server_type_openrouter": "OpenRouter",
1353
+ "@server_type_openrouter": {
1354
+ "description": "OpenRouter server type card title"
1355
+ },
1356
+
1357
+ "server_type_openrouter_sub": "UNIFIED CLOUD",
1358
+ "@server_type_openrouter_sub": {
1359
+ "description": "OpenRouter server type card subtitle"
1360
+ },
1361
+
1362
+ "ready_continue": "READY TO CONTINUE",
1363
+ "@ready_continue": {
1364
+ "description": "Onboarding ready status"
1365
+ },
1366
+
1367
+ "waiting_selection": "WAITING FOR SELECTION",
1368
+ "@waiting_selection": {
1369
+ "description": "Onboarding waiting status"
1370
+ },
1371
+
1372
+ "setup_connection": "Setup Connection",
1373
+ "@setup_connection": {
1374
+ "description": "Onboarding setup screen title"
1375
+ },
1376
+
1377
+ "setup_connection_desc": "Configure your {server} server to start chatting.",
1378
+ "@setup_connection_desc": {
1379
+ "description": "Onboarding setup description",
1380
+ "placeholders": {
1381
+ "server": {
1382
+ "type": "String",
1383
+ "example": "LM Studio"
1384
+ }
1385
+ }
1386
+ },
1387
+
1388
+ "server_name": "Server Name",
1389
+ "@server_name": {
1390
+ "description": "Server name field label"
1391
+ },
1392
+
1393
+ "name_required": "Name required",
1394
+ "@name_required": {
1395
+ "description": "Validation: name required"
1396
+ },
1397
+
1398
+ "name_max_50": "Max 50 characters",
1399
+ "@name_max_50": {
1400
+ "description": "Validation: max 50 chars"
1401
+ },
1402
+
1403
+ "host_label": "Host / IP Address",
1404
+ "@host_label": {
1405
+ "description": "Host field label"
1406
+ },
1407
+
1408
+ "host_required": "Host required",
1409
+ "@host_required": {
1410
+ "description": "Validation: host required"
1411
+ },
1412
+
1413
+ "port_label": "Port",
1414
+ "@port_label": {
1415
+ "description": "Port field label"
1416
+ },
1417
+
1418
+ "port_required": "Port required",
1419
+ "@port_required": {
1420
+ "description": "Validation: port required"
1421
+ },
1422
+
1423
+ "port_invalid": "Must be a number",
1424
+ "@port_invalid": {
1425
+ "description": "Validation: invalid port number"
1426
+ },
1427
+
1428
+ "port_range": "Enter a valid port (1-65535)",
1429
+ "@port_range": {
1430
+ "description": "Validation: port range"
1431
+ },
1432
+
1433
+ "api_key_required": "API Key *",
1434
+ "@api_key_required": {
1435
+ "description": "Required API key label"
1436
+ },
1437
+
1438
+ "api_key_optional": "API Key (Optional)",
1439
+ "@api_key_optional": {
1440
+ "description": "Optional API key label"
1441
+ },
1442
+
1443
+ "api_key_required_openrouter": "API Key required for OpenRouter",
1444
+ "@api_key_required_openrouter": {
1445
+ "description": "Validation: API key for OpenRouter"
1446
+ },
1447
+
1448
+ "api_key_format": "OpenRouter API keys start with sk-",
1449
+ "@api_key_format": {
1450
+ "description": "Validation: API key format"
1451
+ },
1452
+
1453
+ "my_server_hint": "My Server",
1454
+ "@my_server_hint": {
1455
+ "description": "Server name field hint"
1456
+ },
1457
+
1458
+ "name_length_validation": "Name must be 50 characters or less",
1459
+ "@name_length_validation": {
1460
+ "description": "Validation: name too long"
1461
+ },
1462
+
1463
+ "host_valid": "Enter a valid hostname or IP address",
1464
+ "@host_valid": {
1465
+ "description": "Validation: invalid host"
1466
+ },
1467
+
1468
+ "api_key_hint_openrouter": "sk-...",
1469
+ "@api_key_hint_openrouter": {
1470
+ "description": "OpenRouter API key hint"
1471
+ },
1472
+
1473
+ "api_key_hint_generic": "For authenticated servers",
1474
+ "@api_key_hint_generic": {
1475
+ "description": "Generic API key hint"
1476
+ },
1477
+
1478
+ "update_server": "Update Server",
1479
+ "@update_server": {
1480
+ "description": "Update server button"
1481
+ },
1482
+
1483
+ "save_server": "Save Server",
1484
+ "@save_server": {
1485
+ "description": "Save server button"
1486
+ },
1487
+
1488
+ "server_updated": "Server updated",
1489
+ "@server_updated": {
1490
+ "description": "Server update snackbar"
1491
+ },
1492
+
1493
+ "server_added": "Server added",
1494
+ "@server_added": {
1495
+ "description": "Server added snackbar"
1496
+ },
1497
+
1498
+ "download_model_title": "Download a Model",
1499
+ "@download_model_title": {
1500
+ "description": "Onboarding download model screen title"
1501
+ },
1502
+
1503
+ "download_model_desc": "Choose a model to download.\nIt will run locally on your device.",
1504
+ "@download_model_desc": {
1505
+ "description": "Onboarding download model description"
1506
+ },
1507
+
1508
+ "on_device_android_only": "On-device inference is currently available on Android only.",
1509
+ "@on_device_android_only": {
1510
+ "description": "Platform limitation notice"
1511
+ },
1512
+
1513
+ "total_ram": "Total RAM",
1514
+ "@total_ram": {
1515
+ "description": "Total RAM label"
1516
+ },
1517
+
1518
+ "available": "Available",
1519
+ "@available": {
1520
+ "description": "Available label"
1521
+ },
1522
+
1523
+ "ram_min_required": "{fileSize} GB RAM min",
1524
+ "@ram_min_required": {
1525
+ "description": "Minimum RAM requirement label",
1526
+ "placeholders": {
1527
+ "fileSize": {
1528
+ "type": "String",
1529
+ "example": "4"
1530
+ }
1531
+ }
1532
+ },
1533
+
1534
+ "download_progress": "{percent}% • {speed}",
1535
+ "@download_progress": {
1536
+ "description": "Download progress display",
1537
+ "placeholders": {
1538
+ "percent": {
1539
+ "type": "String",
1540
+ "example": "45"
1541
+ },
1542
+ "speed": {
1543
+ "type": "String",
1544
+ "example": "2.5 MB/s"
1545
+ }
1546
+ }
1547
+ },
1548
+
1549
+ "eta_label": "ETA: {eta}",
1550
+ "@eta_label": {
1551
+ "description": "Estimated time remaining label",
1552
+ "placeholders": {
1553
+ "eta": {
1554
+ "type": "String",
1555
+ "example": "5 minutes"
1556
+ }
1557
+ }
1558
+ },
1559
+
1560
+ "paused_progress": "Paused - {percent}%",
1561
+ "@paused_progress": {
1562
+ "description": "Paused download status with progress",
1563
+ "placeholders": {
1564
+ "percent": {
1565
+ "type": "String",
1566
+ "example": "45"
1567
+ }
1568
+ }
1569
+ },
1570
+
1571
+ "ram_warning_body_download": "This model requires at least {ram} GB RAM, but your device has {totalMemory}. It may not run correctly or could cause the app to crash.",
1572
+ "@ram_warning_body_download": {
1573
+ "description": "RAM warning during download",
1574
+ "placeholders": {
1575
+ "ram": {
1576
+ "type": "String",
1577
+ "example": "4"
1578
+ },
1579
+ "totalMemory": {
1580
+ "type": "String",
1581
+ "example": "6 GB"
1582
+ }
1583
+ }
1584
+ },
1585
+
1586
+ "ram_warning_body_load": "Your device has {availableRAM} available RAM, but this model recommends at least {ram} GB. Loading it might fail or cause instability.",
1587
+ "@ram_warning_body_load": {
1588
+ "description": "RAM warning during model load",
1589
+ "placeholders": {
1590
+ "availableRAM": {
1591
+ "type": "String",
1592
+ "example": "2 GB"
1593
+ },
1594
+ "ram": {
1595
+ "type": "String",
1596
+ "example": "4"
1597
+ }
1598
+ }
1599
+ },
1600
+
1601
+ "choose_theme": "Choose Theme",
1602
+ "@choose_theme": {
1603
+ "description": "Onboarding theme selection title"
1604
+ },
1605
+
1606
+ "choose_theme_desc": "Personalize the app appearance. You can always change this later in settings.",
1607
+ "@choose_theme_desc": {
1608
+ "description": "Onboarding theme selection description"
1609
+ },
1610
+
1611
+ "theme_card_system": "System",
1612
+ "@theme_card_system": {
1613
+ "description": "System theme card title"
1614
+ },
1615
+
1616
+ "theme_card_system_sub": "Matches your device settings",
1617
+ "@theme_card_system_sub": {
1618
+ "description": "System theme card subtitle"
1619
+ },
1620
+
1621
+ "theme_card_light": "Light",
1622
+ "@theme_card_light": {
1623
+ "description": "Light theme card title"
1624
+ },
1625
+
1626
+ "theme_card_light_sub": "Clean and bright",
1627
+ "@theme_card_light_sub": {
1628
+ "description": "Light theme card subtitle"
1629
+ },
1630
+
1631
+ "theme_card_dark": "Dark",
1632
+ "@theme_card_dark": {
1633
+ "description": "Dark theme card title"
1634
+ },
1635
+
1636
+ "theme_card_dark_sub": "Easy on the eyes",
1637
+ "@theme_card_dark_sub": {
1638
+ "description": "Dark theme card subtitle"
1639
+ },
1640
+
1641
+ "theme_card_claude": "Claude",
1642
+ "@theme_card_claude": {
1643
+ "description": "Claude theme card title"
1644
+ },
1645
+
1646
+ "theme_card_claude_sub": "A warm, peach-tinted theme",
1647
+ "@theme_card_claude_sub": {
1648
+ "description": "Claude theme card subtitle"
1649
+ },
1650
+
1651
+ "stay_updated": "Stay Updated",
1652
+ "@stay_updated": {
1653
+ "description": "Onboarding notification screen heading"
1654
+ },
1655
+
1656
+ "stay_updated_desc": "Get notified when your AI models finish downloading or when long-running tasks complete.",
1657
+ "@stay_updated_desc": {
1658
+ "description": "Onboarding notification screen description"
1659
+ },
1660
+
1661
+ "notification_benefit_downloads": "Model download progress",
1662
+ "@notification_benefit_downloads": {
1663
+ "description": "Notification benefit list item"
1664
+ },
1665
+
1666
+ "notification_benefit_completions": "Generation completions",
1667
+ "@notification_benefit_completions": {
1668
+ "description": "Notification benefit list item"
1669
+ },
1670
+
1671
+ "notification_benefit_background": "Background tasks status",
1672
+ "@notification_benefit_background": {
1673
+ "description": "Notification benefit list item"
1674
+ },
1675
+
1676
+ "allow_notifications": "Allow Notifications",
1677
+ "@allow_notifications": {
1678
+ "description": "Allow notifications button"
1679
+ },
1680
+
1681
+ "servers_title": "Servers",
1682
+ "@servers_title": {
1683
+ "description": "Server list screen title"
1684
+ },
1685
+
1686
+ "no_servers_yet": "No Servers Yet",
1687
+ "@no_servers_yet": {
1688
+ "description": "Empty server list title"
1689
+ },
1690
+
1691
+ "no_servers_desc": "Add your first server to start chatting with AI models.",
1692
+ "@no_servers_desc": {
1693
+ "description": "Empty server list description"
1694
+ },
1695
+
1696
+ "add_server": "Add Server",
1697
+ "@add_server": {
1698
+ "description": "Add server button"
1699
+ },
1700
+
1701
+ "switched_to_server": "Switched to {name}",
1702
+ "@switched_to_server": {
1703
+ "description": "Server switch snackbar",
1704
+ "placeholders": {
1705
+ "name": {
1706
+ "type": "String",
1707
+ "example": "My Server"
1708
+ }
1709
+ }
1710
+ },
1711
+
1712
+ "edit_server": "Edit Server",
1713
+ "@edit_server": {
1714
+ "description": "Edit server screen title"
1715
+ },
1716
+
1717
+ "add_server_title": "Add Server",
1718
+ "@add_server_title": {
1719
+ "description": "Add server screen title"
1720
+ },
1721
+
1722
+ "server_type_label": "Server Type",
1723
+ "@server_type_label": {
1724
+ "description": "Server type section label"
1725
+ },
1726
+
1727
+ "server_icon_label": "Server Icon",
1728
+ "@server_icon_label": {
1729
+ "description": "Server icon section label"
1730
+ },
1731
+
1732
+ "default_icon": "Default icon",
1733
+ "@default_icon": {
1734
+ "description": "Default icon fallback text"
1735
+ },
1736
+
1737
+ "server_type_lm_studio_display": "LM Studio",
1738
+ "@server_type_lm_studio_display": {
1739
+ "description": "LM Studio server type display"
1740
+ },
1741
+
1742
+ "server_type_openai_display": "OpenAI Compatible",
1743
+ "@server_type_openai_display": {
1744
+ "description": "OpenAI compatible server type display"
1745
+ },
1746
+
1747
+ "server_type_ollama_display": "Ollama",
1748
+ "@server_type_ollama_display": {
1749
+ "description": "Ollama server type display"
1750
+ },
1751
+
1752
+ "server_type_openrouter_display": "OpenRouter",
1753
+ "@server_type_openrouter_display": {
1754
+ "description": "OpenRouter server type display"
1755
+ },
1756
+
1757
+ "server_type_on_device_display": "On-Device",
1758
+ "@server_type_on_device_display": {
1759
+ "description": "On-device server type display"
1760
+ },
1761
+
1762
+ "server_address_openrouter": "openrouter.ai",
1763
+ "@server_address_openrouter": {
1764
+ "description": "OpenRouter default address"
1765
+ },
1766
+
1767
+ "server_address_on_device": "Local inference",
1768
+ "@server_address_on_device": {
1769
+ "description": "On-device server address"
1770
+ },
1771
+
1772
+ "server_address_format": "{host}:{port}",
1773
+ "@server_address_format": {
1774
+ "description": "Server address display format",
1775
+ "placeholders": {
1776
+ "host": {
1777
+ "type": "String",
1778
+ "example": "192.168.1.1"
1779
+ },
1780
+ "port": {
1781
+ "type": "String",
1782
+ "example": "1234"
1783
+ }
1784
+ }
1785
+ },
1786
+
1787
+ "default_badge": "Default",
1788
+ "@default_badge": {
1789
+ "description": "Default server badge"
1790
+ },
1791
+
1792
+ "set_as_default": "Set as Default",
1793
+ "@set_as_default": {
1794
+ "description": "Set as default menu item"
1795
+ },
1796
+
1797
+ "select_icon": "Select Icon",
1798
+ "@select_icon": {
1799
+ "description": "Icon picker sheet title"
1800
+ },
1801
+
1802
+ "select_icon_desc": "Choose an icon for your server",
1803
+ "@select_icon_desc": {
1804
+ "description": "Icon picker sheet description"
1805
+ },
1806
+
1807
+ "search_icons_hint": "Search icons...",
1808
+ "@search_icons_hint": {
1809
+ "description": "Icon search field placeholder"
1810
+ },
1811
+
1812
+ "server_icon_stack": "Server Stack",
1813
+ "@server_icon_stack": {
1814
+ "description": "Server icon name"
1815
+ },
1816
+
1817
+ "server_icon_stack2": "Server Stack 02",
1818
+ "@server_icon_stack2": {
1819
+ "description": "Server icon name"
1820
+ },
1821
+
1822
+ "server_icon_stack3": "Server Stack 03",
1823
+ "@server_icon_stack3": {
1824
+ "description": "Server icon name"
1825
+ },
1826
+
1827
+ "server_icon_cloud": "Cloud",
1828
+ "@server_icon_cloud": {
1829
+ "description": "Server icon name"
1830
+ },
1831
+
1832
+ "server_icon_cloud_server": "Cloud Server",
1833
+ "@server_icon_cloud_server": {
1834
+ "description": "Server icon name"
1835
+ },
1836
+
1837
+ "server_icon_mcp": "MCP Server",
1838
+ "@server_icon_mcp": {
1839
+ "description": "Server icon name"
1840
+ },
1841
+
1842
+ "server_icon_database": "Database",
1843
+ "@server_icon_database": {
1844
+ "description": "Server icon name"
1845
+ },
1846
+
1847
+ "server_icon_database1": "Database 01",
1848
+ "@server_icon_database1": {
1849
+ "description": "Server icon name"
1850
+ },
1851
+
1852
+ "server_icon_database2": "Database 02",
1853
+ "@server_icon_database2": {
1854
+ "description": "Server icon name"
1855
+ },
1856
+
1857
+ "server_icon_cpu": "CPU",
1858
+ "@server_icon_cpu": {
1859
+ "description": "Server icon name"
1860
+ },
1861
+
1862
+ "server_icon_chip": "Chip",
1863
+ "@server_icon_chip": {
1864
+ "description": "Server icon name"
1865
+ },
1866
+
1867
+ "server_icon_chip2": "Chip 02",
1868
+ "@server_icon_chip2": {
1869
+ "description": "Server icon name"
1870
+ },
1871
+
1872
+ "server_icon_computer": "Computer",
1873
+ "@server_icon_computer": {
1874
+ "description": "Server icon name"
1875
+ },
1876
+
1877
+ "server_icon_laptop": "Laptop",
1878
+ "@server_icon_laptop": {
1879
+ "description": "Server icon name"
1880
+ },
1881
+
1882
+ "server_icon_terminal": "Computer Terminal",
1883
+ "@server_icon_terminal": {
1884
+ "description": "Server icon name"
1885
+ },
1886
+
1887
+ "server_icon_code": "Code",
1888
+ "@server_icon_code": {
1889
+ "description": "Server icon name"
1890
+ },
1891
+
1892
+ "server_icon_ai_brain": "AI Brain",
1893
+ "@server_icon_ai_brain": {
1894
+ "description": "Server icon name"
1895
+ },
1896
+
1897
+ "server_icon_ai_brain2": "AI Brain 02",
1898
+ "@server_icon_ai_brain2": {
1899
+ "description": "Server icon name"
1900
+ },
1901
+
1902
+ "server_icon_ai_cloud": "AI Cloud",
1903
+ "@server_icon_ai_cloud": {
1904
+ "description": "Server icon name"
1905
+ },
1906
+
1907
+ "server_icon_ai_network": "AI Network",
1908
+ "@server_icon_ai_network": {
1909
+ "description": "Server icon name"
1910
+ },
1911
+
1912
+ "server_icon_ai_chat": "AI Chat",
1913
+ "@server_icon_ai_chat": {
1914
+ "description": "Server icon name"
1915
+ },
1916
+
1917
+ "server_icon_cellular": "Cellular Network",
1918
+ "@server_icon_cellular": {
1919
+ "description": "Server icon name"
1920
+ },
1921
+
1922
+ "server_icon_plug1": "Plug 01",
1923
+ "@server_icon_plug1": {
1924
+ "description": "Server icon name"
1925
+ },
1926
+
1927
+ "server_icon_plug2": "Plug 02",
1928
+ "@server_icon_plug2": {
1929
+ "description": "Server icon name"
1930
+ },
1931
+
1932
+ "server_icon_bot": "Bot",
1933
+ "@server_icon_bot": {
1934
+ "description": "Server icon name"
1935
+ },
1936
+
1937
+ "server_icon_bot2": "Bot 02",
1938
+ "@server_icon_bot2": {
1939
+ "description": "Server icon name"
1940
+ },
1941
+
1942
+ "server_icon_robotic": "Robotic",
1943
+ "@server_icon_robotic": {
1944
+ "description": "Server icon name"
1945
+ },
1946
+
1947
+ "server_icon_rocket": "Rocket",
1948
+ "@server_icon_rocket": {
1949
+ "description": "Server icon name"
1950
+ },
1951
+
1952
+ "server_icon_star": "Star",
1953
+ "@server_icon_star": {
1954
+ "description": "Server icon name"
1955
+ },
1956
+
1957
+ "server_icon_settings1": "Settings 01",
1958
+ "@server_icon_settings1": {
1959
+ "description": "Server icon name"
1960
+ },
1961
+
1962
+ "server_icon_settings2": "Settings 02",
1963
+ "@server_icon_settings2": {
1964
+ "description": "Server icon name"
1965
+ },
1966
+
1967
+ "server_icon_home1": "Home 01",
1968
+ "@server_icon_home1": {
1969
+ "description": "Server icon name"
1970
+ },
1971
+
1972
+ "server_icon_home2": "Home 02",
1973
+ "@server_icon_home2": {
1974
+ "description": "Server icon name"
1975
+ },
1976
+
1977
+ "server_icon_folder1": "Folder 01",
1978
+ "@server_icon_folder1": {
1979
+ "description": "Server icon name"
1980
+ },
1981
+
1982
+ "server_icon_folder2": "Folder 02",
1983
+ "@server_icon_folder2": {
1984
+ "description": "Server icon name"
1985
+ },
1986
+
1987
+ "server_icon_file1": "File 01",
1988
+ "@server_icon_file1": {
1989
+ "description": "Server icon name"
1990
+ },
1991
+
1992
+ "server_icon_lock": "Lock",
1993
+ "@server_icon_lock": {
1994
+ "description": "Server icon name"
1995
+ },
1996
+
1997
+ "server_icon_key": "Key 01",
1998
+ "@server_icon_key": {
1999
+ "description": "Server icon name"
2000
+ },
2001
+
2002
+ "server_icon_link": "Link 01",
2003
+ "@server_icon_link": {
2004
+ "description": "Server icon name"
2005
+ },
2006
+
2007
+ "server_icon_globe": "Globe",
2008
+ "@server_icon_globe": {
2009
+ "description": "Server icon name"
2010
+ },
2011
+
2012
+ "server_icon_api": "API",
2013
+ "@server_icon_api": {
2014
+ "description": "Server icon name"
2015
+ },
2016
+
2017
+ "server_icon_arrow_right": "Arrow Right 01",
2018
+ "@server_icon_arrow_right": {
2019
+ "description": "Server icon name"
2020
+ },
2021
+
2022
+ "server_icon_check": "Check Circle",
2023
+ "@server_icon_check": {
2024
+ "description": "Server icon name"
2025
+ },
2026
+
2027
+ "server_icon_alert": "Alert Circle",
2028
+ "@server_icon_alert": {
2029
+ "description": "Server icon name"
2030
+ },
2031
+
2032
+ "server_icon_info": "Info Circle",
2033
+ "@server_icon_info": {
2034
+ "description": "Server icon name"
2035
+ },
2036
+
2037
+ "server_icon_zap": "Zap",
2038
+ "@server_icon_zap": {
2039
+ "description": "Server icon name"
2040
+ },
2041
+
2042
+ "server_icon_cloud_upload": "Cloud Upload",
2043
+ "@server_icon_cloud_upload": {
2044
+ "description": "Server icon name"
2045
+ },
2046
+
2047
+ "server_icon_cloud_download": "Cloud Download",
2048
+ "@server_icon_cloud_download": {
2049
+ "description": "Server icon name"
2050
+ },
2051
+
2052
+ "server_icon_refresh": "Refresh",
2053
+ "@server_icon_refresh": {
2054
+ "description": "Server icon name"
2055
+ },
2056
+
2057
+ "server_icon_hard_drive": "Hard Drive",
2058
+ "@server_icon_hard_drive": {
2059
+ "description": "Server icon name"
2060
+ },
2061
+
2062
+ "server_icon_drive": "Drive",
2063
+ "@server_icon_drive": {
2064
+ "description": "Server icon name"
2065
+ },
2066
+
2067
+ "personas_title": "Personas",
2068
+ "@personas_title": {
2069
+ "description": "Persona list screen title"
2070
+ },
2071
+
2072
+ "persona_category_general": "General",
2073
+ "@persona_category_general": {
2074
+ "description": "General persona category"
2075
+ },
2076
+
2077
+ "persona_category_coding": "Coding",
2078
+ "@persona_category_coding": {
2079
+ "description": "Coding persona category"
2080
+ },
2081
+
2082
+ "persona_category_education": "Education",
2083
+ "@persona_category_education": {
2084
+ "description": "Education persona category"
2085
+ },
2086
+
2087
+ "persona_category_creative": "Creative",
2088
+ "@persona_category_creative": {
2089
+ "description": "Creative persona category"
2090
+ },
2091
+
2092
+ "persona_builtin_section": "BUILT-IN",
2093
+ "@persona_builtin_section": {
2094
+ "description": "Built-in personas section label"
2095
+ },
2096
+
2097
+ "persona_my_section": "MY PERSONAS",
2098
+ "@persona_my_section": {
2099
+ "description": "My personas section label"
2100
+ },
2101
+
2102
+ "clone_edit": "Clone & Edit",
2103
+ "@clone_edit": {
2104
+ "description": "Clone and edit persona action"
2105
+ },
2106
+
2107
+ "builtin_badge": "Built-in",
2108
+ "@builtin_badge": {
2109
+ "description": "Built-in persona badge"
2110
+ },
2111
+
2112
+ "no_personas_found": "No personas found",
2113
+ "@no_personas_found": {
2114
+ "description": "Empty persona list title"
2115
+ },
2116
+
2117
+ "no_personas_desc": "Create your first persona to customize AI behavior.",
2118
+ "@no_personas_desc": {
2119
+ "description": "Empty persona list subtitle"
2120
+ },
2121
+
2122
+ "edit_persona": "Edit Persona",
2123
+ "@edit_persona": {
2124
+ "description": "Edit persona screen title"
2125
+ },
2126
+
2127
+ "create_persona": "Create Persona",
2128
+ "@create_persona": {
2129
+ "description": "Create persona screen title"
2130
+ },
2131
+
2132
+ "create_persona_button": "Create",
2133
+ "@create_persona_button": {
2134
+ "description": "Create persona action button"
2135
+ },
2136
+
2137
+ "emoji_label": "Emoji",
2138
+ "@emoji_label": {
2139
+ "description": "Emoji selection label"
2140
+ },
2141
+
2142
+ "name_label": "Name",
2143
+ "@name_label": {
2144
+ "description": "Name field label"
2145
+ },
2146
+
2147
+ "my_persona_hint": "My Persona",
2148
+ "@my_persona_hint": {
2149
+ "description": "Persona name hint"
2150
+ },
2151
+
2152
+ "category_label": "Category",
2153
+ "@category_label": {
2154
+ "description": "Category field label"
2155
+ },
2156
+
2157
+ "description_optional": "Description (optional)",
2158
+ "@description_optional": {
2159
+ "description": "Optional description label"
2160
+ },
2161
+
2162
+ "description_hint": "What this persona does...",
2163
+ "@description_hint": {
2164
+ "description": "Description field hint"
2165
+ },
2166
+
2167
+ "system_prompt": "System Prompt",
2168
+ "@system_prompt": {
2169
+ "description": "System prompt section label"
2170
+ },
2171
+
2172
+ "character_count_max": "{currentLen}/4000",
2173
+ "@character_count_max": {
2174
+ "description": "Character counter for system prompt",
2175
+ "placeholders": {
2176
+ "currentLen": {
2177
+ "type": "int",
2178
+ "example": "150"
2179
+ }
2180
+ }
2181
+ },
2182
+
2183
+ "no_prompt_placeholder": "No prompt yet...",
2184
+ "@no_prompt_placeholder": {
2185
+ "description": "Preview placeholder when no prompt"
2186
+ },
2187
+
2188
+ "prompt_hint": "You are a helpful assistant...",
2189
+ "@prompt_hint": {
2190
+ "description": "System prompt text field hint"
2191
+ },
2192
+
2193
+ "prompt_required": "System prompt is required",
2194
+ "@prompt_required": {
2195
+ "description": "Validation: prompt required"
2196
+ },
2197
+
2198
+ "prompt_max_chars": "Max 4000 characters",
2199
+ "@prompt_max_chars": {
2200
+ "description": "Validation: prompt max chars"
2201
+ },
2202
+
2203
+ "advanced_settings": "Advanced Settings",
2204
+ "@advanced_settings": {
2205
+ "description": "Expandable advanced settings section"
2206
+ },
2207
+
2208
+ "temperature_label": "Temperature (0.0-2.0)",
2209
+ "@temperature_label": {
2210
+ "description": "Temperature field label in personas"
2211
+ },
2212
+
2213
+ "top_p_label": "Top P (0.0-1.0)",
2214
+ "@top_p_label": {
2215
+ "description": "Top P field label in personas"
2216
+ },
2217
+
2218
+ "temp_hint": "0.7",
2219
+ "@temp_hint": {
2220
+ "description": "Temperature field hint"
2221
+ },
2222
+
2223
+ "top_p_hint": "0.9",
2224
+ "@top_p_hint": {
2225
+ "description": "Top P field hint"
2226
+ },
2227
+
2228
+ "range_0_2": "0.0-2.0",
2229
+ "@range_0_2": {
2230
+ "description": "Range validation error (temperature)"
2231
+ },
2232
+
2233
+ "range_0_1": "0.0-1.0",
2234
+ "@range_0_1": {
2235
+ "description": "Range validation error (top P)"
2236
+ },
2237
+
2238
+ "persona_updated": "Persona updated",
2239
+ "@persona_updated": {
2240
+ "description": "Persona update snackbar"
2241
+ },
2242
+
2243
+ "persona_created": "Persona created",
2244
+ "@persona_created": {
2245
+ "description": "Persona creation snackbar"
2246
+ },
2247
+
2248
+ "tts_models_title": "Text To Speech Models",
2249
+ "@tts_models_title": {
2250
+ "description": "TTS model manager screen title"
2251
+ },
2252
+
2253
+ "always_available": "Always available",
2254
+ "@always_available": {
2255
+ "description": "Always available status"
2256
+ },
2257
+
2258
+ "tts_system_desc": "Uses your device's built-in text-to-speech engine.\nNo downloads required. Voice selection uses your device's system settings.",
2259
+ "@tts_system_desc": {
2260
+ "description": "System TTS engine description"
2261
+ },
2262
+
2263
+ "downloading_status": "Downloading...",
2264
+ "@downloading_status": {
2265
+ "description": "Downloading status text"
2266
+ },
2267
+
2268
+ "tts_kitten_desc": "Lightning-fast neural TTS with 8 expressive voices.\nRequires {size} download.",
2269
+ "@tts_kitten_desc": {
2270
+ "description": "Kitten TTS engine description",
2271
+ "placeholders": {
2272
+ "size": {
2273
+ "type": "String",
2274
+ "example": "250 MB"
2275
+ }
2276
+ }
2277
+ },
2278
+
2279
+ "tts_piper_desc": "Fast offline Piper voices with 2 expressive voices.\nRequires {size} download per voice.",
2280
+ "@tts_piper_desc": {
2281
+ "description": "Piper TTS engine description",
2282
+ "placeholders": {
2283
+ "size": {
2284
+ "type": "String",
2285
+ "example": "50 MB"
2286
+ }
2287
+ }
2288
+ },
2289
+
2290
+ "engine_spec": "{sizeMb} MB · {ramMb} MB RAM · {voiceCount} voices",
2291
+ "@engine_spec": {
2292
+ "description": "Engine specification display",
2293
+ "placeholders": {
2294
+ "sizeMb": {
2295
+ "type": "String",
2296
+ "example": "250"
2297
+ },
2298
+ "ramMb": {
2299
+ "type": "String",
2300
+ "example": "100"
2301
+ },
2302
+ "voiceCount": {
2303
+ "type": "int",
2304
+ "example": "8"
2305
+ }
2306
+ }
2307
+ },
2308
+
2309
+ "on_device_models_title": "On-Device Models",
2310
+ "@on_device_models_title": {
2311
+ "description": "On-device model manager screen title"
2312
+ },
2313
+
2314
+ "available_models": "Available Models",
2315
+ "@available_models": {
2316
+ "description": "Available models section title"
2317
+ },
2318
+
2319
+ "device_memory": "Device Memory",
2320
+ "@device_memory": {
2321
+ "description": "Device memory card title"
2322
+ },
2323
+
2324
+ "ram_usage": "RAM Usage",
2325
+ "@ram_usage": {
2326
+ "description": "RAM usage label"
2327
+ },
2328
+
2329
+ "memory_healthy": "Healthy",
2330
+ "@memory_healthy": {
2331
+ "description": "Memory status healthy"
2332
+ },
2333
+
2334
+ "memory_critical": "Critical",
2335
+ "@memory_critical": {
2336
+ "description": "Memory status critical"
2337
+ },
2338
+
2339
+ "memory_low": "Low",
2340
+ "@memory_low": {
2341
+ "description": "Memory status low"
2342
+ },
2343
+
2344
+ "ram_used": "{percent}% used",
2345
+ "@ram_used": {
2346
+ "description": "RAM usage percentage",
2347
+ "placeholders": {
2348
+ "percent": {
2349
+ "type": "String",
2350
+ "example": "45"
2351
+ }
2352
+ }
2353
+ },
2354
+
2355
+ "available_ram": "Available RAM",
2356
+ "@available_ram": {
2357
+ "description": "Available RAM stat label"
2358
+ },
2359
+
2360
+ "total_capacity": "Total Capacity",
2361
+ "@total_capacity": {
2362
+ "description": "Total capacity stat label"
2363
+ },
2364
+
2365
+ "loaded_status": "Loaded",
2366
+ "@loaded_status": {
2367
+ "description": "Model loaded status"
2368
+ },
2369
+
2370
+ "inference_backend": "Inference Backend",
2371
+ "@inference_backend": {
2372
+ "description": "Backend selector section title"
2373
+ },
2374
+
2375
+ "backend_ios_notice": "Only CPU backend is available on iOS.",
2376
+ "@backend_ios_notice": {
2377
+ "description": "iOS backend limitation notice"
2378
+ },
2379
+
2380
+ "backend_cpu_desc": "Works on all devices. Most compatible.",
2381
+ "@backend_cpu_desc": {
2382
+ "description": "CPU backend description"
2383
+ },
2384
+
2385
+ "backend_gpu_desc": "OpenCL acceleration. Faster on supported devices.",
2386
+ "@backend_gpu_desc": {
2387
+ "description": "GPU backend description"
2388
+ },
2389
+
2390
+ "backend_npu_desc": "Vendor NPU (Qualcomm/MediaTek). Fastest inference.",
2391
+ "@backend_npu_desc": {
2392
+ "description": "NPU backend description"
2393
+ },
2394
+
2395
+ "select_model_title": "Select Model",
2396
+ "@select_model_title": {
2397
+ "description": "Model picker sheet title"
2398
+ },
2399
+
2400
+ "refresh_models": "Refresh models",
2401
+ "@refresh_models": {
2402
+ "description": "Refresh models tooltip"
2403
+ },
2404
+
2405
+ "search_models_hint": "Search models...",
2406
+ "@search_models_hint": {
2407
+ "description": "Model search field hint"
2408
+ },
2409
+
2410
+ "no_server_connected": "No server connected",
2411
+ "@no_server_connected": {
2412
+ "description": "Model picker empty state title"
2413
+ },
2414
+
2415
+ "add_server_first": "Add a server first to see available models.",
2416
+ "@add_server_first": {
2417
+ "description": "Model picker empty state subtitle"
2418
+ },
2419
+
2420
+ "failed_load_models": "Failed to load models",
2421
+ "@failed_load_models": {
2422
+ "description": "Model load error title"
2423
+ },
2424
+
2425
+ "no_models_available": "No models available",
2426
+ "@no_models_available": {
2427
+ "description": "Empty models state"
2428
+ },
2429
+
2430
+ "no_models_match": "No models match \"{searchQuery}\"",
2431
+ "@no_models_match": {
2432
+ "description": "No search results for models",
2433
+ "placeholders": {
2434
+ "searchQuery": {
2435
+ "type": "String",
2436
+ "example": "gpt"
2437
+ }
2438
+ }
2439
+ },
2440
+
2441
+ "model_load_failed": "Failed to load model: {error}",
2442
+ "@model_load_failed": {
2443
+ "description": "Model load failure snackbar",
2444
+ "placeholders": {
2445
+ "error": {
2446
+ "type": "String",
2447
+ "example": "Out of memory"
2448
+ }
2449
+ }
2450
+ },
2451
+
2452
+ "model_unloaded_ollama": "{name} will be unloaded once the keep-alive time passes",
2453
+ "@model_unloaded_ollama": {
2454
+ "description": "Ollama model unloaded status",
2455
+ "placeholders": {
2456
+ "name": {
2457
+ "type": "String",
2458
+ "example": "codellama"
2459
+ }
2460
+ }
2461
+ },
2462
+
2463
+ "model_unloaded_success": "{name} unloaded successfully",
2464
+ "@model_unloaded_success": {
2465
+ "description": "Model unloaded success snackbar",
2466
+ "placeholders": {
2467
+ "name": {
2468
+ "type": "String",
2469
+ "example": "codellama"
2470
+ }
2471
+ }
2472
+ },
2473
+
2474
+ "model_unload_failed": "Failed to unload model: {error}",
2475
+ "@model_unload_failed": {
2476
+ "description": "Model unload failure snackbar",
2477
+ "placeholders": {
2478
+ "error": {
2479
+ "type": "String",
2480
+ "example": "Connection error"
2481
+ }
2482
+ }
2483
+ },
2484
+
2485
+ "unload_from_server": "Unload from server",
2486
+ "@unload_from_server": {
2487
+ "description": "Unload model tooltip"
2488
+ },
2489
+
2490
+ "context_chip": "{ctx} ctx",
2491
+ "@context_chip": {
2492
+ "description": "Context length chip",
2493
+ "placeholders": {
2494
+ "ctx": {
2495
+ "type": "String",
2496
+ "example": "8192"
2497
+ }
2498
+ }
2499
+ },
2500
+
2501
+ "download_notification_title": "Downloading {modelName}...",
2502
+ "@download_notification_title": {
2503
+ "description": "Download notification title",
2504
+ "placeholders": {
2505
+ "modelName": {
2506
+ "type": "String",
2507
+ "example": "Qwen 3"
2508
+ }
2509
+ }
2510
+ },
2511
+
2512
+ "download_complete_notification": "Download complete!",
2513
+ "@download_complete_notification": {
2514
+ "description": "Download complete notification title"
2515
+ },
2516
+
2517
+ "download_complete_body": "{modelName} has been downloaded successfully.",
2518
+ "@download_complete_body": {
2519
+ "description": "Download complete notification body",
2520
+ "placeholders": {
2521
+ "modelName": {
2522
+ "type": "String",
2523
+ "example": "Qwen 3"
2524
+ }
2525
+ }
2526
+ },
2527
+
2528
+ "download_failed_notification": "Download failed: {error}",
2529
+ "@download_failed_notification": {
2530
+ "description": "Download failed notification title",
2531
+ "placeholders": {
2532
+ "error": {
2533
+ "type": "String",
2534
+ "example": "Network error"
2535
+ }
2536
+ }
2537
+ },
2538
+
2539
+ "download_failed_body": "Failed to download {modelName}.",
2540
+ "@download_failed_body": {
2541
+ "description": "Download failed notification body",
2542
+ "placeholders": {
2543
+ "modelName": {
2544
+ "type": "String",
2545
+ "example": "Qwen 3"
2546
+ }
2547
+ }
2548
+ },
2549
+
2550
+ "engine_name_system": "System TTS",
2551
+ "@engine_name_system": {
2552
+ "description": "System TTS engine display name"
2553
+ },
2554
+
2555
+ "engine_tagline_system": "Built-in device engine",
2556
+ "@engine_tagline_system": {
2557
+ "description": "System TTS engine tagline"
2558
+ },
2559
+
2560
+ "engine_name_kitten": "Kitten TTS",
2561
+ "@engine_name_kitten": {
2562
+ "description": "Kitten TTS engine display name"
2563
+ },
2564
+
2565
+ "engine_tagline_kitten": "High-speed neural TTS",
2566
+ "@engine_tagline_kitten": {
2567
+ "description": "Kitten TTS engine tagline"
2568
+ },
2569
+
2570
+ "engine_name_sherpa": "Sherpa ONNX VITS",
2571
+ "@engine_name_sherpa": {
2572
+ "description": "Sherpa ONNX engine display name"
2573
+ },
2574
+
2575
+ "engine_tagline_sherpa": "Offline Piper voices",
2576
+ "@engine_tagline_sherpa": {
2577
+ "description": "Sherpa ONNX engine tagline"
2578
+ },
2579
+
2580
+ "voice_jasper": "Jasper",
2581
+ "@voice_jasper": {
2582
+ "description": "Kitten TTS voice name"
2583
+ },
2584
+
2585
+ "voice_bella": "Bella",
2586
+ "@voice_bella": {
2587
+ "description": "Kitten TTS voice name"
2588
+ },
2589
+
2590
+ "voice_bruno": "Bruno",
2591
+ "@voice_bruno": {
2592
+ "description": "Kitten TTS voice name"
2593
+ },
2594
+
2595
+ "voice_luna": "Luna",
2596
+ "@voice_luna": {
2597
+ "description": "Kitten TTS voice name"
2598
+ },
2599
+
2600
+ "voice_hugo": "Hugo",
2601
+ "@voice_hugo": {
2602
+ "description": "Kitten TTS voice name"
2603
+ },
2604
+
2605
+ "voice_rosie": "Rosie",
2606
+ "@voice_rosie": {
2607
+ "description": "Kitten TTS voice name"
2608
+ },
2609
+
2610
+ "voice_leo": "Leo",
2611
+ "@voice_leo": {
2612
+ "description": "Kitten TTS voice name"
2613
+ },
2614
+
2615
+ "voice_kiki": "Kiki",
2616
+ "@voice_kiki": {
2617
+ "description": "Kitten TTS voice name"
2618
+ },
2619
+
2620
+ "voice_lessac": "Lessac (US)",
2621
+ "@voice_lessac": {
2622
+ "description": "Piper voice name"
2623
+ },
2624
+
2625
+ "voice_ryan": "Ryan (US)",
2626
+ "@voice_ryan": {
2627
+ "description": "Piper voice name"
2628
+ },
2629
+
2630
+ "model_qwen_3": "Qwen 3 0.6B",
2631
+ "@model_qwen_3": {
2632
+ "description": "On-device model name"
2633
+ },
2634
+
2635
+ "model_qwen_3_desc": "Smallest general-purpose chat model. Fast responses, low memory usage.",
2636
+ "@model_qwen_3_desc": {
2637
+ "description": "Qwen 3 model description"
2638
+ },
2639
+
2640
+ "model_license_apache": "Apache-2.0",
2641
+ "@model_license_apache": {
2642
+ "description": "Apache license label"
2643
+ },
2644
+
2645
+ "model_qwen_25": "Qwen 2.5 1.5B Instruct",
2646
+ "@model_qwen_25": {
2647
+ "description": "On-device model name"
2648
+ },
2649
+
2650
+ "model_qwen_25_desc": "Balanced quality and size. Good for general conversation.",
2651
+ "@model_qwen_25_desc": {
2652
+ "description": "Qwen 2.5 model description"
2653
+ },
2654
+
2655
+ "model_deepseek": "DeepSeek R1 Distill Qwen 1.5B",
2656
+ "@model_deepseek": {
2657
+ "description": "On-device model name"
2658
+ },
2659
+
2660
+ "model_deepseek_desc": "Reasoning and chain-of-thought model. Best for logical tasks.",
2661
+ "@model_deepseek_desc": {
2662
+ "description": "DeepSeek model description"
2663
+ },
2664
+
2665
+ "model_license_mit": "MIT",
2666
+ "@model_license_mit": {
2667
+ "description": "MIT license label"
2668
+ },
2669
+
2670
+ "model_gemma": "Gemma 4 E2B Instruct",
2671
+ "@model_gemma": {
2672
+ "description": "On-device model name"
2673
+ },
2674
+
2675
+ "model_gemma_desc": "Google flagship model. Highest quality, requires more RAM.",
2676
+ "@model_gemma_desc": {
2677
+ "description": "Gemma model description"
2678
+ },
2679
+
2680
+ "export_header": "*Exported from LocalMind — {date}*",
2681
+ "@export_header": {
2682
+ "description": "Markdown export header",
2683
+ "placeholders": {
2684
+ "date": {
2685
+ "type": "String",
2686
+ "example": "May 17, 2025"
2687
+ }
2688
+ }
2689
+ },
2690
+
2691
+ "export_role_user": "## 👤 User",
2692
+ "@export_role_user": {
2693
+ "description": "Markdown export user role label"
2694
+ },
2695
+
2696
+ "export_role_assistant": "## 🤖 Assistant",
2697
+ "@export_role_assistant": {
2698
+ "description": "Markdown export assistant role label"
2699
+ },
2700
+
2701
+ "export_role_system": "## ⚙️ System",
2702
+ "@export_role_system": {
2703
+ "description": "Markdown export system role label"
2704
+ },
2705
+
2706
+ "export_role_tool": "## 🔧 Tool",
2707
+ "@export_role_tool": {
2708
+ "description": "Markdown export tool role label"
2709
+ },
2710
+
2711
+ "export_text_user": "[USER]",
2712
+ "@export_text_user": {
2713
+ "description": "Text export user role prefix"
2714
+ },
2715
+
2716
+ "export_text_assistant": "[ASSISTANT]",
2717
+ "@export_text_assistant": {
2718
+ "description": "Text export assistant role prefix"
2719
+ },
2720
+
2721
+ "export_text_system": "[SYSTEM]",
2722
+ "@export_text_system": {
2723
+ "description": "Text export system role prefix"
2724
+ },
2725
+
2726
+ "export_text_tool": "[TOOL]",
2727
+ "@export_text_tool": {
2728
+ "description": "Text export tool role prefix"
2729
+ },
2730
+
2731
+ "export_label_user": "USER",
2732
+ "@export_label_user": {
2733
+ "description": "Export user role label"
2734
+ },
2735
+
2736
+ "export_label_assistant": "ASSISTANT",
2737
+ "@export_label_assistant": {
2738
+ "description": "Export assistant role label"
2739
+ },
2740
+
2741
+ "export_label_system": "SYSTEM",
2742
+ "@export_label_system": {
2743
+ "description": "Export system role label"
2744
+ },
2745
+
2746
+ "export_label_tool": "TOOL",
2747
+ "@export_label_tool": {
2748
+ "description": "Export tool role label"
2749
+ },
2750
+
2751
+ "select_model_hint": "Select a model to start chatting",
2752
+ "@select_model_hint": {
2753
+ "description": "Fallback text when no model is selected"
2754
+ },
2755
+
2756
+ "test_notification_title": "Test notification",
2757
+ "@test_notification_title": {
2758
+ "description": "Test notification title"
2759
+ },
2760
+
2761
+ "test_notification_body": "This is a test notification for model download progress.",
2762
+ "@test_notification_body": {
2763
+ "description": "Test notification body"
2764
+ }
2765
+}
lib/l10n/app_es.arb
new
+495
@@ -0,0 +1,495 @@
1
+{
2
+ "@@locale": "es",
3
+ "active": "Activo",
4
+ "active_integrations": "Integraciones activas",
5
+ "add_ephemeral_mcp": "Añadir servidor MCP efímero",
6
+ "add_server": "Añadir servidor",
7
+ "add_server_first": "Añada un servidor primero para ver los modelos disponibles.",
8
+ "add_server_title": "Añadir servidor",
9
+ "advanced_settings": "Ajustes avanzados",
10
+ "all": "Todo",
11
+ "allow_notifications": "Permitir notificaciones",
12
+ "always_available": "Siempre disponible",
13
+ "api_key_format": "Las claves de API de OpenRouter comienzan con sk-",
14
+ "api_key_hint_generic": "Para servidores autenticados",
15
+ "api_key_hint_openrouter": "sk-...",
16
+ "api_key_optional": "Clave API (Opcional)",
17
+ "api_key_required": "Clave API *",
18
+ "api_key_required_openrouter": "Clave API requerida para OpenRouter",
19
+ "app_name": "LocalMind",
20
+ "app_tagline": "Su IA. Su dispositivo. Sus normas.",
21
+ "app_version": "1.0.0",
22
+ "attach_images_tooltip": "Adjuntar imágenes",
23
+ "auto_execute_tools": "Ejecutar herramientas automáticamente",
24
+ "auto_generate_titles": "Autogenerar títulos",
25
+ "available": "Disponible",
26
+ "available_models": "Modelos disponibles",
27
+ "available_ram": "RAM disponible",
28
+ "backend_cpu_desc": "Funciona en todos los dispositivos. Más compatible.",
29
+ "backend_gpu_desc": "Aceleración OpenCL. Más rápido en dispositivos compatibles.",
30
+ "backend_ios_notice": "Solo el motor de CPU está disponible en iOS.",
31
+ "backend_npu_desc": "NPU del fabricante (Qualcomm/MediaTek). Inferencia más rápida.",
32
+ "builtin_badge": "Integrado",
33
+ "calculating": "Calculando...",
34
+ "cancel": "Cancelar",
35
+ "cannot_undo": "Esta acción no se puede deshacer.",
36
+ "category_label": "Categoría",
37
+ "change_persona": "Cambiar personaje",
38
+ "character_count": "{length} caracteres",
39
+ "character_count_max": "{currentLen}/4000",
40
+ "chat_history_title": "Historial de chat",
41
+ "chat_input_hint": "Pregunte cualquier cosa",
42
+ "chat_parameters_tooltip": "Parámetros del chat",
43
+ "chat_settings_title": "Ajustes de chat",
44
+ "chat_title": "LocalMind",
45
+ "choose_theme": "Elegir tema",
46
+ "choose_theme_desc": "Personalice la apariencia de la aplicación. Siempre puede cambiar esto más tarde en los ajustes.",
47
+ "clear": "Limpiar",
48
+ "clear_conversation": "Limpiar conversación",
49
+ "clear_conversation_body": "Esto eliminará todos los mensajes de esta conversación.",
50
+ "clear_conversation_title": "¿Limpiar conversación?",
51
+ "clone_edit": "Clonar y editar",
52
+ "close": "Cerrar",
53
+ "code_theme_dark": "Tema de código (Oscuro)",
54
+ "code_theme_desc": "Elija el tema de resaltado de sintaxis para los bloques de código.",
55
+ "code_theme_light": "Tema de código (Claro)",
56
+ "configure": "Configurar",
57
+ "configuring_server": "Configurando servidor...",
58
+ "confirm": "Confirmar",
59
+ "connection_error": "Error de conexión. Compruebe su servidor.",
60
+ "connection_failed": "Conexión fallida. Compruebe sus ajustes.",
61
+ "connection_successful": "¡Conexión exitosa!",
62
+ "context_chip": "{ctx} ctx",
63
+ "context_length": "Longitud del contexto",
64
+ "context_length_desc": "Ventana de historial",
65
+ "continue_action": "Continuar",
66
+ "conversation_date": "{month}/{day}/{year}",
67
+ "conversation_days_ago": "hace {days}d",
68
+ "conversation_hours_ago": "hace {hours}h",
69
+ "conversation_id": "ID de conversación",
70
+ "conversation_just_now": "Hace un momento",
71
+ "conversation_minutes_ago": "hace {minutes}m",
72
+ "conversation_yesterday": "Ayer",
73
+ "copied": "¡Copiado!",
74
+ "copied_markdown": "Copiado como Markdown",
75
+ "copied_to_clipboard": "Copiado al portapapeles",
76
+ "copy": "Copiar",
77
+ "copy_info": "Copiar información",
78
+ "copy_markdown": "Copiar como Markdown",
79
+ "create_persona": "Crear personaje",
80
+ "create_persona_button": "Crear",
81
+ "created_at": "Creado el",
82
+ "debug_dialog_desc": "Información de diagnóstico para ayudar a identificar problemas de sincronización.",
83
+ "default_badge": "Predeterminado",
84
+ "default_icon": "Icono predeterminado",
85
+ "delete": "Eliminar",
86
+ "delete_all_conversations": "Eliminar todas las conversaciones",
87
+ "delete_conversation": "¿Eliminar conversación?",
88
+ "delete_conversation_body": "¿Está seguro de que desea eliminar \"{title}\"? Esto no se puede deshacer.",
89
+ "delete_conversation_title": "¿Eliminar conversación?",
90
+ "delete_message_title": "¿Eliminar mensaje?",
91
+ "delete_model_body": "¿Está seguro de que desea eliminar {name}?",
92
+ "delete_model_body_with_size": "¿Está seguro de que desea eliminar {name}? Esto liberará aproximadamente {size} de espacio.\n\nPuede volver a descargar este modelo más tarde si es necesario.",
93
+ "delete_model_title": "Eliminar modelo",
94
+ "delete_persona_body": "Esto no se puede deshacer.",
95
+ "delete_persona_title": "¿Eliminar \"{name}\"?",
96
+ "delete_server_body": "¿Está seguro de que desea eliminar \"{name}\"? Esto no se puede deshacer.",
97
+ "delete_server_title": "Eliminar servidor",
98
+ "delete_voice_body": "¿Está seguro de que desea eliminar {name}? Esto liberará aproximadamente {size} de espacio.\n\nPuede volver a descargar esta voz más tarde si es necesario.",
99
+ "delete_voice_title": "Eliminar voz",
100
+ "description_hint": "Lo que hace este personaje...",
101
+ "description_optional": "Descripción (opcional)",
102
+ "device_memory": "Memoria del dispositivo",
103
+ "disconnected": "Desconectado del servidor.",
104
+ "done": "Listo",
105
+ "download": "Descargar",
106
+ "download_complete_body": "{modelName} se ha descargado correctamente.",
107
+ "download_complete_notification": "¡Descarga completa!",
108
+ "download_failed": "Descarga fallida",
109
+ "download_failed_body": "No se pudo descargar {modelName}.",
110
+ "download_failed_notification": "Descarga fallida: {error}",
111
+ "download_model_desc": "Elija un modelo para descargar.\nSe ejecutará localmente en su dispositivo.",
112
+ "download_model_title": "Descargar un modelo",
113
+ "download_notification_title": "Descargando {modelName}...",
114
+ "download_progress": "{percent}% • {speed}",
115
+ "downloaded": "Descargado",
116
+ "downloading_status": "Descargando...",
117
+ "edit": "Editar",
118
+ "edit_message": "Editar mensaje",
119
+ "edit_message_desc": "Al guardar se eliminará la respuesta del asistente que aparece a continuación y se volverá a generar.",
120
+ "edit_persona": "Editar personaje",
121
+ "edit_server": "Editar servidor",
122
+ "emoji_label": "Emoji",
123
+ "enable": "Habilitar",
124
+ "enable_mcp": "Habilitar MCP",
125
+ "enable_notifications": "Habilitar notificaciones",
126
+ "enable_notifications_desc": "Reciba notificaciones cuando los modelos terminen de descargarse.",
127
+ "engine_name_kitten": "Kitten TTS",
128
+ "engine_name_sherpa": "Sherpa ONNX VITS",
129
+ "engine_name_system": "TTS del sistema",
130
+ "engine_spec": "{sizeMb} MB · {ramMb} MB de RAM · {voiceCount} voces",
131
+ "engine_tagline_kitten": "TTS neural de alta velocidad",
132
+ "engine_tagline_sherpa": "Voces Piper sin conexión",
133
+ "engine_tagline_system": "Motor integrado en el dispositivo",
134
+ "enter_new_title": "Introduzca un nuevo título",
135
+ "error": "Error",
136
+ "error_with_message": "Error: {error}",
137
+ "eta_label": "Tiempo restante: {eta}",
138
+ "expected_messages": "Mensajes esperados",
139
+ "export_header": "*Exportado de LocalMind — {date}*",
140
+ "export_label_assistant": "ASISTENTE",
141
+ "export_label_system": "SISTEMA",
142
+ "export_label_tool": "HERRAMIENTA",
143
+ "export_label_user": "USUARIO",
144
+ "export_role_assistant": "## 🤖 Asistente",
145
+ "export_role_system": "## ⚙️ Sistema",
146
+ "export_role_tool": "## 🔧 Herramienta",
147
+ "export_role_user": "## 👤 Usuario",
148
+ "export_text_assistant": "[ASISTENTE]",
149
+ "export_text_system": "[SISTEMA]",
150
+ "export_text_tool": "[HERRAMIENTA]",
151
+ "export_text_user": "[USUARIO]",
152
+ "failed_load_models": "No se pudieron cargar los modelos",
153
+ "finish_setup": "Finalizar configuración",
154
+ "font_preview": "El rápido zorro marrón salta sobre el perro perezoso.",
155
+ "font_size": "Tamaño de fuente",
156
+ "font_size_desc": "Ajustar el tamaño del texto en el chat.",
157
+ "haptic_feedback": "Comentarios hápticos",
158
+ "history_missing_desc": "O bien los mensajes de este chat fueron eliminados o el registro de historial está dañado.",
159
+ "history_missing_title": "Historial faltante",
160
+ "host_label": "Host / Dirección IP",
161
+ "host_required": "Host requerido",
162
+ "host_valid": "Introduzca un nombre de host o dirección IP válida",
163
+ "inference_backend": "Motor de inferencia",
164
+ "initializing": "Inicializando...",
165
+ "initializing_services": "Inicializando servicios...",
166
+ "install": "Instalar",
167
+ "installed": "Instalado",
168
+ "label_completed": "{label} completado",
169
+ "language_system_default": "Predeterminado del sistema",
170
+ "last_error": "Último error:",
171
+ "load": "Cargar",
172
+ "loaded_status": "Cargado",
173
+ "loading": "Cargando...",
174
+ "loading_model": "Cargando {modelId}...",
175
+ "loading_model_error": "Error: {error}",
176
+ "manage_on_device_models": "Gestionar modelos locales",
177
+ "manage_servers": "Gestionar servidores",
178
+ "manage_tts_models": "Gestionar modelos TTS",
179
+ "max_tokens": "Máximo de tokens",
180
+ "max_tokens_desc": "Límite de respuesta",
181
+ "may_be_large": "Puede ser demasiado grande para este dispositivo",
182
+ "mcp_disabled_warning": "MCP está deshabilitado globalmente. Habilítelo en Ajustes para usar estas funciones.",
183
+ "mcp_enable_chat": "Habilitar MCP para este chat",
184
+ "mcp_label_placeholder": "Etiqueta",
185
+ "mcp_tab": "MCP",
186
+ "mcp_url_placeholder": "URL (https://...)",
187
+ "memory_critical": "Crítico",
188
+ "memory_healthy": "Saludable",
189
+ "memory_low": "Bajo",
190
+ "message_options": "Opciones de mensaje",
191
+ "model_deepseek": "DeepSeek R1 Distill Qwen 1.5B",
192
+ "model_deepseek_desc": "Modelo de razonamiento y cadena de pensamiento. El mejor para tareas lógicas.",
193
+ "model_gemma": "Gemma 4 E2B Instruct",
194
+ "model_gemma_desc": "Modelo insignia de Google. Máxima calidad, requiere más RAM.",
195
+ "model_license_apache": "Apache-2.0",
196
+ "model_license_mit": "MIT",
197
+ "model_load_failed": "No se pudo cargar el modelo: {error}",
198
+ "model_loaded": "Modelo cargado: {modelId} ({backend})",
199
+ "model_qwen_25": "Qwen 2.5 1.5B Instruct",
200
+ "model_qwen_25_desc": "Calidad y tamaño equilibrados. Bueno para conversación general.",
201
+ "model_qwen_3": "Qwen 3 0.6B",
202
+ "model_qwen_3_desc": "Modelo de chat de propósito general más pequeño. Respuestas rápidas, bajo uso de memoria.",
203
+ "model_unload_failed": "No se pudo descargar el modelo de la memoria: {error}",
204
+ "model_unloaded_ollama": "{name} se liberará de la memoria una vez que pase el tiempo de espera activo",
205
+ "model_unloaded_success": "{name} liberado de memoria correctamente",
206
+ "more": "Más",
207
+ "my_persona_hint": "Mi personaje",
208
+ "my_server_hint": "Mi servidor",
209
+ "name_label": "Nombre",
210
+ "name_length_validation": "El nombre debe tener 50 caracteres o menos",
211
+ "name_max_50": "Máx. 50 caracteres",
212
+ "name_required": "Nombre requerido",
213
+ "nav_history": "Historial",
214
+ "nav_local_models": "Modelos locales",
215
+ "nav_new_chat": "Nuevo chat",
216
+ "nav_personas": "Personajes",
217
+ "nav_servers": "Servidores",
218
+ "nav_settings": "Ajustes",
219
+ "nav_tts": "Texto a voz",
220
+ "new_chat_mcp_default": "Nuevo chat MCP predeterminado",
221
+ "no_conversations_yet": "Aún sin conversaciones",
222
+ "no_model_loaded": "Ningún modelo cargado. Toque \"Gestionar modelos locales\" para descargar y cargar un modelo.",
223
+ "no_models_available": "No hay modelos disponibles",
224
+ "no_models_match": "Ningún modelo coincide con \"{searchQuery}\"",
225
+ "no_personas_desc": "Cree su primer personaje para personalizar el comportamiento de la IA.",
226
+ "no_personas_found": "No se encontraron personajes",
227
+ "no_prompt_placeholder": "Aún sin indicaciones...",
228
+ "no_results_found": "No se encontraron resultados",
229
+ "no_server_connected": "Ningún servidor conectado",
230
+ "no_server_selected": "Ningún servidor seleccionado",
231
+ "no_servers_desc": "Añada su primer servidor para comenzar a chatear con modelos de IA.",
232
+ "no_servers_yet": "Aún sin servidores",
233
+ "none": "Ninguno",
234
+ "none_selected": "Ninguno seleccionado",
235
+ "not_downloaded": "No descargado",
236
+ "not_installed": "No instalado",
237
+ "not_now": "Ahora no",
238
+ "notification_benefit_background": "Estado de las tareas en segundo plano",
239
+ "notification_benefit_completions": "Completados de generación",
240
+ "notification_benefit_downloads": "Progreso de descarga del modelo",
241
+ "offline": "Sin conexión",
242
+ "older_section": "MÁS ANTIGUOS",
243
+ "on_device_android_only": "La inferencia local está disponible actualmente solo en Android.",
244
+ "on_device_models_title": "Modelos locales",
245
+ "onboarding_connect_desc": "Conéctese a LM Studio, Ollama o\nOpenRouter para comenzar su experiencia\nprivada de IA.",
246
+ "onboarding_connect_server": "Conecte su\nservidor",
247
+ "onboarding_localmind": "LOCALMIND",
248
+ "online": "En línea",
249
+ "open_source": "Código abierto",
250
+ "open_source_desc": "LocalMind es de código abierto. Siga nuestro progreso o contribuya en GitHub.",
251
+ "options_tooltip": "Opciones",
252
+ "parameters_tab": "Parámetros",
253
+ "pause": "Pausar",
254
+ "paused_progress": "Pausado - {percent}%",
255
+ "persona_builtin_section": "INTEGRADO",
256
+ "persona_category_coding": "Programación",
257
+ "persona_category_creative": "Creativo",
258
+ "persona_category_education": "Educación",
259
+ "persona_category_general": "General",
260
+ "persona_created": "Personaje creado",
261
+ "persona_my_section": "MIS PERSONAJES",
262
+ "persona_updated": "Personaje actualizado",
263
+ "personas_title": "Personajes",
264
+ "pin": "Fijar",
265
+ "pinned_section": "FIJADOS",
266
+ "port_invalid": "Debe ser un número",
267
+ "port_label": "Puerto",
268
+ "port_range": "Introduzca un puerto válido (1-65535)",
269
+ "port_required": "Puerto requerido",
270
+ "preparing_app": "Preparando aplicación...",
271
+ "preview": "Vista previa",
272
+ "preview_failed": "Vista previa fallida: {error}",
273
+ "previous_30_days": "ÚLTIMOS 30 DÍAS",
274
+ "previous_7_days": "ÚLTIMOS 7 DÍAS",
275
+ "privacy_info": "\"LocalMind nunca ve sus datos\"",
276
+ "proceed_anyway": "Proceder de todos modos",
277
+ "processing": "Procesando...",
278
+ "prompt_hint": "Usted es un asistente servicial...",
279
+ "prompt_max_chars": "Máx. 4000 caracteres",
280
+ "prompt_required": "La instrucción del sistema es requerida",
281
+ "quick_async": "¿Cómo uso async/await?",
282
+ "quick_debug": "Depura esto por mí",
283
+ "quick_explain": "Explica este código",
284
+ "quick_write": "Ayúdame a escribir una función",
285
+ "ram_min_required": "{fileSize} GB de RAM mín.",
286
+ "ram_usage": "Uso de RAM",
287
+ "ram_used": "{percent}% usado",
288
+ "ram_warning": "Advertencia de RAM",
289
+ "ram_warning_body_download": "Este modelo requiere al menos {ram} GB de RAM, pero su dispositivo tiene {totalMemory}. Es posible que no se ejecute correctamente o que cause el cierre de la aplicación.",
290
+ "ram_warning_body_load": "Su dispositivo tiene {availableRAM} de RAM disponible, pero este modelo recomienda al menos {ram} GB. Cargarlo podría fallar o causar inestabilidad.",
291
+ "range_0_1": "0.0-1.0",
292
+ "range_0_2": "0.0-2.0",
293
+ "read_aloud": "Leer en voz alta",
294
+ "ready": "Listo",
295
+ "ready_continue": "LISTO PARA CONTINUAR",
296
+ "recent_chats": "Chats recientes",
297
+ "recommended": "RECOMENDADO",
298
+ "refresh_models": "Actualizar modelos",
299
+ "remove_persona": "Eliminar personaje",
300
+ "rename": "Renombrar",
301
+ "rename_conversation": "Renombrar conversación",
302
+ "reset_defaults": "Restablecer valores predeterminados",
303
+ "reset_settings_defaults": "Restablecer ajustes a los predeterminados",
304
+ "resume": "Reanudar",
305
+ "retry": "Reintentar",
306
+ "save": "Guardar",
307
+ "save_changes": "Guardar cambios",
308
+ "save_continue": "Guardar y continuar",
309
+ "save_regenerate": "Guardar y regenerar",
310
+ "save_server": "Guardar servidor",
311
+ "search_hint": "Buscar conversaciones...",
312
+ "search_icons_hint": "Buscar iconos...",
313
+ "search_models_hint": "Buscar modelos...",
314
+ "see_all": "Ver todo",
315
+ "select": "Seleccionar",
316
+ "select_icon": "Seleccionar icono",
317
+ "select_icon_desc": "Elija un icono para su servidor",
318
+ "select_model": "Seleccionar modelo",
319
+ "select_model_hint": "Seleccione un modelo para empezar a chatear",
320
+ "select_model_title": "Seleccionar modelo",
321
+ "select_persona": "Seleccionar personaje",
322
+ "send_message_tooltip": "Enviar mensaje",
323
+ "send_on_enter": "Enviar al presionar Enter",
324
+ "server_added": "Servidor añadido",
325
+ "server_address_format": "{host}:{port}",
326
+ "server_address_on_device": "Inferencia local",
327
+ "server_address_openrouter": "openrouter.ai",
328
+ "server_icon_ai_brain": "Cerebro de IA",
329
+ "server_icon_ai_brain2": "Cerebro de IA 02",
330
+ "server_icon_ai_chat": "Chat de IA",
331
+ "server_icon_ai_cloud": "Nube de IA",
332
+ "server_icon_ai_network": "Red de IA",
333
+ "server_icon_alert": "Círculo de alerta",
334
+ "server_icon_api": "API",
335
+ "server_icon_arrow_right": "Flecha derecha 01",
336
+ "server_icon_bot": "Bot",
337
+ "server_icon_bot2": "Bot 02",
338
+ "server_icon_cellular": "Red celular",
339
+ "server_icon_check": "Círculo de verificación",
340
+ "server_icon_chip": "Chip",
341
+ "server_icon_chip2": "Chip 02",
342
+ "server_icon_cloud": "Nube",
343
+ "server_icon_cloud_download": "Descarga en la nube",
344
+ "server_icon_cloud_server": "Servidor en la nube",
345
+ "server_icon_cloud_upload": "Subida a la nube",
346
+ "server_icon_code": "Código",
347
+ "server_icon_computer": "Computadora",
348
+ "server_icon_cpu": "CPU",
349
+ "server_icon_database": "Base de datos",
350
+ "server_icon_database1": "Base de datos 01",
351
+ "server_icon_database2": "Base de datos 02",
352
+ "server_icon_drive": "Unidad",
353
+ "server_icon_file1": "Archivo 01",
354
+ "server_icon_folder1": "Carpeta 01",
355
+ "server_icon_folder2": "Carpeta 02",
356
+ "server_icon_globe": "Globo",
357
+ "server_icon_hard_drive": "Disco duro",
358
+ "server_icon_home1": "Inicio 01",
359
+ "server_icon_home2": "Inicio 02",
360
+ "server_icon_info": "Círculo de información",
361
+ "server_icon_key": "Clave 01",
362
+ "server_icon_label": "Icono de servidor",
363
+ "server_icon_laptop": "Laptop",
364
+ "server_icon_link": "Enlace 01",
365
+ "server_icon_lock": "Bloqueo",
366
+ "server_icon_mcp": "Servidor MCP",
367
+ "server_icon_plug1": "Enchufe 01",
368
+ "server_icon_plug2": "Enchufe 02",
369
+ "server_icon_refresh": "Actualizar",
370
+ "server_icon_robotic": "Robótico",
371
+ "server_icon_rocket": "Cohete",
372
+ "server_icon_settings1": "Ajustes 01",
373
+ "server_icon_settings2": "Ajustes 02",
374
+ "server_icon_stack": "Pila de servidores",
375
+ "server_icon_stack2": "Pila de servidores 02",
376
+ "server_icon_stack3": "Pila de servidores 03",
377
+ "server_icon_star": "Estrella",
378
+ "server_icon_terminal": "Terminal de computadora",
379
+ "server_icon_zap": "Zap",
380
+ "server_name": "Nombre de servidor",
381
+ "server_type_label": "Tipo de servidor",
382
+ "server_type_lm_studio": "LM Studio",
383
+ "server_type_lm_studio_display": "LM Studio",
384
+ "server_type_lm_studio_sub": "API LOCAL",
385
+ "server_type_ollama": "Ollama",
386
+ "server_type_ollama_display": "Ollama",
387
+ "server_type_ollama_sub": "MOTOR CLI",
388
+ "server_type_on_device": "Local",
389
+ "server_type_on_device_display": "Local",
390
+ "server_type_on_device_sub": "NO SE REQUIERE SERVIDOR",
391
+ "server_type_openai_display": "Compatible con OpenAI",
392
+ "server_type_openrouter": "OpenRouter",
393
+ "server_type_openrouter_display": "OpenRouter",
394
+ "server_type_openrouter_sub": "NUBE UNIFICADA",
395
+ "server_updated": "Servidor actualizado",
396
+ "servers_title": "Servidores",
397
+ "set_as_default": "Establecer como predeterminado",
398
+ "set_persona": "Establecer personaje",
399
+ "settings_about": "Acerca de",
400
+ "settings_appearance": "Apariencia",
401
+ "settings_behavior": "Comportamiento",
402
+ "settings_data_management": "Gestión de datos",
403
+ "settings_default_persona": "Personaje predeterminado",
404
+ "settings_default_server": "Servidor predeterminado",
405
+ "settings_language": "Idioma",
406
+ "settings_on_device": "Inferencia local",
407
+ "settings_privacy": "Privacidad",
408
+ "settings_title": "Ajustes",
409
+ "settings_tts": "Texto a voz",
410
+ "setup_connection": "Configurar conexión",
411
+ "setup_connection_desc": "Configure su servidor {server} para comenzar a chatear.",
412
+ "share": "Compartir",
413
+ "show_data_indicator": "Mostrar indicador de datos",
414
+ "show_system_messages": "Mostrar mensajes del sistema",
415
+ "skip": "Omitir",
416
+ "something_went_wrong": "Algo salió mal",
417
+ "star_on_github": "Dar estrella en GitHub",
418
+ "start_conversation": "Comenzar una conversación",
419
+ "start_new_chat": "Comenzar nuevo chat",
420
+ "start_new_conversation": "Comenzar una nueva conversación",
421
+ "startup_failed": "Fallo en el inicio",
422
+ "stay_updated": "Manténgase al día",
423
+ "stay_updated_desc": "Reciba notificaciones cuando sus modelos de IA terminen de descargarse o cuando se completen tareas largas.",
424
+ "stop": "Detener",
425
+ "stop_generation_tooltip": "Detener generación",
426
+ "stop_reading": "Detener lectura",
427
+ "streaming_responses": "Respuestas en tiempo real (streaming)",
428
+ "switch_server": "Cambiar de servidor",
429
+ "switch_server_subtitle": "Elija un servidor al que conectarse",
430
+ "switched_to_server": "Cambiado a {name}",
431
+ "system_prompt": "Instrucción del sistema",
432
+ "technical_details": "Detalles técnicos",
433
+ "temp_hint": "0.7",
434
+ "temperature": "Temperatura",
435
+ "temperature_desc": "Controla la aleatoriedad: Mayor = Creativo, Menor = Enfocado",
436
+ "temperature_label": "Temperatura (0.0-2.0)",
437
+ "test_connection": "Probar conexión",
438
+ "test_notification_body": "Esta es una notificación de prueba para el progreso de descarga de modelos.",
439
+ "test_notification_title": "Notificación de prueba",
440
+ "testing": "Probando...",
441
+ "theme": "Tema",
442
+ "theme_card_claude": "Claude",
443
+ "theme_card_claude_sub": "Un tema cálido con tonos durazno",
444
+ "theme_card_dark": "Oscuro",
445
+ "theme_card_dark_sub": "Fácil para los ojos",
446
+ "theme_card_light": "Claro",
447
+ "theme_card_light_sub": "Limpio y brillante",
448
+ "theme_card_system": "Sistema",
449
+ "theme_card_system_sub": "Coincide con los ajustes de su dispositivo",
450
+ "theme_claude": "Claude",
451
+ "theme_dark": "Oscuro",
452
+ "theme_light": "Claro",
453
+ "theme_system": "Sistema",
454
+ "thinking": "Pensando",
455
+ "today_section": "HOY",
456
+ "tool_label": "Herramienta: {toolCallId}",
457
+ "tool_unknown": "Herramienta: Desconocida",
458
+ "top_p": "Top P",
459
+ "top_p_desc": "Umbral de muestreo de núcleo",
460
+ "top_p_hint": "0.9",
461
+ "top_p_label": "Top P (0.0-1.0)",
462
+ "total_capacity": "Capacidad total",
463
+ "total_ram": "RAM total",
464
+ "try_different_search": "Pruebe con un término de búsqueda diferente",
465
+ "tts_engine": "Motor TTS",
466
+ "tts_engine_kitten": "Kitten TTS",
467
+ "tts_engine_system": "TTS del sistema",
468
+ "tts_kitten_desc": "TTS neural ultrarrápido con 8 voces expresivas.\nRequiere descarga de {size}.",
469
+ "tts_models_title": "Modelos de texto a voz",
470
+ "tts_piper_desc": "Voces Piper sin conexión rápidas con 2 voces expresivas.\nRequiere una descarga de {size} por voz.",
471
+ "tts_speed": "Velocidad de TTS",
472
+ "tts_speed_desc": "Ajustar la velocidad de reproducción.",
473
+ "tts_system_desc": "Utiliza el motor de texto a voz integrado de su dispositivo.\nNo requiere descargas. La selección de voz utiliza los ajustes del sistema de su dispositivo.",
474
+ "unknown_error": "Error desconocido",
475
+ "unload": "Liberar de memoria",
476
+ "unload_from_server": "Liberar del servidor",
477
+ "unpin": "Desfijar",
478
+ "update_server": "Actualizar servidor",
479
+ "voice": "Voz",
480
+ "voice_bella": "Bella",
481
+ "voice_bruno": "Bruno",
482
+ "voice_female": "Femenino",
483
+ "voice_hugo": "Hugo",
484
+ "voice_jasper": "Jasper",
485
+ "voice_kiki": "Kiki",
486
+ "voice_leo": "Leo",
487
+ "voice_lessac": "Lessac (US)",
488
+ "voice_luna": "Luna",
489
+ "voice_male": "Masculino",
490
+ "voice_other": "Otro",
491
+ "voice_rosie": "Rosie",
492
+ "voice_ryan": "Ryan (US)",
493
+ "waiting_selection": "ESPERANDO SELECCIÓN",
494
+ "yesterday_section": "AYER"
495
+}
\ No newline at end of file
lib/l10n/app_hi.arb
new
+495
@@ -0,0 +1,495 @@
1
+{
2
+ "@@locale": "hi",
3
+ "active": "सक्रिय",
4
+ "active_integrations": "सक्रिय एकीकरण",
5
+ "add_ephemeral_mcp": "अस्थायी MCP सर्वर जोड़ें",
6
+ "add_server": "सर्वर जोड़ें",
7
+ "add_server_first": "उपलब्ध मॉडल देखने के लिए पहले एक सर्वर जोड़ें।",
8
+ "add_server_title": "सर्वर जोड़ें",
9
+ "advanced_settings": "उन्नत सेटिंग्स",
10
+ "all": "सभी",
11
+ "allow_notifications": "सूचनाओं की अनुमति दें",
12
+ "always_available": "हमेशा उपलब्ध",
13
+ "api_key_format": "OpenRouter API कुंजियाँ sk- से शुरू होती हैं",
14
+ "api_key_hint_generic": "प्रमाणित सर्वर के लिए",
15
+ "api_key_hint_openrouter": "sk-...",
16
+ "api_key_optional": "API कुंजी (वैकल्पिक)",
17
+ "api_key_required": "API कुंजी *",
18
+ "api_key_required_openrouter": "OpenRouter के लिए API कुंजी आवश्यक है",
19
+ "app_name": "LocalMind",
20
+ "app_tagline": "आपका AI. आपका डिवाइस. आपके नियम।",
21
+ "app_version": "1.0.0",
22
+ "attach_images_tooltip": "छवियाँ संलग्न करें",
23
+ "auto_execute_tools": "स्वचालित रूप से उपकरण निष्पादित करें",
24
+ "auto_generate_titles": "स्वचालित रूप से शीर्षक उत्पन्न करें",
25
+ "available": "उपलब्ध",
26
+ "available_models": "उपलब्ध मॉडल",
27
+ "available_ram": "उपलब्ध रैम (RAM)",
28
+ "backend_cpu_desc": "सभी उपकरणों पर काम करता है। सबसे अनुकूल।",
29
+ "backend_gpu_desc": "OpenCL त्वरण। समर्थित उपकरणों पर तेज़।",
30
+ "backend_ios_notice": "iOS पर केवल CPU बैकएंड उपलब्ध है।",
31
+ "backend_npu_desc": "विक्रेता NPU (Qualcomm/MediaTek)। सबसे तेज़ अनुमान।",
32
+ "builtin_badge": "इन-बिल्ट",
33
+ "calculating": "गणना की जा रही है...",
34
+ "cancel": "रद्द करें",
35
+ "cannot_undo": "इस क्रिया को पूर्ववत नहीं किया जा सकता।",
36
+ "category_label": "श्रेणी",
37
+ "change_persona": "चरित्र बदलें",
38
+ "character_count": "{length} वर्ण",
39
+ "character_count_max": "{currentLen}/4000",
40
+ "chat_history_title": "चैट इतिहास",
41
+ "chat_input_hint": "कुछ भी पूछें",
42
+ "chat_parameters_tooltip": "चैट पैरामीटर",
43
+ "chat_settings_title": "चैट सेटिंग्स",
44
+ "chat_title": "LocalMind",
45
+ "choose_theme": "थीम चुनें",
46
+ "choose_theme_desc": "ऐप के रूप को निजीकृत करें। आप इसे बाद में सेटिंग्स में कभी भी बदल सकते हैं।",
47
+ "clear": "साफ़ करें",
48
+ "clear_conversation": "बातचीत साफ़ करें",
49
+ "clear_conversation_body": "यह इस बातचीत के सभी संदेशों को हटा देगा।",
50
+ "clear_conversation_title": "बातचीत साफ़ करें?",
51
+ "clone_edit": "क्लोन और संपादित करें",
52
+ "close": "बंद करें",
53
+ "code_theme_dark": "कोड थीम (डार्क)",
54
+ "code_theme_desc": "कोड ब्लॉक के लिए सिंटैक्स हाइलाइटिंग थीम चुनें।",
55
+ "code_theme_light": "कोड थीम (लाइट)",
56
+ "configure": "कॉन्फ़िगर करें",
57
+ "configuring_server": "सर्वर कॉन्फ़िगर किया जा रहा है...",
58
+ "confirm": "पुष्टि करें",
59
+ "connection_error": "कनेक्शन त्रुटि। अपने सर्वर की जांच करें।",
60
+ "connection_failed": "कनेक्शन विफल रहा। अपनी सेटिंग्स की जांच करें।",
61
+ "connection_successful": "कनेक्शन सफल रहा!",
62
+ "context_chip": "{ctx} संदर्भ (ctx)",
63
+ "context_length": "संदर्भ लंबाई (Context Length)",
64
+ "context_length_desc": "इतिहास विंडो",
65
+ "continue_action": "जारी रखें",
66
+ "conversation_date": "{month}/{day}/{year}",
67
+ "conversation_days_ago": "{days} दिन पहले",
68
+ "conversation_hours_ago": "{hours} घंटे पहले",
69
+ "conversation_id": "बातचीत आईडी",
70
+ "conversation_just_now": "अभी-अभी",
71
+ "conversation_minutes_ago": "{minutes} मिनट पहले",
72
+ "conversation_yesterday": "कल",
73
+ "copied": "कॉपी किया गया!",
74
+ "copied_markdown": "Markdown के रूप में कॉपी किया गया",
75
+ "copied_to_clipboard": "क्लिपबोर्ड पर कॉपी किया गया",
76
+ "copy": "कॉपी करें",
77
+ "copy_info": "जानकारी कॉपी करें",
78
+ "copy_markdown": "Markdown के रूप में कॉपी करें",
79
+ "create_persona": "चरित्र बनाएं",
80
+ "create_persona_button": "बनाएं",
81
+ "created_at": "निर्माण का समय",
82
+ "debug_dialog_desc": "सिंक्रनाइज़ेशन समस्याओं की पहचान करने में सहायता के लिए नैदानिक जानकारी।",
83
+ "default_badge": "डिफ़ॉल्ट",
84
+ "default_icon": "डिफ़ॉल्ट आइकन",
85
+ "delete": "हटाएं",
86
+ "delete_all_conversations": "सभी बातचीत हटाएं",
87
+ "delete_conversation": "बातचीत हटाएं?",
88
+ "delete_conversation_body": "क्या आप निश्चित रूप से \"{title}\" को हटाना चाहते हैं? इसे पूर्ववत नहीं किया जा सकता।",
89
+ "delete_conversation_title": "बातचीत हटाएं?",
90
+ "delete_message_title": "संदेश हटाएं?",
91
+ "delete_model_body": "क्या आप निश्चित रूप से {name} को हटाना चाहते हैं?",
92
+ "delete_model_body_with_size": "क्या आप निश्चित रूप से {name} को हटाना चाहते हैं? इससे लगभग {size} स्थान खाली हो जाएगा।\n\nयदि आवश्यकता हो तो आप बाद में इस मॉडल को फिर से डाउनलोड कर सकते हैं।",
93
+ "delete_model_title": "मॉडल हटाएं",
94
+ "delete_persona_body": "इसे पूर्ववत नहीं किया जा सकता।",
95
+ "delete_persona_title": "\"{name}\" हटाएं?",
96
+ "delete_server_body": "क्या आप निश्चित रूप से \"{name}\" को हटाना चाहते हैं? इसे पूर्ववत नहीं किया जा सकता।",
97
+ "delete_server_title": "सर्वर हटाएं",
98
+ "delete_voice_body": "क्या आप निश्चित रूप से {name} को हटाना चाहते हैं? इससे लगभग {size} स्थान खाली हो जाएगा।\n\nयदि आवश्यकता हो तो आप बाद में इस आवाज को फिर से डाउनलोड कर सकते हैं।",
99
+ "delete_voice_title": "आवाज हटाएं",
100
+ "description_hint": "यह चरित्र क्या करता है...",
101
+ "description_optional": "विवरण (वैकल्पिक)",
102
+ "device_memory": "डिवाइस मेमोरी",
103
+ "disconnected": "सर्वर से कनेक्शन टूट गया।",
104
+ "done": "पूर्ण",
105
+ "download": "डाउनलोड करें",
106
+ "download_complete_body": "{modelName} सफलतापूर्वक डाउनलोड हो गया है।",
107
+ "download_complete_notification": "डाउनलोड पूर्ण!",
108
+ "download_failed": "डाउनलोड विफल",
109
+ "download_failed_body": "{modelName} डाउनलोड करने में विफल।",
110
+ "download_failed_notification": "डाउनलोड विफल: {error}",
111
+ "download_model_desc": "डाउनलोड करने के लिए एक मॉडल चुनें।\nयह आपके डिवाइस पर स्थानीय रूप से चलेगा।",
112
+ "download_model_title": "मॉडल डाउनलोड करें",
113
+ "download_notification_title": "{modelName} डाउनलोड किया जा रहा है...",
114
+ "download_progress": "{percent}% • {speed}",
115
+ "downloaded": "डाउनलोड किया गया",
116
+ "downloading_status": "डाउनलोड किया जा रहा है...",
117
+ "edit": "संपादित करें",
118
+ "edit_message": "संदेश संपादित करें",
119
+ "edit_message_desc": "सहेजने से नीचे सहायक की प्रतिक्रिया हट जाएगी और पुनः उत्पन्न होगी।",
120
+ "edit_persona": "चरित्र संपादित करें",
121
+ "edit_server": "सर्वर संपादित करें",
122
+ "emoji_label": "इमोजी",
123
+ "enable": "सक्षम करें",
124
+ "enable_mcp": "MCP सक्षम करें",
125
+ "enable_notifications": "सूचनाएं सक्षम करें",
126
+ "enable_notifications_desc": "मॉडल डाउनलोड पूरा होने पर सूचित किया जाए।",
127
+ "engine_name_kitten": "Kitten TTS",
128
+ "engine_name_sherpa": "Sherpa ONNX VITS",
129
+ "engine_name_system": "सिस्टम TTS",
130
+ "engine_spec": "{sizeMb} MB · {ramMb} MB रैम · {voiceCount} आवाजें",
131
+ "engine_tagline_kitten": "उच्च गति न्यूरल TTS",
132
+ "engine_tagline_sherpa": "ऑफ़लाइन पाइपर (Piper) आवाज़ें",
133
+ "engine_tagline_system": "इन-बिल्ट डिवाइस इंजन",
134
+ "enter_new_title": "नया शीर्षक दर्ज करें",
135
+ "error": "त्रुटि",
136
+ "error_with_message": "त्रुटि: {error}",
137
+ "eta_label": "शेष समय (ETA): {eta}",
138
+ "expected_messages": "अपेक्षित संदेश",
139
+ "export_header": "*LocalMind से निर्यात किया गया — {date}*",
140
+ "export_label_assistant": "सहायक",
141
+ "export_label_system": "सिस्टम",
142
+ "export_label_tool": "उपकरण",
143
+ "export_label_user": "उपयोगकर्ता",
144
+ "export_role_assistant": "## 🤖 सहायक",
145
+ "export_role_system": "## ⚙️ सिस्टम",
146
+ "export_role_tool": "## 🔧 उपकरण",
147
+ "export_role_user": "## 👤 उपयोगकर्ता",
148
+ "export_text_assistant": "[सहायक]",
149
+ "export_text_system": "[सिस्टम]",
150
+ "export_text_tool": "[उपकरण]",
151
+ "export_text_user": "[उपयोगकर्ता]",
152
+ "failed_load_models": "मॉडल लोड करने में विफल",
153
+ "finish_setup": "सेटअप पूर्ण करें",
154
+ "font_preview": "त्वरित भूरी लोमड़ी आलसी कुत्ते के ऊपर से कूदती है।",
155
+ "font_size": "फ़ॉन्ट का आकार",
156
+ "font_size_desc": "चैट में टेक्स्ट का आकार समायोजित करें।",
157
+ "haptic_feedback": "हैप्टिक फीडबैक",
158
+ "history_missing_desc": "या तो इस चैट के संदेश हटा दिए गए थे या इतिहास रिकॉर्ड दूषित हो गया है।",
159
+ "history_missing_title": "इतिहास गायब है",
160
+ "host_label": "होस्ट / IP पता",
161
+ "host_required": "होस्ट आवश्यक है",
162
+ "host_valid": "एक वैध होस्टनाम या IP पता दर्ज करें",
163
+ "inference_backend": "अनुमान बैकएंड (Inference Backend)",
164
+ "initializing": "प्रारंभ किया जा रहा है...",
165
+ "initializing_services": "सेवाएं प्रारंभ की जा रही हैं...",
166
+ "install": "इंस्टॉल करें",
167
+ "installed": "इंस्टॉल किया गया",
168
+ "label_completed": "{label} पूर्ण",
169
+ "language_system_default": "सिस्टम डिफ़ॉल्ट",
170
+ "last_error": "अंतिम त्रुटि:",
171
+ "load": "लोड करें",
172
+ "loaded_status": "लोड किया गया",
173
+ "loading": "लोड हो रहा है...",
174
+ "loading_model": "{modelId} लोड किया जा रहा है...",
175
+ "loading_model_error": "त्रुटि: {error}",
176
+ "manage_on_device_models": "ऑन-डिवाइस मॉडल प्रबंधित करें",
177
+ "manage_servers": "सर्वर प्रबंधित करें",
178
+ "manage_tts_models": "TTS मॉडल प्रबंधित करें",
179
+ "max_tokens": "अधिकतम टोकन",
180
+ "max_tokens_desc": "प्रतिक्रिया सीमा",
181
+ "may_be_large": "इस डिवाइस के लिए बहुत बड़ा हो सकता है",
182
+ "mcp_disabled_warning": "MCP वैश्विक रूप से अक्षम है। इन सुविधाओं का उपयोग करने के लिए सेटिंग्स में इसे सक्षम करें।",
183
+ "mcp_enable_chat": "इस चैट के लिए MCP सक्षम करें",
184
+ "mcp_label_placeholder": "लेबल",
185
+ "mcp_tab": "MCP",
186
+ "mcp_url_placeholder": "URL (https://...)",
187
+ "memory_critical": "महत्वपूर्ण",
188
+ "memory_healthy": "स्वस्थ",
189
+ "memory_low": "कम",
190
+ "message_options": "संदेश विकल्प",
191
+ "model_deepseek": "DeepSeek R1 Distill Qwen 1.5B",
192
+ "model_deepseek_desc": "तर्क और विचार-श्रृंखला मॉडल। तार्किक कार्यों के लिए सर्वोत्तम।",
193
+ "model_gemma": "Gemma 4 E2B Instruct",
194
+ "model_gemma_desc": "गूगल का फ्लैगशिप मॉडल। उच्चतम गुणवत्ता, अधिक रैम की आवश्यकता है।",
195
+ "model_license_apache": "Apache-2.0",
196
+ "model_license_mit": "MIT",
197
+ "model_load_failed": "मॉडल लोड करने में विफल: {error}",
198
+ "model_loaded": "मॉडल लोड किया गया: {modelId} ({backend})",
199
+ "model_qwen_25": "Qwen 2.5 1.5B Instruct",
200
+ "model_qwen_25_desc": "संतुलित गुणवत्ता और आकार। सामान्य बातचीत के लिए अच्छा है।",
201
+ "model_qwen_3": "Qwen 3 0.6B",
202
+ "model_qwen_3_desc": "सबसे छोटा सामान्य प्रयोजन चैट मॉडल। त्वरित प्रतिक्रियाएं, कम मेमोरी उपयोग।",
203
+ "model_unload_failed": "मॉडल अनलोड करने में विफल: {error}",
204
+ "model_unloaded_ollama": "कीप-अलाइव (keep-alive) समय बीतने के बाद {name} को अनलोड कर दिया जाएगा",
205
+ "model_unloaded_success": "{name} सफलतापूर्वक अनलोड हो गया",
206
+ "more": "अधिक",
207
+ "my_persona_hint": "मेरा चरित्र",
208
+ "my_server_hint": "मेरा सर्वर",
209
+ "name_label": "नाम",
210
+ "name_length_validation": "नाम 50 वर्ण या उससे कम होना चाहिए",
211
+ "name_max_50": "अधिकतम 50 वर्ण",
212
+ "name_required": "नाम आवश्यक है",
213
+ "nav_history": "इतिहास",
214
+ "nav_local_models": "स्थानीय मॉडल",
215
+ "nav_new_chat": "नई चैट",
216
+ "nav_personas": "चरित्र (Personas)",
217
+ "nav_servers": "सर्वर",
218
+ "nav_settings": "सेटिंग्स",
219
+ "nav_tts": "टेक्स्ट टू स्पीच",
220
+ "new_chat_mcp_default": "नई चैट MCP डिफ़ॉल्ट",
221
+ "no_conversations_yet": "अभी तक कोई बातचीत नहीं",
222
+ "no_model_loaded": "कोई मॉडल लोड नहीं है। मॉडल डाउनलोड और लोड करने के लिए \"ऑन-डिवाइस मॉडल प्रबंधित करें\" पर टैप करें।",
223
+ "no_models_available": "कोई मॉडल उपलब्ध नहीं है",
224
+ "no_models_match": "\"{searchQuery}\" से मेल खाने वाला कोई मॉडल नहीं मिला",
225
+ "no_personas_desc": "AI के व्यवहार को अनुकूलित करने के लिए अपना पहला चरित्र बनाएं।",
226
+ "no_personas_found": "कोई चरित्र नहीं मिला",
227
+ "no_prompt_placeholder": "अभी तक कोई प्रॉम्ट नहीं...",
228
+ "no_results_found": "कोई परिणाम नहीं मिला",
229
+ "no_server_connected": "कोई सर्वर कनेक्ट नहीं है",
230
+ "no_server_selected": "कोई सर्वर चयनित नहीं है",
231
+ "no_servers_desc": "AI मॉडल के साथ चैट करना शुरू करने के लिए अपना पहला सर्वर जोड़ें।",
232
+ "no_servers_yet": "अभी तक कोई सर्वर नहीं है",
233
+ "none": "कोई नहीं",
234
+ "none_selected": "कोई चयनित नहीं है",
235
+ "not_downloaded": "डाउनलोड नहीं किया गया",
236
+ "not_installed": "इंस्टॉल नहीं किया गया",
237
+ "not_now": "अभी नहीं",
238
+ "notification_benefit_background": "पृष्ठभूमि कार्यों की स्थिति",
239
+ "notification_benefit_completions": "उत्पादन पूर्णता",
240
+ "notification_benefit_downloads": "मॉडल डाउनलोड प्रगति",
241
+ "offline": "ऑफ़लाइन",
242
+ "older_section": "पुराने",
243
+ "on_device_android_only": "ऑन-डिवाइस अनुमान वर्तमान में केवल Android पर उपलब्ध है।",
244
+ "on_device_models_title": "ऑन-डिवाइस मॉडल",
245
+ "onboarding_connect_desc": "अपना निजी AI अनुभव शुरू करने के लिए\nLM Studio, Ollama, या OpenRouter से\nकनेक्ट करें।",
246
+ "onboarding_connect_server": "अपना सर्वर\nकनेक्ट करें",
247
+ "onboarding_localmind": "LOCALMIND",
248
+ "online": "ऑनलाइन",
249
+ "open_source": "ओपन सोर्स",
250
+ "open_source_desc": "LocalMind ओपन सोर्स है। गिटहब (GitHub) पर हमारी प्रगति का अनुसरण करें या योगदान दें।",
251
+ "options_tooltip": "विकल्प",
252
+ "parameters_tab": "पैरामीटर",
253
+ "pause": "विराम दें",
254
+ "paused_progress": "रोका गया - {percent}%",
255
+ "persona_builtin_section": "इन-बिल्ट",
256
+ "persona_category_coding": "कोडिंग",
257
+ "persona_category_creative": "रचनात्मक",
258
+ "persona_category_education": "शिक्षा",
259
+ "persona_category_general": "सामान्य",
260
+ "persona_created": "चरित्र निर्मित",
261
+ "persona_my_section": "मेरे चरित्र (Personas)",
262
+ "persona_updated": "चरित्र अद्यतित (updated)",
263
+ "personas_title": "चरित्र (Personas)",
264
+ "pin": "पिन करें",
265
+ "pinned_section": "पिन किया गया",
266
+ "port_invalid": "एक संख्या होनी चाहिए",
267
+ "port_label": "पोर्ट",
268
+ "port_range": "एक वैध पोर्ट दर्ज करें (1-65535)",
269
+ "port_required": "पोर्ट आवश्यक है",
270
+ "preparing_app": "ऐप तैयार किया जा रहा है...",
271
+ "preview": "पूर्वावलोकन",
272
+ "preview_failed": "पूर्वावलोकन विफल: {error}",
273
+ "previous_30_days": "पिछले 30 दिन",
274
+ "previous_7_days": "पिछले 7 दिन",
275
+ "privacy_info": "\"LocalMind आपके डेटा को कभी नहीं देखता है\"",
276
+ "proceed_anyway": "वैसे भी आगे बढ़ें",
277
+ "processing": "प्रसंस्करण किया जा रहा है...",
278
+ "prompt_hint": "आप एक सहायक हैं...",
279
+ "prompt_max_chars": "अधिकतम 4000 वर्ण",
280
+ "prompt_required": "सिस्टम प्रॉम्ट आवश्यक है",
281
+ "quick_async": "मैं async/await का उपयोग कैसे करूं?",
282
+ "quick_debug": "इसे मेरे लिए डीबग करें",
283
+ "quick_explain": "इस कोड को समझाएं",
284
+ "quick_write": "मुझे एक फ़ंक्शन लिखने में मदद करें",
285
+ "ram_min_required": "न्यूनतम {fileSize} GB रैम (RAM) आवश्यक",
286
+ "ram_usage": "रैम का उपयोग",
287
+ "ram_used": "{percent}% प्रयुक्त",
288
+ "ram_warning": "रैम चेतावनी",
289
+ "ram_warning_body_download": "इस मॉडल के लिए कम से कम {ram} GB रैम की आवश्यकता है, लेकिन आपके डिवाइस में {totalMemory} है। यह सही ढंग से काम नहीं कर सकता है या ऐप क्रैश हो सकता है।",
290
+ "ram_warning_body_load": "आपके डिवाइस में {availableRAM} उपलब्ध रैम है, लेकिन यह मॉडल कम से कम {ram} GB की सिफारिश करता है। इसे लोड करना विफल हो सकता है या अस्थिरता पैदा कर सकता है।",
291
+ "range_0_1": "0.0-1.0",
292
+ "range_0_2": "0.0-2.0",
293
+ "read_aloud": "ज़ोर से पढ़ें",
294
+ "ready": "तैयार",
295
+ "ready_continue": "जारी रखने के लिए तैयार",
296
+ "recent_chats": "हालिया चैट",
297
+ "recommended": "अनुशंसित",
298
+ "refresh_models": "मॉडल रीफ्रेश करें",
299
+ "remove_persona": "चरित्र हटाएं",
300
+ "rename": "नाम बदलें",
301
+ "rename_conversation": "बातचीत का नाम बदलें",
302
+ "reset_defaults": "डिफ़ॉल्ट पर रीसेट करें",
303
+ "reset_settings_defaults": "सेटिंग्स को डिफ़ॉल्ट पर रीसेट करें",
304
+ "resume": "फिर से शुरू करें",
305
+ "retry": "पुनः प्रयास करें",
306
+ "save": "सहेजें",
307
+ "save_changes": "बदलाव सहेजें",
308
+ "save_continue": "सहेजें और जारी रखें",
309
+ "save_regenerate": "सहेजें और पुनः उत्पन्न करें",
310
+ "save_server": "सर्वर सहेजें",
311
+ "search_hint": "बातचीत खोजें...",
312
+ "search_icons_hint": "आइकन खोजें...",
313
+ "search_models_hint": "मॉडल खोजें...",
314
+ "see_all": "सभी देखें",
315
+ "select": "चुनें",
316
+ "select_icon": "आइकन चुनें",
317
+ "select_icon_desc": "अपने सर्वर के लिए एक आइकन चुनें",
318
+ "select_model": "मॉडल चुनें",
319
+ "select_model_hint": "चैटिंग शुरू करने के लिए एक मॉडल चुनें",
320
+ "select_model_title": "मॉडल चुनें",
321
+ "select_persona": "चरित्र चुनें",
322
+ "send_message_tooltip": "संदेश भेजें",
323
+ "send_on_enter": "एंटर (Enter) दबाने पर भेजें",
324
+ "server_added": "सर्वर जोड़ा गया",
325
+ "server_address_format": "{host}:{port}",
326
+ "server_address_on_device": "स्थानीय अनुमान (Local Inference)",
327
+ "server_address_openrouter": "openrouter.ai",
328
+ "server_icon_ai_brain": "AI ब्रेन",
329
+ "server_icon_ai_brain2": "AI ब्रेन 02",
330
+ "server_icon_ai_chat": "AI चैट",
331
+ "server_icon_ai_cloud": "AI क्लाउड",
332
+ "server_icon_ai_network": "AI नेटवर्क",
333
+ "server_icon_alert": "चेतावनी वृत्त",
334
+ "server_icon_api": "API",
335
+ "server_icon_arrow_right": "दायाँ तीर 01",
336
+ "server_icon_bot": "बॉट",
337
+ "server_icon_bot2": "बॉट 02",
338
+ "server_icon_cellular": "सेलुलर नेटवर्क",
339
+ "server_icon_check": "सत्यापन वृत्त",
340
+ "server_icon_chip": "चिप",
341
+ "server_icon_chip2": "चिप 02",
342
+ "server_icon_cloud": "क्लाउड",
343
+ "server_icon_cloud_download": "क्लाउड डाउनलोड",
344
+ "server_icon_cloud_server": "क्लाउड सर्वर",
345
+ "server_icon_cloud_upload": "क्लाउड अपलोड",
346
+ "server_icon_code": "कोड",
347
+ "server_icon_computer": "कंप्यूटर",
348
+ "server_icon_cpu": "CPU",
349
+ "server_icon_database": "डेटाबेस",
350
+ "server_icon_database1": "डेटाबेस 01",
351
+ "server_icon_database2": "डेटाबेस 02",
352
+ "server_icon_drive": "ड्राइव",
353
+ "server_icon_file1": "फ़ाइल 01",
354
+ "server_icon_folder1": "फ़ोल्डर 01",
355
+ "server_icon_folder2": "फ़ोल्डर 02",
356
+ "server_icon_globe": "ग्लोब",
357
+ "server_icon_hard_drive": "हार्ड ड्राइव",
358
+ "server_icon_home1": "होम 01",
359
+ "server_icon_home2": "होम 02",
360
+ "server_icon_info": "सूचना वृत्त",
361
+ "server_icon_key": "कुंजी 01",
362
+ "server_icon_label": "सर्वर आइकन",
363
+ "server_icon_laptop": "लैपटॉप",
364
+ "server_icon_link": "लिंक 01",
365
+ "server_icon_lock": "लॉक",
366
+ "server_icon_mcp": "MCP सर्वर",
367
+ "server_icon_plug1": "प्लग 01",
368
+ "server_icon_plug2": "प्लग 02",
369
+ "server_icon_refresh": "रीफ्रेश करें",
370
+ "server_icon_robotic": "रोबोटिक",
371
+ "server_icon_rocket": "रॉकेट",
372
+ "server_icon_settings1": "सेटिंग्स 01",
373
+ "server_icon_settings2": "सेटिंग्स 02",
374
+ "server_icon_stack": "सर्वर स्टैक",
375
+ "server_icon_stack2": "सर्वर स्टैक 02",
376
+ "server_icon_stack3": "सर्वर स्टैक 03",
377
+ "server_icon_star": "स्टार",
378
+ "server_icon_terminal": "कंप्यूटर टर्मिनल",
379
+ "server_icon_zap": "जैप",
380
+ "server_name": "सर्वर का नाम",
381
+ "server_type_label": "सर्वर का प्रकार",
382
+ "server_type_lm_studio": "LM Studio",
383
+ "server_type_lm_studio_display": "LM Studio",
384
+ "server_type_lm_studio_sub": "स्थानीय API",
385
+ "server_type_ollama": "Ollama",
386
+ "server_type_ollama_display": "Ollama",
387
+ "server_type_ollama_sub": "CLI इंजन",
388
+ "server_type_on_device": "ऑन-डिवाइस",
389
+ "server_type_on_device_display": "ऑन-डिवाइस",
390
+ "server_type_on_device_sub": "किसी सर्वर की आवश्यकता नहीं है",
391
+ "server_type_openai_display": "OpenAI संगत",
392
+ "server_type_openrouter": "OpenRouter",
393
+ "server_type_openrouter_display": "OpenRouter",
394
+ "server_type_openrouter_sub": "एकीकृत क्लाउड",
395
+ "server_updated": "सर्वर अपडेट किया गया",
396
+ "servers_title": "सर्वर",
397
+ "set_as_default": "डिफ़ॉल्ट रूप में सेट करें",
398
+ "set_persona": "चरित्र सेट करें",
399
+ "settings_about": "परिचय",
400
+ "settings_appearance": "दिखावट",
401
+ "settings_behavior": "व्यवहार",
402
+ "settings_data_management": "डेटा प्रबंधन",
403
+ "settings_default_persona": "डिफ़ॉल्ट चरित्र",
404
+ "settings_default_server": "डिफ़ॉल्ट सर्वर",
405
+ "settings_language": "भाषा",
406
+ "settings_on_device": "ऑन-डिवाइस अनुमान (On-Device Inference)",
407
+ "settings_privacy": "गोपनीयता",
408
+ "settings_title": "सेटिंग्स",
409
+ "settings_tts": "टेक्स्ट-टू-स्पीच",
410
+ "setup_connection": "कनेक्शन सेटअप करें",
411
+ "setup_connection_desc": "चैटिंग शुरू करने के लिए अपने {server} सर्वर को कॉन्फ़िगर करें।",
412
+ "share": "साझा करें",
413
+ "show_data_indicator": "डेटा सूचक दिखाएं",
414
+ "show_system_messages": "सिस्टम संदेश दिखाएं",
415
+ "skip": "छोड़ें",
416
+ "something_went_wrong": "कुछ गलत हो गया",
417
+ "star_on_github": "GitHub पर स्टार दें",
418
+ "start_conversation": "बातचीत शुरू करें",
419
+ "start_new_chat": "नई चैट शुरू करें",
420
+ "start_new_conversation": "नई बातचीत शुरू करें",
421
+ "startup_failed": "स्टार्टअप विफल रहा",
422
+ "stay_updated": "अपडेट रहें",
423
+ "stay_updated_desc": "जब आपके AI मॉडल डाउनलोड हो जाएं या लंबे समय तक चलने वाले कार्य पूरे हो जाएं तो सूचित किया जाए।",
424
+ "stop": "रोकें",
425
+ "stop_generation_tooltip": "उत्पादन रोकें",
426
+ "stop_reading": "पढ़ना बंद करें",
427
+ "streaming_responses": "प्रतिक्रियाएं स्ट्रीम करें",
428
+ "switch_server": "सर्वर बदलें",
429
+ "switch_server_subtitle": "कनेक्ट करने के लिए एक सर्वर चुनें",
430
+ "switched_to_server": "{name} पर स्विच किया गया",
431
+ "system_prompt": "सिस्टम प्रॉम्ट",
432
+ "technical_details": "तकनीकी विवरण",
433
+ "temp_hint": "0.7",
434
+ "temperature": "तापमान",
435
+ "temperature_desc": "यादृच्छिकता को नियंत्रित करता है: उच्च = रचनात्मक, निम्न = केंद्रित",
436
+ "temperature_label": "तापमान (0.0-2.0)",
437
+ "test_connection": "कनेक्शन का परीक्षण करें",
438
+ "test_notification_body": "यह मॉडल डाउनलोड प्रगति के लिए एक परीक्षण सूचना है।",
439
+ "test_notification_title": "परीक्षण सूचना",
440
+ "testing": "परीक्षण किया जा रहा है...",
441
+ "theme": "थीम",
442
+ "theme_card_claude": "Claude",
443
+ "theme_card_claude_sub": "एक हल्का नारंगी-पीला (peach) थीम",
444
+ "theme_card_dark": "डार्क",
445
+ "theme_card_dark_sub": "आँखों के लिए आरामदायक",
446
+ "theme_card_light": "लाइट",
447
+ "theme_card_light_sub": "साफ़ और उज्ज्वल",
448
+ "theme_card_system": "सिस्टम",
449
+ "theme_card_system_sub": "आपके डिवाइस की सेटिंग्स से मेल खाता है",
450
+ "theme_claude": "Claude",
451
+ "theme_dark": "डार्क",
452
+ "theme_light": "लाइट",
453
+ "theme_system": "सिस्टम",
454
+ "thinking": "सोच रहा है",
455
+ "today_section": "आज",
456
+ "tool_label": "उपकरण: {toolCallId}",
457
+ "tool_unknown": "उपकरण: अज्ञात",
458
+ "top_p": "Top P",
459
+ "top_p_desc": "न्यूक्लियस सैंपलिंग थ्रेशोल्ड",
460
+ "top_p_hint": "0.9",
461
+ "top_p_label": "Top P (0.0-1.0)",
462
+ "total_capacity": "कुल क्षमता",
463
+ "total_ram": "कुल रैम",
464
+ "try_different_search": "कोई अन्य खोज शब्द आज़माएं",
465
+ "tts_engine": "TTS इंजन",
466
+ "tts_engine_kitten": "Kitten TTS",
467
+ "tts_engine_system": "सिस्टम TTS",
468
+ "tts_kitten_desc": "8 अभिव्यंजक आवाज़ों के साथ बिजली की तरह तेज़ न्यूरल TTS।\n{size} डाउनलोड की आवश्यकता है।",
469
+ "tts_models_title": "टेक्स्ट टू स्पीच मॉडल",
470
+ "tts_piper_desc": "2 अभिव्यंजक आवाज़ों के साथ तेज़ ऑफ़लाइन पाइपर (Piper) आवाज़ें।\nप्रति आवाज़ {size} डाउनलोड की आवश्यकता है।",
471
+ "tts_speed": "TTS गति",
472
+ "tts_speed_desc": "प्लेबैक की गति समायोजित करें।",
473
+ "tts_system_desc": "आपके डिवाइस के इन-बिल्ट टेक्स्ट-टू-स्पीच इंजन का उपयोग करता है।\nकिसी डाउनलोड की आवश्यकता नहीं है। आवाज चयन आपके डिवाइस की सिस्टम सेटिंग्स का उपयोग करता है।",
474
+ "unknown_error": "अज्ञात त्रुटि",
475
+ "unload": "अनलोड करें",
476
+ "unload_from_server": "सर्वर से अनलोड करें",
477
+ "unpin": "अनपिन करें",
478
+ "update_server": "सर्वर अपडेट करें",
479
+ "voice": "आवाज",
480
+ "voice_bella": "Bella",
481
+ "voice_bruno": "Bruno",
482
+ "voice_female": "महिला",
483
+ "voice_hugo": "Hugo",
484
+ "voice_jasper": "Jasper",
485
+ "voice_kiki": "Kiki",
486
+ "voice_leo": "Leo",
487
+ "voice_lessac": "Lessac (US)",
488
+ "voice_luna": "Luna",
489
+ "voice_male": "पुरुष",
490
+ "voice_other": "अन्य",
491
+ "voice_rosie": "Rosie",
492
+ "voice_ryan": "Ryan (US)",
493
+ "waiting_selection": "चयन की प्रतीक्षा की जा रही है",
494
+ "yesterday_section": "कल"
495
+}
\ No newline at end of file
lib/l10n/app_localizations.dart
new
+3108
@@ -0,0 +1,3108 @@
1
+import 'dart:async';
2
+
3
+import 'package:flutter/foundation.dart';
4
+import 'package:flutter/widgets.dart';
5
+import 'package:flutter_localizations/flutter_localizations.dart';
6
+import 'package:intl/intl.dart' as intl;
7
+
8
+import 'app_localizations_ar.dart';
9
+import 'app_localizations_bn.dart';
10
+import 'app_localizations_en.dart';
11
+import 'app_localizations_es.dart';
12
+import 'app_localizations_hi.dart';
13
+import 'app_localizations_zh.dart';
14
+
15
+// ignore_for_file: type=lint
16
+
17
+/// Callers can lookup localized strings with an instance of AppLocalizations
18
+/// returned by `AppLocalizations.of(context)`.
19
+///
20
+/// Applications need to include `AppLocalizations.delegate()` in their app's
21
+/// `localizationDelegates` list, and the locales they support in the app's
22
+/// `supportedLocales` list. For example:
23
+///
24
+/// ```dart
25
+/// import 'l10n/app_localizations.dart';
26
+///
27
+/// return MaterialApp(
28
+/// localizationsDelegates: AppLocalizations.localizationsDelegates,
29
+/// supportedLocales: AppLocalizations.supportedLocales,
30
+/// home: MyApplicationHome(),
31
+/// );
32
+/// ```
33
+///
34
+/// ## Update pubspec.yaml
35
+///
36
+/// Please make sure to update your pubspec.yaml to include the following
37
+/// packages:
38
+///
39
+/// ```yaml
40
+/// dependencies:
41
+/// # Internationalization support.
42
+/// flutter_localizations:
43
+/// sdk: flutter
44
+/// intl: any # Use the pinned version from flutter_localizations
45
+///
46
+/// # Rest of dependencies
47
+/// ```
48
+///
49
+/// ## iOS Applications
50
+///
51
+/// iOS applications define key application metadata, including supported
52
+/// locales, in an Info.plist file that is built into the application bundle.
53
+/// To configure the locales supported by your app, you’ll need to edit this
54
+/// file.
55
+///
56
+/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
57
+/// Then, in the Project Navigator, open the Info.plist file under the Runner
58
+/// project’s Runner folder.
59
+///
60
+/// Next, select the Information Property List item, select Add Item from the
61
+/// Editor menu, then select Localizations from the pop-up menu.
62
+///
63
+/// Select and expand the newly-created Localizations item then, for each
64
+/// locale your application supports, add a new item and select the locale
65
+/// you wish to add from the pop-up menu in the Value field. This list should
66
+/// be consistent with the languages listed in the AppLocalizations.supportedLocales
67
+/// property.
68
+abstract class AppLocalizations {
69
+ AppLocalizations(String locale)
70
+ : localeName = intl.Intl.canonicalizedLocale(locale.toString());
71
+
72
+ final String localeName;
73
+
74
+ static AppLocalizations? of(BuildContext context) {
75
+ return Localizations.of<AppLocalizations>(context, AppLocalizations);
76
+ }
77
+
78
+ static const LocalizationsDelegate<AppLocalizations> delegate =
79
+ _AppLocalizationsDelegate();
80
+
81
+ /// A list of this localizations delegate along with the default localizations
82
+ /// delegates.
83
+ ///
84
+ /// Returns a list of localizations delegates containing this delegate along with
85
+ /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
86
+ /// and GlobalWidgetsLocalizations.delegate.
87
+ ///
88
+ /// Additional delegates can be added by appending to this list in
89
+ /// MaterialApp. This list does not have to be used at all if a custom list
90
+ /// of delegates is preferred or required.
91
+ static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
92
+ <LocalizationsDelegate<dynamic>>[
93
+ delegate,
94
+ GlobalMaterialLocalizations.delegate,
95
+ GlobalCupertinoLocalizations.delegate,
96
+ GlobalWidgetsLocalizations.delegate,
97
+ ];
98
+
99
+ /// A list of this localizations delegate's supported locales.
100
+ static const List<Locale> supportedLocales = <Locale>[
101
+ Locale('ar'),
102
+ Locale('bn'),
103
+ Locale('en'),
104
+ Locale('es'),
105
+ Locale('hi'),
106
+ Locale('zh'),
107
+ ];
108
+
109
+ /// Application display name
110
+ ///
111
+ /// In en, this message translates to:
112
+ /// **'LocalMind'**
113
+ String get app_name;
114
+
115
+ /// Application tagline shown in settings and splash
116
+ ///
117
+ /// In en, this message translates to:
118
+ /// **'Your AI. Your Device. Your Rules.'**
119
+ String get app_tagline;
120
+
121
+ /// Application version number
122
+ ///
123
+ /// In en, this message translates to:
124
+ /// **'1.0.0'**
125
+ String get app_version;
126
+
127
+ /// Generic cancel button label
128
+ ///
129
+ /// In en, this message translates to:
130
+ /// **'Cancel'**
131
+ String get cancel;
132
+
133
+ /// Generic confirm button label
134
+ ///
135
+ /// In en, this message translates to:
136
+ /// **'Confirm'**
137
+ String get confirm;
138
+
139
+ /// Generic delete button label
140
+ ///
141
+ /// In en, this message translates to:
142
+ /// **'Delete'**
143
+ String get delete;
144
+
145
+ /// Generic save button label
146
+ ///
147
+ /// In en, this message translates to:
148
+ /// **'Save'**
149
+ String get save;
150
+
151
+ /// Generic retry button label
152
+ ///
153
+ /// In en, this message translates to:
154
+ /// **'Retry'**
155
+ String get retry;
156
+
157
+ /// Generic close button label
158
+ ///
159
+ /// In en, this message translates to:
160
+ /// **'Close'**
161
+ String get close;
162
+
163
+ /// Generic done button label
164
+ ///
165
+ /// In en, this message translates to:
166
+ /// **'Done'**
167
+ String get done;
168
+
169
+ /// Generic continue button label
170
+ ///
171
+ /// In en, this message translates to:
172
+ /// **'Continue'**
173
+ String get continue_action;
174
+
175
+ /// Generic skip button label
176
+ ///
177
+ /// In en, this message translates to:
178
+ /// **'Skip'**
179
+ String get skip;
180
+
181
+ /// Install action button
182
+ ///
183
+ /// In en, this message translates to:
184
+ /// **'Install'**
185
+ String get install;
186
+
187
+ /// Download action button
188
+ ///
189
+ /// In en, this message translates to:
190
+ /// **'Download'**
191
+ String get download;
192
+
193
+ /// Resume download button
194
+ ///
195
+ /// In en, this message translates to:
196
+ /// **'Resume'**
197
+ String get resume;
198
+
199
+ /// Pause download button
200
+ ///
201
+ /// In en, this message translates to:
202
+ /// **'Pause'**
203
+ String get pause;
204
+
205
+ /// Stop action button
206
+ ///
207
+ /// In en, this message translates to:
208
+ /// **'Stop'**
209
+ String get stop;
210
+
211
+ /// Edit action label
212
+ ///
213
+ /// In en, this message translates to:
214
+ /// **'Edit'**
215
+ String get edit;
216
+
217
+ /// Preview action label
218
+ ///
219
+ /// In en, this message translates to:
220
+ /// **'Preview'**
221
+ String get preview;
222
+
223
+ /// Unload model action
224
+ ///
225
+ /// In en, this message translates to:
226
+ /// **'Unload'**
227
+ String get unload;
228
+
229
+ /// Load model action
230
+ ///
231
+ /// In en, this message translates to:
232
+ /// **'Load'**
233
+ String get load;
234
+
235
+ /// Rename action label
236
+ ///
237
+ /// In en, this message translates to:
238
+ /// **'Rename'**
239
+ String get rename;
240
+
241
+ /// Pin conversation action
242
+ ///
243
+ /// In en, this message translates to:
244
+ /// **'Pin'**
245
+ String get pin;
246
+
247
+ /// Unpin conversation action
248
+ ///
249
+ /// In en, this message translates to:
250
+ /// **'Unpin'**
251
+ String get unpin;
252
+
253
+ /// Share action label
254
+ ///
255
+ /// In en, this message translates to:
256
+ /// **'Share'**
257
+ String get share;
258
+
259
+ /// Copy action label
260
+ ///
261
+ /// In en, this message translates to:
262
+ /// **'Copy'**
263
+ String get copy;
264
+
265
+ /// Copied confirmation text
266
+ ///
267
+ /// In en, this message translates to:
268
+ /// **'Copied!'**
269
+ String get copied;
270
+
271
+ /// Snackbar message after copy
272
+ ///
273
+ /// In en, this message translates to:
274
+ /// **'Copied to clipboard'**
275
+ String get copied_to_clipboard;
276
+
277
+ /// Select action button
278
+ ///
279
+ /// In en, this message translates to:
280
+ /// **'Select'**
281
+ String get select;
282
+
283
+ /// Active status badge
284
+ ///
285
+ /// In en, this message translates to:
286
+ /// **'Active'**
287
+ String get active;
288
+
289
+ /// All filter label
290
+ ///
291
+ /// In en, this message translates to:
292
+ /// **'All'**
293
+ String get all;
294
+
295
+ /// No selection label
296
+ ///
297
+ /// In en, this message translates to:
298
+ /// **'None'**
299
+ String get none;
300
+
301
+ /// Dropdown placeholder when nothing selected
302
+ ///
303
+ /// In en, this message translates to:
304
+ /// **'None selected'**
305
+ String get none_selected;
306
+
307
+ /// Connection status online
308
+ ///
309
+ /// In en, this message translates to:
310
+ /// **'Online'**
311
+ String get online;
312
+
313
+ /// Connection status offline
314
+ ///
315
+ /// In en, this message translates to:
316
+ /// **'Offline'**
317
+ String get offline;
318
+
319
+ /// Error status text
320
+ ///
321
+ /// In en, this message translates to:
322
+ /// **'Error'**
323
+ String get error;
324
+
325
+ /// Fallback unknown error message
326
+ ///
327
+ /// In en, this message translates to:
328
+ /// **'Unknown error'**
329
+ String get unknown_error;
330
+
331
+ /// Dismiss action for notifications
332
+ ///
333
+ /// In en, this message translates to:
334
+ /// **'Not Now'**
335
+ String get not_now;
336
+
337
+ /// Enable action button
338
+ ///
339
+ /// In en, this message translates to:
340
+ /// **'Enable'**
341
+ String get enable;
342
+
343
+ /// Proceed despite warning
344
+ ///
345
+ /// In en, this message translates to:
346
+ /// **'Proceed Anyway'**
347
+ String get proceed_anyway;
348
+
349
+ /// Button to test server connection
350
+ ///
351
+ /// In en, this message translates to:
352
+ /// **'Test Connection'**
353
+ String get test_connection;
354
+
355
+ /// Button label while testing connection
356
+ ///
357
+ /// In en, this message translates to:
358
+ /// **'Testing...'**
359
+ String get testing;
360
+
361
+ /// Connection test success message
362
+ ///
363
+ /// In en, this message translates to:
364
+ /// **'Connection successful!'**
365
+ String get connection_successful;
366
+
367
+ /// Connection test failure message
368
+ ///
369
+ /// In en, this message translates to:
370
+ /// **'Connection failed. Check your settings.'**
371
+ String get connection_failed;
372
+
373
+ /// Save and proceed button
374
+ ///
375
+ /// In en, this message translates to:
376
+ /// **'Save & Continue'**
377
+ String get save_continue;
378
+
379
+ /// Save changes button
380
+ ///
381
+ /// In en, this message translates to:
382
+ /// **'Save Changes'**
383
+ String get save_changes;
384
+
385
+ /// Complete onboarding button
386
+ ///
387
+ /// In en, this message translates to:
388
+ /// **'Finish Setup'**
389
+ String get finish_setup;
390
+
391
+ /// Button to start a new chat
392
+ ///
393
+ /// In en, this message translates to:
394
+ /// **'Start New Chat'**
395
+ String get start_new_chat;
396
+
397
+ /// Warning for destructive actions
398
+ ///
399
+ /// In en, this message translates to:
400
+ /// **'This action cannot be undone.'**
401
+ String get cannot_undo;
402
+
403
+ /// RAM warning dialog title
404
+ ///
405
+ /// In en, this message translates to:
406
+ /// **'RAM Warning'**
407
+ String get ram_warning;
408
+
409
+ /// Recommended badge on model cards
410
+ ///
411
+ /// In en, this message translates to:
412
+ /// **'RECOMMENDED'**
413
+ String get recommended;
414
+
415
+ /// Warning when model may exceed device RAM
416
+ ///
417
+ /// In en, this message translates to:
418
+ /// **'May be too large for this device'**
419
+ String get may_be_large;
420
+
421
+ /// ETA placeholder during download
422
+ ///
423
+ /// In en, this message translates to:
424
+ /// **'Calculating...'**
425
+ String get calculating;
426
+
427
+ /// Download failure status
428
+ ///
429
+ /// In en, this message translates to:
430
+ /// **'Download failed'**
431
+ String get download_failed;
432
+
433
+ /// Download completed status
434
+ ///
435
+ /// In en, this message translates to:
436
+ /// **'Downloaded'**
437
+ String get downloaded;
438
+
439
+ /// Model not yet downloaded
440
+ ///
441
+ /// In en, this message translates to:
442
+ /// **'Not downloaded'**
443
+ String get not_downloaded;
444
+
445
+ /// Installed status
446
+ ///
447
+ /// In en, this message translates to:
448
+ /// **'Installed'**
449
+ String get installed;
450
+
451
+ /// Not installed status
452
+ ///
453
+ /// In en, this message translates to:
454
+ /// **'Not installed'**
455
+ String get not_installed;
456
+
457
+ /// Generic loading indicator text
458
+ ///
459
+ /// In en, this message translates to:
460
+ /// **'Loading...'**
461
+ String get loading;
462
+
463
+ /// Model thinking indicator
464
+ ///
465
+ /// In en, this message translates to:
466
+ /// **'Thinking'**
467
+ String get thinking;
468
+
469
+ /// Processing indicator text
470
+ ///
471
+ /// In en, this message translates to:
472
+ /// **'Processing...'**
473
+ String get processing;
474
+
475
+ /// Initializing status
476
+ ///
477
+ /// In en, this message translates to:
478
+ /// **'Initializing...'**
479
+ String get initializing;
480
+
481
+ /// Ready status text
482
+ ///
483
+ /// In en, this message translates to:
484
+ /// **'Ready'**
485
+ String get ready;
486
+
487
+ /// Bootstrap stage message
488
+ ///
489
+ /// In en, this message translates to:
490
+ /// **'Preparing app...'**
491
+ String get preparing_app;
492
+
493
+ /// Bootstrap stage message
494
+ ///
495
+ /// In en, this message translates to:
496
+ /// **'Initializing services...'**
497
+ String get initializing_services;
498
+
499
+ /// Bootstrap stage message
500
+ ///
501
+ /// In en, this message translates to:
502
+ /// **'Configuring server...'**
503
+ String get configuring_server;
504
+
505
+ /// Bootstrap error message
506
+ ///
507
+ /// In en, this message translates to:
508
+ /// **'Startup failed'**
509
+ String get startup_failed;
510
+
511
+ /// Generic error heading on bootstrap screen
512
+ ///
513
+ /// In en, this message translates to:
514
+ /// **'Something went wrong'**
515
+ String get something_went_wrong;
516
+
517
+ /// Delete model dialog title
518
+ ///
519
+ /// In en, this message translates to:
520
+ /// **'Delete Model'**
521
+ String get delete_model_title;
522
+
523
+ /// Delete model confirmation dialog body
524
+ ///
525
+ /// In en, this message translates to:
526
+ /// **'Are you sure you want to delete {name}?'**
527
+ String delete_model_body(String name);
528
+
529
+ /// Delete model dialog body with size info
530
+ ///
531
+ /// In en, this message translates to:
532
+ /// **'Are you sure you want to delete {name}? This will free up approximately {size} of space.\n\nYou can download this model again later if needed.'**
533
+ String delete_model_body_with_size(String name, String size);
534
+
535
+ /// Delete voice confirmation dialog title
536
+ ///
537
+ /// In en, this message translates to:
538
+ /// **'Delete Voice'**
539
+ String get delete_voice_title;
540
+
541
+ /// Delete voice confirmation dialog body
542
+ ///
543
+ /// In en, this message translates to:
544
+ /// **'Are you sure you want to delete {name}? This will free up approximately {size} of space.\n\nYou can download this voice again later if needed.'**
545
+ String delete_voice_body(String name, String size);
546
+
547
+ /// Delete server confirmation dialog title
548
+ ///
549
+ /// In en, this message translates to:
550
+ /// **'Delete Server'**
551
+ String get delete_server_title;
552
+
553
+ /// Delete server confirmation body
554
+ ///
555
+ /// In en, this message translates to:
556
+ /// **'Are you sure you want to delete \"{name}\"? This cannot be undone.'**
557
+ String delete_server_body(String name);
558
+
559
+ /// Delete conversation dialog title
560
+ ///
561
+ /// In en, this message translates to:
562
+ /// **'Delete conversation?'**
563
+ String get delete_conversation_title;
564
+
565
+ /// Delete conversation dialog body
566
+ ///
567
+ /// In en, this message translates to:
568
+ /// **'Are you sure you want to delete \"{title}\"? This cannot be undone.'**
569
+ String delete_conversation_body(String title);
570
+
571
+ /// Delete message dialog title
572
+ ///
573
+ /// In en, this message translates to:
574
+ /// **'Delete message?'**
575
+ String get delete_message_title;
576
+
577
+ /// Delete persona dialog title
578
+ ///
579
+ /// In en, this message translates to:
580
+ /// **'Delete \"{name}\"?'**
581
+ String delete_persona_title(String name);
582
+
583
+ /// Delete persona dialog body
584
+ ///
585
+ /// In en, this message translates to:
586
+ /// **'This cannot be undone.'**
587
+ String get delete_persona_body;
588
+
589
+ /// Clear conversation dialog title
590
+ ///
591
+ /// In en, this message translates to:
592
+ /// **'Clear conversation?'**
593
+ String get clear_conversation_title;
594
+
595
+ /// Clear conversation confirmation body
596
+ ///
597
+ /// In en, this message translates to:
598
+ /// **'This will delete all messages in this conversation.'**
599
+ String get clear_conversation_body;
600
+
601
+ /// Clear action button
602
+ ///
603
+ /// In en, this message translates to:
604
+ /// **'Clear'**
605
+ String get clear;
606
+
607
+ /// Completion status message
608
+ ///
609
+ /// In en, this message translates to:
610
+ /// **'{label} completed'**
611
+ String label_completed(String label);
612
+
613
+ /// Error display with message
614
+ ///
615
+ /// In en, this message translates to:
616
+ /// **'Error: {error}'**
617
+ String error_with_message(String error);
618
+
619
+ /// Audio preview failed message
620
+ ///
621
+ /// In en, this message translates to:
622
+ /// **'Preview failed: {error}'**
623
+ String preview_failed(String error);
624
+
625
+ /// Model loading status
626
+ ///
627
+ /// In en, this message translates to:
628
+ /// **'Loading {modelId}...'**
629
+ String loading_model(String modelId);
630
+
631
+ /// Model loaded status in settings
632
+ ///
633
+ /// In en, this message translates to:
634
+ /// **'Model loaded: {modelId} ({backend})'**
635
+ String model_loaded(String modelId, String backend);
636
+
637
+ /// Status when no on-device model is loaded
638
+ ///
639
+ /// In en, this message translates to:
640
+ /// **'No model loaded. Tap \"Manage On-Device Models\" to download and load a model.'**
641
+ String get no_model_loaded;
642
+
643
+ /// Model loading error status
644
+ ///
645
+ /// In en, this message translates to:
646
+ /// **'Error: {error}'**
647
+ String loading_model_error(String error);
648
+
649
+ /// Delete conversation dialog title
650
+ ///
651
+ /// In en, this message translates to:
652
+ /// **'Delete conversation?'**
653
+ String get delete_conversation;
654
+
655
+ /// Navigation item for chat history
656
+ ///
657
+ /// In en, this message translates to:
658
+ /// **'History'**
659
+ String get nav_history;
660
+
661
+ /// Navigation item for servers
662
+ ///
663
+ /// In en, this message translates to:
664
+ /// **'Servers'**
665
+ String get nav_servers;
666
+
667
+ /// Navigation item for local on-device models
668
+ ///
669
+ /// In en, this message translates to:
670
+ /// **'Local Models'**
671
+ String get nav_local_models;
672
+
673
+ /// Navigation item for TTS models
674
+ ///
675
+ /// In en, this message translates to:
676
+ /// **'Text To Speech'**
677
+ String get nav_tts;
678
+
679
+ /// Navigation item for personas
680
+ ///
681
+ /// In en, this message translates to:
682
+ /// **'Personas'**
683
+ String get nav_personas;
684
+
685
+ /// Navigation item for settings
686
+ ///
687
+ /// In en, this message translates to:
688
+ /// **'Settings'**
689
+ String get nav_settings;
690
+
691
+ /// Button to start a new chat
692
+ ///
693
+ /// In en, this message translates to:
694
+ /// **'New Chat'**
695
+ String get nav_new_chat;
696
+
697
+ /// Search field placeholder for conversations
698
+ ///
699
+ /// In en, this message translates to:
700
+ /// **'Search conversations...'**
701
+ String get search_hint;
702
+
703
+ /// Placeholder when no server is selected
704
+ ///
705
+ /// In en, this message translates to:
706
+ /// **'No server selected'**
707
+ String get no_server_selected;
708
+
709
+ /// Switch server sheet title
710
+ ///
711
+ /// In en, this message translates to:
712
+ /// **'Switch Server'**
713
+ String get switch_server;
714
+
715
+ /// Switch server sheet subtitle
716
+ ///
717
+ /// In en, this message translates to:
718
+ /// **'Choose a server to connect to'**
719
+ String get switch_server_subtitle;
720
+
721
+ /// Button to manage servers list
722
+ ///
723
+ /// In en, this message translates to:
724
+ /// **'Manage Servers'**
725
+ String get manage_servers;
726
+
727
+ /// GitHub repo card title
728
+ ///
729
+ /// In en, this message translates to:
730
+ /// **'Open Source'**
731
+ String get open_source;
732
+
733
+ /// GitHub repo card description
734
+ ///
735
+ /// In en, this message translates to:
736
+ /// **'LocalMind is open source. Follow our progress or contribute on GitHub.'**
737
+ String get open_source_desc;
738
+
739
+ /// Button to star repo on GitHub
740
+ ///
741
+ /// In en, this message translates to:
742
+ /// **'Star on GitHub'**
743
+ String get star_on_github;
744
+
745
+ /// Settings screen title
746
+ ///
747
+ /// In en, this message translates to:
748
+ /// **'Settings'**
749
+ String get settings_title;
750
+
751
+ /// Settings section header for appearance
752
+ ///
753
+ /// In en, this message translates to:
754
+ /// **'Appearance'**
755
+ String get settings_appearance;
756
+
757
+ /// Settings section label for language selection
758
+ ///
759
+ /// In en, this message translates to:
760
+ /// **'Language'**
761
+ String get settings_language;
762
+
763
+ /// Option to use the device's system language
764
+ ///
765
+ /// In en, this message translates to:
766
+ /// **'System Default'**
767
+ String get language_system_default;
768
+
769
+ /// Settings section header for TTS
770
+ ///
771
+ /// In en, this message translates to:
772
+ /// **'Text-to-Speech'**
773
+ String get settings_tts;
774
+
775
+ /// Settings section header for behavior
776
+ ///
777
+ /// In en, this message translates to:
778
+ /// **'Behavior'**
779
+ String get settings_behavior;
780
+
781
+ /// Settings section header for on-device
782
+ ///
783
+ /// In en, this message translates to:
784
+ /// **'On-Device Inference'**
785
+ String get settings_on_device;
786
+
787
+ /// Settings section header for default server
788
+ ///
789
+ /// In en, this message translates to:
790
+ /// **'Default Server'**
791
+ String get settings_default_server;
792
+
793
+ /// Settings section header for default persona
794
+ ///
795
+ /// In en, this message translates to:
796
+ /// **'Default Persona'**
797
+ String get settings_default_persona;
798
+
799
+ /// Settings section header for privacy
800
+ ///
801
+ /// In en, this message translates to:
802
+ /// **'Privacy'**
803
+ String get settings_privacy;
804
+
805
+ /// Settings section header for data management
806
+ ///
807
+ /// In en, this message translates to:
808
+ /// **'Data Management'**
809
+ String get settings_data_management;
810
+
811
+ /// Settings section header for about
812
+ ///
813
+ /// In en, this message translates to:
814
+ /// **'About'**
815
+ String get settings_about;
816
+
817
+ /// Theme selection label
818
+ ///
819
+ /// In en, this message translates to:
820
+ /// **'Theme'**
821
+ String get theme;
822
+
823
+ /// System theme option
824
+ ///
825
+ /// In en, this message translates to:
826
+ /// **'System'**
827
+ String get theme_system;
828
+
829
+ /// Light theme option
830
+ ///
831
+ /// In en, this message translates to:
832
+ /// **'Light'**
833
+ String get theme_light;
834
+
835
+ /// Dark theme option
836
+ ///
837
+ /// In en, this message translates to:
838
+ /// **'Dark'**
839
+ String get theme_dark;
840
+
841
+ /// Claude theme option
842
+ ///
843
+ /// In en, this message translates to:
844
+ /// **'Claude'**
845
+ String get theme_claude;
846
+
847
+ /// Font size slider label
848
+ ///
849
+ /// In en, this message translates to:
850
+ /// **'Font Size'**
851
+ String get font_size;
852
+
853
+ /// Font size setting description
854
+ ///
855
+ /// In en, this message translates to:
856
+ /// **'Adjust text size in chat.'**
857
+ String get font_size_desc;
858
+
859
+ /// Font preview sample text
860
+ ///
861
+ /// In en, this message translates to:
862
+ /// **'The quick brown fox jumps over the lazy dog.'**
863
+ String get font_preview;
864
+
865
+ /// Dark code theme dropdown label
866
+ ///
867
+ /// In en, this message translates to:
868
+ /// **'Code Theme (Dark)'**
869
+ String get code_theme_dark;
870
+
871
+ /// Light code theme dropdown label
872
+ ///
873
+ /// In en, this message translates to:
874
+ /// **'Code Theme (Light)'**
875
+ String get code_theme_light;
876
+
877
+ /// Code theme setting description
878
+ ///
879
+ /// In en, this message translates to:
880
+ /// **'Choose syntax highlighting theme for code blocks.'**
881
+ String get code_theme_desc;
882
+
883
+ /// TTS engine dropdown label
884
+ ///
885
+ /// In en, this message translates to:
886
+ /// **'TTS Engine'**
887
+ String get tts_engine;
888
+
889
+ /// System TTS engine option
890
+ ///
891
+ /// In en, this message translates to:
892
+ /// **'System TTS'**
893
+ String get tts_engine_system;
894
+
895
+ /// Kitten TTS engine option
896
+ ///
897
+ /// In en, this message translates to:
898
+ /// **'Kitten TTS'**
899
+ String get tts_engine_kitten;
900
+
901
+ /// Voice selection label
902
+ ///
903
+ /// In en, this message translates to:
904
+ /// **'Voice'**
905
+ String get voice;
906
+
907
+ /// Female voice gender label
908
+ ///
909
+ /// In en, this message translates to:
910
+ /// **'Female'**
911
+ String get voice_female;
912
+
913
+ /// Male voice gender label
914
+ ///
915
+ /// In en, this message translates to:
916
+ /// **'Male'**
917
+ String get voice_male;
918
+
919
+ /// Other voice gender label
920
+ ///
921
+ /// In en, this message translates to:
922
+ /// **'Other'**
923
+ String get voice_other;
924
+
925
+ /// TTS speed slider label
926
+ ///
927
+ /// In en, this message translates to:
928
+ /// **'TTS Speed'**
929
+ String get tts_speed;
930
+
931
+ /// TTS speed setting description
932
+ ///
933
+ /// In en, this message translates to:
934
+ /// **'Adjust the playback rate.'**
935
+ String get tts_speed_desc;
936
+
937
+ /// Button to manage TTS models
938
+ ///
939
+ /// In en, this message translates to:
940
+ /// **'Manage TTS Models'**
941
+ String get manage_tts_models;
942
+
943
+ /// Button to manage on-device models
944
+ ///
945
+ /// In en, this message translates to:
946
+ /// **'Manage On-Device Models'**
947
+ String get manage_on_device_models;
948
+
949
+ /// Toggle for streaming responses
950
+ ///
951
+ /// In en, this message translates to:
952
+ /// **'Streaming Responses'**
953
+ String get streaming_responses;
954
+
955
+ /// Toggle for auto-generating conversation titles
956
+ ///
957
+ /// In en, this message translates to:
958
+ /// **'Auto-generate Titles'**
959
+ String get auto_generate_titles;
960
+
961
+ /// Toggle for send on enter behavior
962
+ ///
963
+ /// In en, this message translates to:
964
+ /// **'Send on Enter'**
965
+ String get send_on_enter;
966
+
967
+ /// Toggle for system messages visibility
968
+ ///
969
+ /// In en, this message translates to:
970
+ /// **'Show System Messages'**
971
+ String get show_system_messages;
972
+
973
+ /// Toggle for haptic feedback
974
+ ///
975
+ /// In en, this message translates to:
976
+ /// **'Haptic Feedback'**
977
+ String get haptic_feedback;
978
+
979
+ /// Toggle for MCP feature
980
+ ///
981
+ /// In en, this message translates to:
982
+ /// **'Enable MCP'**
983
+ String get enable_mcp;
984
+
985
+ /// Toggle for MCP default in new chats
986
+ ///
987
+ /// In en, this message translates to:
988
+ /// **'New Chat MCP Default'**
989
+ String get new_chat_mcp_default;
990
+
991
+ /// Toggle for data indicator visibility
992
+ ///
993
+ /// In en, this message translates to:
994
+ /// **'Show Data Indicator'**
995
+ String get show_data_indicator;
996
+
997
+ /// Privacy information text
998
+ ///
999
+ /// In en, this message translates to:
1000
+ /// **'\"LocalMind never sees your data\"'**
1001
+ String get privacy_info;
1002
+
1003
+ /// Dangerous action button to clear all conversations
1004
+ ///
1005
+ /// In en, this message translates to:
1006
+ /// **'Delete All Conversations'**
1007
+ String get delete_all_conversations;
1008
+
1009
+ /// Button to reset all settings
1010
+ ///
1011
+ /// In en, this message translates to:
1012
+ /// **'Reset Settings to Defaults'**
1013
+ String get reset_settings_defaults;
1014
+
1015
+ /// Fallback chat screen title
1016
+ ///
1017
+ /// In en, this message translates to:
1018
+ /// **'LocalMind'**
1019
+ String get chat_title;
1020
+
1021
+ /// Tooltip for chat parameters button
1022
+ ///
1023
+ /// In en, this message translates to:
1024
+ /// **'Chat Parameters'**
1025
+ String get chat_parameters_tooltip;
1026
+
1027
+ /// Menu item to change persona
1028
+ ///
1029
+ /// In en, this message translates to:
1030
+ /// **'Change Persona'**
1031
+ String get change_persona;
1032
+
1033
+ /// Menu item to set persona
1034
+ ///
1035
+ /// In en, this message translates to:
1036
+ /// **'Set Persona'**
1037
+ String get set_persona;
1038
+
1039
+ /// Menu item to remove persona
1040
+ ///
1041
+ /// In en, this message translates to:
1042
+ /// **'Remove Persona'**
1043
+ String get remove_persona;
1044
+
1045
+ /// Menu item to clear conversation
1046
+ ///
1047
+ /// In en, this message translates to:
1048
+ /// **'Clear Conversation'**
1049
+ String get clear_conversation;
1050
+
1051
+ /// Connection error banner text
1052
+ ///
1053
+ /// In en, this message translates to:
1054
+ /// **'Connection error. Check your server.'**
1055
+ String get connection_error;
1056
+
1057
+ /// Disconnected banner text
1058
+ ///
1059
+ /// In en, this message translates to:
1060
+ /// **'Disconnected from server.'**
1061
+ String get disconnected;
1062
+
1063
+ /// Configure action button
1064
+ ///
1065
+ /// In en, this message translates to:
1066
+ /// **'Configure'**
1067
+ String get configure;
1068
+
1069
+ /// Model selection placeholder
1070
+ ///
1071
+ /// In en, this message translates to:
1072
+ /// **'Select Model'**
1073
+ String get select_model;
1074
+
1075
+ /// Persona selection sheet title
1076
+ ///
1077
+ /// In en, this message translates to:
1078
+ /// **'Select Persona'**
1079
+ String get select_persona;
1080
+
1081
+ /// Empty state heading
1082
+ ///
1083
+ /// In en, this message translates to:
1084
+ /// **'Start a conversation'**
1085
+ String get start_conversation;
1086
+
1087
+ /// Recent chats section label
1088
+ ///
1089
+ /// In en, this message translates to:
1090
+ /// **'Recent chats'**
1091
+ String get recent_chats;
1092
+
1093
+ /// See all link text
1094
+ ///
1095
+ /// In en, this message translates to:
1096
+ /// **'See all'**
1097
+ String get see_all;
1098
+
1099
+ /// Quick prompt chip text
1100
+ ///
1101
+ /// In en, this message translates to:
1102
+ /// **'Help me write a function'**
1103
+ String get quick_write;
1104
+
1105
+ /// Quick prompt chip text
1106
+ ///
1107
+ /// In en, this message translates to:
1108
+ /// **'Explain this code'**
1109
+ String get quick_explain;
1110
+
1111
+ /// Quick prompt chip text
1112
+ ///
1113
+ /// In en, this message translates to:
1114
+ /// **'Debug this for me'**
1115
+ String get quick_debug;
1116
+
1117
+ /// Quick prompt chip text
1118
+ ///
1119
+ /// In en, this message translates to:
1120
+ /// **'How do I use async/await?'**
1121
+ String get quick_async;
1122
+
1123
+ /// Corrupted chat error title
1124
+ ///
1125
+ /// In en, this message translates to:
1126
+ /// **'History Missing'**
1127
+ String get history_missing_title;
1128
+
1129
+ /// Corrupted chat error description
1130
+ ///
1131
+ /// In en, this message translates to:
1132
+ /// **'Either the messages in this chat were deleted or the history record is corrupted.'**
1133
+ String get history_missing_desc;
1134
+
1135
+ /// Button for debug details
1136
+ ///
1137
+ /// In en, this message translates to:
1138
+ /// **'Technical Details'**
1139
+ String get technical_details;
1140
+
1141
+ /// Diagnostic label
1142
+ ///
1143
+ /// In en, this message translates to:
1144
+ /// **'Last Error:'**
1145
+ String get last_error;
1146
+
1147
+ /// Button to copy debug info
1148
+ ///
1149
+ /// In en, this message translates to:
1150
+ /// **'Copy Info'**
1151
+ String get copy_info;
1152
+
1153
+ /// Debug row label
1154
+ ///
1155
+ /// In en, this message translates to:
1156
+ /// **'Conversation ID'**
1157
+ String get conversation_id;
1158
+
1159
+ /// Debug row label
1160
+ ///
1161
+ /// In en, this message translates to:
1162
+ /// **'Created At'**
1163
+ String get created_at;
1164
+
1165
+ /// Debug row label
1166
+ ///
1167
+ /// In en, this message translates to:
1168
+ /// **'Expected Messages'**
1169
+ String get expected_messages;
1170
+
1171
+ /// Debug dialog description
1172
+ ///
1173
+ /// In en, this message translates to:
1174
+ /// **'Diagnostic information to help identify synchronization issues.'**
1175
+ String get debug_dialog_desc;
1176
+
1177
+ /// Chat input field hint
1178
+ ///
1179
+ /// In en, this message translates to:
1180
+ /// **'Ask anything'**
1181
+ String get chat_input_hint;
1182
+
1183
+ /// Tooltip for send button
1184
+ ///
1185
+ /// In en, this message translates to:
1186
+ /// **'Send message'**
1187
+ String get send_message_tooltip;
1188
+
1189
+ /// Tooltip for stop button
1190
+ ///
1191
+ /// In en, this message translates to:
1192
+ /// **'Stop generation'**
1193
+ String get stop_generation_tooltip;
1194
+
1195
+ /// Tooltip for attach button
1196
+ ///
1197
+ /// In en, this message translates to:
1198
+ /// **'Attach images'**
1199
+ String get attach_images_tooltip;
1200
+
1201
+ /// Tool call label in chat bubble
1202
+ ///
1203
+ /// In en, this message translates to:
1204
+ /// **'Tool: {toolCallId}'**
1205
+ String tool_label(String toolCallId);
1206
+
1207
+ /// Unknown tool call fallback
1208
+ ///
1209
+ /// In en, this message translates to:
1210
+ /// **'Tool: Unknown'**
1211
+ String get tool_unknown;
1212
+
1213
+ /// Message actions sheet title
1214
+ ///
1215
+ /// In en, this message translates to:
1216
+ /// **'Message options'**
1217
+ String get message_options;
1218
+
1219
+ /// Copy as markdown option
1220
+ ///
1221
+ /// In en, this message translates to:
1222
+ /// **'Copy as Markdown'**
1223
+ String get copy_markdown;
1224
+
1225
+ /// Copied as markdown snackbar
1226
+ ///
1227
+ /// In en, this message translates to:
1228
+ /// **'Copied as Markdown'**
1229
+ String get copied_markdown;
1230
+
1231
+ /// Read aloud action
1232
+ ///
1233
+ /// In en, this message translates to:
1234
+ /// **'Read Aloud'**
1235
+ String get read_aloud;
1236
+
1237
+ /// Stop reading action
1238
+ ///
1239
+ /// In en, this message translates to:
1240
+ /// **'Stop Reading'**
1241
+ String get stop_reading;
1242
+
1243
+ /// More actions label
1244
+ ///
1245
+ /// In en, this message translates to:
1246
+ /// **'More'**
1247
+ String get more;
1248
+
1249
+ /// Character count display
1250
+ ///
1251
+ /// In en, this message translates to:
1252
+ /// **'{length} characters'**
1253
+ String character_count(int length);
1254
+
1255
+ /// Edit message dialog title
1256
+ ///
1257
+ /// In en, this message translates to:
1258
+ /// **'Edit message'**
1259
+ String get edit_message;
1260
+
1261
+ /// Edit message dialog description
1262
+ ///
1263
+ /// In en, this message translates to:
1264
+ /// **'Saving will remove the assistant response below and regenerate.'**
1265
+ String get edit_message_desc;
1266
+
1267
+ /// Save and regenerate button
1268
+ ///
1269
+ /// In en, this message translates to:
1270
+ /// **'Save & regenerate'**
1271
+ String get save_regenerate;
1272
+
1273
+ /// Chat settings sheet title
1274
+ ///
1275
+ /// In en, this message translates to:
1276
+ /// **'Chat Settings'**
1277
+ String get chat_settings_title;
1278
+
1279
+ /// Reset to defaults button
1280
+ ///
1281
+ /// In en, this message translates to:
1282
+ /// **'Reset Defaults'**
1283
+ String get reset_defaults;
1284
+
1285
+ /// Chat settings parameters tab
1286
+ ///
1287
+ /// In en, this message translates to:
1288
+ /// **'Parameters'**
1289
+ String get parameters_tab;
1290
+
1291
+ /// Chat settings MCP tab
1292
+ ///
1293
+ /// In en, this message translates to:
1294
+ /// **'MCP'**
1295
+ String get mcp_tab;
1296
+
1297
+ /// Temperature slider label
1298
+ ///
1299
+ /// In en, this message translates to:
1300
+ /// **'Temperature'**
1301
+ String get temperature;
1302
+
1303
+ /// Temperature description
1304
+ ///
1305
+ /// In en, this message translates to:
1306
+ /// **'Controls randomness: Higher = Creative, Lower = Focused'**
1307
+ String get temperature_desc;
1308
+
1309
+ /// Top P slider label
1310
+ ///
1311
+ /// In en, this message translates to:
1312
+ /// **'Top P'**
1313
+ String get top_p;
1314
+
1315
+ /// Top P description
1316
+ ///
1317
+ /// In en, this message translates to:
1318
+ /// **'Nucleus sampling threshold'**
1319
+ String get top_p_desc;
1320
+
1321
+ /// Max tokens input label
1322
+ ///
1323
+ /// In en, this message translates to:
1324
+ /// **'Max Tokens'**
1325
+ String get max_tokens;
1326
+
1327
+ /// Max tokens description
1328
+ ///
1329
+ /// In en, this message translates to:
1330
+ /// **'Response limit'**
1331
+ String get max_tokens_desc;
1332
+
1333
+ /// Context length input label
1334
+ ///
1335
+ /// In en, this message translates to:
1336
+ /// **'Context Length'**
1337
+ String get context_length;
1338
+
1339
+ /// Context length description
1340
+ ///
1341
+ /// In en, this message translates to:
1342
+ /// **'History window'**
1343
+ String get context_length_desc;
1344
+
1345
+ /// Warning when MCP is disabled
1346
+ ///
1347
+ /// In en, this message translates to:
1348
+ /// **'MCP is disabled globally. Enable it in Settings to use these features.'**
1349
+ String get mcp_disabled_warning;
1350
+
1351
+ /// Toggle to enable MCP for current chat
1352
+ ///
1353
+ /// In en, this message translates to:
1354
+ /// **'Enable MCP for this chat'**
1355
+ String get mcp_enable_chat;
1356
+
1357
+ /// Toggle for auto-executing tools
1358
+ ///
1359
+ /// In en, this message translates to:
1360
+ /// **'Auto-execute tools'**
1361
+ String get auto_execute_tools;
1362
+
1363
+ /// Section label for adding temp MCP server
1364
+ ///
1365
+ /// In en, this message translates to:
1366
+ /// **'Add Ephemeral MCP Server'**
1367
+ String get add_ephemeral_mcp;
1368
+
1369
+ /// MCP label field placeholder
1370
+ ///
1371
+ /// In en, this message translates to:
1372
+ /// **'Label'**
1373
+ String get mcp_label_placeholder;
1374
+
1375
+ /// MCP URL field placeholder
1376
+ ///
1377
+ /// In en, this message translates to:
1378
+ /// **'URL (https://...)'**
1379
+ String get mcp_url_placeholder;
1380
+
1381
+ /// Active MCP integrations section
1382
+ ///
1383
+ /// In en, this message translates to:
1384
+ /// **'Active Integrations'**
1385
+ String get active_integrations;
1386
+
1387
+ /// Notification banner title
1388
+ ///
1389
+ /// In en, this message translates to:
1390
+ /// **'Enable Notifications'**
1391
+ String get enable_notifications;
1392
+
1393
+ /// Notification banner subtitle
1394
+ ///
1395
+ /// In en, this message translates to:
1396
+ /// **'Get notified when models finish downloading.'**
1397
+ String get enable_notifications_desc;
1398
+
1399
+ /// Chat history screen title
1400
+ ///
1401
+ /// In en, this message translates to:
1402
+ /// **'Chat History'**
1403
+ String get chat_history_title;
1404
+
1405
+ /// Recent conversation timestamp
1406
+ ///
1407
+ /// In en, this message translates to:
1408
+ /// **'Just now'**
1409
+ String get conversation_just_now;
1410
+
1411
+ /// Minutes ago timestamp
1412
+ ///
1413
+ /// In en, this message translates to:
1414
+ /// **'{minutes}m ago'**
1415
+ String conversation_minutes_ago(int minutes);
1416
+
1417
+ /// Hours ago timestamp
1418
+ ///
1419
+ /// In en, this message translates to:
1420
+ /// **'{hours}h ago'**
1421
+ String conversation_hours_ago(int hours);
1422
+
1423
+ /// Yesterday timestamp
1424
+ ///
1425
+ /// In en, this message translates to:
1426
+ /// **'Yesterday'**
1427
+ String get conversation_yesterday;
1428
+
1429
+ /// Days ago timestamp
1430
+ ///
1431
+ /// In en, this message translates to:
1432
+ /// **'{days}d ago'**
1433
+ String conversation_days_ago(int days);
1434
+
1435
+ /// Full date timestamp
1436
+ ///
1437
+ /// In en, this message translates to:
1438
+ /// **'{month}/{day}/{year}'**
1439
+ String conversation_date(int month, int day, int year);
1440
+
1441
+ /// Options menu tooltip
1442
+ ///
1443
+ /// In en, this message translates to:
1444
+ /// **'Options'**
1445
+ String get options_tooltip;
1446
+
1447
+ /// Search empty state
1448
+ ///
1449
+ /// In en, this message translates to:
1450
+ /// **'No results found'**
1451
+ String get no_results_found;
1452
+
1453
+ /// Empty conversations state
1454
+ ///
1455
+ /// In en, this message translates to:
1456
+ /// **'No conversations yet'**
1457
+ String get no_conversations_yet;
1458
+
1459
+ /// Search empty state subtitle
1460
+ ///
1461
+ /// In en, this message translates to:
1462
+ /// **'Try a different search term'**
1463
+ String get try_different_search;
1464
+
1465
+ /// Empty state subtitle when no conversations
1466
+ ///
1467
+ /// In en, this message translates to:
1468
+ /// **'Start a new conversation'**
1469
+ String get start_new_conversation;
1470
+
1471
+ /// Rename dialog title
1472
+ ///
1473
+ /// In en, this message translates to:
1474
+ /// **'Rename conversation'**
1475
+ String get rename_conversation;
1476
+
1477
+ /// Rename text field hint
1478
+ ///
1479
+ /// In en, this message translates to:
1480
+ /// **'Enter new title'**
1481
+ String get enter_new_title;
1482
+
1483
+ /// Pinned conversations section header
1484
+ ///
1485
+ /// In en, this message translates to:
1486
+ /// **'PINNED'**
1487
+ String get pinned_section;
1488
+
1489
+ /// Today section header
1490
+ ///
1491
+ /// In en, this message translates to:
1492
+ /// **'TODAY'**
1493
+ String get today_section;
1494
+
1495
+ /// Yesterday section header
1496
+ ///
1497
+ /// In en, this message translates to:
1498
+ /// **'YESTERDAY'**
1499
+ String get yesterday_section;
1500
+
1501
+ /// Previous 7 days section header
1502
+ ///
1503
+ /// In en, this message translates to:
1504
+ /// **'PREVIOUS 7 DAYS'**
1505
+ String get previous_7_days;
1506
+
1507
+ /// Previous 30 days section header
1508
+ ///
1509
+ /// In en, this message translates to:
1510
+ /// **'PREVIOUS 30 DAYS'**
1511
+ String get previous_30_days;
1512
+
1513
+ /// Older conversations section header
1514
+ ///
1515
+ /// In en, this message translates to:
1516
+ /// **'OLDER'**
1517
+ String get older_section;
1518
+
1519
+ /// Onboarding screen branding text
1520
+ ///
1521
+ /// In en, this message translates to:
1522
+ /// **'LOCALMIND'**
1523
+ String get onboarding_localmind;
1524
+
1525
+ /// Onboarding heading for server connection
1526
+ ///
1527
+ /// In en, this message translates to:
1528
+ /// **'Connect Your\nServer'**
1529
+ String get onboarding_connect_server;
1530
+
1531
+ /// Onboarding server connection description
1532
+ ///
1533
+ /// In en, this message translates to:
1534
+ /// **'Connect to LM Studio, Ollama, or\nOpenRouter to start your private AI\nexperience.'**
1535
+ String get onboarding_connect_desc;
1536
+
1537
+ /// On-device server type card title
1538
+ ///
1539
+ /// In en, this message translates to:
1540
+ /// **'On-Device'**
1541
+ String get server_type_on_device;
1542
+
1543
+ /// On-device server type card subtitle
1544
+ ///
1545
+ /// In en, this message translates to:
1546
+ /// **'NO SERVER NEEDED'**
1547
+ String get server_type_on_device_sub;
1548
+
1549
+ /// LM Studio server type card title
1550
+ ///
1551
+ /// In en, this message translates to:
1552
+ /// **'LM Studio'**
1553
+ String get server_type_lm_studio;
1554
+
1555
+ /// LM Studio server type card subtitle
1556
+ ///
1557
+ /// In en, this message translates to:
1558
+ /// **'LOCAL API'**
1559
+ String get server_type_lm_studio_sub;
1560
+
1561
+ /// Ollama server type card title
1562
+ ///
1563
+ /// In en, this message translates to:
1564
+ /// **'Ollama'**
1565
+ String get server_type_ollama;
1566
+
1567
+ /// Ollama server type card subtitle
1568
+ ///
1569
+ /// In en, this message translates to:
1570
+ /// **'CLI ENGINE'**
1571
+ String get server_type_ollama_sub;
1572
+
1573
+ /// OpenRouter server type card title
1574
+ ///
1575
+ /// In en, this message translates to:
1576
+ /// **'OpenRouter'**
1577
+ String get server_type_openrouter;
1578
+
1579
+ /// OpenRouter server type card subtitle
1580
+ ///
1581
+ /// In en, this message translates to:
1582
+ /// **'UNIFIED CLOUD'**
1583
+ String get server_type_openrouter_sub;
1584
+
1585
+ /// Onboarding ready status
1586
+ ///
1587
+ /// In en, this message translates to:
1588
+ /// **'READY TO CONTINUE'**
1589
+ String get ready_continue;
1590
+
1591
+ /// Onboarding waiting status
1592
+ ///
1593
+ /// In en, this message translates to:
1594
+ /// **'WAITING FOR SELECTION'**
1595
+ String get waiting_selection;
1596
+
1597
+ /// Onboarding setup screen title
1598
+ ///
1599
+ /// In en, this message translates to:
1600
+ /// **'Setup Connection'**
1601
+ String get setup_connection;
1602
+
1603
+ /// Onboarding setup description
1604
+ ///
1605
+ /// In en, this message translates to:
1606
+ /// **'Configure your {server} server to start chatting.'**
1607
+ String setup_connection_desc(String server);
1608
+
1609
+ /// Server name field label
1610
+ ///
1611
+ /// In en, this message translates to:
1612
+ /// **'Server Name'**
1613
+ String get server_name;
1614
+
1615
+ /// Validation: name required
1616
+ ///
1617
+ /// In en, this message translates to:
1618
+ /// **'Name required'**
1619
+ String get name_required;
1620
+
1621
+ /// Validation: max 50 chars
1622
+ ///
1623
+ /// In en, this message translates to:
1624
+ /// **'Max 50 characters'**
1625
+ String get name_max_50;
1626
+
1627
+ /// Host field label
1628
+ ///
1629
+ /// In en, this message translates to:
1630
+ /// **'Host / IP Address'**
1631
+ String get host_label;
1632
+
1633
+ /// Validation: host required
1634
+ ///
1635
+ /// In en, this message translates to:
1636
+ /// **'Host required'**
1637
+ String get host_required;
1638
+
1639
+ /// Port field label
1640
+ ///
1641
+ /// In en, this message translates to:
1642
+ /// **'Port'**
1643
+ String get port_label;
1644
+
1645
+ /// Validation: port required
1646
+ ///
1647
+ /// In en, this message translates to:
1648
+ /// **'Port required'**
1649
+ String get port_required;
1650
+
1651
+ /// Validation: invalid port number
1652
+ ///
1653
+ /// In en, this message translates to:
1654
+ /// **'Must be a number'**
1655
+ String get port_invalid;
1656
+
1657
+ /// Validation: port range
1658
+ ///
1659
+ /// In en, this message translates to:
1660
+ /// **'Enter a valid port (1-65535)'**
1661
+ String get port_range;
1662
+
1663
+ /// Required API key label
1664
+ ///
1665
+ /// In en, this message translates to:
1666
+ /// **'API Key *'**
1667
+ String get api_key_required;
1668
+
1669
+ /// Optional API key label
1670
+ ///
1671
+ /// In en, this message translates to:
1672
+ /// **'API Key (Optional)'**
1673
+ String get api_key_optional;
1674
+
1675
+ /// Validation: API key for OpenRouter
1676
+ ///
1677
+ /// In en, this message translates to:
1678
+ /// **'API Key required for OpenRouter'**
1679
+ String get api_key_required_openrouter;
1680
+
1681
+ /// Validation: API key format
1682
+ ///
1683
+ /// In en, this message translates to:
1684
+ /// **'OpenRouter API keys start with sk-'**
1685
+ String get api_key_format;
1686
+
1687
+ /// Server name field hint
1688
+ ///
1689
+ /// In en, this message translates to:
1690
+ /// **'My Server'**
1691
+ String get my_server_hint;
1692
+
1693
+ /// Validation: name too long
1694
+ ///
1695
+ /// In en, this message translates to:
1696
+ /// **'Name must be 50 characters or less'**
1697
+ String get name_length_validation;
1698
+
1699
+ /// Validation: invalid host
1700
+ ///
1701
+ /// In en, this message translates to:
1702
+ /// **'Enter a valid hostname or IP address'**
1703
+ String get host_valid;
1704
+
1705
+ /// OpenRouter API key hint
1706
+ ///
1707
+ /// In en, this message translates to:
1708
+ /// **'sk-...'**
1709
+ String get api_key_hint_openrouter;
1710
+
1711
+ /// Generic API key hint
1712
+ ///
1713
+ /// In en, this message translates to:
1714
+ /// **'For authenticated servers'**
1715
+ String get api_key_hint_generic;
1716
+
1717
+ /// Update server button
1718
+ ///
1719
+ /// In en, this message translates to:
1720
+ /// **'Update Server'**
1721
+ String get update_server;
1722
+
1723
+ /// Save server button
1724
+ ///
1725
+ /// In en, this message translates to:
1726
+ /// **'Save Server'**
1727
+ String get save_server;
1728
+
1729
+ /// Server update snackbar
1730
+ ///
1731
+ /// In en, this message translates to:
1732
+ /// **'Server updated'**
1733
+ String get server_updated;
1734
+
1735
+ /// Server added snackbar
1736
+ ///
1737
+ /// In en, this message translates to:
1738
+ /// **'Server added'**
1739
+ String get server_added;
1740
+
1741
+ /// Onboarding download model screen title
1742
+ ///
1743
+ /// In en, this message translates to:
1744
+ /// **'Download a Model'**
1745
+ String get download_model_title;
1746
+
1747
+ /// Onboarding download model description
1748
+ ///
1749
+ /// In en, this message translates to:
1750
+ /// **'Choose a model to download.\nIt will run locally on your device.'**
1751
+ String get download_model_desc;
1752
+
1753
+ /// Platform limitation notice
1754
+ ///
1755
+ /// In en, this message translates to:
1756
+ /// **'On-device inference is currently available on Android only.'**
1757
+ String get on_device_android_only;
1758
+
1759
+ /// Total RAM label
1760
+ ///
1761
+ /// In en, this message translates to:
1762
+ /// **'Total RAM'**
1763
+ String get total_ram;
1764
+
1765
+ /// Available label
1766
+ ///
1767
+ /// In en, this message translates to:
1768
+ /// **'Available'**
1769
+ String get available;
1770
+
1771
+ /// Minimum RAM requirement label
1772
+ ///
1773
+ /// In en, this message translates to:
1774
+ /// **'{fileSize} GB RAM min'**
1775
+ String ram_min_required(String fileSize);
1776
+
1777
+ /// Download progress display
1778
+ ///
1779
+ /// In en, this message translates to:
1780
+ /// **'{percent}% • {speed}'**
1781
+ String download_progress(String percent, String speed);
1782
+
1783
+ /// Estimated time remaining label
1784
+ ///
1785
+ /// In en, this message translates to:
1786
+ /// **'ETA: {eta}'**
1787
+ String eta_label(String eta);
1788
+
1789
+ /// Paused download status with progress
1790
+ ///
1791
+ /// In en, this message translates to:
1792
+ /// **'Paused - {percent}%'**
1793
+ String paused_progress(String percent);
1794
+
1795
+ /// RAM warning during download
1796
+ ///
1797
+ /// In en, this message translates to:
1798
+ /// **'This model requires at least {ram} GB RAM, but your device has {totalMemory}. It may not run correctly or could cause the app to crash.'**
1799
+ String ram_warning_body_download(String ram, String totalMemory);
1800
+
1801
+ /// RAM warning during model load
1802
+ ///
1803
+ /// In en, this message translates to:
1804
+ /// **'Your device has {availableRAM} available RAM, but this model recommends at least {ram} GB. Loading it might fail or cause instability.'**
1805
+ String ram_warning_body_load(String availableRAM, String ram);
1806
+
1807
+ /// Onboarding theme selection title
1808
+ ///
1809
+ /// In en, this message translates to:
1810
+ /// **'Choose Theme'**
1811
+ String get choose_theme;
1812
+
1813
+ /// Onboarding theme selection description
1814
+ ///
1815
+ /// In en, this message translates to:
1816
+ /// **'Personalize the app appearance. You can always change this later in settings.'**
1817
+ String get choose_theme_desc;
1818
+
1819
+ /// System theme card title
1820
+ ///
1821
+ /// In en, this message translates to:
1822
+ /// **'System'**
1823
+ String get theme_card_system;
1824
+
1825
+ /// System theme card subtitle
1826
+ ///
1827
+ /// In en, this message translates to:
1828
+ /// **'Matches your device settings'**
1829
+ String get theme_card_system_sub;
1830
+
1831
+ /// Light theme card title
1832
+ ///
1833
+ /// In en, this message translates to:
1834
+ /// **'Light'**
1835
+ String get theme_card_light;
1836
+
1837
+ /// Light theme card subtitle
1838
+ ///
1839
+ /// In en, this message translates to:
1840
+ /// **'Clean and bright'**
1841
+ String get theme_card_light_sub;
1842
+
1843
+ /// Dark theme card title
1844
+ ///
1845
+ /// In en, this message translates to:
1846
+ /// **'Dark'**
1847
+ String get theme_card_dark;
1848
+
1849
+ /// Dark theme card subtitle
1850
+ ///
1851
+ /// In en, this message translates to:
1852
+ /// **'Easy on the eyes'**
1853
+ String get theme_card_dark_sub;
1854
+
1855
+ /// Claude theme card title
1856
+ ///
1857
+ /// In en, this message translates to:
1858
+ /// **'Claude'**
1859
+ String get theme_card_claude;
1860
+
1861
+ /// Claude theme card subtitle
1862
+ ///
1863
+ /// In en, this message translates to:
1864
+ /// **'A warm, peach-tinted theme'**
1865
+ String get theme_card_claude_sub;
1866
+
1867
+ /// Onboarding notification screen heading
1868
+ ///
1869
+ /// In en, this message translates to:
1870
+ /// **'Stay Updated'**
1871
+ String get stay_updated;
1872
+
1873
+ /// Onboarding notification screen description
1874
+ ///
1875
+ /// In en, this message translates to:
1876
+ /// **'Get notified when your AI models finish downloading or when long-running tasks complete.'**
1877
+ String get stay_updated_desc;
1878
+
1879
+ /// Notification benefit list item
1880
+ ///
1881
+ /// In en, this message translates to:
1882
+ /// **'Model download progress'**
1883
+ String get notification_benefit_downloads;
1884
+
1885
+ /// Notification benefit list item
1886
+ ///
1887
+ /// In en, this message translates to:
1888
+ /// **'Generation completions'**
1889
+ String get notification_benefit_completions;
1890
+
1891
+ /// Notification benefit list item
1892
+ ///
1893
+ /// In en, this message translates to:
1894
+ /// **'Background tasks status'**
1895
+ String get notification_benefit_background;
1896
+
1897
+ /// Allow notifications button
1898
+ ///
1899
+ /// In en, this message translates to:
1900
+ /// **'Allow Notifications'**
1901
+ String get allow_notifications;
1902
+
1903
+ /// Server list screen title
1904
+ ///
1905
+ /// In en, this message translates to:
1906
+ /// **'Servers'**
1907
+ String get servers_title;
1908
+
1909
+ /// Empty server list title
1910
+ ///
1911
+ /// In en, this message translates to:
1912
+ /// **'No Servers Yet'**
1913
+ String get no_servers_yet;
1914
+
1915
+ /// Empty server list description
1916
+ ///
1917
+ /// In en, this message translates to:
1918
+ /// **'Add your first server to start chatting with AI models.'**
1919
+ String get no_servers_desc;
1920
+
1921
+ /// Add server button
1922
+ ///
1923
+ /// In en, this message translates to:
1924
+ /// **'Add Server'**
1925
+ String get add_server;
1926
+
1927
+ /// Server switch snackbar
1928
+ ///
1929
+ /// In en, this message translates to:
1930
+ /// **'Switched to {name}'**
1931
+ String switched_to_server(String name);
1932
+
1933
+ /// Edit server screen title
1934
+ ///
1935
+ /// In en, this message translates to:
1936
+ /// **'Edit Server'**
1937
+ String get edit_server;
1938
+
1939
+ /// Add server screen title
1940
+ ///
1941
+ /// In en, this message translates to:
1942
+ /// **'Add Server'**
1943
+ String get add_server_title;
1944
+
1945
+ /// Server type section label
1946
+ ///
1947
+ /// In en, this message translates to:
1948
+ /// **'Server Type'**
1949
+ String get server_type_label;
1950
+
1951
+ /// Server icon section label
1952
+ ///
1953
+ /// In en, this message translates to:
1954
+ /// **'Server Icon'**
1955
+ String get server_icon_label;
1956
+
1957
+ /// Default icon fallback text
1958
+ ///
1959
+ /// In en, this message translates to:
1960
+ /// **'Default icon'**
1961
+ String get default_icon;
1962
+
1963
+ /// LM Studio server type display
1964
+ ///
1965
+ /// In en, this message translates to:
1966
+ /// **'LM Studio'**
1967
+ String get server_type_lm_studio_display;
1968
+
1969
+ /// OpenAI compatible server type display
1970
+ ///
1971
+ /// In en, this message translates to:
1972
+ /// **'OpenAI Compatible'**
1973
+ String get server_type_openai_display;
1974
+
1975
+ /// Ollama server type display
1976
+ ///
1977
+ /// In en, this message translates to:
1978
+ /// **'Ollama'**
1979
+ String get server_type_ollama_display;
1980
+
1981
+ /// OpenRouter server type display
1982
+ ///
1983
+ /// In en, this message translates to:
1984
+ /// **'OpenRouter'**
1985
+ String get server_type_openrouter_display;
1986
+
1987
+ /// On-device server type display
1988
+ ///
1989
+ /// In en, this message translates to:
1990
+ /// **'On-Device'**
1991
+ String get server_type_on_device_display;
1992
+
1993
+ /// OpenRouter default address
1994
+ ///
1995
+ /// In en, this message translates to:
1996
+ /// **'openrouter.ai'**
1997
+ String get server_address_openrouter;
1998
+
1999
+ /// On-device server address
2000
+ ///
2001
+ /// In en, this message translates to:
2002
+ /// **'Local inference'**
2003
+ String get server_address_on_device;
2004
+
2005
+ /// Server address display format
2006
+ ///
2007
+ /// In en, this message translates to:
2008
+ /// **'{host}:{port}'**
2009
+ String server_address_format(String host, String port);
2010
+
2011
+ /// Default server badge
2012
+ ///
2013
+ /// In en, this message translates to:
2014
+ /// **'Default'**
2015
+ String get default_badge;
2016
+
2017
+ /// Set as default menu item
2018
+ ///
2019
+ /// In en, this message translates to:
2020
+ /// **'Set as Default'**
2021
+ String get set_as_default;
2022
+
2023
+ /// Icon picker sheet title
2024
+ ///
2025
+ /// In en, this message translates to:
2026
+ /// **'Select Icon'**
2027
+ String get select_icon;
2028
+
2029
+ /// Icon picker sheet description
2030
+ ///
2031
+ /// In en, this message translates to:
2032
+ /// **'Choose an icon for your server'**
2033
+ String get select_icon_desc;
2034
+
2035
+ /// Icon search field placeholder
2036
+ ///
2037
+ /// In en, this message translates to:
2038
+ /// **'Search icons...'**
2039
+ String get search_icons_hint;
2040
+
2041
+ /// Server icon name
2042
+ ///
2043
+ /// In en, this message translates to:
2044
+ /// **'Server Stack'**
2045
+ String get server_icon_stack;
2046
+
2047
+ /// Server icon name
2048
+ ///
2049
+ /// In en, this message translates to:
2050
+ /// **'Server Stack 02'**
2051
+ String get server_icon_stack2;
2052
+
2053
+ /// Server icon name
2054
+ ///
2055
+ /// In en, this message translates to:
2056
+ /// **'Server Stack 03'**
2057
+ String get server_icon_stack3;
2058
+
2059
+ /// Server icon name
2060
+ ///
2061
+ /// In en, this message translates to:
2062
+ /// **'Cloud'**
2063
+ String get server_icon_cloud;
2064
+
2065
+ /// Server icon name
2066
+ ///
2067
+ /// In en, this message translates to:
2068
+ /// **'Cloud Server'**
2069
+ String get server_icon_cloud_server;
2070
+
2071
+ /// Server icon name
2072
+ ///
2073
+ /// In en, this message translates to:
2074
+ /// **'MCP Server'**
2075
+ String get server_icon_mcp;
2076
+
2077
+ /// Server icon name
2078
+ ///
2079
+ /// In en, this message translates to:
2080
+ /// **'Database'**
2081
+ String get server_icon_database;
2082
+
2083
+ /// Server icon name
2084
+ ///
2085
+ /// In en, this message translates to:
2086
+ /// **'Database 01'**
2087
+ String get server_icon_database1;
2088
+
2089
+ /// Server icon name
2090
+ ///
2091
+ /// In en, this message translates to:
2092
+ /// **'Database 02'**
2093
+ String get server_icon_database2;
2094
+
2095
+ /// Server icon name
2096
+ ///
2097
+ /// In en, this message translates to:
2098
+ /// **'CPU'**
2099
+ String get server_icon_cpu;
2100
+
2101
+ /// Server icon name
2102
+ ///
2103
+ /// In en, this message translates to:
2104
+ /// **'Chip'**
2105
+ String get server_icon_chip;
2106
+
2107
+ /// Server icon name
2108
+ ///
2109
+ /// In en, this message translates to:
2110
+ /// **'Chip 02'**
2111
+ String get server_icon_chip2;
2112
+
2113
+ /// Server icon name
2114
+ ///
2115
+ /// In en, this message translates to:
2116
+ /// **'Computer'**
2117
+ String get server_icon_computer;
2118
+
2119
+ /// Server icon name
2120
+ ///
2121
+ /// In en, this message translates to:
2122
+ /// **'Laptop'**
2123
+ String get server_icon_laptop;
2124
+
2125
+ /// Server icon name
2126
+ ///
2127
+ /// In en, this message translates to:
2128
+ /// **'Computer Terminal'**
2129
+ String get server_icon_terminal;
2130
+
2131
+ /// Server icon name
2132
+ ///
2133
+ /// In en, this message translates to:
2134
+ /// **'Code'**
2135
+ String get server_icon_code;
2136
+
2137
+ /// Server icon name
2138
+ ///
2139
+ /// In en, this message translates to:
2140
+ /// **'AI Brain'**
2141
+ String get server_icon_ai_brain;
2142
+
2143
+ /// Server icon name
2144
+ ///
2145
+ /// In en, this message translates to:
2146
+ /// **'AI Brain 02'**
2147
+ String get server_icon_ai_brain2;
2148
+
2149
+ /// Server icon name
2150
+ ///
2151
+ /// In en, this message translates to:
2152
+ /// **'AI Cloud'**
2153
+ String get server_icon_ai_cloud;
2154
+
2155
+ /// Server icon name
2156
+ ///
2157
+ /// In en, this message translates to:
2158
+ /// **'AI Network'**
2159
+ String get server_icon_ai_network;
2160
+
2161
+ /// Server icon name
2162
+ ///
2163
+ /// In en, this message translates to:
2164
+ /// **'AI Chat'**
2165
+ String get server_icon_ai_chat;
2166
+
2167
+ /// Server icon name
2168
+ ///
2169
+ /// In en, this message translates to:
2170
+ /// **'Cellular Network'**
2171
+ String get server_icon_cellular;
2172
+
2173
+ /// Server icon name
2174
+ ///
2175
+ /// In en, this message translates to:
2176
+ /// **'Plug 01'**
2177
+ String get server_icon_plug1;
2178
+
2179
+ /// Server icon name
2180
+ ///
2181
+ /// In en, this message translates to:
2182
+ /// **'Plug 02'**
2183
+ String get server_icon_plug2;
2184
+
2185
+ /// Server icon name
2186
+ ///
2187
+ /// In en, this message translates to:
2188
+ /// **'Bot'**
2189
+ String get server_icon_bot;
2190
+
2191
+ /// Server icon name
2192
+ ///
2193
+ /// In en, this message translates to:
2194
+ /// **'Bot 02'**
2195
+ String get server_icon_bot2;
2196
+
2197
+ /// Server icon name
2198
+ ///
2199
+ /// In en, this message translates to:
2200
+ /// **'Robotic'**
2201
+ String get server_icon_robotic;
2202
+
2203
+ /// Server icon name
2204
+ ///
2205
+ /// In en, this message translates to:
2206
+ /// **'Rocket'**
2207
+ String get server_icon_rocket;
2208
+
2209
+ /// Server icon name
2210
+ ///
2211
+ /// In en, this message translates to:
2212
+ /// **'Star'**
2213
+ String get server_icon_star;
2214
+
2215
+ /// Server icon name
2216
+ ///
2217
+ /// In en, this message translates to:
2218
+ /// **'Settings 01'**
2219
+ String get server_icon_settings1;
2220
+
2221
+ /// Server icon name
2222
+ ///
2223
+ /// In en, this message translates to:
2224
+ /// **'Settings 02'**
2225
+ String get server_icon_settings2;
2226
+
2227
+ /// Server icon name
2228
+ ///
2229
+ /// In en, this message translates to:
2230
+ /// **'Home 01'**
2231
+ String get server_icon_home1;
2232
+
2233
+ /// Server icon name
2234
+ ///
2235
+ /// In en, this message translates to:
2236
+ /// **'Home 02'**
2237
+ String get server_icon_home2;
2238
+
2239
+ /// Server icon name
2240
+ ///
2241
+ /// In en, this message translates to:
2242
+ /// **'Folder 01'**
2243
+ String get server_icon_folder1;
2244
+
2245
+ /// Server icon name
2246
+ ///
2247
+ /// In en, this message translates to:
2248
+ /// **'Folder 02'**
2249
+ String get server_icon_folder2;
2250
+
2251
+ /// Server icon name
2252
+ ///
2253
+ /// In en, this message translates to:
2254
+ /// **'File 01'**
2255
+ String get server_icon_file1;
2256
+
2257
+ /// Server icon name
2258
+ ///
2259
+ /// In en, this message translates to:
2260
+ /// **'Lock'**
2261
+ String get server_icon_lock;
2262
+
2263
+ /// Server icon name
2264
+ ///
2265
+ /// In en, this message translates to:
2266
+ /// **'Key 01'**
2267
+ String get server_icon_key;
2268
+
2269
+ /// Server icon name
2270
+ ///
2271
+ /// In en, this message translates to:
2272
+ /// **'Link 01'**
2273
+ String get server_icon_link;
2274
+
2275
+ /// Server icon name
2276
+ ///
2277
+ /// In en, this message translates to:
2278
+ /// **'Globe'**
2279
+ String get server_icon_globe;
2280
+
2281
+ /// Server icon name
2282
+ ///
2283
+ /// In en, this message translates to:
2284
+ /// **'API'**
2285
+ String get server_icon_api;
2286
+
2287
+ /// Server icon name
2288
+ ///
2289
+ /// In en, this message translates to:
2290
+ /// **'Arrow Right 01'**
2291
+ String get server_icon_arrow_right;
2292
+
2293
+ /// Server icon name
2294
+ ///
2295
+ /// In en, this message translates to:
2296
+ /// **'Check Circle'**
2297
+ String get server_icon_check;
2298
+
2299
+ /// Server icon name
2300
+ ///
2301
+ /// In en, this message translates to:
2302
+ /// **'Alert Circle'**
2303
+ String get server_icon_alert;
2304
+
2305
+ /// Server icon name
2306
+ ///
2307
+ /// In en, this message translates to:
2308
+ /// **'Info Circle'**
2309
+ String get server_icon_info;
2310
+
2311
+ /// Server icon name
2312
+ ///
2313
+ /// In en, this message translates to:
2314
+ /// **'Zap'**
2315
+ String get server_icon_zap;
2316
+
2317
+ /// Server icon name
2318
+ ///
2319
+ /// In en, this message translates to:
2320
+ /// **'Cloud Upload'**
2321
+ String get server_icon_cloud_upload;
2322
+
2323
+ /// Server icon name
2324
+ ///
2325
+ /// In en, this message translates to:
2326
+ /// **'Cloud Download'**
2327
+ String get server_icon_cloud_download;
2328
+
2329
+ /// Server icon name
2330
+ ///
2331
+ /// In en, this message translates to:
2332
+ /// **'Refresh'**
2333
+ String get server_icon_refresh;
2334
+
2335
+ /// Server icon name
2336
+ ///
2337
+ /// In en, this message translates to:
2338
+ /// **'Hard Drive'**
2339
+ String get server_icon_hard_drive;
2340
+
2341
+ /// Server icon name
2342
+ ///
2343
+ /// In en, this message translates to:
2344
+ /// **'Drive'**
2345
+ String get server_icon_drive;
2346
+
2347
+ /// Persona list screen title
2348
+ ///
2349
+ /// In en, this message translates to:
2350
+ /// **'Personas'**
2351
+ String get personas_title;
2352
+
2353
+ /// General persona category
2354
+ ///
2355
+ /// In en, this message translates to:
2356
+ /// **'General'**
2357
+ String get persona_category_general;
2358
+
2359
+ /// Coding persona category
2360
+ ///
2361
+ /// In en, this message translates to:
2362
+ /// **'Coding'**
2363
+ String get persona_category_coding;
2364
+
2365
+ /// Education persona category
2366
+ ///
2367
+ /// In en, this message translates to:
2368
+ /// **'Education'**
2369
+ String get persona_category_education;
2370
+
2371
+ /// Creative persona category
2372
+ ///
2373
+ /// In en, this message translates to:
2374
+ /// **'Creative'**
2375
+ String get persona_category_creative;
2376
+
2377
+ /// Built-in personas section label
2378
+ ///
2379
+ /// In en, this message translates to:
2380
+ /// **'BUILT-IN'**
2381
+ String get persona_builtin_section;
2382
+
2383
+ /// My personas section label
2384
+ ///
2385
+ /// In en, this message translates to:
2386
+ /// **'MY PERSONAS'**
2387
+ String get persona_my_section;
2388
+
2389
+ /// Clone and edit persona action
2390
+ ///
2391
+ /// In en, this message translates to:
2392
+ /// **'Clone & Edit'**
2393
+ String get clone_edit;
2394
+
2395
+ /// Built-in persona badge
2396
+ ///
2397
+ /// In en, this message translates to:
2398
+ /// **'Built-in'**
2399
+ String get builtin_badge;
2400
+
2401
+ /// Empty persona list title
2402
+ ///
2403
+ /// In en, this message translates to:
2404
+ /// **'No personas found'**
2405
+ String get no_personas_found;
2406
+
2407
+ /// Empty persona list subtitle
2408
+ ///
2409
+ /// In en, this message translates to:
2410
+ /// **'Create your first persona to customize AI behavior.'**
2411
+ String get no_personas_desc;
2412
+
2413
+ /// Edit persona screen title
2414
+ ///
2415
+ /// In en, this message translates to:
2416
+ /// **'Edit Persona'**
2417
+ String get edit_persona;
2418
+
2419
+ /// Create persona screen title
2420
+ ///
2421
+ /// In en, this message translates to:
2422
+ /// **'Create Persona'**
2423
+ String get create_persona;
2424
+
2425
+ /// Create persona action button
2426
+ ///
2427
+ /// In en, this message translates to:
2428
+ /// **'Create'**
2429
+ String get create_persona_button;
2430
+
2431
+ /// Emoji selection label
2432
+ ///
2433
+ /// In en, this message translates to:
2434
+ /// **'Emoji'**
2435
+ String get emoji_label;
2436
+
2437
+ /// Name field label
2438
+ ///
2439
+ /// In en, this message translates to:
2440
+ /// **'Name'**
2441
+ String get name_label;
2442
+
2443
+ /// Persona name hint
2444
+ ///
2445
+ /// In en, this message translates to:
2446
+ /// **'My Persona'**
2447
+ String get my_persona_hint;
2448
+
2449
+ /// Category field label
2450
+ ///
2451
+ /// In en, this message translates to:
2452
+ /// **'Category'**
2453
+ String get category_label;
2454
+
2455
+ /// Optional description label
2456
+ ///
2457
+ /// In en, this message translates to:
2458
+ /// **'Description (optional)'**
2459
+ String get description_optional;
2460
+
2461
+ /// Description field hint
2462
+ ///
2463
+ /// In en, this message translates to:
2464
+ /// **'What this persona does...'**
2465
+ String get description_hint;
2466
+
2467
+ /// System prompt section label
2468
+ ///
2469
+ /// In en, this message translates to:
2470
+ /// **'System Prompt'**
2471
+ String get system_prompt;
2472
+
2473
+ /// Character counter for system prompt
2474
+ ///
2475
+ /// In en, this message translates to:
2476
+ /// **'{currentLen}/4000'**
2477
+ String character_count_max(int currentLen);
2478
+
2479
+ /// Preview placeholder when no prompt
2480
+ ///
2481
+ /// In en, this message translates to:
2482
+ /// **'No prompt yet...'**
2483
+ String get no_prompt_placeholder;
2484
+
2485
+ /// System prompt text field hint
2486
+ ///
2487
+ /// In en, this message translates to:
2488
+ /// **'You are a helpful assistant...'**
2489
+ String get prompt_hint;
2490
+
2491
+ /// Validation: prompt required
2492
+ ///
2493
+ /// In en, this message translates to:
2494
+ /// **'System prompt is required'**
2495
+ String get prompt_required;
2496
+
2497
+ /// Validation: prompt max chars
2498
+ ///
2499
+ /// In en, this message translates to:
2500
+ /// **'Max 4000 characters'**
2501
+ String get prompt_max_chars;
2502
+
2503
+ /// Expandable advanced settings section
2504
+ ///
2505
+ /// In en, this message translates to:
2506
+ /// **'Advanced Settings'**
2507
+ String get advanced_settings;
2508
+
2509
+ /// Temperature field label in personas
2510
+ ///
2511
+ /// In en, this message translates to:
2512
+ /// **'Temperature (0.0-2.0)'**
2513
+ String get temperature_label;
2514
+
2515
+ /// Top P field label in personas
2516
+ ///
2517
+ /// In en, this message translates to:
2518
+ /// **'Top P (0.0-1.0)'**
2519
+ String get top_p_label;
2520
+
2521
+ /// Temperature field hint
2522
+ ///
2523
+ /// In en, this message translates to:
2524
+ /// **'0.7'**
2525
+ String get temp_hint;
2526
+
2527
+ /// Top P field hint
2528
+ ///
2529
+ /// In en, this message translates to:
2530
+ /// **'0.9'**
2531
+ String get top_p_hint;
2532
+
2533
+ /// Range validation error (temperature)
2534
+ ///
2535
+ /// In en, this message translates to:
2536
+ /// **'0.0-2.0'**
2537
+ String get range_0_2;
2538
+
2539
+ /// Range validation error (top P)
2540
+ ///
2541
+ /// In en, this message translates to:
2542
+ /// **'0.0-1.0'**
2543
+ String get range_0_1;
2544
+
2545
+ /// Persona update snackbar
2546
+ ///
2547
+ /// In en, this message translates to:
2548
+ /// **'Persona updated'**
2549
+ String get persona_updated;
2550
+
2551
+ /// Persona creation snackbar
2552
+ ///
2553
+ /// In en, this message translates to:
2554
+ /// **'Persona created'**
2555
+ String get persona_created;
2556
+
2557
+ /// TTS model manager screen title
2558
+ ///
2559
+ /// In en, this message translates to:
2560
+ /// **'Text To Speech Models'**
2561
+ String get tts_models_title;
2562
+
2563
+ /// Always available status
2564
+ ///
2565
+ /// In en, this message translates to:
2566
+ /// **'Always available'**
2567
+ String get always_available;
2568
+
2569
+ /// System TTS engine description
2570
+ ///
2571
+ /// In en, this message translates to:
2572
+ /// **'Uses your device\'s built-in text-to-speech engine.\nNo downloads required. Voice selection uses your device\'s system settings.'**
2573
+ String get tts_system_desc;
2574
+
2575
+ /// Downloading status text
2576
+ ///
2577
+ /// In en, this message translates to:
2578
+ /// **'Downloading...'**
2579
+ String get downloading_status;
2580
+
2581
+ /// Kitten TTS engine description
2582
+ ///
2583
+ /// In en, this message translates to:
2584
+ /// **'Lightning-fast neural TTS with 8 expressive voices.\nRequires {size} download.'**
2585
+ String tts_kitten_desc(String size);
2586
+
2587
+ /// Piper TTS engine description
2588
+ ///
2589
+ /// In en, this message translates to:
2590
+ /// **'Fast offline Piper voices with 2 expressive voices.\nRequires {size} download per voice.'**
2591
+ String tts_piper_desc(String size);
2592
+
2593
+ /// Engine specification display
2594
+ ///
2595
+ /// In en, this message translates to:
2596
+ /// **'{sizeMb} MB · {ramMb} MB RAM · {voiceCount} voices'**
2597
+ String engine_spec(String sizeMb, String ramMb, int voiceCount);
2598
+
2599
+ /// On-device model manager screen title
2600
+ ///
2601
+ /// In en, this message translates to:
2602
+ /// **'On-Device Models'**
2603
+ String get on_device_models_title;
2604
+
2605
+ /// Available models section title
2606
+ ///
2607
+ /// In en, this message translates to:
2608
+ /// **'Available Models'**
2609
+ String get available_models;
2610
+
2611
+ /// Device memory card title
2612
+ ///
2613
+ /// In en, this message translates to:
2614
+ /// **'Device Memory'**
2615
+ String get device_memory;
2616
+
2617
+ /// RAM usage label
2618
+ ///
2619
+ /// In en, this message translates to:
2620
+ /// **'RAM Usage'**
2621
+ String get ram_usage;
2622
+
2623
+ /// Memory status healthy
2624
+ ///
2625
+ /// In en, this message translates to:
2626
+ /// **'Healthy'**
2627
+ String get memory_healthy;
2628
+
2629
+ /// Memory status critical
2630
+ ///
2631
+ /// In en, this message translates to:
2632
+ /// **'Critical'**
2633
+ String get memory_critical;
2634
+
2635
+ /// Memory status low
2636
+ ///
2637
+ /// In en, this message translates to:
2638
+ /// **'Low'**
2639
+ String get memory_low;
2640
+
2641
+ /// RAM usage percentage
2642
+ ///
2643
+ /// In en, this message translates to:
2644
+ /// **'{percent}% used'**
2645
+ String ram_used(String percent);
2646
+
2647
+ /// Available RAM stat label
2648
+ ///
2649
+ /// In en, this message translates to:
2650
+ /// **'Available RAM'**
2651
+ String get available_ram;
2652
+
2653
+ /// Total capacity stat label
2654
+ ///
2655
+ /// In en, this message translates to:
2656
+ /// **'Total Capacity'**
2657
+ String get total_capacity;
2658
+
2659
+ /// Model loaded status
2660
+ ///
2661
+ /// In en, this message translates to:
2662
+ /// **'Loaded'**
2663
+ String get loaded_status;
2664
+
2665
+ /// Backend selector section title
2666
+ ///
2667
+ /// In en, this message translates to:
2668
+ /// **'Inference Backend'**
2669
+ String get inference_backend;
2670
+
2671
+ /// iOS backend limitation notice
2672
+ ///
2673
+ /// In en, this message translates to:
2674
+ /// **'Only CPU backend is available on iOS.'**
2675
+ String get backend_ios_notice;
2676
+
2677
+ /// CPU backend description
2678
+ ///
2679
+ /// In en, this message translates to:
2680
+ /// **'Works on all devices. Most compatible.'**
2681
+ String get backend_cpu_desc;
2682
+
2683
+ /// GPU backend description
2684
+ ///
2685
+ /// In en, this message translates to:
2686
+ /// **'OpenCL acceleration. Faster on supported devices.'**
2687
+ String get backend_gpu_desc;
2688
+
2689
+ /// NPU backend description
2690
+ ///
2691
+ /// In en, this message translates to:
2692
+ /// **'Vendor NPU (Qualcomm/MediaTek). Fastest inference.'**
2693
+ String get backend_npu_desc;
2694
+
2695
+ /// Model picker sheet title
2696
+ ///
2697
+ /// In en, this message translates to:
2698
+ /// **'Select Model'**
2699
+ String get select_model_title;
2700
+
2701
+ /// Refresh models tooltip
2702
+ ///
2703
+ /// In en, this message translates to:
2704
+ /// **'Refresh models'**
2705
+ String get refresh_models;
2706
+
2707
+ /// Model search field hint
2708
+ ///
2709
+ /// In en, this message translates to:
2710
+ /// **'Search models...'**
2711
+ String get search_models_hint;
2712
+
2713
+ /// Model picker empty state title
2714
+ ///
2715
+ /// In en, this message translates to:
2716
+ /// **'No server connected'**
2717
+ String get no_server_connected;
2718
+
2719
+ /// Model picker empty state subtitle
2720
+ ///
2721
+ /// In en, this message translates to:
2722
+ /// **'Add a server first to see available models.'**
2723
+ String get add_server_first;
2724
+
2725
+ /// Model load error title
2726
+ ///
2727
+ /// In en, this message translates to:
2728
+ /// **'Failed to load models'**
2729
+ String get failed_load_models;
2730
+
2731
+ /// Empty models state
2732
+ ///
2733
+ /// In en, this message translates to:
2734
+ /// **'No models available'**
2735
+ String get no_models_available;
2736
+
2737
+ /// No search results for models
2738
+ ///
2739
+ /// In en, this message translates to:
2740
+ /// **'No models match \"{searchQuery}\"'**
2741
+ String no_models_match(String searchQuery);
2742
+
2743
+ /// Model load failure snackbar
2744
+ ///
2745
+ /// In en, this message translates to:
2746
+ /// **'Failed to load model: {error}'**
2747
+ String model_load_failed(String error);
2748
+
2749
+ /// Ollama model unloaded status
2750
+ ///
2751
+ /// In en, this message translates to:
2752
+ /// **'{name} will be unloaded once the keep-alive time passes'**
2753
+ String model_unloaded_ollama(String name);
2754
+
2755
+ /// Model unloaded success snackbar
2756
+ ///
2757
+ /// In en, this message translates to:
2758
+ /// **'{name} unloaded successfully'**
2759
+ String model_unloaded_success(String name);
2760
+
2761
+ /// Model unload failure snackbar
2762
+ ///
2763
+ /// In en, this message translates to:
2764
+ /// **'Failed to unload model: {error}'**
2765
+ String model_unload_failed(String error);
2766
+
2767
+ /// Unload model tooltip
2768
+ ///
2769
+ /// In en, this message translates to:
2770
+ /// **'Unload from server'**
2771
+ String get unload_from_server;
2772
+
2773
+ /// Context length chip
2774
+ ///
2775
+ /// In en, this message translates to:
2776
+ /// **'{ctx} ctx'**
2777
+ String context_chip(String ctx);
2778
+
2779
+ /// Download notification title
2780
+ ///
2781
+ /// In en, this message translates to:
2782
+ /// **'Downloading {modelName}...'**
2783
+ String download_notification_title(String modelName);
2784
+
2785
+ /// Download complete notification title
2786
+ ///
2787
+ /// In en, this message translates to:
2788
+ /// **'Download complete!'**
2789
+ String get download_complete_notification;
2790
+
2791
+ /// Download complete notification body
2792
+ ///
2793
+ /// In en, this message translates to:
2794
+ /// **'{modelName} has been downloaded successfully.'**
2795
+ String download_complete_body(String modelName);
2796
+
2797
+ /// Download failed notification title
2798
+ ///
2799
+ /// In en, this message translates to:
2800
+ /// **'Download failed: {error}'**
2801
+ String download_failed_notification(String error);
2802
+
2803
+ /// Download failed notification body
2804
+ ///
2805
+ /// In en, this message translates to:
2806
+ /// **'Failed to download {modelName}.'**
2807
+ String download_failed_body(String modelName);
2808
+
2809
+ /// System TTS engine display name
2810
+ ///
2811
+ /// In en, this message translates to:
2812
+ /// **'System TTS'**
2813
+ String get engine_name_system;
2814
+
2815
+ /// System TTS engine tagline
2816
+ ///
2817
+ /// In en, this message translates to:
2818
+ /// **'Built-in device engine'**
2819
+ String get engine_tagline_system;
2820
+
2821
+ /// Kitten TTS engine display name
2822
+ ///
2823
+ /// In en, this message translates to:
2824
+ /// **'Kitten TTS'**
2825
+ String get engine_name_kitten;
2826
+
2827
+ /// Kitten TTS engine tagline
2828
+ ///
2829
+ /// In en, this message translates to:
2830
+ /// **'High-speed neural TTS'**
2831
+ String get engine_tagline_kitten;
2832
+
2833
+ /// Sherpa ONNX engine display name
2834
+ ///
2835
+ /// In en, this message translates to:
2836
+ /// **'Sherpa ONNX VITS'**
2837
+ String get engine_name_sherpa;
2838
+
2839
+ /// Sherpa ONNX engine tagline
2840
+ ///
2841
+ /// In en, this message translates to:
2842
+ /// **'Offline Piper voices'**
2843
+ String get engine_tagline_sherpa;
2844
+
2845
+ /// Kitten TTS voice name
2846
+ ///
2847
+ /// In en, this message translates to:
2848
+ /// **'Jasper'**
2849
+ String get voice_jasper;
2850
+
2851
+ /// Kitten TTS voice name
2852
+ ///
2853
+ /// In en, this message translates to:
2854
+ /// **'Bella'**
2855
+ String get voice_bella;
2856
+
2857
+ /// Kitten TTS voice name
2858
+ ///
2859
+ /// In en, this message translates to:
2860
+ /// **'Bruno'**
2861
+ String get voice_bruno;
2862
+
2863
+ /// Kitten TTS voice name
2864
+ ///
2865
+ /// In en, this message translates to:
2866
+ /// **'Luna'**
2867
+ String get voice_luna;
2868
+
2869
+ /// Kitten TTS voice name
2870
+ ///
2871
+ /// In en, this message translates to:
2872
+ /// **'Hugo'**
2873
+ String get voice_hugo;
2874
+
2875
+ /// Kitten TTS voice name
2876
+ ///
2877
+ /// In en, this message translates to:
2878
+ /// **'Rosie'**
2879
+ String get voice_rosie;
2880
+
2881
+ /// Kitten TTS voice name
2882
+ ///
2883
+ /// In en, this message translates to:
2884
+ /// **'Leo'**
2885
+ String get voice_leo;
2886
+
2887
+ /// Kitten TTS voice name
2888
+ ///
2889
+ /// In en, this message translates to:
2890
+ /// **'Kiki'**
2891
+ String get voice_kiki;
2892
+
2893
+ /// Piper voice name
2894
+ ///
2895
+ /// In en, this message translates to:
2896
+ /// **'Lessac (US)'**
2897
+ String get voice_lessac;
2898
+
2899
+ /// Piper voice name
2900
+ ///
2901
+ /// In en, this message translates to:
2902
+ /// **'Ryan (US)'**
2903
+ String get voice_ryan;
2904
+
2905
+ /// On-device model name
2906
+ ///
2907
+ /// In en, this message translates to:
2908
+ /// **'Qwen 3 0.6B'**
2909
+ String get model_qwen_3;
2910
+
2911
+ /// Qwen 3 model description
2912
+ ///
2913
+ /// In en, this message translates to:
2914
+ /// **'Smallest general-purpose chat model. Fast responses, low memory usage.'**
2915
+ String get model_qwen_3_desc;
2916
+
2917
+ /// Apache license label
2918
+ ///
2919
+ /// In en, this message translates to:
2920
+ /// **'Apache-2.0'**
2921
+ String get model_license_apache;
2922
+
2923
+ /// On-device model name
2924
+ ///
2925
+ /// In en, this message translates to:
2926
+ /// **'Qwen 2.5 1.5B Instruct'**
2927
+ String get model_qwen_25;
2928
+
2929
+ /// Qwen 2.5 model description
2930
+ ///
2931
+ /// In en, this message translates to:
2932
+ /// **'Balanced quality and size. Good for general conversation.'**
2933
+ String get model_qwen_25_desc;
2934
+
2935
+ /// On-device model name
2936
+ ///
2937
+ /// In en, this message translates to:
2938
+ /// **'DeepSeek R1 Distill Qwen 1.5B'**
2939
+ String get model_deepseek;
2940
+
2941
+ /// DeepSeek model description
2942
+ ///
2943
+ /// In en, this message translates to:
2944
+ /// **'Reasoning and chain-of-thought model. Best for logical tasks.'**
2945
+ String get model_deepseek_desc;
2946
+
2947
+ /// MIT license label
2948
+ ///
2949
+ /// In en, this message translates to:
2950
+ /// **'MIT'**
2951
+ String get model_license_mit;
2952
+
2953
+ /// On-device model name
2954
+ ///
2955
+ /// In en, this message translates to:
2956
+ /// **'Gemma 4 E2B Instruct'**
2957
+ String get model_gemma;
2958
+
2959
+ /// Gemma model description
2960
+ ///
2961
+ /// In en, this message translates to:
2962
+ /// **'Google flagship model. Highest quality, requires more RAM.'**
2963
+ String get model_gemma_desc;
2964
+
2965
+ /// Markdown export header
2966
+ ///
2967
+ /// In en, this message translates to:
2968
+ /// **'*Exported from LocalMind — {date}*'**
2969
+ String export_header(String date);
2970
+
2971
+ /// Markdown export user role label
2972
+ ///
2973
+ /// In en, this message translates to:
2974
+ /// **'## 👤 User'**
2975
+ String get export_role_user;
2976
+
2977
+ /// Markdown export assistant role label
2978
+ ///
2979
+ /// In en, this message translates to:
2980
+ /// **'## 🤖 Assistant'**
2981
+ String get export_role_assistant;
2982
+
2983
+ /// Markdown export system role label
2984
+ ///
2985
+ /// In en, this message translates to:
2986
+ /// **'## ⚙️ System'**
2987
+ String get export_role_system;
2988
+
2989
+ /// Markdown export tool role label
2990
+ ///
2991
+ /// In en, this message translates to:
2992
+ /// **'## 🔧 Tool'**
2993
+ String get export_role_tool;
2994
+
2995
+ /// Text export user role prefix
2996
+ ///
2997
+ /// In en, this message translates to:
2998
+ /// **'[USER]'**
2999
+ String get export_text_user;
3000
+
3001
+ /// Text export assistant role prefix
3002
+ ///
3003
+ /// In en, this message translates to:
3004
+ /// **'[ASSISTANT]'**
3005
+ String get export_text_assistant;
3006
+
3007
+ /// Text export system role prefix
3008
+ ///
3009
+ /// In en, this message translates to:
3010
+ /// **'[SYSTEM]'**
3011
+ String get export_text_system;
3012
+
3013
+ /// Text export tool role prefix
3014
+ ///
3015
+ /// In en, this message translates to:
3016
+ /// **'[TOOL]'**
3017
+ String get export_text_tool;
3018
+
3019
+ /// Export user role label
3020
+ ///
3021
+ /// In en, this message translates to:
3022
+ /// **'USER'**
3023
+ String get export_label_user;
3024
+
3025
+ /// Export assistant role label
3026
+ ///
3027
+ /// In en, this message translates to:
3028
+ /// **'ASSISTANT'**
3029
+ String get export_label_assistant;
3030
+
3031
+ /// Export system role label
3032
+ ///
3033
+ /// In en, this message translates to:
3034
+ /// **'SYSTEM'**
3035
+ String get export_label_system;
3036
+
3037
+ /// Export tool role label
3038
+ ///
3039
+ /// In en, this message translates to:
3040
+ /// **'TOOL'**
3041
+ String get export_label_tool;
3042
+
3043
+ /// Fallback text when no model is selected
3044
+ ///
3045
+ /// In en, this message translates to:
3046
+ /// **'Select a model to start chatting'**
3047
+ String get select_model_hint;
3048
+
3049
+ /// Test notification title
3050
+ ///
3051
+ /// In en, this message translates to:
3052
+ /// **'Test notification'**
3053
+ String get test_notification_title;
3054
+
3055
+ /// Test notification body
3056
+ ///
3057
+ /// In en, this message translates to:
3058
+ /// **'This is a test notification for model download progress.'**
3059
+ String get test_notification_body;
3060
+}
3061
+
3062
+class _AppLocalizationsDelegate
3063
+ extends LocalizationsDelegate<AppLocalizations> {
3064
+ const _AppLocalizationsDelegate();
3065
+
3066
+ @override
3067
+ Future<AppLocalizations> load(Locale locale) {
3068
+ return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
3069
+ }
3070
+
3071
+ @override
3072
+ bool isSupported(Locale locale) => <String>[
3073
+ 'ar',
3074
+ 'bn',
3075
+ 'en',
3076
+ 'es',
3077
+ 'hi',
3078
+ 'zh',
3079
+ ].contains(locale.languageCode);
3080
+
3081
+ @override
3082
+ bool shouldReload(_AppLocalizationsDelegate old) => false;
3083
+}
3084
+
3085
+AppLocalizations lookupAppLocalizations(Locale locale) {
3086
+ // Lookup logic when only language code is specified.
3087
+ switch (locale.languageCode) {
3088
+ case 'ar':
3089
+ return AppLocalizationsAr();
3090
+ case 'bn':
3091
+ return AppLocalizationsBn();
3092
+ case 'en':
3093
+ return AppLocalizationsEn();
3094
+ case 'es':
3095
+ return AppLocalizationsEs();
3096
+ case 'hi':
3097
+ return AppLocalizationsHi();
3098
+ case 'zh':
3099
+ return AppLocalizationsZh();
3100
+ }
3101
+
3102
+ throw FlutterError(
3103
+ 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
3104
+ 'an issue with the localizations generation tool. Please file an issue '
3105
+ 'on GitHub with a reproducible sample app and the gen-l10n configuration '
3106
+ 'that was used.',
3107
+ );
3108
+}
lib/l10n/app_localizations_ar.dart
new
+1593
@@ -0,0 +1,1593 @@
1
+// ignore: unused_import
2
+import 'package:intl/intl.dart' as intl;
3
+import 'app_localizations.dart';
4
+
5
+// ignore_for_file: type=lint
6
+
7
+/// The translations for Arabic (`ar`).
8
+class AppLocalizationsAr extends AppLocalizations {
9
+ AppLocalizationsAr([String locale = 'ar']) : super(locale);
10
+
11
+ @override
12
+ String get app_name => 'LocalMind';
13
+
14
+ @override
15
+ String get app_tagline => 'ذكاؤك. جهازك. قواعدك.';
16
+
17
+ @override
18
+ String get app_version => '1.0.0';
19
+
20
+ @override
21
+ String get cancel => 'إلغاء';
22
+
23
+ @override
24
+ String get confirm => 'تأكيد';
25
+
26
+ @override
27
+ String get delete => 'حذف';
28
+
29
+ @override
30
+ String get save => 'حفظ';
31
+
32
+ @override
33
+ String get retry => 'إعادة المحاولة';
34
+
35
+ @override
36
+ String get close => 'إغلاق';
37
+
38
+ @override
39
+ String get done => 'تم';
40
+
41
+ @override
42
+ String get continue_action => 'متابعة';
43
+
44
+ @override
45
+ String get skip => 'تخطي';
46
+
47
+ @override
48
+ String get install => 'تثبيت';
49
+
50
+ @override
51
+ String get download => 'تنزيل';
52
+
53
+ @override
54
+ String get resume => 'استئناف';
55
+
56
+ @override
57
+ String get pause => 'إيقاف مؤقت';
58
+
59
+ @override
60
+ String get stop => 'إيقاف';
61
+
62
+ @override
63
+ String get edit => 'تعديل';
64
+
65
+ @override
66
+ String get preview => 'معاينة';
67
+
68
+ @override
69
+ String get unload => 'تفريغ';
70
+
71
+ @override
72
+ String get load => 'تحميل';
73
+
74
+ @override
75
+ String get rename => 'إعادة تسمية';
76
+
77
+ @override
78
+ String get pin => 'تثبيت';
79
+
80
+ @override
81
+ String get unpin => 'إلغاء التثبيت';
82
+
83
+ @override
84
+ String get share => 'مشاركة';
85
+
86
+ @override
87
+ String get copy => 'نسخ';
88
+
89
+ @override
90
+ String get copied => 'تم النسخ!';
91
+
92
+ @override
93
+ String get copied_to_clipboard => 'تم النسخ إلى الحافظة';
94
+
95
+ @override
96
+ String get select => 'اختيار';
97
+
98
+ @override
99
+ String get active => 'نشط';
100
+
101
+ @override
102
+ String get all => 'الكل';
103
+
104
+ @override
105
+ String get none => 'لا شيء';
106
+
107
+ @override
108
+ String get none_selected => 'لم يتم اختيار شيء';
109
+
110
+ @override
111
+ String get online => 'متصل';
112
+
113
+ @override
114
+ String get offline => 'غير متصل';
115
+
116
+ @override
117
+ String get error => 'خطأ';
118
+
119
+ @override
120
+ String get unknown_error => 'خطأ غير معروف';
121
+
122
+ @override
123
+ String get not_now => 'ليس الآن';
124
+
125
+ @override
126
+ String get enable => 'تفعيل';
127
+
128
+ @override
129
+ String get proceed_anyway => 'متابعة على أي حال';
130
+
131
+ @override
132
+ String get test_connection => 'اختبار الاتصال';
133
+
134
+ @override
135
+ String get testing => 'جارٍ الاختبار...';
136
+
137
+ @override
138
+ String get connection_successful => 'تم الاتصال بنجاح!';
139
+
140
+ @override
141
+ String get connection_failed => 'فشل الاتصال. تحقق من إعداداتك.';
142
+
143
+ @override
144
+ String get save_continue => 'حفظ ومتابعة';
145
+
146
+ @override
147
+ String get save_changes => 'حفظ التغييرات';
148
+
149
+ @override
150
+ String get finish_setup => 'إنهاء الإعداد';
151
+
152
+ @override
153
+ String get start_new_chat => 'بدء محادثة جديدة';
154
+
155
+ @override
156
+ String get cannot_undo => 'لا يمكن التراجع عن هذا الإجراء.';
157
+
158
+ @override
159
+ String get ram_warning => 'تحذير الذاكرة';
160
+
161
+ @override
162
+ String get recommended => 'موصى به';
163
+
164
+ @override
165
+ String get may_be_large => 'قد يكون كبيراً جداً لهذا الجهاز';
166
+
167
+ @override
168
+ String get calculating => 'جارٍ الحساب...';
169
+
170
+ @override
171
+ String get download_failed => 'فشل التنزيل';
172
+
173
+ @override
174
+ String get downloaded => 'تم التنزيل';
175
+
176
+ @override
177
+ String get not_downloaded => 'لم يتم التنزيل';
178
+
179
+ @override
180
+ String get installed => 'مثبت';
181
+
182
+ @override
183
+ String get not_installed => 'غير مثبت';
184
+
185
+ @override
186
+ String get loading => 'جارٍ التحميل...';
187
+
188
+ @override
189
+ String get thinking => 'تفكير';
190
+
191
+ @override
192
+ String get processing => 'جارٍ المعالجة...';
193
+
194
+ @override
195
+ String get initializing => 'جارٍ التهيئة...';
196
+
197
+ @override
198
+ String get ready => 'جاهز';
199
+
200
+ @override
201
+ String get preparing_app => 'جارٍ تجهيز التطبيق...';
202
+
203
+ @override
204
+ String get initializing_services => 'جارٍ تهيئة الخدمات...';
205
+
206
+ @override
207
+ String get configuring_server => 'جارٍ إعداد الخادم...';
208
+
209
+ @override
210
+ String get startup_failed => 'فشل بدء التشغيل';
211
+
212
+ @override
213
+ String get something_went_wrong => 'حدث خطأ ما';
214
+
215
+ @override
216
+ String get delete_model_title => 'حذف النموذج';
217
+
218
+ @override
219
+ String delete_model_body(String name) {
220
+ return 'هل أنت متأكد من حذف $name؟';
221
+ }
222
+
223
+ @override
224
+ String delete_model_body_with_size(String name, String size) {
225
+ return 'هل أنت متأكد من حذف $name؟ سيؤدي ذلك إلى تحرير مساحة تقارب $size.\n\nيمكنك تنزيل هذا النموذج مرة أخرى لاحقاً إذا احتجت إليه.';
226
+ }
227
+
228
+ @override
229
+ String get delete_voice_title => 'حذف الصوت';
230
+
231
+ @override
232
+ String delete_voice_body(String name, String size) {
233
+ return 'هل أنت متأكد من حذف $name؟ سيؤدي ذلك إلى تحرير مساحة تقارب $size.\n\nيمكنك تنزيل هذا الصوت مرة أخرى لاحقاً إذا احتجت إليه.';
234
+ }
235
+
236
+ @override
237
+ String get delete_server_title => 'حذف الخادم';
238
+
239
+ @override
240
+ String delete_server_body(String name) {
241
+ return 'هل أنت متأكد من حذف \"$name\"؟ لا يمكن التراجع عن هذا.';
242
+ }
243
+
244
+ @override
245
+ String get delete_conversation_title => 'حذف المحادثة؟';
246
+
247
+ @override
248
+ String delete_conversation_body(String title) {
249
+ return 'هل أنت متأكد من حذف \"$title\"؟ لا يمكن التراجع عن هذا.';
250
+ }
251
+
252
+ @override
253
+ String get delete_message_title => 'حذف الرسالة؟';
254
+
255
+ @override
256
+ String delete_persona_title(String name) {
257
+ return 'حذف \"$name\"؟';
258
+ }
259
+
260
+ @override
261
+ String get delete_persona_body => 'لا يمكن التراجع عن هذا.';
262
+
263
+ @override
264
+ String get clear_conversation_title => 'مسح المحادثة؟';
265
+
266
+ @override
267
+ String get clear_conversation_body =>
268
+ 'سيؤدي هذا إلى حذف جميع الرسائل في هذه المحادثة.';
269
+
270
+ @override
271
+ String get clear => 'مسح';
272
+
273
+ @override
274
+ String label_completed(String label) {
275
+ return 'اكتمل $label';
276
+ }
277
+
278
+ @override
279
+ String error_with_message(String error) {
280
+ return 'خطأ: $error';
281
+ }
282
+
283
+ @override
284
+ String preview_failed(String error) {
285
+ return 'فشلت المعاينة: $error';
286
+ }
287
+
288
+ @override
289
+ String loading_model(String modelId) {
290
+ return 'جارٍ تحميل $modelId...';
291
+ }
292
+
293
+ @override
294
+ String model_loaded(String modelId, String backend) {
295
+ return 'تم تحميل النموذج: $modelId ($backend)';
296
+ }
297
+
298
+ @override
299
+ String get no_model_loaded =>
300
+ 'لم يتم تحميل أي نموذج. اضغط على \"إدارة النماذج المحلية\" لتنزيل وتحميل نموذج.';
301
+
302
+ @override
303
+ String loading_model_error(String error) {
304
+ return 'خطأ: $error';
305
+ }
306
+
307
+ @override
308
+ String get delete_conversation => 'حذف المحادثة؟';
309
+
310
+ @override
311
+ String get nav_history => 'السجل';
312
+
313
+ @override
314
+ String get nav_servers => 'الخوادم';
315
+
316
+ @override
317
+ String get nav_local_models => 'النماذج المحلية';
318
+
319
+ @override
320
+ String get nav_tts => 'تحويل النص إلى كلام';
321
+
322
+ @override
323
+ String get nav_personas => 'الشخصيات';
324
+
325
+ @override
326
+ String get nav_settings => 'الإعدادات';
327
+
328
+ @override
329
+ String get nav_new_chat => 'محادثة جديدة';
330
+
331
+ @override
332
+ String get search_hint => 'البحث في المحادثات...';
333
+
334
+ @override
335
+ String get no_server_selected => 'لم يتم تحديد خادم';
336
+
337
+ @override
338
+ String get switch_server => 'تغيير الخادم';
339
+
340
+ @override
341
+ String get switch_server_subtitle => 'اختر خادماً للاتصال به';
342
+
343
+ @override
344
+ String get manage_servers => 'إدارة الخوادم';
345
+
346
+ @override
347
+ String get open_source => 'مفتوح المصدر';
348
+
349
+ @override
350
+ String get open_source_desc =>
351
+ 'LocalMind هو تطبيق مفتوح المصدر. تابع تقدمنا أو ساهم على GitHub.';
352
+
353
+ @override
354
+ String get star_on_github => 'نجمة على GitHub';
355
+
356
+ @override
357
+ String get settings_title => 'الإعدادات';
358
+
359
+ @override
360
+ String get settings_appearance => 'المظهر';
361
+
362
+ @override
363
+ String get settings_language => 'اللغة';
364
+
365
+ @override
366
+ String get language_system_default => 'إعدادات النظام';
367
+
368
+ @override
369
+ String get settings_tts => 'تحويل النص إلى كلام';
370
+
371
+ @override
372
+ String get settings_behavior => 'السلوك';
373
+
374
+ @override
375
+ String get settings_on_device => 'الاستدلال المحلي';
376
+
377
+ @override
378
+ String get settings_default_server => 'الخادم الافتراضي';
379
+
380
+ @override
381
+ String get settings_default_persona => 'الشخصية الافتراضية';
382
+
383
+ @override
384
+ String get settings_privacy => 'الخصوصية';
385
+
386
+ @override
387
+ String get settings_data_management => 'إدارة البيانات';
388
+
389
+ @override
390
+ String get settings_about => 'حول';
391
+
392
+ @override
393
+ String get theme => 'السمة';
394
+
395
+ @override
396
+ String get theme_system => 'النظام';
397
+
398
+ @override
399
+ String get theme_light => 'فاتح';
400
+
401
+ @override
402
+ String get theme_dark => 'داكن';
403
+
404
+ @override
405
+ String get theme_claude => 'Claude';
406
+
407
+ @override
408
+ String get font_size => 'حجم الخط';
409
+
410
+ @override
411
+ String get font_size_desc => 'ضبط حجم النص في المحادثة.';
412
+
413
+ @override
414
+ String get font_preview => 'النص التجريبي لمعاينة حجم الخط الجديد.';
415
+
416
+ @override
417
+ String get code_theme_dark => 'سمة الكود (داكن)';
418
+
419
+ @override
420
+ String get code_theme_light => 'سمة الكود (فاتح)';
421
+
422
+ @override
423
+ String get code_theme_desc => 'اختر سمة تمييز الصياغة لكتل الأكواد.';
424
+
425
+ @override
426
+ String get tts_engine => 'محرك تحويل النص إلى كلام';
427
+
428
+ @override
429
+ String get tts_engine_system => 'محرك النظام';
430
+
431
+ @override
432
+ String get tts_engine_kitten => 'Kitten TTS';
433
+
434
+ @override
435
+ String get voice => 'الصوت';
436
+
437
+ @override
438
+ String get voice_female => 'أنثى';
439
+
440
+ @override
441
+ String get voice_male => 'ذكر';
442
+
443
+ @override
444
+ String get voice_other => 'آخر';
445
+
446
+ @override
447
+ String get tts_speed => 'سرعة القراءة';
448
+
449
+ @override
450
+ String get tts_speed_desc => 'ضبط معدل التشغيل.';
451
+
452
+ @override
453
+ String get manage_tts_models => 'إدارة نماذج TTS';
454
+
455
+ @override
456
+ String get manage_on_device_models => 'إدارة النماذج المحلية';
457
+
458
+ @override
459
+ String get streaming_responses => 'الردود المتدفقة';
460
+
461
+ @override
462
+ String get auto_generate_titles => 'إنشاء العناوين تلقائياً';
463
+
464
+ @override
465
+ String get send_on_enter => 'إرسال عند الضغط على Enter';
466
+
467
+ @override
468
+ String get show_system_messages => 'إظهار رسائل النظام';
469
+
470
+ @override
471
+ String get haptic_feedback => 'الاستجابة اللمسية';
472
+
473
+ @override
474
+ String get enable_mcp => 'تفعيل MCP';
475
+
476
+ @override
477
+ String get new_chat_mcp_default => 'تفعيل MCP افتراضياً في المحادثات الجديدة';
478
+
479
+ @override
480
+ String get show_data_indicator => 'إظهار مؤشر البيانات';
481
+
482
+ @override
483
+ String get privacy_info => '\"LocalMind لا يطلع على بياناتك أبداً\"';
484
+
485
+ @override
486
+ String get delete_all_conversations => 'حذف جميع المحادثات';
487
+
488
+ @override
489
+ String get reset_settings_defaults => 'إعادة الإعدادات إلى الوضع الافتراضي';
490
+
491
+ @override
492
+ String get chat_title => 'LocalMind';
493
+
494
+ @override
495
+ String get chat_parameters_tooltip => 'معلمات المحادثة';
496
+
497
+ @override
498
+ String get change_persona => 'تغيير الشخصية';
499
+
500
+ @override
501
+ String get set_persona => 'تعيين شخصية';
502
+
503
+ @override
504
+ String get remove_persona => 'إزالة الشخصية';
505
+
506
+ @override
507
+ String get clear_conversation => 'مسح المحادثة';
508
+
509
+ @override
510
+ String get connection_error => 'خطأ في الاتصال. تحقق من الخادم الخاص بك.';
511
+
512
+ @override
513
+ String get disconnected => 'تم قطع الاتصال بالخادم.';
514
+
515
+ @override
516
+ String get configure => 'إعداد';
517
+
518
+ @override
519
+ String get select_model => 'اختيار نموذج';
520
+
521
+ @override
522
+ String get select_persona => 'اختيار شخصية';
523
+
524
+ @override
525
+ String get start_conversation => 'ابدأ محادثة';
526
+
527
+ @override
528
+ String get recent_chats => 'المحادثات الأخيرة';
529
+
530
+ @override
531
+ String get see_all => 'عرض الكل';
532
+
533
+ @override
534
+ String get quick_write => 'ساعدني في كتابة دالة';
535
+
536
+ @override
537
+ String get quick_explain => 'اشرح هذا الكود';
538
+
539
+ @override
540
+ String get quick_debug => 'صحح لي هذا الخطأ';
541
+
542
+ @override
543
+ String get quick_async => 'كيف أستخدم async/await؟';
544
+
545
+ @override
546
+ String get history_missing_title => 'السجل مفقود';
547
+
548
+ @override
549
+ String get history_missing_desc =>
550
+ 'إما أن رسائل هذه المحادثة قد حُذفت أو أن سجل المحادثة تالف.';
551
+
552
+ @override
553
+ String get technical_details => 'التفاصيل التقنية';
554
+
555
+ @override
556
+ String get last_error => 'آخر خطأ:';
557
+
558
+ @override
559
+ String get copy_info => 'نسخ المعلومات';
560
+
561
+ @override
562
+ String get conversation_id => 'معرف المحادثة';
563
+
564
+ @override
565
+ String get created_at => 'تاريخ الإنشاء';
566
+
567
+ @override
568
+ String get expected_messages => 'الرسائل المتوقعة';
569
+
570
+ @override
571
+ String get debug_dialog_desc =>
572
+ 'معلومات تشخيصية للمساعدة في تحديد مشاكل المزامنة.';
573
+
574
+ @override
575
+ String get chat_input_hint => 'اسأل أي شيء';
576
+
577
+ @override
578
+ String get send_message_tooltip => 'إرسال الرسالة';
579
+
580
+ @override
581
+ String get stop_generation_tooltip => 'إيقاف التوليد';
582
+
583
+ @override
584
+ String get attach_images_tooltip => 'إرفاق صور';
585
+
586
+ @override
587
+ String tool_label(String toolCallId) {
588
+ return 'أداة: $toolCallId';
589
+ }
590
+
591
+ @override
592
+ String get tool_unknown => 'أداة: غير معروفة';
593
+
594
+ @override
595
+ String get message_options => 'خيارات الرسالة';
596
+
597
+ @override
598
+ String get copy_markdown => 'نسخ كـ Markdown';
599
+
600
+ @override
601
+ String get copied_markdown => 'تم النسخ كـ Markdown';
602
+
603
+ @override
604
+ String get read_aloud => 'القراءة بصوت عالٍ';
605
+
606
+ @override
607
+ String get stop_reading => 'إيقاف القراءة';
608
+
609
+ @override
610
+ String get more => 'المزيد';
611
+
612
+ @override
613
+ String character_count(int length) {
614
+ return '$length حرف';
615
+ }
616
+
617
+ @override
618
+ String get edit_message => 'تعديل الرسالة';
619
+
620
+ @override
621
+ String get edit_message_desc =>
622
+ 'سيؤدي الحفظ إلى إزالة رد المساعد أدناه وإعادة التوليد.';
623
+
624
+ @override
625
+ String get save_regenerate => 'حفظ وإعادة التوليد';
626
+
627
+ @override
628
+ String get chat_settings_title => 'إعدادات المحادثة';
629
+
630
+ @override
631
+ String get reset_defaults => 'إعادة الإعدادات الافتراضية';
632
+
633
+ @override
634
+ String get parameters_tab => 'المعلمات';
635
+
636
+ @override
637
+ String get mcp_tab => 'MCP';
638
+
639
+ @override
640
+ String get temperature => 'درجة الحرارة';
641
+
642
+ @override
643
+ String get temperature_desc =>
644
+ 'التحكم في العشوائية: أعلى = إبداعي، أقل = مركز';
645
+
646
+ @override
647
+ String get top_p => 'Top P';
648
+
649
+ @override
650
+ String get top_p_desc => 'حد أخذ العينات النووي';
651
+
652
+ @override
653
+ String get max_tokens => 'الحد الأقصى للرموز';
654
+
655
+ @override
656
+ String get max_tokens_desc => 'حد الاستجابة';
657
+
658
+ @override
659
+ String get context_length => 'طول السياق';
660
+
661
+ @override
662
+ String get context_length_desc => 'نافذة السجل';
663
+
664
+ @override
665
+ String get mcp_disabled_warning =>
666
+ 'MCP معطل عالمياً. فعّله في الإعدادات لاستخدام هذه الميزات.';
667
+
668
+ @override
669
+ String get mcp_enable_chat => 'تفعيل MCP لهذه المحادثة';
670
+
671
+ @override
672
+ String get auto_execute_tools => 'تشغيل الأدوات تلقائياً';
673
+
674
+ @override
675
+ String get add_ephemeral_mcp => 'إضافة خادم MCP مؤقت';
676
+
677
+ @override
678
+ String get mcp_label_placeholder => 'التسمية';
679
+
680
+ @override
681
+ String get mcp_url_placeholder => 'الرابط (https://...)';
682
+
683
+ @override
684
+ String get active_integrations => 'التكاملات النشطة';
685
+
686
+ @override
687
+ String get enable_notifications => 'تفعيل الإشعارات';
688
+
689
+ @override
690
+ String get enable_notifications_desc =>
691
+ 'احصل على إشعار عند اكتمال تنزيل النماذج.';
692
+
693
+ @override
694
+ String get chat_history_title => 'سجل المحادثات';
695
+
696
+ @override
697
+ String get conversation_just_now => 'الآن';
698
+
699
+ @override
700
+ String conversation_minutes_ago(int minutes) {
701
+ return 'منذ $minutes دقيقة';
702
+ }
703
+
704
+ @override
705
+ String conversation_hours_ago(int hours) {
706
+ return 'منذ $hours ساعة';
707
+ }
708
+
709
+ @override
710
+ String get conversation_yesterday => 'أمس';
711
+
712
+ @override
713
+ String conversation_days_ago(int days) {
714
+ return 'منذ $days يوم';
715
+ }
716
+
717
+ @override
718
+ String conversation_date(int month, int day, int year) {
719
+ return '$year/$month/$day';
720
+ }
721
+
722
+ @override
723
+ String get options_tooltip => 'خيارات';
724
+
725
+ @override
726
+ String get no_results_found => 'لا توجد نتائج';
727
+
728
+ @override
729
+ String get no_conversations_yet => 'لا توجد محادثات بعد';
730
+
731
+ @override
732
+ String get try_different_search => 'جرّب مصطلح بحث مختلف';
733
+
734
+ @override
735
+ String get start_new_conversation => 'ابدأ محادثة جديدة';
736
+
737
+ @override
738
+ String get rename_conversation => 'إعادة تسمية المحادثة';
739
+
740
+ @override
741
+ String get enter_new_title => 'أدخل عنواناً جديداً';
742
+
743
+ @override
744
+ String get pinned_section => 'مثبت';
745
+
746
+ @override
747
+ String get today_section => 'اليوم';
748
+
749
+ @override
750
+ String get yesterday_section => 'أمس';
751
+
752
+ @override
753
+ String get previous_7_days => 'آخر 7 أيام';
754
+
755
+ @override
756
+ String get previous_30_days => 'آخر 30 يوماً';
757
+
758
+ @override
759
+ String get older_section => 'الأقدم';
760
+
761
+ @override
762
+ String get onboarding_localmind => 'LOCALMIND';
763
+
764
+ @override
765
+ String get onboarding_connect_server => 'اتصل\nبخادمك';
766
+
767
+ @override
768
+ String get onboarding_connect_desc =>
769
+ 'اتصل بـ LM Studio أو Ollama أو\nOpenRouter لبدء تجربة الذكاء\nالاصطناعي الخاصة بك.';
770
+
771
+ @override
772
+ String get server_type_on_device => 'محلي';
773
+
774
+ @override
775
+ String get server_type_on_device_sub => 'لا يحتاج خادماً';
776
+
777
+ @override
778
+ String get server_type_lm_studio => 'LM Studio';
779
+
780
+ @override
781
+ String get server_type_lm_studio_sub => 'API محلي';
782
+
783
+ @override
784
+ String get server_type_ollama => 'Ollama';
785
+
786
+ @override
787
+ String get server_type_ollama_sub => 'محرك أوامر';
788
+
789
+ @override
790
+ String get server_type_openrouter => 'OpenRouter';
791
+
792
+ @override
793
+ String get server_type_openrouter_sub => 'سحابة موحدة';
794
+
795
+ @override
796
+ String get ready_continue => 'جاهز للمتابعة';
797
+
798
+ @override
799
+ String get waiting_selection => 'بانتظار الاختيار';
800
+
801
+ @override
802
+ String get setup_connection => 'إعداد الاتصال';
803
+
804
+ @override
805
+ String setup_connection_desc(String server) {
806
+ return 'قم بإعداد خادم $server لبدء المحادثة.';
807
+ }
808
+
809
+ @override
810
+ String get server_name => 'اسم الخادم';
811
+
812
+ @override
813
+ String get name_required => 'الاسم مطلوب';
814
+
815
+ @override
816
+ String get name_max_50 => 'الحد الأقصى 50 حرفاً';
817
+
818
+ @override
819
+ String get host_label => 'المضيف / عنوان IP';
820
+
821
+ @override
822
+ String get host_required => 'المضيف مطلوب';
823
+
824
+ @override
825
+ String get port_label => 'المنفذ';
826
+
827
+ @override
828
+ String get port_required => 'المنفذ مطلوب';
829
+
830
+ @override
831
+ String get port_invalid => 'يجب أن يكون رقماً';
832
+
833
+ @override
834
+ String get port_range => 'أدخل منفذاً صالحاً (1-65535)';
835
+
836
+ @override
837
+ String get api_key_required => 'مفتاح API *';
838
+
839
+ @override
840
+ String get api_key_optional => 'مفتاح API (اختياري)';
841
+
842
+ @override
843
+ String get api_key_required_openrouter => 'مفتاح API مطلوب لـ OpenRouter';
844
+
845
+ @override
846
+ String get api_key_format => 'مفاتيح OpenRouter API تبدأ بـ sk-';
847
+
848
+ @override
849
+ String get my_server_hint => 'خادمي';
850
+
851
+ @override
852
+ String get name_length_validation => 'يجب ألا يتجاوز الاسم 50 حرفاً';
853
+
854
+ @override
855
+ String get host_valid => 'أدخل اسماً مضيفاً أو عنوان IP صالحاً';
856
+
857
+ @override
858
+ String get api_key_hint_openrouter => 'sk-...';
859
+
860
+ @override
861
+ String get api_key_hint_generic => 'للخوادم الموثقة';
862
+
863
+ @override
864
+ String get update_server => 'تحديث الخادم';
865
+
866
+ @override
867
+ String get save_server => 'حفظ الخادم';
868
+
869
+ @override
870
+ String get server_updated => 'تم تحديث الخادم';
871
+
872
+ @override
873
+ String get server_added => 'تمت إضافة الخادم';
874
+
875
+ @override
876
+ String get download_model_title => 'تنزيل نموذج';
877
+
878
+ @override
879
+ String get download_model_desc =>
880
+ 'اختر نموذجاً للتنزيل.\nسيتم تشغيله محلياً على جهازك.';
881
+
882
+ @override
883
+ String get on_device_android_only =>
884
+ 'الاستدلال المحلي متاح حالياً على Android فقط.';
885
+
886
+ @override
887
+ String get total_ram => 'إجمالي الذاكرة';
888
+
889
+ @override
890
+ String get available => 'متاح';
891
+
892
+ @override
893
+ String ram_min_required(String fileSize) {
894
+ return '$fileSize جيجابايت ذاكرة كحد أدنى';
895
+ }
896
+
897
+ @override
898
+ String download_progress(String percent, String speed) {
899
+ return '$percent% • $speed';
900
+ }
901
+
902
+ @override
903
+ String eta_label(String eta) {
904
+ return 'الوقت المتبقي: $eta';
905
+ }
906
+
907
+ @override
908
+ String paused_progress(String percent) {
909
+ return 'متوقف مؤقتاً - $percent%';
910
+ }
911
+
912
+ @override
913
+ String ram_warning_body_download(String ram, String totalMemory) {
914
+ return 'هذا النموذج يتطلب $ram جيجابايت ذاكرة على الأقل، ولكن جهازك يحتوي على $totalMemory. قد لا يعمل بشكل صحيح أو قد يتسبب في تعطل التطبيق.';
915
+ }
916
+
917
+ @override
918
+ String ram_warning_body_load(String availableRAM, String ram) {
919
+ return 'جهازك يحتوي على $availableRAM ذاكرة متاحة، لكن هذا النموذج يوصي بـ $ram جيجابايت على الأقل. قد يفشل تحميله أو يسبب عدم استقرار.';
920
+ }
921
+
922
+ @override
923
+ String get choose_theme => 'اختر السمة';
924
+
925
+ @override
926
+ String get choose_theme_desc =>
927
+ 'خصص مظهر التطبيق. يمكنك دائماً تغيير هذا لاحقاً في الإعدادات.';
928
+
929
+ @override
930
+ String get theme_card_system => 'النظام';
931
+
932
+ @override
933
+ String get theme_card_system_sub => 'يطابق إعدادات جهازك';
934
+
935
+ @override
936
+ String get theme_card_light => 'فاتح';
937
+
938
+ @override
939
+ String get theme_card_light_sub => 'نظيف ومشرق';
940
+
941
+ @override
942
+ String get theme_card_dark => 'داكن';
943
+
944
+ @override
945
+ String get theme_card_dark_sub => 'مريح للعينين';
946
+
947
+ @override
948
+ String get theme_card_claude => 'Claude';
949
+
950
+ @override
951
+ String get theme_card_claude_sub => 'سمة دافئة بلون الخوخ';
952
+
953
+ @override
954
+ String get stay_updated => 'ابق على اطلاع';
955
+
956
+ @override
957
+ String get stay_updated_desc =>
958
+ 'احصل على إشعارات عند اكتمال تنزيل نماذج الذكاء الاصطناعي أو عند انتهاء المهام الطويلة.';
959
+
960
+ @override
961
+ String get notification_benefit_downloads => 'تقدم تنزيل النماذج';
962
+
963
+ @override
964
+ String get notification_benefit_completions => 'اكتمال التوليد';
965
+
966
+ @override
967
+ String get notification_benefit_background => 'حالة المهام الخلفية';
968
+
969
+ @override
970
+ String get allow_notifications => 'السماح بالإشعارات';
971
+
972
+ @override
973
+ String get servers_title => 'الخوادم';
974
+
975
+ @override
976
+ String get no_servers_yet => 'لا توجد خوادم بعد';
977
+
978
+ @override
979
+ String get no_servers_desc =>
980
+ 'أضف خادمك الأول لبدء المحادثة مع نماذج الذكاء الاصطناعي.';
981
+
982
+ @override
983
+ String get add_server => 'إضافة خادم';
984
+
985
+ @override
986
+ String switched_to_server(String name) {
987
+ return 'تم التبديل إلى $name';
988
+ }
989
+
990
+ @override
991
+ String get edit_server => 'تعديل الخادم';
992
+
993
+ @override
994
+ String get add_server_title => 'إضافة خادم';
995
+
996
+ @override
997
+ String get server_type_label => 'نوع الخادم';
998
+
999
+ @override
1000
+ String get server_icon_label => 'أيقونة الخادم';
1001
+
1002
+ @override
1003
+ String get default_icon => 'الأيقونة الافتراضية';
1004
+
1005
+ @override
1006
+ String get server_type_lm_studio_display => 'LM Studio';
1007
+
1008
+ @override
1009
+ String get server_type_openai_display => 'متوافق مع OpenAI';
1010
+
1011
+ @override
1012
+ String get server_type_ollama_display => 'Ollama';
1013
+
1014
+ @override
1015
+ String get server_type_openrouter_display => 'OpenRouter';
1016
+
1017
+ @override
1018
+ String get server_type_on_device_display => 'محلي';
1019
+
1020
+ @override
1021
+ String get server_address_openrouter => 'openrouter.ai';
1022
+
1023
+ @override
1024
+ String get server_address_on_device => 'استدلال محلي';
1025
+
1026
+ @override
1027
+ String server_address_format(String host, String port) {
1028
+ return '$host:$port';
1029
+ }
1030
+
1031
+ @override
1032
+ String get default_badge => 'افتراضي';
1033
+
1034
+ @override
1035
+ String get set_as_default => 'تعيين كافتراضي';
1036
+
1037
+ @override
1038
+ String get select_icon => 'اختيار أيقونة';
1039
+
1040
+ @override
1041
+ String get select_icon_desc => 'اختر أيقونة لخادمك';
1042
+
1043
+ @override
1044
+ String get search_icons_hint => 'البحث في الأيقونات...';
1045
+
1046
+ @override
1047
+ String get server_icon_stack => 'Server Stack';
1048
+
1049
+ @override
1050
+ String get server_icon_stack2 => 'Server Stack 02';
1051
+
1052
+ @override
1053
+ String get server_icon_stack3 => 'Server Stack 03';
1054
+
1055
+ @override
1056
+ String get server_icon_cloud => 'Cloud';
1057
+
1058
+ @override
1059
+ String get server_icon_cloud_server => 'Cloud Server';
1060
+
1061
+ @override
1062
+ String get server_icon_mcp => 'MCP Server';
1063
+
1064
+ @override
1065
+ String get server_icon_database => 'Database';
1066
+
1067
+ @override
1068
+ String get server_icon_database1 => 'Database 01';
1069
+
1070
+ @override
1071
+ String get server_icon_database2 => 'Database 02';
1072
+
1073
+ @override
1074
+ String get server_icon_cpu => 'CPU';
1075
+
1076
+ @override
1077
+ String get server_icon_chip => 'Chip';
1078
+
1079
+ @override
1080
+ String get server_icon_chip2 => 'Chip 02';
1081
+
1082
+ @override
1083
+ String get server_icon_computer => 'Computer';
1084
+
1085
+ @override
1086
+ String get server_icon_laptop => 'Laptop';
1087
+
1088
+ @override
1089
+ String get server_icon_terminal => 'Computer Terminal';
1090
+
1091
+ @override
1092
+ String get server_icon_code => 'Code';
1093
+
1094
+ @override
1095
+ String get server_icon_ai_brain => 'AI Brain';
1096
+
1097
+ @override
1098
+ String get server_icon_ai_brain2 => 'AI Brain 02';
1099
+
1100
+ @override
1101
+ String get server_icon_ai_cloud => 'AI Cloud';
1102
+
1103
+ @override
1104
+ String get server_icon_ai_network => 'AI Network';
1105
+
1106
+ @override
1107
+ String get server_icon_ai_chat => 'AI Chat';
1108
+
1109
+ @override
1110
+ String get server_icon_cellular => 'Cellular Network';
1111
+
1112
+ @override
1113
+ String get server_icon_plug1 => 'Plug 01';
1114
+
1115
+ @override
1116
+ String get server_icon_plug2 => 'Plug 02';
1117
+
1118
+ @override
1119
+ String get server_icon_bot => 'Bot';
1120
+
1121
+ @override
1122
+ String get server_icon_bot2 => 'Bot 02';
1123
+
1124
+ @override
1125
+ String get server_icon_robotic => 'Robotic';
1126
+
1127
+ @override
1128
+ String get server_icon_rocket => 'Rocket';
1129
+
1130
+ @override
1131
+ String get server_icon_star => 'Star';
1132
+
1133
+ @override
1134
+ String get server_icon_settings1 => 'Settings 01';
1135
+
1136
+ @override
1137
+ String get server_icon_settings2 => 'Settings 02';
1138
+
1139
+ @override
1140
+ String get server_icon_home1 => 'Home 01';
1141
+
1142
+ @override
1143
+ String get server_icon_home2 => 'Home 02';
1144
+
1145
+ @override
1146
+ String get server_icon_folder1 => 'Folder 01';
1147
+
1148
+ @override
1149
+ String get server_icon_folder2 => 'Folder 02';
1150
+
1151
+ @override
1152
+ String get server_icon_file1 => 'File 01';
1153
+
1154
+ @override
1155
+ String get server_icon_lock => 'Lock';
1156
+
1157
+ @override
1158
+ String get server_icon_key => 'Key 01';
1159
+
1160
+ @override
1161
+ String get server_icon_link => 'Link 01';
1162
+
1163
+ @override
1164
+ String get server_icon_globe => 'Globe';
1165
+
1166
+ @override
1167
+ String get server_icon_api => 'API';
1168
+
1169
+ @override
1170
+ String get server_icon_arrow_right => 'Arrow Right 01';
1171
+
1172
+ @override
1173
+ String get server_icon_check => 'Check Circle';
1174
+
1175
+ @override
1176
+ String get server_icon_alert => 'Alert Circle';
1177
+
1178
+ @override
1179
+ String get server_icon_info => 'Info Circle';
1180
+
1181
+ @override
1182
+ String get server_icon_zap => 'Zap';
1183
+
1184
+ @override
1185
+ String get server_icon_cloud_upload => 'Cloud Upload';
1186
+
1187
+ @override
1188
+ String get server_icon_cloud_download => 'Cloud Download';
1189
+
1190
+ @override
1191
+ String get server_icon_refresh => 'Refresh';
1192
+
1193
+ @override
1194
+ String get server_icon_hard_drive => 'Hard Drive';
1195
+
1196
+ @override
1197
+ String get server_icon_drive => 'Drive';
1198
+
1199
+ @override
1200
+ String get personas_title => 'الشخصيات';
1201
+
1202
+ @override
1203
+ String get persona_category_general => 'عام';
1204
+
1205
+ @override
1206
+ String get persona_category_coding => 'برمجة';
1207
+
1208
+ @override
1209
+ String get persona_category_education => 'تعليم';
1210
+
1211
+ @override
1212
+ String get persona_category_creative => 'إبداعي';
1213
+
1214
+ @override
1215
+ String get persona_builtin_section => 'مضمنة';
1216
+
1217
+ @override
1218
+ String get persona_my_section => 'شخصياتي';
1219
+
1220
+ @override
1221
+ String get clone_edit => 'نسخ وتعديل';
1222
+
1223
+ @override
1224
+ String get builtin_badge => 'مضمنة';
1225
+
1226
+ @override
1227
+ String get no_personas_found => 'لم يتم العثور على شخصيات';
1228
+
1229
+ @override
1230
+ String get no_personas_desc =>
1231
+ 'أنشئ شخصيتك الأولى لتخصيص سلوك الذكاء الاصطناعي.';
1232
+
1233
+ @override
1234
+ String get edit_persona => 'تعديل الشخصية';
1235
+
1236
+ @override
1237
+ String get create_persona => 'إنشاء شخصية';
1238
+
1239
+ @override
1240
+ String get create_persona_button => 'إنشاء';
1241
+
1242
+ @override
1243
+ String get emoji_label => 'رمز تعبيري';
1244
+
1245
+ @override
1246
+ String get name_label => 'الاسم';
1247
+
1248
+ @override
1249
+ String get my_persona_hint => 'شخصيتي';
1250
+
1251
+ @override
1252
+ String get category_label => 'الفئة';
1253
+
1254
+ @override
1255
+ String get description_optional => 'الوصف (اختياري)';
1256
+
1257
+ @override
1258
+ String get description_hint => 'ماذا تفعل هذه الشخصية...';
1259
+
1260
+ @override
1261
+ String get system_prompt => 'موجه النظام';
1262
+
1263
+ @override
1264
+ String character_count_max(int currentLen) {
1265
+ return '$currentLen/4000';
1266
+ }
1267
+
1268
+ @override
1269
+ String get no_prompt_placeholder => 'لا يوجد موجه بعد...';
1270
+
1271
+ @override
1272
+ String get prompt_hint => 'أنت مساعد مفيد...';
1273
+
1274
+ @override
1275
+ String get prompt_required => 'موجه النظام مطلوب';
1276
+
1277
+ @override
1278
+ String get prompt_max_chars => 'الحد الأقصى 4000 حرف';
1279
+
1280
+ @override
1281
+ String get advanced_settings => 'الإعدادات المتقدمة';
1282
+
1283
+ @override
1284
+ String get temperature_label => 'درجة الحرارة (0.0-2.0)';
1285
+
1286
+ @override
1287
+ String get top_p_label => 'Top P (0.0-1.0)';
1288
+
1289
+ @override
1290
+ String get temp_hint => '0.7';
1291
+
1292
+ @override
1293
+ String get top_p_hint => '0.9';
1294
+
1295
+ @override
1296
+ String get range_0_2 => '0.0-2.0';
1297
+
1298
+ @override
1299
+ String get range_0_1 => '0.0-1.0';
1300
+
1301
+ @override
1302
+ String get persona_updated => 'تم تحديث الشخصية';
1303
+
1304
+ @override
1305
+ String get persona_created => 'تم إنشاء الشخصية';
1306
+
1307
+ @override
1308
+ String get tts_models_title => 'نماذج تحويل النص إلى كلام';
1309
+
1310
+ @override
1311
+ String get always_available => 'متاح دائماً';
1312
+
1313
+ @override
1314
+ String get tts_system_desc =>
1315
+ 'يستخدم محرك تحويل النص إلى كلام المدمج في جهازك.\nلا يتطلب أي تنزيلات. اختيار الصوت يستخدم إعدادات نظام جهازك.';
1316
+
1317
+ @override
1318
+ String get downloading_status => 'جارٍ التنزيل...';
1319
+
1320
+ @override
1321
+ String tts_kitten_desc(String size) {
1322
+ return 'TTS عصبي فائق السرعة مع 8 أصوات معبرة.\nيتطلب تنزيل $size.';
1323
+ }
1324
+
1325
+ @override
1326
+ String tts_piper_desc(String size) {
1327
+ return 'أصوات Piper سريعة دون اتصال مع صوتين معبرين.\nيتطلب تنزيل $size لكل صوت.';
1328
+ }
1329
+
1330
+ @override
1331
+ String engine_spec(String sizeMb, String ramMb, int voiceCount) {
1332
+ return '$sizeMb ميجابايت · $ramMb ميجابايت ذاكرة · $voiceCount أصوات';
1333
+ }
1334
+
1335
+ @override
1336
+ String get on_device_models_title => 'النماذج المحلية';
1337
+
1338
+ @override
1339
+ String get available_models => 'النماذج المتاحة';
1340
+
1341
+ @override
1342
+ String get device_memory => 'ذاكرة الجهاز';
1343
+
1344
+ @override
1345
+ String get ram_usage => 'استخدام الذاكرة';
1346
+
1347
+ @override
1348
+ String get memory_healthy => 'جيد';
1349
+
1350
+ @override
1351
+ String get memory_critical => 'حرج';
1352
+
1353
+ @override
1354
+ String get memory_low => 'منخفض';
1355
+
1356
+ @override
1357
+ String ram_used(String percent) {
1358
+ return '$percent% مستخدم';
1359
+ }
1360
+
1361
+ @override
1362
+ String get available_ram => 'الذاكرة المتاحة';
1363
+
1364
+ @override
1365
+ String get total_capacity => 'السعة الإجمالية';
1366
+
1367
+ @override
1368
+ String get loaded_status => 'تم التحميل';
1369
+
1370
+ @override
1371
+ String get inference_backend => 'محطة الاستدلال';
1372
+
1373
+ @override
1374
+ String get backend_ios_notice => 'محطة CPU فقط متاحة على iOS.';
1375
+
1376
+ @override
1377
+ String get backend_cpu_desc => 'يعمل على جميع الأجهزة. الأكثر توافقاً.';
1378
+
1379
+ @override
1380
+ String get backend_gpu_desc => 'تسريع OpenCL. أسرع على الأجهزة المدعومة.';
1381
+
1382
+ @override
1383
+ String get backend_npu_desc =>
1384
+ 'NPU من الشركة المصنعة (Qualcomm/MediaTek). أسرع استدلال.';
1385
+
1386
+ @override
1387
+ String get select_model_title => 'اختيار نموذج';
1388
+
1389
+ @override
1390
+ String get refresh_models => 'تحديث النماذج';
1391
+
1392
+ @override
1393
+ String get search_models_hint => 'البحث في النماذج...';
1394
+
1395
+ @override
1396
+ String get no_server_connected => 'لا يوجد خادم متصل';
1397
+
1398
+ @override
1399
+ String get add_server_first => 'أضف خادماً أولاً لرؤية النماذج المتاحة.';
1400
+
1401
+ @override
1402
+ String get failed_load_models => 'فشل تحميل النماذج';
1403
+
1404
+ @override
1405
+ String get no_models_available => 'لا توجد نماذج متاحة';
1406
+
1407
+ @override
1408
+ String no_models_match(String searchQuery) {
1409
+ return 'لا توجد نماذج تطابق \"$searchQuery\"';
1410
+ }
1411
+
1412
+ @override
1413
+ String model_load_failed(String error) {
1414
+ return 'فشل تحميل النموذج: $error';
1415
+ }
1416
+
1417
+ @override
1418
+ String model_unloaded_ollama(String name) {
1419
+ return 'سيتم تفريغ $name بعد انتهاء وقت الاحتفاظ';
1420
+ }
1421
+
1422
+ @override
1423
+ String model_unloaded_success(String name) {
1424
+ return 'تم تفريغ $name بنجاح';
1425
+ }
1426
+
1427
+ @override
1428
+ String model_unload_failed(String error) {
1429
+ return 'فشل تفريغ النموذج: $error';
1430
+ }
1431
+
1432
+ @override
1433
+ String get unload_from_server => 'تفريغ من الخادم';
1434
+
1435
+ @override
1436
+ String context_chip(String ctx) {
1437
+ return '$ctx سياق';
1438
+ }
1439
+
1440
+ @override
1441
+ String download_notification_title(String modelName) {
1442
+ return 'جارٍ تنزيل $modelName...';
1443
+ }
1444
+
1445
+ @override
1446
+ String get download_complete_notification => 'اكتمل التنزيل!';
1447
+
1448
+ @override
1449
+ String download_complete_body(String modelName) {
1450
+ return 'تم تنزيل $modelName بنجاح.';
1451
+ }
1452
+
1453
+ @override
1454
+ String download_failed_notification(String error) {
1455
+ return 'فشل التنزيل: $error';
1456
+ }
1457
+
1458
+ @override
1459
+ String download_failed_body(String modelName) {
1460
+ return 'فشل تنزيل $modelName.';
1461
+ }
1462
+
1463
+ @override
1464
+ String get engine_name_system => 'TTS النظام';
1465
+
1466
+ @override
1467
+ String get engine_tagline_system => 'محرك الجهاز المدمج';
1468
+
1469
+ @override
1470
+ String get engine_name_kitten => 'Kitten TTS';
1471
+
1472
+ @override
1473
+ String get engine_tagline_kitten => 'TTS عصبي عالي السرعة';
1474
+
1475
+ @override
1476
+ String get engine_name_sherpa => 'Sherpa ONNX VITS';
1477
+
1478
+ @override
1479
+ String get engine_tagline_sherpa => 'أصوات Piper دون اتصال';
1480
+
1481
+ @override
1482
+ String get voice_jasper => 'Jasper';
1483
+
1484
+ @override
1485
+ String get voice_bella => 'Bella';
1486
+
1487
+ @override
1488
+ String get voice_bruno => 'Bruno';
1489
+
1490
+ @override
1491
+ String get voice_luna => 'Luna';
1492
+
1493
+ @override
1494
+ String get voice_hugo => 'Hugo';
1495
+
1496
+ @override
1497
+ String get voice_rosie => 'Rosie';
1498
+
1499
+ @override
1500
+ String get voice_leo => 'Leo';
1501
+
1502
+ @override
1503
+ String get voice_kiki => 'Kiki';
1504
+
1505
+ @override
1506
+ String get voice_lessac => 'Lessac (أمريكي)';
1507
+
1508
+ @override
1509
+ String get voice_ryan => 'Ryan (أمريكي)';
1510
+
1511
+ @override
1512
+ String get model_qwen_3 => 'Qwen 3 0.6B';
1513
+
1514
+ @override
1515
+ String get model_qwen_3_desc =>
1516
+ 'أصغر نموذج محادثة عام. استجابات سريعة، استخدام منخفض للذاكرة.';
1517
+
1518
+ @override
1519
+ String get model_license_apache => 'Apache-2.0';
1520
+
1521
+ @override
1522
+ String get model_qwen_25 => 'Qwen 2.5 1.5B Instruct';
1523
+
1524
+ @override
1525
+ String get model_qwen_25_desc => 'جودة وحجم متوازنان. مناسب للمحادثة العامة.';
1526
+
1527
+ @override
1528
+ String get model_deepseek => 'DeepSeek R1 Distill Qwen 1.5B';
1529
+
1530
+ @override
1531
+ String get model_deepseek_desc =>
1532
+ 'نموذج استدلال وسلسلة أفكار. الأفضل للمهام المنطقية.';
1533
+
1534
+ @override
1535
+ String get model_license_mit => 'MIT';
1536
+
1537
+ @override
1538
+ String get model_gemma => 'Gemma 4 E2B Instruct';
1539
+
1540
+ @override
1541
+ String get model_gemma_desc =>
1542
+ 'نموذج Google الرائد. أعلى جودة، يتطلب ذاكرة أكبر.';
1543
+
1544
+ @override
1545
+ String export_header(String date) {
1546
+ return '*تم التصدير من LocalMind — $date*';
1547
+ }
1548
+
1549
+ @override
1550
+ String get export_role_user => '## 👤 المستخدم';
1551
+
1552
+ @override
1553
+ String get export_role_assistant => '## 🤖 المساعد';
1554
+
1555
+ @override
1556
+ String get export_role_system => '## ⚙️ النظام';
1557
+
1558
+ @override
1559
+ String get export_role_tool => '## 🔧 الأداة';
1560
+
1561
+ @override
1562
+ String get export_text_user => '[المستخدم]';
1563
+
1564
+ @override
1565
+ String get export_text_assistant => '[المساعد]';
1566
+
1567
+ @override
1568
+ String get export_text_system => '[النظام]';
1569
+
1570
+ @override
1571
+ String get export_text_tool => '[الأداة]';
1572
+
1573
+ @override
1574
+ String get export_label_user => 'المستخدم';
1575
+
1576
+ @override
1577
+ String get export_label_assistant => 'المساعد';
1578
+
1579
+ @override
1580
+ String get export_label_system => 'النظام';
1581
+
1582
+ @override
1583
+ String get export_label_tool => 'الأداة';
1584
+
1585
+ @override
1586
+ String get select_model_hint => 'اختر نموذجاً لبدء المحادثة';
1587
+
1588
+ @override
1589
+ String get test_notification_title => 'إشعار تجريبي';
1590
+
1591
+ @override
1592
+ String get test_notification_body => 'هذا إشعار تجريبي لتقدم تنزيل النموذج.';
1593
+}
lib/l10n/app_localizations_bn.dart
new
+1603
@@ -0,0 +1,1603 @@
1
+// ignore: unused_import
2
+import 'package:intl/intl.dart' as intl;
3
+import 'app_localizations.dart';
4
+
5
+// ignore_for_file: type=lint
6
+
7
+/// The translations for Bengali Bangla (`bn`).
8
+class AppLocalizationsBn extends AppLocalizations {
9
+ AppLocalizationsBn([String locale = 'bn']) : super(locale);
10
+
11
+ @override
12
+ String get app_name => 'LocalMind';
13
+
14
+ @override
15
+ String get app_tagline => 'আপনার এআই। আপনার ডিভাইস। আপনার নিয়ম।';
16
+
17
+ @override
18
+ String get app_version => '1.0.0';
19
+
20
+ @override
21
+ String get cancel => 'বাতিল করুন';
22
+
23
+ @override
24
+ String get confirm => 'নিশ্চিত করুন';
25
+
26
+ @override
27
+ String get delete => 'মুছে ফেলুন';
28
+
29
+ @override
30
+ String get save => 'সংরক্ষণ করুন';
31
+
32
+ @override
33
+ String get retry => 'আবার চেষ্টা করুন';
34
+
35
+ @override
36
+ String get close => 'বন্ধ করুন';
37
+
38
+ @override
39
+ String get done => 'সম্পন্ন';
40
+
41
+ @override
42
+ String get continue_action => 'চালিয়ে যান';
43
+
44
+ @override
45
+ String get skip => 'এড়িয়ে যান';
46
+
47
+ @override
48
+ String get install => 'ইনস্টল করুন';
49
+
50
+ @override
51
+ String get download => 'ডাউনলোড করুন';
52
+
53
+ @override
54
+ String get resume => 'পুনরায় শুরু করুন';
55
+
56
+ @override
57
+ String get pause => 'বিরতি দিন';
58
+
59
+ @override
60
+ String get stop => 'বন্ধ করুন';
61
+
62
+ @override
63
+ String get edit => 'সম্পাদনা করুন';
64
+
65
+ @override
66
+ String get preview => 'প্রিভিউ';
67
+
68
+ @override
69
+ String get unload => 'আনলোড করুন';
70
+
71
+ @override
72
+ String get load => 'লোড করুন';
73
+
74
+ @override
75
+ String get rename => 'নাম পরিবর্তন করুন';
76
+
77
+ @override
78
+ String get pin => 'পিন করুন';
79
+
80
+ @override
81
+ String get unpin => 'আনপিন করুন';
82
+
83
+ @override
84
+ String get share => 'শেয়ার করুন';
85
+
86
+ @override
87
+ String get copy => 'কপি করুন';
88
+
89
+ @override
90
+ String get copied => 'কপি করা হয়েছে!';
91
+
92
+ @override
93
+ String get copied_to_clipboard => 'ক্লিপবোর্ডে কপি করা হয়েছে';
94
+
95
+ @override
96
+ String get select => 'নির্বাচন করুন';
97
+
98
+ @override
99
+ String get active => 'সক্রিয়';
100
+
101
+ @override
102
+ String get all => 'সব';
103
+
104
+ @override
105
+ String get none => 'কোনোটিই নয়';
106
+
107
+ @override
108
+ String get none_selected => 'কোনোটিই নির্বাচন করা হয়নি';
109
+
110
+ @override
111
+ String get online => 'অনলাইন';
112
+
113
+ @override
114
+ String get offline => 'অফলাইন';
115
+
116
+ @override
117
+ String get error => 'ত্রুটি';
118
+
119
+ @override
120
+ String get unknown_error => 'অজানা ত্রুটি';
121
+
122
+ @override
123
+ String get not_now => 'এখন নয়';
124
+
125
+ @override
126
+ String get enable => 'সক্ষম করুন';
127
+
128
+ @override
129
+ String get proceed_anyway => 'তবুও এগিয়ে যান';
130
+
131
+ @override
132
+ String get test_connection => 'সংযোগ পরীক্ষা করুন';
133
+
134
+ @override
135
+ String get testing => 'পরীক্ষা করা হচ্ছে...';
136
+
137
+ @override
138
+ String get connection_successful => 'সংযোগ সফল হয়েছে!';
139
+
140
+ @override
141
+ String get connection_failed =>
142
+ 'সংযোগ ব্যর্থ হয়েছে। আপনার সেটিংস পরীক্ষা করুন।';
143
+
144
+ @override
145
+ String get save_continue => 'সংরক্ষণ করুন এবং এগিয়ে যান';
146
+
147
+ @override
148
+ String get save_changes => 'পরিবর্তনগুলো সংরক্ষণ করুন';
149
+
150
+ @override
151
+ String get finish_setup => 'সেটআপ সম্পন্ন করুন';
152
+
153
+ @override
154
+ String get start_new_chat => 'নতুন চ্যাট শুরু করুন';
155
+
156
+ @override
157
+ String get cannot_undo => 'এই কাজটি আর পূর্বাবস্থায় ফিরিয়ে আনা যাবে না।';
158
+
159
+ @override
160
+ String get ram_warning => 'র্যাম সংক্রান্ত সতর্কবার্তা';
161
+
162
+ @override
163
+ String get recommended => 'সুপারিশকৃত';
164
+
165
+ @override
166
+ String get may_be_large => 'এই ডিভাইসের জন্য অনেক বড় হতে পারে';
167
+
168
+ @override
169
+ String get calculating => 'গণনা করা হচ্ছে...';
170
+
171
+ @override
172
+ String get download_failed => 'ডাউনলোড ব্যর্থ হয়েছে';
173
+
174
+ @override
175
+ String get downloaded => 'ডাউনলোড করা হয়েছে';
176
+
177
+ @override
178
+ String get not_downloaded => 'ডাউনলোড করা হয়নি';
179
+
180
+ @override
181
+ String get installed => 'ইনস্টল করা হয়েছে';
182
+
183
+ @override
184
+ String get not_installed => 'ইনস্টল করা হয়নি';
185
+
186
+ @override
187
+ String get loading => 'লোড হচ্ছে...';
188
+
189
+ @override
190
+ String get thinking => 'চিন্তা করছে';
191
+
192
+ @override
193
+ String get processing => 'প্রসেস করা হচ্ছে...';
194
+
195
+ @override
196
+ String get initializing => 'আরম্ভ করা হচ্ছে...';
197
+
198
+ @override
199
+ String get ready => 'প্রস্তুত';
200
+
201
+ @override
202
+ String get preparing_app => 'অ্যাপ প্রস্তুত করা হচ্ছে...';
203
+
204
+ @override
205
+ String get initializing_services => 'সার্ভিসসমূহ আরম্ভ করা হচ্ছে...';
206
+
207
+ @override
208
+ String get configuring_server => 'সার্ভার কনফিগার করা হচ্ছে...';
209
+
210
+ @override
211
+ String get startup_failed => 'স্টার্টআপ ব্যর্থ হয়েছে';
212
+
213
+ @override
214
+ String get something_went_wrong => 'কিছু একটা ভুল হয়েছে';
215
+
216
+ @override
217
+ String get delete_model_title => 'মডেল মুছে ফেলুন';
218
+
219
+ @override
220
+ String delete_model_body(String name) {
221
+ return 'আপনি কি নিশ্চিত যে আপনি $name মুছে ফেলতে চান?';
222
+ }
223
+
224
+ @override
225
+ String delete_model_body_with_size(String name, String size) {
226
+ return 'আপনি কি নিশ্চিত যে আপনি $name মুছে ফেলতে চান? এতে প্রায় $size খালি জায়গা তৈরি হবে।\n\nপ্রয়োজন হলে আপনি পরবর্তীতে এই মডেলটি আবার ডাউনলোড করতে পারেন।';
227
+ }
228
+
229
+ @override
230
+ String get delete_voice_title => 'ভয়েস মুছে ফেলুন';
231
+
232
+ @override
233
+ String delete_voice_body(String name, String size) {
234
+ return 'আপনি কি নিশ্চিত যে আপনি $name মুছে ফেলতে চান? এতে প্রায় $size খালি জায়গা তৈরি হবে।\n\nপ্রয়োজন হলে আপনি পরবর্তীতে এই ভয়েসটি আবার ডাউনলোড করতে পারেন।';
235
+ }
236
+
237
+ @override
238
+ String get delete_server_title => 'সার্ভার মুছে ফেলুন';
239
+
240
+ @override
241
+ String delete_server_body(String name) {
242
+ return 'আপনি কি নিশ্চিত যে আপনি \"$name\" মুছে ফেলতে চান? এটি আর পূর্বাবস্থায় ফিরিয়ে আনা যাবে না।';
243
+ }
244
+
245
+ @override
246
+ String get delete_conversation_title => 'কথোপকথন মুছে ফেলবেন?';
247
+
248
+ @override
249
+ String delete_conversation_body(String title) {
250
+ return 'আপনি কি নিশ্চিত যে আপনি \"$title\" মুছে ফেলতে চান? এটি আর পূর্বাবস্থায় ফিরিয়ে আনা যাবে না।';
251
+ }
252
+
253
+ @override
254
+ String get delete_message_title => 'বার্তা মুছে ফেলবেন?';
255
+
256
+ @override
257
+ String delete_persona_title(String name) {
258
+ return '\"$name\" মুছে ফেলবেন?';
259
+ }
260
+
261
+ @override
262
+ String get delete_persona_body => 'এটি আর পূর্বাবস্থায় ফিরিয়ে আনা যাবে না।';
263
+
264
+ @override
265
+ String get clear_conversation_title => 'কথোপকথন মুছে ফেলবেন?';
266
+
267
+ @override
268
+ String get clear_conversation_body =>
269
+ 'এটি এই কথোপকথনের সমস্ত বার্তা মুছে ফেলবে।';
270
+
271
+ @override
272
+ String get clear => 'মুছুন';
273
+
274
+ @override
275
+ String label_completed(String label) {
276
+ return '$label সম্পন্ন হয়েছে';
277
+ }
278
+
279
+ @override
280
+ String error_with_message(String error) {
281
+ return 'ত্রুটি: $error';
282
+ }
283
+
284
+ @override
285
+ String preview_failed(String error) {
286
+ return 'প্রিভিউ ব্যর্থ হয়েছে: $error';
287
+ }
288
+
289
+ @override
290
+ String loading_model(String modelId) {
291
+ return '$modelId লোড করা হচ্ছে...';
292
+ }
293
+
294
+ @override
295
+ String model_loaded(String modelId, String backend) {
296
+ return 'মডেল লোড করা হয়েছে: $modelId ($backend)';
297
+ }
298
+
299
+ @override
300
+ String get no_model_loaded =>
301
+ 'কোনো মডেল লোড করা নেই। একটি মডেল ডাউনলোড এবং লোড করতে \"অন-ডিভাইস মডেল পরিচালনা করুন\" ট্যাপ করুন।';
302
+
303
+ @override
304
+ String loading_model_error(String error) {
305
+ return 'ত্রুটি: $error';
306
+ }
307
+
308
+ @override
309
+ String get delete_conversation => 'কথোপকথন মুছে ফেলবেন?';
310
+
311
+ @override
312
+ String get nav_history => 'ইতিহাস';
313
+
314
+ @override
315
+ String get nav_servers => 'সার্ভারসমূহ';
316
+
317
+ @override
318
+ String get nav_local_models => 'লোকাল মডেলসমূহ';
319
+
320
+ @override
321
+ String get nav_tts => 'টেক্সট টু স্পিচ';
322
+
323
+ @override
324
+ String get nav_personas => 'পার্সোনা';
325
+
326
+ @override
327
+ String get nav_settings => 'সেটিংস';
328
+
329
+ @override
330
+ String get nav_new_chat => 'নতুন চ্যাট';
331
+
332
+ @override
333
+ String get search_hint => 'কথোপকথন খুঁজুন...';
334
+
335
+ @override
336
+ String get no_server_selected => 'কোনো সার্ভার নির্বাচিত নেই';
337
+
338
+ @override
339
+ String get switch_server => 'সার্ভার পরিবর্তন করুন';
340
+
341
+ @override
342
+ String get switch_server_subtitle => 'সংযোগ করার জন্য একটি সার্ভার চয়ন করুন';
343
+
344
+ @override
345
+ String get manage_servers => 'সার্ভার পরিচালনা করুন';
346
+
347
+ @override
348
+ String get open_source => 'ওপেন সোর্স';
349
+
350
+ @override
351
+ String get open_source_desc =>
352
+ 'LocalMind একটি ওপেন সোর্স প্রকল্প। গিটহাবে (GitHub) আমাদের অগ্রগতি অনুসরণ করুন বা অবদান রাখুন।';
353
+
354
+ @override
355
+ String get star_on_github => 'গিটহাবে স্টার দিন';
356
+
357
+ @override
358
+ String get settings_title => 'সেটিংস';
359
+
360
+ @override
361
+ String get settings_appearance => 'চেহারা ও থিম';
362
+
363
+ @override
364
+ String get settings_language => 'ভাষা';
365
+
366
+ @override
367
+ String get language_system_default => 'সিস্টেম ডিফল্ট';
368
+
369
+ @override
370
+ String get settings_tts => 'টেক্সট-টু-স্পিচ';
371
+
372
+ @override
373
+ String get settings_behavior => 'আচরণ';
374
+
375
+ @override
376
+ String get settings_on_device => 'অন-ডিভাইস ইনফারেন্স';
377
+
378
+ @override
379
+ String get settings_default_server => 'ডিফল্ট সার্ভার';
380
+
381
+ @override
382
+ String get settings_default_persona => 'ডিফল্ট পার্সোনা';
383
+
384
+ @override
385
+ String get settings_privacy => 'গোপনীয়তা';
386
+
387
+ @override
388
+ String get settings_data_management => 'ডেটা পরিচালনা';
389
+
390
+ @override
391
+ String get settings_about => 'পরিচিতি';
392
+
393
+ @override
394
+ String get theme => 'থিম';
395
+
396
+ @override
397
+ String get theme_system => 'সিস্টেম';
398
+
399
+ @override
400
+ String get theme_light => 'লাইট';
401
+
402
+ @override
403
+ String get theme_dark => 'ডার্ক';
404
+
405
+ @override
406
+ String get theme_claude => 'Claude';
407
+
408
+ @override
409
+ String get font_size => 'ফন্টের সাইজ';
410
+
411
+ @override
412
+ String get font_size_desc => 'চ্যাটে লেখার সাইজ সামঞ্জস্য করুন।';
413
+
414
+ @override
415
+ String get font_preview =>
416
+ 'যেকোনো লেখার সাইজ কেমন দেখাবে তা এখানে দেখা যাবে।';
417
+
418
+ @override
419
+ String get code_theme_dark => 'কোড থিম (ডার্ক)';
420
+
421
+ @override
422
+ String get code_theme_light => 'কোড থিম (লাইট)';
423
+
424
+ @override
425
+ String get code_theme_desc =>
426
+ 'কোড ব্লকের জন্য সিনট্যাক্স হাইলাইটিং থিম নির্বাচন করুন।';
427
+
428
+ @override
429
+ String get tts_engine => 'টিটিএস (TTS) ইঞ্জিন';
430
+
431
+ @override
432
+ String get tts_engine_system => 'System TTS';
433
+
434
+ @override
435
+ String get tts_engine_kitten => 'Kitten TTS';
436
+
437
+ @override
438
+ String get voice => 'ভয়েস (Voice)';
439
+
440
+ @override
441
+ String get voice_female => 'নারী';
442
+
443
+ @override
444
+ String get voice_male => 'পুরুষ';
445
+
446
+ @override
447
+ String get voice_other => 'অন্যান্য';
448
+
449
+ @override
450
+ String get tts_speed => 'টিটিএস স্পিড';
451
+
452
+ @override
453
+ String get tts_speed_desc => 'প্লেব্যাকের গতি সামঞ্জস্য করুন।';
454
+
455
+ @override
456
+ String get manage_tts_models => 'টিটিএস (TTS) মডেল পরিচালনা করুন';
457
+
458
+ @override
459
+ String get manage_on_device_models => 'অন-ডিভাইস মডেল পরিচালনা করুন';
460
+
461
+ @override
462
+ String get streaming_responses => 'স্ট্রিমিং প্রতিক্রিয়া';
463
+
464
+ @override
465
+ String get auto_generate_titles => 'স্বয়ংক্রিয় শিরোনাম তৈরি করুন';
466
+
467
+ @override
468
+ String get send_on_enter => 'এন্টার (Enter) চাপলে পাঠান';
469
+
470
+ @override
471
+ String get show_system_messages => 'সিস্টেম বার্তাসমূহ দেখান';
472
+
473
+ @override
474
+ String get haptic_feedback => 'হ্যাপটিক ফিডব্যাক';
475
+
476
+ @override
477
+ String get enable_mcp => 'MCP সক্ষম করুন';
478
+
479
+ @override
480
+ String get new_chat_mcp_default => 'নতুন চ্যাটে ডিফল্ট MCP';
481
+
482
+ @override
483
+ String get show_data_indicator => 'ডেটা নির্দেশক দেখান';
484
+
485
+ @override
486
+ String get privacy_info => '\"LocalMind আপনার ডেটা কখনোই দেখে না\"';
487
+
488
+ @override
489
+ String get delete_all_conversations => 'সমস্ত কথোপকথন মুছে ফেলুন';
490
+
491
+ @override
492
+ String get reset_settings_defaults => 'সেটিংস ডিফল্ট মানে রিসেট করুন';
493
+
494
+ @override
495
+ String get chat_title => 'LocalMind';
496
+
497
+ @override
498
+ String get chat_parameters_tooltip => 'চ্যাটের প্যারামিটারসমূহ';
499
+
500
+ @override
501
+ String get change_persona => 'পার্সোনা পরিবর্তন করুন';
502
+
503
+ @override
504
+ String get set_persona => 'পার্সোনা সেট করুন';
505
+
506
+ @override
507
+ String get remove_persona => 'পার্সোনা সরিয়ে ফেলুন';
508
+
509
+ @override
510
+ String get clear_conversation => 'কথোপকথন মুছুন';
511
+
512
+ @override
513
+ String get connection_error => 'সংযোগ ত্রুটি। আপনার সার্ভার পরীক্ষা করুন।';
514
+
515
+ @override
516
+ String get disconnected => 'সার্ভার থেকে সংযোগ বিচ্ছিন্ন হয়েছে।';
517
+
518
+ @override
519
+ String get configure => 'কনফিগার করুন';
520
+
521
+ @override
522
+ String get select_model => 'মডেল নির্বাচন করুন';
523
+
524
+ @override
525
+ String get select_persona => 'পার্সোনা নির্বাচন করুন';
526
+
527
+ @override
528
+ String get start_conversation => 'কথোপকথন শুরু করুন';
529
+
530
+ @override
531
+ String get recent_chats => 'সাম্প্রতিক চ্যাটসমূহ';
532
+
533
+ @override
534
+ String get see_all => 'সব দেখুন';
535
+
536
+ @override
537
+ String get quick_write => 'আমাকে একটি ফাংশন লিখতে সাহায্য করুন';
538
+
539
+ @override
540
+ String get quick_explain => 'এই কোডটি ব্যাখ্যা করুন';
541
+
542
+ @override
543
+ String get quick_debug => 'এটি আমার জন্য ডিবাগ করে দিন';
544
+
545
+ @override
546
+ String get quick_async => 'আমি কীভাবে async/await ব্যবহার করব?';
547
+
548
+ @override
549
+ String get history_missing_title => 'চ্যাটের ইতিহাস পাওয়া যায়নি';
550
+
551
+ @override
552
+ String get history_missing_desc =>
553
+ 'হয় এই চ্যাটের বার্তাগুলো মুছে ফেলা হয়েছে অথবা চ্যাটের ইতিহাসটি দূষিত (corrupted) হয়েছে।';
554
+
555
+ @override
556
+ String get technical_details => 'প্রযুক্তিগত বিবরণ';
557
+
558
+ @override
559
+ String get last_error => 'শেষ ত্রুটি:';
560
+
561
+ @override
562
+ String get copy_info => 'তথ্য কপি করুন';
563
+
564
+ @override
565
+ String get conversation_id => 'কথোপকথন আইডি';
566
+
567
+ @override
568
+ String get created_at => 'তৈরির সময়';
569
+
570
+ @override
571
+ String get expected_messages => 'প্রত্যাশিত বার্তাসমূহ';
572
+
573
+ @override
574
+ String get debug_dialog_desc =>
575
+ 'সিঙ্ক্রোনাইজেশন সমস্যাগুলো সনাক্ত করতে সাহায্য করার জন্য ডায়াগনস্টিক তথ্য।';
576
+
577
+ @override
578
+ String get chat_input_hint => 'যেকোনো কিছু জিজ্ঞাসা করুন';
579
+
580
+ @override
581
+ String get send_message_tooltip => 'বার্তা পাঠান';
582
+
583
+ @override
584
+ String get stop_generation_tooltip => 'জেনারেশন বন্ধ করুন';
585
+
586
+ @override
587
+ String get attach_images_tooltip => 'ছবি সংযুক্ত করুন';
588
+
589
+ @override
590
+ String tool_label(String toolCallId) {
591
+ return 'টুল: $toolCallId';
592
+ }
593
+
594
+ @override
595
+ String get tool_unknown => 'টুল: অজানা';
596
+
597
+ @override
598
+ String get message_options => 'বার্তার বিকল্পসমূহ';
599
+
600
+ @override
601
+ String get copy_markdown => 'মার্কডাউন হিসেবে কপি করুন';
602
+
603
+ @override
604
+ String get copied_markdown => 'মার্কডাউন হিসেবে কপি করা হয়েছে';
605
+
606
+ @override
607
+ String get read_aloud => 'জোরে পড়ুন';
608
+
609
+ @override
610
+ String get stop_reading => 'পড়া বন্ধ করুন';
611
+
612
+ @override
613
+ String get more => 'আরও';
614
+
615
+ @override
616
+ String character_count(int length) {
617
+ return '$lengthটি অক্ষর';
618
+ }
619
+
620
+ @override
621
+ String get edit_message => 'বার্তা সম্পাদনা করুন';
622
+
623
+ @override
624
+ String get edit_message_desc =>
625
+ 'সংরক্ষণ করলে নিচের অ্যাসিস্ট্যান্টের প্রতিক্রিয়াটি মুছে যাবে এবং পুনরায় জেনারেট হবে।';
626
+
627
+ @override
628
+ String get save_regenerate => 'সংরক্ষণ করুন এবং পুনরায় জেনারেট করুন';
629
+
630
+ @override
631
+ String get chat_settings_title => 'চ্যাট সেটিংস';
632
+
633
+ @override
634
+ String get reset_defaults => 'ডিফল্ট মানে রিসেট করুন';
635
+
636
+ @override
637
+ String get parameters_tab => 'প্যারামিটারসমূহ';
638
+
639
+ @override
640
+ String get mcp_tab => 'MCP';
641
+
642
+ @override
643
+ String get temperature => 'টেম্পারেচার (Temperature)';
644
+
645
+ @override
646
+ String get temperature_desc =>
647
+ 'র্যান্ডমনেস নিয়ন্ত্রণ করে: উচ্চমান = সৃজনশীল, নিম্নমান = নিবদ্ধ';
648
+
649
+ @override
650
+ String get top_p => 'টপ পি (Top P)';
651
+
652
+ @override
653
+ String get top_p_desc => 'নিউক্লিয়াস স্যাম্পলিং থ্রেশহোল্ড';
654
+
655
+ @override
656
+ String get max_tokens => 'সর্বোচ্চ টোকেন';
657
+
658
+ @override
659
+ String get max_tokens_desc => 'প্রতিক্রিয়ার সীমা';
660
+
661
+ @override
662
+ String get context_length => 'কনটেক্সট দৈর্ঘ্য';
663
+
664
+ @override
665
+ String get context_length_desc => 'ইতিহাসের উইন্ডো';
666
+
667
+ @override
668
+ String get mcp_disabled_warning =>
669
+ 'MCP বিশ্বব্যাপী নিষ্ক্রিয় করা আছে। এই বৈশিষ্ট্যগুলো ব্যবহার করতে সেটিংসে এটি সক্ষম করুন।';
670
+
671
+ @override
672
+ String get mcp_enable_chat => 'এই চ্যাটের জন্য MCP সক্ষম করুন';
673
+
674
+ @override
675
+ String get auto_execute_tools => 'স্বয়ংক্রিয়ভাবে টুলস নির্বাহ করুন';
676
+
677
+ @override
678
+ String get add_ephemeral_mcp => 'সাময়িক MCP সার্ভার যুক্ত করুন';
679
+
680
+ @override
681
+ String get mcp_label_placeholder => 'লেবেল';
682
+
683
+ @override
684
+ String get mcp_url_placeholder => 'ইউআরএল (https://...)';
685
+
686
+ @override
687
+ String get active_integrations => 'সক্রিয় ইন্টিগ্রেশনসমূহ';
688
+
689
+ @override
690
+ String get enable_notifications => 'নোটিফিকেশন সক্ষম করুন';
691
+
692
+ @override
693
+ String get enable_notifications_desc =>
694
+ 'মডেল ডাউনলোডিং শেষ হলে নোটিফিকেশন পান।';
695
+
696
+ @override
697
+ String get chat_history_title => 'চ্যাটের ইতিহাস';
698
+
699
+ @override
700
+ String get conversation_just_now => 'এইমাত্র';
701
+
702
+ @override
703
+ String conversation_minutes_ago(int minutes) {
704
+ return '$minutesমিনিট আগে';
705
+ }
706
+
707
+ @override
708
+ String conversation_hours_ago(int hours) {
709
+ return '$hoursঘণ্টা আগে';
710
+ }
711
+
712
+ @override
713
+ String get conversation_yesterday => 'গতকাল';
714
+
715
+ @override
716
+ String conversation_days_ago(int days) {
717
+ return '$daysদিন আগে';
718
+ }
719
+
720
+ @override
721
+ String conversation_date(int month, int day, int year) {
722
+ return '$month/$day/$year';
723
+ }
724
+
725
+ @override
726
+ String get options_tooltip => 'বিকল্পসমূহ';
727
+
728
+ @override
729
+ String get no_results_found => 'কোনো ফলাফল পাওয়া যায়নি';
730
+
731
+ @override
732
+ String get no_conversations_yet => 'এখনও কোনো কথোপকথন নেই';
733
+
734
+ @override
735
+ String get try_different_search =>
736
+ 'অন্য কোনো অনুসন্ধান শব্দ দিয়ে চেষ্টা করুন';
737
+
738
+ @override
739
+ String get start_new_conversation => 'নতুন কথোপকথন শুরু করুন';
740
+
741
+ @override
742
+ String get rename_conversation => 'কথোপকথন পুনর্নামকরণ করুন';
743
+
744
+ @override
745
+ String get enter_new_title => 'নতুন শিরোনাম লিখুন';
746
+
747
+ @override
748
+ String get pinned_section => 'পিন করা কথোপকথন';
749
+
750
+ @override
751
+ String get today_section => 'আজ';
752
+
753
+ @override
754
+ String get yesterday_section => 'গতকাল';
755
+
756
+ @override
757
+ String get previous_7_days => 'বিগত ৭ দিন';
758
+
759
+ @override
760
+ String get previous_30_days => 'বিগত ৩০ দিন';
761
+
762
+ @override
763
+ String get older_section => 'পূর্বের';
764
+
765
+ @override
766
+ String get onboarding_localmind => 'LOCALMIND';
767
+
768
+ @override
769
+ String get onboarding_connect_server => 'আপনার সার্ভার\nসংযোগ করুন';
770
+
771
+ @override
772
+ String get onboarding_connect_desc =>
773
+ 'আপনার নিজস্ব ব্যক্তিগত এআই অভিজ্ঞতা শুরু করতে\nLM Studio, Ollama, অথবা OpenRouter-এর সাথে\nসংযোগ করুন।';
774
+
775
+ @override
776
+ String get server_type_on_device => 'অন-ডিভাইস (On-Device)';
777
+
778
+ @override
779
+ String get server_type_on_device_sub => 'কোনো সার্ভারের প্রয়োজন নেই';
780
+
781
+ @override
782
+ String get server_type_lm_studio => 'LM Studio';
783
+
784
+ @override
785
+ String get server_type_lm_studio_sub => 'লোকাল এপিআই';
786
+
787
+ @override
788
+ String get server_type_ollama => 'Ollama';
789
+
790
+ @override
791
+ String get server_type_ollama_sub => 'সিএলআই ইঞ্জিন';
792
+
793
+ @override
794
+ String get server_type_openrouter => 'OpenRouter';
795
+
796
+ @override
797
+ String get server_type_openrouter_sub => 'ইউনিফাইড ক্লাউড';
798
+
799
+ @override
800
+ String get ready_continue => 'এগিয়ে যাওয়ার জন্য প্রস্তুত';
801
+
802
+ @override
803
+ String get waiting_selection => 'নির্বাচনের জন্য অপেক্ষা করা হচ্ছে';
804
+
805
+ @override
806
+ String get setup_connection => 'সংযোগ সেটআপ করুন';
807
+
808
+ @override
809
+ String setup_connection_desc(String server) {
810
+ return 'চ্যাট শুরু করতে আপনার $server সার্ভার কনফিগার করুন।';
811
+ }
812
+
813
+ @override
814
+ String get server_name => 'সার্ভারের নাম';
815
+
816
+ @override
817
+ String get name_required => 'নাম আবশ্যক';
818
+
819
+ @override
820
+ String get name_max_50 => 'সর্বোচ্চ ৫০টি অক্ষর';
821
+
822
+ @override
823
+ String get host_label => 'হোস্ট / আইপি (IP) ঠিকানা';
824
+
825
+ @override
826
+ String get host_required => 'হোস্টের ঠিকানা আবশ্যক';
827
+
828
+ @override
829
+ String get port_label => 'পোর্ট (Port)';
830
+
831
+ @override
832
+ String get port_required => 'পোর্ট আবশ্যক';
833
+
834
+ @override
835
+ String get port_invalid => 'অবশ্যই একটি সংখ্যা হতে হবে';
836
+
837
+ @override
838
+ String get port_range => 'একটি বৈধ পোর্ট নম্বর লিখুন (১-৬৫৫৩৫)';
839
+
840
+ @override
841
+ String get api_key_required => 'এপিআই কি *';
842
+
843
+ @override
844
+ String get api_key_optional => 'এপিআই কি (ঐচ্ছিক)';
845
+
846
+ @override
847
+ String get api_key_required_openrouter =>
848
+ 'OpenRouter-এর জন্য এপিআই কি প্রয়োজন';
849
+
850
+ @override
851
+ String get api_key_format => 'OpenRouter API কি sk- দিয়ে শুরু হয়';
852
+
853
+ @override
854
+ String get my_server_hint => 'আমার সার্ভার';
855
+
856
+ @override
857
+ String get name_length_validation =>
858
+ 'নাম অবশ্যই ৫০ অক্ষরের বা তার কম হতে হবে';
859
+
860
+ @override
861
+ String get host_valid => 'একটি বৈধ হোস্টনেম বা আইপি ঠিকানা লিখুন';
862
+
863
+ @override
864
+ String get api_key_hint_openrouter => 'sk-...';
865
+
866
+ @override
867
+ String get api_key_hint_generic => 'প্রমাণিত সার্ভারের জন্য';
868
+
869
+ @override
870
+ String get update_server => 'সার্ভার আপডেট করুন';
871
+
872
+ @override
873
+ String get save_server => 'সার্ভার সংরক্ষণ করুন';
874
+
875
+ @override
876
+ String get server_updated => 'সার্ভার আপডেট করা হয়েছে';
877
+
878
+ @override
879
+ String get server_added => 'সার্ভার যুক্ত করা হয়েছে';
880
+
881
+ @override
882
+ String get download_model_title => 'মডেল ডাউনলোড করুন';
883
+
884
+ @override
885
+ String get download_model_desc =>
886
+ 'ডাউনলোড করার জন্য একটি মডেল চয়ন করুন।\nএটি আপনার ডিভাইসে স্থানীয়ভাবে চলবে।';
887
+
888
+ @override
889
+ String get on_device_android_only =>
890
+ 'অন-ডিভাইস ইনফারেন্স বর্তমানে শুধুমাত্র অ্যান্ড্রয়েডেই উপলব্ধ।';
891
+
892
+ @override
893
+ String get total_ram => 'মোট র্যাম';
894
+
895
+ @override
896
+ String get available => 'উপলব্ধ';
897
+
898
+ @override
899
+ String ram_min_required(String fileSize) {
900
+ return 'সর্বনিম্ন $fileSize GB র্যাম (RAM) প্রয়োজন';
901
+ }
902
+
903
+ @override
904
+ String download_progress(String percent, String speed) {
905
+ return '$percent% • $speed';
906
+ }
907
+
908
+ @override
909
+ String eta_label(String eta) {
910
+ return 'অবশিষ্ট সময় (ETA): $eta';
911
+ }
912
+
913
+ @override
914
+ String paused_progress(String percent) {
915
+ return 'সাময়িক বিরতি - $percent%';
916
+ }
917
+
918
+ @override
919
+ String ram_warning_body_download(String ram, String totalMemory) {
920
+ return 'এই মডেলটির জন্য অন্তত $ram GB র্যাম প্রয়োজন, কিন্তু আপনার ডিভাইসে $totalMemory রয়েছে। এটি সঠিকভাবে নাও চলতে পারে অথবা অ্যাপটি ক্র্যাশ করতে পারে।';
921
+ }
922
+
923
+ @override
924
+ String ram_warning_body_load(String availableRAM, String ram) {
925
+ return 'আপনার ডিভাইসে $availableRAM খালি র্যাম রয়েছে, কিন্তু এই মডেলটির জন্য অন্তত $ram GB প্রয়োজন। এটি লোড করতে ব্যর্থ হতে পারে অথবা সিস্টেমে অস্থিরতা দেখা দিতে পারে।';
926
+ }
927
+
928
+ @override
929
+ String get choose_theme => 'থিম নির্বাচন করুন';
930
+
931
+ @override
932
+ String get choose_theme_desc =>
933
+ 'অ্যাপের চেহারা ব্যক্তিগতকৃত করুন। আপনি পরবর্তীতে সেটিংস থেকে এটি যেকোনো সময় পরিবর্তন করতে পারবেন।';
934
+
935
+ @override
936
+ String get theme_card_system => 'সিস্টেম';
937
+
938
+ @override
939
+ String get theme_card_system_sub => 'আপনার ডিভাইসের সেটিংসের সাথে মিলে যায়';
940
+
941
+ @override
942
+ String get theme_card_light => 'লাইট';
943
+
944
+ @override
945
+ String get theme_card_light_sub => 'পরিষ্কার এবং উজ্জ্বল';
946
+
947
+ @override
948
+ String get theme_card_dark => 'ডার্ক';
949
+
950
+ @override
951
+ String get theme_card_dark_sub => 'চোখের জন্য আরামদায়ক';
952
+
953
+ @override
954
+ String get theme_card_claude => 'Claude';
955
+
956
+ @override
957
+ String get theme_card_claude_sub => 'একটি উষ্ণ, পিচ রঙের থিম';
958
+
959
+ @override
960
+ String get stay_updated => 'আপ-টু-ডেট থাকুন';
961
+
962
+ @override
963
+ String get stay_updated_desc =>
964
+ 'আপনার এআই মডেলগুলোর ডাউনলোড সম্পন্ন হলে অথবা দীর্ঘস্থায়ী কাজগুলো শেষ হলে নোটিফিকেশন পান।';
965
+
966
+ @override
967
+ String get notification_benefit_downloads => 'মডেল ডাউনলোডের অগ্রগতি';
968
+
969
+ @override
970
+ String get notification_benefit_completions => 'জেনারেশন সম্পন্ন হওয়ার তথ্য';
971
+
972
+ @override
973
+ String get notification_benefit_background => 'ব্যাকগ্রাউন্ড কাজের অবস্থা';
974
+
975
+ @override
976
+ String get allow_notifications => 'নোটিফিকেশন অনুমোদন করুন';
977
+
978
+ @override
979
+ String get servers_title => 'সার্ভারসমূহ';
980
+
981
+ @override
982
+ String get no_servers_yet => 'এখনও কোনো সার্ভার নেই';
983
+
984
+ @override
985
+ String get no_servers_desc =>
986
+ 'এআই মডেলগুলোর সাথে চ্যাট করা শুরু করতে আপনার প্রথম সার্ভার যুক্ত করুন।';
987
+
988
+ @override
989
+ String get add_server => 'সার্ভার যুক্ত করুন';
990
+
991
+ @override
992
+ String switched_to_server(String name) {
993
+ return '$name-এ স্থানান্তরিত হয়েছে';
994
+ }
995
+
996
+ @override
997
+ String get edit_server => 'সার্ভার সম্পাদনা করুন';
998
+
999
+ @override
1000
+ String get add_server_title => 'সার্ভার যুক্ত করুন';
1001
+
1002
+ @override
1003
+ String get server_type_label => 'সার্ভারের ধরন';
1004
+
1005
+ @override
1006
+ String get server_icon_label => 'সার্ভার আইকন';
1007
+
1008
+ @override
1009
+ String get default_icon => 'ডিফল্ট আইকন';
1010
+
1011
+ @override
1012
+ String get server_type_lm_studio_display => 'LM Studio';
1013
+
1014
+ @override
1015
+ String get server_type_openai_display => 'ওপেনএআই সামঞ্জস্যপূর্ণ';
1016
+
1017
+ @override
1018
+ String get server_type_ollama_display => 'Ollama';
1019
+
1020
+ @override
1021
+ String get server_type_openrouter_display => 'OpenRouter';
1022
+
1023
+ @override
1024
+ String get server_type_on_device_display => 'অন-ডিভাইস (On-Device)';
1025
+
1026
+ @override
1027
+ String get server_address_openrouter => 'openrouter.ai';
1028
+
1029
+ @override
1030
+ String get server_address_on_device => 'স্থানীয় ইনফারেন্স (Local Inference)';
1031
+
1032
+ @override
1033
+ String server_address_format(String host, String port) {
1034
+ return '$host:$port';
1035
+ }
1036
+
1037
+ @override
1038
+ String get default_badge => 'ডিফল্ট';
1039
+
1040
+ @override
1041
+ String get set_as_default => 'ডিফল্ট হিসেবে সেট করুন';
1042
+
1043
+ @override
1044
+ String get select_icon => 'আইকন নির্বাচন করুন';
1045
+
1046
+ @override
1047
+ String get select_icon_desc => 'আপনার সার্ভারের জন্য একটি আইকন চয়ন করুন';
1048
+
1049
+ @override
1050
+ String get search_icons_hint => 'আইকন খুঁজুন...';
1051
+
1052
+ @override
1053
+ String get server_icon_stack => 'সার্ভার স্ট্যাক';
1054
+
1055
+ @override
1056
+ String get server_icon_stack2 => 'সার্ভার স্ট্যাক ০২';
1057
+
1058
+ @override
1059
+ String get server_icon_stack3 => 'সার্ভার স্ট্যাক ০৩';
1060
+
1061
+ @override
1062
+ String get server_icon_cloud => 'ক্লাউড';
1063
+
1064
+ @override
1065
+ String get server_icon_cloud_server => 'ক্লাউড সার্ভার';
1066
+
1067
+ @override
1068
+ String get server_icon_mcp => 'MCP সার্ভার';
1069
+
1070
+ @override
1071
+ String get server_icon_database => 'ডেটাবেস';
1072
+
1073
+ @override
1074
+ String get server_icon_database1 => 'ডেটাবেস ০১';
1075
+
1076
+ @override
1077
+ String get server_icon_database2 => 'ডেটাবেস ০২';
1078
+
1079
+ @override
1080
+ String get server_icon_cpu => 'সিপিইউ (CPU)';
1081
+
1082
+ @override
1083
+ String get server_icon_chip => 'চিপ (Chip)';
1084
+
1085
+ @override
1086
+ String get server_icon_chip2 => 'চিপ ০২';
1087
+
1088
+ @override
1089
+ String get server_icon_computer => 'কম্পিউটার';
1090
+
1091
+ @override
1092
+ String get server_icon_laptop => 'ল্যাপটপ';
1093
+
1094
+ @override
1095
+ String get server_icon_terminal => 'কম্পিউটার টার্মিনাল';
1096
+
1097
+ @override
1098
+ String get server_icon_code => 'কোড';
1099
+
1100
+ @override
1101
+ String get server_icon_ai_brain => 'এআই ব্রেইন (AI Brain)';
1102
+
1103
+ @override
1104
+ String get server_icon_ai_brain2 => 'এআই ব্রেইন ০২ (AI Brain 02)';
1105
+
1106
+ @override
1107
+ String get server_icon_ai_cloud => 'এআই ক্লাউড (AI Cloud)';
1108
+
1109
+ @override
1110
+ String get server_icon_ai_network => 'এআই নেটওয়ার্ক (AI Network)';
1111
+
1112
+ @override
1113
+ String get server_icon_ai_chat => 'এআই চ্যাট (AI Chat)';
1114
+
1115
+ @override
1116
+ String get server_icon_cellular => 'সেলুলার নেটওয়ার্ক';
1117
+
1118
+ @override
1119
+ String get server_icon_plug1 => 'প্লাগ ০১';
1120
+
1121
+ @override
1122
+ String get server_icon_plug2 => 'প্লাগ ০২';
1123
+
1124
+ @override
1125
+ String get server_icon_bot => 'বট (Bot)';
1126
+
1127
+ @override
1128
+ String get server_icon_bot2 => 'বট ০২';
1129
+
1130
+ @override
1131
+ String get server_icon_robotic => 'রোবোটিক';
1132
+
1133
+ @override
1134
+ String get server_icon_rocket => 'রকেট';
1135
+
1136
+ @override
1137
+ String get server_icon_star => 'তারা';
1138
+
1139
+ @override
1140
+ String get server_icon_settings1 => 'সেটিংস ০১';
1141
+
1142
+ @override
1143
+ String get server_icon_settings2 => 'সেটিংস ০২';
1144
+
1145
+ @override
1146
+ String get server_icon_home1 => 'হোম ০১';
1147
+
1148
+ @override
1149
+ String get server_icon_home2 => 'হোম ০২';
1150
+
1151
+ @override
1152
+ String get server_icon_folder1 => 'ফোল্ডার ০১';
1153
+
1154
+ @override
1155
+ String get server_icon_folder2 => 'ফোল্ডার ০২';
1156
+
1157
+ @override
1158
+ String get server_icon_file1 => 'ফাইল ০১';
1159
+
1160
+ @override
1161
+ String get server_icon_lock => 'লক';
1162
+
1163
+ @override
1164
+ String get server_icon_key => 'কি ০১';
1165
+
1166
+ @override
1167
+ String get server_icon_link => 'লিঙ্ক ০১';
1168
+
1169
+ @override
1170
+ String get server_icon_globe => 'গ্লোব';
1171
+
1172
+ @override
1173
+ String get server_icon_api => 'এপিআই (API)';
1174
+
1175
+ @override
1176
+ String get server_icon_arrow_right => 'ডানমুখী তীর ০১';
1177
+
1178
+ @override
1179
+ String get server_icon_check => 'টিকচিহ্ন বৃত্ত';
1180
+
1181
+ @override
1182
+ String get server_icon_alert => 'সতর্কীকরণ বৃত্ত';
1183
+
1184
+ @override
1185
+ String get server_icon_info => 'তথ্য বৃত্ত';
1186
+
1187
+ @override
1188
+ String get server_icon_zap => 'জ্যাপ';
1189
+
1190
+ @override
1191
+ String get server_icon_cloud_upload => 'ক্লাউড আপলোড';
1192
+
1193
+ @override
1194
+ String get server_icon_cloud_download => 'ক্লাউড ডাউনলোড';
1195
+
1196
+ @override
1197
+ String get server_icon_refresh => 'রিফ্রেশ';
1198
+
1199
+ @override
1200
+ String get server_icon_hard_drive => 'হার্ড ড্রাইভ';
1201
+
1202
+ @override
1203
+ String get server_icon_drive => 'ড্রাইভ';
1204
+
1205
+ @override
1206
+ String get personas_title => 'পার্সোনা';
1207
+
1208
+ @override
1209
+ String get persona_category_general => 'সাধারণ';
1210
+
1211
+ @override
1212
+ String get persona_category_coding => 'কোডিং';
1213
+
1214
+ @override
1215
+ String get persona_category_education => 'শিক্ষা';
1216
+
1217
+ @override
1218
+ String get persona_category_creative => 'সৃজনশীল';
1219
+
1220
+ @override
1221
+ String get persona_builtin_section => 'বিল্ট-ইন';
1222
+
1223
+ @override
1224
+ String get persona_my_section => 'আমার পার্সোনা';
1225
+
1226
+ @override
1227
+ String get clone_edit => 'ক্লোন ও সম্পাদনা করুন';
1228
+
1229
+ @override
1230
+ String get builtin_badge => 'বিল্ট-ইন';
1231
+
1232
+ @override
1233
+ String get no_personas_found => 'কোনো পার্সোনা পাওয়া যায়নি';
1234
+
1235
+ @override
1236
+ String get no_personas_desc =>
1237
+ 'এআই আচরণ কাস্টমাইজ করতে আপনার প্রথম পার্সোনা তৈরি করুন।';
1238
+
1239
+ @override
1240
+ String get edit_persona => 'পার্সোনা সম্পাদনা করুন';
1241
+
1242
+ @override
1243
+ String get create_persona => 'পার্সোনা তৈরি করুন';
1244
+
1245
+ @override
1246
+ String get create_persona_button => 'তৈরি করুন';
1247
+
1248
+ @override
1249
+ String get emoji_label => 'ইমোজি';
1250
+
1251
+ @override
1252
+ String get name_label => 'নাম';
1253
+
1254
+ @override
1255
+ String get my_persona_hint => 'আমার পার্সোনা';
1256
+
1257
+ @override
1258
+ String get category_label => 'বিভাগ';
1259
+
1260
+ @override
1261
+ String get description_optional => 'বিবরণ (ঐচ্ছিক)';
1262
+
1263
+ @override
1264
+ String get description_hint => 'এই পার্সোনাটি কী কাজ করে...';
1265
+
1266
+ @override
1267
+ String get system_prompt => 'সিস্টেম প্রম্পট';
1268
+
1269
+ @override
1270
+ String character_count_max(int currentLen) {
1271
+ return '$currentLen/৪০০০';
1272
+ }
1273
+
1274
+ @override
1275
+ String get no_prompt_placeholder => 'এখনও কোনো প্রম্পট নেই...';
1276
+
1277
+ @override
1278
+ String get prompt_hint => 'আপনি একজন সহায়ক অ্যাসিস্ট্যান্ট...';
1279
+
1280
+ @override
1281
+ String get prompt_required => 'সিস্টেম প্রম্পট আবশ্যক';
1282
+
1283
+ @override
1284
+ String get prompt_max_chars => 'সর্বোচ্চ ৪০০০ অক্ষর';
1285
+
1286
+ @override
1287
+ String get advanced_settings => 'উন্নত সেটিংস';
1288
+
1289
+ @override
1290
+ String get temperature_label => 'টেম্পারেচার (০.০-২.০)';
1291
+
1292
+ @override
1293
+ String get top_p_label => 'টপ পি (০.০-১.০)';
1294
+
1295
+ @override
1296
+ String get temp_hint => '0.7';
1297
+
1298
+ @override
1299
+ String get top_p_hint => '0.9';
1300
+
1301
+ @override
1302
+ String get range_0_2 => '0.0-2.0';
1303
+
1304
+ @override
1305
+ String get range_0_1 => '0.0-1.0';
1306
+
1307
+ @override
1308
+ String get persona_updated => 'পার্সোনা আপডেট করা হয়েছে';
1309
+
1310
+ @override
1311
+ String get persona_created => 'পার্সোনা তৈরি করা হয়েছে';
1312
+
1313
+ @override
1314
+ String get tts_models_title => 'টেক্সট টু স্পিচ মডেলসমূহ';
1315
+
1316
+ @override
1317
+ String get always_available => 'সর্বদা উপলব্ধ';
1318
+
1319
+ @override
1320
+ String get tts_system_desc =>
1321
+ 'আপনার ডিভাইসের বিল্ট-ইন টেক্সট-টু-স্পিচ ইঞ্জিন ব্যবহার করে।\nকোনো ডাউনলোডের প্রয়োজন নেই। ভয়েস নির্বাচন আপনার ডিভাইসের সিস্টেম সেটিংস ব্যবহার করে।';
1322
+
1323
+ @override
1324
+ String get downloading_status => 'ডাউনলোড করা হচ্ছে...';
1325
+
1326
+ @override
1327
+ String tts_kitten_desc(String size) {
1328
+ return '৮টি অভিব্যক্তিপূর্ণ ভয়েসসহ বিদ্যুৎ-গতির নিউরাল টিটিএস।\nএটি ডাউনলোড করতে $size প্রয়োজন।';
1329
+ }
1330
+
1331
+ @override
1332
+ String tts_piper_desc(String size) {
1333
+ return '২টি অভিব্যক্তিপূর্ণ ভয়েসসহ দ্রুত অফলাইন পাইপার ভয়েস।\nভয়েস প্রতি ডাউনলোড করতে $size প্রয়োজন।';
1334
+ }
1335
+
1336
+ @override
1337
+ String engine_spec(String sizeMb, String ramMb, int voiceCount) {
1338
+ return '$sizeMb MB · $ramMb MB RAM · $voiceCountটি ভয়েস';
1339
+ }
1340
+
1341
+ @override
1342
+ String get on_device_models_title => 'অন-ডিভাইস মডেলসমূহ';
1343
+
1344
+ @override
1345
+ String get available_models => 'উপলব্ধ মডেলসমূহ';
1346
+
1347
+ @override
1348
+ String get device_memory => 'ডিভাইসের মেমরি';
1349
+
1350
+ @override
1351
+ String get ram_usage => 'র্যাম ব্যবহার';
1352
+
1353
+ @override
1354
+ String get memory_healthy => 'স্বাস্থ্যকর';
1355
+
1356
+ @override
1357
+ String get memory_critical => 'সংকটপূর্ণ';
1358
+
1359
+ @override
1360
+ String get memory_low => 'কম';
1361
+
1362
+ @override
1363
+ String ram_used(String percent) {
1364
+ return '$percent% ব্যবহৃত';
1365
+ }
1366
+
1367
+ @override
1368
+ String get available_ram => 'উপলব্ধ র্যাম (RAM)';
1369
+
1370
+ @override
1371
+ String get total_capacity => 'মোট ক্ষমতা';
1372
+
1373
+ @override
1374
+ String get loaded_status => 'লোড করা হয়েছে';
1375
+
1376
+ @override
1377
+ String get inference_backend => 'ইনফারেন্স ব্যাকএন্ড';
1378
+
1379
+ @override
1380
+ String get backend_ios_notice => 'iOS-এ শুধুমাত্র CPU ব্যাকএন্ড উপলব্ধ।';
1381
+
1382
+ @override
1383
+ String get backend_cpu_desc =>
1384
+ 'সব ডিভাইসে কাজ করে। সবচেয়ে বেশি সামঞ্জস্যপূর্ণ।';
1385
+
1386
+ @override
1387
+ String get backend_gpu_desc => 'OpenCL ত্বরণ। সমর্থিত ডিভাইসে দ্রুততর।';
1388
+
1389
+ @override
1390
+ String get backend_npu_desc =>
1391
+ 'ভেন্ডর NPU (Qualcomm/MediaTek)। দ্রুততম ইনফারেন্স।';
1392
+
1393
+ @override
1394
+ String get select_model_title => 'মডেল নির্বাচন করুন';
1395
+
1396
+ @override
1397
+ String get refresh_models => 'মডেল রিফ্রেশ করুন';
1398
+
1399
+ @override
1400
+ String get search_models_hint => 'মডেল খুঁজুন...';
1401
+
1402
+ @override
1403
+ String get no_server_connected => 'কোনো সার্ভার সংযুক্ত নেই';
1404
+
1405
+ @override
1406
+ String get add_server_first =>
1407
+ 'উপলব্ধ মডেলগুলো দেখতে প্রথমে একটি সার্ভার যুক্ত করুন।';
1408
+
1409
+ @override
1410
+ String get failed_load_models => 'মডেল লোড করতে ব্যর্থ হয়েছে';
1411
+
1412
+ @override
1413
+ String get no_models_available => 'কোনো মডেল উপলব্ধ নেই';
1414
+
1415
+ @override
1416
+ String no_models_match(String searchQuery) {
1417
+ return '\"$searchQuery\"-এর সাথে কোনো মডেল মেলেনি';
1418
+ }
1419
+
1420
+ @override
1421
+ String model_load_failed(String error) {
1422
+ return 'মডেল লোড করতে ব্যর্থ হয়েছে: $error';
1423
+ }
1424
+
1425
+ @override
1426
+ String model_unloaded_ollama(String name) {
1427
+ return 'কিপ-অ্যালাইভ (keep-alive) সময় পার হলে $name আনলোড করা হবে';
1428
+ }
1429
+
1430
+ @override
1431
+ String model_unloaded_success(String name) {
1432
+ return '$name সফলভাবে আনলোড করা হয়েছে';
1433
+ }
1434
+
1435
+ @override
1436
+ String model_unload_failed(String error) {
1437
+ return 'মডেল আনলোড করতে ব্যর্থ হয়েছে: $error';
1438
+ }
1439
+
1440
+ @override
1441
+ String get unload_from_server => 'সার্ভার থেকে আনলোড করুন';
1442
+
1443
+ @override
1444
+ String context_chip(String ctx) {
1445
+ return '$ctx ctx';
1446
+ }
1447
+
1448
+ @override
1449
+ String download_notification_title(String modelName) {
1450
+ return '$modelName ডাউনলোড করা হচ্ছে...';
1451
+ }
1452
+
1453
+ @override
1454
+ String get download_complete_notification => 'ডাউনলোড সম্পন্ন হয়েছে!';
1455
+
1456
+ @override
1457
+ String download_complete_body(String modelName) {
1458
+ return '$modelName সফলভাবে ডাউনলোড করা হয়েছে।';
1459
+ }
1460
+
1461
+ @override
1462
+ String download_failed_notification(String error) {
1463
+ return 'ডাউনলোড ব্যর্থ হয়েছে: $error';
1464
+ }
1465
+
1466
+ @override
1467
+ String download_failed_body(String modelName) {
1468
+ return '$modelName ডাউনলোড করতে ব্যর্থ হয়েছে।';
1469
+ }
1470
+
1471
+ @override
1472
+ String get engine_name_system => 'System TTS';
1473
+
1474
+ @override
1475
+ String get engine_tagline_system => 'ডিভাইসের বিল্ট-ইন ইঞ্জিন';
1476
+
1477
+ @override
1478
+ String get engine_name_kitten => 'Kitten TTS';
1479
+
1480
+ @override
1481
+ String get engine_tagline_kitten => 'উচ্চ-গতির নিউরাল টিটিএস (TTS)';
1482
+
1483
+ @override
1484
+ String get engine_name_sherpa => 'Sherpa ONNX VITS';
1485
+
1486
+ @override
1487
+ String get engine_tagline_sherpa => 'অফলাইন পাইপার (Piper) ভয়েসসমূহ';
1488
+
1489
+ @override
1490
+ String get voice_jasper => 'Jasper';
1491
+
1492
+ @override
1493
+ String get voice_bella => 'Bella';
1494
+
1495
+ @override
1496
+ String get voice_bruno => 'Bruno';
1497
+
1498
+ @override
1499
+ String get voice_luna => 'Luna';
1500
+
1501
+ @override
1502
+ String get voice_hugo => 'Hugo';
1503
+
1504
+ @override
1505
+ String get voice_rosie => 'Rosie';
1506
+
1507
+ @override
1508
+ String get voice_leo => 'Leo';
1509
+
1510
+ @override
1511
+ String get voice_kiki => 'Kiki';
1512
+
1513
+ @override
1514
+ String get voice_lessac => 'Lessac (US)';
1515
+
1516
+ @override
1517
+ String get voice_ryan => 'Ryan (US)';
1518
+
1519
+ @override
1520
+ String get model_qwen_3 => 'Qwen 3 0.6B';
1521
+
1522
+ @override
1523
+ String get model_qwen_3_desc =>
1524
+ 'সবচেয়ে ছোট সাধারণ ব্যবহারের চ্যাট মডেল। দ্রুত প্রতিক্রিয়া, কম মেমরি ব্যবহার।';
1525
+
1526
+ @override
1527
+ String get model_license_apache => 'Apache-2.0';
1528
+
1529
+ @override
1530
+ String get model_qwen_25 => 'Qwen 2.5 1.5B Instruct';
1531
+
1532
+ @override
1533
+ String get model_qwen_25_desc =>
1534
+ 'মান এবং আকারের চমৎকার ভারসাম্য। সাধারণ কথোপকথনের জন্য ভালো।';
1535
+
1536
+ @override
1537
+ String get model_deepseek => 'DeepSeek R1 Distill Qwen 1.5B';
1538
+
1539
+ @override
1540
+ String get model_deepseek_desc =>
1541
+ 'রিজনিং এবং চেইন-অফ-থট মডেল। যৌক্তিক কাজের জন্য সেরা।';
1542
+
1543
+ @override
1544
+ String get model_license_mit => 'MIT';
1545
+
1546
+ @override
1547
+ String get model_gemma => 'Gemma 4 E2B Instruct';
1548
+
1549
+ @override
1550
+ String get model_gemma_desc =>
1551
+ 'গুগলের ফ্ল্যাগশিপ মডেল। সর্বোচ্চ মানসম্পন্ন, অধিক র্যাম প্রয়োজন।';
1552
+
1553
+ @override
1554
+ String export_header(String date) {
1555
+ return '*LocalMind থেকে রপ্তানি করা হয়েছে — $date*';
1556
+ }
1557
+
1558
+ @override
1559
+ String get export_role_user => '## 👤 ব্যবহারকারী';
1560
+
1561
+ @override
1562
+ String get export_role_assistant => '## 🤖 অ্যাসিস্ট্যান্ট';
1563
+
1564
+ @override
1565
+ String get export_role_system => '## ⚙️ সিস্টেম';
1566
+
1567
+ @override
1568
+ String get export_role_tool => '## 🔧 টুল';
1569
+
1570
+ @override
1571
+ String get export_text_user => '[ব্যবহারকারী]';
1572
+
1573
+ @override
1574
+ String get export_text_assistant => '[অ্যাসিস্ট্যান্ট]';
1575
+
1576
+ @override
1577
+ String get export_text_system => '[সিস্টেম]';
1578
+
1579
+ @override
1580
+ String get export_text_tool => '[টুল]';
1581
+
1582
+ @override
1583
+ String get export_label_user => 'ব্যবহারকারী';
1584
+
1585
+ @override
1586
+ String get export_label_assistant => 'অ্যাসিস্ট্যান্ট';
1587
+
1588
+ @override
1589
+ String get export_label_system => 'সিস্টেম';
1590
+
1591
+ @override
1592
+ String get export_label_tool => 'টুল';
1593
+
1594
+ @override
1595
+ String get select_model_hint => 'চ্যাট শুরু করতে একটি মডেল নির্বাচন করুন';
1596
+
1597
+ @override
1598
+ String get test_notification_title => 'পরীক্ষা নোটিফিকেশন';
1599
+
1600
+ @override
1601
+ String get test_notification_body =>
1602
+ 'এটি মডেল ডাউনলোডের অগ্রগতির জন্য একটি পরীক্ষা নোটিফিকেশন।';
1603
+}
lib/l10n/app_localizations_en.dart
new
+1597
@@ -0,0 +1,1597 @@
1
+// ignore: unused_import
2
+import 'package:intl/intl.dart' as intl;
3
+import 'app_localizations.dart';
4
+
5
+// ignore_for_file: type=lint
6
+
7
+/// The translations for English (`en`).
8
+class AppLocalizationsEn extends AppLocalizations {
9
+ AppLocalizationsEn([String locale = 'en']) : super(locale);
10
+
11
+ @override
12
+ String get app_name => 'LocalMind';
13
+
14
+ @override
15
+ String get app_tagline => 'Your AI. Your Device. Your Rules.';
16
+
17
+ @override
18
+ String get app_version => '1.0.0';
19
+
20
+ @override
21
+ String get cancel => 'Cancel';
22
+
23
+ @override
24
+ String get confirm => 'Confirm';
25
+
26
+ @override
27
+ String get delete => 'Delete';
28
+
29
+ @override
30
+ String get save => 'Save';
31
+
32
+ @override
33
+ String get retry => 'Retry';
34
+
35
+ @override
36
+ String get close => 'Close';
37
+
38
+ @override
39
+ String get done => 'Done';
40
+
41
+ @override
42
+ String get continue_action => 'Continue';
43
+
44
+ @override
45
+ String get skip => 'Skip';
46
+
47
+ @override
48
+ String get install => 'Install';
49
+
50
+ @override
51
+ String get download => 'Download';
52
+
53
+ @override
54
+ String get resume => 'Resume';
55
+
56
+ @override
57
+ String get pause => 'Pause';
58
+
59
+ @override
60
+ String get stop => 'Stop';
61
+
62
+ @override
63
+ String get edit => 'Edit';
64
+
65
+ @override
66
+ String get preview => 'Preview';
67
+
68
+ @override
69
+ String get unload => 'Unload';
70
+
71
+ @override
72
+ String get load => 'Load';
73
+
74
+ @override
75
+ String get rename => 'Rename';
76
+
77
+ @override
78
+ String get pin => 'Pin';
79
+
80
+ @override
81
+ String get unpin => 'Unpin';
82
+
83
+ @override
84
+ String get share => 'Share';
85
+
86
+ @override
87
+ String get copy => 'Copy';
88
+
89
+ @override
90
+ String get copied => 'Copied!';
91
+
92
+ @override
93
+ String get copied_to_clipboard => 'Copied to clipboard';
94
+
95
+ @override
96
+ String get select => 'Select';
97
+
98
+ @override
99
+ String get active => 'Active';
100
+
101
+ @override
102
+ String get all => 'All';
103
+
104
+ @override
105
+ String get none => 'None';
106
+
107
+ @override
108
+ String get none_selected => 'None selected';
109
+
110
+ @override
111
+ String get online => 'Online';
112
+
113
+ @override
114
+ String get offline => 'Offline';
115
+
116
+ @override
117
+ String get error => 'Error';
118
+
119
+ @override
120
+ String get unknown_error => 'Unknown error';
121
+
122
+ @override
123
+ String get not_now => 'Not Now';
124
+
125
+ @override
126
+ String get enable => 'Enable';
127
+
128
+ @override
129
+ String get proceed_anyway => 'Proceed Anyway';
130
+
131
+ @override
132
+ String get test_connection => 'Test Connection';
133
+
134
+ @override
135
+ String get testing => 'Testing...';
136
+
137
+ @override
138
+ String get connection_successful => 'Connection successful!';
139
+
140
+ @override
141
+ String get connection_failed => 'Connection failed. Check your settings.';
142
+
143
+ @override
144
+ String get save_continue => 'Save & Continue';
145
+
146
+ @override
147
+ String get save_changes => 'Save Changes';
148
+
149
+ @override
150
+ String get finish_setup => 'Finish Setup';
151
+
152
+ @override
153
+ String get start_new_chat => 'Start New Chat';
154
+
155
+ @override
156
+ String get cannot_undo => 'This action cannot be undone.';
157
+
158
+ @override
159
+ String get ram_warning => 'RAM Warning';
160
+
161
+ @override
162
+ String get recommended => 'RECOMMENDED';
163
+
164
+ @override
165
+ String get may_be_large => 'May be too large for this device';
166
+
167
+ @override
168
+ String get calculating => 'Calculating...';
169
+
170
+ @override
171
+ String get download_failed => 'Download failed';
172
+
173
+ @override
174
+ String get downloaded => 'Downloaded';
175
+
176
+ @override
177
+ String get not_downloaded => 'Not downloaded';
178
+
179
+ @override
180
+ String get installed => 'Installed';
181
+
182
+ @override
183
+ String get not_installed => 'Not installed';
184
+
185
+ @override
186
+ String get loading => 'Loading...';
187
+
188
+ @override
189
+ String get thinking => 'Thinking';
190
+
191
+ @override
192
+ String get processing => 'Processing...';
193
+
194
+ @override
195
+ String get initializing => 'Initializing...';
196
+
197
+ @override
198
+ String get ready => 'Ready';
199
+
200
+ @override
201
+ String get preparing_app => 'Preparing app...';
202
+
203
+ @override
204
+ String get initializing_services => 'Initializing services...';
205
+
206
+ @override
207
+ String get configuring_server => 'Configuring server...';
208
+
209
+ @override
210
+ String get startup_failed => 'Startup failed';
211
+
212
+ @override
213
+ String get something_went_wrong => 'Something went wrong';
214
+
215
+ @override
216
+ String get delete_model_title => 'Delete Model';
217
+
218
+ @override
219
+ String delete_model_body(String name) {
220
+ return 'Are you sure you want to delete $name?';
221
+ }
222
+
223
+ @override
224
+ String delete_model_body_with_size(String name, String size) {
225
+ return 'Are you sure you want to delete $name? This will free up approximately $size of space.\n\nYou can download this model again later if needed.';
226
+ }
227
+
228
+ @override
229
+ String get delete_voice_title => 'Delete Voice';
230
+
231
+ @override
232
+ String delete_voice_body(String name, String size) {
233
+ return 'Are you sure you want to delete $name? This will free up approximately $size of space.\n\nYou can download this voice again later if needed.';
234
+ }
235
+
236
+ @override
237
+ String get delete_server_title => 'Delete Server';
238
+
239
+ @override
240
+ String delete_server_body(String name) {
241
+ return 'Are you sure you want to delete \"$name\"? This cannot be undone.';
242
+ }
243
+
244
+ @override
245
+ String get delete_conversation_title => 'Delete conversation?';
246
+
247
+ @override
248
+ String delete_conversation_body(String title) {
249
+ return 'Are you sure you want to delete \"$title\"? This cannot be undone.';
250
+ }
251
+
252
+ @override
253
+ String get delete_message_title => 'Delete message?';
254
+
255
+ @override
256
+ String delete_persona_title(String name) {
257
+ return 'Delete \"$name\"?';
258
+ }
259
+
260
+ @override
261
+ String get delete_persona_body => 'This cannot be undone.';
262
+
263
+ @override
264
+ String get clear_conversation_title => 'Clear conversation?';
265
+
266
+ @override
267
+ String get clear_conversation_body =>
268
+ 'This will delete all messages in this conversation.';
269
+
270
+ @override
271
+ String get clear => 'Clear';
272
+
273
+ @override
274
+ String label_completed(String label) {
275
+ return '$label completed';
276
+ }
277
+
278
+ @override
279
+ String error_with_message(String error) {
280
+ return 'Error: $error';
281
+ }
282
+
283
+ @override
284
+ String preview_failed(String error) {
285
+ return 'Preview failed: $error';
286
+ }
287
+
288
+ @override
289
+ String loading_model(String modelId) {
290
+ return 'Loading $modelId...';
291
+ }
292
+
293
+ @override
294
+ String model_loaded(String modelId, String backend) {
295
+ return 'Model loaded: $modelId ($backend)';
296
+ }
297
+
298
+ @override
299
+ String get no_model_loaded =>
300
+ 'No model loaded. Tap \"Manage On-Device Models\" to download and load a model.';
301
+
302
+ @override
303
+ String loading_model_error(String error) {
304
+ return 'Error: $error';
305
+ }
306
+
307
+ @override
308
+ String get delete_conversation => 'Delete conversation?';
309
+
310
+ @override
311
+ String get nav_history => 'History';
312
+
313
+ @override
314
+ String get nav_servers => 'Servers';
315
+
316
+ @override
317
+ String get nav_local_models => 'Local Models';
318
+
319
+ @override
320
+ String get nav_tts => 'Text To Speech';
321
+
322
+ @override
323
+ String get nav_personas => 'Personas';
324
+
325
+ @override
326
+ String get nav_settings => 'Settings';
327
+
328
+ @override
329
+ String get nav_new_chat => 'New Chat';
330
+
331
+ @override
332
+ String get search_hint => 'Search conversations...';
333
+
334
+ @override
335
+ String get no_server_selected => 'No server selected';
336
+
337
+ @override
338
+ String get switch_server => 'Switch Server';
339
+
340
+ @override
341
+ String get switch_server_subtitle => 'Choose a server to connect to';
342
+
343
+ @override
344
+ String get manage_servers => 'Manage Servers';
345
+
346
+ @override
347
+ String get open_source => 'Open Source';
348
+
349
+ @override
350
+ String get open_source_desc =>
351
+ 'LocalMind is open source. Follow our progress or contribute on GitHub.';
352
+
353
+ @override
354
+ String get star_on_github => 'Star on GitHub';
355
+
356
+ @override
357
+ String get settings_title => 'Settings';
358
+
359
+ @override
360
+ String get settings_appearance => 'Appearance';
361
+
362
+ @override
363
+ String get settings_language => 'Language';
364
+
365
+ @override
366
+ String get language_system_default => 'System Default';
367
+
368
+ @override
369
+ String get settings_tts => 'Text-to-Speech';
370
+
371
+ @override
372
+ String get settings_behavior => 'Behavior';
373
+
374
+ @override
375
+ String get settings_on_device => 'On-Device Inference';
376
+
377
+ @override
378
+ String get settings_default_server => 'Default Server';
379
+
380
+ @override
381
+ String get settings_default_persona => 'Default Persona';
382
+
383
+ @override
384
+ String get settings_privacy => 'Privacy';
385
+
386
+ @override
387
+ String get settings_data_management => 'Data Management';
388
+
389
+ @override
390
+ String get settings_about => 'About';
391
+
392
+ @override
393
+ String get theme => 'Theme';
394
+
395
+ @override
396
+ String get theme_system => 'System';
397
+
398
+ @override
399
+ String get theme_light => 'Light';
400
+
401
+ @override
402
+ String get theme_dark => 'Dark';
403
+
404
+ @override
405
+ String get theme_claude => 'Claude';
406
+
407
+ @override
408
+ String get font_size => 'Font Size';
409
+
410
+ @override
411
+ String get font_size_desc => 'Adjust text size in chat.';
412
+
413
+ @override
414
+ String get font_preview => 'The quick brown fox jumps over the lazy dog.';
415
+
416
+ @override
417
+ String get code_theme_dark => 'Code Theme (Dark)';
418
+
419
+ @override
420
+ String get code_theme_light => 'Code Theme (Light)';
421
+
422
+ @override
423
+ String get code_theme_desc =>
424
+ 'Choose syntax highlighting theme for code blocks.';
425
+
426
+ @override
427
+ String get tts_engine => 'TTS Engine';
428
+
429
+ @override
430
+ String get tts_engine_system => 'System TTS';
431
+
432
+ @override
433
+ String get tts_engine_kitten => 'Kitten TTS';
434
+
435
+ @override
436
+ String get voice => 'Voice';
437
+
438
+ @override
439
+ String get voice_female => 'Female';
440
+
441
+ @override
442
+ String get voice_male => 'Male';
443
+
444
+ @override
445
+ String get voice_other => 'Other';
446
+
447
+ @override
448
+ String get tts_speed => 'TTS Speed';
449
+
450
+ @override
451
+ String get tts_speed_desc => 'Adjust the playback rate.';
452
+
453
+ @override
454
+ String get manage_tts_models => 'Manage TTS Models';
455
+
456
+ @override
457
+ String get manage_on_device_models => 'Manage On-Device Models';
458
+
459
+ @override
460
+ String get streaming_responses => 'Streaming Responses';
461
+
462
+ @override
463
+ String get auto_generate_titles => 'Auto-generate Titles';
464
+
465
+ @override
466
+ String get send_on_enter => 'Send on Enter';
467
+
468
+ @override
469
+ String get show_system_messages => 'Show System Messages';
470
+
471
+ @override
472
+ String get haptic_feedback => 'Haptic Feedback';
473
+
474
+ @override
475
+ String get enable_mcp => 'Enable MCP';
476
+
477
+ @override
478
+ String get new_chat_mcp_default => 'New Chat MCP Default';
479
+
480
+ @override
481
+ String get show_data_indicator => 'Show Data Indicator';
482
+
483
+ @override
484
+ String get privacy_info => '\"LocalMind never sees your data\"';
485
+
486
+ @override
487
+ String get delete_all_conversations => 'Delete All Conversations';
488
+
489
+ @override
490
+ String get reset_settings_defaults => 'Reset Settings to Defaults';
491
+
492
+ @override
493
+ String get chat_title => 'LocalMind';
494
+
495
+ @override
496
+ String get chat_parameters_tooltip => 'Chat Parameters';
497
+
498
+ @override
499
+ String get change_persona => 'Change Persona';
500
+
501
+ @override
502
+ String get set_persona => 'Set Persona';
503
+
504
+ @override
505
+ String get remove_persona => 'Remove Persona';
506
+
507
+ @override
508
+ String get clear_conversation => 'Clear Conversation';
509
+
510
+ @override
511
+ String get connection_error => 'Connection error. Check your server.';
512
+
513
+ @override
514
+ String get disconnected => 'Disconnected from server.';
515
+
516
+ @override
517
+ String get configure => 'Configure';
518
+
519
+ @override
520
+ String get select_model => 'Select Model';
521
+
522
+ @override
523
+ String get select_persona => 'Select Persona';
524
+
525
+ @override
526
+ String get start_conversation => 'Start a conversation';
527
+
528
+ @override
529
+ String get recent_chats => 'Recent chats';
530
+
531
+ @override
532
+ String get see_all => 'See all';
533
+
534
+ @override
535
+ String get quick_write => 'Help me write a function';
536
+
537
+ @override
538
+ String get quick_explain => 'Explain this code';
539
+
540
+ @override
541
+ String get quick_debug => 'Debug this for me';
542
+
543
+ @override
544
+ String get quick_async => 'How do I use async/await?';
545
+
546
+ @override
547
+ String get history_missing_title => 'History Missing';
548
+
549
+ @override
550
+ String get history_missing_desc =>
551
+ 'Either the messages in this chat were deleted or the history record is corrupted.';
552
+
553
+ @override
554
+ String get technical_details => 'Technical Details';
555
+
556
+ @override
557
+ String get last_error => 'Last Error:';
558
+
559
+ @override
560
+ String get copy_info => 'Copy Info';
561
+
562
+ @override
563
+ String get conversation_id => 'Conversation ID';
564
+
565
+ @override
566
+ String get created_at => 'Created At';
567
+
568
+ @override
569
+ String get expected_messages => 'Expected Messages';
570
+
571
+ @override
572
+ String get debug_dialog_desc =>
573
+ 'Diagnostic information to help identify synchronization issues.';
574
+
575
+ @override
576
+ String get chat_input_hint => 'Ask anything';
577
+
578
+ @override
579
+ String get send_message_tooltip => 'Send message';
580
+
581
+ @override
582
+ String get stop_generation_tooltip => 'Stop generation';
583
+
584
+ @override
585
+ String get attach_images_tooltip => 'Attach images';
586
+
587
+ @override
588
+ String tool_label(String toolCallId) {
589
+ return 'Tool: $toolCallId';
590
+ }
591
+
592
+ @override
593
+ String get tool_unknown => 'Tool: Unknown';
594
+
595
+ @override
596
+ String get message_options => 'Message options';
597
+
598
+ @override
599
+ String get copy_markdown => 'Copy as Markdown';
600
+
601
+ @override
602
+ String get copied_markdown => 'Copied as Markdown';
603
+
604
+ @override
605
+ String get read_aloud => 'Read Aloud';
606
+
607
+ @override
608
+ String get stop_reading => 'Stop Reading';
609
+
610
+ @override
611
+ String get more => 'More';
612
+
613
+ @override
614
+ String character_count(int length) {
615
+ return '$length characters';
616
+ }
617
+
618
+ @override
619
+ String get edit_message => 'Edit message';
620
+
621
+ @override
622
+ String get edit_message_desc =>
623
+ 'Saving will remove the assistant response below and regenerate.';
624
+
625
+ @override
626
+ String get save_regenerate => 'Save & regenerate';
627
+
628
+ @override
629
+ String get chat_settings_title => 'Chat Settings';
630
+
631
+ @override
632
+ String get reset_defaults => 'Reset Defaults';
633
+
634
+ @override
635
+ String get parameters_tab => 'Parameters';
636
+
637
+ @override
638
+ String get mcp_tab => 'MCP';
639
+
640
+ @override
641
+ String get temperature => 'Temperature';
642
+
643
+ @override
644
+ String get temperature_desc =>
645
+ 'Controls randomness: Higher = Creative, Lower = Focused';
646
+
647
+ @override
648
+ String get top_p => 'Top P';
649
+
650
+ @override
651
+ String get top_p_desc => 'Nucleus sampling threshold';
652
+
653
+ @override
654
+ String get max_tokens => 'Max Tokens';
655
+
656
+ @override
657
+ String get max_tokens_desc => 'Response limit';
658
+
659
+ @override
660
+ String get context_length => 'Context Length';
661
+
662
+ @override
663
+ String get context_length_desc => 'History window';
664
+
665
+ @override
666
+ String get mcp_disabled_warning =>
667
+ 'MCP is disabled globally. Enable it in Settings to use these features.';
668
+
669
+ @override
670
+ String get mcp_enable_chat => 'Enable MCP for this chat';
671
+
672
+ @override
673
+ String get auto_execute_tools => 'Auto-execute tools';
674
+
675
+ @override
676
+ String get add_ephemeral_mcp => 'Add Ephemeral MCP Server';
677
+
678
+ @override
679
+ String get mcp_label_placeholder => 'Label';
680
+
681
+ @override
682
+ String get mcp_url_placeholder => 'URL (https://...)';
683
+
684
+ @override
685
+ String get active_integrations => 'Active Integrations';
686
+
687
+ @override
688
+ String get enable_notifications => 'Enable Notifications';
689
+
690
+ @override
691
+ String get enable_notifications_desc =>
692
+ 'Get notified when models finish downloading.';
693
+
694
+ @override
695
+ String get chat_history_title => 'Chat History';
696
+
697
+ @override
698
+ String get conversation_just_now => 'Just now';
699
+
700
+ @override
701
+ String conversation_minutes_ago(int minutes) {
702
+ return '${minutes}m ago';
703
+ }
704
+
705
+ @override
706
+ String conversation_hours_ago(int hours) {
707
+ return '${hours}h ago';
708
+ }
709
+
710
+ @override
711
+ String get conversation_yesterday => 'Yesterday';
712
+
713
+ @override
714
+ String conversation_days_ago(int days) {
715
+ return '${days}d ago';
716
+ }
717
+
718
+ @override
719
+ String conversation_date(int month, int day, int year) {
720
+ return '$month/$day/$year';
721
+ }
722
+
723
+ @override
724
+ String get options_tooltip => 'Options';
725
+
726
+ @override
727
+ String get no_results_found => 'No results found';
728
+
729
+ @override
730
+ String get no_conversations_yet => 'No conversations yet';
731
+
732
+ @override
733
+ String get try_different_search => 'Try a different search term';
734
+
735
+ @override
736
+ String get start_new_conversation => 'Start a new conversation';
737
+
738
+ @override
739
+ String get rename_conversation => 'Rename conversation';
740
+
741
+ @override
742
+ String get enter_new_title => 'Enter new title';
743
+
744
+ @override
745
+ String get pinned_section => 'PINNED';
746
+
747
+ @override
748
+ String get today_section => 'TODAY';
749
+
750
+ @override
751
+ String get yesterday_section => 'YESTERDAY';
752
+
753
+ @override
754
+ String get previous_7_days => 'PREVIOUS 7 DAYS';
755
+
756
+ @override
757
+ String get previous_30_days => 'PREVIOUS 30 DAYS';
758
+
759
+ @override
760
+ String get older_section => 'OLDER';
761
+
762
+ @override
763
+ String get onboarding_localmind => 'LOCALMIND';
764
+
765
+ @override
766
+ String get onboarding_connect_server => 'Connect Your\nServer';
767
+
768
+ @override
769
+ String get onboarding_connect_desc =>
770
+ 'Connect to LM Studio, Ollama, or\nOpenRouter to start your private AI\nexperience.';
771
+
772
+ @override
773
+ String get server_type_on_device => 'On-Device';
774
+
775
+ @override
776
+ String get server_type_on_device_sub => 'NO SERVER NEEDED';
777
+
778
+ @override
779
+ String get server_type_lm_studio => 'LM Studio';
780
+
781
+ @override
782
+ String get server_type_lm_studio_sub => 'LOCAL API';
783
+
784
+ @override
785
+ String get server_type_ollama => 'Ollama';
786
+
787
+ @override
788
+ String get server_type_ollama_sub => 'CLI ENGINE';
789
+
790
+ @override
791
+ String get server_type_openrouter => 'OpenRouter';
792
+
793
+ @override
794
+ String get server_type_openrouter_sub => 'UNIFIED CLOUD';
795
+
796
+ @override
797
+ String get ready_continue => 'READY TO CONTINUE';
798
+
799
+ @override
800
+ String get waiting_selection => 'WAITING FOR SELECTION';
801
+
802
+ @override
803
+ String get setup_connection => 'Setup Connection';
804
+
805
+ @override
806
+ String setup_connection_desc(String server) {
807
+ return 'Configure your $server server to start chatting.';
808
+ }
809
+
810
+ @override
811
+ String get server_name => 'Server Name';
812
+
813
+ @override
814
+ String get name_required => 'Name required';
815
+
816
+ @override
817
+ String get name_max_50 => 'Max 50 characters';
818
+
819
+ @override
820
+ String get host_label => 'Host / IP Address';
821
+
822
+ @override
823
+ String get host_required => 'Host required';
824
+
825
+ @override
826
+ String get port_label => 'Port';
827
+
828
+ @override
829
+ String get port_required => 'Port required';
830
+
831
+ @override
832
+ String get port_invalid => 'Must be a number';
833
+
834
+ @override
835
+ String get port_range => 'Enter a valid port (1-65535)';
836
+
837
+ @override
838
+ String get api_key_required => 'API Key *';
839
+
840
+ @override
841
+ String get api_key_optional => 'API Key (Optional)';
842
+
843
+ @override
844
+ String get api_key_required_openrouter => 'API Key required for OpenRouter';
845
+
846
+ @override
847
+ String get api_key_format => 'OpenRouter API keys start with sk-';
848
+
849
+ @override
850
+ String get my_server_hint => 'My Server';
851
+
852
+ @override
853
+ String get name_length_validation => 'Name must be 50 characters or less';
854
+
855
+ @override
856
+ String get host_valid => 'Enter a valid hostname or IP address';
857
+
858
+ @override
859
+ String get api_key_hint_openrouter => 'sk-...';
860
+
861
+ @override
862
+ String get api_key_hint_generic => 'For authenticated servers';
863
+
864
+ @override
865
+ String get update_server => 'Update Server';
866
+
867
+ @override
868
+ String get save_server => 'Save Server';
869
+
870
+ @override
871
+ String get server_updated => 'Server updated';
872
+
873
+ @override
874
+ String get server_added => 'Server added';
875
+
876
+ @override
877
+ String get download_model_title => 'Download a Model';
878
+
879
+ @override
880
+ String get download_model_desc =>
881
+ 'Choose a model to download.\nIt will run locally on your device.';
882
+
883
+ @override
884
+ String get on_device_android_only =>
885
+ 'On-device inference is currently available on Android only.';
886
+
887
+ @override
888
+ String get total_ram => 'Total RAM';
889
+
890
+ @override
891
+ String get available => 'Available';
892
+
893
+ @override
894
+ String ram_min_required(String fileSize) {
895
+ return '$fileSize GB RAM min';
896
+ }
897
+
898
+ @override
899
+ String download_progress(String percent, String speed) {
900
+ return '$percent% • $speed';
901
+ }
902
+
903
+ @override
904
+ String eta_label(String eta) {
905
+ return 'ETA: $eta';
906
+ }
907
+
908
+ @override
909
+ String paused_progress(String percent) {
910
+ return 'Paused - $percent%';
911
+ }
912
+
913
+ @override
914
+ String ram_warning_body_download(String ram, String totalMemory) {
915
+ return 'This model requires at least $ram GB RAM, but your device has $totalMemory. It may not run correctly or could cause the app to crash.';
916
+ }
917
+
918
+ @override
919
+ String ram_warning_body_load(String availableRAM, String ram) {
920
+ return 'Your device has $availableRAM available RAM, but this model recommends at least $ram GB. Loading it might fail or cause instability.';
921
+ }
922
+
923
+ @override
924
+ String get choose_theme => 'Choose Theme';
925
+
926
+ @override
927
+ String get choose_theme_desc =>
928
+ 'Personalize the app appearance. You can always change this later in settings.';
929
+
930
+ @override
931
+ String get theme_card_system => 'System';
932
+
933
+ @override
934
+ String get theme_card_system_sub => 'Matches your device settings';
935
+
936
+ @override
937
+ String get theme_card_light => 'Light';
938
+
939
+ @override
940
+ String get theme_card_light_sub => 'Clean and bright';
941
+
942
+ @override
943
+ String get theme_card_dark => 'Dark';
944
+
945
+ @override
946
+ String get theme_card_dark_sub => 'Easy on the eyes';
947
+
948
+ @override
949
+ String get theme_card_claude => 'Claude';
950
+
951
+ @override
952
+ String get theme_card_claude_sub => 'A warm, peach-tinted theme';
953
+
954
+ @override
955
+ String get stay_updated => 'Stay Updated';
956
+
957
+ @override
958
+ String get stay_updated_desc =>
959
+ 'Get notified when your AI models finish downloading or when long-running tasks complete.';
960
+
961
+ @override
962
+ String get notification_benefit_downloads => 'Model download progress';
963
+
964
+ @override
965
+ String get notification_benefit_completions => 'Generation completions';
966
+
967
+ @override
968
+ String get notification_benefit_background => 'Background tasks status';
969
+
970
+ @override
971
+ String get allow_notifications => 'Allow Notifications';
972
+
973
+ @override
974
+ String get servers_title => 'Servers';
975
+
976
+ @override
977
+ String get no_servers_yet => 'No Servers Yet';
978
+
979
+ @override
980
+ String get no_servers_desc =>
981
+ 'Add your first server to start chatting with AI models.';
982
+
983
+ @override
984
+ String get add_server => 'Add Server';
985
+
986
+ @override
987
+ String switched_to_server(String name) {
988
+ return 'Switched to $name';
989
+ }
990
+
991
+ @override
992
+ String get edit_server => 'Edit Server';
993
+
994
+ @override
995
+ String get add_server_title => 'Add Server';
996
+
997
+ @override
998
+ String get server_type_label => 'Server Type';
999
+
1000
+ @override
1001
+ String get server_icon_label => 'Server Icon';
1002
+
1003
+ @override
1004
+ String get default_icon => 'Default icon';
1005
+
1006
+ @override
1007
+ String get server_type_lm_studio_display => 'LM Studio';
1008
+
1009
+ @override
1010
+ String get server_type_openai_display => 'OpenAI Compatible';
1011
+
1012
+ @override
1013
+ String get server_type_ollama_display => 'Ollama';
1014
+
1015
+ @override
1016
+ String get server_type_openrouter_display => 'OpenRouter';
1017
+
1018
+ @override
1019
+ String get server_type_on_device_display => 'On-Device';
1020
+
1021
+ @override
1022
+ String get server_address_openrouter => 'openrouter.ai';
1023
+
1024
+ @override
1025
+ String get server_address_on_device => 'Local inference';
1026
+
1027
+ @override
1028
+ String server_address_format(String host, String port) {
1029
+ return '$host:$port';
1030
+ }
1031
+
1032
+ @override
1033
+ String get default_badge => 'Default';
1034
+
1035
+ @override
1036
+ String get set_as_default => 'Set as Default';
1037
+
1038
+ @override
1039
+ String get select_icon => 'Select Icon';
1040
+
1041
+ @override
1042
+ String get select_icon_desc => 'Choose an icon for your server';
1043
+
1044
+ @override
1045
+ String get search_icons_hint => 'Search icons...';
1046
+
1047
+ @override
1048
+ String get server_icon_stack => 'Server Stack';
1049
+
1050
+ @override
1051
+ String get server_icon_stack2 => 'Server Stack 02';
1052
+
1053
+ @override
1054
+ String get server_icon_stack3 => 'Server Stack 03';
1055
+
1056
+ @override
1057
+ String get server_icon_cloud => 'Cloud';
1058
+
1059
+ @override
1060
+ String get server_icon_cloud_server => 'Cloud Server';
1061
+
1062
+ @override
1063
+ String get server_icon_mcp => 'MCP Server';
1064
+
1065
+ @override
1066
+ String get server_icon_database => 'Database';
1067
+
1068
+ @override
1069
+ String get server_icon_database1 => 'Database 01';
1070
+
1071
+ @override
1072
+ String get server_icon_database2 => 'Database 02';
1073
+
1074
+ @override
1075
+ String get server_icon_cpu => 'CPU';
1076
+
1077
+ @override
1078
+ String get server_icon_chip => 'Chip';
1079
+
1080
+ @override
1081
+ String get server_icon_chip2 => 'Chip 02';
1082
+
1083
+ @override
1084
+ String get server_icon_computer => 'Computer';
1085
+
1086
+ @override
1087
+ String get server_icon_laptop => 'Laptop';
1088
+
1089
+ @override
1090
+ String get server_icon_terminal => 'Computer Terminal';
1091
+
1092
+ @override
1093
+ String get server_icon_code => 'Code';
1094
+
1095
+ @override
1096
+ String get server_icon_ai_brain => 'AI Brain';
1097
+
1098
+ @override
1099
+ String get server_icon_ai_brain2 => 'AI Brain 02';
1100
+
1101
+ @override
1102
+ String get server_icon_ai_cloud => 'AI Cloud';
1103
+
1104
+ @override
1105
+ String get server_icon_ai_network => 'AI Network';
1106
+
1107
+ @override
1108
+ String get server_icon_ai_chat => 'AI Chat';
1109
+
1110
+ @override
1111
+ String get server_icon_cellular => 'Cellular Network';
1112
+
1113
+ @override
1114
+ String get server_icon_plug1 => 'Plug 01';
1115
+
1116
+ @override
1117
+ String get server_icon_plug2 => 'Plug 02';
1118
+
1119
+ @override
1120
+ String get server_icon_bot => 'Bot';
1121
+
1122
+ @override
1123
+ String get server_icon_bot2 => 'Bot 02';
1124
+
1125
+ @override
1126
+ String get server_icon_robotic => 'Robotic';
1127
+
1128
+ @override
1129
+ String get server_icon_rocket => 'Rocket';
1130
+
1131
+ @override
1132
+ String get server_icon_star => 'Star';
1133
+
1134
+ @override
1135
+ String get server_icon_settings1 => 'Settings 01';
1136
+
1137
+ @override
1138
+ String get server_icon_settings2 => 'Settings 02';
1139
+
1140
+ @override
1141
+ String get server_icon_home1 => 'Home 01';
1142
+
1143
+ @override
1144
+ String get server_icon_home2 => 'Home 02';
1145
+
1146
+ @override
1147
+ String get server_icon_folder1 => 'Folder 01';
1148
+
1149
+ @override
1150
+ String get server_icon_folder2 => 'Folder 02';
1151
+
1152
+ @override
1153
+ String get server_icon_file1 => 'File 01';
1154
+
1155
+ @override
1156
+ String get server_icon_lock => 'Lock';
1157
+
1158
+ @override
1159
+ String get server_icon_key => 'Key 01';
1160
+
1161
+ @override
1162
+ String get server_icon_link => 'Link 01';
1163
+
1164
+ @override
1165
+ String get server_icon_globe => 'Globe';
1166
+
1167
+ @override
1168
+ String get server_icon_api => 'API';
1169
+
1170
+ @override
1171
+ String get server_icon_arrow_right => 'Arrow Right 01';
1172
+
1173
+ @override
1174
+ String get server_icon_check => 'Check Circle';
1175
+
1176
+ @override
1177
+ String get server_icon_alert => 'Alert Circle';
1178
+
1179
+ @override
1180
+ String get server_icon_info => 'Info Circle';
1181
+
1182
+ @override
1183
+ String get server_icon_zap => 'Zap';
1184
+
1185
+ @override
1186
+ String get server_icon_cloud_upload => 'Cloud Upload';
1187
+
1188
+ @override
1189
+ String get server_icon_cloud_download => 'Cloud Download';
1190
+
1191
+ @override
1192
+ String get server_icon_refresh => 'Refresh';
1193
+
1194
+ @override
1195
+ String get server_icon_hard_drive => 'Hard Drive';
1196
+
1197
+ @override
1198
+ String get server_icon_drive => 'Drive';
1199
+
1200
+ @override
1201
+ String get personas_title => 'Personas';
1202
+
1203
+ @override
1204
+ String get persona_category_general => 'General';
1205
+
1206
+ @override
1207
+ String get persona_category_coding => 'Coding';
1208
+
1209
+ @override
1210
+ String get persona_category_education => 'Education';
1211
+
1212
+ @override
1213
+ String get persona_category_creative => 'Creative';
1214
+
1215
+ @override
1216
+ String get persona_builtin_section => 'BUILT-IN';
1217
+
1218
+ @override
1219
+ String get persona_my_section => 'MY PERSONAS';
1220
+
1221
+ @override
1222
+ String get clone_edit => 'Clone & Edit';
1223
+
1224
+ @override
1225
+ String get builtin_badge => 'Built-in';
1226
+
1227
+ @override
1228
+ String get no_personas_found => 'No personas found';
1229
+
1230
+ @override
1231
+ String get no_personas_desc =>
1232
+ 'Create your first persona to customize AI behavior.';
1233
+
1234
+ @override
1235
+ String get edit_persona => 'Edit Persona';
1236
+
1237
+ @override
1238
+ String get create_persona => 'Create Persona';
1239
+
1240
+ @override
1241
+ String get create_persona_button => 'Create';
1242
+
1243
+ @override
1244
+ String get emoji_label => 'Emoji';
1245
+
1246
+ @override
1247
+ String get name_label => 'Name';
1248
+
1249
+ @override
1250
+ String get my_persona_hint => 'My Persona';
1251
+
1252
+ @override
1253
+ String get category_label => 'Category';
1254
+
1255
+ @override
1256
+ String get description_optional => 'Description (optional)';
1257
+
1258
+ @override
1259
+ String get description_hint => 'What this persona does...';
1260
+
1261
+ @override
1262
+ String get system_prompt => 'System Prompt';
1263
+
1264
+ @override
1265
+ String character_count_max(int currentLen) {
1266
+ return '$currentLen/4000';
1267
+ }
1268
+
1269
+ @override
1270
+ String get no_prompt_placeholder => 'No prompt yet...';
1271
+
1272
+ @override
1273
+ String get prompt_hint => 'You are a helpful assistant...';
1274
+
1275
+ @override
1276
+ String get prompt_required => 'System prompt is required';
1277
+
1278
+ @override
1279
+ String get prompt_max_chars => 'Max 4000 characters';
1280
+
1281
+ @override
1282
+ String get advanced_settings => 'Advanced Settings';
1283
+
1284
+ @override
1285
+ String get temperature_label => 'Temperature (0.0-2.0)';
1286
+
1287
+ @override
1288
+ String get top_p_label => 'Top P (0.0-1.0)';
1289
+
1290
+ @override
1291
+ String get temp_hint => '0.7';
1292
+
1293
+ @override
1294
+ String get top_p_hint => '0.9';
1295
+
1296
+ @override
1297
+ String get range_0_2 => '0.0-2.0';
1298
+
1299
+ @override
1300
+ String get range_0_1 => '0.0-1.0';
1301
+
1302
+ @override
1303
+ String get persona_updated => 'Persona updated';
1304
+
1305
+ @override
1306
+ String get persona_created => 'Persona created';
1307
+
1308
+ @override
1309
+ String get tts_models_title => 'Text To Speech Models';
1310
+
1311
+ @override
1312
+ String get always_available => 'Always available';
1313
+
1314
+ @override
1315
+ String get tts_system_desc =>
1316
+ 'Uses your device\'s built-in text-to-speech engine.\nNo downloads required. Voice selection uses your device\'s system settings.';
1317
+
1318
+ @override
1319
+ String get downloading_status => 'Downloading...';
1320
+
1321
+ @override
1322
+ String tts_kitten_desc(String size) {
1323
+ return 'Lightning-fast neural TTS with 8 expressive voices.\nRequires $size download.';
1324
+ }
1325
+
1326
+ @override
1327
+ String tts_piper_desc(String size) {
1328
+ return 'Fast offline Piper voices with 2 expressive voices.\nRequires $size download per voice.';
1329
+ }
1330
+
1331
+ @override
1332
+ String engine_spec(String sizeMb, String ramMb, int voiceCount) {
1333
+ return '$sizeMb MB · $ramMb MB RAM · $voiceCount voices';
1334
+ }
1335
+
1336
+ @override
1337
+ String get on_device_models_title => 'On-Device Models';
1338
+
1339
+ @override
1340
+ String get available_models => 'Available Models';
1341
+
1342
+ @override
1343
+ String get device_memory => 'Device Memory';
1344
+
1345
+ @override
1346
+ String get ram_usage => 'RAM Usage';
1347
+
1348
+ @override
1349
+ String get memory_healthy => 'Healthy';
1350
+
1351
+ @override
1352
+ String get memory_critical => 'Critical';
1353
+
1354
+ @override
1355
+ String get memory_low => 'Low';
1356
+
1357
+ @override
1358
+ String ram_used(String percent) {
1359
+ return '$percent% used';
1360
+ }
1361
+
1362
+ @override
1363
+ String get available_ram => 'Available RAM';
1364
+
1365
+ @override
1366
+ String get total_capacity => 'Total Capacity';
1367
+
1368
+ @override
1369
+ String get loaded_status => 'Loaded';
1370
+
1371
+ @override
1372
+ String get inference_backend => 'Inference Backend';
1373
+
1374
+ @override
1375
+ String get backend_ios_notice => 'Only CPU backend is available on iOS.';
1376
+
1377
+ @override
1378
+ String get backend_cpu_desc => 'Works on all devices. Most compatible.';
1379
+
1380
+ @override
1381
+ String get backend_gpu_desc =>
1382
+ 'OpenCL acceleration. Faster on supported devices.';
1383
+
1384
+ @override
1385
+ String get backend_npu_desc =>
1386
+ 'Vendor NPU (Qualcomm/MediaTek). Fastest inference.';
1387
+
1388
+ @override
1389
+ String get select_model_title => 'Select Model';
1390
+
1391
+ @override
1392
+ String get refresh_models => 'Refresh models';
1393
+
1394
+ @override
1395
+ String get search_models_hint => 'Search models...';
1396
+
1397
+ @override
1398
+ String get no_server_connected => 'No server connected';
1399
+
1400
+ @override
1401
+ String get add_server_first => 'Add a server first to see available models.';
1402
+
1403
+ @override
1404
+ String get failed_load_models => 'Failed to load models';
1405
+
1406
+ @override
1407
+ String get no_models_available => 'No models available';
1408
+
1409
+ @override
1410
+ String no_models_match(String searchQuery) {
1411
+ return 'No models match \"$searchQuery\"';
1412
+ }
1413
+
1414
+ @override
1415
+ String model_load_failed(String error) {
1416
+ return 'Failed to load model: $error';
1417
+ }
1418
+
1419
+ @override
1420
+ String model_unloaded_ollama(String name) {
1421
+ return '$name will be unloaded once the keep-alive time passes';
1422
+ }
1423
+
1424
+ @override
1425
+ String model_unloaded_success(String name) {
1426
+ return '$name unloaded successfully';
1427
+ }
1428
+
1429
+ @override
1430
+ String model_unload_failed(String error) {
1431
+ return 'Failed to unload model: $error';
1432
+ }
1433
+
1434
+ @override
1435
+ String get unload_from_server => 'Unload from server';
1436
+
1437
+ @override
1438
+ String context_chip(String ctx) {
1439
+ return '$ctx ctx';
1440
+ }
1441
+
1442
+ @override
1443
+ String download_notification_title(String modelName) {
1444
+ return 'Downloading $modelName...';
1445
+ }
1446
+
1447
+ @override
1448
+ String get download_complete_notification => 'Download complete!';
1449
+
1450
+ @override
1451
+ String download_complete_body(String modelName) {
1452
+ return '$modelName has been downloaded successfully.';
1453
+ }
1454
+
1455
+ @override
1456
+ String download_failed_notification(String error) {
1457
+ return 'Download failed: $error';
1458
+ }
1459
+
1460
+ @override
1461
+ String download_failed_body(String modelName) {
1462
+ return 'Failed to download $modelName.';
1463
+ }
1464
+
1465
+ @override
1466
+ String get engine_name_system => 'System TTS';
1467
+
1468
+ @override
1469
+ String get engine_tagline_system => 'Built-in device engine';
1470
+
1471
+ @override
1472
+ String get engine_name_kitten => 'Kitten TTS';
1473
+
1474
+ @override
1475
+ String get engine_tagline_kitten => 'High-speed neural TTS';
1476
+
1477
+ @override
1478
+ String get engine_name_sherpa => 'Sherpa ONNX VITS';
1479
+
1480
+ @override
1481
+ String get engine_tagline_sherpa => 'Offline Piper voices';
1482
+
1483
+ @override
1484
+ String get voice_jasper => 'Jasper';
1485
+
1486
+ @override
1487
+ String get voice_bella => 'Bella';
1488
+
1489
+ @override
1490
+ String get voice_bruno => 'Bruno';
1491
+
1492
+ @override
1493
+ String get voice_luna => 'Luna';
1494
+
1495
+ @override
1496
+ String get voice_hugo => 'Hugo';
1497
+
1498
+ @override
1499
+ String get voice_rosie => 'Rosie';
1500
+
1501
+ @override
1502
+ String get voice_leo => 'Leo';
1503
+
1504
+ @override
1505
+ String get voice_kiki => 'Kiki';
1506
+
1507
+ @override
1508
+ String get voice_lessac => 'Lessac (US)';
1509
+
1510
+ @override
1511
+ String get voice_ryan => 'Ryan (US)';
1512
+
1513
+ @override
1514
+ String get model_qwen_3 => 'Qwen 3 0.6B';
1515
+
1516
+ @override
1517
+ String get model_qwen_3_desc =>
1518
+ 'Smallest general-purpose chat model. Fast responses, low memory usage.';
1519
+
1520
+ @override
1521
+ String get model_license_apache => 'Apache-2.0';
1522
+
1523
+ @override
1524
+ String get model_qwen_25 => 'Qwen 2.5 1.5B Instruct';
1525
+
1526
+ @override
1527
+ String get model_qwen_25_desc =>
1528
+ 'Balanced quality and size. Good for general conversation.';
1529
+
1530
+ @override
1531
+ String get model_deepseek => 'DeepSeek R1 Distill Qwen 1.5B';
1532
+
1533
+ @override
1534
+ String get model_deepseek_desc =>
1535
+ 'Reasoning and chain-of-thought model. Best for logical tasks.';
1536
+
1537
+ @override
1538
+ String get model_license_mit => 'MIT';
1539
+
1540
+ @override
1541
+ String get model_gemma => 'Gemma 4 E2B Instruct';
1542
+
1543
+ @override
1544
+ String get model_gemma_desc =>
1545
+ 'Google flagship model. Highest quality, requires more RAM.';
1546
+
1547
+ @override
1548
+ String export_header(String date) {
1549
+ return '*Exported from LocalMind — $date*';
1550
+ }
1551
+
1552
+ @override
1553
+ String get export_role_user => '## 👤 User';
1554
+
1555
+ @override
1556
+ String get export_role_assistant => '## 🤖 Assistant';
1557
+
1558
+ @override
1559
+ String get export_role_system => '## ⚙️ System';
1560
+
1561
+ @override
1562
+ String get export_role_tool => '## 🔧 Tool';
1563
+
1564
+ @override
1565
+ String get export_text_user => '[USER]';
1566
+
1567
+ @override
1568
+ String get export_text_assistant => '[ASSISTANT]';
1569
+
1570
+ @override
1571
+ String get export_text_system => '[SYSTEM]';
1572
+
1573
+ @override
1574
+ String get export_text_tool => '[TOOL]';
1575
+
1576
+ @override
1577
+ String get export_label_user => 'USER';
1578
+
1579
+ @override
1580
+ String get export_label_assistant => 'ASSISTANT';
1581
+
1582
+ @override
1583
+ String get export_label_system => 'SYSTEM';
1584
+
1585
+ @override
1586
+ String get export_label_tool => 'TOOL';
1587
+
1588
+ @override
1589
+ String get select_model_hint => 'Select a model to start chatting';
1590
+
1591
+ @override
1592
+ String get test_notification_title => 'Test notification';
1593
+
1594
+ @override
1595
+ String get test_notification_body =>
1596
+ 'This is a test notification for model download progress.';
1597
+}
lib/l10n/app_localizations_es.dart
new
+1609
@@ -0,0 +1,1609 @@
1
+// ignore: unused_import
2
+import 'package:intl/intl.dart' as intl;
3
+import 'app_localizations.dart';
4
+
5
+// ignore_for_file: type=lint
6
+
7
+/// The translations for Spanish Castilian (`es`).
8
+class AppLocalizationsEs extends AppLocalizations {
9
+ AppLocalizationsEs([String locale = 'es']) : super(locale);
10
+
11
+ @override
12
+ String get app_name => 'LocalMind';
13
+
14
+ @override
15
+ String get app_tagline => 'Su IA. Su dispositivo. Sus normas.';
16
+
17
+ @override
18
+ String get app_version => '1.0.0';
19
+
20
+ @override
21
+ String get cancel => 'Cancelar';
22
+
23
+ @override
24
+ String get confirm => 'Confirmar';
25
+
26
+ @override
27
+ String get delete => 'Eliminar';
28
+
29
+ @override
30
+ String get save => 'Guardar';
31
+
32
+ @override
33
+ String get retry => 'Reintentar';
34
+
35
+ @override
36
+ String get close => 'Cerrar';
37
+
38
+ @override
39
+ String get done => 'Listo';
40
+
41
+ @override
42
+ String get continue_action => 'Continuar';
43
+
44
+ @override
45
+ String get skip => 'Omitir';
46
+
47
+ @override
48
+ String get install => 'Instalar';
49
+
50
+ @override
51
+ String get download => 'Descargar';
52
+
53
+ @override
54
+ String get resume => 'Reanudar';
55
+
56
+ @override
57
+ String get pause => 'Pausar';
58
+
59
+ @override
60
+ String get stop => 'Detener';
61
+
62
+ @override
63
+ String get edit => 'Editar';
64
+
65
+ @override
66
+ String get preview => 'Vista previa';
67
+
68
+ @override
69
+ String get unload => 'Liberar de memoria';
70
+
71
+ @override
72
+ String get load => 'Cargar';
73
+
74
+ @override
75
+ String get rename => 'Renombrar';
76
+
77
+ @override
78
+ String get pin => 'Fijar';
79
+
80
+ @override
81
+ String get unpin => 'Desfijar';
82
+
83
+ @override
84
+ String get share => 'Compartir';
85
+
86
+ @override
87
+ String get copy => 'Copiar';
88
+
89
+ @override
90
+ String get copied => '¡Copiado!';
91
+
92
+ @override
93
+ String get copied_to_clipboard => 'Copiado al portapapeles';
94
+
95
+ @override
96
+ String get select => 'Seleccionar';
97
+
98
+ @override
99
+ String get active => 'Activo';
100
+
101
+ @override
102
+ String get all => 'Todo';
103
+
104
+ @override
105
+ String get none => 'Ninguno';
106
+
107
+ @override
108
+ String get none_selected => 'Ninguno seleccionado';
109
+
110
+ @override
111
+ String get online => 'En línea';
112
+
113
+ @override
114
+ String get offline => 'Sin conexión';
115
+
116
+ @override
117
+ String get error => 'Error';
118
+
119
+ @override
120
+ String get unknown_error => 'Error desconocido';
121
+
122
+ @override
123
+ String get not_now => 'Ahora no';
124
+
125
+ @override
126
+ String get enable => 'Habilitar';
127
+
128
+ @override
129
+ String get proceed_anyway => 'Proceder de todos modos';
130
+
131
+ @override
132
+ String get test_connection => 'Probar conexión';
133
+
134
+ @override
135
+ String get testing => 'Probando...';
136
+
137
+ @override
138
+ String get connection_successful => '¡Conexión exitosa!';
139
+
140
+ @override
141
+ String get connection_failed => 'Conexión fallida. Compruebe sus ajustes.';
142
+
143
+ @override
144
+ String get save_continue => 'Guardar y continuar';
145
+
146
+ @override
147
+ String get save_changes => 'Guardar cambios';
148
+
149
+ @override
150
+ String get finish_setup => 'Finalizar configuración';
151
+
152
+ @override
153
+ String get start_new_chat => 'Comenzar nuevo chat';
154
+
155
+ @override
156
+ String get cannot_undo => 'Esta acción no se puede deshacer.';
157
+
158
+ @override
159
+ String get ram_warning => 'Advertencia de RAM';
160
+
161
+ @override
162
+ String get recommended => 'RECOMENDADO';
163
+
164
+ @override
165
+ String get may_be_large => 'Puede ser demasiado grande para este dispositivo';
166
+
167
+ @override
168
+ String get calculating => 'Calculando...';
169
+
170
+ @override
171
+ String get download_failed => 'Descarga fallida';
172
+
173
+ @override
174
+ String get downloaded => 'Descargado';
175
+
176
+ @override
177
+ String get not_downloaded => 'No descargado';
178
+
179
+ @override
180
+ String get installed => 'Instalado';
181
+
182
+ @override
183
+ String get not_installed => 'No instalado';
184
+
185
+ @override
186
+ String get loading => 'Cargando...';
187
+
188
+ @override
189
+ String get thinking => 'Pensando';
190
+
191
+ @override
192
+ String get processing => 'Procesando...';
193
+
194
+ @override
195
+ String get initializing => 'Inicializando...';
196
+
197
+ @override
198
+ String get ready => 'Listo';
199
+
200
+ @override
201
+ String get preparing_app => 'Preparando aplicación...';
202
+
203
+ @override
204
+ String get initializing_services => 'Inicializando servicios...';
205
+
206
+ @override
207
+ String get configuring_server => 'Configurando servidor...';
208
+
209
+ @override
210
+ String get startup_failed => 'Fallo en el inicio';
211
+
212
+ @override
213
+ String get something_went_wrong => 'Algo salió mal';
214
+
215
+ @override
216
+ String get delete_model_title => 'Eliminar modelo';
217
+
218
+ @override
219
+ String delete_model_body(String name) {
220
+ return '¿Está seguro de que desea eliminar $name?';
221
+ }
222
+
223
+ @override
224
+ String delete_model_body_with_size(String name, String size) {
225
+ return '¿Está seguro de que desea eliminar $name? Esto liberará aproximadamente $size de espacio.\n\nPuede volver a descargar este modelo más tarde si es necesario.';
226
+ }
227
+
228
+ @override
229
+ String get delete_voice_title => 'Eliminar voz';
230
+
231
+ @override
232
+ String delete_voice_body(String name, String size) {
233
+ return '¿Está seguro de que desea eliminar $name? Esto liberará aproximadamente $size de espacio.\n\nPuede volver a descargar esta voz más tarde si es necesario.';
234
+ }
235
+
236
+ @override
237
+ String get delete_server_title => 'Eliminar servidor';
238
+
239
+ @override
240
+ String delete_server_body(String name) {
241
+ return '¿Está seguro de que desea eliminar \"$name\"? Esto no se puede deshacer.';
242
+ }
243
+
244
+ @override
245
+ String get delete_conversation_title => '¿Eliminar conversación?';
246
+
247
+ @override
248
+ String delete_conversation_body(String title) {
249
+ return '¿Está seguro de que desea eliminar \"$title\"? Esto no se puede deshacer.';
250
+ }
251
+
252
+ @override
253
+ String get delete_message_title => '¿Eliminar mensaje?';
254
+
255
+ @override
256
+ String delete_persona_title(String name) {
257
+ return '¿Eliminar \"$name\"?';
258
+ }
259
+
260
+ @override
261
+ String get delete_persona_body => 'Esto no se puede deshacer.';
262
+
263
+ @override
264
+ String get clear_conversation_title => '¿Limpiar conversación?';
265
+
266
+ @override
267
+ String get clear_conversation_body =>
268
+ 'Esto eliminará todos los mensajes de esta conversación.';
269
+
270
+ @override
271
+ String get clear => 'Limpiar';
272
+
273
+ @override
274
+ String label_completed(String label) {
275
+ return '$label completado';
276
+ }
277
+
278
+ @override
279
+ String error_with_message(String error) {
280
+ return 'Error: $error';
281
+ }
282
+
283
+ @override
284
+ String preview_failed(String error) {
285
+ return 'Vista previa fallida: $error';
286
+ }
287
+
288
+ @override
289
+ String loading_model(String modelId) {
290
+ return 'Cargando $modelId...';
291
+ }
292
+
293
+ @override
294
+ String model_loaded(String modelId, String backend) {
295
+ return 'Modelo cargado: $modelId ($backend)';
296
+ }
297
+
298
+ @override
299
+ String get no_model_loaded =>
300
+ 'Ningún modelo cargado. Toque \"Gestionar modelos locales\" para descargar y cargar un modelo.';
301
+
302
+ @override
303
+ String loading_model_error(String error) {
304
+ return 'Error: $error';
305
+ }
306
+
307
+ @override
308
+ String get delete_conversation => '¿Eliminar conversación?';
309
+
310
+ @override
311
+ String get nav_history => 'Historial';
312
+
313
+ @override
314
+ String get nav_servers => 'Servidores';
315
+
316
+ @override
317
+ String get nav_local_models => 'Modelos locales';
318
+
319
+ @override
320
+ String get nav_tts => 'Texto a voz';
321
+
322
+ @override
323
+ String get nav_personas => 'Personajes';
324
+
325
+ @override
326
+ String get nav_settings => 'Ajustes';
327
+
328
+ @override
329
+ String get nav_new_chat => 'Nuevo chat';
330
+
331
+ @override
332
+ String get search_hint => 'Buscar conversaciones...';
333
+
334
+ @override
335
+ String get no_server_selected => 'Ningún servidor seleccionado';
336
+
337
+ @override
338
+ String get switch_server => 'Cambiar de servidor';
339
+
340
+ @override
341
+ String get switch_server_subtitle => 'Elija un servidor al que conectarse';
342
+
343
+ @override
344
+ String get manage_servers => 'Gestionar servidores';
345
+
346
+ @override
347
+ String get open_source => 'Código abierto';
348
+
349
+ @override
350
+ String get open_source_desc =>
351
+ 'LocalMind es de código abierto. Siga nuestro progreso o contribuya en GitHub.';
352
+
353
+ @override
354
+ String get star_on_github => 'Dar estrella en GitHub';
355
+
356
+ @override
357
+ String get settings_title => 'Ajustes';
358
+
359
+ @override
360
+ String get settings_appearance => 'Apariencia';
361
+
362
+ @override
363
+ String get settings_language => 'Idioma';
364
+
365
+ @override
366
+ String get language_system_default => 'Predeterminado del sistema';
367
+
368
+ @override
369
+ String get settings_tts => 'Texto a voz';
370
+
371
+ @override
372
+ String get settings_behavior => 'Comportamiento';
373
+
374
+ @override
375
+ String get settings_on_device => 'Inferencia local';
376
+
377
+ @override
378
+ String get settings_default_server => 'Servidor predeterminado';
379
+
380
+ @override
381
+ String get settings_default_persona => 'Personaje predeterminado';
382
+
383
+ @override
384
+ String get settings_privacy => 'Privacidad';
385
+
386
+ @override
387
+ String get settings_data_management => 'Gestión de datos';
388
+
389
+ @override
390
+ String get settings_about => 'Acerca de';
391
+
392
+ @override
393
+ String get theme => 'Tema';
394
+
395
+ @override
396
+ String get theme_system => 'Sistema';
397
+
398
+ @override
399
+ String get theme_light => 'Claro';
400
+
401
+ @override
402
+ String get theme_dark => 'Oscuro';
403
+
404
+ @override
405
+ String get theme_claude => 'Claude';
406
+
407
+ @override
408
+ String get font_size => 'Tamaño de fuente';
409
+
410
+ @override
411
+ String get font_size_desc => 'Ajustar el tamaño del texto en el chat.';
412
+
413
+ @override
414
+ String get font_preview =>
415
+ 'El rápido zorro marrón salta sobre el perro perezoso.';
416
+
417
+ @override
418
+ String get code_theme_dark => 'Tema de código (Oscuro)';
419
+
420
+ @override
421
+ String get code_theme_light => 'Tema de código (Claro)';
422
+
423
+ @override
424
+ String get code_theme_desc =>
425
+ 'Elija el tema de resaltado de sintaxis para los bloques de código.';
426
+
427
+ @override
428
+ String get tts_engine => 'Motor TTS';
429
+
430
+ @override
431
+ String get tts_engine_system => 'TTS del sistema';
432
+
433
+ @override
434
+ String get tts_engine_kitten => 'Kitten TTS';
435
+
436
+ @override
437
+ String get voice => 'Voz';
438
+
439
+ @override
440
+ String get voice_female => 'Femenino';
441
+
442
+ @override
443
+ String get voice_male => 'Masculino';
444
+
445
+ @override
446
+ String get voice_other => 'Otro';
447
+
448
+ @override
449
+ String get tts_speed => 'Velocidad de TTS';
450
+
451
+ @override
452
+ String get tts_speed_desc => 'Ajustar la velocidad de reproducción.';
453
+
454
+ @override
455
+ String get manage_tts_models => 'Gestionar modelos TTS';
456
+
457
+ @override
458
+ String get manage_on_device_models => 'Gestionar modelos locales';
459
+
460
+ @override
461
+ String get streaming_responses => 'Respuestas en tiempo real (streaming)';
462
+
463
+ @override
464
+ String get auto_generate_titles => 'Autogenerar títulos';
465
+
466
+ @override
467
+ String get send_on_enter => 'Enviar al presionar Enter';
468
+
469
+ @override
470
+ String get show_system_messages => 'Mostrar mensajes del sistema';
471
+
472
+ @override
473
+ String get haptic_feedback => 'Comentarios hápticos';
474
+
475
+ @override
476
+ String get enable_mcp => 'Habilitar MCP';
477
+
478
+ @override
479
+ String get new_chat_mcp_default => 'Nuevo chat MCP predeterminado';
480
+
481
+ @override
482
+ String get show_data_indicator => 'Mostrar indicador de datos';
483
+
484
+ @override
485
+ String get privacy_info => '\"LocalMind nunca ve sus datos\"';
486
+
487
+ @override
488
+ String get delete_all_conversations => 'Eliminar todas las conversaciones';
489
+
490
+ @override
491
+ String get reset_settings_defaults =>
492
+ 'Restablecer ajustes a los predeterminados';
493
+
494
+ @override
495
+ String get chat_title => 'LocalMind';
496
+
497
+ @override
498
+ String get chat_parameters_tooltip => 'Parámetros del chat';
499
+
500
+ @override
501
+ String get change_persona => 'Cambiar personaje';
502
+
503
+ @override
504
+ String get set_persona => 'Establecer personaje';
505
+
506
+ @override
507
+ String get remove_persona => 'Eliminar personaje';
508
+
509
+ @override
510
+ String get clear_conversation => 'Limpiar conversación';
511
+
512
+ @override
513
+ String get connection_error => 'Error de conexión. Compruebe su servidor.';
514
+
515
+ @override
516
+ String get disconnected => 'Desconectado del servidor.';
517
+
518
+ @override
519
+ String get configure => 'Configurar';
520
+
521
+ @override
522
+ String get select_model => 'Seleccionar modelo';
523
+
524
+ @override
525
+ String get select_persona => 'Seleccionar personaje';
526
+
527
+ @override
528
+ String get start_conversation => 'Comenzar una conversación';
529
+
530
+ @override
531
+ String get recent_chats => 'Chats recientes';
532
+
533
+ @override
534
+ String get see_all => 'Ver todo';
535
+
536
+ @override
537
+ String get quick_write => 'Ayúdame a escribir una función';
538
+
539
+ @override
540
+ String get quick_explain => 'Explica este código';
541
+
542
+ @override
543
+ String get quick_debug => 'Depura esto por mí';
544
+
545
+ @override
546
+ String get quick_async => '¿Cómo uso async/await?';
547
+
548
+ @override
549
+ String get history_missing_title => 'Historial faltante';
550
+
551
+ @override
552
+ String get history_missing_desc =>
553
+ 'O bien los mensajes de este chat fueron eliminados o el registro de historial está dañado.';
554
+
555
+ @override
556
+ String get technical_details => 'Detalles técnicos';
557
+
558
+ @override
559
+ String get last_error => 'Último error:';
560
+
561
+ @override
562
+ String get copy_info => 'Copiar información';
563
+
564
+ @override
565
+ String get conversation_id => 'ID de conversación';
566
+
567
+ @override
568
+ String get created_at => 'Creado el';
569
+
570
+ @override
571
+ String get expected_messages => 'Mensajes esperados';
572
+
573
+ @override
574
+ String get debug_dialog_desc =>
575
+ 'Información de diagnóstico para ayudar a identificar problemas de sincronización.';
576
+
577
+ @override
578
+ String get chat_input_hint => 'Pregunte cualquier cosa';
579
+
580
+ @override
581
+ String get send_message_tooltip => 'Enviar mensaje';
582
+
583
+ @override
584
+ String get stop_generation_tooltip => 'Detener generación';
585
+
586
+ @override
587
+ String get attach_images_tooltip => 'Adjuntar imágenes';
588
+
589
+ @override
590
+ String tool_label(String toolCallId) {
591
+ return 'Herramienta: $toolCallId';
592
+ }
593
+
594
+ @override
595
+ String get tool_unknown => 'Herramienta: Desconocida';
596
+
597
+ @override
598
+ String get message_options => 'Opciones de mensaje';
599
+
600
+ @override
601
+ String get copy_markdown => 'Copiar como Markdown';
602
+
603
+ @override
604
+ String get copied_markdown => 'Copiado como Markdown';
605
+
606
+ @override
607
+ String get read_aloud => 'Leer en voz alta';
608
+
609
+ @override
610
+ String get stop_reading => 'Detener lectura';
611
+
612
+ @override
613
+ String get more => 'Más';
614
+
615
+ @override
616
+ String character_count(int length) {
617
+ return '$length caracteres';
618
+ }
619
+
620
+ @override
621
+ String get edit_message => 'Editar mensaje';
622
+
623
+ @override
624
+ String get edit_message_desc =>
625
+ 'Al guardar se eliminará la respuesta del asistente que aparece a continuación y se volverá a generar.';
626
+
627
+ @override
628
+ String get save_regenerate => 'Guardar y regenerar';
629
+
630
+ @override
631
+ String get chat_settings_title => 'Ajustes de chat';
632
+
633
+ @override
634
+ String get reset_defaults => 'Restablecer valores predeterminados';
635
+
636
+ @override
637
+ String get parameters_tab => 'Parámetros';
638
+
639
+ @override
640
+ String get mcp_tab => 'MCP';
641
+
642
+ @override
643
+ String get temperature => 'Temperatura';
644
+
645
+ @override
646
+ String get temperature_desc =>
647
+ 'Controla la aleatoriedad: Mayor = Creativo, Menor = Enfocado';
648
+
649
+ @override
650
+ String get top_p => 'Top P';
651
+
652
+ @override
653
+ String get top_p_desc => 'Umbral de muestreo de núcleo';
654
+
655
+ @override
656
+ String get max_tokens => 'Máximo de tokens';
657
+
658
+ @override
659
+ String get max_tokens_desc => 'Límite de respuesta';
660
+
661
+ @override
662
+ String get context_length => 'Longitud del contexto';
663
+
664
+ @override
665
+ String get context_length_desc => 'Ventana de historial';
666
+
667
+ @override
668
+ String get mcp_disabled_warning =>
669
+ 'MCP está deshabilitado globalmente. Habilítelo en Ajustes para usar estas funciones.';
670
+
671
+ @override
672
+ String get mcp_enable_chat => 'Habilitar MCP para este chat';
673
+
674
+ @override
675
+ String get auto_execute_tools => 'Ejecutar herramientas automáticamente';
676
+
677
+ @override
678
+ String get add_ephemeral_mcp => 'Añadir servidor MCP efímero';
679
+
680
+ @override
681
+ String get mcp_label_placeholder => 'Etiqueta';
682
+
683
+ @override
684
+ String get mcp_url_placeholder => 'URL (https://...)';
685
+
686
+ @override
687
+ String get active_integrations => 'Integraciones activas';
688
+
689
+ @override
690
+ String get enable_notifications => 'Habilitar notificaciones';
691
+
692
+ @override
693
+ String get enable_notifications_desc =>
694
+ 'Reciba notificaciones cuando los modelos terminen de descargarse.';
695
+
696
+ @override
697
+ String get chat_history_title => 'Historial de chat';
698
+
699
+ @override
700
+ String get conversation_just_now => 'Hace un momento';
701
+
702
+ @override
703
+ String conversation_minutes_ago(int minutes) {
704
+ return 'hace ${minutes}m';
705
+ }
706
+
707
+ @override
708
+ String conversation_hours_ago(int hours) {
709
+ return 'hace ${hours}h';
710
+ }
711
+
712
+ @override
713
+ String get conversation_yesterday => 'Ayer';
714
+
715
+ @override
716
+ String conversation_days_ago(int days) {
717
+ return 'hace ${days}d';
718
+ }
719
+
720
+ @override
721
+ String conversation_date(int month, int day, int year) {
722
+ return '$month/$day/$year';
723
+ }
724
+
725
+ @override
726
+ String get options_tooltip => 'Opciones';
727
+
728
+ @override
729
+ String get no_results_found => 'No se encontraron resultados';
730
+
731
+ @override
732
+ String get no_conversations_yet => 'Aún sin conversaciones';
733
+
734
+ @override
735
+ String get try_different_search =>
736
+ 'Pruebe con un término de búsqueda diferente';
737
+
738
+ @override
739
+ String get start_new_conversation => 'Comenzar una nueva conversación';
740
+
741
+ @override
742
+ String get rename_conversation => 'Renombrar conversación';
743
+
744
+ @override
745
+ String get enter_new_title => 'Introduzca un nuevo título';
746
+
747
+ @override
748
+ String get pinned_section => 'FIJADOS';
749
+
750
+ @override
751
+ String get today_section => 'HOY';
752
+
753
+ @override
754
+ String get yesterday_section => 'AYER';
755
+
756
+ @override
757
+ String get previous_7_days => 'ÚLTIMOS 7 DÍAS';
758
+
759
+ @override
760
+ String get previous_30_days => 'ÚLTIMOS 30 DÍAS';
761
+
762
+ @override
763
+ String get older_section => 'MÁS ANTIGUOS';
764
+
765
+ @override
766
+ String get onboarding_localmind => 'LOCALMIND';
767
+
768
+ @override
769
+ String get onboarding_connect_server => 'Conecte su\nservidor';
770
+
771
+ @override
772
+ String get onboarding_connect_desc =>
773
+ 'Conéctese a LM Studio, Ollama o\nOpenRouter para comenzar su experiencia\nprivada de IA.';
774
+
775
+ @override
776
+ String get server_type_on_device => 'Local';
777
+
778
+ @override
779
+ String get server_type_on_device_sub => 'NO SE REQUIERE SERVIDOR';
780
+
781
+ @override
782
+ String get server_type_lm_studio => 'LM Studio';
783
+
784
+ @override
785
+ String get server_type_lm_studio_sub => 'API LOCAL';
786
+
787
+ @override
788
+ String get server_type_ollama => 'Ollama';
789
+
790
+ @override
791
+ String get server_type_ollama_sub => 'MOTOR CLI';
792
+
793
+ @override
794
+ String get server_type_openrouter => 'OpenRouter';
795
+
796
+ @override
797
+ String get server_type_openrouter_sub => 'NUBE UNIFICADA';
798
+
799
+ @override
800
+ String get ready_continue => 'LISTO PARA CONTINUAR';
801
+
802
+ @override
803
+ String get waiting_selection => 'ESPERANDO SELECCIÓN';
804
+
805
+ @override
806
+ String get setup_connection => 'Configurar conexión';
807
+
808
+ @override
809
+ String setup_connection_desc(String server) {
810
+ return 'Configure su servidor $server para comenzar a chatear.';
811
+ }
812
+
813
+ @override
814
+ String get server_name => 'Nombre de servidor';
815
+
816
+ @override
817
+ String get name_required => 'Nombre requerido';
818
+
819
+ @override
820
+ String get name_max_50 => 'Máx. 50 caracteres';
821
+
822
+ @override
823
+ String get host_label => 'Host / Dirección IP';
824
+
825
+ @override
826
+ String get host_required => 'Host requerido';
827
+
828
+ @override
829
+ String get port_label => 'Puerto';
830
+
831
+ @override
832
+ String get port_required => 'Puerto requerido';
833
+
834
+ @override
835
+ String get port_invalid => 'Debe ser un número';
836
+
837
+ @override
838
+ String get port_range => 'Introduzca un puerto válido (1-65535)';
839
+
840
+ @override
841
+ String get api_key_required => 'Clave API *';
842
+
843
+ @override
844
+ String get api_key_optional => 'Clave API (Opcional)';
845
+
846
+ @override
847
+ String get api_key_required_openrouter =>
848
+ 'Clave API requerida para OpenRouter';
849
+
850
+ @override
851
+ String get api_key_format =>
852
+ 'Las claves de API de OpenRouter comienzan con sk-';
853
+
854
+ @override
855
+ String get my_server_hint => 'Mi servidor';
856
+
857
+ @override
858
+ String get name_length_validation =>
859
+ 'El nombre debe tener 50 caracteres o menos';
860
+
861
+ @override
862
+ String get host_valid => 'Introduzca un nombre de host o dirección IP válida';
863
+
864
+ @override
865
+ String get api_key_hint_openrouter => 'sk-...';
866
+
867
+ @override
868
+ String get api_key_hint_generic => 'Para servidores autenticados';
869
+
870
+ @override
871
+ String get update_server => 'Actualizar servidor';
872
+
873
+ @override
874
+ String get save_server => 'Guardar servidor';
875
+
876
+ @override
877
+ String get server_updated => 'Servidor actualizado';
878
+
879
+ @override
880
+ String get server_added => 'Servidor añadido';
881
+
882
+ @override
883
+ String get download_model_title => 'Descargar un modelo';
884
+
885
+ @override
886
+ String get download_model_desc =>
887
+ 'Elija un modelo para descargar.\nSe ejecutará localmente en su dispositivo.';
888
+
889
+ @override
890
+ String get on_device_android_only =>
891
+ 'La inferencia local está disponible actualmente solo en Android.';
892
+
893
+ @override
894
+ String get total_ram => 'RAM total';
895
+
896
+ @override
897
+ String get available => 'Disponible';
898
+
899
+ @override
900
+ String ram_min_required(String fileSize) {
901
+ return '$fileSize GB de RAM mín.';
902
+ }
903
+
904
+ @override
905
+ String download_progress(String percent, String speed) {
906
+ return '$percent% • $speed';
907
+ }
908
+
909
+ @override
910
+ String eta_label(String eta) {
911
+ return 'Tiempo restante: $eta';
912
+ }
913
+
914
+ @override
915
+ String paused_progress(String percent) {
916
+ return 'Pausado - $percent%';
917
+ }
918
+
919
+ @override
920
+ String ram_warning_body_download(String ram, String totalMemory) {
921
+ return 'Este modelo requiere al menos $ram GB de RAM, pero su dispositivo tiene $totalMemory. Es posible que no se ejecute correctamente o que cause el cierre de la aplicación.';
922
+ }
923
+
924
+ @override
925
+ String ram_warning_body_load(String availableRAM, String ram) {
926
+ return 'Su dispositivo tiene $availableRAM de RAM disponible, pero este modelo recomienda al menos $ram GB. Cargarlo podría fallar o causar inestabilidad.';
927
+ }
928
+
929
+ @override
930
+ String get choose_theme => 'Elegir tema';
931
+
932
+ @override
933
+ String get choose_theme_desc =>
934
+ 'Personalice la apariencia de la aplicación. Siempre puede cambiar esto más tarde en los ajustes.';
935
+
936
+ @override
937
+ String get theme_card_system => 'Sistema';
938
+
939
+ @override
940
+ String get theme_card_system_sub =>
941
+ 'Coincide con los ajustes de su dispositivo';
942
+
943
+ @override
944
+ String get theme_card_light => 'Claro';
945
+
946
+ @override
947
+ String get theme_card_light_sub => 'Limpio y brillante';
948
+
949
+ @override
950
+ String get theme_card_dark => 'Oscuro';
951
+
952
+ @override
953
+ String get theme_card_dark_sub => 'Fácil para los ojos';
954
+
955
+ @override
956
+ String get theme_card_claude => 'Claude';
957
+
958
+ @override
959
+ String get theme_card_claude_sub => 'Un tema cálido con tonos durazno';
960
+
961
+ @override
962
+ String get stay_updated => 'Manténgase al día';
963
+
964
+ @override
965
+ String get stay_updated_desc =>
966
+ 'Reciba notificaciones cuando sus modelos de IA terminen de descargarse o cuando se completen tareas largas.';
967
+
968
+ @override
969
+ String get notification_benefit_downloads =>
970
+ 'Progreso de descarga del modelo';
971
+
972
+ @override
973
+ String get notification_benefit_completions => 'Completados de generación';
974
+
975
+ @override
976
+ String get notification_benefit_background =>
977
+ 'Estado de las tareas en segundo plano';
978
+
979
+ @override
980
+ String get allow_notifications => 'Permitir notificaciones';
981
+
982
+ @override
983
+ String get servers_title => 'Servidores';
984
+
985
+ @override
986
+ String get no_servers_yet => 'Aún sin servidores';
987
+
988
+ @override
989
+ String get no_servers_desc =>
990
+ 'Añada su primer servidor para comenzar a chatear con modelos de IA.';
991
+
992
+ @override
993
+ String get add_server => 'Añadir servidor';
994
+
995
+ @override
996
+ String switched_to_server(String name) {
997
+ return 'Cambiado a $name';
998
+ }
999
+
1000
+ @override
1001
+ String get edit_server => 'Editar servidor';
1002
+
1003
+ @override
1004
+ String get add_server_title => 'Añadir servidor';
1005
+
1006
+ @override
1007
+ String get server_type_label => 'Tipo de servidor';
1008
+
1009
+ @override
1010
+ String get server_icon_label => 'Icono de servidor';
1011
+
1012
+ @override
1013
+ String get default_icon => 'Icono predeterminado';
1014
+
1015
+ @override
1016
+ String get server_type_lm_studio_display => 'LM Studio';
1017
+
1018
+ @override
1019
+ String get server_type_openai_display => 'Compatible con OpenAI';
1020
+
1021
+ @override
1022
+ String get server_type_ollama_display => 'Ollama';
1023
+
1024
+ @override
1025
+ String get server_type_openrouter_display => 'OpenRouter';
1026
+
1027
+ @override
1028
+ String get server_type_on_device_display => 'Local';
1029
+
1030
+ @override
1031
+ String get server_address_openrouter => 'openrouter.ai';
1032
+
1033
+ @override
1034
+ String get server_address_on_device => 'Inferencia local';
1035
+
1036
+ @override
1037
+ String server_address_format(String host, String port) {
1038
+ return '$host:$port';
1039
+ }
1040
+
1041
+ @override
1042
+ String get default_badge => 'Predeterminado';
1043
+
1044
+ @override
1045
+ String get set_as_default => 'Establecer como predeterminado';
1046
+
1047
+ @override
1048
+ String get select_icon => 'Seleccionar icono';
1049
+
1050
+ @override
1051
+ String get select_icon_desc => 'Elija un icono para su servidor';
1052
+
1053
+ @override
1054
+ String get search_icons_hint => 'Buscar iconos...';
1055
+
1056
+ @override
1057
+ String get server_icon_stack => 'Pila de servidores';
1058
+
1059
+ @override
1060
+ String get server_icon_stack2 => 'Pila de servidores 02';
1061
+
1062
+ @override
1063
+ String get server_icon_stack3 => 'Pila de servidores 03';
1064
+
1065
+ @override
1066
+ String get server_icon_cloud => 'Nube';
1067
+
1068
+ @override
1069
+ String get server_icon_cloud_server => 'Servidor en la nube';
1070
+
1071
+ @override
1072
+ String get server_icon_mcp => 'Servidor MCP';
1073
+
1074
+ @override
1075
+ String get server_icon_database => 'Base de datos';
1076
+
1077
+ @override
1078
+ String get server_icon_database1 => 'Base de datos 01';
1079
+
1080
+ @override
1081
+ String get server_icon_database2 => 'Base de datos 02';
1082
+
1083
+ @override
1084
+ String get server_icon_cpu => 'CPU';
1085
+
1086
+ @override
1087
+ String get server_icon_chip => 'Chip';
1088
+
1089
+ @override
1090
+ String get server_icon_chip2 => 'Chip 02';
1091
+
1092
+ @override
1093
+ String get server_icon_computer => 'Computadora';
1094
+
1095
+ @override
1096
+ String get server_icon_laptop => 'Laptop';
1097
+
1098
+ @override
1099
+ String get server_icon_terminal => 'Terminal de computadora';
1100
+
1101
+ @override
1102
+ String get server_icon_code => 'Código';
1103
+
1104
+ @override
1105
+ String get server_icon_ai_brain => 'Cerebro de IA';
1106
+
1107
+ @override
1108
+ String get server_icon_ai_brain2 => 'Cerebro de IA 02';
1109
+
1110
+ @override
1111
+ String get server_icon_ai_cloud => 'Nube de IA';
1112
+
1113
+ @override
1114
+ String get server_icon_ai_network => 'Red de IA';
1115
+
1116
+ @override
1117
+ String get server_icon_ai_chat => 'Chat de IA';
1118
+
1119
+ @override
1120
+ String get server_icon_cellular => 'Red celular';
1121
+
1122
+ @override
1123
+ String get server_icon_plug1 => 'Enchufe 01';
1124
+
1125
+ @override
1126
+ String get server_icon_plug2 => 'Enchufe 02';
1127
+
1128
+ @override
1129
+ String get server_icon_bot => 'Bot';
1130
+
1131
+ @override
1132
+ String get server_icon_bot2 => 'Bot 02';
1133
+
1134
+ @override
1135
+ String get server_icon_robotic => 'Robótico';
1136
+
1137
+ @override
1138
+ String get server_icon_rocket => 'Cohete';
1139
+
1140
+ @override
1141
+ String get server_icon_star => 'Estrella';
1142
+
1143
+ @override
1144
+ String get server_icon_settings1 => 'Ajustes 01';
1145
+
1146
+ @override
1147
+ String get server_icon_settings2 => 'Ajustes 02';
1148
+
1149
+ @override
1150
+ String get server_icon_home1 => 'Inicio 01';
1151
+
1152
+ @override
1153
+ String get server_icon_home2 => 'Inicio 02';
1154
+
1155
+ @override
1156
+ String get server_icon_folder1 => 'Carpeta 01';
1157
+
1158
+ @override
1159
+ String get server_icon_folder2 => 'Carpeta 02';
1160
+
1161
+ @override
1162
+ String get server_icon_file1 => 'Archivo 01';
1163
+
1164
+ @override
1165
+ String get server_icon_lock => 'Bloqueo';
1166
+
1167
+ @override
1168
+ String get server_icon_key => 'Clave 01';
1169
+
1170
+ @override
1171
+ String get server_icon_link => 'Enlace 01';
1172
+
1173
+ @override
1174
+ String get server_icon_globe => 'Globo';
1175
+
1176
+ @override
1177
+ String get server_icon_api => 'API';
1178
+
1179
+ @override
1180
+ String get server_icon_arrow_right => 'Flecha derecha 01';
1181
+
1182
+ @override
1183
+ String get server_icon_check => 'Círculo de verificación';
1184
+
1185
+ @override
1186
+ String get server_icon_alert => 'Círculo de alerta';
1187
+
1188
+ @override
1189
+ String get server_icon_info => 'Círculo de información';
1190
+
1191
+ @override
1192
+ String get server_icon_zap => 'Zap';
1193
+
1194
+ @override
1195
+ String get server_icon_cloud_upload => 'Subida a la nube';
1196
+
1197
+ @override
1198
+ String get server_icon_cloud_download => 'Descarga en la nube';
1199
+
1200
+ @override
1201
+ String get server_icon_refresh => 'Actualizar';
1202
+
1203
+ @override
1204
+ String get server_icon_hard_drive => 'Disco duro';
1205
+
1206
+ @override
1207
+ String get server_icon_drive => 'Unidad';
1208
+
1209
+ @override
1210
+ String get personas_title => 'Personajes';
1211
+
1212
+ @override
1213
+ String get persona_category_general => 'General';
1214
+
1215
+ @override
1216
+ String get persona_category_coding => 'Programación';
1217
+
1218
+ @override
1219
+ String get persona_category_education => 'Educación';
1220
+
1221
+ @override
1222
+ String get persona_category_creative => 'Creativo';
1223
+
1224
+ @override
1225
+ String get persona_builtin_section => 'INTEGRADO';
1226
+
1227
+ @override
1228
+ String get persona_my_section => 'MIS PERSONAJES';
1229
+
1230
+ @override
1231
+ String get clone_edit => 'Clonar y editar';
1232
+
1233
+ @override
1234
+ String get builtin_badge => 'Integrado';
1235
+
1236
+ @override
1237
+ String get no_personas_found => 'No se encontraron personajes';
1238
+
1239
+ @override
1240
+ String get no_personas_desc =>
1241
+ 'Cree su primer personaje para personalizar el comportamiento de la IA.';
1242
+
1243
+ @override
1244
+ String get edit_persona => 'Editar personaje';
1245
+
1246
+ @override
1247
+ String get create_persona => 'Crear personaje';
1248
+
1249
+ @override
1250
+ String get create_persona_button => 'Crear';
1251
+
1252
+ @override
1253
+ String get emoji_label => 'Emoji';
1254
+
1255
+ @override
1256
+ String get name_label => 'Nombre';
1257
+
1258
+ @override
1259
+ String get my_persona_hint => 'Mi personaje';
1260
+
1261
+ @override
1262
+ String get category_label => 'Categoría';
1263
+
1264
+ @override
1265
+ String get description_optional => 'Descripción (opcional)';
1266
+
1267
+ @override
1268
+ String get description_hint => 'Lo que hace este personaje...';
1269
+
1270
+ @override
1271
+ String get system_prompt => 'Instrucción del sistema';
1272
+
1273
+ @override
1274
+ String character_count_max(int currentLen) {
1275
+ return '$currentLen/4000';
1276
+ }
1277
+
1278
+ @override
1279
+ String get no_prompt_placeholder => 'Aún sin indicaciones...';
1280
+
1281
+ @override
1282
+ String get prompt_hint => 'Usted es un asistente servicial...';
1283
+
1284
+ @override
1285
+ String get prompt_required => 'La instrucción del sistema es requerida';
1286
+
1287
+ @override
1288
+ String get prompt_max_chars => 'Máx. 4000 caracteres';
1289
+
1290
+ @override
1291
+ String get advanced_settings => 'Ajustes avanzados';
1292
+
1293
+ @override
1294
+ String get temperature_label => 'Temperatura (0.0-2.0)';
1295
+
1296
+ @override
1297
+ String get top_p_label => 'Top P (0.0-1.0)';
1298
+
1299
+ @override
1300
+ String get temp_hint => '0.7';
1301
+
1302
+ @override
1303
+ String get top_p_hint => '0.9';
1304
+
1305
+ @override
1306
+ String get range_0_2 => '0.0-2.0';
1307
+
1308
+ @override
1309
+ String get range_0_1 => '0.0-1.0';
1310
+
1311
+ @override
1312
+ String get persona_updated => 'Personaje actualizado';
1313
+
1314
+ @override
1315
+ String get persona_created => 'Personaje creado';
1316
+
1317
+ @override
1318
+ String get tts_models_title => 'Modelos de texto a voz';
1319
+
1320
+ @override
1321
+ String get always_available => 'Siempre disponible';
1322
+
1323
+ @override
1324
+ String get tts_system_desc =>
1325
+ 'Utiliza el motor de texto a voz integrado de su dispositivo.\nNo requiere descargas. La selección de voz utiliza los ajustes del sistema de su dispositivo.';
1326
+
1327
+ @override
1328
+ String get downloading_status => 'Descargando...';
1329
+
1330
+ @override
1331
+ String tts_kitten_desc(String size) {
1332
+ return 'TTS neural ultrarrápido con 8 voces expresivas.\nRequiere descarga de $size.';
1333
+ }
1334
+
1335
+ @override
1336
+ String tts_piper_desc(String size) {
1337
+ return 'Voces Piper sin conexión rápidas con 2 voces expresivas.\nRequiere una descarga de $size por voz.';
1338
+ }
1339
+
1340
+ @override
1341
+ String engine_spec(String sizeMb, String ramMb, int voiceCount) {
1342
+ return '$sizeMb MB · $ramMb MB de RAM · $voiceCount voces';
1343
+ }
1344
+
1345
+ @override
1346
+ String get on_device_models_title => 'Modelos locales';
1347
+
1348
+ @override
1349
+ String get available_models => 'Modelos disponibles';
1350
+
1351
+ @override
1352
+ String get device_memory => 'Memoria del dispositivo';
1353
+
1354
+ @override
1355
+ String get ram_usage => 'Uso de RAM';
1356
+
1357
+ @override
1358
+ String get memory_healthy => 'Saludable';
1359
+
1360
+ @override
1361
+ String get memory_critical => 'Crítico';
1362
+
1363
+ @override
1364
+ String get memory_low => 'Bajo';
1365
+
1366
+ @override
1367
+ String ram_used(String percent) {
1368
+ return '$percent% usado';
1369
+ }
1370
+
1371
+ @override
1372
+ String get available_ram => 'RAM disponible';
1373
+
1374
+ @override
1375
+ String get total_capacity => 'Capacidad total';
1376
+
1377
+ @override
1378
+ String get loaded_status => 'Cargado';
1379
+
1380
+ @override
1381
+ String get inference_backend => 'Motor de inferencia';
1382
+
1383
+ @override
1384
+ String get backend_ios_notice =>
1385
+ 'Solo el motor de CPU está disponible en iOS.';
1386
+
1387
+ @override
1388
+ String get backend_cpu_desc =>
1389
+ 'Funciona en todos los dispositivos. Más compatible.';
1390
+
1391
+ @override
1392
+ String get backend_gpu_desc =>
1393
+ 'Aceleración OpenCL. Más rápido en dispositivos compatibles.';
1394
+
1395
+ @override
1396
+ String get backend_npu_desc =>
1397
+ 'NPU del fabricante (Qualcomm/MediaTek). Inferencia más rápida.';
1398
+
1399
+ @override
1400
+ String get select_model_title => 'Seleccionar modelo';
1401
+
1402
+ @override
1403
+ String get refresh_models => 'Actualizar modelos';
1404
+
1405
+ @override
1406
+ String get search_models_hint => 'Buscar modelos...';
1407
+
1408
+ @override
1409
+ String get no_server_connected => 'Ningún servidor conectado';
1410
+
1411
+ @override
1412
+ String get add_server_first =>
1413
+ 'Añada un servidor primero para ver los modelos disponibles.';
1414
+
1415
+ @override
1416
+ String get failed_load_models => 'No se pudieron cargar los modelos';
1417
+
1418
+ @override
1419
+ String get no_models_available => 'No hay modelos disponibles';
1420
+
1421
+ @override
1422
+ String no_models_match(String searchQuery) {
1423
+ return 'Ningún modelo coincide con \"$searchQuery\"';
1424
+ }
1425
+
1426
+ @override
1427
+ String model_load_failed(String error) {
1428
+ return 'No se pudo cargar el modelo: $error';
1429
+ }
1430
+
1431
+ @override
1432
+ String model_unloaded_ollama(String name) {
1433
+ return '$name se liberará de la memoria una vez que pase el tiempo de espera activo';
1434
+ }
1435
+
1436
+ @override
1437
+ String model_unloaded_success(String name) {
1438
+ return '$name liberado de memoria correctamente';
1439
+ }
1440
+
1441
+ @override
1442
+ String model_unload_failed(String error) {
1443
+ return 'No se pudo descargar el modelo de la memoria: $error';
1444
+ }
1445
+
1446
+ @override
1447
+ String get unload_from_server => 'Liberar del servidor';
1448
+
1449
+ @override
1450
+ String context_chip(String ctx) {
1451
+ return '$ctx ctx';
1452
+ }
1453
+
1454
+ @override
1455
+ String download_notification_title(String modelName) {
1456
+ return 'Descargando $modelName...';
1457
+ }
1458
+
1459
+ @override
1460
+ String get download_complete_notification => '¡Descarga completa!';
1461
+
1462
+ @override
1463
+ String download_complete_body(String modelName) {
1464
+ return '$modelName se ha descargado correctamente.';
1465
+ }
1466
+
1467
+ @override
1468
+ String download_failed_notification(String error) {
1469
+ return 'Descarga fallida: $error';
1470
+ }
1471
+
1472
+ @override
1473
+ String download_failed_body(String modelName) {
1474
+ return 'No se pudo descargar $modelName.';
1475
+ }
1476
+
1477
+ @override
1478
+ String get engine_name_system => 'TTS del sistema';
1479
+
1480
+ @override
1481
+ String get engine_tagline_system => 'Motor integrado en el dispositivo';
1482
+
1483
+ @override
1484
+ String get engine_name_kitten => 'Kitten TTS';
1485
+
1486
+ @override
1487
+ String get engine_tagline_kitten => 'TTS neural de alta velocidad';
1488
+
1489
+ @override
1490
+ String get engine_name_sherpa => 'Sherpa ONNX VITS';
1491
+
1492
+ @override
1493
+ String get engine_tagline_sherpa => 'Voces Piper sin conexión';
1494
+
1495
+ @override
1496
+ String get voice_jasper => 'Jasper';
1497
+
1498
+ @override
1499
+ String get voice_bella => 'Bella';
1500
+
1501
+ @override
1502
+ String get voice_bruno => 'Bruno';
1503
+
1504
+ @override
1505
+ String get voice_luna => 'Luna';
1506
+
1507
+ @override
1508
+ String get voice_hugo => 'Hugo';
1509
+
1510
+ @override
1511
+ String get voice_rosie => 'Rosie';
1512
+
1513
+ @override
1514
+ String get voice_leo => 'Leo';
1515
+
1516
+ @override
1517
+ String get voice_kiki => 'Kiki';
1518
+
1519
+ @override
1520
+ String get voice_lessac => 'Lessac (US)';
1521
+
1522
+ @override
1523
+ String get voice_ryan => 'Ryan (US)';
1524
+
1525
+ @override
1526
+ String get model_qwen_3 => 'Qwen 3 0.6B';
1527
+
1528
+ @override
1529
+ String get model_qwen_3_desc =>
1530
+ 'Modelo de chat de propósito general más pequeño. Respuestas rápidas, bajo uso de memoria.';
1531
+
1532
+ @override
1533
+ String get model_license_apache => 'Apache-2.0';
1534
+
1535
+ @override
1536
+ String get model_qwen_25 => 'Qwen 2.5 1.5B Instruct';
1537
+
1538
+ @override
1539
+ String get model_qwen_25_desc =>
1540
+ 'Calidad y tamaño equilibrados. Bueno para conversación general.';
1541
+
1542
+ @override
1543
+ String get model_deepseek => 'DeepSeek R1 Distill Qwen 1.5B';
1544
+
1545
+ @override
1546
+ String get model_deepseek_desc =>
1547
+ 'Modelo de razonamiento y cadena de pensamiento. El mejor para tareas lógicas.';
1548
+
1549
+ @override
1550
+ String get model_license_mit => 'MIT';
1551
+
1552
+ @override
1553
+ String get model_gemma => 'Gemma 4 E2B Instruct';
1554
+
1555
+ @override
1556
+ String get model_gemma_desc =>
1557
+ 'Modelo insignia de Google. Máxima calidad, requiere más RAM.';
1558
+
1559
+ @override
1560
+ String export_header(String date) {
1561
+ return '*Exportado de LocalMind — $date*';
1562
+ }
1563
+
1564
+ @override
1565
+ String get export_role_user => '## 👤 Usuario';
1566
+
1567
+ @override
1568
+ String get export_role_assistant => '## 🤖 Asistente';
1569
+
1570
+ @override
1571
+ String get export_role_system => '## ⚙️ Sistema';
1572
+
1573
+ @override
1574
+ String get export_role_tool => '## 🔧 Herramienta';
1575
+
1576
+ @override
1577
+ String get export_text_user => '[USUARIO]';
1578
+
1579
+ @override
1580
+ String get export_text_assistant => '[ASISTENTE]';
1581
+
1582
+ @override
1583
+ String get export_text_system => '[SISTEMA]';
1584
+
1585
+ @override
1586
+ String get export_text_tool => '[HERRAMIENTA]';
1587
+
1588
+ @override
1589
+ String get export_label_user => 'USUARIO';
1590
+
1591
+ @override
1592
+ String get export_label_assistant => 'ASISTENTE';
1593
+
1594
+ @override
1595
+ String get export_label_system => 'SISTEMA';
1596
+
1597
+ @override
1598
+ String get export_label_tool => 'HERRAMIENTA';
1599
+
1600
+ @override
1601
+ String get select_model_hint => 'Seleccione un modelo para empezar a chatear';
1602
+
1603
+ @override
1604
+ String get test_notification_title => 'Notificación de prueba';
1605
+
1606
+ @override
1607
+ String get test_notification_body =>
1608
+ 'Esta es una notificación de prueba para el progreso de descarga de modelos.';
1609
+}
lib/l10n/app_localizations_hi.dart
new
+1600
@@ -0,0 +1,1600 @@
1
+// ignore: unused_import
2
+import 'package:intl/intl.dart' as intl;
3
+import 'app_localizations.dart';
4
+
5
+// ignore_for_file: type=lint
6
+
7
+/// The translations for Hindi (`hi`).
8
+class AppLocalizationsHi extends AppLocalizations {
9
+ AppLocalizationsHi([String locale = 'hi']) : super(locale);
10
+
11
+ @override
12
+ String get app_name => 'LocalMind';
13
+
14
+ @override
15
+ String get app_tagline => 'आपका AI. आपका डिवाइस. आपके नियम।';
16
+
17
+ @override
18
+ String get app_version => '1.0.0';
19
+
20
+ @override
21
+ String get cancel => 'रद्द करें';
22
+
23
+ @override
24
+ String get confirm => 'पुष्टि करें';
25
+
26
+ @override
27
+ String get delete => 'हटाएं';
28
+
29
+ @override
30
+ String get save => 'सहेजें';
31
+
32
+ @override
33
+ String get retry => 'पुनः प्रयास करें';
34
+
35
+ @override
36
+ String get close => 'बंद करें';
37
+
38
+ @override
39
+ String get done => 'पूर्ण';
40
+
41
+ @override
42
+ String get continue_action => 'जारी रखें';
43
+
44
+ @override
45
+ String get skip => 'छोड़ें';
46
+
47
+ @override
48
+ String get install => 'इंस्टॉल करें';
49
+
50
+ @override
51
+ String get download => 'डाउनलोड करें';
52
+
53
+ @override
54
+ String get resume => 'फिर से शुरू करें';
55
+
56
+ @override
57
+ String get pause => 'विराम दें';
58
+
59
+ @override
60
+ String get stop => 'रोकें';
61
+
62
+ @override
63
+ String get edit => 'संपादित करें';
64
+
65
+ @override
66
+ String get preview => 'पूर्वावलोकन';
67
+
68
+ @override
69
+ String get unload => 'अनलोड करें';
70
+
71
+ @override
72
+ String get load => 'लोड करें';
73
+
74
+ @override
75
+ String get rename => 'नाम बदलें';
76
+
77
+ @override
78
+ String get pin => 'पिन करें';
79
+
80
+ @override
81
+ String get unpin => 'अनपिन करें';
82
+
83
+ @override
84
+ String get share => 'साझा करें';
85
+
86
+ @override
87
+ String get copy => 'कॉपी करें';
88
+
89
+ @override
90
+ String get copied => 'कॉपी किया गया!';
91
+
92
+ @override
93
+ String get copied_to_clipboard => 'क्लिपबोर्ड पर कॉपी किया गया';
94
+
95
+ @override
96
+ String get select => 'चुनें';
97
+
98
+ @override
99
+ String get active => 'सक्रिय';
100
+
101
+ @override
102
+ String get all => 'सभी';
103
+
104
+ @override
105
+ String get none => 'कोई नहीं';
106
+
107
+ @override
108
+ String get none_selected => 'कोई चयनित नहीं है';
109
+
110
+ @override
111
+ String get online => 'ऑनलाइन';
112
+
113
+ @override
114
+ String get offline => 'ऑफ़लाइन';
115
+
116
+ @override
117
+ String get error => 'त्रुटि';
118
+
119
+ @override
120
+ String get unknown_error => 'अज्ञात त्रुटि';
121
+
122
+ @override
123
+ String get not_now => 'अभी नहीं';
124
+
125
+ @override
126
+ String get enable => 'सक्षम करें';
127
+
128
+ @override
129
+ String get proceed_anyway => 'वैसे भी आगे बढ़ें';
130
+
131
+ @override
132
+ String get test_connection => 'कनेक्शन का परीक्षण करें';
133
+
134
+ @override
135
+ String get testing => 'परीक्षण किया जा रहा है...';
136
+
137
+ @override
138
+ String get connection_successful => 'कनेक्शन सफल रहा!';
139
+
140
+ @override
141
+ String get connection_failed =>
142
+ 'कनेक्शन विफल रहा। अपनी सेटिंग्स की जांच करें।';
143
+
144
+ @override
145
+ String get save_continue => 'सहेजें और जारी रखें';
146
+
147
+ @override
148
+ String get save_changes => 'बदलाव सहेजें';
149
+
150
+ @override
151
+ String get finish_setup => 'सेटअप पूर्ण करें';
152
+
153
+ @override
154
+ String get start_new_chat => 'नई चैट शुरू करें';
155
+
156
+ @override
157
+ String get cannot_undo => 'इस क्रिया को पूर्ववत नहीं किया जा सकता।';
158
+
159
+ @override
160
+ String get ram_warning => 'रैम चेतावनी';
161
+
162
+ @override
163
+ String get recommended => 'अनुशंसित';
164
+
165
+ @override
166
+ String get may_be_large => 'इस डिवाइस के लिए बहुत बड़ा हो सकता है';
167
+
168
+ @override
169
+ String get calculating => 'गणना की जा रही है...';
170
+
171
+ @override
172
+ String get download_failed => 'डाउनलोड विफल';
173
+
174
+ @override
175
+ String get downloaded => 'डाउनलोड किया गया';
176
+
177
+ @override
178
+ String get not_downloaded => 'डाउनलोड नहीं किया गया';
179
+
180
+ @override
181
+ String get installed => 'इंस्टॉल किया गया';
182
+
183
+ @override
184
+ String get not_installed => 'इंस्टॉल नहीं किया गया';
185
+
186
+ @override
187
+ String get loading => 'लोड हो रहा है...';
188
+
189
+ @override
190
+ String get thinking => 'सोच रहा है';
191
+
192
+ @override
193
+ String get processing => 'प्रसंस्करण किया जा रहा है...';
194
+
195
+ @override
196
+ String get initializing => 'प्रारंभ किया जा रहा है...';
197
+
198
+ @override
199
+ String get ready => 'तैयार';
200
+
201
+ @override
202
+ String get preparing_app => 'ऐप तैयार किया जा रहा है...';
203
+
204
+ @override
205
+ String get initializing_services => 'सेवाएं प्रारंभ की जा रही हैं...';
206
+
207
+ @override
208
+ String get configuring_server => 'सर्वर कॉन्फ़िगर किया जा रहा है...';
209
+
210
+ @override
211
+ String get startup_failed => 'स्टार्टअप विफल रहा';
212
+
213
+ @override
214
+ String get something_went_wrong => 'कुछ गलत हो गया';
215
+
216
+ @override
217
+ String get delete_model_title => 'मॉडल हटाएं';
218
+
219
+ @override
220
+ String delete_model_body(String name) {
221
+ return 'क्या आप निश्चित रूप से $name को हटाना चाहते हैं?';
222
+ }
223
+
224
+ @override
225
+ String delete_model_body_with_size(String name, String size) {
226
+ return 'क्या आप निश्चित रूप से $name को हटाना चाहते हैं? इससे लगभग $size स्थान खाली हो जाएगा।\n\nयदि आवश्यकता हो तो आप बाद में इस मॉडल को फिर से डाउनलोड कर सकते हैं।';
227
+ }
228
+
229
+ @override
230
+ String get delete_voice_title => 'आवाज हटाएं';
231
+
232
+ @override
233
+ String delete_voice_body(String name, String size) {
234
+ return 'क्या आप निश्चित रूप से $name को हटाना चाहते हैं? इससे लगभग $size स्थान खाली हो जाएगा।\n\nयदि आवश्यकता हो तो आप बाद में इस आवाज को फिर से डाउनलोड कर सकते हैं।';
235
+ }
236
+
237
+ @override
238
+ String get delete_server_title => 'सर्वर हटाएं';
239
+
240
+ @override
241
+ String delete_server_body(String name) {
242
+ return 'क्या आप निश्चित रूप से \"$name\" को हटाना चाहते हैं? इसे पूर्ववत नहीं किया जा सकता।';
243
+ }
244
+
245
+ @override
246
+ String get delete_conversation_title => 'बातचीत हटाएं?';
247
+
248
+ @override
249
+ String delete_conversation_body(String title) {
250
+ return 'क्या आप निश्चित रूप से \"$title\" को हटाना चाहते हैं? इसे पूर्ववत नहीं किया जा सकता।';
251
+ }
252
+
253
+ @override
254
+ String get delete_message_title => 'संदेश हटाएं?';
255
+
256
+ @override
257
+ String delete_persona_title(String name) {
258
+ return '\"$name\" हटाएं?';
259
+ }
260
+
261
+ @override
262
+ String get delete_persona_body => 'इसे पूर्ववत नहीं किया जा सकता।';
263
+
264
+ @override
265
+ String get clear_conversation_title => 'बातचीत साफ़ करें?';
266
+
267
+ @override
268
+ String get clear_conversation_body =>
269
+ 'यह इस बातचीत के सभी संदेशों को हटा देगा।';
270
+
271
+ @override
272
+ String get clear => 'साफ़ करें';
273
+
274
+ @override
275
+ String label_completed(String label) {
276
+ return '$label पूर्ण';
277
+ }
278
+
279
+ @override
280
+ String error_with_message(String error) {
281
+ return 'त्रुटि: $error';
282
+ }
283
+
284
+ @override
285
+ String preview_failed(String error) {
286
+ return 'पूर्वावलोकन विफल: $error';
287
+ }
288
+
289
+ @override
290
+ String loading_model(String modelId) {
291
+ return '$modelId लोड किया जा रहा है...';
292
+ }
293
+
294
+ @override
295
+ String model_loaded(String modelId, String backend) {
296
+ return 'मॉडल लोड किया गया: $modelId ($backend)';
297
+ }
298
+
299
+ @override
300
+ String get no_model_loaded =>
301
+ 'कोई मॉडल लोड नहीं है। मॉडल डाउनलोड और लोड करने के लिए \"ऑन-डिवाइस मॉडल प्रबंधित करें\" पर टैप करें।';
302
+
303
+ @override
304
+ String loading_model_error(String error) {
305
+ return 'त्रुटि: $error';
306
+ }
307
+
308
+ @override
309
+ String get delete_conversation => 'बातचीत हटाएं?';
310
+
311
+ @override
312
+ String get nav_history => 'इतिहास';
313
+
314
+ @override
315
+ String get nav_servers => 'सर्वर';
316
+
317
+ @override
318
+ String get nav_local_models => 'स्थानीय मॉडल';
319
+
320
+ @override
321
+ String get nav_tts => 'टेक्स्ट टू स्पीच';
322
+
323
+ @override
324
+ String get nav_personas => 'चरित्र (Personas)';
325
+
326
+ @override
327
+ String get nav_settings => 'सेटिंग्स';
328
+
329
+ @override
330
+ String get nav_new_chat => 'नई चैट';
331
+
332
+ @override
333
+ String get search_hint => 'बातचीत खोजें...';
334
+
335
+ @override
336
+ String get no_server_selected => 'कोई सर्वर चयनित नहीं है';
337
+
338
+ @override
339
+ String get switch_server => 'सर्वर बदलें';
340
+
341
+ @override
342
+ String get switch_server_subtitle => 'कनेक्ट करने के लिए एक सर्वर चुनें';
343
+
344
+ @override
345
+ String get manage_servers => 'सर्वर प्रबंधित करें';
346
+
347
+ @override
348
+ String get open_source => 'ओपन सोर्स';
349
+
350
+ @override
351
+ String get open_source_desc =>
352
+ 'LocalMind ओपन सोर्स है। गिटहब (GitHub) पर हमारी प्रगति का अनुसरण करें या योगदान दें।';
353
+
354
+ @override
355
+ String get star_on_github => 'GitHub पर स्टार दें';
356
+
357
+ @override
358
+ String get settings_title => 'सेटिंग्स';
359
+
360
+ @override
361
+ String get settings_appearance => 'दिखावट';
362
+
363
+ @override
364
+ String get settings_language => 'भाषा';
365
+
366
+ @override
367
+ String get language_system_default => 'सिस्टम डिफ़ॉल्ट';
368
+
369
+ @override
370
+ String get settings_tts => 'टेक्स्ट-टू-स्पीच';
371
+
372
+ @override
373
+ String get settings_behavior => 'व्यवहार';
374
+
375
+ @override
376
+ String get settings_on_device => 'ऑन-डिवाइस अनुमान (On-Device Inference)';
377
+
378
+ @override
379
+ String get settings_default_server => 'डिफ़ॉल्ट सर्वर';
380
+
381
+ @override
382
+ String get settings_default_persona => 'डिफ़ॉल्ट चरित्र';
383
+
384
+ @override
385
+ String get settings_privacy => 'गोपनीयता';
386
+
387
+ @override
388
+ String get settings_data_management => 'डेटा प्रबंधन';
389
+
390
+ @override
391
+ String get settings_about => 'परिचय';
392
+
393
+ @override
394
+ String get theme => 'थीम';
395
+
396
+ @override
397
+ String get theme_system => 'सिस्टम';
398
+
399
+ @override
400
+ String get theme_light => 'लाइट';
401
+
402
+ @override
403
+ String get theme_dark => 'डार्क';
404
+
405
+ @override
406
+ String get theme_claude => 'Claude';
407
+
408
+ @override
409
+ String get font_size => 'फ़ॉन्ट का आकार';
410
+
411
+ @override
412
+ String get font_size_desc => 'चैट में टेक्स्ट का आकार समायोजित करें।';
413
+
414
+ @override
415
+ String get font_preview =>
416
+ 'त्वरित भूरी लोमड़ी आलसी कुत्ते के ऊपर से कूदती है।';
417
+
418
+ @override
419
+ String get code_theme_dark => 'कोड थीम (डार्क)';
420
+
421
+ @override
422
+ String get code_theme_light => 'कोड थीम (लाइट)';
423
+
424
+ @override
425
+ String get code_theme_desc =>
426
+ 'कोड ब्लॉक के लिए सिंटैक्स हाइलाइटिंग थीम चुनें।';
427
+
428
+ @override
429
+ String get tts_engine => 'TTS इंजन';
430
+
431
+ @override
432
+ String get tts_engine_system => 'सिस्टम TTS';
433
+
434
+ @override
435
+ String get tts_engine_kitten => 'Kitten TTS';
436
+
437
+ @override
438
+ String get voice => 'आवाज';
439
+
440
+ @override
441
+ String get voice_female => 'महिला';
442
+
443
+ @override
444
+ String get voice_male => 'पुरुष';
445
+
446
+ @override
447
+ String get voice_other => 'अन्य';
448
+
449
+ @override
450
+ String get tts_speed => 'TTS गति';
451
+
452
+ @override
453
+ String get tts_speed_desc => 'प्लेबैक की गति समायोजित करें।';
454
+
455
+ @override
456
+ String get manage_tts_models => 'TTS मॉडल प्रबंधित करें';
457
+
458
+ @override
459
+ String get manage_on_device_models => 'ऑन-डिवाइस मॉडल प्रबंधित करें';
460
+
461
+ @override
462
+ String get streaming_responses => 'प्रतिक्रियाएं स्ट्रीम करें';
463
+
464
+ @override
465
+ String get auto_generate_titles => 'स्वचालित रूप से शीर्षक उत्पन्न करें';
466
+
467
+ @override
468
+ String get send_on_enter => 'एंटर (Enter) दबाने पर भेजें';
469
+
470
+ @override
471
+ String get show_system_messages => 'सिस्टम संदेश दिखाएं';
472
+
473
+ @override
474
+ String get haptic_feedback => 'हैप्टिक फीडबैक';
475
+
476
+ @override
477
+ String get enable_mcp => 'MCP सक्षम करें';
478
+
479
+ @override
480
+ String get new_chat_mcp_default => 'नई चैट MCP डिफ़ॉल्ट';
481
+
482
+ @override
483
+ String get show_data_indicator => 'डेटा सूचक दिखाएं';
484
+
485
+ @override
486
+ String get privacy_info => '\"LocalMind आपके डेटा को कभी नहीं देखता है\"';
487
+
488
+ @override
489
+ String get delete_all_conversations => 'सभी बातचीत हटाएं';
490
+
491
+ @override
492
+ String get reset_settings_defaults => 'सेटिंग्स को डिफ़ॉल्ट पर रीसेट करें';
493
+
494
+ @override
495
+ String get chat_title => 'LocalMind';
496
+
497
+ @override
498
+ String get chat_parameters_tooltip => 'चैट पैरामीटर';
499
+
500
+ @override
501
+ String get change_persona => 'चरित्र बदलें';
502
+
503
+ @override
504
+ String get set_persona => 'चरित्र सेट करें';
505
+
506
+ @override
507
+ String get remove_persona => 'चरित्र हटाएं';
508
+
509
+ @override
510
+ String get clear_conversation => 'बातचीत साफ़ करें';
511
+
512
+ @override
513
+ String get connection_error => 'कनेक्शन त्रुटि। अपने सर्वर की जांच करें।';
514
+
515
+ @override
516
+ String get disconnected => 'सर्वर से कनेक्शन टूट गया।';
517
+
518
+ @override
519
+ String get configure => 'कॉन्फ़िगर करें';
520
+
521
+ @override
522
+ String get select_model => 'मॉडल चुनें';
523
+
524
+ @override
525
+ String get select_persona => 'चरित्र चुनें';
526
+
527
+ @override
528
+ String get start_conversation => 'बातचीत शुरू करें';
529
+
530
+ @override
531
+ String get recent_chats => 'हालिया चैट';
532
+
533
+ @override
534
+ String get see_all => 'सभी देखें';
535
+
536
+ @override
537
+ String get quick_write => 'मुझे एक फ़ंक्शन लिखने में मदद करें';
538
+
539
+ @override
540
+ String get quick_explain => 'इस कोड को समझाएं';
541
+
542
+ @override
543
+ String get quick_debug => 'इसे मेरे लिए डीबग करें';
544
+
545
+ @override
546
+ String get quick_async => 'मैं async/await का उपयोग कैसे करूं?';
547
+
548
+ @override
549
+ String get history_missing_title => 'इतिहास गायब है';
550
+
551
+ @override
552
+ String get history_missing_desc =>
553
+ 'या तो इस चैट के संदेश हटा दिए गए थे या इतिहास रिकॉर्ड दूषित हो गया है।';
554
+
555
+ @override
556
+ String get technical_details => 'तकनीकी विवरण';
557
+
558
+ @override
559
+ String get last_error => 'अंतिम त्रुटि:';
560
+
561
+ @override
562
+ String get copy_info => 'जानकारी कॉपी करें';
563
+
564
+ @override
565
+ String get conversation_id => 'बातचीत आईडी';
566
+
567
+ @override
568
+ String get created_at => 'निर्माण का समय';
569
+
570
+ @override
571
+ String get expected_messages => 'अपेक्षित संदेश';
572
+
573
+ @override
574
+ String get debug_dialog_desc =>
575
+ 'सिंक्रनाइज़ेशन समस्याओं की पहचान करने में सहायता के लिए नैदानिक जानकारी।';
576
+
577
+ @override
578
+ String get chat_input_hint => 'कुछ भी पूछें';
579
+
580
+ @override
581
+ String get send_message_tooltip => 'संदेश भेजें';
582
+
583
+ @override
584
+ String get stop_generation_tooltip => 'उत्पादन रोकें';
585
+
586
+ @override
587
+ String get attach_images_tooltip => 'छवियाँ संलग्न करें';
588
+
589
+ @override
590
+ String tool_label(String toolCallId) {
591
+ return 'उपकरण: $toolCallId';
592
+ }
593
+
594
+ @override
595
+ String get tool_unknown => 'उपकरण: अज्ञात';
596
+
597
+ @override
598
+ String get message_options => 'संदेश विकल्प';
599
+
600
+ @override
601
+ String get copy_markdown => 'Markdown के रूप में कॉपी करें';
602
+
603
+ @override
604
+ String get copied_markdown => 'Markdown के रूप में कॉपी किया गया';
605
+
606
+ @override
607
+ String get read_aloud => 'ज़ोर से पढ़ें';
608
+
609
+ @override
610
+ String get stop_reading => 'पढ़ना बंद करें';
611
+
612
+ @override
613
+ String get more => 'अधिक';
614
+
615
+ @override
616
+ String character_count(int length) {
617
+ return '$length वर्ण';
618
+ }
619
+
620
+ @override
621
+ String get edit_message => 'संदेश संपादित करें';
622
+
623
+ @override
624
+ String get edit_message_desc =>
625
+ 'सहेजने से नीचे सहायक की प्रतिक्रिया हट जाएगी और पुनः उत्पन्न होगी।';
626
+
627
+ @override
628
+ String get save_regenerate => 'सहेजें और पुनः उत्पन्न करें';
629
+
630
+ @override
631
+ String get chat_settings_title => 'चैट सेटिंग्स';
632
+
633
+ @override
634
+ String get reset_defaults => 'डिफ़ॉल्ट पर रीसेट करें';
635
+
636
+ @override
637
+ String get parameters_tab => 'पैरामीटर';
638
+
639
+ @override
640
+ String get mcp_tab => 'MCP';
641
+
642
+ @override
643
+ String get temperature => 'तापमान';
644
+
645
+ @override
646
+ String get temperature_desc =>
647
+ 'यादृच्छिकता को नियंत्रित करता है: उच्च = रचनात्मक, निम्न = केंद्रित';
648
+
649
+ @override
650
+ String get top_p => 'Top P';
651
+
652
+ @override
653
+ String get top_p_desc => 'न्यूक्लियस सैंपलिंग थ्रेशोल्ड';
654
+
655
+ @override
656
+ String get max_tokens => 'अधिकतम टोकन';
657
+
658
+ @override
659
+ String get max_tokens_desc => 'प्रतिक्रिया सीमा';
660
+
661
+ @override
662
+ String get context_length => 'संदर्भ लंबाई (Context Length)';
663
+
664
+ @override
665
+ String get context_length_desc => 'इतिहास विंडो';
666
+
667
+ @override
668
+ String get mcp_disabled_warning =>
669
+ 'MCP वैश्विक रूप से अक्षम है। इन सुविधाओं का उपयोग करने के लिए सेटिंग्स में इसे सक्षम करें।';
670
+
671
+ @override
672
+ String get mcp_enable_chat => 'इस चैट के लिए MCP सक्षम करें';
673
+
674
+ @override
675
+ String get auto_execute_tools => 'स्वचालित रूप से उपकरण निष्पादित करें';
676
+
677
+ @override
678
+ String get add_ephemeral_mcp => 'अस्थायी MCP सर्वर जोड़ें';
679
+
680
+ @override
681
+ String get mcp_label_placeholder => 'लेबल';
682
+
683
+ @override
684
+ String get mcp_url_placeholder => 'URL (https://...)';
685
+
686
+ @override
687
+ String get active_integrations => 'सक्रिय एकीकरण';
688
+
689
+ @override
690
+ String get enable_notifications => 'सूचनाएं सक्षम करें';
691
+
692
+ @override
693
+ String get enable_notifications_desc =>
694
+ 'मॉडल डाउनलोड पूरा होने पर सूचित किया जाए।';
695
+
696
+ @override
697
+ String get chat_history_title => 'चैट इतिहास';
698
+
699
+ @override
700
+ String get conversation_just_now => 'अभी-अभी';
701
+
702
+ @override
703
+ String conversation_minutes_ago(int minutes) {
704
+ return '$minutes मिनट पहले';
705
+ }
706
+
707
+ @override
708
+ String conversation_hours_ago(int hours) {
709
+ return '$hours घंटे पहले';
710
+ }
711
+
712
+ @override
713
+ String get conversation_yesterday => 'कल';
714
+
715
+ @override
716
+ String conversation_days_ago(int days) {
717
+ return '$days दिन पहले';
718
+ }
719
+
720
+ @override
721
+ String conversation_date(int month, int day, int year) {
722
+ return '$month/$day/$year';
723
+ }
724
+
725
+ @override
726
+ String get options_tooltip => 'विकल्प';
727
+
728
+ @override
729
+ String get no_results_found => 'कोई परिणाम नहीं मिला';
730
+
731
+ @override
732
+ String get no_conversations_yet => 'अभी तक कोई बातचीत नहीं';
733
+
734
+ @override
735
+ String get try_different_search => 'कोई अन्य खोज शब्द आज़माएं';
736
+
737
+ @override
738
+ String get start_new_conversation => 'नई बातचीत शुरू करें';
739
+
740
+ @override
741
+ String get rename_conversation => 'बातचीत का नाम बदलें';
742
+
743
+ @override
744
+ String get enter_new_title => 'नया शीर्षक दर्ज करें';
745
+
746
+ @override
747
+ String get pinned_section => 'पिन किया गया';
748
+
749
+ @override
750
+ String get today_section => 'आज';
751
+
752
+ @override
753
+ String get yesterday_section => 'कल';
754
+
755
+ @override
756
+ String get previous_7_days => 'पिछले 7 दिन';
757
+
758
+ @override
759
+ String get previous_30_days => 'पिछले 30 दिन';
760
+
761
+ @override
762
+ String get older_section => 'पुराने';
763
+
764
+ @override
765
+ String get onboarding_localmind => 'LOCALMIND';
766
+
767
+ @override
768
+ String get onboarding_connect_server => 'अपना सर्वर\nकनेक्ट करें';
769
+
770
+ @override
771
+ String get onboarding_connect_desc =>
772
+ 'अपना निजी AI अनुभव शुरू करने के लिए\nLM Studio, Ollama, या OpenRouter से\nकनेक्ट करें।';
773
+
774
+ @override
775
+ String get server_type_on_device => 'ऑन-डिवाइस';
776
+
777
+ @override
778
+ String get server_type_on_device_sub => 'किसी सर्वर की आवश्यकता नहीं है';
779
+
780
+ @override
781
+ String get server_type_lm_studio => 'LM Studio';
782
+
783
+ @override
784
+ String get server_type_lm_studio_sub => 'स्थानीय API';
785
+
786
+ @override
787
+ String get server_type_ollama => 'Ollama';
788
+
789
+ @override
790
+ String get server_type_ollama_sub => 'CLI इंजन';
791
+
792
+ @override
793
+ String get server_type_openrouter => 'OpenRouter';
794
+
795
+ @override
796
+ String get server_type_openrouter_sub => 'एकीकृत क्लाउड';
797
+
798
+ @override
799
+ String get ready_continue => 'जारी रखने के लिए तैयार';
800
+
801
+ @override
802
+ String get waiting_selection => 'चयन की प्रतीक्षा की जा रही है';
803
+
804
+ @override
805
+ String get setup_connection => 'कनेक्शन सेटअप करें';
806
+
807
+ @override
808
+ String setup_connection_desc(String server) {
809
+ return 'चैटिंग शुरू करने के लिए अपने $server सर्वर को कॉन्फ़िगर करें।';
810
+ }
811
+
812
+ @override
813
+ String get server_name => 'सर्वर का नाम';
814
+
815
+ @override
816
+ String get name_required => 'नाम आवश्यक है';
817
+
818
+ @override
819
+ String get name_max_50 => 'अधिकतम 50 वर्ण';
820
+
821
+ @override
822
+ String get host_label => 'होस्ट / IP पता';
823
+
824
+ @override
825
+ String get host_required => 'होस्ट आवश्यक है';
826
+
827
+ @override
828
+ String get port_label => 'पोर्ट';
829
+
830
+ @override
831
+ String get port_required => 'पोर्ट आवश्यक है';
832
+
833
+ @override
834
+ String get port_invalid => 'एक संख्या होनी चाहिए';
835
+
836
+ @override
837
+ String get port_range => 'एक वैध पोर्ट दर्ज करें (1-65535)';
838
+
839
+ @override
840
+ String get api_key_required => 'API कुंजी *';
841
+
842
+ @override
843
+ String get api_key_optional => 'API कुंजी (वैकल्पिक)';
844
+
845
+ @override
846
+ String get api_key_required_openrouter =>
847
+ 'OpenRouter के लिए API कुंजी आवश्यक है';
848
+
849
+ @override
850
+ String get api_key_format => 'OpenRouter API कुंजियाँ sk- से शुरू होती हैं';
851
+
852
+ @override
853
+ String get my_server_hint => 'मेरा सर्वर';
854
+
855
+ @override
856
+ String get name_length_validation => 'नाम 50 वर्ण या उससे कम होना चाहिए';
857
+
858
+ @override
859
+ String get host_valid => 'एक वैध होस्टनाम या IP पता दर्ज करें';
860
+
861
+ @override
862
+ String get api_key_hint_openrouter => 'sk-...';
863
+
864
+ @override
865
+ String get api_key_hint_generic => 'प्रमाणित सर्वर के लिए';
866
+
867
+ @override
868
+ String get update_server => 'सर्वर अपडेट करें';
869
+
870
+ @override
871
+ String get save_server => 'सर्वर सहेजें';
872
+
873
+ @override
874
+ String get server_updated => 'सर्वर अपडेट किया गया';
875
+
876
+ @override
877
+ String get server_added => 'सर्वर जोड़ा गया';
878
+
879
+ @override
880
+ String get download_model_title => 'मॉडल डाउनलोड करें';
881
+
882
+ @override
883
+ String get download_model_desc =>
884
+ 'डाउनलोड करने के लिए एक मॉडल चुनें।\nयह आपके डिवाइस पर स्थानीय रूप से चलेगा।';
885
+
886
+ @override
887
+ String get on_device_android_only =>
888
+ 'ऑन-डिवाइस अनुमान वर्तमान में केवल Android पर उपलब्ध है।';
889
+
890
+ @override
891
+ String get total_ram => 'कुल रैम';
892
+
893
+ @override
894
+ String get available => 'उपलब्ध';
895
+
896
+ @override
897
+ String ram_min_required(String fileSize) {
898
+ return 'न्यूनतम $fileSize GB रैम (RAM) आवश्यक';
899
+ }
900
+
901
+ @override
902
+ String download_progress(String percent, String speed) {
903
+ return '$percent% • $speed';
904
+ }
905
+
906
+ @override
907
+ String eta_label(String eta) {
908
+ return 'शेष समय (ETA): $eta';
909
+ }
910
+
911
+ @override
912
+ String paused_progress(String percent) {
913
+ return 'रोका गया - $percent%';
914
+ }
915
+
916
+ @override
917
+ String ram_warning_body_download(String ram, String totalMemory) {
918
+ return 'इस मॉडल के लिए कम से कम $ram GB रैम की आवश्यकता है, लेकिन आपके डिवाइस में $totalMemory है। यह सही ढंग से काम नहीं कर सकता है या ऐप क्रैश हो सकता है।';
919
+ }
920
+
921
+ @override
922
+ String ram_warning_body_load(String availableRAM, String ram) {
923
+ return 'आपके डिवाइस में $availableRAM उपलब्ध रैम है, लेकिन यह मॉडल कम से कम $ram GB की सिफारिश करता है। इसे लोड करना विफल हो सकता है या अस्थिरता पैदा कर सकता है।';
924
+ }
925
+
926
+ @override
927
+ String get choose_theme => 'थीम चुनें';
928
+
929
+ @override
930
+ String get choose_theme_desc =>
931
+ 'ऐप के रूप को निजीकृत करें। आप इसे बाद में सेटिंग्स में कभी भी बदल सकते हैं।';
932
+
933
+ @override
934
+ String get theme_card_system => 'सिस्टम';
935
+
936
+ @override
937
+ String get theme_card_system_sub => 'आपके डिवाइस की सेटिंग्स से मेल खाता है';
938
+
939
+ @override
940
+ String get theme_card_light => 'लाइट';
941
+
942
+ @override
943
+ String get theme_card_light_sub => 'साफ़ और उज्ज्वल';
944
+
945
+ @override
946
+ String get theme_card_dark => 'डार्क';
947
+
948
+ @override
949
+ String get theme_card_dark_sub => 'आँखों के लिए आरामदायक';
950
+
951
+ @override
952
+ String get theme_card_claude => 'Claude';
953
+
954
+ @override
955
+ String get theme_card_claude_sub => 'एक हल्का नारंगी-पीला (peach) थीम';
956
+
957
+ @override
958
+ String get stay_updated => 'अपडेट रहें';
959
+
960
+ @override
961
+ String get stay_updated_desc =>
962
+ 'जब आपके AI मॉडल डाउनलोड हो जाएं या लंबे समय तक चलने वाले कार्य पूरे हो जाएं तो सूचित किया जाए।';
963
+
964
+ @override
965
+ String get notification_benefit_downloads => 'मॉडल डाउनलोड प्रगति';
966
+
967
+ @override
968
+ String get notification_benefit_completions => 'उत्पादन पूर्णता';
969
+
970
+ @override
971
+ String get notification_benefit_background => 'पृष्ठभूमि कार्यों की स्थिति';
972
+
973
+ @override
974
+ String get allow_notifications => 'सूचनाओं की अनुमति दें';
975
+
976
+ @override
977
+ String get servers_title => 'सर्वर';
978
+
979
+ @override
980
+ String get no_servers_yet => 'अभी तक कोई सर्वर नहीं है';
981
+
982
+ @override
983
+ String get no_servers_desc =>
984
+ 'AI मॉडल के साथ चैट करना शुरू करने के लिए अपना पहला सर्वर जोड़ें।';
985
+
986
+ @override
987
+ String get add_server => 'सर्वर जोड़ें';
988
+
989
+ @override
990
+ String switched_to_server(String name) {
991
+ return '$name पर स्विच किया गया';
992
+ }
993
+
994
+ @override
995
+ String get edit_server => 'सर्वर संपादित करें';
996
+
997
+ @override
998
+ String get add_server_title => 'सर्वर जोड़ें';
999
+
1000
+ @override
1001
+ String get server_type_label => 'सर्वर का प्रकार';
1002
+
1003
+ @override
1004
+ String get server_icon_label => 'सर्वर आइकन';
1005
+
1006
+ @override
1007
+ String get default_icon => 'डिफ़ॉल्ट आइकन';
1008
+
1009
+ @override
1010
+ String get server_type_lm_studio_display => 'LM Studio';
1011
+
1012
+ @override
1013
+ String get server_type_openai_display => 'OpenAI संगत';
1014
+
1015
+ @override
1016
+ String get server_type_ollama_display => 'Ollama';
1017
+
1018
+ @override
1019
+ String get server_type_openrouter_display => 'OpenRouter';
1020
+
1021
+ @override
1022
+ String get server_type_on_device_display => 'ऑन-डिवाइस';
1023
+
1024
+ @override
1025
+ String get server_address_openrouter => 'openrouter.ai';
1026
+
1027
+ @override
1028
+ String get server_address_on_device => 'स्थानीय अनुमान (Local Inference)';
1029
+
1030
+ @override
1031
+ String server_address_format(String host, String port) {
1032
+ return '$host:$port';
1033
+ }
1034
+
1035
+ @override
1036
+ String get default_badge => 'डिफ़ॉल्ट';
1037
+
1038
+ @override
1039
+ String get set_as_default => 'डिफ़ॉल्ट रूप में सेट करें';
1040
+
1041
+ @override
1042
+ String get select_icon => 'आइकन चुनें';
1043
+
1044
+ @override
1045
+ String get select_icon_desc => 'अपने सर्वर के लिए एक आइकन चुनें';
1046
+
1047
+ @override
1048
+ String get search_icons_hint => 'आइकन खोजें...';
1049
+
1050
+ @override
1051
+ String get server_icon_stack => 'सर्वर स्टैक';
1052
+
1053
+ @override
1054
+ String get server_icon_stack2 => 'सर्वर स्टैक 02';
1055
+
1056
+ @override
1057
+ String get server_icon_stack3 => 'सर्वर स्टैक 03';
1058
+
1059
+ @override
1060
+ String get server_icon_cloud => 'क्लाउड';
1061
+
1062
+ @override
1063
+ String get server_icon_cloud_server => 'क्लाउड सर्वर';
1064
+
1065
+ @override
1066
+ String get server_icon_mcp => 'MCP सर्वर';
1067
+
1068
+ @override
1069
+ String get server_icon_database => 'डेटाबेस';
1070
+
1071
+ @override
1072
+ String get server_icon_database1 => 'डेटाबेस 01';
1073
+
1074
+ @override
1075
+ String get server_icon_database2 => 'डेटाबेस 02';
1076
+
1077
+ @override
1078
+ String get server_icon_cpu => 'CPU';
1079
+
1080
+ @override
1081
+ String get server_icon_chip => 'चिप';
1082
+
1083
+ @override
1084
+ String get server_icon_chip2 => 'चिप 02';
1085
+
1086
+ @override
1087
+ String get server_icon_computer => 'कंप्यूटर';
1088
+
1089
+ @override
1090
+ String get server_icon_laptop => 'लैपटॉप';
1091
+
1092
+ @override
1093
+ String get server_icon_terminal => 'कंप्यूटर टर्मिनल';
1094
+
1095
+ @override
1096
+ String get server_icon_code => 'कोड';
1097
+
1098
+ @override
1099
+ String get server_icon_ai_brain => 'AI ब्रेन';
1100
+
1101
+ @override
1102
+ String get server_icon_ai_brain2 => 'AI ब्रेन 02';
1103
+
1104
+ @override
1105
+ String get server_icon_ai_cloud => 'AI क्लाउड';
1106
+
1107
+ @override
1108
+ String get server_icon_ai_network => 'AI नेटवर्क';
1109
+
1110
+ @override
1111
+ String get server_icon_ai_chat => 'AI चैट';
1112
+
1113
+ @override
1114
+ String get server_icon_cellular => 'सेलुलर नेटवर्क';
1115
+
1116
+ @override
1117
+ String get server_icon_plug1 => 'प्लग 01';
1118
+
1119
+ @override
1120
+ String get server_icon_plug2 => 'प्लग 02';
1121
+
1122
+ @override
1123
+ String get server_icon_bot => 'बॉट';
1124
+
1125
+ @override
1126
+ String get server_icon_bot2 => 'बॉट 02';
1127
+
1128
+ @override
1129
+ String get server_icon_robotic => 'रोबोटिक';
1130
+
1131
+ @override
1132
+ String get server_icon_rocket => 'रॉकेट';
1133
+
1134
+ @override
1135
+ String get server_icon_star => 'स्टार';
1136
+
1137
+ @override
1138
+ String get server_icon_settings1 => 'सेटिंग्स 01';
1139
+
1140
+ @override
1141
+ String get server_icon_settings2 => 'सेटिंग्स 02';
1142
+
1143
+ @override
1144
+ String get server_icon_home1 => 'होम 01';
1145
+
1146
+ @override
1147
+ String get server_icon_home2 => 'होम 02';
1148
+
1149
+ @override
1150
+ String get server_icon_folder1 => 'फ़ोल्डर 01';
1151
+
1152
+ @override
1153
+ String get server_icon_folder2 => 'फ़ोल्डर 02';
1154
+
1155
+ @override
1156
+ String get server_icon_file1 => 'फ़ाइल 01';
1157
+
1158
+ @override
1159
+ String get server_icon_lock => 'लॉक';
1160
+
1161
+ @override
1162
+ String get server_icon_key => 'कुंजी 01';
1163
+
1164
+ @override
1165
+ String get server_icon_link => 'लिंक 01';
1166
+
1167
+ @override
1168
+ String get server_icon_globe => 'ग्लोब';
1169
+
1170
+ @override
1171
+ String get server_icon_api => 'API';
1172
+
1173
+ @override
1174
+ String get server_icon_arrow_right => 'दायाँ तीर 01';
1175
+
1176
+ @override
1177
+ String get server_icon_check => 'सत्यापन वृत्त';
1178
+
1179
+ @override
1180
+ String get server_icon_alert => 'चेतावनी वृत्त';
1181
+
1182
+ @override
1183
+ String get server_icon_info => 'सूचना वृत्त';
1184
+
1185
+ @override
1186
+ String get server_icon_zap => 'जैप';
1187
+
1188
+ @override
1189
+ String get server_icon_cloud_upload => 'क्लाउड अपलोड';
1190
+
1191
+ @override
1192
+ String get server_icon_cloud_download => 'क्लाउड डाउनलोड';
1193
+
1194
+ @override
1195
+ String get server_icon_refresh => 'रीफ्रेश करें';
1196
+
1197
+ @override
1198
+ String get server_icon_hard_drive => 'हार्ड ड्राइव';
1199
+
1200
+ @override
1201
+ String get server_icon_drive => 'ड्राइव';
1202
+
1203
+ @override
1204
+ String get personas_title => 'चरित्र (Personas)';
1205
+
1206
+ @override
1207
+ String get persona_category_general => 'सामान्य';
1208
+
1209
+ @override
1210
+ String get persona_category_coding => 'कोडिंग';
1211
+
1212
+ @override
1213
+ String get persona_category_education => 'शिक्षा';
1214
+
1215
+ @override
1216
+ String get persona_category_creative => 'रचनात्मक';
1217
+
1218
+ @override
1219
+ String get persona_builtin_section => 'इन-बिल्ट';
1220
+
1221
+ @override
1222
+ String get persona_my_section => 'मेरे चरित्र (Personas)';
1223
+
1224
+ @override
1225
+ String get clone_edit => 'क्लोन और संपादित करें';
1226
+
1227
+ @override
1228
+ String get builtin_badge => 'इन-बिल्ट';
1229
+
1230
+ @override
1231
+ String get no_personas_found => 'कोई चरित्र नहीं मिला';
1232
+
1233
+ @override
1234
+ String get no_personas_desc =>
1235
+ 'AI के व्यवहार को अनुकूलित करने के लिए अपना पहला चरित्र बनाएं।';
1236
+
1237
+ @override
1238
+ String get edit_persona => 'चरित्र संपादित करें';
1239
+
1240
+ @override
1241
+ String get create_persona => 'चरित्र बनाएं';
1242
+
1243
+ @override
1244
+ String get create_persona_button => 'बनाएं';
1245
+
1246
+ @override
1247
+ String get emoji_label => 'इमोजी';
1248
+
1249
+ @override
1250
+ String get name_label => 'नाम';
1251
+
1252
+ @override
1253
+ String get my_persona_hint => 'मेरा चरित्र';
1254
+
1255
+ @override
1256
+ String get category_label => 'श्रेणी';
1257
+
1258
+ @override
1259
+ String get description_optional => 'विवरण (वैकल्पिक)';
1260
+
1261
+ @override
1262
+ String get description_hint => 'यह चरित्र क्या करता है...';
1263
+
1264
+ @override
1265
+ String get system_prompt => 'सिस्टम प्रॉम्ट';
1266
+
1267
+ @override
1268
+ String character_count_max(int currentLen) {
1269
+ return '$currentLen/4000';
1270
+ }
1271
+
1272
+ @override
1273
+ String get no_prompt_placeholder => 'अभी तक कोई प्रॉम्ट नहीं...';
1274
+
1275
+ @override
1276
+ String get prompt_hint => 'आप एक सहायक हैं...';
1277
+
1278
+ @override
1279
+ String get prompt_required => 'सिस्टम प्रॉम्ट आवश्यक है';
1280
+
1281
+ @override
1282
+ String get prompt_max_chars => 'अधिकतम 4000 वर्ण';
1283
+
1284
+ @override
1285
+ String get advanced_settings => 'उन्नत सेटिंग्स';
1286
+
1287
+ @override
1288
+ String get temperature_label => 'तापमान (0.0-2.0)';
1289
+
1290
+ @override
1291
+ String get top_p_label => 'Top P (0.0-1.0)';
1292
+
1293
+ @override
1294
+ String get temp_hint => '0.7';
1295
+
1296
+ @override
1297
+ String get top_p_hint => '0.9';
1298
+
1299
+ @override
1300
+ String get range_0_2 => '0.0-2.0';
1301
+
1302
+ @override
1303
+ String get range_0_1 => '0.0-1.0';
1304
+
1305
+ @override
1306
+ String get persona_updated => 'चरित्र अद्यतित (updated)';
1307
+
1308
+ @override
1309
+ String get persona_created => 'चरित्र निर्मित';
1310
+
1311
+ @override
1312
+ String get tts_models_title => 'टेक्स्ट टू स्पीच मॉडल';
1313
+
1314
+ @override
1315
+ String get always_available => 'हमेशा उपलब्ध';
1316
+
1317
+ @override
1318
+ String get tts_system_desc =>
1319
+ 'आपके डिवाइस के इन-बिल्ट टेक्स्ट-टू-स्पीच इंजन का उपयोग करता है।\nकिसी डाउनलोड की आवश्यकता नहीं है। आवाज चयन आपके डिवाइस की सिस्टम सेटिंग्स का उपयोग करता है।';
1320
+
1321
+ @override
1322
+ String get downloading_status => 'डाउनलोड किया जा रहा है...';
1323
+
1324
+ @override
1325
+ String tts_kitten_desc(String size) {
1326
+ return '8 अभिव्यंजक आवाज़ों के साथ बिजली की तरह तेज़ न्यूरल TTS।\n$size डाउनलोड की आवश्यकता है।';
1327
+ }
1328
+
1329
+ @override
1330
+ String tts_piper_desc(String size) {
1331
+ return '2 अभिव्यंजक आवाज़ों के साथ तेज़ ऑफ़लाइन पाइपर (Piper) आवाज़ें।\nप्रति आवाज़ $size डाउनलोड की आवश्यकता है।';
1332
+ }
1333
+
1334
+ @override
1335
+ String engine_spec(String sizeMb, String ramMb, int voiceCount) {
1336
+ return '$sizeMb MB · $ramMb MB रैम · $voiceCount आवाजें';
1337
+ }
1338
+
1339
+ @override
1340
+ String get on_device_models_title => 'ऑन-डिवाइस मॉडल';
1341
+
1342
+ @override
1343
+ String get available_models => 'उपलब्ध मॉडल';
1344
+
1345
+ @override
1346
+ String get device_memory => 'डिवाइस मेमोरी';
1347
+
1348
+ @override
1349
+ String get ram_usage => 'रैम का उपयोग';
1350
+
1351
+ @override
1352
+ String get memory_healthy => 'स्वस्थ';
1353
+
1354
+ @override
1355
+ String get memory_critical => 'महत्वपूर्ण';
1356
+
1357
+ @override
1358
+ String get memory_low => 'कम';
1359
+
1360
+ @override
1361
+ String ram_used(String percent) {
1362
+ return '$percent% प्रयुक्त';
1363
+ }
1364
+
1365
+ @override
1366
+ String get available_ram => 'उपलब्ध रैम (RAM)';
1367
+
1368
+ @override
1369
+ String get total_capacity => 'कुल क्षमता';
1370
+
1371
+ @override
1372
+ String get loaded_status => 'लोड किया गया';
1373
+
1374
+ @override
1375
+ String get inference_backend => 'अनुमान बैकएंड (Inference Backend)';
1376
+
1377
+ @override
1378
+ String get backend_ios_notice => 'iOS पर केवल CPU बैकएंड उपलब्ध है।';
1379
+
1380
+ @override
1381
+ String get backend_cpu_desc => 'सभी उपकरणों पर काम करता है। सबसे अनुकूल।';
1382
+
1383
+ @override
1384
+ String get backend_gpu_desc => 'OpenCL त्वरण। समर्थित उपकरणों पर तेज़।';
1385
+
1386
+ @override
1387
+ String get backend_npu_desc =>
1388
+ 'विक्रेता NPU (Qualcomm/MediaTek)। सबसे तेज़ अनुमान।';
1389
+
1390
+ @override
1391
+ String get select_model_title => 'मॉडल चुनें';
1392
+
1393
+ @override
1394
+ String get refresh_models => 'मॉडल रीफ्रेश करें';
1395
+
1396
+ @override
1397
+ String get search_models_hint => 'मॉडल खोजें...';
1398
+
1399
+ @override
1400
+ String get no_server_connected => 'कोई सर्वर कनेक्ट नहीं है';
1401
+
1402
+ @override
1403
+ String get add_server_first =>
1404
+ 'उपलब्ध मॉडल देखने के लिए पहले एक सर्वर जोड़ें।';
1405
+
1406
+ @override
1407
+ String get failed_load_models => 'मॉडल लोड करने में विफल';
1408
+
1409
+ @override
1410
+ String get no_models_available => 'कोई मॉडल उपलब्ध नहीं है';
1411
+
1412
+ @override
1413
+ String no_models_match(String searchQuery) {
1414
+ return '\"$searchQuery\" से मेल खाने वाला कोई मॉडल नहीं मिला';
1415
+ }
1416
+
1417
+ @override
1418
+ String model_load_failed(String error) {
1419
+ return 'मॉडल लोड करने में विफल: $error';
1420
+ }
1421
+
1422
+ @override
1423
+ String model_unloaded_ollama(String name) {
1424
+ return 'कीप-अलाइव (keep-alive) समय बीतने के बाद $name को अनलोड कर दिया जाएगा';
1425
+ }
1426
+
1427
+ @override
1428
+ String model_unloaded_success(String name) {
1429
+ return '$name सफलतापूर्वक अनलोड हो गया';
1430
+ }
1431
+
1432
+ @override
1433
+ String model_unload_failed(String error) {
1434
+ return 'मॉडल अनलोड करने में विफल: $error';
1435
+ }
1436
+
1437
+ @override
1438
+ String get unload_from_server => 'सर्वर से अनलोड करें';
1439
+
1440
+ @override
1441
+ String context_chip(String ctx) {
1442
+ return '$ctx संदर्भ (ctx)';
1443
+ }
1444
+
1445
+ @override
1446
+ String download_notification_title(String modelName) {
1447
+ return '$modelName डाउनलोड किया जा रहा है...';
1448
+ }
1449
+
1450
+ @override
1451
+ String get download_complete_notification => 'डाउनलोड पूर्ण!';
1452
+
1453
+ @override
1454
+ String download_complete_body(String modelName) {
1455
+ return '$modelName सफलतापूर्वक डाउनलोड हो गया है।';
1456
+ }
1457
+
1458
+ @override
1459
+ String download_failed_notification(String error) {
1460
+ return 'डाउनलोड विफल: $error';
1461
+ }
1462
+
1463
+ @override
1464
+ String download_failed_body(String modelName) {
1465
+ return '$modelName डाउनलोड करने में विफल।';
1466
+ }
1467
+
1468
+ @override
1469
+ String get engine_name_system => 'सिस्टम TTS';
1470
+
1471
+ @override
1472
+ String get engine_tagline_system => 'इन-बिल्ट डिवाइस इंजन';
1473
+
1474
+ @override
1475
+ String get engine_name_kitten => 'Kitten TTS';
1476
+
1477
+ @override
1478
+ String get engine_tagline_kitten => 'उच्च गति न्यूरल TTS';
1479
+
1480
+ @override
1481
+ String get engine_name_sherpa => 'Sherpa ONNX VITS';
1482
+
1483
+ @override
1484
+ String get engine_tagline_sherpa => 'ऑफ़लाइन पाइपर (Piper) आवाज़ें';
1485
+
1486
+ @override
1487
+ String get voice_jasper => 'Jasper';
1488
+
1489
+ @override
1490
+ String get voice_bella => 'Bella';
1491
+
1492
+ @override
1493
+ String get voice_bruno => 'Bruno';
1494
+
1495
+ @override
1496
+ String get voice_luna => 'Luna';
1497
+
1498
+ @override
1499
+ String get voice_hugo => 'Hugo';
1500
+
1501
+ @override
1502
+ String get voice_rosie => 'Rosie';
1503
+
1504
+ @override
1505
+ String get voice_leo => 'Leo';
1506
+
1507
+ @override
1508
+ String get voice_kiki => 'Kiki';
1509
+
1510
+ @override
1511
+ String get voice_lessac => 'Lessac (US)';
1512
+
1513
+ @override
1514
+ String get voice_ryan => 'Ryan (US)';
1515
+
1516
+ @override
1517
+ String get model_qwen_3 => 'Qwen 3 0.6B';
1518
+
1519
+ @override
1520
+ String get model_qwen_3_desc =>
1521
+ 'सबसे छोटा सामान्य प्रयोजन चैट मॉडल। त्वरित प्रतिक्रियाएं, कम मेमोरी उपयोग।';
1522
+
1523
+ @override
1524
+ String get model_license_apache => 'Apache-2.0';
1525
+
1526
+ @override
1527
+ String get model_qwen_25 => 'Qwen 2.5 1.5B Instruct';
1528
+
1529
+ @override
1530
+ String get model_qwen_25_desc =>
1531
+ 'संतुलित गुणवत्ता और आकार। सामान्य बातचीत के लिए अच्छा है।';
1532
+
1533
+ @override
1534
+ String get model_deepseek => 'DeepSeek R1 Distill Qwen 1.5B';
1535
+
1536
+ @override
1537
+ String get model_deepseek_desc =>
1538
+ 'तर्क और विचार-श्रृंखला मॉडल। तार्किक कार्यों के लिए सर्वोत्तम।';
1539
+
1540
+ @override
1541
+ String get model_license_mit => 'MIT';
1542
+
1543
+ @override
1544
+ String get model_gemma => 'Gemma 4 E2B Instruct';
1545
+
1546
+ @override
1547
+ String get model_gemma_desc =>
1548
+ 'गूगल का फ्लैगशिप मॉडल। उच्चतम गुणवत्ता, अधिक रैम की आवश्यकता है।';
1549
+
1550
+ @override
1551
+ String export_header(String date) {
1552
+ return '*LocalMind से निर्यात किया गया — $date*';
1553
+ }
1554
+
1555
+ @override
1556
+ String get export_role_user => '## 👤 उपयोगकर्ता';
1557
+
1558
+ @override
1559
+ String get export_role_assistant => '## 🤖 सहायक';
1560
+
1561
+ @override
1562
+ String get export_role_system => '## ⚙️ सिस्टम';
1563
+
1564
+ @override
1565
+ String get export_role_tool => '## 🔧 उपकरण';
1566
+
1567
+ @override
1568
+ String get export_text_user => '[उपयोगकर्ता]';
1569
+
1570
+ @override
1571
+ String get export_text_assistant => '[सहायक]';
1572
+
1573
+ @override
1574
+ String get export_text_system => '[सिस्टम]';
1575
+
1576
+ @override
1577
+ String get export_text_tool => '[उपकरण]';
1578
+
1579
+ @override
1580
+ String get export_label_user => 'उपयोगकर्ता';
1581
+
1582
+ @override
1583
+ String get export_label_assistant => 'सहायक';
1584
+
1585
+ @override
1586
+ String get export_label_system => 'सिस्टम';
1587
+
1588
+ @override
1589
+ String get export_label_tool => 'उपकरण';
1590
+
1591
+ @override
1592
+ String get select_model_hint => 'चैटिंग शुरू करने के लिए एक मॉडल चुनें';
1593
+
1594
+ @override
1595
+ String get test_notification_title => 'परीक्षण सूचना';
1596
+
1597
+ @override
1598
+ String get test_notification_body =>
1599
+ 'यह मॉडल डाउनलोड प्रगति के लिए एक परीक्षण सूचना है।';
1600
+}
lib/l10n/app_localizations_zh.dart
new
+1573
@@ -0,0 +1,1573 @@
1
+// ignore: unused_import
2
+import 'package:intl/intl.dart' as intl;
3
+import 'app_localizations.dart';
4
+
5
+// ignore_for_file: type=lint
6
+
7
+/// The translations for Chinese (`zh`).
8
+class AppLocalizationsZh extends AppLocalizations {
9
+ AppLocalizationsZh([String locale = 'zh']) : super(locale);
10
+
11
+ @override
12
+ String get app_name => 'LocalMind';
13
+
14
+ @override
15
+ String get app_tagline => '您的 AI。您的设备。您的规则。';
16
+
17
+ @override
18
+ String get app_version => '1.0.0';
19
+
20
+ @override
21
+ String get cancel => '取消';
22
+
23
+ @override
24
+ String get confirm => '确认';
25
+
26
+ @override
27
+ String get delete => '删除';
28
+
29
+ @override
30
+ String get save => '保存';
31
+
32
+ @override
33
+ String get retry => '重试';
34
+
35
+ @override
36
+ String get close => '关闭';
37
+
38
+ @override
39
+ String get done => '完成';
40
+
41
+ @override
42
+ String get continue_action => '继续';
43
+
44
+ @override
45
+ String get skip => '跳过';
46
+
47
+ @override
48
+ String get install => '安装';
49
+
50
+ @override
51
+ String get download => '下载';
52
+
53
+ @override
54
+ String get resume => '继续';
55
+
56
+ @override
57
+ String get pause => '暂停';
58
+
59
+ @override
60
+ String get stop => '停止';
61
+
62
+ @override
63
+ String get edit => '编辑';
64
+
65
+ @override
66
+ String get preview => '预览';
67
+
68
+ @override
69
+ String get unload => '卸载';
70
+
71
+ @override
72
+ String get load => '加载';
73
+
74
+ @override
75
+ String get rename => '重命名';
76
+
77
+ @override
78
+ String get pin => 'Pin';
79
+
80
+ @override
81
+ String get unpin => '取消 Pin';
82
+
83
+ @override
84
+ String get share => '分享';
85
+
86
+ @override
87
+ String get copy => '复制';
88
+
89
+ @override
90
+ String get copied => '已复制!';
91
+
92
+ @override
93
+ String get copied_to_clipboard => '已复制到剪贴板';
94
+
95
+ @override
96
+ String get select => '选择';
97
+
98
+ @override
99
+ String get active => '已启用';
100
+
101
+ @override
102
+ String get all => '全部';
103
+
104
+ @override
105
+ String get none => '无';
106
+
107
+ @override
108
+ String get none_selected => '未选择';
109
+
110
+ @override
111
+ String get online => '在线';
112
+
113
+ @override
114
+ String get offline => '离线';
115
+
116
+ @override
117
+ String get error => '错误';
118
+
119
+ @override
120
+ String get unknown_error => '未知错误';
121
+
122
+ @override
123
+ String get not_now => '暂不';
124
+
125
+ @override
126
+ String get enable => '启用';
127
+
128
+ @override
129
+ String get proceed_anyway => '仍然继续';
130
+
131
+ @override
132
+ String get test_connection => '测试连接';
133
+
134
+ @override
135
+ String get testing => '正在测试...';
136
+
137
+ @override
138
+ String get connection_successful => '连接成功!';
139
+
140
+ @override
141
+ String get connection_failed => '连接失败。请检查您的设置。';
142
+
143
+ @override
144
+ String get save_continue => '保存并继续';
145
+
146
+ @override
147
+ String get save_changes => '保存更改';
148
+
149
+ @override
150
+ String get finish_setup => '完成设置';
151
+
152
+ @override
153
+ String get start_new_chat => '开始新聊天';
154
+
155
+ @override
156
+ String get cannot_undo => '此操作无法撤销。';
157
+
158
+ @override
159
+ String get ram_warning => '内存警告';
160
+
161
+ @override
162
+ String get recommended => '推荐';
163
+
164
+ @override
165
+ String get may_be_large => '可能对于此设备来说过大';
166
+
167
+ @override
168
+ String get calculating => '正在计算...';
169
+
170
+ @override
171
+ String get download_failed => '下载失败';
172
+
173
+ @override
174
+ String get downloaded => '已下载';
175
+
176
+ @override
177
+ String get not_downloaded => '未下载';
178
+
179
+ @override
180
+ String get installed => '已安装';
181
+
182
+ @override
183
+ String get not_installed => '未安装';
184
+
185
+ @override
186
+ String get loading => '正在加载...';
187
+
188
+ @override
189
+ String get thinking => '正在思考';
190
+
191
+ @override
192
+ String get processing => '正在处理...';
193
+
194
+ @override
195
+ String get initializing => '正在初始化...';
196
+
197
+ @override
198
+ String get ready => '就绪';
199
+
200
+ @override
201
+ String get preparing_app => '正在准备应用...';
202
+
203
+ @override
204
+ String get initializing_services => '正在初始化服务...';
205
+
206
+ @override
207
+ String get configuring_server => '正在配置服务器...';
208
+
209
+ @override
210
+ String get startup_failed => '启动失败';
211
+
212
+ @override
213
+ String get something_went_wrong => '出了点问题';
214
+
215
+ @override
216
+ String get delete_model_title => '删除模型';
217
+
218
+ @override
219
+ String delete_model_body(String name) {
220
+ return '您确定要删除 $name 吗?';
221
+ }
222
+
223
+ @override
224
+ String delete_model_body_with_size(String name, String size) {
225
+ return '您确定要删除 $name 吗?这释放大约 $size 的空间。\n\n如有需要,您稍后可以重新下载此模型。';
226
+ }
227
+
228
+ @override
229
+ String get delete_voice_title => '删除语音';
230
+
231
+ @override
232
+ String delete_voice_body(String name, String size) {
233
+ return '您确定要删除 $name 吗?这释放大约 $size 的空间。\n\n如有需要,您稍后可以重新下载此语音。';
234
+ }
235
+
236
+ @override
237
+ String get delete_server_title => '删除服务器';
238
+
239
+ @override
240
+ String delete_server_body(String name) {
241
+ return '您确定要删除 \"$name\" 吗?此操作无法撤销。';
242
+ }
243
+
244
+ @override
245
+ String get delete_conversation_title => '删除对话吗?';
246
+
247
+ @override
248
+ String delete_conversation_body(String title) {
249
+ return '您确定要删除 \"$title\" 吗?此操作无法撤销。';
250
+ }
251
+
252
+ @override
253
+ String get delete_message_title => '删除消息吗?';
254
+
255
+ @override
256
+ String delete_persona_title(String name) {
257
+ return '删除 \"$name\" 吗?';
258
+ }
259
+
260
+ @override
261
+ String get delete_persona_body => '此操作无法撤销。';
262
+
263
+ @override
264
+ String get clear_conversation_title => '清除对话吗?';
265
+
266
+ @override
267
+ String get clear_conversation_body => '这会删除此对话中的所有消息。';
268
+
269
+ @override
270
+ String get clear => '清除';
271
+
272
+ @override
273
+ String label_completed(String label) {
274
+ return '$label 已完成';
275
+ }
276
+
277
+ @override
278
+ String error_with_message(String error) {
279
+ return '错误: $error';
280
+ }
281
+
282
+ @override
283
+ String preview_failed(String error) {
284
+ return '预览失败: $error';
285
+ }
286
+
287
+ @override
288
+ String loading_model(String modelId) {
289
+ return '正在加载 $modelId...';
290
+ }
291
+
292
+ @override
293
+ String model_loaded(String modelId, String backend) {
294
+ return '模型已加载: $modelId ($backend)';
295
+ }
296
+
297
+ @override
298
+ String get no_model_loaded => '未加载任何模型。点击 “管理设备端模型” 以下载并加载模型。';
299
+
300
+ @override
301
+ String loading_model_error(String error) {
302
+ return '错误: $error';
303
+ }
304
+
305
+ @override
306
+ String get delete_conversation => '删除对话吗?';
307
+
308
+ @override
309
+ String get nav_history => '历史';
310
+
311
+ @override
312
+ String get nav_servers => '服务器';
313
+
314
+ @override
315
+ String get nav_local_models => '本地模型';
316
+
317
+ @override
318
+ String get nav_tts => '语音合成';
319
+
320
+ @override
321
+ String get nav_personas => '角色';
322
+
323
+ @override
324
+ String get nav_settings => '设置';
325
+
326
+ @override
327
+ String get nav_new_chat => '新聊天';
328
+
329
+ @override
330
+ String get search_hint => '搜索对话...';
331
+
332
+ @override
333
+ String get no_server_selected => '未选择服务器';
334
+
335
+ @override
336
+ String get switch_server => '切换服务器';
337
+
338
+ @override
339
+ String get switch_server_subtitle => '选择要连接的服务器';
340
+
341
+ @override
342
+ String get manage_servers => '管理服务器';
343
+
344
+ @override
345
+ String get open_source => '开源';
346
+
347
+ @override
348
+ String get open_source_desc => 'LocalMind 是开源项目。可在 GitHub 上关注我们的进展或参与贡献。';
349
+
350
+ @override
351
+ String get star_on_github => '在 GitHub 上点亮 Star';
352
+
353
+ @override
354
+ String get settings_title => '设置';
355
+
356
+ @override
357
+ String get settings_appearance => '外观';
358
+
359
+ @override
360
+ String get settings_language => '语言';
361
+
362
+ @override
363
+ String get language_system_default => '系统默认';
364
+
365
+ @override
366
+ String get settings_tts => '语音合成 (TTS)';
367
+
368
+ @override
369
+ String get settings_behavior => '行为';
370
+
371
+ @override
372
+ String get settings_on_device => '设备端推理';
373
+
374
+ @override
375
+ String get settings_default_server => '默认服务器';
376
+
377
+ @override
378
+ String get settings_default_persona => '默认角色';
379
+
380
+ @override
381
+ String get settings_privacy => '隐私';
382
+
383
+ @override
384
+ String get settings_data_management => '数据管理';
385
+
386
+ @override
387
+ String get settings_about => '关于';
388
+
389
+ @override
390
+ String get theme => '主题';
391
+
392
+ @override
393
+ String get theme_system => '系统';
394
+
395
+ @override
396
+ String get theme_light => '亮色';
397
+
398
+ @override
399
+ String get theme_dark => '暗色';
400
+
401
+ @override
402
+ String get theme_claude => 'Claude';
403
+
404
+ @override
405
+ String get font_size => '字体大小';
406
+
407
+ @override
408
+ String get font_size_desc => '调整聊天中的文本大小。';
409
+
410
+ @override
411
+ String get font_preview => '敏捷的棕色狐狸跳过那只懒狗。';
412
+
413
+ @override
414
+ String get code_theme_dark => '代码主题 (暗色)';
415
+
416
+ @override
417
+ String get code_theme_light => '代码主题 (亮色)';
418
+
419
+ @override
420
+ String get code_theme_desc => '选择代码块的语法高亮主题。';
421
+
422
+ @override
423
+ String get tts_engine => 'TTS 引擎';
424
+
425
+ @override
426
+ String get tts_engine_system => '系统 TTS';
427
+
428
+ @override
429
+ String get tts_engine_kitten => 'Kitten TTS';
430
+
431
+ @override
432
+ String get voice => '语音';
433
+
434
+ @override
435
+ String get voice_female => '女声';
436
+
437
+ @override
438
+ String get voice_male => '男声';
439
+
440
+ @override
441
+ String get voice_other => '其他';
442
+
443
+ @override
444
+ String get tts_speed => 'TTS 速度';
445
+
446
+ @override
447
+ String get tts_speed_desc => '调整播放速率。';
448
+
449
+ @override
450
+ String get manage_tts_models => '管理 TTS 模型';
451
+
452
+ @override
453
+ String get manage_on_device_models => '管理设备端模型';
454
+
455
+ @override
456
+ String get streaming_responses => '流式响应';
457
+
458
+ @override
459
+ String get auto_generate_titles => '自动生成标题';
460
+
461
+ @override
462
+ String get send_on_enter => '按 Enter 键发送';
463
+
464
+ @override
465
+ String get show_system_messages => '显示系统消息';
466
+
467
+ @override
468
+ String get haptic_feedback => '触觉反馈';
469
+
470
+ @override
471
+ String get enable_mcp => '启用 MCP';
472
+
473
+ @override
474
+ String get new_chat_mcp_default => '新聊天默认启用 MCP';
475
+
476
+ @override
477
+ String get show_data_indicator => '显示数据指示器';
478
+
479
+ @override
480
+ String get privacy_info => '\"LocalMind 绝不查看您的数据\"';
481
+
482
+ @override
483
+ String get delete_all_conversations => '删除所有对话';
484
+
485
+ @override
486
+ String get reset_settings_defaults => '重置设置至默认值';
487
+
488
+ @override
489
+ String get chat_title => 'LocalMind';
490
+
491
+ @override
492
+ String get chat_parameters_tooltip => '聊天参数';
493
+
494
+ @override
495
+ String get change_persona => '更换角色';
496
+
497
+ @override
498
+ String get set_persona => '设置角色';
499
+
500
+ @override
501
+ String get remove_persona => '删除角色';
502
+
503
+ @override
504
+ String get clear_conversation => '清除对话';
505
+
506
+ @override
507
+ String get connection_error => '连接错误。请检查您的服务器。';
508
+
509
+ @override
510
+ String get disconnected => '与服务器断开连接。';
511
+
512
+ @override
513
+ String get configure => '配置';
514
+
515
+ @override
516
+ String get select_model => '选择模型';
517
+
518
+ @override
519
+ String get select_persona => '选择角色';
520
+
521
+ @override
522
+ String get start_conversation => '开始对话';
523
+
524
+ @override
525
+ String get recent_chats => '最近聊天';
526
+
527
+ @override
528
+ String get see_all => '查看全部';
529
+
530
+ @override
531
+ String get quick_write => '帮我写一个函数';
532
+
533
+ @override
534
+ String get quick_explain => '解释这段代码';
535
+
536
+ @override
537
+ String get quick_debug => '帮我调试这个';
538
+
539
+ @override
540
+ String get quick_async => '我该如何使用 async/await?';
541
+
542
+ @override
543
+ String get history_missing_title => '历史记录丢失';
544
+
545
+ @override
546
+ String get history_missing_desc => '此聊天中的消息已被删除,或历史记录已损坏。';
547
+
548
+ @override
549
+ String get technical_details => '技术细节';
550
+
551
+ @override
552
+ String get last_error => '上次错误:';
553
+
554
+ @override
555
+ String get copy_info => '复制信息';
556
+
557
+ @override
558
+ String get conversation_id => '对话 ID';
559
+
560
+ @override
561
+ String get created_at => '创建于';
562
+
563
+ @override
564
+ String get expected_messages => '预期消息';
565
+
566
+ @override
567
+ String get debug_dialog_desc => '用以帮助识别同步问题的诊断信息。';
568
+
569
+ @override
570
+ String get chat_input_hint => '问任何问题';
571
+
572
+ @override
573
+ String get send_message_tooltip => '发送消息';
574
+
575
+ @override
576
+ String get stop_generation_tooltip => '停止生成';
577
+
578
+ @override
579
+ String get attach_images_tooltip => '附加图片';
580
+
581
+ @override
582
+ String tool_label(String toolCallId) {
583
+ return '工具: $toolCallId';
584
+ }
585
+
586
+ @override
587
+ String get tool_unknown => '工具: 未知';
588
+
589
+ @override
590
+ String get message_options => '消息选项';
591
+
592
+ @override
593
+ String get copy_markdown => '复制为 Markdown';
594
+
595
+ @override
596
+ String get copied_markdown => '已复制为 Markdown';
597
+
598
+ @override
599
+ String get read_aloud => '朗读';
600
+
601
+ @override
602
+ String get stop_reading => '停止朗读';
603
+
604
+ @override
605
+ String get more => '更多';
606
+
607
+ @override
608
+ String character_count(int length) {
609
+ return '$length 个字符';
610
+ }
611
+
612
+ @override
613
+ String get edit_message => '编辑消息';
614
+
615
+ @override
616
+ String get edit_message_desc => '保存将移除下方助手的回复并重新生成。';
617
+
618
+ @override
619
+ String get save_regenerate => '保存并重新生成';
620
+
621
+ @override
622
+ String get chat_settings_title => '聊天设置';
623
+
624
+ @override
625
+ String get reset_defaults => '重置默认值';
626
+
627
+ @override
628
+ String get parameters_tab => '参数';
629
+
630
+ @override
631
+ String get mcp_tab => 'MCP';
632
+
633
+ @override
634
+ String get temperature => '温度';
635
+
636
+ @override
637
+ String get temperature_desc => '控制随机性:较高 = 创意,较低 = 专注';
638
+
639
+ @override
640
+ String get top_p => 'Top P';
641
+
642
+ @override
643
+ String get top_p_desc => '核采样阈值';
644
+
645
+ @override
646
+ String get max_tokens => '最大 Token 数';
647
+
648
+ @override
649
+ String get max_tokens_desc => '响应限制';
650
+
651
+ @override
652
+ String get context_length => '上下文长度';
653
+
654
+ @override
655
+ String get context_length_desc => '历史窗口';
656
+
657
+ @override
658
+ String get mcp_disabled_warning => 'MCP 已在全局被禁用。请在设置中启用以使用这些功能。';
659
+
660
+ @override
661
+ String get mcp_enable_chat => '为此聊天启用 MCP';
662
+
663
+ @override
664
+ String get auto_execute_tools => '自动执行工具';
665
+
666
+ @override
667
+ String get add_ephemeral_mcp => '添加临时 MCP 服务器';
668
+
669
+ @override
670
+ String get mcp_label_placeholder => '标签';
671
+
672
+ @override
673
+ String get mcp_url_placeholder => 'URL (https://...)';
674
+
675
+ @override
676
+ String get active_integrations => '活动集成';
677
+
678
+ @override
679
+ String get enable_notifications => '启用通知';
680
+
681
+ @override
682
+ String get enable_notifications_desc => '在模型下载完成时获得通知。';
683
+
684
+ @override
685
+ String get chat_history_title => '聊天历史';
686
+
687
+ @override
688
+ String get conversation_just_now => '刚刚';
689
+
690
+ @override
691
+ String conversation_minutes_ago(int minutes) {
692
+ return '$minutes分钟前';
693
+ }
694
+
695
+ @override
696
+ String conversation_hours_ago(int hours) {
697
+ return '$hours小时前';
698
+ }
699
+
700
+ @override
701
+ String get conversation_yesterday => '昨天';
702
+
703
+ @override
704
+ String conversation_days_ago(int days) {
705
+ return '$days天前';
706
+ }
707
+
708
+ @override
709
+ String conversation_date(int month, int day, int year) {
710
+ return '$month/$day/$year';
711
+ }
712
+
713
+ @override
714
+ String get options_tooltip => '选项';
715
+
716
+ @override
717
+ String get no_results_found => '未找到结果';
718
+
719
+ @override
720
+ String get no_conversations_yet => '尚无对话';
721
+
722
+ @override
723
+ String get try_different_search => '尝试不同的搜索词';
724
+
725
+ @override
726
+ String get start_new_conversation => '开始新对话';
727
+
728
+ @override
729
+ String get rename_conversation => '重命名对话';
730
+
731
+ @override
732
+ String get enter_new_title => '输入新标题';
733
+
734
+ @override
735
+ String get pinned_section => '已 pin';
736
+
737
+ @override
738
+ String get today_section => '今天';
739
+
740
+ @override
741
+ String get yesterday_section => '昨天';
742
+
743
+ @override
744
+ String get previous_7_days => '前 7 天';
745
+
746
+ @override
747
+ String get previous_30_days => '前 30 天';
748
+
749
+ @override
750
+ String get older_section => '更早';
751
+
752
+ @override
753
+ String get onboarding_localmind => 'LOCALMIND';
754
+
755
+ @override
756
+ String get onboarding_connect_server => '连接您的\n服务器';
757
+
758
+ @override
759
+ String get onboarding_connect_desc =>
760
+ '连接到 LM Studio、Ollama 或\nOpenRouter,开启您的私有 AI\n体验。';
761
+
762
+ @override
763
+ String get server_type_on_device => '设备端';
764
+
765
+ @override
766
+ String get server_type_on_device_sub => '无需服务器';
767
+
768
+ @override
769
+ String get server_type_lm_studio => 'LM Studio';
770
+
771
+ @override
772
+ String get server_type_lm_studio_sub => '本地 API';
773
+
774
+ @override
775
+ String get server_type_ollama => 'Ollama';
776
+
777
+ @override
778
+ String get server_type_ollama_sub => 'CLI 引擎';
779
+
780
+ @override
781
+ String get server_type_openrouter => 'OpenRouter';
782
+
783
+ @override
784
+ String get server_type_openrouter_sub => '统一云';
785
+
786
+ @override
787
+ String get ready_continue => '准备好继续';
788
+
789
+ @override
790
+ String get waiting_selection => '等待选择';
791
+
792
+ @override
793
+ String get setup_connection => '设置连接';
794
+
795
+ @override
796
+ String setup_connection_desc(String server) {
797
+ return '配置您的 $server 服务器以开始聊天。';
798
+ }
799
+
800
+ @override
801
+ String get server_name => '服务器名称';
802
+
803
+ @override
804
+ String get name_required => '需要名称';
805
+
806
+ @override
807
+ String get name_max_50 => '最多 50 个字符';
808
+
809
+ @override
810
+ String get host_label => '主机 / IP 地址';
811
+
812
+ @override
813
+ String get host_required => '需要主机地址';
814
+
815
+ @override
816
+ String get port_label => '端口';
817
+
818
+ @override
819
+ String get port_required => '需要端口';
820
+
821
+ @override
822
+ String get port_invalid => '必须是数字';
823
+
824
+ @override
825
+ String get port_range => '输入有效的端口号 (1-65535)';
826
+
827
+ @override
828
+ String get api_key_required => 'API 密钥 *';
829
+
830
+ @override
831
+ String get api_key_optional => 'API 密钥 (可选)';
832
+
833
+ @override
834
+ String get api_key_required_openrouter => 'OpenRouter 需要 API 密钥';
835
+
836
+ @override
837
+ String get api_key_format => 'OpenRouter API 密钥以 sk- 开头';
838
+
839
+ @override
840
+ String get my_server_hint => '我的服务器';
841
+
842
+ @override
843
+ String get name_length_validation => '名称必须在 50 个字符以内';
844
+
845
+ @override
846
+ String get host_valid => '输入有效的主机名或 IP 地址';
847
+
848
+ @override
849
+ String get api_key_hint_openrouter => 'sk-...';
850
+
851
+ @override
852
+ String get api_key_hint_generic => '适用于已验证的服务器';
853
+
854
+ @override
855
+ String get update_server => '更新服务器';
856
+
857
+ @override
858
+ String get save_server => '保存服务器';
859
+
860
+ @override
861
+ String get server_updated => '服务器已更新';
862
+
863
+ @override
864
+ String get server_added => '服务器已添加';
865
+
866
+ @override
867
+ String get download_model_title => '下载模型';
868
+
869
+ @override
870
+ String get download_model_desc => '选择要下载的模型。\n它将在您的设备上本地运行。';
871
+
872
+ @override
873
+ String get on_device_android_only => '设备端推理当前仅可在 Android 上使用。';
874
+
875
+ @override
876
+ String get total_ram => '总内存';
877
+
878
+ @override
879
+ String get available => '可用';
880
+
881
+ @override
882
+ String ram_min_required(String fileSize) {
883
+ return '最低要求 $fileSize GB 内存 (RAM)';
884
+ }
885
+
886
+ @override
887
+ String download_progress(String percent, String speed) {
888
+ return '$percent% • $speed';
889
+ }
890
+
891
+ @override
892
+ String eta_label(String eta) {
893
+ return '预计剩余时间: $eta';
894
+ }
895
+
896
+ @override
897
+ String paused_progress(String percent) {
898
+ return '已暂停 - $percent%';
899
+ }
900
+
901
+ @override
902
+ String ram_warning_body_download(String ram, String totalMemory) {
903
+ return '此模型需要至少 $ram GB 内存,但您的设备仅有 $totalMemory。它可能无法正常运行,或会导致应用崩溃。';
904
+ }
905
+
906
+ @override
907
+ String ram_warning_body_load(String availableRAM, String ram) {
908
+ return '您的设备当前有 $availableRAM 可用内存,但此模型推荐至少 $ram GB。加载它可能会失败或导致系统不稳定。';
909
+ }
910
+
911
+ @override
912
+ String get choose_theme => '选择主题';
913
+
914
+ @override
915
+ String get choose_theme_desc => '个性化应用程序外观。您随时可以在设置中更改此项。';
916
+
917
+ @override
918
+ String get theme_card_system => '系统';
919
+
920
+ @override
921
+ String get theme_card_system_sub => '匹配您的设备设置';
922
+
923
+ @override
924
+ String get theme_card_light => '亮色';
925
+
926
+ @override
927
+ String get theme_card_light_sub => '干净明亮';
928
+
929
+ @override
930
+ String get theme_card_dark => '暗色';
931
+
932
+ @override
933
+ String get theme_card_dark_sub => '保护眼睛';
934
+
935
+ @override
936
+ String get theme_card_claude => 'Claude';
937
+
938
+ @override
939
+ String get theme_card_claude_sub => '温馨的桃色色调主题';
940
+
941
+ @override
942
+ String get stay_updated => '保持更新';
943
+
944
+ @override
945
+ String get stay_updated_desc => '在您的 AI 模型下载完成或长时间运行的任务结束时获得通知。';
946
+
947
+ @override
948
+ String get notification_benefit_downloads => '模型下载进度';
949
+
950
+ @override
951
+ String get notification_benefit_completions => '生成完成';
952
+
953
+ @override
954
+ String get notification_benefit_background => '后台任务状态';
955
+
956
+ @override
957
+ String get allow_notifications => '允许通知';
958
+
959
+ @override
960
+ String get servers_title => '服务器';
961
+
962
+ @override
963
+ String get no_servers_yet => '尚无服务器';
964
+
965
+ @override
966
+ String get no_servers_desc => '添加您的首个服务器以开始与 AI 模型聊天。';
967
+
968
+ @override
969
+ String get add_server => '添加服务器';
970
+
971
+ @override
972
+ String switched_to_server(String name) {
973
+ return '已切换到 $name';
974
+ }
975
+
976
+ @override
977
+ String get edit_server => '编辑服务器';
978
+
979
+ @override
980
+ String get add_server_title => '添加服务器';
981
+
982
+ @override
983
+ String get server_type_label => '服务器类型';
984
+
985
+ @override
986
+ String get server_icon_label => '服务器图标';
987
+
988
+ @override
989
+ String get default_icon => '默认图标';
990
+
991
+ @override
992
+ String get server_type_lm_studio_display => 'LM Studio';
993
+
994
+ @override
995
+ String get server_type_openai_display => '兼容 OpenAI';
996
+
997
+ @override
998
+ String get server_type_ollama_display => 'Ollama';
999
+
1000
+ @override
1001
+ String get server_type_openrouter_display => 'OpenRouter';
1002
+
1003
+ @override
1004
+ String get server_type_on_device_display => '设备端';
1005
+
1006
+ @override
1007
+ String get server_address_openrouter => 'openrouter.ai';
1008
+
1009
+ @override
1010
+ String get server_address_on_device => '本地推理';
1011
+
1012
+ @override
1013
+ String server_address_format(String host, String port) {
1014
+ return '$host:$port';
1015
+ }
1016
+
1017
+ @override
1018
+ String get default_badge => '默认';
1019
+
1020
+ @override
1021
+ String get set_as_default => '设为默认';
1022
+
1023
+ @override
1024
+ String get select_icon => '选择图标';
1025
+
1026
+ @override
1027
+ String get select_icon_desc => '为您的服务器选择一个图标';
1028
+
1029
+ @override
1030
+ String get search_icons_hint => '搜索图标...';
1031
+
1032
+ @override
1033
+ String get server_icon_stack => '服务器堆栈';
1034
+
1035
+ @override
1036
+ String get server_icon_stack2 => '服务器堆栈 02';
1037
+
1038
+ @override
1039
+ String get server_icon_stack3 => '服务器堆栈 03';
1040
+
1041
+ @override
1042
+ String get server_icon_cloud => '云';
1043
+
1044
+ @override
1045
+ String get server_icon_cloud_server => '云端服务器';
1046
+
1047
+ @override
1048
+ String get server_icon_mcp => 'MCP 服务器';
1049
+
1050
+ @override
1051
+ String get server_icon_database => '数据库';
1052
+
1053
+ @override
1054
+ String get server_icon_database1 => '数据库 01';
1055
+
1056
+ @override
1057
+ String get server_icon_database2 => '数据库 02';
1058
+
1059
+ @override
1060
+ String get server_icon_cpu => 'CPU';
1061
+
1062
+ @override
1063
+ String get server_icon_chip => '芯片';
1064
+
1065
+ @override
1066
+ String get server_icon_chip2 => '芯片 02';
1067
+
1068
+ @override
1069
+ String get server_icon_computer => '电脑';
1070
+
1071
+ @override
1072
+ String get server_icon_laptop => '笔记本电脑';
1073
+
1074
+ @override
1075
+ String get server_icon_terminal => '电脑终端';
1076
+
1077
+ @override
1078
+ String get server_icon_code => '代码';
1079
+
1080
+ @override
1081
+ String get server_icon_ai_brain => 'AI 大脑';
1082
+
1083
+ @override
1084
+ String get server_icon_ai_brain2 => 'AI 大脑 02';
1085
+
1086
+ @override
1087
+ String get server_icon_ai_cloud => 'AI 云端';
1088
+
1089
+ @override
1090
+ String get server_icon_ai_network => 'AI 网络';
1091
+
1092
+ @override
1093
+ String get server_icon_ai_chat => 'AI 聊天';
1094
+
1095
+ @override
1096
+ String get server_icon_cellular => '蜂窝网络';
1097
+
1098
+ @override
1099
+ String get server_icon_plug1 => '插头 01';
1100
+
1101
+ @override
1102
+ String get server_icon_plug2 => '插头 02';
1103
+
1104
+ @override
1105
+ String get server_icon_bot => '机器人';
1106
+
1107
+ @override
1108
+ String get server_icon_bot2 => '机器人 02';
1109
+
1110
+ @override
1111
+ String get server_icon_robotic => '机器人声音';
1112
+
1113
+ @override
1114
+ String get server_icon_rocket => '火箭';
1115
+
1116
+ @override
1117
+ String get server_icon_star => 'Star';
1118
+
1119
+ @override
1120
+ String get server_icon_settings1 => '设置 01';
1121
+
1122
+ @override
1123
+ String get server_icon_settings2 => '设置 02';
1124
+
1125
+ @override
1126
+ String get server_icon_home1 => '主页 01';
1127
+
1128
+ @override
1129
+ String get server_icon_home2 => '主页 02';
1130
+
1131
+ @override
1132
+ String get server_icon_folder1 => '文件夹 01';
1133
+
1134
+ @override
1135
+ String get server_icon_folder2 => '文件夹 02';
1136
+
1137
+ @override
1138
+ String get server_icon_file1 => '文件 01';
1139
+
1140
+ @override
1141
+ String get server_icon_lock => '锁';
1142
+
1143
+ @override
1144
+ String get server_icon_key => '钥匙 01';
1145
+
1146
+ @override
1147
+ String get server_icon_link => '链接 01';
1148
+
1149
+ @override
1150
+ String get server_icon_globe => '地球仪';
1151
+
1152
+ @override
1153
+ String get server_icon_api => 'API';
1154
+
1155
+ @override
1156
+ String get server_icon_arrow_right => '右箭头 01';
1157
+
1158
+ @override
1159
+ String get server_icon_check => '勾选圆圈';
1160
+
1161
+ @override
1162
+ String get server_icon_alert => '警报圆圈';
1163
+
1164
+ @override
1165
+ String get server_icon_info => '信息圆圈';
1166
+
1167
+ @override
1168
+ String get server_icon_zap => 'Zap';
1169
+
1170
+ @override
1171
+ String get server_icon_cloud_upload => '云端上传';
1172
+
1173
+ @override
1174
+ String get server_icon_cloud_download => '云端下载';
1175
+
1176
+ @override
1177
+ String get server_icon_refresh => '刷新';
1178
+
1179
+ @override
1180
+ String get server_icon_hard_drive => '硬盘';
1181
+
1182
+ @override
1183
+ String get server_icon_drive => '驱动器';
1184
+
1185
+ @override
1186
+ String get personas_title => '角色';
1187
+
1188
+ @override
1189
+ String get persona_category_general => '通用';
1190
+
1191
+ @override
1192
+ String get persona_category_coding => '编程';
1193
+
1194
+ @override
1195
+ String get persona_category_education => '教育';
1196
+
1197
+ @override
1198
+ String get persona_category_creative => '创意';
1199
+
1200
+ @override
1201
+ String get persona_builtin_section => '内置';
1202
+
1203
+ @override
1204
+ String get persona_my_section => '我的角色';
1205
+
1206
+ @override
1207
+ String get clone_edit => '克隆并编辑';
1208
+
1209
+ @override
1210
+ String get builtin_badge => '内置';
1211
+
1212
+ @override
1213
+ String get no_personas_found => '未找到任何角色';
1214
+
1215
+ @override
1216
+ String get no_personas_desc => '创建您的第一个角色以定制 AI 行为。';
1217
+
1218
+ @override
1219
+ String get edit_persona => '编辑角色';
1220
+
1221
+ @override
1222
+ String get create_persona => '创建角色';
1223
+
1224
+ @override
1225
+ String get create_persona_button => '创建';
1226
+
1227
+ @override
1228
+ String get emoji_label => '表情符号';
1229
+
1230
+ @override
1231
+ String get name_label => '名称';
1232
+
1233
+ @override
1234
+ String get my_persona_hint => '我的角色';
1235
+
1236
+ @override
1237
+ String get category_label => '分类';
1238
+
1239
+ @override
1240
+ String get description_optional => '描述 (可选)';
1241
+
1242
+ @override
1243
+ String get description_hint => '此角色要做的是...';
1244
+
1245
+ @override
1246
+ String get system_prompt => '系统提示词';
1247
+
1248
+ @override
1249
+ String character_count_max(int currentLen) {
1250
+ return '$currentLen/4000';
1251
+ }
1252
+
1253
+ @override
1254
+ String get no_prompt_placeholder => '尚无提示词...';
1255
+
1256
+ @override
1257
+ String get prompt_hint => '你是一个得力的助手...';
1258
+
1259
+ @override
1260
+ String get prompt_required => '需要系统提示词';
1261
+
1262
+ @override
1263
+ String get prompt_max_chars => '最多 4000 个字符';
1264
+
1265
+ @override
1266
+ String get advanced_settings => '高级设置';
1267
+
1268
+ @override
1269
+ String get temperature_label => '温度 (0.0-2.0)';
1270
+
1271
+ @override
1272
+ String get top_p_label => 'Top P (0.0-1.0)';
1273
+
1274
+ @override
1275
+ String get temp_hint => '0.7';
1276
+
1277
+ @override
1278
+ String get top_p_hint => '0.9';
1279
+
1280
+ @override
1281
+ String get range_0_2 => '0.0-2.0';
1282
+
1283
+ @override
1284
+ String get range_0_1 => '0.0-1.0';
1285
+
1286
+ @override
1287
+ String get persona_updated => '角色已更新';
1288
+
1289
+ @override
1290
+ String get persona_created => '角色已创建';
1291
+
1292
+ @override
1293
+ String get tts_models_title => '语音合成模型';
1294
+
1295
+ @override
1296
+ String get always_available => '始终可用';
1297
+
1298
+ @override
1299
+ String get tts_system_desc => '使用您设备内置的语音合成引擎。\n无需额外下载。语音选择依赖您的设备系统设置。';
1300
+
1301
+ @override
1302
+ String get downloading_status => '正在下载...';
1303
+
1304
+ @override
1305
+ String tts_kitten_desc(String size) {
1306
+ return '拥有 8 种表现力声音的闪电般高速神经网络 TTS。\n需要下载 $size。';
1307
+ }
1308
+
1309
+ @override
1310
+ String tts_piper_desc(String size) {
1311
+ return '快速离线 Piper 语音,拥有 2 种极具表现力的声音。\n每个声音需要下载 $size。';
1312
+ }
1313
+
1314
+ @override
1315
+ String engine_spec(String sizeMb, String ramMb, int voiceCount) {
1316
+ return '$sizeMb MB · $ramMb MB 内存 · $voiceCount 种语音';
1317
+ }
1318
+
1319
+ @override
1320
+ String get on_device_models_title => '设备端模型';
1321
+
1322
+ @override
1323
+ String get available_models => '可用模型';
1324
+
1325
+ @override
1326
+ String get device_memory => '设备内存';
1327
+
1328
+ @override
1329
+ String get ram_usage => '内存占用';
1330
+
1331
+ @override
1332
+ String get memory_healthy => '健康';
1333
+
1334
+ @override
1335
+ String get memory_critical => '严重警告';
1336
+
1337
+ @override
1338
+ String get memory_low => '偏低';
1339
+
1340
+ @override
1341
+ String ram_used(String percent) {
1342
+ return '已使用 $percent%';
1343
+ }
1344
+
1345
+ @override
1346
+ String get available_ram => '可用内存';
1347
+
1348
+ @override
1349
+ String get total_capacity => '总容量';
1350
+
1351
+ @override
1352
+ String get loaded_status => '已加载';
1353
+
1354
+ @override
1355
+ String get inference_backend => '推理后端';
1356
+
1357
+ @override
1358
+ String get backend_ios_notice => 'iOS 上仅支持 CPU 后端。';
1359
+
1360
+ @override
1361
+ String get backend_cpu_desc => '适用于所有设备。兼容性最好。';
1362
+
1363
+ @override
1364
+ String get backend_gpu_desc => 'OpenCL 加速。在支持 of 设备上运行更快。';
1365
+
1366
+ @override
1367
+ String get backend_npu_desc => '厂商 NPU (高通/联发科)。最快的推理速度。';
1368
+
1369
+ @override
1370
+ String get select_model_title => '选择模型';
1371
+
1372
+ @override
1373
+ String get refresh_models => '刷新模型';
1374
+
1375
+ @override
1376
+ String get search_models_hint => '搜索模型...';
1377
+
1378
+ @override
1379
+ String get no_server_connected => '未连接服务器';
1380
+
1381
+ @override
1382
+ String get add_server_first => '请先添加服务器以查看可用模型。';
1383
+
1384
+ @override
1385
+ String get failed_load_models => '加载模型失败';
1386
+
1387
+ @override
1388
+ String get no_models_available => '无可用模型';
1389
+
1390
+ @override
1391
+ String no_models_match(String searchQuery) {
1392
+ return '没有匹配 \"$searchQuery\" 的模型';
1393
+ }
1394
+
1395
+ @override
1396
+ String model_load_failed(String error) {
1397
+ return '加载模型失败: $error';
1398
+ }
1399
+
1400
+ @override
1401
+ String model_unloaded_ollama(String name) {
1402
+ return '在 keep-alive 时间过去后,$name 将被自动卸载';
1403
+ }
1404
+
1405
+ @override
1406
+ String model_unloaded_success(String name) {
1407
+ return '$name 已成功卸载';
1408
+ }
1409
+
1410
+ @override
1411
+ String model_unload_failed(String error) {
1412
+ return '卸载模型失败: $error';
1413
+ }
1414
+
1415
+ @override
1416
+ String get unload_from_server => '从服务器卸载';
1417
+
1418
+ @override
1419
+ String context_chip(String ctx) {
1420
+ return '$ctx 上下文 (ctx)';
1421
+ }
1422
+
1423
+ @override
1424
+ String download_notification_title(String modelName) {
1425
+ return '正在下载 $modelName...';
1426
+ }
1427
+
1428
+ @override
1429
+ String get download_complete_notification => '下载完成!';
1430
+
1431
+ @override
1432
+ String download_complete_body(String modelName) {
1433
+ return '$modelName 已成功下载。';
1434
+ }
1435
+
1436
+ @override
1437
+ String download_failed_notification(String error) {
1438
+ return '下载失败: $error';
1439
+ }
1440
+
1441
+ @override
1442
+ String download_failed_body(String modelName) {
1443
+ return '下载 $modelName 失败。';
1444
+ }
1445
+
1446
+ @override
1447
+ String get engine_name_system => '系统 TTS';
1448
+
1449
+ @override
1450
+ String get engine_tagline_system => '内置设备引擎';
1451
+
1452
+ @override
1453
+ String get engine_name_kitten => 'Kitten TTS';
1454
+
1455
+ @override
1456
+ String get engine_tagline_kitten => '高速神经网络 TTS';
1457
+
1458
+ @override
1459
+ String get engine_name_sherpa => 'Sherpa ONNX VITS';
1460
+
1461
+ @override
1462
+ String get engine_tagline_sherpa => '离线 Piper 语音';
1463
+
1464
+ @override
1465
+ String get voice_jasper => 'Jasper';
1466
+
1467
+ @override
1468
+ String get voice_bella => 'Bella';
1469
+
1470
+ @override
1471
+ String get voice_bruno => 'Bruno';
1472
+
1473
+ @override
1474
+ String get voice_luna => 'Luna';
1475
+
1476
+ @override
1477
+ String get voice_hugo => 'Hugo';
1478
+
1479
+ @override
1480
+ String get voice_rosie => 'Rosie';
1481
+
1482
+ @override
1483
+ String get voice_leo => 'Leo';
1484
+
1485
+ @override
1486
+ String get voice_kiki => 'Kiki';
1487
+
1488
+ @override
1489
+ String get voice_lessac => 'Lessac (美音)';
1490
+
1491
+ @override
1492
+ String get voice_ryan => 'Ryan (美音)';
1493
+
1494
+ @override
1495
+ String get model_qwen_3 => 'Qwen 3 0.6B';
1496
+
1497
+ @override
1498
+ String get model_qwen_3_desc => '最小的通用聊天模型。响应速度快,内存消耗低。';
1499
+
1500
+ @override
1501
+ String get model_license_apache => 'Apache-2.0';
1502
+
1503
+ @override
1504
+ String get model_qwen_25 => 'Qwen 2.5 1.5B Instruct';
1505
+
1506
+ @override
1507
+ String get model_qwen_25_desc => '平衡的质量和大小。适合一般对话。';
1508
+
1509
+ @override
1510
+ String get model_deepseek => 'DeepSeek R1 Distill Qwen 1.5B';
1511
+
1512
+ @override
1513
+ String get model_deepseek_desc => '推理与思维链模型。最适合逻辑任务。';
1514
+
1515
+ @override
1516
+ String get model_license_mit => 'MIT';
1517
+
1518
+ @override
1519
+ String get model_gemma => 'Gemma 4 E2B Instruct';
1520
+
1521
+ @override
1522
+ String get model_gemma_desc => 'Google 旗舰模型。最高质量,需要更多内存。';
1523
+
1524
+ @override
1525
+ String export_header(String date) {
1526
+ return '*导出自 LocalMind — $date*';
1527
+ }
1528
+
1529
+ @override
1530
+ String get export_role_user => '## 👤 用户';
1531
+
1532
+ @override
1533
+ String get export_role_assistant => '## 🤖 助手';
1534
+
1535
+ @override
1536
+ String get export_role_system => '## ⚙️ 系统';
1537
+
1538
+ @override
1539
+ String get export_role_tool => '## 🔧 工具';
1540
+
1541
+ @override
1542
+ String get export_text_user => '[用户]';
1543
+
1544
+ @override
1545
+ String get export_text_assistant => '[助手]';
1546
+
1547
+ @override
1548
+ String get export_text_system => '[系统]';
1549
+
1550
+ @override
1551
+ String get export_text_tool => '[工具]';
1552
+
1553
+ @override
1554
+ String get export_label_user => '用户';
1555
+
1556
+ @override
1557
+ String get export_label_assistant => '助手';
1558
+
1559
+ @override
1560
+ String get export_label_system => '系统';
1561
+
1562
+ @override
1563
+ String get export_label_tool => '工具';
1564
+
1565
+ @override
1566
+ String get select_model_hint => '选择一个模型以开始聊天';
1567
+
1568
+ @override
1569
+ String get test_notification_title => '测试通知';
1570
+
1571
+ @override
1572
+ String get test_notification_body => '这是用于展示模型下载进度的测试通知。';
1573
+}
lib/l10n/app_zh.arb
new
+495
@@ -0,0 +1,495 @@
1
+{
2
+ "@@locale": "zh",
3
+ "active": "已启用",
4
+ "active_integrations": "活动集成",
5
+ "add_ephemeral_mcp": "添加临时 MCP 服务器",
6
+ "add_server": "添加服务器",
7
+ "add_server_first": "请先添加服务器以查看可用模型。",
8
+ "add_server_title": "添加服务器",
9
+ "advanced_settings": "高级设置",
10
+ "all": "全部",
11
+ "allow_notifications": "允许通知",
12
+ "always_available": "始终可用",
13
+ "api_key_format": "OpenRouter API 密钥以 sk- 开头",
14
+ "api_key_hint_generic": "适用于已验证的服务器",
15
+ "api_key_hint_openrouter": "sk-...",
16
+ "api_key_optional": "API 密钥 (可选)",
17
+ "api_key_required": "API 密钥 *",
18
+ "api_key_required_openrouter": "OpenRouter 需要 API 密钥",
19
+ "app_name": "LocalMind",
20
+ "app_tagline": "您的 AI。您的设备。您的规则。",
21
+ "app_version": "1.0.0",
22
+ "attach_images_tooltip": "附加图片",
23
+ "auto_execute_tools": "自动执行工具",
24
+ "auto_generate_titles": "自动生成标题",
25
+ "available": "可用",
26
+ "available_models": "可用模型",
27
+ "available_ram": "可用内存",
28
+ "backend_cpu_desc": "适用于所有设备。兼容性最好。",
29
+ "backend_gpu_desc": "OpenCL 加速。在支持 of 设备上运行更快。",
30
+ "backend_ios_notice": "iOS 上仅支持 CPU 后端。",
31
+ "backend_npu_desc": "厂商 NPU (高通/联发科)。最快的推理速度。",
32
+ "builtin_badge": "内置",
33
+ "calculating": "正在计算...",
34
+ "cancel": "取消",
35
+ "cannot_undo": "此操作无法撤销。",
36
+ "category_label": "分类",
37
+ "change_persona": "更换角色",
38
+ "character_count": "{length} 个字符",
39
+ "character_count_max": "{currentLen}/4000",
40
+ "chat_history_title": "聊天历史",
41
+ "chat_input_hint": "问任何问题",
42
+ "chat_parameters_tooltip": "聊天参数",
43
+ "chat_settings_title": "聊天设置",
44
+ "chat_title": "LocalMind",
45
+ "choose_theme": "选择主题",
46
+ "choose_theme_desc": "个性化应用程序外观。您随时可以在设置中更改此项。",
47
+ "clear": "清除",
48
+ "clear_conversation": "清除对话",
49
+ "clear_conversation_body": "这会删除此对话中的所有消息。",
50
+ "clear_conversation_title": "清除对话吗?",
51
+ "clone_edit": "克隆并编辑",
52
+ "close": "关闭",
53
+ "code_theme_dark": "代码主题 (暗色)",
54
+ "code_theme_desc": "选择代码块的语法高亮主题。",
55
+ "code_theme_light": "代码主题 (亮色)",
56
+ "configure": "配置",
57
+ "configuring_server": "正在配置服务器...",
58
+ "confirm": "确认",
59
+ "connection_error": "连接错误。请检查您的服务器。",
60
+ "connection_failed": "连接失败。请检查您的设置。",
61
+ "connection_successful": "连接成功!",
62
+ "context_chip": "{ctx} 上下文 (ctx)",
63
+ "context_length": "上下文长度",
64
+ "context_length_desc": "历史窗口",
65
+ "continue_action": "继续",
66
+ "conversation_date": "{month}/{day}/{year}",
67
+ "conversation_days_ago": "{days}天前",
68
+ "conversation_hours_ago": "{hours}小时前",
69
+ "conversation_id": "对话 ID",
70
+ "conversation_just_now": "刚刚",
71
+ "conversation_minutes_ago": "{minutes}分钟前",
72
+ "conversation_yesterday": "昨天",
73
+ "copied": "已复制!",
74
+ "copied_markdown": "已复制为 Markdown",
75
+ "copied_to_clipboard": "已复制到剪贴板",
76
+ "copy": "复制",
77
+ "copy_info": "复制信息",
78
+ "copy_markdown": "复制为 Markdown",
79
+ "create_persona": "创建角色",
80
+ "create_persona_button": "创建",
81
+ "created_at": "创建于",
82
+ "debug_dialog_desc": "用以帮助识别同步问题的诊断信息。",
83
+ "default_badge": "默认",
84
+ "default_icon": "默认图标",
85
+ "delete": "删除",
86
+ "delete_all_conversations": "删除所有对话",
87
+ "delete_conversation": "删除对话吗?",
88
+ "delete_conversation_body": "您确定要删除 \"{title}\" 吗?此操作无法撤销。",
89
+ "delete_conversation_title": "删除对话吗?",
90
+ "delete_message_title": "删除消息吗?",
91
+ "delete_model_body": "您确定要删除 {name} 吗?",
92
+ "delete_model_body_with_size": "您确定要删除 {name} 吗?这释放大约 {size} 的空间。\n\n如有需要,您稍后可以重新下载此模型。",
93
+ "delete_model_title": "删除模型",
94
+ "delete_persona_body": "此操作无法撤销。",
95
+ "delete_persona_title": "删除 \"{name}\" 吗?",
96
+ "delete_server_body": "您确定要删除 \"{name}\" 吗?此操作无法撤销。",
97
+ "delete_server_title": "删除服务器",
98
+ "delete_voice_body": "您确定要删除 {name} 吗?这释放大约 {size} 的空间。\n\n如有需要,您稍后可以重新下载此语音。",
99
+ "delete_voice_title": "删除语音",
100
+ "description_hint": "此角色要做的是...",
101
+ "description_optional": "描述 (可选)",
102
+ "device_memory": "设备内存",
103
+ "disconnected": "与服务器断开连接。",
104
+ "done": "完成",
105
+ "download": "下载",
106
+ "download_complete_body": "{modelName} 已成功下载。",
107
+ "download_complete_notification": "下载完成!",
108
+ "download_failed": "下载失败",
109
+ "download_failed_body": "下载 {modelName} 失败。",
110
+ "download_failed_notification": "下载失败: {error}",
111
+ "download_model_desc": "选择要下载的模型。\n它将在您的设备上本地运行。",
112
+ "download_model_title": "下载模型",
113
+ "download_notification_title": "正在下载 {modelName}...",
114
+ "download_progress": "{percent}% • {speed}",
115
+ "downloaded": "已下载",
116
+ "downloading_status": "正在下载...",
117
+ "edit": "编辑",
118
+ "edit_message": "编辑消息",
119
+ "edit_message_desc": "保存将移除下方助手的回复并重新生成。",
120
+ "edit_persona": "编辑角色",
121
+ "edit_server": "编辑服务器",
122
+ "emoji_label": "表情符号",
123
+ "enable": "启用",
124
+ "enable_mcp": "启用 MCP",
125
+ "enable_notifications": "启用通知",
126
+ "enable_notifications_desc": "在模型下载完成时获得通知。",
127
+ "engine_name_kitten": "Kitten TTS",
128
+ "engine_name_sherpa": "Sherpa ONNX VITS",
129
+ "engine_name_system": "系统 TTS",
130
+ "engine_spec": "{sizeMb} MB · {ramMb} MB 内存 · {voiceCount} 种语音",
131
+ "engine_tagline_kitten": "高速神经网络 TTS",
132
+ "engine_tagline_sherpa": "离线 Piper 语音",
133
+ "engine_tagline_system": "内置设备引擎",
134
+ "enter_new_title": "输入新标题",
135
+ "error": "错误",
136
+ "error_with_message": "错误: {error}",
137
+ "eta_label": "预计剩余时间: {eta}",
138
+ "expected_messages": "预期消息",
139
+ "export_header": "*导出自 LocalMind — {date}*",
140
+ "export_label_assistant": "助手",
141
+ "export_label_system": "系统",
142
+ "export_label_tool": "工具",
143
+ "export_label_user": "用户",
144
+ "export_role_assistant": "## 🤖 助手",
145
+ "export_role_system": "## ⚙️ 系统",
146
+ "export_role_tool": "## 🔧 工具",
147
+ "export_role_user": "## 👤 用户",
148
+ "export_text_assistant": "[助手]",
149
+ "export_text_system": "[系统]",
150
+ "export_text_tool": "[工具]",
151
+ "export_text_user": "[用户]",
152
+ "failed_load_models": "加载模型失败",
153
+ "finish_setup": "完成设置",
154
+ "font_preview": "敏捷的棕色狐狸跳过那只懒狗。",
155
+ "font_size": "字体大小",
156
+ "font_size_desc": "调整聊天中的文本大小。",
157
+ "haptic_feedback": "触觉反馈",
158
+ "history_missing_desc": "此聊天中的消息已被删除,或历史记录已损坏。",
159
+ "history_missing_title": "历史记录丢失",
160
+ "host_label": "主机 / IP 地址",
161
+ "host_required": "需要主机地址",
162
+ "host_valid": "输入有效的主机名或 IP 地址",
163
+ "inference_backend": "推理后端",
164
+ "initializing": "正在初始化...",
165
+ "initializing_services": "正在初始化服务...",
166
+ "install": "安装",
167
+ "installed": "已安装",
168
+ "label_completed": "{label} 已完成",
169
+ "language_system_default": "系统默认",
170
+ "last_error": "上次错误:",
171
+ "load": "加载",
172
+ "loaded_status": "已加载",
173
+ "loading": "正在加载...",
174
+ "loading_model": "正在加载 {modelId}...",
175
+ "loading_model_error": "错误: {error}",
176
+ "manage_on_device_models": "管理设备端模型",
177
+ "manage_servers": "管理服务器",
178
+ "manage_tts_models": "管理 TTS 模型",
179
+ "max_tokens": "最大 Token 数",
180
+ "max_tokens_desc": "响应限制",
181
+ "may_be_large": "可能对于此设备来说过大",
182
+ "mcp_disabled_warning": "MCP 已在全局被禁用。请在设置中启用以使用这些功能。",
183
+ "mcp_enable_chat": "为此聊天启用 MCP",
184
+ "mcp_label_placeholder": "标签",
185
+ "mcp_tab": "MCP",
186
+ "mcp_url_placeholder": "URL (https://...)",
187
+ "memory_critical": "严重警告",
188
+ "memory_healthy": "健康",
189
+ "memory_low": "偏低",
190
+ "message_options": "消息选项",
191
+ "model_deepseek": "DeepSeek R1 Distill Qwen 1.5B",
192
+ "model_deepseek_desc": "推理与思维链模型。最适合逻辑任务。",
193
+ "model_gemma": "Gemma 4 E2B Instruct",
194
+ "model_gemma_desc": "Google 旗舰模型。最高质量,需要更多内存。",
195
+ "model_license_apache": "Apache-2.0",
196
+ "model_license_mit": "MIT",
197
+ "model_load_failed": "加载模型失败: {error}",
198
+ "model_loaded": "模型已加载: {modelId} ({backend})",
199
+ "model_qwen_25": "Qwen 2.5 1.5B Instruct",
200
+ "model_qwen_25_desc": "平衡的质量和大小。适合一般对话。",
201
+ "model_qwen_3": "Qwen 3 0.6B",
202
+ "model_qwen_3_desc": "最小的通用聊天模型。响应速度快,内存消耗低。",
203
+ "model_unload_failed": "卸载模型失败: {error}",
204
+ "model_unloaded_ollama": "在 keep-alive 时间过去后,{name} 将被自动卸载",
205
+ "model_unloaded_success": "{name} 已成功卸载",
206
+ "more": "更多",
207
+ "my_persona_hint": "我的角色",
208
+ "my_server_hint": "我的服务器",
209
+ "name_label": "名称",
210
+ "name_length_validation": "名称必须在 50 个字符以内",
211
+ "name_max_50": "最多 50 个字符",
212
+ "name_required": "需要名称",
213
+ "nav_history": "历史",
214
+ "nav_local_models": "本地模型",
215
+ "nav_new_chat": "新聊天",
216
+ "nav_personas": "角色",
217
+ "nav_servers": "服务器",
218
+ "nav_settings": "设置",
219
+ "nav_tts": "语音合成",
220
+ "new_chat_mcp_default": "新聊天默认启用 MCP",
221
+ "no_conversations_yet": "尚无对话",
222
+ "no_model_loaded": "未加载任何模型。点击 “管理设备端模型” 以下载并加载模型。",
223
+ "no_models_available": "无可用模型",
224
+ "no_models_match": "没有匹配 \"{searchQuery}\" 的模型",
225
+ "no_personas_desc": "创建您的第一个角色以定制 AI 行为。",
226
+ "no_personas_found": "未找到任何角色",
227
+ "no_prompt_placeholder": "尚无提示词...",
228
+ "no_results_found": "未找到结果",
229
+ "no_server_connected": "未连接服务器",
230
+ "no_server_selected": "未选择服务器",
231
+ "no_servers_desc": "添加您的首个服务器以开始与 AI 模型聊天。",
232
+ "no_servers_yet": "尚无服务器",
233
+ "none": "无",
234
+ "none_selected": "未选择",
235
+ "not_downloaded": "未下载",
236
+ "not_installed": "未安装",
237
+ "not_now": "暂不",
238
+ "notification_benefit_background": "后台任务状态",
239
+ "notification_benefit_completions": "生成完成",
240
+ "notification_benefit_downloads": "模型下载进度",
241
+ "offline": "离线",
242
+ "older_section": "更早",
243
+ "on_device_android_only": "设备端推理当前仅可在 Android 上使用。",
244
+ "on_device_models_title": "设备端模型",
245
+ "onboarding_connect_desc": "连接到 LM Studio、Ollama 或\nOpenRouter,开启您的私有 AI\n体验。",
246
+ "onboarding_connect_server": "连接您的\n服务器",
247
+ "onboarding_localmind": "LOCALMIND",
248
+ "online": "在线",
249
+ "open_source": "开源",
250
+ "open_source_desc": "LocalMind 是开源项目。可在 GitHub 上关注我们的进展或参与贡献。",
251
+ "options_tooltip": "选项",
252
+ "parameters_tab": "参数",
253
+ "pause": "暂停",
254
+ "paused_progress": "已暂停 - {percent}%",
255
+ "persona_builtin_section": "内置",
256
+ "persona_category_coding": "编程",
257
+ "persona_category_creative": "创意",
258
+ "persona_category_education": "教育",
259
+ "persona_category_general": "通用",
260
+ "persona_created": "角色已创建",
261
+ "persona_my_section": "我的角色",
262
+ "persona_updated": "角色已更新",
263
+ "personas_title": "角色",
264
+ "pin": "Pin",
265
+ "pinned_section": "已 pin",
266
+ "port_invalid": "必须是数字",
267
+ "port_label": "端口",
268
+ "port_range": "输入有效的端口号 (1-65535)",
269
+ "port_required": "需要端口",
270
+ "preparing_app": "正在准备应用...",
271
+ "preview": "预览",
272
+ "preview_failed": "预览失败: {error}",
273
+ "previous_30_days": "前 30 天",
274
+ "previous_7_days": "前 7 天",
275
+ "privacy_info": "\"LocalMind 绝不查看您的数据\"",
276
+ "proceed_anyway": "仍然继续",
277
+ "processing": "正在处理...",
278
+ "prompt_hint": "你是一个得力的助手...",
279
+ "prompt_max_chars": "最多 4000 个字符",
280
+ "prompt_required": "需要系统提示词",
281
+ "quick_async": "我该如何使用 async/await?",
282
+ "quick_debug": "帮我调试这个",
283
+ "quick_explain": "解释这段代码",
284
+ "quick_write": "帮我写一个函数",
285
+ "ram_min_required": "最低要求 {fileSize} GB 内存 (RAM)",
286
+ "ram_usage": "内存占用",
287
+ "ram_used": "已使用 {percent}%",
288
+ "ram_warning": "内存警告",
289
+ "ram_warning_body_download": "此模型需要至少 {ram} GB 内存,但您的设备仅有 {totalMemory}。它可能无法正常运行,或会导致应用崩溃。",
290
+ "ram_warning_body_load": "您的设备当前有 {availableRAM} 可用内存,但此模型推荐至少 {ram} GB。加载它可能会失败或导致系统不稳定。",
291
+ "range_0_1": "0.0-1.0",
292
+ "range_0_2": "0.0-2.0",
293
+ "read_aloud": "朗读",
294
+ "ready": "就绪",
295
+ "ready_continue": "准备好继续",
296
+ "recent_chats": "最近聊天",
297
+ "recommended": "推荐",
298
+ "refresh_models": "刷新模型",
299
+ "remove_persona": "删除角色",
300
+ "rename": "重命名",
301
+ "rename_conversation": "重命名对话",
302
+ "reset_defaults": "重置默认值",
303
+ "reset_settings_defaults": "重置设置至默认值",
304
+ "resume": "继续",
305
+ "retry": "重试",
306
+ "save": "保存",
307
+ "save_changes": "保存更改",
308
+ "save_continue": "保存并继续",
309
+ "save_regenerate": "保存并重新生成",
310
+ "save_server": "保存服务器",
311
+ "search_hint": "搜索对话...",
312
+ "search_icons_hint": "搜索图标...",
313
+ "search_models_hint": "搜索模型...",
314
+ "see_all": "查看全部",
315
+ "select": "选择",
316
+ "select_icon": "选择图标",
317
+ "select_icon_desc": "为您的服务器选择一个图标",
318
+ "select_model": "选择模型",
319
+ "select_model_hint": "选择一个模型以开始聊天",
320
+ "select_model_title": "选择模型",
321
+ "select_persona": "选择角色",
322
+ "send_message_tooltip": "发送消息",
323
+ "send_on_enter": "按 Enter 键发送",
324
+ "server_added": "服务器已添加",
325
+ "server_address_format": "{host}:{port}",
326
+ "server_address_on_device": "本地推理",
327
+ "server_address_openrouter": "openrouter.ai",
328
+ "server_icon_ai_brain": "AI 大脑",
329
+ "server_icon_ai_brain2": "AI 大脑 02",
330
+ "server_icon_ai_chat": "AI 聊天",
331
+ "server_icon_ai_cloud": "AI 云端",
332
+ "server_icon_ai_network": "AI 网络",
333
+ "server_icon_alert": "警报圆圈",
334
+ "server_icon_api": "API",
335
+ "server_icon_arrow_right": "右箭头 01",
336
+ "server_icon_bot": "机器人",
337
+ "server_icon_bot2": "机器人 02",
338
+ "server_icon_cellular": "蜂窝网络",
339
+ "server_icon_check": "勾选圆圈",
340
+ "server_icon_chip": "芯片",
341
+ "server_icon_chip2": "芯片 02",
342
+ "server_icon_cloud": "云",
343
+ "server_icon_cloud_download": "云端下载",
344
+ "server_icon_cloud_server": "云端服务器",
345
+ "server_icon_cloud_upload": "云端上传",
346
+ "server_icon_code": "代码",
347
+ "server_icon_computer": "电脑",
348
+ "server_icon_cpu": "CPU",
349
+ "server_icon_database": "数据库",
350
+ "server_icon_database1": "数据库 01",
351
+ "server_icon_database2": "数据库 02",
352
+ "server_icon_drive": "驱动器",
353
+ "server_icon_file1": "文件 01",
354
+ "server_icon_folder1": "文件夹 01",
355
+ "server_icon_folder2": "文件夹 02",
356
+ "server_icon_globe": "地球仪",
357
+ "server_icon_hard_drive": "硬盘",
358
+ "server_icon_home1": "主页 01",
359
+ "server_icon_home2": "主页 02",
360
+ "server_icon_info": "信息圆圈",
361
+ "server_icon_key": "钥匙 01",
362
+ "server_icon_label": "服务器图标",
363
+ "server_icon_laptop": "笔记本电脑",
364
+ "server_icon_link": "链接 01",
365
+ "server_icon_lock": "锁",
366
+ "server_icon_mcp": "MCP 服务器",
367
+ "server_icon_plug1": "插头 01",
368
+ "server_icon_plug2": "插头 02",
369
+ "server_icon_refresh": "刷新",
370
+ "server_icon_robotic": "机器人声音",
371
+ "server_icon_rocket": "火箭",
372
+ "server_icon_settings1": "设置 01",
373
+ "server_icon_settings2": "设置 02",
374
+ "server_icon_stack": "服务器堆栈",
375
+ "server_icon_stack2": "服务器堆栈 02",
376
+ "server_icon_stack3": "服务器堆栈 03",
377
+ "server_icon_star": "Star",
378
+ "server_icon_terminal": "电脑终端",
379
+ "server_icon_zap": "Zap",
380
+ "server_name": "服务器名称",
381
+ "server_type_label": "服务器类型",
382
+ "server_type_lm_studio": "LM Studio",
383
+ "server_type_lm_studio_display": "LM Studio",
384
+ "server_type_lm_studio_sub": "本地 API",
385
+ "server_type_ollama": "Ollama",
386
+ "server_type_ollama_display": "Ollama",
387
+ "server_type_ollama_sub": "CLI 引擎",
388
+ "server_type_on_device": "设备端",
389
+ "server_type_on_device_display": "设备端",
390
+ "server_type_on_device_sub": "无需服务器",
391
+ "server_type_openai_display": "兼容 OpenAI",
392
+ "server_type_openrouter": "OpenRouter",
393
+ "server_type_openrouter_display": "OpenRouter",
394
+ "server_type_openrouter_sub": "统一云",
395
+ "server_updated": "服务器已更新",
396
+ "servers_title": "服务器",
397
+ "set_as_default": "设为默认",
398
+ "set_persona": "设置角色",
399
+ "settings_about": "关于",
400
+ "settings_appearance": "外观",
401
+ "settings_behavior": "行为",
402
+ "settings_data_management": "数据管理",
403
+ "settings_default_persona": "默认角色",
404
+ "settings_default_server": "默认服务器",
405
+ "settings_language": "语言",
406
+ "settings_on_device": "设备端推理",
407
+ "settings_privacy": "隐私",
408
+ "settings_title": "设置",
409
+ "settings_tts": "语音合成 (TTS)",
410
+ "setup_connection": "设置连接",
411
+ "setup_connection_desc": "配置您的 {server} 服务器以开始聊天。",
412
+ "share": "分享",
413
+ "show_data_indicator": "显示数据指示器",
414
+ "show_system_messages": "显示系统消息",
415
+ "skip": "跳过",
416
+ "something_went_wrong": "出了点问题",
417
+ "star_on_github": "在 GitHub 上点亮 Star",
418
+ "start_conversation": "开始对话",
419
+ "start_new_chat": "开始新聊天",
420
+ "start_new_conversation": "开始新对话",
421
+ "startup_failed": "启动失败",
422
+ "stay_updated": "保持更新",
423
+ "stay_updated_desc": "在您的 AI 模型下载完成或长时间运行的任务结束时获得通知。",
424
+ "stop": "停止",
425
+ "stop_generation_tooltip": "停止生成",
426
+ "stop_reading": "停止朗读",
427
+ "streaming_responses": "流式响应",
428
+ "switch_server": "切换服务器",
429
+ "switch_server_subtitle": "选择要连接的服务器",
430
+ "switched_to_server": "已切换到 {name}",
431
+ "system_prompt": "系统提示词",
432
+ "technical_details": "技术细节",
433
+ "temp_hint": "0.7",
434
+ "temperature": "温度",
435
+ "temperature_desc": "控制随机性:较高 = 创意,较低 = 专注",
436
+ "temperature_label": "温度 (0.0-2.0)",
437
+ "test_connection": "测试连接",
438
+ "test_notification_body": "这是用于展示模型下载进度的测试通知。",
439
+ "test_notification_title": "测试通知",
440
+ "testing": "正在测试...",
441
+ "theme": "主题",
442
+ "theme_card_claude": "Claude",
443
+ "theme_card_claude_sub": "温馨的桃色色调主题",
444
+ "theme_card_dark": "暗色",
445
+ "theme_card_dark_sub": "保护眼睛",
446
+ "theme_card_light": "亮色",
447
+ "theme_card_light_sub": "干净明亮",
448
+ "theme_card_system": "系统",
449
+ "theme_card_system_sub": "匹配您的设备设置",
450
+ "theme_claude": "Claude",
451
+ "theme_dark": "暗色",
452
+ "theme_light": "亮色",
453
+ "theme_system": "系统",
454
+ "thinking": "正在思考",
455
+ "today_section": "今天",
456
+ "tool_label": "工具: {toolCallId}",
457
+ "tool_unknown": "工具: 未知",
458
+ "top_p": "Top P",
459
+ "top_p_desc": "核采样阈值",
460
+ "top_p_hint": "0.9",
461
+ "top_p_label": "Top P (0.0-1.0)",
462
+ "total_capacity": "总容量",
463
+ "total_ram": "总内存",
464
+ "try_different_search": "尝试不同的搜索词",
465
+ "tts_engine": "TTS 引擎",
466
+ "tts_engine_kitten": "Kitten TTS",
467
+ "tts_engine_system": "系统 TTS",
468
+ "tts_kitten_desc": "拥有 8 种表现力声音的闪电般高速神经网络 TTS。\n需要下载 {size}。",
469
+ "tts_models_title": "语音合成模型",
470
+ "tts_piper_desc": "快速离线 Piper 语音,拥有 2 种极具表现力的声音。\n每个声音需要下载 {size}。",
471
+ "tts_speed": "TTS 速度",
472
+ "tts_speed_desc": "调整播放速率。",
473
+ "tts_system_desc": "使用您设备内置的语音合成引擎。\n无需额外下载。语音选择依赖您的设备系统设置。",
474
+ "unknown_error": "未知错误",
475
+ "unload": "卸载",
476
+ "unload_from_server": "从服务器卸载",
477
+ "unpin": "取消 Pin",
478
+ "update_server": "更新服务器",
479
+ "voice": "语音",
480
+ "voice_bella": "Bella",
481
+ "voice_bruno": "Bruno",
482
+ "voice_female": "女声",
483
+ "voice_hugo": "Hugo",
484
+ "voice_jasper": "Jasper",
485
+ "voice_kiki": "Kiki",
486
+ "voice_leo": "Leo",
487
+ "voice_lessac": "Lessac (美音)",
488
+ "voice_luna": "Luna",
489
+ "voice_male": "男声",
490
+ "voice_other": "其他",
491
+ "voice_rosie": "Rosie",
492
+ "voice_ryan": "Ryan (美音)",
493
+ "waiting_selection": "等待选择",
494
+ "yesterday_section": "昨天"
495
+}
\ No newline at end of file
pubspec.lock
+10
-10
@@ -439,7 +439,7 @@ packages:
439
source: hosted
440
version: "8.0.0"
441
flutter_localizations:
442
- dependency: transitive
442
+ dependency: "direct main"
443
description: flutter
444
source: sdk
445
version: "0.0.0"
@@ -646,7 +646,7 @@ packages:
646
source: hosted
647
version: "1.1.7"
648
intl:
649
- dependency: transitive
649
+ dependency: "direct main"
650
description:
651
name: intl
652
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
@@ -769,10 +769,10 @@ packages:
769
dependency: transitive
770
description:
771
name: matcher
772
- sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
772
+ sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
773
url: "https://pub.dev"
774
source: hosted
775
- version: "0.12.19"
775
+ version: "0.12.18"
776
material_color_utilities:
777
dependency: transitive
778
description:
@@ -1311,26 +1311,26 @@ packages:
1311
dependency: transitive
1312
description:
1313
name: test
1314
- sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
1314
+ sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a"
1315
url: "https://pub.dev"
1316
source: hosted
1317
- version: "1.30.0"
1317
+ version: "1.29.0"
1318
test_api:
1319
dependency: transitive
1320
description:
1321
name: test_api
1322
- sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
1322
+ sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
1323
url: "https://pub.dev"
1324
source: hosted
1325
- version: "0.7.10"
1325
+ version: "0.7.9"
1326
test_core:
1327
dependency: transitive
1328
description:
1329
name: test_core
1330
- sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
1330
+ sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943"
1331
url: "https://pub.dev"
1332
source: hosted
1333
- version: "0.6.16"
1333
+ version: "0.6.15"
1334
theme_extensions_builder_annotation:
1335
dependency: transitive
1336
description:
pubspec.yaml
+5
@@ -30,6 +30,9 @@ environment:
30
dependencies:
31
flutter:
32
sdk: flutter
33
+ flutter_localizations:
34
+ sdk: flutter
35
+ intl: any
36
37
# The following adds the Cupertino Icons font to your application.
38
# Use with the CupertinoIcons class for iOS style icons.
@@ -84,6 +87,8 @@ dev_dependencies:
87
88
# The following section is specific to Flutter packages.
89
flutter:
90
+ generate: true
91
+
92
# The following line ensures that the Material Icons font is
93
# included with your application, so that you can use the icons in
94
# the material Icons class.