@@ -331,10 +331,7 @@ final class Loader {
331331 * Initialize the sdk logic.
332332 */
333333 public static function init () {
334- /**
335- * This filter can be used to localize the labels inside each product.
336- */
337- self ::$ labels = apply_filters ( 'themeisle_sdk_labels ' , self ::$ labels );
334+ self ::localize_labels ();
338335 if ( ! isset ( self ::$ instance ) && ! ( self ::$ instance instanceof Loader ) ) {
339336 self ::$ instance = new Loader ();
340337 $ modules = array_merge ( self ::$ available_modules , apply_filters ( 'themeisle_sdk_modules ' , [] ) );
@@ -346,8 +343,90 @@ public static function init() {
346343 self ::$ available_modules = $ modules ;
347344
348345 add_action ( 'themeisle_sdk_first_activation ' , array ( __CLASS__ , 'activate ' ) );
346+
349347 }
350348 }
349+
350+ /**
351+ * Localize the labels.
352+ */
353+ public static function localize_labels () {
354+ $ originals = self ::$ labels ;
355+ $ all_translations = [];
356+
357+ global $ wp_filter ;
358+ if ( isset ( $ wp_filter ['themeisle_sdk_labels ' ] ) ) {
359+ foreach ( $ wp_filter ['themeisle_sdk_labels ' ]->callbacks as $ priority => $ hooks ) {
360+ foreach ( $ hooks as $ hook ) {
361+ // Each callback gets fresh originals, not previous callback's output
362+ $ result = call_user_func ( $ hook ['function ' ], $ originals );
363+ $ all_translations [] = $ result ;
364+ }
365+ }
366+
367+ // Remove the filter so it doesn't run again via apply_filters
368+ remove_all_filters ( 'themeisle_sdk_labels ' );
369+ }
370+
371+ // Merge all results, first real translation wins
372+ self ::$ labels = self ::merge_all_translations ( $ originals , $ all_translations );
373+ }
374+ /**
375+ * Merge all translations.
376+ *
377+ * @param array $originals The original labels.
378+ * @param array $all_translations The all translations.
379+ *
380+ * @return array The merged labels.
381+ */
382+ private static function merge_all_translations ( $ originals , $ all_translations ) {
383+ $ result = $ originals ;
384+
385+ foreach ( $ all_translations as $ translations ) {
386+ $ result = self ::merge_if_translated ( $ result , $ translations , $ originals );
387+ }
388+
389+ return $ result ;
390+ }
391+ /**
392+ * Merge if translated.
393+ *
394+ * @param array $current The current labels.
395+ * @param array $new The new labels.
396+ * @param array $originals The original labels.
397+ * @return array The merged labels.
398+ */
399+ private static function merge_if_translated ( $ current , $ new , $ originals ) {
400+ foreach ( $ new as $ key => $ value ) {
401+ if ( ! isset ( $ originals [ $ key ] ) ) {
402+ // New key, accept it
403+ if ( ! isset ( $ current [ $ key ] ) ) {
404+ $ current [ $ key ] = $ value ;
405+ }
406+ continue ;
407+ }
408+
409+ if ( is_array ( $ value ) && is_array ( $ originals [ $ key ] ) ) {
410+ $ current [ $ key ] = self ::merge_if_translated (
411+ $ current [ $ key ],
412+ $ value ,
413+ $ originals [ $ key ]
414+ );
415+ } else {
416+ // Only accept if:
417+ // 1. New value is actually translated (differs from original)
418+ // 2. Current value is NOT already translated
419+ $ is_new_translated = ( $ value !== $ originals [ $ key ] );
420+ $ is_current_untranslated = ( $ current [ $ key ] === $ originals [ $ key ] );
421+
422+ if ( $ is_new_translated && $ is_current_untranslated ) {
423+ $ current [ $ key ] = $ value ;
424+ }
425+ }
426+ }
427+
428+ return $ current ;
429+ }
351430
352431 /**
353432 * Get cache token used in API requests.
0 commit comments