Я построил полноценную образовательную платформу для изучения иврита — с интерактивными тренажерами, умным словарем на 4000+ слов и системой подписок. В статье рассказываю о нетривиальных технических решениях, архитектурных выборах и ошибках, которые пришлось исправлять по ходу.
Демо: hebrewglot.com
Стек: Next.js 15, TypeScript, PostgreSQL + SQLite, Stripe, NextAuth
? Предыстория: почему вообще это началось
Я начал учить иврит и быстро столкнулся с проблемой: хороших онлайн-ресурсов на русском языке почти нет.
Что есть:
Duolingo — поверхностный, не объясняет грамматику
Pealim — отличный сайт для спряжений, но только для спряжений
Ульпаны — дорого ($300-500/месяц) и нужно ездить
Чего не хватало:
Интерактивных упражнений с мгновенной обратной связью
Нормального объяснения системы биньянов (это как виды глаголов, но сложнее)
Словаря с примерами использования в контексте
Всего этого на русском языке без необходимости учить через английский
Я подумал: "А что если сделать самому?" И понеслось...

Прежде чем рассказывать про код, нужно понять специфику языка. Иврит — это не английский. Там все устроено по-другому, и это создает интересные технические челленджи.
Корневая система
В иврите слова строятся из корней (обычно 3 буквы). Представьте, что у вас есть конструктор LEGO, где 3 базовых кубика порождают сотни вариантов.
Корень כ-ת-ב (к-т-б) = "писание"
От него образуются:
כָּתַב (katav) — "писал"
כּוֹתֵב (kotev) — "пишет"
מִכְתָּב (michtav) — "письмо" (объект)
כְּתוֹבֶת (ktovet) — "адрес"
כְּתִיבָה (ktiva) — "письмо" (процесс)
Видите паттерн? К-Т-Б везде, но слова разные. Для программиста это звучит как "идеальная задача для шаблонизации".
Система биньянов
Это породы глаголов — всего их 7 штук. Как спряжения в русском, но намного строже и системнее.
Один и тот же корень в разных биньянах дает разные значения:
Корень ש-ב-ר (ш-б-р) = "ломать"
ПААЛ: שָׁבַר (shabar) — "сломал" (просто действие)
НИФЪАЛЬ: נִשְׁבַּר (nishbar) — "сломался" (пассив/рефлексив)
ПИЭЛЬ: שִׁבֵּר (shiber) — "разбил на куски" (интенсив)
hИФЪИЛЬ: הִשְׁבִּיר (hishbir) — "заставил сломать" (каузатив)
Задача для программиста: Как это всё структурировать и генерировать автоматически? Именно здесь начинается самое интересное.
? Идея и MVP
Что должно было быть в продукте
Минимум (MVP):
✅ Словарь с поиском по русскому/ивриту
✅ Тренажёр для спряжений
✅ Объяснение биньянов с примерами
Хорошо бы добавить:
4. ✅ Уроки для начинающих (от алфавита)
5. ✅ Статистика прогресса
6. ✅ Блог с полезными статьями (для SEO)
7. ✅ Монетизация (чтобы окупить хостинг)
Выбор технологий
Тут я исходил из простого принципа: "Используй то, что знаешь, но не бойся учить новое".
Next.js 15 — потому что:
Server-side rendering для SEO (важно для образовательного контента)
API routes (не нужен отдельный бэкенд)
App Router с Server Components (новая фишка, хотел попробовать в деле)
Большое комьюнити и быстрые решения проблем
TypeScript — потому что:
Иврит имеет жесткую грамматическую структуру
Типобезопасность = меньше багов в морфологии
Автодополнение = быстрее писать сложный код
Рефакторинг становится безопасным
PostgreSQL + SQLite — об этом подробнее ниже, это самое интересное архитектурное решение
Stripe — для подписок:
Простая интеграция
Надежный
Автоматическое управление подписками
?️ Архитектура: необычное решение с двумя базами
Проблема
У меня было два типа данных с совершенно разными характеристиками:
1. Пользовательские данные (меняются часто):
Учетные записи
Статистика прогресса
История ответов
Подписки
~100 записей на пользователя
2. Словарь иврита (статичные, read-only):
4000+ слов
Все формы спряжений (~200 форм на глагол)
Переводы на русский и английский
Примеры использования
Размер: 44 МБ
Проблема: Держать 44 МБ статичных данных в облачной PostgreSQL — это:
? Дорого (облачные БД берут за объем)
? Медленно (network latency при каждом запросе)
? Бессмысленно (данные не меняются)
? Сложно обновлять (нужны миграции для контента)
Решение: гибридная схема

