File: /var/www/html/wppartneramazingsecret/wp-content/plugins/polylang/include/translate-option.php
<?php
/**
* @package Polylang
*/
/**
* Registers and translates strings in an option.
* When a string is updated in an original option, the translations of the old string are assigned to the new original string.
*
* @since 2.9
*/
class PLL_Translate_Option {
/**
* Array of option keys to translate.
*
* @var array
*/
private $keys;
/**
* Array of updated strings.
*
* @var array
*/
private $updated_strings = array();
/**
* Constructor
*
* @since 2.9
*
* @param string $name Option name.
* @param array $keys Recursive array of option keys to translate in the form:
* @example array(
* 'option_key_to_translate_1' => 1,
* 'option_key_to_translate_2' => 1,
* 'my_group' => array(
* 'sub_key_to_translate_1' => 1,
* 'sub_key_to_translate_2' => 1,
* ),
* )
*
* Note: only keys are interpreted. Any scalar can be used as values.
* @param array $args {
* Optional. Array of arguments for registering the option.
*
* @type string $context The group in which the strings will be registered.
* @type string $sanitize_callback A callback function that sanitizes the option's value.
* }
*/
public function __construct( $name, $keys = array(), $args = array() ) {
// Registers the strings.
$context = isset( $args['context'] ) ? $args['context'] : 'Polylang';
$this->register_string_recursive( $context, $name, get_option( $name ), $keys );
// Translates the strings.
$this->keys = $keys;
add_filter( 'option_' . $name, array( $this, 'translate' ) ); // Make sure to add this filter after options are registered.
// Filters updated values.
add_filter( 'pre_update_option_' . $name, array( $this, 'pre_update_option' ), 10, 3 );
add_action( 'update_option_' . $name, array( $this, 'update_option' ) );
// Sanitizes translated strings.
if ( empty( $args['sanitize_callback'] ) ) {
add_filter( 'pll_sanitize_string_translation', array( $this, 'sanitize_option' ), 10, 2 );
} else {
add_filter( 'pll_sanitize_string_translation', $args['sanitize_callback'], 10, 3 );
}
}
/**
* Translates the strings registered for an option.
*
* @since 1.0
*
* @param mixed $value Either a string to translate or a list of strings to translate.
* @return mixed Translated string(s).
*/
public function translate( $value ) {
return $this->translate_string_recursive( $value, $this->keys );
}
/**
* Recursively translates the strings registered for an option.
*
* @since 1.0
*
* @param mixed $values Either a string to translate or a list of strings to translate.
* @param array|bool $key Array of option keys to translate.
* @return array|string Translated string(s)
*/
protected function translate_string_recursive( $values, $key ) {
$children = is_array( $key ) ? $key : array();
if ( is_array( $values ) || is_object( $values ) ) {
if ( count( $children ) ) {
foreach ( $children as $name => $child ) {
if ( is_array( $values ) && isset( $values[ $name ] ) ) {
$values[ $name ] = $this->translate_string_recursive( $values[ $name ], $child );
continue;
}
if ( is_object( $values ) && isset( $values->$name ) ) {
$values->$name = $this->translate_string_recursive( $values->$name, $child );
continue;
}
$pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
foreach ( $values as $n => &$value ) {
// The first case could be handled by the next one, but we avoid calls to preg_match here.
if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
$value = $this->translate_string_recursive( $value, $child );
}
}
}
} else {
// Parent key is a wildcard and no sub-key has been whitelisted.
foreach ( $values as &$value ) {
$value = $this->translate_string_recursive( $value, $key );
}
}
} else {
$values = pll__( $values );
}
return $values;
}
/**
* Recursively registers strings for an option.
*
* @since 1.0
* @since 2.7 Signature modified
*
* @param string $context The group in which the strings will be registered.
* @param string $option Option name.
* @param mixed $values Option value.
* @param array|bool $key Array of option keys to translate.
* @return void
*/
protected function register_string_recursive( $context, $option, $values, $key ) {
if ( is_object( $values ) ) {
$values = (array) $values;
}
if ( is_array( $values ) ) {
$children = is_array( $key ) ? $key : array();
if ( count( $children ) ) {
foreach ( $children as $name => $child ) {
if ( isset( $values[ $name ] ) ) {
$this->register_string_recursive( $context, $name, $values[ $name ], $child );
continue;
}
$pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
foreach ( $values as $n => $value ) {
// The first case could be handled by the next one, but we avoid calls to preg_match here.
if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
$this->register_string_recursive( $context, $n, $value, $child );
}
}
}
} else {
foreach ( $values as $n => $value ) {
// Parent key is a wildcard and no sub-key has been whitelisted.
$this->register_string_recursive( $context, $n, $value, $key );
}
}
} else {
PLL_Admin_Strings::register_string( $option, $values, $context, true );
}
}
/**
* Filters an option before it is updated.
*
* This is the step 1 in the update process, in which we prevent the update of
* strings to their translations by filtering them out, and we store the updated strings
* for the next step.
*
* @since 2.9
*
* @param mixed $value The new, unserialized option value.
* @param mixed $old_value The old (filtered) option value.
* @param string $name Option name.
* @return mixed
*/
public function pre_update_option( $value, $old_value, $name ) {
// Stores the unfiltered old option value before it is updated in DB.
remove_filter( 'option_' . $name, array( $this, 'translate' ), 10, 2 );
$unfiltered_old_value = get_option( $name );
add_filter( 'option_' . $name, array( $this, 'translate' ), 20, 2 );
// Load strings translations according to the admin language filter
$locale = pll_current_language( 'locale' );
if ( empty( $locale ) ) {
$locale = pll_default_language( 'locale' );
}
PLL()->load_strings_translations( $locale );
// Filters out the strings which would be updated to their translations and stores the updated strings.
$value = $this->check_value_recursive( $unfiltered_old_value, $value, $this->keys );
return $value;
}
/**
* Updates the string translations to keep the same translated value when updating the original option.
*
* This is the step 2 in the update process. Knowing all strings that have been updated,
* we remove the old strings from the strings translations and replace them by
* the new strings with the old translations.
*
* @since 2.9
*
* @return void
*/
public function update_option() {
$curlang = pll_current_language();
if ( ! empty( $this->updated_strings ) ) {
foreach ( pll_languages_list() as $lang ) {
$language = PLL()->model->get_language( $lang );
$mo = new PLL_MO();
$mo->import_from_db( $language );
foreach ( $this->updated_strings as $old_string => $string ) {
$translation = $mo->translate( $old_string );
if ( ( empty( $curlang ) && $translation === $old_string ) || $lang === $curlang ) {
$translation = $string;
}
// Removes the old entry and replace it by the new one, with the same translation.
$mo->delete_entry( $old_string );
$mo->add_entry( $mo->make_entry( $string, $translation ) );
}
$mo->export_to_db( $language );
}
}
}
/**
* Recursively compares the updated strings to the translation of the old string.
*
* This is the heart of the update process. If an updated string is found to be
* the same as the translation of the old string, we restore the old string to
* prevent the update in {@see PLL_Translate_Option::pre_update_option()}, otherwise
* the updated string is stored in {@see PLL_Translate_Option::updated_strings} to be able to
* later assign the translations to the new value in {@see PLL_Translate_Option::update_option()}.
*
* @since 2.9
*
* @param mixed $old_values The old option value.
* @param mixed $values The new option value..
* @param array|bool $key Array of option keys to translate.
* @return mixed
*/
protected function check_value_recursive( $old_values, $values, $key ) {
$children = is_array( $key ) ? $key : array();
if ( is_array( $values ) || is_object( $values ) ) {
if ( count( $children ) ) {
foreach ( $children as $name => $child ) {
if ( is_array( $values ) && is_array( $old_values ) && isset( $old_values[ $name ], $values[ $name ] ) ) {
$values[ $name ] = $this->check_value_recursive( $old_values[ $name ], $values[ $name ], $child );
continue;
}
if ( is_object( $values ) && is_object( $old_values ) && isset( $old_values->$name, $values->$name ) ) {
$values->$name = $this->check_value_recursive( $old_values->$name, $values->$name, $child );
continue;
}
$pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
foreach ( $values as $n => $value ) {
// The first case could be handled by the next one, but we avoid calls to preg_match here.
if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
if ( is_array( $values ) && is_array( $old_values ) && isset( $old_values[ $n ] ) ) {
$values[ $n ] = $this->check_value_recursive( $old_values[ $n ], $value, $child );
}
if ( is_object( $values ) && is_object( $old_values ) && isset( $old_values->$n ) ) {
$values->$n = $this->check_value_recursive( $old_values->$n, $value, $child );
}
}
}
}
} else {
// Parent key is a wildcard and no sub-key has been whitelisted.
foreach ( $values as $n => $value ) {
if ( is_array( $values ) && is_array( $old_values ) && isset( $old_values[ $n ] ) ) {
$values[ $n ] = $this->check_value_recursive( $old_values[ $n ], $value, $key );
}
if ( is_object( $values ) && is_object( $old_values ) && isset( $old_values->$n ) ) {
$values->$n = $this->check_value_recursive( $old_values->$n, $value, $key );
}
}
}
} elseif ( $old_values !== $values ) {
if ( pll__( $old_values ) === $values ) {
$values = $old_values; // Prevents updating the value to its translation.
} else {
$this->updated_strings[ $old_values ] = $values; // Stores the updated strings.
}
}
return $values;
}
/**
* Sanitizes the option value.
*
* @since 2.9
*
* @param string $value The unsanitised value.
* @param string $name The name of the option.
* @return string Sanitized value.
*/
public function sanitize_option( $value, $name ) {
return sanitize_option( $name, $value );
}
}