031d974ba5c9526cdc7f1b2ee045e6cec28bd2e7
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Localization / Parser / LocallangXmlParser.php
1 <?php
2 namespace TYPO3\CMS\Core\Localization\Parser;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16 /**
17 * Parser for XML locallang file.
18 *
19 * @author Dominique Feyer <dfeyer@reelpeek.net>
20 */
21 class LocallangXmlParser extends \TYPO3\CMS\Core\Localization\Parser\AbstractXmlParser {
22
23 /**
24 * Associative array of "filename => parsed data" pairs.
25 *
26 * @var array
27 */
28 protected $parsedTargetFiles;
29
30 /**
31 * Returns parsed representation of XML file.
32 *
33 * @param string $sourcePath Source file path
34 * @param string $languageKey Language key
35 * @param string $charset Charset
36 * @return array
37 */
38 public function getParsedData($sourcePath, $languageKey, $charset = '') {
39 $this->sourcePath = $sourcePath;
40 $this->languageKey = $languageKey;
41 $this->charset = $this->getCharset($languageKey, $charset);
42 // Parse source
43 $parsedSource = $this->parseXmlFile();
44 // Parse target
45 $localizedTargetPath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName(\TYPO3\CMS\Core\Utility\GeneralUtility::llXmlAutoFileName($this->sourcePath, $this->languageKey));
46 $targetPath = $this->languageKey !== 'default' && @is_file($localizedTargetPath) ? $localizedTargetPath : $this->sourcePath;
47 try {
48 $parsedTarget = $this->getParsedTargetData($targetPath);
49 } catch (\TYPO3\CMS\Core\Localization\Exception\InvalidXmlFileException $e) {
50 $parsedTarget = $this->getParsedTargetData($this->sourcePath);
51 }
52 $LOCAL_LANG = array();
53 $LOCAL_LANG[$languageKey] = $parsedSource;
54 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG[$languageKey], $parsedTarget);
55 return $LOCAL_LANG;
56 }
57
58 /**
59 * Returns array representation of XLIFF data, starting from a root node.
60 *
61 * @param SimpleXMLElement $root XML root element
62 * @param string $element Target or Source
63 * @return array
64 */
65 protected function doParsingFromRootForElement(\SimpleXMLElement $root, $element) {
66 $bodyOfFileTag = $root->data->languageKey;
67 // Check if the source llxml file contains localized records
68 $localizedBodyOfFileTag = $root->data->xpath('languageKey[@index=\'' . $this->languageKey . '\']');
69 if ($element === 'source' || $this->languageKey === 'default') {
70 $parsedData = $this->getParsedDataForElement($bodyOfFileTag, $element);
71 } else {
72 $parsedData = array();
73 }
74 if ($element === 'target' && isset($localizedBodyOfFileTag[0]) && $localizedBodyOfFileTag[0] instanceof \SimpleXMLElement) {
75 $parsedDataTarget = $this->getParsedDataForElement($localizedBodyOfFileTag[0], $element);
76 $mergedData = $parsedDataTarget + $parsedData;
77 if ($this->languageKey === 'default') {
78 $parsedData = array_intersect_key($mergedData, $parsedData, $parsedDataTarget);
79 } else {
80 $parsedData = array_intersect_key($mergedData, $parsedDataTarget);
81 }
82 }
83 return $parsedData;
84 }
85
86 /**
87 * Parse the given language key tag
88 *
89 * @param SimpleXMLElement $bodyOfFileTag
90 * @param string $element
91 * @return array
92 */
93 protected function getParsedDataForElement(\SimpleXMLElement $bodyOfFileTag, $element) {
94 $parsedData = array();
95 if (count($bodyOfFileTag->children()) == 0) {
96 // Check for externally-referenced resource:
97 // <languageKey index="fr">EXT:yourext/path/to/localized/locallang.xml</languageKey>
98 $reference = sprintf('%s', $bodyOfFileTag);
99 if (substr($reference, -4) === '.xml') {
100 return $this->getParsedTargetData(\TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($reference));
101 }
102 }
103 foreach ($bodyOfFileTag->children() as $translationElement) {
104 if ($translationElement->getName() === 'label') {
105 // If restype would be set, it could be metadata from Gettext to XLIFF conversion (and we don't need this data)
106 $parsedData[(string) $translationElement['index']][0] = array(
107 $element => (string) $translationElement
108 );
109 }
110 }
111 return $parsedData;
112 }
113
114 /**
115 * Returns array representation of XLIFF data, starting from a root node.
116 *
117 * @param \SimpleXMLElement $root A root node
118 * @return array An array representing parsed XLIFF
119 */
120 protected function doParsingFromRoot(\SimpleXMLElement $root) {
121 return $this->doParsingFromRootForElement($root, 'source');
122 }
123
124 /**
125 * Returns array representation of XLIFF data, starting from a root node.
126 *
127 * @param \SimpleXMLElement $root A root node
128 * @return array An array representing parsed XLIFF
129 */
130 protected function doParsingTargetFromRoot(\SimpleXMLElement $root) {
131 return $this->doParsingFromRootForElement($root, 'target');
132 }
133
134 /**
135 * Returns parsed representation of XML file.
136 *
137 * Parses XML if it wasn't done before. Caches parsed data.
138 *
139 * @param string $path An absolute path to XML file
140 * @return array Parsed XML file
141 */
142 public function getParsedTargetData($path) {
143 if (!isset($this->parsedTargetFiles[$path])) {
144 $this->parsedTargetFiles[$path] = $this->parseXmlTargetFile($path);
145 }
146 return $this->parsedTargetFiles[$path];
147 }
148
149 /**
150 * Reads and parses XML file and returns internal representation of data.
151 *
152 * @param string $targetPath Path of the target file
153 * @return array
154 * @throws \TYPO3\CMS\Core\Localization\Exception\InvalidXmlFileException
155 */
156 protected function parseXmlTargetFile($targetPath) {
157 $rootXmlNode = FALSE;
158 if (file_exists($targetPath)) {
159 $rootXmlNode = simplexml_load_file($targetPath, 'SimpleXmlElement', \LIBXML_NOWARNING);
160 }
161 if (!isset($rootXmlNode) || $rootXmlNode === FALSE) {
162 throw new \TYPO3\CMS\Core\Localization\Exception\InvalidXmlFileException('The path provided does not point to existing and accessible well-formed XML file (' . $targetPath . ').', 1278155987);
163 }
164 return $this->doParsingTargetFromRoot($rootXmlNode);
165 }
166
167 }