diff options
Diffstat (limited to 'MLEB/Translate/ffs/PremadeMediawikiExtensionGroups.php')
-rw-r--r-- | MLEB/Translate/ffs/PremadeMediawikiExtensionGroups.php | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/MLEB/Translate/ffs/PremadeMediawikiExtensionGroups.php b/MLEB/Translate/ffs/PremadeMediawikiExtensionGroups.php new file mode 100644 index 00000000..b57a2148 --- /dev/null +++ b/MLEB/Translate/ffs/PremadeMediawikiExtensionGroups.php @@ -0,0 +1,366 @@ +<?php +/** + * Classes for %MediaWiki extension translation. + * + * @file + * @author Niklas Laxström + * @license GPL-2.0-or-later + */ + +/** + * Class which handles special definition format for %MediaWiki extensions and skins. + */ +class PremadeMediawikiExtensionGroups { + /** @var bool */ + protected $useConfigure = true; + + /** @var string */ + protected $idPrefix = 'ext-'; + + /** @var int */ + protected $namespace = NS_MEDIAWIKI; + + /** + * @var string + * @see __construct + */ + protected $path; + + /** + * @var string + * @see __construct + */ + protected $definitionFile; + + /** + * @param string $def Absolute path to the definition file. See + * tests/data/mediawiki-extensions.txt for example. + * @param string $path General prefix to the file locations without + * the extension specific part. Should start with %GROUPROOT%/ or + * otherwise export path will be wrong. The export path is + * constructed by replacing %GROUPROOT%/ with target directory. + */ + public function __construct( $def, $path ) { + $this->definitionFile = $def; + $this->path = $path; + } + + /** + * Whether to use the Configure extension to load extension home pages. + * + * @since 2012-03-22 + * @param bool $value Whether Configure should be used. + */ + public function setUseConfigure( $value ) { + $this->useConfigure = $value; + } + + /** + * How to prefix message group ids. + * + * @since 2012-03-22 + * @param string $value + */ + public function setGroupPrefix( $value ) { + $this->idPrefix = $value; + } + + /** + * Which namespace holds the messages. + * + * @since 2012-03-22 + * @param int $value + */ + public function setNamespace( $value ) { + $this->namespace = $value; + } + + /** + * Makes an group id from extension name + * @param string $name + * @return string + */ + public static function foldId( $name ) { + return preg_replace( '/\s+/', '', strtolower( $name ) ); + } + + /** + * Hook: TranslatePostInitGroups + * @param array &$list + * @param array &$deps + * @return true + */ + public function register( array &$list, array &$deps ) { + $groups = $this->parseFile(); + $groups = $this->processGroups( $groups ); + foreach ( $groups as $id => $g ) { + $list[$id] = $this->createMessageGroup( $id, $g ); + } + + $deps[] = new FileDependency( $this->definitionFile ); + + return true; + } + + /** + * Creates MediaWikiExtensionMessageGroup objects from parsed data. + * @param string $id unique group id already prefixed + * @param array $info array of group info + * @return MediaWikiExtensionMessageGroup + */ + protected function createMessageGroup( $id, $info ) { + $conf = []; + $conf['BASIC']['class'] = MediaWikiExtensionMessageGroup::class; + $conf['BASIC']['id'] = $id; + $conf['BASIC']['namespace'] = $this->namespace; + $conf['BASIC']['label'] = $info['name']; + + if ( isset( $info['desc'] ) ) { + $conf['BASIC']['description'] = $info['desc']; + } else { + $conf['BASIC']['descriptionmsg'] = $info['descmsg']; + $conf['BASIC']['extensionurl'] = $info['url']; + } + + $conf['FILES']['class'] = JsonFFS::class; + $conf['FILES']['sourcePattern'] = $this->path . '/' . $info['file']; + + // @todo Find a better way + if ( isset( $info['aliasfile'] ) ) { + $conf['FILES']['aliasFileSource'] = $this->path . '/' . $info['aliasfile']; + $conf['FILES']['aliasFile'] = $info['aliasfile']; + } + if ( isset( $info['magicfile'] ) ) { + $conf['FILES']['magicFileSource'] = $this->path . '/' . $info['magicfile']; + $conf['FILES']['magicFile'] = $info['magicfile']; + } + + if ( isset( $info['prefix'] ) ) { + $conf['MANGLER']['class'] = StringMatcher::class; + $conf['MANGLER']['prefix'] = $info['prefix']; + $conf['MANGLER']['patterns'] = $info['mangle']; + + $mangler = new StringMatcher( $info['prefix'], $info['mangle'] ); + if ( isset( $info['ignored'] ) ) { + $info['ignored'] = $mangler->mangleList( $info['ignored'] ); + } + if ( isset( $info['optional'] ) ) { + $info['optional'] = $mangler->mangleList( $info['optional'] ); + } + } + + $conf['VALIDATORS'] = [ + [ 'id' => 'BraceBalance' ], + [ 'id' => 'MediaWikiLink' ], + [ 'id' => 'MediaWikiPageName' ], + [ 'id' => 'MediaWikiParameter' ], + [ 'id' => 'MediaWikiPlural' ], + ]; + + $conf['INSERTABLES']['class'] = MediaWikiInsertablesSuggester::class; + + if ( isset( $info['optional'] ) ) { + $conf['TAGS']['optional'] = $info['optional']; + } + if ( isset( $info['ignored'] ) ) { + $conf['TAGS']['ignored'] = $info['ignored']; + } + + if ( isset( $info['languages'] ) ) { + $conf['LANGUAGES'] = [ + 'whitelist' => [], + 'blacklist' => [], + ]; + + foreach ( $info['languages'] as $tagSpec ) { + if ( preg_match( '/^([+-])?(.+)$/', $tagSpec, $m ) ) { + list( , $sign, $tag ) = $m; + if ( $sign === '+' ) { + $conf['LANGUAGES']['whitelist'][] = $tag; + } elseif ( $sign === '-' ) { + $conf['LANGUAGES']['blacklist'][] = $tag; + } else { + $conf['LANGUAGES']['blacklist'] = '*'; + $conf['LANGUAGES']['whitelist'][] = $tag; + } + } + } + } + + return MessageGroupBase::factory( $conf ); + } + + protected function parseFile() { + $defines = file_get_contents( $this->definitionFile ); + $linefeed = '(\r\n|\n)'; + $sections = array_map( + 'trim', + preg_split( "/$linefeed{2,}/", $defines, -1, PREG_SPLIT_NO_EMPTY ) + ); + $groups = []; + + foreach ( $sections as $section ) { + $lines = array_map( 'trim', preg_split( "/$linefeed/", $section ) ); + $newgroup = []; + + foreach ( $lines as $line ) { + if ( $line === '' || $line[0] === '#' ) { + continue; + } + + if ( strpos( $line, '=' ) === false ) { + if ( empty( $newgroup['name'] ) ) { + $newgroup['name'] = $line; + } else { + throw new MWException( 'Trying to define name twice: ' . $line ); + } + } else { + list( $key, $value ) = array_map( 'trim', explode( '=', $line, 2 ) ); + switch ( $key ) { + case 'aliasfile': + case 'desc': + case 'descmsg': + case 'file': + case 'id': + case 'magicfile': + case 'var': + $newgroup[$key] = $value; + break; + case 'optional': + case 'ignored': + case 'languages': + $values = array_map( 'trim', explode( ',', $value ) ); + if ( !isset( $newgroup[$key] ) ) { + $newgroup[$key] = []; + } + $newgroup[$key] = array_merge( $newgroup[$key], $values ); + break; + case 'prefix': + list( $prefix, $messages ) = array_map( + 'trim', + explode( '|', $value, 2 ) + ); + if ( isset( $newgroup['prefix'] ) && $newgroup['prefix'] !== $prefix ) { + throw new MWException( + "Only one prefix supported: {$newgroup['prefix']} !== $prefix" + ); + } + $newgroup['prefix'] = $prefix; + + if ( !isset( $newgroup['mangle'] ) ) { + $newgroup['mangle'] = []; + } + + $messages = array_map( 'trim', explode( ',', $messages ) ); + $newgroup['mangle'] = array_merge( $newgroup['mangle'], $messages ); + break; + default: + throw new MWException( 'Unknown key:' . $key ); + } + } + } + + if ( count( $newgroup ) ) { + if ( empty( $newgroup['name'] ) ) { + throw new MWException( "Name missing\n" . print_r( $newgroup, true ) ); + } + $groups[] = $newgroup; + } + } + + return $groups; + } + + protected function processGroups( $groups ) { + $configureData = $this->loadConfigureExtensionData(); + $fixedGroups = []; + foreach ( $groups as $g ) { + $name = $g['name']; + + if ( isset( $g['id'] ) ) { + $id = $g['id']; + } else { + $id = $this->idPrefix . preg_replace( '/\s+/', '', strtolower( $name ) ); + } + + if ( !isset( $g['file'] ) ) { + $file = preg_replace( '/\s+/', '', "$name/i18n/%CODE%.json" ); + } else { + $file = $g['file']; + } + + if ( isset( $g['descmsg'] ) ) { + $descmsg = $g['descmsg']; + } else { + $descmsg = str_replace( $this->idPrefix, '', $id ) . '-desc'; + } + + $configureId = self::foldId( $name ); + if ( isset( $configureData[$configureId]['url'] ) ) { + $url = $configureData[$configureId]['url']; + } else { + $url = false; + } + + $newgroup = [ + 'name' => $name, + 'file' => $file, + 'descmsg' => $descmsg, + 'url' => $url, + ]; + + $copyvars = [ + 'aliasfile', + 'desc', + 'ignored', + 'languages', + 'magicfile', + 'mangle', + 'optional', + 'prefix', + 'var', + ]; + + foreach ( $copyvars as $var ) { + if ( isset( $g[$var] ) ) { + $newgroup[$var] = $g[$var]; + } + } + + // Mark some fixed form optional messages automatically + if ( !isset( $newgroup['optional' ] ) ) { + $newgroup['optional'] = []; + } + + // Mark extension name and skin names optional. + $newgroup['optional'][] = '*-extensionname'; + $newgroup['optional'][] = 'skinname-*'; + + $fixedGroups[$id] = $newgroup; + } + + return $fixedGroups; + } + + protected function loadConfigureExtensionData() { + if ( !$this->useConfigure ) { + return []; + } + + global $wgAutoloadClasses; + + $postfix = 'Configure/load_txt_def/TxtDef.php'; + if ( !file_exists( "{$this->path}/$postfix" ) ) { + return []; + } + + $wgAutoloadClasses['TxtDef'] = "{$this->path}/$postfix"; + // @phan-suppress-next-line PhanUndeclaredClassMethod Autoloaded above + $tmp = TxtDef::loadFromFile( "{$this->path}/Configure/settings/Settings-ext.txt" ); + + return array_combine( + array_map( [ __CLASS__, 'foldId' ], array_keys( $tmp ) ), + array_values( $tmp ) + ); + } +} |