91 lines
3.8 KiB
Markdown
91 lines
3.8 KiB
Markdown
# Localization
|
|
|
|
Storefront routes can be locale-prefixed (`/el/proionta`, `/en/products`) using a middleware
|
|
that reads directly from Lunar's `languages` table — the same table the Filament **Languages**
|
|
resource manages, so there's no separate locale config to keep in sync.
|
|
|
|
---
|
|
|
|
## Why prefix every locale, including the default
|
|
|
|
Leaving the default locale bare at the root (`/proionta` for Greek, `/en/products` for English)
|
|
creates ambiguity: is `/` the language-neutral homepage or specifically the Greek version? It
|
|
also complicates `hreflang` (needs a self-referencing tag on the root plus a possibly-duplicate
|
|
`x-default`) and risks duplicate content if a bot or campaign link reaches the root without a
|
|
language signal.
|
|
|
|
Prefixing every locale avoids this: every URL unambiguously declares its language, `hreflang`
|
|
tags are symmetrical, and adding a locale later requires no URL restructuring.
|
|
|
|
---
|
|
|
|
## Opt-in, not global
|
|
|
|
The middleware is registered as a **named alias** (`locale`), not pushed onto the `web`
|
|
middleware group. Apply it explicitly to the route group(s) that make up your storefront:
|
|
|
|
```php
|
|
// routes/web.php
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
Route::middleware('locale')->group(function () {
|
|
Route::get('/{locale}', HomeController::class);
|
|
Route::get('/{locale}/proionta', ProductIndexController::class);
|
|
Route::get('/{locale}/proionta/{slug}', ProductShowController::class);
|
|
});
|
|
```
|
|
|
|
It is **not** applied automatically because storefront routes aren't the only routes living
|
|
under `web` in a shop:
|
|
|
|
- The Filament admin panel (`/boboko*`, see `PanelServiceProvider`) has its own routing/auth
|
|
concerns and must never be locale-redirected.
|
|
- Livewire's internal update endpoint (`/livewire/update`) must resolve without a locale prefix.
|
|
- Webhooks, health checks, and other non-storefront routes shouldn't be touched.
|
|
|
|
If a shop's entire `web.php` *is* the storefront, wrapping the whole file in the group above is
|
|
fine — just keep admin/Livewire/webhook routes registered outside of it (as they already are).
|
|
|
|
---
|
|
|
|
## Behavior
|
|
|
|
`Modules\Core\Localization\LocaleMiddleware`:
|
|
|
|
1. Reads the first path segment (`request()->segment(1)`).
|
|
2. Matches it against `Lunar\Models\Language::code`.
|
|
- **Match** — `App::setLocale($code)` is set, and `locale` / `language` request attributes
|
|
are populated for controllers/views to use.
|
|
- **No match** (missing, wrong, or unknown segment) — redirects to the same path prefixed
|
|
with a resolved locale:
|
|
- the best match from the `Accept-Language` header against available language codes, or
|
|
- the language flagged `default` in the `languages` table, or
|
|
- the first language row, as a last resort.
|
|
|
|
The language list is cached with `Cache::rememberForever()` under `core.localization.languages`
|
|
and invalidated automatically by `Modules\Core\Localization\LanguageCacheObserver`, which
|
|
observes `Lunar\Models\Language` `saved`/`deleted` events. Adding, editing, or removing a
|
|
language via the Filament **Languages** resource clears the cache immediately — no TTL, no
|
|
stale reads.
|
|
|
|
---
|
|
|
|
## Reading the resolved locale/language downstream
|
|
|
|
```php
|
|
// In a controller or view composer
|
|
$locale = $request->attributes->get('locale'); // e.g. "el"
|
|
$language = $request->attributes->get('language'); // Lunar\Models\Language instance
|
|
```
|
|
|
|
Use `$language->id` when querying Lunar's translatable content (e.g. `Url::where('language_id', ...)`).
|
|
|
|
---
|
|
|
|
## Single-language shops
|
|
|
|
If a shop has only one row in `languages`, the middleware still enforces the prefix (e.g. every
|
|
URL under `/en/...`) rather than special-casing it away — this keeps behavior identical across
|
|
shops and avoids a silent restructuring if a second language is added later. If a shop genuinely
|
|
never wants locale prefixes, don't apply the `locale` middleware to its routes at all.
|