761 lines
22 KiB
Markdown
761 lines
22 KiB
Markdown
# Localization Guide - Worker Mobile App
|
|
|
|
Complete guide for managing translations and localization in the Worker Mobile App.
|
|
|
|
## Overview
|
|
|
|
The Worker app supports **Vietnamese** (primary) and **English** (secondary) languages with **450+ translation keys** covering all UI elements, messages, and user interactions.
|
|
|
|
### Key Features
|
|
|
|
- **Primary Language**: Vietnamese (`vi_VN`)
|
|
- **Secondary Language**: English (`en_US`)
|
|
- **Translation Keys**: 450+ comprehensive translations
|
|
- **Auto-generation**: Flutter's `gen-l10n` tool
|
|
- **Type Safety**: Fully type-safe localization API
|
|
- **Fallback Support**: Automatic fallback to Vietnamese if device locale is unsupported
|
|
- **Pluralization**: Full ICU message format support
|
|
- **Parameterized Strings**: Support for dynamic values
|
|
- **Helper Extensions**: Convenient access utilities
|
|
- **Date/Time Formatting**: Locale-specific formatting
|
|
|
|
## Configuration
|
|
|
|
### l10n.yaml
|
|
|
|
```yaml
|
|
arb-dir: lib/l10n
|
|
template-arb-file: app_en.arb
|
|
output-localization-file: app_localizations.dart
|
|
output-dir: lib/generated/l10n
|
|
nullable-getter: false
|
|
```
|
|
|
|
### pubspec.yaml
|
|
|
|
```yaml
|
|
dependencies:
|
|
flutter:
|
|
sdk: flutter
|
|
flutter_localizations:
|
|
sdk: flutter
|
|
|
|
flutter:
|
|
generate: true
|
|
```
|
|
|
|
## File Structure
|
|
|
|
```
|
|
lib/
|
|
l10n/
|
|
app_en.arb # English translations (template)
|
|
app_vi.arb # Vietnamese translations (PRIMARY)
|
|
generated/l10n/ # Auto-generated (DO NOT EDIT)
|
|
app_localizations.dart # Generated base class
|
|
app_localizations_en.dart # Generated English implementation
|
|
app_localizations_vi.dart # Generated Vietnamese implementation
|
|
core/
|
|
utils/
|
|
l10n_extensions.dart # Helper extensions for easy access
|
|
|
|
l10n.yaml # Localization configuration
|
|
LOCALIZATION.md # This guide
|
|
```
|
|
|
|
## Translation Coverage
|
|
|
|
### Comprehensive Feature Coverage (450+ Keys)
|
|
|
|
| Category | Keys | Examples |
|
|
|----------|------|----------|
|
|
| **Authentication** | 25+ | login, phone, verifyOTP, enterOTP, resendOTP, register, logout |
|
|
| **Navigation** | 10+ | home, products, loyalty, account, more, backToHome, goToHomePage |
|
|
| **Common Actions** | 30+ | save, cancel, delete, edit, search, filter, confirm, apply, clear, refresh, share, copy |
|
|
| **Status Labels** | 20+ | pending, processing, shipping, completed, cancelled, active, inactive, expired, draft, sent, accepted, rejected |
|
|
| **Form Labels** | 30+ | name, email, password, address, street, city, district, ward, postalCode, company, taxId, dateOfBirth, gender |
|
|
| **User Types** | 5+ | contractor, architect, distributor, broker, selectUserType |
|
|
| **Loyalty System** | 50+ | points, diamond, platinum, gold, rewards, referral, tierBenefits, pointsMultiplier, specialOffers, exclusiveDiscounts |
|
|
| **Products & Shopping** | 35+ | product, price, addToCart, cart, checkout, sku, brand, model, specification, availability, newArrival, bestSeller |
|
|
| **Cart & Checkout** | 30+ | cartEmpty, updateQuantity, removeFromCart, clearCart, proceedToCheckout, orderSummary, selectAddress, selectPaymentMethod |
|
|
| **Orders & Payments** | 40+ | orders, orderNumber, orderStatus, paymentMethod, deliveryAddress, trackOrder, cancelOrder, orderTimeline, trackingNumber |
|
|
| **Projects & Quotes** | 45+ | projects, createProject, quotes, budget, progress, client, location, projectPhotos, projectDocuments, quoteItems |
|
|
| **Account & Profile** | 40+ | profile, editProfile, addresses, changePassword, uploadAvatar, passwordStrength, enableNotifications, selectLanguage |
|
|
| **Loyalty Transactions** | 20+ | transactionType, earnPoints, redeemPoints, bonusPoints, refundPoints, pointsExpiry, disputeTransaction |
|
|
| **Gifts & Rewards** | 25+ | myGifts, activeGifts, usedGifts, expiredGifts, giftDetails, rewardCategory, vouchers, pointsCost, expiryDate |
|
|
| **Referral Program** | 15+ | referralInvite, referralReward, shareYourCode, friendRegisters, bothGetRewards, totalReferrals |
|
|
| **Validation Messages** | 20+ | fieldRequired, invalidEmail, invalidPhone, passwordTooShort, passwordsNotMatch, incorrectPassword |
|
|
| **Error Messages** | 15+ | error, networkError, serverError, sessionExpired, notFound, unauthorized, connectionError, syncFailed |
|
|
| **Success Messages** | 15+ | success, savedSuccessfully, updatedSuccessfully, deletedSuccessfully, redeemSuccessful, photoUploaded |
|
|
| **Loading States** | 10+ | loading, loadingData, processing, pleaseWait, syncInProgress, syncCompleted |
|
|
| **Empty States** | 15+ | noData, noResults, noProductsFound, noOrdersYet, noProjectsYet, noNotifications, noGiftsYet |
|
|
| **Date & Time** | 20+ | today, yesterday, thisWeek, thisMonth, dateRange, from, to, minutesAgo, hoursAgo, daysAgo, justNow |
|
|
| **Notifications** | 25+ | notifications, markAsRead, markAllAsRead, deleteNotification, clearNotifications, unreadNotifications |
|
|
| **Chat** | 20+ | chat, sendMessage, typeMessage, typingIndicator, online, offline, messageRead, messageDelivered |
|
|
| **Filters & Sorting** | 15+ | filterBy, sortBy, priceAscending, priceDescending, nameAscending, dateAscending, applyFilters |
|
|
| **Offline & Sync** | 15+ | offlineMode, syncData, lastSyncAt, noInternetConnection, checkConnection, retryConnection |
|
|
| **Miscellaneous** | 20+ | version, help, aboutUs, privacyPolicy, termsOfService, feedback, rateApp, comingSoon, underMaintenance |
|
|
|
|
### Special Features
|
|
|
|
#### Pluralization Support
|
|
- `itemsInCart` - 0/1/many items
|
|
- `ordersCount` - 0/1/many orders
|
|
- `projectsCount` - 0/1/many projects
|
|
- `daysRemaining` - 0/1/many days
|
|
|
|
#### Parameterized Translations
|
|
- `welcomeTo(appName)` - Dynamic app name
|
|
- `otpSentTo(phone)` - Phone number
|
|
- `pointsToNextTier(points, tier)` - Points and tier
|
|
- `redeemConfirmMessage(points, reward)` - Redemption confirmation
|
|
- `orderNumberIs(orderNumber)` - Order number display
|
|
- `estimatedDeliveryDate(date)` - Delivery date
|
|
|
|
#### Date/Time Formatting
|
|
- `formatDate` - DD/MM/YYYY (VI) or MM/DD/YYYY (EN)
|
|
- `formatDateTime` - Full date-time with locale
|
|
- `minutesAgo`, `hoursAgo`, `daysAgo`, etc. - Relative time
|
|
|
|
#### Currency Formatting
|
|
- `formatCurrency` - Vietnamese Dong (₫) with proper grouping
|
|
|
|
## Usage Examples
|
|
|
|
### Basic Usage
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
|
|
class LoginPage extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(l10n.login),
|
|
),
|
|
body: Column(
|
|
children: [
|
|
TextField(
|
|
decoration: InputDecoration(
|
|
labelText: l10n.phone,
|
|
hintText: l10n.enterPhone,
|
|
),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {},
|
|
child: Text(l10n.continueButton),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Using Extension for Cleaner Code (Recommended)
|
|
|
|
```dart
|
|
import 'package:worker/core/utils/l10n_extensions.dart';
|
|
|
|
class ProductCard extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// Much cleaner than AppLocalizations.of(context)!
|
|
return Column(
|
|
children: [
|
|
Text(context.l10n.product),
|
|
Text(context.l10n.price),
|
|
ElevatedButton(
|
|
onPressed: () {},
|
|
child: Text(context.l10n.addToCart),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Using Helper Utilities
|
|
|
|
```dart
|
|
import 'package:worker/core/utils/l10n_extensions.dart';
|
|
|
|
class OrderCard extends StatelessWidget {
|
|
final Order order;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Card(
|
|
child: Column(
|
|
children: [
|
|
// Format currency
|
|
Text(L10nHelper.formatCurrency(context, order.total)),
|
|
// Vietnamese: "1.500.000 ₫"
|
|
// English: "1,500,000 ₫"
|
|
|
|
// Format date
|
|
Text(L10nHelper.formatDate(context, order.createdAt)),
|
|
// Vietnamese: "17/10/2025"
|
|
// English: "10/17/2025"
|
|
|
|
// Relative time
|
|
Text(L10nHelper.formatRelativeTime(context, order.createdAt)),
|
|
// Vietnamese: "5 phút trước"
|
|
// English: "5 minutes ago"
|
|
|
|
// Status with helper
|
|
Text(L10nHelper.getOrderStatus(context, order.status)),
|
|
// Returns localized status string
|
|
|
|
// Item count with pluralization
|
|
Text(L10nHelper.formatItemCount(context, order.itemCount)),
|
|
// Vietnamese: "3 sản phẩm"
|
|
// English: "3 items"
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Parameterized Translations
|
|
|
|
```dart
|
|
// Points balance with parameter
|
|
final pointsText = context.l10n.pointsBalance;
|
|
// Result: "1,000 điểm" (Vietnamese) or "1,000 points" (English)
|
|
|
|
// OTP sent message with phone parameter
|
|
final message = AppLocalizations.of(context)!.otpSentTo('0912345678');
|
|
// Result: "Mã OTP đã được gửi đến 0912345678"
|
|
|
|
// Points to next tier with multiple parameters
|
|
final tierMessage = context.l10n.pointsToNextTier;
|
|
// Uses placeholders: {points} and {tier}
|
|
```
|
|
|
|
### Checking Current Language
|
|
|
|
```dart
|
|
import 'package:worker/core/utils/l10n_extensions.dart';
|
|
|
|
class LanguageIndicator extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
children: [
|
|
Text('Language Code: ${context.languageCode}'), // "vi" or "en"
|
|
Text('Is Vietnamese: ${context.isVietnamese}'), // true/false
|
|
Text('Is English: ${context.isEnglish}'), // true/false
|
|
],
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Adding New Translations
|
|
|
|
### Step 1: Add to ARB Files
|
|
|
|
**lib/l10n/app_en.arb** (English - Template):
|
|
```json
|
|
{
|
|
"newFeature": "New Feature",
|
|
"@newFeature": {
|
|
"description": "Description of the new feature"
|
|
}
|
|
}
|
|
```
|
|
|
|
**lib/l10n/app_vi.arb** (Vietnamese):
|
|
```json
|
|
{
|
|
"newFeature": "Tính năng mới",
|
|
"@newFeature": {
|
|
"description": "Description of the new feature"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 2: Regenerate Localization Files
|
|
|
|
```bash
|
|
flutter gen-l10n
|
|
```
|
|
|
|
### Step 3: Use in Code
|
|
|
|
```dart
|
|
Text(context.l10n.newFeature)
|
|
```
|
|
|
|
## Parameterized Translations
|
|
|
|
### Simple Parameter
|
|
|
|
**ARB File:**
|
|
```json
|
|
{
|
|
"welcome": "Welcome, {name}!",
|
|
"@welcome": {
|
|
"description": "Welcome message",
|
|
"placeholders": {
|
|
"name": {
|
|
"type": "String",
|
|
"example": "John"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```dart
|
|
Text(context.l10n.welcome('John'))
|
|
// Result: "Welcome, John!"
|
|
```
|
|
|
|
### Multiple Parameters
|
|
|
|
**ARB File:**
|
|
```json
|
|
{
|
|
"orderSummary": "Order #{orderNumber} for {amount}",
|
|
"@orderSummary": {
|
|
"description": "Order summary text",
|
|
"placeholders": {
|
|
"orderNumber": {
|
|
"type": "String",
|
|
"example": "12345"
|
|
},
|
|
"amount": {
|
|
"type": "String",
|
|
"example": "100,000 ₫"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```dart
|
|
Text(context.l10n.orderSummary('12345', '100,000 ₫'))
|
|
```
|
|
|
|
### Number Parameters
|
|
|
|
**ARB File:**
|
|
```json
|
|
{
|
|
"itemCount": "{count} items",
|
|
"@itemCount": {
|
|
"description": "Number of items",
|
|
"placeholders": {
|
|
"count": {
|
|
"type": "int",
|
|
"example": "5"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```dart
|
|
Text(context.l10n.itemCount(5))
|
|
// Result: "5 items"
|
|
```
|
|
|
|
## Pluralization
|
|
|
|
Flutter's localization supports pluralization with the ICU message format:
|
|
|
|
**ARB File:**
|
|
```json
|
|
{
|
|
"itemCountPlural": "{count,plural, =0{No items} =1{1 item} other{{count} items}}",
|
|
"@itemCountPlural": {
|
|
"description": "Item count with pluralization",
|
|
"placeholders": {
|
|
"count": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```dart
|
|
Text(context.l10n.itemCountPlural(0)) // "No items"
|
|
Text(context.l10n.itemCountPlural(1)) // "1 item"
|
|
Text(context.l10n.itemCountPlural(5)) // "5 items"
|
|
```
|
|
|
|
## Date & Time Formatting
|
|
|
|
Use the `intl` package for locale-aware date/time formatting:
|
|
|
|
```dart
|
|
import 'package:intl/intl.dart';
|
|
|
|
// Format date based on current locale
|
|
final now = DateTime.now();
|
|
final locale = Localizations.localeOf(context).toString();
|
|
|
|
// Vietnamese: "17/10/2025"
|
|
// English: "10/17/2025"
|
|
final dateFormatter = DateFormat.yMd(locale);
|
|
final formattedDate = dateFormatter.format(now);
|
|
|
|
// Vietnamese: "17 tháng 10, 2025"
|
|
// English: "October 17, 2025"
|
|
final longDateFormatter = DateFormat.yMMMMd(locale);
|
|
final formattedLongDate = longDateFormatter.format(now);
|
|
```
|
|
|
|
## Changing Language at Runtime
|
|
|
|
### Create Language Provider
|
|
|
|
```dart
|
|
// lib/core/providers/language_provider.dart
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
final languageProvider = StateNotifierProvider<LanguageNotifier, Locale>((ref) {
|
|
return LanguageNotifier();
|
|
});
|
|
|
|
class LanguageNotifier extends StateNotifier<Locale> {
|
|
LanguageNotifier() : super(const Locale('vi', 'VN')) {
|
|
_loadSavedLanguage();
|
|
}
|
|
|
|
Future<void> _loadSavedLanguage() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final languageCode = prefs.getString('language_code') ?? 'vi';
|
|
final countryCode = prefs.getString('country_code') ?? 'VN';
|
|
state = Locale(languageCode, countryCode);
|
|
}
|
|
|
|
Future<void> setLanguage(Locale locale) async {
|
|
state = locale;
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setString('language_code', locale.languageCode);
|
|
await prefs.setString('country_code', locale.countryCode ?? '');
|
|
}
|
|
|
|
void setVietnamese() => setLanguage(const Locale('vi', 'VN'));
|
|
void setEnglish() => setLanguage(const Locale('en', 'US'));
|
|
}
|
|
```
|
|
|
|
### Update WorkerApp
|
|
|
|
```dart
|
|
class WorkerApp extends ConsumerWidget {
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final locale = ref.watch(languageProvider);
|
|
|
|
return MaterialApp(
|
|
locale: locale,
|
|
// ... other configurations
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Language Selector Widget
|
|
|
|
```dart
|
|
class LanguageSelector extends ConsumerWidget {
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final currentLocale = ref.watch(languageProvider);
|
|
|
|
return DropdownButton<Locale>(
|
|
value: currentLocale,
|
|
items: const [
|
|
DropdownMenuItem(
|
|
value: Locale('vi', 'VN'),
|
|
child: Text('Tiếng Việt'),
|
|
),
|
|
DropdownMenuItem(
|
|
value: Locale('en', 'US'),
|
|
child: Text('English'),
|
|
),
|
|
],
|
|
onChanged: (locale) {
|
|
if (locale != null) {
|
|
ref.read(languageProvider.notifier).setLanguage(locale);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### 1. Naming Conventions
|
|
|
|
- Use **camelCase** for translation keys
|
|
- Be descriptive but concise
|
|
- Group related translations with prefixes (e.g., `order*`, `payment*`)
|
|
- Avoid abbreviations
|
|
|
|
**Good:**
|
|
```json
|
|
{
|
|
"loginButton": "Login",
|
|
"orderNumber": "Order Number",
|
|
"paymentMethod": "Payment Method"
|
|
}
|
|
```
|
|
|
|
**Bad:**
|
|
```json
|
|
{
|
|
"btn_login": "Login",
|
|
"ord_num": "Order Number",
|
|
"pay_meth": "Payment Method"
|
|
}
|
|
```
|
|
|
|
### 2. Reserved Keywords
|
|
|
|
Avoid Dart reserved keywords as translation keys:
|
|
|
|
- `continue` → Use `continueButton` instead
|
|
- `switch` → Use `switchButton` instead
|
|
- `class` → Use `className` instead
|
|
- `return` → Use `returnButton` instead
|
|
|
|
### 3. Context in Descriptions
|
|
|
|
Always add `@` descriptions to provide context:
|
|
|
|
```json
|
|
{
|
|
"save": "Save",
|
|
"@save": {
|
|
"description": "Button label to save changes"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Consistent Formatting
|
|
|
|
Maintain consistent capitalization and punctuation:
|
|
|
|
**Vietnamese:**
|
|
- Sentence case for labels
|
|
- No period at the end of single phrases
|
|
- Use full Vietnamese diacritics
|
|
|
|
**English:**
|
|
- Title Case for buttons and headers
|
|
- Sentence case for descriptions
|
|
- Consistent use of punctuation
|
|
|
|
### 5. Placeholder Examples
|
|
|
|
Always provide example values for placeholders:
|
|
|
|
```json
|
|
{
|
|
"greeting": "Hello, {name}!",
|
|
"@greeting": {
|
|
"description": "Greeting message",
|
|
"placeholders": {
|
|
"name": {
|
|
"type": "String",
|
|
"example": "John"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Testing Localizations
|
|
|
|
### Widget Tests
|
|
|
|
```dart
|
|
testWidgets('Login page shows Vietnamese translations', (tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
locale: const Locale('vi', 'VN'),
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
home: LoginPage(),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Đăng nhập'), findsOneWidget);
|
|
expect(find.text('Số điện thoại'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Login page shows English translations', (tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
locale: const Locale('en', 'US'),
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
home: LoginPage(),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Login'), findsOneWidget);
|
|
expect(find.text('Phone Number'), findsOneWidget);
|
|
});
|
|
```
|
|
|
|
### Translation Completeness Test
|
|
|
|
```dart
|
|
void main() {
|
|
test('All Vietnamese translations match English keys', () {
|
|
final enFile = File('lib/l10n/app_en.arb');
|
|
final viFile = File('lib/l10n/app_vi.arb');
|
|
|
|
final enJson = jsonDecode(enFile.readAsStringSync());
|
|
final viJson = jsonDecode(viFile.readAsStringSync());
|
|
|
|
final enKeys = enJson.keys.where((k) => !k.startsWith('@')).toList();
|
|
final viKeys = viJson.keys.where((k) => !k.startsWith('@')).toList();
|
|
|
|
expect(enKeys.length, viKeys.length);
|
|
for (final key in enKeys) {
|
|
expect(viKeys.contains(key), isTrue, reason: 'Missing key: $key');
|
|
}
|
|
});
|
|
}
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Issue: "AppLocalizations not found"
|
|
|
|
**Solution:** Run the code generator:
|
|
```bash
|
|
flutter gen-l10n
|
|
```
|
|
|
|
### Issue: "Duplicate keys in ARB file"
|
|
|
|
**Solution:** Each key must be unique within an ARB file. Check for duplicates.
|
|
|
|
### Issue: "Invalid placeholder type"
|
|
|
|
**Solution:** Supported types are: `String`, `num`, `int`, `double`, `DateTime`, `Object`
|
|
|
|
### Issue: "Translations not updating"
|
|
|
|
**Solution:**
|
|
1. Run `flutter gen-l10n`
|
|
2. Hot restart (not hot reload) the app
|
|
3. Clear build cache if needed: `flutter clean`
|
|
|
|
## Translation Workflow
|
|
|
|
### For Developers
|
|
|
|
1. **Add English translation** to `app_en.arb`
|
|
2. **Add Vietnamese translation** to `app_vi.arb`
|
|
3. **Run code generator**: `flutter gen-l10n`
|
|
4. **Use in code**: `context.l10n.newKey`
|
|
5. **Test both languages**
|
|
|
|
### For Translators
|
|
|
|
1. **Review** the English ARB file (`app_en.arb`)
|
|
2. **Translate** each key to Vietnamese in `app_vi.arb`
|
|
3. **Maintain** the same structure and placeholders
|
|
4. **Add** `@key` descriptions if needed
|
|
5. **Test** context and meaning
|
|
|
|
## Resources
|
|
|
|
- [Flutter Internationalization](https://docs.flutter.dev/development/accessibility-and-localization/internationalization)
|
|
- [ARB File Format](https://github.com/google/app-resource-bundle/wiki/ApplicationResourceBundleSpecification)
|
|
- [ICU Message Format](https://unicode-org.github.io/icu/userguide/format_parse/messages/)
|
|
- [Intl Package](https://pub.dev/packages/intl)
|
|
|
|
## Translation Statistics
|
|
|
|
- **Total Translation Keys**: 450+
|
|
- **Languages**: 2 (Vietnamese, English)
|
|
- **Coverage**: 100% (Both languages fully translated)
|
|
- **Parameterized Keys**: 20+
|
|
- **Pluralization Keys**: 10+
|
|
- **Categories**: 26 major categories
|
|
- **Helper Functions**: 15+ utility methods
|
|
|
|
## Quick Reference Table
|
|
|
|
| Category | Key Count | Examples |
|
|
|----------|-----------|----------|
|
|
| Authentication | 25+ | login, phone, verifyOTP, register, logout |
|
|
| Navigation | 10+ | home, products, loyalty, account, more |
|
|
| Common Actions | 30+ | save, cancel, delete, edit, search, filter |
|
|
| Status Labels | 20+ | pending, completed, active, expired |
|
|
| Form Labels | 30+ | name, email, address, company, taxId |
|
|
| User Types | 5+ | contractor, architect, distributor, broker |
|
|
| Loyalty System | 50+ | points, rewards, referral, tierBenefits |
|
|
| Products | 35+ | product, price, cart, sku, brand |
|
|
| Cart & Checkout | 30+ | cartEmpty, updateQuantity, orderSummary |
|
|
| Orders & Payments | 40+ | orderNumber, payment, trackOrder |
|
|
| Projects & Quotes | 45+ | projectName, budget, quotes |
|
|
| Account & Profile | 40+ | profile, settings, addresses |
|
|
| Loyalty Transactions | 20+ | earnPoints, redeemPoints, bonusPoints |
|
|
| Gifts & Rewards | 25+ | myGifts, activeGifts, rewardCategory |
|
|
| Referral Program | 15+ | referralInvite, shareYourCode |
|
|
| Validation Messages | 20+ | fieldRequired, invalidEmail |
|
|
| Error Messages | 15+ | error, networkError, sessionExpired |
|
|
| Success Messages | 15+ | success, savedSuccessfully |
|
|
| Loading States | 10+ | loading, processing, syncInProgress |
|
|
| Empty States | 15+ | noData, noResults, noProductsFound |
|
|
| Date & Time | 20+ | today, yesterday, minutesAgo |
|
|
| Notifications | 25+ | notifications, markAsRead |
|
|
| Chat | 20+ | chat, sendMessage, typingIndicator |
|
|
| Filters & Sorting | 15+ | filterBy, sortBy, applyFilters |
|
|
| Offline & Sync | 15+ | offlineMode, syncData, lastSyncAt |
|
|
| Miscellaneous | 20+ | version, help, feedback, comingSoon |
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
The Worker app localization system provides:
|
|
|
|
- **Comprehensive Coverage**: 450+ translation keys across 26 categories
|
|
- **Full Bilingual Support**: Vietnamese (primary) and English (secondary)
|
|
- **Advanced Features**: Pluralization, parameterization, date/time formatting
|
|
- **Developer-Friendly**: Helper extensions and utilities for easy integration
|
|
- **Type-Safe**: Flutter's code generation ensures compile-time safety
|
|
- **Maintainable**: Clear organization and documentation
|
|
|
|
### Key Files
|
|
|
|
- `/Users/ssg/project/worker/lib/l10n/app_vi.arb` - Vietnamese translations
|
|
- `/Users/ssg/project/worker/lib/l10n/app_en.arb` - English translations
|
|
- `/Users/ssg/project/worker/lib/core/utils/l10n_extensions.dart` - Helper utilities
|
|
- `/Users/ssg/project/worker/l10n.yaml` - Configuration
|
|
- `/Users/ssg/project/worker/LOCALIZATION.md` - This documentation
|
|
|
|
---
|
|
|
|
**Last Updated**: October 17, 2025
|
|
**Version**: 1.0.0
|
|
**Languages Supported**: Vietnamese (Primary), English (Secondary)
|
|
**Total Translation Keys**: 450+
|
|
**Maintained By**: Worker App Development Team
|