26 private $contentLanguage;
28 public function __construct() {
29 parent::__construct();
30 $this->addDescription(
'Creates a dump file that can be imported to a TtmServer' );
34 'Which directory to output files to',
41 'How many threads to use',
47 $availableMethods = array_keys( $this->getAvailableCompressionWrappers() );
48 $values = count( $availableMethods ) ? implode(
', ', $availableMethods ) :
'NONE';
51 "Use a compression filter. Possible values: $values",
57 $this->requireExtension(
'Translate' );
61 private function getAvailableCompressionWrappers(): array {
63 $filters = stream_get_filters();
64 foreach ( $filters as $f ) {
65 if ( preg_match(
'/^compress\..+$/', $f ) ) {
66 $out[$f] = $f .
'://';
72 public function execute() {
73 $this->contentLanguage = MediaWikiServices::getInstance()->getContentLanguage();
75 $threads = (int)$this->getOption(
'threads', 1 );
76 $outputDir = $this->getOption(
'output-directory' );
77 $requestedWrapper = $this->getOption(
'compress' );
78 $availableWrappers = $this->getAvailableCompressionWrappers();
79 if ( $requestedWrapper && !isset( $availableWrappers[$requestedWrapper] ) ) {
81 "Compression wrapper '$requestedWrapper' is not supported"
84 $wrapper = $availableWrappers[$requestedWrapper] ??
'';
85 $suffix = $requestedWrapper ?
".$requestedWrapper" :
'';
89 $groups = $this->getGroupsInPerformanceOrder();
90 foreach ( $groups as $groupId => $group ) {
91 $path = $wrapper . rtrim( $outputDir,
'/' ) .
'/' . $groupId .
'.json' . $suffix;
93 $executor->runInParallel(
94 function (
int $pid ) use ( $groupId ) {
95 $this->output(
"Forked process $pid to process $groupId\n" );
97 function () use ( $group, $path ) {
98 $output = FormatJson::encode(
99 $this->getOutput( $group ),
103 file_put_contents( $path, $output );
108 $this->output(
"Done.\n" );
121 private function getGroupsInPerformanceOrder(): array {
122 $groupStats = MessageGroupStats::forLanguage(
123 $this->contentLanguage->getCode(),
124 MessageGroupStats::FLAG_CACHE_ONLY
129 function ( array $a, array $b ):
int {
130 return -1 * $this->sortGroupsBySize( $a, $b );
135 foreach ( array_keys( $groupStats ) as $groupId ) {
136 $group = MessageGroups::getGroup( $groupId );
137 if ( $group->isMeta() ) {
141 $groups[$group->getId()] = $group;
147 private function sortGroupsBySize( array $a, array $b ):
int {
148 return $a[MessageGroupStats::TOTAL] <=> $b[MessageGroupStats::TOTAL];
151 private function getOutput(
MessageGroup $group ): array {
154 $groupId = $group->
getId();
157 $stats = MessageGroupStats::forGroup( $groupId );
159 foreach ( $stats as $language => $numbers ) {
160 if ( $numbers[MessageGroupStats::TRANSLATED] === 0 ) {
164 $collection->resetForNewLanguage( $language );
165 $collection->filter( MessageCollection::FILTER_IGNORED, MessageCollection::EXCLUDE_MATCHING );
166 $collection->filter( MessageCollection::FILTER_TRANSLATED, MessageCollection::INCLUDE_MATCHING );
167 $collection->loadTranslations();
169 foreach ( $collection->keys() as $mkey => $titleValue ) {
172 $message = $collection[$mkey];
174 if ( !isset( $out[$mkey] ) ) {
176 'wikiId' => WikiMap::getCurrentWikiId(),
177 'title' => $handle->getTitleForBase()->getPrefixedText(),
178 'sourceLanguage' => $sourceLanguage,
179 'primaryGroup' => $groupId,
184 $out[$mkey][
'values'][] = [
185 'language' => $language,
186 'value' => $message->translation(),
187 'revision' => $message->getProperty(
'revision' ),
192 return array_values( $out );