diff --git a/potx.inc b/potx.inc index a083f2a..1405150 100644 --- a/potx.inc +++ b/potx.inc @@ -212,6 +212,11 @@ function _potx_process_file($file_path, $strip_prefix = 0, $save_callback = '_po _potx_parse_twig_file($code, $file_name, $save_callback); } + $constraint_extract = FALSE; + if (substr($name_parts['filename'], -10) == 'Constraint' && $api_version > POTX_API_7) { + $constraint_extract = TRUE; + } + // Extract raw PHP language tokens. $raw_tokens = token_get_all($code); unset($code); @@ -226,11 +231,14 @@ function _potx_process_file($file_path, $strip_prefix = 0, $save_callback = '_po if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) { if (is_array($token)) { $token[] = $line_number; + + $constraint_match = $constraint_extract && $token[0] == T_VARIABLE && strlen($token[1]) >= 7 && substr_compare($token[1], 'message', -7, 7, true) === 0; + // Fill array for finding token offsets quickly. - if (in_array($token[0], array(T_STRING, T_DOC_COMMENT)) || ($token[0] == T_VARIABLE && $token[1] == '$t')) { + if (in_array($token[0], array(T_STRING, T_DOC_COMMENT)) || ($token[0] == T_VARIABLE && $token[1] == '$t') || $constraint_match) { // Give doc comments a specific key because their content varies. - $key = ($token[0] == T_DOC_COMMENT) ? 'T_DOC_COMMENT' : $token[1]; + $key = ($token[0] == T_DOC_COMMENT) ? 'T_DOC_COMMENT' : ($constraint_match ? 'T_POTX_CONSTRAINT' : $token[1]); if (!isset($_potx_lookup[$key])) { $_potx_lookup[$key] = array(); } @@ -299,6 +307,10 @@ function _potx_process_file($file_path, $strip_prefix = 0, $save_callback = '_po _potx_find_translation_annotations($file_name, $save_callback); } + if ($constraint_extract) { + _potx_find_constraint_messages($file_name, $save_callback); + } + // Special handling of some Drupal core files. if ($api_version < POTX_API_8 && (($basename == 'locale.inc' && $api_version < POTX_API_7) || $basename == 'iso.inc')) { _potx_find_language_names($file_name, $save_callback, $api_version); @@ -1752,6 +1764,50 @@ function _potx_find_routing_yml_file_strings($code, $file_name, $save_callback, } } + +/** + * Detect validation constraint messages. Drupal 8+ + * + * This sequences is searched for: + * T_POTX_CONSTRAINT = T_CONSTANT_ENCAPSED_STRING + * + * note: T_POTX_CONSTRAINT is marked for T_VARIABLE tokens inside .php files + * with the "Constraint" suffix, where the token is "$message", or ends + * with "Message" + * + * @param $file_name + * Name of file parsed. + * @param $save_callback + * Callback function used to save strings. + */ +function _potx_find_constraint_messages($file_name, $save_callback) { + global $_potx_tokens, $_potx_lookup; + + foreach ($_potx_lookup['T_POTX_CONSTRAINT'] as $key => $ti) { + + if ($_potx_tokens[$ti + 1] == '=' && $_potx_tokens[$ti + 2][0] == T_CONSTANT_ENCAPSED_STRING) { + $str = $_potx_tokens[$ti + 2][1]; + + // plural format + if (strpos($str, '|') !== FALSE) { + $quo = substr($str, 0, 1); + $break = strpos($str, '|'); + $singular = substr($str, 0, $break) . $quo; + $plural = $quo . substr($str, $break + 1); + $save_callback( + _potx_format_quoted_string($singular) ."\0". _potx_format_quoted_string($plural), + POTX_CONTEXT_NONE, + $file_name, + $_potx_tokens[$ti][2] + ); + } + else { + $save_callback(_potx_format_quoted_string($str), POTX_CONTEXT_NONE, $file_name, $_potx_tokens[$ti][2]); + } + } + } +} + /** * Collect a list of file names relevant for extraction, * starting from the given path. diff --git a/tests/TestConstraint.php b/tests/TestConstraint.php new file mode 100644 index 0000000..e6035e1 --- /dev/null +++ b/tests/TestConstraint.php @@ -0,0 +1,8 @@ +assertNoMsgID('mapping'); $this->assertNoMsgID('sequence'); } + + /** + * Test parsing Drupal 8 validation constraint messages. + */ + public function testDrupal8ConstraintMessages() { + $filename = drupal_get_path('module', 'potx') .'/tests/TestConstraint.php'; + $this->parseFile($filename, POTX_API_8); + + $this->assertMsgID('Test message'); + $this->assertMsgID('Test message 2'); + $this->assertPluralID('1 test message', '@count test message'); + $this->assertNoMsgID('Not a message for translation'); + } /** * Test parsing of Drupal 6 info file. Drupal 5 and 7 have no other rules.