From e83c3117a5b1f21dc45dcde051dfb6a9a6d4c546 Mon Sep 17 00:00:00 2001 From: Quanysh Date: Thu, 3 Sep 2026 12:19:28 +0000 Subject: [PATCH] Add i18n (RU/EN/KZ), streaming platforms, mobile improvements --- index.html | 150 +++++++++++++++------------ script.js | 295 ++++++++++++++++++++++++++++++++++++++++++++++++++++- style.css | 131 ++++++++++++++++++++++++ 3 files changed, 506 insertions(+), 70 deletions(-) diff --git a/index.html b/index.html index 214e5ff..e0e9cae 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + FILMORA — Find your movie tonight @@ -23,16 +23,23 @@
- +
+ + +
@@ -40,45 +47,45 @@
-

Что смотрим сегодня?

-

Ответь на несколько вопросов — и мы найдём фильм

+

Что смотрим сегодня?

+

Ответь на несколько вопросов — и мы найдём фильм

-

Настроение

+

Настроение

@@ -86,46 +93,46 @@
-

Фильтры

- +

Фильтры

+
- +
- +
- + - - - - - + + + + +
@@ -150,11 +157,11 @@
@@ -163,6 +170,12 @@

@@ -175,14 +188,15 @@

+
@@ -192,9 +206,9 @@ @@ -206,32 +220,32 @@
-
diff --git a/script.js b/script.js index 7c927dc..87e3452 100644 --- a/script.js +++ b/script.js @@ -1,3 +1,189 @@ +// ==================== I18N TRANSLATIONS ==================== +const TRANSLATIONS = { + ru: { + favorites: "Избранное", + history: "История", + heroTitle: "Что смотрим сегодня?", + heroSubtitle: "Ответь на несколько вопросов — и мы найдём фильм", + moodTitle: "Настроение", + moodFun: "Весело", + moodScary: "Страшно", + moodThink: "Подумать", + moodRomance: "Романтика", + moodDrive: "Драйв", + moodComedy: "Комедия", + moodSad: "Грустное", + moodUnusual: "Что-то необычное", + filtersTitle: "Фильтры", + resetFilters: "Сбросить фильтры", + filterGenre: "Жанр", + genreAny: "Любой жанр", + genreAction: "Боевик", + genreComedy: "Комедия", + genreDrama: "Драма", + genreScifi: "Фантастика", + genreHorror: "Ужасы", + genreThriller: "Триллер", + genreAdventure: "Приключения", + genreFantasy: "Фэнтези", + genreMystery: "Детектив", + genreRomance: "Романтика", + genreAnimation: "Анимация", + genreCrime: "Криминал", + genreHistory: "История", + filterYear: "Год", + yearAny: "Любой год", + yearBefore1990: "До 1990", + filterRating: "Рейтинг", + ratingAny: "Любой", + filterDuration: "Длительность", + durationAny: "Любая", + duration90: "До 90 минут", + duration120: "До 120 минут", + duration150: "До 150 минут", + duration150plus: "Более 150 минут", + randomMode: "Я НЕ ЗНАЮ, ЧТО ХОЧУ", + findMovie: "НАЙТИ ФИЛЬМ", + watchNow: "Смотреть", + addFavorite: "В избранное", + anotherMovie: "Другой фильм", + emptyTitle: "Кажется, мы слишком сильно ограничили выбор", + emptyText: "Попробуй убрать один из фильтров", + back: "Назад", + myFavorites: "Мои фильмы ❤️", + favoritesEmpty: "Здесь пока пусто", + findFirstFilm: "Найдём тебе первый фильм? 🎬", + viewingHistory: "История просмотров", + historyEmpty: "История пуста", + noHistory: "Ты ещё не смотрел фильмы через FILMORA", + clearHistory: "Очистить историю", + whereToWatch: "Где смотреть", + streamingAvailable: "Доступно на платформах" + }, + en: { + favorites: "Favorites", + history: "History", + heroTitle: "What are we watching tonight?", + heroSubtitle: "Answer a few questions and we'll find your perfect movie", + moodTitle: "Mood", + moodFun: "Fun", + moodScary: "Scary", + moodThink: "Thoughtful", + moodRomance: "Romance", + moodDrive: "Action", + moodComedy: "Comedy", + moodSad: "Sad", + moodUnusual: "Something unusual", + filtersTitle: "Filters", + resetFilters: "Reset filters", + filterGenre: "Genre", + genreAny: "Any genre", + genreAction: "Action", + genreComedy: "Comedy", + genreDrama: "Drama", + genreScifi: "Sci-Fi", + genreHorror: "Horror", + genreThriller: "Thriller", + genreAdventure: "Adventure", + genreFantasy: "Fantasy", + genreMystery: "Mystery", + genreRomance: "Romance", + genreAnimation: "Animation", + genreCrime: "Crime", + genreHistory: "History", + filterYear: "Year", + yearAny: "Any year", + yearBefore1990: "Before 1990", + filterRating: "Rating", + ratingAny: "Any", + filterDuration: "Duration", + durationAny: "Any", + duration90: "Up to 90 min", + duration120: "Up to 120 min", + duration150: "Up to 150 min", + duration150plus: "Over 150 min", + randomMode: "I DON'T KNOW WHAT I WANT", + findMovie: "FIND MOVIE", + watchNow: "Watch Now", + addFavorite: "Add to favorites", + anotherMovie: "Another movie", + emptyTitle: "Hmm, filters are too strict", + emptyText: "Try removing some filters", + back: "Back", + myFavorites: "My Movies ❤️", + favoritesEmpty: "Nothing here yet", + findFirstFilm: "Find your first movie? 🎬", + viewingHistory: "Viewing History", + historyEmpty: "History is empty", + noHistory: "You haven't watched any movies through FILMORA yet", + clearHistory: "Clear history", + whereToWatch: "Where to watch", + streamingAvailable: "Available on" + }, + kk: { + favorites: "Таңдаулы", + history: "Тарих", + heroTitle: "Бүгін не көреміз?", + heroSubtitle: "Бірнеше сұраққа жауап бер — біз фильм табамыз", + moodTitle: "Көңіл-күй", + moodFun: "Көңілді", + moodScary: "Қорқынышты", + moodThink: "Ойлану", + moodRomance: "Романтика", + moodDrive: "Драйв", + moodComedy: "Комедия", + moodSad: "Мұңлы", + moodUnusual: "Ерекше нәрсе", + filtersTitle: "Сүзгілер", + resetFilters: "Сүзгілерді тазалау", + filterGenre: "Жанр", + genreAny: "Кез келген жанр", + genreAction: "Боевик", + genreComedy: "Комедия", + genreDrama: "Драма", + genreScifi: "Фантастика", + genreHorror: "Қорқынышты", + genreThriller: "Триллер", + genreAdventure: "Шытырман", + genreFantasy: "Фэнтези", + genreMystery: "Детектив", + genreRomance: "Романтика", + genreAnimation: "Анимация", + genreCrime: "Қылмыс", + genreHistory: "Тарих", + filterYear: "Жыл", + yearAny: "Кез келген жыл", + yearBefore1990: "1990 дейін", + filterRating: "Рейтинг", + ratingAny: "Кез келген", + filterDuration: "Ұзақтығы", + durationAny: "Кез келген", + duration90: "90 минутқа дейін", + duration120: "120 минутқа дейін", + duration150: "150 минутқа дейін", + duration150plus: "150 минуттан көп", + randomMode: "БІЛМЕЙМІН НЕ ҚАЛАЙМЫН", + findMovie: "ФИЛЬМ ТАБУ", + watchNow: "Көру", + addFavorite: "Таңдаулыға қосу", + anotherMovie: "Басқа фильм", + emptyTitle: "Сүзгілер тым қатаң", + emptyText: "Бір сүзгіні алып таста", + back: "Артқа", + myFavorites: "Менің фильмдерім ❤️", + favoritesEmpty: "Әлі бос", + findFirstFilm: "Алғашқы фильмді табайық па? 🎬", + viewingHistory: "Көру тарихы", + historyEmpty: "Тарих бос", + noHistory: "FILMORA арқылы әлі фильм көрмедің", + clearHistory: "Тарихты тазалау", + whereToWatch: "Қайда көруге", + streamingAvailable: "Платформаларда қолжетімді" + } +}; + +let currentLang = localStorage.getItem('filmora_lang') || 'ru'; + // ==================== MOVIE DATABASE ==================== const MOVIES = [ { @@ -9,8 +195,12 @@ const MOVIES = [ duration: 142, genres: ["drama", "crime"], moods: ["think", "sad"], - description: "Бухгалтер Энди Дюфрейн обвинён в убийстве собственной жены и её любовника. Оказавшись в тюрьме под названием Шоушенк, он сталкивается с жестокостью и беззаконием, царящими по обе стороны решётки.", - poster: "https://image.tmdb.org/t/p/w500/q6y0Go1tsGEsmtFryDOJo3dEmqu.jpg" + description: "Бухгалтер Энди Дюфрейн обвинён в убийстве собственной жены и её любовника. Оказавшись в тюрьме под названием Шоушенк, он сталкивается с жестокостью и беззаконием.", + poster: "https://image.tmdb.org/t/p/w500/q6y0Go1tsGEsmtFryDOJo3dEmqu.jpg", + streaming: [ + { name: "Netflix", icon: "🎬", url: "https://www.netflix.com" }, + { name: "Kinopoisk", icon: "🎭", url: "https://www.kinopoisk.ru" } + ] }, { id: 2, @@ -941,6 +1131,36 @@ function displayMovie(movie) { ).join(''); movieDescription.textContent = movie.description; + // Update streaming info + const streamingInfo = document.getElementById('streamingInfo'); + if (movie.streaming && movie.streaming.length > 0) { + const t = TRANSLATIONS[currentLang]; + streamingInfo.innerHTML = ` +

