first commit

This commit is contained in:
2023-09-12 21:41:04 +02:00
commit 3361a7f053
13284 changed files with 2116755 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
<?php
interface IWPML_TM_Word_Calculator_Post {
public function count_words( WPML_Post_Element $post_element, $lang = null );
}

View File

@@ -0,0 +1,103 @@
<?php
class WPML_TM_Word_Calculator_Post_Custom_Fields implements IWPML_TM_Word_Calculator_Post {
/** @var WPML_TM_Word_Calculator $calculator */
private $calculator;
/** @var array|null $cf_settings from `$sitepress_settings['translation-management']['custom_fields_translation']` */
private $cf_settings;
/** @var array $fields_to_count */
private $fields_to_count = array();
/**
* WPML_TM_Word_Calculator_Post_Custom_Fields constructor.
*
* $cf_settings:
*
* <code>
* $array = [
* 'custom-field-1' => WPML_TRANSLATE_CUSTOM_FIELD,
* 'custom-field-2' => WPML_COPY_CUSTOM_FIELD,
* 'custom-field-3' => WPML_IGNORE_CUSTOM_FIELD,
* 'custom-field-4' => WPML_IGNORE_CUSTOM_FIELD,
* 'custom-field-5' => WPML_COPY_ONCE_CUSTOM_FIELD,
* ]
* </code>
*
* @param \WPML_TM_Word_Calculator $calculator An instance of WPML_TM_Word_Calculator.
* @param array|null $cf_settings An associative array where they key is the name of the custom field and the value is an integer representing the translation method.
*
* @see inc/constants.php for the values of the constsnts
*/
public function __construct( WPML_TM_Word_Calculator $calculator, array $cf_settings = null ) {
$this->calculator = $calculator;
$this->cf_settings = $cf_settings;
}
public function count_words( WPML_Post_Element $post_element, $lang = null ) {
$words = 0;
$post_id = $post_element->get_id();
$post_lang = $post_element->get_language_code();
if ( ! $post_lang || ! $post_id || ! $this->is_registered_type( $post_element )
|| empty( $this->cf_settings ) || ! is_array( $this->cf_settings )
) {
return $words;
}
$cf_to_count = $this->get_translatable_fields_to_count( $post_id );
foreach ( $cf_to_count as $cf ) {
$custom_fields_value = get_post_meta( $post_id, $cf );
if ( ! $custom_fields_value ) {
continue;
}
if ( is_scalar( $custom_fields_value ) ) {
// only support scalar values for now
$words += $this->calculator->count_words( $custom_fields_value, $post_lang );
} else {
foreach ( $custom_fields_value as $custom_fields_value_item ) {
if ( $custom_fields_value_item && is_scalar( $custom_fields_value_item ) ) {
// only support scalar values for now
$words += $this->calculator->count_words( $custom_fields_value_item, $post_lang );
}
}
}
}
return (int) $words;
}
/** @return bool */
private function is_registered_type( WPML_Post_Element $post_element ) {
$post_types = get_post_types();
return in_array( $post_element->get_type(), $post_types );
}
/** @return array */
private function get_translatable_fields_to_count( $post_id ) {
if ( ! $this->fields_to_count ) {
foreach ( $this->cf_settings as $cf => $mode ) {
if ( in_array( (int) $mode, array( WPML_TRANSLATE_CUSTOM_FIELD, WPML_COPY_ONCE_CUSTOM_FIELD ), true ) ) {
$this->fields_to_count[] = $cf;
}
}
}
/**
* Allow to modify the custom fields whose words will be counted.
*
* @param array $fields_to_count The fields to include when counting the words.
* @param int $post_id The ID of the post for which we are counting the words.
*
* @see \WPML_TM_Word_Calculator_Post_Custom_Fields::__construct
*/
return apply_filters( 'wpml_words_count_custom_fields_to_count', $this->fields_to_count, $post_id );
}
}

View File

