$state, 'switched' => isset( $GLOBALS['wp_locale_switcher'] ) && switch_to_locale( swiftling_site_locale() ), ); $state = array_merge( $state, array( 'mode' => '', 'language' => '', 'locale' => '', 'values' => array(), ) ); } /** * Translates into a language for a while, until swiftling_resume_translation(): stored values, texts * from code and links are read in the language, and WordPress, the theme and plugins run in its * locale. For what is written outside the reader's page, such as an email to a customer sent from * the admin or by a scheduled task in the language of the customer's order. Switches nest with * pauses; each needs its resume. * * The filters that translate are added where the request has none, as in the admin, and taken off * again on resume. A slug that is not one of the site's languages is the original, as a pause. * * @param string $slug Address slug of a target language. */ function swiftling_switch_translation( $slug ) { $language = '' !== (string) $slug ? swiftling_target_language( (string) $slug ) : null; if ( ! $language ) { swiftling_pause_translation(); return; } $paused = &swiftling_paused_translations(); $state = &swiftling_state(); $added = array(); foreach ( swiftling_source_hooks( 'translate' ) as $hook ) { if ( false === has_filter( $hook[0], $hook[1] ) ) { add_filter( $hook[0], $hook[1], $hook[2], $hook[3] ); $added[] = $hook; } } $paused[] = array( 'state' => $state, 'switched' => isset( $GLOBALS['wp_locale_switcher'] ) && switch_to_locale( $language['wp_locale'] ), 'added' => $added, ); // Read anew in the language: what the request read so far is another language's. $state = array_merge( $state, array( 'mode' => 'translate', 'language' => (string) $slug, 'locale' => $language['locale'], 'hashes' => array(), 'strings' => array(), 'partial' => array(), 'values' => array(), ) ); } /** * Puts the request back as it was before swiftling_pause_translation() or * swiftling_switch_translation(). */ function swiftling_resume_translation() { $paused = &swiftling_paused_translations(); if ( ! $paused ) { return; } $last = array_pop( $paused ); $state = &swiftling_state(); $state = $last['state']; if ( $last['switched'] ) { restore_previous_locale(); } foreach ( $last['added'] ?? array() as $hook ) { remove_filter( $hook[0], $hook[1], $hook[2] ); } } /** * Returns the pauses and switches swiftling_pause_translation() and swiftling_switch_translation() * started, the last one last. A reference, so the tests can start a new request. * * @return array */ function &swiftling_paused_translations() { static $paused = array(); return $paused; } /** * Returns the locale WordPress runs in without SwiftLing: the site's own, which its pages in the * original run in. Worked out as get_locale() does, from the site's language setting, but not * from the global $locale it keeps it in: wp-settings.php stores there what get_locale() returns * once WordPress has loaded, which in a request in another language is that language's locale. * * @return string */ function swiftling_site_locale() { global $wp_local_package; $locale = isset( $wp_local_package ) ? (string) $wp_local_package : ''; if ( defined( 'WPLANG' ) ) { $locale = (string) WPLANG; } if ( is_multisite() ) { $setting = wp_installing() ? get_site_option( 'WPLANG' ) : get_option( 'WPLANG' ); if ( false === $setting && ! wp_installing() ) { $setting = get_site_option( 'WPLANG' ); } } else { $setting = get_option( 'WPLANG' ); } if ( false !== $setting ) { $locale = (string) $setting; } $locale = '' === $locale ? 'en_US' : $locale; // Other code may choose the site's locale too; only SwiftLing's choice is left out, and the // locale switched to for a while, as for an email, which WordPress's switcher answers with. $filtered = remove_filter( 'locale', 'swiftling_filter_locale', 100 ); $switcher = isset( $GLOBALS['wp_locale_switcher'] ) ? array( $GLOBALS['wp_locale_switcher'], 'filter_locale' ) : null; $priority = $switcher ? has_filter( 'locale', $switcher ) : false; if ( false !== $priority ) { remove_filter( 'locale', $switcher, $priority ); } /** This filter is documented in wp-includes/l10n.php */ $locale = (string) apply_filters( 'locale', $locale ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress's own filter, as get_locale() applies it. if ( false !== $priority ) { add_filter( 'locale', $switcher, $priority ); } if ( $filtered ) { add_filter( 'locale', 'swiftling_filter_locale', 100 ); } return $locale; } /** * Returns the language the page is shown in: a target in translate mode, the original otherwise. * * @return array Language as swiftling_languages() gives it. */ function swiftling_page_language() { $state = &swiftling_state(); $language = 'translate' === swiftling_mode() ? swiftling_target_language( $state['language'] ) : null; return $language ? $language : swiftling_languages()['source']; } /** * Sets the lang attribute of the page to the language it is shown in: the code of a custom * language, not the close language WordPress runs in for it. * * @param string $output Attributes of the html element. * @return string */ function swiftling_language_attributes( $output ) { $tag = str_replace( '_', '-', swiftling_page_language()['locale'] ); return preg_replace( '/lang="[^"]*"/', 'lang="' . esc_attr( $tag ) . '"', $output ); } /** * Sets the writing direction of a page in a custom language, which its close language does not * decide: Persian on the texts of English is still right to left. Other languages keep the * direction of their WordPress translation. */ function swiftling_set_text_direction() { global $wp_locale; $language = swiftling_page_language(); if ( null !== $language['rtl'] && $wp_locale instanceof WP_Locale ) { $wp_locale->text_direction = $language['rtl'] ? 'rtl' : 'ltr'; } }