diff --git a/README.md b/README.md new file mode 100644 index 0000000..9566e1f --- /dev/null +++ b/README.md @@ -0,0 +1,282 @@ +# Specification: Translation Dictionary for Living Christianity + +## Overview + +"Sanasto Wiki" is a web-based dictionary application for simultaneous translators in the living Christianity. The application provides publicly accessible translations while restricting editing and commenting to invited contributors. + +## Core Concepts + +### Supported languages +We currently support the following languages: +* Finnish +* English +* Swedish +* Norwegian +* Russian +* German + +### Entry +An entry represents a translatable unit which may be: + +- `word` — Single word +- `phrase` — Multi-word expression or idiom, sentence +- `name` — Person's name (for consistent transliteration) +- `title` — Book, publication, or hymn title +- `reference` — Biblical or doctrinal term +- `other` — Any other translatable text unit + +Each entry has translations in multiple languages. + +### Suggested Meaning +When translators disagree on a translation or want to suggest alternatives (regional variations, contextual meanings, etc.), they can submit a suggested meaning for community review. + +## Technical Stack + +* Framework: Rails 8 +* Database: SQLite with FTS5 +* Authentication: Rails 8 built-in authentication +* Authorization: Invitation-only for contributors +* Deployment: Kamal + +--- + +## Database Schema +``` +# db/schema.rb + +ActiveRecord::Schema[8.0].define(version: 2025_01_22_100000) do + create_table "entries", force: :cascade do |t| + t.integer "category", null: false # word, phrase, name, title, reference, other + + # Language columns + t.string "fi" # Finnish + t.string "en" # English + t.string "sv" # Swedish + t.string "no" # Norwegian + t.string "ru" # Russian + t.string "de" # German + + t.text "notes" + t.boolean "verified", default: false + t.integer "created_by_id" + t.integer "updated_by_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + + t.index ["category"], name: "index_entries_on_category" + end + + create_table "suggested_meanings", force: :cascade do |t| + t.integer "entry_id", null: false + t.string "language_code", null: false + t.string "alternative_translation", null: false + t.text "context" + t.text "reasoning" + t.string "source" + t.string "region" + t.integer "status", default: 0 # pending, accepted, rejected + t.integer "submitted_by_id" + t.integer "reviewed_by_id" + t.datetime "reviewed_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + + t.index ["entry_id"], name: "index_suggested_meanings_on_entry_id" + t.index ["language_code"], name: "index_suggested_meanings_on_language_code" + t.index ["status"], name: "index_suggested_meanings_on_status" + end + + create_table "comments", force: :cascade do |t| + t.integer "user_id", null: false + t.string "commentable_type", null: false + t.integer "commentable_id", null: false + t.text "body", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + + t.index ["commentable_type", "commentable_id"], name: "index_comments_on_commentable" + end + + create_table "users", force: :cascade do |t| + t.string "email", null: false + t.string "password_digest", null: false + t.string "name" + t.integer "role", default: 0 # contributor, reviewer, admin + t.string "primary_language" + t.string "invitation_token" + t.datetime "invitation_sent_at" + t.datetime "invitation_accepted_at" + t.integer "invited_by_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + + t.index ["email"], name: "index_users_on_email", unique: true + t.index ["invitation_token"], name: "index_users_on_invitation_token", unique: true + end + + create_table "entry_versions", force: :cascade do |t| + t.integer "entry_id", null: false + t.integer "user_id", null: false + t.json "changes_made", null: false + t.string "change_type" # create, update, verify + t.datetime "created_at", null: false + + t.index ["entry_id"], name: "index_entry_versions_on_entry_id" + end + + create_table "supported_languages", force: :cascade do |t| + t.string "code", null: false + t.string "name", null: false + t.string "native_name", null: false + t.integer "sort_order", default: 0 + t.boolean "active", default: true + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + + t.index ["code"], name: "index_supported_languages_on_code", unique: true + end + + add_foreign_key "suggested_meanings", "entries" + add_foreign_key "suggested_meanings", "supported_languages", column: "language_code", primary_key: "code" + add_foreign_key "suggested_meanings", "users", column: "submitted_by_id" + add_foreign_key "suggested_meanings", "users", column: "reviewed_by_id" + add_foreign_key "comments", "users" + add_foreign_key "entries", "users", column: "created_by_id" + add_foreign_key "entries", "users", column: "updated_by_id" + add_foreign_key "entry_versions", "entries" + add_foreign_key "entry_versions", "users" +end +``` + +--- + +## Initial Data + +Found in 'Kristillisyyden sanasto ver 23.5.2013.xlsx' + +--- + +## User Roles & Permissions + +| Action | Public | Contributor | Reviewer | Admin | +|--------|--------|-------------|----------|-------| +| View entries | ✓ | ✓ | ✓ | ✓ | +| Search entries | ✓ | ✓ | ✓ | ✓ | +| Create entry | | ✓ | ✓ | ✓ | +| Edit entry | | ✓ | ✓ | ✓ | +| Add comment | | ✓ | ✓ | ✓ | +| Submit suggested meaning | | ✓ | ✓ | ✓ | +| Review suggested meanings | | | ✓ | ✓ | +| Mark entry as verified | | | ✓ | ✓ | +| Invite new users | | | | ✓ | +| Manage users | | | | ✓ | + +--- + +## Features + +### Public Features + +**Search & Browse** +- Full-text search across all languages +- Filter by category +- Alphabetical browsing per language +- View entry with all translations +- Download the entries table as an XLSX file, without :id attribute + +### Contributor Features + +**Entry Management** +- Create new entries with translations in known languages +- Edit existing translations +- Leave empty translations for others to fill in +- Add notes/context to entries +- View edit history + +**Discussion** +- Add comments to entries +- Submit alternative translations as suggested meanings +- Participate in translation discussions + +### Reviewer Features + +**Quality Control** +- Review and approve/reject suggested meanings +- Mark entries as verified +- Merge duplicate entries + +### Admin Features + +**User Management** +- Send invitations via email +- Assign/change user roles +- Deactivate users + +**System Management** +- Bulk import/export (CSV) +- Database backup + +--- + +## Supported Languages + +Initial seed data: + +| Code | Name | Native Name | Sort Order | +|------|------|-------------|------------| +| fi | Finnish | Suomi | 1 | +| en | English | English | 2 | +| sv | Swedish | Svenska | 3 | +| no | Norwegian | Norsk | 4 | +| de | German | Deutsch | 5 | +| ru | Russian | Русский | 6 | + +--- + +## User Interface + +### Pages + +1. **Home/Search** — Search box, quick stats, recent entries +2. **Browse** — Alphabetical listing with language tabs +3. **Entry View** — Single entry with all translations, comments, suggested meanings +4. **Entry Edit** — Form for editing translations (contributors only) +5. **New Entry** — Form for creating entries (contributors only) +6. **Suggested Meanings Queue** — List for reviewers +7. **User Profile** — Personal settings, contribution history +8. **Admin Dashboard** — User management, invitations, stats + +### Design Principles +- Clean, readable typography (translations must be easy to read quickly) +- Mobile-friendly (translators may use during services) +- Fast search (primary use case) +- Clear visual distinction between verified and unverified entries + +--- + +## API (Optional Future) + +REST API for potential mobile app or integration: +``` +GET /api/entries +GET /api/entries/:id +GET /api/entries/search?q=:query&lang=:code +POST /api/entries (authenticated) +PATCH /api/entries/:id (authenticated) +``` + +## Deployment + +Server: Single VPS with Kamal +Database: SQLite with Litestream for replication/backup +Assets: Propshaft (Rails 8 default) +Background Jobs: Solid Queue (for invitation emails) + + +## Future Considerations + +Export to PDF/print format for offline use +Audio pronunciation recordings +Mobile app +Offline mode with sync +Additional languages via migration