Skip to content

Commit dfdd2c6

Browse files
Merge pull request #152 from SoiBien-AI/feat/vietnamese-localization
feat(i18n): add Vietnamese translation and UI localization keys
2 parents 93ed023 + 4533420 commit dfdd2c6

45 files changed

Lines changed: 10315 additions & 6685 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/app/Providers/AppServiceProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ private function availableLocales(): array
164164
'pl' => 'Polski',
165165
'tr' => 'Türkçe',
166166
'de' => 'Deutsch',
167+
'vi' => 'Tiếng Việt',
167168
];
168169
}
169170
}

backend/config/app.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
'pl' => 'Polski',
9494
'tr' => 'Türkçe',
9595
'de' => 'Deutsch',
96+
'vi' => 'Tiếng Việt',
9697
],
9798

9899
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),

backend/lang/de.json

Lines changed: 1936 additions & 1935 deletions
Large diffs are not rendered by default.

backend/lang/en.json

Lines changed: 1936 additions & 1935 deletions
Large diffs are not rendered by default.

backend/lang/pl.json

Lines changed: 1936 additions & 1935 deletions
Large diffs are not rendered by default.

backend/lang/tr.json

Lines changed: 248 additions & 247 deletions
Large diffs are not rendered by default.

backend/lang/vi.json

Lines changed: 3599 additions & 0 deletions
Large diffs are not rendered by default.

backend/resources/js/Pages/admin/AuditLogs.jsx

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useState } from 'react';
22
import { Head, router, usePage } from '@inertiajs/react';
33
import AppLayout from '../../layouts/AppLayout';
4+
import { __ } from '../../lib/i18n';
45

