|null */ private $passHashes = null; /** @var bool whether the current pass changed any hash */ private $passChanged = false; /** * Hash shouldRegisterForm() just computed, reused instead of hashing twice. * * @var array{0:int,1:string}|null */ private $pendingHash = null; public function registerStrings($formId, array $contextStrings) { foreach ($contextStrings as $context => $string) { do_action('wpml_register_single_string', self::DOMAIN, TranslationStringName::forForm($context, $formId), $string); } } public function onAdminInit() { add_action('bitform_form_saved', [$this, 'registerFormStrings']); add_action('admin_init', [$this, 'maybeRegisterAllForms']); } public function onAjaxInit($requestLang) { $lang = $this->getCurrentLanguage(); if ('' !== $lang) { do_action('wpml_switch_language', $lang); } } /** * `bitform_form_saved` callback — registers one form's strings. * * @param mixed $formId */ public function registerFormStrings($formId) { $formId = (int) $formId; $strings = RegistrationCollector::collectAllStrings($formId); if ($this->shouldRegisterForm($formId, $strings)) { $this->registerStrings($formId, $strings); $this->afterFormRegistered($formId, $strings); } $this->afterRegistrationPass(); } /** * @param string $page * * @return bool */ protected function isTranslationScreen($page) { return '' !== $page && false !== strpos($page, 'wpml-string-translation'); } /** * Registrations persist and each is a WPML read plus write, so an unchanged * form is skipped. The screen reloads on every filter and page change. * * @param int $formId * @param array $strings * * @return bool */ protected function shouldRegisterForm($formId, array $strings) { if (null === $this->passHashes) { $this->passHashes = self::hashes(); $this->passChanged = false; } $hash = self::hashStrings($strings); $this->pendingHash = [$formId, $hash]; return !isset($this->passHashes[$formId]) || $this->passHashes[$formId] !== $hash; } /** * @param int $formId * @param array $strings */ protected function afterFormRegistered($formId, array $strings) { $hash = (is_array($this->pendingHash) && $formId === $this->pendingHash[0]) ? $this->pendingHash[1] : self::hashStrings($strings); $this->passHashes[$formId] = $hash; $this->passChanged = true; } /** * One option write for the whole pass, not one per form. */ protected function afterRegistrationPass() { if ($this->passChanged && is_array($this->passHashes)) { update_option(self::HASH_OPTION, $this->passHashes, false); } $this->passHashes = null; $this->passChanged = false; $this->pendingHash = null; } /** * Validated bf_lang, else WPML's current language. * * @return string */ protected function detectLanguage() { $requestLang = TranslationManager::getRequestLang(); if ('' !== $requestLang) { $active = apply_filters('wpml_active_languages', null); if (is_array($active) && isset($active[$requestLang])) { return $requestLang; } } $current = apply_filters('wpml_current_language', null); return is_string($current) ? $current : ''; } /** * @param string $string * @param string $context * @param int $formId * * @return mixed */ protected function lookup($string, $context, $formId) { $lang = $this->getCurrentLanguage(); return apply_filters( 'wpml_translate_single_string', $string, self::DOMAIN, TranslationStringName::forForm($context, $formId), '' !== $lang ? $lang : null ); } /** * @return array */ private static function hashes() { $hashes = get_option(self::HASH_OPTION, []); return is_array($hashes) ? $hashes : []; } /** * @param array $strings * * @return string */ private static function hashStrings(array $strings) { $encoded = wp_json_encode($strings); return md5(is_string($encoded) ? $encoded : serialize($strings)); } }