locale = config('app.locale'); } /** * Set the current locale for translations. * * @param string $locale The locale string (e.g., 'en', 'fr'). * @return void */ public function setLocale(string $locale): void { $this->locale = $locale; } /** * Get the current locale used by the manager. * * @return string The current locale. */ public function getLocale(): string { return $this->locale; } /** * Translate the given key using database overrides first, then Laravel's language files. * * @param string $key The translation key (e.g., 'pages.title' or 'cms::pages.title'). * @param array $replace Key-value pairs for placeholder replacements. * @param string|null $locale Override locale for this specific translation. * @return string The translated string or a formatted key. */ public function translate(string $key, array $replace = [], ?string $locale = null): string { $locale = $locale ?: $this->locale; /** * 1. Split key into group and item. * SiteWeaver uses 'group.item' or 'package::group.item'. * If no group is provided, we default to 'cms'. */ $group = 'cms'; $item = $key; if (str_contains($key, '::')) { [$group, $item] = explode('::', $key); } elseif (str_contains($key, '.')) { [$group, $item] = explode('.', $key, 2); } /** * 2. Check Database for Override. * We use Cache::rememberForever to avoid frequent database hits for static strings. * The cache is cleared in updateOverride() when a value is changed. */ $cacheKey = "translation.{$locale}.{$group}.{$item}"; $value = Cache::rememberForever($cacheKey, function () use ($locale, $group, $item) { $record = Translation::where('locale', $locale) ->where('group', $group) ->where('key', $item) ->first(); return $record ? $record->value : null; }); /** * 3. Fallback to Laravel's built-in __() * if no database override exists. */ if ($value === null) { $translated = __($key, $replace, $locale); // If __() returns the key itself, it means it's not found in files either. // In this case, we format the key into a human-readable fallback (e.g. 'my_key' -> 'My key'). return $translated === $key ? $this->formatKey($key) : $translated; } /** * 4. Replace placeholders in DB value. * We manually handle :placeholder replacement to mirror Laravel's behavior * for database-sourced strings. */ return $this->makeReplacements($value, $replace); } /** * Store or update a translation override in the database and clear relevant cache. * * @param string $locale The locale for the override. * @param string $group The translation group/namespace. * @param string $key The translation key within the group. * @param string $value The override value. * @return Translation The created or updated translation instance. */ public function updateOverride(string $locale, string $group, string $key, string $value): Translation { $translation = Translation::updateOrCreate( ['locale' => $locale, 'group' => $group, 'key' => $key], ['value' => $value] ); Cache::forget("translation.{$locale}.{$group}.{$key}"); return $translation; } /** * Format a missing translation key into a human-readable string as a fallback. * * @param string $key The original translation key. * @return string The formatted fallback string. */ protected function formatKey(string $key): string { // Simple humanizing of the key if not found $parts = explode('::', $key); $item = end($parts); $parts = explode('.', $item); $last = end($parts); return ucfirst(str_replace(['_', '-'], ' ', $last)); } /** * Replace placeholders in a translation string with actual values. * Supports :name, :Name, and :NAME formats. * * @param string $line The translation line with placeholders. * @param array $replace Key-value pairs of replacements. * @return string The finalized translation string. */ protected function makeReplacements(string $line, array $replace): string { if (empty($replace)) { return $line; } foreach ($replace as $key => $value) { $line = str_replace( [':'.$key, ':'.ucfirst($key), ':'.strtoupper($key)], [$value, ucfirst($value), strtoupper($value)], $line ); } return $line; } }