# План разработки лендинга для 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
{{ $t('meta.title') }}
```
### Структурированные данные (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
{{ $t('hero.title') }}
{{ $t('hero.subtitle') }}
{{ $t('hero.viewFeatures') }}
```
### Feature Card
```vue
{{ title }}
{{ description }}
```
---
## Контакты и ресурсы
- **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`