${t.whereToWatch}

+
+ ${movie.streaming.map(s => ` + + ${s.icon} + ${s.name} + + `).join('')} +
+ `; + streamingInfo.style.display = 'block'; + } else { + streamingInfo.style.display = 'none'; + } + + // Update watch button + const watchBtn = document.getElementById('watchButton'); + if (movie.streaming && movie.streaming.length > 0) { + watchBtn.href = movie.streaming[0].url; + watchBtn.style.display = 'flex'; + } else { + watchBtn.href = '#'; + watchBtn.style.display = 'none'; + } + // Update favorite button state const isFavorite = favorites.some(f => f.id === movie.id); addToFavoritesBtn.classList.toggle('active', isFavorite); @@ -1128,7 +1348,78 @@ randomModeBtn.addEventListener('click', () => { findMovie(); }); +// ==================== I18N FUNCTION ==================== +function updateTexts() { + const t = TRANSLATIONS[currentLang]; + + // Update all elements with data-i18n attribute + document.querySelectorAll('[data-i18n]').forEach(el => { + const key = el.dataset.i18n; + if (t[key]) { + el.textContent = t[key]; + } + }); + + // Update genre labels in select + const genreSelect = document.getElementById('genreFilter'); + const genreMap = { + any: 'genreAny', + action: 'genreAction', + comedy: 'genreComedy', + drama: 'genreDrama', + scifi: 'genreScifi', + horror: 'genreHorror', + thriller: 'genreThriller', + adventure: 'genreAdventure', + fantasy: 'genreFantasy', + mystery: 'genreMystery', + romance: 'genreRomance', + animation: 'genreAnimation', + crime: 'genreCrime', + history: 'genreHistory' + }; + + Object.entries(genreMap).forEach(([value, key]) => { + const option = genreSelect.querySelector(`option[value="${value}"]`); + if (option && t[key]) { + option.textContent = t[key]; + } + }); + + // Update duration labels + const durationSelect = document.getElementById('durationFilter'); + const durationMap = { + any: 'durationAny', + 90: 'duration90', + 120: 'duration120', + 150: 'duration150', + '150+': 'duration150plus' + }; + + Object.entries(durationMap).forEach(([value, key]) => { + const option = durationSelect.querySelector(`option[value="${value}"]`); + if (option && t[key]) { + option.textContent = t[key]; + } + }); + + // Save language + localStorage.setItem('filmora_lang', currentLang); + document.documentElement.lang = currentLang; +} + +// Language switcher +const languageSelect = document.getElementById('languageSelect'); +if (languageSelect) { + languageSelect.value = currentLang; + languageSelect.addEventListener('change', (e) => { + currentLang = e.target.value; + updateTexts(); + }); +} + // ==================== INITIALIZATION ==================== document.addEventListener('DOMContentLoaded', () => { + updateTexts(); showPage('home'); }); diff --git a/style.css b/style.css index acb0cc4..6590da5 100644 --- a/style.css +++ b/style.css @@ -130,6 +130,42 @@ body { border-bottom: 1px solid rgba(255, 255, 255, 0.06); backdrop-filter: blur(10px); position: relative; + flex-wrap: wrap; + gap: 16px; +} + +.header-controls { + display: flex; + align-items: center; + gap: 16px; +} + +.language-select { + padding: 10px 16px; + background: linear-gradient(145deg, var(--bg-card), var(--bg-hover)); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: var(--border-radius-sm); + color: var(--text-primary); + cursor: pointer; + transition: var(--transition); + font-size: 14px; + font-weight: 600; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23f5a623' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 12px center; + background-size: 18px; + padding-right: 40px; +} + +.language-select:hover { + border-color: var(--accent); +} + +.language-select:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 20px var(--accent-glow); } .header::after { @@ -552,6 +588,101 @@ body { pointer-events: none; } +/* Poster Overlay with Watch Button */ +.poster-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: var(--transition); + backdrop-filter: blur(4px); +} + +.movie-poster:hover .poster-overlay { + opacity: 1; +} + +.watch-btn { + display: flex; + align-items: center; + gap: 12px; + padding: 18px 36px; + background: linear-gradient(135deg, var(--accent), var(--accent-hover)); + color: var(--bg-primary); + border-radius: var(--border-radius-sm); + text-decoration: none; + font-weight: 700; + font-size: 18px; + text-transform: uppercase; + letter-spacing: 1px; + transform: translateY(20px); + transition: var(--transition); + box-shadow: 0 8px 32px var(--accent-glow); +} + +.movie-poster:hover .watch-btn { + transform: translateY(0); +} + +.watch-btn:hover { + transform: translateY(-4px) scale(1.05); + box-shadow: 0 16px 48px var(--accent-glow); +} + +.watch-btn span:first-child { + font-size: 24px; +} + +.streaming-info { + padding: 20px; + background: linear-gradient(145deg, rgba(245, 166, 35, 0.08), transparent); + border-radius: var(--border-radius-sm); + margin-top: 8px; +} + +.streaming-info h4 { + font-size: 14px; + color: var(--text-secondary); + margin-bottom: 12px; + text-transform: uppercase; + letter-spacing: 1px; +} + +.streaming-services { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.streaming-service { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + background: rgba(255, 255, 255, 0.05); + border-radius: 20px; + font-size: 13px; + color: var(--text-secondary); + border: 1px solid rgba(255, 255, 255, 0.1); + transition: var(--transition); +} + +.streaming-service:hover { + background: rgba(245, 166, 35, 0.15); + border-color: var(--accent); + color: var(--accent); +} + +.streaming-service .icon { + font-size: 16px; +} + .movie-poster img { width: 100%; height: 100%;