Lang files
Lay out translations exactly as any Laravel package would, under the add-on’s ownresources/lang/{locale}/{group}.php:
langNamespaces()
Registering the translator namespace makes it available to Laravel’s own __() calls, but the panel’s Vue frontend cannot read PHP lang files directly. Opt the namespace into the panel’s translations endpoint from the add-on’s Section (or SectionExtension):
{namespace}::{group} message keys — cached and versioned together with the panel’s own strings. Code that is not a Section can call Lunar\Panel\PanelManager (via the Panel facade)‘s translations('example-addon') directly for the same effect.
Using the keys
The same keys work on both the server and the client, through each side’s own resolver:-
Server-side (navigation labels, flash messages) — plain Laravel lang-key resolution through
__():Passing the lang key string directly (rather than calling__()inline) is the established pattern for navigation labels — the registry resolves it through__()when the navigation tree is shared to the frontend. -
Client-side — Vue pages import
useI18nfromvue-i18n(externalised to the panel’s own shared instance by@lunarphp/panel-vite-plugin, so the add-on never bundles a second i18n runtime) and callt()with the namespaced key:
Locales
The panel ships translations for 16 locales:ar, bg, de, en, es, fa, fr, hr, hu, mn, nl, pl, pt_BR, ro, tr, vi. An add-on is not required to match that set — ship whichever locales are supported. A locale the add-on lacks falls back to the app’s fallback locale for that namespace only, so a partially translated add-on never blanks out the rest of the panel’s own locale switcher for a staff member using an unsupported language.
Staff pick their panel language from the user menu; the choice persists as staff.preferred_locale and applies to the translations an add-on’s own strings resolve into, same as the panel’s.
The runtime escape hatch
window.LunarPanel.registerTranslations(locale, namespace, messages) pushes vue-i18n messages for a namespace directly at runtime, merging over whatever the translations endpoint served. Prefer langNamespaces() (PHP lang files) for anything that should version and cache alongside the rest of the panel’s translations — this call exists for messages that only ever exist client-side and have no PHP-side lang file to back them.