Files
server/core/lib/L10N/LocaleResolver.php
T
Sebastian 0cc255a087 feat: localizations
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-07-07 16:56:58 -04:00

72 lines
2.3 KiB
PHP

<?php
namespace KTXC\L10N;
use DI\Attribute\Inject;
use KTXC\Http\Request\Request;
use KTXC\Service\UserAccountsService;
/**
* Resolves the effective UI locale for the current user.
*
* Resolution chain: user setting (core.locale) -> Accept-Language
* negotiation -> fallback. Tenant default slots in between once tenant
* settings expose one (Phase 3).
*/
class LocaleResolver
{
public const FALLBACK = 'en';
public const SETTING_KEY = 'core.locale';
public function __construct(
private readonly UserAccountsService $userService,
#[Inject('rootDir')] private readonly string $rootDir,
) {}
/**
* Locales with a core catalog on disk (public/l10n/*.json), always
* including the fallback. This backs the language picker, so a locale
* is only offered when it has at least core-shell coverage.
*
* @return string[]
*/
public function available(): array
{
$locales = [self::FALLBACK];
foreach (glob($this->rootDir . '/public/l10n/*.json') ?: [] as $file) {
$locales[] = basename($file, '.json');
}
$locales = array_values(array_unique($locales));
sort($locales);
return $locales;
}
public function resolve(Request $request): string
{
$available = $this->available();
// 1. explicit user preference
$settings = $this->userService->fetchSettings([self::SETTING_KEY]);
$preference = $settings[self::SETTING_KEY] ?? null;
if (is_string($preference) && in_array($preference, $available, true)) {
return $preference;
}
// 2. Accept-Language negotiation. getPreferredLanguage() returns the
// first candidate when nothing matches, so the fallback leads the
// list; it also normalizes to underscore form (pt_BR) while catalogs
// use BCP-47 hyphens.
$candidates = array_merge([self::FALLBACK], array_diff($available, [self::FALLBACK]));
$negotiated = $request->getPreferredLanguage($candidates);
if ($negotiated !== null) {
$negotiated = str_replace('_', '-', $negotiated);
if (in_array($negotiated, $available, true)) {
return $negotiated;
}
}
// 3. fallback
return self::FALLBACK;
}
}