diff --git a/package.json b/package.json index 965f9e21..b997df01 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "type-check": "tsc --noEmit", "validate:ui": "node scripts/validate-ui.js", "validate:web3": "node scripts/validate-web3.js", - "validate": "npm run validate:ui && npm run validate:web3" + "validate:i18n": "node scripts/validate-i18n.js", + "validate": "npm run validate:ui && npm run validate:web3 && npm run validate:i18n" }, "dependencies": { "@dnd-kit/core": "^6.3.1", diff --git a/scripts/validate-i18n.js b/scripts/validate-i18n.js new file mode 100644 index 00000000..1869140f --- /dev/null +++ b/scripts/validate-i18n.js @@ -0,0 +1,111 @@ +/** + * validate-i18n.js — Build-time translation key validation + * + * Loads en.json as the reference and compares every other locale file + * in src/locales/ to ensure key parity. Exits with code 1 if any + * locale has missing or extra keys. + * + * Usage: node scripts/validate-i18n.js + */ + +import { readFileSync, readdirSync } from 'fs'; +import { join, basename } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = fileURLToPath(new URL('.', import.meta.url)); +const LOCALES_DIR = join(__dirname, '..', 'src', 'locales'); +const REFERENCE_LANG = 'en'; + +// ── helpers ────────────────────────────────────────────────────────── + +/** Recursively collect every leaf-key path from a nested object. */ +function getKeys(obj, prefix = '') { + const keys = []; + for (const key of Object.keys(obj)) { + const fullKey = prefix ? `${prefix}.${key}` : key; + if (typeof obj[key] === 'object' && obj[key] !== null) { + keys.push(...getKeys(obj[key], fullKey)); + } else { + keys.push(fullKey); + } + } + return keys; +} + +// ── main ───────────────────────────────────────────────────────────── + +function main() { + // Discover locale JSON files + const files = readdirSync(LOCALES_DIR).filter( + (f) => f.endsWith('.json') + ); + + if (files.length === 0) { + console.error('❌ No locale JSON files found in', LOCALES_DIR); + process.exit(1); + } + + // Load reference + const refFile = `${REFERENCE_LANG}.json`; + if (!files.includes(refFile)) { + console.error(`❌ Reference locale file "${refFile}" not found`); + process.exit(1); + } + + const refPath = join(LOCALES_DIR, refFile); + const refData = JSON.parse(readFileSync(refPath, 'utf-8')); + const refKeys = getKeys(refData).sort(); + + console.log(`\n📖 Reference: ${refFile} (${refKeys.length} keys)\n`); + + let hasErrors = false; + + for (const file of files) { + if (file === refFile) continue; + + const lang = basename(file, '.json'); + const filePath = join(LOCALES_DIR, file); + + let data; + try { + data = JSON.parse(readFileSync(filePath, 'utf-8')); + } catch (err) { + console.error(`❌ ${lang}: Failed to parse ${file} — ${err.message}`); + hasErrors = true; + continue; + } + + const langKeys = getKeys(data).sort(); + + const missing = refKeys.filter((k) => !langKeys.includes(k)); + const extra = langKeys.filter((k) => !refKeys.includes(k)); + + if (missing.length === 0 && extra.length === 0) { + console.log(` ✅ ${lang}: All ${refKeys.length} keys present`); + } else { + hasErrors = true; + + if (missing.length > 0) { + console.error(` ❌ ${lang}: ${missing.length} MISSING key(s):`); + missing.forEach((k) => console.error(` - ${k}`)); + } + + if (extra.length > 0) { + console.warn(` ⚠️ ${lang}: ${extra.length} EXTRA key(s):`); + extra.forEach((k) => console.warn(` + ${k}`)); + } + } + } + + console.log(''); + + if (hasErrors) { + console.error('❌ Translation validation FAILED — fix the issues above.\n'); + process.exit(1); + } + + console.log('✅ All translations are complete and in sync.\n'); + process.exit(0); +} + +main(); diff --git a/src/hooks/useInternationalization.tsx b/src/hooks/useInternationalization.tsx index 818cfdc0..8003d531 100644 --- a/src/hooks/useInternationalization.tsx +++ b/src/hooks/useInternationalization.tsx @@ -115,9 +115,16 @@ export function I18nProvider({ // Translation function const t = useCallback( (key: string, params?: Record) => { - return getTranslation(translations, key, params); + const result = getTranslation(translations, key, params); + + // Warn in development when a key resolves to its raw path (missing translation) + if (process.env.NODE_ENV === 'development' && result === key) { + console.warn(`[i18n] Missing translation key: "${key}" for language "${language}"`); + } + + return result; }, - [translations], + [translations, language], ); // Formatting functions diff --git a/src/locales/ar.json b/src/locales/ar.json index a8fcee70..020ebd57 100644 --- a/src/locales/ar.json +++ b/src/locales/ar.json @@ -17,7 +17,8 @@ "submit": "إرسال", "confirm": "تأكيد", "yes": "نعم", - "no": "لا" + "no": "لا", + "welcome": "مرحباً" }, "navigation": { "home": "الرئيسية", diff --git a/src/locales/de.json b/src/locales/de.json new file mode 100644 index 00000000..d25c82f9 --- /dev/null +++ b/src/locales/de.json @@ -0,0 +1,89 @@ +{ + "common": { + "loading": "Laden...", + "error": "Fehler", + "success": "Erfolg", + "cancel": "Abbrechen", + "save": "Speichern", + "delete": "Löschen", + "edit": "Bearbeiten", + "create": "Erstellen", + "search": "Suchen", + "filter": "Filtern", + "close": "Schließen", + "back": "Zurück", + "next": "Weiter", + "previous": "Zurück", + "submit": "Absenden", + "confirm": "Bestätigen", + "yes": "Ja", + "no": "Nein", + "welcome": "Willkommen" + }, + "navigation": { + "home": "Startseite", + "courses": "Kurse", + "dashboard": "Dashboard", + "profile": "Profil", + "settings": "Einstellungen", + "messages": "Nachrichten", + "notifications": "Benachrichtigungen", + "logout": "Abmelden", + "login": "Anmelden", + "signup": "Registrieren" + }, + "course": { + "title": "Kurs", + "enroll": "Einschreiben", + "enrolled": "Eingeschrieben", + "progress": "Fortschritt", + "lessons": "Lektionen", + "duration": "Dauer", + "instructor": "Dozent", + "reviews": "Bewertungen", + "rating": "Bewertung", + "description": "Beschreibung", + "syllabus": "Lehrplan", + "resources": "Ressourcen" + }, + "dashboard": { + "welcome": "Willkommen", + "myCourses": "Meine Kurse", + "recentActivity": "Letzte Aktivität", + "upcomingDeadlines": "Anstehende Fristen", + "recommendedCourses": "Empfohlene Kurse", + "learningStreak": "Lernserie", + "progressSummary": "Fortschrittsübersicht" + }, + "profile": { + "editProfile": "Profil Bearbeiten", + "preferences": "Einstellungen", + "language": "Sprache", + "theme": "Design", + "notifications": "Benachrichtigungen", + "privacy": "Datenschutz", + "account": "Konto" + }, + "i18n": { + "selectLanguage": "Sprache Auswählen", + "currentLanguage": "Aktuelle Sprache", + "languageChanged": "Sprache erfolgreich geändert", + "rtlMode": "Rechts-nach-Links-Modus", + "ltrMode": "Links-nach-Rechts-Modus" + }, + "validation": { + "required": "Dieses Feld ist erforderlich", + "email": "Bitte geben Sie eine gültige E-Mail-Adresse ein", + "minLength": "Mindestlänge beträgt {{min}} Zeichen", + "maxLength": "Maximale Länge beträgt {{max}} Zeichen", + "passwordMismatch": "Passwörter stimmen nicht überein" + }, + "errors": { + "generic": "Ein Fehler ist aufgetreten", + "network": "Netzwerkfehler. Bitte überprüfen Sie Ihre Verbindung", + "notFound": "Nicht gefunden", + "unauthorized": "Nicht autorisiert", + "forbidden": "Verboten", + "serverError": "Serverfehler" + } +} diff --git a/src/locales/en.json b/src/locales/en.json index 9b830488..3e0af9d6 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -17,7 +17,8 @@ "submit": "Submit", "confirm": "Confirm", "yes": "Yes", - "no": "No" + "no": "No", + "welcome": "Welcome" }, "navigation": { "home": "Home", diff --git a/src/locales/es.json b/src/locales/es.json index aca118a7..200717c1 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -17,7 +17,8 @@ "submit": "Enviar", "confirm": "Confirmar", "yes": "Sí", - "no": "No" + "no": "No", + "welcome": "Bienvenido" }, "navigation": { "home": "Inicio", diff --git a/src/locales/fr.json b/src/locales/fr.json new file mode 100644 index 00000000..9716878a --- /dev/null +++ b/src/locales/fr.json @@ -0,0 +1,89 @@ +{ + "common": { + "loading": "Chargement...", + "error": "Erreur", + "success": "Succès", + "cancel": "Annuler", + "save": "Enregistrer", + "delete": "Supprimer", + "edit": "Modifier", + "create": "Créer", + "search": "Rechercher", + "filter": "Filtrer", + "close": "Fermer", + "back": "Retour", + "next": "Suivant", + "previous": "Précédent", + "submit": "Soumettre", + "confirm": "Confirmer", + "yes": "Oui", + "no": "Non", + "welcome": "Bienvenue" + }, + "navigation": { + "home": "Accueil", + "courses": "Cours", + "dashboard": "Tableau de bord", + "profile": "Profil", + "settings": "Paramètres", + "messages": "Messages", + "notifications": "Notifications", + "logout": "Déconnexion", + "login": "Connexion", + "signup": "S'inscrire" + }, + "course": { + "title": "Cours", + "enroll": "S'inscrire", + "enrolled": "Inscrit", + "progress": "Progression", + "lessons": "Leçons", + "duration": "Durée", + "instructor": "Instructeur", + "reviews": "Avis", + "rating": "Note", + "description": "Description", + "syllabus": "Programme", + "resources": "Ressources" + }, + "dashboard": { + "welcome": "Bienvenue", + "myCourses": "Mes Cours", + "recentActivity": "Activité Récente", + "upcomingDeadlines": "Échéances à Venir", + "recommendedCourses": "Cours Recommandés", + "learningStreak": "Série d'Apprentissage", + "progressSummary": "Résumé de Progression" + }, + "profile": { + "editProfile": "Modifier le Profil", + "preferences": "Préférences", + "language": "Langue", + "theme": "Thème", + "notifications": "Notifications", + "privacy": "Confidentialité", + "account": "Compte" + }, + "i18n": { + "selectLanguage": "Sélectionner la Langue", + "currentLanguage": "Langue Actuelle", + "languageChanged": "Langue modifiée avec succès", + "rtlMode": "Mode Droite à Gauche", + "ltrMode": "Mode Gauche à Droite" + }, + "validation": { + "required": "Ce champ est obligatoire", + "email": "Veuillez entrer une adresse e-mail valide", + "minLength": "La longueur minimale est de {{min}} caractères", + "maxLength": "La longueur maximale est de {{max}} caractères", + "passwordMismatch": "Les mots de passe ne correspondent pas" + }, + "errors": { + "generic": "Une erreur est survenue", + "network": "Erreur réseau. Veuillez vérifier votre connexion", + "notFound": "Non trouvé", + "unauthorized": "Non autorisé", + "forbidden": "Interdit", + "serverError": "Erreur du serveur" + } +} diff --git a/src/locales/he.json b/src/locales/he.json new file mode 100644 index 00000000..bf720e87 --- /dev/null +++ b/src/locales/he.json @@ -0,0 +1,89 @@ +{ + "common": { + "loading": "טוען...", + "error": "שגיאה", + "success": "הצלחה", + "cancel": "ביטול", + "save": "שמירה", + "delete": "מחיקה", + "edit": "עריכה", + "create": "יצירה", + "search": "חיפוש", + "filter": "סינון", + "close": "סגירה", + "back": "חזרה", + "next": "הבא", + "previous": "הקודם", + "submit": "שליחה", + "confirm": "אישור", + "yes": "כן", + "no": "לא", + "welcome": "ברוכים הבאים" + }, + "navigation": { + "home": "דף הבית", + "courses": "קורסים", + "dashboard": "לוח בקרה", + "profile": "פרופיל", + "settings": "הגדרות", + "messages": "הודעות", + "notifications": "התראות", + "logout": "התנתקות", + "login": "התחברות", + "signup": "הרשמה" + }, + "course": { + "title": "קורס", + "enroll": "הרשמה", + "enrolled": "רשום", + "progress": "התקדמות", + "lessons": "שיעורים", + "duration": "משך", + "instructor": "מרצה", + "reviews": "ביקורות", + "rating": "דירוג", + "description": "תיאור", + "syllabus": "סילבוס", + "resources": "משאבים" + }, + "dashboard": { + "welcome": "ברוכים הבאים", + "myCourses": "הקורסים שלי", + "recentActivity": "פעילות אחרונה", + "upcomingDeadlines": "מועדים קרובים", + "recommendedCourses": "קורסים מומלצים", + "learningStreak": "רצף למידה", + "progressSummary": "סיכום התקדמות" + }, + "profile": { + "editProfile": "עריכת פרופיל", + "preferences": "העדפות", + "language": "שפה", + "theme": "ערכת נושא", + "notifications": "התראות", + "privacy": "פרטיות", + "account": "חשבון" + }, + "i18n": { + "selectLanguage": "בחר שפה", + "currentLanguage": "שפה נוכחית", + "languageChanged": "השפה שונתה בהצלחה", + "rtlMode": "מצב ימין לשמאל", + "ltrMode": "מצב שמאל לימין" + }, + "validation": { + "required": "שדה זה הוא חובה", + "email": "אנא הזן כתובת דוא\"ל תקינה", + "minLength": "אורך מינימלי הוא {{min}} תווים", + "maxLength": "אורך מקסימלי הוא {{max}} תווים", + "passwordMismatch": "הסיסמאות אינן תואמות" + }, + "errors": { + "generic": "אירעה שגיאה", + "network": "שגיאת רשת. אנא בדוק את החיבור שלך", + "notFound": "לא נמצא", + "unauthorized": "לא מורשה", + "forbidden": "אסור", + "serverError": "שגיאת שרת" + } +} diff --git a/src/locales/it.json b/src/locales/it.json new file mode 100644 index 00000000..bf16a743 --- /dev/null +++ b/src/locales/it.json @@ -0,0 +1,89 @@ +{ + "common": { + "loading": "Caricamento...", + "error": "Errore", + "success": "Successo", + "cancel": "Annulla", + "save": "Salva", + "delete": "Elimina", + "edit": "Modifica", + "create": "Crea", + "search": "Cerca", + "filter": "Filtra", + "close": "Chiudi", + "back": "Indietro", + "next": "Avanti", + "previous": "Precedente", + "submit": "Invia", + "confirm": "Conferma", + "yes": "Sì", + "no": "No", + "welcome": "Benvenuto" + }, + "navigation": { + "home": "Home", + "courses": "Corsi", + "dashboard": "Pannello", + "profile": "Profilo", + "settings": "Impostazioni", + "messages": "Messaggi", + "notifications": "Notifiche", + "logout": "Esci", + "login": "Accedi", + "signup": "Registrati" + }, + "course": { + "title": "Corso", + "enroll": "Iscriviti", + "enrolled": "Iscritto", + "progress": "Progresso", + "lessons": "Lezioni", + "duration": "Durata", + "instructor": "Istruttore", + "reviews": "Recensioni", + "rating": "Valutazione", + "description": "Descrizione", + "syllabus": "Programma", + "resources": "Risorse" + }, + "dashboard": { + "welcome": "Benvenuto", + "myCourses": "I Miei Corsi", + "recentActivity": "Attività Recente", + "upcomingDeadlines": "Scadenze Imminenti", + "recommendedCourses": "Corsi Consigliati", + "learningStreak": "Serie di Apprendimento", + "progressSummary": "Riepilogo Progressi" + }, + "profile": { + "editProfile": "Modifica Profilo", + "preferences": "Preferenze", + "language": "Lingua", + "theme": "Tema", + "notifications": "Notifiche", + "privacy": "Privacy", + "account": "Account" + }, + "i18n": { + "selectLanguage": "Seleziona Lingua", + "currentLanguage": "Lingua Corrente", + "languageChanged": "Lingua cambiata con successo", + "rtlMode": "Modalità Destra a Sinistra", + "ltrMode": "Modalità Sinistra a Destra" + }, + "validation": { + "required": "Questo campo è obbligatorio", + "email": "Inserisci un indirizzo e-mail valido", + "minLength": "La lunghezza minima è di {{min}} caratteri", + "maxLength": "La lunghezza massima è di {{max}} caratteri", + "passwordMismatch": "Le password non corrispondono" + }, + "errors": { + "generic": "Si è verificato un errore", + "network": "Errore di rete. Verifica la tua connessione", + "notFound": "Non trovato", + "unauthorized": "Non autorizzato", + "forbidden": "Vietato", + "serverError": "Errore del server" + } +} diff --git a/src/locales/ja.json b/src/locales/ja.json new file mode 100644 index 00000000..c5f01787 --- /dev/null +++ b/src/locales/ja.json @@ -0,0 +1,89 @@ +{ + "common": { + "loading": "読み込み中...", + "error": "エラー", + "success": "成功", + "cancel": "キャンセル", + "save": "保存", + "delete": "削除", + "edit": "編集", + "create": "作成", + "search": "検索", + "filter": "フィルター", + "close": "閉じる", + "back": "戻る", + "next": "次へ", + "previous": "前へ", + "submit": "送信", + "confirm": "確認", + "yes": "はい", + "no": "いいえ", + "welcome": "ようこそ" + }, + "navigation": { + "home": "ホーム", + "courses": "コース", + "dashboard": "ダッシュボード", + "profile": "プロフィール", + "settings": "設定", + "messages": "メッセージ", + "notifications": "通知", + "logout": "ログアウト", + "login": "ログイン", + "signup": "新規登録" + }, + "course": { + "title": "コース", + "enroll": "受講登録", + "enrolled": "登録済み", + "progress": "進捗", + "lessons": "レッスン", + "duration": "期間", + "instructor": "講師", + "reviews": "レビュー", + "rating": "評価", + "description": "説明", + "syllabus": "シラバス", + "resources": "リソース" + }, + "dashboard": { + "welcome": "ようこそ", + "myCourses": "マイコース", + "recentActivity": "最近のアクティビティ", + "upcomingDeadlines": "今後の締め切り", + "recommendedCourses": "おすすめコース", + "learningStreak": "学習連続記録", + "progressSummary": "進捗の概要" + }, + "profile": { + "editProfile": "プロフィール編集", + "preferences": "設定", + "language": "言語", + "theme": "テーマ", + "notifications": "通知", + "privacy": "プライバシー", + "account": "アカウント" + }, + "i18n": { + "selectLanguage": "言語を選択", + "currentLanguage": "現在の言語", + "languageChanged": "言語が正常に変更されました", + "rtlMode": "右から左モード", + "ltrMode": "左から右モード" + }, + "validation": { + "required": "この項目は必須です", + "email": "有効なメールアドレスを入力してください", + "minLength": "最小文字数は{{min}}文字です", + "maxLength": "最大文字数は{{max}}文字です", + "passwordMismatch": "パスワードが一致しません" + }, + "errors": { + "generic": "エラーが発生しました", + "network": "ネットワークエラー。接続を確認してください", + "notFound": "見つかりません", + "unauthorized": "認証されていません", + "forbidden": "アクセスが禁止されています", + "serverError": "サーバーエラー" + } +} diff --git a/src/locales/ko.json b/src/locales/ko.json new file mode 100644 index 00000000..a8215f75 --- /dev/null +++ b/src/locales/ko.json @@ -0,0 +1,89 @@ +{ + "common": { + "loading": "로딩 중...", + "error": "오류", + "success": "성공", + "cancel": "취소", + "save": "저장", + "delete": "삭제", + "edit": "편집", + "create": "생성", + "search": "검색", + "filter": "필터", + "close": "닫기", + "back": "뒤로", + "next": "다음", + "previous": "이전", + "submit": "제출", + "confirm": "확인", + "yes": "예", + "no": "아니요", + "welcome": "환영합니다" + }, + "navigation": { + "home": "홈", + "courses": "강좌", + "dashboard": "대시보드", + "profile": "프로필", + "settings": "설정", + "messages": "메시지", + "notifications": "알림", + "logout": "로그아웃", + "login": "로그인", + "signup": "회원가입" + }, + "course": { + "title": "강좌", + "enroll": "등록", + "enrolled": "등록됨", + "progress": "진행률", + "lessons": "수업", + "duration": "기간", + "instructor": "강사", + "reviews": "리뷰", + "rating": "평점", + "description": "설명", + "syllabus": "교과 과정", + "resources": "자료" + }, + "dashboard": { + "welcome": "환영합니다", + "myCourses": "내 강좌", + "recentActivity": "최근 활동", + "upcomingDeadlines": "다가오는 마감일", + "recommendedCourses": "추천 강좌", + "learningStreak": "학습 연속 기록", + "progressSummary": "진행 요약" + }, + "profile": { + "editProfile": "프로필 편집", + "preferences": "환경 설정", + "language": "언어", + "theme": "테마", + "notifications": "알림", + "privacy": "개인정보", + "account": "계정" + }, + "i18n": { + "selectLanguage": "언어 선택", + "currentLanguage": "현재 언어", + "languageChanged": "언어가 성공적으로 변경되었습니다", + "rtlMode": "오른쪽에서 왼쪽 모드", + "ltrMode": "왼쪽에서 오른쪽 모드" + }, + "validation": { + "required": "이 필드는 필수입니다", + "email": "유효한 이메일 주소를 입력해 주세요", + "minLength": "최소 {{min}}자 이상이어야 합니다", + "maxLength": "최대 {{max}}자까지 입력할 수 있습니다", + "passwordMismatch": "비밀번호가 일치하지 않습니다" + }, + "errors": { + "generic": "오류가 발생했습니다", + "network": "네트워크 오류. 연결을 확인해 주세요", + "notFound": "찾을 수 없습니다", + "unauthorized": "인증되지 않았습니다", + "forbidden": "접근이 금지되었습니다", + "serverError": "서버 오류" + } +} diff --git a/src/locales/pt.json b/src/locales/pt.json new file mode 100644 index 00000000..e87c6e8d --- /dev/null +++ b/src/locales/pt.json @@ -0,0 +1,89 @@ +{ + "common": { + "loading": "Carregando...", + "error": "Erro", + "success": "Sucesso", + "cancel": "Cancelar", + "save": "Salvar", + "delete": "Excluir", + "edit": "Editar", + "create": "Criar", + "search": "Buscar", + "filter": "Filtrar", + "close": "Fechar", + "back": "Voltar", + "next": "Próximo", + "previous": "Anterior", + "submit": "Enviar", + "confirm": "Confirmar", + "yes": "Sim", + "no": "Não", + "welcome": "Bem-vindo" + }, + "navigation": { + "home": "Início", + "courses": "Cursos", + "dashboard": "Painel", + "profile": "Perfil", + "settings": "Configurações", + "messages": "Mensagens", + "notifications": "Notificações", + "logout": "Sair", + "login": "Entrar", + "signup": "Cadastrar" + }, + "course": { + "title": "Curso", + "enroll": "Matricular-se", + "enrolled": "Matriculado", + "progress": "Progresso", + "lessons": "Aulas", + "duration": "Duração", + "instructor": "Instrutor", + "reviews": "Avaliações", + "rating": "Nota", + "description": "Descrição", + "syllabus": "Ementa", + "resources": "Recursos" + }, + "dashboard": { + "welcome": "Bem-vindo", + "myCourses": "Meus Cursos", + "recentActivity": "Atividade Recente", + "upcomingDeadlines": "Próximos Prazos", + "recommendedCourses": "Cursos Recomendados", + "learningStreak": "Sequência de Aprendizado", + "progressSummary": "Resumo de Progresso" + }, + "profile": { + "editProfile": "Editar Perfil", + "preferences": "Preferências", + "language": "Idioma", + "theme": "Tema", + "notifications": "Notificações", + "privacy": "Privacidade", + "account": "Conta" + }, + "i18n": { + "selectLanguage": "Selecionar Idioma", + "currentLanguage": "Idioma Atual", + "languageChanged": "Idioma alterado com sucesso", + "rtlMode": "Modo Direita para Esquerda", + "ltrMode": "Modo Esquerda para Direita" + }, + "validation": { + "required": "Este campo é obrigatório", + "email": "Por favor, insira um endereço de e-mail válido", + "minLength": "O comprimento mínimo é de {{min}} caracteres", + "maxLength": "O comprimento máximo é de {{max}} caracteres", + "passwordMismatch": "As senhas não coincidem" + }, + "errors": { + "generic": "Ocorreu um erro", + "network": "Erro de rede. Por favor, verifique sua conexão", + "notFound": "Não encontrado", + "unauthorized": "Não autorizado", + "forbidden": "Proibido", + "serverError": "Erro do servidor" + } +} diff --git a/src/locales/ru.json b/src/locales/ru.json new file mode 100644 index 00000000..fcb79087 --- /dev/null +++ b/src/locales/ru.json @@ -0,0 +1,89 @@ +{ + "common": { + "loading": "Загрузка...", + "error": "Ошибка", + "success": "Успешно", + "cancel": "Отмена", + "save": "Сохранить", + "delete": "Удалить", + "edit": "Редактировать", + "create": "Создать", + "search": "Поиск", + "filter": "Фильтр", + "close": "Закрыть", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "submit": "Отправить", + "confirm": "Подтвердить", + "yes": "Да", + "no": "Нет", + "welcome": "Добро пожаловать" + }, + "navigation": { + "home": "Главная", + "courses": "Курсы", + "dashboard": "Панель управления", + "profile": "Профиль", + "settings": "Настройки", + "messages": "Сообщения", + "notifications": "Уведомления", + "logout": "Выйти", + "login": "Войти", + "signup": "Регистрация" + }, + "course": { + "title": "Курс", + "enroll": "Записаться", + "enrolled": "Записан", + "progress": "Прогресс", + "lessons": "Уроки", + "duration": "Длительность", + "instructor": "Преподаватель", + "reviews": "Отзывы", + "rating": "Рейтинг", + "description": "Описание", + "syllabus": "Учебный план", + "resources": "Ресурсы" + }, + "dashboard": { + "welcome": "Добро пожаловать", + "myCourses": "Мои Курсы", + "recentActivity": "Последняя Активность", + "upcomingDeadlines": "Ближайшие Сроки", + "recommendedCourses": "Рекомендуемые Курсы", + "learningStreak": "Серия Обучения", + "progressSummary": "Сводка Прогресса" + }, + "profile": { + "editProfile": "Редактировать Профиль", + "preferences": "Настройки", + "language": "Язык", + "theme": "Тема", + "notifications": "Уведомления", + "privacy": "Конфиденциальность", + "account": "Аккаунт" + }, + "i18n": { + "selectLanguage": "Выбрать Язык", + "currentLanguage": "Текущий Язык", + "languageChanged": "Язык успешно изменен", + "rtlMode": "Режим Справа Налево", + "ltrMode": "Режим Слева Направо" + }, + "validation": { + "required": "Это поле обязательно для заполнения", + "email": "Пожалуйста, введите действительный адрес электронной почты", + "minLength": "Минимальная длина — {{min}} символов", + "maxLength": "Максимальная длина — {{max}} символов", + "passwordMismatch": "Пароли не совпадают" + }, + "errors": { + "generic": "Произошла ошибка", + "network": "Ошибка сети. Пожалуйста, проверьте подключение", + "notFound": "Не найдено", + "unauthorized": "Не авторизован", + "forbidden": "Запрещено", + "serverError": "Ошибка сервера" + } +} diff --git a/src/locales/zh.json b/src/locales/zh.json new file mode 100644 index 00000000..6ed2db32 --- /dev/null +++ b/src/locales/zh.json @@ -0,0 +1,89 @@ +{ + "common": { + "loading": "加载中...", + "error": "错误", + "success": "成功", + "cancel": "取消", + "save": "保存", + "delete": "删除", + "edit": "编辑", + "create": "创建", + "search": "搜索", + "filter": "筛选", + "close": "关闭", + "back": "返回", + "next": "下一步", + "previous": "上一步", + "submit": "提交", + "confirm": "确认", + "yes": "是", + "no": "否", + "welcome": "欢迎" + }, + "navigation": { + "home": "首页", + "courses": "课程", + "dashboard": "仪表盘", + "profile": "个人资料", + "settings": "设置", + "messages": "消息", + "notifications": "通知", + "logout": "退出登录", + "login": "登录", + "signup": "注册" + }, + "course": { + "title": "课程", + "enroll": "报名", + "enrolled": "已报名", + "progress": "进度", + "lessons": "课时", + "duration": "时长", + "instructor": "讲师", + "reviews": "评论", + "rating": "评分", + "description": "描述", + "syllabus": "大纲", + "resources": "资源" + }, + "dashboard": { + "welcome": "欢迎", + "myCourses": "我的课程", + "recentActivity": "最近活动", + "upcomingDeadlines": "即将到期", + "recommendedCourses": "推荐课程", + "learningStreak": "连续学习", + "progressSummary": "进度概要" + }, + "profile": { + "editProfile": "编辑个人资料", + "preferences": "偏好设置", + "language": "语言", + "theme": "主题", + "notifications": "通知", + "privacy": "隐私", + "account": "账户" + }, + "i18n": { + "selectLanguage": "选择语言", + "currentLanguage": "当前语言", + "languageChanged": "语言切换成功", + "rtlMode": "从右到左模式", + "ltrMode": "从左到右模式" + }, + "validation": { + "required": "此字段为必填项", + "email": "请输入有效的电子邮件地址", + "minLength": "最少需要{{min}}个字符", + "maxLength": "最多允许{{max}}个字符", + "passwordMismatch": "两次输入的密码不一致" + }, + "errors": { + "generic": "发生了一个错误", + "network": "网络错误,请检查您的连接", + "notFound": "未找到", + "unauthorized": "未授权", + "forbidden": "禁止访问", + "serverError": "服务器错误" + } +}