feat: Enhance localization compliance by enforcing usage of localized keys for user-facing strings and updating related documentation
This commit is contained in:
@@ -58,6 +58,7 @@ and load any additional skills as needed for specific review challenges.
|
|||||||
6. Missing tests for use cases or repositories
|
6. Missing tests for use cases or repositories
|
||||||
7. Complex BLoC without bloc_test coverage
|
7. Complex BLoC without bloc_test coverage
|
||||||
8. Test coverage below 70% for business logic
|
8. Test coverage below 70% for business logic
|
||||||
|
9. Hardcoded user-facing strings — must use `core_localization` (Slang) via `t.<section>.<key>`. All `Text('...')` with literal English/Spanish strings in presentation layer must be replaced with localized keys
|
||||||
|
|
||||||
### MODERATE (Request Fix, can be deferred with justification):
|
### MODERATE (Request Fix, can be deferred with justification):
|
||||||
1. Missing doc comments on public APIs
|
1. Missing doc comments on public APIs
|
||||||
@@ -103,7 +104,7 @@ Verify:
|
|||||||
- Business logic resides exclusively in use cases
|
- Business logic resides exclusively in use cases
|
||||||
- Entities are in domain, models in data, widgets in presentation
|
- Entities are in domain, models in data, widgets in presentation
|
||||||
|
|
||||||
### Step 3: Design System Compliance
|
### Step 3: Design System & Localization Compliance
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Hardcoded colors
|
# Hardcoded colors
|
||||||
@@ -117,9 +118,22 @@ grep -rn -E "EdgeInsets\.(all|symmetric|only)\(" apps/mobile/apps/*/lib/features
|
|||||||
|
|
||||||
# Direct icon imports
|
# Direct icon imports
|
||||||
grep -rn "^import.*icons" apps/mobile/apps/*/lib/features/
|
grep -rn "^import.*icons" apps/mobile/apps/*/lib/features/
|
||||||
|
|
||||||
|
# Hardcoded user-facing strings (look for Text('...') with literal strings)
|
||||||
|
grep -rn "Text(['\"]" apps/mobile/packages/features/
|
||||||
|
# Also check for hardcoded strings in SnackBar, AlertDialog, AppBar title, etc.
|
||||||
|
grep -rn "title: ['\"]" apps/mobile/packages/features/
|
||||||
|
grep -rn "label: ['\"]" apps/mobile/packages/features/
|
||||||
|
grep -rn "hintText: ['\"]" apps/mobile/packages/features/
|
||||||
```
|
```
|
||||||
|
|
||||||
All styling must come from the design system. No exceptions.
|
All styling must come from the design system. All user-facing strings must come from `core_localization` via Slang (`t.<section>.<key>`). No exceptions.
|
||||||
|
|
||||||
|
**Localization rules:**
|
||||||
|
- Strings defined in `packages/core_localization/lib/src/l10n/en.i18n.json` and `es.i18n.json`
|
||||||
|
- Accessed via `t.<section>.<key>` (e.g., `t.client_create_order.review.invalid_arguments`)
|
||||||
|
- Both `en` and `es` JSON files must be updated together
|
||||||
|
- Regenerate with `dart run slang` from `packages/core_localization/` directory
|
||||||
|
|
||||||
### Step 4: State Management Review
|
### Step 4: State Management Review
|
||||||
For every BLoC in changed files, verify:
|
For every BLoC in changed files, verify:
|
||||||
|
|||||||
@@ -266,11 +266,29 @@ design_system/
|
|||||||
- Export `TranslationProvider` for `context.strings` access
|
- Export `TranslationProvider` for `context.strings` access
|
||||||
- Map domain failures to localized error messages via `ErrorTranslator`
|
- Map domain failures to localized error messages via `ErrorTranslator`
|
||||||
|
|
||||||
|
**String Definition:**
|
||||||
|
- Strings are defined in `packages/core_localization/lib/src/l10n/en.i18n.json` (English) and `es.i18n.json` (Spanish)
|
||||||
|
- Both files MUST be updated together when adding/modifying strings
|
||||||
|
- Generated output: `strings.g.dart`, `strings_en.g.dart`, `strings_es.g.dart`
|
||||||
|
- Regenerate with: `cd packages/core_localization && dart run slang`
|
||||||
|
|
||||||
**Feature Integration:**
|
**Feature Integration:**
|
||||||
```dart
|
```dart
|
||||||
// Features access strings
|
// ✅ CORRECT: Access via Slang's global `t` accessor
|
||||||
Text(context.strings.loginButton)
|
import 'package:core_localization/core_localization.dart';
|
||||||
|
|
||||||
|
Text(t.client_create_order.review.invalid_arguments)
|
||||||
|
Text(t.errors.order.creation_failed)
|
||||||
|
|
||||||
|
// ❌ FORBIDDEN: Hardcoded user-facing strings
|
||||||
|
Text('Invalid review arguments') // Must use localized key
|
||||||
|
Text('Order created!') // Must use localized key
|
||||||
|
```
|
||||||
|
|
||||||
|
**RESTRICTION:** ALL user-facing strings in the presentation layer (Text widgets, SnackBars, AppBar titles, hints, labels, error messages, dialogs) MUST use localized keys via `t.<section>.<key>`. No hardcoded English or Spanish strings.
|
||||||
|
|
||||||
|
**BLoC Error Flow:**
|
||||||
|
```dart
|
||||||
// BLoCs emit domain failures (not strings)
|
// BLoCs emit domain failures (not strings)
|
||||||
emit(AuthError(InvalidCredentialsFailure()));
|
emit(AuthError(InvalidCredentialsFailure()));
|
||||||
|
|
||||||
@@ -879,6 +897,11 @@ Navigator.push(context, MaterialPageRoute(...)); // ← Use Modular
|
|||||||
Modular.to.navigate('/profile'); // ← Use safe extensions
|
Modular.to.navigate('/profile'); // ← Use safe extensions
|
||||||
```
|
```
|
||||||
|
|
||||||
|
❌ **Hardcoded user-facing strings**
|
||||||
|
```dart
|
||||||
|
Text('Order created successfully!'); // ← Use t.section.key from core_localization
|
||||||
|
```
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
The architecture enforces:
|
The architecture enforces:
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
/// Displays the "Review & Submit" title and subtitle at the top of the
|
|
||||||
/// review order page.
|
|
||||||
class ReviewOrderHeader extends StatelessWidget {
|
|
||||||
const ReviewOrderHeader({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: UiConstants.space6,
|
|
||||||
right: UiConstants.space6,
|
|
||||||
top: UiConstants.space4,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
'Review & Submit',
|
|
||||||
style: UiTypography.headline2m,
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space1),
|
|
||||||
Text(
|
|
||||||
'Confirm details before posting',
|
|
||||||
style: UiTypography.body2r.textSecondary,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,18 +19,18 @@ class ReviewOrderInfoRow extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
spacing: UiConstants.space2,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.body3r.textSecondary,
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: UiConstants.space3),
|
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
value,
|
value,
|
||||||
style: UiTypography.body3m,
|
style: UiTypography.body2m,
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class ReviewOrderPositionsCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusXl,
|
borderRadius: UiConstants.radiusXl,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border, width: 0.5),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ReviewOrderSectionCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusXl,
|
borderRadius: UiConstants.radiusXl,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border, width: 0.5),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -39,10 +39,7 @@ class ReviewOrderSectionCard extends StatelessWidget {
|
|||||||
if (onEdit != null)
|
if (onEdit != null)
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: onEdit,
|
onTap: onEdit,
|
||||||
child: Text(
|
child: Text('Edit', style: UiTypography.body3m.primary),
|
||||||
'Edit',
|
|
||||||
style: UiTypography.body3m.primary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:design_system/design_system.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'review_order_action_bar.dart';
|
import 'review_order_action_bar.dart';
|
||||||
import 'review_order_basics_card.dart';
|
import 'review_order_basics_card.dart';
|
||||||
import 'review_order_header.dart';
|
|
||||||
import 'review_order_positions_card.dart';
|
import 'review_order_positions_card.dart';
|
||||||
import 'review_order_total_banner.dart';
|
import 'review_order_total_banner.dart';
|
||||||
|
|
||||||
@@ -56,10 +55,11 @@ class ReviewOrderView extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: UiColors.bgMenu,
|
|
||||||
appBar: UiAppBar(
|
appBar: UiAppBar(
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
onLeadingPressed: onBack,
|
onLeadingPressed: onBack,
|
||||||
|
title: 'Review & Submit',
|
||||||
|
subtitle: 'Confirm details before posting',
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -68,7 +68,6 @@ class ReviewOrderView extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const ReviewOrderHeader(),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: UiConstants.space6,
|
horizontal: UiConstants.space6,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
specVersion: "v1"
|
specVersion: "v1"
|
||||||
serviceId: "krow-workforce-db-validation"
|
serviceId: "krow-workforce-db"
|
||||||
location: "us-central1"
|
location: "us-central1"
|
||||||
schema:
|
schema:
|
||||||
source: "./schema"
|
source: "./schema"
|
||||||
@@ -7,7 +7,7 @@ schema:
|
|||||||
postgresql:
|
postgresql:
|
||||||
database: "krow_db"
|
database: "krow_db"
|
||||||
cloudSql:
|
cloudSql:
|
||||||
instanceId: "krow-sql-validation"
|
instanceId: "krow-sql"
|
||||||
# schemaValidation: "STRICT" # STRICT mode makes Postgres schema match Data Connect exactly.
|
# schemaValidation: "STRICT" # STRICT mode makes Postgres schema match Data Connect exactly.
|
||||||
# schemaValidation: "COMPATIBLE" # COMPATIBLE mode makes Postgres schema compatible with Data Connect.
|
# schemaValidation: "COMPATIBLE" # COMPATIBLE mode makes Postgres schema compatible with Data Connect.
|
||||||
connectorDirs: ["./connector"]
|
connectorDirs: ["./connector"]
|
||||||
|
|||||||
Reference in New Issue
Block a user