WooCommerce Multilingual – run WooCommerce with WPML - Version 2.3

Version Description

  • Refactor translation and currency conversion of products & variations in cart
  • A problem we had with shipping selection was resolved in WooCommerce itself
  • Improved synchronization of global product attributes, whether used for variations or not
  • Custom product attributes registered as strings when defined in the backend
  • Don't adjust the currency symbol in WooCommerce settings page
  • Term and product category order is synchronized among languages
  • Additional filters for WooCommerce emails
  • Fixed layered nav widgets in translated shop page
  • Synchronize Product Categories
Download this release

Release Info

Developer dgwatkins
Plugin Icon 128x128 WooCommerce Multilingual – run WooCommerce with WPML
Version 2.3
Comparing to
See all releases

Code changes from version 2.2 to 2.3

Files changed (3) hide show
  1. readme.txt +17 -2
  2. woocommerce_wpml.class.php +258 -174
  3. wpml-woocommerce.php +2 -2
readme.txt CHANGED
@@ -1,11 +1,11 @@
1
  === WooCommerce Multilingual - run WooCommerce with WPML ===
2
- Contributors: AmirHelzer, dominykasgel, dgwatkins
3
  Donate link: http://wpml.org/documentation/related-projects/woocommerce-multilingual/
4
  Tags: CMS, woocommerce, commerce, ecommerce, e-commerce, products, WPML, multilingual, e-shop, shop
5
  License: GPLv2
6
  Requires at least: 3.0
7
  Tested up to: 3.5
8
- Stable tag: 2.2
9
 
10
  Allows running fully multilingual e-commerce sites using WooCommerce and WPML.
11
 
@@ -68,6 +68,21 @@ In order for the checkout and store pages to appear translated, you need to crea
68
 
69
  == Changelog ==
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  = 2.1 =
72
  * Add admin notices for required plugins
73
  * Add support for 'Review Order' and 'Lost Password' pages
1
  === WooCommerce Multilingual - run WooCommerce with WPML ===
2
+ Contributors: AmirHelzer, dominykasgel, dgwatkins, adelval
3
  Donate link: http://wpml.org/documentation/related-projects/woocommerce-multilingual/
4
  Tags: CMS, woocommerce, commerce, ecommerce, e-commerce, products, WPML, multilingual, e-shop, shop
5
  License: GPLv2
6
  Requires at least: 3.0
7
  Tested up to: 3.5
8
+ Stable tag: 2.3
9
 
10
  Allows running fully multilingual e-commerce sites using WooCommerce and WPML.
11
 
68
 
69
  == Changelog ==
70
 
71
+ = 2.3 =
72
+ * Refactor translation and currency conversion of products & variations in cart
73
+ * A problem we had with shipping selection was resolved in WooCommerce itself
74
+ * Improved synchronization of global product attributes, whether used for variations or not
75
+ * Custom product attributes registered as strings when defined in the backend
76
+ * Don't adjust the currency symbol in WooCommerce settings page
77
+ * Term and product category order is synchronized among languages
78
+ * Additional filters for WooCommerce emails
79
+ * Fixed layered nav widgets in translated shop page
80
+ * Synchronize Product Categories
81
+
82
+ = 2.2 =
83
+ * Price in mini-cart refreshed when changing language
84
+ * Fix bug in multilingual currency setting that slipped in 2.1
85
+
86
  = 2.1 =
87
  * Add admin notices for required plugins
88
  * Add support for 'Review Order' and 'Lost Password' pages
