InfraGuard is an 8-language console with Korean as the head-office default and English as the global fallback. This article covers (1) how a user switches language from the header switcher, (2) how the display language is resolved on every request, and (3) how an admin chooses which languages appear in the switcher.
Overview — what it does
Every label, menu item, and message in the console goes through a translation key (__('menu.security'), etc.) rather than hardcoded English. Changing the language makes the server render the UI from that locale's translation files (resources/lang/{locale}/*.php) starting with the next request.
The supported language catalog is the 8 entries under admin in config/locales.php. Each language is always shown by its own native label, regardless of the currently active locale.
| Code | Native label | Tag | Real translation |
|---|---|---|---|
ko |
한국어 | KO | ✅ Fully translated (head-office default) |
en |
English | EN | ✅ Fully translated (global fallback) |
ja |
日本語 | JA | ⏳ Falls back to en |
zh |
中文(简体) | ZH | ⏳ Falls back to en |
es |
Español | ES | ⏳ Falls back to en |
fr |
Français | FR | ⏳ Falls back to en |
de |
Deutsch | DE | ⏳ Falls back to en |
ru |
Русский | RU | ⏳ Falls back to en |
Initially only ko and en are really translated (
priority => ['ko','en']inconfig/locales.php). The other six (ja·zh·es·fr·de·ru) have no translation files yet and automatically fall back to en (fallback_locale = eninconfig/app.php). So pickingjatoday still shows untranslated strings in English — the switcher lists all 8, only the wording falls back.
When to use
- A visitor wants to view the console in a familiar language — use the header language switcher.
- You want to switch language even on the login screen (before a session exists) —
/lang/{locale}is allowed for guests. - You want to expose only certain languages org-wide and set the default language for first visits — an admin edits the enabled set and order (see "Managing enabled languages" below).
Layout
Header language switcher (every screen)
The header language dropdown (layouts.partials.locale-switch + ui/locale-dd.js) lists only the languages the admin has enabled. Each item shows the native label plus a short tag (e.g. KO, EN), and the current locale is marked active.
Administration ▸ Localization (managing enabled languages)
On the Administration ▸ Localization screen (reachable from the top-right button), for the 8-language catalog you can:
- Toggle each language on/off (whether it appears in the switcher).
- Reorder by drag — the top enabled language becomes the site primary (default).
- Save with a minimum of one language enforced (saving zero is rejected).
How to use
1) Switch from the header switcher
- Open the language dropdown in the header.
- Pick a language → the browser navigates to
GET /lang/{locale}. - The server stores your choice and redirects back to the previous page (
redirect()->back()). The new language applies from the next render.
The switch endpoint looks like this:
GET /lang/ko # switch to Korean
GET /lang/en # switch to English
GET /lang/ja # Japanese (untranslated → en fallback)
What the server (LocaleController@switch) does on switch:
| Store | Key | Value | Lifetime |
|---|---|---|---|
| Session | infr_locale |
chosen code | for the session |
| Session | infr_lang |
chosen code | for the session (profile-language sync) |
| Cookie | infr_locale |
chosen code | 1 year (persists across visits) |
| DB | users.lang |
chosen code | permanent (logged-in users only) |
When a logged-in user switches, the choice is also persisted to
users.lang(profile language), so the language survives the next login. Guest (not-logged-in) choices are kept only in session and cookie.
Calling
/lang/{locale}with an unsupported (or admin-disabled) code is silently ignored and the current language is kept — no error is thrown.
2) How the display language is resolved (SetLocale)
The SetLocale middleware determines the locale on every web request (including the login screen) using the priority below. Each candidate is accepted only if it is a currently enabled language.
1. session infr_locale (explicitly chosen via switcher this session)
2. cookie infr_locale (persistent choice across visits · applies to login screen before session)
3. session infr_lang (logged-in user profile users.lang → loaded into session at login)
4. site-wide default (Locales::primary() = first language in the enabled list)
Browser Accept-Language auto-detection is NOT performed. A first visit with no session, cookie, or profile choice is served deterministically in the admin-defined site default language, not the browser language (site-wide authority). Visitors can always change it with the header switcher.
3) Managing enabled languages (admin)
- Go to
Administration ▸ Localization. - Toggle on the languages to expose (the switcher, switching, profile language, and inventory multilingual editing all go through this set), and toggle off the rest.
- Reorder by drag — the top enabled language becomes the default.
- Save. The save is audited to
infr_audit_logaslocalization.save.
Reducing the enabled languages immediately shrinks the header switcher's availability. As a safeguard, at least one language must stay enabled (saving zero returns 422), and if no valid code remains the system falls back to the full catalog so at least one is always guaranteed.
Data sources
| Item | Location |
|---|---|
| Language catalog (8 + labels) | config/locales.php → admin |
| Real-translation priority | config/locales.php → priority = ['ko','en'] |
| Fallback locale | config/app.php → fallback_locale = en |
| Translation files | resources/lang/{locale}/{msg,menu,infra,security,...}.php (rebound via useLangPath in bootstrap/app.php) |
| Enabled language subset | infr_setting (group=i18n, key=enabled) — CSV |
| Display order | infr_setting (group=i18n, key=order) — CSV |
| Profile language | users.lang |
| Session choice | session infr_locale / infr_lang |
| Persistent choice | cookie infr_locale (1 year) |
The enabled-languages and order settings are read by App\Support\Locales with a 60-second cache. On DB error or no setting, it falls back safely to an empty array (→ full catalog). After a save, Locales::flush() invalidates the cache immediately.
-- Where the enabled set and order are stored (example)
SELECT `group`, `key`, `value` FROM infr_setting WHERE `group` = 'i18n';
-- group | key | value
-- i18n | order | ko,en,ja,zh,es,fr,de,ru
-- i18n | enabled | ko,en
Handoff to the frontend
The server passes the active locale and message pack into the view bootstrap (e.g. window.__t = @json(__('msg')), window.__locale). Vue pages and mixins use these to render client-side strings and format dates against the active UI locale.
Access
| Action | Minimum level |
|---|---|
Switch language GET /lang/{locale} |
Guest allowed — no login required (works on the login screen too) |
View/save enabled languages Administration ▸ Localization |
admin = 10 |
- Switching language itself is open to anyone (it is not a sensitive action).
- The admin screen that decides which languages appear in the switcher is admin-only: double defense of route group
infr.level:10plus a controller self-guard (user_level >= 10). The level tiers are demo=1 · staff=5 · admin=10 (see Levels · RBAC).
Troubleshooting & FAQ
The language I want is missing from the switcher
It is most likely disabled by an admin. Turn its toggle on in Administration ▸ Localization. The switcher, switching, and profile language all go through the enabled set.
I switched language but some text is still English
ja·zh·es·fr·de·ru have no real translation files yet and fall back to en (initial strategy). Seeing only the untranslated strings in English is expected. ko and en are fully translated.
First visit does not open in my browser language
That is intended. InfraGuard does not read Accept-Language; it opens in the admin-defined site default language (the first language in the enabled list). Once you switch, the choice is saved to session and cookie and kept on later visits.
After logging out and back in, my language resets
If the cookie (infr_locale, 1 year) is present, it persists. For logged-in users, the choice is also saved to users.lang and restored as the profile language on the next login. With neither cookie nor profile, it opens in the site default.
What happens with an unknown code like /lang/xx?
An unsupported code is silently ignored and the current language is kept (no error). The only valid codes are the enabled ones among the 8.
What if I disable every language?
It is blocked. Saving enforces a minimum of one (saving zero returns 422). Even if the setting became corrupt, the system falls back to the full catalog so the switcher never goes empty.
Related
- Administration ▸ Localization (manage enabled languages)
- My Profile · language
- Navigation · menu structure
- Theme (light · dark)
- Levels · RBAC
- Using this manual
