PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / classes / countries.php

countries.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/classes/countries.php

1,804 lines 46.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * StoreEngine countries
4 *
5 * @package StoreEngine\l10n
6 */
7
8 namespace StoreEngine\Classes;
9
10 use StoreEngine\Traits\Singleton;
11 use StoreEngine\Utils\Formatting;
12 use StoreEngine\Utils\Helper;
13
14 /** @define "STOREENGINE_ROOT_DIR_PATH" "./../../" */
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit;
18 }
19
20 class Countries {
21
22 use Singleton;
23
24 /**
25 * Locales list.
26 *
27 * @var array
28 */
29 public array $locale = [];
30
31 /**
32 * List of address formats for locales.
33 *
34 * @var array
35 */
36 public array $address_formats = [];
37
38 /**
39 * Cache of geographical regions.
40 *
41 * Only to be used by the get_* and load_* methods, as other methods may expect the regions to be
42 * loaded on demand.
43 *
44 * @var array
45 */
46 private array $geo_cache = [];
47
48 /**
49 * Auto-load in-accessible properties on demand.
50 *
51 * @param mixed $key Key.
52 *
53 * @return array|false
54 */
55 public function __get( $key ) {
56 if ( 'countries' === $key ) {
57 return $this->get_countries();
58 } elseif ( 'states' === $key ) {
59 return $this->get_states();
60 } elseif ( 'continents' === $key ) {
61 return $this->get_continents();
62 }
63
64 return false;
65 }
66
67 /**
68 * Get all countries.
69 *
70 * @return array
71 */
72 public function get_countries(): array {
73 if ( empty( $this->geo_cache['countries'] ) ) {
74 /**
75 * Allows filtering of the list of countries in StoreEngine.
76 *
77 * @param array $countries
78 */
79 $this->geo_cache['countries'] = apply_filters( 'storeengine/countries', include STOREENGINE_ROOT_DIR_PATH . 'i18n/countries.php' );
80 if ( apply_filters( 'storeengine/sort_countries', true ) ) {
81 Helper::asort_by_locale( $this->geo_cache['countries'] );
82 }
83 }
84
85 return $this->geo_cache['countries'];
86 }
87
88 /**
89 * @param string $cc
90 *
91 * @return string|null
92 */
93 public function get_country( string $cc ): ?string {
94 return $this->get_countries()[ $cc ] ?? null;
95 }
96
97 /**
98 * Check if a given code represents a valid ISO 3166-1 alpha-2 code for a country known to us.
99 *
100 * @param string $country_code The country code to check as a ISO 3166-1 alpha-2 code.
101 *
102 * @return bool True if the country is known to us, false otherwise.
103 */
104 public function country_exists( string $country_code ): bool {
105 return isset( $this->get_countries()[ $country_code ] );
106 }
107
108 /**
109 * Get all continents.
110 *
111 * @return array
112 */
113 public function get_continents(): array {
114 if ( empty( $this->geo_cache['continents'] ) ) {
115 /**
116 * Allows filtering of continents in StoreEngine.
117 *
118 * @param array[array] $continents
119 */
120 $this->geo_cache['continents'] = apply_filters( 'storeengine/continents', include STOREENGINE_ROOT_DIR_PATH . 'i18n/continents.php' );
121 }
122
123 return $this->geo_cache['continents'];
124 }
125
126 /**
127 * Get continent code for a country code.
128 *
129 * @param string $cc Country code.
130 *
131 * @return string
132 */
133 public function get_continent_code_for_country( string $cc ): string {
134 $cc = trim( strtoupper( $cc ) );
135 $continents = $this->get_continents();
136 $continents_and_ccs = wp_list_pluck( $continents, 'countries' );
137 foreach ( $continents_and_ccs as $continent_code => $countries ) {
138 if ( false !== array_search( $cc, $countries, true ) ) {
139 return $continent_code;
140 }
141 }
142
143 return '';
144 }
145
146 /**
147 * Get calling code for a country code.
148 *
149 * @param string $cc Country code.
150 *
151 * @return string|array Some countries have multiple. The code will be stripped of - and spaces and always be prefixed with +.
152 */
153 public function get_country_calling_code( string $cc ) {
154 $codes = wp_cache_get( 'calling-codes', 'countries' );
155
156 if ( ! $codes ) {
157 $codes = include STOREENGINE_ROOT_DIR_PATH . 'i18n/phone.php';
158 wp_cache_set( 'calling-codes', $codes, 'countries' );
159 }
160
161 $calling_code = $codes[ $cc ] ?? '';
162
163 if ( is_array( $calling_code ) ) {
164 $calling_code = $calling_code[0];
165 }
166
167 return $calling_code;
168 }
169
170 /**
171 * Get continents that the store ships to.
172 *
173 * @return array
174 */
175 public function get_shipping_continents(): array {
176 $continents = $this->get_continents();
177 $shipping_countries = $this->get_shipping_countries();
178 $shipping_country_codes = array_keys( $shipping_countries );
179 $shipping_continents = [];
180
181 foreach ( $continents as $continent_code => $continent ) {
182 if ( count( array_intersect( $continent['countries'], $shipping_country_codes ) ) ) {
183 $shipping_continents[ $continent_code ] = $continent;
184 }
185 }
186
187 return $shipping_continents;
188 }
189
190 /**
191 * Get the states for a country.
192 *
193 * @param string|null $cc Country code.
194 *
195 * @return false|array of states
196 */
197 public function get_states( ?string $cc = null ) {
198 if ( ! isset( $this->geo_cache['states'] ) ) {
199 /**
200 * Allows filtering of country states in StoreEngine.
201 *
202 * @param array $states
203 */
204 $this->geo_cache['states'] = apply_filters( 'storeengine/states', include STOREENGINE_ROOT_DIR_PATH . 'i18n/states.php' );
205 }
206
207 if ( ! is_null( $cc ) ) {
208 return $this->geo_cache['states'][ $cc ] ?? false;
209 } else {
210 return $this->geo_cache['states'];
211 }
212 }
213
214 /**
215 * Get the base address (first line) for the store.
216 *
217 * @return string
218 */
219 public function get_base_address(): string {
220 $base_address = Helper::get_settings( 'store_address', '' );
221
222 return apply_filters( 'storeengine/countries_base_address', $base_address );
223 }
224
225 /**
226 * Get the base address (second line) for the store.
227 *
228 * @return string
229 */
230 public function get_base_address_2(): string {
231 $base_address_2 = Helper::get_settings( 'store_address_2', '' );
232
233 return apply_filters( 'storeengine/countries_base_address_2', $base_address_2 );
234 }
235
236 /**
237 * Get the base country for the store.
238 *
239 * @return string
240 */
241 public function get_base_country(): string {
242 return (string) apply_filters( 'storeengine/countries_base_country', Helper::get_settings( 'store_country', '' ) );
243 }
244
245 /**
246 * Get the base state for the store.
247 *
248 * @return string
249 */
250 public function get_base_state(): string {
251 return (string) apply_filters( 'storeengine/countries_base_state', Helper::get_settings( 'store_state', '' ) );
252 }
253
254 /**
255 * Get the base city for the store.
256 *
257 * @return string
258 * @version 3.1.1
259 */
260 public function get_base_city(): string {
261 return (string) apply_filters( 'storeengine/countries_base_city', Helper::get_settings( 'store_city', '' ) );
262 }
263
264 /**
265 * Get the base postcode for the store.
266 *
267 * @return string
268 */
269 public function get_base_postcode(): string {
270 return (string) apply_filters( 'storeengine/countries_base_postcode', Helper::get_settings( 'store_postcode', '' ) );
271 }
272
273 /**
274 * Get countries that the store sells to.
275 *
276 * @return array
277 */
278 public function get_allowed_countries(): array {
279 $countries = $this->countries;
280 $allowed_countries = Helper::get_settings( 'allowed_countries' );
281
282 if ( 'all_except' === $allowed_countries ) {
283 $except_countries = Helper::get_settings( 'all_except_countries', [] );
284
285 if ( $except_countries ) {
286 foreach ( $except_countries as $country ) {
287 unset( $countries[ $country ] );
288 }
289 }
290 } elseif ( 'specific' === $allowed_countries ) {
291 $countries = [];
292 $raw_countries = Helper::get_settings( 'specific_allowed_countries', [] );
293
294 if ( $raw_countries ) {
295 foreach ( $raw_countries as $country ) {
296 $countries[ $country ] = $this->countries[ $country ];
297 }
298 }
299 }
300
301 /**
302 * Filter the list of allowed selling countries.
303 *
304 * @param array $countries
305 */
306 return apply_filters( 'storeengine/countries_allowed_countries', $countries );
307 }
308
309 /**
310 * Get countries that the store ships to.
311 *
312 * @return array
313 */
314 public function get_shipping_countries(): array {
315 // If shipping is disabled, return an empty array.
316 if ( 'disabled' === Helper::get_settings( 'ship_to_countries' ) ) {
317 return [];
318 }
319
320 // Default to selling countries.
321 $countries = $this->get_allowed_countries();
322
323 // All indicates that all countries are allowed, regardless of where you sell to.
324 if ( 'all' === Helper::get_settings( 'ship_to_countries' ) ) {
325 $countries = $this->get_countries();
326 } elseif ( 'specific' === Helper::get_settings( 'ship_to_countries' ) ) {
327 $countries = [];
328 $raw_countries = Helper::get_settings( 'specific_ship_to_countries', [] );
329
330 if ( $raw_countries ) {
331 foreach ( $raw_countries as $country ) {
332 $countries[ $country ] = $this->countries[ $country ];
333 }
334 }
335 }
336
337 /**
338 * Filter the list of allowed selling countries.
339 *
340 * @param array $countries
341 */
342 return apply_filters( 'storeengine/shipping/countries', $countries );
343 }
344
345 /**
346 * Get allowed country states.
347 *
348 * @return array
349 */
350 public function get_allowed_country_states(): array {
351 if ( Helper::get_settings( 'allowed_countries' ) !== 'specific' ) {
352 return $this->states;
353 }
354
355 $states = [];
356
357 $raw_countries = Helper::get_settings( 'specific_allowed_countries' );
358
359 if ( $raw_countries ) {
360 foreach ( $raw_countries as $country ) {
361 if ( isset( $this->states[ $country ] ) ) {
362 $states[ $country ] = $this->states[ $country ];
363 }
364 }
365 }
366
367 return apply_filters( 'storeengine/countries_allowed_country_states', $states );
368 }
369
370 /**
371 * Get shipping country states.
372 *
373 * @return array
374 */
375 public function get_shipping_country_states(): array {
376 if ( Helper::get_settings( 'ship_to_countries' ) === '' ) {
377 return $this->get_allowed_country_states();
378 }
379
380 if ( Helper::get_settings( 'ship_to_countries' ) !== 'specific' ) {
381 return $this->states;
382 }
383
384 $states = [];
385
386 $raw_countries = Helper::get_settings( 'specific_ship_to_countries' );
387
388 if ( $raw_countries ) {
389 foreach ( $raw_countries as $country ) {
390 if ( ! empty( $this->states[ $country ] ) ) {
391 $states[ $country ] = $this->states[ $country ];
392 }
393 }
394 }
395
396 return apply_filters( 'storeengine/countries_shipping_country_states', $states );
397 }
398
399 /**
400 * Gets an array of countries in the EU.
401 *
402 * @param string $type Type of countries to retrieve. Blank for EU member countries. eu_vat for EU VAT countries.
403 *
404 * @return string[]
405 */
406 public function get_european_union_countries( $type = '' ) {
407 $countries = [
408 'AT',
409 'BE',
410 'BG',
411 'CY',
412 'CZ',
413 'DE',
414 'DK',
415 'EE',
416 'ES',
417 'FI',
418 'FR',
419 'GR',
420 'HR',
421 'HU',
422 'IE',
423 'IT',
424 'LT',
425 'LU',
426 'LV',
427 'MT',
428 'NL',
429 'PL',
430 'PT',
431 'RO',
432 'SE',
433 'SI',
434 'SK',
435 ];
436
437 if ( 'eu_vat' === $type ) {
438 $countries[] = 'MC';
439 }
440
441 return apply_filters( 'storeengine/european_union_countries', $countries, $type );
442 }
443
444 /**
445 * Gets an array of countries using VAT.
446 *
447 * @return string[] of country codes.
448 */
449 public function get_vat_countries() {
450 $eu_countries = $this->get_european_union_countries();
451 $vat_countries = [
452 'AE',
453 'AL',
454 'AR',
455 'AZ',
456 'BB',
457 'BH',
458 'BO',
459 'BS',
460 'BY',
461 'CL',
462 'CO',
463 'EC',
464 'EG',
465 'ET',
466 'FJ',
467 'GB',
468 'GH',
469 'GM',
470 'GT',
471 'IL',
472 'IM',
473 'IN',
474 'IR',
475 'KN',
476 'KR',
477 'KZ',
478 'LK',
479 'MC',
480 'MD',
481 'ME',
482 'MK',
483 'MN',
484 'MU',
485 'MX',
486 'NA',
487 'NG',
488 'NO',
489 'NP',
490 'PS',
491 'PY',
492 'RS',
493 'RU',
494 'RW',
495 'SA',
496 'SV',
497 'TH',
498 'TR',
499 'UA',
500 'UY',
501 'UZ',
502 'VE',
503 'VN',
504 'ZA',
505 ];
506
507 return apply_filters( 'storeengine/vat_countries', array_merge( $eu_countries, $vat_countries ) );
508 }
509
510 /**
511 * Gets the correct string for shipping - either 'to the' or 'to'.
512 *
513 * @param string $country_code Country code.
514 *
515 * @return string
516 */
517 public function shipping_to_prefix( $country_code = '' ) {
518 if ( ! $country_code ) {
519 $customer = Helper::get_customer();
520 $country_code = $customer ? $customer->get_shipping_country() ?? ( $customer->get_billing_country() ?? '' ) : '';
521 }
522
523 $countries = [ 'AE', 'CZ', 'DO', 'GB', 'NL', 'PH', 'US', 'USAF' ];
524 $return = in_array( $country_code, $countries, true ) ? _x( 'to the', 'shipping country prefix', 'storeengine' ) : _x( 'to', 'shipping country prefix', 'storeengine' );
525
526 return apply_filters( 'storeengine/countries_shipping_to_prefix', $return, $country_code );
527 }
528
529 /**
530 * Prefix certain countries with 'the'.
531 *
532 * @param string $country_code Country code.
533 *
534 * @return string
535 */
536 public function estimated_for_prefix( $country_code = '' ) {
537 $country_code = $country_code ? $country_code : $this->get_base_country();
538 $countries = [ 'AE', 'CZ', 'DO', 'GB', 'NL', 'PH', 'US', 'USAF' ];
539 $return = in_array( $country_code, $countries, true ) ? __( 'the', 'storeengine' ) . ' ' : '';
540
541 return apply_filters( 'storeengine/countries_estimated_for_prefix', $return, $country_code );
542 }
543
544 /**
545 * Correctly name tax in some countries VAT on the frontend.
546 *
547 * @return string
548 */
549 public function tax_or_vat() {
550 $return = in_array( $this->get_base_country(), $this->get_vat_countries(), true ) ? __( 'VAT', 'storeengine' ) : __( 'Tax', 'storeengine' );
551
552 return apply_filters( 'storeengine/countries_tax_or_vat', $return );
553 }
554
555 /**
556 * Include the Inc Tax label.
557 *
558 * @return string
559 */
560 public function inc_tax_or_vat() {
561 $return = in_array( $this->get_base_country(), $this->get_vat_countries(), true ) ? __( '(incl. VAT)', 'storeengine' ) : __( '(incl. tax)', 'storeengine' );
562
563 return apply_filters( 'storeengine/countries_inc_tax_or_vat', $return );
564 }
565
566 /**
567 * Include the Ex Tax label.
568 *
569 * @return string
570 */
571 public function ex_tax_or_vat(): string {
572 $return = in_array( $this->get_base_country(), $this->get_vat_countries(), true ) ? __( '(ex. VAT)', 'storeengine' ) : __( '(ex. tax)', 'storeengine' );
573
574 return apply_filters( 'storeengine/countries_ex_tax_or_vat', $return );
575 }
576
577 /**
578 * Outputs the list of countries and states for use in dropdown boxes.
579 *
580 * @param string $selected_country Selected country.
581 * @param string $selected_state Selected state.
582 * @param bool $escape If we should escape HTML.
583 */
584 public function country_dropdown_options( string $selected_country = '', string $selected_state = '', bool $escape = false ) {
585 if ( $this->countries ) {
586 foreach ( $this->countries as $key => $value ) {
587 $states = $this->get_states( $key );
588 if ( $states ) {
589 // Maybe default the selected state as the first one.
590 if ( $selected_country === $key && '*' === $selected_state ) {
591 $selected_state = key( $states ) ?? '*';
592 }
593
594 echo '<optgroup label="' . esc_attr( $value ) . '">';
595 foreach ( $states as $state_key => $state_value ) {
596 echo '<option value="' . esc_attr( $key ) . ':' . esc_attr( $state_key ) . '"';
597
598 if ( $selected_country === $key && $selected_state === $state_key ) {
599 echo ' selected="selected"';
600 }
601
602 echo '>' . esc_html( $value ) . ' &mdash; ' . ( $escape ? esc_html( $state_value ) : $state_value ) . '</option>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
603 }
604 echo '</optgroup>';
605 } else {
606 echo '<option';
607 if ( $selected_country === $key && '*' === $selected_state ) {
608 echo ' selected="selected"';
609 }
610 echo ' value="' . esc_attr( $key ) . '">' . ( $escape ? esc_html( $value ) : $value ) . '</option>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
611 }
612 }
613 }
614 }
615
616 /**
617 * Get country address formats.
618 *
619 * These define how addresses are formatted for display in various countries.
620 *
621 * @return array
622 */
623 public function get_address_formats(): array {
624 if ( empty( $this->address_formats ) ) {
625 $this->address_formats = apply_filters(
626 'storeengine/localisation_address_formats',
627 [
628 'default' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode}\n{country}",
629 'AT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
630 'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
631 'BE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
632 'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state_code} {postcode}\n{country}",
633 'CH' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
634 'CL' => "{company}\n{name}\n{address_1}\n{address_2}\n{state}\n{postcode} {city}\n{country}",
635 'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",
636 'CZ' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
637 'DE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
638 'DK' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
639 'EE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
640 'ES' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city}\n{state}\n{country}",
641 'FI' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
642 'FR' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city_upper}\n{country}",
643 'HK' => "{company}\n{first_name} {last_name_upper}\n{address_1}\n{address_2}\n{city_upper}\n{state_upper}\n{country}",
644 'HU' => "{last_name} {first_name}\n{company}\n{city}\n{address_1}\n{address_2}\n{postcode}\n{country}",
645 'IN' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {postcode}\n{state}, {country}",
646 'IS' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
647 'IT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode}\n{city}\n{state_upper}\n{country}",
648 'JM' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode_upper}\n{country}",
649 'JP' => "{postcode}\n{state} {city} {address_1}\n{address_2}\n{company}\n{last_name} {first_name}\n{country}",
650 'LI' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
651 'NL' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
652 'NO' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
653 'NZ' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {postcode}\n{country}",
654 'PL' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
655 'PR' => "{company}\n{name}\n{address_1} {address_2}\n{city} \n{country} {postcode}",
656 'PT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
657 'RS' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
658 'SE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
659 'SI' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
660 'SK' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
661 'TR' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city} {state}\n{country}",
662 'TW' => "{company}\n{last_name} {first_name}\n{address_1}\n{address_2}\n{state}, {city} {postcode}\n{country}",
663 'UG' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}, {country}",
664 'US' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}, {state_code} {postcode}\n{country}",
665 'VN' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {postcode}\n{country}",
666 ]
667 );
668 }
669
670 return $this->address_formats;
671 }
672
673 /**
674 * Get country address format.
675 *
676 * @param array $args Arguments.
677 * @param string $separator How to separate address lines.
678 *
679 * @return string
680 */
681 public function get_formatted_address( $args = [], $separator = '<br/>' ): string {
682 $default_args = [
683 'first_name' => '',
684 'last_name' => '',
685 'company' => '',
686 'address_1' => '',
687 'address_2' => '',
688 'city' => '',
689 'state' => '',
690 'postcode' => '',
691 'country' => '',
692 ];
693
694 $args = array_map( fn( $v ) => trim( $v ?? '' ), wp_parse_args( $args, $default_args ) );
695 $state = $args['state'];
696 $country = $args['country'];
697
698 // Get all formats.
699 $formats = $this->get_address_formats();
700
701 // Get format for the address' country.
702 $format = ( $country && isset( $formats[ $country ] ) ) ? $formats[ $country ] : $formats['default'];
703
704 // Handle full country name.
705 $full_country = ( isset( $this->countries[ $country ] ) ) ? $this->countries[ $country ] : $country;
706
707 // Country is not needed if the same as base.
708 if ( $country === $this->get_base_country() && ! apply_filters( 'storeengine/formatted_address_force_country_display', false ) ) {
709 $format = str_replace( '{country}', '', $format );
710 }
711
712 // Handle full state name.
713 $full_state = ( $country && $state && isset( $this->states[ $country ][ $state ] ) ) ? $this->states[ $country ][ $state ] : $state;
714
715 // Substitute address parts into the string.
716 $replace = array_map(
717 'esc_html',
718 apply_filters(
719 'storeengine/formatted_address_replacements',
720 [
721 '{first_name}' => $args['first_name'],
722 '{last_name}' => $args['last_name'],
723 '{name}' => sprintf(
724 /* translators: 1: first name 2: last name */
725 _x( '%1$s %2$s', 'full name', 'storeengine' ),
726 $args['first_name'],
727 $args['last_name']
728 ),
729 '{company}' => $args['company'],
730 '{address_1}' => $args['address_1'],
731 '{address_2}' => $args['address_2'],
732 '{city}' => $args['city'],
733 '{state}' => $full_state,
734 '{postcode}' => $args['postcode'],
735 '{country}' => $full_country,
736 '{first_name_upper}' => Formatting::strtoupper( $args['first_name'] ),
737 '{last_name_upper}' => Formatting::strtoupper( $args['last_name'] ),
738 '{name_upper}' => Formatting::strtoupper(
739 sprintf(
740 /* translators: 1: first name 2: last name */
741 _x( '%1$s %2$s', 'full name', 'storeengine' ),
742 $args['first_name'],
743 $args['last_name']
744 )
745 ),
746 '{company_upper}' => Formatting::strtoupper( $args['company'] ),
747 '{address_1_upper}' => Formatting::strtoupper( $args['address_1'] ),
748 '{address_2_upper}' => Formatting::strtoupper( $args['address_2'] ),
749 '{city_upper}' => Formatting::strtoupper( $args['city'] ),
750 '{state_upper}' => Formatting::strtoupper( $full_state ),
751 '{state_code}' => Formatting::strtoupper( $state ),
752 '{postcode_upper}' => Formatting::strtoupper( $args['postcode'] ),
753 '{country_upper}' => Formatting::strtoupper( $full_country ),
754 ],
755 $args
756 )
757 );
758
759 $formatted_address = str_replace( array_keys( $replace ), $replace, $format );
760
761 // Clean up white space.
762 $formatted_address = preg_replace( '/ +/', ' ', trim( $formatted_address ) );
763 $formatted_address = preg_replace( '/\n\n+/', "\n", $formatted_address );
764
765 // Break newlines apart and remove empty lines/trim commas and white space.
766 $formatted_address = explode( "\n", $formatted_address );
767 $formatted_address = array_filter( array_map( [ $this, 'trim_formatted_address_line' ], $formatted_address ) );
768
769 // Add html breaks.
770 // We're done!
771 return implode( $separator, $formatted_address );
772 }
773
774 /**
775 * Trim white space and commas off a line.
776 *
777 * @param string $line Line.
778 *
779 * @return string
780 */
781 private function trim_formatted_address_line( string $line ): string {
782 return trim( $line, ', ' );
783 }
784
785 /**
786 * Returns the fields we show by default. This can be filtered later on.
787 *
788 * @return array
789 */
790 public function get_default_address_fields(): array {
791 $address_2_label = __( 'Apartment, suite, unit, etc.', 'storeengine' );
792
793 // If necessary, append '(optional)' to the placeholder: we don't need to worry about the
794 // label, though, as storeengine/form_field() takes care of that.
795 if ( 'optional' === Helper::get_settings( 'checkout_address_2_field', 'optional' ) ) {
796 $address_2_placeholder = __( 'Apartment, suite, unit, etc. (optional)', 'storeengine' );
797 } else {
798 $address_2_placeholder = $address_2_label;
799 }
800
801 $fields = [
802 'first_name' => [
803 'label' => __( 'First name', 'storeengine' ),
804 'required' => true,
805 'class' => [ 'storeengine-col-6' ],
806 'autocomplete' => 'given-name',
807 'priority' => 10,
808 ],
809 'last_name' => [
810 'label' => __( 'Last name', 'storeengine' ),
811 'required' => true,
812 'class' => [ 'storeengine-col-6' ],
813 'autocomplete' => 'family-name',
814 'priority' => 20,
815 ],
816 'company' => [
817 'label' => __( 'Company name', 'storeengine' ),
818 'class' => [ 'storeengine-col-12' ],
819 'autocomplete' => 'organization',
820 'priority' => 30,
821 'required' => 'required' === Helper::get_settings( 'checkout_company_field', 'optional' ),
822 ],
823 'country' => [
824 'type' => 'country',
825 'label' => __( 'Country / Region', 'storeengine' ),
826 'required' => true,
827 'class' => [ 'storeengine-col-12', 'address-field', 'update_totals_on_change' ],
828 'autocomplete' => 'country',
829 'priority' => 40,
830 ],
831 'address_1' => [
832 'label' => __( 'Street address', 'storeengine' ),
833 /* translators: use local order of street name and house number. */
834 'placeholder' => esc_attr__( 'House number and street name', 'storeengine' ),
835 'required' => true,
836 'class' => [ 'storeengine-col-12', 'address-field' ],
837 'autocomplete' => 'address-line1',
838 'priority' => 50,
839 ],
840 'address_2' => [
841 'label' => $address_2_label,
842 'label_class' => [ 'screen-reader-text' ],
843 'placeholder' => esc_attr( $address_2_placeholder ),
844 'class' => [ 'storeengine-col-12', 'address-field' ],
845 'autocomplete' => 'address-line2',
846 'priority' => 60,
847 'required' => 'required' === Helper::get_settings( 'checkout_address_2_field', 'optional' ),
848 ],
849 'city' => [
850 'label' => __( 'Town / City', 'storeengine' ),
851 'required' => true,
852 'class' => [ 'storeengine-col-12', 'address-field' ],
853 'autocomplete' => 'address-level2',
854 'priority' => 70,
855 ],
856 'state' => [
857 'type' => 'state',
858 'label' => __( 'State / County', 'storeengine' ),
859 'required' => true,
860 'class' => [ 'storeengine-col-12', 'address-field' ],
861 'validate' => [ 'state' ],
862 'autocomplete' => 'address-level1',
863 'priority' => 80,
864 ],
865 'postcode' => [
866 'label' => __( 'Postcode / ZIP', 'storeengine' ),
867 'required' => true,
868 'class' => [ 'storeengine-col-12', 'address-field' ],
869 'validate' => [ 'postcode' ],
870 'autocomplete' => 'postal-code',
871 'priority' => 90,
872 ],
873 'phone' => [
874 'label' => __( 'Phone', 'storeengine' ),
875 'required' => false,
876 'type' => 'tel',
877 'class' => [ 'storeengine-col-12' ],
878 'validate' => [ 'phone' ],
879 'autocomplete' => 'tel',
880 'priority' => 100,
881 ],
882 'email' => [
883 'label' => __( 'Email address', 'storeengine' ),
884 'required' => false,
885 'type' => 'email',
886 'class' => [ 'storeengine-col-12' ],
887 'validate' => [ 'email' ],
888 'autocomplete' => 'email',
889 'priority' => 110,
890 ],
891 ];
892
893 if ( 'hidden' === Helper::get_settings( 'checkout_company_field', 'optional' ) ) {
894 unset( $fields['company'] );
895 }
896
897 if ( 'hidden' === Helper::get_settings( 'checkout_address_2_field', 'optional' ) ) {
898 unset( $fields['address_2'] );
899 }
900
901 $default_address_fields = apply_filters( 'storeengine/default_address_fields', $fields );
902
903 // Sort each of the fields based on priority.
904 uasort( $default_address_fields, [ Helper::class, 'checkout_fields_uasort_comparison' ] );
905
906 return $default_address_fields;
907 }
908
909 /**
910 * Get JS selectors for fields which are shown/hidden depending on the locale.
911 *
912 * @return array
913 */
914 public function get_country_locale_field_selectors(): array {
915 return apply_filters( 'storeengine/country_locale_field_selectors', [
916 'address_1' => '#billing_address_1_field, #shipping_address_1_field',
917 'address_2' => '#billing_address_2_field, #shipping_address_2_field',
918 'state' => '#billing_state_field, #shipping_state_field, #calc_shipping_state_field',
919 'postcode' => '#billing_postcode_field, #shipping_postcode_field, #calc_shipping_postcode_field',
920 'city' => '#billing_city_field, #shipping_city_field, #calc_shipping_city_field',
921 ] );
922 }
923
924 /**
925 * Get country locale settings.
926 *
927 * These locales override the default country selections after a country is chosen.
928 *
929 * @return array
930 */
931 public function get_country_locale(): array {
932 if ( empty( $this->locale ) ) {
933 $this->locale = apply_filters(
934 'storeengine/get_country_locale',
935 [
936 'AE' => [
937 'postcode' => [
938 'required' => false,
939 'hidden' => true,
940 ],
941 'state' => [
942 'required' => false,
943 ],
944 ],
945 'AF' => [
946 'state' => [
947 'required' => false,
948 'hidden' => true,
949 ],
950 ],
951 'AL' => [
952 'state' => [
953 'label' => __( 'County', 'storeengine' ),
954 ],
955 ],
956 'AO' => [
957 'postcode' => [
958 'required' => false,
959 'hidden' => true,
960 ],
961 'state' => [
962 'label' => __( 'Province', 'storeengine' ),
963 ],
964 ],
965 'AT' => [
966 'postcode' => [
967 'priority' => 65,
968 ],
969 'state' => [
970 'required' => false,
971 'hidden' => true,
972 ],
973 ],
974 'AU' => [
975 'city' => [
976 'label' => __( 'Suburb', 'storeengine' ),
977 ],
978 'postcode' => [
979 'label' => __( 'Postcode', 'storeengine' ),
980 ],
981 'state' => [
982 'label' => __( 'State', 'storeengine' ),
983 ],
984 ],
985 'AX' => [
986 'postcode' => [
987 'priority' => 65,
988 ],
989 'state' => [
990 'required' => false,
991 'hidden' => true,
992 ],
993 ],
994 'BA' => [
995 'postcode' => [
996 'priority' => 65,
997 ],
998 'state' => [
999 'label' => __( 'Canton', 'storeengine' ),
1000 'required' => false,
1001 'hidden' => true,
1002 ],
1003 ],
1004 'BD' => [
1005 'postcode' => [
1006 'required' => false,
1007 ],
1008 'state' => [
1009 'label' => __( 'District', 'storeengine' ),
1010 ],
1011 ],
1012 'BE' => [
1013 'postcode' => [
1014 'priority' => 65,
1015 ],
1016 'state' => [
1017 'required' => false,
1018 'hidden' => true,
1019 ],
1020 ],
1021 'BG' => [
1022 'state' => [
1023 'required' => false,
1024 ],
1025 ],
1026 'BH' => [
1027 'postcode' => [
1028 'required' => false,
1029 ],
1030 'state' => [
1031 'required' => false,
1032 'hidden' => true,
1033 ],
1034 ],
1035 'BI' => [
1036 'state' => [
1037 'required' => false,
1038 'hidden' => true,
1039 ],
1040 ],
1041 'BO' => [
1042 'postcode' => [
1043 'required' => false,
1044 'hidden' => true,
1045 ],
1046 'state' => [
1047 'label' => __( 'Department', 'storeengine' ),
1048 ],
1049 ],
1050 'BS' => [
1051 'postcode' => [
1052 'required' => false,
1053 'hidden' => true,
1054 ],
1055 ],
1056 'BZ' => [
1057 'postcode' => [
1058 'required' => false,
1059 'hidden' => true,
1060 ],
1061 'state' => [
1062 'required' => false,
1063 ],
1064 ],
1065 'CA' => [
1066 'postcode' => [
1067 'label' => __( 'Postal code', 'storeengine' ),
1068 ],
1069 'state' => [
1070 'label' => __( 'Province', 'storeengine' ),
1071 ],
1072 ],
1073 'CH' => [
1074 'postcode' => [
1075 'priority' => 65,
1076 ],
1077 'state' => [
1078 'label' => __( 'Canton', 'storeengine' ),
1079 'required' => false,
1080 ],
1081 ],
1082 'CL' => [
1083 'city' => [
1084 'required' => true,
1085 ],
1086 'postcode' => [
1087 'required' => false,
1088 // Hidden for stores within Chile.
1089 'hidden' => 'CL' === $this->get_base_country(),
1090 ],
1091 'state' => [
1092 'label' => __( 'Region', 'storeengine' ),
1093 ],
1094 ],
1095 'CN' => [
1096 'state' => [
1097 'label' => __( 'Province', 'storeengine' ),
1098 ],
1099 ],
1100 'CO' => [
1101 'postcode' => [
1102 'required' => false,
1103 ],
1104 'state' => [
1105 'label' => __( 'Department', 'storeengine' ),
1106 ],
1107 ],
1108 'CR' => [
1109 'state' => [
1110 'label' => __( 'Province', 'storeengine' ),
1111 ],
1112 ],
1113 'CW' => [
1114 'postcode' => [
1115 'required' => false,
1116 'hidden' => true,
1117 ],
1118 'state' => [
1119 'required' => false,
1120 ],
1121 ],
1122 'CY' => [
1123 'state' => [
1124 'required' => false,
1125 'hidden' => true,
1126 ],
1127 ],
1128 'CZ' => [
1129 'state' => [
1130 'required' => false,
1131 'hidden' => true,
1132 ],
1133 ],
1134 'DE' => [
1135 'postcode' => [
1136 'priority' => 65,
1137 ],
1138 'state' => [
1139 'required' => false,
1140 ],
1141 ],
1142 'DK' => [
1143 'postcode' => [
1144 'priority' => 65,
1145 ],
1146 'state' => [
1147 'required' => false,
1148 'hidden' => true,
1149 ],
1150 ],
1151 'DO' => [
1152 'state' => [
1153 'label' => __( 'Province', 'storeengine' ),
1154 ],
1155 ],
1156 'EC' => [
1157 'state' => [
1158 'label' => __( 'Province', 'storeengine' ),
1159 ],
1160 ],
1161 'EE' => [
1162 'postcode' => [
1163 'priority' => 65,
1164 ],
1165 'state' => [
1166 'required' => false,
1167 'hidden' => true,
1168 ],
1169 ],
1170 'ET' => [
1171 'state' => [
1172 'required' => false,
1173 'hidden' => true,
1174 ],
1175 ],
1176 'FI' => [
1177 'postcode' => [
1178 'priority' => 65,
1179 ],
1180 'state' => [
1181 'required' => false,
1182 'hidden' => true,
1183 ],
1184 ],
1185 'FR' => [
1186 'postcode' => [
1187 'priority' => 65,
1188 ],
1189 'state' => [
1190 'required' => false,
1191 'hidden' => true,
1192 ],
1193 ],
1194 'GG' => [
1195 'state' => [
1196 'required' => false,
1197 'label' => __( 'Parish', 'storeengine' ),
1198 ],
1199 ],
1200 'GH' => [
1201 'postcode' => [
1202 'required' => false,
1203 ],
1204 'state' => [
1205 'label' => __( 'Region', 'storeengine' ),
1206 ],
1207 ],
1208 'GP' => [
1209 'state' => [
1210 'required' => false,
1211 'hidden' => true,
1212 ],
1213 ],
1214 'GF' => [
1215 'state' => [
1216 'required' => false,
1217 'hidden' => true,
1218 ],
1219 ],
1220 'GR' => [
1221 'state' => [
1222 'required' => false,
1223 ],
1224 ],
1225 'GT' => [
1226 'postcode' => [
1227 'required' => false,
1228 ],
1229 'state' => [
1230 'label' => __( 'Department', 'storeengine' ),
1231 ],
1232 ],
1233 'HK' => [
1234 'postcode' => [
1235 'required' => false,
1236 ],
1237 'city' => [
1238 'label' => __( 'Town / District', 'storeengine' ),
1239 ],
1240 'state' => [
1241 'label' => __( 'Region', 'storeengine' ),
1242 ],
1243 ],
1244 'HN' => [
1245 'state' => [
1246 'label' => __( 'Department', 'storeengine' ),
1247 ],
1248 ],
1249 'HU' => [
1250 'last_name' => [
1251 'class' => [ 'storeengine-col-6' ],
1252 'priority' => 10,
1253 ],
1254 'first_name' => [
1255 'class' => [ 'storeengine-col-6' ],
1256 'priority' => 20,
1257 ],
1258 'postcode' => [
1259 'class' => [ 'storeengine-col-6', 'address-field' ],
1260 'priority' => 65,
1261 ],
1262 'city' => [
1263 'class' => [ 'storeengine-col-6', 'address-field' ],
1264 ],
1265 'address_1' => [
1266 'priority' => 71,
1267 ],
1268 'address_2' => [
1269 'priority' => 72,
1270 ],
1271 'state' => [
1272 'label' => __( 'County', 'storeengine' ),
1273 'required' => false,
1274 ],
1275 ],
1276 'ID' => [
1277 'state' => [
1278 'label' => __( 'Province', 'storeengine' ),
1279 ],
1280 ],
1281 'IE' => [
1282 'postcode' => [
1283 'required' => false,
1284 'label' => __( 'Eircode', 'storeengine' ),
1285 ],
1286 'state' => [
1287 'label' => __( 'County', 'storeengine' ),
1288 ],
1289 ],
1290 'IS' => [
1291 'postcode' => [
1292 'priority' => 65,
1293 ],
1294 'state' => [
1295 'required' => false,
1296 'hidden' => true,
1297 ],
1298 ],
1299 'IL' => [
1300 'postcode' => [
1301 'priority' => 65,
1302 ],
1303 'state' => [
1304 'required' => false,
1305 'hidden' => true,
1306 ],
1307 ],
1308 'IM' => [
1309 'state' => [
1310 'required' => false,
1311 'hidden' => true,
1312 ],
1313 ],
1314 'IN' => [
1315 'postcode' => [
1316 'label' => __( 'PIN Code', 'storeengine' ),
1317 ],
1318 'state' => [
1319 'label' => __( 'State', 'storeengine' ),
1320 ],
1321 ],
1322 'IR' => [
1323 'state' => [
1324 'priority' => 50,
1325 ],
1326 'city' => [
1327 'priority' => 60,
1328 ],
1329 'address_1' => [
1330 'priority' => 70,
1331 ],
1332 'address_2' => [
1333 'priority' => 80,
1334 ],
1335 ],
1336 'IT' => [
1337 'postcode' => [
1338 'priority' => 65,
1339 ],
1340 'state' => [
1341 'required' => true,
1342 'label' => __( 'Province', 'storeengine' ),
1343 ],
1344 ],
1345 'JM' => [
1346 'city' => [
1347 'label' => __( 'Town / City / Post Office', 'storeengine' ),
1348 ],
1349 'postcode' => [
1350 'required' => false,
1351 'label' => __( 'Postal Code', 'storeengine' ),
1352 ],
1353 'state' => [
1354 'required' => true,
1355 'label' => __( 'Parish', 'storeengine' ),
1356 ],
1357 ],
1358 'JP' => [
1359 'last_name' => [
1360 'class' => [ 'storeengine-col-6' ],
1361 'priority' => 10,
1362 ],
1363 'first_name' => [
1364 'class' => [ 'storeengine-col-6' ],
1365 'priority' => 20,
1366 ],
1367 'postcode' => [
1368 'class' => [ 'storeengine-col-6', 'address-field' ],
1369 'priority' => 65,
1370 ],
1371 'state' => [
1372 'label' => __( 'Prefecture', 'storeengine' ),
1373 'class' => [ 'storeengine-col-6', 'address-field' ],
1374 'priority' => 66,
1375 ],
1376 'city' => [
1377 'priority' => 67,
1378 ],
1379 'address_1' => [
1380 'priority' => 68,
1381 ],
1382 'address_2' => [
1383 'priority' => 69,
1384 ],
1385 ],
1386 'KN' => [
1387 'postcode' => [
1388 'required' => false,
1389 'label' => __( 'Postal code', 'storeengine' ),
1390 ],
1391 'state' => [
1392 'required' => true,
1393 'label' => __( 'Parish', 'storeengine' ),
1394 ],
1395 ],
1396 'KR' => [
1397 'state' => [
1398 'required' => false,
1399 'hidden' => true,
1400 ],
1401 ],
1402 'KW' => [
1403 'state' => [
1404 'required' => false,
1405 'hidden' => true,
1406 ],
1407 ],
1408 'LV' => [
1409 'state' => [
1410 'label' => __( 'Municipality', 'storeengine' ),
1411 'required' => false,
1412 ],
1413 ],
1414 'LB' => [
1415 'state' => [
1416 'required' => false,
1417 'hidden' => true,
1418 ],
1419 ],
1420 'MF' => [
1421 'state' => [
1422 'required' => false,
1423 'hidden' => true,
1424 ],
1425 ],
1426 'MQ' => [
1427 'state' => [
1428 'required' => false,
1429 'hidden' => true,
1430 ],
1431 ],
1432 'MT' => [
1433 'state' => [
1434 'required' => false,
1435 'hidden' => true,
1436 ],
1437 ],
1438 'MZ' => [
1439 'postcode' => [
1440 'required' => false,
1441 'hidden' => true,
1442 ],
1443 'state' => [
1444 'label' => __( 'Province', 'storeengine' ),
1445 ],
1446 ],
1447 'NI' => [
1448 'state' => [
1449 'label' => __( 'Department', 'storeengine' ),
1450 ],
1451 ],
1452 'NL' => [
1453 'postcode' => [
1454 'priority' => 65,
1455 ],
1456 'state' => [
1457 'required' => false,
1458 'hidden' => true,
1459 ],
1460 ],
1461 'NG' => [
1462 'postcode' => [
1463 'label' => __( 'Postcode', 'storeengine' ),
1464 'required' => false,
1465 'hidden' => true,
1466 ],
1467 'state' => [
1468 'label' => __( 'State', 'storeengine' ),
1469 ],
1470 ],
1471 'NZ' => [
1472 'postcode' => [
1473 'label' => __( 'Postcode', 'storeengine' ),
1474 ],
1475 'state' => [
1476 'required' => false,
1477 'label' => __( 'Region', 'storeengine' ),
1478 ],
1479 ],
1480 'NO' => [
1481 'postcode' => [
1482 'priority' => 65,
1483 ],
1484 'state' => [
1485 'required' => false,
1486 'hidden' => true,
1487 ],
1488 ],
1489 'NP' => [
1490 'state' => [
1491 'label' => __( 'State / Zone', 'storeengine' ),
1492 ],
1493 'postcode' => [
1494 'required' => false,
1495 ],
1496 ],
1497 'PA' => [
1498 'state' => [
1499 'label' => __( 'Province', 'storeengine' ),
1500 ],
1501 ],
1502 'PL' => [
1503 'postcode' => [
1504 'priority' => 65,
1505 ],
1506 'state' => [
1507 'required' => false,
1508 'hidden' => true,
1509 ],
1510 ],
1511 'PR' => [
1512 'city' => [
1513 'label' => __( 'Municipality', 'storeengine' ),
1514 ],
1515 'state' => [
1516 'required' => false,
1517 'hidden' => true,
1518 ],
1519 ],
1520 'PT' => [
1521 'state' => [
1522 'required' => false,
1523 'hidden' => true,
1524 ],
1525 ],
1526 'PY' => [
1527 'state' => [
1528 'label' => __( 'Department', 'storeengine' ),
1529 ],
1530 ],
1531 'RE' => [
1532 'state' => [
1533 'required' => false,
1534 'hidden' => true,
1535 ],
1536 ],
1537 'RO' => [
1538 'state' => [
1539 'label' => __( 'County', 'storeengine' ),
1540 'required' => true,
1541 ],
1542 ],
1543 'RS' => [
1544 'city' => [
1545 'required' => true,
1546 ],
1547 'postcode' => [
1548 'required' => true,
1549 ],
1550 'state' => [
1551 'label' => __( 'District', 'storeengine' ),
1552 'required' => false,
1553 ],
1554 ],
1555 'RW' => [
1556 'state' => [
1557 'required' => false,
1558 'hidden' => true,
1559 ],
1560 ],
1561 'SG' => [
1562 'state' => [
1563 'required' => false,
1564 'hidden' => true,
1565 ],
1566 'city' => [
1567 'required' => false,
1568 ],
1569 ],
1570 'SK' => [
1571 'postcode' => [
1572 'priority' => 65,
1573 ],
1574 'state' => [
1575 'required' => false,
1576 'hidden' => true,
1577 ],
1578 ],
1579 'SI' => [
1580 'postcode' => [
1581 'priority' => 65,
1582 ],
1583 'state' => [
1584 'required' => false,
1585 'hidden' => true,
1586 ],
1587 ],
1588 'SR' => [
1589 'postcode' => [
1590 'required' => false,
1591 'hidden' => true,
1592 ],
1593 ],
1594 'SV' => [
1595 'state' => [
1596 'label' => __( 'Department', 'storeengine' ),
1597 ],
1598 ],
1599 'ES' => [
1600 'postcode' => [
1601 'priority' => 65,
1602 ],
1603 'state' => [
1604 'label' => __( 'Province', 'storeengine' ),
1605 ],
1606 ],
1607 'LI' => [
1608 'postcode' => [
1609 'priority' => 65,
1610 ],
1611 'state' => [
1612 'required' => false,
1613 'hidden' => true,
1614 ],
1615 ],
1616 'LK' => [
1617 'state' => [
1618 'required' => false,
1619 'hidden' => true,
1620 ],
1621 ],
1622 'LU' => [
1623 'state' => [
1624 'required' => false,
1625 'hidden' => true,
1626 ],
1627 ],
1628 'MD' => [
1629 'state' => [
1630 'label' => __( 'Municipality / District', 'storeengine' ),
1631 ],
1632 ],
1633 'SE' => [
1634 'postcode' => [
1635 'priority' => 65,
1636 ],
1637 'state' => [
1638 'required' => false,
1639 'hidden' => true,
1640 ],
1641 ],
1642 'TR' => [
1643 'postcode' => [
1644 'priority' => 65,
1645 ],
1646 'state' => [
1647 'label' => __( 'Province', 'storeengine' ),
1648 ],
1649 ],
1650 'UG' => [
1651 'postcode' => [
1652 'required' => false,
1653 'hidden' => true,
1654 ],
1655 'city' => [
1656 'label' => __( 'Town / Village', 'storeengine' ),
1657 'required' => true,
1658 ],
1659 'state' => [
1660 'label' => __( 'District', 'storeengine' ),
1661 'required' => true,
1662 ],
1663 ],
1664 'US' => [
1665 'postcode' => [
1666 'label' => __( 'ZIP Code', 'storeengine' ),
1667 ],
1668 'state' => [
1669 'label' => __( 'State', 'storeengine' ),
1670 ],
1671 ],
1672 'UY' => [
1673 'state' => [
1674 'label' => __( 'Department', 'storeengine' ),
1675 ],
1676 ],
1677 'GB' => [
1678 'postcode' => [
1679 'label' => __( 'Postcode', 'storeengine' ),
1680 ],
1681 'state' => [
1682 'label' => __( 'County', 'storeengine' ),
1683 'required' => false,
1684 ],
1685 ],
1686 'ST' => [
1687 'postcode' => [
1688 'required' => false,
1689 'hidden' => true,
1690 ],
1691 'state' => [
1692 'label' => __( 'District', 'storeengine' ),
1693 ],
1694 ],
1695 'VN' => [
1696 'state' => [
1697 'required' => false,
1698 'hidden' => true,
1699 ],
1700 'postcode' => [
1701 'priority' => 65,
1702 'required' => false,
1703 'hidden' => false,
1704 ],
1705 'address_2' => [
1706 'required' => false,
1707 'hidden' => false,
1708 ],
1709 ],
1710 'WS' => [
1711 'postcode' => [
1712 'required' => false,
1713 'hidden' => true,
1714 ],
1715 ],
1716 'YT' => [
1717 'state' => [
1718 'required' => false,
1719 'hidden' => true,
1720 ],
1721 ],
1722 'ZA' => [
1723 'state' => [
1724 'label' => __( 'Province', 'storeengine' ),
1725 ],
1726 ],
1727 'ZW' => [
1728 'postcode' => [
1729 'required' => false,
1730 'hidden' => true,
1731 ],
1732 ],
1733 ]
1734 );
1735
1736 $this->locale = array_intersect_key( $this->locale, array_merge( $this->get_allowed_countries(), $this->get_shipping_countries() ) );
1737
1738 // Default Locale Can be filtered to override fields in get_address_fields(). Countries with no specific locale will use default.
1739 $this->locale['default'] = apply_filters( 'storeengine/get_country_locale_default', $this->get_default_address_fields() );
1740
1741 // Filter default AND shop base locales to allow overrides via a single function. These will be used when changing countries on the checkout.
1742 if ( ! isset( $this->locale[ $this->get_base_country() ] ) ) {
1743 $this->locale[ $this->get_base_country() ] = $this->locale['default'];
1744 }
1745
1746 $this->locale['default'] = apply_filters( 'storeengine/get_country_locale_base', $this->locale['default'] );
1747 $this->locale[ $this->get_base_country() ] = apply_filters( 'storeengine/get_country_locale_base', $this->locale[ $this->get_base_country() ] );
1748 }
1749
1750 return $this->locale;
1751 }
1752
1753 /**
1754 * Apply locale and get address fields.
1755 *
1756 * @param string $country Country.
1757 * @param string $type Address type, defaults to 'billing_'.
1758 *
1759 * @return array
1760 */
1761 public function get_address_fields( string $country = '', string $type = 'billing_' ): array {
1762 if ( ! $country ) {
1763 $country = $this->get_base_country();
1764 }
1765
1766 $fields = $this->get_default_address_fields();
1767 $locale = $this->get_country_locale();
1768
1769 if ( isset( $locale[ $country ] ) ) {
1770 $fields = Helper::array_overlay( $fields, $locale[ $country ] );
1771 }
1772
1773 // Prepend field keys.
1774 $address_fields = [];
1775
1776 foreach ( $fields as $key => $value ) {
1777 if ( 'state' === $key ) {
1778 $value['country_field'] = $type . 'country';
1779 $value['country'] = $country;
1780 }
1781 $address_fields[ $type . $key ] = $value;
1782 }
1783
1784 // Add email and phone fields.
1785 if ( 'billing_' === $type ) {
1786 $address_fields['billing_phone']['required'] = 'required' === Helper::get_settings( 'checkout_phone_field', 'required' );
1787 $address_fields['billing_email']['required'] = true;
1788 }
1789
1790 /**
1791 * Important note on this filter: Changes to address fields can and will be overridden by
1792 * the storeengine/default_address_fields. The locales/default locales apply on top based
1793 * on country selection. If you want to change things like the required status of an
1794 * address field, filter storeengine/default_address_fields instead.
1795 */
1796 $address_fields = apply_filters( 'storeengine/' . $type . 'fields', $address_fields, $country );
1797
1798 // Sort each of the fields based on priority.
1799 uasort( $address_fields, [ Helper::class, 'checkout_fields_uasort_comparison' ] );
1800
1801 return $address_fields;
1802 }
1803 }
1804