woocommerce_wpml.class.php CHANGED
@@ -64,8 +64,6 @@ class woocommerce_wpml {
64
  add_filter('woocommerce_get_checkout_payment_url', array($this, 'do_redirect'));
65
  add_filter('woocommerce_get_cancel_order_url', array($this, 'do_redirect'));
66
  add_filter('woocommerce_get_return_url', array($this, 'do_redirect'));
67
- add_filter('woocommerce_in_cart_product_title', array($this, 'in_cart_product_title'), 13, 2);
68
- add_filter('woocommerce_in_cart_product_id', array($this, 'in_cart_product_id'), 11, 2);
69
  add_filter('woocommerce_params', array($this, 'ajax_params'));
70
  add_filter('woocommerce_redirect', array($this, 'do_redirect'));
71
  add_filter('woocommerce_attribute_label', array($this, 'translate_attributes'), 14, 2);
@@ -78,8 +76,10 @@ class woocommerce_wpml {
78
  add_filter('woocommerce_json_search_found_products', array($this, 'search_products'));
79
  add_filter('woocommerce_currency', array($this, 'set_ml_currency'));
80
  add_action('admin_print_scripts', array($this,'js_scripts_setup'), 11);
81
- add_action('wp_head', array($this, 'refresh_cart'));
82
- add_action('init', array($this, 'translate_email_notifications'));
 
 
83
 
84
  // Slug translation
85
  add_filter('gettext_with_context', array($this, 'default_slug_translation'), 0, 4);
@@ -104,15 +104,20 @@ class woocommerce_wpml {
104
  add_filter('raw_woocommerce_price', array($this, 'woocommerce_price'));
105
  add_filter('woocommerce_order_amount_total', array($this, 'woocommerce_price'));
106
  add_filter('woocommerce_order_amount_item_total', array($this, 'woocommerce_price'));
 
107
  add_filter('woocommerce_order_amount_shipping', array($this, 'woocommerce_price'));
108
  add_filter('woocommerce_order_amount_total_tax', array($this, 'woocommerce_price'));
 
109
  }
110
-
111
  add_filter('woocommerce_currency_symbol', array($this, 'woocommerce_currency_symbol'), 2);
112
  }
113
 
114
- add_action('woocommerce_email_header', array($this, 'email_header'), 0);
 
 
 
115
  add_action('woocommerce_email_footer', array($this, 'email_footer'), 0);
 
116
  add_action('localize_woocommerce_on_ajax', array($this, 'localize_on_ajax'));
117
  add_action('woocommerce_shipping_update_ajax', array($this, 'shipping_update'));
118
 
@@ -134,6 +139,7 @@ class woocommerce_wpml {
134
  } else {
135
  add_filter('pre_get_posts', array($this, 'shop_page_query'), 9);
136
  add_filter('icl_ls_languages', array($this, 'translate_ls_shop_url'));
 
137
  }
138
 
139
  // Hooks for translating product attribute values
@@ -242,8 +248,11 @@ class woocommerce_wpml {
242
  add_filter('woocommerce_available_shipping_methods', array($this, 'register_shipping_methods'));
243
  add_filter('woocommerce_countries_tax_or_vat', array($this, 'register_tax_label'));
244
  add_action('option_woocommerce_tax_rates', array($this, 'tax_rates'));
 
 
 
245
  }
246
-
247
  function set_price_config() {
248
  global $sitepress, $iclTranslationManagement;
249
 
@@ -335,8 +344,64 @@ class woocommerce_wpml {
335
  return icl_object_id($id, 'page', true);
336
  }
337
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  function translate_email_notifications() {
339
- $email_actions = array(
340
  'woocommerce_order_status_pending_to_processing',
341
  'woocommerce_order_status_pending_to_completed',
342
  'woocommerce_order_status_pending_to_on-hold',
@@ -344,10 +409,21 @@ class woocommerce_wpml {
344
  'woocommerce_order_status_failed_to_completed',
345
  'woocommerce_order_status_pending_to_processing',
346
  'woocommerce_order_status_pending_to_on-hold',
347
- 'woocommerce_order_status_completed'
 
 
 
 
 
 
 
 
348
  );
 
 
 
349
  foreach ( $email_actions as $action ) {
350
- add_action( $action, array( &$this, 'translate_email_notification'), 9 );
351
  }
352
  }
353
 
@@ -362,22 +438,7 @@ class woocommerce_wpml {
362
 
363
  }
364
 
365
- function translate_ls_shop_url($languages) {
366
- global $sitepress;
367
- $shop_id = get_option('woocommerce_shop_page_id');
368
- $front_id = icl_object_id(get_option('page_on_front'), 'page');
369
- if (is_post_type_archive('product')) {
370
- foreach ($languages as &$language) {
371
- if ($front_id == $shop_id) {
372
- $url = $sitepress->language_url($language['language_code']);
373
- } else {
374
- $url = get_permalink(icl_object_id($shop_id, 'page', true, $language['language_code']));
375
- }
376
- $language['url'] = $url;
377
- }
378
- }
379
- return $languages;
380
- }
381
 
382
  /**
383
  * Adds admin notice.
@@ -574,38 +635,6 @@ class woocommerce_wpml {
574
  return get_permalink(icl_object_id(get_option('woocommerce_checkout_page_id'), 'page', true));
575
  }
576
 
577
- /**
578
- * After email translation switch language to default.
579
- *
580
- * @param type $title
581
- * @param $_product
582
- * @return type
583
- */
584
- function in_cart_product_title($title, $_product){
585
- if (is_array($_product)) {
586
- $product_id = $_product['product_id']; // backward compatibility
587
- } else {
588
- $product_id = $_product->id;
589
- }
590
- $product_id = icl_object_id($product_id, 'product', false, ICL_LANGUAGE_CODE);
591
-
592
- if($product_id){
593
- $title = get_the_title($product_id);
594
- }
595
-
596
- return $title;
597
- }
598
-
599
- /**
600
- * Adjusts WooCommerce product ID to be added in cart (original product ID).
601
- *
602
- * @param type $product_id
603
- * @return type
604
- */
605
- function in_cart_product_id($product_id) {
606
- return icl_object_id($product_id, 'product', true);
607
- }
608
-
609
  /**
610
  * Filters WooCommerce AJAX params
611
  *
@@ -671,7 +700,6 @@ class woocommerce_wpml {
671
  function translate_attributes($name){
672
  if(function_exists('icl_register_string')){
673
  icl_register_string('woocommerce', $name .'_attribute', $name);
674
-
675
  $name = icl_t('woocommerce', $name .'_attribute', $name);
676
  }
677
 
@@ -739,11 +767,7 @@ class woocommerce_wpml {
739
  $out = array();
740
  foreach ($terms as $term) {
741
  $term = trim($term);
742
- if ($sitepress->get_default_language() == $sitepress->get_current_language()) {
743
- $term = icl_translate('woocommerce', $term .'_attribute_name', $term);
744
- } else {
745
- $term = icl_t('woocommerce', $term .'_attribute_name', $term);
746
- }
747
  $out[] = $term;
748
  }
749
 
@@ -810,25 +834,47 @@ class woocommerce_wpml {
810
  }
811
  }
812
 
813
- /**
814
- * Filters the currency symbol.
815
- */
816
- function woocommerce_currency_symbol($currency_symbol){
817
- global $sitepress, $wpdb;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
818
 
819
- $db_currency = $wpdb->get_row("SELECT code FROM ". $wpdb->prefix ."icl_currencies WHERE language_code = '". $sitepress->get_current_language() ."'");
 
 
 
 
 
 
820
 
821
- if($db_currency && get_option('icl_enable_multi_currency') == 'yes'){
822
- $db_currency = $db_currency->code;
823
- if(in_array($db_currency, array_keys($this->currencies))){
824
- $currency_symbol = $this->currencies[$db_currency];
825
- } else {
826
- $currency_symbol = $db_currency;
827
- }
828
- }
829
 
830
- return $currency_symbol;
831
- }
 
 
 
 
 
 
 
 
 
832
 
833
  /**
834
  * Filters the product price.
@@ -850,59 +896,11 @@ class woocommerce_wpml {
850
  return $price;
851
  }
852
 
853
- /**
854
- * Translates WooCommerce emails.
855
- *
856
- * @global type $sitepress
857
- * @global type $order_id
858
- * @return type
859
- */
860
- function email_header() {
861
- global $sitepress, $order_id;
862
-
863
- $lang = get_post_meta($order_id, 'wpml_language', TRUE);
864
-
865
- if(empty($lang)){
866
- if(isset($_SESSION['wpml_globalcart_language'])){
867
- $lang = $_SESSION['wpml_globalcart_language'];
868
- } else {
869
- $lang = $sitepress->get_current_language();
870
- }
871
- }
872
-
873
- $sitepress->switch_lang($lang, true);
874
- }
875
-
876
- /**
877
- * After email translation switch language to default.
878
- *
879
- * @global type $sitepress
880
- * @return type
881
- */
882
- function email_footer() {
883
- global $sitepress;
884
-
885
- $sitepress->switch_lang();
886
- }
887
-
888
- /*
889
- * WC compat layer: get product id
890
- */
891
-
892
- function get_product_id_from_order_item($item) {
893
- if ( version_compare( WOOCOMMERCE_VERSION, "2.0.0" ) >= 0 ) {
894
- // WC 2.0
895
- return isset($item['product_id']) ? $item['product_id'] : false;
896
- } else {
897
- return isset($item['product_id']) ? $item['product_id'] : false;
898
- }
899
- }
900
-
901
-
902
  /*
903
  * WC compat layer: get product
904
  */
905
-
906
  function wcml_get_product($product_id) {
907
  if ( version_compare( WOOCOMMERCE_VERSION, "2.0.0" ) >= 0 ) {
908
  // WC 2.0
@@ -1107,6 +1105,8 @@ class woocommerce_wpml {
1107
  }
1108
  }
1109
 
 
 
1110
  foreach ($posts as $post_id => $translation) {
1111
  $lang = $translation->language_code;
1112
 
@@ -1175,19 +1175,61 @@ class woocommerce_wpml {
1175
  $wpdb->update($wpdb->postmeta, $data, $where);
1176
  }
1177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1178
  $get_variation_term_id = $wpdb->get_var("SELECT term_id FROM $wpdb->terms WHERE name = 'variable'");
1179
  $get_variation_term_taxonomy_id = $wpdb->get_var("SELECT tt.term_taxonomy_id FROM $wpdb->term_relationships tr
1180
  LEFT JOIN $wpdb->term_taxonomy tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
1181
- WHERE object_id = '$duplicated_post_id' AND taxonomy = 'product_type'");
1182
 
1183
  $is_post_has_variations = $wpdb->get_results("SELECT * FROM $wpdb->term_relationships WHERE object_id = '$duplicated_post_id' AND term_taxonomy_id = '$get_variation_term_taxonomy_id'");
1184
  if(!empty($is_post_has_variations)) $is_post_has_variations = TRUE;
1185
 
1186
- // synchronize term data, postmeta and post variations
1187
  if($is_post_has_variations){
1188
  // synchronize post variations
1189
  $get_all_post_variations = $wpdb->get_results("SELECT * FROM $wpdb->posts
1190
- WHERE post_status = 'publish' AND post_type = 'product_variation' AND post_parent = '$duplicated_post_id'");
1191
 
1192
  $duplicated_post_variation_ids = array();
1193
  foreach($get_all_post_variations as $k => $post_data){
@@ -1254,14 +1296,13 @@ class woocommerce_wpml {
1254
  'comment_count' => $post_data->comment_count
1255
  ));
1256
  update_post_meta($variation_id, '_wcml_duplicate_of_variation', $post_data->ID);
1257
- update_post_meta($variation_id, '_icl_duplicate_of', $post_data->ID);
1258
  $trid = $sitepress->get_element_trid($post_data->ID, 'post_product_variation');
1259
- $sitepress->set_element_language_details($variation_id, 'post_product_variation', $trid, $lang, $sitepress->get_default_language());
1260
  }
1261
  }
1262
 
1263
  $get_current_post_variations = $wpdb->get_results("SELECT * FROM $wpdb->posts
1264
- WHERE post_status = 'publish' AND post_type = 'product_variation' AND post_parent = '$post_id'");
1265
 
1266
  // Delete variations that no longer exist
1267
  foreach ($get_current_post_variations as $post_data) {
@@ -1280,7 +1321,6 @@ class woocommerce_wpml {
1280
  $current_post_variation_ids[] = $post_data->ID;
1281
  }
1282
 
1283
- $taxs = array();
1284
  foreach($duplicated_post_variation_ids as $dp_key => $duplicated_post_variation_id){
1285
  $get_all_post_meta = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE post_id = '$duplicated_post_variation_id'");
1286
 
@@ -1293,7 +1333,6 @@ class woocommerce_wpml {
1293
  if (substr($meta_key, 0, 10) == 'attribute_') {
1294
  $tax = substr($meta_key, 10);
1295
  if (taxonomy_exists($tax)) {
1296
- $taxs[] = $tax;
1297
  $attid = get_term_by('slug', $meta_value, $tax)->term_taxonomy_id;
1298
  $trid = $sitepress->get_element_trid($attid, 'tax_' . $tax);
1299
  if ($trid) {
@@ -1311,27 +1350,63 @@ class woocommerce_wpml {
1311
  }
1312
  }
1313
 
1314
- // Sync terms
1315
- $taxs = array_unique($taxs);
1316
- foreach ($taxs as $tax) {
1317
- $terms = wp_get_object_terms($duplicated_post_id, $tax);
1318
- $update = array();
1319
- foreach ($terms as $term) {
1320
- $trid = $sitepress->get_element_trid($term->term_taxonomy_id, 'tax_' . $tax);
1321
- $translations = $sitepress->get_element_translations($trid,'tax_' . $tax);
1322
- if (isset($translations[$lang])) {
1323
- $update[] = intval($translations[$lang]->term_id);
1324
- }
1325
- }
1326
- wp_set_object_terms($post_id, $update, $tax);
1327
- }
1328
 
1329
- }
 
 
 
 
 
1330
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1331
  }
 
 
 
1332
 
1333
  }
1334
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1335
  /**
1336
  * Creates WCML page.
1337
  */
@@ -1484,29 +1559,38 @@ class woocommerce_wpml {
1484
  <?php
1485
  }
1486
  }
1487
-
1488
- function refresh_cart() {
1489
- ?>
1490
- <script type="text/javascript">
1491
- jQuery(document).ready(function($) {
1492
- if ( $( '.widget_shopping_cart_content' ) ) {
1493
- setTimeout(function() {
1494
- if ($.cookie('wcml-previous-language') != '<?php echo ICL_LANGUAGE_CODE ?>') {
1495
- $.cookie('wcml-previous-language', '<?php echo ICL_LANGUAGE_CODE ?>');
1496
- $.ajax( $fragment_refresh );
1497
- }
1498
- }, 0);
 
 
 
 
 
 
 
 
 
1499
  }
1500
- });
1501
- </script>
1502
- <?php
1503
  }
1504
 
1505
- /**
1506
- * WooCommerce Multilingual deactivation hook.
1507
- */
1508
- function wcml_deactivate(){
1509
- delete_option('wpml_dismiss_doc_main');
1510
- }
1511
 
1512
  }
64
  add_filter('woocommerce_get_checkout_payment_url', array($this, 'do_redirect'));
65
  add_filter('woocommerce_get_cancel_order_url', array($this, 'do_redirect'));
66
  add_filter('woocommerce_get_return_url', array($this, 'do_redirect'));
 
 
67
  add_filter('woocommerce_params', array($this, 'ajax_params'));
68
  add_filter('woocommerce_redirect', array($this, 'do_redirect'));
69
  add_filter('woocommerce_attribute_label', array($this, 'translate_attributes'), 14, 2);
76
  add_filter('woocommerce_json_search_found_products', array($this, 'search_products'));
77
  add_filter('woocommerce_currency', array($this, 'set_ml_currency'));
78
  add_action('admin_print_scripts', array($this,'js_scripts_setup'), 11);
79
+
80
+ // cart functions
81
+ add_action('woocommerce_get_cart_item_from_session', array($this, 'translate_cart_contents'), 10, 3);
82
+ add_action('woocommerce_cart_loaded_from_session', array($this, 'translate_cart_subtotal'));
83
 
84
  // Slug translation
85
  add_filter('gettext_with_context', array($this, 'default_slug_translation'), 0, 4);
104
  add_filter('raw_woocommerce_price', array($this, 'woocommerce_price'));
105
  add_filter('woocommerce_order_amount_total', array($this, 'woocommerce_price'));
106
  add_filter('woocommerce_order_amount_item_total', array($this, 'woocommerce_price'));
107
+ add_filter('woocommerce_order_amount_item_subtotal', array($this, 'woocommerce_price'));
108
  add_filter('woocommerce_order_amount_shipping', array($this, 'woocommerce_price'));
109
  add_filter('woocommerce_order_amount_total_tax', array($this, 'woocommerce_price'));
110
+ add_filter('woocommerce_order_amount_cart_discount',array($this,'woocommerce_price'));
111
  }
 
112
  add_filter('woocommerce_currency_symbol', array($this, 'woocommerce_currency_symbol'), 2);
113
  }
114
 
115
+ //emails
116
+ add_action('init', array($this, 'translate_email_notifications'));
117
+ //wrappers for email's body
118
+ add_action('woocommerce_email_header', array($this, 'email_header'), 0);
119
  add_action('woocommerce_email_footer', array($this, 'email_footer'), 0);
120
+
121
  add_action('localize_woocommerce_on_ajax', array($this, 'localize_on_ajax'));
122
  add_action('woocommerce_shipping_update_ajax', array($this, 'shipping_update'));
123
 
139
  } else {
140
  add_filter('pre_get_posts', array($this, 'shop_page_query'), 9);
141
  add_filter('icl_ls_languages', array($this, 'translate_ls_shop_url'));
142
+ add_filter('parse_request', array($this, 'adjust_shop_page'));
143
  }
144
 
145
  // Hooks for translating product attribute values
248
  add_filter('woocommerce_available_shipping_methods', array($this, 'register_shipping_methods'));
249
  add_filter('woocommerce_countries_tax_or_vat', array($this, 'register_tax_label'));
250
  add_action('option_woocommerce_tax_rates', array($this, 'tax_rates'));
251
+ add_action('updated_post_meta', array($this,'update_post_meta'), 100, 4);
252
+ add_action('added_post_meta', array($this,'update_post_meta'), 100, 4);
253
+ add_action('updated_woocommerce_term_meta',array($this,'sync_term_order'), 100,4);
254
  }
255
+
256
  function set_price_config() {
257
  global $sitepress, $iclTranslationManagement;
258
 
344
  return icl_object_id($id, 'page', true);
345
  }
346
 
347
+ /**
348
+ * Translate shop url
349
+ */
350
+ function translate_ls_shop_url($languages) {
351
+ global $sitepress;
352
+ $shop_id = get_option('woocommerce_shop_page_id');
353
+ $front_id = icl_object_id(get_option('page_on_front'), 'page');
354
+ if (is_post_type_archive('product')) {
355
+ foreach ($languages as &$language) {
356
+ if ($front_id == $shop_id) {
357
+ $url = $sitepress->language_url($language['language_code']);
358
+ } else {
359
+ $url = get_permalink(icl_object_id($shop_id, 'page', true, $language['language_code']));
360
+ }
361
+ $language['url'] = $url;
362
+ }
363
+ }
364
+ return $languages;
365
+ }
366
+
367
+ /**
368
+ * Translate WooCommerce emails.
369
+ *
370
+ * @global type $sitepress
371
+ * @global type $order_id
372
+ * @return type
373
+ */
374
+ function email_header() {
375
+ //note: $_SESSION is not used since Woocommerce 2.0, but this is set by us
376
+ //we could actually set $woocommerce->session->wpml_language instead and avoid db access
377
+ global $sitepress, $order_id;
378
+ $lang = get_post_meta($order_id, 'wpml_language', TRUE);
379
+
380
+ if(empty($lang)){
381
+ if(isset($_SESSION['wpml_globalcart_language'])){
382
+ $lang = $_SESSION['wpml_globalcart_language'];
383
+ } else {
384
+ $lang = $sitepress->get_current_language();
385
+ }
386
+ }
387
+
388
+ $sitepress->switch_lang($lang, true);
389
+ }
390
+
391
+ /**
392
+ * After email translation switch language to default.
393
+ *
394
+ * @global type $sitepress
395
+ * @return type
396
+ */
397
+ function email_footer() {
398
+ global $sitepress;
399
+
400
+ $sitepress->switch_lang();
401
+ }
402
+
403
  function translate_email_notifications() {
404
+ $email_actions = array( //the first ones worked for WC 1.6
405
  'woocommerce_order_status_pending_to_processing',
406
  'woocommerce_order_status_pending_to_completed',
407
  'woocommerce_order_status_pending_to_on-hold',
409
  'woocommerce_order_status_failed_to_completed',
410
  'woocommerce_order_status_pending_to_processing',
411
  'woocommerce_order_status_pending_to_on-hold',
412
+ 'woocommerce_order_status_completed',
413
+ //next are new, at least since WC 2.04; they also began to add the postfix '_notification'
414
+ //not clear they are consistent accross versions between 2.0 and 2.0.8, so adding them without postfix as well
415
+ 'woocommerce_new_customer_note',
416
+ 'woocommerce_reset_password'
417
+ /* the following are not added because messages to admins should not switch language; left here for reference of other _notification actions
418
+ 'woocommerce_low_stock',
419
+ 'woocommerce_no_stock',
420
+ 'woocommerce_product_on_backorder'*/
421
  );
422
+ //added for WC 2.0.x
423
+ $email_actions = array_merge($email_actions,array_map(function($val) {return $val.'_notification';},$email_actions));
424
+
425
  foreach ( $email_actions as $action ) {
426
+ add_action( $action, array($this, 'translate_email_notification'), 9 );
427
  }
428
  }
429
 
438
 
439
  }
440
 
441
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
 
443
  /**
444
  * Adds admin notice.
635
  return get_permalink(icl_object_id(get_option('woocommerce_checkout_page_id'), 'page', true));
636
  }
637
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
  /**
639
  * Filters WooCommerce AJAX params
640
  *
700
  function translate_attributes($name){
701
  if(function_exists('icl_register_string')){
702
  icl_register_string('woocommerce', $name .'_attribute', $name);
 
703
  $name = icl_t('woocommerce', $name .'_attribute', $name);
704
  }
705
 
767
  $out = array();
768
  foreach ($terms as $term) {
769
  $term = trim($term);
770
+ $term = icl_t('woocommerce', $term .'_attribute_name', $term);
 
 
 
 
771
  $out[] = $term;
772
  }
773
 
834
  }
835
  }
836
 
837
+ function adjust_shop_page($q) {
838
+ global $sitepress;
839
+ if ($sitepress->get_default_language() != $sitepress->get_current_language()) {
840
+ if (!empty($q->query_vars['pagename'])) {
841
+ $shop_page = get_post( woocommerce_get_page_id('shop') );
842
+ if ($shop_page->post_name == $q->query_vars['pagename']) {
843
+ unset($q->query_vars['page']);
844
+ unset($q->query_vars['pagename']);
845
+ $q->query_vars['post_type'] = 'product';
846
+ }
847
+ }
848
+ }
849
+ }
850
+
851
+ /**
852
+ * Filters the currency symbol.
853
+ */
854
+ function woocommerce_currency_symbol($currency_symbol){
855
+ global $sitepress, $wpdb;
856
 
857
+ // Dont process currency symbols in the settings screen
858
+ if(function_exists('get_current_screen')) {
859
+ $screen = get_current_screen();
860
+ if (!empty($screen) && $screen->id == 'woocommerce_page_woocommerce_settings') {
861
+ return $currency_symbol;
862
+ }
863
+ }
864
 
865
+ $db_currency = $wpdb->get_row("SELECT code FROM ". $wpdb->prefix ."icl_currencies WHERE language_code = '". $sitepress->get_current_language() ."'");
 
 
 
 
 
 
 
866
 
867
+ if($db_currency && get_option('icl_enable_multi_currency') == 'yes'){
868
+ $db_currency = $db_currency->code;
869
+ if(in_array($db_currency, array_keys($this->currencies))){
870
+ $currency_symbol = $this->currencies[$db_currency];
871
+ } else {
872
+ $currency_symbol = $db_currency;
873
+ }
874
+ }
875
+
876
+ return $currency_symbol;
877
+ }
878
 
879
  /**
880
  * Filters the product price.
896
  return $price;
897
  }
898
 
899
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
900
  /*
901
  * WC compat layer: get product
902
  */
903
+
904
  function wcml_get_product($product_id) {
905
  if ( version_compare( WOOCOMMERCE_VERSION, "2.0.0" ) >= 0 ) {
906
  // WC 2.0
1105
  }
1106
  }
1107
 
1108
+ // TODO: move outside the loop all db queries on duplicated_post_id
1109
+ // don't want to do it so close to release, just in case
1110
  foreach ($posts as $post_id => $translation) {
1111
  $lang = $translation->language_code;
1112
 
1175
  $wpdb->update($wpdb->postmeta, $data, $where);
1176
  }
1177
 
1178
+ //sync product categories
1179
+ $terms = get_the_terms($duplicated_post_id, 'product_cat');
1180
+ foreach ($terms as $term) {
1181
+ $trid = $sitepress->get_element_trid($term->term_id, 'tax_product_cat');
1182
+ if ($trid) {
1183
+ $translations = $sitepress->get_element_translations($trid,'tax_product_cat');
1184
+ if (isset($translations[$lang])) {
1185
+ $updates[$tax][] = intval($translations[$lang]->term_id);
1186
+ }
1187
+ }
1188
+ }
1189
+
1190
+ //synchronize term data, postmeta (Woocommerce "global" product attributes and custom attributes)
1191
+ $taxs = array();
1192
+ $updates = array();
1193
+
1194
+ $taxonomies = get_post_meta($duplicated_post_id, '_product_attributes', true);
1195
+ //error_log('Trans tax '.var_export($taxonomies,true));
1196
+ foreach ($taxonomies as $taxonomy) {
1197
+ if ($taxonomy['is_taxonomy']) { // Global product attribute
1198
+ $tax = $taxonomy['name'];
1199
+ $taxs[] = $tax;
1200
+ $updates[$tax] = array();
1201
+ $terms = get_the_terms($duplicated_post_id, $tax);
1202
+ foreach ($terms as $term) {
1203
+ $trid = $sitepress->get_element_trid($term->term_id, 'tax_' . $tax);
1204
+ if ($trid) {
1205
+ $translations = $sitepress->get_element_translations($trid,'tax_' . $tax);
1206
+ if (isset($translations[$lang])) {
1207
+ $updates[$tax][] = intval($translations[$lang]->term_id);
1208
+ }
1209
+ }
1210
+ }
1211
+ }
1212
+ }
1213
+
1214
+ // Sync terms for main product
1215
+ $taxs = array_unique($taxs);
1216
+ foreach ($taxs as $tax) {
1217
+ wp_set_object_terms($post_id, $updates[$tax], $tax);
1218
+ }
1219
+
1220
+ // synchronize post variations
1221
  $get_variation_term_id = $wpdb->get_var("SELECT term_id FROM $wpdb->terms WHERE name = 'variable'");
1222
  $get_variation_term_taxonomy_id = $wpdb->get_var("SELECT tt.term_taxonomy_id FROM $wpdb->term_relationships tr
1223
  LEFT JOIN $wpdb->term_taxonomy tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
1224
+ WHERE object_id = '$duplicated_post_id' AND taxonomy = 'product_type' AND tt.term_id = '$get_variation_term_id'");
1225
 
1226
  $is_post_has_variations = $wpdb->get_results("SELECT * FROM $wpdb->term_relationships WHERE object_id = '$duplicated_post_id' AND term_taxonomy_id = '$get_variation_term_taxonomy_id'");
1227
  if(!empty($is_post_has_variations)) $is_post_has_variations = TRUE;
1228
 
 
1229
  if($is_post_has_variations){
1230
  // synchronize post variations
1231
  $get_all_post_variations = $wpdb->get_results("SELECT * FROM $wpdb->posts
1232
+ WHERE post_status = 'publish' AND post_type = 'product_variation' AND post_parent = '$duplicated_post_id' ORDER BY ID");
1233
 
1234
  $duplicated_post_variation_ids = array();
1235
  foreach($get_all_post_variations as $k => $post_data){
1296
  'comment_count' => $post_data->comment_count
1297
  ));
1298
  update_post_meta($variation_id, '_wcml_duplicate_of_variation', $post_data->ID);
 
1299
  $trid = $sitepress->get_element_trid($post_data->ID, 'post_product_variation');
1300
+ $sitepress->set_element_language_details($variation_id, 'post_product_variation', $trid, $lang, $language_details->source_language_code);
1301
  }
1302
  }
1303
 
1304
  $get_current_post_variations = $wpdb->get_results("SELECT * FROM $wpdb->posts
1305
+ WHERE post_status = 'publish' AND post_type = 'product_variation' AND post_parent = '$post_id' ORDER BY ID");
1306
 
1307
  // Delete variations that no longer exist
1308
  foreach ($get_current_post_variations as $post_data) {
1321
  $current_post_variation_ids[] = $post_data->ID;
1322
  }
1323
 
 
1324
  foreach($duplicated_post_variation_ids as $dp_key => $duplicated_post_variation_id){
1325
  $get_all_post_meta = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE post_id = '$duplicated_post_variation_id'");
1326
 
1333
  if (substr($meta_key, 0, 10) == 'attribute_') {
1334
  $tax = substr($meta_key, 10);
1335
  if (taxonomy_exists($tax)) {
 
1336
  $attid = get_term_by('slug', $meta_value, $tax)->term_taxonomy_id;
1337
  $trid = $sitepress->get_element_trid($attid, 'tax_' . $tax);
1338
  if ($trid) {
1350
  }
1351
  }
1352
 
1353
+ }
1354
+ }
1355
+ }
 
 
 
 
 
 
 
 
 
 
 
1356
 
1357
+
1358
+ /**
1359
+ * Sync term order for product attributes, categories and tags
1360
+ */
1361
+ function sync_term_order($meta_id, $object_id, $meta_key, $meta_value) {
1362
+ global $sitepress,$wpdb,$pagenow;
1363
 
1364
+ if (!isset($_POST['thetaxonomy']) || !taxonomy_exists($_POST['thetaxonomy']) || substr($meta_key,0,5) != 'order')
1365
+ return;
1366
+
1367
+ $tax = $_POST['thetaxonomy'];
1368
+ //error_log(__FUNCTION__." $tax ".var_export(func_get_args(),true));
1369
+
1370
+ $lang_details = $sitepress->get_element_language_details($object_id,'tax_' . $tax);
1371
+ //error_log(var_export($lang_details,true));
1372
+ $lang = $lang_details->language_code;
1373
+ $translations = $sitepress->get_element_translations($lang_details->trid,'tax_' . $tax);
1374
+ if ($translations) foreach ($translations as $trans) {
1375
+ if ($trans->language_code != $lang) {
1376
+ //error_log("set_term_order {$trans->language_code} {$trans->element_id} $meta_key $meta_value");
1377
+ //cannot use update_woocommerce_termmeta or update_metadata as it would end calling this function again in endless loop
1378
+ $wpdb->update($wpdb->prefix.'woocommerce_termmeta',
1379
+ array('meta_value' => $meta_value),
1380
+ array('woocommerce_term_id' => $trans->element_id,'meta_key' => $meta_key));
1381
+ }
1382
  }
1383
+ }
1384
+
1385
+ function sync_variation_order($variation_id) {
1386
 
1387
  }
1388
 
1389
+ function sanitize_cpa_values($values) {
1390
+ // Text based, separate by pipe
1391
+ $values = explode('|', esc_html(stripslashes($values)));
1392
+ $values = array_map('trim', $values);
1393
+ $values = implode('|', $values);
1394
+ return $values;
1395
+ }
1396
+
1397
+ function update_post_meta($meta_id, $object_id, $meta_key, $_meta_value) {
1398
+ if ($meta_key == '_product_attributes' || $meta_key == 'attribute_names') {
1399
+ foreach (maybe_unserialize($_meta_value) as $attr_slug => $attr) {
1400
+ if (isset($attr['value'])) {
1401
+ $values = explode('|',$this->sanitize_cpa_values($attr['value']));
1402
+ foreach($values as $value) {
1403
+ icl_register_string('woocommerce',ucfirst($value).'_attribute_name',$value);
1404
+ }
1405
+ }
1406
+ }
1407
+ }
1408
+ }
1409
+
1410
  /**
1411
  * Creates WCML page.
1412
  */
1559
  <?php
1560
  }
1561
  }
