Using languages (multi-language / i18n) in the GP247 system
August 4, 2026 Β· View on GitHub
π Language: π»π³ TiαΊΏng Viα»t Β· π¬π§ English (current)
Using languages (multi-language / i18n) in the GP247 system
Introduction
This document explains how the GP247 multi-language (i18n) system works: where translation strings live, in what priority order the system picks the display language, and how you add a new language, edit individual strings, and update translations when upgrading packages. It targets GP247 developers and site administrators. After reading it, you will understand the mechanism and know exactly where to go to change any text shown on the storefront or admin.
βΉοΈ Quick terms: locale = a short language code such as
vi,en. Translation string = a "key β text" pair (e.g.action.saveβSave). Admin = the site administration area.
1. How it works
GP247 uses a database-first (DB-first) model. There are two distinct concepts, matching two database tables:
| Table | Role | Example content |
|---|---|---|
admin_language | The list of installed languages on the site (on/off, order, flag, RTL) | vi β TiαΊΏng Viα»t Β· en β English |
languages | The actual string store (key β text, per locale) | action.save / en β Save |
(Real table names carry the GP247_DB_PREFIX prefix, gp247_ by default β e.g. gp247_languages.)
1.1. Where do translation strings live?
- The primary source is the
languagestable in the DB. This is where every string is read from for display. - On-disk language files are only a fallback. Each package ships a
Lang/{en,vi}/directory (e.g.vendor/gp247/core/src/Lang/), registered with Laravel under thegp247::namespace. They are used only when a matching key is not present in the DB. - The initial data for the
languagestable comes from each package's seeder (not from the lang files); see section 4.
Columns of the languages table:
code // translation key, e.g. "action.save" (unique together with location)
text // translated value, e.g. "Save"
position // group/section, e.g. "action", "store.admin" (used for filtering in admin)
location // locale, e.g. "en" or "vi"
Columns of the admin_language table: name (display name), code (locale code, unique), icon
(flag), status (1 = enabled), rtl (1 = right-to-left layout), sort (ordering).
1.2. How a string is rendered
In code/views, strings are fetched through the helper gp247_language_render():
gp247_language_render('action.save'); // -> "Save" (when the current locale is en)
gp247_language_render('welcome', ['name' => 'An']); // replaces :name in the string with "An"
This function runs in the following order:
- Resolve the current locale via
gp247_get_locale()(which is Laravel'sapp()->getLocale()). - Load all strings for that locale from the DB:
Languages::getListAll($locale)β returns acode => textmap. The result is cached within a single request (not re-queried repeatedly during the same page load). - If the key exists in the DB β return that
text(and substitute any:keyparameters). - If it does not exist β fall back to Laravel's
trans($string)(reads on-disk lang files).
Related helper functions:
| Function | Purpose |
|---|---|
gp247_language_render($key, $replace, $locale) | Get a string by key (most common) |
gp247_language_quickly($key, $default) | Same as above, but allows a default value |
gp247_get_locale() | Get the currently displayed locale |
gp247_lang_switch($code) | Switch the current language (validates + writes to session) |
gp247_language_all() | Get the list of enabled languages (from admin_language) |
β οΈ Technical note: do not call the language functions inside
__constructor an early-running middleware β the locale may not be set yet at that point, leading to the wrong display language.
2. Priority order
There are two layers of priority to distinguish.
2.1. Display-locale priority (which language is active for this request)
Decided by the Localization middleware
(vendor/gp247/core/src/Middleware/Localization.php), in order from highest to lowest:
session('locale')β if the user has already picked a language (still in session) β highest priority.gp247_store_info('language')β the store's configured default language.config('app.locale')β Laravel's default locale (in.env/config/app.php).- The chosen value is re-validated against the list of enabled languages (
admin_language,status = 1). If invalid β the first enabled language bysortis used.
Once decided, the middleware calls app()->setLocale(...) and writes it back to the session.
There is no URL-prefix language detection (e.g.
/vi/...). The locale is driven by session + the store's default language. Users change the language through the language switcher in the UI (which callsgp247_lang_switch()), and the new value is saved to the session for later requests.
2.2. String-source priority (where the text comes from for a key)
For the same key:
- The DB row (
languages, matchinglocation+code) β highest priority. - The on-disk lang file via
trans()β used only when the DB has no such key.
β Important consequence: to change a string, edit it in the DB (via admin), because the DB
version always wins over the file. Editing lang files under vendor/ is usually ineffective when the
DB already has the matching key.
3. How to add & edit
There are two different tasks: adding/editing a language (a row in admin_language) and
adding/editing strings (rows in languages). Both are done in the Admin area.
3.1. Add a new language
- Log in to Admin and go to the Language screen (path
/{admin_prefix}/language). - Click the add button and fill in:
- Name β display name (e.g.
ζ₯ζ¬θͺ). - Code β locale code, lowercase, unique (e.g.
ja). - Icon β flag/icon (optional).
- RTL β enable for right-to-left languages (e.g. Arabic).
- Sort β display order; Status β enable (1) to make the language active.
- Name β display name (e.g.
- Save. If successful, the new language appears in the list and in the storefront language switcher.
At this point the new language has no strings yet in
languages. Go to step 3.3 to enter the translations.
3.2. Edit / enable-disable / delete a language
- Go back to the Language screen and choose Edit on the relevant row to change
name/flag/order or toggle
Status. - Safety constraints:
- The system always keeps at least 1 enabled language β you cannot disable them all.
- You cannot delete the two base languages
vianden(protected by theGP247_GUARD_LANGUAGEconstant).
3.3. Add / edit strings (translations)
- Go to the Language string manager screen (path
/{admin_prefix}/language_manager). - Pick the language to edit; you can filter by position (group) or type a keyword to
search quickly. When viewing a language other than
en, the screen shows the English source side by side for reference. - Edit an existing string: edit the text field directly and save. The system upserts the exact
row by the
location+codepair. - Add a new string: click add, enter the
code(key) andtext.- β οΈ A newly added string is always created for the
enlocale first (thecodeis unique). Then open each other language and fill in the translation for that same key as in step 3.
- β οΈ A newly added string is always created for the
Expected result: after saving, reload the corresponding page on the storefront/admin β the new string shows immediately (the DB is read first). If you don't see the change, check that you are editing the correct locale that is currently being displayed (see section 2.1).
4. How to update
"Updating languages" here means refreshing the package's default strings into the DB (e.g. after
composer update brings new or changed strings). There are two modes, differing in whether they
overwrite your edits:
| How to run | Mode | Effect |
|---|---|---|
gp247:core-install / gp247:core-update | insertOrIgnore (safe) | Only adds missing keys; keeps every string you edited |
gp247:language-update | upsert (overwrite) | Overwrites text back to the package defaults for every key |
Safe update (keeps your edits)
Run after updating packages with Composer:
php artisan gp247:core-update
This command adds the missing strings without touching the text you customized in admin.
Overwrite update (reset to package defaults)
php artisan gp247:language-update
- The command runs core β front β shop in turn; any package not installed is skipped.
- β οΈ Caution: this command overwrites every string you edited in admin, resetting them to the
package's latest defaults. If you customized a lot, back up the
languagestable first.
βΉοΈ There is no
vendor:publishfor language files. Translation strings are delivered via DB seeders, not through published lang files. So the standard way to update is the two artisan commands above.
Q&A
Q1: I edited a lang file in vendor/gp247/core/src/Lang/vi/... but the site text didn't change?
β That's by design. The DB version (languages) always takes priority over files. Edit the string
in Admin β Language string manager instead of editing the file.
Q2: Where is the fastest place to change a string?
β Admin β Language string manager (/language_manager), filter by keyword, edit inline and save.
No deploy or command needed.
Q3: What's the difference between gp247:core-update and gp247:language-update?
β core-update is safe β it only adds missing keys and keeps your edits. language-update
overwrites every string back to the package defaults. To keep customizations β use core-update;
to reset to the latest defaults β use language-update (back up first).
Q4: I added a new language but the site is still blank in that language?
β Adding a language only creates a row in admin_language; there are no translations yet. You must
enter the strings for that locale in the Language string manager (section 3.3), or provide your own
seeder.
Q5: Why can't I delete Vietnamese / English?
β The two base languages (vi, en) are protected by the GP247_GUARD_LANGUAGE constant so the
system always has a standard language to fall back to. You can hide them elsewhere, but not remove
them from the list.
Q6: Where do I change the site's default language?
β The display locale prioritizes session('locale') β the store's default language β
config('app.locale'). To change the site-wide default, adjust the store's default language and/or
APP_LOCALE in .env.
Q7: Does GP247 detect language from the URL like /vi/...?
β No. The language is driven by session + the store's default language, not by a URL prefix.
Q8: What is position in the languages table for?
β It is a grouping label that helps filter/search strings in admin (e.g. action, store.admin).
It does not affect which string is displayed β that is decided by the code + location pair.
Q9: Are translation strings cached long-term?
β No. There is only a per-request cache (each locale queries the DB once per page load). Edits in admin take effect on the very next page load.
Q10: The shop package has no on-disk lang directory β where does its text come from?
β All shop strings live in the DB (languages), loaded by DataShopLanguageSeeder at install/update
time. That is why shop needs no on-disk lang files.
π Last updated: 2026-07-30 Β· βοΈ Author: GP247