🌐 Ngôn ngữ: 🇻🇳 Tiếng Việt (hiện tại) · 🇬🇧 English
Cách sử dụng ngôn ngữ (đa ngôn ngữ / i18n) trong hệ thống GP247
Giới thiệu
Tài liệu này giải thích hệ thống đa ngôn ngữ (i18n) của GP247 hoạt động ra sao: chuỗi dịch được lưu ở đâu, hệ thống chọn ngôn ngữ hiển thị theo thứ tự ưu tiên nào, và cách bạn thêm ngôn ngữ mới, chỉnh sửa từng câu chữ, cũng như cập nhật lại chuỗi dịch khi nâng cấp gói. Tài liệu dành cho lập trình viên và người quản trị site GP247. Đọc xong bạn hiểu rõ cơ chế và biết chính xác vào đâu để đổi một dòng chữ hiển thị trên site/admin.
ℹ️ Thuật ngữ nhanh: locale = mã ngôn ngữ ngắn như
vi,en. Chuỗi dịch (translation string) = một cặp "mã → câu chữ" (ví dụaction.save→Lưu). Admin = khu vực quản trị site.
1. Cách thức hoạt động
GP247 dùng mô hình ưu tiên cơ sở dữ liệu (DB-first). Có hai khái niệm tách biệt, tương ứng hai bảng trong DB:
| Bảng | Vai trò | Ví dụ nội dung |
|---|---|---|
admin_language |
Danh sách các ngôn ngữ đã cài trên site (bật/tắt, thứ tự, cờ, RTL) | vi — Tiếng Việt · en — English |
languages |
Kho câu chữ thực tế (mã → text, theo từng locale) | action.save / vi → Lưu |
(Tên bảng thật có tiền tố theo GP247_DB_PREFIX, mặc định gp247_ — ví dụ gp247_languages.)
1.1. Chuỗi dịch nằm ở đâu?
- Nguồn chính là bảng
languagestrong DB. Đây là nơi mọi câu chữ được đọc ra để hiển thị. - File ngôn ngữ trên ổ đĩa chỉ là phương án dự phòng. Mỗi gói có sẵn thư mục
Lang/{en,vi}/(ví dụvendor/gp247/core/src/Lang/), đăng ký qua Laravel dưới namespacegp247::. Chúng chỉ được dùng khi trong DB không có mã tương ứng. - Dữ liệu ban đầu cho bảng
languagesđến từ seeder của từng gói (không phải từ file lang), xem mục 4.
Cột của bảng languages:
code // mã chuỗi dịch, ví dụ "action.save" (unique cùng với location)
text // câu chữ đã dịch, ví dụ "Lưu"
position // nhóm/khu vực, ví dụ "action", "store.admin" (dùng để lọc trong admin)
location // locale, ví dụ "en" hoặc "vi"
Cột của bảng admin_language: name (tên hiển thị), code (mã locale, duy nhất), icon (cờ),
status (1 = đang bật), rtl (1 = bố cục phải-sang-trái), sort (thứ tự sắp xếp).
1.2. Luồng hiển thị một câu chữ
Trong code/view, câu chữ được lấy ra qua hàm helper gp247_language_render():
gp247_language_render('action.save'); // -> "Lưu" (nếu locale hiện tại là vi)
gp247_language_render('welcome', ['name' => 'An']); // thay :name trong chuỗi bằng "An"
Hàm này chạy theo trình tự:
- Xác định locale hiện tại bằng
gp247_get_locale()(chính làapp()->getLocale()của Laravel). - Lấy toàn bộ chuỗi của locale đó từ DB:
Languages::getListAll($locale)→ trả về mapcode => text. Kết quả được cache trong phạm vi một request (không truy vấn lại nhiều lần trong cùng lần tải trang). - Nếu mã có trong DB → trả về
textđó (và thay các tham số:keynếu có). - Nếu không có → dự phòng bằng
trans($string)của Laravel (đọc file lang trên ổ đĩa).
Các hàm helper liên quan:
| Hàm | Công dụng |
|---|---|
gp247_language_render($key, $replace, $locale) |
Lấy câu chữ theo mã (dùng phổ biến nhất) |
gp247_language_quickly($key, $default) |
Như trên nhưng cho phép đặt giá trị mặc định |
gp247_get_locale() |
Lấy locale đang hiển thị |
gp247_lang_switch($code) |
Đổi ngôn ngữ hiện tại (kiểm tra hợp lệ + ghi vào session) |
gp247_language_all() |
Lấy danh sách ngôn ngữ đang bật (từ admin_language) |
⚠️ Lưu ý kỹ thuật: không gọi các hàm ngôn ngữ trong
__constructhoặc middleware chạy sớm — lúc đó locale có thể chưa được thiết lập, dẫn tới hiển thị sai ngôn ngữ.
2. Thứ tự ưu tiên
Có hai tầng ưu tiên cần phân biệt.
2.1. Ưu tiên chọn locale hiển thị (ngôn ngữ nào đang bật cho request này)
Do middleware Localization (vendor/gp247/core/src/Middleware/Localization.php) quyết định, theo
thứ tự từ cao xuống thấp:
session('locale')— nếu người dùng đã chọn ngôn ngữ (session còn giữ) → ưu tiên cao nhất.gp247_store_info('language')— ngôn ngữ mặc định cấu hình cho store.config('app.locale')— locale mặc định của Laravel (trong.env/config/app.php).- Giá trị chọn được sẽ kiểm tra lại với danh sách ngôn ngữ đang bật (
admin_language,status = 1). Nếu không hợp lệ → lấy ngôn ngữ đang bật đầu tiên theo thứ tựsort.
Sau khi chốt, middleware gọi app()->setLocale(...) và ghi lại vào session.
Không có cơ chế nhận diện ngôn ngữ theo tiền tố URL (ví dụ
/vi/...). Locale được điều khiển bằng session + ngôn ngữ mặc định của store. Người dùng đổi ngôn ngữ qua nút chuyển ngôn ngữ trên giao diện (gọigp247_lang_switch()), giá trị mới được lưu vào session cho các request sau.
2.2. Ưu tiên nguồn câu chữ (lấy text ở đâu cho một mã)
Với cùng một mã chuỗi dịch:
- Bản ghi trong DB (
languages, khớplocation+code) — ưu tiên cao nhất. - File lang trên ổ đĩa qua
trans()— chỉ dùng khi DB không có mã đó.
→ Hệ quả quan trọng: muốn đổi một câu chữ, hãy sửa trong DB (qua admin), vì bản DB luôn thắng
bản file. Sửa file lang trong vendor/ thường vô tác dụng nếu DB đã có mã tương ứng.
3. Cách thêm & chỉnh sửa
Có hai việc khác nhau: thêm/sửa một ngôn ngữ (dòng trong admin_language) và thêm/sửa câu
chữ (dòng trong languages). Cả hai làm trong khu vực Admin.
3.1. Thêm một ngôn ngữ mới
- Đăng nhập Admin, vào màn hình Ngôn ngữ (đường dẫn
/{admin_prefix}/language). - Bấm nút thêm mới, điền:
- Name — tên hiển thị (ví dụ
日本語). - Code — mã locale, viết thường, duy nhất (ví dụ
ja). - Icon — cờ/biểu tượng (tùy chọn).
- RTL — bật nếu ngôn ngữ đọc từ phải sang trái (ví dụ tiếng Ả Rập).
- Sort — thứ tự sắp xếp; Status — bật (1) để ngôn ngữ có hiệu lực.
- Name — tên hiển thị (ví dụ
- Lưu lại. Nếu thành công, ngôn ngữ mới xuất hiện trong danh sách và ở nút chuyển ngôn ngữ ngoài site.
Lúc này ngôn ngữ mới chưa có câu chữ nào trong
languages. Sang bước 3.3 để nhập bản dịch.
3.2. Sửa / bật-tắt / xóa một ngôn ngữ
- Vào lại màn hình Ngôn ngữ, chọn Edit ở dòng tương ứng để đổi tên/cờ/thứ tự hoặc bật-tắt
Status. - Ràng buộc an toàn:
- Hệ thống luôn giữ tối thiểu 1 ngôn ngữ đang bật — không thể tắt hết.
- Không thể xóa hai ngôn ngữ gốc
vivàen(được bảo vệ bởi hằngGP247_GUARD_LANGUAGE).
3.3. Thêm / sửa câu chữ (bản dịch)
- Vào màn hình Quản lý chuỗi ngôn ngữ (đường dẫn
/{admin_prefix}/language_manager). - Chọn ngôn ngữ cần sửa, có thể lọc theo position (nhóm) hoặc gõ từ khóa để tìm nhanh.
Khi xem ngôn ngữ khác
en, màn hình hiển thị kèm bản gốc tiếng Anh bên cạnh để đối chiếu. - Sửa câu chữ có sẵn: sửa trực tiếp ô text rồi lưu. Hệ thống ghi đè (upsert) đúng dòng theo
cặp
location+code. - Thêm câu chữ mới: bấm thêm mới, nhập
code(mã) vàtext.- ⚠️ Câu chữ thêm mới luôn được tạo cho locale
entrước (mãcodelà duy nhất). Sau đó bạn mở từng ngôn ngữ khác và điền bản dịch cho cùng mã đó ở bước 3.
- ⚠️ Câu chữ thêm mới luôn được tạo cho locale
Kết quả mong đợi: sau khi lưu, tải lại trang tương ứng trên site/admin — câu chữ mới sẽ hiển thị ngay (vì DB được đọc ưu tiên). Nếu chưa thấy đổi, kiểm tra bạn có đang sửa đúng locale đang hiển thị không (xem mục 2.1).
4. Cách cập nhật
"Cập nhật ngôn ngữ" ở đây nghĩa là làm mới các chuỗi dịch mặc định của gói vào DB (ví dụ sau khi
composer update mang về câu chữ mới hoặc chuỗi đã chỉnh). Có hai chế độ, khác nhau ở việc có ghi
đè chỉnh sửa của bạn hay không:
| Cách chạy | Chế độ | Ảnh hưởng |
|---|---|---|
gp247:core-install / gp247:core-update |
insertOrIgnore (an toàn) | Chỉ thêm mã còn thiếu; giữ nguyên mọi câu chữ bạn đã sửa |
gp247:language-update |
upsert (ghi đè) | Ghi đè text về đúng bản mặc định của gói cho mọi mã |
Cập nhật an toàn (giữ chỉnh sửa)
Chạy sau khi cập nhật gói bằng Composer:
php artisan gp247:core-update
Lệnh này bổ sung các chuỗi dịch còn thiếu mà không đụng tới câu chữ bạn đã tùy biến trong admin.
Cập nhật ghi đè (lấy lại bản chuẩn của gói)
php artisan gp247:language-update
- Lệnh tự chạy lần lượt core → front → shop; gói nào chưa cài thì bỏ qua.
- ⚠️ Cẩn trọng: lệnh này ghi đè mọi câu chữ bạn từng chỉnh trong admin, đưa về đúng bản mặc
định mới nhất của gói. Nếu đã tùy biến nhiều, hãy sao lưu bảng
languagestrước khi chạy.
ℹ️ Không có
vendor:publishcho file ngôn ngữ. Chuỗi dịch được phân phối qua seeder ghi vào DB, không phải qua file lang publish ra ngoài. Vì vậy cách cập nhật chuẩn là hai lệnh artisan ở trên.
Hỏi & Đáp (Q&A)
Câu 1: Tôi sửa file lang trong vendor/gp247/core/src/Lang/vi/... mà site không đổi chữ?
→ Đúng như thiết kế. Bản trong DB (languages) luôn được ưu tiên hơn file. Hãy sửa câu chữ trong
Admin → Quản lý chuỗi ngôn ngữ thay vì sửa file.
Câu 2: Đổi một câu chữ thì làm ở đâu nhanh nhất?
→ Admin → Quản lý chuỗi ngôn ngữ (/language_manager), lọc theo từ khóa, sửa trực tiếp rồi lưu.
Không cần deploy hay chạy lệnh.
Câu 3: Khác nhau giữa gp247:core-update và gp247:language-update?
→ core-update an toàn — chỉ thêm mã còn thiếu, giữ nguyên chỉnh sửa của bạn. language-update
ghi đè mọi chuỗi về bản mặc định của gói. Muốn giữ tùy biến → dùng core-update; muốn lấy lại
bản chuẩn mới nhất → dùng language-update (nhớ sao lưu trước).
Câu 4: Thêm ngôn ngữ mới nhưng site vẫn trống chữ ở ngôn ngữ đó?
→ Thêm ngôn ngữ chỉ tạo dòng trong admin_language, chưa có bản dịch. Bạn phải nhập câu chữ cho
locale đó trong Quản lý chuỗi ngôn ngữ (mục 3.3), hoặc cung cấp seeder riêng.
Câu 5: Vì sao không xóa được tiếng Việt / tiếng Anh?
→ Hai ngôn ngữ gốc (vi, en) được bảo vệ bởi hằng GP247_GUARD_LANGUAGE để hệ thống luôn có ngôn
ngữ chuẩn để dự phòng. Bạn có thể tắt hiển thị chúng ở chỗ khác, nhưng không xóa khỏi danh sách.
Câu 6: Đổi ngôn ngữ mặc định của site ở đâu?
→ Locale hiển thị ưu tiên session('locale') → ngôn ngữ mặc định của store → config('app.locale').
Để đổi mặc định toàn site, chỉnh ngôn ngữ mặc định của store (cấu hình store) và/hoặc APP_LOCALE
trong .env.
Câu 7: GP247 có nhận ngôn ngữ theo URL kiểu /vi/... không?
→ Không. Ngôn ngữ được điều khiển qua session + ngôn ngữ mặc định của store, không qua tiền tố URL.
Câu 8: position trong bảng languages để làm gì?
→ Nó là nhãn nhóm giúp lọc/tìm câu chữ trong admin (ví dụ action, store.admin). Nó không ảnh
hưởng tới việc câu chữ nào được hiển thị — việc đó do cặp code + location quyết định.
Câu 9: Chuỗi dịch có được cache lâu dài không?
→ Không. Chỉ có cache trong phạm vi một request (mỗi locale truy vấn DB một lần cho mỗi lần tải trang). Sửa trong admin có hiệu lực ngay ở lần tải trang kế tiếp.
Câu 10: Gói shop không có thư mục lang trên ổ đĩa, vậy chữ ở đâu ra?
→ Toàn bộ chuỗi của shop nằm trong DB (languages), do DataShopLanguageSeeder nạp vào khi cài/cập
nhật. Đây là lý do shop không cần file lang trên ổ đĩa.
📅 Cập nhật lần cuối: 2026-07-30 · ✍️ Tác giả (Author): GP247