1562
+
1563
+ function translate_cart_contents($item, $values, $key) {
1564
+ if ( version_compare( WOOCOMMERCE_VERSION, "2.0.0" ) < 0 ) {
1565
+ // clearing subtotal triggers calculate_totals (WC 1.x)
1566
+ // for WC 2.x its done with the function below
1567
+ $_SESSION['subtotal'] = 0;
1568
+ }
1569
+
1570
+ // translate the product id and product data
1571
+ $item['product_id'] = icl_object_id($item['product_id'], 'product', true);
1572
+ if ($item['variation_id']) {
1573
+ $item['variation_id'] = icl_object_id($item['variation_id'], 'product_variation', true);
1574
+ }
1575
+ $product_id = $item['variation_id'] ? $item['variation_id'] : $item['product_id'];
1576
+ return array(
1577
+ 'product_id' => $item['product_id'],
1578
+ 'variation_id' => $item['variation_id'],
1579
+ 'variation' => $item['variation'],
1580
+ 'quantity' => $item['quantity'],
1581
+ 'data' => $this->wcml_get_product($product_id)
1582
+ );
1583
  }
1584
+
1585
+ function translate_cart_subtotal($cart) {
1586
+ $cart->calculate_totals();
1587
  }
1588
 
1589
+ /**
1590
+ * WooCommerce Multilingual deactivation hook.
1591
+ */
1592
+ function wcml_deactivate(){
1593
+ delete_option('wpml_dismiss_doc_main');
1594
+ }
1595
 
