# Hướng dẫn Đa ngôn ngữ (i18n) ## Tổng quan Hệ thống hỗ trợ 2 ngôn ngữ: - **Tiếng Việt (vi)** - Ngôn ngữ chính - **Tiếng Anh (en)** - Ngôn ngữ dự phòng Cấu hình trong `.env`: ``` APP_LOCALE=vi APP_FALLBACK_LOCALE=en ``` ## Cấu trúc thư mục ``` lang/ ├── en/ │ ├── app.php # UI chung: navigation, labels, status, widgets │ ├── feedback.php # Domain feedback: services, notifications, interactions │ └── enums.php # Labels cho enums └── vi/ ├── app.php ├── feedback.php └── enums.php ``` ## Quy tắc đặt tên Translation Keys ### 1. Resource Labels ```php // Pattern: app.resource_{name} 'resource_feedbacks' => 'Phản ánh', 'resource_products' => 'Sản phẩm', 'resource_customers' => 'Khách hàng', ``` ### 2. Status Labels ```php // Pattern: app.status_{name} 'status_pending' => 'Chờ xử lý', 'status_processing' => 'Đang xử lý', 'status_resolved' => 'Đã xử lý', 'status_closed' => 'Đã đóng', ``` ### 3. Form Labels ```php // Pattern: app.{field_name} 'name' => 'Tên', 'email' => 'Email', 'phone' => 'Điện thoại', ``` ### 4. Enum Labels ```php // Pattern: enums.{enum_name}.{case_value} 'service.management_fee' => 'Phí quản lý', 'contract_status.active' => 'Đang hiệu lực', ``` ### 5. Feedback Domain ```php // Pattern: feedback.{action}_{context} 'already_closed' => 'Phiếu này đã được đóng.', 'close_permission_denied' => 'Chỉ lãnh đạo cấp phòng mới có quyền đóng phiếu.', ``` ## Cách sử dụng trong code ### Resource Labels ```php // Phải override getPluralLabel() - KHÔNG tự dịch public static function getPluralLabel(): ?string { return __('app.resource_feedbacks'); } // Phải override getNavigationGroup() - KHÔNG tự dịch public static function getNavigationGroup(): ?string { return __('app.nav_customer_care'); } ``` ### Form Fields ```php // PHẢI thêm ->label() explicit TextInput::make('name')->label(__('app.name')) Select::make('status')->label(__('app.status')) ``` ### RelationManager Title ```php // Phải override getTitle() - KHÔNG dùng $title property public static function getTitle(Model $ownerRecord, string $pageClass): string { return __('app.services'); } ``` ### Widget Heading/Description ```php // Phải override getHeading()/getDescription() - KHÔNG dùng property public function getHeading(): string | Htmlable | null { return __('app.widget_avg_time_by_handler'); } ``` ### Enum Labels ```php public function label(): string { return match ($this) { self::ACTIVE => __('enums.contract_status.active'), }; } ``` ## Cách thêm Translation Key mới ### Bước 1: Thêm vào `lang/vi/app.php` ```php return [ // ... existing keys ... 'new_key' => 'Giá trị tiếng Việt', ]; ``` ### Bước 2: Thêm vào `lang/en/app.php` ```php return [ // ... existing keys ... 'new_key' => 'English value', ]; ``` ### Bước 3: Sử dụng trong code ```php echo __('app.new_key'); ``` ## Lưu ý quan trọng 1. **`$pluralLabel` property** - KHÔNG tự dịch, phải override `getPluralLabel()` 2. **`getNavigationGroup()`** - KHÔNG tự dịch, phải gọi `__()` 3. **`$heading`/`$description`** trong ChartWidget - KHÔNG tự dịch, phải override methods 4. **Form fields** - PHẢI thêm `->label()` explicit 5. **`$title`** trong RelationManager - KHÔNG tự dịch, phải override `getTitle()` 6. **Filament vendor** - Có sẵn 57 file dịch tiếng Việt, tự kích hoạt khi `APP_LOCALE=vi` ## Kiểm tra Translation ```bash # Kiểm tra key có tồn tại không php artisan tinker --execute="echo __('app.new_key');" # Kiểm tra locale hiện tại php artisan tinker --execute="echo app()->getLocale();" ```