annotate core/lib/Drupal/Component/Gettext/PoStreamReader.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Component\Gettext;
Chris@0 4
Chris@17 5 use Drupal\Component\Render\FormattableMarkup;
Chris@0 6
Chris@0 7 /**
Chris@0 8 * Implements Gettext PO stream reader.
Chris@0 9 *
Chris@0 10 * The PO file format parsing is implemented according to the documentation at
Chris@0 11 * http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
Chris@0 12 */
Chris@0 13 class PoStreamReader implements PoStreamInterface, PoReaderInterface {
Chris@0 14
Chris@0 15 /**
Chris@0 16 * Source line number of the stream being parsed.
Chris@0 17 *
Chris@0 18 * @var int
Chris@0 19 */
Chris@17 20 protected $lineNumber = 0;
Chris@0 21
Chris@0 22 /**
Chris@0 23 * Parser context for the stream reader state machine.
Chris@0 24 *
Chris@0 25 * Possible contexts are:
Chris@0 26 * - 'COMMENT' (#)
Chris@0 27 * - 'MSGID' (msgid)
Chris@0 28 * - 'MSGID_PLURAL' (msgid_plural)
Chris@0 29 * - 'MSGCTXT' (msgctxt)
Chris@0 30 * - 'MSGSTR' (msgstr or msgstr[])
Chris@0 31 * - 'MSGSTR_ARR' (msgstr_arg)
Chris@0 32 *
Chris@0 33 * @var string
Chris@0 34 */
Chris@17 35 protected $context = 'COMMENT';
Chris@0 36
Chris@0 37 /**
Chris@0 38 * Current entry being read. Incomplete.
Chris@0 39 *
Chris@0 40 * @var array
Chris@0 41 */
Chris@17 42 protected $currentItem = [];
Chris@0 43
Chris@0 44 /**
Chris@0 45 * Current plural index for plural translations.
Chris@0 46 *
Chris@0 47 * @var int
Chris@0 48 */
Chris@17 49 protected $currentPluralIndex = 0;
Chris@0 50
Chris@0 51 /**
Chris@0 52 * URI of the PO stream that is being read.
Chris@0 53 *
Chris@0 54 * @var string
Chris@0 55 */
Chris@17 56 protected $uri = '';
Chris@0 57
Chris@0 58 /**
Chris@0 59 * Language code for the PO stream being read.
Chris@0 60 *
Chris@0 61 * @var string
Chris@0 62 */
Chris@17 63 protected $langcode = NULL;
Chris@0 64
Chris@0 65 /**
Chris@0 66 * File handle of the current PO stream.
Chris@0 67 *
Chris@0 68 * @var resource
Chris@0 69 */
Chris@17 70 protected $fd;
Chris@0 71
Chris@0 72 /**
Chris@0 73 * The PO stream header.
Chris@0 74 *
Chris@0 75 * @var \Drupal\Component\Gettext\PoHeader
Chris@0 76 */
Chris@17 77 protected $header;
Chris@0 78
Chris@0 79 /**
Chris@0 80 * Object wrapper for the last read source/translation pair.
Chris@0 81 *
Chris@0 82 * @var \Drupal\Component\Gettext\PoItem
Chris@0 83 */
Chris@17 84 protected $lastItem;
Chris@0 85
Chris@0 86 /**
Chris@0 87 * Indicator of whether the stream reading is finished.
Chris@0 88 *
Chris@0 89 * @var bool
Chris@0 90 */
Chris@17 91 protected $finished;
Chris@0 92
Chris@0 93 /**
Chris@0 94 * Array of translated error strings recorded on reading this stream so far.
Chris@0 95 *
Chris@0 96 * @var array
Chris@0 97 */
Chris@17 98 protected $errors;
Chris@0 99
Chris@0 100 /**
Chris@0 101 * {@inheritdoc}
Chris@0 102 */
Chris@0 103 public function getLangcode() {
Chris@17 104 return $this->langcode;
Chris@0 105 }
Chris@0 106
Chris@0 107 /**
Chris@0 108 * {@inheritdoc}
Chris@0 109 */
Chris@0 110 public function setLangcode($langcode) {
Chris@17 111 $this->langcode = $langcode;
Chris@0 112 }
Chris@0 113
Chris@0 114 /**
Chris@0 115 * {@inheritdoc}
Chris@0 116 */
Chris@0 117 public function getHeader() {
Chris@17 118 return $this->header;
Chris@0 119 }
Chris@0 120
Chris@0 121 /**
Chris@0 122 * Implements Drupal\Component\Gettext\PoMetadataInterface::setHeader().
Chris@0 123 *
Chris@0 124 * Not applicable to stream reading and therefore not implemented.
Chris@0 125 */
Chris@0 126 public function setHeader(PoHeader $header) {
Chris@0 127 }
Chris@0 128
Chris@0 129 /**
Chris@0 130 * {@inheritdoc}
Chris@0 131 */
Chris@0 132 public function getURI() {
Chris@17 133 return $this->uri;
Chris@0 134 }
Chris@0 135
Chris@0 136 /**
Chris@0 137 * {@inheritdoc}
Chris@0 138 */
Chris@0 139 public function setURI($uri) {
Chris@17 140 $this->uri = $uri;
Chris@0 141 }
Chris@0 142
Chris@0 143 /**
Chris@0 144 * Implements Drupal\Component\Gettext\PoStreamInterface::open().
Chris@0 145 *
Chris@0 146 * Opens the stream and reads the header. The stream is ready for reading
Chris@0 147 * items after.
Chris@0 148 *
Chris@14 149 * @throws \Exception
Chris@0 150 * If the URI is not yet set.
Chris@0 151 */
Chris@0 152 public function open() {
Chris@17 153 if (!empty($this->uri)) {
Chris@17 154 $this->fd = fopen($this->uri, 'rb');
Chris@0 155 $this->readHeader();
Chris@0 156 }
Chris@0 157 else {
Chris@0 158 throw new \Exception('Cannot open stream without URI set.');
Chris@0 159 }
Chris@0 160 }
Chris@0 161
Chris@0 162 /**
Chris@0 163 * Implements Drupal\Component\Gettext\PoStreamInterface::close().
Chris@0 164 *
Chris@14 165 * @throws \Exception
Chris@0 166 * If the stream is not open.
Chris@0 167 */
Chris@0 168 public function close() {
Chris@17 169 if ($this->fd) {
Chris@17 170 fclose($this->fd);
Chris@0 171 }
Chris@0 172 else {
Chris@0 173 throw new \Exception('Cannot close stream that is not open.');
Chris@0 174 }
Chris@0 175 }
Chris@0 176
Chris@0 177 /**
Chris@0 178 * {@inheritdoc}
Chris@0 179 */
Chris@0 180 public function readItem() {
Chris@0 181 // Clear out the last item.
Chris@17 182 $this->lastItem = NULL;
Chris@0 183
Chris@0 184 // Read until finished with the stream or a complete item was identified.
Chris@17 185 while (!$this->finished && is_null($this->lastItem)) {
Chris@0 186 $this->readLine();
Chris@0 187 }
Chris@0 188
Chris@17 189 return $this->lastItem;
Chris@0 190 }
Chris@0 191
Chris@0 192 /**
Chris@0 193 * Sets the seek position for the current PO stream.
Chris@0 194 *
Chris@0 195 * @param int $seek
Chris@0 196 * The new seek position to set.
Chris@0 197 */
Chris@0 198 public function setSeek($seek) {
Chris@17 199 fseek($this->fd, $seek);
Chris@0 200 }
Chris@0 201
Chris@0 202 /**
Chris@0 203 * Gets the pointer position of the current PO stream.
Chris@0 204 */
Chris@0 205 public function getSeek() {
Chris@17 206 return ftell($this->fd);
Chris@0 207 }
Chris@0 208
Chris@0 209 /**
Chris@0 210 * Read the header from the PO stream.
Chris@0 211 *
Chris@0 212 * The header is a special case PoItem, using the empty string as source and
Chris@0 213 * key-value pairs as translation. We just reuse the item reader logic to
Chris@0 214 * read the header.
Chris@0 215 */
Chris@0 216 private function readHeader() {
Chris@0 217 $item = $this->readItem();
Chris@0 218 // Handle the case properly when the .po file is empty (0 bytes).
Chris@0 219 if (!$item) {
Chris@0 220 return;
Chris@0 221 }
Chris@0 222 $header = new PoHeader();
Chris@0 223 $header->setFromString(trim($item->getTranslation()));
Chris@17 224 $this->header = $header;
Chris@0 225 }
Chris@0 226
Chris@0 227 /**
Chris@0 228 * Reads a line from the PO stream and stores data internally.
Chris@0 229 *
Chris@17 230 * Expands $this->current_item based on new data for the current item. If
Chris@0 231 * this line ends the current item, it is saved with setItemFromArray() with
Chris@17 232 * data from $this->current_item.
Chris@0 233 *
Chris@0 234 * An internal state machine is maintained in this reader using
Chris@17 235 * $this->context as the reading state. PO items are in between COMMENT
Chris@0 236 * states (when items have at least one line or comment in between them) or
Chris@0 237 * indicated by MSGSTR or MSGSTR_ARR followed immediately by an MSGID or
Chris@0 238 * MSGCTXT (when items closely follow each other).
Chris@0 239 *
Chris@0 240 * @return
Chris@0 241 * FALSE if an error was logged, NULL otherwise. The errors are considered
Chris@0 242 * non-blocking, so reading can continue, while the errors are collected
Chris@0 243 * for later presentation.
Chris@0 244 */
Chris@0 245 private function readLine() {
Chris@0 246 // Read a line and set the stream finished indicator if it was not
Chris@0 247 // possible anymore.
Chris@17 248 $line = fgets($this->fd);
Chris@17 249 $this->finished = ($line === FALSE);
Chris@0 250
Chris@17 251 if (!$this->finished) {
Chris@0 252
Chris@17 253 if ($this->lineNumber == 0) {
Chris@0 254 // The first line might come with a UTF-8 BOM, which should be removed.
Chris@0 255 $line = str_replace("\xEF\xBB\xBF", '', $line);
Chris@0 256 // Current plurality for 'msgstr[]'.
Chris@17 257 $this->currentPluralIndex = 0;
Chris@0 258 }
Chris@0 259
Chris@0 260 // Track the line number for error reporting.
Chris@17 261 $this->lineNumber++;
Chris@0 262
Chris@0 263 // Initialize common values for error logging.
Chris@0 264 $log_vars = [
Chris@0 265 '%uri' => $this->getURI(),
Chris@17 266 '%line' => $this->lineNumber,
Chris@0 267 ];
Chris@0 268
Chris@0 269 // Trim away the linefeed. \\n might appear at the end of the string if
Chris@0 270 // another line continuing the same string follows. We can remove that.
Chris@0 271 $line = trim(strtr($line, ["\\\n" => ""]));
Chris@0 272
Chris@0 273 if (!strncmp('#', $line, 1)) {
Chris@0 274 // Lines starting with '#' are comments.
Chris@0 275
Chris@17 276 if ($this->context == 'COMMENT') {
Chris@0 277 // Already in comment context, add to current comment.
Chris@17 278 $this->currentItem['#'][] = substr($line, 1);
Chris@0 279 }
Chris@17 280 elseif (($this->context == 'MSGSTR') || ($this->context == 'MSGSTR_ARR')) {
Chris@0 281 // We are currently in string context, save current item.
Chris@17 282 $this->setItemFromArray($this->currentItem);
Chris@0 283
Chris@0 284 // Start a new entry for the comment.
Chris@17 285 $this->currentItem = [];
Chris@17 286 $this->currentItem['#'][] = substr($line, 1);
Chris@0 287
Chris@17 288 $this->context = 'COMMENT';
Chris@0 289 return;
Chris@0 290 }
Chris@0 291 else {
Chris@0 292 // A comment following any other context is a syntax error.
Chris@17 293 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: "msgstr" was expected but not found on line %line.', $log_vars);
Chris@0 294 return FALSE;
Chris@0 295 }
Chris@0 296 return;
Chris@0 297 }
Chris@0 298 elseif (!strncmp('msgid_plural', $line, 12)) {
Chris@0 299 // A plural form for the current source string.
Chris@0 300
Chris@17 301 if ($this->context != 'MSGID') {
Chris@0 302 // A plural form can only be added to an msgid directly.
Chris@17 303 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: "msgid_plural" was expected but not found on line %line.', $log_vars);
Chris@0 304 return FALSE;
Chris@0 305 }
Chris@0 306
Chris@0 307 // Remove 'msgid_plural' and trim away whitespace.
Chris@0 308 $line = trim(substr($line, 12));
Chris@0 309
Chris@0 310 // Only the plural source string is left, parse it.
Chris@0 311 $quoted = $this->parseQuoted($line);
Chris@0 312 if ($quoted === FALSE) {
Chris@0 313 // The plural form must be wrapped in quotes.
Chris@17 314 $this->errors[] = new FormattableMarkup('The translation stream %uri contains a syntax error on line %line.', $log_vars);
Chris@0 315 return FALSE;
Chris@0 316 }
Chris@0 317
Chris@0 318 // Append the plural source to the current entry.
Chris@17 319 if (is_string($this->currentItem['msgid'])) {
Chris@0 320 // The first value was stored as string. Now we know the context is
Chris@0 321 // plural, it is converted to array.
Chris@17 322 $this->currentItem['msgid'] = [$this->currentItem['msgid']];
Chris@0 323 }
Chris@17 324 $this->currentItem['msgid'][] = $quoted;
Chris@0 325
Chris@17 326 $this->context = 'MSGID_PLURAL';
Chris@0 327 return;
Chris@0 328 }
Chris@0 329 elseif (!strncmp('msgid', $line, 5)) {
Chris@0 330 // Starting a new message.
Chris@0 331
Chris@17 332 if (($this->context == 'MSGSTR') || ($this->context == 'MSGSTR_ARR')) {
Chris@0 333 // We are currently in string context, save current item.
Chris@17 334 $this->setItemFromArray($this->currentItem);
Chris@0 335
Chris@0 336 // Start a new context for the msgid.
Chris@17 337 $this->currentItem = [];
Chris@0 338 }
Chris@17 339 elseif ($this->context == 'MSGID') {
Chris@0 340 // We are currently already in the context, meaning we passed an id with no data.
Chris@17 341 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: "msgid" is unexpected on line %line.', $log_vars);
Chris@0 342 return FALSE;
Chris@0 343 }
Chris@0 344
Chris@0 345 // Remove 'msgid' and trim away whitespace.
Chris@0 346 $line = trim(substr($line, 5));
Chris@0 347
Chris@0 348 // Only the message id string is left, parse it.
Chris@0 349 $quoted = $this->parseQuoted($line);
Chris@0 350 if ($quoted === FALSE) {
Chris@0 351 // The message id must be wrapped in quotes.
Chris@17 352 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: invalid format for "msgid" on line %line.', $log_vars, $log_vars);
Chris@0 353 return FALSE;
Chris@0 354 }
Chris@0 355
Chris@17 356 $this->currentItem['msgid'] = $quoted;
Chris@17 357 $this->context = 'MSGID';
Chris@0 358 return;
Chris@0 359 }
Chris@0 360 elseif (!strncmp('msgctxt', $line, 7)) {
Chris@0 361 // Starting a new context.
Chris@0 362
Chris@17 363 if (($this->context == 'MSGSTR') || ($this->context == 'MSGSTR_ARR')) {
Chris@0 364 // We are currently in string context, save current item.
Chris@17 365 $this->setItemFromArray($this->currentItem);
Chris@17 366 $this->currentItem = [];
Chris@0 367 }
Chris@17 368 elseif (!empty($this->currentItem['msgctxt'])) {
Chris@0 369 // A context cannot apply to another context.
Chris@17 370 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: "msgctxt" is unexpected on line %line.', $log_vars);
Chris@0 371 return FALSE;
Chris@0 372 }
Chris@0 373
Chris@0 374 // Remove 'msgctxt' and trim away whitespaces.
Chris@0 375 $line = trim(substr($line, 7));
Chris@0 376
Chris@0 377 // Only the msgctxt string is left, parse it.
Chris@0 378 $quoted = $this->parseQuoted($line);
Chris@0 379 if ($quoted === FALSE) {
Chris@0 380 // The context string must be quoted.
Chris@17 381 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: invalid format for "msgctxt" on line %line.', $log_vars);
Chris@0 382 return FALSE;
Chris@0 383 }
Chris@0 384
Chris@17 385 $this->currentItem['msgctxt'] = $quoted;
Chris@0 386
Chris@17 387 $this->context = 'MSGCTXT';
Chris@0 388 return;
Chris@0 389 }
Chris@0 390 elseif (!strncmp('msgstr[', $line, 7)) {
Chris@0 391 // A message string for a specific plurality.
Chris@0 392
Chris@17 393 if (($this->context != 'MSGID') &&
Chris@17 394 ($this->context != 'MSGCTXT') &&
Chris@17 395 ($this->context != 'MSGID_PLURAL') &&
Chris@17 396 ($this->context != 'MSGSTR_ARR')) {
Chris@17 397 // Plural message strings must come after msgid, msgctxt,
Chris@0 398 // msgid_plural, or other msgstr[] entries.
Chris@17 399 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: "msgstr[]" is unexpected on line %line.', $log_vars);
Chris@0 400 return FALSE;
Chris@0 401 }
Chris@0 402
Chris@0 403 // Ensure the plurality is terminated.
Chris@0 404 if (strpos($line, ']') === FALSE) {
Chris@17 405 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: invalid format for "msgstr[]" on line %line.', $log_vars);
Chris@0 406 return FALSE;
Chris@0 407 }
Chris@0 408
Chris@0 409 // Extract the plurality.
Chris@0 410 $frombracket = strstr($line, '[');
Chris@17 411 $this->currentPluralIndex = substr($frombracket, 1, strpos($frombracket, ']') - 1);
Chris@0 412
Chris@0 413 // Skip to the next whitespace and trim away any further whitespace,
Chris@0 414 // bringing $line to the message text only.
Chris@0 415 $line = trim(strstr($line, " "));
Chris@0 416
Chris@0 417 $quoted = $this->parseQuoted($line);
Chris@0 418 if ($quoted === FALSE) {
Chris@0 419 // The string must be quoted.
Chris@17 420 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: invalid format for "msgstr[]" on line %line.', $log_vars);
Chris@0 421 return FALSE;
Chris@0 422 }
Chris@17 423 if (!isset($this->currentItem['msgstr']) || !is_array($this->currentItem['msgstr'])) {
Chris@17 424 $this->currentItem['msgstr'] = [];
Chris@0 425 }
Chris@0 426
Chris@17 427 $this->currentItem['msgstr'][$this->currentPluralIndex] = $quoted;
Chris@0 428
Chris@17 429 $this->context = 'MSGSTR_ARR';
Chris@0 430 return;
Chris@0 431 }
Chris@0 432 elseif (!strncmp("msgstr", $line, 6)) {
Chris@0 433 // A string pair for an msgid (with optional context).
Chris@0 434
Chris@17 435 if (($this->context != 'MSGID') && ($this->context != 'MSGCTXT')) {
Chris@0 436 // Strings are only valid within an id or context scope.
Chris@17 437 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: "msgstr" is unexpected on line %line.', $log_vars);
Chris@0 438 return FALSE;
Chris@0 439 }
Chris@0 440
Chris@0 441 // Remove 'msgstr' and trim away away whitespaces.
Chris@0 442 $line = trim(substr($line, 6));
Chris@0 443
Chris@0 444 // Only the msgstr string is left, parse it.
Chris@0 445 $quoted = $this->parseQuoted($line);
Chris@0 446 if ($quoted === FALSE) {
Chris@0 447 // The string must be quoted.
Chris@17 448 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: invalid format for "msgstr" on line %line.', $log_vars);
Chris@0 449 return FALSE;
Chris@0 450 }
Chris@0 451
Chris@17 452 $this->currentItem['msgstr'] = $quoted;
Chris@0 453
Chris@17 454 $this->context = 'MSGSTR';
Chris@0 455 return;
Chris@0 456 }
Chris@0 457 elseif ($line != '') {
Chris@0 458 // Anything that is not a token may be a continuation of a previous token.
Chris@0 459
Chris@0 460 $quoted = $this->parseQuoted($line);
Chris@0 461 if ($quoted === FALSE) {
Chris@0 462 // This string must be quoted.
Chris@17 463 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: string continuation expected on line %line.', $log_vars);
Chris@0 464 return FALSE;
Chris@0 465 }
Chris@0 466
Chris@0 467 // Append the string to the current item.
Chris@17 468 if (($this->context == 'MSGID') || ($this->context == 'MSGID_PLURAL')) {
Chris@17 469 if (is_array($this->currentItem['msgid'])) {
Chris@0 470 // Add string to last array element for plural sources.
Chris@17 471 $last_index = count($this->currentItem['msgid']) - 1;
Chris@17 472 $this->currentItem['msgid'][$last_index] .= $quoted;
Chris@0 473 }
Chris@0 474 else {
Chris@0 475 // Singular source, just append the string.
Chris@17 476 $this->currentItem['msgid'] .= $quoted;
Chris@0 477 }
Chris@0 478 }
Chris@17 479 elseif ($this->context == 'MSGCTXT') {
Chris@0 480 // Multiline context name.
Chris@17 481 $this->currentItem['msgctxt'] .= $quoted;
Chris@0 482 }
Chris@17 483 elseif ($this->context == 'MSGSTR') {
Chris@0 484 // Multiline translation string.
Chris@17 485 $this->currentItem['msgstr'] .= $quoted;
Chris@0 486 }
Chris@17 487 elseif ($this->context == 'MSGSTR_ARR') {
Chris@0 488 // Multiline plural translation string.
Chris@17 489 $this->currentItem['msgstr'][$this->currentPluralIndex] .= $quoted;
Chris@0 490 }
Chris@0 491 else {
Chris@0 492 // No valid context to append to.
Chris@17 493 $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: unexpected string on line %line.', $log_vars);
Chris@0 494 return FALSE;
Chris@0 495 }
Chris@0 496 return;
Chris@0 497 }
Chris@0 498 }
Chris@0 499
Chris@0 500 // Empty line read or EOF of PO stream, close out the last entry.
Chris@17 501 if (($this->context == 'MSGSTR') || ($this->context == 'MSGSTR_ARR')) {
Chris@17 502 $this->setItemFromArray($this->currentItem);
Chris@17 503 $this->currentItem = [];
Chris@0 504 }
Chris@17 505 elseif ($this->context != 'COMMENT') {
Chris@17 506 $this->errors[] = new FormattableMarkup('The translation stream %uri ended unexpectedly at line %line.', $log_vars);
Chris@0 507 return FALSE;
Chris@0 508 }
Chris@14 509
Chris@14 510 return;
Chris@0 511 }
Chris@0 512
Chris@0 513 /**
Chris@0 514 * Store the parsed values as a PoItem object.
Chris@0 515 */
Chris@0 516 public function setItemFromArray($value) {
Chris@0 517 $plural = FALSE;
Chris@0 518
Chris@0 519 $comments = '';
Chris@0 520 if (isset($value['#'])) {
Chris@0 521 $comments = $this->shortenComments($value['#']);
Chris@0 522 }
Chris@0 523
Chris@0 524 if (is_array($value['msgstr'])) {
Chris@0 525 // Sort plural variants by their form index.
Chris@0 526 ksort($value['msgstr']);
Chris@0 527 $plural = TRUE;
Chris@0 528 }
Chris@0 529
Chris@0 530 $item = new PoItem();
Chris@0 531 $item->setContext(isset($value['msgctxt']) ? $value['msgctxt'] : '');
Chris@0 532 $item->setSource($value['msgid']);
Chris@0 533 $item->setTranslation($value['msgstr']);
Chris@0 534 $item->setPlural($plural);
Chris@0 535 $item->setComment($comments);
Chris@17 536 $item->setLangcode($this->langcode);
Chris@0 537
Chris@17 538 $this->lastItem = $item;
Chris@0 539
Chris@17 540 $this->context = 'COMMENT';
Chris@0 541 }
Chris@0 542
Chris@0 543 /**
Chris@0 544 * Parses a string in quotes.
Chris@0 545 *
Chris@0 546 * @param $string
Chris@0 547 * A string specified with enclosing quotes.
Chris@0 548 *
Chris@0 549 * @return
Chris@0 550 * The string parsed from inside the quotes.
Chris@0 551 */
Chris@0 552 public function parseQuoted($string) {
Chris@0 553 if (substr($string, 0, 1) != substr($string, -1, 1)) {
Chris@0 554 // Start and end quotes must be the same.
Chris@0 555 return FALSE;
Chris@0 556 }
Chris@0 557 $quote = substr($string, 0, 1);
Chris@0 558 $string = substr($string, 1, -1);
Chris@0 559 if ($quote == '"') {
Chris@0 560 // Double quotes: strip slashes.
Chris@0 561 return stripcslashes($string);
Chris@0 562 }
Chris@0 563 elseif ($quote == "'") {
Chris@0 564 // Simple quote: return as-is.
Chris@0 565 return $string;
Chris@0 566 }
Chris@0 567 else {
Chris@0 568 // Unrecognized quote.
Chris@0 569 return FALSE;
Chris@0 570 }
Chris@0 571 }
Chris@0 572
Chris@0 573 /**
Chris@0 574 * Generates a short, one-string version of the passed comment array.
Chris@0 575 *
Chris@0 576 * @param $comment
Chris@0 577 * An array of strings containing a comment.
Chris@0 578 *
Chris@0 579 * @return
Chris@0 580 * Short one-string version of the comment.
Chris@0 581 */
Chris@0 582 private function shortenComments($comment) {
Chris@0 583 $comm = '';
Chris@0 584 while (count($comment)) {
Chris@0 585 $test = $comm . substr(array_shift($comment), 1) . ', ';
Chris@0 586 if (strlen($comm) < 130) {
Chris@0 587 $comm = $test;
Chris@0 588 }
Chris@0 589 else {
Chris@0 590 break;
Chris@0 591 }
Chris@0 592 }
Chris@0 593 return trim(substr($comm, 0, -2));
Chris@0 594 }
Chris@0 595
Chris@0 596 }