PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.8
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.8
2.12.8 2.12.7 2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 All 98 releases
← All changes | inc/compatibility/multilingual/string-collector.php +114 -9 2.12.1 → 2.12.8 View file →
@@ -70,8 +70,13 @@
70 70 */
71 71 public function __construct() {
72 72 add_action( 'save_post_' . SRFM_FORMS_POST_TYPE, [ $this, 'on_form_save' ], 20, 1 );
73 73
74 + // Prune the form's String Package when the form is permanently deleted so
75 + // orphaned packages and their translations don't linger. Fires only on
76 + // permanent delete, not on trash (a trashed form may be restored).
77 + add_action( 'before_delete_post', [ $this, 'on_form_delete' ], 10, 1 );
78 +
74 79 // Register the GLOBAL built-in validation strings once per admin request
75 80 // (no-op when no multilingual provider is active). These strings are not
76 81 // per-form, so they belong on an admin/authoring hook rather than on every
77 82 // frontend request. WPML dedupes by (domain, name, value), so re-asserting
@@ -79,11 +84,48 @@
79 84 // the WPML-activated-after-SureForms case.
80 85 if ( is_admin() ) {
81 86 add_action( 'admin_init', [ $this, 'collect_validation_messages' ] );
82 87 }
88 +
89 + // Declare our String Package kind so WPML's "Translate Everything
90 + // Automatically" gate — which reads this filter, not just the per-package
91 + // post association — queues SureForms form packages for auto-translation.
92 + // Registered unconditionally: WPML only fires this filter when active, and
93 + // the callback is a pure array append, so it is a no-op otherwise.
94 + add_filter( 'wpml_active_string_package_kinds', [ $this, 'declare_package_kind' ] );
83 95 }
84 96
85 97 /**
98 + * Declare the SureForms String Package kind to WPML.
99 + *
100 + * WPML's package-level "Translate Everything Automatically" gate reads this
101 + * filter to decide which string-package kinds to auto-translate. Each form is
102 + * registered as one package with kind {@see String_Translator::PACKAGE_KIND},
103 + * from which WPML derives the kind slug via `sanitize_title()`; computing the
104 + * key the same way here guarantees it matches the slug WPML assigns to our
105 + * packages (no hardcoded slug that could drift from the kind label).
106 + *
107 + * @param mixed $kinds Associative map of kind slug => { title, slug, plural }.
108 + * @since 2.12.3
109 + * @return mixed The kinds map with the SureForms Form kind added.
110 + */
111 + public function declare_package_kind( $kinds ) {
112 + if ( ! is_array( $kinds ) ) {
113 + return $kinds;
114 + }
115 +
116 + $slug = sanitize_title( String_Translator::PACKAGE_KIND );
117 +
118 + $kinds[ $slug ] = [
119 + 'title' => String_Translator::PACKAGE_KIND,
120 + 'slug' => $slug,
121 + 'plural' => __( 'SureForms Forms', 'sureforms' ),
122 + ];
123 +
124 + return $kinds;
125 + }
126 +
127 + /**
86 128 * Entry point hooked to save_post_{post_type}.
87 129 *
88 130 * Skips autosaves and revisions, and bails when no multilingual provider is
89 131 * active. Otherwise delegates to {@see collect()} to register all strings.
@@ -110,8 +152,49 @@
110 152 $this->collect( $form_id );
111 153 }
112 154
113 155 /**
156 + * Delete the form's String Package when the form is permanently deleted.
157 + *
158 + * Hooked to before_delete_post (not trash) so a package is only removed when
159 + * its form is gone for good. Bails for other post types and when no provider
160 + * is active.
161 + *
162 + * @param int $form_id The post ID being deleted.
163 + * @since 2.12.3
164 + * @return void
165 + */
166 + public function on_form_delete( int $form_id ): void {
167 + if ( SRFM_FORMS_POST_TYPE !== get_post_type( $form_id ) ) {
168 + return;
169 + }
170 +
171 + $provider = Multilingual_Manager::get_instance()->provider();
172 +
173 + if ( ! $provider->is_active() ) {
174 + return;
175 + }
176 +
177 + // delete_package() is intentionally absent from the Provider interface (see the
178 + // note there): declaring it would fatal any third-party provider written against
179 + // 2.11.0-2.12.2. Feature-detect instead, so a custom provider without it simply
180 + // skips cleanup rather than crashing.
181 + if ( ! method_exists( $provider, 'delete_package' ) ) {
182 + return;
183 + }
184 +
185 + // Deletion only needs the package identity (name + kind); build it directly
186 + // rather than String_Translator::form_package(), which also runs
187 + // get_the_title() / get_edit_post_link() the delete path doesn't use.
188 + $provider->delete_package(
189 + [
190 + 'name' => (string) $form_id,
191 + 'kind' => String_Translator::PACKAGE_KIND,
192 + ]
193 + );
194 + }
195 +
196 + /**
114 197 * Walk the form and register every translatable string with the provider.
115 198 *
116 199 * Public so unit tests can exercise the collection logic directly without
117 200 * needing to fire the save_post action, and so migration code paths can
@@ -118,15 +201,19 @@
118 201 * back-fill strings for existing forms.
119 202 *
120 203 * @param int $form_id The form post ID.
121 204 * @since 2.11.0
122 - * @return void
205 + * @since 2.12.3 Returns whether collection actually ran, so callers (notably the
206 + * backfill) can distinguish "collected" from "silently skipped because
207 + * the provider went inactive" and avoid recording false progress.
208 + * @return bool True when the form's strings were registered, false when the provider
209 + * was unavailable and nothing was done.
123 210 */
124 - public function collect( int $form_id ): void {
211 + public function collect( int $form_id ): bool {
125 212 $provider = Multilingual_Manager::get_instance()->provider();
126 213
127 214 if ( ! $provider->is_active() ) {
128 - return;
215 + return false;
129 216 }
130 217
131 218 // Group every per-form string into a single WPML String Package so they
132 219 // surface together under the form in the Translation Editor (instead of as
@@ -138,8 +225,18 @@
138 225 if ( $this->packages_supported ) {
139 226 $provider->start_package( $package );
140 227 }
141 228
229 + // Form title (post title) — shown as a heading on the form and as the
230 + // instant-form banner, so it is translatable like any other string.
231 + $this->register_form_string(
232 + $form_id,
233 + String_Translator::title_name(),
234 + Helper::get_string_value( get_the_title( $form_id ) ),
235 + // "Group: Leaf" — WPML splits on ': ' to nest this under a Settings group.
236 + __( 'Settings', 'sureforms' ) . ': ' . __( 'Form title', 'sureforms' )
237 + );
238 +
142 239 // Submit button text.
143 240 $this->register_form_string(
144 241 $form_id,
145 242 String_Translator::submit_button_name(),
@@ -183,23 +280,29 @@
183 280
184 281 // reply_to is an email address (or a smart tag resolving to one), not
185 282 // human-readable copy, so it is intentionally excluded from the
186 283 // translatable set. from_name can legitimately be a localized display name.
284 + // [ meta key, label, editor type ]. The string name stays keyed on
285 + // 'body' for continuity, but the value has to be read from
286 + // `email_body` - that is what _srfm_email_notification's sanitize
287 + // callback stores, so reading 'body' always found nothing and the
288 + // message body was never registered for translation at all.
187 289 $fields = [
188 - 'subject' => [ __( 'Subject', 'sureforms' ), 'LINE' ],
189 - 'body' => [ __( 'Message body', 'sureforms' ), 'AREA' ],
190 - 'from_name' => [ __( '"From" name', 'sureforms' ), 'LINE' ],
290 + 'subject' => [ 'subject', __( 'Subject', 'sureforms' ), 'LINE' ],
291 + 'body' => [ 'email_body', __( 'Message body', 'sureforms' ), 'AREA' ],
292 + 'from_name' => [ 'from_name', __( '"From" name', 'sureforms' ), 'LINE' ],
191 293 ];
192 294 /* translators: %d is the notification number. */
193 295 $notification_group = __( 'Notifications', 'sureforms' ) . '/' . sprintf( __( 'Notification #%d', 'sureforms' ), (int) $index + 1 );
194 296 foreach ( $fields as $field => $meta ) {
195 - $value = isset( $notification[ $field ] ) ? Helper::get_string_value( $notification[ $field ] ) : '';
297 + $meta_key = $meta[0];
298 + $value = isset( $notification[ $meta_key ] ) ? Helper::get_string_value( $notification[ $meta_key ] ) : '';
196 299 $this->register_form_string(
197 300 $form_id,
198 301 String_Translator::notification_name( (int) $index, $field ),
199 302 $value,
200 - $notification_group . ': ' . $meta[0],
201 - $meta[1]
303 + $notification_group . ': ' . $meta[1],
304 + $meta[2]
202 305 );
203 306 }
204 307 }
205 308
@@ -225,8 +328,10 @@
225 328 if ( $this->packages_supported ) {
226 329 $provider->finish_package( $package );
227 330 }
228 331 $this->active_package = null;
332 +
333 + return true;
229 334 }
230 335
231 336 /**
232 337 * Register every built-in dynamic validation message with the multilingual