Files
breadcrumb-the-shire/lib/Http/LocaleResolver.php

127 lines
3.7 KiB
PHP
Raw Normal View History

2026-02-04 23:31:53 +01:00
<?php
namespace MintyPHP\Http;
use MintyPHP\I18n;
/**
* Handles locale detection and URL manipulation for multi-language support.
*/
class LocaleResolver
{
private array $availableLocales;
private string $defaultLocale;
private string $basePath;
public function __construct(?array $availableLocales = null, ?string $defaultLocale = null)
{
$this->defaultLocale = $defaultLocale ?? I18n::$defaultLocale;
$this->availableLocales = $availableLocales ?? (defined('APP_LOCALES') ? APP_LOCALES : [$this->defaultLocale]);
$this->basePath = trim(parse_url(\MintyPHP\Router::getBaseUrl(), PHP_URL_PATH) ?: '/', '/');
2026-02-04 23:31:53 +01:00
}
/**
* Parse a request URI and extract locale information.
*
* @return array{
* locale: string,
* pathWithoutLocale: string,
* query: string,
* hadLocaleInUrl: bool,
* hadInvalidLocale: bool
* }
*/
public function parseUri(string $uri): array
{
$path = parse_url($uri, PHP_URL_PATH) ?: '';
$query = parse_url($uri, PHP_URL_QUERY) ?: '';
$relativePath = Request::stripBasePath($path);
2026-02-04 23:31:53 +01:00
$segments = $relativePath === '' ? [] : explode('/', $relativePath);
$localeCandidate = $segments[0] ?? '';
$hadLocaleInUrl = false;
$hadInvalidLocale = false;
$locale = '';
if ($localeCandidate !== '' && in_array($localeCandidate, $this->availableLocales, true)) {
$locale = array_shift($segments);
$hadLocaleInUrl = true;
} elseif ($localeCandidate !== '' && $this->looksLikeLocale($localeCandidate)) {
array_shift($segments);
$hadInvalidLocale = true;
}
return [
'locale' => $locale,
'pathWithoutLocale' => implode('/', $segments),
'query' => $query,
'hadLocaleInUrl' => $hadLocaleInUrl,
'hadInvalidLocale' => $hadInvalidLocale,
];
}
/**
* Resolve the effective locale from multiple sources (priority order):
* 1. URL segment (explicit)
* 2. User session preference
* 3. Cookie preference
* 4. Default locale
*/
public function resolveLocale(string $urlLocale, ?string $userLocale = null, ?string $cookieLocale = null): string
{
if ($urlLocale !== '' && $this->isValidLocale($urlLocale)) {
return $urlLocale;
}
if ($userLocale !== null && $userLocale !== '' && $this->isValidLocale($userLocale)) {
return $userLocale;
}
if ($cookieLocale !== null && $cookieLocale !== '' && $this->isValidLocale($cookieLocale)) {
return $cookieLocale;
}
return $this->defaultLocale;
}
/**
* Build a new REQUEST_URI without the locale segment.
*/
public function buildUriWithoutLocale(string $pathWithoutLocale, string $query): string
{
$newPath = $this->basePath !== '' ? '/' . $this->basePath : '';
if ($pathWithoutLocale !== '') {
$newPath .= '/' . $pathWithoutLocale;
} elseif ($newPath === '') {
$newPath = '/';
}
return $newPath . ($query !== '' ? '?' . $query : '');
}
/**
* Check if a locale string is valid (exists in available locales).
*/
public function isValidLocale(string $locale): bool
{
return in_array($locale, $this->availableLocales, true);
}
public function getAvailableLocales(): array
{
return $this->availableLocales;
}
public function getDefaultLocale(): string
{
return $this->defaultLocale;
}
private function looksLikeLocale(string $candidate): bool
{
return preg_match('/^[a-z]{2}(?:-[a-z]{2})?$/i', $candidate) === 1;
}
}