Skip to content

Repository files navigation

Sanakota

CI CodeQL Status License Node pnpm

中文说明

A Finnish learning dictionary that turns lookup into study.

Sanakota helps learners move from a real Finnish form in the wild to an understood, saved, and reviewable sense. It is built for inflected forms, compounds, phrases, partial understanding, and bilingual learner workflows instead of assuming that every search starts with a clean dictionary headword.

Sanakota search-first home screen

Contents

Why Sanakota

Most language tools separate the lookup moment from the learning loop.

A dictionary can explain a word, but it often does not preserve which sense mattered in context, how the observed surface form maps back to a lemma, or whether the result should become study material. A flashcard tool can help later, but usually starts after the original reading or listening moment has already disappeared.

Sanakota is organized around a tighter loop:

search -> understand -> save -> review

Current Scope

Sanakota is currently a pnpm monorepo for a search-first Finnish learning dictionary. The active scope is intentionally focused:

  • Learner lookup: lemma search, generated surface forms, close matches, compounds, collocations, constructions, and staged resolution through a dedicated dictionary service.
  • Sense-level study: saved learning state is attached to senses rather than flattening a whole lexeme into one card.
  • Bilingual presentation: learner-facing UI and dictionary payloads support English and Simplified Chinese, with persistent language selection in the web app.
  • Dictionary-first data model: source imports, canonical dictionary rows, runtime releases, generated fallback payloads, and promotion workflows are kept as separate layers.
  • Open development workflow: CI, CodeQL, Dependabot, issue templates, contribution guidance, security policy, and a root verification command are included.

Sanakota is not yet a production SaaS, a full Anki/FSRS replacement, a native mobile app, or a general-purpose Finnish NLP platform.

Architecture

Sanakota models dictionary knowledge around SurfaceForm -> Lexeme -> Sense. The surface form is what a learner saw or typed, the lexeme is the lemma-level entry, and the sense is the unit that can be understood, saved, reviewed, and eventually measured.

flowchart TB
  browser["Learner browser"]

  subgraph frontend["apps/web"]
    web["React + Vite PWA\nsearch, detail, library, review"]
  end

  subgraph runtime["Runtime services"]
    api["apps/api\nAuth, study state, preferences,\nlegacy dictionary/search surfaces"]
    dictionary["apps/dictionary-service\nLookup, suggest, fallback jobs,\npromotion workflows"]
  end

  subgraph data["Data stores"]
    appdb[("sanakota_db\nusers, saved senses,\nsource imports")]
    dictdb[("sanakota_dictionary\nversioned releases,\nentry payloads, fallback jobs")]
    meili[("Meilisearch\nrelease-scoped resolution index")]
  end

  subgraph build["Dictionary build path"]
    imports["apps/api scripts\nFinnWordNet/Kielitoimisto import,\nmorphology, materialization"]
    pipeline["packages/dictionary-pipeline\npublish release and index docs"]
  end

  providers["Optional fallback providers\nfixture, OpenAI, DeepSeek"]
  shared["packages/shared\ncontracts, constants, types"]

  browser --> web
  web -->|auth, saved senses, study preferences| api
  web -->|lookup, suggest, fallback status| dictionary
  api --> appdb
  dictionary --> dictdb
  dictionary --> meili
  dictionary --> providers
  imports --> appdb
  imports -->|normalized release inputs| pipeline
  pipeline --> dictdb
  pipeline --> meili
  web -.-> shared
  api -.-> shared
  dictionary -.-> shared
  pipeline -.-> shared
Loading

