# 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.