11 files changed
+4043
-214
assets/images/flag_fr.png
Binary files /dev/null and b/assets/images/flag_fr.png differ
assets/images/flag_ru.png
Binary files /dev/null and b/assets/images/flag_ru.png differ
assets/images/flag_tr.png
Binary files a/assets/images/flag_tr.png and b/assets/images/flag_tr.png differ
lib/features/onboarding/screens/onboarding_language_screen.dart
+114
-92
@@ -142,6 +142,16 @@ const _languages = [
142
shortText: 'Tr',
143
gradient: [Color(0xFFE11D48), Color(0xFFBE123C)],
144
),
145
+ _LanguageOption(
146
+ code: 'fr',
147
+ nativeName: 'Français',
148
+ englishName: 'French',
149
+ flag: '🇫🇷',
150
+ flagAsset: 'assets/images/flag_fr.png',
151
+ countryCode: 'FR',
152
+ shortText: 'Fr',
153
+ gradient: [Color(0xFF2563EB), Color(0xFF1D4ED8)],
154
+ ),
155
];
156
157
class OnboardingLanguageScreen extends ConsumerStatefulWidget {
@@ -259,12 +269,17 @@ class _OnboardingLanguageScreenState
269
Actor(
270
delay: 60.ms,
271
acts: [.fadeIn(), .slideY(from: 0.08)],
262
- child: ListView.separated(
272
+ child: GridView.builder(
273
shrinkWrap: true,
274
physics: const NeverScrollableScrollPhysics(),
275
+ gridDelegate:
276
+ const SliverGridDelegateWithFixedCrossAxisCount(
277
+ crossAxisCount: 2,
278
+ mainAxisSpacing: 12,
279
+ crossAxisSpacing: 12,
280
+ mainAxisExtent: 92,
281
+ ),
282
itemCount: _languages.length,
266
- separatorBuilder: (context, index) =>
267
- const SizedBox(height: 12),
283
itemBuilder: (context, index) {
284
final lang = _languages[index];
285
final isSelected = _selectedCode == lang.code;
@@ -281,10 +296,7 @@ class _OnboardingLanguageScreenState
296
},
297
child: AnimatedContainer(
298
duration: const Duration(milliseconds: 200),
284
- padding: const EdgeInsets.symmetric(
285
- horizontal: 16,
286
- vertical: 12,
287
- ),
299
+ padding: const EdgeInsets.all(12),
300
decoration: BoxDecoration(
301
color: theme.colorScheme.surface,
302
borderRadius: BorderRadius.circular(16),
@@ -307,105 +319,115 @@ class _OnboardingLanguageScreenState
319
]
320
: null,
321
),
310
- child: Row(
322
+ child: Column(
323
+ crossAxisAlignment: CrossAxisAlignment.start,
324
+ mainAxisAlignment:
325
+ MainAxisAlignment.spaceBetween,
326
children: [
312
- Container(
313
- width: 48,
314
- height: 32,
315
- decoration: BoxDecoration(
316
- color: Colors.white,
317
- borderRadius: BorderRadius.circular(6),
318
- border: Border.all(
319
- color: theme.colorScheme.outline
320
- .withValues(alpha: 0.15),
321
- width: 1,
322
- ),
323
- boxShadow: [
324
- BoxShadow(
325
- color: Colors.black.withValues(
326
- alpha: 0.05,
327
+ Row(
328
+ mainAxisAlignment:
329
+ MainAxisAlignment.spaceBetween,
330
+ children: [
331
+ Container(
332
+ width: 36,
333
+ height: 24,
334
+ decoration: BoxDecoration(
335
+ color: Colors.white,
336
+ borderRadius:
337
+ BorderRadius.circular(5),
338
+ border: Border.all(
339
+ color: theme.colorScheme.outline
340
+ .withValues(alpha: 0.15),
341
+ width: 1,
342
),
328
- blurRadius: 4,
329
- offset: const Offset(0, 2),
343
+ boxShadow: [
344
+ BoxShadow(
345
+ color: Colors.black.withValues(
346
+ alpha: 0.05,
347
+ ),
348
+ blurRadius: 4,
349
+ offset: const Offset(0, 2),
350
+ ),
351
+ ],
352
),
331
- ],
332
- ),
333
- alignment: Alignment.center,
334
- child: ClipRRect(
335
- borderRadius: BorderRadius.circular(5),
336
- child: Image.asset(
337
- lang.flagAsset,
338
- width: 48,
339
- height: 32,
340
- fit: BoxFit.cover,
341
- errorBuilder:
342
- (context, error, stackTrace) {
353
+ alignment: Alignment.center,
354
+ child: ClipRRect(
355
+ borderRadius:
356
+ BorderRadius.circular(4),
357
+ child: Image.asset(
358
+ lang.flagAsset,
359
+ width: 36,
360
+ height: 24,
361
+ fit: BoxFit.cover,
362
+ errorBuilder: (context, error,
363
+ stackTrace) {
364
return Text(
365
lang.flag,
366
style: const TextStyle(
346
- fontSize: 18,
367
+ fontSize: 14,
368
),
369
);
370
},
371
+ ),
372
+ ),
373
),
351
- ),
352
- ),
353
- const SizedBox(width: 16),
354
- Expanded(
355
- child: Column(
356
- crossAxisAlignment:
357
- CrossAxisAlignment.start,
358
- mainAxisSize: MainAxisSize.min,
359
- children: [
360
- Text(
361
- lang.nativeName,
362
- style: theme.textTheme.titleMedium
363
- ?.copyWith(
364
- fontWeight: FontWeight.bold,
365
- ),
374
+ AnimatedContainer(
375
+ duration: const Duration(
376
+ milliseconds: 200,
377
),
367
- const SizedBox(height: 2),
368
- Text(
369
- '${lang.englishName} • ${lang.shortText.toUpperCase()}',
370
- style: theme.textTheme.labelSmall
371
- ?.copyWith(
372
- color: theme
373
- .colorScheme
374
- .onSurface
375
- .withValues(alpha: 0.5),
376
- ),
378
+ width: 20,
379
+ height: 20,
380
+ decoration: BoxDecoration(
381
+ shape: BoxShape.circle,
382
+ color: isSelected
383
+ ? theme.colorScheme.primary
384
+ : Colors.transparent,
385
+ border: Border.all(
386
+ color: isSelected
387
+ ? theme.colorScheme.primary
388
+ : theme.colorScheme.outline
389
+ .withValues(alpha: 0.3),
390
+ width: 2,
391
+ ),
392
),
378
- ],
379
- ),
393
+ child: isSelected
394
+ ? const HugeIcon(
395
+ icon: HugeIcons
396
+ .strokeRoundedTick01,
397
+ color: Colors.white,
398
+ size: 12,
399
+ )
400
+ : null,
401
+ ),
402
+ ],
403
),
381
- const SizedBox(width: 8),
382
- AnimatedContainer(
383
- duration: const Duration(
384
- milliseconds: 200,
385
- ),
386
- width: 22,
387
- height: 22,
388
- decoration: BoxDecoration(
389
- shape: BoxShape.circle,
390
- color: isSelected
391
- ? theme.colorScheme.primary
392
- : Colors.transparent,
393
- border: Border.all(
394
- color: isSelected
395
- ? theme.colorScheme.primary
396
- : theme.colorScheme.outline
397
- .withValues(alpha: 0.3),
398
- width: 2,
404
+ Column(
405
+ crossAxisAlignment:
406
+ CrossAxisAlignment.start,
407
+ mainAxisSize: MainAxisSize.min,
408
+ children: [
409
+ Text(
410
+ lang.nativeName,
411
+ maxLines: 1,
412
+ overflow: TextOverflow.ellipsis,
413
+ style: theme.textTheme.titleSmall
414
+ ?.copyWith(
415
+ fontWeight: FontWeight.bold,
416
+ ),
417
+ ),
418
+ const SizedBox(height: 2),
419
+ Text(
420
+ '${lang.englishName} • ${lang.shortText.toUpperCase()}',
421
+ maxLines: 1,
422
+ overflow: TextOverflow.ellipsis,
423
+ style: theme.textTheme.labelSmall
424
+ ?.copyWith(
425
+ color: theme.colorScheme.onSurface
426
+ .withValues(alpha: 0.5),
427
+ fontSize: 10,
428
+ ),
429
),
400
- ),
401
- child: isSelected
402
- ? const HugeIcon(
403
- icon:
404
- HugeIcons.strokeRoundedTick01,
405
- color: Colors.white,
406
- size: 14,
407
- )
408
- : null,
430
+ ],
431
),
432
],
433
),
lib/features/onboarding/screens/onboarding_server_type_screen.dart
+136
-122
@@ -138,89 +138,94 @@ class _OnboardingServerTypeScreenState
138
final crossAxisCount =
139
constraints.maxWidth >= 560 ? 3 : 2;
140
141
- return GridView.count(
142
- shrinkWrap: true,
143
- physics: const NeverScrollableScrollPhysics(),
144
- padding: const EdgeInsets.only(top: 24),
145
- crossAxisCount: crossAxisCount,
146
- mainAxisSpacing: 12,
147
- crossAxisSpacing: 12,
148
- mainAxisExtent: 144,
141
+ return Column(
142
children: [
150
- _buildServerCard(
151
- type: ServerType.onDevice,
152
- title: l10n.server_type_on_device,
153
- subtitle: l10n.server_type_on_device_desc,
154
- iconWidget: HugeIcon(icon:
155
- HugeIcons.strokeRoundedSmartPhone01,
156
- color: _selectedType == ServerType.onDevice
157
- ? theme.colorScheme.primary
158
- : theme.colorScheme.onSurface,
159
- ),
160
- theme: theme,
161
- ),
162
- _buildServerCard(
163
- type: ServerType.lmStudio,
164
- title: l10n.server_type_lm_studio,
165
- subtitle: l10n.server_type_lm_studio_desc,
166
- iconWidget: HugeIcon(icon:
167
- HugeIcons.strokeRoundedComputerTerminal01,
168
- color: _selectedType == ServerType.lmStudio
169
- ? theme.colorScheme.primary
170
- : theme.colorScheme.onSurface,
171
- ),
172
- theme: theme,
173
- ),
174
- _buildServerCard(
175
- type: ServerType.openAICompatible,
176
- title: l10n.server_type_openai_display,
177
- subtitle: l10n.openai_compatible_api,
178
- iconWidget: HugeIcon(
179
- icon: HugeIcons.strokeRoundedApi,
180
- color:
181
- _selectedType ==
182
- ServerType.openAICompatible
183
- ? theme.colorScheme.primary
184
- : theme.colorScheme.onSurface,
185
- ),
186
- theme: theme,
187
- ),
188
- _buildServerCard(
189
- type: ServerType.ollama,
190
- title: l10n.server_type_ollama,
191
- subtitle: l10n.server_type_ollama_desc,
192
- iconWidget: HugeIcon(icon:
193
- HugeIcons.strokeRoundedRobot01,
194
- color: _selectedType == ServerType.ollama
195
- ? theme.colorScheme.primary
196
- : theme.colorScheme.onSurface,
197
- ),
198
- theme: theme,
199
- ),
200
- _buildServerCard(
201
- type: ServerType.ollamaCloud,
202
- title: l10n.server_type_ollama_cloud,
203
- subtitle: l10n.server_type_ollama_cloud_sub,
204
- iconWidget: HugeIcon(
205
- icon: HugeIcons.strokeRoundedAiCloud,
206
- color: _selectedType == ServerType.ollamaCloud
207
- ? theme.colorScheme.primary
208
- : theme.colorScheme.onSurface,
209
- ),
210
- theme: theme,
211
- ),
212
- _buildServerCard(
213
- type: ServerType.openRouter,
214
- title: l10n.server_type_openrouter,
215
- subtitle: l10n.server_type_openrouter_sub,
216
- iconWidget: HugeIcon(
217
- icon: HugeIcons.strokeRoundedCloudServer,
218
- color: _selectedType == ServerType.openRouter
219
- ? theme.colorScheme.primary
220
- : theme.colorScheme.onSurface,
221
- ),
222
- theme: theme,
143
+ GridView.count(
144
+ shrinkWrap: true,
145
+ physics: const NeverScrollableScrollPhysics(),
146
+ padding: const EdgeInsets.only(top: 24),
147
+ crossAxisCount: crossAxisCount,
148
+ mainAxisSpacing: 12,
149
+ crossAxisSpacing: 12,
150
+ mainAxisExtent: 144,
151
+ children: [
152
+ _buildServerCard(
153
+ type: ServerType.onDevice,
154
+ title: l10n.server_type_on_device,
155
+ subtitle: l10n.server_type_on_device_desc,
156
+ iconWidget: HugeIcon(
157
+ icon: HugeIcons.strokeRoundedSmartPhone01,
158
+ color: _selectedType == ServerType.onDevice
159
+ ? theme.colorScheme.primary
160
+ : theme.colorScheme.onSurface,
161
+ ),
162
+ theme: theme,
163
+ ),
164
+ _buildServerCard(
165
+ type: ServerType.lmStudio,
166
+ title: l10n.server_type_lm_studio,
167
+ subtitle: l10n.server_type_lm_studio_desc,
168
+ iconWidget: HugeIcon(
169
+ icon: HugeIcons.strokeRoundedComputerTerminal01,
170
+ color: _selectedType == ServerType.lmStudio
171
+ ? theme.colorScheme.primary
172
+ : theme.colorScheme.onSurface,
173
+ ),
174
+ theme: theme,
175
+ ),
176
+ _buildServerCard(
177
+ type: ServerType.openAICompatible,
178
+ title: l10n.server_type_openai_display,
179
+ subtitle: l10n.openai_compatible_api,
180
+ iconWidget: HugeIcon(
181
+ icon: HugeIcons.strokeRoundedApi,
182
+ color:
183
+ _selectedType ==
184
+ ServerType.openAICompatible
185
+ ? theme.colorScheme.primary
186
+ : theme.colorScheme.onSurface,
187
+ ),
188
+ theme: theme,
189
+ ),
190
+ _buildServerCard(
191
+ type: ServerType.ollama,
192
+ title: l10n.server_type_ollama,
193
+ subtitle: l10n.server_type_ollama_desc,
194
+ iconWidget: HugeIcon(
195
+ icon: HugeIcons.strokeRoundedRobot01,
196
+ color: _selectedType == ServerType.ollama
197
+ ? theme.colorScheme.primary
198
+ : theme.colorScheme.onSurface,
199
+ ),
200
+ theme: theme,
201
+ ),
202
+ _buildServerCard(
203
+ type: ServerType.ollamaCloud,
204
+ title: l10n.server_type_ollama_cloud,
205
+ subtitle: l10n.server_type_ollama_cloud_sub,
206
+ iconWidget: HugeIcon(
207
+ icon: HugeIcons.strokeRoundedAiCloud,
208
+ color: _selectedType == ServerType.ollamaCloud
209
+ ? theme.colorScheme.primary
210
+ : theme.colorScheme.onSurface,
211
+ ),
212
+ theme: theme,
213
+ ),
214
+ _buildServerCard(
215
+ type: ServerType.openRouter,
216
+ title: l10n.server_type_openrouter,
217
+ subtitle: l10n.server_type_openrouter_sub,
218
+ iconWidget: HugeIcon(
219
+ icon: HugeIcons.strokeRoundedCloudServer,
220
+ color: _selectedType == ServerType.openRouter
221
+ ? theme.colorScheme.primary
222
+ : theme.colorScheme.onSurface,
223
+ ),
224
+ theme: theme,
225
+ ),
226
+ ],
227
),
228
+ const SizedBox(height: 12),
229
_buildMoreCard(
230
theme: theme,
231
title: l10n.add_more,
@@ -433,7 +438,11 @@ class _OnboardingServerTypeScreenState
438
child: Stack(
439
children: [
440
Container(
436
- padding: const EdgeInsets.all(16),
441
+ width: double.infinity,
442
+ padding: const EdgeInsets.symmetric(
443
+ horizontal: 16,
444
+ vertical: 14,
445
+ ),
446
decoration: BoxDecoration(
447
gradient: LinearGradient(
448
begin: Alignment.topLeft,
@@ -450,61 +459,66 @@ class _OnboardingServerTypeScreenState
459
color: theme.colorScheme.outline.withValues(alpha: 0.18),
460
),
461
),
453
- child: Column(
454
- crossAxisAlignment: CrossAxisAlignment.start,
462
+ child: Row(
463
children: [
456
- Row(
457
- children: [
458
- Container(
459
- padding: const EdgeInsets.all(8),
460
- decoration: BoxDecoration(
461
- color: theme.colorScheme.primary.withValues(
462
- alpha: 0.12,
463
- ),
464
- borderRadius: BorderRadius.circular(11),
465
- ),
466
- child: HugeIcon(icon:
467
- HugeIcons.strokeRoundedAdd01,
468
- size: 18,
469
- color: theme.colorScheme.primary,
470
- ),
464
+ Container(
465
+ padding: const EdgeInsets.all(8),
466
+ decoration: BoxDecoration(
467
+ color: theme.colorScheme.primary.withValues(
468
+ alpha: 0.12,
469
),
472
- const Spacer(),
473
- ],
474
- ),
475
- const Spacer(),
476
- Text(
477
- title,
478
- maxLines: 1,
479
- overflow: TextOverflow.ellipsis,
480
- style: theme.textTheme.titleMedium?.copyWith(
481
- fontWeight: FontWeight.bold,
482
- fontSize: 14.5,
470
+ borderRadius: BorderRadius.circular(11),
471
+ ),
472
+ child: HugeIcon(
473
+ icon: HugeIcons.strokeRoundedAdd01,
474
+ size: 18,
475
+ color: theme.colorScheme.primary,
476
),
477
),
485
- const SizedBox(height: 4),
486
- Text(
487
- subtitle,
488
- maxLines: 1,
489
- overflow: TextOverflow.ellipsis,
490
- style: theme.textTheme.labelSmall?.copyWith(
491
- letterSpacing: 1.0,
492
- fontSize: 9.5,
493
- color: theme.colorScheme.onSurface.withValues(alpha: 0.55),
478
+ const SizedBox(width: 14),
479
+ Expanded(
480
+ child: Column(
481
+ mainAxisAlignment: MainAxisAlignment.center,
482
+ crossAxisAlignment: CrossAxisAlignment.start,
483
+ mainAxisSize: MainAxisSize.min,
484
+ children: [
485
+ Text(
486
+ title,
487
+ maxLines: 1,
488
+ overflow: TextOverflow.ellipsis,
489
+ style: theme.textTheme.titleMedium?.copyWith(
490
+ fontWeight: FontWeight.bold,
491
+ fontSize: 14.5,
492
+ ),
493
+ ),
494
+ const SizedBox(height: 2),
495
+ Text(
496
+ subtitle,
497
+ maxLines: 1,
498
+ overflow: TextOverflow.ellipsis,
499
+ style: theme.textTheme.labelSmall?.copyWith(
500
+ letterSpacing: 1.0,
501
+ fontSize: 9.5,
502
+ color: theme.colorScheme.onSurface.withValues(
503
+ alpha: 0.55,
504
+ ),
505
+ ),
506
+ ),
507
+ ],
508
),
509
),
510
],
511
),
512
),
513
PositionedDirectional(
500
- end: 4,
501
- bottom: 2,
514
+ end: 12,
515
+ bottom: -8,
516
child: IgnorePointer(
517
child: Opacity(
518
opacity: 0.06,
519
child: HugeIcon(
520
icon: HugeIcons.strokeRoundedGithub,
507
- size: 54,
521
+ size: 60,
522
color: theme.colorScheme.onSurface,
523
),
524
),
lib/features/settings/views/settings_screen.dart
+1
@@ -1039,6 +1039,7 @@ class _LanguageSetting extends StatelessWidget {
1039
('hi', 'हिन्दी', 'assets/images/flag_in.png', '🇮🇳'),
1040
('ru', 'Русский', 'assets/images/flag_ru.png', '🇷🇺'),
1041
('tr', 'Türkçe', 'assets/images/flag_tr.png', '🇹🇷'),
1042
+ ('fr', 'Français', 'assets/images/flag_fr.png', '🇫🇷'),
1043
];
1044
1045
final String? current;
lib/l10n/app_fr.arb
new
+882
@@ -0,0 +1,882 @@
1
+{
2
+ "@@locale": "fr",
3
+ "app_name": "LocalMind",
4
+ "app_tagline": "Votre IA. Votre Appareil. Vos Règles.",
5
+ "app_version": "1.0.0",
6
+ "cancel": "Annuler",
7
+ "confirm": "Confirmer",
8
+ "delete": "Supprimer",
9
+ "save": "Enregistrer",
10
+ "retry": "Réessayer",
11
+ "close": "Fermer",
12
+ "done": "Terminé",
13
+ "continue_action": "Continuer",
14
+ "skip": "Passer",
15
+ "install": "Installer",
16
+ "download": "Télécharger",
17
+ "resume": "Reprendre",
18
+ "pause": "Pause",
19
+ "stop": "Arrêter",
20
+ "edit": "Modifier",
21
+ "preview": "Aperçu",
22
+ "unload": "Décharger",
23
+ "load": "Charger",
24
+ "rename": "Renommer",
25
+ "pin": "Épingler",
26
+ "unpin": "Désépingler",
27
+ "share": "Partager",
28
+ "copy": "Copier",
29
+ "copied": "Copié !",
30
+ "copied_to_clipboard": "Copié dans le presse-papiers",
31
+ "select": "Select",
32
+ "active": "Active",
33
+ "all": "Tout",
34
+ "none": "Aucun",
35
+ "none_selected": "Nonne selected",
36
+ "online": "Online",
37
+ "connected": "Connected",
38
+ "checking": "Checking",
39
+ "offline": "Offline",
40
+ "error": "Erreur",
41
+ "unknown_error": "Unknown error",
42
+ "not_now": "Nont Nonw",
43
+ "enable": "Enable",
44
+ "proceed_anyway": "Proceed Anyway",
45
+ "test_connection": "Test Connection",
46
+ "testing": "Testing...",
47
+ "connection_successful": "Connection successful!",
48
+ "connection_failed": "Connection failed. Check your settings.",
49
+ "save_continue": "Enregistrer & Continue",
50
+ "save_changes": "Enregistrer Changes",
51
+ "finish_setup": "Finish Setup",
52
+ "start_new_chat": "Start New Chat",
53
+ "cannot_undo": "This action cannot be undone.",
54
+ "ram_warning": "RAM Warning",
55
+ "recommended": "RECOMMENDED",
56
+ "may_be_large": "May be too large for this device",
57
+ "calculating": "Calculating...",
58
+ "download_failed": "Télécharger failed",
59
+ "downloaded": "Downloaded",
60
+ "not_downloaded": "Nont downloaded",
61
+ "installed": "Installed",
62
+ "not_installed": "Nont installed",
63
+ "loading": "Chargement...",
64
+ "thinking": "Thinking",
65
+ "processing": "Processing...",
66
+ "initializing": "Initializing...",
67
+ "ready": "Ready",
68
+ "preparing_app": "Preparing app...",
69
+ "initializing_services": "Initializing services...",
70
+ "configuring_server": "Configuring server...",
71
+ "startup_failed": "Startup failed",
72
+ "something_went_wrong": "Something went wrong",
73
+ "delete_model_title": "Supprimer le modèle",
74
+ "delete_model_body": "Voulez-vous vraiment supprimer {name} ?",
75
+ "delete_model_body_with_size": "Voulez-vous vraiment supprimer {name} ? Cela libérera environ {size} d'espace.\n\nVous pourrez retélécharger ce modèle plus tard si nécessaire.",
76
+ "delete_voice_title": "Supprimer la voix",
77
+ "delete_voice_body": "Voulez-vous vraiment supprimer {name} ? Cela libérera environ {size} d'espace.\n\nVous pourrez retélécharger cette voix plus tard si nécessaire.",
78
+ "delete_server_title": "Supprimer le serveur",
79
+ "delete_server_body": "Voulez-vous vraiment supprimer « {name} » ? Cette action est irréversible.",
80
+ "delete_conversation_title": "Supprimer la conversation",
81
+ "delete_conversation_body": "Voulez-vous vraiment supprimer « {title} » ? Cette action est irréversible.",
82
+ "delete_message_title": "Supprimer message?",
83
+ "delete_persona_title": "Supprimer « {name} » ?",
84
+ "delete_persona_body": "Voulez-vous vraiment supprimer ce persona ? Cette action ne peut pas être annulée.",
85
+ "delete_builtin_persona_body": "This is a built-in persona. You can restore it later from Paramètres.",
86
+ "restore_builtin_personas": "Restauration default personas",
87
+ "restore_builtin_personas_desc": "Re-add any built-in personas you've deleted",
88
+ "restore_builtin_personas_success": "Default personas restored",
89
+ "clear_personas": "Clear personas",
90
+ "enable_image_compression": "Compress images before sending",
91
+ "enable_image_compression_desc": "Resize and compress attached images so uploads stay within server limits",
92
+ "image_compression_level": "Compression aggressiveness",
93
+ "image_compression_level_desc": "Higher aggressiveness produces smaller uploads at lower quality",
94
+ "image_compression_level_low": "Low",
95
+ "image_compression_level_medium": "Medium",
96
+ "image_compression_level_high": "High",
97
+ "sort_models_tooltip": "Sort models",
98
+ "sort_by_favorites": "Favorites first",
99
+ "sort_by_name": "Name (A-Z)",
100
+ "sort_by_size_smallest": "Size (smallest first)",
101
+ "sort_by_size_largest": "Size (largest first)",
102
+ "sort_by_context_length": "Context length",
103
+ "bulk_ai_rename_progress": "Renommage {done}/{total}...",
104
+ "selected_count": "{count} sélectionné(s)",
105
+ "ai_rename_tooltip": "Rename selected with AI",
106
+ "new_chat_in_folder_tooltip": "New chat in this folder",
107
+ "total_tokens_count": "{count} jetons",
108
+ "smart_replies_use_persona": "Use persona in smart replies",
109
+ "smart_replies_use_persona_desc": "Suggested replies match the tone of the active persona instead of a generic assistant",
110
+ "keep_persona_on_new_chat": "Keep persona on new chat",
111
+ "keep_persona_on_new_chat_desc": "Don't clear the selected persona(s) after starting a new chat",
112
+ "role_swap_button_enabled": "Show role-swap button",
113
+ "role_swap_button_enabled_desc": "Show a button in the chat input to send your message as the assistant instead of the user, without generating a response",
114
+ "send_as_user_tooltip": "Send as user",
115
+ "send_as_assistant_tooltip": "Send as assistant (no response)",
116
+ "insert_without_generating_tooltip": "Insert without generating",
117
+ "token_usage_title": "Token Usage",
118
+ "total_tokens_label": "Tokens used",
119
+ "usage_percent_label": "Context used",
120
+ "export_choice_title": "Export",
121
+ "export_choice_body": "How would you like to export this?",
122
+ "copy_to_clipboard": "Copy to Clipboard",
123
+ "bulk_export_conversations_success": "{count} conversations exportées",
124
+ "bulk_ai_rename_confirm_title": "Rename with AI?",
125
+ "bulk_ai_rename_confirm_body": "This will ask the AI to generate a new title for each of the {count} selected conversations, replacing their current titles. This can take a while and cannot be undone.",
126
+ "sort_by_modified_date": "Last modified",
127
+ "sort_by_created_date": "Date created",
128
+ "sort_title": "Sort",
129
+ "clear_conversation_title": "Clear conversation?",
130
+ "clear_conversation_body": "This will delete all messages in this conversation.",
131
+ "clear": "Effacer",
132
+ "label_completed": "{label} completed",
133
+ "error_with_message": "Error: {error}",
134
+ "preview_failed": "Preview failed: {error}",
135
+ "loading_model": "Loading {modelId}...",
136
+ "model_loaded": "Modèle loaded: {modelId} ({backend})",
137
+ "no_model_loaded": "Aucun model loaded. Tap \"Manage On-Device Models\" to download and load a model.",
138
+ "loading_model_error": "Error: {error}",
139
+ "delete_conversation": "Supprimer conversation?",
140
+ "nav_history": "History",
141
+ "nav_servers": "Servers",
142
+ "nav_local_models": "Local Models",
143
+ "nav_tts": "Text To Speech",
144
+ "nav_personas": "Personas",
145
+ "nav_settings": "Paramètres",
146
+ "nav_new_chat": "New Chat",
147
+ "search_hint": "Search conversations...",
148
+ "no_server_selected": "Aucun server selected",
149
+ "switch_server": "Switch Server",
150
+ "switch_server_subtitle": "Choisir a server to connect to",
151
+ "manage_servers": "Manage Servers",
152
+ "open_source": "Open Source",
153
+ "open_source_desc": "LocalMind is open source. Follow our progress or contribute on GitHub.",
154
+ "star_on_github": "Star on GitHub",
155
+ "add_more": "Ajouter more",
156
+ "on_github": "on GitHub",
157
+ "could_not_open_github": "Impossible d'ouvrir GitHub. Veuillez réessayer plus tard.",
158
+ "settings_title": "Paramètres",
159
+ "settings_appearance": "Appearance",
160
+ "settings_language": "Langue",
161
+ "language_system_default": "Par défaut du système",
162
+ "settings_tts": "Text-to-Speech",
163
+ "settings_behavior": "Behavior",
164
+ "settings_on_device": "On-Device Inference",
165
+ "settings_default_server": "Default Server",
166
+ "settings_default_persona": "Default Persona",
167
+ "settings_privacy": "Privacy",
168
+ "settings_data_management": "Data Management",
169
+ "settings_about": "À propos",
170
+ "theme": "Thème",
171
+ "theme_system": "System",
172
+ "theme_light": "Light",
173
+ "theme_dark": "Dark",
174
+ "theme_claude": "Claude",
175
+ "font_size": "Font Size",
176
+ "font_size_desc": "Adjust text size in chat.",
177
+ "font_preview": "The quick brown fox jumps over the lazy dog.",
178
+ "code_theme_dark": "Code Thème (Dark)",
179
+ "code_theme_light": "Code Thème (Light)",
180
+ "code_theme_desc": "Choisir syntax highlighting theme for code blocks.",
181
+ "tts_engine": "TTS Engine",
182
+ "tts_engine_system": "System TTS",
183
+ "tts_engine_kitten": "Kitten TTS",
184
+ "voice": "Voice",
185
+ "voice_female": "Female",
186
+ "voice_male": "Male",
187
+ "voice_other": "Other",
188
+ "tts_speed": "TTS Speed",
189
+ "tts_speed_desc": "Adjust the playback rate.",
190
+ "manage_tts_models": "Manage TTS Models",
191
+ "manage_on_device_models": "Manage On-Device Models",
192
+ "enable_smart_reply": "On-Device Smart Replies",
193
+ "ai_user_response_enabled": "AI user message (hold send)",
194
+ "ai_user_response_enabled_desc": "Hold the send button for 3 seconds to have the AI write and send your next message",
195
+ "ai_user_response_tooltip": "Generate user message with AI",
196
+ "streaming_responses": "Streaming Responses",
197
+ "auto_generate_titles": "Auto-generate Titles",
198
+ "send_on_enter": "Send on Enter",
199
+ "show_system_messages": "Send Default Prompt système",
200
+ "show_system_messages_desc": "When no persona is selected, send a default assistant system prompt with each request",
201
+ "show_system_messages_in_chat": "Show System Messages in Chat",
202
+ "show_system_messages_in_chat_desc": "Display system messages (e.g. from an imported backup) as visible bubbles in the conversation",
203
+ "haptic_feedback": "Haptic Feedback",
204
+ "enable_mcp": "Enable MCP",
205
+ "new_chat_mcp_default": "New Chat MCP Default",
206
+ "show_data_indicator": "Show Data Indicator",
207
+ "privacy_info": "\"LocalMind never sees your data\"",
208
+ "delete_all_conversations": "Supprimer All Conversations",
209
+ "reset_settings_defaults": "Reset Paramètres to Defaults",
210
+ "chat_title": "LocalMind",
211
+ "chat_parameters_tooltip": "Chat Parameters",
212
+ "change_persona": "Change Persona",
213
+ "set_persona": "Set Persona",
214
+ "remove_persona": "Retirer Persona",
215
+ "clear_conversation": "Clear Conversation",
216
+ "connection_error": "Connection error. Check your server.",
217
+ "disconnected": "Disconnected from server.",
218
+ "configure": "Configure",
219
+ "select_model": "Sélectionner Model",
220
+ "select_persona": "Sélectionner Persona",
221
+ "manage_personas": "Manage personas",
222
+ "personas_combine_hint": "Sélectionner multiple personas in chat to stack their system prompts.",
223
+ "start_conversation": "Start a conversation",
224
+ "recent_chats": "Recent chats",
225
+ "see_all": "See all",
226
+ "quick_write": "Help me write a function",
227
+ "quick_explain": "Explain this code",
228
+ "quick_debug": "Debug this for me",
229
+ "quick_async": "How do I use async/await?",
230
+ "history_missing_title": "History Missing",
231
+ "history_missing_desc": "Either the messages in this chat were deleted or the history record is corrupted.",
232
+ "technical_details": "Technical Details",
233
+ "last_error": "Last Error:",
234
+ "copy_info": "Copy Info",
235
+ "conversation_id": "Conversation ID",
236
+ "created_at": "Created At",
237
+ "expected_messages": "Expected Messages",
238
+ "debug_dialog_desc": "Diagnostic information to help identify synchronization issues.",
239
+ "chat_input_hint": "Ask anything",
240
+ "send_message_tooltip": "Send message",
241
+ "stop_generation_tooltip": "Stop generation",
242
+ "attach_images_tooltip": "Attach images or text",
243
+ "start_listening_tooltip": "Start listening",
244
+ "stop_listening_tooltip": "Stop listening",
245
+ "tool_label": "Tool: {toolCallId}",
246
+ "tool_unknown": "Tool: Unknown",
247
+ "message_options": "Message options",
248
+ "copy_markdown": "Copy as Markdown",
249
+ "copied_markdown": "Copied as Markdown",
250
+ "read_aloud": "Read Aloud",
251
+ "stop_reading": "Stop Reading",
252
+ "more": "More",
253
+ "character_count": "{length} characters",
254
+ "edit_message": "Modifier message",
255
+ "edit_message_desc": "Saving will remove the assistant response below and regenerate.",
256
+ "save_regenerate": "Enregistrer & regenerate",
257
+ "chat_settings_title": "Chat Paramètres",
258
+ "reset_defaults": "Reset Defaults",
259
+ "parameters_tab": "Parameters",
260
+ "mcp_tab": "MCP",
261
+ "temperature": "Température",
262
+ "temperature_desc": "Controls randomness: Higher = Creative, Lower = Focused",
263
+ "top_p": "Top P",
264
+ "top_p_desc": "Nucleus sampling threshold",
265
+ "max_tokens": "Max Tokens",
266
+ "max_tokens_desc": "Response limit",
267
+ "context_length": "Context Length",
268
+ "context_length_desc": "History window",
269
+ "mcp_disabled_warning": "MCP is disabled globally. Enable it in Paramètres to use these features.",
270
+ "mcp_enable_chat": "Enable MCP for this chat",
271
+ "auto_execute_tools": "Auto-execute tools",
272
+ "beta_label": "Beta",
273
+ "experimental_label": "Experimental",
274
+ "add_ephemeral_mcp": "Ajouter Ephemeral MCP Server",
275
+ "mcp_label_placeholder": "Label",
276
+ "mcp_url_placeholder": "URL (https://...)",
277
+ "active_integrations": "Active Integrations",
278
+ "enable_notifications": "Enable Nontifications",
279
+ "enable_notifications_desc": "Get notified when models finish downloading.",
280
+ "chat_history_title": "Chat History",
281
+ "conversation_just_now": "Just now",
282
+ "conversation_minutes_ago": "{minutes}m ago",
283
+ "conversation_hours_ago": "{hours}h ago",
284
+ "conversation_yesterday": "Ouiterday",
285
+ "conversation_days_ago": "{days}d ago",
286
+ "conversation_date": "{month}/{day}/{year}",
287
+ "options_tooltip": "Options",
288
+ "no_results_found": "Aucun results found",
289
+ "no_conversations_yet": "Aucun conversations yet",
290
+ "try_different_search": "Try a different search term",
291
+ "start_new_conversation": "Start a new conversation",
292
+ "rename_conversation": "Rename conversation",
293
+ "enter_new_title": "Enter new title",
294
+ "pinned_section": "PINNED",
295
+ "today_section": "TODAY",
296
+ "yesterday_section": "YESTERDAY",
297
+ "previous_7_days": "PREVIOUS 7 DAYS",
298
+ "previous_30_days": "PREVIOUS 30 DAYS",
299
+ "older_section": "OLDER",
300
+ "onboarding_choose_language": "Découvrez les langues de LocalMind",
301
+ "onboarding_choose_language_desc": "Sélectionner your preferred language. You can change this anytime in settings.",
302
+ "onboarding_localmind": "LocalMind",
303
+ "onboarding_connect_server": "Connect Your\nServer",
304
+ "onboarding_connect_desc": "Connect to LM Studio, Ollama,\nOllama Cloud, or OpenRouter to\nstart your private AI experience.",
305
+ "openai_compatible_api": "OpenAI-compatible API",
306
+ "https_requires_ssl": "HTTPS requires SSL",
307
+ "most_local_setups_use_http": "Most local setups use http://",
308
+ "onboarding_welcome": "Welcome to LocalMind",
309
+ "server_type_on_device": "On-Device",
310
+ "server_type_lm_studio": "LM Studio",
311
+ "server_type_ollama": "Ollama",
312
+ "server_type_ollama_cloud": "Ollama Cloud",
313
+ "server_type_ollama_cloud_sub": "CLOUD MANAGED",
314
+ "server_type_ollama_cloud_display": "Ollama Cloud",
315
+ "server_address_ollama_cloud": "ollama.com",
316
+ "ollama_cloud_disclosure": "By connecting Ollama Cloud, your chat messages and inputs are sent to Ollama's managed servers for inference. LocalMind does not track or store your conversations. You can revoke this key any time at ollama.com/settings/keys.",
317
+ "api_key_required_ollama_cloud": "API Key required for Ollama Cloud",
318
+ "api_key_hint_ollama_cloud": "Paste your Ollama Cloud API key",
319
+ "add_server_ollama_cloud_subtitle": "Connect to Ollama Cloud with an API key from ollama.com/settings/keys to access managed cloud models.",
320
+ "add_server_openrouter_subtitle": "Connect through OpenRouter with a valid API key and keep this profile ready for model routing.",
321
+ "add_server_endpoint_subtitle": "Configure a local or self-hosted endpoint, then verify the connection before saving.",
322
+ "server_type_openrouter": "OpenRouter",
323
+ "server_type_openrouter_sub": "UNIFIED CLOUD",
324
+ "ready_continue": "READY TO CONTINUE",
325
+ "waiting_selection": "WAITING FOR SELECTION",
326
+ "setup_connection": "Setup Connection",
327
+ "setup_connection_desc": "Configure your {server} server to start chatting.",
328
+ "server_name": "Serveur Name",
329
+ "name_required": "Name required",
330
+ "name_max_50": "Max 50 characters",
331
+ "host_label": "Host / IP Address",
332
+ "host_required": "Host required",
333
+ "port_label": "Port",
334
+ "port_required": "Port required",
335
+ "port_invalid": "Must be a number",
336
+ "port_range": "Enter a valid port (1-65535)",
337
+ "api_key_required": "API Key *",
338
+ "api_key_optional": "API Key (Optional)",
339
+ "api_key_required_openrouter": "API Key required for OpenRouter",
340
+ "api_key_format": "OpenRouter API keys start with sk-",
341
+ "my_server_hint": "My Server",
342
+ "name_length_validation": "Name must be 50 characters or less",
343
+ "host_valid": "Enter a valid hostname or IP address",
344
+ "api_key_hint_openrouter": "sk-...",
345
+ "api_key_hint_generic": "For authenticated servers",
346
+ "update_server": "Update Server",
347
+ "save_server": "Enregistrer Server",
348
+ "server_updated": "Serveur updated",
349
+ "server_added": "Serveur added",
350
+ "download_model_title": "Télécharger a Model",
351
+ "download_model_desc": "Choisir a model to download.\nIt will run locally on your device.",
352
+ "on_device_android_only": "On-device inference is currently available on Android only.",
353
+ "total_ram": "Total RAM",
354
+ "available": "Available",
355
+ "ram_min_required": "{fileSize} GB RAM min",
356
+ "download_progress": "{percent}% • {speed}",
357
+ "eta_label": "ETA: {eta}",
358
+ "paused_progress": "Paused - {percent}%",
359
+ "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.",
360
+ "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.",
361
+ "choose_theme": "Choisir Thème",
362
+ "choose_theme_desc": "Personalize the app appearance. You can always change this later in settings.",
363
+ "theme_card_system": "System",
364
+ "theme_card_system_sub": "Matches your device settings",
365
+ "theme_card_light": "Light",
366
+ "theme_card_light_sub": "Clean and bright",
367
+ "theme_card_dark": "Dark",
368
+ "theme_card_dark_sub": "Easy on the eyes",
369
+ "theme_card_claude": "Claude",
370
+ "theme_card_claude_sub": "A warm, peach-tinted theme",
371
+ "stay_updated": "Stay Updated",
372
+ "stay_updated_desc": "Get notified when your AI models finish downloading or when long-running tasks complete.",
373
+ "notification_benefit_downloads": "Modèle download progress",
374
+ "notification_benefit_completions": "Generation completions",
375
+ "notification_benefit_background": "Background tasks status",
376
+ "allow_notifications": "Allow Nontifications",
377
+ "servers_title": "Servers",
378
+ "no_servers_yet": "Aucun Servers Yet",
379
+ "no_servers_desc": "Ajouter your first server to start chatting with AI models.",
380
+ "add_server": "Ajouter Server",
381
+ "switched_to_server": "Switched to {name}",
382
+ "edit_server": "Modifier Server",
383
+ "add_server_title": "Ajouter Server",
384
+ "server_type_label": "Serveur Type",
385
+ "server_icon_label": "Serveur Icon",
386
+ "default_icon": "Default icon",
387
+ "server_type_lm_studio_display": "LM Studio",
388
+ "server_type_openai_display": "OpenAI Compatible",
389
+ "server_type_ollama_display": "Ollama",
390
+ "server_type_openrouter_display": "OpenRouter",
391
+ "server_type_on_device_display": "On-Device",
392
+ "server_address_openrouter": "openrouter.ai",
393
+ "server_address_on_device": "Local inference",
394
+ "server_address_format": "{host}:{port}",
395
+ "default_badge": "Default",
396
+ "set_as_default": "Set as Default",
397
+ "select_icon": "Sélectionner Icon",
398
+ "select_icon_desc": "Choisir an icon for your server",
399
+ "search_icons_hint": "Search icons...",
400
+ "server_icon_stack": "Serveur Stack",
401
+ "server_icon_stack2": "Serveur Stack 02",
402
+ "server_icon_stack3": "Serveur Stack 03",
403
+ "server_icon_cloud": "Cloud",
404
+ "server_icon_cloud_server": "Cloud Server",
405
+ "server_icon_mcp": "MCP Server",
406
+ "server_icon_database": "Database",
407
+ "server_icon_database1": "Database 01",
408
+ "server_icon_database2": "Database 02",
409
+ "server_icon_cpu": "CPU",
410
+ "server_icon_chip": "Chip",
411
+ "server_icon_chip2": "Chip 02",
412
+ "server_icon_computer": "Computer",
413
+ "server_icon_laptop": "Laptop",
414
+ "server_icon_terminal": "Computer Terminal",
415
+ "server_icon_code": "Code",
416
+ "server_icon_ai_brain": "AI Brain",
417
+ "server_icon_ai_brain2": "AI Brain 02",
418
+ "server_icon_ai_cloud": "AI Cloud",
419
+ "server_icon_ai_network": "AI Network",
420
+ "server_icon_ai_chat": "AI Chat",
421
+ "server_icon_cellular": "Cellular Network",
422
+ "server_icon_plug1": "Plug 01",
423
+ "server_icon_plug2": "Plug 02",
424
+ "server_icon_bot": "Bot",
425
+ "server_icon_bot2": "Bot 02",
426
+ "server_icon_robotic": "Robotic",
427
+ "server_icon_rocket": "Rocket",
428
+ "server_icon_star": "Star",
429
+ "server_icon_settings1": "Paramètres 01",
430
+ "server_icon_settings2": "Paramètres 02",
431
+ "server_icon_home1": "Home 01",
432
+ "server_icon_home2": "Home 02",
433
+ "server_icon_folder1": "Folder 01",
434
+ "server_icon_folder2": "Folder 02",
435
+ "server_icon_file1": "File 01",
436
+ "server_icon_lock": "Lock",
437
+ "server_icon_key": "Key 01",
438
+ "server_icon_link": "Link 01",
439
+ "server_icon_globe": "Globe",
440
+ "server_icon_api": "API",
441
+ "server_icon_arrow_right": "Arrow Right 01",
442
+ "server_icon_check": "Check Circle",
443
+ "server_icon_alert": "Alert Circle",
444
+ "server_icon_info": "Info Circle",
445
+ "server_icon_zap": "Zap",
446
+ "server_icon_cloud_upload": "Cloud Upload",
447
+ "server_icon_cloud_download": "Cloud Download",
448
+ "server_icon_refresh": "Refresh",
449
+ "server_icon_hard_drive": "Hard Drive",
450
+ "server_icon_drive": "Drive",
451
+ "personas_title": "Personas",
452
+ "persona_category_general": "General",
453
+ "persona_category_coding": "Coding",
454
+ "persona_category_education": "Education",
455
+ "persona_category_creative": "Creative",
456
+ "persona_builtin_section": "BUILT-IN",
457
+ "persona_my_section": "MY PERSONAS",
458
+ "clone_edit": "Clone & Edit",
459
+ "builtin_badge": "Built-in",
460
+ "no_personas_found": "Aucun personas found",
461
+ "no_personas_desc": "Créer your first persona to customize AI behavior.",
462
+ "edit_persona": "Modifier Persona",
463
+ "create_persona": "Créer Persona",
464
+ "create_persona_button": "Create",
465
+ "emoji_label": "Emoji",
466
+ "name_label": "Name",
467
+ "my_persona_hint": "My Persona",
468
+ "category_label": "Category",
469
+ "description_optional": "Description (optional)",
470
+ "description_hint": "What this persona does...",
471
+ "system_prompt": "Prompt système",
472
+ "character_count_max": "{currentLen}/4000",
473
+ "no_prompt_placeholder": "Aucun prompt yet...",
474
+ "prompt_hint": "You are a helpful assistant...",
475
+ "prompt_required": "System prompt is required",
476
+ "prompt_max_chars": "Max 4000 characters",
477
+ "advanced_settings": "Advanced Paramètres",
478
+ "temperature_label": "Température (0.0-2.0)",
479
+ "top_p_label": "Top P (0.0-1.0)",
480
+ "temp_hint": "0.7",
481
+ "top_p_hint": "0.9",
482
+ "range_0_2": "0.0-2.0",
483
+ "range_0_1": "0.0-1.0",
484
+ "persona_updated": "Persona updated",
485
+ "persona_created": "Persona created",
486
+ "tts_models_title": "Text To Speech Models",
487
+ "always_available": "Always available",
488
+ "tts_system_desc": "Uses your device's built-in text-to-speech engine.\nAucun downloads required. Voice selection uses your device's system settings.",
489
+ "downloading_status": "Downloading...",
490
+ "tts_kitten_desc": "Lightning-fast neural TTS with 8 expressive voices.\nRequires {size} download.",
491
+ "tts_piper_desc": "Fast offline Piper voices with 2 expressive voices.\nRequires {size} download per voice.",
492
+ "engine_spec": "{sizeMb} MB · {ramMb} MB RAM · {voiceCount} voices",
493
+ "on_device_models_title": "On-Device Models",
494
+ "settings_huggingface_token": "Hugging Face Token (Optional)",
495
+ "settings_huggingface_token_desc": "Required only for gated models (e.g. Gemma). Get a token at huggingface.co/settings/tokens.",
496
+ "settings_huggingface_token_set": "Token saved",
497
+ "settings_huggingface_token_cleared": "Token cleared",
498
+ "model_requires_huggingface_token": "Requires a Hugging Face token",
499
+ "model_missing_huggingface_token": "This model is gated on Hugging Face. Ajouter a token in Paramètres → On-Device Inference to download it.",
500
+ "set_huggingface_token": "Set token",
501
+ "clear_huggingface_token": "Clear",
502
+ "edit_huggingface_token_dialog_title": "Hugging Face Access Token",
503
+ "huggingface_token_dialog_hint": "hf_…",
504
+ "server_type_ollama_desc": "Local AI engine. Aucun API key required.",
505
+ "server_type_on_device_desc": "Runs on your phone. Some models need a Hugging Face token.",
506
+ "server_type_lm_studio_desc": "Local API server. Aucun API key required.",
507
+ "available_models": "Available Models",
508
+ "device_memory": "Device Memory",
509
+ "ram_usage": "RAM Usage",
510
+ "memory_healthy": "Healthy",
511
+ "memory_critical": "Critical",
512
+ "memory_low": "Low",
513
+ "ram_used": "{percent}% used",
514
+ "available_ram": "Available RAM",
515
+ "total_capacity": "Total Capacity",
516
+ "loaded_status": "Loaded",
517
+ "inference_backend": "Inference Backend",
518
+ "backend_ios_notice": "Only CPU backend is available on iOS.",
519
+ "backend_cpu_desc": "Works on all devices. Most compatible.",
520
+ "backend_gpu_desc": "OpenCL acceleration. Faster on supported devices.",
521
+ "backend_npu_desc": "Vendor NPU (Qualcomm/MediaTek). Fastest inference.",
522
+ "select_model_title": "Sélectionner Model",
523
+ "refresh_models": "Refresh models",
524
+ "search_models_hint": "Search models...",
525
+ "no_server_connected": "Aucun server connected",
526
+ "add_server_first": "Ajouter a server first to see available models.",
527
+ "failed_load_models": "Failed to load models",
528
+ "no_models_available": "Aucun models available",
529
+ "no_models_match": "Aucun models match \"{searchQuery}\"",
530
+ "model_load_failed": "Failed to load model: {error}",
531
+ "model_unloaded_ollama": "Unload requested for {name}. If Ollama is reachable, the model is released immediately.",
532
+ "model_unloaded_success": "{name} unloaded successfully",
533
+ "model_unload_failed": "Failed to unload model: {error}",
534
+ "unload_from_server": "Unload from server",
535
+ "context_chip": "{ctx} ctx",
536
+ "unload_all_models": "Unload all",
537
+ "loaded_models_count": "{count} loaded",
538
+ "all_models_unloaded": "All models unloaded",
539
+ "branch_chat": "Branch chat",
540
+ "branch_chat_desc": "Start a new conversation from this message",
541
+ "edit_assistant_message_desc": "Modifier the assistant response text.",
542
+ "switch_to_model": "Switch to {modelName}",
543
+ "download_notification_title": "Downloading {modelName}...",
544
+ "download_complete_notification": "Télécharger complete!",
545
+ "download_complete_body": "{modelName} has been downloaded successfully.",
546
+ "download_failed_notification": "Télécharger failed: {error}",
547
+ "download_failed_body": "Failed to download {modelName}.",
548
+ "engine_name_system": "System TTS",
549
+ "engine_tagline_system": "Built-in device engine",
550
+ "engine_name_kitten": "Kitten TTS",
551
+ "engine_tagline_kitten": "High-speed neural TTS",
552
+ "engine_name_sherpa": "Sherpa ONNX VITS",
553
+ "engine_tagline_sherpa": "Offline Piper voices",
554
+ "voice_jasper": "Jasper",
555
+ "voice_bella": "Bella",
556
+ "voice_bruno": "Bruno",
557
+ "voice_luna": "Luna",
558
+ "voice_hugo": "Hugo",
559
+ "voice_rosie": "Rosie",
560
+ "voice_leo": "Leo",
561
+ "voice_kiki": "Kiki",
562
+ "voice_lessac": "Lessac (US)",
563
+ "voice_ryan": "Ryan (US)",
564
+ "model_qwen_3": "Qwen 3 0.6B",
565
+ "model_qwen_3_desc": "Smallest general-purpose chat model. Fast responses, low memory usage.",
566
+ "model_license_apache": "Apache-2.0",
567
+ "model_qwen_25": "Qwen 2.5 1.5B Instruct",
568
+ "model_qwen_25_desc": "Balanced quality and size. Good for general conversation.",
569
+ "model_deepseek": "DeepSeek R1 Distill Qwen 1.5B",
570
+ "model_deepseek_desc": "Reasoning and chain-of-thought model. Best for logical tasks.",
571
+ "model_license_mit": "MIT",
572
+ "model_gemma": "Gemma 4 E2B Instruct",
573
+ "model_gemma_desc": "Google flagship model. Highest quality, requires more RAM.",
574
+ "export_header": "*Exported from LocalMind — {date}*",
575
+ "export_role_user": "## 👤 User",
576
+ "export_role_assistant": "## 🤖 Assistant",
577
+ "export_role_system": "## ⚙️ System",
578
+ "export_role_tool": "## 🔧 Tool",
579
+ "export_text_user": "[USER]",
580
+ "export_text_assistant": "[ASSISTANT]",
581
+ "export_text_system": "[SYSTEM]",
582
+ "export_text_tool": "[TOOL]",
583
+ "export_label_user": "USER",
584
+ "export_label_assistant": "ASSISTANT",
585
+ "export_label_system": "SYSTEM",
586
+ "export_label_tool": "TOOL",
587
+ "select_model_hint": "Sélectionner a model to start chatting",
588
+ "test_notification_title": "Test notification",
589
+ "test_notification_body": "This is a test notification for model download progress.",
590
+ "tts_supports_background": "Supports background playback as native audio",
591
+ "tts_other_services_background_note": "Nonte: The other TTS services support background playback as native audio.",
592
+ "gguf_imported_models_title": "Imported GGUF models",
593
+ "gguf_imported_models_empty_subtitle": "Import a GGUF from your device or add one from Hugging Face. Imported models run locally with llama.cpp.",
594
+ "gguf_imported_models_ready": "imported models ready for local inference.",
595
+ "gguf_curated_models_subtitle": "Curated on-device models you can download and manage inside LocalMind.",
596
+ "gguf_only_supported": "Only GGUF models are supported for this import.",
597
+ "gguf_imported_from_local_file": "imported from local file.",
598
+ "gguf_import_failed": "Failed to import GGUF model",
599
+ "gguf_imported_from_huggingface": "imported from Hugging Face.",
600
+ "gguf_import_canceled": "GGUF import canceled.",
601
+ "gguf_enter_huggingface_url": "Enter a Hugging Face GGUF URL.",
602
+ "gguf_only_official_huggingface_urls": "Only official Hugging Face GGUF URLs are supported.",
603
+ "gguf_use_https_url": "Use an HTTPS Hugging Face URL for GGUF import.",
604
+ "gguf_url_must_point_to_file": "The Hugging Face URL must point directly to a .gguf file.",
605
+ "gguf_unable_to_detect_file_name": "Unable to determine the GGUF file name.",
606
+ "gguf_download_empty": "The downloaded GGUF file was empty or missing.",
607
+ "gguf_selected_file_missing": "Selected model file does not exist.",
608
+ "gguf_import_action": "Import GGUF",
609
+ "gguf_overview_title": "Bring your own GGUF models",
610
+ "gguf_overview_subtitle": "Import a .gguf from local storage or download one straight from Hugging Face. Imported models stay on this device and load with llama.cpp.",
611
+ "gguf_imported_count_label": "imported",
612
+ "gguf_local_files_label": "local files",
613
+ "gguf_huggingface_label": "Hugging Face",
614
+ "gguf_import_local_title": "Import local GGUF",
615
+ "gguf_import_local_subtitle": "Copy a .gguf file from this device",
616
+ "gguf_import_huggingface_title": "Import from Hugging Face",
617
+ "gguf_import_huggingface_subtitle": "Paste a GGUF URL or repo path",
618
+ "gguf_no_imported_title": "Aucun imported GGUF models yet",
619
+ "gguf_no_imported_subtitle": "You can bring your own GGUF file from device storage or paste a Hugging Face URL or repo path that points to a .gguf file.",
620
+ "gguf_import_huggingface_dialog_title": "Import GGUF from Hugging Face",
621
+ "gguf_import_huggingface_dialog_subtitle": "Paste a direct GGUF URL or a Hugging Face repo path like `owner/repo/blob/main/model.gguf`. Blob links are converted automatically.",
622
+ "gguf_url_or_repo_path": "GGUF URL or repo path",
623
+ "paste": "Paste",
624
+ "gguf_browse": "Browse GGUFs",
625
+ "gguf_huggingface_token_ready": "Hugging Face token ready",
626
+ "gguf_huggingface_token_optional": "Token optional but recommended",
627
+ "gguf_huggingface_token_ready_desc": "Your saved token will be used automatically for gated or private repositories.",
628
+ "gguf_huggingface_token_optional_desc": "Requires a Hugging Face token. Ajouter one in Paramètres if this GGUF is gated or private.",
629
+ "gguf_downloading": "Downloading GGUF",
630
+ "gguf_preparing": "Preparing",
631
+ "gguf_preparing_download": "Preparing download...",
632
+ "gguf_cancel_import": "Annuler import",
633
+ "clipboard_empty": "Clipboard is empty.",
634
+ "could_not_open_huggingface": "Could not open Hugging Face.",
635
+ "gguf_paste_url_error": "Paste a Hugging Face GGUF URL or repo path.",
636
+ "gguf_blob_link": "Blob link",
637
+ "gguf_repository_label": "Repository",
638
+ "gguf_detected_path_label": "Detected path",
639
+ "gguf_imported_section_label": "Imported GGUF",
640
+ "gguf_already_available": "Already available on this device",
641
+ "gguf_curated_models_short": "Curated on-device models",
642
+ "execute_tool_title": "Execute Tool",
643
+ "execute_tool_request_desc": "The model is requesting to execute the following tool:",
644
+ "reject": "Reject",
645
+ "approve": "Approve",
646
+ "server_type_help": "Pick the provider before filling connection details.",
647
+ "server_identity_title": "Identity",
648
+ "server_identity_desc": "Name this server and choose how it appears in the list.",
649
+ "server_connection_title": "Connection",
650
+ "server_connection_desc": "Use the address and port exposed by your server.",
651
+ "server_authentication_title": "Authentication",
652
+ "server_authentication_required_desc": "OpenRouter requires an API key before testing.",
653
+ "server_authentication_optional_desc": "Leave the API key empty if this server does not require one.",
654
+ "mcp_tools_title": "MCP Tools",
655
+ "available_tools": "Available tools",
656
+ "unable_load_tools": "Unable to load tools",
657
+ "no_tools_registered": "Aucun tools registered",
658
+ "no_tools_registered_desc": "Enable the example MCP server or add MCP integrations from chat settings.",
659
+ "example_mcp_server_title": "Example MCP server",
660
+ "example_mcp_server_desc": "Registers example.echo and example.word_count through the same MCP tool provider used by external servers.",
661
+ "disable_example_server": "Disable example server",
662
+ "enable_example_server": "Enable example server",
663
+ "built_in_label": "Built-in",
664
+ "highlights_label": "Highlights",
665
+ "built_with_label": "Built with",
666
+ "local_label": "Local",
667
+ "gguf_format_label": "GGUF",
668
+ "tool_status_requested": "Requested",
669
+ "tool_status_approved": "Approved",
670
+ "tool_status_rejected": "Rejected",
671
+ "tool_status_running": "Running",
672
+ "tool_status_done": "Done",
673
+ "tool_status_failed": "Failed",
674
+ "model_favorite_toggle": "Toggle favorite",
675
+ "model_note_label": "Nonte",
676
+ "model_note_hint": "Ajouter a note about this model…",
677
+ "unload_models_before_load": "Unload all models before loading a new one",
678
+ "temp_chat_keyboard_incognito": "Incognito keyboard in temporary chat",
679
+ "temp_chat_keyboard_incognito_desc": "Disables keyboard learning and suggestions in temporary chats (e.g. SwiftKey incognito).",
680
+ "resume_last_chat": "Resume last chat on launch",
681
+ "resume_last_chat_desc": "Restauration your last open conversation when reopening the app.",
682
+ "export_all_data": "Export all data",
683
+ "import_all_data": "Import all data",
684
+ "export_data_success": "Sauvegarde exported successfully",
685
+ "import_data_success": "Sauvegarde imported successfully",
686
+ "import_data_failed": "Failed to import backup: {error}",
687
+ "import_data_confirm": "Import conversations and custom personas from this backup? Existing items with the same IDs will be updated.",
688
+ "import_settings_confirm": "Replace current settings with the imported backup?",
689
+ "export_conversations": "Export conversations",
690
+ "import_conversations": "Import conversations",
691
+ "export_personas": "Export personas",
692
+ "import_personas": "Import personas",
693
+ "export_settings": "Export settings",
694
+ "import_settings": "Import settings",
695
+ "export_all_zip": "Export all (ZIP)",
696
+ "import_all_zip": "Import all (ZIP)",
697
+ "duplicate_chat": "Duplicate chat",
698
+ "duplicate_chat_success": "Chat duplicated",
699
+ "move_to_folder": "Move to folder",
700
+ "remove_from_folder": "Retirer from folder",
701
+ "create_folder": "Créer folder",
702
+ "new_folder": "New folder",
703
+ "folder_name_hint": "Folder name",
704
+ "all_chats": "All",
705
+ "unfiled_chats": "Unfiled",
706
+ "create": "Create",
707
+ "server_path_prefix_label": "API path prefix",
708
+ "server_path_prefix_hint": "/your-secret-token",
709
+ "search_message_contents": "Search message contents",
710
+ "message_search_results": "Message matches",
711
+ "saved_messages_title": "Saved Messages",
712
+ "nav_saved_messages": "Saved Messages",
713
+ "saved_messages_empty": "Aucun saved messages yet. Bookmark a message from its options menu.",
714
+ "save_message": "Enregistrer message",
715
+ "message_saved": "Message saved",
716
+ "token_count": "{count} tokens",
717
+ "estimated_token_count": "~{count} tokens (estimated)",
718
+ "test_tts_section_title": "Test voice",
719
+ "test_tts_hint": "Enter text to hear the current TTS engine…",
720
+ "test_speak_button": "Speak",
721
+ "scroll_to_bottom": "Scroll to bottom",
722
+ "generate_ai_response": "Generate AI response",
723
+ "no_response": "Aucun response",
724
+ "export": "Export",
725
+ "import": "Import",
726
+ "conversations_label": "Conversations",
727
+ "personas_label": "Personas",
728
+ "settings_label": "Paramètres",
729
+ "export_conversation": "Export conversation",
730
+ "tts_process_markdown": "Process markdown for speech",
731
+ "tts_process_markdown_desc": "Strip formatting like **bold** before reading aloud",
732
+ "tts_skip_seconds": "Skip interval",
733
+ "tts_skip_seconds_desc": "Forward and rewind jump size during playback",
734
+ "tts_skip_seconds_value": "{seconds}s",
735
+ "preview_system_prompts": "Preview system prompts",
736
+ "welcome_message_1": "What can I help you with today?",
737
+ "welcome_message_2": "Ask me anything — I'm ready when you are.",
738
+ "welcome_message_3": "Your data is processed locally and never leaves your device.",
739
+ "welcome_message_4": "Need ideas? Try one of the quick prompts.",
740
+ "temporary_chat": "Temporary chat",
741
+ "temporary_chat_desc": "Chats aren't saved to history.",
742
+ "temporary_chat_banner": "Temporary chat — not saved to history",
743
+ "temporary_chat_save_warning_title": "Enregistrer message in temporary chat?",
744
+ "temporary_chat_save_warning_body": "This chat is temporary and hidden from history. The saved message will still appear in Saved Messages.",
745
+ "save_to_history": "Enregistrer to history",
746
+ "share_conversation": "Share conversation",
747
+ "download_tts_audio": "Télécharger audio",
748
+ "tts_download_unavailable": "Télécharger is only available for Piper and Kitten TTS",
749
+ "tts_download_no_audio": "Aucun audio available to download yet",
750
+ "tts_download_success": "Audio saved",
751
+ "return_to_chat": "Return to chat",
752
+ "return_to_temp_chat": "Return to temporary chat",
753
+ "insert_saved_message": "Insert saved message",
754
+ "insert_saved_message_desc": "Choisir a saved message to add to your input",
755
+ "model_info": "Modèle info",
756
+ "model_name": "Modèle name",
757
+ "model_identifier": "Identifier",
758
+ "not_available": "Nont available",
759
+ "save_message_folders": "Enregistrer message",
760
+ "remove_from_saved": "Retirer from saved",
761
+ "message_already_saved": "Saved",
762
+ "stream_ttft": "Time to first token",
763
+ "stream_tokens_per_sec": "Tokens per second",
764
+ "stream_stop_reason": "Stop reason",
765
+ "stream_input_tokens": "Input tokens",
766
+ "stream_output_tokens": "Output tokens",
767
+ "stream_generation_time": "Generation time",
768
+ "attach_image": "Photos",
769
+ "attach_text_document": "Documents",
770
+ "attach_shortcut_images": "Photos",
771
+ "attach_shortcut_documents": "Files",
772
+ "attach_shortcut_saved": "Saved",
773
+ "add_attachment": "Ajouter attachment",
774
+ "add_to_chat": "Ajouter to chat",
775
+ "choose_what_to_attach": "What would you like to add?",
776
+ "choose_attachment_subtitle": "Pick a source to attach to your message",
777
+ "photo_permission_denied": "Photo access is required to attach images",
778
+ "select_model_prompt": "Sélectionner model",
779
+ "characters_label": "Characters",
780
+ "exit_temporary_chat_title": "Exit temporary chat?",
781
+ "exit_temporary_chat_body": "This will discard the current temporary chat and return to a new chat.",
782
+ "saved_message_temp_snap_unavailable": "This message was saved from a temporary chat and can't be opened in its original conversation.",
783
+ "filter_title": "Filter",
784
+ "filter_pinned": "Pinned",
785
+ "filter_archived": "Archived",
786
+ "filter_temp_chats": "Temporary chats",
787
+ "filter_user_messages": "User messages",
788
+ "filter_assistant_messages": "Assistant messages",
789
+ "archive_chat": "Archive",
790
+ "unarchive_chat": "Unarchive",
791
+ "conversation_message_count": "{count, plural, one{1 message} other{{count} messages}}",
792
+ "conversation_character_count": "{count} chars",
793
+ "generate_title_with_ai": "Generate with AI",
794
+ "generating_title": "Generating...",
795
+ "generate_title_failed": "Could not generate a title",
796
+ "lm_studio_model_browser_title": "Browse models",
797
+ "lm_studio_model_search_hint": "Search models by name or author…",
798
+ "lm_studio_staff_picks": "Staff picks",
799
+ "lm_studio_community_models": "Community models",
800
+ "lm_studio_no_models": "Aucun models found",
801
+ "lm_studio_models_count": "{count} models",
802
+ "lm_studio_browse_models": "Browse & download",
803
+ "lm_studio_model_search": "LMS Modèle Search",
804
+ "lm_studio_downloads_title": "Downloads",
805
+ "lm_studio_choose_quant": "Choisir a download option",
806
+ "lm_studio_use_default_quant": "Use default",
807
+ "lm_studio_recommended": "Recommended",
808
+ "lm_studio_clear_downloads": "Clear finished",
809
+ "lm_studio_no_downloads": "Aucun downloads yet",
810
+ "lm_studio_downloads_disclaimer": "Downloads run on the LM Studio host. Pausing, stopping, and deleting models must be done on that computer — not from this app.",
811
+ "lm_studio_staff_pick": "Staff pick",
812
+ "lm_studio_params": "PARAMS",
813
+ "lm_studio_arch": "ARCH",
814
+ "lm_studio_domain": "DOMAIN",
815
+ "lm_studio_format": "FORMAT",
816
+ "lm_studio_vision": "Vision",
817
+ "lm_studio_tool_use": "Tool use",
818
+ "lm_studio_reasoning": "Reasoning",
819
+ "lm_studio_download_options": "Télécharger options",
820
+ "lm_studio_download": "Download",
821
+ "lm_studio_download_size": "Télécharger {size}",
822
+ "lm_studio_downloading_percent": "Downloading {percent}%",
823
+ "lm_studio_readme_unavailable": "README not available for this model.",
824
+ "lm_studio_full_gpu_offload": "Full GPU offload possible",
825
+ "lm_studio_partial_gpu_offload": "Partial GPU offload possible",
826
+ "lm_studio_likely_too_large": "Likely too large",
827
+ "lm_studio_available_ram_gb": "Available RAM (GB, optional)",
828
+ "lm_studio_available_vram_gb": "Available VRAM (GB, optional)",
829
+ "lm_studio_memory_settings_title": "Memory for recommendations",
830
+ "lm_studio_memory_settings_desc": "Used to estimate whether models fit on your machine in the model browser.",
831
+ "think_button_label": "Think",
832
+ "thinking_mode_title": "Thinking mode",
833
+ "reasoning_effort_low": "Low",
834
+ "reasoning_effort_medium": "Medium",
835
+ "reasoning_effort_high": "High",
836
+ "could_not_read_file": "Could not read file",
837
+ "server_offline": "Serveur Offline",
838
+ "could_not_establish_connection": "Could not establish a connection to the server. Please check if your server is running and the host/port settings are correct.",
839
+ "retry_connection": "Retry Connection",
840
+ "tokens_label": "Tokens",
841
+ "enter_context_length": "Enter context length...",
842
+ "openrouter_disclosure": "By connecting this provider, your chat messages and inputs will be sent to their servers. LocalMind does not track or store your conversations.",
843
+ "welcome_message_cloud": "Your messages are sent to your connected provider.",
844
+ "privacy_policy": "Politique de confidentialité",
845
+ "cloud_sync": "S3 Synchronisation Cloud",
846
+ "cloud_sync_description": "End-to-end encrypted sync to your own S3-compatible server",
847
+ "cloud_sync_endpoint": "Endpoint URL",
848
+ "cloud_sync_bucket": "Bucket",
849
+ "cloud_sync_region": "Region",
850
+ "cloud_sync_prefix": "Prefix",
851
+ "cloud_sync_access_key": "Access key ID",
852
+ "cloud_sync_secret_key": "Secret access key",
853
+ "cloud_sync_session_token": "Session token (optional)",
854
+ "cloud_sync_passphrase": "Encryption passphrase",
855
+ "cloud_sync_confirm_passphrase": "Confirm passphrase",
856
+ "cloud_sync_path_style": "Use path-style addressing",
857
+ "cloud_sync_allow_http": "Allow insecure HTTP",
858
+ "cloud_sync_http_warning": "HTTP exposes request metadata and credentials to the network. Use it only for a trusted local S3 server.",
859
+ "cloud_sync_test": "Test connection",
860
+ "cloud_sync_enable": "Enable encrypted sync",
861
+ "cloud_sync_now": "Sync now",
862
+ "cloud_sync_disconnect": "Disconnect this device",
863
+ "cloud_sync_last_synced": "Last synced",
864
+ "cloud_sync_never": "Never",
865
+ "cloud_sync_conflicts": "Conflicts preserved",
866
+ "cloud_sync_passphrase_mismatch": "Passphrases do not match",
867
+ "crash_report_title": "Something went wrong",
868
+ "crash_report_stack_trace": "Stack trace",
869
+ "crash_report_tap_to_expand": "Tap to expand",
870
+ "crash_report_button": "Report this crash",
871
+ "crash_try_again": "Try again",
872
+ "crash_report_empty_stack": "<empty>",
873
+ "crash_report_disclaimer": "Reporting opens GitHub with diagnostics prefilled. You stay in control — nothing is submitted automatically. Please review and remove any sensitive content before submitting.",
874
+ "crash_report_copied": "Copied to clipboard",
875
+ "report_a_problem": "Report a problem",
876
+ "rename_folder": "Rename folder",
877
+ "delete_folder": "Supprimer folder",
878
+ "delete_folder_title": "Supprimer folder?",
879
+ "delete_folder_body": "Are you sure you want to delete \"{name}\"? Conversations or saved messages inside will be moved back to \"Unfiled\". This cannot be undone.",
880
+ "folder_name_required": "Please enter a folder name",
881
+ "model_required_toast": "You need to select a model first"
882
+}
\ No newline at end of file
lib/l10n/app_localizations.dart
+5
@@ -9,6 +9,7 @@ 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_fr.dart';
13
import 'app_localizations_hi.dart';
14
import 'app_localizations_it.dart';
15
import 'app_localizations_ja.dart';
@@ -106,6 +107,7 @@ abstract class AppLocalizations {
107
Locale('bn'),
108
Locale('en'),
109
Locale('es'),
110
+ Locale('fr'),
111
Locale('hi'),
112
Locale('it'),
113
Locale('ja'),
@@ -5405,6 +5407,7 @@ class _AppLocalizationsDelegate
5407
'bn',
5408
'en',
5409
'es',
5410
+ 'fr',
5411
'hi',
5412
'it',
5413
'ja',
@@ -5440,6 +5443,8 @@ AppLocalizations lookupAppLocalizations(Locale locale) {
5443
return AppLocalizationsEn();
5444
case 'es':
5445
return AppLocalizationsEs();
5446
+ case 'fr':
5447
+ return AppLocalizationsFr();
5448
case 'hi':
5449
return AppLocalizationsHi();
5450
case 'it':
lib/l10n/app_localizations_fr.dart
new
+2881
@@ -0,0 +1,2881 @@
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 French (`fr`).
8
+class AppLocalizationsFr extends AppLocalizations {
9
+ AppLocalizationsFr([String locale = 'fr']) : super(locale);
10
+
11
+ @override
12
+ String get app_name => 'LocalMind';
13
+
14
+ @override
15
+ String get app_tagline => 'Votre IA. Votre Appareil. Vos Règles.';
16
+
17
+ @override
18
+ String get app_version => '1.0.0';
19
+
20
+ @override
21
+ String get cancel => 'Annuler';
22
+
23
+ @override
24
+ String get confirm => 'Confirmer';
25
+
26
+ @override
27
+ String get delete => 'Supprimer';
28
+
29
+ @override
30
+ String get save => 'Enregistrer';
31
+
32
+ @override
33
+ String get retry => 'Réessayer';
34
+
35
+ @override
36
+ String get close => 'Fermer';
37
+
38
+ @override
39
+ String get done => 'Terminé';
40
+
41
+ @override
42
+ String get continue_action => 'Continuer';
43
+
44
+ @override
45
+ String get skip => 'Passer';
46
+
47
+ @override
48
+ String get install => 'Installer';
49
+
50
+ @override
51
+ String get download => 'Télécharger';
52
+
53
+ @override
54
+ String get resume => 'Reprendre';
55
+
56
+ @override
57
+ String get pause => 'Pause';
58
+
59
+ @override
60
+ String get stop => 'Arrêter';
61
+
62
+ @override
63
+ String get edit => 'Modifier';
64
+
65
+ @override
66
+ String get preview => 'Aperçu';
67
+
68
+ @override
69
+ String get unload => 'Décharger';
70
+
71
+ @override
72
+ String get load => 'Charger';
73
+
74
+ @override
75
+ String get rename => 'Renommer';
76
+
77
+ @override
78
+ String get pin => 'Épingler';
79
+
80
+ @override
81
+ String get unpin => 'Désépingler';
82
+
83
+ @override
84
+ String get share => 'Partager';
85
+
86
+ @override
87
+ String get copy => 'Copier';
88
+
89
+ @override
90
+ String get copied => 'Copié !';
91
+
92
+ @override
93
+ String get copied_to_clipboard => 'Copié dans le presse-papiers';
94
+
95
+ @override
96
+ String get select => 'Select';
97
+
98
+ @override
99
+ String get active => 'Active';
100
+
101
+ @override
102
+ String get all => 'Tout';
103
+
104
+ @override
105
+ String get none => 'Aucun';
106
+
107
+ @override
108
+ String get none_selected => 'Nonne selected';
109
+
110
+ @override
111
+ String get online => 'Online';
112
+
113
+ @override
114
+ String get connected => 'Connected';
115
+
116
+ @override
117
+ String get checking => 'Checking';
118
+
119
+ @override
120
+ String get offline => 'Offline';
121
+
122
+ @override
123
+ String get error => 'Erreur';
124
+
125
+ @override
126
+ String get unknown_error => 'Unknown error';
127
+
128
+ @override
129
+ String get not_now => 'Nont Nonw';
130
+
131
+ @override
132
+ String get enable => 'Enable';
133
+
134
+ @override
135
+ String get proceed_anyway => 'Proceed Anyway';
136
+
137
+ @override
138
+ String get test_connection => 'Test Connection';
139
+
140
+ @override
141
+ String get testing => 'Testing...';
142
+
143
+ @override
144
+ String get connection_successful => 'Connection successful!';
145
+
146
+ @override
147
+ String get connection_failed => 'Connection failed. Check your settings.';
148
+
149
+ @override
150
+ String get save_continue => 'Enregistrer & Continue';
151
+
152
+ @override
153
+ String get save_changes => 'Enregistrer Changes';
154
+
155
+ @override
156
+ String get finish_setup => 'Finish Setup';
157
+
158
+ @override
159
+ String get start_new_chat => 'Start New Chat';
160
+
161
+ @override
162
+ String get cannot_undo => 'This action cannot be undone.';
163
+
164
+ @override
165
+ String get ram_warning => 'RAM Warning';
166
+
167
+ @override
168
+ String get recommended => 'RECOMMENDED';
169
+
170
+ @override
171
+ String get may_be_large => 'May be too large for this device';
172
+
173
+ @override
174
+ String get calculating => 'Calculating...';
175
+
176
+ @override
177
+ String get download_failed => 'Télécharger failed';
178
+
179
+ @override
180
+ String get downloaded => 'Downloaded';
181
+
182
+ @override
183
+ String get not_downloaded => 'Nont downloaded';
184
+
185
+ @override
186
+ String get installed => 'Installed';
187
+
188
+ @override
189
+ String get not_installed => 'Nont installed';
190
+
191
+ @override
192
+ String get loading => 'Chargement...';
193
+
194
+ @override
195
+ String get thinking => 'Thinking';
196
+
197
+ @override
198
+ String get processing => 'Processing...';
199
+
200
+ @override
201
+ String get initializing => 'Initializing...';
202
+
203
+ @override
204
+ String get ready => 'Ready';
205
+
206
+ @override
207
+ String get preparing_app => 'Preparing app...';
208
+
209
+ @override
210
+ String get initializing_services => 'Initializing services...';
211
+
212
+ @override
213
+ String get configuring_server => 'Configuring server...';
214
+
215
+ @override
216
+ String get startup_failed => 'Startup failed';
217
+
218
+ @override
219
+ String get something_went_wrong => 'Something went wrong';
220
+
221
+ @override
222
+ String get delete_model_title => 'Supprimer le modèle';
223
+
224
+ @override
225
+ String delete_model_body(String name) {
226
+ return 'Voulez-vous vraiment supprimer $name ?';
227
+ }
228
+
229
+ @override
230
+ String delete_model_body_with_size(String name, String size) {
231
+ return 'Voulez-vous vraiment supprimer $name ? Cela libérera environ $size d\'espace.\n\nVous pourrez retélécharger ce modèle plus tard si nécessaire.';
232
+ }
233
+
234
+ @override
235
+ String get delete_voice_title => 'Supprimer la voix';
236
+
237
+ @override
238
+ String delete_voice_body(String name, String size) {
239
+ return 'Voulez-vous vraiment supprimer $name ? Cela libérera environ $size d\'espace.\n\nVous pourrez retélécharger cette voix plus tard si nécessaire.';
240
+ }
241
+
242
+ @override
243
+ String get delete_server_title => 'Supprimer le serveur';
244
+
245
+ @override
246
+ String delete_server_body(String name) {
247
+ return 'Voulez-vous vraiment supprimer « $name » ? Cette action est irréversible.';
248
+ }
249
+
250
+ @override
251
+ String get delete_conversation_title => 'Supprimer la conversation';
252
+
253
+ @override
254
+ String delete_conversation_body(String title) {
255
+ return 'Voulez-vous vraiment supprimer « $title » ? Cette action est irréversible.';
256
+ }
257
+
258
+ @override
259
+ String get delete_message_title => 'Supprimer message?';
260
+
261
+ @override
262
+ String delete_persona_title(String name) {
263
+ return 'Supprimer « $name » ?';
264
+ }
265
+
266
+ @override
267
+ String get delete_persona_body =>
268
+ 'Voulez-vous vraiment supprimer ce persona ? Cette action ne peut pas être annulée.';
269
+
270
+ @override
271
+ String get delete_builtin_persona_body =>
272
+ 'This is a built-in persona. You can restore it later from Paramètres.';
273
+
274
+ @override
275
+ String get restore_builtin_personas => 'Restauration default personas';
276
+
277
+ @override
278
+ String get restore_builtin_personas_desc =>
279
+ 'Re-add any built-in personas you\'ve deleted';
280
+
281
+ @override
282
+ String get restore_builtin_personas_success => 'Default personas restored';
283
+
284
+ @override
285
+ String get clear_personas => 'Clear personas';
286
+
287
+ @override
288
+ String get enable_image_compression => 'Compress images before sending';
289
+
290
+ @override
291
+ String get enable_image_compression_desc =>
292
+ 'Resize and compress attached images so uploads stay within server limits';
293
+
294
+ @override
295
+ String get image_compression_level => 'Compression aggressiveness';
296
+
297
+ @override
298
+ String get image_compression_level_desc =>
299
+ 'Higher aggressiveness produces smaller uploads at lower quality';
300
+
301
+ @override
302
+ String get image_compression_level_low => 'Low';
303
+
304
+ @override
305
+ String get image_compression_level_medium => 'Medium';
306
+
307
+ @override
308
+ String get image_compression_level_high => 'High';
309
+
310
+ @override
311
+ String get sort_models_tooltip => 'Sort models';
312
+
313
+ @override
314
+ String get sort_by_favorites => 'Favorites first';
315
+
316
+ @override
317
+ String get sort_by_name => 'Name (A-Z)';
318
+
319
+ @override
320
+ String get sort_by_size_smallest => 'Size (smallest first)';
321
+
322
+ @override
323
+ String get sort_by_size_largest => 'Size (largest first)';
324
+
325
+ @override
326
+ String get sort_by_context_length => 'Context length';
327
+
328
+ @override
329
+ String bulk_ai_rename_progress(int done, int total) {
330
+ return 'Renommage $done/$total...';
331
+ }
332
+
333
+ @override
334
+ String selected_count(int count) {
335
+ return '$count sélectionné(s)';
336
+ }
337
+
338
+ @override
339
+ String get ai_rename_tooltip => 'Rename selected with AI';
340
+
341
+ @override
342
+ String get new_chat_in_folder_tooltip => 'New chat in this folder';
343
+
344
+ @override
345
+ String total_tokens_count(int count) {
346
+ return '$count jetons';
347
+ }
348
+
349
+ @override
350
+ String get smart_replies_use_persona => 'Use persona in smart replies';
351
+
352
+ @override
353
+ String get smart_replies_use_persona_desc =>
354
+ 'Suggested replies match the tone of the active persona instead of a generic assistant';
355
+
356
+ @override
357
+ String get keep_persona_on_new_chat => 'Keep persona on new chat';
358
+
359
+ @override
360
+ String get keep_persona_on_new_chat_desc =>
361
+ 'Don\'t clear the selected persona(s) after starting a new chat';
362
+
363
+ @override
364
+ String get role_swap_button_enabled => 'Show role-swap button';
365
+
366
+ @override
367
+ String get role_swap_button_enabled_desc =>
368
+ 'Show a button in the chat input to send your message as the assistant instead of the user, without generating a response';
369
+
370
+ @override
371
+ String get send_as_user_tooltip => 'Send as user';
372
+
373
+ @override
374
+ String get send_as_assistant_tooltip => 'Send as assistant (no response)';
375
+
376
+ @override
377
+ String get insert_without_generating_tooltip => 'Insert without generating';
378
+
379
+ @override
380
+ String get token_usage_title => 'Token Usage';
381
+
382
+ @override
383
+ String get total_tokens_label => 'Tokens used';
384
+
385
+ @override
386
+ String get usage_percent_label => 'Context used';
387
+
388
+ @override
389
+ String get export_choice_title => 'Export';
390
+
391
+ @override
392
+ String get export_choice_body => 'How would you like to export this?';
393
+
394
+ @override
395
+ String get copy_to_clipboard => 'Copy to Clipboard';
396
+
397
+ @override
398
+ String bulk_export_conversations_success(int count) {
399
+ return '$count conversations exportées';
400
+ }
401
+
402
+ @override
403
+ String get bulk_ai_rename_confirm_title => 'Rename with AI?';
404
+
405
+ @override
406
+ String bulk_ai_rename_confirm_body(int count) {
407
+ return 'This will ask the AI to generate a new title for each of the $count selected conversations, replacing their current titles. This can take a while and cannot be undone.';
408
+ }
409
+
410
+ @override
411
+ String get sort_by_modified_date => 'Last modified';
412
+
413
+ @override
414
+ String get sort_by_created_date => 'Date created';
415
+
416
+ @override
417
+ String get sort_title => 'Sort';
418
+
419
+ @override
420
+ String get clear_conversation_title => 'Clear conversation?';
421
+
422
+ @override
423
+ String get clear_conversation_body =>
424
+ 'This will delete all messages in this conversation.';
425
+
426
+ @override
427
+ String get clear => 'Effacer';
428
+
429
+ @override
430
+ String label_completed(String label) {
431
+ return '$label completed';
432
+ }
433
+
434
+ @override
435
+ String error_with_message(String error) {
436
+ return 'Error: $error';
437
+ }
438
+
439
+ @override
440
+ String preview_failed(String error) {
441
+ return 'Preview failed: $error';
442
+ }
443
+
444
+ @override
445
+ String loading_model(String modelId) {
446
+ return 'Loading $modelId...';
447
+ }
448
+
449
+ @override
450
+ String model_loaded(String modelId, String backend) {
451
+ return 'Modèle loaded: $modelId ($backend)';
452
+ }
453
+
454
+ @override
455
+ String get no_model_loaded =>
456
+ 'Aucun model loaded. Tap \"Manage On-Device Models\" to download and load a model.';
457
+
458
+ @override
459
+ String loading_model_error(String error) {
460
+ return 'Error: $error';
461
+ }
462
+
463
+ @override
464
+ String get delete_conversation => 'Supprimer conversation?';
465
+
466
+ @override
467
+ String get nav_history => 'History';
468
+
469
+ @override
470
+ String get nav_servers => 'Servers';
471
+
472
+ @override
473
+ String get nav_local_models => 'Local Models';
474
+
475
+ @override
476
+ String get nav_tts => 'Text To Speech';
477
+
478
+ @override
479
+ String get nav_personas => 'Personas';
480
+
481
+ @override
482
+ String get nav_settings => 'Paramètres';
483
+
484
+ @override
485
+ String get nav_new_chat => 'New Chat';
486
+
487
+ @override
488
+ String get search_hint => 'Search conversations...';
489
+
490
+ @override
491
+ String get no_server_selected => 'Aucun server selected';
492
+
493
+ @override
494
+ String get switch_server => 'Switch Server';
495
+
496
+ @override
497
+ String get switch_server_subtitle => 'Choisir a server to connect to';
498
+
499
+ @override
500
+ String get manage_servers => 'Manage Servers';
501
+
502
+ @override
503
+ String get open_source => 'Open Source';
504
+
505
+ @override
506
+ String get open_source_desc =>
507
+ 'LocalMind is open source. Follow our progress or contribute on GitHub.';
508
+
509
+ @override
510
+ String get star_on_github => 'Star on GitHub';
511
+
512
+ @override
513
+ String get add_more => 'Ajouter more';
514
+
515
+ @override
516
+ String get on_github => 'on GitHub';
517
+
518
+ @override
519
+ String get could_not_open_github =>
520
+ 'Impossible d\'ouvrir GitHub. Veuillez réessayer plus tard.';
521
+
522
+ @override
523
+ String get settings_title => 'Paramètres';
524
+
525
+ @override
526
+ String get settings_appearance => 'Appearance';
527
+
528
+ @override
529
+ String get settings_language => 'Langue';
530
+
531
+ @override
532
+ String get language_system_default => 'Par défaut du système';
533
+
534
+ @override
535
+ String get settings_tts => 'Text-to-Speech';
536
+
537
+ @override
538
+ String get settings_behavior => 'Behavior';
539
+
540
+ @override
541
+ String get settings_on_device => 'On-Device Inference';
542
+
543
+ @override
544
+ String get settings_default_server => 'Default Server';
545
+
546
+ @override
547
+ String get settings_default_persona => 'Default Persona';
548
+
549
+ @override
550
+ String get settings_privacy => 'Privacy';
551
+
552
+ @override
553
+ String get settings_data_management => 'Data Management';
554
+
555
+ @override
556
+ String get settings_about => 'À propos';
557
+
558
+ @override
559
+ String get theme => 'Thème';
560
+
561
+ @override
562
+ String get theme_system => 'System';
563
+
564
+ @override
565
+ String get theme_light => 'Light';
566
+
567
+ @override
568
+ String get theme_dark => 'Dark';
569
+
570
+ @override
571
+ String get theme_claude => 'Claude';
572
+
573
+ @override
574
+ String get font_size => 'Font Size';
575
+
576
+ @override
577
+ String get font_size_desc => 'Adjust text size in chat.';
578
+
579
+ @override
580
+ String get font_preview => 'The quick brown fox jumps over the lazy dog.';
581
+
582
+ @override
583
+ String get code_theme_dark => 'Code Thème (Dark)';
584
+
585
+ @override
586
+ String get code_theme_light => 'Code Thème (Light)';
587
+
588
+ @override
589
+ String get code_theme_desc =>
590
+ 'Choisir syntax highlighting theme for code blocks.';
591
+
592
+ @override
593
+ String get tts_engine => 'TTS Engine';
594
+
595
+ @override
596
+ String get tts_engine_system => 'System TTS';
597
+
598
+ @override
599
+ String get tts_engine_kitten => 'Kitten TTS';
600
+
601
+ @override
602
+ String get voice => 'Voice';
603
+
604
+ @override
605
+ String get voice_female => 'Female';
606
+
607
+ @override
608
+ String get voice_male => 'Male';
609
+
610
+ @override
611
+ String get voice_other => 'Other';
612
+
613
+ @override
614
+ String get tts_speed => 'TTS Speed';
615
+
616
+ @override
617
+ String get tts_speed_desc => 'Adjust the playback rate.';
618
+
619
+ @override
620
+ String get manage_tts_models => 'Manage TTS Models';
621
+
622
+ @override
623
+ String get manage_on_device_models => 'Manage On-Device Models';
624
+
625
+ @override
626
+ String get enable_smart_reply => 'On-Device Smart Replies';
627
+
628
+ @override
629
+ String get ai_user_response_enabled => 'AI user message (hold send)';
630
+
631
+ @override
632
+ String get ai_user_response_enabled_desc =>
633
+ 'Hold the send button for 3 seconds to have the AI write and send your next message';
634
+
635
+ @override
636
+ String get ai_user_response_tooltip => 'Generate user message with AI';
637
+
638
+ @override
639
+ String get streaming_responses => 'Streaming Responses';
640
+
641
+ @override
642
+ String get auto_generate_titles => 'Auto-generate Titles';
643
+
644
+ @override
645
+ String get send_on_enter => 'Send on Enter';
646
+
647
+ @override
648
+ String get show_system_messages => 'Send Default Prompt système';
649
+
650
+ @override
651
+ String get show_system_messages_desc =>
652
+ 'When no persona is selected, send a default assistant system prompt with each request';
653
+
654
+ @override
655
+ String get show_system_messages_in_chat => 'Show System Messages in Chat';
656
+
657
+ @override
658
+ String get show_system_messages_in_chat_desc =>
659
+ 'Display system messages (e.g. from an imported backup) as visible bubbles in the conversation';
660
+
661
+ @override
662
+ String get haptic_feedback => 'Haptic Feedback';
663
+
664
+ @override
665
+ String get enable_mcp => 'Enable MCP';
666
+
667
+ @override
668
+ String get new_chat_mcp_default => 'New Chat MCP Default';
669
+
670
+ @override
671
+ String get show_data_indicator => 'Show Data Indicator';
672
+
673
+ @override
674
+ String get privacy_info => '\"LocalMind never sees your data\"';
675
+
676
+ @override
677
+ String get delete_all_conversations => 'Supprimer All Conversations';
678
+
679
+ @override
680
+ String get reset_settings_defaults => 'Reset Paramètres to Defaults';
681
+
682
+ @override
683
+ String get chat_title => 'LocalMind';
684
+
685
+ @override
686
+ String get chat_parameters_tooltip => 'Chat Parameters';
687
+
688
+ @override
689
+ String get change_persona => 'Change Persona';
690
+
691
+ @override
692
+ String get set_persona => 'Set Persona';
693
+
694
+ @override
695
+ String get remove_persona => 'Retirer Persona';
696
+
697
+ @override
698
+ String get clear_conversation => 'Clear Conversation';
699
+
700
+ @override
701
+ String get connection_error => 'Connection error. Check your server.';
702
+
703
+ @override
704
+ String get disconnected => 'Disconnected from server.';
705
+
706
+ @override
707
+ String get configure => 'Configure';
708
+
709
+ @override
710
+ String get select_model => 'Sélectionner Model';
711
+
712
+ @override
713
+ String get select_persona => 'Sélectionner Persona';
714
+
715
+ @override
716
+ String get manage_personas => 'Manage personas';
717
+
718
+ @override
719
+ String get personas_combine_hint =>
720
+ 'Sélectionner multiple personas in chat to stack their system prompts.';
721
+
722
+ @override
723
+ String get start_conversation => 'Start a conversation';
724
+
725
+ @override
726
+ String get recent_chats => 'Recent chats';
727
+
728
+ @override
729
+ String get see_all => 'See all';
730
+
731
+ @override
732
+ String get quick_write => 'Help me write a function';
733
+
734
+ @override
735
+ String get quick_explain => 'Explain this code';
736
+
737
+ @override
738
+ String get quick_debug => 'Debug this for me';
739
+
740
+ @override
741
+ String get quick_async => 'How do I use async/await?';
742
+
743
+ @override
744
+ String get history_missing_title => 'History Missing';
745
+
746
+ @override
747
+ String get history_missing_desc =>
748
+ 'Either the messages in this chat were deleted or the history record is corrupted.';
749
+
750
+ @override
751
+ String get technical_details => 'Technical Details';
752
+
753
+ @override
754
+ String get last_error => 'Last Error:';
755
+
756
+ @override
757
+ String get copy_info => 'Copy Info';
758
+
759
+ @override
760
+ String get conversation_id => 'Conversation ID';
761
+
762
+ @override
763
+ String get created_at => 'Created At';
764
+
765
+ @override
766
+ String get expected_messages => 'Expected Messages';
767
+
768
+ @override
769
+ String get debug_dialog_desc =>
770
+ 'Diagnostic information to help identify synchronization issues.';
771
+
772
+ @override
773
+ String get chat_input_hint => 'Ask anything';
774
+
775
+ @override
776
+ String get send_message_tooltip => 'Send message';
777
+
778
+ @override
779
+ String get stop_generation_tooltip => 'Stop generation';
780
+
781
+ @override
782
+ String get attach_images_tooltip => 'Attach images or text';
783
+
784
+ @override
785
+ String get start_listening_tooltip => 'Start listening';
786
+
787
+ @override
788
+ String get stop_listening_tooltip => 'Stop listening';
789
+
790
+ @override
791
+ String tool_label(String toolCallId) {
792
+ return 'Tool: $toolCallId';
793
+ }
794
+
795
+ @override
796
+ String get tool_unknown => 'Tool: Unknown';
797
+
798
+ @override
799
+ String get message_options => 'Message options';
800
+
801
+ @override
802
+ String get copy_markdown => 'Copy as Markdown';
803
+
804
+ @override
805
+ String get copied_markdown => 'Copied as Markdown';
806
+
807
+ @override
808
+ String get read_aloud => 'Read Aloud';
809
+
810
+ @override
811
+ String get stop_reading => 'Stop Reading';
812
+
813
+ @override
814
+ String get more => 'More';
815
+
816
+ @override
817
+ String character_count(int length) {
818
+ return '$length characters';
819
+ }
820
+
821
+ @override
822
+ String get edit_message => 'Modifier message';
823
+
824
+ @override
825
+ String get edit_message_desc =>
826
+ 'Saving will remove the assistant response below and regenerate.';
827
+
828
+ @override
829
+ String get save_regenerate => 'Enregistrer & regenerate';
830
+
831
+ @override
832
+ String get chat_settings_title => 'Chat Paramètres';
833
+
834
+ @override
835
+ String get reset_defaults => 'Reset Defaults';
836
+
837
+ @override
838
+ String get parameters_tab => 'Parameters';
839
+
840
+ @override
841
+ String get mcp_tab => 'MCP';
842
+
843
+ @override
844
+ String get temperature => 'Température';
845
+
846
+ @override
847
+ String get temperature_desc =>
848
+ 'Controls randomness: Higher = Creative, Lower = Focused';
849
+
850
+ @override
851
+ String get top_p => 'Top P';
852
+
853
+ @override
854
+ String get top_p_desc => 'Nucleus sampling threshold';
855
+
856
+ @override
857
+ String get max_tokens => 'Max Tokens';
858
+
859
+ @override
860
+ String get max_tokens_desc => 'Response limit';
861
+
862
+ @override
863
+ String get context_length => 'Context Length';
864
+
865
+ @override
866
+ String get context_length_desc => 'History window';
867
+
868
+ @override
869
+ String get mcp_disabled_warning =>
870
+ 'MCP is disabled globally. Enable it in Paramètres to use these features.';
871
+
872
+ @override
873
+ String get mcp_enable_chat => 'Enable MCP for this chat';
874
+
875
+ @override
876
+ String get auto_execute_tools => 'Auto-execute tools';
877
+
878
+ @override
879
+ String get beta_label => 'Beta';
880
+
881
+ @override
882
+ String get experimental_label => 'Experimental';
883
+
884
+ @override
885
+ String get add_ephemeral_mcp => 'Ajouter Ephemeral MCP Server';
886
+
887
+ @override
888
+ String get mcp_label_placeholder => 'Label';
889
+
890
+ @override
891
+ String get mcp_url_placeholder => 'URL (https://...)';
892
+
893
+ @override
894
+ String get active_integrations => 'Active Integrations';
895
+
896
+ @override
897
+ String get enable_notifications => 'Enable Nontifications';
898
+
899
+ @override
900
+ String get enable_notifications_desc =>
901
+ 'Get notified when models finish downloading.';
902
+
903
+ @override
904
+ String get chat_history_title => 'Chat History';
905
+
906
+ @override
907
+ String get conversation_just_now => 'Just now';
908
+
909
+ @override
910
+ String conversation_minutes_ago(int minutes) {
911
+ return '${minutes}m ago';
912
+ }
913
+
914
+ @override
915
+ String conversation_hours_ago(int hours) {
916
+ return '${hours}h ago';
917
+ }
918
+
919
+ @override
920
+ String get conversation_yesterday => 'Ouiterday';
921
+
922
+ @override
923
+ String conversation_days_ago(int days) {
924
+ return '${days}d ago';
925
+ }
926
+
927
+ @override
928
+ String conversation_date(int month, int day, int year) {
929
+ return '$month/$day/$year';
930
+ }
931
+
932
+ @override
933
+ String get options_tooltip => 'Options';
934
+
935
+ @override
936
+ String get no_results_found => 'Aucun results found';
937
+
938
+ @override
939
+ String get no_conversations_yet => 'Aucun conversations yet';
940
+
941
+ @override
942
+ String get try_different_search => 'Try a different search term';
943
+
944
+ @override
945
+ String get start_new_conversation => 'Start a new conversation';
946
+
947
+ @override
948
+ String get rename_conversation => 'Rename conversation';
949
+
950
+ @override
951
+ String get enter_new_title => 'Enter new title';
952
+
953
+ @override
954
+ String get pinned_section => 'PINNED';
955
+
956
+ @override
957
+ String get today_section => 'TODAY';
958
+
959
+ @override
960
+ String get yesterday_section => 'YESTERDAY';
961
+
962
+ @override
963
+ String get previous_7_days => 'PREVIOUS 7 DAYS';
964
+
965
+ @override
966
+ String get previous_30_days => 'PREVIOUS 30 DAYS';
967
+
968
+ @override
969
+ String get older_section => 'OLDER';
970
+
971
+ @override
972
+ String get onboarding_choose_language => 'Découvrez les langues de LocalMind';
973
+
974
+ @override
975
+ String get onboarding_choose_language_desc =>
976
+ 'Sélectionner your preferred language. You can change this anytime in settings.';
977
+
978
+ @override
979
+ String get onboarding_localmind => 'LocalMind';
980
+
981
+ @override
982
+ String get onboarding_connect_server => 'Connect Your\nServer';
983
+
984
+ @override
985
+ String get onboarding_connect_desc =>
986
+ 'Connect to LM Studio, Ollama,\nOllama Cloud, or OpenRouter to\nstart your private AI experience.';
987
+
988
+ @override
989
+ String get openai_compatible_api => 'OpenAI-compatible API';
990
+
991
+ @override
992
+ String get https_requires_ssl => 'HTTPS requires SSL';
993
+
994
+ @override
995
+ String get most_local_setups_use_http => 'Most local setups use http://';
996
+
997
+ @override
998
+ String get onboarding_welcome => 'Welcome to LocalMind';
999
+
1000
+ @override
1001
+ String get server_type_on_device => 'On-Device';
1002
+
1003
+ @override
1004
+ String get server_type_lm_studio => 'LM Studio';
1005
+
1006
+ @override
1007
+ String get server_type_ollama => 'Ollama';
1008
+
1009
+ @override
1010
+ String get server_type_ollama_cloud => 'Ollama Cloud';
1011
+
1012
+ @override
1013
+ String get server_type_ollama_cloud_sub => 'CLOUD MANAGED';
1014
+
1015
+ @override
1016
+ String get server_type_ollama_cloud_display => 'Ollama Cloud';
1017
+
1018
+ @override
1019
+ String get server_address_ollama_cloud => 'ollama.com';
1020
+
1021
+ @override
1022
+ String get ollama_cloud_disclosure =>
1023
+ 'By connecting Ollama Cloud, your chat messages and inputs are sent to Ollama\'s managed servers for inference. LocalMind does not track or store your conversations. You can revoke this key any time at ollama.com/settings/keys.';
1024
+
1025
+ @override
1026
+ String get api_key_required_ollama_cloud =>
1027
+ 'API Key required for Ollama Cloud';
1028
+
1029
+ @override
1030
+ String get api_key_hint_ollama_cloud => 'Paste your Ollama Cloud API key';
1031
+
1032
+ @override
1033
+ String get add_server_ollama_cloud_subtitle =>
1034
+ 'Connect to Ollama Cloud with an API key from ollama.com/settings/keys to access managed cloud models.';
1035
+
1036
+ @override
1037
+ String get add_server_openrouter_subtitle =>
1038
+ 'Connect through OpenRouter with a valid API key and keep this profile ready for model routing.';
1039
+
1040
+ @override
1041
+ String get add_server_endpoint_subtitle =>
1042
+ 'Configure a local or self-hosted endpoint, then verify the connection before saving.';
1043
+
1044
+ @override
1045
+ String get server_type_openrouter => 'OpenRouter';
1046
+
1047
+ @override
1048
+ String get server_type_openrouter_sub => 'UNIFIED CLOUD';
1049
+
1050
+ @override
1051
+ String get ready_continue => 'READY TO CONTINUE';
1052
+
1053
+ @override
1054
+ String get waiting_selection => 'WAITING FOR SELECTION';
1055
+
1056
+ @override
1057
+ String get setup_connection => 'Setup Connection';
1058
+
1059
+ @override
1060
+ String setup_connection_desc(String server) {
1061
+ return 'Configure your $server server to start chatting.';
1062
+ }
1063
+
1064
+ @override
1065
+ String get server_name => 'Serveur Name';
1066
+
1067
+ @override
1068
+ String get name_required => 'Name required';
1069
+
1070
+ @override
1071
+ String get name_max_50 => 'Max 50 characters';
1072
+
1073
+ @override
1074
+ String get host_label => 'Host / IP Address';
1075
+
1076
+ @override
1077
+ String get host_required => 'Host required';
1078
+
1079
+ @override
1080
+ String get port_label => 'Port';
1081
+
1082
+ @override
1083
+ String get port_required => 'Port required';
1084
+
1085
+ @override
1086
+ String get port_invalid => 'Must be a number';
1087
+
1088
+ @override
1089
+ String get port_range => 'Enter a valid port (1-65535)';
1090
+
1091
+ @override
1092
+ String get api_key_required => 'API Key *';
1093
+
1094
+ @override
1095
+ String get api_key_optional => 'API Key (Optional)';
1096
+
1097
+ @override
1098
+ String get api_key_required_openrouter => 'API Key required for OpenRouter';
1099
+
1100
+ @override
1101
+ String get api_key_format => 'OpenRouter API keys start with sk-';
1102
+
1103
+ @override
1104
+ String get my_server_hint => 'My Server';
1105
+
1106
+ @override
1107
+ String get name_length_validation => 'Name must be 50 characters or less';
1108
+
1109
+ @override
1110
+ String get host_valid => 'Enter a valid hostname or IP address';
1111
+
1112
+ @override
1113
+ String get api_key_hint_openrouter => 'sk-...';
1114
+
1115
+ @override
1116
+ String get api_key_hint_generic => 'For authenticated servers';
1117
+
1118
+ @override
1119
+ String get update_server => 'Update Server';
1120
+
1121
+ @override
1122
+ String get save_server => 'Enregistrer Server';
1123
+
1124
+ @override
1125
+ String get server_updated => 'Serveur updated';
1126
+
1127
+ @override
1128
+ String get server_added => 'Serveur added';
1129
+
1130
+ @override
1131
+ String get download_model_title => 'Télécharger a Model';
1132
+
1133
+ @override
1134
+ String get download_model_desc =>
1135
+ 'Choisir a model to download.\nIt will run locally on your device.';
1136
+
1137
+ @override
1138
+ String get on_device_android_only =>
1139
+ 'On-device inference is currently available on Android only.';
1140
+
1141
+ @override
1142
+ String get total_ram => 'Total RAM';
1143
+
1144
+ @override
1145
+ String get available => 'Available';
1146
+
1147
+ @override
1148
+ String ram_min_required(String fileSize) {
1149
+ return '$fileSize GB RAM min';
1150
+ }
1151
+
1152
+ @override
1153
+ String download_progress(String percent, String speed) {
1154
+ return '$percent% • $speed';
1155
+ }
1156
+
1157
+ @override
1158
+ String eta_label(String eta) {
1159
+ return 'ETA: $eta';
1160
+ }
1161
+
1162
+ @override
1163
+ String paused_progress(String percent) {
1164
+ return 'Paused - $percent%';
1165
+ }
1166
+
1167
+ @override
1168
+ String ram_warning_body_download(String ram, String totalMemory) {
1169
+ 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.';
1170
+ }
1171
+
1172
+ @override
1173
+ String ram_warning_body_load(String availableRAM, String ram) {
1174
+ return 'Your device has $availableRAM available RAM, but this model recommends at least $ram GB. Loading it might fail or cause instability.';
1175
+ }
1176
+
1177
+ @override
1178
+ String get choose_theme => 'Choisir Thème';
1179
+
1180
+ @override
1181
+ String get choose_theme_desc =>
1182
+ 'Personalize the app appearance. You can always change this later in settings.';
1183
+
1184
+ @override
1185
+ String get theme_card_system => 'System';
1186
+
1187
+ @override
1188
+ String get theme_card_system_sub => 'Matches your device settings';
1189
+
1190
+ @override
1191
+ String get theme_card_light => 'Light';
1192
+
1193
+ @override
1194
+ String get theme_card_light_sub => 'Clean and bright';
1195
+
1196
+ @override
1197
+ String get theme_card_dark => 'Dark';
1198
+
1199
+ @override
1200
+ String get theme_card_dark_sub => 'Easy on the eyes';
1201
+
1202
+ @override
1203
+ String get theme_card_claude => 'Claude';
1204
+
1205
+ @override
1206
+ String get theme_card_claude_sub => 'A warm, peach-tinted theme';
1207
+
1208
+ @override
1209
+ String get stay_updated => 'Stay Updated';
1210
+
1211
+ @override
1212
+ String get stay_updated_desc =>
1213
+ 'Get notified when your AI models finish downloading or when long-running tasks complete.';
1214
+
1215
+ @override
1216
+ String get notification_benefit_downloads => 'Modèle download progress';
1217
+
1218
+ @override
1219
+ String get notification_benefit_completions => 'Generation completions';
1220
+
1221
+ @override
1222
+ String get notification_benefit_background => 'Background tasks status';
1223
+
1224
+ @override
1225
+ String get allow_notifications => 'Allow Nontifications';
1226
+
1227
+ @override
1228
+ String get servers_title => 'Servers';
1229
+
1230
+ @override
1231
+ String get no_servers_yet => 'Aucun Servers Yet';
1232
+
1233
+ @override
1234
+ String get no_servers_desc =>
1235
+ 'Ajouter your first server to start chatting with AI models.';
1236
+
1237
+ @override
1238
+ String get add_server => 'Ajouter Server';
1239
+
1240
+ @override
1241
+ String switched_to_server(String name) {
1242
+ return 'Switched to $name';
1243
+ }
1244
+
1245
+ @override
1246
+ String get edit_server => 'Modifier Server';
1247
+
1248
+ @override
1249
+ String get add_server_title => 'Ajouter Server';
1250
+
1251
+ @override
1252
+ String get server_type_label => 'Serveur Type';
1253
+
1254
+ @override
1255
+ String get server_icon_label => 'Serveur Icon';
1256
+
1257
+ @override
1258
+ String get default_icon => 'Default icon';
1259
+
1260
+ @override
1261
+ String get server_type_lm_studio_display => 'LM Studio';
1262
+
1263
+ @override
1264
+ String get server_type_openai_display => 'OpenAI Compatible';
1265
+
1266
+ @override
1267
+ String get server_type_ollama_display => 'Ollama';
1268
+
1269
+ @override
1270
+ String get server_type_openrouter_display => 'OpenRouter';
1271
+
1272
+ @override
1273
+ String get server_type_on_device_display => 'On-Device';
1274
+
1275
+ @override
1276
+ String get server_address_openrouter => 'openrouter.ai';
1277
+
1278
+ @override
1279
+ String get server_address_on_device => 'Local inference';
1280
+
1281
+ @override
1282
+ String server_address_format(String host, String port) {
1283
+ return '$host:$port';
1284
+ }
1285
+
1286
+ @override
1287
+ String get default_badge => 'Default';
1288
+
1289
+ @override
1290
+ String get set_as_default => 'Set as Default';
1291
+
1292
+ @override
1293
+ String get select_icon => 'Sélectionner Icon';
1294
+
1295
+ @override
1296
+ String get select_icon_desc => 'Choisir an icon for your server';
1297
+
1298
+ @override
1299
+ String get search_icons_hint => 'Search icons...';
1300
+
1301
+ @override
1302
+ String get server_icon_stack => 'Serveur Stack';
1303
+
1304
+ @override
1305
+ String get server_icon_stack2 => 'Serveur Stack 02';
1306
+
1307
+ @override
1308
+ String get server_icon_stack3 => 'Serveur Stack 03';
1309
+
1310
+ @override
1311
+ String get server_icon_cloud => 'Cloud';
1312
+
1313
+ @override
1314
+ String get server_icon_cloud_server => 'Cloud Server';
1315
+
1316
+ @override
1317
+ String get server_icon_mcp => 'MCP Server';
1318
+
1319
+ @override
1320
+ String get server_icon_database => 'Database';
1321
+
1322
+ @override
1323
+ String get server_icon_database1 => 'Database 01';
1324
+
1325
+ @override
1326
+ String get server_icon_database2 => 'Database 02';
1327
+
1328
+ @override
1329
+ String get server_icon_cpu => 'CPU';
1330
+
1331
+ @override
1332
+ String get server_icon_chip => 'Chip';
1333
+
1334
+ @override
1335
+ String get server_icon_chip2 => 'Chip 02';
1336
+
1337
+ @override
1338
+ String get server_icon_computer => 'Computer';
1339
+
1340
+ @override
1341
+ String get server_icon_laptop => 'Laptop';
1342
+
1343
+ @override
1344
+ String get server_icon_terminal => 'Computer Terminal';
1345
+
1346
+ @override
1347
+ String get server_icon_code => 'Code';
1348
+
1349
+ @override
1350
+ String get server_icon_ai_brain => 'AI Brain';
1351
+
1352
+ @override
1353
+ String get server_icon_ai_brain2 => 'AI Brain 02';
1354
+
1355
+ @override
1356
+ String get server_icon_ai_cloud => 'AI Cloud';
1357
+
1358
+ @override
1359
+ String get server_icon_ai_network => 'AI Network';
1360
+
1361
+ @override
1362
+ String get server_icon_ai_chat => 'AI Chat';
1363
+
1364
+ @override
1365
+ String get server_icon_cellular => 'Cellular Network';
1366
+
1367
+ @override
1368
+ String get server_icon_plug1 => 'Plug 01';
1369
+
1370
+ @override
1371
+ String get server_icon_plug2 => 'Plug 02';
1372
+
1373
+ @override
1374
+ String get server_icon_bot => 'Bot';
1375
+
1376
+ @override
1377
+ String get server_icon_bot2 => 'Bot 02';
1378
+
1379
+ @override
1380
+ String get server_icon_robotic => 'Robotic';
1381
+
1382
+ @override
1383
+ String get server_icon_rocket => 'Rocket';
1384
+
1385
+ @override
1386
+ String get server_icon_star => 'Star';
1387
+
1388
+ @override
1389
+ String get server_icon_settings1 => 'Paramètres 01';
1390
+
1391
+ @override
1392
+ String get server_icon_settings2 => 'Paramètres 02';
1393
+
1394
+ @override
1395
+ String get server_icon_home1 => 'Home 01';
1396
+
1397
+ @override
1398
+ String get server_icon_home2 => 'Home 02';
1399
+
1400
+ @override
1401
+ String get server_icon_folder1 => 'Folder 01';
1402
+
1403
+ @override
1404
+ String get server_icon_folder2 => 'Folder 02';
1405
+
1406
+ @override
1407
+ String get server_icon_file1 => 'File 01';
1408
+
1409
+ @override
1410
+ String get server_icon_lock => 'Lock';
1411
+
1412
+ @override
1413
+ String get server_icon_key => 'Key 01';
1414
+
1415
+ @override
1416
+ String get server_icon_link => 'Link 01';
1417
+
1418
+ @override
1419
+ String get server_icon_globe => 'Globe';
1420
+
1421
+ @override
1422
+ String get server_icon_api => 'API';
1423
+
1424
+ @override
1425
+ String get server_icon_arrow_right => 'Arrow Right 01';
1426
+
1427
+ @override
1428
+ String get server_icon_check => 'Check Circle';
1429
+
1430
+ @override
1431
+ String get server_icon_alert => 'Alert Circle';
1432
+
1433
+ @override
1434
+ String get server_icon_info => 'Info Circle';
1435
+
1436
+ @override
1437
+ String get server_icon_zap => 'Zap';
1438
+
1439
+ @override
1440
+ String get server_icon_cloud_upload => 'Cloud Upload';
1441
+
1442
+ @override
1443
+ String get server_icon_cloud_download => 'Cloud Download';
1444
+
1445
+ @override
1446
+ String get server_icon_refresh => 'Refresh';
1447
+
1448
+ @override
1449
+ String get server_icon_hard_drive => 'Hard Drive';
1450
+
1451
+ @override
1452
+ String get server_icon_drive => 'Drive';
1453
+
1454
+ @override
1455
+ String get personas_title => 'Personas';
1456
+
1457
+ @override
1458
+ String get persona_category_general => 'General';
1459
+
1460
+ @override
1461
+ String get persona_category_coding => 'Coding';
1462
+
1463
+ @override
1464
+ String get persona_category_education => 'Education';
1465
+
1466
+ @override
1467
+ String get persona_category_creative => 'Creative';
1468
+
1469
+ @override
1470
+ String get persona_builtin_section => 'BUILT-IN';
1471
+
1472
+ @override
1473
+ String get persona_my_section => 'MY PERSONAS';
1474
+
1475
+ @override
1476
+ String get clone_edit => 'Clone & Edit';
1477
+
1478
+ @override
1479
+ String get builtin_badge => 'Built-in';
1480
+
1481
+ @override
1482
+ String get no_personas_found => 'Aucun personas found';
1483
+
1484
+ @override
1485
+ String get no_personas_desc =>
1486
+ 'Créer your first persona to customize AI behavior.';
1487
+
1488
+ @override
1489
+ String get edit_persona => 'Modifier Persona';
1490
+
1491
+ @override
1492
+ String get create_persona => 'Créer Persona';
1493
+
1494
+ @override
1495
+ String get create_persona_button => 'Create';
1496
+
1497
+ @override
1498
+ String get emoji_label => 'Emoji';
1499
+
1500
+ @override
1501
+ String get name_label => 'Name';
1502
+
1503
+ @override
1504
+ String get my_persona_hint => 'My Persona';
1505
+
1506
+ @override
1507
+ String get category_label => 'Category';
1508
+
1509
+ @override
1510
+ String get description_optional => 'Description (optional)';
1511
+
1512
+ @override
1513
+ String get description_hint => 'What this persona does...';
1514
+
1515
+ @override
1516
+ String get system_prompt => 'Prompt système';
1517
+
1518
+ @override
1519
+ String character_count_max(int currentLen) {
1520
+ return '$currentLen/4000';
1521
+ }
1522
+
1523
+ @override
1524
+ String get no_prompt_placeholder => 'Aucun prompt yet...';
1525
+
1526
+ @override
1527
+ String get prompt_hint => 'You are a helpful assistant...';
1528
+
1529
+ @override
1530
+ String get prompt_required => 'System prompt is required';
1531
+
1532
+ @override
1533
+ String get prompt_max_chars => 'Max 4000 characters';
1534
+
1535
+ @override
1536
+ String get advanced_settings => 'Advanced Paramètres';
1537
+
1538
+ @override
1539
+ String get temperature_label => 'Température (0.0-2.0)';
1540
+
1541
+ @override
1542
+ String get top_p_label => 'Top P (0.0-1.0)';
1543
+
1544
+ @override
1545
+ String get temp_hint => '0.7';
1546
+
1547
+ @override
1548
+ String get top_p_hint => '0.9';
1549
+
1550
+ @override
1551
+ String get range_0_2 => '0.0-2.0';
1552
+
1553
+ @override
1554
+ String get range_0_1 => '0.0-1.0';
1555
+
1556
+ @override
1557
+ String get persona_updated => 'Persona updated';
1558
+
1559
+ @override
1560
+ String get persona_created => 'Persona created';
1561
+
1562
+ @override
1563
+ String get tts_models_title => 'Text To Speech Models';
1564
+
1565
+ @override
1566
+ String get always_available => 'Always available';
1567
+
1568
+ @override
1569
+ String get tts_system_desc =>
1570
+ 'Uses your device\'s built-in text-to-speech engine.\nAucun downloads required. Voice selection uses your device\'s system settings.';
1571
+
1572
+ @override
1573
+ String get downloading_status => 'Downloading...';
1574
+
1575
+ @override
1576
+ String tts_kitten_desc(String size) {
1577
+ return 'Lightning-fast neural TTS with 8 expressive voices.\nRequires $size download.';
1578
+ }
1579
+
1580
+ @override
1581
+ String tts_piper_desc(String size) {
1582
+ return 'Fast offline Piper voices with 2 expressive voices.\nRequires $size download per voice.';
1583
+ }
1584
+
1585
+ @override
1586
+ String engine_spec(String sizeMb, String ramMb, int voiceCount) {
1587
+ return '$sizeMb MB · $ramMb MB RAM · $voiceCount voices';
1588
+ }
1589
+
1590
+ @override
1591
+ String get on_device_models_title => 'On-Device Models';
1592
+
1593
+ @override
1594
+ String get settings_huggingface_token => 'Hugging Face Token (Optional)';
1595
+
1596
+ @override
1597
+ String get settings_huggingface_token_desc =>
1598
+ 'Required only for gated models (e.g. Gemma). Get a token at huggingface.co/settings/tokens.';
1599
+
1600
+ @override
1601
+ String get settings_huggingface_token_set => 'Token saved';
1602
+
1603
+ @override
1604
+ String get settings_huggingface_token_cleared => 'Token cleared';
1605
+
1606
+ @override
1607
+ String get model_requires_huggingface_token =>
1608
+ 'Requires a Hugging Face token';
1609
+
1610
+ @override
1611
+ String get model_missing_huggingface_token =>
1612
+ 'This model is gated on Hugging Face. Ajouter a token in Paramètres → On-Device Inference to download it.';
1613
+
1614
+ @override
1615
+ String get set_huggingface_token => 'Set token';
1616
+
1617
+ @override
1618
+ String get clear_huggingface_token => 'Clear';
1619
+
1620
+ @override
1621
+ String get edit_huggingface_token_dialog_title => 'Hugging Face Access Token';
1622
+
1623
+ @override
1624
+ String get huggingface_token_dialog_hint => 'hf_…';
1625
+
1626
+ @override
1627
+ String get server_type_ollama_desc =>
1628
+ 'Local AI engine. Aucun API key required.';
1629
+
1630
+ @override
1631
+ String get server_type_on_device_desc =>
1632
+ 'Runs on your phone. Some models need a Hugging Face token.';
1633
+
1634
+ @override
1635
+ String get server_type_lm_studio_desc =>
1636
+ 'Local API server. Aucun API key required.';
1637
+
1638
+ @override
1639
+ String get available_models => 'Available Models';
1640
+
1641
+ @override
1642
+ String get device_memory => 'Device Memory';
1643
+
1644
+ @override
1645
+ String get ram_usage => 'RAM Usage';
1646
+
1647
+ @override
1648
+ String get memory_healthy => 'Healthy';
1649
+
1650
+ @override
1651
+ String get memory_critical => 'Critical';
1652
+
1653
+ @override
1654
+ String get memory_low => 'Low';
1655
+
1656
+ @override
1657
+ String ram_used(String percent) {
1658
+ return '$percent% used';
1659
+ }
1660
+
1661
+ @override
1662
+ String get available_ram => 'Available RAM';
1663
+
1664
+ @override
1665
+ String get total_capacity => 'Total Capacity';
1666
+
1667
+ @override
1668
+ String get loaded_status => 'Loaded';
1669
+
1670
+ @override
1671
+ String get inference_backend => 'Inference Backend';
1672
+
1673
+ @override
1674
+ String get backend_ios_notice => 'Only CPU backend is available on iOS.';
1675
+
1676
+ @override
1677
+ String get backend_cpu_desc => 'Works on all devices. Most compatible.';
1678
+
1679
+ @override
1680
+ String get backend_gpu_desc =>
1681
+ 'OpenCL acceleration. Faster on supported devices.';
1682
+
1683
+ @override
1684
+ String get backend_npu_desc =>
1685
+ 'Vendor NPU (Qualcomm/MediaTek). Fastest inference.';
1686
+
1687
+ @override
1688
+ String get select_model_title => 'Sélectionner Model';
1689
+
1690
+ @override
1691
+ String get refresh_models => 'Refresh models';
1692
+
1693
+ @override
1694
+ String get search_models_hint => 'Search models...';
1695
+
1696
+ @override
1697
+ String get no_server_connected => 'Aucun server connected';
1698
+
1699
+ @override
1700
+ String get add_server_first =>
1701
+ 'Ajouter a server first to see available models.';
1702
+
1703
+ @override
1704
+ String get failed_load_models => 'Failed to load models';
1705
+
1706
+ @override
1707
+ String get no_models_available => 'Aucun models available';
1708
+
1709
+ @override
1710
+ String no_models_match(String searchQuery) {
1711
+ return 'Aucun models match \"$searchQuery\"';
1712
+ }
1713
+
1714
+ @override
1715
+ String model_load_failed(String error) {
1716
+ return 'Failed to load model: $error';
1717
+ }
1718
+
1719
+ @override
1720
+ String model_unloaded_ollama(String name) {
1721
+ return 'Unload requested for $name. If Ollama is reachable, the model is released immediately.';
1722
+ }
1723
+
1724
+ @override
1725
+ String model_unloaded_success(String name) {
1726
+ return '$name unloaded successfully';
1727
+ }
1728
+
1729
+ @override
1730
+ String model_unload_failed(String error) {
1731
+ return 'Failed to unload model: $error';
1732
+ }
1733
+
1734
+ @override
1735
+ String get unload_from_server => 'Unload from server';
1736
+
1737
+ @override
1738
+ String context_chip(String ctx) {
1739
+ return '$ctx ctx';
1740
+ }
1741
+
1742
+ @override
1743
+ String get unload_all_models => 'Unload all';
1744
+
1745
+ @override
1746
+ String loaded_models_count(int count) {
1747
+ return '$count loaded';
1748
+ }
1749
+
1750
+ @override
1751
+ String get all_models_unloaded => 'All models unloaded';
1752
+
1753
+ @override
1754
+ String get branch_chat => 'Branch chat';
1755
+
1756
+ @override
1757
+ String get branch_chat_desc => 'Start a new conversation from this message';
1758
+
1759
+ @override
1760
+ String get edit_assistant_message_desc =>
1761
+ 'Modifier the assistant response text.';
1762
+
1763
+ @override
1764
+ String switch_to_model(String modelName, Object model) {
1765
+ return 'Switch to $modelName';
1766
+ }
1767
+
1768
+ @override
1769
+ String download_notification_title(String modelName) {
1770
+ return 'Downloading $modelName...';
1771
+ }
1772
+
1773
+ @override
1774
+ String get download_complete_notification => 'Télécharger complete!';
1775
+
1776
+ @override
1777
+ String download_complete_body(String modelName) {
1778
+ return '$modelName has been downloaded successfully.';
1779
+ }
1780
+
1781
+ @override
1782
+ String download_failed_notification(String error) {
1783
+ return 'Télécharger failed: $error';
1784
+ }
1785
+
1786
+ @override
1787
+ String download_failed_body(String modelName) {
1788
+ return 'Failed to download $modelName.';
1789
+ }
1790
+
1791
+ @override
1792
+ String get engine_name_system => 'System TTS';
1793
+
1794
+ @override
1795
+ String get engine_tagline_system => 'Built-in device engine';
1796
+
1797
+ @override
1798
+ String get engine_name_kitten => 'Kitten TTS';
1799
+
1800
+ @override
1801
+ String get engine_tagline_kitten => 'High-speed neural TTS';
1802
+
1803
+ @override
1804
+ String get engine_name_sherpa => 'Sherpa ONNX VITS';
1805
+
1806
+ @override
1807
+ String get engine_tagline_sherpa => 'Offline Piper voices';
1808
+
1809
+ @override
1810
+ String get voice_jasper => 'Jasper';
1811
+
1812
+ @override
1813
+ String get voice_bella => 'Bella';
1814
+
1815
+ @override
1816
+ String get voice_bruno => 'Bruno';
1817
+
1818
+ @override
1819
+ String get voice_luna => 'Luna';
1820
+
1821
+ @override
1822
+ String get voice_hugo => 'Hugo';
1823
+
1824
+ @override
1825
+ String get voice_rosie => 'Rosie';
1826
+
1827
+ @override
1828
+ String get voice_leo => 'Leo';
1829
+
1830
+ @override
1831
+ String get voice_kiki => 'Kiki';
1832
+
1833
+ @override
1834
+ String get voice_lessac => 'Lessac (US)';
1835
+
1836
+ @override
1837
+ String get voice_ryan => 'Ryan (US)';
1838
+
1839
+ @override
1840
+ String get model_qwen_3 => 'Qwen 3 0.6B';
1841
+
1842
+ @override
1843
+ String get model_qwen_3_desc =>
1844
+ 'Smallest general-purpose chat model. Fast responses, low memory usage.';
1845
+
1846
+ @override
1847
+ String get model_license_apache => 'Apache-2.0';
1848
+
1849
+ @override
1850
+ String get model_qwen_25 => 'Qwen 2.5 1.5B Instruct';
1851
+
1852
+ @override
1853
+ String get model_qwen_25_desc =>
1854
+ 'Balanced quality and size. Good for general conversation.';
1855
+
1856
+ @override
1857
+ String get model_deepseek => 'DeepSeek R1 Distill Qwen 1.5B';
1858
+
1859
+ @override
1860
+ String get model_deepseek_desc =>
1861
+ 'Reasoning and chain-of-thought model. Best for logical tasks.';
1862
+
1863
+ @override
1864
+ String get model_license_mit => 'MIT';
1865
+
1866
+ @override
1867
+ String get model_gemma => 'Gemma 4 E2B Instruct';
1868
+
1869
+ @override
1870
+ String get model_gemma_desc =>
1871
+ 'Google flagship model. Highest quality, requires more RAM.';
1872
+
1873
+ @override
1874
+ String export_header(String date) {
1875
+ return '*Exported from LocalMind — $date*';
1876
+ }
1877
+
1878
+ @override
1879
+ String get export_role_user => '## 👤 User';
1880
+
1881
+ @override
1882
+ String get export_role_assistant => '## 🤖 Assistant';
1883
+
1884
+ @override
1885
+ String get export_role_system => '## ⚙️ System';
1886
+
1887
+ @override
1888
+ String get export_role_tool => '## 🔧 Tool';
1889
+
1890
+ @override
1891
+ String get export_text_user => '[USER]';
1892
+
1893
+ @override
1894
+ String get export_text_assistant => '[ASSISTANT]';
1895
+
1896
+ @override
1897
+ String get export_text_system => '[SYSTEM]';
1898
+
1899
+ @override
1900
+ String get export_text_tool => '[TOOL]';
1901
+
1902
+ @override
1903
+ String get export_label_user => 'USER';
1904
+
1905
+ @override
1906
+ String get export_label_assistant => 'ASSISTANT';
1907
+
1908
+ @override
1909
+ String get export_label_system => 'SYSTEM';
1910
+
1911
+ @override
1912
+ String get export_label_tool => 'TOOL';
1913
+
1914
+ @override
1915
+ String get select_model_hint => 'Sélectionner a model to start chatting';
1916
+
1917
+ @override
1918
+ String get test_notification_title => 'Test notification';
1919
+
1920
+ @override
1921
+ String get test_notification_body =>
1922
+ 'This is a test notification for model download progress.';
1923
+
1924
+ @override
1925
+ String get tts_supports_background =>
1926
+ 'Supports background playback as native audio';
1927
+
1928
+ @override
1929
+ String get tts_other_services_background_note =>
1930
+ 'Nonte: The other TTS services support background playback as native audio.';
1931
+
1932
+ @override
1933
+ String get gguf_imported_models_title => 'Imported GGUF models';
1934
+
1935
+ @override
1936
+ String get gguf_imported_models_empty_subtitle =>
1937
+ 'Import a GGUF from your device or add one from Hugging Face. Imported models run locally with llama.cpp.';
1938
+
1939
+ @override
1940
+ String get gguf_imported_models_ready =>
1941
+ 'imported models ready for local inference.';
1942
+
1943
+ @override
1944
+ String get gguf_curated_models_subtitle =>
1945
+ 'Curated on-device models you can download and manage inside LocalMind.';
1946
+
1947
+ @override
1948
+ String get gguf_only_supported =>
1949
+ 'Only GGUF models are supported for this import.';
1950
+
1951
+ @override
1952
+ String get gguf_imported_from_local_file => 'imported from local file.';
1953
+
1954
+ @override
1955
+ String get gguf_import_failed => 'Failed to import GGUF model';
1956
+
1957
+ @override
1958
+ String get gguf_imported_from_huggingface => 'imported from Hugging Face.';
1959
+
1960
+ @override
1961
+ String get gguf_import_canceled => 'GGUF import canceled.';
1962
+
1963
+ @override
1964
+ String get gguf_enter_huggingface_url => 'Enter a Hugging Face GGUF URL.';
1965
+
1966
+ @override
1967
+ String get gguf_only_official_huggingface_urls =>
1968
+ 'Only official Hugging Face GGUF URLs are supported.';
1969
+
1970
+ @override
1971
+ String get gguf_use_https_url =>
1972
+ 'Use an HTTPS Hugging Face URL for GGUF import.';
1973
+
1974
+ @override
1975
+ String get gguf_url_must_point_to_file =>
1976
+ 'The Hugging Face URL must point directly to a .gguf file.';
1977
+
1978
+ @override
1979
+ String get gguf_unable_to_detect_file_name =>
1980
+ 'Unable to determine the GGUF file name.';
1981
+
1982
+ @override
1983
+ String get gguf_download_empty =>
1984
+ 'The downloaded GGUF file was empty or missing.';
1985
+
1986
+ @override
1987
+ String get gguf_selected_file_missing =>
1988
+ 'Selected model file does not exist.';
1989
+
1990
+ @override
1991
+ String get gguf_import_action => 'Import GGUF';
1992
+
1993
+ @override
1994
+ String get gguf_overview_title => 'Bring your own GGUF models';
1995
+
1996
+ @override
1997
+ String get gguf_overview_subtitle =>
1998
+ 'Import a .gguf from local storage or download one straight from Hugging Face. Imported models stay on this device and load with llama.cpp.';
1999
+
2000
+ @override
2001
+ String get gguf_imported_count_label => 'imported';
2002
+
2003
+ @override
2004
+ String get gguf_local_files_label => 'local files';
2005
+
2006
+ @override
2007
+ String get gguf_huggingface_label => 'Hugging Face';
2008
+
2009
+ @override
2010
+ String get gguf_import_local_title => 'Import local GGUF';
2011
+
2012
+ @override
2013
+ String get gguf_import_local_subtitle => 'Copy a .gguf file from this device';
2014
+
2015
+ @override
2016
+ String get gguf_import_huggingface_title => 'Import from Hugging Face';
2017
+
2018
+ @override
2019
+ String get gguf_import_huggingface_subtitle =>
2020
+ 'Paste a GGUF URL or repo path';
2021
+
2022
+ @override
2023
+ String get gguf_no_imported_title => 'Aucun imported GGUF models yet';
2024
+
2025
+ @override
2026
+ String get gguf_no_imported_subtitle =>
2027
+ 'You can bring your own GGUF file from device storage or paste a Hugging Face URL or repo path that points to a .gguf file.';
2028
+
2029
+ @override
2030
+ String get gguf_import_huggingface_dialog_title =>
2031
+ 'Import GGUF from Hugging Face';
2032
+
2033
+ @override
2034
+ String get gguf_import_huggingface_dialog_subtitle =>
2035
+ 'Paste a direct GGUF URL or a Hugging Face repo path like `owner/repo/blob/main/model.gguf`. Blob links are converted automatically.';
2036
+
2037
+ @override
2038
+ String get gguf_url_or_repo_path => 'GGUF URL or repo path';
2039
+
2040
+ @override
2041
+ String get paste => 'Paste';
2042
+
2043
+ @override
2044
+ String get gguf_browse => 'Browse GGUFs';
2045
+
2046
+ @override
2047
+ String get gguf_huggingface_token_ready => 'Hugging Face token ready';
2048
+
2049
+ @override
2050
+ String get gguf_huggingface_token_optional =>
2051
+ 'Token optional but recommended';
2052
+
2053
+ @override
2054
+ String get gguf_huggingface_token_ready_desc =>
2055
+ 'Your saved token will be used automatically for gated or private repositories.';
2056
+
2057
+ @override
2058
+ String get gguf_huggingface_token_optional_desc =>
2059
+ 'Requires a Hugging Face token. Ajouter one in Paramètres if this GGUF is gated or private.';
2060
+
2061
+ @override
2062
+ String get gguf_downloading => 'Downloading GGUF';
2063
+
2064
+ @override
2065
+ String get gguf_preparing => 'Preparing';
2066
+
2067
+ @override
2068
+ String get gguf_preparing_download => 'Preparing download...';
2069
+
2070
+ @override
2071
+ String get gguf_cancel_import => 'Annuler import';
2072
+
2073
+ @override
2074
+ String get clipboard_empty => 'Clipboard is empty.';
2075
+
2076
+ @override
2077
+ String get could_not_open_huggingface => 'Could not open Hugging Face.';
2078
+
2079
+ @override
2080
+ String get gguf_paste_url_error =>
2081
+ 'Paste a Hugging Face GGUF URL or repo path.';
2082
+
2083
+ @override
2084
+ String get gguf_blob_link => 'Blob link';
2085
+
2086
+ @override
2087
+ String get gguf_repository_label => 'Repository';
2088
+
2089
+ @override
2090
+ String get gguf_detected_path_label => 'Detected path';
2091
+
2092
+ @override
2093
+ String get gguf_imported_section_label => 'Imported GGUF';
2094
+
2095
+ @override
2096
+ String get gguf_already_available => 'Already available on this device';
2097
+
2098
+ @override
2099
+ String get gguf_curated_models_short => 'Curated on-device models';
2100
+
2101
+ @override
2102
+ String get execute_tool_title => 'Execute Tool';
2103
+
2104
+ @override
2105
+ String get execute_tool_request_desc =>
2106
+ 'The model is requesting to execute the following tool:';
2107
+
2108
+ @override
2109
+ String get reject => 'Reject';
2110
+
2111
+ @override
2112
+ String get approve => 'Approve';
2113
+
2114
+ @override
2115
+ String get server_type_help =>
2116
+ 'Pick the provider before filling connection details.';
2117
+
2118
+ @override
2119
+ String get server_identity_title => 'Identity';
2120
+
2121
+ @override
2122
+ String get server_identity_desc =>
2123
+ 'Name this server and choose how it appears in the list.';
2124
+
2125
+ @override
2126
+ String get server_connection_title => 'Connection';
2127
+
2128
+ @override
2129
+ String get server_connection_desc =>
2130
+ 'Use the address and port exposed by your server.';
2131
+
2132
+ @override
2133
+ String get server_authentication_title => 'Authentication';
2134
+
2135
+ @override
2136
+ String get server_authentication_required_desc =>
2137
+ 'OpenRouter requires an API key before testing.';
2138
+
2139
+ @override
2140
+ String get server_authentication_optional_desc =>
2141
+ 'Leave the API key empty if this server does not require one.';
2142
+
2143
+ @override
2144
+ String get mcp_tools_title => 'MCP Tools';
2145
+
2146
+ @override
2147
+ String get available_tools => 'Available tools';
2148
+
2149
+ @override
2150
+ String get unable_load_tools => 'Unable to load tools';
2151
+
2152
+ @override
2153
+ String get no_tools_registered => 'Aucun tools registered';
2154
+
2155
+ @override
2156
+ String get no_tools_registered_desc =>
2157
+ 'Enable the example MCP server or add MCP integrations from chat settings.';
2158
+
2159
+ @override
2160
+ String get example_mcp_server_title => 'Example MCP server';
2161
+
2162
+ @override
2163
+ String get example_mcp_server_desc =>
2164
+ 'Registers example.echo and example.word_count through the same MCP tool provider used by external servers.';
2165
+
2166
+ @override
2167
+ String get disable_example_server => 'Disable example server';
2168
+
2169
+ @override
2170
+ String get enable_example_server => 'Enable example server';
2171
+
2172
+ @override
2173
+ String get built_in_label => 'Built-in';
2174
+
2175
+ @override
2176
+ String get highlights_label => 'Highlights';
2177
+
2178
+ @override
2179
+ String get built_with_label => 'Built with';
2180
+
2181
+ @override
2182
+ String get local_label => 'Local';
2183
+
2184
+ @override
2185
+ String get gguf_format_label => 'GGUF';
2186
+
2187
+ @override
2188
+ String get tool_status_requested => 'Requested';
2189
+
2190
+ @override
2191
+ String get tool_status_approved => 'Approved';
2192
+
2193
+ @override
2194
+ String get tool_status_rejected => 'Rejected';
2195
+
2196
+ @override
2197
+ String get tool_status_running => 'Running';
2198
+
2199
+ @override
2200
+ String get tool_status_done => 'Done';
2201
+
2202
+ @override
2203
+ String get tool_status_failed => 'Failed';
2204
+
2205
+ @override
2206
+ String get model_favorite_toggle => 'Toggle favorite';
2207
+
2208
+ @override
2209
+ String get model_note_label => 'Nonte';
2210
+
2211
+ @override
2212
+ String get model_note_hint => 'Ajouter a note about this model…';
2213
+
2214
+ @override
2215
+ String get unload_models_before_load =>
2216
+ 'Unload all models before loading a new one';
2217
+
2218
+ @override
2219
+ String get temp_chat_keyboard_incognito =>
2220
+ 'Incognito keyboard in temporary chat';
2221
+
2222
+ @override
2223
+ String get temp_chat_keyboard_incognito_desc =>
2224
+ 'Disables keyboard learning and suggestions in temporary chats (e.g. SwiftKey incognito).';
2225
+
2226
+ @override
2227
+ String get resume_last_chat => 'Resume last chat on launch';
2228
+
2229
+ @override
2230
+ String get resume_last_chat_desc =>
2231
+ 'Restauration your last open conversation when reopening the app.';
2232
+
2233
+ @override
2234
+ String get export_all_data => 'Export all data';
2235
+
2236
+ @override
2237
+ String get import_all_data => 'Import all data';
2238
+
2239
+ @override
2240
+ String get export_data_success => 'Sauvegarde exported successfully';
2241
+
2242
+ @override
2243
+ String get import_data_success => 'Sauvegarde imported successfully';
2244
+
2245
+ @override
2246
+ String import_data_failed(String error) {
2247
+ return 'Failed to import backup: $error';
2248
+ }
2249
+
2250
+ @override
2251
+ String get import_data_confirm =>
2252
+ 'Import conversations and custom personas from this backup? Existing items with the same IDs will be updated.';
2253
+
2254
+ @override
2255
+ String get import_settings_confirm =>
2256
+ 'Replace current settings with the imported backup?';
2257
+
2258
+ @override
2259
+ String get export_conversations => 'Export conversations';
2260
+
2261
+ @override
2262
+ String get import_conversations => 'Import conversations';
2263
+
2264
+ @override
2265
+ String get export_personas => 'Export personas';
2266
+
2267
+ @override
2268
+ String get import_personas => 'Import personas';
2269
+
2270
+ @override
2271
+ String get export_settings => 'Export settings';
2272
+
2273
+ @override
2274
+ String get import_settings => 'Import settings';
2275
+
2276
+ @override
2277
+ String get export_all_zip => 'Export all (ZIP)';
2278
+
2279
+ @override
2280
+ String get import_all_zip => 'Import all (ZIP)';
2281
+
2282
+ @override
2283
+ String get duplicate_chat => 'Duplicate chat';
2284
+
2285
+ @override
2286
+ String get duplicate_chat_success => 'Chat duplicated';
2287
+
2288
+ @override
2289
+ String get move_to_folder => 'Move to folder';
2290
+
2291
+ @override
2292
+ String get remove_from_folder => 'Retirer from folder';
2293
+
2294
+ @override
2295
+ String get create_folder => 'Créer folder';
2296
+
2297
+ @override
2298
+ String get new_folder => 'New folder';
2299
+
2300
+ @override
2301
+ String get folder_name_hint => 'Folder name';
2302
+
2303
+ @override
2304
+ String get all_chats => 'All';
2305
+
2306
+ @override
2307
+ String get unfiled_chats => 'Unfiled';
2308
+
2309
+ @override
2310
+ String get create => 'Create';
2311
+
2312
+ @override
2313
+ String get server_path_prefix_label => 'API path prefix';
2314
+
2315
+ @override
2316
+ String get server_path_prefix_hint => '/your-secret-token';
2317
+
2318
+ @override
2319
+ String get search_message_contents => 'Search message contents';
2320
+
2321
+ @override
2322
+ String get message_search_results => 'Message matches';
2323
+
2324
+ @override
2325
+ String get saved_messages_title => 'Saved Messages';
2326
+
2327
+ @override
2328
+ String get nav_saved_messages => 'Saved Messages';
2329
+
2330
+ @override
2331
+ String get saved_messages_empty =>
2332
+ 'Aucun saved messages yet. Bookmark a message from its options menu.';
2333
+
2334
+ @override
2335
+ String get save_message => 'Enregistrer message';
2336
+
2337
+ @override
2338
+ String get message_saved => 'Message saved';
2339
+
2340
+ @override
2341
+ String token_count(int count) {
2342
+ return '$count tokens';
2343
+ }
2344
+
2345
+ @override
2346
+ String estimated_token_count(int count) {
2347
+ return '~$count tokens (estimated)';
2348
+ }
2349
+
2350
+ @override
2351
+ String get test_tts_section_title => 'Test voice';
2352
+
2353
+ @override
2354
+ String get test_tts_hint => 'Enter text to hear the current TTS engine…';
2355
+
2356
+ @override
2357
+ String get test_speak_button => 'Speak';
2358
+
2359
+ @override
2360
+ String get scroll_to_bottom => 'Scroll to bottom';
2361
+
2362
+ @override
2363
+ String get generate_ai_response => 'Generate AI response';
2364
+
2365
+ @override
2366
+ String get no_response => 'Aucun response';
2367
+
2368
+ @override
2369
+ String get export => 'Export';
2370
+
2371
+ @override
2372
+ String get import => 'Import';
2373
+
2374
+ @override
2375
+ String get conversations_label => 'Conversations';
2376
+
2377
+ @override
2378
+ String get personas_label => 'Personas';
2379
+
2380
+ @override
2381
+ String get settings_label => 'Paramètres';
2382
+
2383
+ @override
2384
+ String get export_conversation => 'Export conversation';
2385
+
2386
+ @override
2387
+ String get tts_process_markdown => 'Process markdown for speech';
2388
+
2389
+ @override
2390
+ String get tts_process_markdown_desc =>
2391
+ 'Strip formatting like **bold** before reading aloud';
2392
+
2393
+ @override
2394
+ String get tts_skip_seconds => 'Skip interval';
2395
+
2396
+ @override
2397
+ String get tts_skip_seconds_desc =>
2398
+ 'Forward and rewind jump size during playback';
2399
+
2400
+ @override
2401
+ String tts_skip_seconds_value(int seconds) {
2402
+ return '${seconds}s';
2403
+ }
2404
+
2405
+ @override
2406
+ String get preview_system_prompts => 'Preview system prompts';
2407
+
2408
+ @override
2409
+ String get welcome_message_1 => 'What can I help you with today?';
2410
+
2411
+ @override
2412
+ String get welcome_message_2 => 'Ask me anything — I\'m ready when you are.';
2413
+
2414
+ @override
2415
+ String get welcome_message_3 =>
2416
+ 'Your data is processed locally and never leaves your device.';
2417
+
2418
+ @override
2419
+ String get welcome_message_4 => 'Need ideas? Try one of the quick prompts.';
2420
+
2421
+ @override
2422
+ String get temporary_chat => 'Temporary chat';
2423
+
2424
+ @override
2425
+ String get temporary_chat_desc => 'Chats aren\'t saved to history.';
2426
+
2427
+ @override
2428
+ String get temporary_chat_banner => 'Temporary chat — not saved to history';
2429
+
2430
+ @override
2431
+ String get temporary_chat_save_warning_title =>
2432
+ 'Enregistrer message in temporary chat?';
2433
+
2434
+ @override
2435
+ String get temporary_chat_save_warning_body =>
2436
+ 'This chat is temporary and hidden from history. The saved message will still appear in Saved Messages.';
2437
+
2438
+ @override
2439
+ String get save_to_history => 'Enregistrer to history';
2440
+
2441
+ @override
2442
+ String get share_conversation => 'Share conversation';
2443
+
2444
+ @override
2445
+ String get download_tts_audio => 'Télécharger audio';
2446
+
2447
+ @override
2448
+ String get tts_download_unavailable =>
2449
+ 'Télécharger is only available for Piper and Kitten TTS';
2450
+
2451
+ @override
2452
+ String get tts_download_no_audio => 'Aucun audio available to download yet';
2453
+
2454
+ @override
2455
+ String get tts_download_success => 'Audio saved';
2456
+
2457
+ @override
2458
+ String get return_to_chat => 'Return to chat';
2459
+
2460
+ @override
2461
+ String get return_to_temp_chat => 'Return to temporary chat';
2462
+
2463
+ @override
2464
+ String get insert_saved_message => 'Insert saved message';
2465
+
2466
+ @override
2467
+ String get insert_saved_message_desc =>
2468
+ 'Choisir a saved message to add to your input';
2469
+
2470
+ @override
2471
+ String get model_info => 'Modèle info';
2472
+
2473
+ @override
2474
+ String get model_name => 'Modèle name';
2475
+
2476
+ @override
2477
+ String get model_identifier => 'Identifier';
2478
+
2479
+ @override
2480
+ String get not_available => 'Nont available';
2481
+
2482
+ @override
2483
+ String get save_message_folders => 'Enregistrer message';
2484
+
2485
+ @override
2486
+ String get remove_from_saved => 'Retirer from saved';
2487
+
2488
+ @override
2489
+ String get message_already_saved => 'Saved';
2490
+
2491
+ @override
2492
+ String get stream_ttft => 'Time to first token';
2493
+
2494
+ @override
2495
+ String get stream_tokens_per_sec => 'Tokens per second';
2496
+
2497
+ @override
2498
+ String get stream_stop_reason => 'Stop reason';
2499
+
2500
+ @override
2501
+ String get stream_input_tokens => 'Input tokens';
2502
+
2503
+ @override
2504
+ String get stream_output_tokens => 'Output tokens';
2505
+
2506
+ @override
2507
+ String get stream_generation_time => 'Generation time';
2508
+
2509
+ @override
2510
+ String get attach_image => 'Photos';
2511
+
2512
+ @override
2513
+ String get attach_text_document => 'Documents';
2514
+
2515
+ @override
2516
+ String get attach_shortcut_images => 'Photos';
2517
+
2518
+ @override
2519
+ String get attach_shortcut_documents => 'Files';
2520
+
2521
+ @override
2522
+ String get attach_shortcut_saved => 'Saved';
2523
+
2524
+ @override
2525
+ String get add_attachment => 'Ajouter attachment';
2526
+
2527
+ @override
2528
+ String get add_to_chat => 'Ajouter to chat';
2529
+
2530
+ @override
2531
+ String get choose_what_to_attach => 'What would you like to add?';
2532
+
2533
+ @override
2534
+ String get choose_attachment_subtitle =>
2535
+ 'Pick a source to attach to your message';
2536
+
2537
+ @override
2538
+ String get photo_permission_denied =>
2539
+ 'Photo access is required to attach images';
2540
+
2541
+ @override
2542
+ String get select_model_prompt => 'Sélectionner model';
2543
+
2544
+ @override
2545
+ String get characters_label => 'Characters';
2546
+
2547
+ @override
2548
+ String get exit_temporary_chat_title => 'Exit temporary chat?';
2549
+
2550
+ @override
2551
+ String get exit_temporary_chat_body =>
2552
+ 'This will discard the current temporary chat and return to a new chat.';
2553
+
2554
+ @override
2555
+ String get saved_message_temp_snap_unavailable =>
2556
+ 'This message was saved from a temporary chat and can\'t be opened in its original conversation.';
2557
+
2558
+ @override
2559
+ String get filter_title => 'Filter';
2560
+
2561
+ @override
2562
+ String get filter_pinned => 'Pinned';
2563
+
2564
+ @override
2565
+ String get filter_archived => 'Archived';
2566
+
2567
+ @override
2568
+ String get filter_temp_chats => 'Temporary chats';
2569
+
2570
+ @override
2571
+ String get filter_user_messages => 'User messages';
2572
+
2573
+ @override
2574
+ String get filter_assistant_messages => 'Assistant messages';
2575
+
2576
+ @override
2577
+ String get archive_chat => 'Archive';
2578
+
2579
+ @override
2580
+ String get unarchive_chat => 'Unarchive';
2581
+
2582
+ @override
2583
+ String conversation_message_count(int count) {
2584
+ String _temp0 = intl.Intl.pluralLogic(
2585
+ count,
2586
+ locale: localeName,
2587
+ other: '$count messages',
2588
+ one: '1 message',
2589
+ );
2590
+ return '$_temp0';
2591
+ }
2592
+
2593
+ @override
2594
+ String conversation_character_count(int count) {
2595
+ return '$count chars';
2596
+ }
2597
+
2598
+ @override
2599
+ String get generate_title_with_ai => 'Generate with AI';
2600
+
2601
+ @override
2602
+ String get generating_title => 'Generating...';
2603
+
2604
+ @override
2605
+ String get generate_title_failed => 'Could not generate a title';
2606
+
2607
+ @override
2608
+ String get lm_studio_model_browser_title => 'Browse models';
2609
+
2610
+ @override
2611
+ String get lm_studio_model_search_hint => 'Search models by name or author…';
2612
+
2613
+ @override
2614
+ String get lm_studio_staff_picks => 'Staff picks';
2615
+
2616
+ @override
2617
+ String get lm_studio_community_models => 'Community models';
2618
+
2619
+ @override
2620
+ String get lm_studio_no_models => 'Aucun models found';
2621
+
2622
+ @override
2623
+ String lm_studio_models_count(int count) {
2624
+ return '$count models';
2625
+ }
2626
+
2627
+ @override
2628
+ String get lm_studio_browse_models => 'Browse & download';
2629
+
2630
+ @override
2631
+ String get lm_studio_model_search => 'LMS Modèle Search';
2632
+
2633
+ @override
2634
+ String get lm_studio_downloads_title => 'Downloads';
2635
+
2636
+ @override
2637
+ String get lm_studio_choose_quant => 'Choisir a download option';
2638
+
2639
+ @override
2640
+ String get lm_studio_use_default_quant => 'Use default';
2641
+
2642
+ @override
2643
+ String get lm_studio_recommended => 'Recommended';
2644
+
2645
+ @override
2646
+ String get lm_studio_clear_downloads => 'Clear finished';
2647
+
2648
+ @override
2649
+ String get lm_studio_no_downloads => 'Aucun downloads yet';
2650
+
2651
+ @override
2652
+ String get lm_studio_downloads_disclaimer =>
2653
+ 'Downloads run on the LM Studio host. Pausing, stopping, and deleting models must be done on that computer — not from this app.';
2654
+
2655
+ @override
2656
+ String get lm_studio_staff_pick => 'Staff pick';
2657
+
2658
+ @override
2659
+ String get lm_studio_params => 'PARAMS';
2660
+
2661
+ @override
2662
+ String get lm_studio_arch => 'ARCH';
2663
+
2664
+ @override
2665
+ String get lm_studio_domain => 'DOMAIN';
2666
+
2667
+ @override
2668
+ String get lm_studio_format => 'FORMAT';
2669
+
2670
+ @override
2671
+ String get lm_studio_vision => 'Vision';
2672
+
2673
+ @override
2674
+ String get lm_studio_tool_use => 'Tool use';
2675
+
2676
+ @override
2677
+ String get lm_studio_reasoning => 'Reasoning';
2678
+
2679
+ @override
2680
+ String get lm_studio_download_options => 'Télécharger options';
2681
+
2682
+ @override
2683
+ String get lm_studio_download => 'Download';
2684
+
2685
+ @override
2686
+ String lm_studio_download_size(String size) {
2687
+ return 'Télécharger $size';
2688
+ }
2689
+
2690
+ @override
2691
+ String lm_studio_downloading_percent(int percent) {
2692
+ return 'Downloading $percent%';
2693
+ }
2694
+
2695
+ @override
2696
+ String get lm_studio_readme_unavailable =>
2697
+ 'README not available for this model.';
2698
+
2699
+ @override
2700
+ String get lm_studio_full_gpu_offload => 'Full GPU offload possible';
2701
+
2702
+ @override
2703
+ String get lm_studio_partial_gpu_offload => 'Partial GPU offload possible';
2704
+
2705
+ @override
2706
+ String get lm_studio_likely_too_large => 'Likely too large';
2707
+
2708
+ @override
2709
+ String get lm_studio_available_ram_gb => 'Available RAM (GB, optional)';
2710
+
2711
+ @override
2712
+ String get lm_studio_available_vram_gb => 'Available VRAM (GB, optional)';
2713
+
2714
+ @override
2715
+ String get lm_studio_memory_settings_title => 'Memory for recommendations';
2716
+
2717
+ @override
2718
+ String get lm_studio_memory_settings_desc =>
2719
+ 'Used to estimate whether models fit on your machine in the model browser.';
2720
+
2721
+ @override
2722
+ String get think_button_label => 'Think';
2723
+
2724
+ @override
2725
+ String get thinking_mode_title => 'Thinking mode';
2726
+
2727
+ @override
2728
+ String get reasoning_effort_low => 'Low';
2729
+
2730
+ @override
2731
+ String get reasoning_effort_medium => 'Medium';
2732
+
2733
+ @override
2734
+ String get reasoning_effort_high => 'High';
2735
+
2736
+ @override
2737
+ String get could_not_read_file => 'Could not read file';
2738
+
2739
+ @override
2740
+ String get server_offline => 'Serveur Offline';
2741
+
2742
+ @override
2743
+ String get could_not_establish_connection =>
2744
+ 'Could not establish a connection to the server. Please check if your server is running and the host/port settings are correct.';
2745
+
2746
+ @override
2747
+ String get retry_connection => 'Retry Connection';
2748
+
2749
+ @override
2750
+ String get tokens_label => 'Tokens';
2751
+
2752
+ @override
2753
+ String get enter_context_length => 'Enter context length...';
2754
+
2755
+ @override
2756
+ String get openrouter_disclosure =>
2757
+ 'By connecting this provider, your chat messages and inputs will be sent to their servers. LocalMind does not track or store your conversations.';
2758
+
2759
+ @override
2760
+ String get welcome_message_cloud =>
2761
+ 'Your messages are sent to your connected provider.';
2762
+
2763
+ @override
2764
+ String get privacy_policy => 'Politique de confidentialité';
2765
+
2766
+ @override
2767
+ String get cloud_sync => 'S3 Synchronisation Cloud';
2768
+
2769
+ @override
2770
+ String get cloud_sync_description =>
2771
+ 'End-to-end encrypted sync to your own S3-compatible server';
2772
+
2773
+ @override
2774
+ String get cloud_sync_endpoint => 'Endpoint URL';
2775
+
2776
+ @override
2777
+ String get cloud_sync_bucket => 'Bucket';
2778
+
2779
+ @override
2780
+ String get cloud_sync_region => 'Region';
2781
+
2782
+ @override
2783
+ String get cloud_sync_prefix => 'Prefix';
2784
+
2785
+ @override
2786
+ String get cloud_sync_access_key => 'Access key ID';
2787
+
2788
+ @override
2789
+ String get cloud_sync_secret_key => 'Secret access key';
2790
+
2791
+ @override
2792
+ String get cloud_sync_session_token => 'Session token (optional)';
2793
+
2794
+ @override
2795
+ String get cloud_sync_passphrase => 'Encryption passphrase';
2796
+
2797
+ @override
2798
+ String get cloud_sync_confirm_passphrase => 'Confirm passphrase';
2799
+
2800
+ @override
2801
+ String get cloud_sync_path_style => 'Use path-style addressing';
2802
+
2803
+ @override
2804
+ String get cloud_sync_allow_http => 'Allow insecure HTTP';
2805
+
2806
+ @override
2807
+ String get cloud_sync_http_warning =>
2808
+ 'HTTP exposes request metadata and credentials to the network. Use it only for a trusted local S3 server.';
2809
+
2810
+ @override
2811
+ String get cloud_sync_test => 'Test connection';
2812
+
2813
+ @override
2814
+ String get cloud_sync_enable => 'Enable encrypted sync';
2815
+
2816
+ @override
2817
+ String get cloud_sync_now => 'Sync now';
2818
+
2819
+ @override
2820
+ String get cloud_sync_disconnect => 'Disconnect this device';
2821
+
2822
+ @override
2823
+ String get cloud_sync_last_synced => 'Last synced';
2824
+
2825
+ @override
2826
+ String get cloud_sync_never => 'Never';
2827
+
2828
+ @override
2829
+ String get cloud_sync_conflicts => 'Conflicts preserved';
2830
+
2831
+ @override
2832
+ String get cloud_sync_passphrase_mismatch => 'Passphrases do not match';
2833
+
2834
+ @override
2835
+ String get crash_report_title => 'Something went wrong';
2836
+
2837
+ @override
2838
+ String get crash_report_stack_trace => 'Stack trace';
2839
+
2840
+ @override
2841
+ String get crash_report_tap_to_expand => 'Tap to expand';
2842
+
2843
+ @override
2844
+ String get crash_report_button => 'Report this crash';
2845
+
2846
+ @override
2847
+ String get crash_try_again => 'Try again';
2848
+
2849
+ @override
2850
+ String get crash_report_empty_stack => '<empty>';
2851
+
2852
+ @override
2853
+ String get crash_report_disclaimer =>
2854
+ 'Reporting opens GitHub with diagnostics prefilled. You stay in control — nothing is submitted automatically. Please review and remove any sensitive content before submitting.';
2855
+
2856
+ @override
2857
+ String get crash_report_copied => 'Copied to clipboard';
2858
+
2859
+ @override
2860
+ String get report_a_problem => 'Report a problem';
2861
+
2862
+ @override
2863
+ String get rename_folder => 'Rename folder';
2864
+
2865
+ @override
2866
+ String get delete_folder => 'Supprimer folder';
2867
+
2868
+ @override
2869
+ String get delete_folder_title => 'Supprimer folder?';
2870
+
2871
+ @override
2872
+ String delete_folder_body(String name) {
2873
+ return 'Are you sure you want to delete \"$name\"? Conversations or saved messages inside will be moved back to \"Unfiled\". This cannot be undone.';
2874
+ }
2875
+
2876
+ @override
2877
+ String get folder_name_required => 'Please enter a folder name';
2878
+
2879
+ @override
2880
+ String get model_required_toast => 'You need to select a model first';
2881
+}
test/core/utils/locale_utils_test.dart
+11
@@ -28,6 +28,17 @@ void main() {
28
);
29
});
30
31
+ test('matches French locale exactly', () {
32
+ expect(
33
+ findSupportedLocale('fr', AppLocalizations.supportedLocales),
34
+ const Locale('fr'),
35
+ );
36
+ expect(
37
+ findSupportedLocale('fr_FR', AppLocalizations.supportedLocales),
38
+ const Locale('fr'),
39
+ );
40
+ });
41
+
42
test('matches Traditional Chinese exactly', () {
43
expect(
44
findSupportedLocale('zh_TW', AppLocalizations.supportedLocales),
test/features/onboarding/onboarding_language_screen_test.dart
+13
@@ -15,4 +15,17 @@ void main() {
15
expect(l10n.settings_language, 'Dil');
16
expect(l10n.onboarding_choose_language, 'LocalMind dillerini keşfedin');
17
});
18
+
19
+ test('French locale is supported and loads translations', () {
20
+ expect(
21
+ AppLocalizations.supportedLocales.contains(const Locale('fr')),
22
+ isTrue,
23
+ );
24
+
25
+ final l10n = lookupAppLocalizations(const Locale('fr'));
26
+ expect(l10n.app_name, 'LocalMind');
27
+ expect(l10n.cancel, 'Annuler');
28
+ expect(l10n.settings_language, 'Langue');
29
+ expect(l10n.onboarding_choose_language, 'Découvrez les langues de LocalMind');
30
+ });
31
}