feat(i18n): add FR/EN internationalization with in-app language selector

- gen-l10n setup (flutter_localizations, intl, l10n.yaml, ARB en/fr)
- localeProvider (persisted) wired into MaterialApp
- language selector in the System page (alongside the color palette)
- all user-facing strings across screens moved to AppLocalizations

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 12:13:51 +02:00
co-authored by Claude Opus 4.8
parent 409ab9c1dd
commit a2a7ffd79f
26 changed files with 1012 additions and 70 deletions
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import '../../../l10n/app_localizations.dart';
/// Boutons d'action de l'écran game over : rejouer ou retourner au Pokédex.
class GameOverActions extends StatelessWidget {
@@ -9,11 +10,12 @@ class GameOverActions extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l = AppLocalizations.of(context)!;
return Column(
children: [
_button(label: "TRY AGAIN", icon: Icons.refresh, color: const Color(0xFF2962FF), onPressed: onTryAgain),
_button(label: l.tryAgain, icon: Icons.refresh, color: const Color(0xFF2962FF), onPressed: onTryAgain),
const SizedBox(height: 16),
_button(label: "BACK TO POKEDEX", icon: Icons.menu_book, color: const Color(0xFFA66A00), onPressed: onBack),
_button(label: l.backToPokedex, icon: Icons.menu_book, color: const Color(0xFFA66A00), onPressed: onBack),
],
);
}
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import '../../../l10n/app_localizations.dart';
import '../pokemon_image.dart';
/// Bloc supérieur de l'écran game over : bannière "GAME OVER", image et nom du Pokémon.
@@ -13,6 +14,7 @@ class GameOverHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l = AppLocalizations.of(context)!;
return Container(
decoration: BoxDecoration(color: _darkRed, border: Border.all(color: _darkRed, width: 4)),
child: Container(
@@ -24,9 +26,9 @@ class GameOverHeader extends StatelessWidget {
Container(
color: _darkRed,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
child: const Text(
"GAME OVER",
style: TextStyle(
child: Text(
l.gameOver,
style: const TextStyle(
fontSize: 26,
color: Colors.yellow,
fontWeight: FontWeight.bold,
@@ -40,7 +42,7 @@ class GameOverHeader extends StatelessWidget {
SizedBox(height: 140, child: PokemonImage(imageUrl: pokemonImage, fit: BoxFit.contain)),
const SizedBox(height: 12),
Text(
"It was $pokemonName!",
l.itWas(pokemonName),
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 22,
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import '../../../l10n/app_localizations.dart';
/// Rangée de statistiques de fin de partie (STREAK / SEEN / SCORE).
class GameOverStats extends StatelessWidget {
@@ -10,13 +11,14 @@ class GameOverStats extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l = AppLocalizations.of(context)!;
return Row(
children: [
Expanded(child: _StatBox(label: "STREAK", value: streak)),
Expanded(child: _StatBox(label: l.statStreak, value: streak)),
const SizedBox(width: 16),
Expanded(child: _StatBox(label: "SEEN", value: seen)),
Expanded(child: _StatBox(label: l.statSeen, value: seen)),
const SizedBox(width: 16),
Expanded(child: _StatBox(label: "SCORE", value: score)),
Expanded(child: _StatBox(label: l.statScore, value: score)),
],
);
}