docs: 更新协议文档并清理过期的问题追踪文档
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
# L10n Cleanup + Stable Error Code + Frontend Text Migration Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Remove redundant l10n wrappers, introduce backend stable/mappable error codes for HTTP contracts, and continue frontend hardcoded-text localization migration to zh/en with default zh.
|
||||
|
||||
**Architecture:** Keep Flutter UI localization in `lib/l10n` as single source of truth, minimize cross-layer localization coupling, and use backend RFC7807 + `code`/`params` as machine-readable contract. Frontend maps `code -> l10n key` for user-facing messages while preserving fallback behavior.
|
||||
|
||||
**Tech Stack:** Flutter gen-l10n, FastAPI (RFC7807), Pydantic models, Dio client error mapping, existing AGENTS/rules constraints.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Freeze and baseline current behavior
|
||||
|
||||
**Files:**
|
||||
- Modify: none (read-only task)
|
||||
- Verify: `apps/lib/**`, `backend/src/**`, `docs/protocols/**`
|
||||
|
||||
**Step 1: Snapshot app localization status**
|
||||
|
||||
Run: `python scripts/count_cn_literals.py` (or equivalent one-off command)
|
||||
Expected: baseline count and top files with Chinese literals.
|
||||
|
||||
**Step 2: Snapshot backend detail-string usage**
|
||||
|
||||
Run: `python scripts/count_http_detail_usage.py` (or equivalent one-off command)
|
||||
Expected: per-file count of `HTTPException(detail=...)` hotspots.
|
||||
|
||||
**Step 3: Capture baseline checks**
|
||||
|
||||
Run: `cd apps && flutter analyze`
|
||||
Expected: no new errors, only known existing warnings/infos.
|
||||
|
||||
Run: `cd backend && uv run pytest -q` (or targeted fast suite if full too slow)
|
||||
Expected: baseline pass/fail recorded for regression comparison.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Refactor l10n structure to remove redundant wrapper responsibilities
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/app/app.dart`
|
||||
- Modify: UI files currently importing `apps/lib/core/l10n/l10n.dart`
|
||||
- Delete/Modify: `apps/lib/core/l10n/l10n.dart` (depending on final outcome)
|
||||
- Verify: generated files under `apps/lib/l10n/`
|
||||
|
||||
**Step 1: Define target rule**
|
||||
|
||||
Rule:
|
||||
- UI layer uses `context.l10n`.
|
||||
- Non-UI layer does not depend on ad-hoc global locale state.
|
||||
- If non-UI needs localization, pass already-localized strings in from caller or inject mapper service.
|
||||
|
||||
**Step 2: Write failing/static guard checks**
|
||||
|
||||
Add temporary grep checks:
|
||||
- Fail when new code adds `L10n.current` in feature/presentation.
|
||||
- Fail when `core/l10n/l10n.dart` is reintroduced for convenience access.
|
||||
|
||||
**Step 3: Replace call sites incrementally**
|
||||
|
||||
For each file:
|
||||
1. Replace `L10n.current.xxx` with `context.l10n.xxx` where `BuildContext` exists.
|
||||
2. For cubit/service/form validators, inject message providers or pass messages from UI.
|
||||
3. Keep behavior unchanged.
|
||||
|
||||
**Step 4: Remove locale global mutation path**
|
||||
|
||||
In `app.dart`:
|
||||
- Remove `L10n.setLocale(...)` style side effects.
|
||||
- Keep Flutter-native delegates + `supportedLocales` + default locale logic.
|
||||
|
||||
**Step 5: Delete redundant wrapper (if no remaining valid use case)**
|
||||
|
||||
Delete `apps/lib/core/l10n/l10n.dart` only after all references are removed and non-UI strategy is in place.
|
||||
|
||||
**Step 6: Verify**
|
||||
|
||||
Run: `cd apps && flutter gen-l10n && flutter analyze`
|
||||
Expected: no errors.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Define backend stable error code contract (RFC7807 extension)
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/src/core/http/response.py`
|
||||
- Modify: `backend/src/app.py`
|
||||
- Create: `backend/src/core/http/errors.py`
|
||||
- Modify: `docs/protocols/agent/api-endpoints.md`
|
||||
- (optional) Create: `docs/protocols/common/error-contract.md`
|
||||
|
||||
**Step 1: Extend problem details schema**
|
||||
|
||||
Add fields:
|
||||
- `code: str | None`
|
||||
- `params: dict[str, str | int | float | bool] | None`
|
||||
|
||||
Preserve RFC7807 required fields and media type.
|
||||
|
||||
**Step 2: Introduce unified domain error type**
|
||||
|
||||
In `core/http/errors.py`, create exception class carrying:
|
||||
- http status
|
||||
- stable error code (UPPER_SNAKE_CASE)
|
||||
- optional params
|
||||
- optional internal detail
|
||||
|
||||
**Step 3: Wire global exception handlers**
|
||||
|
||||
In `app.py`:
|
||||
- Convert domain exceptions to problem+json with `code` and `params`.
|
||||
- Keep fallback for unknown exceptions.
|
||||
|
||||
**Step 4: Define code naming convention**
|
||||
|
||||
Examples:
|
||||
- `AUTH_INVALID_TOKEN`
|
||||
- `AUTH_TOKEN_EXPIRED`
|
||||
- `SCHEDULE_ITEM_NOT_FOUND`
|
||||
- `TODO_TITLE_REQUIRED`
|
||||
- `FRIENDSHIP_ALREADY_EXISTS`
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Migrate backend hotspots from free-text detail to stable codes
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/src/v1/friendships/service.py`
|
||||
- Modify: `backend/src/v1/schedule_items/service.py`
|
||||
- Modify: `backend/src/v1/todo/service.py`
|
||||
- Modify: `backend/src/v1/agent/service.py`
|
||||
- Modify: `backend/src/v1/users/service.py`
|
||||
- Modify: `backend/src/v1/memories/service.py`
|
||||
- Modify: `backend/src/v1/auth/gateway.py`
|
||||
- Modify: `backend/src/v1/agent/router.py`
|
||||
- Modify: other files with `HTTPException(detail=...)`
|
||||
|
||||
**Step 1: Prioritize by impact**
|
||||
|
||||
Order:
|
||||
1. Auth
|
||||
2. Agent
|
||||
3. Todo/Schedule/Friendships
|
||||
4. Users/Memories
|
||||
|
||||
**Step 2: Replace throw sites**
|
||||
|
||||
For each detail-based throw:
|
||||
1. Map to stable `code`.
|
||||
2. Keep detail only as optional server diagnostic text.
|
||||
3. Add params when useful (e.g., max size, field, limit).
|
||||
|
||||
**Step 3: Preserve backwards compatibility window**
|
||||
|
||||
During transition:
|
||||
- Keep `detail` present.
|
||||
- Add `code`/`params` immediately.
|
||||
- Frontend prefers `code`, falls back to existing behavior.
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Frontend network error mapping to l10n via backend code
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/core/network/api_exception.dart`
|
||||
- Create: `apps/lib/core/network/error_code_mapper.dart`
|
||||
- Modify: call sites currently displaying raw backend detail
|
||||
- Modify: `apps/lib/l10n/app_zh.arb`, `apps/lib/l10n/app_en.arb`
|
||||
|
||||
**Step 1: Parse `code` and `params` from response payload**
|
||||
|
||||
In `ApiException.fromDioError`:
|
||||
- Read RFC7807 + extension fields.
|
||||
- Keep `statusCode` fallback behavior.
|
||||
|
||||
**Step 2: Map `code -> localized message`**
|
||||
|
||||
Implement central mapper:
|
||||
- Input: code/status/params
|
||||
- Output: localized user-facing string key resolution
|
||||
|
||||
**Step 3: Fallback strategy**
|
||||
|
||||
Priority:
|
||||
1. known code mapping
|
||||
2. status-based generic mapping
|
||||
3. safe generic fallback (`request failed` localized)
|
||||
|
||||
**Step 4: Replace UI direct usage of raw server detail**
|
||||
|
||||
Audit and update places where `e.toString()` or backend detail is shown directly.
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Continue frontend hardcoded text migration (remaining files)
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/settings/presentation/screens/*.dart` (remaining high-count files)
|
||||
- Modify: `apps/lib/features/calendar/presentation/screens/*.dart`
|
||||
- Modify: `apps/lib/features/calendar/presentation/widgets/*.dart`
|
||||
- Modify: `apps/lib/l10n/app_zh.arb`, `apps/lib/l10n/app_en.arb`
|
||||
|
||||
**Step 1: Batch by screen group**
|
||||
|
||||
Batch A: settings deep pages
|
||||
Batch B: calendar pages
|
||||
Batch C: shared/home leftovers
|
||||
|
||||
**Step 2: Migrate with key hygiene**
|
||||
|
||||
Rules:
|
||||
- key names are feature-prefixed and stable
|
||||
- dynamic texts use placeholders, not string concatenation
|
||||
- avoid duplicate semantic keys
|
||||
|
||||
**Step 3: After each batch, run verification**
|
||||
|
||||
Run:
|
||||
- `cd apps && flutter gen-l10n`
|
||||
- `cd apps && flutter analyze`
|
||||
|
||||
Track remaining hardcoded-literal count after each batch.
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Protocol docs and test updates
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/protocols/agent/api-endpoints.md`
|
||||
- Modify/Create: `docs/protocols/common/error-contract.md`
|
||||
- Modify: backend integration/unit tests asserting only `detail`
|
||||
- Modify: frontend tests around error display/mapping
|
||||
|
||||
**Step 1: Document new error response shape**
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "about:blank",
|
||||
"title": "Unprocessable Entity",
|
||||
"status": 422,
|
||||
"detail": "Validation failed",
|
||||
"code": "TODO_TITLE_REQUIRED",
|
||||
"params": {"field": "title"},
|
||||
"instance": "/api/v1/todo"
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Update tests to assert codes first**
|
||||
|
||||
Replace brittle text assertions with:
|
||||
- `status`
|
||||
- `code`
|
||||
- optional `params`
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Final verification gate
|
||||
|
||||
**Files:**
|
||||
- Verify only
|
||||
|
||||
**Step 1: Apps verification**
|
||||
|
||||
Run:
|
||||
- `cd apps && flutter gen-l10n`
|
||||
- `cd apps && flutter analyze`
|
||||
|
||||
**Step 2: Backend verification**
|
||||
|
||||
Run:
|
||||
- `cd backend && uv run ruff check .`
|
||||
- `cd backend && uv run basedpyright`
|
||||
- `cd backend && uv run pytest -q`
|
||||
|
||||
**Step 3: Cross-contract smoke**
|
||||
|
||||
Run targeted API checks ensuring error payload includes `code` for representative modules.
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Rollout and compatibility
|
||||
|
||||
**Files:**
|
||||
- Modify: release notes/changelog if used
|
||||
|
||||
**Step 1: Progressive rollout strategy**
|
||||
|
||||
- Phase 1: backend emits both `detail` + `code`
|
||||
- Phase 2: frontend consumes `code` with fallback
|
||||
- Phase 3: clean up legacy detail-dependent branches
|
||||
|
||||
**Step 2: Monitoring**
|
||||
|
||||
- Log unknown/unmapped error codes on frontend
|
||||
- Add backend metrics for top emitted error codes
|
||||
|
||||
---
|
||||
|
||||
## Risks and mitigations
|
||||
|
||||
- Risk: non-UI code loses localization access after wrapper removal
|
||||
- Mitigation: inject messages from UI/service boundary; avoid static locale globals.
|
||||
- Risk: backend code migration is broad (many detail throws)
|
||||
- Mitigation: staged module-by-module migration + compatibility window.
|
||||
- Risk: front/back mismatch in error code enum
|
||||
- Mitigation: shared protocol doc + CI checks for known code list.
|
||||
|
||||
## Done criteria
|
||||
|
||||
- `apps/lib/core/l10n/l10n.dart` removed or reduced to zero-overlap minimal utility with explicit justification.
|
||||
- Backend RFC7807 responses include stable `code` (and optional `params`) on migrated endpoints.
|
||||
- Frontend maps known codes to zh/en l10n; raw detail is no longer primary user-facing string.
|
||||
- Hardcoded visible Chinese text count in `apps/lib` reduced to agreed threshold or zero for targeted modules.
|
||||
- Docs and tests updated accordingly.
|
||||
Reference in New Issue
Block a user