# План разработки лендинга для Voice-to-Text ## Обзор проекта **Voice-to-Text** — десктопное приложение для преобразования голоса в текст с фокусом на приватность и офлайн-поддержку. Построено на Tauri, Rust и Vue 3. ### Ключевые особенности приложения: - 🎤 **Real-time транскрипция** с поддержкой нескольких провайдеров (Deepgram, AssemblyAI, Whisper) - 🔒 **Privacy-focused** — API ключи хранятся локально, нет облачного хранилища - 🌍 **Кроссплатформенность** — macOS, Windows, Linux - ⚡ **Глобальные хоткеи** — быстрый доступ через горячие клавиши - 📋 **Автоматическое копирование** в буфер обмена - 🎨 **Современный UI** с glass morphism эффектами - 🌐 **Мультиязычность** — поддержка 6 языков (en, ru, es, fr, de, uk) --- ## Технологический стек ### Основные технологии: - **Nuxt 3** — фреймворк для Vue.js с SSR/SSG - **Vuetify 3** — Material Design компоненты - **Vue I18n** — локализация (совместимо с текущим `vue-i18n`) - **TypeScript** — типизация - **Vite** — сборщик (встроен в Nuxt) - **Pinia** — управление общим состоянием (через `@pinia/nuxt`) ### Дополнительные библиотеки: - **@nuxtjs/i18n** — интеграция i18n с Nuxt - **@nuxtjs/seo** — SEO оптимизация - **@vueuse/nuxt** — композаблы для Vue (например `usePreferredDark`) - **nuxt-icon** — иконки - **@nuxtjs/ipx** — обработка изображений (опционально) - **swiper** — карусель скриншотов (Vue интеграция) - **@nuxt/eslint** + **Prettier** — линтинг и форматирование кода Примечания по зависимостям: - Google Fonts лучше не подключать как внешнюю зависимость. Либо системный шрифт, либо self-host (проще с приватностью и стабильностью). --- ## Архитектура проекта ``` landing/ ├── data/ # статические данные/конфиги секций (без Nuxt контекста) ├── types/ # TS типы (без Nuxt контекста) ├── utils/ # чистые функции (без Nuxt контекста, легко тестировать) ├── nuxt.config.ts # Конфигурация Nuxt ├── package.json ├── tsconfig.json ├── .env # Переменные окружения │ ├── locales/ # Файлы локализации │ ├── en.json │ ├── ru.json │ ├── es.json │ ├── fr.json │ ├── de.json │ └── uk.json │ ├── assets/ # Статические ресурсы │ ├── images/ │ │ ├── hero-bg.jpg │ │ ├── screenshot-dark.png │ │ ├── screenshot-light.png │ │ ├── features/ │ │ └── platforms/ │ ├── videos/ # Демо-видео (опционально) │ └── styles/ │ └── main.scss # Глобальные стили │ ├── components/ # Vue компоненты │ ├── layout/ │ │ ├── AppHeader.vue # Шапка с навигацией │ │ ├── AppFooter.vue # Подвал │ │ └── LanguageSwitcher.vue # Переключатель языков │ │ │ ├── sections/ │ │ ├── HeroSection.vue # Главный экран │ │ ├── FeaturesSection.vue # Особенности │ │ ├── ProvidersSection.vue # STT провайдеры │ │ ├── ScreenshotsSection.vue # Скриншоты │ │ ├── DownloadSection.vue # Секция загрузки │ │ ├── PrivacySection.vue # Приватность │ │ └── FAQSection.vue # Частые вопросы │ │ │ ├── ui/ │ │ ├── DownloadButton.vue # Кнопка загрузки │ │ ├── FeatureCard.vue # Карточка фичи │ │ ├── PlatformBadge.vue # Бейдж платформы │ │ └── ScreenshotCarousel.vue # Карусель скриншотов │ │ │ └── common/ │ ├── AppLogo.vue │ └── ThemeToggle.vue # Переключатель темы (если нужен) │ ├── composables/ # Композаблы │ ├── useDownload.ts # Логика загрузки │ ├── useAnalytics.ts # Аналитика (опционально) │ ├── usePlatform.ts # Определение платформы │ ├── useBrowserTheme.ts # Определение темы браузера │ └── useLocation.ts # Определение локации пользователя │ ├── layouts/ │ └── default.vue # Основной layout │ ├── plugins/ # Плагины Nuxt │ ├── vuetify.ts # Инициализация Vuetify │ └── init-theme-locale.client.ts # Автоинициализация темы и локали │ ├── pages/ │ ├── index.vue # Главная страница │ ├── download.vue # Страница загрузки │ └── privacy.vue # Политика приватности (опционально) │ ├── public/ # Публичные файлы │ ├── favicon.ico │ ├── robots.txt │ └── sitemap.xml │ └── server/ # Server API (если нужен) ``` --- ## Правила разделения логики - **composables/**: завязаны на Nuxt/Vue (реактивность, `useCookie`, `navigateTo`, `useRoute`, `useFetch`, `useHead` и т.д.) - **utils/**: чистые функции без Nuxt контекста (легко тестировать, переиспользовать) ## Структура страниц ### 1. Главная страница (`/`) #### Hero Section - **Заголовок**: "Voice to Text — Privacy-Focused Transcription" - **Подзаголовок**: Краткое описание приложения - **CTA кнопки**: - "Download for [Platform]" (определяется автоматически) - "View Features" (скролл к секции) - **Фоновое изображение/видео**: Демонстрация приложения #### Features Section **6 основных фич в виде карточек:** 1. **Real-time Transcription** - Иконка: 🎤 - Описание: Мгновенная транскрипция с частичными результатами 2. **Privacy-Focused** - Иконка: 🔒 - Описание: API ключи хранятся локально, нет облачного хранилища 3. **Multiple Providers** - Иконка: 🌐 - Описание: Deepgram, AssemblyAI, Whisper (офлайн) 4. **Cross-Platform** - Иконка: 💻 - Описание: macOS, Windows, Linux 5. **Global Hotkeys** - Иконка: ⌨️ - Описание: Быстрый доступ через горячие клавиши 6. **Auto-Copy** - Иконка: 📋 - Описание: Автоматическое копирование в буфер обмена #### Providers Section **Детальное описание STT провайдеров:** - **Deepgram** (Nova-2/3) - Низкая задержка - Высокое качество - Автоматический выбор модели (Nova-3 для английского, Nova-2 для русского) - **AssemblyAI** (Universal-Streaming v3) - Высокое качество - Облачный сервис - **Whisper Local** (офлайн) - Полностью офлайн - Требует cmake и загрузки модели #### Screenshots Section **Карусель скриншотов (Swiper):** - Используем Swiper для Vue: `https://swiperjs.com/vue` - На **десктопе** должно быть видно **сразу несколько** скриншотов на экране (не один с обязательным “далее”) - Пример: `slidesPerView: 2-4` на широких экранах с `breakpoints` - Допускается режим с “частичным” превью следующего слайда - На мобильных: 1 скрин, свайп, пагинация - Набор скринов: - Темная тема - Светлая тема - Настройки - Процесс записи #### Download Section **Платформо-специфичные кнопки загрузки с автоопределением ОС:** - Определяем текущую ОС пользователя и показываем **приоритетно** релевантную загрузку - Если ОС определить не удалось — показываем **все** ОС - Для macOS учитывать архитектуру (Apple Silicon vs Intel): - Если получилось определить — выбираем нужную сборку по умолчанию - Если нет — показываем оба варианта и даём выбрать вручную **Дополнительно:** - Версия приложения - Размер файла - Системные требования - Changelog ссылка #### Privacy Section **Ключевые моменты приватности:** - Локальное хранение API ключей - Нет облачного хранилища транскрипций - Опциональное использование собственных API ключей **Open Source:** - Да: часть компонентов/модулей планируем сделать open-source (для маркетинга и доверия) #### FAQ Section **Частые вопросы:** 1. Какие платформы поддерживаются? 2. Нужен ли интернет для работы? 3. Как настроить API ключи? 4. Можно ли использовать офлайн? 5. Как изменить горячие клавиши? 6. Безопасны ли мои данные? ### 2. Страница загрузки (`/download`) - **Определение платформы** автоматически - **Кнопки загрузки** для всех платформ - **Инструкции по установке** для каждой ОС - **Системные требования** - **Changelog** (последние версии) --- ## Локализация (i18n) ### Поддерживаемые языки: - 🇺🇸 English (`en`) - 🇷🇺 Русский (`ru`) - 🇪🇸 Español (`es`) - 🇫🇷 Français (`fr`) - 🇩🇪 Deutsch (`de`) - 🇺🇦 Українська (`uk`) ### Структура файлов локализации: ```json // locales/en.json { "meta": { "title": "Voice to Text - Privacy-Focused Transcription", "description": "..." }, "nav": { "features": "Features", "download": "Download", "privacy": "Privacy" }, "hero": { "title": "Voice to Text", "subtitle": "Privacy-focused voice-to-text application with offline support", "download": "Download for {platform}", "viewFeatures": "View Features" }, "features": { "realtime": { "title": "Real-time Transcription", "description": "..." }, "privacy": { "title": "Privacy-Focused", "description": "..." } // ... остальные фичи }, "providers": { "title": "Multiple STT Providers", "deepgram": { "name": "Deepgram", "description": "..." } // ... остальные провайдеры }, "download": { "title": "Download", "forPlatform": "Download for {platform}", "systemRequirements": "System Requirements", "version": "Version {version}" }, "faq": { "title": "Frequently Asked Questions", "items": [ { "question": "...", "answer": "..." } ] } } ``` ### Модель контента (чтобы i18n не превращалась в ад) Проблема классическая: если хранить “структуру секций” в `data/*`, а весь контент раскидать по ключам i18n, то любая правка превращается в квест “найди 20 ключей в 6 языках”. Решение — разделить два слоя: - **Микрокопирайт** (кнопки, лейблы, мелкие подписи) — остаётся в `landing/locales/*`. - **Контент секций** (FAQ, список фич, провайдеры, тексты блоков) — лежит в **локализованных контент-файлах** с одинаковой структурой по всем языкам. Рекомендуемая схема: - `landing/content/en.ts`, `landing/content/ru.ts`, ... (или `.json`, если удобнее). - Внутри — один типизированный объект `LandingContent`, где: - элементы имеют **стабильные `id`** (например `faq.items[].id`, `features.items[].id`) - порядок можно менять без рефакторинга компонентов - длинные тексты редактируются “в одном месте” для каждой локали Минимальные правила дисциплины: - `id` неизменяемы (меняем текст, но не идентификатор). - Если добавили/удалили элемент — правим **все локали** (это легко проверяется автоматикой). - Для контента не используем “вложенные ключи на 10 уровней”, держим структуру простой и читаемой. Проверка качества: - Добавляем маленькую проверку (скрипт/тест), которая сравнивает структуру контента между локалями и падает, если где-то не хватает ключей/элементов. Минимальный “контракт” этой проверки: - сравниваем структуру (ключи/массивы по `id`), а не тексты - ошибка должна показывать, **какой `id`/ключ отсутствует** и **в какой локали** - проверка запускается локально и в CI (чтобы не ловить это уже на проде) ### Настройка i18n в Nuxt: ```typescript // nuxt.config.ts export default defineNuxtConfig({ modules: [ '@nuxtjs/i18n', 'vuetify/nuxt' ], i18n: { locales: [ { code: 'en', iso: 'en-US', file: 'en.json', name: 'English' }, { code: 'ru', iso: 'ru-RU', file: 'ru.json', name: 'Русский' }, { code: 'es', iso: 'es-ES', file: 'es.json', name: 'Español' }, { code: 'fr', iso: 'fr-FR', file: 'fr.json', name: 'Français' }, { code: 'de', iso: 'de-DE', file: 'de.json', name: 'Deutsch' }, { code: 'uk', iso: 'uk-UA', file: 'uk.json', name: 'Українська' } ], defaultLocale: 'en', strategy: 'prefix_except_default', detectBrowserLanguage: { useCookie: true, cookieKey: 'i18n_redirected', redirectOn: 'root', // Определение языка по локации (через composable) alwaysRedirect: false, fallbackLocale: 'en' } } }) ``` ### Автоматическое определение языка по локации: Логика определения языка: 1. **Проверка cookie** — если пользователь уже выбирал язык, использовать его 2. **Определение по браузеру** — `navigator.language` или `navigator.languages` 3. **Fallback** — английский язык по умолчанию Важно: - Лендинг планируется как **SSG (статический)**. Значит IP-геолокация на сервере здесь неуместна: негде “серверу” исполняться на каждый запрос. - Если когда-то понадобится geo-редирект — это отдельная задача (edge/runtime), не часть текущего лендинга. **Маппинг стран к языкам:** - 🇷🇺 Россия, Беларусь, Казахстан → `ru` - 🇺🇦 Украина → `uk` - 🇪🇸 Испания, Латинская Америка → `es` - 🇫🇷 Франция, Бельгия, Швейцария (французский) → `fr` - 🇩🇪 Германия, Австрия, Швейцария (немецкий) → `de` - 🇺🇸 Остальные → `en` --- ## Компоненты Vuetify ### Используемые компоненты: 1. **v-app-bar** — шапка сайта 2. **v-container**, **v-row**, **v-col** — сетка 3. **v-card** — карточки фич 4. **v-btn** — кнопки 5. **v-carousel** — карусель скриншотов 6. **v-expansion-panels** — FAQ аккордеон 7. **v-chip** — бейджи платформ 8. **v-select** — выбор языка 9. **v-icon** — иконки 10. **v-divider** — разделители ### Кастомизация темы: ```typescript // plugins/vuetify.ts import { createVuetify } from 'vuetify' export default defineNuxtPlugin((nuxtApp) => { // Определение темы браузера при инициализации const getInitialTheme = (): 'dark' | 'light' => { if (process.client) { // Проверка сохраненной темы в localStorage const savedTheme = localStorage.getItem('vuetify-theme') if (savedTheme === 'dark' || savedTheme === 'light') { return savedTheme } // Определение по системным настройкам браузера if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { return 'dark' } } return 'light' // Fallback для SSR } const vuetify = createVuetify({ theme: { defaultTheme: getInitialTheme(), themes: { dark: { colors: { primary: '#6366f1', // indigo secondary: '#8b5cf6', // purple accent: '#ec4899', // pink background: '#0f172a', // slate-900 surface: '#1e293b', // slate-800 } }, light: { colors: { primary: '#6366f1', secondary: '#8b5cf6', accent: '#ec4899', background: '#ffffff', surface: '#f8fafc', // slate-50 } } } } }) // Слушатель изменений системной темы if (process.client) { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') const handleThemeChange = (e: MediaQueryListEvent) => { const savedTheme = localStorage.getItem('vuetify-theme') // Автоматически менять только если пользователь не выбирал тему вручную if (!savedTheme) { vuetify.theme.global.name.value = e.matches ? 'dark' : 'light' } } mediaQuery.addEventListener('change', handleThemeChange) } nuxtApp.vueApp.use(vuetify) }) ``` --- ## SEO оптимизация ### Meta теги: ```vue ``` ### Структурированные данные (JSON-LD): ```typescript // composables/useStructuredData.ts export const useStructuredData = () => { const { $i18n } = useNuxtApp() const softwareApplication = { "@context": "https://schema.org", "@type": "SoftwareApplication", "name": "Voice to Text", "applicationCategory": "UtilityApplication", "operatingSystem": ["macOS", "Windows", "Linux"], "offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" } } return { softwareApplication } } ``` --- ## Функциональность ### 1. Определение платформы ```typescript // composables/usePlatform.ts export const usePlatform = () => { const platform = ref<'macos' | 'windows' | 'linux' | 'unknown'>('unknown') if (process.client) { const userAgent = navigator.userAgent.toLowerCase() if (userAgent.includes('mac')) platform.value = 'macos' else if (userAgent.includes('win')) platform.value = 'windows' else if (userAgent.includes('linux')) platform.value = 'linux' } const downloadUrl = computed(() => { // Логика формирования URL для загрузки const baseUrl = 'https://github.com/777genius/voice-to-text/releases' // ... }) return { platform, downloadUrl } } ``` ### 2. Определение темы браузера ```typescript // composables/useBrowserTheme.ts import { usePreferredDark } from '@vueuse/core' import { useTheme } from 'vuetify' export const useBrowserTheme = () => { const { $vuetify } = useNuxtApp() const preferredDark = usePreferredDark() const theme = useTheme() // Инициализация темы при первом посещении const initTheme = () => { if (process.client) { const savedTheme = localStorage.getItem('vuetify-theme') if (savedTheme) { // Использовать сохраненную тему theme.global.name.value = savedTheme as 'dark' | 'light' } else { // Использовать системную тему браузера theme.global.name.value = preferredDark.value ? 'dark' : 'light' localStorage.setItem('vuetify-theme', theme.global.name.value) } } } // Переключение темы const toggleTheme = () => { const newTheme = theme.global.name.value === 'dark' ? 'light' : 'dark' theme.global.name.value = newTheme localStorage.setItem('vuetify-theme', newTheme) } // Установка конкретной темы const setTheme = (themeName: 'dark' | 'light') => { theme.global.name.value = themeName localStorage.setItem('vuetify-theme', themeName) } // Слушатель изменений системной темы (только если пользователь не выбирал вручную) if (process.client) { watch(preferredDark, (isDark) => { const savedTheme = localStorage.getItem('vuetify-theme') if (!savedTheme) { theme.global.name.value = isDark ? 'dark' : 'light' } }) } return { currentTheme: computed(() => theme.global.name.value), isDark: computed(() => theme.global.name.value === 'dark'), initTheme, toggleTheme, setTheme } } ``` ### 3. Определение локации пользователя ```typescript // composables/useLocation.ts export const useLocation = () => { const { $i18n } = useNuxtApp() const locale = useCookie('i18n_redirected', { default: () => 'en' }) // Маппинг стран к языкам const countryToLocale: Record = { 'RU': 'ru', // Россия 'BY': 'ru', // Беларусь 'KZ': 'ru', // Казахстан 'UA': 'uk', // Украина 'ES': 'es', // Испания 'MX': 'es', // Мексика 'AR': 'es', // Аргентина 'CO': 'es', // Колумбия 'CL': 'es', // Чили 'PE': 'es', // Перу 'FR': 'fr', // Франция 'BE': 'fr', // Бельгия (французский) 'CH': 'de', // Швейцария (по умолчанию немецкий, можно улучшить) 'DE': 'de', // Германия 'AT': 'de', // Австрия } // Определение языка по браузеру const getBrowserLocale = (): string => { if (process.client) { const browserLang = navigator.language || (navigator as any).userLanguage const langCode = browserLang.split('-')[0].toLowerCase() // Проверка поддерживаемых языков const supportedLocales = ['en', 'ru', 'es', 'fr', 'de', 'uk'] if (supportedLocales.includes(langCode)) { return langCode } // Проверка полного кода (например, ru-RU) const fullCode = browserLang.toLowerCase() if (fullCode.startsWith('ru')) return 'ru' if (fullCode.startsWith('uk')) return 'uk' if (fullCode.startsWith('es')) return 'es' if (fullCode.startsWith('fr')) return 'fr' if (fullCode.startsWith('de')) return 'de' } return 'en' } // Важно: лендинг статический (SSG), поэтому IP-геолокацию не используем. // Инициализация языка при первом посещении const initLocale = async () => { // Если язык уже выбран пользователем, не менять if (locale.value && locale.value !== 'en') { return } // Приоритет: cookie > браузер > fallback let detectedLocale = locale.value || 'en' // На клиенте определяем по браузеру detectedLocale = getBrowserLocale() // Устанавливаем язык только если он отличается от текущего if (detectedLocale !== $i18n.locale.value) { const { switchLocalePath } = useI18n() await navigateTo(switchLocalePath(detectedLocale)) } } return { currentLocale: computed(() => $i18n.locale.value), initLocale, getBrowserLocale } } ``` ### 4. Плагин для автоматической инициализации ```typescript // plugins/init-theme-locale.client.ts export default defineNuxtPlugin(async () => { const { initTheme } = useBrowserTheme() const { initLocale } = useLocation() // Инициализация темы initTheme() // Инициализация локали (только при первом посещении) const hasVisited = useCookie('has_visited', { default: () => false }) if (!hasVisited.value) { await initLocale() hasVisited.value = true } }) ``` ### 5. Статистика загрузок (опционально) Не делаем. ### 6. Аналитика (опционально) ```typescript // composables/useAnalytics.ts export const useAnalytics = () => { const trackDownload = (platform: string) => { if (process.client) { // Google Analytics, Plausible, или другая аналитика gtag('event', 'download', { platform }) } } return { trackDownload } } ``` **Решение по аналитике:** - Делаем **GA4** (Google Analytics) + события: - `download_click` (platform, arch, version, locale) - `download_page_view` - `language_change` - `theme_change` - `faq_open` - `cta_view_features_click` - Для подключения нужен `GA4 Measurement ID` (например, `G-XXXXXXXXXX`) и/или доступы к аккаунту. В коде предусматриваем конфиг через env, а фактическое подключение выполняется владельцем аккаунта. --- ## Дизайн и стилизация ### Цветовая схема: **Темная тема:** - Фон: `#0f172a` (slate-900) - Поверхности: `#1e293b` (slate-800) - Акцент: `#6366f1` (indigo-500) - Текст: `#f1f5f9` (slate-100) **Светлая тема:** - Фон: `#ffffff` - Поверхности: `#f8fafc` (slate-50) - Акцент: `#6366f1` (indigo-500) - Текст: `#0f172a` (slate-900) ### Типографика: - **Заголовки**: Inter или System Font Stack - **Текст**: Inter или System Font Stack - **Моноширинный**: JetBrains Mono (для кода) ### Анимации: - Плавные переходы при скролле - Hover эффекты на карточках - Параллакс для hero секции (опционально) --- ## Деплой ### Рекомендуемые платформы: 1. **Vercel** (рекомендуется) - Автоматический деплой из Git - SSR/SSG поддержка - CDN по умолчанию 2. **Netlify** - Аналогично Vercel - Хорошая поддержка Nuxt 3. **GitHub Pages** (только SSG) - Бесплатный хостинг - Требует `nuxt generate` ### Target деплой: - **Render** - Режим: **Static Site Generation (SSG)** (статическая генерация) ### Конфигурация для деплоя: ```typescript // nuxt.config.ts export default defineNuxtConfig({ // Для SSG (пререндер страниц, а не SPA): ssr: true, nitro: { preset: 'static' }, routeRules: { '/': { prerender: true }, '/download': { prerender: true }, }, }) ``` ### Важно про i18n + SSG (критично для SEO) `routeRules` выше — это только базовый пример. Для i18n нужно гарантировать, что **пререндерятся все локали**. Правило: - Источник правды — список локалей + список страниц. - На их основе формируем список путей для пререндера и для sitemap. Пример логики (идея, не финальный код): - Локали: `['en', 'ru', 'es', 'fr', 'de', 'uk']`, default = `en` - Страницы: `['/', '/download']` - Генерация путей: - для `en`: `['/', '/download']` - для остальных: `['/ru', '/ru/download']`, `['/es', '/es/download']`, ... Это решает сразу две проблемы: - не теряем локали при деплое (все страницы реально существуют как статик) - sitemap/alternate можно генерировать из той же таблицы См. также: `landing/docs/ARCHITECTURE_GUARDRAILS.md` (раздел про “источник правды по URL”). --- ## Чеклист разработки ### Фаза 1: Настройка проекта - [ ] Инициализация Nuxt 3 проекта - [ ] Установка Vuetify 3 - [ ] Настройка TypeScript - [ ] Настройка Pinia (`@pinia/nuxt`) и базовых stores (общие состояния не держим “размазанными” по компонентам) - [ ] Настройка i18n с определением локации - [ ] Настройка Vuetify с определением темы браузера - [ ] Установка @vueuse/nuxt для usePreferredDark - [ ] ESLint (`@nuxt/eslint`) + Prettier (единый стиль форматирования) - [ ] Базовая структура папок ### Фаза 2: Layout и навигация - [ ] Создание `default.vue` layout - [ ] Компонент `AppHeader.vue` с навигацией - [ ] Компонент `AppFooter.vue` - [ ] Компонент `LanguageSwitcher.vue` - [ ] Адаптивная навигация (мобильное меню) ### Фаза 3: Главная страница - [ ] Hero Section - [ ] Features Section (6 карточек) - [ ] Providers Section - [ ] Screenshots Section (карусель) - [ ] Download Section - [ ] Privacy Section - [ ] FAQ Section ### Фаза 4: Локализация и тема - [ ] Переводы для всех 6 языков - [ ] Композабл `useBrowserTheme.ts` для определения темы - [ ] Композабл `useLocation.ts` для определения локации - [ ] Плагин `init-theme-locale.client.ts` для автоинициализации - [ ] Маппинг стран к языкам - [ ] Определение языка по браузеру (client-side) - [ ] Определение темы по системным настройкам - [ ] Сохранение выбора пользователя в cookies/localStorage - [ ] Тестирование переключения языков и темы - [ ] SEO мета-теги для каждого языка - [ ] Правильные URL для каждого языка ### Фаза 5: Функциональность - [ ] Определение платформы пользователя - [ ] Кнопки загрузки с правильными ссылками - [ ] Страница `/download` с инструкциями - [ ] Аналитика (GA4) + события (скачивание, смена языка/темы, FAQ, CTA) ### Фаза 6: Оптимизация - [ ] Оптимизация изображений - [ ] Lazy loading для секций - [ ] SEO оптимизация - [ ] Структурированные данные - [ ] Sitemap и robots.txt ### Фаза 7: Тестирование - [ ] Тестирование на разных устройствах - [ ] Тестирование всех языков - [ ] Тестирование автоматического определения языка по браузеру - [ ] Тестирование автоматического определения темы - [ ] Тестирование переключения темы вручную - [ ] Тестирование сохранения выбора пользователя - [ ] Проверка производительности - [ ] Проверка доступности (a11y) ### Фаза 8: Деплой - [ ] Настройка CI/CD - [ ] Деплой на выбранную платформу - [ ] Настройка домена - [ ] SSL сертификат --- ## Дополнительные рекомендации ### Определение темы и локации: - **Приоритет определения языка:** 1. Cookie (если пользователь уже выбирал) 2. Браузерные настройки (`navigator.language`) 3. Fallback на английский - **Приоритет определения темы:** 1. localStorage (если пользователь выбирал вручную) 2. Системные настройки браузера (`prefers-color-scheme`) 3. Fallback на светлую тему - **Альтернативные API для геолокации:** - Не используем в рамках текущего статического лендинга (SSG). - **Обработка ошибок:** - Таймаут для API запросов (3 секунды) - Graceful fallback на браузерные настройки - Логирование ошибок для отладки ### Производительность: - Использовать `nuxt/image` для оптимизации изображений - Lazy loading для компонентов ниже fold - Code splitting для больших компонентов - Никаких внешних geo-запросов: меньше точек отказа и проще с приватностью. ### Доступность: - Семантический HTML - ARIA атрибуты - Keyboard navigation - Контрастность цветов (WCAG AA) ### Аналитика: - Google Analytics 4 - Plausible (privacy-focused) - Yandex Metrika (для русскоязычной аудитории) ### Мониторинг: - Sentry для отслеживания ошибок - Uptime monitoring --- ## Примеры реализации компонентов ### Hero Section ```vue ``` ### Feature Card ```vue ``` --- ## Контакты и ресурсы - **GitHub**: https://github.com/777genius/voice-to-text - **Документация**: (если есть) - **Поддержка**: (если есть) --- **Версия плана**: 1.0 **Дата создания**: 2025-01-17 **Статус**: Готов к реализации --- ## План итераций (сначала планируем, затем перепроверяем, потом реализуем) Процесс: 1) Сначала готовим максимально подробные планы итераций. 2) Затем **несколько раз** перепроверяем планы (полнота, несостыковки, риски, критерии готовности). 3) Только после этого начинаем реализацию пошагово. 4) После реализации — сверяемся с планами и ещё раз перепроверяем соответствие. Файлы итераций: - `landing/docs/iterations/ITERATION_00_REQUIREMENTS.md` - `landing/docs/iterations/ITERATION_01_SCAFFOLDING.md` - `landing/docs/iterations/ITERATION_02_UI_SECTIONS.md` - `landing/docs/iterations/ITERATION_03_SWIPER_AND_DOWNLOAD.md` - `landing/docs/iterations/ITERATION_04_ANALYTICS_SEO_SSG_RENDER.md`