56
const ACTION_STYLES = {
67
created: 'bg-green-100 text-green-800',
@@ -12,7 +13,7 @@ function ActionBadge({ action }) {
1213
const cls = ACTION_STYLES[action] ?? 'bg-gray-100 text-gray-600';
1314
return (
1415
<span className={`px-2 py-1 rounded text-xs font-medium ${cls}`}>
15-
{action ? action.charAt(0).toUpperCase() + action.slice(1) : '—'}
16+
{action ? __(action.charAt(0).toUpperCase() + action.slice(1)) : '—'}
1617
</span>
1718
);
1819
}
@@ -28,7 +29,7 @@ function ExpandableChanges({ log }) {
2829
onClick={() => setExpanded((v) => !v)}
2930
className="text-blue-600 hover:text-blue-800 text-sm"
3031
>
31-
{expanded ? 'Hide' : 'View'} Changes
32+
{expanded ? __('Hide') : __('View')} {__('Changes')}
3233
</button>
3334
{expanded && (
3435
<div className="mt-2 p-3 bg-gray-50 rounded text-xs">
@@ -47,13 +48,13 @@ function ExpandableChanges({ log }) {
4748
if (log.action === 'created') {
4849
return (
4950
<span className="text-gray-500 text-sm">
50-
Created with {Object.keys(log.after_state ?? {}).length} fields
51+
{__('Created with :count fields', { count: Object.keys(log.after_state ?? {}).length })}
5152
</span>
5253
);
5354
}
5455

5556
if (log.action === 'deleted') {
56-
return <span className="text-gray-500 text-sm">Record deleted</span>;
57+
return <span className="text-gray-500 text-sm">{__('Record deleted')}</span>;
5758
}
5859

5960
return null;
@@ -125,52 +126,52 @@ export default function AuditLogs() {
125126

126127
return (
127128
<>
128-
<Head title="Audit Logs" />
129+
<Head title={__('Audit Logs')} />
129130
<div className="max-w-7xl mx-auto">
130131
{/* Header */}
131132
<div className="mb-6">
132-
<h1 className="text-3xl font-bold text-gray-800">Audit Logs</h1>
133-
<p className="text-gray-600 mt-2">Track all system changes and user activities</p>
133+
<h1 className="text-3xl font-bold text-gray-800">{__('Audit Logs')}</h1>
134+
<p className="text-gray-600 mt-2">{__('Track all system changes and user activities')}</p>
134135
</div>
135136

136137
{/* Filters */}
137138
<div className="bg-white rounded-lg shadow-sm p-5 mb-6">
138139
<div className="flex justify-between items-center mb-4">
139-
<h2 className="text-lg font-bold text-gray-800">Filters</h2>
140+
<h2 className="text-lg font-bold text-gray-800">{__('Filters')}</h2>
140141
<button
141142
type="button"
142143
onClick={() => setShowFilters((v) => !v)}
143144
className="px-4 py-2 text-sm font-medium rounded-lg border border-gray-300 text-gray-700 hover:bg-gray-50 transition-colors"
144145
>
145-
{showFilters ? 'Hide Filters' : 'Show Filters'}
146+
{showFilters ? __('Hide Filters') : __('Show Filters')}
146147
</button>
147148
</div>
148149

149150
{showFilters && (
150151
<div>
151152
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
152153
<div>
153-
<label className="block text-sm font-medium text-gray-700 mb-1">Entity Type</label>
154+
<label className="block text-sm font-medium text-gray-700 mb-1">{__('Entity Type')}</label>
154155
<select
155156
value={form.entity_type}
156157
onChange={(e) => setForm((f) => ({ ...f, entity_type: e.target.value }))}
157158
className="form-input w-full"
158159
>
159-
<option value="">All Types</option>
160+
<option value="">{__('All Types')}</option>
160161
{entityTypes.map((t) => (
161162
<option key={t} value={t}>{t}</option>
162163
))}
163164
</select>
164165
</div>
165166

166167
<div>
167-
<label className="block text-sm font-medium text-gray-700 mb-1">User</label>
168+
<label className="block text-sm font-medium text-gray-700 mb-1">{__('User')}</label>
168169
<select
169170
value={form.user_id}
170171
onChange={(e) => setForm((f) => ({ ...f, user_id: e.target.value }))}
171172
className="form-input w-full"
172173
>
173-
<option value="">All Users</option>
174+
<option value="">{__('All Users')}</option>
174175
{users.map((u) => (
175176
<option key={u.id} value={u.id}>
176177
{u.name} ({u.username})
@@ -180,21 +181,21 @@ export default function AuditLogs() {
180181
</div>
181182

182183
<div>
183-
<label className="block text-sm font-medium text-gray-700 mb-1">Action</label>
184+
<label className="block text-sm font-medium text-gray-700 mb-1">{__('Action')}</label>
184185
<select
185186
value={form.action}
186187
onChange={(e) => setForm((f) => ({ ...f, action: e.target.value }))}
187188
className="form-input w-full"
188189
>
189-
<option value="">All Actions</option>
190-
<option value="created">Created</option>
191-
<option value="updated">Updated</option>
192-
<option value="deleted">Deleted</option>
190+
<option value="">{__('All Actions')}</option>
191+
<option value="created">{__('Created')}</option>
192+
<option value="updated">{__('Updated')}</option>
193+
<option value="deleted">{__('Deleted')}</option>
193194
</select>
194195
</div>
195196

196197
<div>
197-
<label className="block text-sm font-medium text-gray-700 mb-1">Start Date</label>
198+
<label className="block text-sm font-medium text-gray-700 mb-1">{__('Start Date')}</label>
198199
<input
199200
type="date"
200201
value={form.start_date}
@@ -204,7 +205,7 @@ export default function AuditLogs() {
204205
</div>
205206

206207
<div>
207-
<label className="block text-sm font-medium text-gray-700 mb-1">End Date</label>
208+
<label className="block text-sm font-medium text-gray-700 mb-1">{__('End Date')}</label>
208209
<input
209210
type="date"
210211
value={form.end_date}
@@ -220,20 +221,20 @@ export default function AuditLogs() {
220221
onClick={() => apply()}
221222
className="px-4 py-2 text-sm font-medium rounded-lg bg-blue-600 text-white hover:bg-blue-700 transition-colors"
222223
>
223-
Apply Filters
224+
{__('Apply Filters')}
224225
</button>
225226
<button
226227
type="button"
227228
onClick={clear}
228229
className="px-4 py-2 text-sm font-medium rounded-lg border border-gray-300 text-gray-700 hover:bg-gray-50 transition-colors"
229230
>
230-
Clear Filters
231+
{__('Clear Filters')}
231232
</button>
232233
<a
233234
href={exportUrl()}
234235
className="ml-auto px-4 py-2 text-sm font-medium rounded-lg border border-gray-300 text-gray-700 hover:bg-gray-50 transition-colors"
235236
>
236-
Export to CSV
237+
{__('Export to CSV')}
237238
</a>
238239
</div>
239240
</div>
@@ -243,18 +244,18 @@ export default function AuditLogs() {
243244
{/* Table */}
244245
<div className="bg-white rounded-lg shadow-sm">
245246
{logs.length === 0 ? (
246-
<div className="text-center py-12 text-gray-500">No audit logs found</div>
247+
<div className="text-center py-12 text-gray-500">{__('No audit logs found')}</div>
247248
) : (
248249
<>
249250
<div className="overflow-x-auto">
250251
<table className="min-w-full divide-y divide-gray-200">
251252
<thead className="bg-gray-50">
252253
<tr>
253-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Timestamp</th>
254-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">User</th>
255-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Entity</th>
256-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Action</th>
257-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Details</th>
254+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{__('Timestamp')}</th>
255+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{__('User')}</th>
256+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{__('Entity')}</th>
257+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{__('Action')}</th>
258+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{__('Details')}</th>
258259
</tr>
259260
</thead>
260261
<tbody className="bg-white divide-y divide-gray-200">
@@ -266,7 +267,7 @@ export default function AuditLogs() {
266267
: '—'}
267268
</td>
268269
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
269-
{log.user?.name ?? 'System'}
270+
{log.user?.name ?? __('System')}
270271
</td>
271272
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
272273
{log.entity_type

0 commit comments

Comments
 (0)