1596
  }
wpml-woocommerce.php CHANGED
@@ -5,11 +5,11 @@
5
  Description: Allows running fully multilingual e-Commerce sites with WooCommerce and WPML. <a href="http://wpml.org/documentation/related-projects/woocommerce-multilingual/">Documentation</a>.
6
  Author: ICanLocalize
7
  Author URI: http://wpml.org/
8
- Version: 2.2
9
  */
10
 
11
  if(defined('WCML_VERSION')) return;
12
- define('WCML_VERSION', '2.2');
13
  define('WCML_PLUGIN_PATH', dirname(__FILE__));
14
  define('WCML_PLUGIN_FOLDER', basename(WCML_PLUGIN_PATH));
15
  define('WCML_PLUGIN_URL', plugins_url() . '/' . WCML_PLUGIN_FOLDER);
5
  Description: Allows running fully multilingual e-Commerce sites with WooCommerce and WPML. <a href="http://wpml.org/documentation/related-projects/woocommerce-multilingual/">Documentation</a>.
6
  Author: ICanLocalize
7
  Author URI: http://wpml.org/
8
+ Version: 2.3
9
  */
10
 
11
  if(defined('WCML_VERSION')) return;
12
+ define('WCML_VERSION', '2.3');
13
  define('WCML_PLUGIN_PATH', dirname(__FILE__));
14
  define('WCML_PLUGIN_FOLDER', basename(WCML_PLUGIN_PATH));
15
  define('WCML_PLUGIN_URL', plugins_url() . '/' . WCML_PLUGIN_FOLDER);