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