feat: add i18n support for Vietnamese and English
Some checks failed
Tests / PHP 8.3 (push) Has been cancelled
Tests / PHP 8.4 (push) Has been cancelled
Tests / PHP 8.5 (push) Has been cancelled

This commit is contained in:
2026-05-04 10:40:39 +00:00
parent 3a8db5cae6
commit 8c6b71cb8a
64 changed files with 4652 additions and 254 deletions

107
AGENTS.md
View File

@@ -68,11 +68,19 @@ minicrm/
│ │ │ ├── Customers/
│ │ │ │ └── RelationManagers/FeedbacksRelationManager.php
│ │ │ ├── FeedbackChannels/
│ │ │ ── FeedbackTags/
│ │ │ ── FeedbackTags/
│ │ │ ├── Contracts/
│ │ │ ├── Users/
│ │ │ └── Roles/
│ │ └── Widgets/
│ │ ├── ResolvedTicketsWidget.php # Dashboard: resolved tickets table
│ │ ├── AvgProcessingTimeWidget.php # Dashboard: stats overview
│ │ └── HandlerPerformanceWidget.php # Dashboard: bar chart per handler
│ ├── Enums/
│ │ ├── ContractType.php
│ │ ├── ContractStatus.php
│ │ ├── HandoverStatus.php
│ │ └── ServiceType.php
│ ├── Models/
│ │ ├── User.php # HasRoles (Spatie), isAdmin(), isManager()
│ │ ├── Department.php # NEW: name, manager_id → User
@@ -101,6 +109,15 @@ minicrm/
│ │ └── TicketClosed.php # DB + Mail for ticket closure
│ └── Providers/Filament/
│ └── AdminPanelProvider.php
├── lang/ # i18n translation files
│ ├── en/
│ │ ├── app.php # General UI labels, navigation, status, widgets
│ │ ├── feedback.php # Feedback domain: services, notifications, interactions
│ │ └── enums.php # Enum labels
│ └── vi/
│ ├── app.php
│ ├── feedback.php
│ └── enums.php
├── resources/views/filament/resources/feedback/
│ ├── similar-cases.blade.php
│ └── attachment-links.blade.php # Modal listing attachments with signed download URLs
@@ -217,19 +234,84 @@ feedback_interactions, feedback_tags, feedback_feedback_tag, products, customers
- AvgProcessingTimeWidget: 4 stats (resolved count, avg time, pending count, escalated count)
- HandlerPerformanceWidget: bar chart of avg processing time per handler
## Internationalization (i18n)
### Configuration
- Default locale: `vi` (Vietnamese), fallback: `en` (English)
- Set in `.env`: `APP_LOCALE=vi`, `APP_FALLBACK_LOCALE=en`
- Filament uses `app()->getLocale()` — no extra panel config needed
### Translation Files
```
lang/
├── en/
│ ├── app.php # General UI: navigation, labels, status, widgets, blade strings
│ ├── feedback.php # Feedback domain: services, notifications, interactions
│ └── enums.php # Enum labels (HandoverStatus, ServiceType, ContractStatus, ContractType)
└── vi/
├── app.php
├── feedback.php
└── enums.php
```
### Translation Patterns
```php
// Resource labels — MUST override getPluralLabel() (not auto-translated)
public static function getPluralLabel(): ?string
{
return __('app.resource_feedbacks');
}
// Navigation group — MUST override getNavigationGroup() (not auto-translated)
public static function getNavigationGroup(): ?string
{
return __('app.nav_customer_care');
}
// Form fields — MUST add explicit ->label()
TextInput::make('name')->label(__('app.name'))
// Status options
'options' => [
'pending' => __('app.status_pending'),
'processing' => __('app.status_processing'),
]
// Enum labels
public function label(): string
{
return match ($this) {
self::ACTIVE => __('enums.contract_status.active'),
};
}
// Widget heading/description — override getHeading()/getDescription()
public function getHeading(): string | Htmlable | null
{
return __('app.widget_avg_time_by_handler');
}
```
### Key Gotchas
- `$pluralLabel` property does NOT auto-translate — must override `getPluralLabel()`
- `getNavigationGroup()` does NOT auto-translate — must call `__()` explicitly
- `$heading`/`$description` in ChartWidget are raw properties — must override `getHeading()`/`getDescription()`
- Form fields without `->label()` use Filament's `filament-panels::` namespace (may not resolve correctly)
- Always add `->label(__('key'))` to TextInput, Select, Textarea, etc.
## Navigation Structure
```
Dashboard
├── Customer Care (group)
│ └── Feedbacks (chat-bubble-left-right, sort=1)
└── Management (group)
├── Products (building-storefront, sort=1)
├── Customers (user-group, sort=2)
├── Channels (inbox-stack, sort=3)
├── Tags (tag, sort=default)
├── Users (users, sort=5) — admin only
└── Roles (shield-check, sort=6) — admin only
├── Customer Care / Chăm sóc khách hàng (group)
│ └── Feedbacks / Phản ánh (chat-bubble-left-right, sort=1)
└── Management / Quản lý (group)
├── Products / Sản phẩm (building-storefront, sort=1)
├── Customers / Khách hàng (user-group, sort=2)
├── Channels / Kênh phản ánh (inbox-stack, sort=3)
├── Tags / Thẻ (tag, sort=default)
├── Users / Người dùng (users, sort=5) — admin only
└── Roles / Vai trò (shield-check, sort=6) — admin only
```
Dashboard widgets: ResolvedTicketsWidget, AvgProcessingTimeWidget, HandlerPerformanceWidget (admin/manager only)
@@ -337,3 +419,8 @@ $user->assignRole('manager');
8. **ChartWidget properties**: `$heading` and `$columnSpan` are non-static in Filament's ChartWidget.
9. **Manager closing permission**: Manager must be department head (`Department.manager_id`) to close tickets in that department.
10. **Docker**: Entrypoint auto-generates APP_KEY and runs migrations. SQLite persists via volume mount.
11. **i18n - pluralLabel**: `$pluralLabel` property does NOT auto-translate — must override `getPluralLabel()` and call `__()`.
12. **i18n - navigationGroup**: `getNavigationGroup()` does NOT auto-translate — must return `__('key')` explicitly.
13. **i18n - ChartWidget heading**: `$heading`/`$description` are raw properties — must override `getHeading()`/`getDescription()`.
14. **i18n - form labels**: Form fields without `->label()` use Filament's `filament-panels::` namespace — always add explicit `->label(__('key'))`.
15. **i18n - Filament vendor**: Filament ships 57 Vietnamese translation files — auto-activated when `APP_LOCALE=vi`.