Files
food-ai/CLAUDE.md
dbastrikin 54b10d51e2 feat: Flutter client localisation (12 languages)
Add flutter_localizations + intl, 12 ARB files (en/ru/es/de/fr/it/pt/zh/ja/ko/ar/hi),
replace all hardcoded Russian UI strings with AppLocalizations, detect system locale
on first launch, localise bottom nav bar labels, document rule in CLAUDE.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:22:52 +02:00

144 lines
4.7 KiB
Markdown

# Project Rules
## Language
- All code comments must be written in **English**.
- All git commit messages must be written in **English**.
## What NOT to commit
- The `examples/` directory — it contains sample images and fixtures used for manual
testing only. It is listed in `.gitignore` and must never be staged or committed.
## Localisation
**Rule:** Every table that stores human-readable text must have a companion `{table}_translations`
table. The base table always contains the English canonical text (used as fallback).
Translations live only in `_translations` tables — never as extra columns (`name_ru`, `title_de`).
### SQL pattern
```sql
-- Base table — English canonical text always present
CREATE TABLE cuisines (
slug VARCHAR(50) PRIMARY KEY,
name TEXT NOT NULL,
sort_order SMALLINT NOT NULL DEFAULT 0
);
-- Translation companion
CREATE TABLE cuisine_translations (
cuisine_slug VARCHAR(50) REFERENCES cuisines(slug) ON DELETE CASCADE,
lang VARCHAR(10) NOT NULL,
name TEXT NOT NULL,
PRIMARY KEY (cuisine_slug, lang)
);
```
### Query pattern
```sql
SELECT COALESCE(ct.name, c.name) AS name
FROM cuisines c
LEFT JOIN cuisine_translations ct ON ct.cuisine_slug = c.slug AND ct.lang = $lang
```
### Already compliant tables
`units` + `unit_translations`, `ingredient_categories` + `ingredient_category_translations`,
`ingredients` + `ingredient_translations` + `ingredient_aliases`,
`recipes` + `recipe_translations`, `cuisines` + `cuisine_translations`,
`tags` + `tag_translations`, `dish_categories` + `dish_category_translations`,
`dishes` + `dish_translations`, `recipe_ingredients` + `recipe_ingredient_translations`,
`recipe_steps` + `recipe_step_translations`.
### Rule when adding new entities
When adding a new entity with text fields, always create a `{table}_translations`
companion table and use LEFT JOIN COALESCE in all read queries.
## File and Directory Naming
- **File names:** snake_case — `token_verifier.go`, `wire_gen.go`, `entity.go`.
- **Package directories:** lowercase, no separators — follow Go convention
(`savedrecipe/`, `openai/`, `infra/`). Do not use underscores in package names.
- **Non-package directories** (migrations, docs, scripts): snake_case is acceptable.
## Naming
**No abbreviated variable names.** Variables must use full descriptive names.
Single- or two-letter names are only allowed when the abbreviation *is* the full description
(e.g., a loop index `i`, a matrix coordinate `x`).
### Examples
| Avoid | Use instead |
|-------|-------------|
| `err` | `parseError`, `writeError`, `dbError`, `validationError` |
| `ctx` | `requestContext`, `handlerContext`, `queryContext` |
| `s` | `service`, `server` |
| `r` | `request`, `repository` |
| `w` | `writer`, `responseWriter` |
| `h` | `handler` |
| `req` | `request` |
| `res` | `response`, `result` |
| `msg` | `message` |
| `cfg` | `config` |
| `db` | `database` |
| `tx` | `transaction` |
| `buf` | `buffer` |
| `tmp` | `temp`, or a descriptive name |
This rule applies to all languages in this repo (Go and Dart).
In Go, name variables to avoid shadowing the `error` built-in and the `context` package —
use descriptive prefixes: `parseError`, `requestContext`, etc.
## Flutter Client Localisation
**Rule:** Every UI string in `client/` must go through `AppLocalizations`.
Never hardcode user-visible text in Dart source files.
### Current setup
- `flutter_localizations` (sdk: flutter) + `intl: ^0.20.2` in `client/pubspec.yaml`
- `generate: true` under `flutter:` in `client/pubspec.yaml`
- `client/l10n.yaml` — generator config (arb-dir, template-arb-file, output-class)
- Generated class: `package:food_ai/l10n/app_localizations.dart`
### Supported languages
`en`, `ru`, `es`, `de`, `fr`, `it`, `pt`, `zh`, `ja`, `ko`, `ar`, `hi`
### ARB files
All translation files live in `client/lib/l10n/`:
- `app_en.arb` — English (template / canonical)
- `app_ru.arb`, `app_es.arb`, `app_de.arb`, `app_fr.arb`, `app_it.arb`
- `app_pt.arb`, `app_zh.arb`, `app_ja.arb`, `app_ko.arb`, `app_ar.arb`, `app_hi.arb`
### Usage pattern
```dart
import 'package:food_ai/l10n/app_localizations.dart';
// Inside build():
final l10n = AppLocalizations.of(context)!;
Text(l10n.someKey)
```
### Adding new strings
1. Add the key + English value to `client/lib/l10n/app_en.arb` (template file).
2. Add the same key with the correct translation to **all other 11 ARB files**.
3. Run `flutter gen-l10n` inside `client/` to regenerate `AppLocalizations`.
4. Use `l10n.<newKey>` in the widget.
For parameterised strings use the ICU placeholder syntax in ARB files:
```json
"queuePosition": "Position {position}",
"@queuePosition": { "placeholders": { "position": { "type": "int" } } }
```
Then call `l10n.queuePosition(n)` in Dart.