*/ private const SUPPORTED_LOCALES = ['en', 'de']; public function __construct( private SettingsResolver $settingsResolver, private WorkspaceContext $workspaceContext, ) {} /** * @return list */ public static function supportedLocales(): array { return self::SUPPORTED_LOCALES; } /** * @return array */ public static function localeOptions(): array { return [ 'en' => __('localization.locales.en'), 'de' => __('localization.locales.de'), ]; } public static function isSupported(mixed $locale): bool { return self::normalize($locale) !== null; } public static function normalize(mixed $locale): ?string { if (! is_string($locale)) { return null; } $normalized = strtolower(trim($locale)); return in_array($normalized, self::SUPPORTED_LOCALES, true) ? $normalized : null; } /** * @return array{ * locale: string, * source: string, * fallback_locale: string, * user_preference_locale: ?string, * workspace_default_locale: ?string, * machine_artifacts_invariant: true * } */ public function resolve(Request $request, ?string $plane = null): array { $plane = $this->normalizePlane($plane, $request); $explicitOverride = $this->explicitOverride($request); $systemDefault = (string) config('app.fallback_locale', 'en'); if ($plane === 'system') { return $this->resolveFromSources( explicitOverride: $explicitOverride, userPreference: null, workspaceDefault: null, systemDefault: $systemDefault, includeUserPreference: false, includeWorkspaceDefault: false, ); } $user = $request->user(); $userPreference = $user instanceof User ? $user->preferred_locale : null; $workspaceDefault = $this->workspaceDefault($request); return $this->resolveFromSources( explicitOverride: $explicitOverride, userPreference: $userPreference, workspaceDefault: $workspaceDefault, systemDefault: $systemDefault, includeUserPreference: true, includeWorkspaceDefault: true, ); } /** * @return array{ * locale: string, * source: string, * fallback_locale: string, * user_preference_locale: ?string, * workspace_default_locale: ?string, * machine_artifacts_invariant: true * } */ public function resolveFromSources( mixed $explicitOverride, mixed $userPreference, mixed $workspaceDefault, mixed $systemDefault, bool $includeUserPreference = true, bool $includeWorkspaceDefault = true, ): array { $fallbackLocale = self::normalize(config('app.fallback_locale', 'en')) ?? 'en'; $candidates = [ self::SOURCE_EXPLICIT_OVERRIDE => self::normalize($explicitOverride), ]; if ($includeUserPreference) { $candidates[self::SOURCE_USER_PREFERENCE] = self::normalize($userPreference); } if ($includeWorkspaceDefault) { $candidates[self::SOURCE_WORKSPACE_DEFAULT] = self::normalize($workspaceDefault); } $candidates[self::SOURCE_SYSTEM_DEFAULT] = self::normalize($systemDefault) ?? $fallbackLocale; foreach ($candidates as $source => $locale) { if ($locale !== null) { return [ 'locale' => $locale, 'source' => $source, 'fallback_locale' => $fallbackLocale, 'user_preference_locale' => $includeUserPreference ? self::normalize($userPreference) : null, 'workspace_default_locale' => $includeWorkspaceDefault ? self::normalize($workspaceDefault) : null, 'machine_artifacts_invariant' => true, ]; } } return [ 'locale' => $fallbackLocale, 'source' => self::SOURCE_SYSTEM_DEFAULT, 'fallback_locale' => $fallbackLocale, 'user_preference_locale' => $includeUserPreference ? self::normalize($userPreference) : null, 'workspace_default_locale' => $includeWorkspaceDefault ? self::normalize($workspaceDefault) : null, 'machine_artifacts_invariant' => true, ]; } private function explicitOverride(Request $request): ?string { $queryLocale = self::normalize($request->query('locale')); if ($queryLocale !== null) { return $queryLocale; } if (! $request->hasSession()) { return null; } return self::normalize($request->session()->get(self::SESSION_OVERRIDE_KEY)); } private function workspaceDefault(Request $request): ?string { $workspace = $this->workspaceContext->currentWorkspace($request); if (! $workspace instanceof Workspace) { return null; } return self::normalize($this->settingsResolver->resolveValue( workspace: $workspace, domain: self::SETTING_DOMAIN, key: self::SETTING_DEFAULT_LOCALE, )); } private function normalizePlane(?string $plane, Request $request): string { $plane = strtolower(trim((string) $plane)); if (in_array($plane, ['admin', 'tenant', 'system'], true)) { return $plane; } return $request->is('system', 'system/*') ? 'system' : 'admin'; } }