1<?php 2 3namespace Gettext\Extractors; 4 5use Gettext\Translations; 6use Gettext\Translation; 7use Gettext\Utils\HeadersExtractorTrait; 8 9/** 10 * Class to get gettext strings from php files returning arrays. 11 */ 12class Po extends Extractor implements ExtractorInterface 13{ 14 use HeadersExtractorTrait; 15 16 /** 17 * Parses a .po file and append the translations found in the Translations instance. 18 * 19 * {@inheritdoc} 20 */ 21 public static function fromString($string, Translations $translations, array $options = []) 22 { 23 $lines = explode("\n", $string); 24 $i = 0; 25 26 $translation = $translations->createNewTranslation('', ''); 27 28 for ($n = count($lines); $i < $n; ++$i) { 29 $line = trim($lines[$i]); 30 $line = static::fixMultiLines($line, $lines, $i); 31 32 if ($line === '') { 33 if ($translation->is('', '')) { 34 static::extractHeaders($translation->getTranslation(), $translations); 35 } elseif ($translation->hasOriginal()) { 36 $translations[] = $translation; 37 } 38 39 $translation = $translations->createNewTranslation('', ''); 40 continue; 41 } 42 43 $splitLine = preg_split('/\s+/', $line, 2); 44 $key = $splitLine[0]; 45 $data = isset($splitLine[1]) ? $splitLine[1] : ''; 46 47 if ($key === '#~') { 48 $translation->setDisabled(true); 49 50 $splitLine = preg_split('/\s+/', $data, 2); 51 $key = $splitLine[0]; 52 $data = isset($splitLine[1]) ? $splitLine[1] : ''; 53 } 54 55 switch ($key) { 56 case '#': 57 $translation->addComment($data); 58 $append = null; 59 break; 60 61 case '#.': 62 $translation->addExtractedComment($data); 63 $append = null; 64 break; 65 66 case '#,': 67 foreach (array_map('trim', explode(',', trim($data))) as $value) { 68 $translation->addFlag($value); 69 } 70 $append = null; 71 break; 72 73 case '#:': 74 foreach (preg_split('/\s+/', trim($data)) as $value) { 75 if (preg_match('/^(.+)(:(\d*))?$/U', $value, $matches)) { 76 $translation->addReference($matches[1], isset($matches[3]) ? $matches[3] : null); 77 } 78 } 79 $append = null; 80 break; 81 82 case 'msgctxt': 83 $translation = $translation->getClone(static::convertString($data)); 84 $append = 'Context'; 85 break; 86 87 case 'msgid': 88 $translation = $translation->getClone(null, static::convertString($data)); 89 $append = 'Original'; 90 break; 91 92 case 'msgid_plural': 93 $translation->setPlural(static::convertString($data)); 94 $append = 'Plural'; 95 break; 96 97 case 'msgstr': 98 case 'msgstr[0]': 99 $translation->setTranslation(static::convertString($data)); 100 $append = 'Translation'; 101 break; 102 103 case 'msgstr[1]': 104 $translation->setPluralTranslations([static::convertString($data)]); 105 $append = 'PluralTranslation'; 106 break; 107 108 default: 109 if (strpos($key, 'msgstr[') === 0) { 110 $p = $translation->getPluralTranslations(); 111 $p[] = static::convertString($data); 112 113 $translation->setPluralTranslations($p); 114 $append = 'PluralTranslation'; 115 break; 116 } 117 118 if (isset($append)) { 119 if ($append === 'Context') { 120 $translation = $translation->getClone($translation->getContext() 121 ."\n" 122 .static::convertString($data)); 123 break; 124 } 125 126 if ($append === 'Original') { 127 $translation = $translation->getClone(null, $translation->getOriginal() 128 ."\n" 129 .static::convertString($data)); 130 break; 131 } 132 133 if ($append === 'PluralTranslation') { 134 $p = $translation->getPluralTranslations(); 135 $p[] = array_pop($p)."\n".static::convertString($data); 136 $translation->setPluralTranslations($p); 137 break; 138 } 139 140 $getMethod = 'get'.$append; 141 $setMethod = 'set'.$append; 142 $translation->$setMethod($translation->$getMethod()."\n".static::convertString($data)); 143 } 144 break; 145 } 146 } 147 148 if ($translation->hasOriginal() && !in_array($translation, iterator_to_array($translations))) { 149 $translations[] = $translation; 150 } 151 } 152 153 /** 154 * Gets one string from multiline strings. 155 * 156 * @param string $line 157 * @param array $lines 158 * @param int &$i 159 * 160 * @return string 161 */ 162 protected static function fixMultiLines($line, array $lines, &$i) 163 { 164 for ($j = $i, $t = count($lines); $j < $t; ++$j) { 165 if (substr($line, -1, 1) == '"' 166 && isset($lines[$j + 1]) 167 && substr(trim($lines[$j + 1]), 0, 1) == '"' 168 ) { 169 $line = substr($line, 0, -1).substr(trim($lines[$j + 1]), 1); 170 } else { 171 $i = $j; 172 break; 173 } 174 } 175 176 return $line; 177 } 178 179 /** 180 * Convert a string from its PO representation. 181 * 182 * @param string $value 183 * 184 * @return string 185 */ 186 public static function convertString($value) 187 { 188 if (!$value) { 189 return ''; 190 } 191 192 if ($value[0] === '"') { 193 $value = substr($value, 1, -1); 194 } 195 196 return strtr( 197 $value, 198 [ 199 '\\\\' => '\\', 200 '\\a' => "\x07", 201 '\\b' => "\x08", 202 '\\t' => "\t", 203 '\\n' => "\n", 204 '\\v' => "\x0b", 205 '\\f' => "\x0c", 206 '\\r' => "\r", 207 '\\"' => '"', 208 ] 209 ); 210 } 211} 212