/includes/class-wc-countries.php

  1. <?php 
  2.  
  3. if ( ! defined( 'ABSPATH' ) ) { 
  4. exit; // Exit if accessed directly 
  5.  
  6. /** 
  7. * WooCommerce countries 
  8. * 
  9. * The WooCommerce countries class stores country/state data. 
  10. * 
  11. * @class WC_Countries 
  12. * @version 2.3.0 
  13. * @package WooCommerce/Classes 
  14. * @category Class 
  15. * @author WooThemes 
  16. */ 
  17. class WC_Countries { 
  18.  
  19. /** @var array Array of locales */ 
  20. public $locale; 
  21.  
  22. /** @var array Array of address formats for locales */ 
  23. public $address_formats; 
  24.  
  25. /** 
  26. * Auto-load in-accessible properties on demand. 
  27. * @param mixed $key 
  28. * @return mixed 
  29. */ 
  30. public function __get( $key ) { 
  31. if ( 'countries' == $key ) { 
  32. return $this->get_countries(); 
  33. } elseif ( 'states' == $key ) { 
  34. return $this->get_states(); 
  35.  
  36. /** 
  37. * Get all countries. 
  38. * @return array 
  39. */ 
  40. public function get_countries() { 
  41. if ( empty( $this->countries ) ) { 
  42. $this->countries = apply_filters( 'woocommerce_countries', include( WC()->plugin_path() . '/i18n/countries.php' ) ); 
  43. if ( apply_filters( 'woocommerce_sort_countries', true ) ) { 
  44. asort( $this->countries ); 
  45. return $this->countries; 
  46.  
  47. /** 
  48. * Get all continents. 
  49. * @return array 
  50. */ 
  51. public function get_continents() { 
  52. if ( empty( $this->continents ) ) { 
  53. $this->continents = apply_filters( 'woocommerce_continents', include( WC()->plugin_path() . '/i18n/continents.php' ) ); 
  54. return $this->continents; 
  55.  
  56. /** 
  57. * Get continent code for a country code. 
  58. * @since 2.6.0 
  59. * @param string $cc string 
  60. * @return string 
  61. */ 
  62. public function get_continent_code_for_country( $cc ) { 
  63. $cc = trim( strtoupper( $cc ) ); 
  64. $continents = $this->get_continents(); 
  65. $continents_and_ccs = wp_list_pluck( $continents, 'countries' ); 
  66. foreach ( $continents_and_ccs as $continent_code => $countries ) { 
  67. if ( false !== array_search( $cc, $countries ) ) { 
  68. return $continent_code; 
  69. return ''; 
  70.  
  71. /** 
  72. * Load the states. 
  73. */ 
  74. public function load_country_states() { 
  75. global $states; 
  76.  
  77. // States set to array() are blank i.e. the country has no use for the state field. 
  78. $states = array( 
  79. 'AF' => array(),  
  80. 'AT' => array(),  
  81. 'AX' => array(),  
  82. 'BE' => array(),  
  83. 'BI' => array(),  
  84. 'CZ' => array(),  
  85. 'DE' => array(),  
  86. 'DK' => array(),  
  87. 'EE' => array(),  
  88. 'FI' => array(),  
  89. 'FR' => array(),  
  90. 'GP' => array(),  
  91. 'GF' => array(),  
  92. 'IS' => array(),  
  93. 'IL' => array(),  
  94. 'KR' => array(),  
  95. 'KW' => array(),  
  96. 'LB' => array(),  
  97. 'MQ' => array(),  
  98. 'NL' => array(),  
  99. 'NO' => array(),  
  100. 'PL' => array(),  
  101. 'PT' => array(),  
  102. 'RE' => array(),  
  103. 'SG' => array(),  
  104. 'SK' => array(),  
  105. 'SI' => array(),  
  106. 'LK' => array(),  
  107. 'SE' => array(),  
  108. 'VN' => array(),  
  109. 'YT' => array(),  
  110. ); 
  111.  
  112. // Load only the state files the shop owner wants/needs. 
  113. $allowed = array_merge( $this->get_allowed_countries(), $this->get_shipping_countries() ); 
  114.  
  115. if ( ! empty( $allowed ) ) { 
  116. foreach ( $allowed as $code => $country ) { 
  117. if ( ! isset( $states[ $code ] ) && file_exists( WC()->plugin_path() . '/i18n/states/' . $code . '.php' ) ) { 
  118. include( WC()->plugin_path() . '/i18n/states/' . $code . '.php' ); 
  119.  
  120. $this->states = apply_filters( 'woocommerce_states', $states ); 
  121.  
  122. /** 
  123. * Get the states for a country. 
  124. * @param string $cc country code 
  125. * @return array of states 
  126. */ 
  127. public function get_states( $cc = null ) { 
  128. if ( empty( $this->states ) ) { 
  129. $this->load_country_states(); 
  130.  
  131. if ( ! is_null( $cc ) ) { 
  132. return isset( $this->states[ $cc ] ) ? $this->states[ $cc ] : false; 
  133. } else { 
  134. return $this->states; 
  135.  
  136. /** 
  137. * Get the base country for the store. 
  138. * @return string 
  139. */ 
  140. public function get_base_country() { 
  141. $default = wc_get_base_location(); 
  142. return apply_filters( 'woocommerce_countries_base_country', $default['country'] ); 
  143.  
  144. /** 
  145. * Get the base state for the store. 
  146. * @return string 
  147. */ 
  148. public function get_base_state() { 
  149. $default = wc_get_base_location(); 
  150. return apply_filters( 'woocommerce_countries_base_state', $default['state'] ); 
  151.  
  152. /** 
  153. * Get the base city for the store. 
  154. * @return string 
  155. */ 
  156. public function get_base_city() { 
  157. return apply_filters( 'woocommerce_countries_base_city', '' ); 
  158.  
  159. /** 
  160. * Get the base postcode for the store. 
  161. * @return string 
  162. */ 
  163. public function get_base_postcode() { 
  164. return apply_filters( 'woocommerce_countries_base_postcode', '' ); 
  165.  
  166. /** 
  167. * Get the allowed countries for the store. 
  168. * @return array 
  169. */ 
  170. public function get_allowed_countries() { 
  171. if ( 'all' === get_option( 'woocommerce_allowed_countries' ) ) { 
  172. return $this->countries; 
  173.  
  174. if ( 'all_except' === get_option( 'woocommerce_allowed_countries' ) ) { 
  175. $except_countries = get_option( 'woocommerce_all_except_countries', array() ); 
  176.  
  177. if ( ! $except_countries ) { 
  178. return $this->countries; 
  179. } else { 
  180. $all_except_countries = $this->countries; 
  181. foreach ( $except_countries as $country ) { 
  182. unset( $all_except_countries[ $country ] ); 
  183. return apply_filters( 'woocommerce_countries_allowed_countries', $all_except_countries ); 
  184.  
  185. $countries = array(); 
  186.  
  187. $raw_countries = get_option( 'woocommerce_specific_allowed_countries', array() ); 
  188.  
  189. if ( $raw_countries ) { 
  190. foreach ( $raw_countries as $country ) { 
  191. $countries[ $country ] = $this->countries[ $country ]; 
  192.  
  193. return apply_filters( 'woocommerce_countries_allowed_countries', $countries ); 
  194.  
  195. /** 
  196. * Get the countries you ship to. 
  197. * @return array 
  198. */ 
  199. public function get_shipping_countries() { 
  200. if ( '' === get_option( 'woocommerce_ship_to_countries' ) ) { 
  201. return $this->get_allowed_countries(); 
  202.  
  203. if ( 'all' === get_option( 'woocommerce_ship_to_countries' ) ) { 
  204. return $this->countries; 
  205.  
  206. $countries = array(); 
  207.  
  208. $raw_countries = get_option( 'woocommerce_specific_ship_to_countries' ); 
  209.  
  210. if ( $raw_countries ) { 
  211. foreach ( $raw_countries as $country ) { 
  212. $countries[ $country ] = $this->countries[ $country ]; 
  213.  
  214. return apply_filters( 'woocommerce_countries_shipping_countries', $countries ); 
  215.  
  216. /** 
  217. * Get allowed country states. 
  218. * @return array 
  219. */ 
  220. public function get_allowed_country_states() { 
  221. if ( get_option( 'woocommerce_allowed_countries' ) !== 'specific' ) { 
  222. return $this->states; 
  223.  
  224. $states = array(); 
  225.  
  226. $raw_countries = get_option( 'woocommerce_specific_allowed_countries' ); 
  227.  
  228. if ( $raw_countries ) { 
  229. foreach ( $raw_countries as $country ) { 
  230. if ( isset( $this->states[ $country ] ) ) { 
  231. $states[ $country ] = $this->states[ $country ]; 
  232.  
  233. return apply_filters( 'woocommerce_countries_allowed_country_states', $states ); 
  234.  
  235. /** 
  236. * Get shipping country states. 
  237. * @return array 
  238. */ 
  239. public function get_shipping_country_states() { 
  240. if ( get_option( 'woocommerce_ship_to_countries' ) == '' ) { 
  241. return $this->get_allowed_country_states(); 
  242.  
  243. if ( get_option( 'woocommerce_ship_to_countries' ) !== 'specific' ) { 
  244. return $this->states; 
  245.  
  246. $states = array(); 
  247.  
  248. $raw_countries = get_option( 'woocommerce_specific_ship_to_countries' ); 
  249.  
  250. if ( $raw_countries ) { 
  251. foreach ( $raw_countries as $country ) { 
  252. if ( ! empty( $this->states[ $country ] ) ) { 
  253. $states[ $country ] = $this->states[ $country ]; 
  254.  
  255. return apply_filters( 'woocommerce_countries_shipping_country_states', $states ); 
  256.  
  257. /** 
  258. * Gets an array of countries in the EU. 
  259. * 
  260. * MC (monaco) and IM (isle of man, part of UK) also use VAT. 
  261. * 
  262. * @param $type Type of countries to retrieve. Blank for EU member countries. eu_vat for EU VAT countries. 
  263. * @return string[] 
  264. */ 
  265. public function get_european_union_countries( $type = '' ) { 
  266. $countries = array( 'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HU', 'HR', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK' ); 
  267.  
  268. if ( 'eu_vat' === $type ) { 
  269. $countries[] = 'MC'; 
  270. $countries[] = 'IM'; 
  271.  
  272. return $countries; 
  273.  
  274. /** 
  275. * Gets the correct string for shipping - either 'to the' or 'to' 
  276. * @return string 
  277. */ 
  278. public function shipping_to_prefix( $country_code = '' ) { 
  279. $country_code = $country_code ? $country_code : WC()->customer->get_shipping_country(); 
  280. $countries = array( 'GB', 'US', 'AE', 'CZ', 'DO', 'NL', 'PH', 'USAF' ); 
  281. $return = in_array( $country_code, $countries ) ? __( 'to the', 'woocommerce' ) : __( 'to', 'woocommerce' ); 
  282.  
  283. return apply_filters( 'woocommerce_countries_shipping_to_prefix', $return, $country_code ); 
  284.  
  285. /** 
  286. * Prefix certain countries with 'the' 
  287. * @return string 
  288. */ 
  289. public function estimated_for_prefix( $country_code = '' ) { 
  290. $country_code = $country_code ? $country_code : $this->get_base_country(); 
  291. $countries = array( 'GB', 'US', 'AE', 'CZ', 'DO', 'NL', 'PH', 'USAF' ); 
  292. $return = in_array( $country_code, $countries ) ? __( 'the', 'woocommerce' ) . ' ' : ''; 
  293.  
  294. return apply_filters( 'woocommerce_countries_estimated_for_prefix', $return, $country_code ); 
  295.  
  296. /** 
  297. * Correctly name tax in some countries VAT on the frontend. 
  298. * @return string 
  299. */ 
  300. public function tax_or_vat() { 
  301. $return = in_array( $this->get_base_country(), $this->get_european_union_countries( 'eu_vat' ) ) ? __( 'VAT', 'woocommerce' ) : __( 'Tax', 'woocommerce' ); 
  302.  
  303. return apply_filters( 'woocommerce_countries_tax_or_vat', $return ); 
  304.  
  305. /** 
  306. * Include the Inc Tax label. 
  307. * @return string 
  308. */ 
  309. public function inc_tax_or_vat() { 
  310. $return = in_array( $this->get_base_country(), $this->get_european_union_countries( 'eu_vat' ) ) ? __( '(incl. VAT)', 'woocommerce' ) : __( '(incl. tax)', 'woocommerce' ); 
  311.  
  312. return apply_filters( 'woocommerce_countries_inc_tax_or_vat', $return ); 
  313.  
  314. /** 
  315. * Include the Ex Tax label. 
  316. * @return string 
  317. */ 
  318. public function ex_tax_or_vat() { 
  319. $return = in_array( $this->get_base_country(), $this->get_european_union_countries( 'eu_vat' ) ) ? __( '(ex. VAT)', 'woocommerce' ) : __( '(ex. tax)', 'woocommerce' ); 
  320.  
  321. return apply_filters( 'woocommerce_countries_ex_tax_or_vat', $return ); 
  322.  
  323. /** 
  324. * Outputs the list of countries and states for use in dropdown boxes. 
  325. * @param string $selected_country (default: '') 
  326. * @param string $selected_state (default: '') 
  327. * @param bool $escape (default: false) 
  328. * @param bool $escape (default: false) 
  329. */ 
  330. public function country_dropdown_options( $selected_country = '', $selected_state = '', $escape = false ) { 
  331. if ( $this->countries ) foreach ( $this->countries as $key => $value ) : 
  332. if ( $states = $this->get_states( $key ) ) : 
  333. echo '<optgroup label="' . esc_attr( $value ) . '">'; 
  334. foreach ( $states as $state_key => $state_value ) : 
  335. echo '<option value="' . esc_attr( $key ) . ':' . $state_key . '"'; 
  336.  
  337. if ( $selected_country == $key && $selected_state == $state_key ) { 
  338. echo ' selected="selected"'; 
  339.  
  340. echo '>' . $value . ' — ' . ( $escape ? esc_js( $state_value ) : $state_value ) . '</option>'; 
  341. endforeach; 
  342. echo '</optgroup>'; 
  343. else : 
  344. echo '<option'; 
  345. if ( $selected_country == $key && '*' == $selected_state ) { 
  346. echo ' selected="selected"'; 
  347. echo ' value="' . esc_attr( $key ) . '">' . ( $escape ? esc_js( $value ) : $value ) . '</option>'; 
  348. endif; 
  349. endforeach; 
  350.  
  351. /** 
  352. * Get country address formats. 
  353. * 
  354. * These define how addresses are formatted for display in various countries. 
  355. * 
  356. * @return array 
  357. */ 
  358. public function get_address_formats() { 
  359. if ( empty( $this->address_formats ) ) { 
  360. $this->address_formats = apply_filters( 'woocommerce_localisation_address_formats', array( 
  361. 'default' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode}\n{country}",  
  362. 'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",  
  363. 'AT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  364. 'BE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  365. 'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",  
  366. 'CH' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  367. 'CL' => "{company}\n{name}\n{address_1}\n{address_2}\n{state}\n{postcode} {city}\n{country}",  
  368. 'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",  
  369. 'CZ' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  370. 'DE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  371. 'EE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  372. 'FI' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  373. 'DK' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  374. 'FR' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city_upper}\n{country}",  
  375. 'HK' => "{company}\n{first_name} {last_name_upper}\n{address_1}\n{address_2}\n{city_upper}\n{state_upper}\n{country}",  
  376. 'HU' => "{name}\n{company}\n{city}\n{address_1}\n{address_2}\n{postcode}\n{country}",  
  377. 'IN' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} - {postcode}\n{state}, {country}",  
  378. 'IS' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  379. 'IT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode}\n{city}\n{state_upper}\n{country}",  
  380. 'JP' => "{postcode}\n{state}{city}{address_1}\n{address_2}\n{company}\n{last_name} {first_name}\n{country}",  
  381. 'TW' => "{company}\n{last_name} {first_name}\n{address_1}\n{address_2}\n{state}, {city} {postcode}\n{country}",  
  382. 'LI' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  383. 'NL' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  384. 'NZ' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {postcode}\n{country}",  
  385. 'NO' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  386. 'PL' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  387. 'PT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  388. 'SK' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  389. 'SI' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  390. 'ES' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city}\n{state}\n{country}",  
  391. 'SE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",  
  392. 'TR' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city} {state}\n{country}",  
  393. 'US' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}, {state_code} {postcode}\n{country}",  
  394. 'VN' => "{name}\n{company}\n{address_1}\n{city}\n{country}",  
  395. ) ); 
  396. return $this->address_formats; 
  397.  
  398. /** 
  399. * Get country address format. 
  400. * @param array $args (default: array()) 
  401. * @return string address 
  402. */ 
  403. public function get_formatted_address( $args = array() ) { 
  404. $default_args = array( 
  405. 'first_name' => '',  
  406. 'last_name' => '',  
  407. 'company' => '',  
  408. 'address_1' => '',  
  409. 'address_2' => '',  
  410. 'city' => '',  
  411. 'state' => '',  
  412. 'postcode' => '',  
  413. 'country' => '',  
  414. ); 
  415.  
  416. $args = array_map( 'trim', wp_parse_args( $args, $default_args ) ); 
  417.  
  418. extract( $args ); 
  419.  
  420. // Get all formats 
  421. $formats = $this->get_address_formats(); 
  422.  
  423. // Get format for the address' country 
  424. $format = ( $country && isset( $formats[ $country ] ) ) ? $formats[ $country ] : $formats['default']; 
  425.  
  426. // Handle full country name 
  427. $full_country = ( isset( $this->countries[ $country ] ) ) ? $this->countries[ $country ] : $country; 
  428.  
  429. // Country is not needed if the same as base 
  430. if ( $country == $this->get_base_country() && ! apply_filters( 'woocommerce_formatted_address_force_country_display', false ) ) { 
  431. $format = str_replace( '{country}', '', $format ); 
  432.  
  433. // Handle full state name 
  434. $full_state = ( $country && $state && isset( $this->states[ $country ][ $state ] ) ) ? $this->states[ $country ][ $state ] : $state; 
  435.  
  436. // Substitute address parts into the string 
  437. $replace = array_map( 'esc_html', apply_filters( 'woocommerce_formatted_address_replacements', array( 
  438. '{first_name}' => $first_name,  
  439. '{last_name}' => $last_name,  
  440. '{name}' => $first_name . ' ' . $last_name,  
  441. '{company}' => $company,  
  442. '{address_1}' => $address_1,  
  443. '{address_2}' => $address_2,  
  444. '{city}' => $city,  
  445. '{state}' => $full_state,  
  446. '{postcode}' => $postcode,  
  447. '{country}' => $full_country,  
  448. '{first_name_upper}' => strtoupper( $first_name ),  
  449. '{last_name_upper}' => strtoupper( $last_name ),  
  450. '{name_upper}' => strtoupper( $first_name . ' ' . $last_name ),  
  451. '{company_upper}' => strtoupper( $company ),  
  452. '{address_1_upper}' => strtoupper( $address_1 ),  
  453. '{address_2_upper}' => strtoupper( $address_2 ),  
  454. '{city_upper}' => strtoupper( $city ),  
  455. '{state_upper}' => strtoupper( $full_state ),  
  456. '{state_code}' => strtoupper( $state ),  
  457. '{postcode_upper}' => strtoupper( $postcode ),  
  458. '{country_upper}' => strtoupper( $full_country ),  
  459. ), $args ) ); 
  460.  
  461. $formatted_address = str_replace( array_keys( $replace ), $replace, $format ); 
  462.  
  463. // Clean up white space 
  464. $formatted_address = preg_replace( '/ +/', ' ', trim( $formatted_address ) ); 
  465. $formatted_address = preg_replace( '/\n\n+/', "\n", $formatted_address ); 
  466.  
  467. // Break newlines apart and remove empty lines/trim commas and white space 
  468. $formatted_address = array_filter( array_map( array( $this, 'trim_formatted_address_line' ), explode( "\n", $formatted_address ) ) ); 
  469.  
  470. // Add html breaks 
  471. $formatted_address = implode( '<br/>', $formatted_address ); 
  472.  
  473. // We're done! 
  474. return $formatted_address; 
  475.  
  476. /** 
  477. * Trim white space and commas off a line. 
  478. * @param string $line 
  479. * @return string 
  480. */ 
  481. private function trim_formatted_address_line( $line ) { 
  482. return trim( $line, ", " ); 
  483.  
  484. /** 
  485. * Returns the fields we show by default. This can be filtered later on. 
  486. * @return array 
  487. */ 
  488. public function get_default_address_fields() { 
  489. $fields = array( 
  490. 'first_name' => array( 
  491. 'label' => __( 'First name', 'woocommerce' ),  
  492. 'required' => true,  
  493. 'class' => array( 'form-row-first' ),  
  494. 'autocomplete' => 'given-name',  
  495. 'autofocus' => true,  
  496. 'priority' => 10,  
  497. ),  
  498. 'last_name' => array( 
  499. 'label' => __( 'Last name', 'woocommerce' ),  
  500. 'required' => true,  
  501. 'class' => array( 'form-row-last' ),  
  502. 'autocomplete' => 'family-name',  
  503. 'priority' => 20,  
  504. ),  
  505. 'company' => array( 
  506. 'label' => __( 'Company name', 'woocommerce' ),  
  507. 'class' => array( 'form-row-wide' ),  
  508. 'autocomplete' => 'organization',  
  509. 'priority' => 30,  
  510. ),  
  511. 'country' => array( 
  512. 'type' => 'country',  
  513. 'label' => __( 'Country', 'woocommerce' ),  
  514. 'required' => true,  
  515. 'class' => array( 'form-row-wide', 'address-field', 'update_totals_on_change' ),  
  516. 'autocomplete' => 'country',  
  517. 'priority' => 40,  
  518. ),  
  519. 'address_1' => array( 
  520. 'label' => __( 'Address', 'woocommerce' ),  
  521. 'placeholder' => esc_attr__( 'Street address', 'woocommerce' ),  
  522. 'required' => true,  
  523. 'class' => array( 'form-row-wide', 'address-field' ),  
  524. 'autocomplete' => 'address-line1',  
  525. 'priority' => 50,  
  526. ),  
  527. 'address_2' => array( 
  528. 'placeholder' => esc_attr__( 'Apartment, suite, unit etc. (optional)', 'woocommerce' ),  
  529. 'class' => array( 'form-row-wide', 'address-field' ),  
  530. 'required' => false,  
  531. 'autocomplete' => 'address-line2',  
  532. 'priority' => 60,  
  533. ),  
  534. 'city' => array( 
  535. 'label' => __( 'Town / City', 'woocommerce' ),  
  536. 'required' => true,  
  537. 'class' => array( 'form-row-wide', 'address-field' ),  
  538. 'autocomplete' => 'address-level2',  
  539. 'priority' => 70,  
  540. ),  
  541. 'state' => array( 
  542. 'type' => 'state',  
  543. 'label' => __( 'State / County', 'woocommerce' ),  
  544. 'required' => true,  
  545. 'class' => array( 'form-row-wide', 'address-field' ),  
  546. 'validate' => array( 'state' ),  
  547. 'autocomplete' => 'address-level1',  
  548. 'priority' => 80,  
  549. ),  
  550. 'postcode' => array( 
  551. 'label' => __( 'Postcode / ZIP', 'woocommerce' ),  
  552. 'required' => true,  
  553. 'class' => array( 'form-row-wide', 'address-field' ),  
  554. 'validate' => array( 'postcode' ),  
  555. 'autocomplete' => 'postal-code',  
  556. 'priority' => 90,  
  557. ),  
  558. ); 
  559.  
  560. return apply_filters( 'woocommerce_default_address_fields', $fields ); 
  561.  
  562. /** 
  563. * Get JS selectors for fields which are shown/hidden depending on the locale. 
  564. * @return array 
  565. */ 
  566. public function get_country_locale_field_selectors() { 
  567. $locale_fields = array( 
  568. 'address_1' => '#billing_address_1_field, #shipping_address_1_field',  
  569. 'address_2' => '#billing_address_2_field, #shipping_address_2_field',  
  570. 'state' => '#billing_state_field, #shipping_state_field, #calc_shipping_state_field',  
  571. 'postcode' => '#billing_postcode_field, #shipping_postcode_field, #calc_shipping_postcode_field',  
  572. 'city' => '#billing_city_field, #shipping_city_field, #calc_shipping_city_field',  
  573. ); 
  574. return apply_filters( 'woocommerce_country_locale_field_selectors', $locale_fields ); 
  575.  
  576. /** 
  577. * Get country locale settings. 
  578. * 
  579. * These locales override the default country selections after a country is chosen. 
  580. * 
  581. * @return array 
  582. */ 
  583. public function get_country_locale() { 
  584. if ( empty( $this->locale ) ) { 
  585. $this->locale = apply_filters( 'woocommerce_get_country_locale', array( 
  586. 'AE' => array( 
  587. 'postcode' => array( 
  588. 'required' => false,  
  589. 'hidden' => true,  
  590. ),  
  591. 'state' => array( 
  592. 'required' => false,  
  593. ),  
  594. ),  
  595. 'AF' => array( 
  596. 'state' => array( 
  597. 'required' => false,  
  598. ),  
  599. ),  
  600. 'AT' => array( 
  601. 'postcode' => array( 
  602. 'priority' => 65,  
  603. ),  
  604. 'state' => array( 
  605. 'required' => false,  
  606. ),  
  607. ),  
  608. 'AU' => array( 
  609. 'city' => array( 
  610. 'label' => __( 'Suburb', 'woocommerce' ),  
  611. ),  
  612. 'postcode' => array( 
  613. 'label' => __( 'Postcode', 'woocommerce' ),  
  614. ),  
  615. 'state' => array( 
  616. 'label' => __( 'State', 'woocommerce' ),  
  617. ),  
  618. ),  
  619. 'AX' => array( 
  620. 'postcode' => array( 
  621. 'priority' => 65,  
  622. ),  
  623. 'state' => array( 
  624. 'required' => false,  
  625. ),  
  626. ),  
  627. 'BD' => array( 
  628. 'postcode' => array( 
  629. 'required' => false,  
  630. ),  
  631. 'state' => array( 
  632. 'label' => __( 'District', 'woocommerce' ),  
  633. ),  
  634. ),  
  635. 'BE' => array( 
  636. 'postcode' => array( 
  637. 'priority' => 65,  
  638. ),  
  639. 'state' => array( 
  640. 'required' => false,  
  641. 'label' => __( 'Province', 'woocommerce' ),  
  642. ),  
  643. ),  
  644. 'BI' => array( 
  645. 'state' => array( 
  646. 'required' => false,  
  647. ),  
  648. ),  
  649. 'BO' => array( 
  650. 'postcode' => array( 
  651. 'required' => false,  
  652. 'hidden' => true,  
  653. ),  
  654. ),  
  655. 'BS' => array( 
  656. 'postcode' => array( 
  657. 'required' => false,  
  658. 'hidden' => true,  
  659. ),  
  660. ),  
  661. 'CA' => array( 
  662. 'state' => array( 
  663. 'label' => __( 'Province', 'woocommerce' ),  
  664. ),  
  665. ),  
  666. 'CH' => array( 
  667. 'postcode' => array( 
  668. 'priority' => 65,  
  669. ),  
  670. 'state' => array( 
  671. 'label' => __( 'Canton', 'woocommerce' ),  
  672. 'required' => false,  
  673. ),  
  674. ),  
  675. 'CL' => array( 
  676. 'city' => array( 
  677. 'required' => true,  
  678. ),  
  679. 'postcode' => array( 
  680. 'required' => false,  
  681. ),  
  682. 'state' => array( 
  683. 'label' => __( 'Region', 'woocommerce' ),  
  684. ),  
  685. ),  
  686. 'CN' => array( 
  687. 'state' => array( 
  688. 'label' => __( 'Province', 'woocommerce' ),  
  689. ),  
  690. ),  
  691. 'CO' => array( 
  692. 'postcode' => array( 
  693. 'required' => false,  
  694. ),  
  695. ),  
  696. 'CZ' => array( 
  697. 'state' => array( 
  698. 'required' => false,  
  699. ),  
  700. ),  
  701. 'DE' => array( 
  702. 'postcode' => array( 
  703. 'priority' => 65,  
  704. ),  
  705. 'state' => array( 
  706. 'required' => false,  
  707. ),  
  708. ),  
  709. 'DK' => array( 
  710. 'postcode' => array( 
  711. 'priority' => 65,  
  712. ),  
  713. 'state' => array( 
  714. 'required' => false,  
  715. ),  
  716. ),  
  717. 'EE' => array( 
  718. 'postcode' => array( 
  719. 'priority' => 65,  
  720. ),  
  721. 'state' => array( 
  722. 'required' => false,  
  723. ),  
  724. ),  
  725. 'FI' => array( 
  726. 'postcode' => array( 
  727. 'priority' => 65,  
  728. ),  
  729. 'state' => array( 
  730. 'required' => false,  
  731. ),  
  732. ),  
  733. 'FR' => array( 
  734. 'postcode' => array( 
  735. 'priority' => 65,  
  736. ),  
  737. 'state' => array( 
  738. 'required' => false,  
  739. ),  
  740. ),  
  741. 'GP' => array( 
  742. 'state' => array( 
  743. 'required' => false,  
  744. ),  
  745. ),  
  746. 'GF' => array( 
  747. 'state' => array( 
  748. 'required' => false,  
  749. ),  
  750. ),  
  751. 'HK' => array( 
  752. 'postcode' => array( 
  753. 'required' => false,  
  754. ),  
  755. 'city' => array( 
  756. 'label' => __( 'Town / District', 'woocommerce' ),  
  757. ),  
  758. 'state' => array( 
  759. 'label' => __( 'Region', 'woocommerce' ),  
  760. ),  
  761. ),  
  762. 'HU' => array( 
  763. 'state' => array( 
  764. 'label' => __( 'County', 'woocommerce' ),  
  765. ),  
  766. ),  
  767. 'ID' => array( 
  768. 'state' => array( 
  769. 'label' => __( 'Province', 'woocommerce' ),  
  770. ),  
  771. ),  
  772. 'IE' => array( 
  773. 'postcode' => array( 
  774. 'required' => false,  
  775. 'label' => __( 'Eircode', 'woocommerce' ),  
  776. ),  
  777. 'state' => array( 
  778. 'label' => __( 'County', 'woocommerce' ),  
  779. ),  
  780. ),  
  781. 'IS' => array( 
  782. 'postcode' => array( 
  783. 'priority' => 65,  
  784. ),  
  785. 'state' => array( 
  786. 'required' => false,  
  787. ),  
  788. ),  
  789. 'IL' => array( 
  790. 'postcode' => array( 
  791. 'priority' => 65,  
  792. ),  
  793. 'state' => array( 
  794. 'required' => false,  
  795. ),  
  796. ),  
  797. 'IT' => array( 
  798. 'postcode' => array( 
  799. 'priority' => 65,  
  800. ),  
  801. 'state' => array( 
  802. 'required' => true,  
  803. 'label' => __( 'Province', 'woocommerce' ),  
  804. ),  
  805. ),  
  806. 'JP' => array( 
  807. 'state' => array( 
  808. 'label' => __( 'Prefecture', 'woocommerce' ),  
  809. 'priority' => 66,  
  810. ),  
  811. 'postcode' => array( 
  812. 'priority' => 65,  
  813. ),  
  814. ),  
  815. 'KR' => array( 
  816. 'state' => array( 
  817. 'required' => false,  
  818. ),  
  819. ),  
  820. 'KW' => array( 
  821. 'state' => array( 
  822. 'required' => false,  
  823. ),  
  824. ),  
  825. 'LB' => array( 
  826. 'state' => array( 
  827. 'required' => false,  
  828. ),  
  829. ),  
  830. 'MQ' => array( 
  831. 'state' => array( 
  832. 'required' => false,  
  833. ),  
  834. ),  
  835. 'NL' => array( 
  836. 'postcode' => array( 
  837. 'priority' => 65,  
  838. ),  
  839. 'state' => array( 
  840. 'required' => false,  
  841. 'label' => __( 'Province', 'woocommerce' ),  
  842. ),  
  843. ),  
  844. 'NZ' => array( 
  845. 'postcode' => array( 
  846. 'label' => __( 'Postcode', 'woocommerce' ),  
  847. ),  
  848. 'state' => array( 
  849. 'required' => false,  
  850. 'label' => __( 'Region', 'woocommerce' ),  
  851. ),  
  852. ),  
  853. 'NO' => array( 
  854. 'postcode' => array( 
  855. 'priority' => 65,  
  856. ),  
  857. 'state' => array( 
  858. 'required' => false,  
  859. ),  
  860. ),  
  861. 'NP' => array( 
  862. 'state' => array( 
  863. 'label' => __( 'State / Zone', 'woocommerce' ),  
  864. ),  
  865. 'postcode' => array( 
  866. 'required' => false,  
  867. ),  
  868. ),  
  869. 'PL' => array( 
  870. 'postcode' => array( 
  871. 'priority' => 65,  
  872. ),  
  873. 'state' => array( 
  874. 'required' => false,  
  875. ),  
  876. ),  
  877. 'PT' => array( 
  878. 'state' => array( 
  879. 'required' => false,  
  880. ),  
  881. ),  
  882. 'RE' => array( 
  883. 'state' => array( 
  884. 'required' => false,  
  885. ),  
  886. ),  
  887. 'RO' => array( 
  888. 'state' => array( 
  889. 'required' => false,  
  890. ),  
  891. ),  
  892. 'SG' => array( 
  893. 'state' => array( 
  894. 'required' => false,  
  895. ),  
  896. ),  
  897. 'SK' => array( 
  898. 'postcode' => array( 
  899. 'priority' => 65,  
  900. ),  
  901. 'state' => array( 
  902. 'required' => false,  
  903. ),  
  904. ),  
  905. 'SI' => array( 
  906. 'postcode' => array( 
  907. 'priority' => 65,  
  908. ),  
  909. 'state' => array( 
  910. 'required' => false,  
  911. ),  
  912. ),  
  913. 'ES' => array( 
  914. 'postcode' => array( 
  915. 'priority' => 65,  
  916. ),  
  917. 'state' => array( 
  918. 'label' => __( 'Province', 'woocommerce' ),  
  919. ),  
  920. ),  
  921. 'LI' => array( 
  922. 'postcode' => array( 
  923. 'priority' => 65,  
  924. ),  
  925. 'state' => array( 
  926. 'label' => __( 'Municipality', 'woocommerce' ),  
  927. 'required' => false,  
  928. ),  
  929. ),  
  930. 'LK' => array( 
  931. 'state' => array( 
  932. 'required' => false,  
  933. ),  
  934. ),  
  935. 'SE' => array( 
  936. 'postcode' => array( 
  937. 'priority' => 65,  
  938. ),  
  939. 'state' => array( 
  940. 'required' => false,  
  941. ),  
  942. ),  
  943. 'TR' => array( 
  944. 'postcode' => array( 
  945. 'priority' => 65,  
  946. ),  
  947. 'state' => array( 
  948. 'label' => __( 'Province', 'woocommerce' ),  
  949. ),  
  950. ),  
  951. 'US' => array( 
  952. 'postcode' => array( 
  953. 'label' => __( 'ZIP', 'woocommerce' ),  
  954. ),  
  955. 'state' => array( 
  956. 'label' => __( 'State', 'woocommerce' ),  
  957. ),  
  958. ),  
  959. 'GB' => array( 
  960. 'postcode' => array( 
  961. 'label' => __( 'Postcode', 'woocommerce' ),  
  962. ),  
  963. 'state' => array( 
  964. 'label' => __( 'County', 'woocommerce' ),  
  965. 'required' => false,  
  966. ),  
  967. ),  
  968. 'VN' => array( 
  969. 'state' => array( 
  970. 'required' => false,  
  971. ),  
  972. 'postcode' => array( 
  973. 'priority' => 65,  
  974. 'required' => false,  
  975. 'hidden' => false,  
  976. ),  
  977. 'address_2' => array( 
  978. 'required' => false,  
  979. 'hidden' => true,  
  980. ),  
  981. ),  
  982. 'WS' => array( 
  983. 'postcode' => array( 
  984. 'required' => false,  
  985. 'hidden' => true,  
  986. ),  
  987. ),  
  988. 'YT' => array( 
  989. 'state' => array( 
  990. 'required' => false,  
  991. ),  
  992. ),  
  993. 'ZA' => array( 
  994. 'state' => array( 
  995. 'label' => __( 'Province', 'woocommerce' ),  
  996. ),  
  997. ),  
  998. 'ZW' => array( 
  999. 'postcode' => array( 
  1000. 'required' => false,  
  1001. 'hidden' => true,  
  1002. ),  
  1003. ),  
  1004. )); 
  1005.  
  1006. $this->locale = array_intersect_key( $this->locale, array_merge( $this->get_allowed_countries(), $this->get_shipping_countries() ) ); 
  1007.  
  1008. // Default Locale Can be filtered to override fields in get_address_fields(). Countries with no specific locale will use default. 
  1009. $this->locale['default'] = apply_filters( 'woocommerce_get_country_locale_default', $this->get_default_address_fields() ); 
  1010.  
  1011. // Filter default AND shop base locales to allow overides via a single function. These will be used when changing countries on the checkout 
  1012. if ( ! isset( $this->locale[ $this->get_base_country() ] ) ) { 
  1013. $this->locale[ $this->get_base_country() ] = $this->locale['default']; 
  1014.  
  1015. $this->locale['default'] = apply_filters( 'woocommerce_get_country_locale_base', $this->locale['default'] ); 
  1016. $this->locale[ $this->get_base_country() ] = apply_filters( 'woocommerce_get_country_locale_base', $this->locale[ $this->get_base_country() ] ); 
  1017.  
  1018. return $this->locale; 
  1019.  
  1020. /** 
  1021. * Apply locale and get address fields. 
  1022. * @param mixed $country (default: '') 
  1023. * @param string $type (default: 'billing_') 
  1024. * @return array 
  1025. */ 
  1026. public function get_address_fields( $country = '', $type = 'billing_' ) { 
  1027. if ( ! $country ) { 
  1028. $country = $this->get_base_country(); 
  1029.  
  1030. $fields = $this->get_default_address_fields(); 
  1031. $locale = $this->get_country_locale(); 
  1032.  
  1033. if ( isset( $locale[ $country ] ) ) { 
  1034. $fields = wc_array_overlay( $fields, $locale[ $country ] ); 
  1035.  
  1036. // Prepend field keys 
  1037. $address_fields = array(); 
  1038.  
  1039. foreach ( $fields as $key => $value ) { 
  1040. $keys = array_keys( $fields ); 
  1041. $address_fields[ $type . $key ] = $value; 
  1042.  
  1043. // Add email and phone fields. 
  1044. if ( 'billing_' === $type ) { 
  1045. $address_fields['billing_phone'] = array( 
  1046. 'label' => __( 'Phone', 'woocommerce' ),  
  1047. 'required' => true,  
  1048. 'type' => 'tel',  
  1049. 'class' => array( 'form-row-first' ),  
  1050. 'validate' => array( 'phone' ),  
  1051. 'autocomplete' => 'tel',  
  1052. 'priority' => 100,  
  1053. ); 
  1054. $address_fields['billing_email'] = array( 
  1055. 'label' => __( 'Email address', 'woocommerce' ),  
  1056. 'required' => true,  
  1057. 'type' => 'email',  
  1058. 'class' => array( 'form-row-last' ),  
  1059. 'validate' => array( 'email' ),  
  1060. 'autocomplete' => 'no' === get_option( 'woocommerce_registration_generate_username' ) ? 'email' : 'email username',  
  1061. 'priority' => 110,  
  1062. ); 
  1063.  
  1064. /** 
  1065. * Important note on this filter: Changes to address fields can and will be overridden by 
  1066. * the woocommerce_default_address_fields. The locales/default locales apply on top based 
  1067. * on country selection. If you want to change things like the required status of an 
  1068. * address field, filter woocommerce_default_address_fields instead. 
  1069. */ 
  1070. return apply_filters( 'woocommerce_' . $type . 'fields', $address_fields, $country ); 
.