@@ -0,0 +1,48 @@
<?php
class WPML_TM_Word_Calculator_Post_Object implements IWPML_TM_Word_Calculator_Post {
/** @var WPML_TM_Word_Calculator $calculator */
private $calculator;
/** @var WPML_TM_Word_Calculator_Post_Packages $packages_calculator */
private $packages_calculator;
public function __construct(
WPML_TM_Word_Calculator $calculator,
WPML_TM_Word_Calculator_Post_Packages $packages_calculator
) {
$this->calculator = $calculator;
$this->packages_calculator = $packages_calculator;
}
/**
* @param WPML_Post_Element $post_element
* @param string $lang
*
* @return int
*/
public function count_words( WPML_Post_Element $post_element, $lang = null ) {
$words = 0;
$wp_post = $post_element->get_wp_object();
$source_lang = $post_element->get_language_code();
if ( $wp_post ) {
$words += $this->calculator->count_words( $wp_post->post_title, $source_lang );
$words += $this->calculator->count_words( $wp_post->post_excerpt, $source_lang );
$words += $this->calculator->count_words( $wp_post->post_name, $source_lang );
if ( $this->has_string_packages( $post_element ) ) {
$words += $this->packages_calculator->count_words( $post_element, $lang );
} else {
$words += $this->calculator->count_words( $wp_post->post_content, $source_lang );
}
}
return $words;
}
private function has_string_packages( WPML_Post_Element $post_element ) {
return (bool) $this->packages_calculator->count_words( $post_element, null );
}
}

View File