┌─────────────────────────────────────────┐
│ Next.js приложение │
├─────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌───────────────┐ │
│ │ PostgreSQL │ │ SQLite │ │
│ │ (Render) │ │ (локально) │ │
│ ├──────────────┤ ├───────────────┤ │
│ │ Users │ │ Words (4000+) │ │
│ │ Sessions │ │ Roots │ │
│ │ Stats │ │ Conjugations │ │
│ │ Subscriptions│ │ Examples │ │
│ └──────────────┘ └───────────────┘ │
│ ↕ ↕ │
│ Prisma better-sqlite3 │
└─────────────────────────────────────────┘

PostgreSQL (облако) — для всего, что меняется
SQLite (файл в проекте) — для словаря
Создал менеджер баз данных, который прозрачно работает с обеими:
// src/lib/database.ts
export class DatabaseManager {
private sqlite: Database
private prisma: PrismaClient
// Словарь → SQLite (быстро, локально)
async searchWords(query: string, locale: string) {
const stmt = this.sqlite.prepare(`
SELECT * FROM words
WHERE form LIKE ? OR translation LIKE ?
LIMIT 50
`)
return stmt.all(`%${query}%`, `%${query}%`)
}
// Статистика → PostgreSQL (облако)
async saveUserStats(userId: string, stats: any) {
return this.prisma.userStats.upsert({
where: { userId },
update: stats,
create: { userId, ...stats }
})
}
}
Плюсы этого подхода:
⚡ SQLite очень быстрый (нет сети, всё локально, < 1ms)
? Экономия на хостинге (~$20/месяц)
? Деплой проще (SQLite файл в репозитории)
? Prepared statements работают мгновенно
Минусы:
? Нельзя обновлять словарь на лету (нужен редеплой)
Но для образовательного контента это не проблема (обновления редки)
? Морфологический движок: самая сложная часть
Это сердце всей системы. Нужно было научить программу генерировать формы глаголов по правилам.
Задача
Пользователь вводит (или система генерирует):
Корень: כ-ת-ב
Биньян: ПААЛ
Время: прошедшее
Лицо: я (1-е лицо, единственное число)
Программа должна выдать: כָּתַבְתִּי (katavti — "я писал")
И так для всех комбинаций: 7 биньянов × 4 времени × 9 лиц = 252 формы для каждого корня!
Как это работает
Шаг 1: Шаблоны с плейсхолдерами
Для каждого биньяна создал шаблоны:
// src/lib/morph/verbRules.ts
const PAAL_TEMPLATE = {
past: {
'1s': { // 1-е лицо единственное число
template: '{r1}ַ{r2}ְ{r3}ְתִּי',
transcription: '{r1}a{r2}{r3}ti'
},
'3ms': { // 3-е лицо муж.род ед.число
template: '{r1}ַ{r2}ַ{r3}',
transcription: '{r1}a{r2}a{r3}'
}
// ... всего 9 форм для каждого времени
},
present: {
'ms': {
template: '{r1}וֹ{r2}ֵ{r3}',
transcription: '{r1}o{r2}e{r3}'
}
// ...
},
future: { /* ... */ },
imperative: { /* ... */ }
}
Шаг 2: Подстановка корня
function realizeVerb(
root: [string, string, string],
template: string
): string {
const [r1, r2, r3] = root
return template
.replace('{r1}', r1)
.replace('{r2}', r2)
.replace('{r3}', r3)
}
// Пример использования
realizeVerb(['כ', 'ת', 'ב'], '{r1}ַ{r2}ְ{r3}ְתִּי')
// → 'כַתְבְתִּי'
Шаг 3: Обработка особых случаев
Некоторые буквы ведут себя странно (гортанные буквы א, ה, ח, ע):
// Если первая буква корня — א (алеф),
// она "проглатывает" некоторые гласные
if (r1 === 'א' && binyan === 'PAAL') {
// Специальная обработка для слабых корней
template = adjustForWeakRoot(template, 'first')
}
// Если последняя буква — ה (хей),
// она меняется в некоторых формах
if (r3 === 'ה') {
template = adjustForWeakRoot(template, 'last')
}
Результат:
Теперь из одного корня можно сгенерировать все 200+ форм автоматически!
const forms = generateAllForms('כתב', 'PAAL')
// → {
// past: {
// '1s': 'כתבתי',
// '2ms': 'כתבת',
// '3ms': 'כתב',
// // ... всего 9 форм
// },
// present: {
// 'ms': 'כותב',
// 'fs': 'כותבת',
// 'mp': 'כותבים',
// 'fp': 'כותבות'
// },
// future: { /* ... */ },
// imperative: { /* ... */ }
// }
Зачем это нужно
Тренажёр: генерирую вопросы на лету (не нужно хранить все формы)
Словарь: показываю все формы слова динамически
Проверка ответов: сравниваю с правильной формой
Масштабируемость: новый корень = автоматически 200+ слов
? Интернационализация: русский И английский
Здесь я потратил слишком много времени, но узнал важный урок о приоритизации.
Проблема
Хотел поддержать два языка:
?? Русский (основная аудитория)
?? Английский (для масштабирования и международной аудитории)
Простая часть: UI переводы (кнопки, меню)
// src/hooks/useI18n.ts
const translations = {
ru: {
'search': 'Поиск',
'login': 'Войти',
'dictionary': 'Словарь'
},
en: {
'search': 'Search',
'login': 'Login',
'dictionary': 'Dictionary'
}
}
Сложная часть: Контент из базы данных
Слова в БД на иврите и русском. Как их показать англоязычным пользователям?
Решение: многоуровневая система переводов
// src/lib/translationService.ts
// Уровень 1: Кеш (500+ предзаполненных переводов)
const translationCache = new Map([
['учить', 'to learn'],
['писать', 'to write'],
['говорить', 'to speak'],
// ... еще 500+ самых частых слов
])
// Уровень 2: Паттерн-анализ
function translateText(text: string, targetLocale: string): string {
// Проверяем кеш
if (translationCache.has(text)) {
return translationCache.get(text)!
}
// Анализируем паттерны (например, глагольные формы)
if (text.endsWith('ть')) { // русский инфинитив
const englishForm = convertToEnglishInfinitive(text)
return 'to ' + englishForm
}
// Паттерны существительных
if (text.endsWith('ие') || text.endsWith('ость')) {
return handleAbstractNoun(text)
}
// Резервный вариант: показываем как есть
return text
}
Результат: ~100% покрытие переводов без внешних API
Что я узнал (важный урок!)
Ошибка: Потратил месяц на полную локализацию, хотя 70% пользователей русскоязычные.
Правильно было бы:
Запустить только на русском
Собрать аудиторию
Посмотреть метрики
Локализовать по требованию
Урок: Делай интернационализацию, когда она реально нужна, а не "на будущее". YAGNI принцип работает.
? Интерактивные тренажёры
Это то, ради чего всё затевалось. Пассивное чтение учебников не работает — нужна практика.
Типы тренажёров
1. Лёгкий режим: выбор из вариантов (Multiple Choice)
Вопрос: Как будет "я писал" на иврите?
A) כותב
B) כתבתי ← правильный ответ
C) אכתוב
D) כתב
2. Сложный режим: ввод формы (Free Input)
Введите форму глагола כתב (писать)
Время: прошедшее
Лицо: мы
Ваш ответ: [_______]
Правильно: כתבנו
3. Спряжения: полная таблица
Настоящее время (הווה)
он пишет → כותב
она пишет → כותבת
они писали → כתבו
Генерация вопросов
Вопросы генерируются на лету:
// Упрощенный пример генерации вопросов
export async function generateQuestions(locale: string, difficulty: string) {
// Берем случайные слова из БД
const words = await getRandomWords(10)
// Для каждого слова генерируем формы
const questions = words.map(word => {
const forms = generateAllForms(word.root, word.binyan)
// Случайное время и лицо
const tense = randomChoice(['past', 'present', 'future'])
const person = randomChoice(['1s', '2ms', '3fs', '1p'])
const correctAnswer = forms[tense][person]
// Генерируем неправильные варианты
const wrongAnswers = generateDistractors(word, tense, person)
// Формируем вопрос на нужном языке
const question = formatQuestion(word, person, tense, locale)
return {
question,
correctAnswer,
wrongAnswers,
metadata: { tense, person, binyan: word.binyan }
}
})
return questions
}
Статистика и геймификация
После каждого ответа сохраняю результат в двух местах:
Локально (localStorage) — для быстрого доступа:
const stats = {
totalAnswers: 142,
correctAnswers: 98,
wrongAnswers: 44,
accuracy: 69.0,
streak: 5, // дней подряд
lastActiveDate: '2025-11-04'
}
В облаке (PostgreSQL) — для кросс-девайсности:
await prisma.userStats.update({
where: { userId },
data: {
totalWords: { increment: 1 },
correctAnswers: { increment: isCorrect ? 1 : 0 },
wrongAnswers: { increment: isCorrect ? 0 : 1 },
lastActiveDate: new Date(),
streak: calculateStreak(user.lastActiveDate)
}
})
Эффект: Пользователи видят прогресс → мотивация растет → retention улучшается
? Монетизация: Stripe и Premium контент
Мне нужно было как-то окупать хостинг (~$15/мес) и мотивировать себя развивать проект.
Модель: Freemium
Бесплатно (Forever Free):
✅ Базовый словарь (4000 самых частых слов)
✅ Первые 2 урока (алфавит, чтение, базовая грамматика)
✅ Ограниченный тренажёр (10 слов в день)
✅ Объяснение биньянов
Premium ($10/мес):
✨ Полный словарь (4000+ слов)
✨ Все 12 уроков
✨ Неограниченные тренажёры
✨ Статистика и прогресс
✨ Без рекламы
✨ Поддержка проекта
Почему $10? Это дешевле, чем:
Один урок с репетитором ($15-30)
Месяц Duolingo Plus ($13)
Подписка на любой учебник ($20+)
Интеграция Stripe
Шаг 1: Создание Checkout Session
// Пример создания сессии оплаты
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(req: Request) {
const { priceId } = await req.json()
const session = await getCurrentSession()
if (!session?.user) {
return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
// Создаём сессию оплаты
const checkoutSession = await stripe.checkout.sessions.create({
customer_email: session.user.email,
line_items: [{ price: priceId, quantity: 1 }],
mode: 'subscription',
success_url: `${YOUR_DOMAIN}/success`,
cancel_url: `${YOUR_DOMAIN}/pricing`,
metadata: { userId: session.user.id }
})
return Response.json({ url: checkoutSession.url })
}
Шаг 2: Обработка Webhook (критично!)
// Упрощенный пример обработки webhook
export async function POST(req: Request) {
// Проверяем подпись Stripe
const event = await verifyWebhookSignature(req)
if (!event) {
return Response.json({ error: 'Invalid signature' }, { status: 400 })
}
// Обрабатываем события
switch (event.type) {
case 'checkout.session.completed':
await handleSuccessfulPayment(event.data)
break
case 'customer.subscription.updated':
await updateSubscriptionStatus(event.data)
break
case 'customer.subscription.deleted':
await handleCancellation(event.data)
break
}
return Response.json({ received: true })
}
Важно: Всегда проверяйте подписи webhook для защиты от подделки запросов.
Шаг 3: Защита контента (PremiumGate)
// components/PremiumGate.tsx
export async function PremiumGate({
children
}: {
children: ReactNode
}) {
const session = await getServerSession(authOptions)
if (!session) {
return
}
// Проверяем подписку
const subscription = await prisma.subscription.findUnique({
where: { userId: session.user.id }
})
const hasPremium =
subscription?.status === 'active' &&
subscription.currentPeriodEnd > new Date()
if (!hasPremium) {
return (
<div>
<h3>? Premium контент</h3>
<p>Этот урок доступен только в Premium подписке</p>
<button>Получить Premium за $10/мес</button>
</div>
)
}
return <>{children}
}
Использование:
// app/[locale]/lessons/[id]/page.tsx
export default async function LessonPage({ params }) {
const lesson = await getLesson(params.id)
if (lesson.isPremium) {
return (
)
}
return
}
? Блог для SEO: контент-маркетинг
Понял, что просто приложение без контента никто не найдет в поиске. Нужен органический трафик.
Что сделал
Написал 22 статьи на темы, которые люди ищут:
"10 причин начать учить иврит"
"Как выучить иврит с нуля онлайн"
"100 разговорных фраз на каждый день"
"Ошибки начинающих при изучении иврита"
"Как читать на иврите за неделю"
И т.д.
Технология: MDX
MDX = Markdown + React компоненты. Можно вставлять интерактивные элементы прямо в текст статьи.
---
title: "Ошибки начинающих при изучении иврита"
date: "2025-10-28"
description: "10 типичных ошибок и как их избежать"
keywords: ["ошибки иврит", "как учить иврит"]
---
## Ошибка 1: Игнорировать биньяны
**Не делайте так!** Биньяны — это основа всей системы.
**Попробуйте сами прямо сейчас:**
SEO оптимизация
Что сделал для поисковиков:
✅ Semantic HTML (
<article>,<header>,<section>)✅ Meta tags (title, description, keywords)
✅ OpenGraph изображения (автогенерация через DeepAI API)
✅ Structured data (JSON-LD для Google)
✅ Sitemap.xml (автоматический)
✅ robots.txt (правильная индексация)
✅ Быстрая загрузка (Server Components + Image optimization)
Результат:
// Lighthouse Score
{
performance: 94,
accessibility: 98,
bestPractices: 100,
seo: 100
}
? Деплой и продакшен
Для хостинга выбрал облачную платформу с:
Недорогим PostgreSQL tier
Простым деплоем (git push = deploy)
Поддержкой Next.js из коробки
Автоматическими SSL сертификатами
Европейским регионом (ближе к целевой аудитории)
Конфигурация
# config.yaml
services:
- type: web
name: hebrewglot
env: node
region: frankfurt # ближе к целевой аудитории
plan: starter # $7/мес
buildCommand: |
npx prisma generate &&
npx prisma migrate deploy &&
next build
startCommand: npm start
envVars:
- key: DATABASE_URL
fromDatabase:
name: hebrewglot-db
property: connectionString
- key: NEXTAUTH_SECRET
generateValue: true
- key: NEXTAUTH_URL
value: https://hebrewglot.com
databases:
- name: hebrewglot-db
databaseName: hebrewglot
plan: starter # бесплатно (1GB)
CI/CD Pipeline
Процесс деплоя (автоматический):
# 1. Push в репозиторий
git add .
git commit -m "Add new feature"
git push origin main
# 2. Платформа автоматически:
# → Скачивает код
# → Устанавливает зависимости
# → Запускает миграции БД
# → Собирает проект
# → Деплоит новую версию
# → Делает zero-downtime restart
# Время деплоя: ~4-5 минут
? Метрики и результаты
Производительность
Web Vitals:
LCP (Largest Contentful Paint): 1.2s ✅
FID (First Input Delay): 45ms ✅
CLS (Cumulative Layout Shift): 0.02 ✅
Размеры бандлов:
JavaScript: 180KB (gzip) — основной бандл
CSS: 12KB (gzip) — Tailwind
Первая загрузка: < 2s на 3G
Lighthouse Score:
{
"performance": 94,
"accessibility": 98,
"bestPractices": 100,
"seo": 100
}
Технические метрики
Слов в словаре: 4,000+
Корней глаголов: 1,266
Статей в блоге: 22 (RU), 2 (EN)
Уроков: 16
Строк кода: ~15,000
Тестов: 47 (unit + integration)
Типов TypeScript: 150+
? Ошибки, которые я совершил
Честно о том, что пошло не так (и как исправлял).
1. Переусложнил интернационализацию
Что сделал: Потратил месяц на полную поддержку английского с первого дня
Реальность: 70% пользователей русскоязычные, английской версией никто не пользовался первые 3 месяца
Урок: Делай локализацию по требованию, а не "на будущее". Принцип YAGNI (You Aren't Gonna Need It) работает.
Как исправил: Сделал русскую версию основной, английскую — второстепенной. Сэкономил бы месяц работы.
2. Игнорировал мобильную версию в начале
Что сделал: Сначала десктоп во всей красе, потом "адаптируем"
Правильно: Mobile-first подход с самого начала
Почему: 60% пользователей с телефонов, они видели плохой UX
Как исправил: Переделал весь UI с фокусом на мобильные экраны. Заняло еще 2 недели.
3. Не валидировал цену подписки
Что сделал: Установил $10/мес "на глаз", потому что "так у всех"
Правильно: Опросить потенциальных пользователей ДО запуска
Результат: Первые пользователи сказали "дорого для РФ", пришлось делать региональные цены
Как исправил: Добавил опрос, сделал $9 для всего мира.
4. Написал весь код сам (включая морфологию)
Что сделал: Всё с нуля, включая правила спряжений
Правильно: Использовать существующие API или библиотеки (например, Pealim API)
Почему: "Изобретение колеса" заняло 3 недели
Но: Зато теперь у меня 100% контроль, никакой зависимости от внешних сервисов, и работает офлайн. Спорное решение.
5. Недооценил важность контента
Что сделал: Сделал крутую техническую платформу, написал 3 статьи для блога
Правильно: Content is king. Писать статьи параллельно с разработкой
Результат: Первые 2 месяца — 0 органического трафика
Как исправил: Нанял копирайтера, написали 20 статей за месяц. Трафик вырос в 10 раз.
? Что я узнал (главные инсайты)
Технические инсайты
1. SQLite в продакшене — это нормально
Для read-only данных — идеальное решение
Быстрее PostgreSQL для статичного контента (нет network latency)
Проще деплоить (файл в репе)
Меньше точек отказа
2. Next.js App Router готов к продакшену
Server Components реально ускоряют (меньше JS в браузере)
Но документация местами сырая (много trial & error)
Community большое, ответы находятся быстро
3. TypeScript окупается на сложной логике
Морфология без типов = ад и боль
Рефакторинг с типами = безопасно и легко
Обучение команды занимает +2 недели, но оно того стоит
4. Монорепозиторий удобен для малых проектов
Всё в одном месте
Нет проблем с версионированием между фронтом и бэком
Но становится медленным на >50k LOC
5. Prepared statements в SQLite — must have
Защита от SQL injection
В разы быстрее обычных запросов
Кэширование плана выполнения
Бизнес-инсайты
1. SEO > платная реклама (для образовательных проектов)
Одна хорошая статья = 100+ визитов в месяц
Контент живет годами
Google Ads для ниши "иврит" стоит $3+ за клик
2. Freemium работает, если бесплатная часть реально полезна
Дай попробовать продукт
Покажи ценность
5-10% конвертируются в платящих
3. Community > маркетинг
Сообщества в соцсетях дали больше пользователей, чем реклама
Форумы и тематические площадки — бесплатный охват
Главное — не спамить, а помогать
4. Качество > количество
Лучше 12 крутых уроков, чем 50 средненьких
Пользователи ценят глубину
Retention выше у качественного контента
? Что дальше: планы на будущее
Ближайшие месяцы (Q1 2025)
[ ] Мобильное приложение (React Native + Expo)
[ ] AI помощник для составления предложений (OpenAI API)
[ ] Голосовой тренажер (Web Speech API)
[ ] Flashcards система (Spaced Repetition Algorithm)
[ ] Геймификация (достижения, рейтинги, соревнования)
Мечты на будущее (2025-2026)
Расширение на другие семитские языки (арабский, фарси — та же корневая система)
B2B версия для школ и ульпанов
Интеграция с существующими ульпанами (дополнительная домашка)
API для разработчиков (морфология как сервис)
Community features (форум, обмен опытом, языковые партнеры)
? Попробуйте сами
? Демо: hebrewglot.com
Что можно попробовать бесплатно:
? Словарь — поиск по 500 словам + примеры
? Тренажёры — 10 слов в день бесплатно
? Первые 2 урока — от алфавита до базовой грамматики
? Блог — все 22 статьи доступны
Для разработчиков:
Морфологический движок работает прямо в браузере
TypeScript для типобезопасности
Server Components для оптимизации
? Выводы и takeaways
Что сработало отлично:
✅ Гибридная схема БД (PostgreSQL + SQLite) — быстро и дешево
✅ Морфологический движок с шаблонами — генерация форм в runtime
✅ SEO через блог — основной источник трафика
✅ Freemium модель — люди пробуют, потом платят
✅ TypeScript для сложной логики — меньше багов, проще рефакторинг
Что не сработало или было ошибкой:
❌ Слишком ранняя интернационализация — потратил месяц впустую
❌ Desktop-first подход — большинство с мобильных
❌ Игнорирование маркетинга на старте — долго не было пользователей
❌ Изобретение колеса везде — где-то можно было использовать готовые решения
Главный урок для других разработчиков:
> "Делай, ошибайся, учись, итерируй. Идеальный код в вакууме никому не нужен — важен работающий продукт в руках реальных пользователей. Ship early, ship often."
Второй важный урок:
> "Сложная техническая часть (морфология) — это круто и интересно, но без контента, маркетинга и понимания аудитории это просто хобби-проект. Нужен баланс между технологиями и бизнесом."
? Полезные ресурсы
Если хотите повторить похожий проект:
Технологии:
Next.js 15 App Router — официальная документация
Prisma ORM — работа с PostgreSQL
better-sqlite3 — самый быстрый SQLite для Node.js
NextAuth.js — аутентификация
Stripe Docs — подписки и платежи
EdTech:
How to Build an EdTech Platform — Y Combinator
Spaced Repetition Algorithm — для запоминания
Если изучаете иврит:
HebrewGlot — моя платформа (можете попробовать)
Pealim — отличный справочник по спряжениям
Reverso Context — примеры использования слов
? Вопросы и обратная связь
Буду рад ответить на вопросы в комментариях!
Интересные темы для обсуждения:
Как вы решаете проблему хранения больших статичных данных?
Кто-нибудь еще использует SQLite в продакшене?
Делали ли вы морфологические движки для других языков?
Какие у вас опыт с монетизацией EdTech проектов?
Контакты:
Демо: hebrewglot.com
Статья написана: 4 ноября 2025
Комментарии (10)

PEACE_dez
06.11.2025 16:53просмотрел сайт "по диагонали" (пока что)
коммент из серии "а почему противоракетный обтекатель на покрашен?!" :у вас выбор языка ( РУсский правда не работает) обозначен "GB"
Корректнее (и понятне) было как везде принято просто "EN"(glish)
Overphase
06.11.2025 16:53Именно. GB - это страна, а не язык.
Автору (Роману): коды стран и коды языков - это разные вещи, и на них есть стандарты: первое ISO 3166, второе ISO 639

DmitryO
06.11.2025 16:53Интересно, спасибо! Вопрос: я правильно понимаю, что better-sqlite3 используете как persistent local storage? Можно чуть подробнее, как это работает? Как на клиенте появляется Sqlite?

KohINoor
06.11.2025 16:53Теперь из одного корня можно сгенерировать все 200+ форм автоматически!
В том числе несуществующие? Не все корни имеют соответствующие слова во всех биньянах.

Overphase
06.11.2025 16:53Все ли гзарот обрабатываются? В частности, им посвящена большая глава номер 2 в учебнике, упомянутом выше в другом комментарии ("Иврит через мозг")

AnthonyDS
06.11.2025 16:53
Над UI нужно ещё поработать...
Отступы
Скругления
Текст кнопки в одно слово
Межстрочный интервал в заголовке +1..2px
Diacut
Лет 20-25 назад был сайтик для изучения иврита. Очень было красиво сделано. Вся эта глагольная система объяснена, описана, структурирована итд итп. И бесплатно. Жаль, не помню имя автора. Поискал, не нашёл. Наверное, уже и нет его, сайтика. Помню только, что жёлтенький был. И, главное, никакого жаваскрипта!
Да, словарь есть отдельно, ИРИС Подольского. Очень удобный.
flatlandianin
розетта-стоун наверное и там три десятка языков и ещё отдельно софтом аналог ходил на 26 языков с анимацией рото-глотки
Overphase
Иврит через мозг https://ivrita.net/pravila.htm
Великолепный учебник не только по глаголам, но и по грамматике в целом. Но без лексики.