26 private int $pageImportCount = 0;
27 private int $totalPagesBeingImported = 0;
29 public function __construct() {
30 parent::__construct();
33 'Path to the XML file to be imported',
38 'Name of the user performing the import',
44 'Prefix to apply to unknown (and possibly also known) usernames',
50 'Comment added to the log for the import',
56 'Whether to apply the prefix to usernames that exist locally',
61 'Target page name to import the page to',
67 'Override existing target page if it exists',
73 'skip-translating-title',
74 'Should translation of title be skipped',
79 'Comma separated list of priority language codes',
84 'priority-languages-reason',
85 'Reason for setting the priority languages',
90 'force-priority-languages',
91 'Only allow translations to the priority languages',
95 'disallow-transclusion',
96 'Disable translation aware transclusion for this page',
100 'use-old-syntax-version',
101 'Use the old syntax version for translatable pages',
105 $this->requireExtension(
'Translate' );
110 $this->pageImportCount = 0;
111 $importFilePath = $this->getPathOfFileToImport();
112 $importUser = $this->getImportUser();
113 $comment = $this->getOption(
'comment' );
114 $interwikiPrefix = $this->getInterwikiPrefix();
115 $assignKnownUsers = $this->hasOption(
'assign-known-users' );
116 $targetPage = $this->getTargetPageName();
117 $translatablePageSettings = $this->getTranslatablePageSettings();
121 $this->totalPagesBeingImported = substr_count( file_get_contents( $importFilePath ),
'</page>' );
122 $importer = Services::getInstance()->getTranslatableBundleImporter();
123 $this->output(
"Starting import for file '$importFilePath'...\n" );
124 $importer->setPageImportCompleteCallback( [ $this,
'logPageImportComplete' ] );
125 $bundleTitle = $importer->import(
130 RequestContext::getMain(),
135 $this->error(
"An error occurred during import: {$e->getMessage()}\n" );
136 $this->error(
"Stacktrace: {$e->getTraceAsString()} .\n" );
137 $this->fatalError(
'Stopping import.' );
141 "Translatable bundle {$bundleTitle->getPrefixedText()} was imported. " .
142 "Total {$this->pageImportCount} page(s) were created\n"
145 $this->output(
"Now marking {$bundleTitle->getPrefixedText()} for translation...\n" );
147 $this->markPageForTranslation( $bundleTitle, $translatablePageSettings, $importUser );
150 public function logPageImportComplete( Title $title, ForeignTitle $foreignTitle ): void {
151 ++$this->pageImportCount;
152 $currentProgress = str_pad(
153 (
string)$this->pageImportCount,
154 strlen( (
string)$this->totalPagesBeingImported ),
159 $progressCounter =
"($currentProgress/$this->totalPagesBeingImported)";
160 $this->output(
"$progressCounter {$foreignTitle->getFullText()} --> {$title->getFullText()}\n" );
163 private function getPathOfFileToImport(): string {
164 $xmlPath = $this->getArg(
'xml-path' );
165 if ( !file_exists( $xmlPath ) ) {
166 $this->fatalError(
"File '$xmlPath' does not exist" );
169 if ( !is_readable( $xmlPath ) ) {
170 $this->fatalError(
"File '$xmlPath' is not readable" );
176 private function getImportUser(): UserIdentity {
177 $username = $this->getOption(
'user' );
179 $userFactory = MediaWikiServices::getInstance()->getUserFactory();
180 $user = $userFactory->newFromName( $username );
182 if ( $user ===
null || !$user->isNamed() ) {
183 $this->fatalError(
"User $username does not exist." );
189 private function getInterwikiPrefix(): string {
190 $interwikiPrefix = trim( $this->getOption(
'interwiki-prefix',
'' ) );
191 if ( $interwikiPrefix ===
'' ) {
192 $this->fatalError(
'Argument interwiki-prefix cannot be empty.' );
195 return $interwikiPrefix;
198 private function getPriorityLanguages(): array {
199 $priorityLanguageCodes = self::commaList2Array( $this->getOption(
'priority-languages' ) ??
'' );
200 $knownLanguageCodes = array_keys( Utilities::getLanguageNames(
'en' ) );
201 $invalidLanguageCodes = array_diff( $priorityLanguageCodes, $knownLanguageCodes );
203 if ( $invalidLanguageCodes ) {
205 'Unknown priority language code(s): ' . implode(
', ', $invalidLanguageCodes )
209 return $priorityLanguageCodes;
212 private function markPageForTranslation(
214 TranslatablePageSettings $translatablePageSettings,
215 UserIdentity $importUser
217 $translatablePageMarker = Services::getInstance()->getTranslatablePageMarker();
218 $user = MediaWikiServices::getInstance()->getUserFactory()->newFromUserIdentity( $importUser );
220 $operation = $translatablePageMarker->getMarkOperation(
221 $bundleTitle->toPageRecord( IDBAccessObject::READ_LATEST ),
223 $translatablePageSettings->shouldTranslateTitle()
225 }
catch ( TranslatablePageMarkException $e ) {
226 $this->error(
"Error while marking page {$bundleTitle->getPrefixedText()} for translation.\n" );
227 $this->error(
"Fix the issues and mark the page for translation using Special:PageTranslation.\n\n" );
228 $this->fatalError( wfMessage( $e->getMessageObject() )->text() );
231 $statusFormatter = MediaWikiServices::getInstance()
232 ->getFormatterFactory()
233 ->getStatusFormatter( RequestContext::getMain() );
235 $unitNameValidationResult = $operation->getUnitValidationStatus();
236 if ( !$unitNameValidationResult->isOK() ) {
237 $this->output(
"Unit validation failed for {$bundleTitle->getPrefixedText()}.\n" );
238 $this->fatalError( $statusFormatter->getMessage( $unitNameValidationResult )->text() );
242 $translatablePageMarker->markForTranslation(
244 $translatablePageSettings,
245 RequestContext::getMain(),
248 $this->output(
"The page {$bundleTitle->getPrefixedText()} has been marked for translation.\n" );
249 }
catch ( TranslatablePageMarkException $e ) {
250 $this->error(
"Error while marking page {$bundleTitle->getPrefixedText()} for translation.\n" );
251 $this->error(
"Fix the issues and mark the page for translation using Special:PageTranslation.\n\n" );
252 $this->fatalError( $e->getMessageObject()->text() );
256 private function getTranslatablePageSettings(): TranslatablePageSettings {
257 return new TranslatablePageSettings(
258 $this->getPriorityLanguages(),
259 $this->hasOption(
'force-priority-languages' ),
260 $this->getOption(
'priority-languages-reason' ) ??
'',
262 !$this->hasOption(
'skip-translating-title' ),
263 !$this->hasOption(
'use-old-syntax-version' ),
264 !$this->hasOption(
'disallow-transclusion' ),
268 private function getTargetPageName(): ?Title {
269 $targetPage = $this->getOption(
'target-name' );
270 if ( $targetPage ===
null ) {
275 $targetPageTitle = MediaWikiServices::getInstance()->getTitleFactory()->newFromTextThrow( $targetPage );
276 }
catch ( MalformedTitleException $e ) {
278 "Target page name $targetPage does not appear to be valid: {$e->getMessage()}"
282 $shouldOverride = $this->hasOption(
'override' );
283 if ( $targetPageTitle->exists() && !$shouldOverride ) {
285 "Specified target page $targetPage already exists. Use '--override' if you still want to import"
289 if ( !$targetPageTitle->canExist() ) {
290 $this->fatalError(
"The target page name $targetPage cannot be created" );
293 return $targetPageTitle;
return[ 'Translate:AggregateGroupManager'=> static function(MediaWikiServices $services):AggregateGroupManager { return new AggregateGroupManager($services->getTitleFactory(), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:AggregateGroupMessageGroupFactory'=> static function(MediaWikiServices $services):AggregateGroupMessageGroupFactory { return new AggregateGroupMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:ConfigHelper'=> static function():ConfigHelper { return new ConfigHelper();}, 'Translate:CsvTranslationImporter'=> static function(MediaWikiServices $services):CsvTranslationImporter { return new CsvTranslationImporter( $services->getWikiPageFactory());}, 'Translate:EntitySearch'=> static function(MediaWikiServices $services):EntitySearch { return new EntitySearch($services->getMainWANObjectCache(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), MessageGroups::singleton(), $services->getNamespaceInfo(), $services->get( 'Translate:MessageIndex'), $services->getTitleParser(), $services->getTitleFormatter());}, 'Translate:ExternalMessageSourceStateComparator'=> static function(MediaWikiServices $services):ExternalMessageSourceStateComparator { return new ExternalMessageSourceStateComparator(new SimpleStringComparator(), $services->getRevisionLookup(), $services->getPageStore());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance(LogNames::GROUP_SYNCHRONIZATION), $services->get( 'Translate:MessageIndex'), $services->getTitleFactory(), $services->get( 'Translate:MessageGroupSubscription'), new ServiceOptions(ExternalMessageSourceStateImporter::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:FileBasedMessageGroupFactory'=> static function(MediaWikiServices $services):FileBasedMessageGroupFactory { return new FileBasedMessageGroupFactory(new MessageGroupConfigurationParser(), new ServiceOptions(FileBasedMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:FileFormatFactory'=> static function(MediaWikiServices $services):FileFormatFactory { return new FileFormatFactory( $services->getObjectFactory());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:HookDefinedMessageGroupFactory'=> static function(MediaWikiServices $services):HookDefinedMessageGroupFactory { return new HookDefinedMessageGroupFactory( $services->get( 'Translate:HookRunner'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleDependencyPurger'=> static function(MediaWikiServices $services):MessageBundleDependencyPurger { return new MessageBundleDependencyPurger( $services->get( 'Translate:TranslatableBundleFactory'));}, 'Translate:MessageBundleMessageGroupFactory'=> static function(MediaWikiServices $services):MessageBundleMessageGroupFactory { return new MessageBundleMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'), new ServiceOptions(MessageBundleMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:MessageBundleTranslationLoader'=> static function(MediaWikiServices $services):MessageBundleTranslationLoader { return new MessageBundleTranslationLoader( $services->getLanguageFallback());}, 'Translate:MessageGroupMetadata'=> static function(MediaWikiServices $services):MessageGroupMetadata { return new MessageGroupMetadata( $services->getConnectionProvider());}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getConnectionProvider(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getLinkRenderer(), $services->get( 'Translate:MessageGroupReviewStore'), $services->get( 'Translate:MessageGroupMetadata'), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, 'Translate:MessageGroupSubscription'=> static function(MediaWikiServices $services):MessageGroupSubscription { return new MessageGroupSubscription($services->get( 'Translate:MessageGroupSubscriptionStore'), $services->getJobQueueGroup(), $services->getUserIdentityLookup(), LoggerFactory::getInstance(LogNames::GROUP_SUBSCRIPTION), new ServiceOptions(MessageGroupSubscription::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:MessageGroupSubscriptionHookHandler'=> static function(MediaWikiServices $services):MessageGroupSubscriptionHookHandler { return new MessageGroupSubscriptionHookHandler($services->get( 'Translate:MessageGroupSubscription'), $services->getUserFactory());}, 'Translate:MessageGroupSubscriptionStore'=> static function(MediaWikiServices $services):MessageGroupSubscriptionStore { return new MessageGroupSubscriptionStore( $services->getConnectionProvider());}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=(array) $services->getMainConfig() ->get( 'TranslateMessageIndex');$class=array_shift( $params);$implementationMap=['HashMessageIndex'=> HashMessageIndex::class, 'CDBMessageIndex'=> CDBMessageIndex::class, 'DatabaseMessageIndex'=> DatabaseMessageIndex::class, 'hash'=> HashMessageIndex::class, 'cdb'=> CDBMessageIndex::class, 'database'=> DatabaseMessageIndex::class,];$messageIndexStoreClass=$implementationMap[$class] ?? $implementationMap['database'];return new MessageIndex(new $messageIndexStoreClass, $services->getMainWANObjectCache(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), LoggerFactory::getInstance(LogNames::MAIN), $services->getMainObjectStash(), $services->getConnectionProvider(), new ServiceOptions(MessageIndex::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessagePrefixStats'=> static function(MediaWikiServices $services):MessagePrefixStats { return new MessagePrefixStats( $services->getTitleParser());}, 'Translate:ParsingPlaceholderFactory'=> static function():ParsingPlaceholderFactory { return new ParsingPlaceholderFactory();}, 'Translate:PersistentCache'=> static function(MediaWikiServices $services):PersistentCache { return new PersistentDatabaseCache($services->getConnectionProvider(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore( $services->getConnectionProvider());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleDeleter'=> static function(MediaWikiServices $services):TranslatableBundleDeleter { return new TranslatableBundleDeleter($services->getMainObjectStash(), $services->getJobQueueGroup(), $services->get( 'Translate:SubpageListBuilder'), $services->get( 'Translate:TranslatableBundleFactory'));}, 'Translate:TranslatableBundleExporter'=> static function(MediaWikiServices $services):TranslatableBundleExporter { return new TranslatableBundleExporter($services->get( 'Translate:SubpageListBuilder'), $services->getWikiExporterFactory(), $services->getConnectionProvider());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleImporter'=> static function(MediaWikiServices $services):TranslatableBundleImporter { return new TranslatableBundleImporter($services->getWikiImporterFactory(), $services->get( 'Translate:TranslatablePageParser'), $services->getRevisionLookup(), $services->getNamespaceInfo(), $services->getTitleFactory(), $services->getFormatterFactory());}, 'Translate:TranslatableBundleMover'=> static function(MediaWikiServices $services):TranslatableBundleMover { return new TranslatableBundleMover($services->getMovePageFactory(), $services->getJobQueueGroup(), $services->getLinkBatchFactory(), $services->get( 'Translate:TranslatableBundleFactory'), $services->get( 'Translate:SubpageListBuilder'), $services->getConnectionProvider(), $services->getObjectCacheFactory(), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getConnectionProvider() ->getPrimaryDatabase(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageMarker'=> static function(MediaWikiServices $services):TranslatablePageMarker { return new TranslatablePageMarker($services->getConnectionProvider(), $services->getJobQueueGroup(), $services->getLinkRenderer(), MessageGroups::singleton(), $services->get( 'Translate:MessageIndex'), $services->getTitleFormatter(), $services->getTitleParser(), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:TranslatablePageStateStore'), $services->get( 'Translate:TranslationUnitStoreFactory'), $services->get( 'Translate:MessageGroupMetadata'), $services->getWikiPageFactory(), $services->get( 'Translate:TranslatablePageView'), $services->get( 'Translate:MessageGroupSubscription'), $services->getFormatterFactory());}, 'Translate:TranslatablePageMessageGroupFactory'=> static function(MediaWikiServices $services):TranslatablePageMessageGroupFactory { return new TranslatablePageMessageGroupFactory(new ServiceOptions(TranslatablePageMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStateStore'=> static function(MediaWikiServices $services):TranslatablePageStateStore { return new TranslatablePageStateStore($services->get( 'Translate:PersistentCache'), $services->getPageStore());}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), $services->get( 'Translate:RevTagStore'), $services->getConnectionProvider(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:TranslatablePageView'=> static function(MediaWikiServices $services):TranslatablePageView { return new TranslatablePageView($services->getConnectionProvider(), $services->get( 'Translate:TranslatablePageStateStore'), new ServiceOptions(TranslatablePageView::SERVICE_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslateSandbox'=> static function(MediaWikiServices $services):TranslateSandbox { return new TranslateSandbox($services->getUserFactory(), $services->getConnectionProvider(), $services->getPermissionManager(), $services->getAuthManager(), $services->getUserGroupManager(), $services->getActorStore(), $services->getUserOptionsManager(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), new ServiceOptions(TranslateSandbox::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { return new TranslationStashStorage( $services->getConnectionProvider() ->getPrimaryDatabase());}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory(), $services->getConnectionProvider());}, 'Translate:TranslationUnitStoreFactory'=> static function(MediaWikiServices $services):TranslationUnitStoreFactory { return new TranslationUnitStoreFactory( $services->getDBLoadBalancer());}, 'Translate:TranslatorActivity'=> static function(MediaWikiServices $services):TranslatorActivity { $query=new TranslatorActivityQuery($services->getMainConfig(), $services->getDBLoadBalancer());return new TranslatorActivity($services->getMainObjectStash(), $query, $services->getJobQueueGroup());}, 'Translate:TtmServerFactory'=> static function(MediaWikiServices $services):TtmServerFactory { $config=$services->getMainConfig();$default=$config->get( 'TranslateTranslationDefaultService');if( $default===false) { $default=null;} return new TtmServerFactory( $config->get( 'TranslateTranslationServices'), $default);}]
@phpcs-require-sorted-array