43 private $titleFormatter;
47 private const VALID_FORMATS = [
'export-as-po',
'export-to-file',
'export-as-csv' ];
49 public function __construct( TitleFormatter $titleFormatter, Parser $parser ) {
50 parent::__construct(
'ExportTranslations' );
51 $this->titleFormatter = $titleFormatter;
52 $this->parser = $parser;
57 $out = $this->getOutput();
58 $request = $this->getRequest();
59 $lang = $this->getLanguage();
63 $this->groupId = $request->getText(
'group', $par ??
'' );
64 $this->language = $request->getVal(
'language', $lang->getCode() );
65 $this->format = $request->getText(
'format' );
69 if ( $this->groupId ) {
70 $status = $this->checkInput();
71 if ( !$status->isGood() ) {
72 $out->wrapWikiTextAsInterface(
74 $status->getWikiText(
false,
false, $lang )
83 private function outputForm(): void {
89 'label-message' =>
'translate-page-group',
90 'options' => $this->getGroupOptions(),
91 'default' => $this->groupId,
98 'label-message' =>
'translate-page-language',
99 'options' => $this->getLanguageOptions(),
100 'default' => $this->language,
106 'label-message' =>
'translate-export-form-format',
108 'options' => $this->getFormatOptions(),
109 'default' => $this->format,
112 HTMLForm::factory(
'ooui', $fields, $this->getContext() )
114 ->setWrapperLegendMsg(
'translate-page-settings-legend' )
115 ->setSubmitTextMsg(
'translate-submit' )
117 ->displayForm(
false );
120 private function getGroupOptions(): array {
121 $selected = $this->groupId;
122 $groups = MessageGroups::getAllGroups();
123 uasort( $groups, [ MessageGroups::class,
'groupLabelSort' ] );
126 foreach ( $groups as $id => $group ) {
127 if ( !$group->exists()
128 || ( MessageGroups::getPriority( $group ) ===
'discouraged' && $id !== $selected )
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(): void {
206 $out = $this->getOutput();
207 $group = MessageGroups::getGroup( $this->groupId );
208 $collection = $this->setupCollection( $group );
210 switch ( $this->format ) {
216 $ffs = $group->getFFS();
227 $ffs->setOfflineMode(
true );
229 $filename =
"{$group->getId()}_{$this->language}.po";
230 $this->sendExportHeaders( $filename );
232 echo $ffs->writeIntoVariable( $collection );
235 case 'export-to-file':
241 throw new LogicException(
242 "'export-to-file' requested for a non FileBasedMessageGroup {$group->getId()}"
246 $filename = basename( $group->getSourceFilePath( $collection->getLanguage() ) );
247 $this->sendExportHeaders( $filename );
249 echo $group->getFFS()->writeIntoVariable( $collection );
252 case 'export-as-csv':
254 $filename =
"{$group->getId()}_{$this->language}.csv";
255 $this->sendExportHeaders( $filename );
256 $this->exportCSV( $collection, $group->getSourceLanguage() );
265 $translatablePage = TranslatablePage::newFromTitle( $group->getTitle() );
266 $translationPage = $translatablePage->getTranslationPage( $collection->getLanguage() );
268 $translationPage->filterMessageCollection( $collection );
269 $text = $translationPage->generateSourceFromMessageCollection( $this->parser, $collection );
271 $displayTitle = $translatablePage->getPageDisplayTitle( $this->language );
272 if ( $displayTitle ) {
273 $text =
"{{DISPLAYTITLE:$displayTitle}}$text";
276 $box = Html::element(
278 [
'id' =>
'wpTextbox',
'rows' => 40, ],
281 $out->addHTML( $box );
286 private function setupCollection(
MessageGroup $group ): MessageCollection {
287 $collection = $group->initCollection( $this->language );
290 $translateDocCode = $this->getConfig()->get(
'TranslateDocumentationLanguageCode' );
291 if ( $this->language !== $translateDocCode
294 $collection->filter(
'ignored' );
297 $collection->loadTranslations();
303 private function sendExportHeaders(
string $fileName ): void {
304 $response = $this->getRequest()->response();
305 $response->header(
'Content-Type: text/plain; charset=UTF-8' );
306 $response->header(
"Content-Disposition: attachment; filename=\"$fileName\"" );
309 private function exportCSV( MessageCollection $collection,
string $sourceLanguageCode ): void {
310 $fp = fopen(
'php://output',
'w' );
311 $exportingSourceLanguage = $sourceLanguageCode === $this->language;
314 $this->msg(
'translate-export-csv-message-title' )->text(),
315 $this->msg(
'translate-export-csv-definition' )->text()
318 if ( !$exportingSourceLanguage ) {
319 $header[] = $this->language;
322 fputcsv( $fp, $header );
324 foreach ( $collection->keys() as $messageKey => $titleValue ) {
325 $message = $collection[ $messageKey ];
326 $prefixedTitleText = $this->titleFormatter->getPrefixedText( $titleValue );
328 $handle =
new MessageHandle( Title::newFromText( $prefixedTitleText ) );
329 $sourceLanguageTitle = $handle->getTitleForLanguage( $sourceLanguageCode );
331 $row = [ $sourceLanguageTitle->getPrefixedText(), $message->definition() ];
333 if ( !$exportingSourceLanguage ) {
334 $row[] = $message->translation();
337 fputcsv( $fp, $row );
343 protected function getGroupName() {
344 return 'translation';
New-style FFS class that implements support for gettext file format.
Wraps the translatable page sections into a message group.