PKmZ{PVMoFileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; /** * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/) */ class MoFileLoader extends FileLoader { /** * Magic used for validating the format of an MO file as well as * detecting if the machine used to create that file was little endian. */ public const MO_LITTLE_ENDIAN_MAGIC = 0x950412DE; /** * Magic used for validating the format of an MO file as well as * detecting if the machine used to create that file was big endian. */ public const MO_BIG_ENDIAN_MAGIC = 0xDE120495; /** * The size of the header of an MO file in bytes. */ public const MO_HEADER_SIZE = 28; /** * Parses machine object (MO) format, independent of the machine's endian it * was created on. Both 32bit and 64bit systems are supported. */ protected function loadResource(string $resource): array { $stream = fopen($resource, 'r'); $stat = fstat($stream); if ($stat['size'] < self::MO_HEADER_SIZE) { throw new InvalidResourceException('MO stream content has an invalid format.'); } $magic = unpack('V1', fread($stream, 4)); $magic = hexdec(substr(dechex(current($magic)), -8)); if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) { $isBigEndian = false; } elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) { $isBigEndian = true; } else { throw new InvalidResourceException('MO stream content has an invalid format.'); } // formatRevision $this->readLong($stream, $isBigEndian); $count = $this->readLong($stream, $isBigEndian); $offsetId = $this->readLong($stream, $isBigEndian); $offsetTranslated = $this->readLong($stream, $isBigEndian); // sizeHashes $this->readLong($stream, $isBigEndian); // offsetHashes $this->readLong($stream, $isBigEndian); $messages = []; for ($i = 0; $i < $count; ++$i) { $pluralId = null; $translated = null; fseek($stream, $offsetId + $i * 8); $length = $this->readLong($stream, $isBigEndian); $offset = $this->readLong($stream, $isBigEndian); if ($length < 1) { continue; } fseek($stream, $offset); $singularId = fread($stream, $length); if (str_contains($singularId, "\000")) { [$singularId, $pluralId] = explode("\000", $singularId); } fseek($stream, $offsetTranslated + $i * 8); $length = $this->readLong($stream, $isBigEndian); $offset = $this->readLong($stream, $isBigEndian); if ($length < 1) { continue; } fseek($stream, $offset); $translated = fread($stream, $length); if (str_contains($translated, "\000")) { $translated = explode("\000", $translated); } $ids = ['singular' => $singularId, 'plural' => $pluralId]; $item = compact('ids', 'translated'); if (!empty($item['ids']['singular'])) { $id = $item['ids']['singular']; if (isset($item['ids']['plural'])) { $id .= '|'.$item['ids']['plural']; } $messages[$id] = stripcslashes(implode('|', (array) $item['translated'])); } } fclose($stream); return array_filter($messages); } /** * Reads an unsigned long from stream respecting endianness. * * @param resource $stream */ private function readLong($stream, bool $isBigEndian): int { $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4)); $result = current($result); return (int) substr($result, -8); } } PKmZ6LoaderInterface.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\MessageCatalogue; /** * LoaderInterface is the interface implemented by all translation loaders. * * @author Fabien Potencier */ interface LoaderInterface { /** * Loads a locale. * * @throws NotFoundResourceException when the resource cannot be found * @throws InvalidResourceException when the resource cannot be loaded */ public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue; } PKmZα QtFileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\Exception\RuntimeException; use Symfony\Component\Translation\MessageCatalogue; /** * QtFileLoader loads translations from QT Translations XML files. * * @author Benjamin Eberlei */ class QtFileLoader implements LoaderInterface { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!class_exists(XmlUtils::class)) { throw new RuntimeException('Loading translations from the QT format requires the Symfony Config component.'); } if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!file_exists($resource)) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } try { $dom = XmlUtils::loadFile($resource); } catch (\InvalidArgumentException $e) { throw new InvalidResourceException(sprintf('Unable to load "%s".', $resource), $e->getCode(), $e); } $internalErrors = libxml_use_internal_errors(true); libxml_clear_errors(); $xpath = new \DOMXPath($dom); $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]'); $catalogue = new MessageCatalogue($locale); if (1 == $nodes->length) { $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message'); foreach ($translations as $translation) { $translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue; if (!empty($translationValue)) { $catalogue->set( (string) $translation->getElementsByTagName('source')->item(0)->nodeValue, $translationValue, $domain ); } } if (class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource)); } } libxml_use_internal_errors($internalErrors); return $catalogue; } } PKmZArrayLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\MessageCatalogue; /** * ArrayLoader loads translations from a PHP array. * * @author Fabien Potencier */ class ArrayLoader implements LoaderInterface { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { $resource = $this->flatten($resource); $catalogue = new MessageCatalogue($locale); $catalogue->add($resource, $domain); return $catalogue; } /** * Flattens an nested array of translations. * * The scheme used is: * 'key' => ['key2' => ['key3' => 'value']] * Becomes: * 'key.key2.key3' => 'value' */ private function flatten(array $messages): array { $result = []; foreach ($messages as $key => $value) { if (\is_array($value)) { foreach ($this->flatten($value) as $k => $v) { if (null !== $v) { $result[$key.'.'.$k] = $v; } } } elseif (null !== $value) { $result[$key] = $value; } } return $result; } } PKmZJsonFileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; /** * JsonFileLoader loads translations from an json file. * * @author singles */ class JsonFileLoader extends FileLoader { protected function loadResource(string $resource): array { $messages = []; if ($data = file_get_contents($resource)) { $messages = json_decode($data, true); if (0 < $errorCode = json_last_error()) { throw new InvalidResourceException('Error parsing JSON: '.$this->getJSONErrorMessage($errorCode)); } } return $messages; } /** * Translates JSON_ERROR_* constant into meaningful message. */ private function getJSONErrorMessage(int $errorCode): string { return match ($errorCode) { \JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', \JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch', \JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', \JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', \JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded', default => 'Unknown error', }; } } PKmZo'""XliffFileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Util\Exception\InvalidXmlException; use Symfony\Component\Config\Util\Exception\XmlParsingException; use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\Exception\RuntimeException; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Util\XliffUtils; /** * XliffFileLoader loads translations from XLIFF files. * * @author Fabien Potencier */ class XliffFileLoader implements LoaderInterface { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!class_exists(XmlUtils::class)) { throw new RuntimeException('Loading translations from the Xliff format requires the Symfony Config component.'); } if (!$this->isXmlString($resource)) { if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!file_exists($resource)) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } if (!is_file($resource)) { throw new InvalidResourceException(sprintf('This is neither a file nor an XLIFF string "%s".', $resource)); } } try { if ($this->isXmlString($resource)) { $dom = XmlUtils::parse($resource); } else { $dom = XmlUtils::loadFile($resource); } } catch (\InvalidArgumentException|XmlParsingException|InvalidXmlException $e) { throw new InvalidResourceException(sprintf('Unable to load "%s": ', $resource).$e->getMessage(), $e->getCode(), $e); } if ($errors = XliffUtils::validateSchema($dom)) { throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: ', $resource).XliffUtils::getErrorsAsString($errors)); } $catalogue = new MessageCatalogue($locale); $this->extract($dom, $catalogue, $domain); if (is_file($resource) && class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource)); } return $catalogue; } private function extract(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void { $xliffVersion = XliffUtils::getVersionNumber($dom); if ('1.2' === $xliffVersion) { $this->extractXliff1($dom, $catalogue, $domain); } if ('2.0' === $xliffVersion) { $this->extractXliff2($dom, $catalogue, $domain); } } /** * Extract messages and metadata from DOMDocument into a MessageCatalogue. */ private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void { $xml = simplexml_import_dom($dom); $encoding = $dom->encoding ? strtoupper($dom->encoding) : null; $namespace = 'urn:oasis:names:tc:xliff:document:1.2'; $xml->registerXPathNamespace('xliff', $namespace); foreach ($xml->xpath('//xliff:file') as $file) { $fileAttributes = $file->attributes(); $file->registerXPathNamespace('xliff', $namespace); foreach ($file->xpath('.//xliff:prop') as $prop) { $catalogue->setCatalogueMetadata($prop->attributes()['prop-type'], (string) $prop, $domain); } foreach ($file->xpath('.//xliff:trans-unit') as $translation) { $attributes = $translation->attributes(); if (!(isset($attributes['resname']) || isset($translation->source))) { continue; } $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source; // If the xlf file has another encoding specified, try to convert it because // simple_xml will always return utf-8 encoded values $target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding); $catalogue->set((string) $source, $target, $domain); $metadata = [ 'source' => (string) $translation->source, 'file' => [ 'original' => (string) $fileAttributes['original'], ], ]; if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) { $metadata['notes'] = $notes; } if (isset($translation->target) && $translation->target->attributes()) { $metadata['target-attributes'] = []; foreach ($translation->target->attributes() as $key => $value) { $metadata['target-attributes'][$key] = (string) $value; } } if (isset($attributes['id'])) { $metadata['id'] = (string) $attributes['id']; } $catalogue->setMetadata((string) $source, $metadata, $domain); } } } private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void { $xml = simplexml_import_dom($dom); $encoding = $dom->encoding ? strtoupper($dom->encoding) : null; $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0'); foreach ($xml->xpath('//xliff:unit') as $unit) { foreach ($unit->segment as $segment) { $attributes = $unit->attributes(); $source = $attributes['name'] ?? $segment->source; // If the xlf file has another encoding specified, try to convert it because // simple_xml will always return utf-8 encoded values $target = $this->utf8ToCharset((string) ($segment->target ?? $segment->source), $encoding); $catalogue->set((string) $source, $target, $domain); $metadata = []; if (isset($segment->target) && $segment->target->attributes()) { $metadata['target-attributes'] = []; foreach ($segment->target->attributes() as $key => $value) { $metadata['target-attributes'][$key] = (string) $value; } } if (isset($unit->notes)) { $metadata['notes'] = []; foreach ($unit->notes->note as $noteNode) { $note = []; foreach ($noteNode->attributes() as $key => $value) { $note[$key] = (string) $value; } $note['content'] = (string) $noteNode; $metadata['notes'][] = $note; } } $catalogue->setMetadata((string) $source, $metadata, $domain); } } } /** * Convert a UTF8 string to the specified encoding. */ private function utf8ToCharset(string $content, string $encoding = null): string { if ('UTF-8' !== $encoding && !empty($encoding)) { return mb_convert_encoding($content, $encoding, 'UTF-8'); } return $content; } private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array { $notes = []; if (null === $noteElement) { return $notes; } /** @var \SimpleXMLElement $xmlNote */ foreach ($noteElement as $xmlNote) { $noteAttributes = $xmlNote->attributes(); $note = ['content' => $this->utf8ToCharset((string) $xmlNote, $encoding)]; if (isset($noteAttributes['priority'])) { $note['priority'] = (int) $noteAttributes['priority']; } if (isset($noteAttributes['from'])) { $note['from'] = (string) $noteAttributes['from']; } $notes[] = $note; } return $notes; } private function isXmlString(string $resource): bool { return str_starts_with($resource, ' * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; /** * PhpFileLoader loads translations from PHP files returning an array of translations. * * @author Fabien Potencier */ class PhpFileLoader extends FileLoader { private static ?array $cache = []; protected function loadResource(string $resource): array { if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOL) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOL))) { self::$cache = null; } if (null === self::$cache) { return require $resource; } return self::$cache[$resource] ??= require $resource; } } PKmZc1FileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\MessageCatalogue; /** * @author Abdellatif Ait boudad */ abstract class FileLoader extends ArrayLoader { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!file_exists($resource)) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } $messages = $this->loadResource($resource); // empty resource $messages ??= []; // not an array if (!\is_array($messages)) { throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource)); } $catalogue = parent::load($messages, $locale, $domain); if (class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource)); } return $catalogue; } /** * @throws InvalidResourceException if stream content has an invalid format */ abstract protected function loadResource(string $resource): array; } PKmZqPoFileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; /** * @copyright Copyright (c) 2010, Union of RAD https://github.com/UnionOfRAD/lithium * @copyright Copyright (c) 2012, Clemens Tolboom */ class PoFileLoader extends FileLoader { /** * Parses portable object (PO) format. * * From https://www.gnu.org/software/gettext/manual/gettext.html#PO-Files * we should be able to parse files having: * * white-space * # translator-comments * #. extracted-comments * #: reference... * #, flag... * #| msgid previous-untranslated-string * msgid untranslated-string * msgstr translated-string * * extra or different lines are: * * #| msgctxt previous-context * #| msgid previous-untranslated-string * msgctxt context * * #| msgid previous-untranslated-string-singular * #| msgid_plural previous-untranslated-string-plural * msgid untranslated-string-singular * msgid_plural untranslated-string-plural * msgstr[0] translated-string-case-0 * ... * msgstr[N] translated-string-case-n * * The definition states: * - white-space and comments are optional. * - msgid "" that an empty singleline defines a header. * * This parser sacrifices some features of the reference implementation the * differences to that implementation are as follows. * - No support for comments spanning multiple lines. * - Translator and extracted comments are treated as being the same type. * - Message IDs are allowed to have other encodings as just US-ASCII. * * Items with an empty id are ignored. */ protected function loadResource(string $resource): array { $stream = fopen($resource, 'r'); $defaults = [ 'ids' => [], 'translated' => null, ]; $messages = []; $item = $defaults; $flags = []; while ($line = fgets($stream)) { $line = trim($line); if ('' === $line) { // Whitespace indicated current item is done if (!\in_array('fuzzy', $flags)) { $this->addMessage($messages, $item); } $item = $defaults; $flags = []; } elseif (str_starts_with($line, '#,')) { $flags = array_map('trim', explode(',', substr($line, 2))); } elseif (str_starts_with($line, 'msgid "')) { // We start a new msg so save previous // TODO: this fails when comments or contexts are added $this->addMessage($messages, $item); $item = $defaults; $item['ids']['singular'] = substr($line, 7, -1); } elseif (str_starts_with($line, 'msgstr "')) { $item['translated'] = substr($line, 8, -1); } elseif ('"' === $line[0]) { $continues = isset($item['translated']) ? 'translated' : 'ids'; if (\is_array($item[$continues])) { end($item[$continues]); $item[$continues][key($item[$continues])] .= substr($line, 1, -1); } else { $item[$continues] .= substr($line, 1, -1); } } elseif (str_starts_with($line, 'msgid_plural "')) { $item['ids']['plural'] = substr($line, 14, -1); } elseif (str_starts_with($line, 'msgstr[')) { $size = strpos($line, ']'); $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1); } } // save last item if (!\in_array('fuzzy', $flags)) { $this->addMessage($messages, $item); } fclose($stream); return $messages; } /** * Save a translation item to the messages. * * A .po file could contain by error missing plural indexes. We need to * fix these before saving them. */ private function addMessage(array &$messages, array $item): void { if (!empty($item['ids']['singular'])) { $id = stripcslashes($item['ids']['singular']); if (isset($item['ids']['plural'])) { $id .= '|'.stripcslashes($item['ids']['plural']); } $translated = (array) $item['translated']; // PO are by definition indexed so sort by index. ksort($translated); // Make sure every index is filled. end($translated); $count = key($translated); // Fill missing spots with '-'. $empties = array_fill(0, $count + 1, '-'); $translated += $empties; ksort($translated); $messages[$id] = stripcslashes(implode('|', $translated)); } } } PKmZ -$$IcuDatFileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\MessageCatalogue; /** * IcuResFileLoader loads translations from a resource bundle. * * @author stealth35 */ class IcuDatFileLoader extends IcuResFileLoader { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!stream_is_local($resource.'.dat')) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!file_exists($resource.'.dat')) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception) { $rb = null; } if (!$rb) { throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource)); } elseif (intl_is_failure($rb->getErrorCode())) { throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); } $messages = $this->flatten($rb); $catalogue = new MessageCatalogue($locale); $catalogue->add($messages, $domain); if (class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource.'.dat')); } return $catalogue; } } PKmZ>IniFileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; /** * IniFileLoader loads translations from an ini file. * * @author stealth35 */ class IniFileLoader extends FileLoader { protected function loadResource(string $resource): array { return parse_ini_file($resource, true); } } PKmZ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\LogicException; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Parser as YamlParser; use Symfony\Component\Yaml\Yaml; /** * YamlFileLoader loads translations from Yaml files. * * @author Fabien Potencier */ class YamlFileLoader extends FileLoader { private $yamlParser; protected function loadResource(string $resource): array { if (null === $this->yamlParser) { if (!class_exists(\Symfony\Component\Yaml\Parser::class)) { throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.'); } $this->yamlParser = new YamlParser(); } try { $messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { throw new InvalidResourceException(sprintf('The file "%s" does not contain valid YAML: ', $resource).$e->getMessage(), 0, $e); } if (null !== $messages && !\is_array($messages)) { throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource)); } return $messages ?: []; } } PKmZš& IcuResFileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\MessageCatalogue; /** * IcuResFileLoader loads translations from a resource bundle. * * @author stealth35 */ class IcuResFileLoader implements LoaderInterface { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!is_dir($resource)) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception) { $rb = null; } if (!$rb) { throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource)); } elseif (intl_is_failure($rb->getErrorCode())) { throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); } $messages = $this->flatten($rb); $catalogue = new MessageCatalogue($locale); $catalogue->add($messages, $domain); if (class_exists(DirectoryResource::class)) { $catalogue->addResource(new DirectoryResource($resource)); } return $catalogue; } /** * Flattens an ResourceBundle. * * The scheme used is: * key { key2 { key3 { "value" } } } * Becomes: * 'key.key2.key3' => 'value' * * This function takes an array by reference and will modify it * * @param \ResourceBundle $rb The ResourceBundle that will be flattened * @param array $messages Used internally for recursive calls * @param string|null $path Current path being parsed, used internally for recursive calls */ protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null): array { foreach ($rb as $key => $value) { $nodePath = $path ? $path.'.'.$key : $key; if ($value instanceof \ResourceBundle) { $this->flatten($value, $messages, $nodePath); } else { $messages[$nodePath] = $value; } } return $messages; } } PKmZEhCsvFileLoader.phpnuW+A * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\NotFoundResourceException; /** * CsvFileLoader loads translations from CSV files. * * @author Saša Stamenković */ class CsvFileLoader extends FileLoader { private string $delimiter = ';'; private string $enclosure = '"'; private string $escape = '\\'; protected function loadResource(string $resource): array { $messages = []; try { $file = new \SplFileObject($resource, 'rb'); } catch (\RuntimeException $e) { throw new NotFoundResourceException(sprintf('Error opening file "%s".', $resource), 0, $e); } $file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY); $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape); foreach ($file as $data) { if (false === $data) { continue; } if (!str_starts_with($data[0], '#') && isset($data[1]) && 2 === \count($data)) { $messages[$data[0]] = $data[1]; } } return $messages; } /** * Sets the delimiter, enclosure, and escape character for CSV. * * @return void */ public function setCsvControl(string $delimiter = ';', string $enclosure = '"', string $escape = '\\') { $this->delimiter = $delimiter; $this->enclosure = $enclosure; $this->escape = $escape; } } PKZ͐1 Resolver.phpnuW+AgetVars(), static function (string $s, int $i) use ($repository) { return Str::substr($s, 0, $i).self::resolveVariable($repository, Str::substr($s, $i)); }, $value->getChars()); } /** * Resolve a single nested variable. * * @param \Dotenv\Repository\RepositoryInterface $repository * @param string $str * * @return string */ private static function resolveVariable(RepositoryInterface $repository, string $str) { return Regex::replaceCallback( '/\A\${([a-zA-Z0-9_.]+)}/', static function (array $matches) use ($repository) { return Option::fromValue($repository->get($matches[1])) ->getOrElse($matches[0]); }, $str, 1 )->success()->getOrElse($str); } } PKZj Loader.phpnuW+A */ public function load(RepositoryInterface $repository, array $entries) { return \array_reduce($entries, static function (array $vars, Entry $entry) use ($repository) { $name = $entry->getName(); $value = $entry->getValue()->map(static function (Value $value) use ($repository) { return Resolver::resolve($repository, $value); }); if ($value->isDefined()) { $inner = $value->get(); if ($repository->set($name, $inner)) { return \array_merge($vars, [$name => $inner]); } } else { if ($repository->clear($name)) { return \array_merge($vars, [$name => null]); } } return $vars; }, []); } } PKmZ{PVMoFileLoader.phpnuW+APKmZ6LoaderInterface.phpnuW+APKmZα QtFileLoader.phpnuW+APKmZArrayLoader.phpnuW+APKmZ&JsonFileLoader.phpnuW+APKmZo'""C,XliffFileLoader.phpnuW+APKmZ+8NOPhpFileLoader.phpnuW+APKmZc1hSFileLoader.phpnuW+APKmZqcZPoFileLoader.phpnuW+APKmZ -$$nIcuDatFileLoader.phpnuW+APKmZ>uIniFileLoader.phpnuW+APKmZ