translations
This commit is contained in:
@@ -78,7 +78,7 @@ export default function OpeningTrainingPage() {
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="w-12 h-12 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mx-auto"></div>
|
||||
<p className="text-gray-600 dark:text-gray-400">Loading opening training session...</p>
|
||||
<p className="text-gray-600 dark:text-gray-400">{t.learning.openingTrainer.loadingSession}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,16 +95,16 @@ export default function OpeningTrainingPage() {
|
||||
<div className="max-w-6xl mx-auto w-full">
|
||||
<div className="text-center py-12">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Opening Not Found
|
||||
{t.learning.openingTrainer.openingNotFound}
|
||||
</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
The requested opening could not be found.
|
||||
{t.learning.openingTrainer.openingNotFoundDescription}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => router.push('/learning/openings')}
|
||||
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Back to Opening Selection
|
||||
{t.learning.openingTrainer.backToOpeningSelection}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -125,7 +125,7 @@ export default function OpeningTrainingPage() {
|
||||
className="p-2 md:px-4 md:py-2 bg-gray-200 dark:bg-gray-700 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 text-sm font-medium transition-colors flex items-center gap-2"
|
||||
>
|
||||
<ArrowLeft size={20} />
|
||||
<span className="hidden md:inline">Back to Opening Selection</span>
|
||||
<span className="hidden md:inline">{t.learning.openingTrainer.backToOpeningSelection}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useTranslation } from '@/lib/i18n/useTranslation';
|
||||
import { SupportedLanguage } from '@/lib/i18n/translations';
|
||||
|
||||
@@ -14,7 +15,7 @@ export default function Header({ language }: HeaderProps) {
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center justify-between h-16">
|
||||
{/* App Title/Branding */}
|
||||
<div className="flex items-center space-x-3">
|
||||
<Link href="/" className="flex items-center space-x-3 hover:opacity-80 transition-opacity cursor-pointer">
|
||||
<div className="text-2xl">♞</div>
|
||||
<h1 className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
Chess Tutor
|
||||
@@ -22,7 +23,7 @@ export default function Header({ language }: HeaderProps) {
|
||||
<span className="hidden sm:inline-block text-sm text-gray-500 dark:text-gray-400">
|
||||
{t.header.tagline}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex items-center space-x-3">
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { useTranslation } from '@/lib/i18n/useTranslation';
|
||||
import { SupportedLanguage } from '@/lib/i18n/translations';
|
||||
|
||||
interface DeviationDialogProps {
|
||||
openingName: string;
|
||||
movesCompleted: number;
|
||||
onUndo: () => void;
|
||||
onStartGame: () => void;
|
||||
onContinueExploring?: () => void;
|
||||
language: SupportedLanguage;
|
||||
}
|
||||
|
||||
export default function DeviationDialog({
|
||||
@@ -14,18 +18,20 @@ export default function DeviationDialog({
|
||||
onUndo,
|
||||
onStartGame,
|
||||
onContinueExploring,
|
||||
language,
|
||||
}: DeviationDialogProps) {
|
||||
const t = useTranslation(language);
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl p-8 max-w-md mx-4">
|
||||
<div className="text-center mb-6">
|
||||
<div className="text-4xl mb-4">🤔</div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
You've Left the Opening!
|
||||
{t.learning.openingTrainer.deviationTitle}
|
||||
</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
You've studied <span className="font-semibold">{movesCompleted}</span> move
|
||||
{movesCompleted !== 1 ? 's' : ''} of the{' '}
|
||||
{t.learning.openingTrainer.deviationMessage} <span className="font-semibold">{movesCompleted}</span>{' '}
|
||||
{movesCompleted !== 1 ? t.learning.openingTrainer.moves : t.learning.openingTrainer.move} of the{' '}
|
||||
<span className="font-semibold">{openingName}</span>.
|
||||
</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-2">
|
||||
@@ -40,7 +46,7 @@ export default function DeviationDialog({
|
||||
className="w-full px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<span>♟️</span>
|
||||
<span>Continue Playing (Start Game)</span>
|
||||
<span>{t.learning.openingTrainer.continuePlayingButton}</span>
|
||||
</button>
|
||||
|
||||
{/* Secondary action: Undo */}
|
||||
@@ -49,7 +55,7 @@ export default function DeviationDialog({
|
||||
className="w-full px-6 py-3 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 font-medium transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<span>↩️</span>
|
||||
<span>Undo & Return to Opening</span>
|
||||
<span>{t.learning.openingTrainer.undoReturnButton}</span>
|
||||
</button>
|
||||
|
||||
{/* Optional: Continue exploring */}
|
||||
@@ -59,13 +65,13 @@ export default function DeviationDialog({
|
||||
className="w-full px-6 py-3 bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 font-medium transition-colors border border-gray-300 dark:border-gray-600 flex items-center justify-center gap-2"
|
||||
>
|
||||
<span>🔍</span>
|
||||
<span>Explore This Variation</span>
|
||||
<span>{t.learning.openingTrainer.exploreVariationButton}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-gray-500 dark:text-gray-500 mt-4 text-center">
|
||||
Tip: You can always navigate back using the move history
|
||||
{t.learning.openingTrainer.deviationTip}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -559,6 +559,7 @@ export default function OpeningTrainer({ opening, personality, apiKey, language
|
||||
<WikipediaSummary
|
||||
openingName={opening.name}
|
||||
wikipediaSlug={opening.wikipediaSlug}
|
||||
language={language}
|
||||
/>
|
||||
|
||||
{/* Session info */}
|
||||
@@ -589,6 +590,7 @@ export default function OpeningTrainer({ opening, personality, apiKey, language
|
||||
onUndo={handleUndoDeviation}
|
||||
onStartGame={handleStartGameFromPosition}
|
||||
onContinueExploring={handleContinueExploring}
|
||||
language={language}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -3,16 +3,20 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { WikipediaSummary as WikipediaSummaryType } from '@/types/openingTraining';
|
||||
import { getWikipediaSummary } from '@/lib/openingTrainer/wikipediaService';
|
||||
import { useTranslation } from '@/lib/i18n/useTranslation';
|
||||
import { SupportedLanguage } from '@/lib/i18n/translations';
|
||||
|
||||
interface WikipediaSummaryProps {
|
||||
openingName: string;
|
||||
wikipediaSlug?: string; // Preferred: direct slug from database
|
||||
language: SupportedLanguage;
|
||||
}
|
||||
|
||||
export default function WikipediaSummary({ openingName, wikipediaSlug }: WikipediaSummaryProps) {
|
||||
export default function WikipediaSummary({ openingName, wikipediaSlug, language }: WikipediaSummaryProps) {
|
||||
const [summary, setSummary] = useState<WikipediaSummaryType | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const t = useTranslation(language);
|
||||
|
||||
useEffect(() => {
|
||||
fetchSummary();
|
||||
@@ -46,7 +50,7 @@ export default function WikipediaSummary({ openingName, wikipediaSlug }: Wikiped
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-4 h-4 border-2 border-blue-600 dark:border-blue-400 border-t-transparent rounded-full animate-spin"></div>
|
||||
<p className="text-sm text-blue-800 dark:text-blue-300">Loading opening background...</p>
|
||||
<p className="text-sm text-blue-800 dark:text-blue-300">{t.learning.openingTrainer.loadingBackground}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -56,7 +60,7 @@ export default function WikipediaSummary({ openingName, wikipediaSlug }: Wikiped
|
||||
return (
|
||||
<div className="bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
No background information available for this opening.
|
||||
{t.learning.openingTrainer.noBackgroundInfo}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -77,7 +81,7 @@ export default function WikipediaSummary({ openingName, wikipediaSlug }: Wikiped
|
||||
</div>
|
||||
<p className="text-sm text-gray-700 dark:text-gray-300 leading-relaxed">{summary.extract}</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Source: Wikipedia (cached {new Date(summary.fetchedAt).toLocaleDateString()})
|
||||
{t.learning.openingTrainer.wikipediaSource} {new Date(summary.fetchedAt).toLocaleDateString()})
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -215,6 +215,23 @@ export interface Translations {
|
||||
backToLearning: string;
|
||||
findTheMove: string;
|
||||
};
|
||||
openingTrainer: {
|
||||
loadingSession: string;
|
||||
openingNotFound: string;
|
||||
openingNotFoundDescription: string;
|
||||
backToOpeningSelection: string;
|
||||
loadingBackground: string;
|
||||
noBackgroundInfo: string;
|
||||
wikipediaSource: string;
|
||||
deviationTitle: string;
|
||||
deviationMessage: string;
|
||||
deviationTip: string;
|
||||
continuePlayingButton: string;
|
||||
undoReturnButton: string;
|
||||
exploreVariationButton: string;
|
||||
move: string;
|
||||
moves: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -419,6 +436,23 @@ const en: Translations = {
|
||||
backToLearning: 'Back to Learning Area',
|
||||
findTheMove: 'Find the move that creates a',
|
||||
},
|
||||
openingTrainer: {
|
||||
loadingSession: 'Loading opening training session...',
|
||||
openingNotFound: 'Opening Not Found',
|
||||
openingNotFoundDescription: 'The requested opening could not be found.',
|
||||
backToOpeningSelection: 'Back to Opening Selection',
|
||||
loadingBackground: 'Loading opening background...',
|
||||
noBackgroundInfo: 'No background information available for this opening.',
|
||||
wikipediaSource: 'Source: Wikipedia (cached',
|
||||
deviationTitle: "You've Left the Opening!",
|
||||
deviationMessage: "You've studied",
|
||||
deviationTip: 'Tip: You can always navigate back using the move history',
|
||||
continuePlayingButton: 'Continue Playing (Start Game)',
|
||||
undoReturnButton: 'Undo & Return to Opening',
|
||||
exploreVariationButton: 'Explore This Variation',
|
||||
move: 'move',
|
||||
moves: 'moves',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -623,6 +657,23 @@ const de: Translations = {
|
||||
backToLearning: 'Zurück zum Lernbereich',
|
||||
findTheMove: 'Finde den Zug, der eine',
|
||||
},
|
||||
openingTrainer: {
|
||||
loadingSession: 'Lade Eröffnungstraining...',
|
||||
openingNotFound: 'Eröffnung nicht gefunden',
|
||||
openingNotFoundDescription: 'Die angeforderte Eröffnung konnte nicht gefunden werden.',
|
||||
backToOpeningSelection: 'Zurück zur Eröffnungsauswahl',
|
||||
loadingBackground: 'Lade Eröffnungshintergrund...',
|
||||
noBackgroundInfo: 'Keine Hintergrundinformationen für diese Eröffnung verfügbar.',
|
||||
wikipediaSource: 'Quelle: Wikipedia (zwischengespeichert',
|
||||
deviationTitle: 'Du hast die Eröffnung verlassen!',
|
||||
deviationMessage: 'Du hast',
|
||||
deviationTip: 'Tipp: Du kannst jederzeit über die Zughistorie zurücknavigieren',
|
||||
continuePlayingButton: 'Weiterspielen (Spiel starten)',
|
||||
undoReturnButton: 'Rückgängig & zur Eröffnung zurück',
|
||||
exploreVariationButton: 'Diese Variante erkunden',
|
||||
move: 'Zug',
|
||||
moves: 'Züge',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -827,6 +878,23 @@ const fr: Translations = {
|
||||
backToLearning: 'Retour à la zone d\'apprentissage',
|
||||
findTheMove: 'Trouvez le coup qui crée un',
|
||||
},
|
||||
openingTrainer: {
|
||||
loadingSession: 'Chargement de la session d\'entraînement...',
|
||||
openingNotFound: 'Ouverture non trouvée',
|
||||
openingNotFoundDescription: 'L\'ouverture demandée n\'a pas pu être trouvée.',
|
||||
backToOpeningSelection: 'Retour à la sélection d\'ouverture',
|
||||
loadingBackground: 'Chargement des informations sur l\'ouverture...',
|
||||
noBackgroundInfo: 'Aucune information disponible pour cette ouverture.',
|
||||
wikipediaSource: 'Source : Wikipédia (en cache',
|
||||
deviationTitle: 'Vous avez quitté l\'ouverture !',
|
||||
deviationMessage: 'Vous avez étudié',
|
||||
deviationTip: 'Astuce : Vous pouvez toujours revenir en arrière en utilisant l\'historique des coups',
|
||||
continuePlayingButton: 'Continuer à jouer (Démarrer la partie)',
|
||||
undoReturnButton: 'Annuler et retourner à l\'ouverture',
|
||||
exploreVariationButton: 'Explorer cette variante',
|
||||
move: 'coup',
|
||||
moves: 'coups',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1031,6 +1099,23 @@ const it: Translations = {
|
||||
backToLearning: 'Torna all\'area di apprendimento',
|
||||
findTheMove: 'Trova la mossa che crea un',
|
||||
},
|
||||
openingTrainer: {
|
||||
loadingSession: 'Caricamento sessione di allenamento...',
|
||||
openingNotFound: 'Apertura non trovata',
|
||||
openingNotFoundDescription: 'L\'apertura richiesta non è stata trovata.',
|
||||
backToOpeningSelection: 'Torna alla selezione delle aperture',
|
||||
loadingBackground: 'Caricamento informazioni sull\'apertura...',
|
||||
noBackgroundInfo: 'Nessuna informazione disponibile per questa apertura.',
|
||||
wikipediaSource: 'Fonte: Wikipedia (in cache',
|
||||
deviationTitle: 'Hai lasciato l\'apertura!',
|
||||
deviationMessage: 'Hai studiato',
|
||||
deviationTip: 'Suggerimento: Puoi sempre tornare indietro usando la cronologia delle mosse',
|
||||
continuePlayingButton: 'Continua a giocare (Inizia partita)',
|
||||
undoReturnButton: 'Annulla e torna all\'apertura',
|
||||
exploreVariationButton: 'Esplora questa variante',
|
||||
move: 'mossa',
|
||||
moves: 'mosse',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1235,6 +1320,23 @@ const pl: Translations = {
|
||||
backToLearning: 'Powrót do strefy nauki',
|
||||
findTheMove: 'Znajdź ruch, który tworzy',
|
||||
},
|
||||
openingTrainer: {
|
||||
loadingSession: 'Ładowanie sesji treningowej...',
|
||||
openingNotFound: 'Nie znaleziono otwarcia',
|
||||
openingNotFoundDescription: 'Żądane otwarcie nie zostało znalezione.',
|
||||
backToOpeningSelection: 'Powrót do wyboru otwarcia',
|
||||
loadingBackground: 'Ładowanie informacji o otwarciu...',
|
||||
noBackgroundInfo: 'Brak informacji o tym otwarciu.',
|
||||
wikipediaSource: 'Źródło: Wikipedia (w pamięci podręcznej',
|
||||
deviationTitle: 'Opuściłeś otwarcie!',
|
||||
deviationMessage: 'Przestudiowałeś',
|
||||
deviationTip: 'Wskazówka: Zawsze możesz wrócić używając historii ruchów',
|
||||
continuePlayingButton: 'Kontynuuj grę (Rozpocznij partię)',
|
||||
undoReturnButton: 'Cofnij i wróć do otwarcia',
|
||||
exploreVariationButton: 'Zbadaj tę wariant',
|
||||
move: 'ruch',
|
||||
moves: 'ruchy',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user