diff --git a/core/lib/Drupal/Core/Gettext/POHeader.php b/core/lib/Drupal/Core/Gettext/POHeader.php new file mode 100644 index 0000000..286d577 --- /dev/null +++ b/core/lib/Drupal/Core/Gettext/POHeader.php @@ -0,0 +1,391 @@ +1);\n" + + * @author clemens + */ +class POHeader { + + private $_langcode; + private $_projectIdVersion; + private $_potCreationDate; + private $_poRevisionDate; + private $_languageTeam; + private $_mimeVersion; + private $_contentType; + private $_contentTransferEncoding; + private $_pluralForms; + private $_authors; + private $_po_date; + + /** + * Creates a POHeader with default values set. + * + * @param type $langcode + */ + public function __construct($langcode = NULL) { + $this->_langcode = $langcode; + $this->setDefaults(); + } + + static public function mapping() { + return array( + 'Project-Id-Version' => '_projectIdVersion', + // * Report-Msgid-Bugs-To + 'POT-Creation-Date' => '_potCreationDate', + 'PO-Revision-Date' => '_poRevisionDate', + // * Last-Translator + 'Language-Team' => '_languageTeam', + 'MIME-Version' => '_mimeVersion', + // * Language + 'Content-Type' => '_contentType', + 'Content-Transfer-Encoding' => '_contentTransferEncoding', + 'Plural-Forms' => '_pluralForms', + ); + } + + function getPlural() { + return $this->_pluralForms; + } + + /** + * Compile the PO header. + */ + private function compileHeader() { + $output = ''; + + // Add language description and author as comment. + $languages = language_list(); + $language_name = isset($languages[$this->_langcode]) ? $languages[$this->_langcode]->name : ''; + $output .= '# ' . $language_name . ' translation of ' . variable_get('site_name', 'Drupal') . "\n"; + if (!empty($this->_authors)) { + $output .= '# Generated by ' . implode("\n# ", $this->_authors) . "\n"; + } + $output .= "#\n"; + + // Add the actual header information. + $output .= "msgid \"\"\n"; + $output .= "msgstr \"\"\n"; + $output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n"; + $output .= "\"POT-Creation-Date: " . $this->_po_date . "\\n\"\n"; + $output .= "\"PO-Revision-Date: " . $this->_po_date . "\\n\"\n"; + $output .= "\"Last-Translator: NAME \\n\"\n"; + $output .= "\"Language-Team: LANGUAGE \\n\"\n"; + $output .= "\"MIME-Version: 1.0\\n\"\n"; + $output .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n"; + $output .= "\"Content-Transfer-Encoding: 8bit\\n\"\n"; + $output .= "\"Plural-Forms: " . $this->_pluralForms . "\\n\"\n"; + $output .= "\n"; + + return $output; + } + + /** + * Stores a given PO Header string + * + * TODO: the header string is cleaned by the parser :( + * we need to accept unclean version too + * + * @param type $header + */ + public function setFromString($header) { + $values = $this->_locale_import_parse_header($header); + + $this->setDefaults($values); + } + + /** + * TODO: compare with Symfony::setDefaults() + * + * @param type $values + */ + public function setDefaults($values = array()) { + $defaults = array( + 'POT-Creation-Date' => date("Y-m-d H:iO"), + 'Plural-Forms' => 'nplurals=2; plural=(n > 1);', + ); + foreach ($defaults as $key => $value) { + if (empty($values[$key])) { + $values[$key] = $value; + } + } + $mapping = self::mapping(); + foreach ($mapping as $key => $var) { + if (isset($values[$key])) { + $this->{$var} = $values[$key]; + } + } + } + + public function __toString() { + $result = $this->compileHeader() . "\n"; + return $result; + } + + /** + * Parses a Plural-Forms entry from a Gettext Portable Object file header. + * + * @param $pluralforms + * A string containing the Plural-Forms entry. + * @param $filepath + * A string containing the filepath. + * + * @return + * An array containing the number of plurals and a + * formula in PHP for computing the plural form. + */ + function _locale_import_parse_plural_forms($pluralforms, $filepath) { + // First, delete all whitespace + $pluralforms = strtr($pluralforms, array(" " => "", "\t" => "")); + + // Select the parts that define nplurals and plural + $nplurals = strstr($pluralforms, "nplurals="); + if (strpos($nplurals, ";")) { + $nplurals = substr($nplurals, 9, strpos($nplurals, ";") - 9); + } + else { + return FALSE; + } + $plural = strstr($pluralforms, "plural="); + if (strpos($plural, ";")) { + $plural = substr($plural, 7, strpos($plural, ";") - 7); + } + else { + return FALSE; + } + + // Get PHP version of the plural formula + $plural = $this->_locale_import_parse_arithmetic($plural); + + if ($plural !== FALSE) { + return array($nplurals, $plural); + } + else { + drupal_set_message(t('The translation file %filepath contains an error: the plural formula could not be parsed.', array('%filepath' => $filepath)), 'error'); + return FALSE; + } + } + + /** + * Parses a Gettext Portable Object file header. + * + * @param $header + * A string containing the complete header. + * + * @return + * An associative array of key-value pairs. + */ + function _locale_import_parse_header($header) { + $header_parsed = array(); + $lines = array_map('trim', explode("\n", $header)); + foreach ($lines as $line) { + if ($line) { + list($tag, $contents) = explode(":", $line, 2); + $header_parsed[trim($tag)] = trim($contents); + } + } + return $header_parsed; + } + + /** + * Parses and sanitizes an arithmetic formula into a PHP expression. + * + * While parsing, we ensure, that the operators have the right + * precedence and associativity. + * + * @param $string + * A string containing the arithmetic formula. + * + * @return + * The PHP version of the formula. + */ + function _locale_import_parse_arithmetic($string) { + // Operator precedence table + $precedence = array("(" => -1, ")" => -1, "?" => 1, ":" => 1, "||" => 3, "&&" => 4, "==" => 5, "!=" => 5, "<" => 6, ">" => 6, "<=" => 6, ">=" => 6, "+" => 7, "-" => 7, "*" => 8, "/" => 8, "%" => 8); + // Right associativity + $right_associativity = array("?" => 1, ":" => 1); + + $tokens = $this->_locale_import_tokenize_formula($string); + + // Parse by converting into infix notation then back into postfix + // Operator stack - holds math operators and symbols + $operator_stack = array(); + // Element Stack - holds data to be operated on + $element_stack = array(); + + foreach ($tokens as $token) { + $current_token = $token; + + // Numbers and the $n variable are simply pushed into $element_stack + if (is_numeric($token)) { + $element_stack[] = $current_token; + } + elseif ($current_token == "n") { + $element_stack[] = '$n'; + } + elseif ($current_token == "(") { + $operator_stack[] = $current_token; + } + elseif ($current_token == ")") { + $topop = array_pop($operator_stack); + while (isset($topop) && ($topop != "(")) { + $element_stack[] = $topop; + $topop = array_pop($operator_stack); + } + } + elseif (!empty($precedence[$current_token])) { + // If it's an operator, then pop from $operator_stack into $element_stack until the + // precedence in $operator_stack is less than current, then push into $operator_stack + $topop = array_pop($operator_stack); + while (isset($topop) && ($precedence[$topop] >= $precedence[$current_token]) && !(($precedence[$topop] == $precedence[$current_token]) && !empty($right_associativity[$topop]) && !empty($right_associativity[$current_token]))) { + $element_stack[] = $topop; + $topop = array_pop($operator_stack); + } + if ($topop) { + $operator_stack[] = $topop; // Return element to top + } + $operator_stack[] = $current_token; // Parentheses are not needed + } + else { + return FALSE; + } + } + + // Flush operator stack + $topop = array_pop($operator_stack); + while ($topop != NULL) { + $element_stack[] = $topop; + $topop = array_pop($operator_stack); + } + + // Now extract formula from stack + $previous_size = count($element_stack) + 1; + while (count($element_stack) < $previous_size) { + $previous_size = count($element_stack); + for ($i = 2; $i < count($element_stack); $i++) { + $op = $element_stack[$i]; + if (!empty($precedence[$op])) { + $f = ""; + if ($op == ":") { + $f = $element_stack[$i - 2] . "):" . $element_stack[$i - 1] . ")"; + } + elseif ($op == "?") { + $f = "(" . $element_stack[$i - 2] . "?(" . $element_stack[$i - 1]; + } + else { + $f = "(" . $element_stack[$i - 2] . $op . $element_stack[$i - 1] . ")"; + } + array_splice($element_stack, $i - 2, 3, $f); + break; + } + } + } + + // If only one element is left, the number of operators is appropriate + if (count($element_stack) == 1) { + return $element_stack[0]; + } + else { + return FALSE; + } + } + + /** + * Provides backward-compatible formula parsing for token_get_all(). + * + * @param $string + * A string containing the arithmetic formula. + * + * @return + * The PHP version of the formula. + */ + function _locale_import_tokenize_formula($formula) { + $formula = str_replace(" ", "", $formula); + $tokens = array(); + for ($i = 0; $i < strlen($formula); $i++) { + if (is_numeric($formula[$i])) { + $num = $formula[$i]; + $j = $i + 1; + while ($j < strlen($formula) && is_numeric($formula[$j])) { + $num .= $formula[$j]; + $j++; + } + $i = $j - 1; + $tokens[] = $num; + } + elseif ($pos = strpos(" =<>!&|", $formula[$i])) { // We won't have a space + $next = $formula[$i + 1]; + switch ($pos) { + case 1: + case 2: + case 3: + case 4: + if ($next == '=') { + $tokens[] = $formula[$i] . '='; + $i++; + } + else { + $tokens[] = $formula[$i]; + } + break; + case 5: + if ($next == '&') { + $tokens[] = '&&'; + $i++; + } + else { + $tokens[] = $formula[$i]; + } + break; + case 6: + if ($next == '|') { + $tokens[] = '||'; + $i++; + } + else { + $tokens[] = $formula[$i]; + } + break; + } + } + else { + $tokens[] = $formula[$i]; + } + } + return $tokens; + } + +} + +?> diff --git a/core/lib/Drupal/Core/Gettext/POItem.php b/core/lib/Drupal/Core/Gettext/POItem.php new file mode 100644 index 0000000..da30e0b --- /dev/null +++ b/core/lib/Drupal/Core/Gettext/POItem.php @@ -0,0 +1,132 @@ + 'context', + 'msgid' => 'source', + 'msgstr' => 'translation', + '#' => 'comment', + ); + } + + public function fromArray(array $values = array()) { + foreach ($values as $key => $value) { + $this->{$key} = $value; + } + } + + public function __toString() { + return $this->compileTranslation(); + } + + /** + * Compile PO translations strings from a translation object. + * + * Translation object consists of: + * source string (singular) or array of strings (plural) + * translation string (singular) or array of strings (plural) + * plural TRUE: source and translation are plurals + * context source context string + */ + private function compileTranslation() { + $output = ''; + + // Format string context. + if (!empty($this->context)) { + $output .= 'msgctxt ' . $this->formatString($this->context); + } + + // Format translation + if ($this->plural) { + $output .= $this->formatPlural(); + } + else { + $output .= $this->formatSingular(); + } + + // Add one empty line to separate the translations. + $output .= "\n"; + + return $output; + } + + /** + * Formats a plural translation. + */ + private function formatPlural() { + $output = ''; + + // Format source strings. + $output .= 'msgid ' . $this->formatString($this->source[0]); + $output .= 'msgid_plural ' . $this->formatString($this->source[1]); + + foreach ($this->translation as $i => $trans) { + if (isset($this->translation[$i])) { + $output .= 'msgstr[' . $i . '] ' . $this->formatString($trans); + } + else { + $output .= 'msgstr[' . $i . '] ""' . "\n"; + } + } + + return $output; + } + + /** + * Formats a singular translation. + */ + private function formatSingular() { + $output = ''; + $output .= 'msgid ' . $this->formatString($this->source); + $output .= 'msgstr ' . $this->formatString($this->translation); + return $output; + } + + /** + * Formats a string for output on multiple lines. + */ + private function formatString($string) { + // Escape characters for processing. + $string = addcslashes($string, "\0..\37\\\""); + + // Always include a line break after the explicit \n line breaks from + // the source string. Otherwise wrap at 70 chars to accommodate the extra + // format overhead too. + $parts = explode("\n", wordwrap(str_replace('\n', "\\n\n", $string), 70, " \n")); + + // Multiline string should be exported starting with a "" and newline to + // have all lines aligned on the same column. + if (count($parts) > 1) { + return "\"\"\n\"" . implode("\"\n\"", $parts) . "\"\n"; + } + // Single line strings are output on the same line. + else { + return "\"$parts[0]\"\n"; + } + } + +} + +?> diff --git a/core/lib/Drupal/Core/Gettext/PoDatabaseReader.php b/core/lib/Drupal/Core/Gettext/PoDatabaseReader.php index 7e33e37..9796e76 100644 --- a/core/lib/Drupal/Core/Gettext/PoDatabaseReader.php +++ b/core/lib/Drupal/Core/Gettext/PoDatabaseReader.php @@ -3,7 +3,7 @@ namespace Drupal\Core\Gettext; use Drupal\Core\Gettext\BatchStateInterface; -use Drupal\Core\Gettext\PoHeader; +use Drupal\Core\Gettext\POHeader; class PoDatabaseReader implements BatchStateInterface, PoReaderInterface { /* @@ -78,10 +78,10 @@ class PoDatabaseReader implements BatchStateInterface, PoReaderInterface { } function getHeader() { - return new PoHeader($this->getLangcode()); + return new POHeader($this->getLangcode()); } - public function setHeader(PoHeader $header) { + public function setHeader(POHeader $header) { // empty on purpose } @@ -173,7 +173,7 @@ class PoDatabaseReader implements BatchStateInterface, PoReaderInterface { $values = $result->fetchAssoc(); if ($values) { - $poItem = new PoItem(); + $poItem = new POItem(); $poItem->fromArray($values); // Manage state $this->_lid = $values['lid']; @@ -181,4 +181,4 @@ class PoDatabaseReader implements BatchStateInterface, PoReaderInterface { } } -} +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Gettext/PoDatabaseWriter.php b/core/lib/Drupal/Core/Gettext/PoDatabaseWriter.php index 8ec9b1d..6234dab 100644 --- a/core/lib/Drupal/Core/Gettext/PoDatabaseWriter.php +++ b/core/lib/Drupal/Core/Gettext/PoDatabaseWriter.php @@ -3,8 +3,8 @@ namespace Drupal\Core\Gettext; use Drupal\Core\Gettext\POWriter; -use Drupal\Core\Gettext\PoHeader; -use Drupal\Core\Gettext\PoItem; +use Drupal\Core\Gettext\POHeader; +use Drupal\Core\Gettext\POItem; class PoDatabaseWriter implements PoWriterInterface, BatchStateInterface { /* @@ -121,19 +121,19 @@ class PoDatabaseWriter implements PoWriterInterface, BatchStateInterface { return $this->_header; } - function setHeader(PoHeader $header) { + function setHeader(POHeader $header) { $this->_header = $header; $locale_plurals = variable_get('locale_translation_plurals', array()); // Check for options $options = $this->getOptions(); if (empty($options)) { - throw new Exception("Options should be set before assigning a PoHeader"); + throw new Exception("Options should be set before assigning a POHeader"); } $overwrite_options = $options['overwrite_options']; // Check for langcode $lang = $this->_langcode; if (empty($lang)) { - throw new Exception("Langcode should be set before assigning a PoHeader"); + throw new Exception("Langcode should be set before assigning a POHeader"); } if (array_sum($overwrite_options) || empty($locale_plurals[$lang]['plurals'])) { // Get and store the plural formula if available. @@ -153,7 +153,7 @@ class PoDatabaseWriter implements PoWriterInterface, BatchStateInterface { drupal_set_message(print_r($locale_plurals, TRUE)); } - function writeItem(PoItem $item) { + function writeItem(POItem $item) { if ($item->plural) { $item->source = join(LOCALE_PLURAL_DELIMITER, $item->source); $item->translation = join(LOCALE_PLURAL_DELIMITER, $item->translation); @@ -287,4 +287,4 @@ class PoDatabaseWriter implements PoWriterInterface, BatchStateInterface { } } -} +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Gettext/PoFileReader.php b/core/lib/Drupal/Core/Gettext/PoFileReader.php index 85f0dd1..b0b3294 100644 --- a/core/lib/Drupal/Core/Gettext/PoFileReader.php +++ b/core/lib/Drupal/Core/Gettext/PoFileReader.php @@ -14,7 +14,7 @@ namespace Drupal\Core\Gettext; use Drupal\Core\Gettext\BatchStateInterface; use Drupal\Core\Gettext\POReader; -use Drupal\Core\Gettext\PoHeader; +use Drupal\Core\Gettext\POHeader; /** * Defines a Gettext reader for PO format. @@ -116,7 +116,7 @@ class PoFileReader implements BatchStateInterface, PoStreamInterface, PoReaderIn public function setState(array $state) { $this->setURI($state['uri']); $this->setLangcode($state['langcode']); - // Make sure to (re)read the PoHeader + // Make sure to (re)read the POHeader $this->open(); // Move to last read position. if (isset($state['seekpos'])) { @@ -160,7 +160,7 @@ class PoFileReader implements BatchStateInterface, PoStreamInterface, PoReaderIn return $this->_header; } - public function setHeader(PoHeader $header) { + public function setHeader(POHeader $header) { // TODO : throw exception? } @@ -174,7 +174,7 @@ class PoFileReader implements BatchStateInterface, PoStreamInterface, PoReaderIn */ private function readHeader() { $translation = $this->readTranslation(); - $header = new PoHeader; + $header = new POHeader; $header->setFromString(trim($translation->translation)); $this->_header = $header; } @@ -476,7 +476,7 @@ class PoFileReader implements BatchStateInterface, PoStreamInterface, PoReaderIn $plural = TRUE; } - $translation = new PoItem; + $translation = new POItem; $translation->context = isset($value['msgctxt']) ? $value['msgctxt'] : ''; $translation->source = $value['msgid']; $translation->translation = $value['msgstr']; diff --git a/core/lib/Drupal/Core/Gettext/PoFileWriter.php b/core/lib/Drupal/Core/Gettext/PoFileWriter.php index 779a24f..2f239b9 100644 --- a/core/lib/Drupal/Core/Gettext/PoFileWriter.php +++ b/core/lib/Drupal/Core/Gettext/PoFileWriter.php @@ -7,7 +7,7 @@ namespace Drupal\Core\Gettext; -use Drupal\Core\Gettext\PoHeader; +use Drupal\Core\Gettext\POHeader; use Drupal\Core\Gettext\BatchStateInterface; /** @@ -32,7 +32,7 @@ class PoFileWriter implements PoStreamInterface, PoWriterInterface, BatchStateIn return $this->_header; } - public function setHeader(PoHeader $header) { + public function setHeader(POHeader $header) { $this->_header = $header; } @@ -87,7 +87,7 @@ class PoFileWriter implements PoStreamInterface, PoWriterInterface, BatchStateIn $this->write($this->_header); } - public function writeItem(PoItem $item) { + public function writeItem(POItem $item) { $this->write($item); } diff --git a/core/lib/Drupal/Core/Gettext/PoHeader.php b/core/lib/Drupal/Core/Gettext/PoHeader.php deleted file mode 100644 index 4ac3c3d..0000000 --- a/core/lib/Drupal/Core/Gettext/PoHeader.php +++ /dev/null @@ -1,391 +0,0 @@ -1);\n" - - * @author clemens - */ -class PoHeader { - - private $_langcode; - private $_projectIdVersion; - private $_potCreationDate; - private $_poRevisionDate; - private $_languageTeam; - private $_mimeVersion; - private $_contentType; - private $_contentTransferEncoding; - private $_pluralForms; - private $_authors; - private $_po_date; - - /** - * Creates a PoHeader with default values set. - * - * @param type $langcode - */ - public function __construct($langcode = NULL) { - $this->_langcode = $langcode; - $this->setDefaults(); - } - - static public function mapping() { - return array( - 'Project-Id-Version' => '_projectIdVersion', - // * Report-Msgid-Bugs-To - 'POT-Creation-Date' => '_potCreationDate', - 'PO-Revision-Date' => '_poRevisionDate', - // * Last-Translator - 'Language-Team' => '_languageTeam', - 'MIME-Version' => '_mimeVersion', - // * Language - 'Content-Type' => '_contentType', - 'Content-Transfer-Encoding' => '_contentTransferEncoding', - 'Plural-Forms' => '_pluralForms', - ); - } - - function getPlural() { - return $this->_pluralForms; - } - - /** - * Compile the PO header. - */ - private function compileHeader() { - $output = ''; - - // Add language description and author as comment. - $languages = language_list(); - $language_name = isset($languages[$this->_langcode]) ? $languages[$this->_langcode]->name : ''; - $output .= '# ' . $language_name . ' translation of ' . variable_get('site_name', 'Drupal') . "\n"; - if (!empty($this->_authors)) { - $output .= '# Generated by ' . implode("\n# ", $this->_authors) . "\n"; - } - $output .= "#\n"; - - // Add the actual header information. - $output .= "msgid \"\"\n"; - $output .= "msgstr \"\"\n"; - $output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n"; - $output .= "\"POT-Creation-Date: " . $this->_po_date . "\\n\"\n"; - $output .= "\"PO-Revision-Date: " . $this->_po_date . "\\n\"\n"; - $output .= "\"Last-Translator: NAME \\n\"\n"; - $output .= "\"Language-Team: LANGUAGE \\n\"\n"; - $output .= "\"MIME-Version: 1.0\\n\"\n"; - $output .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n"; - $output .= "\"Content-Transfer-Encoding: 8bit\\n\"\n"; - $output .= "\"Plural-Forms: " . $this->_pluralForms . "\\n\"\n"; - $output .= "\n"; - - return $output; - } - - /** - * Stores a given PO Header string - * - * TODO: the header string is cleaned by the parser :( - * we need to accept unclean version too - * - * @param type $header - */ - public function setFromString($header) { - $values = $this->_locale_import_parse_header($header); - - $this->setDefaults($values); - } - - /** - * TODO: compare with Symfony::setDefaults() - * - * @param type $values - */ - public function setDefaults($values = array()) { - $defaults = array( - 'POT-Creation-Date' => date("Y-m-d H:iO"), - 'Plural-Forms' => 'nplurals=2; plural=(n > 1);', - ); - foreach ($defaults as $key => $value) { - if (empty($values[$key])) { - $values[$key] = $value; - } - } - $mapping = self::mapping(); - foreach ($mapping as $key => $var) { - if (isset($values[$key])) { - $this->{$var} = $values[$key]; - } - } - } - - public function __toString() { - $result = $this->compileHeader() . "\n"; - return $result; - } - - /** - * Parses a Plural-Forms entry from a Gettext Portable Object file header. - * - * @param $pluralforms - * A string containing the Plural-Forms entry. - * @param $filepath - * A string containing the filepath. - * - * @return - * An array containing the number of plurals and a - * formula in PHP for computing the plural form. - */ - function _locale_import_parse_plural_forms($pluralforms, $filepath) { - // First, delete all whitespace - $pluralforms = strtr($pluralforms, array(" " => "", "\t" => "")); - - // Select the parts that define nplurals and plural - $nplurals = strstr($pluralforms, "nplurals="); - if (strpos($nplurals, ";")) { - $nplurals = substr($nplurals, 9, strpos($nplurals, ";") - 9); - } - else { - return FALSE; - } - $plural = strstr($pluralforms, "plural="); - if (strpos($plural, ";")) { - $plural = substr($plural, 7, strpos($plural, ";") - 7); - } - else { - return FALSE; - } - - // Get PHP version of the plural formula - $plural = $this->_locale_import_parse_arithmetic($plural); - - if ($plural !== FALSE) { - return array($nplurals, $plural); - } - else { - drupal_set_message(t('The translation file %filepath contains an error: the plural formula could not be parsed.', array('%filepath' => $filepath)), 'error'); - return FALSE; - } - } - - /** - * Parses a Gettext Portable Object file header. - * - * @param $header - * A string containing the complete header. - * - * @return - * An associative array of key-value pairs. - */ - function _locale_import_parse_header($header) { - $header_parsed = array(); - $lines = array_map('trim', explode("\n", $header)); - foreach ($lines as $line) { - if ($line) { - list($tag, $contents) = explode(":", $line, 2); - $header_parsed[trim($tag)] = trim($contents); - } - } - return $header_parsed; - } - - /** - * Parses and sanitizes an arithmetic formula into a PHP expression. - * - * While parsing, we ensure, that the operators have the right - * precedence and associativity. - * - * @param $string - * A string containing the arithmetic formula. - * - * @return - * The PHP version of the formula. - */ - function _locale_import_parse_arithmetic($string) { - // Operator precedence table - $precedence = array("(" => -1, ")" => -1, "?" => 1, ":" => 1, "||" => 3, "&&" => 4, "==" => 5, "!=" => 5, "<" => 6, ">" => 6, "<=" => 6, ">=" => 6, "+" => 7, "-" => 7, "*" => 8, "/" => 8, "%" => 8); - // Right associativity - $right_associativity = array("?" => 1, ":" => 1); - - $tokens = $this->_locale_import_tokenize_formula($string); - - // Parse by converting into infix notation then back into postfix - // Operator stack - holds math operators and symbols - $operator_stack = array(); - // Element Stack - holds data to be operated on - $element_stack = array(); - - foreach ($tokens as $token) { - $current_token = $token; - - // Numbers and the $n variable are simply pushed into $element_stack - if (is_numeric($token)) { - $element_stack[] = $current_token; - } - elseif ($current_token == "n") { - $element_stack[] = '$n'; - } - elseif ($current_token == "(") { - $operator_stack[] = $current_token; - } - elseif ($current_token == ")") { - $topop = array_pop($operator_stack); - while (isset($topop) && ($topop != "(")) { - $element_stack[] = $topop; - $topop = array_pop($operator_stack); - } - } - elseif (!empty($precedence[$current_token])) { - // If it's an operator, then pop from $operator_stack into $element_stack until the - // precedence in $operator_stack is less than current, then push into $operator_stack - $topop = array_pop($operator_stack); - while (isset($topop) && ($precedence[$topop] >= $precedence[$current_token]) && !(($precedence[$topop] == $precedence[$current_token]) && !empty($right_associativity[$topop]) && !empty($right_associativity[$current_token]))) { - $element_stack[] = $topop; - $topop = array_pop($operator_stack); - } - if ($topop) { - $operator_stack[] = $topop; // Return element to top - } - $operator_stack[] = $current_token; // Parentheses are not needed - } - else { - return FALSE; - } - } - - // Flush operator stack - $topop = array_pop($operator_stack); - while ($topop != NULL) { - $element_stack[] = $topop; - $topop = array_pop($operator_stack); - } - - // Now extract formula from stack - $previous_size = count($element_stack) + 1; - while (count($element_stack) < $previous_size) { - $previous_size = count($element_stack); - for ($i = 2; $i < count($element_stack); $i++) { - $op = $element_stack[$i]; - if (!empty($precedence[$op])) { - $f = ""; - if ($op == ":") { - $f = $element_stack[$i - 2] . "):" . $element_stack[$i - 1] . ")"; - } - elseif ($op == "?") { - $f = "(" . $element_stack[$i - 2] . "?(" . $element_stack[$i - 1]; - } - else { - $f = "(" . $element_stack[$i - 2] . $op . $element_stack[$i - 1] . ")"; - } - array_splice($element_stack, $i - 2, 3, $f); - break; - } - } - } - - // If only one element is left, the number of operators is appropriate - if (count($element_stack) == 1) { - return $element_stack[0]; - } - else { - return FALSE; - } - } - - /** - * Provides backward-compatible formula parsing for token_get_all(). - * - * @param $string - * A string containing the arithmetic formula. - * - * @return - * The PHP version of the formula. - */ - function _locale_import_tokenize_formula($formula) { - $formula = str_replace(" ", "", $formula); - $tokens = array(); - for ($i = 0; $i < strlen($formula); $i++) { - if (is_numeric($formula[$i])) { - $num = $formula[$i]; - $j = $i + 1; - while ($j < strlen($formula) && is_numeric($formula[$j])) { - $num .= $formula[$j]; - $j++; - } - $i = $j - 1; - $tokens[] = $num; - } - elseif ($pos = strpos(" =<>!&|", $formula[$i])) { // We won't have a space - $next = $formula[$i + 1]; - switch ($pos) { - case 1: - case 2: - case 3: - case 4: - if ($next == '=') { - $tokens[] = $formula[$i] . '='; - $i++; - } - else { - $tokens[] = $formula[$i]; - } - break; - case 5: - if ($next == '&') { - $tokens[] = '&&'; - $i++; - } - else { - $tokens[] = $formula[$i]; - } - break; - case 6: - if ($next == '|') { - $tokens[] = '||'; - $i++; - } - else { - $tokens[] = $formula[$i]; - } - break; - } - } - else { - $tokens[] = $formula[$i]; - } - } - return $tokens; - } - -} - -?> diff --git a/core/lib/Drupal/Core/Gettext/PoInterface.php b/core/lib/Drupal/Core/Gettext/PoInterface.php index 98ad40d..850f79f 100644 --- a/core/lib/Drupal/Core/Gettext/PoInterface.php +++ b/core/lib/Drupal/Core/Gettext/PoInterface.php @@ -13,7 +13,7 @@ namespace Drupal\Core\Gettext; use Drupal\Core\Gettext\Reader; -use Drupal\Core\Gettext\PoHeader; +use Drupal\Core\Gettext\POHeader; /** * Defines PO / gettext related must haves. @@ -26,6 +26,6 @@ interface PoInterface { function getLangcode(); function getHeader(); - function setHeader(PoHeader $header); + function setHeader(POHeader $header); } diff --git a/core/lib/Drupal/Core/Gettext/PoItem.php b/core/lib/Drupal/Core/Gettext/PoItem.php deleted file mode 100644 index 83834f5..0000000 --- a/core/lib/Drupal/Core/Gettext/PoItem.php +++ /dev/null @@ -1,132 +0,0 @@ - 'context', - 'msgid' => 'source', - 'msgstr' => 'translation', - '#' => 'comment', - ); - } - - public function fromArray(array $values = array()) { - foreach ($values as $key => $value) { - $this->{$key} = $value; - } - } - - public function __toString() { - return $this->compileTranslation(); - } - - /** - * Compile PO translations strings from a translation object. - * - * Translation object consists of: - * source string (singular) or array of strings (plural) - * translation string (singular) or array of strings (plural) - * plural TRUE: source and translation are plurals - * context source context string - */ - private function compileTranslation() { - $output = ''; - - // Format string context. - if (!empty($this->context)) { - $output .= 'msgctxt ' . $this->formatString($this->context); - } - - // Format translation - if ($this->plural) { - $output .= $this->formatPlural(); - } - else { - $output .= $this->formatSingular(); - } - - // Add one empty line to separate the translations. - $output .= "\n"; - - return $output; - } - - /** - * Formats a plural translation. - */ - private function formatPlural() { - $output = ''; - - // Format source strings. - $output .= 'msgid ' . $this->formatString($this->source[0]); - $output .= 'msgid_plural ' . $this->formatString($this->source[1]); - - foreach ($this->translation as $i => $trans) { - if (isset($this->translation[$i])) { - $output .= 'msgstr[' . $i . '] ' . $this->formatString($trans); - } - else { - $output .= 'msgstr[' . $i . '] ""' . "\n"; - } - } - - return $output; - } - - /** - * Formats a singular translation. - */ - private function formatSingular() { - $output = ''; - $output .= 'msgid ' . $this->formatString($this->source); - $output .= 'msgstr ' . $this->formatString($this->translation); - return $output; - } - - /** - * Formats a string for output on multiple lines. - */ - private function formatString($string) { - // Escape characters for processing. - $string = addcslashes($string, "\0..\37\\\""); - - // Always include a line break after the explicit \n line breaks from - // the source string. Otherwise wrap at 70 chars to accommodate the extra - // format overhead too. - $parts = explode("\n", wordwrap(str_replace('\n', "\\n\n", $string), 70, " \n")); - - // Multiline string should be exported starting with a "" and newline to - // have all lines aligned on the same column. - if (count($parts) > 1) { - return "\"\"\n\"" . implode("\"\n\"", $parts) . "\"\n"; - } - // Single line strings are output on the same line. - else { - return "\"$parts[0]\"\n"; - } - } - -} - -?> diff --git a/core/lib/Drupal/Core/Gettext/PoMemoryWriter.php b/core/lib/Drupal/Core/Gettext/PoMemoryWriter.php index 75efbe6..4117887 100644 --- a/core/lib/Drupal/Core/Gettext/PoMemoryWriter.php +++ b/core/lib/Drupal/Core/Gettext/PoMemoryWriter.php @@ -7,7 +7,7 @@ namespace Drupal\Core\Gettext; -use Drupal\Core\Gettext\PoHeader; +use Drupal\Core\Gettext\POHeader; use Drupal\Core\Gettext\BatchStateInterface; /** @@ -41,9 +41,9 @@ class PoMemoryWriter implements PoWriterInterface, BatchStateInterface { * TODO: where is this structure documented? * - array[context][source] = translation * - * @param PoItem $item + * @param POItem $item */ - public function writeItem(PoItem $item) { + public function writeItem(POItem $item) { if (is_array($item->source)) { $item->source = implode(LOCALE_PLURAL_DELIMITER, $item->source); $item->translation = implode(LOCALE_PLURAL_DELIMITER, $item->translation); @@ -66,7 +66,7 @@ class PoMemoryWriter implements PoWriterInterface, BatchStateInterface { // TODO: what } - public function setHeader(PoHeader $header) { + public function setHeader(POHeader $header) { // TODO: what } diff --git a/core/lib/Drupal/Core/Gettext/PoWriterInterface.php b/core/lib/Drupal/Core/Gettext/PoWriterInterface.php index 9d2d99f..e5a13c9 100644 --- a/core/lib/Drupal/Core/Gettext/PoWriterInterface.php +++ b/core/lib/Drupal/Core/Gettext/PoWriterInterface.php @@ -8,12 +8,12 @@ namespace Drupal\Core\Gettext; use Drupal\Core\Gettext\PoInterface; -use Drupal\Core\Gettext\PoItem; +use Drupal\Core\Gettext\POItem; /** * Defines a Gettext writer. */ interface PoWriterInterface extends PoInterface { - function writeItem(PoItem $item); + function writeItem(POItem $item); function writeItems(PoReaderInterface $reader, $count = 10); } diff --git a/core/lib/Drupal/Core/Gettext/testGettext.php b/core/lib/Drupal/Core/Gettext/testGettext.php index 708d377..8ca4e17 100644 --- a/core/lib/Drupal/Core/Gettext/testGettext.php +++ b/core/lib/Drupal/Core/Gettext/testGettext.php @@ -20,8 +20,8 @@ drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); * drush @drupal.d8 php-script core/lib/Drupal/Core/Gettext/testGettext.php */ use Drupal\Core\Gettext\BatchStreamManager; -use Drupal\Core\Gettext\PoHeader; -use Drupal\Core\Gettext\PoItem; +use Drupal\Core\Gettext\POHeader; +use Drupal\Core\Gettext\POItem; use Drupal\Core\Gettext\PoDatabaseWriter; use Drupal\Core\Gettext\PoDatabaseReader; use Drupal\Core\Gettext\PoFileReader; @@ -67,7 +67,7 @@ function gettext_struct($langcode = 'nl') { ); $items = array(); foreach ($src as $values) { - $item = new PoItem(); + $item = new POItem(); $item->fromArray($values); $items[] = $item; } @@ -91,7 +91,7 @@ function testWriter($uri = '') { $writer = new PoFileWriter(); $writer->setLangcode($langcode); - $writer->setHeader(new PoHeader($langcode)); + $writer->setHeader(new POHeader($langcode)); $writer->setURI($uri); $writer->open(); foreach ($items as $item) { @@ -170,7 +170,7 @@ function testPoReader() { function testHeader() { logLine(__FUNCTION__, '='); - $h = new PoHeader(); + $h = new POHeader(); echo "----------------\n"; $h->setFromString(''); @@ -677,4 +677,4 @@ function testFormatPlural() { //var_dump($langs); //pumpAround('nl'); //runAll(); -pumpAll(); +//pumpAll();