36 protected string $language;
37 protected string $format;
38 protected string $groupId;
39 private TitleFormatter $titleFormatter;
40 private ParserFactory $parserFactory;
42 private const VALID_FORMATS = [
'export-as-po',
'export-to-file',
'export-as-csv' ];
44 public function __construct( TitleFormatter $titleFormatter, ParserFactory $parserFactory ) {
45 parent::__construct(
'ExportTranslations' );
46 $this->titleFormatter = $titleFormatter;
47 $this->parserFactory = $parserFactory;
52 $out = $this->getOutput();
53 $request = $this->getRequest();
54 $lang = $this->getLanguage();
58 $this->groupId = $request->getText(
'group', $par ??
'' );
59 $this->language = $request->getVal(
'language', $lang->getCode() );
60 $this->format = $request->getText(
'format' );
63 $out->addModules(
'ext.translate.special.exporttranslations' );
65 if ( $this->groupId ) {
66 $status = $this->checkInput();
67 if ( !$status->isGood() ) {
68 $out->wrapWikiTextAsInterface(
70 $status->getWikiText(
false,
false, $lang )
75 $status = $this->doExport();
76 if ( !$status->isGood() ) {
78 Html::errorBox( $status->getHTML(
false,
false, $lang ) )
84 private function outputForm(): void {
90 'label-message' =>
'translate-page-group',
91 'options' => $this->getGroupOptions(),
92 'default' => $this->groupId,
99 'label-message' =>
'translate-page-language',
100 'options' => $this->getLanguageOptions(),
101 'default' => $this->language,
107 'label-message' =>
'translate-export-form-format',
109 'options' => $this->getFormatOptions(),
110 'default' => $this->format,
113 HTMLForm::factory(
'ooui', $fields, $this->getContext() )
115 ->setId(
'mw-export-message-group-form' )
116 ->setWrapperLegendMsg(
'translate-page-settings-legend' )
117 ->setSubmitTextMsg(
'translate-submit' )
119 ->displayForm(
false );
122 private function getGroupOptions(): array {
123 $selected = $this->groupId;
124 $groups = MessageGroups::getAllGroups();
125 uasort( $groups, [ MessageGroups::class,
'groupLabelSort' ] );
128 foreach ( $groups as $id => $group ) {
129 if ( !$group->exists() ) {
133 $options[$group->getLabel()] = $id;
140 private function getLanguageOptions(): array {
141 $languages = Utilities::getLanguageNames(
'en' );
143 foreach ( $languages as $code => $name ) {
144 $options[
"$code - $name"] = $code;
151 private function getFormatOptions(): array {
153 foreach ( self::VALID_FORMATS as $format ) {
155 $options[ $this->msg(
"translate-taskui-$format" )->escaped() ] = $format;
160 private function checkInput(): Status {
161 $status = Status::newGood();
163 $msgGroup = MessageGroups::getGroup( $this->groupId );
164 if ( $msgGroup ===
null ) {
165 $status->fatal(
'translate-page-no-such-group' );
166 } elseif ( MessageGroups::isDynamic( $msgGroup ) ) {
167 $status->fatal(
'translate-export-not-supported' );
170 $langNames = Utilities::getLanguageNames(
'en' );
171 if ( !isset( $langNames[$this->language] ) ) {
172 $status->fatal(
'translate-page-no-such-language' );
181 && !in_array( $this->format, self::VALID_FORMATS )
183 $status->fatal(
'translate-export-invalid-format' );
186 if ( $this->format ===
'export-to-file'
189 $status->fatal(
'translate-export-format-notsupported' );
192 if ( $msgGroup && !MessageGroups::isDynamic( $msgGroup ) ) {
193 $size = count( $msgGroup->getKeys() );
194 if ( $size > self::MAX_EXPORT_SIZE ) {
196 'translate-export-group-too-large',
197 Message::numParam( self::MAX_EXPORT_SIZE )
205 private function doExport(): Status {
206 $out = $this->getOutput();
207 $group = MessageGroups::getGroup( $this->groupId );
208 $collection = $this->setupCollection( $group );
210 switch ( $this->format ) {
216 $fileFormat = $group->getFFS();
219 if ( !$fileFormat instanceof GettextFormat ) {
224 $fileFormat =
new GettextFormat( $group );
227 $fileFormat->setOfflineMode(
true );
229 $filename =
"{$group->getId()}_{$this->language}.po";
230 $this->sendExportHeaders( $filename );
232 echo $fileFormat->writeIntoVariable( $collection );
235 case 'export-to-file':
239 throw new LogicException(
240 "'export-to-file' requested for a non FileBasedMessageGroup {$group->getId()}"
244 $messages = $group->getFFS()->writeIntoVariable( $collection );
246 if ( $messages ===
'' ) {
247 return Status::newFatal(
'translate-export-format-file-empty' );
251 $filename = basename( $group->getSourceFilePath( $collection->getLanguage() ) );
252 $this->sendExportHeaders( $filename );
256 case 'export-as-csv':
258 $filename =
"{$group->getId()}_{$this->language}.csv";
259 $this->sendExportHeaders( $filename );
260 $this->exportCSV( $collection, $group->getSourceLanguage() );
266 return Status::newFatal(
'translate-export-format-notsupported' );
269 $translatablePage = TranslatablePage::newFromTitle( $group->getTitle() );
270 $translationPage = $translatablePage->getTranslationPage( $collection->getLanguage() );
272 $translationPage->filterMessageCollection( $collection );
273 $text = $translationPage->generateSourceFromMessageCollection(
274 $this->parserFactory->getInstance(),
278 $displayTitle = $translatablePage->getPageDisplayTitle( $this->language );
279 if ( $displayTitle ) {
280 $text =
"{{DISPLAYTITLE:$displayTitle}}$text";
283 $box = Html::element(
285 [
'id' =>
'wpTextbox',
'rows' => 40, ],
288 $out->addHTML( $box );
292 return Status::newGood();
295 private function setupCollection(
MessageGroup $group ): MessageCollection {
296 $collection = $group->initCollection( $this->language );
299 $translateDocCode = $this->getConfig()->get(
'TranslateDocumentationLanguageCode' );
300 if ( $this->language !== $translateDocCode
303 $collection->filter(
'ignored' );
306 $collection->loadTranslations();
312 private function sendExportHeaders(
string $fileName ): void {
313 $response = $this->getRequest()->response();
314 $response->header(
'Content-Type: text/plain; charset=UTF-8' );
315 $response->header(
"Content-Disposition: attachment; filename=\"$fileName\"" );
318 private function exportCSV( MessageCollection $collection,
string $sourceLanguageCode ): void {
319 $fp = fopen(
'php://output',
'w' );
320 $exportingSourceLanguage = $sourceLanguageCode === $this->language;
323 $this->msg(
'translate-export-csv-message-title' )->text(),
324 $this->msg(
'translate-export-csv-definition' )->text()
327 if ( !$exportingSourceLanguage ) {
328 $header[] = $this->language;
331 fputcsv( $fp, $header );
333 foreach ( $collection->keys() as $messageKey => $titleValue ) {
334 $message = $collection[ $messageKey ];
335 $prefixedTitleText = $this->titleFormatter->getPrefixedText( $titleValue );
337 $handle =
new MessageHandle( Title::newFromText( $prefixedTitleText ) );
338 $sourceLanguageTitle = $handle->getTitleForLanguage( $sourceLanguageCode );
340 $row = [ $sourceLanguageTitle->getPrefixedText(), $message->definition() ];
342 if ( !$exportingSourceLanguage ) {
343 $row[] = $message->translation();
346 fputcsv( $fp, $row );
352 protected function getGroupName() {
353 return 'translation';
Wraps the translatable page sections into a message group.