PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 All 138 releases
bit-form / includes / Core / Util / Translation / Provider / WpmlProvider.php

WpmlProvider.php in Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder 3.3.1, at includes/Core/Util/Translation/Provider/WpmlProvider.php

195 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace BitCode\BitForm\Core\Util\Translation\Provider;
4
5 use BitCode\BitForm\Core\Util\Translation\Support\AbstractStringRegistrationProvider;
6 use BitCode\BitForm\Core\Util\Translation\TranslationManager;
7 use BitCode\BitForm\Core\Util\Translation\TranslationStringName;
8
9 /**
10 * WPML adapter (String Translation module).
11 *
12 * Frontend/AJAX are lookup-only. Register-at-render is WPML's documented top
13 * cause of icl_strings bloat, so registration happens on form save
14 * (`bitform_form_saved`) and as a backfill when WPML's own screen is opened.
15 */
16 final class WpmlProvider extends AbstractStringRegistrationProvider
17 {
18 public const DOMAIN = 'bit-form';
19
20 /**
21 * Per-form hash of the last registered string set. WPML persists registrations,
22 * so re-pushing an unchanged form is pure write amplification.
23 */
24 public const HASH_OPTION = 'bitforms_wpml_string_hashes';
25
26 /**
27 * Hash state for the current backfill pass, written back once at the end.
28 *
29 * @var array<int,string>|null
30 */
31 private $passHashes = null;
32
33 /** @var bool whether the current pass changed any hash */
34 private $passChanged = false;
35
36 /**
37 * Hash shouldRegisterForm() just computed, reused instead of hashing twice.
38 *
39 * @var array{0:int,1:string}|null
40 */
41 private $pendingHash = null;
42
43 public function registerStrings($formId, array $contextStrings)
44 {
45 foreach ($contextStrings as $context => $string) {
46 do_action('wpml_register_single_string', self::DOMAIN, TranslationStringName::forForm($context, $formId), $string);
47 }
48 }
49
50 public function onAdminInit()
51 {
52 add_action('bitform_form_saved', [$this, 'registerFormStrings']);
53 add_action('admin_init', [$this, 'maybeRegisterAllForms']);
54 }
55
56 public function onAjaxInit($requestLang)
57 {
58 $lang = $this->getCurrentLanguage();
59 if ('' !== $lang) {
60 do_action('wpml_switch_language', $lang);
61 }
62 }
63
64 /**
65 * `bitform_form_saved` callback — registers one form's strings.
66 *
67 * @param mixed $formId
68 */
69 public function registerFormStrings($formId)
70 {
71 $formId = (int) $formId;
72 $strings = RegistrationCollector::collectAllStrings($formId);
73
74 if ($this->shouldRegisterForm($formId, $strings)) {
75 $this->registerStrings($formId, $strings);
76 $this->afterFormRegistered($formId, $strings);
77 }
78
79 $this->afterRegistrationPass();
80 }
81
82 /**
83 * @param string $page
84 *
85 * @return bool
86 */
87 protected function isTranslationScreen($page)
88 {
89 return '' !== $page && false !== strpos($page, 'wpml-string-translation');
90 }
91
92 /**
93 * Registrations persist and each is a WPML read plus write, so an unchanged
94 * form is skipped. The screen reloads on every filter and page change.
95 *
96 * @param int $formId
97 * @param array<string,string> $strings
98 *
99 * @return bool
100 */
101 protected function shouldRegisterForm($formId, array $strings)
102 {
103 if (null === $this->passHashes) {
104 $this->passHashes = self::hashes();
105 $this->passChanged = false;
106 }
107 $hash = self::hashStrings($strings);
108 $this->pendingHash = [$formId, $hash];
109 return !isset($this->passHashes[$formId]) || $this->passHashes[$formId] !== $hash;
110 }
111
112 /**
113 * @param int $formId
114 * @param array<string,string> $strings
115 */
116 protected function afterFormRegistered($formId, array $strings)
117 {
118 $hash = (is_array($this->pendingHash) && $formId === $this->pendingHash[0])
119 ? $this->pendingHash[1]
120 : self::hashStrings($strings);
121 $this->passHashes[$formId] = $hash;
122 $this->passChanged = true;
123 }
124
125 /**
126 * One option write for the whole pass, not one per form.
127 */
128 protected function afterRegistrationPass()
129 {
130 if ($this->passChanged && is_array($this->passHashes)) {
131 update_option(self::HASH_OPTION, $this->passHashes, false);
132 }
133 $this->passHashes = null;
134 $this->passChanged = false;
135 $this->pendingHash = null;
136 }
137
138 /**
139 * Validated bf_lang, else WPML's current language.
140 *
141 * @return string
142 */
143 protected function detectLanguage()
144 {
145 $requestLang = TranslationManager::getRequestLang();
146 if ('' !== $requestLang) {
147 $active = apply_filters('wpml_active_languages', null);
148 if (is_array($active) && isset($active[$requestLang])) {
149 return $requestLang;
150 }
151 }
152 $current = apply_filters('wpml_current_language', null);
153 return is_string($current) ? $current : '';
154 }
155
156 /**
157 * @param string $string
158 * @param string $context
159 * @param int $formId
160 *
161 * @return mixed
162 */
163 protected function lookup($string, $context, $formId)
164 {
165 $lang = $this->getCurrentLanguage();
166 return apply_filters(
167 'wpml_translate_single_string',
168 $string,
169 self::DOMAIN,
170 TranslationStringName::forForm($context, $formId),
171 '' !== $lang ? $lang : null
172 );
173 }
174
175 /**
176 * @return array<int,string>
177 */
178 private static function hashes()
179 {
180 $hashes = get_option(self::HASH_OPTION, []);
181 return is_array($hashes) ? $hashes : [];
182 }
183
184 /**
185 * @param array<string,string> $strings
186 *
187 * @return string
188 */
189 private static function hashStrings(array $strings)
190 {
191 $encoded = wp_json_encode($strings);
192 return md5(is_string($encoded) ? $encoded : serialize($strings));
193 }
194 }
195