CONTRIBUTING.en.md
Rama main · réplica de lectura del repositorio original.
# Contributing to Spendif.ai
*Italian version: [CONTRIBUTING.md](CONTRIBUTING.md).*
Thanks for your interest! These guidelines describe how to report issues, propose features, and contribute code.
---
## Table of contents
1. [Priority system](#priority-system)
2. [Reporting a bug](#reporting-a-bug)
3. [Proposing a feature](#proposing-a-feature)
4. [Contributing code](#contributing-code)
5. [Translating Spendif.ai](#translating-spendifai)
6. [Development environment setup](#development-environment-setup)
7. [Conventions](#conventions)
---
## Priority system
Every issue carries a priority label. The system has 4 levels:
| Label | Color | Meaning | When we use it |
|-------|-------|---------|----------------|
| 🔴 **P0 · now** | Red | Blocks the beta or is a quick UX fix | Critical bugs, regressions, pre-release quick wins |
| 🟡 **P1 · v2.5** | Yellow | Completes the core workflow | Features that close existing usage loops |
| 🔵 **P2 · v3.0** | Blue | Significant product expansion | New modules, integrations, deep improvements |
| ⚪ **P3 · roadmap** | Gray | Long-term vision | Important ideas without a defined timeline |
### Practical rules
- **P0** → open a PR within the current sprint; if you can't, flag it in the issue comments
- **P1** → target release `v2.5`; milestone assigned when the issue is picked up
- **P2** → target release `v3.0`; can be moved up if an external contribution lands
- **P3** → no timeline; contributions welcome, but quick review is not guaranteed
### How to propose a priority change
Open a comment on the issue with your reasoning. The maintainer re-evaluates and updates the label.
---
## Reporting a bug
1. Search the [open issues](https://github.com/spendifai/spendif-ai/issues) — it may already exist
2. Open a new issue using the **Bug report** template
3. Include:
- Spendif.ai version (or commit hash)
- OS and Docker/Python version
- Steps to reproduce
- Expected vs actual behavior
- Relevant logs (`docker compose logs spendifai`)
---
## Proposing a feature
1. Check the [backlog](https://github.com/spendifai/spendif-ai/issues) — it may already exist as a P2/P3 issue
2. Open an issue using the **Feature request** template
3. Describe:
- The problem it solves (not just the solution)
- Who benefits (end user, developer, ops)
- Estimated impact on privacy, performance, complexity
---
## Contributing code
### Standard flow
```
fork → branch → commit → PR → review → merge
```
1. **Fork** the repository
2. Create a branch: `git checkout -b feat/feature-name` or `fix/bug-name`
3. Develop with tests — see [Setup](#development-environment-setup)
4. Open a **Pull Request** against `develop`
5. Link the PR to the issue with `Closes #N` in the body
### The two long-lived branches
`develop` is where work lands. `main` is what has been released: the website
is served from it, the container image is published from it, and a release is
a tag cut on it. So `main` moves once per release, by merging `develop`, and
never by a direct push.
A pull request therefore targets `develop`, not `main`, unless it repairs
something already in the hands of users.
### What to expect from review
- Feedback within ~3 business days for P0/P1
- Feedback within ~1 week for P2/P3
- The PR is merged only if CI is green (tests + lint + Docker smoke test)
---
## Translating Spendif.ai
Translations are one of the most welcome contributions: all you need is a text editor and a few minutes. You don't need to be a developer.
### Current language status
| Language | Streamlit UI | Landing page (HTML) |
|----------|--------------|---------------------|
| 🇮🇹 Italiano | ✅ master (human-verified) | ✅ master (human-verified) |
| 🇬🇧 English | ✅ LLM-translated, partially verified | ✅ LLM-translated, partially verified |
| 🇩🇪 / 🇫🇷 / 🇪🇸 / 🇵🇹 / 🇳🇱 / 🇯🇵 / 🇵🇱 | ❌ not available | ✅ LLM-translated, **not yet human-verified** |
LLM-only translations are an imperfect baseline: they all need human eyes. If you're a native speaker or have skills in one of these languages, your contribution makes a difference.
### Translation architecture
Spendif.ai has **two areas** that require separate translations:
1. **Streamlit UI** — `sw_artifacts/ui/i18n/<lang>.json` — a flat key→string file per language. Loaded at runtime by the app. The key schema follows the convention `area.element` and `area.element.desc` for descriptions (e.g. `nav.import` + `nav.import.desc`).
2. **Landing page** — `sw_artifacts/index.<lang>.html` — a complete HTML file per language. **Schema in transition**: we're migrating these to a JSON system similar to `ui/i18n/` (tracked in `backlog.json` AI-14). Until that refactor lands, landing translations are done by editing the HTML directly — see the [Landing page (transitional)](#landing-page-transitional) section below.
### Translating the Streamlit UI (recommended — the easiest flow)
#### Step 1 — Pick a language
Example: you want to add German (`de`).
#### Step 2 — Copy the master
```bash
cp sw_artifacts/ui/i18n/en.json sw_artifacts/ui/i18n/de.json
```
Start from `en.json` as the master (it's the reference baseline). Never start from `it.json` unless you're Italian: you risk linguistic calques.
#### Step 3 — Edit the file
Open `sw_artifacts/ui/i18n/de.json` with your editor of choice. The structure is simple:
```json
{
"_language_name": "Deutsch",
"nav.import": "📥 Importieren",
"nav.import.desc": "CSV/XLSX-Dateien aus deinen Bankkonten importieren",
"nav.history": "📜 Importverlauf"
}
```
Rules:
- **Change `_language_name`** to the language name in the language itself (e.g. `Deutsch`, not `German`)
- **Keep keys unchanged** — they are identifiers, not text to translate
- **Keep emoji and formatting unchanged** where present — they are part of the design
- **Keep `{var}` or `%(name)s` substitutions** — they are placeholders for dynamic values
- **`.desc` keys** are longer descriptions used as hints or tooltips — don't skip them
#### Step 4 — Recommended editors for non-developers
If you're not familiar with JSON, any of these makes life easier:
- [**BabelEdit**](https://www.codeandweb.com/babeledit) — free for open source projects. Side-by-side EN/target language UI, highlights missing keys, flags double spaces.
- [**Tolgee Cloud**](https://tolgee.io/) — side-by-side web interface with built-in AI suggestions. Free tier is sufficient.
- [**VS Code**](https://code.visualstudio.com/) with the "JSON Tools" extension — for those who just want a solid editor.
- [**JSONedit**](https://tomeko.net/software/JSONedit/) — Windows desktop, free, lightweight.
#### Step 5 — Validate and PR
```bash
# Verify that the JSON is valid
python -c "import json; json.load(open('sw_artifacts/ui/i18n/de.json'))"
```
Open a PR following the standard flow ([Contributing code](#contributing-code)).
In the PR body include:
- The language added or updated
- How many keys you translated / verified
- Any non-obvious terminology decisions (e.g. "I rendered `ledger` as `Hauptbuch` because...")
### Landing page (transitional)
**Current workflow (transitional)**: edit `sw_artifacts/<lang>/index.html` directly. Open the canonical English `sw_artifacts/index.html` alongside as a reference, change only visible text (NOT CSS, JS, or `href` URLs), save, PR.
Practical tips:
- Keep two editors or two tabs side by side: EN on the left, target language on the right
- Search for `<h1>`, `<h2>`, `<h3>`, `<p>`, `<title>`, `<meta>` to find translatable strings
- **Do NOT translate**: `<style>`, `<script>`, `href="..."`, shell snippets in `<code-body>` (curl/irm/bash), id tags (`id="install"`), tab labels (`🍎 macOS`), proper names (Spendify, Ollama, Streamlit, GitHub)
- Update `<html lang="...">` at the top
**Future workflow (post AI-14)**: we're working to bring landings onto a JSON system similar to `ui/i18n/`. Once it's ready, contributing a language will mean editing a single `i18n/landing/<lang>.json` file instead of an entire HTML. If you want to help unblock this refactor, see backlog item AI-14.
### Human review of existing LLM translations
Even just rereading and correcting an existing translation is a valuable contribution. If you find errors, awkward-sounding phrases, or wrong technical terms, open a PR with the fixes. Note in the commit message:
```
i18n(de): fix awkward LLM phrasings in nav and analytics sections
i18n(ja): correct technical term for "ledger" — 元帳 → 取引履歴
```
---
## Development environment setup
### Prerequisites
| Tool | Minimum version |
|------|-----------------|
| Python | 3.13 |
| uv | any |
| Docker Desktop | any (for local smoke test) |
### Installation
```bash
git clone https://github.com/spendifai/spendif-ai.git
cd spendifai
uv sync
cp .env.example .env
# Startup script (recommended)
./start.sh # UI only (default)
./start.sh api # REST API only
./start.sh all # UI + API
# Or manually
uv run streamlit run app.py
```
### Running tests
```bash
# Tests with coverage
uv run pytest tests/ -v --cov=. --cov-report=term-missing
# A single module
uv run pytest tests/test_normalizer.py -v
# Local Docker smoke test
docker compose -f docker/docker-compose.yml build
docker compose -f docker/docker-compose.yml up -d
curl -sf http://localhost:8501/_stcore/health
docker compose -f docker/docker-compose.yml down -v
```
### Expected coverage thresholds
| Module | Minimum coverage |
|--------|------------------|
| `core/normalizer.py` | 100% |
| `core/description_cleaner.py` | 100% |
| `core/classifier.py` | ≥ 99% |
| All others | ≥ 80% |
---
## Conventions
### Commit messages
We follow [Conventional Commits](https://www.conventionalcommits.org/):
```
feat: add context filtering in the ledger
fix: correct amount parsing with European separator
test: add coverage for detect_internal_transfers
docs: update installation guide for Docker arm64
refactor: extract pairing logic into a separate function
chore: remove dead code in support/core_logic.py
```
### Branch naming
```
feat/crea-regola-da-ledger
fix/encoding-windows-1252
test/phase0-preprocessing
docs/guida-installazione-en
```
### Code style
- **Formatter**: `ruff format` (configured in `pyproject.toml`)
- **Linter**: `ruff check`
- **Type hints**: required for public functions in `core/` modules
- **Decimal**: monetary amounts always use `Decimal`, never `float`
```bash
# Local check before committing
uv run ruff check .
uv run ruff format --check .
```
---
## Questions?
Open a [Discussion](https://github.com/spendifai/spendif-ai/discussions) or write in the comments of the issue closest to your case.