%PDF- %PDF-
| Direktori : /var/www/projetos/stb.ind.br/wp-content/plugins/loco-translate/src/gettext/ |
| Current File : /var/www/projetos/stb.ind.br/wp-content/plugins/loco-translate/src/gettext/Matcher.php |
<?php
/**
* Sync/Merge utility akin to msgmerge
*/
class Loco_gettext_Matcher extends LocoFuzzyMatcher {
/**
* Whether copying translation from source references (normally for a POT we won't)
* @var bool
*/
private $translate;
/**
* Number of translations pulled from source (when source is PO)
* @var int
*/
private $translated;
/**
* Initialize matcher with current valid source strings (ref.pot)
* @param Loco_gettext_Data POT reference
* @param bool whether copying translations from reference data
* @return int
*/
public function loadRefs( Loco_gettext_Data $pot, $translate = false ){
$ntotal = 0;
$this->translate = (bool) $translate;
$this->translated = 0;
/* @var LocoPoMessage $new */
foreach( $pot as $new ){
$ntotal++;
$this->add($new);
}
return $ntotal;
}
/**
* Add further source strings from JSON/JED file
* @param Loco_fs_File json file
* @return int
*/
public function loadJson( Loco_fs_File $file ){
$unique = 0;
$jed = json_decode( $file->getContents(), true );
if( ! is_array($jed) || ! array_key_exists('locale_data',$jed) || ! is_array($jed['locale_data']) ){
throw new Loco_error_Debug( $file->basename().' is not JED formatted');
}
// Without a file reference strings will never be compiled back to the correct JSON.
// JSON files will not contain full line references, but we may know the file at least
if( ! array_key_exists('source',$jed) || ! $jed['source'] ){
throw new Loco_error_Debug( $file->basename().' has no "source" key');
}
$ref = $jed['source'];
// not checking domain key. Should be valid if passed here and should only be one.
foreach( $jed['locale_data'] as $domain => $keys ){
foreach( $keys as $msgid => $arr ){
if( '' === $msgid || ! is_array($arr) || ! isset($arr[0]) ){
continue;
}
$msgctxt = '';
// Unglue "msgctxt\4msgid" unique key
$parts = explode("\4",$msgid,2);
if( array_key_exists(1,$parts) ){
list($msgctxt,$msgid) = $parts;
// TODO handle empty msgid case that uses weird "msgctxt\4(msgctxt)" format?
}
// string may exist in original template, and also in multiple JSONs.
$new = array('source'=>$msgid,'context'=>$msgctxt,'refs'=>$ref );
$old = $this->getArrayRef($new);
if( $old ){
$refs = array_key_exists('refs',$old) ? (string) $old['refs'] : '';
if( '' === $refs ){
$old['refs'] = $ref;
}
else if( 0 === preg_match('/\\b'.preg_quote($ref,'/').'\\b/',$refs) ){
$old['refs'].= ' '.$ref;
}
$new = $old;
}
else {
$unique++;
}
// Add translation from JSON only if not present in merged PO already
if( $this->translate && ( ! array_key_exists('target',$new) || '' === $new['target'] ) ){
$new['target'] = $arr[0];
}
$message = new LocoPoMessage($new);
$this->add($message);
// handle plurals, noting that msgid_plural is not stored in JED structure
if( 1 < count($arr) ){
$index = 0;
$plurals = $old && array_key_exists('plurals',$old) ? $old['plurals'] : array();
while( array_key_exists(++$index,$arr) ){
if( array_key_exists($index,$plurals) ){
$raw = $plurals[$index];
if( $raw instanceof ArrayObject ){
$raw = $raw->getArrayCopy();
}
}
else {
$raw = array('source'=>'','target'=>'');
}
if( $this->translate && ( ! array_key_exists('target',$raw) || '' === $raw['target'] ) ){
$raw['target'] = $arr[$index];
}
// use translation as missing msgid_plural only if msgid matches msgstr (English file)
if( 1 === $index && '' === $raw['source'] ){
if( $arr[0] === $msgid ){
$raw['source'] = $arr[1];
}
/*else {
Loco_error_AdminNotices::debug('msgid_plural missing for msgid '.json_encode($msgid) );
}*/
}
$plurals[$index] = new LocoPoMessage($raw);
}
$message['plurals'] = $plurals;
}
}
}
return $unique;
}
/**
* Shortcut for loading multiple jsons with error tolerance
* @param Loco_fs_File[]
* @return int
*/
public function loadJsons( array $jsons ){
$n = 0;
foreach( $jsons as $jsonfile ){
try {
$n += $this->loadJson($jsonfile);
}
catch( Loco_error_Exception $e ){
Loco_error_AdminNotices::add($e);
}
}
return $n;
}
/**
* Update still-valid sources, deferring unmatched (new strings) for deferred fuzzy match
* @param LocoPoIterator Existing definitions
* @param LocoPoIterator Resultant definitions
* @return string[] keys matched exactly
*/
public function mergeValid( LocoPoIterator $original, LocoPoIterator $merged ){
$valid = array();
$translate = $this->translate;
/* @var LocoPoMessage $old */
foreach( $original as $old ){
$new = $this->match($old);
// if existing source is still valid, merge any changes
if( $new instanceof LocoPoMessage ){
$p = clone $old;
$p->merge($new,$translate);
$merged->push($p);
$valid[] = $p->getKey();
// increment counter if translation was merged
if( $translate && ! $old->translated() ){
$this->translated += $new->translated();
}
}
}
return $valid;
}
/**
* Perform fuzzy matching after all exact matches have been attempted
* @param LocoPoIterator Resultant definitions
* @return string[] strings fuzzy-matched
*/
public function mergeFuzzy( LocoPoIterator $merged ){
$fuzzy = array();
foreach( $this->getFuzzyMatches() as $pair ){
list($old,$new) = $pair;
$p = clone $old;
$p->merge($new);
$merged->push($p);
$fuzzy[] = $p->getKey();
}
return $fuzzy;
}
/**
* Add unmatched strings remaining as NEW source strings
* @param LocoPoIterator Resultant definitions to accept new strings
* @return string[] strings added
*/
public function mergeAdded( LocoPoIterator $merged ){
$added = array();
$translate = $this->translate;
/* @var LocoPoMessage $new */
foreach( $this->unmatched() as $new ){
$p = clone $new;
// remove translations unless configured to keep
if( $p->translated() && ! $translate ){
$p->strip();
}
$merged->push($p);
$added[] = $p->getKey();
}
return $added;
}
/**
* Perform full merge and return result suitable from front end.
* @param LocoPoIterator Existing definitions
* @param LocoPoIterator Resultant definitions
* @return array result
*/
public function merge( LocoPoIterator $original, LocoPoIterator $merged ){
$this->mergeValid($original,$merged);
$fuzzy = $this->mergeFuzzy($merged);
$added = $this->mergeAdded($merged);
/* @var LocoPoMessage $old */
$dropped = array();
foreach( $this->redundant() as $old ){
$dropped[] = $old->getKey();
}
// return to JavaScript with stats in the same form as old front end merge
return array (
'add' => $added,
'fuz' => $fuzzy,
'del' => $dropped,
'trn' => $this->translated,
);
}
/**
* @param array
* @return array
*/
private function getArrayRef( array $a ){
$r = $this->getRef($a);
if( is_null($r) ){
return array();
}
if( $r instanceof ArrayObject ){
return $r->getArrayCopy();
}
throw new Exception( (is_object($r)?get_class($r):gettype($r) ).' returned from '.get_class($this).'::getRef');
}
}