@@ -0,0 +1,51 @@
<?php
class WPML_TM_Word_Calculator_Post_Packages implements IWPML_TM_Word_Calculator_Post {
/** @var WPML_TM_Word_Count_Records $records */
private $records;
/** @var WPML_TM_Count_Composite[] $package_counts */
private $package_counts;
public function __construct( WPML_TM_Word_Count_Records $records ) {
$this->records = $records;
}
/**
* @param WPML_Post_Element $post_element
* @param string|null $lang
*
* @return int
*/
public function count_words( WPML_Post_Element $post_element, $lang = null ) {
if ( $lang ) {
return $this->get_package_counts( $post_element )->get_words_to_translate( $lang );
} else {
return $this->get_package_counts( $post_element )->get_total_words();
}
}
/**
* @param WPML_Post_Element $post_element
*
* @return WPML_TM_Count_Composite
*/
private function get_package_counts( WPML_Post_Element $post_element ) {
$post_id = $post_element->get_id();
if ( ! isset( $this->package_counts[ $post_id ] ) ) {
$counts = $this->records->get_packages_word_counts( $post_id );
$word_count_composite = new WPML_TM_Count_Composite();
foreach ( $counts as $count ) {
$word_count_composite->add_count( $count );
}
$this->package_counts[ $post_id ] = $word_count_composite;
}
return $this->package_counts[ $post_id ];
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* @see https://onthegosystems.myjetbrains.com/youtrack/issue/wpmltm-2327
*/
class WPML_TM_Word_Calculator {
const ASIAN_LANGUAGE_CHAR_SIZE = 6;
/** @var WPML_PHP_Functions $php_functions */
private $php_functions;
public function __construct( WPML_PHP_Functions $php_functions ) {
$this->php_functions = $php_functions;
}
/**
* @param string $string
* @param string $language_code
*
* @return int
*/
public function count_words( $string, $language_code ) {
$sanitized_source = $this->sanitize_string( $string );
$words = 0;
if ( in_array( $language_code, self::get_asian_languages() ) ) {
$words += strlen( strip_tags( $sanitized_source ) ) / self::ASIAN_LANGUAGE_CHAR_SIZE;
} else {
$words += count( preg_split( '/[\s,:;!\.\?\(\)\[\]\-_\'"\\/]+/', $sanitized_source, 0, PREG_SPLIT_NO_EMPTY ) );
}
return (int) $words;
}
/** @return bool */
private function exclude_shortcodes_in_words_count() {
if ( $this->php_functions->defined( 'EXCLUDE_SHORTCODES_IN_WORDS_COUNT' ) ) {
return (bool) $this->php_functions->constant( 'EXCLUDE_SHORTCODES_IN_WORDS_COUNT' );
}
return false;
}
/**
* @param string $source
*
* @return string
*/
private function sanitize_string( $source ) {
$result = $source;
$result = html_entity_decode( $result );
$result = strip_tags( $result );
$result = trim( $result );
$result = $this->strip_urls( $result );
if ( $this->exclude_shortcodes_in_words_count() ) {
$result = strip_shortcodes( $result );
} else {
$result = $this->extract_content_in_shortcodes( $result );
}
return $result;
}
/**
* @param string $string
*
* @return string
*/
private function extract_content_in_shortcodes( $string ) {
return preg_replace( '#(?:\[/?)[^/\]]+/?\]#s', '', $string );
}
/**
* @param string $string
*
* @return string
*/
private function strip_urls( $string ) {
return preg_replace( '/\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i', '', $string );
}
public static function get_asian_languages() {
return array( 'ja', 'ko', 'zh-hans', 'zh-hant', 'mn', 'ne', 'hi', 'pa', 'ta', 'th' );
}
}

View File

@@ -0,0 +1,6 @@
<?php
interface IWPML_TM_Word_Count_Set {
public function process( $id );
}

View File

@@ -0,0 +1,44 @@
<?php
class WPML_TM_Word_Count_Set_Package {
/** @var WPML_ST_Package_Factory $package_factory */
private $package_factory;
/** @var WPML_TM_Word_Count_Records $records */
private $records;
/** @var array $active_langs */
private $active_langs;
public function __construct(
WPML_ST_Package_Factory $package_factory,
WPML_TM_Word_Count_Records $records,
array $active_langs
) {
$this->package_factory = $package_factory;
$this->records = $records;
$this->active_langs = $active_langs;
}
/** @param int $package_id */
public function process( $package_id ) {
$package = $this->package_factory->create( $package_id );
$word_counts = new WPML_TM_Count();
foreach ( $this->active_langs as $lang ) {
if ( $package->get_package_language() === $lang ) {
$word_counts->set_total_words(
$this->records->get_string_words_to_translate_per_lang( null, $package_id )
);
} else {
$word_counts->set_words_to_translate(
$lang,
$this->records->get_string_words_to_translate_per_lang( $lang, $package_id )
);
}
}
$this->records->set_package_word_count( $package_id, $word_counts );
}
}

View File

@@ -0,0 +1,70 @@
<?php
class WPML_TM_Word_Count_Set_Post {
/** @var WPML_Translation_Element_Factory $element_factory */
private $element_factory;
/** @var WPML_TM_Word_Count_Records $records */
private $records;
/** @var IWPML_TM_Word_Calculator_Post[] $post_calculators */
private $post_calculators;
/** @var array $active_langs */
private $active_langs;
/** @var WPML_Post_Element $post_element */
private $post_element;
/**
* @param WPML_Translation_Element_Factory $element_factory
* @param WPML_TM_Word_Count_Records $records
* @param IWPML_TM_Word_Calculator_Post[] $calculators
* @param array $active_langs
*/
public function __construct(
WPML_Translation_Element_Factory $element_factory,
WPML_TM_Word_Count_Records $records,
array $calculators,
array $active_langs
) {
$this->element_factory = $element_factory;
$this->records = $records;
$this->post_calculators = $calculators;
$this->active_langs = $active_langs;
}
/**
* @param int $post_id
*/
public function process( $post_id ) {
$this->post_element = $this->element_factory->create( $post_id, 'post' );
$word_count = new WPML_TM_Count();
foreach ( $this->active_langs as $lang ) {
if ( $this->post_element->get_language_code() === $lang ) {
$word_count->set_total_words( $this->calculate_in_lang( null ) );
} else {
$word_count->set_words_to_translate( $lang, $this->calculate_in_lang( $lang ) );
}
}
$this->records->set_post_word_count( $post_id, $word_count );
}
/**
* @param string $lang
*
* @return int
*/
private function calculate_in_lang( $lang ) {
$words = 0;
foreach ( $this->post_calculators as $calculator ) {
$words += $calculator->count_words( $this->post_element, $lang );
}
return $words;
}
}

View File

@@ -0,0 +1,24 @@
<?php
class WPML_TM_Word_Count_Set_String {
/** @var WPML_TM_Word_Count_Records $records */
private $records;
/** @var WPML_TM_Word_Calculator $calculator */
private $calculator;
public function __construct( WPML_TM_Word_Count_Records $records, WPML_TM_Word_Calculator $calculator ) {
$this->records = $records;
$this->calculator = $calculator;
}
/**
* @param int $string_id
*/
public function process( $string_id ) {
$string = $this->records->get_string_value_and_language( $string_id );
$word_count = $this->calculator->count_words( $string->value, $string->language );
$this->records->set_string_word_count( $string_id, $word_count );
}
}

View File

@@ -0,0 +1,34 @@
<?php
class WPML_TM_Word_Count_Setters_Factory {
/**
* @return IWPML_TM_Word_Count_Set[]
*/
public function create() {
global $sitepress;
$records_factory = new WPML_TM_Word_Count_Records_Factory();
$records = $records_factory->create();
$calculator = new WPML_TM_Word_Calculator( new WPML_PHP_Functions() );
$active_langs = array_keys( $sitepress->get_active_languages() );
$post_calculators = array(
new WPML_TM_Word_Calculator_Post_Object( $calculator, new WPML_TM_Word_Calculator_Post_Packages( $records ) ),
new WPML_TM_Word_Calculator_Post_Custom_Fields( $calculator, \WPML\TM\Settings\Repository::getCustomFields() ),
);
$setters = array(
'post' => new WPML_TM_Word_Count_Set_Post( new WPML_Translation_Element_Factory( $sitepress ), $records, $post_calculators, $active_langs ),
);
if ( class_exists( 'WPML_ST_Package_Factory' ) ) {
$setters['string'] = new WPML_TM_Word_Count_Set_String( $records, $calculator );
$setters['package'] = new WPML_TM_Word_Count_Set_Package( new WPML_ST_Package_Factory(), $records, $active_langs );
}
return $setters;
}
}

View File

@@ -0,0 +1,20 @@
<?php
class WPML_TM_Word_Count_Single_Process_Factory {
public function create() {
/** @var wpdb $wpdb */
global $wpdb;
$setters_factory = new WPML_TM_Word_Count_Setters_Factory();
$dependencies_builder = null;
if ( class_exists( 'WPML_ST_String_Dependencies_Builder' ) ) {
$dependencies_builder = new WPML_ST_String_Dependencies_Builder(
new WPML_ST_String_Dependencies_Records( $wpdb )
);
}
return new WPML_TM_Word_Count_Single_Process( $setters_factory->create(), $dependencies_builder );
}
}

View File

@@ -0,0 +1,37 @@
<?php
class WPML_TM_Word_Count_Single_Process {
/** @var IWPML_TM_Word_Count_Set[] $setters */
private $setters;
/** @var WPML_ST_String_Dependencies_Builder $dependencies_builder */
private $dependencies_builder;
/**
* @param IWPML_TM_Word_Count_Set[] $setters
* @param WPML_ST_String_Dependencies_Builder $dependencies_builder
*/
public function __construct( array $setters, WPML_ST_String_Dependencies_Builder $dependencies_builder = null ) {
$this->setters = $setters;
$this->dependencies_builder = $dependencies_builder;
}
/**
* @param string $element_type
* @param int $element_id
*/
public function process( $element_type, $element_id ) {
if ( $this->dependencies_builder ) {
$dependencies_tree = $this->dependencies_builder->from( $element_type, $element_id );
while ( ! $dependencies_tree->iteration_completed() ) {
$node = $dependencies_tree->get_next();
$this->setters[ $node->get_type() ]->process( $node->get_id() );
$node->detach();
}
} elseif ( 'post' === $element_type ) {
$this->setters['post']->process( $element_id );
}
}
}