Файловый менеджер - Редактировать - /var/www/xthruster/html/wp-content/uploads/flags/MO.tar
Назад
Plural.php 0000644 00000004044 14721711435 0006523 0 ustar 00 <?php namespace WPML\ST\MO; class Plural implements \IWPML_Backend_Action, \IWPML_Frontend_Action { public function add_hooks() { add_filter( 'ngettext', [ $this, 'handle_plural' ], 9, 5 ); add_filter( 'ngettext_with_context', [ $this, 'handle_plural_with_context' ], 9, 6 ); } /** * @param string $translation Translated text. * @param string $single The text to be used if the number is singular. * @param string $plural The text to be used if the number is plural. * @param string $number The number to compare against to use either the singular or plural form. * @param string $domain Text domain. Unique identifier for retrieving translated strings. * * @return string */ public function handle_plural( $translation, $single, $plural, $number, $domain ) { return $this->get_translation( $translation, $single, $plural, $number, function ( $original ) use ( $domain ) { return __( $original, $domain ); } ); } /** * @param string $translation Translated text. * @param string $single The text to be used if the number is singular. * @param string $plural The text to be used if the number is plural. * @param string $number The number to compare against to use either the singular or plural form. * @param string $context Context information for the translators. * @param string $domain Text domain. Unique identifier for retrieving translated strings. * * @return string */ public function handle_plural_with_context( $translation, $single, $plural, $number, $context, $domain ) { return $this->get_translation( $translation, $single, $plural, $number, function ( $original ) use ( $domain, $context ) { return _x( $original, $context, $domain ); } ); } private function get_translation( $translation, $single, $plural, $number, $callback ) { $original = (int) $number === 1 ? $single : $plural; $possible_translation = $callback( $original ); if ( $possible_translation !== $original ) { return $possible_translation; } return $translation; } } Notice/RegenerationInProgressNotice.php 0000644 00000001174 14721711435 0014306 0 ustar 00 <?php namespace WPML\ST\MO\Notice; class RegenerationInProgressNotice extends \WPML_Notice { const ID = 'mo-files-regeneration'; const GROUP = 'mo-files'; public function __construct() { $text = "WPML is updating the .mo files with the translation for strings. This will take a few more moments. During this process, translation for strings is not displaying on the front-end. You can refresh this page in a minute to see if it's done."; $text = __( $text, 'wpml-string-translation' ); parent::__construct( self::ID, $text, self::GROUP ); $this->set_dismissible( false ); $this->set_css_classes( 'warning' ); } } File/Manager.php 0000644 00000001701 14721711435 0007512 0 ustar 00 <?php namespace WPML\ST\MO\File; use GlobIterator; use WPML\Collect\Support\Collection; use WPML\ST\TranslationFile\Domains; use WPML\ST\TranslationFile\StringsRetrieve; use WPML_Language_Records; class Manager extends \WPML\ST\TranslationFile\Manager { public function __construct( StringsRetrieve $strings, Builder $builder, \WP_Filesystem_Direct $filesystem, WPML_Language_Records $language_records, Domains $domains ) { parent::__construct( $strings, $builder, $filesystem, $language_records, $domains ); } /** * @return string */ protected function getFileExtension() { return 'mo'; } /** * @return bool */ public function isPartialFile() { return true; } /** * @return Collection */ protected function getDomains() { return $this->domains->getMODomains(); } /** * @return bool */ public static function hasFiles() { return (bool) ( new GlobIterator( self::getSubdir() . '/*.mo' ) )->count(); } } File/MOFactory.php 0000644 00000000217 14721711435 0010004 0 ustar 00 <?php namespace WPML\ST\MO\File; class MOFactory { /** * @return \MO */ public function createNewInstance() { return new \MO(); } } File/Generator.php 0000644 00000003455 14721711435 0010076 0 ustar 00 <?php namespace WPML\ST\MO\File; use WPML\Collect\Support\Collection; use WPML\ST\TranslateWpmlString; use WPML\ST\TranslationFile\StringEntity; use function wpml_collect; class Generator { /** @var MOFactory */ private $moFactory; public function __construct( MOFactory $moFactory ) { $this->moFactory = $moFactory; } /** * @param StringEntity[] $entries * * @return string */ public function getContent( array $entries ) { $mo = $this->moFactory->createNewInstance(); wpml_collect( $entries ) ->reduce( [ $this, 'createMOFormatEntities' ], wpml_collect( [] ) ) ->filter( function( array $entry ) { return ! empty($entry['singular']); } ) ->each( [ $mo, 'add_entry' ] ); $mem_file = fopen( 'php://memory', 'r+' ); if ( $mem_file === false ) { return ''; } $mo->export_to_file_handle( $mem_file ); rewind( $mem_file ); $mo_content = stream_get_contents( $mem_file ); fclose( $mem_file ); return $mo_content; } /** * @param Collection $carry * @param StringEntity $entry * * @return Collection */ public function createMOFormatEntities( $carry, StringEntity $entry ) { $carry->push( $this->mapStringEntityToMOFormatUsing( $entry, 'original' ) ); if ( TranslateWpmlString::canTranslateWithMO( $entry->get_original(), $entry->get_name() ) ) { $carry->push( $this->mapStringEntityToMOFormatUsing( $entry, 'name' ) ); } return $carry; } /** * @param StringEntity $entry * @param string $singularField * * @return array */ private function mapStringEntityToMOFormatUsing( StringEntity $entry, $singularField ) { return [ 'singular' => $entry->{'get_' . $singularField}(), 'translations' => $entry->get_translations(), 'context' => $entry->get_context(), 'plural' => $entry->get_original_plural(), ]; } } File/FailureHooks.php 0000644 00000007310 14721711435 0010535 0 ustar 00 <?php namespace WPML\ST\MO\File; use WP_Filesystem_Direct; use WPML\ST\MO\Generate\Process\Status; use WPML\ST\MO\Generate\Process\SingleSiteProcess; use WPML\ST\MO\Notice\RegenerationInProgressNotice; use function wpml_get_admin_notices; class FailureHooks implements \IWPML_Backend_Action { use makeDir; const NOTICE_GROUP = 'mo-failure'; const NOTICE_ID_MISSING_FOLDER = 'missing-folder'; /** @var Status */ private $status; /** @var SingleSiteProcess $singleProcess */ private $singleProcess; public function __construct( WP_Filesystem_Direct $filesystem, Status $status, SingleSiteProcess $singleProcess ) { $this->filesystem = $filesystem; $this->status = $status; $this->singleProcess = $singleProcess; } public function add_hooks() { add_action( 'admin_init', [ $this, 'checkDirectories' ] ); } public function checkDirectories() { if ( $this->isDirectoryMissing( WP_LANG_DIR ) ) { $this->resetRegenerateStatus(); $this->displayMissingFolderNotice( WP_LANG_DIR ); return; } if ( $this->isDirectoryMissing( self::getSubdir() ) ) { $this->resetRegenerateStatus(); if ( ! $this->maybeCreateSubdir() ) { $this->displayMissingFolderNotice( self::getSubdir() ); return; } } $notices = wpml_get_admin_notices(); $notices->remove_notice( self::NOTICE_GROUP, self::NOTICE_ID_MISSING_FOLDER ); if ( ! $this->status->isComplete() ) { $this->displayRegenerateInProgressNotice(); $this->singleProcess->runPage(); } if ( $this->status->isComplete() ) { wpml_get_admin_notices()->remove_notice( RegenerationInProgressNotice::GROUP, RegenerationInProgressNotice::ID ); } } /** * @param string $dir */ public function displayMissingFolderNotice( $dir ) { $notices = wpml_get_admin_notices(); $notice = $notices->get_new_notice( self::NOTICE_ID_MISSING_FOLDER, self::missingFolderNoticeContent( $dir ), self::NOTICE_GROUP ); $notice->set_css_classes( 'error' ); $notices->add_notice( $notice ); } /** * @param string $dir * * @return string */ public static function missingFolderNoticeContent( $dir ) { $text = '<p>' . esc_html__( 'WPML String Translation is attempting to write .mo files with translations to folder:', 'wpml-string-translation' ) . '<br/>' . str_replace( '\\', '/', $dir ) . '</p>'; $text .= '<p>' . esc_html__( 'This folder appears to be not writable. This is blocking translation for strings from appearing on the site.', 'wpml-string-translation' ) . '</p>'; $text .= '<p>' . esc_html__( 'To resolve this, please contact your hosting company and request that they make that folder writable.', 'wpml-string-translation' ) . '</p>'; $url = 'https://wpml.org/faq/cannot-write-mo-files/?utm_source=plugin&utm_medium=gui&utm_campaign=wpmlst'; $link = '<a href="' . $url . '" target="_blank" rel="noreferrer noopener" >' . esc_html__( "WPML's documentation on troubleshooting .mo files generation.", 'wpml-string-translation' ) . '</a>'; $text .= '<p>' . sprintf( esc_html__( 'For more details, see %s.', 'wpml-string-translation' ), $link ) . '</p>'; return $text; } private function displayRegenerateInProgressNotice() { $notices = wpml_get_admin_notices(); $notices->add_notice( new RegenerationInProgressNotice() ); } /** * @return string */ public static function getSubdir() { return WP_LANG_DIR . '/' . \WPML\ST\TranslationFile\Manager::SUB_DIRECTORY; } /** * @param string $dir * * @return bool */ private function isDirectoryMissing( $dir ) { return ! $this->filesystem->is_writable( $dir ); } private function resetRegenerateStatus() { $this->status->markIncomplete(); } } File/makeDir.php 0000644 00000001175 14721711435 0007521 0 ustar 00 <?php namespace WPML\ST\MO\File; trait makeDir { /** * @var \WP_Filesystem_Direct */ protected $filesystem; /** @return bool */ public function maybeCreateSubdir() { $subdir = $this->getSubdir(); if ( $this->filesystem->is_dir( $subdir ) && $this->filesystem->is_writable( $subdir ) ) { return true; } $chmod = defined( 'FS_CHMOD_DIR' ) ? FS_CHMOD_DIR : 0755; return $this->filesystem->mkdir( $subdir, $chmod ); } /** * This declaration throws a "Strict standards" warning in PHP 5.6. * @todo: Remove the comment when we drop support for PHP 5.6. */ //abstract public static function getSubdir(); } File/FailureHooksFactory.php 0000644 00000002044 14721711435 0012064 0 ustar 00 <?php namespace WPML\ST\MO\File; use SitePress; use WPML\ST\MO\Generate\Process\ProcessFactory; use function WPML\Container\make; use WPML\ST\MO\Scan\UI\Factory as UiFactory; class FailureHooksFactory implements \IWPML_Backend_Action_Loader { /** * @return FailureHooks|null * @throws \WPML\Auryn\InjectionException */ public function create() { /** @var SitePress $sitepress */ global $sitepress; if ( $sitepress->is_setup_complete() && $this->hasRanPreGenerateViaUi() ) { $inBackground = true; return make( FailureHooks::class, [ ':status' => ProcessFactory::createStatus( $inBackground ), ':singleProcess' => ProcessFactory::createSingle( $inBackground ), ] ); } return null; } /** * @return bool * @throws \WPML\Auryn\InjectionException */ private function hasRanPreGenerateViaUi() { $uiPreGenerateStatus = ProcessFactory::createStatus( false ); return $uiPreGenerateStatus->isComplete() || UiFactory::isDismissed() || ! ProcessFactory::createSingle()->getPagesCount(); } } File/ManagerFactory.php 0000644 00000000432 14721711435 0011042 0 ustar 00 <?php namespace WPML\ST\MO\File; use function WPML\Container\make; class ManagerFactory { /** * @return Manager * @throws \WPML\Auryn\InjectionException */ public static function create() { return make( Manager::class, [ ':builder' => make( Builder::class ) ] ); } } File/Builder.php 0000644 00000000673 14721711435 0007535 0 ustar 00 <?php namespace WPML\ST\MO\File; use WPML\ST\TranslationFile\StringEntity; class Builder extends \WPML\ST\TranslationFile\Builder { /** @var Generator */ private $generator; public function __construct( Generator $generator ) { $this->generator = $generator; } /** * @param StringEntity[] $strings * @return string */ public function get_content( array $strings ) { return $this->generator->getContent( $strings ); } } Generate/StringsRetrieveMOOriginals.php 0000644 00000000542 14721711435 0014260 0 ustar 00 <?php namespace WPML\ST\MO\Generate; use WPML\ST\TranslationFile\StringsRetrieve; class StringsRetrieveMOOriginals extends StringsRetrieve { /** * @param array $row_data * * @return string|null */ public static function parseTranslation( array $row_data ) { return ! empty( $row_data['mo_string'] ) ? $row_data['mo_string'] : null; } } Generate/DomainsAndLanguagesRepository.php 0000644 00000003266 14721711435 0014767 0 ustar 00 <?php namespace WPML\ST\MO\Generate; use wpdb; use WPML\Collect\Support\Collection; use function WPML\Container\make; use WPML\ST\TranslationFile\Domains; use function wpml_collect; use WPML_Locale; class DomainsAndLanguagesRepository { /** @var wpdb */ private $wpdb; /** @var Domains */ private $domains; /** @var WPML_Locale */ private $locale; /** * @param wpdb $wpdb * @param Domains $domains * @param WPML_Locale $wp_locale */ public function __construct( wpdb $wpdb, Domains $domains, WPML_Locale $wp_locale ) { $this->wpdb = $wpdb; $this->domains = $domains; $this->locale = $wp_locale; } /** * @return Collection */ public function get() { return $this->getAllDomains()->map( function ( $row ) { return (object) [ 'domain' => $row->domain, 'locale' => $this->locale->get_locale( $row->languageCode ) ]; } )->values(); } /** * @return Collection */ private function getAllDomains() { $moDomains = $this->domains->getMODomains()->toArray(); if ( ! $moDomains ) { return wpml_collect( [] ); } $sql = " SELECT DISTINCT (BINARY s.context) as `domain`, st.language as `languageCode` FROM {$this->wpdb->prefix}icl_string_translations st INNER JOIN {$this->wpdb->prefix}icl_strings s ON s.id = st.string_id WHERE st.`status` = 10 AND ( st.`value` != st.mo_string OR st.mo_string IS NULL) AND s.context IN(" . wpml_prepare_in( $moDomains ) . ") "; $result = $this->wpdb->get_results( $sql ); return wpml_collect( $result ); } /** * @return bool */ public static function hasTranslationFilesTable() { return make( \WPML_Upgrade_Schema::class )->does_table_exist( 'icl_mo_files_domains' ); } } Generate/GenerateMissingMOFile.php 0000644 00000004717 14721711435 0013145 0 ustar 00 <?php namespace WPML\ST\MO\Generate; use WPML\ST\MO\File\Builder; use WPML\ST\MO\File\makeDir; use WPML\ST\MO\Hooks\LoadMissingMOFiles; use WPML\ST\TranslationFile\StringsRetrieve; use WPML\WP\OptionManager; use function WPML\Container\make; class MissingMOFile { use makeDir; const OPTION_GROUP = 'ST-MO'; const OPTION_NAME = 'missing-mo-processed'; /** * @var Builder */ private $builder; /** * @var StringsRetrieve */ private $stringsRetrieve; /** * @var \WPML_Language_Records */ private $languageRecords; /** * @var OptionManager */ private $optionManager; public function __construct( \WP_Filesystem_Direct $filesystem, Builder $builder, StringsRetrieveMOOriginals $stringsRetrieve, \WPML_Language_Records $languageRecords, OptionManager $optionManager ) { $this->filesystem = $filesystem; $this->builder = $builder; $this->stringsRetrieve = $stringsRetrieve; $this->languageRecords = $languageRecords; $this->optionManager = $optionManager; } /** * @param string $generateMoPath * @param string $domain */ public function run( $generateMoPath, $domain ) { $processed = $this->getProcessed(); if ( ! $processed->contains( basename( $generateMoPath ) ) && $this->maybeCreateSubdir() ) { $locale = make( \WPML_ST_Translations_File_Locale::class )->get( $generateMoPath, $domain ); $strings = $this->stringsRetrieve->get( $domain, $this->languageRecords->get_language_code( $locale ), false ); if ( ! empty( $strings ) ) { $fileContents = $this->builder ->set_language( $locale ) ->get_content( $strings ); $chmod = defined( 'FS_CHMOD_FILE' ) ? FS_CHMOD_FILE : 0644; $this->filesystem->put_contents( $generateMoPath, $fileContents, $chmod ); do_action( 'wpml_st_translation_file_updated', $generateMoPath, $domain, $locale ); } $processed->push( $generateMoPath ); $this->optionManager->set( self::OPTION_GROUP, self::OPTION_NAME, $processed->toArray() ); } } public function isNotProcessed( $generateMoPath ) { return ! $this->getProcessed()->contains( basename($generateMoPath) ); } public static function getSubdir() { return WP_LANG_DIR . LoadMissingMOFiles::MISSING_MO_FILES_DIR; } /** * @return \WPML\Collect\Support\Collection */ private function getProcessed() { return wpml_collect( $this->optionManager->get( self::OPTION_GROUP, self::OPTION_NAME, [] ) ) ->map( function ( $path ) { return basename( $path ); } ); } } Generate/MultiSite/Condition.php 0000644 00000001012 14721711435 0012653 0 ustar 00 <?php namespace WPML\ST\MO\Generate\MultiSite; class Condition { /** * @return bool */ public function shouldRunWithAllSites() { return is_multisite() && ( $this->hasPostBodyParam() || is_super_admin() || defined( 'WP_CLI' ) ); } private function hasPostBodyParam() { $request_body = file_get_contents( 'php://input' ); if ( ! $request_body ) { return false; } $data = (array) json_decode( $request_body ); return isset( $data['runForAllSites'] ) && $data['runForAllSites']; } } Generate/MultiSite/Executor.php 0000644 00000001660 14721711435 0012534 0 ustar 00 <?php namespace WPML\ST\MO\Generate\MultiSite; class Executor { const MAIN_SITE_ID = 1; /** * @param callable $callback * * @return \WPML\Collect\Support\Collection */ public function withEach( $callback ) { $applyCallback = function( $siteId ) use ( $callback ) { switch_to_blog( $siteId ); return [ $siteId, $callback() ]; }; $initialBlogId = get_current_blog_id(); $result = $this->getSiteIds()->map( $applyCallback ); switch_to_blog( $initialBlogId ); return $result; } /** * @return \WPML\Collect\Support\Collection */ public function getSiteIds() { return \wpml_collect( get_sites( [ 'number' => PHP_INT_MAX ] ) )->pluck( 'id' ); } /** * @param int $siteId * @param callable $callback * * @return mixed */ public function executeWith( $siteId, callable $callback ) { switch_to_blog( $siteId ); $result = $callback(); restore_current_blog(); return $result; } } Generate/Process/ProcessFactory.php 0000644 00000003530 14721711435 0013401 0 ustar 00 <?php namespace WPML\ST\MO\Generate\Process; use WPML\ST\MO\File\ManagerFactory; use WPML\ST\MO\Generate\MultiSite\Condition; use WPML\Utils\Pager; use function WPML\Container\make; class ProcessFactory { const FILES_PAGER = 'wpml-st-mo-generate-files-pager'; const FILES_PAGE_SIZE = 20; const SITES_PAGER = 'wpml-st-mo-generate-sites-pager'; /** @var Condition */ private $multiSiteCondition; /** * @param Condition $multiSiteCondition */ public function __construct( Condition $multiSiteCondition = null ) { $this->multiSiteCondition = $multiSiteCondition ?: new Condition(); } /** * @return Process * @throws \WPML\Auryn\InjectionException */ public function create() { $singleSiteProcess = self::createSingle(); if ( $this->multiSiteCondition->shouldRunWithAllSites() ) { return make( MultiSiteProcess::class, [ ':singleSiteProcess' => $singleSiteProcess, ':pager' => new Pager( self::SITES_PAGER, 1 ) ] ); } else { return $singleSiteProcess; } } /** * @param bool $isBackgroundProcess * * @return SingleSiteProcess * @throws \WPML\Auryn\InjectionException */ public static function createSingle( $isBackgroundProcess = false ) { return make( SingleSiteProcess::class, [ ':pager' => new Pager( self::FILES_PAGER, self::FILES_PAGE_SIZE ), ':manager' => ManagerFactory::create(), ':migrateAdminTexts' => \WPML_Admin_Texts::get_migrator(), ':status' => self::createStatus( $isBackgroundProcess ), ] ); } /** * @param bool $isBackgroundProcess * * @return mixed|\Mockery\MockInterface|Status * @throws \WPML\Auryn\InjectionException */ public static function createStatus( $isBackgroundProcess = false ) { return make( Status::class, [ ':optionPrefix' => $isBackgroundProcess ? Status::class . '_background' : null ] ); } } Generate/Process/SingleSiteProcess.php 0000644 00000004655 14721711435 0014051 0 ustar 00 <?php namespace WPML\ST\MO\Generate\Process; use WPML\ST\MO\File\Manager; use WPML\ST\MO\Generate\DomainsAndLanguagesRepository; use WPML\Utils\Pager; class SingleSiteProcess implements Process { CONST TIMEOUT = 5; /** @var DomainsAndLanguagesRepository */ private $domainsAndLanguagesRepository; /** @var Manager */ private $manager; /** @var Status */ private $status; /** @var Pager */ private $pager; /** @var callable */ private $migrateAdminTexts; /** * @param DomainsAndLanguagesRepository $domainsAndLanguagesRepository * @param Manager $manager * @param Status $status * @param Pager $pager * @param callable $migrateAdminTexts */ public function __construct( DomainsAndLanguagesRepository $domainsAndLanguagesRepository, Manager $manager, Status $status, Pager $pager, callable $migrateAdminTexts ) { $this->domainsAndLanguagesRepository = $domainsAndLanguagesRepository; $this->manager = $manager; $this->status = $status; $this->pager = $pager; $this->migrateAdminTexts = $migrateAdminTexts; } public function runAll() { call_user_func( $this->migrateAdminTexts ); $this->getDomainsAndLanguages()->each( function ( $row ) { $this->manager->add( $row->domain, $row->locale ); } ); $this->status->markComplete(); } /** * @return int Remaining */ public function runPage() { if ( $this->pager->getProcessedCount() === 0 ) { call_user_func( $this->migrateAdminTexts ); } $domains = $this->getDomainsAndLanguages();; $remaining = $this->pager->iterate( $domains, function ( $row ) { $this->manager->add( $row->domain, $row->locale ); return true; }, self::TIMEOUT ); if ( $remaining === 0 ) { $this->status->markComplete(); } return $remaining; } public function getPagesCount() { if ( $this->status->isComplete() ) { return 0; } $domains = $this->getDomainsAndLanguages(); if ( $domains->count() === 0 ) { $this->status->markComplete(); } return $domains->count(); } private function getDomainsAndLanguages() { return DomainsAndLanguagesRepository::hasTranslationFilesTable() ? $this->domainsAndLanguagesRepository->get() : wpml_collect(); } /** * @return bool */ public function isCompleted() { return $this->getPagesCount() === 0; } } Generate/Process/Status.php 0000644 00000003074 14721711435 0011721 0 ustar 00 <?php namespace WPML\ST\MO\Generate\Process; class Status { /** @var \SitePress */ private $sitepress; /** @var string */ private $optionPrefix; /** * @param \SitePress $sitepress * @param string|null $optionPrefix */ public function __construct( \SitePress $sitepress, $optionPrefix = null ) { $this->sitepress = $sitepress; $this->optionPrefix = $optionPrefix ?: self::class; } /** * @param bool $allSites */ public function markComplete( $allSites = false ) { $settings = $this->sitepress->get_setting( 'st', [] ); $settings[ $this->getOptionName( $allSites ) ] = true; $this->sitepress->set_setting( 'st', $settings, true ); } /** * @param bool $allSites */ public function markIncomplete( $allSites = false ) { $settings = $this->sitepress->get_setting( 'st', [] ); unset( $settings[ $this->getOptionName( $allSites ) ] ); $this->sitepress->set_setting( 'st', $settings, true ); } public function markIncompleteForAll() { $this->markIncomplete( true ); } /** * @return bool */ public function isComplete() { $st_settings = $this->sitepress->get_setting( 'st', [] ); return isset( $st_settings[ $this->getOptionName( false ) ] ); } /** * @return bool */ public function isCompleteForAllSites() { $st_settings = $this->sitepress->get_setting( 'st', [] ); return isset( $st_settings[ $this->getOptionName( true ) ] ); } private function getOptionName( $allSites ) { return $allSites ? $this->optionPrefix . '_has_run_all_sites' : $this->optionPrefix . '_has_run'; } } Generate/Process/MultiSiteProcess.php 0000644 00000004611 14721711435 0013712 0 ustar 00 <?php namespace WPML\ST\MO\Generate\Process; use WPML\Utils\Pager; use WPML\ST\MO\Generate\MultiSite\Executor; class MultiSiteProcess implements Process { /** @var Executor */ private $multiSiteExecutor; /** @var SingleSiteProcess */ private $singleSiteProcess; /** @var Status */ private $status; /** @var Pager */ private $pager; /** @var SubSiteValidator */ private $subSiteValidator; /** * @param Executor $multiSiteExecutor * @param SingleSiteProcess $singleSiteProcess * @param Status $status * @param Pager $pager * @param SubSiteValidator $subSiteValidator */ public function __construct( Executor $multiSiteExecutor, SingleSiteProcess $singleSiteProcess, Status $status, Pager $pager, SubSiteValidator $subSiteValidator ) { $this->multiSiteExecutor = $multiSiteExecutor; $this->singleSiteProcess = $singleSiteProcess; $this->status = $status; $this->pager = $pager; $this->subSiteValidator = $subSiteValidator; } public function runAll() { $this->multiSiteExecutor->withEach( $this->runIfSetupComplete( [ $this->singleSiteProcess, 'runAll' ] ) ); $this->status->markComplete( true ); } /** * @return int Is completed */ public function runPage() { $remaining = $this->pager->iterate( $this->multiSiteExecutor->getSiteIds(), function ( $siteId ) { return $this->multiSiteExecutor->executeWith( $siteId, $this->runIfSetupComplete( function () { // no more remaining pages which means that process is done return $this->singleSiteProcess->runPage() === 0; } ) ); } ); if ( $remaining === 0 ) { $this->multiSiteExecutor->executeWith( Executor::MAIN_SITE_ID, function () { $this->status->markComplete( true ); } ); } return $remaining; } /** * @return int */ public function getPagesCount() { $isCompletedForAllSites = $this->multiSiteExecutor->executeWith( Executor::MAIN_SITE_ID, [ $this->status, 'isCompleteForAllSites' ] ); if ( $isCompletedForAllSites ) { return 0; } return $this->multiSiteExecutor->getSiteIds()->count(); } /** * @return bool */ public function isCompleted() { return $this->getPagesCount() === 0; } private function runIfSetupComplete( $callback ) { return function () use ( $callback ) { if ( $this->subSiteValidator->isValid() ) { return $callback(); } return true; }; } } Generate/Process/Process.php 0000644 00000000433 14721711435 0012050 0 ustar 00 <?php namespace WPML\ST\MO\Generate\Process; interface Process { public function runAll(); /** * @return int Remaining */ public function runPage(); /** * @return int */ public function getPagesCount(); /** * @return bool */ public function isCompleted(); } Generate/Process/SubSiteValidator.php 0000644 00000000664 14721711435 0013664 0 ustar 00 <?php namespace WPML\ST\MO\Generate\Process; use function WPML\Container\make; class SubSiteValidator { /** * @return bool */ public function isValid() { global $sitepress; return $sitepress->is_setup_complete() && $this->hasTranslationFilesTable(); } /** * @return bool */ private function hasTranslationFilesTable() { return make( \WPML_Upgrade_Schema::class )->does_table_exist( 'icl_mo_files_domains' ); } } Hooks/PreloadThemeMoFile.php 0000644 00000003750 14721711435 0012017 0 ustar 00 <?php namespace WPML\ST\MO\Hooks; use WPML\Collect\Support\Collection; use WPML\ST\Gettext\AutoRegisterSettings; use function WPML\Container\make; class PreloadThemeMoFile implements \IWPML_Action { const SETTING_KEY = 'theme_localization_load_textdomain'; const SETTING_DISABLED = 0; const SETTING_ENABLED = 1; const SETTING_ENABLED_FOR_LOAD_TEXT_DOMAIN = 2; /** @var \SitePress */ private $sitepress; /** @var \wpdb */ private $wpdb; public function __construct( \SitePress $sitepress, \wpdb $wpdb ) { $this->sitepress = $sitepress; $this->wpdb = $wpdb; } public function add_hooks() { $domainsSetting = $this->sitepress->get_setting( 'gettext_theme_domain_name' ); $domains = empty( $domainsSetting ) ? [] : explode( ',', $domainsSetting ); $domains = \wpml_collect( array_map( 'trim', $domains ) ); $loadTextDomainSetting = (int) $this->sitepress->get_setting( static::SETTING_KEY ); $isEnabled = $loadTextDomainSetting === static::SETTING_ENABLED; if ( $loadTextDomainSetting === static::SETTING_ENABLED_FOR_LOAD_TEXT_DOMAIN ) { /** @var AutoRegisterSettings $autoStrings */ $autoStrings = make( AutoRegisterSettings::class ); $isEnabled = $autoStrings->isEnabled(); } if ( $isEnabled && $domains->count() ) { $this->getMOFilesByDomainsAndLocale( $domains, get_locale() )->map( function ( $fileResult ) { load_textdomain( $fileResult->domain, $fileResult->file_path ); } ); } } /** * @param Collection<string> $domains * @param string $locale * * @return Collection */ private function getMOFilesByDomainsAndLocale( $domains, $locale ) { $domainsClause = wpml_prepare_in( $domains->toArray(), '%s' ); $sql = " SELECT file_path, domain FROM {$this->wpdb->prefix}icl_mo_files_domains WHERE domain IN ({$domainsClause}) AND file_path REGEXP %s "; /** @var string $sql */ $sql = $this->wpdb->prepare( $sql, '((\\/|-)' . $locale . '(\\.|-))+' ); return \wpml_collect( $this->wpdb->get_results( $sql ) ); } } Hooks/LoadTextDomain.php 0000644 00000007440 14721711435 0011226 0 ustar 00 <?php namespace WPML\ST\MO\Hooks; use WPML\ST\MO\File\Manager; use WPML\ST\MO\LoadedMODictionary; use WPML_ST_Translations_File_Locale; use function WPML\FP\partial; class LoadTextDomain implements \IWPML_Action { const PRIORITY_OVERRIDE = 10; /** @var Manager $file_manager */ private $file_manager; /** @var WPML_ST_Translations_File_Locale $file_locale */ private $file_locale; /** @var LoadedMODictionary $loaded_mo_dictionary */ private $loaded_mo_dictionary; /** @var array $loaded_domains */ private $loaded_domains = []; public function __construct( Manager $file_manager, WPML_ST_Translations_File_Locale $file_locale, LoadedMODictionary $loaded_mo_dictionary ) { $this->file_manager = $file_manager; $this->file_locale = $file_locale; $this->loaded_mo_dictionary = $loaded_mo_dictionary; } public function add_hooks() { $this->reloadAlreadyLoadedMOFiles(); add_filter( 'override_load_textdomain', [ $this, 'overrideLoadTextDomain' ], 10, 3 ); add_filter( 'override_unload_textdomain', [ $this, 'overrideUnloadTextDomain' ], 10, 2 ); add_action( 'wpml_language_has_switched', [ $this, 'languageHasSwitched' ] ); } /** * When a MO file is loaded, we override the process to load * the custom MO file before. * * That way, the custom MO file will be merged into the subsequent * native MO files and the custom MO translations will always * overwrite the native ones. * * This gives us the ability to build partial custom MO files * with only the modified translations. * * @param bool $override Whether to override the .mo file loading. Default false. * @param string $domain Text domain. Unique identifier for retrieving translated strings. * @param string $mofile Path to the MO file. * * @return bool */ public function overrideLoadTextDomain( $override, $domain, $mofile ) { if ( ! $mofile ) { return $override; } if ( ! $this->isCustomMOLoaded( $domain ) ) { remove_filter( 'override_load_textdomain', [ $this, 'overrideLoadTextDomain' ], 10 ); $locale = $this->file_locale->get( $mofile, $domain ); $this->loadCustomMOFile( $domain, $mofile, $locale ); add_filter( 'override_load_textdomain', [ $this, 'overrideLoadTextDomain' ], 10, 3 ); } $this->loaded_mo_dictionary->addFile( $domain, $mofile ); return $override; } /** * @param bool $override * @param string $domain * * @return bool */ public function overrideUnloadTextDomain( $override, $domain ) { $key = array_search( $domain, $this->loaded_domains ); if ( false !== $key ) { unset( $this->loaded_domains[ $key ] ); } return $override; } /** * @param string $domain * * @return bool */ private function isCustomMOLoaded( $domain ) { return in_array( $domain, $this->loaded_domains, true ); } private function loadCustomMOFile( $domain, $mofile, $locale ) { $wpml_mofile = $this->file_manager->get( $domain, $locale ); if ( $wpml_mofile && $wpml_mofile !== $mofile ) { load_textdomain( $domain, $wpml_mofile ); } $this->setCustomMOLoaded( $domain ); } private function reloadAlreadyLoadedMOFiles() { $this->loaded_mo_dictionary->getEntities()->each( function ( $entity ) { unload_textdomain( $entity->domain ); $locale = $this->file_locale->get( $entity->mofile, $entity->domain ); $this->loadCustomMOFile( $entity->domain, $entity->mofile, $locale ); if ( class_exists( '\WP_Translation_Controller' ) ) { // WP 6.5 - passing locale load_textdomain( $entity->domain, $entity->mofile, $locale ); } else { load_textdomain($entity->domain, $entity->mofile); } } ); } /** * @param string $domain */ private function setCustomMOLoaded( $domain ) { $this->loaded_domains[] = $domain; } public function languageHasSwitched() { $this->loaded_domains = []; } } Hooks/StringsLanguageChanged.php 0000644 00000002602 14721711435 0012714 0 ustar 00 <?php namespace WPML\ST\MO\Hooks; use WPML\FP\Fns; use WPML\FP\Lst; use WPML\FP\Obj; use WPML\ST\MO\File\Manager; use WPML\ST\MO\Generate\DomainsAndLanguagesRepository; use function WPML\FP\pipe; class StringsLanguageChanged implements \IWPML_Action { private $domainsAndLanguageRepository; private $manager; private $getDomainsByStringIds; /** * @param DomainsAndLanguagesRepository $domainsAndLanguageRepository * @param Manager $manager * @param callable $getDomainsByStringIds */ public function __construct( DomainsAndLanguagesRepository $domainsAndLanguageRepository, Manager $manager, callable $getDomainsByStringIds ) { $this->domainsAndLanguageRepository = $domainsAndLanguageRepository; $this->manager = $manager; $this->getDomainsByStringIds = $getDomainsByStringIds; } public function add_hooks() { add_action( 'wpml_st_language_of_strings_changed', [ $this, 'regenerateMOFiles' ] ); } public function regenerateMOFiles( array $strings ) { $stringDomains = call_user_func( $this->getDomainsByStringIds, $strings ); $this->domainsAndLanguageRepository ->get() ->filter( pipe( Obj::prop( 'domain' ), Lst::includes( Fns::__, $stringDomains ) ) ) ->each( function ( $domainLangPair ) { $this->manager->add( $domainLangPair->domain, $domainLangPair->locale ); } ); } } Hooks/DetectPrematurelyTranslatedStrings.php 0000644 00000006103 14721711435 0015403 0 ustar 00 <?php namespace WPML\ST\MO\Hooks; use WPML\ST\Gettext\Settings; class DetectPrematurelyTranslatedStrings implements \IWPML_Action { /** @var string[] */ private $domains = []; /** @var string[] */ private $preloadedDomains = []; /** @var \SitePress */ private $sitepress; /** @var Settings */ private $gettextHooksSettings; /** * @param \SitePress $sitepress */ public function __construct( \SitePress $sitepress, Settings $settings ) { $this->sitepress = $sitepress; $this->gettextHooksSettings = $settings; } /** * Init gettext hooks. */ public function add_hooks() { if ( $this->gettextHooksSettings->isAutoRegistrationEnabled() ) { $domains = $this->sitepress->get_setting( 'gettext_theme_domain_name' ); $this->preloadedDomains = array_filter( array_map( 'trim', explode( ',', $domains ) ) ); add_filter( 'gettext', [ $this, 'gettext_filter' ], 9, 3 ); add_filter( 'gettext_with_context', [ $this, 'gettext_with_context_filter' ], 1, 4 ); add_filter( 'ngettext', [ $this, 'ngettext_filter' ], 9, 5 ); add_filter( 'ngettext_with_context', [ $this, 'ngettext_with_context_filter' ], 9, 6 ); add_filter( 'override_load_textdomain', [ $this, 'registerDomainToPreloading' ], 10, 2 ); } } /** * @param string $translation * @param string $text * @param string|array $domain * * @return string */ public function gettext_filter( $translation, $text, $domain ) { $this->registerDomain( $domain ); return $translation; } /** * @param string $translation * @param string $text * @param string $context * @param string $domain * * @return string */ public function gettext_with_context_filter( $translation, $text, $context, $domain ) { $this->registerDomain( $domain ); return $translation; } /** * @param string $translation * @param string $single * @param string $plural * @param string $number * @param string|array $domain * * @return string */ public function ngettext_filter( $translation, $single, $plural, $number, $domain ) { $this->registerDomain( $domain ); return $translation; } /** * @param string $translation * @param string $single * @param string $plural * @param string $number * @param string $context * @param string $domain * * @return string * */ public function ngettext_with_context_filter( $translation, $single, $plural, $number, $context, $domain ) { $this->registerDomain( $domain ); return $translation; } private function registerDomain( $domain ) { if ( ! in_array( $domain, $this->preloadedDomains ) ) { $this->domains[ $domain ] = true; } } public function registerDomainToPreloading( $plugin_override, $domain ) { if ( array_key_exists( $domain, $this->domains ) && ! in_array( $domain, $this->preloadedDomains, true ) ) { $this->preloadedDomains[] = $domain; $this->sitepress->set_setting( 'gettext_theme_domain_name', implode( ',', array_unique( $this->preloadedDomains ) ) ); $this->sitepress->save_settings(); } return $plugin_override; } } Hooks/Sync.php 0000644 00000002172 14721711435 0007263 0 ustar 00 <?php namespace WPML\ST\MO\Hooks; use WPML\ST\TranslationFile\Sync\FileSync; class Sync implements \IWPML_Frontend_Action, \IWPML_Backend_Action, \IWPML_DIC_Action { /** @var FileSync */ private $fileSync; /** @var callable */ private $useFileSynchronization; public function __construct( FileSync $fileSync, callable $useFileSynchronization ) { $this->fileSync = $fileSync; $this->useFileSynchronization = $useFileSynchronization; } public function add_hooks() { if ( call_user_func( $this->useFileSynchronization ) ) { add_filter( 'override_load_textdomain', [ $this, 'syncCustomMoFileOnLoadTextDomain' ], LoadTextDomain::PRIORITY_OVERRIDE - 1, 3 ); } } public function syncFile( $domain, $moFile ) { if ( call_user_func( $this->useFileSynchronization ) ) { $this->fileSync->sync( $moFile, $domain ); } } /** * @param bool $override * @param string $domain * @param string $moFile * * @return bool */ public function syncCustomMoFileOnLoadTextDomain( $override, $domain, $moFile ) { $this->fileSync->sync( $moFile, $domain ); return $override; } } Hooks/CustomTextDomains.php 0000644 00000011201 14721711435 0011772 0 ustar 00 <?php namespace WPML\ST\MO\Hooks; use WPML\FP\Lst; use WPML\ST\MO\File\Manager; use WPML\ST\MO\JustInTime\MO; use WPML\ST\MO\LoadedMODictionary; use WPML\ST\Storage\StoragePerLanguageInterface; use WPML\ST\TranslationFile\Domains; use function WPML\FP\pipe; use function WPML\FP\spreadArgs; use WPML_Locale; class CustomTextDomains implements \IWPML_Action { const CACHE_ID = 'wpml-st-custom-mo-files'; const CACHE_ALL_LOCALES = 'locales'; const CACHE_KEY_DOMAINS = 'domains'; const CACHE_KEY_FILES = 'files'; /** @var Manager $manager */ private $manager; /** @var Domains $domains */ private $domains; /** @var LoadedMODictionary $loadedDictionary */ private $loadedDictionary; /** @var StoragePerLanguageInterface */ private $cache; /** @var WPML_Locale */ private $locale; /** @var callable */ private $syncMissingFile; /** @var string[] $loaded_custom_domains */ private $loaded_custom_domains = []; public function __construct( Manager $file_manager, Domains $domains, LoadedMODictionary $loadedDictionary, StoragePerLanguageInterface $cache, WPML_Locale $locale, callable $syncMissingFile = null ) { $this->manager = $file_manager; $this->domains = $domains; $this->loadedDictionary = $loadedDictionary; $this->cache = $cache; $this->locale = $locale; $this->syncMissingFile = $syncMissingFile ?: function () {}; // Flush cache when a custom MO file is written, removed or updated. add_action( 'wpml_st_translation_file_written', [ $this, 'clear_cache' ], 10, 0 ); add_action( 'wpml_st_translation_file_removed', [ $this, 'clear_cache' ], 10, 0 ); // The filename could be changed on update, that's why we need to clear the cache. add_action( 'wpml_st_translation_file_updated', [ $this, 'clear_cache' ], 10, 0 ); } public function clear_cache() { $locales = $this->cache->get( self::CACHE_ALL_LOCALES ); if ( ! is_array( $locales ) ) { // No cache. return; } // Clear cache for all locales, because the domains list will change // for all languages when a the first custom translation file is written // for a new domain (even if the other locales don't get that file). foreach ( $locales as $locale ) { $this->cache->delete( $locale ); } // Also flush the list of cached locales. $this->cache->delete( self::CACHE_ALL_LOCALES ); } public function add_hooks() { $this->init_custom_text_domains(); } public function init_custom_text_domains( $locale = null ) { $locale = $locale ?: get_locale(); $addJitMoToL10nGlobal = pipe( Lst::nth( 0 ), function ( $domain ) use ( $locale ) { // Following unset is important because WordPress is setting their // static $noop_translation variable by reference. Without unset it // would become our JustInTime/MO and is used for other domains. // @see wpmldev-2508 unset( $GLOBALS['l10n'][ $domain ] ); $this->loaded_custom_domains[] = $domain; $GLOBALS['l10n'][ $domain ] = new MO( $this->loadedDictionary, $locale, $domain ); } ); $getDomainPathTuple = function ( $domain ) use ( $locale ) { return [ $domain, $this->manager->getFilepath( $domain, $locale ) ]; }; $cache = $this->cache->get( $locale ); // Get domains. if ( isset( $cache[ self::CACHE_KEY_DOMAINS ] ) ) { // Cache hit. $domains = \wpml_collect( $cache[ self::CACHE_KEY_DOMAINS ] ); } else { // No cache for site domains. $cache_update_required = true; $domains = \wpml_collect( $this->domains->getCustomMODomains() ); } $files = $domains->map( $getDomainPathTuple ) ->each( spreadArgs( $this->syncMissingFile ) ) ->each( spreadArgs( [ $this->loadedDictionary, 'addFile' ] ) ); // Load local files. if ( isset( $cache[ self::CACHE_KEY_FILES ] ) ) { // Cache hit. $localeFiles = \wpml_collect( $cache[ self::CACHE_KEY_FILES ] ); } else { // No cache for this locale readable custom .mo files. $cache_update_required = true; $isReadableFile = function ( $domainAndFilePath ) { return is_readable( $domainAndFilePath[1] ); }; $localeFiles = $files->filter( $isReadableFile ); } if ( isset( $cache_update_required ) ) { $this->cache->save( $locale, [ self::CACHE_KEY_DOMAINS => $domains->toArray(), self::CACHE_KEY_FILES => $localeFiles->toArray(), ] ); $cache_locales = $this->cache->get( self::CACHE_ALL_LOCALES ); $cache_locales = is_array( $cache_locales ) ? $cache_locales : []; if ( ! in_array( $locale, $cache_locales, true ) ) { $cache_locales[] = $locale; $this->cache->save( self::CACHE_ALL_LOCALES, array_unique( $cache_locales ) ); } } $localeFiles->each( $addJitMoToL10nGlobal ); } } Hooks/Factory.php 0000644 00000003013 14721711435 0007751 0 ustar 00 <?php namespace WPML\ST\MO\Hooks; use IWPML_Action; use WPML\ST\DB\Mappers\DomainsRepository; use WPML\ST\MO\File\ManagerFactory; use WPML\ST\Storage\WpTransientPerLanguage; use WPML\ST\TranslationFile\Sync\FileSync; use WPML\ST\TranslationFile\UpdateHooksFactory; use WPML\ST\TranslationFile\Hooks; use function WPML\Container\make; class Factory implements \IWPML_Backend_Action_Loader, \IWPML_Frontend_Action_Loader { /** * Create hooks. * * @return IWPML_Action[] * @throws \WPML\Auryn\InjectionException Auryn Exception. */ public function create() { $manager = ManagerFactory::create(); $moFileSync = make( Sync::class, [ ':fileSync' => make( FileSync::class, [ ':manager' => ManagerFactory::create() ] ), ':useFileSynchronization' => [ Hooks::class, 'useFileSynchronization' ], ] ); return [ UpdateHooksFactory::create(), make( LoadTextDomain::class, [ ':file_manager' => $manager ] ), make( CustomTextDomains::class, [ ':file_manager' => $manager, ':cache' => make( WpTransientPerLanguage::class, [ ':id' => CustomTextDomains::CACHE_ID, ] ), ':syncMissingFile' => [ $moFileSync, 'syncFile' ], ] ), make( LanguageSwitch::class ), make( LoadMissingMOFiles::class ), make( PreloadThemeMoFile::class ), make( DetectPrematurelyTranslatedStrings::class ), $moFileSync, make( StringsLanguageChanged::class, [ ':manager' => $manager, ':getDomainsByStringIds' => DomainsRepository::getByStringIds(), ] ), ]; } } Hooks/LoadMissingMOFiles.php 0000644 00000012726 14721711435 0012005 0 ustar 00 <?php namespace WPML\ST\MO\Hooks; use WPML\Collect\Support\Collection; use WPML\ST\MO\Generate\MissingMOFile; use WPML\WP\OptionManager; use function WPML\Container\make; class LoadMissingMOFiles implements \IWPML_Action { const MISSING_MO_FILES_DIR = '/wpml/missing/'; const OPTION_GROUP = 'ST-MO'; const MISSING_MO_OPTION = 'missing-mo'; const TIMEOUT = 10; const WPML_VERSION_INTRODUCING_ST_MO_FLOW = '4.3.0'; /** * @var MissingMOFile */ private $generateMissingMoFile; /** * @var OptionManager */ private $optionManager; /** @var \WPML_ST_Translations_File_Dictionary_Storage_Table */ private $moFilesDictionary; public function __construct( MissingMOFile $generateMissingMoFile, OptionManager $optionManager, \WPML_ST_Translations_File_Dictionary_Storage_Table $moFilesDictionary ) { $this->generateMissingMoFile = $generateMissingMoFile; $this->optionManager = $optionManager; $this->moFilesDictionary = $moFilesDictionary; } public function add_hooks() { if ( $this->wasWpmlInstalledPriorToMoFlowChanges() ) { add_filter( 'load_textdomain_mofile', [ $this, 'recordMissing' ], 10, 2 ); if ( $this->isThemeAndLocalizationPage() ) { // By now this complete feature is probably only required to load // generated files, but for the rare case that someone updates // from < 4.3.0 and has missing files, the genration will still // be triggered on the Themen and Plugins localization page. add_action( 'shutdown', [ $this, 'generateMissing' ] ); } } } /** * @param string $mofile * @param string $domain * * @return string */ public function recordMissing( $mofile, $domain ) { if ( strpos( $mofile, WP_LANG_DIR . '/themes/' ) === 0 ) { return $mofile; } if ( strpos( $mofile, WP_LANG_DIR . '/plugins/' ) === 0 ) { return $mofile; } $missing = $this->getMissing(); if ( self::isReadable( $mofile ) ) { if ( $missing->has( $domain ) ) { $this->saveMissing( $missing->forget( $domain ) ); } return $mofile; } // Light check to see if a file was already generated. $generatedFile = $this->getGeneratedFileName( $mofile, $domain ); if ( self::isReadable( $generatedFile ) && $this->moFilesDictionary->is_path_handled( $mofile, $domain ) ) { // The file exists AND the path is handled by ST. return $generatedFile; } if ( ! $this->isThemeAndLocalizationPage() ) { // All following code is for genarating missing files and that's // only happening on the Theme and Plugins localization page. // -> by now we should consider removing the generation completely. return $mofile; } // Heavy check to see if the file is handled by String Translation. if ( ! $this->moFilesDictionary->find( $mofile ) ) { // Not handled by String Translation. return $mofile; } if ( $this->generateMissingMoFile->isNotProcessed( $generatedFile ) ) { $this->saveMissing( $missing->put( $domain, $mofile ) ); } return $mofile; } public function generateMissing() { $lock = make( 'WPML\Utilities\Lock', [ ':name' => self::class ] ); $missing = $this->getMissing(); if ( $missing->count() && $lock->create() ) { $generate = function ( $pair ) { list( $domain, $mofile ) = $pair; $generatedFile = $this->getGeneratedFileName( $mofile, $domain ); $this->generateMissingMoFile->run( $generatedFile, $domain ); }; $unProcessed = $missing->assocToPair() ->eachWithTimeout( $generate, self::getTimeout() ) ->pairToAssoc(); $this->saveMissing( $unProcessed ); $lock->release(); } } public static function isReadable( $mofile ) { return is_readable( $mofile ); } /** * @return \WPML\Collect\Support\Collection */ private function getMissing() { return wpml_collect( $this->optionManager->get( self::OPTION_GROUP, self::MISSING_MO_OPTION, [] ) ); } /** * @param \WPML\Collect\Support\Collection $missing */ private function saveMissing( \WPML\Collect\Support\Collection $missing ) { $this->optionManager->set( self::OPTION_GROUP, self::MISSING_MO_OPTION, $missing->toArray() ); } public static function getTimeout() { return self::TIMEOUT; } /** * @return bool */ private function wasWpmlInstalledPriorToMoFlowChanges() { $wpml_start_version = \get_option( \WPML_Installation::WPML_START_VERSION_KEY, '0.0.0' ); return version_compare( $wpml_start_version, self::WPML_VERSION_INTRODUCING_ST_MO_FLOW, '<' ); } /** * @param string $mofile * @param string $domain * * @return string */ private function getGeneratedFileName( $mofile, $domain ) { $fileName = basename( $mofile ); if ( $this->isNonDefaultWithMissingDomain( $fileName, $domain ) ) { $fileName = $domain . '-' . $fileName; } return WP_LANG_DIR . self::MISSING_MO_FILES_DIR . $fileName; } /** * There's a fallback for theme that is looking for * this kind of file `wp-content/themes/hybrid/ru_RU.mo`. * We need to add the domain otherwise it collides with * the MO file for the default domain. * * @param string $fileName * @param string $domain * * @return bool */ private function isNonDefaultWithMissingDomain( $fileName, $domain ) { return 'default' !== $domain && preg_match( '/^[a-z]+_?[A-Z]*\.mo$/', $fileName ); } private function isThemeAndLocalizationPage() { global $sitepress; return $sitepress ->get_wp_api() ->is_core_page( 'theme-localization.php' ); } } Hooks/LanguageSwitch.php 0000644 00000012617 14721711435 0011261 0 ustar 00 <?php namespace WPML\ST\MO\Hooks; use WPML\ST\MO\JustInTime\MOFactory; use WPML\ST\MO\WPLocaleProxy; use WPML\ST\Utils\LanguageResolution; class LanguageSwitch implements \IWPML_Action { /** @var MOFactory $jit_mo_factory */ private $jit_mo_factory; /** @var LanguageResolution $language_resolution */ private $language_resolution; /** @var null|string $current_locale */ private static $current_locale; /** @var array $globals_cache */ private static $globals_cache = []; public function __construct( LanguageResolution $language_resolution, MOFactory $jit_mo_factory ) { $this->language_resolution = $language_resolution; $this->jit_mo_factory = $jit_mo_factory; } public function add_hooks() { add_action( 'wpml_language_has_switched', [ $this, 'languageHasSwitched' ] ); } /** @param string $locale */ private function setCurrentLocale( $locale ) { self::$current_locale = $locale; } /** @return string */ public function getCurrentLocale() { return self::$current_locale; } public function languageHasSwitched() { $this->initCurrentLocale(); $new_locale = $this->language_resolution->getCurrentLocale(); $this->switchToLocale( $new_locale ); } public function initCurrentLocale() { if ( ! $this->getCurrentLocale() ) { add_filter( 'locale', [ $this, 'filterLocale' ], PHP_INT_MAX ); $this->setCurrentLocale( $this->language_resolution->getCurrentLocale() ); } } /** * This method will act as the WP Core function `switch_to_locale`, * but in a more efficient way. It will avoid to instantly load * the domains loaded in the previous locale. Instead, it will let * the domains be loaded via the "just in time" function. * * @param string $new_locale */ public function switchToLocale( $new_locale ) { if ( $new_locale === $this->getCurrentLocale() ) { return; } $this->updateCurrentGlobalsCache(); $this->changeWpLocale( $new_locale ); $this->changeMoObjects( $new_locale ); $this->setCurrentLocale( $new_locale ); } /** * @param string|null $locale */ public static function resetCache( $locale = null ) { self::$current_locale = $locale; self::$globals_cache = []; } /** * We need to take a new copy of the current locale globals * because some domains could have been added with the "just in time" * mechanism. */ private function updateCurrentGlobalsCache() { $cache = [ 'wp_locale' => isset( $GLOBALS['wp_locale'] ) ? $GLOBALS['wp_locale'] : null, 'l10n' => isset( $GLOBALS['l10n'] ) ? (array) $GLOBALS['l10n'] : [], ]; self::$globals_cache[ $this->getCurrentLocale() ] = $cache; } /** * @param string $new_locale */ private function changeWpLocale( $new_locale ) { if ( isset( self::$globals_cache[ $new_locale ]['wp_locale'] ) ) { $GLOBALS['wp_locale'] = self::$globals_cache[ $new_locale ]['wp_locale']; } else { /** * WPLocaleProxy is a wrapper of \WP_Locale with a kind of lazy initialization * to avoid loading the default domain for strings that * we don't use in this transitory language. */ $GLOBALS['wp_locale'] = new WPLocaleProxy(); } } /** * @param string $new_locale */ private function changeMoObjects( $new_locale ) { $this->resetTranslationAvailabilityInformation(); $cachedMoObjects = isset( self::$globals_cache[ $new_locale ]['l10n'] ) ? self::$globals_cache[ $new_locale ]['l10n'] : []; /** * The JustInTimeMO objects will replaced themselves on the fly * by the legacy default MO object if a string is translated. * This is because the function "_load_textdomain_just_in_time" * does not support the default domain and MO files outside the * "wp-content/languages" folder. */ $GLOBALS['l10n'] = $this->jit_mo_factory->get( $new_locale, $this->getUnloadedDomains(), $cachedMoObjects ); $this->setLocaleInWP65TranslationController( $new_locale ); } /** * @param string $new_locale * * @return void */ private function setLocaleInWP65TranslationController( $new_locale ) { if ( class_exists( \WP_Translation_Controller::class ) ) { \WP_Translation_Controller::get_instance()->set_locale( $new_locale ); } } private function resetTranslationAvailabilityInformation() { global $wp_textdomain_registry; if ( ! isset( $wp_textdomain_registry ) && function_exists( '_get_path_to_translation' ) ) { _get_path_to_translation( '', true ); } } /** * @param string $locale * * @return string */ public function filterLocale( $locale ) { $currentLocale = $this->getCurrentLocale(); if ( $currentLocale ) { return $currentLocale; } return $locale; } /** * @return array */ private function getUnloadedDomains() { $unloadedDomains = isset( $GLOBALS['l10n_unloaded'] ) ? array_keys( (array) $GLOBALS['l10n_unloaded'] ) : []; // WP 6.5 // When a text domain is unloaded, and later loaded again, WP will keep it in l10n_unloaded. // Prior to WP 6.5, there was a call ``unset( $l10n_unloaded[ $domain ] );`` just after // $l10n[ $domain ] was set. // This is a problem because the domain will be always excluded in our JITMO objects, // and will generate empty domains that should be loaded. if ( class_exists('\WP_Translation_Controller') ) { foreach ( $unloadedDomains as $key => $domain ) { if ( isset( $GLOBALS['l10n'][ $domain ] ) && ! $GLOBALS['l10n'][ $domain ] instanceof \NOOP_Translations ) { unset( $unloadedDomains[ $key ] ); } } } return $unloadedDomains; } } WPLocaleProxy.php 0000644 00000002060 14721711435 0007770 0 ustar 00 <?php namespace WPML\ST\MO; use WP_Locale; class WPLocaleProxy { /** * @var WP_Locale|null $wp_locale */ private $wp_locale; /** * @param string $method * @param array $args * * @return mixed|null */ public function __call( $method, array $args ) { $callback = [ $this->getWPLocale(), $method ]; if ( method_exists( $this->getWPLocale(), $method ) && is_callable( $callback ) ) { return call_user_func_array( $callback , $args ); } return null; } /** * @param string $property * * @return bool */ public function __isset( $property ) { if ( property_exists( \WP_Locale::class, $property ) ) { return true; } return false; } /** * @param string $property * * @return mixed|null */ public function __get( $property ) { if ( $this->__isset( $property ) ) { return $this->getWPLocale()->{$property}; } return null; } /** * @return WP_Locale|null */ private function getWPLocale() { if ( ! $this->wp_locale ) { $this->wp_locale = new WP_Locale(); } return $this->wp_locale; } } JustInTime/DefaultMO.php 0000644 00000000602 14721711435 0011133 0 ustar 00 <?php /** * @author OnTheGo Systems */ namespace WPML\ST\MO\JustInTime; use WPML\ST\MO\LoadedMODictionary; class DefaultMO extends MO { public function __construct( LoadedMODictionary $loaded_mo_dictionary, $locale ) { parent::__construct( $loaded_mo_dictionary, $locale, 'default' ); } protected function loadTextDomain() { load_default_textdomain( $this->locale ); } } JustInTime/MOFactory.php 0000644 00000002330 14721711435 0011156 0 ustar 00 <?php namespace WPML\ST\MO\JustInTime; use WPML\ST\MO\LoadedMODictionary; class MOFactory { /** @var LoadedMODictionary $loaded_mo_dictionary */ private $loaded_mo_dictionary; public function __construct( LoadedMODictionary $loaded_mo_dictionary ) { $this->loaded_mo_dictionary = $loaded_mo_dictionary; } /** * We need to rely on the loaded dictionary rather than `$GLOBALS['l10n]` * because a domain could have been loaded in a language that * does not have a MO file and so it won't be added to the `$GLOBALS['l10n]`. * * @param string $locale * @param array $excluded_domains * @param array $cachedMoObjects * * @return array */ public function get( $locale, array $excluded_domains, array $cachedMoObjects ) { $mo_objects = [ 'default' => isset( $cachedMoObjects['default'] ) ? $cachedMoObjects['default'] : new DefaultMO( $this->loaded_mo_dictionary, $locale ), ]; $excluded_domains[] = 'default'; foreach ( $this->loaded_mo_dictionary->getDomains( $excluded_domains ) as $domain ) { $mo_objects[ $domain ] = isset( $cachedMoObjects[ $domain ] ) ? $cachedMoObjects[ $domain ] : new MO( $this->loaded_mo_dictionary, $locale, $domain ); } return $mo_objects; } } JustInTime/MO.php 0000644 00000005073 14721711435 0007635 0 ustar 00 <?php namespace WPML\ST\MO\JustInTime; use NOOP_Translations; use WPML\ST\MO\LoadedMODictionary; class MO extends \MO { /** @var LoadedMODictionary $loaded_mo_dictionary */ private $loaded_mo_dictionary; /** @var string $locale */ protected $locale; /** @var string $domain */ private $domain; /** @var bool $isLoading */ private $isLoading = false; /** * @param LoadedMODictionary $loaded_mo_dictionary * @param string $locale * @param string $domain */ public function __construct( LoadedMODictionary $loaded_mo_dictionary, $locale, $domain ) { $this->loaded_mo_dictionary = $loaded_mo_dictionary; $this->locale = $locale; $this->domain = $domain; } /** * @param string $singular * @param string $context * * @return string */ public function translate( $singular, $context = null ) { if ( $this->isLoading ) { return $singular; } $this->load(); return _x( $singular, $context, $this->domain ); } /** * @param string $singular * @param string $plural * @param int $count * @param string $context * * @return string */ public function translate_plural( $singular, $plural, $count, $context = null ) { if ( $this->isLoading ) { return $count > 1 ? $plural : $singular; } $this->load(); return _nx( $singular, $plural, $count, $context, $this->domain ); } private function load() { if ( $this->isLoaded() ) { // Abort as the domain just needs to be loaded once. return true; } $this->isLoading = true; $this->loadTextDomain(); if ( ! $this->isLoaded() ) { /** * If we could not load at least one MO file, * we need to assign the domain with a `NOOP_Translations` * object on the 'l10n' global. * This will prevent recursive loop on the current object. */ $GLOBALS['l10n'][ $this->domain ] = new NOOP_Translations(); } $this->isLoading = false; } protected function loadTextDomain() { $this->loaded_mo_dictionary ->getFiles( $this->domain, $this->locale ) ->each( function( $mofile ) { load_textdomain( $this->domain, $mofile, $this->locale ); } ); } /** * In some cases, themes or plugins are hooking on * `override_load_textdomain` so that the function * `load_textdomain` always returns `true` even * if the domain is not set on the global `$l10n`. * * That's why we need to check on the global `$l10n`. * * @return bool */ private function isLoaded() { return isset( $GLOBALS['l10n'][ $this->domain ] ) && ! $GLOBALS['l10n'][ $this->domain ] instanceof self; } } LoadedMODictionary.php 0000644 00000011525 14721711435 0010740 0 ustar 00 <?php namespace WPML\ST\MO; use MO; use stdClass; use WPML\Collect\Support\Collection; use WPML\LIB\WP\WordPress; class LoadedMODictionary { const PATTERN_SEARCH_LOCALE = '#([-]?)([a-z]+[_A-Z]*)(\.mo)$#i'; const LOCALE_PLACEHOLDER = '{LOCALE}'; /** @var array */ private $domainsCache = []; /** @var Collection $mo_files */ private $mo_files; public function __construct() { $this->mo_files = wpml_collect( [] ); $this->collectFilesAddedBeforeInstantiation(); } private function collectFilesAddedBeforeInstantiation() { if ( isset( $GLOBALS['l10n'] ) && is_array( $GLOBALS['l10n'] ) ) { wpml_collect( $GLOBALS['l10n'] )->each( function ( $mo, $domain ) { if ( $mo instanceof MO ) { $this->addFile( $domain, $mo->get_filename() ); } } ); } // WP 6.5 // We need to collect all files loaded in WP_Translation_Controller // at the moment of hooks registration. if ( class_exists('\WP_Translation_Controller') ) { $translationsController = \WP_Translation_Controller::get_instance(); $reflection = new \ReflectionClass( $translationsController ); $property = $reflection->getProperty( 'loaded_files' ); $property->setAccessible( true ); $loaded_files = $property->getValue( $translationsController ); foreach ( $loaded_files as $loaded_file => $loaded_file_data ) { $moFileName = str_replace('.l10n.php', '.mo', $loaded_file); $locale = array_keys($loaded_file_data)[0]; $locale_data = $loaded_file_data[$locale]; foreach ( $locale_data as $domain => $translationFile ) { $this->addFile( $domain, $moFileName ); } } } } /** * @param string $domain * @param string $mofile */ public function addFile( $domain, $mofile ) { // The WP RC version is `6.7-RC1` which is an improper format for comparison (we'd need `6.7.0-RC1`). if ( WordPress::versionCompare( '>', '6.6.999' ) ) { $mofile = $this->maybeAdjustPathToStandardLanguageFolders( $mofile, $domain ); } $mofile_pattern = preg_replace( self::PATTERN_SEARCH_LOCALE, '$1' . self::LOCALE_PLACEHOLDER . '$3', $mofile, 1 ); $hash = md5( $domain . $mofile_pattern ); $entity = (object) [ 'domain' => $domain, 'mofile_pattern' => $mofile_pattern, 'mofile' => $mofile, ]; $this->mo_files->put( $hash, $entity ); $this->domainsCache = []; } /** * @param string $mofile * @param string $domain * * @return string */ private function maybeAdjustPathToStandardLanguageFolders( $mofile, $domain ) { static $pluginsFolder = WP_LANG_DIR . '/plugins/'; static $themesFolder = WP_LANG_DIR . '/themes/'; if ( 0 === strpos( $mofile, $pluginsFolder ) || 0 === strpos( $mofile, $themesFolder ) ) { return $mofile; // The file is already in the standard language folders. } if ( is_dir( dirname( $mofile ) ) ) { return $mofile; // The custom language folder exists. } preg_match( self::PATTERN_SEARCH_LOCALE, $mofile, $matches ); if ( ! isset( $matches[2] ) ) { return $mofile; } $locale = $matches[2]; $moName = $domain . '-' . $locale . '.mo'; if ( is_file( $pluginsFolder . $moName ) ) { return $pluginsFolder . $moName; } elseif ( is_file( $themesFolder . $moName ) ) { return $themesFolder . $moName; } // Fallback, let's try find it in the standard plugin folder (it may work with a different locale). return $pluginsFolder . $moName; } /** * @param array $excluded * * @return array */ public function getDomains( array $excluded = [] ) { $key = md5( implode( $excluded ) ); if ( isset( $this->domainsCache[ $key ] ) ) { return $this->domainsCache[ $key ]; } $domains = $this->mo_files ->reject( $this->excluded( $excluded ) ) ->pluck( 'domain' ) ->unique()->values()->toArray(); $this->domainsCache[ $key ] = $domains; return $domains; } /** * @param string $domain * @param string $locale * * @return Collection */ public function getFiles( $domain, $locale ) { return $this->mo_files ->filter( $this->byDomain( $domain ) ) ->map( $this->getFile( $locale ) ) ->values(); } /** * @return Collection */ public function getEntities() { return $this->mo_files; } /** * @param array $excluded * * @return \Closure */ private function excluded( array $excluded ) { return function ( stdClass $entity ) use ( $excluded ) { return in_array( $entity->domain, $excluded, true ); }; } /** * @param string $domain * * @return \Closure */ private function byDomain( $domain ) { return function ( stdClass $entity ) use ( $domain ) { return $entity->domain === $domain; }; } /** * @param string $locale * * @return \Closure */ private function getFile( $locale ) { return function ( stdClass $entity ) use ( $locale ) { return str_replace( self::LOCALE_PLACEHOLDER, $locale, $entity->mofile_pattern ); }; } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.3-4ubuntu2.24 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка