%PDF- %PDF-
| Direktori : /www/varak.net/wiki.varak.net/extensions/Translate/utils/ |
| Current File : /www/varak.net/wiki.varak.net/extensions/Translate/utils/TranslationHelpers.php |
<?php
/**
* Contains helper class for interface parts that aid translations in doing
* their thing.
*
* @file
* @author Niklas Laxström
* @license GPL-2.0-or-later
*/
/**
* Provides the nice boxes that aid the translators to do their job.
* Boxes contain definition, documentation, other languages, translation memory
* suggestions, highlighted changes etc.
*/
class TranslationHelpers {
/**
* @var MessageHandle
* @since 2012-01-04
*/
protected $handle;
/**
* @var TranslationAidDataProvider
*/
private $dataProvider;
/**
* The group object of the message (or null if there isn't any)
* @var MessageGroup
*/
protected $group;
/**
* The current translation.
* @var string
*/
private $translation;
/**
* HTML id to the text area that contains the translation. Used to insert
* suggestion directly into the text area, for example.
*/
protected $textareaId = 'wpTextbox1';
/**
* Whether to include extra tools to aid translating.
*/
protected $editMode = 'true';
/**
* @param Title $title Title of a page that holds a translation.
* @param string $groupId Group id that should be used, otherwise autodetected from title.
*/
public function __construct( Title $title, $groupId ) {
$this->handle = new MessageHandle( $title );
$this->dataProvider = new TranslationAidDataProvider( $this->handle );
$this->group = $this->getMessageGroup( $this->handle, $groupId );
}
/**
* Tries to determine to which group this message belongs. Falls back to the
* message index if valid group id was not supplied.
*
* @param MessageHandle $handle
* @param string $groupId
* @return MessageGroup|null Group the key belongs to, or null.
*/
protected function getMessageGroup( MessageHandle $handle, $groupId ) {
$mg = MessageGroups::getGroup( $groupId );
# If we were not given (a valid) group
if ( $mg === null ) {
$groupId = MessageIndex::getPrimaryGroupId( $handle );
$mg = MessageGroups::getGroup( $groupId );
}
return $mg;
}
/**
* Gets the HTML id of the text area that contains the translation.
* @return String
*/
public function getTextareaId() {
return $this->textareaId;
}
/**
* Sets the HTML id of the text area that contains the translation.
* @param String $id
*/
public function setTextareaId( $id ) {
$this->textareaId = $id;
}
/**
* Enable or disable extra help for editing.
* @param bool $mode
*/
public function setEditMode( $mode = true ) {
$this->editMode = $mode;
}
/**
* Gets the message definition.
* @return String
*/
public function getDefinition() {
$this->mustBeKnownMessage();
$obj = new MessageDefinitionAid(
$this->group,
$this->handle,
RequestContext::getMain(),
$this->dataProvider
);
return $obj->getData()['value'];
}
/**
* Gets the current message translation. Fuzzy messages will be marked as
* such unless translation is provided manually.
* @return string
*/
public function getTranslation() {
if ( $this->translation === null ) {
$obj = new CurrentTranslationAid(
$this->group,
$this->handle,
RequestContext::getMain(),
$this->dataProvider
);
$aid = $obj->getData();
$this->translation = $aid['value'];
if ( $aid['fuzzy'] ) {
$this->translation = TRANSLATE_FUZZY . $this->translation;
}
}
return $this->translation;
}
/**
* Manual override for the translation. If not given or it is null, the code
* will try to fetch it automatically.
* @param string|null $translation
*/
public function setTranslation( $translation ) {
$this->translation = $translation;
}
/**
* Gets the linguistically correct language code for translation
* @return string
*/
public function getTargetLanguage() {
global $wgLanguageCode, $wgTranslateDocumentationLanguageCode;
$code = $this->handle->getCode();
if ( !$code ) {
$this->mustBeKnownMessage();
$code = $this->group->getSourceLanguage();
}
if ( $code === $wgTranslateDocumentationLanguageCode ) {
return $wgLanguageCode;
}
return $code;
}
/**
* Returns block element HTML snippet that contains the translation aids.
* Not all boxes are shown all the time depending on whether they have
* any information to show and on configuration variables.
* @return String Block level HTML snippet or empty string.
*/
public function getBoxes() {
// Box filter
$all = $this->getBoxNames();
$boxes = [];
foreach ( $all as $type => $cb ) {
$box = $this->callBox( $type, $cb );
if ( $box ) {
$boxes[$type] = $box;
}
}
Hooks::run( 'TranslateGetBoxes', [ $this->group, $this->handle, &$boxes ] );
if ( count( $boxes ) ) {
return Html::rawElement(
'div',
[ 'class' => 'mw-sp-translate-edit-fields' ],
implode( "\n\n", $boxes )
);
} else {
return '';
}
}
/**
* Public since 2012-06-26
*
* @since 2012-01-04
* @param string $type
* @param callback $cb
* @param array $params
* @return mixed
*/
public function callBox( $type, $cb, array $params = [] ) {
try {
return call_user_func_array( $cb, $params );
} catch ( TranslationHelperException $e ) {
return "<!-- Box $type not available: {$e->getMessage()} -->";
}
}
/**
* @return array
*/
public function getBoxNames() {
return [
'other-languages' => [ $this, 'getOtherLanguagesBox' ],
'separator' => [ $this, 'getSeparatorBox' ],
'documentation' => [ $this, 'getDocumentationBox' ],
'definition' => [ $this, 'getDefinitionBox' ],
];
}
public function getDefinitionBox() {
$this->mustHaveDefinition();
$en = $this->getDefinition();
$title = Linker::link(
SpecialPage::getTitleFor( 'Translate' ),
htmlspecialchars( $this->group->getLabel() ),
[],
[
'group' => $this->group->getId(),
'language' => $this->handle->getCode()
]
);
$label =
wfMessage( 'translate-edit-definition' )->escaped() .
wfMessage( 'word-separator' )->escaped() .
wfMessage( 'parentheses' )->rawParams( $title )->escaped();
// Source language object
$sl = Language::factory( $this->group->getSourceLanguage() );
$dialogID = $this->dialogID();
$id = Sanitizer::escapeId( "def-$dialogID" );
$msg = $this->adder( $id, $sl ) . "\n" . Html::rawElement( 'div',
[
'class' => 'mw-translate-edit-deftext',
'dir' => $sl->getDir(),
'lang' => $sl->getHtmlCode(),
],
TranslateUtils::convertWhiteSpaceToHTML( $en )
);
$msg .= $this->wrapInsert( $id, $en );
$class = [ 'class' => 'mw-sp-translate-edit-definition mw-translate-edit-definition' ];
return TranslateUtils::fieldset( $label, $msg, $class );
}
public function getTranslationDisplayBox() {
$en = $this->getTranslation();
if ( $en === null ) {
return null;
}
$label = wfMessage( 'translate-edit-translation' )->escaped();
$class = [ 'class' => 'mw-translate-edit-translation' ];
$msg = Html::rawElement( 'span',
[ 'class' => 'mw-translate-edit-translationtext' ],
TranslateUtils::convertWhiteSpaceToHTML( $en )
);
return TranslateUtils::fieldset( $label, $msg, $class );
}
public function getOtherLanguagesBox() {
$code = $this->handle->getCode();
$page = $this->handle->getKey();
$ns = $this->handle->getTitle()->getNamespace();
$boxes = [];
foreach ( self::getFallbacks( $code ) as $fbcode ) {
$text = TranslateUtils::getMessageContent( $page, $fbcode, $ns );
if ( $text === null ) {
continue;
}
$fbLanguage = Language::factory( $fbcode );
$context = RequestContext::getMain();
$label = TranslateUtils::getLanguageName( $fbcode, $context->getLanguage()->getCode() ) .
$context->msg( 'word-separator' )->text() .
$context->msg( 'parentheses', $fbLanguage->getHtmlCode() )->text();
$target = $this->handle->getTitleForLanguage( $fbcode );
if ( $target ) {
$label = self::ajaxEditLink( $target, $label );
}
$dialogID = $this->dialogID();
$id = Sanitizer::escapeId( "other-$fbcode-$dialogID" );
$params = [ 'class' => 'mw-translate-edit-item' ];
$display = TranslateUtils::convertWhiteSpaceToHTML( $text );
$display = Html::rawElement( 'div', [
'lang' => $fbLanguage->getHtmlCode(),
'dir' => $fbLanguage->getDir() ],
$display
);
$contents = self::legend( $label ) . "\n" . $this->adder( $id, $fbLanguage ) .
$display . self::clear();
$boxes[] = Html::rawElement( 'div', $params, $contents ) .
$this->wrapInsert( $id, $text );
}
if ( count( $boxes ) ) {
$sep = Html::element( 'hr', [ 'class' => 'mw-translate-sep' ] );
return TranslateUtils::fieldset(
wfMessage(
'translate-edit-in-other-languages',
$page
)->escaped(),
implode( "$sep\n", $boxes ),
[ 'class' => 'mw-sp-translate-edit-inother' ]
);
}
return null;
}
public function getSeparatorBox() {
return Html::element( 'div', [ 'class' => 'mw-translate-edit-extra' ] );
}
public function getDocumentationBox() {
global $wgTranslateDocumentationLanguageCode;
if ( !$wgTranslateDocumentationLanguageCode ) {
throw new TranslationHelperException( 'Message documentation language code is not defined' );
}
$context = RequestContext::getMain();
$page = $this->handle->getKey();
$ns = $this->handle->getTitle()->getNamespace();
$title = $this->handle->getTitleForLanguage( $wgTranslateDocumentationLanguageCode );
$edit = self::ajaxEditLink(
$title,
$context->msg( 'translate-edit-contribute' )->text()
);
$info = TranslateUtils::getMessageContent( $page, $wgTranslateDocumentationLanguageCode, $ns );
$class = 'mw-sp-translate-edit-info';
// The information is most likely in English
$divAttribs = [ 'dir' => 'ltr', 'lang' => 'en', 'class' => 'mw-content-ltr' ];
if ( (string)$info === '' ) {
$info = $context->msg( 'translate-edit-no-information' )->text();
$class = 'mw-sp-translate-edit-noinfo';
$lang = $context->getLanguage();
// The message saying that there's no info, should be translated
$divAttribs = [ 'dir' => $lang->getDir(), 'lang' => $lang->getHtmlCode() ];
}
$class .= ' mw-sp-translate-message-documentation';
$contents = $context->getOutput()->parse( $info );
// Remove whatever block element wrapup the parser likes to add
$contents = preg_replace( '~^<([a-z]+)>(.*)</\1>$~us', '\2', $contents );
return TranslateUtils::fieldset(
$context->msg( 'translate-edit-information' )->rawParams( $edit )->escaped(),
Html::rawElement( 'div', $divAttribs, $contents ), [ 'class' => $class ]
);
}
/**
* @param string $label
* @return string
*/
protected static function legend( $label ) {
# Float it to the opposite direction
return Html::rawElement( 'div', [ 'class' => 'mw-translate-legend' ], $label );
}
/**
* @return string
*/
protected static function clear() {
return Html::element( 'div', [ 'style' => 'clear:both;' ] );
}
/**
* @param string $code
* @return array
*/
protected static function getFallbacks( $code ) {
global $wgTranslateLanguageFallbacks;
// User preference has the final say
$user = RequestContext::getMain()->getUser();
$preference = $user->getOption( 'translate-editlangs' );
if ( $preference !== 'default' ) {
$fallbacks = array_map( 'trim', explode( ',', $preference ) );
foreach ( $fallbacks as $k => $v ) {
if ( $v === $code ) {
unset( $fallbacks[$k] );
}
}
return $fallbacks;
}
// Global configuration settings
$fallbacks = [];
if ( isset( $wgTranslateLanguageFallbacks[$code] ) ) {
$fallbacks = (array)$wgTranslateLanguageFallbacks[$code];
}
$list = Language::getFallbacksFor( $code );
array_pop( $list ); // Get 'en' away from the end
$fallbacks = array_merge( $list, $fallbacks );
return array_unique( $fallbacks );
}
/**
* @return string
*/
public function dialogID() {
$hash = sha1( $this->handle->getTitle()->getPrefixedDBkey() );
return substr( $hash, 0, 4 );
}
/**
* @param string $source jQuery selector for element containing the source
* @param Language $lang Language object
* @return string
*/
public function adder( $source, $lang ) {
if ( !$this->editMode ) {
return '';
}
$target = self::jQueryPathId( $this->getTextareaId() );
$source = self::jQueryPathId( $source );
$dir = $lang->getDir();
$params = [
'onclick' => "jQuery($target).val(jQuery($source).text()).focus(); return false;",
'href' => '#',
'title' => wfMessage( 'translate-use-suggestion' )->text(),
'class' => 'mw-translate-adder mw-translate-adder-' . $dir,
];
return Html::element( 'a', $params, '↓' );
}
/**
* @param string|int $id
* @param string $text
* @return string
*/
public function wrapInsert( $id, $text ) {
return Html::element( 'pre', [ 'id' => $id, 'style' => 'display: none;' ], $text );
}
/**
* Ajax-enabled message editing link.
* @param Title $target Title of the target message.
* @param string $text Link text for Linker::link()
* @return string HTML link
*/
public static function ajaxEditLink( Title $target, $text ) {
$handle = new MessageHandle( $target );
$uri = TranslateUtils::getEditorUrl( $handle );
$link = Html::element(
'a',
[ 'href' => $uri ],
$text
);
return $link;
}
/**
* Escapes $id such that it can be used in jQuery selector.
* @param string $id
* @return string
*/
public static function jQueryPathId( $id ) {
$id = preg_replace( '/[^A-Za-z0-9_-]/', '\\\\$0', $id );
return Xml::encodeJsVar( "#$id" );
}
public static function addModules( OutputPage $out ) {
$out->addModuleStyles( 'ext.translate.quickedit' );
// Might be needed, but ajax doesn't load it
// Globals :(
$diff = new DifferenceEngine;
$diff->showDiffStyle();
}
/// @since 2012-01-04
protected function mustBeKnownMessage() {
if ( !$this->group ) {
throw new TranslationHelperException( 'unknown group' );
}
}
/// @since 2012-01-04
protected function mustHaveDefinition() {
if ( (string)$this->getDefinition() === '' ) {
throw new TranslationHelperException( 'message does not have definition' );
}
}
}
/**
* Translation helpers can throw this exception when they cannot do
* anything useful with the current message. This helps in debugging
* why some fields are not shown. See also helpers in TranslationHelpers:
* - mustBeKnownMessage()
* - mustHaveDefinition()
* @since 2012-01-04 (Renamed in 2012-07-24 to fix typo in name)
*/
class TranslationHelperException extends MWException {
}