locale = $locale; $this->paths = $paths ?? []; asort($this->paths); } /** * Translate a string using the selected locale. * Substitution works by replacing tokens like "{$foo}" with the value of * the parameter named "foo" (if supplied). * * @param string $key Locale key * @param array $params Named substitution parameters * * @return ?string */ public function translateSingular(string $key, array $params = []): ?string { $message = $this->getTranslator()->getSingular($key); return $message !== null ? $this->_format($message, $params) : null; } /** * Translate a string using the selected locale with support for plurals. * Substitution works by replacing tokens like "{$foo}" with the value of * the parameter named "foo" (if supplied). * * @param string $key Locale key * @param int $count Count of items * @param array $params Named substitution parameters * * @return string */ public function translatePlural(string $key, int $count, array $params = []): ?string { $message = $this->getTranslator()->getPlural($key, $count); return $message !== null ? $this->_format($message, $params) : null; } /** * Adds a new locale to the bundle */ public function addPath(string $path, int $priority = 0): void { $this->paths[$path] = $priority; $this->setEntries($this->paths); } /** * Retrieves the locale paths (keys) that are part of this bundle together with their priorities (values) * * @return int[] */ public function getEntries(): array { return $this->paths; } /** * Sets the locale paths (keys) that are part of this bundle together with their priorities (values) * * @param int[] $paths */ public function setEntries(array $paths): void { $this->paths = $paths; asort($this->paths); // Clears the cache $this->translator = null; } /** * Lazily build and retrieves the Translator instance */ public function getTranslator(): Translator { if($this->translator) { return $this->translator; } // Caches only the supported locales (avoid spending time with one-offs) $isSupported = Locale::isSupported($this->locale); $loader = function () use ($isSupported): Translator { $translator = new Translator(); // Merge all the locale files into a single structure $firstPath = array_key_first($this->paths); foreach (array_keys($this->paths) as $path) { $translations = LocaleFile::loadArray($path, $isSupported); // Once the first locale file is added, ensures only messages are merged $translator->addTranslations($firstPath === $path ? $translations : ['messages' => $translations['messages']]); } return $translator; }; $key = __METHOD__ . static::MAX_CACHE_LIFETIME . array_reduce(array_keys($this->paths), fn (string $hash, string $path): string => sha1($hash . $path . filemtime($path)), ''); $expiration = DateInterval::createFromDateString(static::MAX_CACHE_LIFETIME); return $this->translator ??= $isSupported ? Cache::remember($key, $expiration, $loader) : $loader(); } /** * Formats the translation */ private function _format(string $message, array $params = []) { return count($params) ? str_replace(array_map(fn (string $search): string => "{\${$search}}", array_keys($params)), array_values($params), $message) : $message; } }