+ $startPos = 0;
+ $sourceBlob = '';
+ $literalBlob = '';
+
+ // Guard against delimiter nulls in the input
+ $text = str_replace( "\000", '', $text );
+
+ $markupMatches = null;
+ $elementMatches = null;
+ while ( $startPos < strlen( $text ) ) {
+ if ( preg_match( $reg, $text, $markupMatches, PREG_OFFSET_CAPTURE, $startPos ) ) {
+ $elementPos = $markupMatches[0][1];
+ $element = $markupMatches[0][0];
+ } else {
+ $elementPos = strlen( $text );
+ $element = '';
+ }
+
+ // Queue the part before the markup for translation in a batch
+ $sourceBlob .= substr( $text, $startPos, $elementPos - $startPos ) . "\000";
+
+ // Advance to the next position
+ $startPos = $elementPos + strlen( $element );
+
+ // Translate any alt or title attributes inside the matched element
+ if ( $element !== '' && preg_match( '/^(<[^>\s]*)\s([^>]*)(.*)$/', $element,
+ $elementMatches ) )
+ {
+ $attrs = Sanitizer::decodeTagAttributes( $elementMatches[2] );
+ $changed = false;
+ foreach ( array( 'title', 'alt' ) as $attrName ) {
+ if ( !isset( $attrs[$attrName] ) ) {
+ continue;
+ }
+ $attr = $attrs[$attrName];
+ // Don't convert URLs
+ if ( !strpos( $attr, '://' ) ) {
+ $attr = $this->translate( $attr, $toVariant );
+ }
+
+ // Remove HTML tags to avoid disrupting the layout
+ $attr = preg_replace( '/<[^>]+>/', '', $attr );
+ if ( $attr !== $attrs[$attrName] ) {
+ $attrs[$attrName] = $attr;
+ $changed = true;
+ }
+ }
+ if ( $changed ) {
+ $element = $elementMatches[1] . Html::expandAttributes( $attrs ) .
+ $elementMatches[3];
+ }
+ }
+ $literalBlob .= $element . "\000";
+ }
+
+ // Do the main translation batch
+ $translatedBlob = $this->translate( $sourceBlob, $toVariant );