@tduncan / Cortana / commits / 39cfa6c

feat: implement internationalization (i18n) support across the application using l10n.

abdulmominsakib committed May 17, 2026 at 20:42 UTC 39cfa6cfeda90ed779cce10733b4d5c5dedb7be0
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.