diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a145a50 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,56 @@ +# Dependencies +node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Build output +.next +out +dist +build + +# Testing +coverage +.nyc_output +*.test.ts +*.test.tsx +*.spec.ts +*.spec.tsx +__tests__ +jest.config.ts +jest.setup.ts + +# Development files +.git +.gitignore +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE +.vscode +.idea +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Documentation +README.md +screenshots +*.md + +# Docker +Dockerfile +.dockerignore +docker-compose.yml + +# Misc +.eslintrc.json +.prettierrc +.editorconfig diff --git a/src/lib/i18n/translations.ts b/src/lib/i18n/translations.ts new file mode 100644 index 0000000..8b94201 --- /dev/null +++ b/src/lib/i18n/translations.ts @@ -0,0 +1,433 @@ +export type SupportedLanguage = 'en' | 'de' | 'fr' | 'it'; + +export interface Translations { + // Common + common: { + close: string; + cancel: string; + confirm: string; + loading: string; + error: string; + }; + + // Header + header: { + tagline: string; + buyMeABeer: string; + github: string; + }; + + // Start Screen + start: { + title: string; + settings: string; + language: string; + apiKey: string; + apiKeyPlaceholder: string; + getApiKey: string; + startGame: string; + resumeGame: string; + startNewGame: string; + chooseCoach: string; + importPosition: string; + importPositionPlaceholder: string; + apiKeyRequired: string; + colorSelection: string; + playAsWhite: string; + playAsBlack: string; + randomColor: string; + }; + + // Game + game: { + playingAs: string; + vs: string; + backToMenu: string; + stockfishStrength: string; + depth: string; + undoMove: string; + gameHistory: string; + analyze: string; + noMovesYet: string; + white: string; + black: string; + }; + + // Tutor + tutor: { + aiCoach: string; + askCoach: string; + hint: string; + bestMove: string; + }; + + // Game Over + gameOver: { + title: string; + checkmate: string; + youWon: string; + youLost: string; + draw: string; + stalemate: string; + mistakes: string; + aiSummary: string; + move: string; + evalBefore: string; + evalAfter: string; + bestMove: string; + newGame: string; + }; + + // Analysis + analysis: { + title: string; + currentPosition: string; + evaluation: string; + bestMove: string; + aiAnalysis: string; + }; + + // API Key Input + apiKeyInput: { + title: string; + description: string; + placeholder: string; + submit: string; + getKey: string; + }; +} + +const en: Translations = { + common: { + close: 'Close', + cancel: 'Cancel', + confirm: 'Confirm', + loading: 'Loading...', + error: 'Error', + }, + header: { + tagline: 'with Gemini & Stockfish', + buyMeABeer: 'Buy Me a Beer', + github: 'GitHub', + }, + start: { + title: 'Chess Tutor AI', + settings: '1. Settings', + language: 'Language', + apiKey: 'Google Gemini API Key', + apiKeyPlaceholder: 'AIzaSy...', + getApiKey: 'Get free key', + startGame: '2. Start Game', + resumeGame: 'Resume Previous Game', + startNewGame: 'Start New Game instead...', + chooseCoach: 'Choose Your Coach:', + importPosition: 'Import Position (Optional FEN)', + importPositionPlaceholder: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', + apiKeyRequired: 'Please enter a valid API Key to continue.', + colorSelection: 'Choose Your Color:', + playAsWhite: 'Play as White', + playAsBlack: 'Play as Black', + randomColor: 'Random', + }, + game: { + playingAs: 'Playing as', + vs: 'vs', + backToMenu: '← Back to Menu', + stockfishStrength: 'Stockfish Strength', + depth: 'Depth', + undoMove: 'Undo Last Move', + gameHistory: 'Game History', + analyze: 'Analyze', + noMovesYet: 'No moves yet.', + white: 'White', + black: 'Black', + }, + tutor: { + aiCoach: 'AI Coach', + askCoach: 'Ask your coach...', + hint: 'Hint', + bestMove: 'Best Move', + }, + gameOver: { + title: 'Game Over', + checkmate: 'Checkmate!', + youWon: 'You won!', + youLost: 'You lost.', + draw: 'Draw!', + stalemate: 'Stalemate!', + mistakes: 'Your Mistakes', + aiSummary: 'AI Summary', + move: 'Move', + evalBefore: 'Eval Before', + evalAfter: 'Eval After', + bestMove: 'Best Move', + newGame: 'New Game', + }, + analysis: { + title: 'Position Analysis', + currentPosition: 'Current Position', + evaluation: 'Evaluation', + bestMove: 'Best Move', + aiAnalysis: 'AI Analysis', + }, + apiKeyInput: { + title: 'API Key Required', + description: 'Please enter your Google Gemini API key to continue.', + placeholder: 'Enter your API key...', + submit: 'Submit', + getKey: 'Get a free API key', + }, +}; + +const de: Translations = { + common: { + close: 'Schließen', + cancel: 'Abbrechen', + confirm: 'Bestätigen', + loading: 'Lädt...', + error: 'Fehler', + }, + header: { + tagline: 'mit Gemini & Stockfish', + buyMeABeer: 'Spendier mir ein Bier', + github: 'GitHub', + }, + start: { + title: 'Chess Tutor AI', + settings: '1. Einstellungen', + language: 'Sprache', + apiKey: 'Google Gemini API-Schlüssel', + apiKeyPlaceholder: 'AIzaSy...', + getApiKey: 'Kostenlosen Schlüssel erhalten', + startGame: '2. Spiel starten', + resumeGame: 'Vorheriges Spiel fortsetzen', + startNewGame: 'Stattdessen neues Spiel starten...', + chooseCoach: 'Wähle deinen Trainer:', + importPosition: 'Position importieren (Optional FEN)', + importPositionPlaceholder: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', + apiKeyRequired: 'Bitte geben Sie einen gültigen API-Schlüssel ein, um fortzufahren.', + colorSelection: 'Wähle deine Farbe:', + playAsWhite: 'Als Weiß spielen', + playAsBlack: 'Als Schwarz spielen', + randomColor: 'Zufällig', + }, + game: { + playingAs: 'Spielst als', + vs: 'gegen', + backToMenu: '← Zurück zum Menü', + stockfishStrength: 'Stockfish-Stärke', + depth: 'Tiefe', + undoMove: 'Letzten Zug rückgängig', + gameHistory: 'Spielverlauf', + analyze: 'Analysieren', + noMovesYet: 'Noch keine Züge.', + white: 'Weiß', + black: 'Schwarz', + }, + tutor: { + aiCoach: 'KI-Trainer', + askCoach: 'Frage deinen Trainer...', + hint: 'Hinweis', + bestMove: 'Bester Zug', + }, + gameOver: { + title: 'Spiel beendet', + checkmate: 'Schachmatt!', + youWon: 'Du hast gewonnen!', + youLost: 'Du hast verloren.', + draw: 'Remis!', + stalemate: 'Patt!', + mistakes: 'Deine Fehler', + aiSummary: 'KI-Zusammenfassung', + move: 'Zug', + evalBefore: 'Bewertung vorher', + evalAfter: 'Bewertung nachher', + bestMove: 'Bester Zug', + newGame: 'Neues Spiel', + }, + analysis: { + title: 'Stellungsanalyse', + currentPosition: 'Aktuelle Stellung', + evaluation: 'Bewertung', + bestMove: 'Bester Zug', + aiAnalysis: 'KI-Analyse', + }, + apiKeyInput: { + title: 'API-Schlüssel erforderlich', + description: 'Bitte geben Sie Ihren Google Gemini API-Schlüssel ein, um fortzufahren.', + placeholder: 'Geben Sie Ihren API-Schlüssel ein...', + submit: 'Absenden', + getKey: 'Kostenlosen API-Schlüssel erhalten', + }, +}; + +const fr: Translations = { + common: { + close: 'Fermer', + cancel: 'Annuler', + confirm: 'Confirmer', + loading: 'Chargement...', + error: 'Erreur', + }, + header: { + tagline: 'avec Gemini & Stockfish', + buyMeABeer: 'Offrez-moi une bière', + github: 'GitHub', + }, + start: { + title: 'Chess Tutor AI', + settings: '1. Paramètres', + language: 'Langue', + apiKey: 'Clé API Google Gemini', + apiKeyPlaceholder: 'AIzaSy...', + getApiKey: 'Obtenir une clé gratuite', + startGame: '2. Démarrer la partie', + resumeGame: 'Reprendre la partie précédente', + startNewGame: 'Démarrer une nouvelle partie...', + chooseCoach: 'Choisissez votre coach :', + importPosition: 'Importer une position (FEN optionnel)', + importPositionPlaceholder: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', + apiKeyRequired: 'Veuillez entrer une clé API valide pour continuer.', + colorSelection: 'Choisissez votre couleur :', + playAsWhite: 'Jouer Blancs', + playAsBlack: 'Jouer Noirs', + randomColor: 'Aléatoire', + }, + game: { + playingAs: 'Jouant', + vs: 'contre', + backToMenu: '← Retour au menu', + stockfishStrength: 'Force de Stockfish', + depth: 'Profondeur', + undoMove: 'Annuler le dernier coup', + gameHistory: 'Historique de la partie', + analyze: 'Analyser', + noMovesYet: 'Aucun coup pour le moment.', + white: 'Blancs', + black: 'Noirs', + }, + tutor: { + aiCoach: 'Coach IA', + askCoach: 'Demandez à votre coach...', + hint: 'Indice', + bestMove: 'Meilleur coup', + }, + gameOver: { + title: 'Partie terminée', + checkmate: 'Échec et mat !', + youWon: 'Vous avez gagné !', + youLost: 'Vous avez perdu.', + draw: 'Nulle !', + stalemate: 'Pat !', + mistakes: 'Vos erreurs', + aiSummary: 'Résumé IA', + move: 'Coup', + evalBefore: 'Éval. avant', + evalAfter: 'Éval. après', + bestMove: 'Meilleur coup', + newGame: 'Nouvelle partie', + }, + analysis: { + title: 'Analyse de position', + currentPosition: 'Position actuelle', + evaluation: 'Évaluation', + bestMove: 'Meilleur coup', + aiAnalysis: 'Analyse IA', + }, + apiKeyInput: { + title: 'Clé API requise', + description: 'Veuillez entrer votre clé API Google Gemini pour continuer.', + placeholder: 'Entrez votre clé API...', + submit: 'Soumettre', + getKey: 'Obtenir une clé API gratuite', + }, +}; + +const it: Translations = { + common: { + close: 'Chiudi', + cancel: 'Annulla', + confirm: 'Conferma', + loading: 'Caricamento...', + error: 'Errore', + }, + header: { + tagline: 'con Gemini & Stockfish', + buyMeABeer: 'Offrimi una birra', + github: 'GitHub', + }, + start: { + title: 'Chess Tutor AI', + settings: '1. Impostazioni', + language: 'Lingua', + apiKey: 'Chiave API Google Gemini', + apiKeyPlaceholder: 'AIzaSy...', + getApiKey: 'Ottieni chiave gratuita', + startGame: '2. Inizia partita', + resumeGame: 'Riprendi partita precedente', + startNewGame: 'Inizia nuova partita...', + chooseCoach: 'Scegli il tuo allenatore:', + importPosition: 'Importa posizione (FEN opzionale)', + importPositionPlaceholder: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', + apiKeyRequired: 'Inserisci una chiave API valida per continuare.', + colorSelection: 'Scegli il tuo colore:', + playAsWhite: 'Gioca Bianco', + playAsBlack: 'Gioca Nero', + randomColor: 'Casuale', + }, + game: { + playingAs: 'Giocando', + vs: 'contro', + backToMenu: '← Torna al menu', + stockfishStrength: 'Forza di Stockfish', + depth: 'Profondità', + undoMove: 'Annulla ultima mossa', + gameHistory: 'Cronologia partita', + analyze: 'Analizza', + noMovesYet: 'Nessuna mossa ancora.', + white: 'Bianco', + black: 'Nero', + }, + tutor: { + aiCoach: 'Allenatore IA', + askCoach: 'Chiedi al tuo allenatore...', + hint: 'Suggerimento', + bestMove: 'Mossa migliore', + }, + gameOver: { + title: 'Partita terminata', + checkmate: 'Scacco matto!', + youWon: 'Hai vinto!', + youLost: 'Hai perso.', + draw: 'Patta!', + stalemate: 'Stallo!', + mistakes: 'I tuoi errori', + aiSummary: 'Riepilogo IA', + move: 'Mossa', + evalBefore: 'Val. prima', + evalAfter: 'Val. dopo', + bestMove: 'Mossa migliore', + newGame: 'Nuova partita', + }, + analysis: { + title: 'Analisi posizione', + currentPosition: 'Posizione attuale', + evaluation: 'Valutazione', + bestMove: 'Mossa migliore', + aiAnalysis: 'Analisi IA', + }, + apiKeyInput: { + title: 'Chiave API richiesta', + description: 'Inserisci la tua chiave API Google Gemini per continuare.', + placeholder: 'Inserisci la tua chiave API...', + submit: 'Invia', + getKey: 'Ottieni chiave API gratuita', + }, +}; + +export const translations: Record = { + en, + de, + fr, + it, +}; diff --git a/src/lib/i18n/useTranslation.ts b/src/lib/i18n/useTranslation.ts new file mode 100644 index 0000000..f52f784 --- /dev/null +++ b/src/lib/i18n/useTranslation.ts @@ -0,0 +1,17 @@ +import { SupportedLanguage, translations, Translations } from './translations'; + +export function useTranslation(language: SupportedLanguage): Translations { + return translations[language] || translations.en; +} + +export function getTranslation(language: SupportedLanguage, key: string): string { + const t = useTranslation(language); + const keys = key.split('.'); + let value: any = t; + + for (const k of keys) { + value = value?.[k]; + } + + return value || key; +}