Design boundaries:

  • Dictionary-first: structured dictionary data is the factual baseline.
  • Study-by-sense: learning state belongs to senses, not undifferentiated words.
  • Source-aware: imported source data, canonical runtime data, generated fallback data, and promoted release data remain auditable.
  • Shared contracts: app boundaries communicate through @sanakota/shared/* contracts instead of duplicate ad hoc DTOs.

For deeper implementation notes, see docs/architecture.md.

Repository Layout

apps/web                     React + Vite frontend and PWA shell
apps/api                     Express API for auth, study state, preferences,
                             legacy search/detail surfaces, and source imports
apps/dictionary-service      Express dictionary runtime for lookup, suggest,
                             fallback generation, and promotion workflows
packages/shared              Shared contracts, constants, and public types
packages/dictionary-pipeline Dictionary release publishing and Meilisearch indexing
docker/postgres              Local database bootstrap helpers
docs                         Architecture and maintainer-facing notes

Quick Start

Prerequisites

  • Node.js 20.20 or newer
  • pnpm 10
  • Docker, for the bundled PostgreSQL and Meilisearch development stack

1. Install dependencies

pnpm install

2. Copy environment files

cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env
cp apps/dictionary-service/.env.example apps/dictionary-service/.env

3. Start local infrastructure

docker compose up -d postgres meilisearch

The Compose stack exposes:

  • PostgreSQL on localhost:5432
  • Meilisearch on http://127.0.0.1:7700
  • Application database sanakota_db
  • Dictionary database sanakota_dictionary

4. Run migrations and publish the starter dictionary release

pnpm --filter sanakota-backend migrate
pnpm --filter sanakota-dictionary-service migrate
pnpm publish:dictionary:phase1a

publish:dictionary:phase1a publishes the checked-in mini release fixture into the dictionary database and Meilisearch. It is enough for local smoke testing without private source data.

5. Start the apps

pnpm dev

Open:

  • Web app: http://localhost:5173
  • Core API: http://localhost:5050
  • Dictionary service: http://localhost:5060

Dictionary Data

The repository keeps dictionary data work explicit and layered:

  1. Source import scripts load source-native records into source_* tables.
  2. Materialization scripts build canonical lexemes, senses, relations, and generated surface forms.
  3. packages/dictionary-pipeline publishes versioned runtime releases into sanakota_dictionary.
  4. Release-scoped resolution documents are indexed in Meilisearch.
  5. apps/dictionary-service serves lookup, suggest, fallback, and promotion flows against the active release.

Useful commands:

pnpm --filter sanakota-backend normalize-kielitoimisto --input=/absolute/path/to/raw-kielitoimisto.json --output=/absolute/path/to/kielitoimisto.entries.ndjson
pnpm --filter sanakota-backend import-finnwordnet
pnpm --filter sanakota-backend import-kielitoimisto --file=/absolute/path/to/kielitoimisto.entries.ndjson
pnpm --filter sanakota-backend materialize-canonical-dictionary
pnpm --filter sanakota-backend materialize-generated-surface-forms
pnpm publish:dictionary:phase1a
pnpm promotion:run
pnpm promotion:publish

Fallback generation can use the local fixture provider by default. OpenAI and DeepSeek providers are optional and are configured through apps/dictionary-service/.env; generated fallback payloads remain provisional until they are explicitly reviewed and promoted.

Development

Root commands are the source of truth:

pnpm build
pnpm lint
pnpm typecheck
pnpm test
pnpm verify
pnpm audit:prod

Targeted commands:

pnpm dev:web
pnpm dev:api
pnpm dev:dictionary
pnpm test:web
pnpm test:api
pnpm test:dictionary
pnpm test:pipeline
pnpm test:shared

Database integration tests are opt-in:

pnpm test:api:db

Project Status

Sanakota is in active alpha. The web learner loop, core study state, dictionary runtime, fallback infrastructure, and publishing pipeline are under active development and covered by unit/contract/integration tests. The project is ready for focused contributions, but not for production deployment without additional hardening.

Current priorities:

  • Improve Chinese and English learner-facing explanations.
  • Strengthen Finnish morphology, compound handling, collocations, and construction coverage.
  • Keep generated fallback data auditable and separate from canonical entries.
  • Harden authentication, account management, rate limits, and operational deployment paths.
  • Refine review scheduling while preserving the simple guest-compatible learner flow.

Contributing

Contributions are welcome, especially small changes that improve lookup quality, study-state correctness, test coverage, documentation, and developer setup.

Before opening a pull request, please read CONTRIBUTING.md and run:

pnpm verify

Please report security issues through the process in SECURITY.md instead of opening a public issue with exploit details.

License

Sanakota is licensed under GPL-3.0-only.

About

A tool used to learn and memorize Finnish words.

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages