HEX
Server: Apache
System: Linux p3plzcpnl476737.prod.phx3.secureserver.net 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: p8pyefaexf70 (9161224)
PHP: 7.4.33
Disabled: NONE
Upload Files
File: //proc/thread-self/cwd/wp-content/plugins/facebook-for-woocommerce/includes/Locale.php
<?php
/**
 * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @package MetaCommerce
 */

namespace WooCommerce\Facebook;

defined( 'ABSPATH' ) || exit;

/**
 * Helper class with utility methods for handling locales in Facebook.
 *
 * @since 2.2.0
 */
class Locale {


	/** @var string default locale */
	const DEFAULT_LOCALE = 'en_US';


	/** @var string[] an array of supported locale identifiers */
	private static $supported_locales = array(
		'af_ZA',
		'ar_AR',
		'as_IN',
		'az_AZ',
		'be_BY',
		'bg_BG',
		'bn_IN',
		'br_FR',
		'bs_BA',
		'ca_ES',
		'cb_IQ',
		'co_FR',
		'cs_CZ',
		'cx_PH',
		'cy_GB',
		'da_DK',
		'de_DE',
		'el_GR',
		'en_GB',
		'en_US',
		'es_ES',
		'es_LA',
		'et_EE',
		'eu_ES',
		'fa_IR',
		'ff_NG',
		'fi_FI',
		'fo_FO',
		'fr_CA',
		'fr_FR',
		'fy_NL',
		'ga_IE',
		'gl_ES',
		'gn_PY',
		'gu_IN',
		'ha_NG',
		'he_IL',
		'hi_IN',
		'hr_HR',
		'hu_HU',
		'hy_AM',
		'id_ID',
		'is_IS',
		'it_IT',
		'ja_JP',
		'ja_KS',
		'jv_ID',
		'ka_GE',
		'kk_KZ',
		'km_KH',
		'kn_IN',
		'ko_KR',
		'ku_TR',
		'lt_LT',
		'lv_LV',
		'mg_MG',
		'mk_MK',
		'ml_IN',
		'mn_MN',
		'mr_IN',
		'ms_MY',
		'mt_MT',
		'my_MM',
		'nb_NO',
		'ne_NP',
		'nl_BE',
		'nl_NL',
		'nn_NO',
		'or_IN',
		'pa_IN',
		'pl_PL',
		'ps_AF',
		'pt_BR',
		'pt_PT',
		'qz_MM',
		'ro_RO',
		'ru_RU',
		'rw_RW',
		'sc_IT',
		'si_LK',
		'sk_SK',
		'sl_SI',
		'so_SO',
		'sq_AL',
		'sr_RS',
		'sv_SE',
		'sw_KE',
		'sz_PL',
		'ta_IN',
		'te_IN',
		'tg_TJ',
		'th_TH',
		'tl_PH',
		'tr_TR',
		'tz_MA',
		'uk_UA',
		'ur_PK',
		'uz_UZ',
		'vi_VN',
		'zh_CN',
		'zh_HK',
		'zh_TW',
	);


	/**
	 * Gets a list of locales supported by Facebook.
	 *
	 * @link https://developers.facebook.com/docs/messenger-platform/messenger-profile/supported-locales/
	 * If the Locale extension is not available, will attempt to match locales to WordPress available language names.
	 *
	 * @since 2.2.0
	 *
	 * @return array associative array of locale identifiers and language labels
	 */
	public static function get_supported_locales() {

		$locales = array();

		if ( class_exists( 'Locale' ) ) {

			foreach ( self::$supported_locales as $locale ) {

				$name = \Locale::getDisplayName( $locale, substr( $locale, 0, 2 ) );
				if ( $name ) {

					$locales[ $locale ] = ucfirst( $name );
				}
			}
		} else {

			include_once ABSPATH . '/wp-admin/includes/translation-install.php';

			$translations = wp_get_available_translations();

			foreach ( self::$supported_locales as $locale ) {

				if ( isset( $translations[ $locale ]['native_name'] ) ) {

					$locales[ $locale ] = $translations[ $locale ]['native_name'];

				} else { // generic match e.g. <it>_IT, <it>_CH (any language in the the <it> group )

					$matched_locale = substr( $locale, 0, 2 );

					if ( isset( $translations[ $matched_locale ]['native_name'] ) ) {
						$locales[ $locale ] = $translations[ $matched_locale ]['native_name'];
					}
				}
			}

			// always include US English
			$locales['en_US'] = _x( 'English (United States)', 'language', 'facebook-for-woocommerce' );
		}

		/**
		 * Filters the locales supported by Facebook Messenger.
		 *
		 * @since 1.10.0
		 *
		 * @param array $locales locales supported by Facebook, in $locale => $name format
		 */
		$locales = (array) apply_filters( 'wc_facebook_messenger_supported_locales', array_unique( $locales ) );

		natcasesort( $locales );

		return $locales;
	}


	/**
	 * Determines if a locale is supported by Facebook.
	 *
	 * @since 2.2.0
	 *
	 * @param string $locale a locale identifier
	 * @return bool
	 */
	public static function is_supported_locale( $locale ) {

		return array_key_exists( $locale, self::get_supported_locales() );
	}

	/**
	 * Languages that Facebook supports using the generic _XX format for language override feeds.
	 * Any regional variant of these languages will be converted to {language}_XX
	 *
	 * @since 3.6.0
	 * @var array
	 */
	private static $facebook_xx_languages = [
		'en', // English (en_US, en_GB, en_CA, etc. → en_XX)
		'es', // Spanish (es_ES, es_MX, es_AR, etc. → es_XX)
		'fr', // French (fr_FR, fr_CA, fr_BE, etc. → fr_XX)
		'nl', // Dutch (nl_NL, nl_BE, etc. → nl_XX)
		'pt', // Portuguese (pt_BR, pt_PT, etc. → pt_XX)
		'no', // Norwegian (no_NO, nb_NO, nn_NO, etc. → no_XX)
		'ja', // Japanese (ja_JP, etc. → ja_XX)
		'tl', // Tagalog (tl_PH, etc. → tl_XX)
	];

	/**
	 * Facebook's valid override values mapping for language override feeds.
	 * Complete mapping of language codes to Facebook's accepted override values.
	 *
	 * @since 3.6.0
	 * @var array
	 */
	private static $facebook_override_values = [
		'af' => 'af_ZA', // Afrikaans
		'ak' => 'ak_GH', // Akan
		'am' => 'am_ET', // Amharic
		'ar' => 'ar_AR', // Arabic
		'as' => 'as_IN', // Assamese
		'ay' => 'ay_BO', // Aymara
		'az' => 'az_AZ', // Azerbaijani
		'be' => 'be_BY', // Belarusian
		'bg' => 'bg_BG', // Bulgarian
		'bm' => 'bm_ML', // Bambara
		'bn' => 'bn_IN', // Bengali
		'bo' => 'bo_CN', // Tibetan
		'br' => 'br_FR', // Breton
		'bs' => 'bs_BA', // Bosnian
		'ca' => 'ca_ES', // Catalan
		'cb' => 'cb_IQ', // Kurdish
		'ci' => 'ci_IT', // Sicilian
		'ck' => 'ck_US', // Cherokee
		'cs' => 'cs_CZ', // Czech
		'cx' => 'cx_PH', // Cebuano
		'cy' => 'cy_GB', // Welsh
		'da' => 'da_DK', // Danish
		'de' => 'de_DE', // German
		'dv' => 'dv_MV', // Dhivehi
		'el' => 'el_GR', // Greek
		'en' => 'en_XX', // English
		'eo' => 'eo_EO', // Esperanto
		'es' => 'es_XX', // Spanish
		'et' => 'et_EE', // Estonian
		'eu' => 'eu_ES', // Basque
		'fa' => 'fa_IR', // Persian
		'ff' => 'ff_NG', // Fulah
		'fi' => 'fi_FI', // Finnish
		'fo' => 'fo_FO', // Faroese
		'fr' => 'fr_XX', // French
		'fy' => 'fy_NL', // Frisian
		'ga' => 'ga_IE', // Irish
		'gd' => 'gd_GB', // Scottish Gaelic
		'gl' => 'gl_ES', // Galician
		'gn' => 'gn_PY', // Guaraní
		'gu' => 'gu_IN', // Gujarati
		'ha' => 'ha_NG', // Hausa
		'he' => 'he_IL', // Hebrew
		'hi' => 'hi_IN', // Hindi
		'hr' => 'hr_HR', // Croatian
		'ht' => 'ht_HT', // Haitian
		'hu' => 'hu_HU', // Hungarian
		'hy' => 'hy_AM', // Armenian
		'id' => 'id_ID', // Indonesian
		'ig' => 'ig_NG', // Igbo
		'is' => 'is_IS', // Icelandic
		'it' => 'it_IT', // Italian
		'iu' => 'iu_CA', // Inuktitut
		'ja' => 'ja_XX', // Japanese
		'jv' => 'jv_ID', // Javanese
		'ka' => 'ka_GE', // Georgian
		'kg' => 'kg_AO', // Kongo
		'kk' => 'kk_KZ', // Kazakh
		'km' => 'km_KH', // Khmer
		'kn' => 'kn_IN', // Kannada
		'ko' => 'ko_KR', // Korean
		'ku' => 'ku_TR', // Kurdish
		'ky' => 'ky_KG', // Kirghiz
		'la' => 'la_VA', // Latin
		'lg' => 'lg_UG', // Ganda
		'li' => 'li_NL', // Limburgish
		'ln' => 'ln_CD', // Lingala
		'lo' => 'lo_LA', // Lao
		'lt' => 'lt_LT', // Lithuanian
		'lv' => 'lv_LV', // Latvian
		'mg' => 'mg_MG', // Malagasy
		'mi' => 'mi_NZ', // Maori
		'mk' => 'mk_MK', // Macedonian
		'ml' => 'ml_IN', // Malayalam
		'mn' => 'mn_MN', // Mongolian
		'mr' => 'mr_IN', // Marathi
		'ms' => 'ms_MY', // Malay
		'mt' => 'mt_MT', // Maltese
		'my' => 'my_MM', // Burmese
		'ne' => 'ne_NP', // Nepali
		'nl' => 'nl_XX', // Dutch
		'no' => 'no_XX', // Norwegian
		'ns' => 'ns_ZA', // Northern Sotho
		'ny' => 'ny_MW', // Nyanja
		'om' => 'om_KE', // Oromo
		'or' => 'or_IN', // Oriya
		'pa' => 'pa_IN', // Punjabi
		'pl' => 'pl_PL', // Polish
		'ps' => 'ps_AF', // Pashto
		'pt' => 'pt_XX', // Portuguese
		'qa' => 'qa_MM', // Shan
		'qd' => 'qd_MM', // Kachin
		'qf' => 'qf_CM', // Ewondo
		'qh' => 'qh_PH', // Iloko
		'qj' => 'qj_ML', // Koyra Chiini Songhay
		'qm' => 'qm_AO', // Umbundu
		'qn' => 'qn_AO', // Kimbundu
		'qp' => 'qp_AO', // Chokwe
		'qq' => 'qq_KE', // EkeGusii
		'qw' => 'qw_KE', // Kalenjin
		'qy' => 'qy_KE', // Dholuo
		'qx' => 'qx_KE', // Kikamba
		'q2' => 'q2_KH', // Western Cham
		'q3' => 'q3_CV', // Kabuverdianui
		'qu' => 'qu_PE', // Quechua
		'rm' => 'rm_CH', // Romansh
		'ro' => 'ro_RO', // Romanian
		'ru' => 'ru_RU', // Russian
		'rw' => 'rw_RW', // Kinyarwanda
		'sa' => 'sa_IN', // Sanskrit
		'sc' => 'sc_IT', // Sardinian
		'sd' => 'sd_PK', // Sindhi
		'se' => 'se_NO', // Northern Sami
		'si' => 'si_LK', // Sinhala
		'sk' => 'sk_SK', // Slovak
		'sl' => 'sl_SI', // Slovenian
		'sn' => 'sn_ZW', // Shona
		'so' => 'so_SO', // Somali
		'sq' => 'sq_AL', // Albanian
		'sr' => 'sr_RS', // Serbian
		'ss' => 'ss_SZ', // Swati
		'st' => 'st_ZA', // Southern Sotho
		'su' => 'su_ID', // Sundanese
		'sv' => 'sv_SE', // Swedish
		'sw' => 'sw_KE', // Swahili
		'sy' => 'sy_SY', // Syriac
		'sz' => 'sz_PL', // Silesian
		'ta' => 'ta_IN', // Tamil
		'te' => 'te_IN', // Telugu
		'tg' => 'tg_TJ', // Tajik
		'th' => 'th_TH', // Thai
		'ti' => 'ti_ET', // Tigrinya
		'tl' => 'tl_XX', // Tagalog
		'tn' => 'tn_BW', // Tswana
		'tr' => 'tr_TR', // Turkish
		'ts' => 'ts_ZA', // Tsonga
		'tt' => 'tt_RU', // Tatar
		'tz' => 'tz_MA', // Tamazight
		'ug' => 'ug_CN', // Uighur
		'uk' => 'uk_UA', // Ukrainian
		'ur' => 'ur_PK', // Urdu
		'uz' => 'uz_UZ', // Uzbek
		've' => 've_ZA', // Venda
		'vi' => 'vi_VN', // Vietnamese
		'wy' => 'wy_PH', // Winaray
		'wo' => 'wo_SN', // Wolof
		'xh' => 'xh_ZA', // Xhosa
		'yi' => 'yi_DE', // Yiddish
		'yo' => 'yo_NG', // Yoruba
		'zh' => 'zh_CN', // Chinese (China) - default to simplified
		'zu' => 'zu_ZA', // Zulu
		'zz' => 'zz_TR', // Zazaki
	];

	/**
	 * Convert locale code to Facebook's supported language override value for language override feeds.
	 *
	 * @since 3.6.0
	 * @param string $locale_code Locale code from localization plugin (e.g., 'es_ES', 'fr_FR')
	 * @return string Facebook-supported language override value (e.g., 'es_XX', 'fr_XX')
	 */
	public static function convert_to_facebook_language_code( string $locale_code ): string {
		// Extract the language part (before the underscore)
		$language_parts = explode( '_', $locale_code );
		$language = strtolower( $language_parts[0] );

		// Special cases where WordPress/Polylang language codes don't match Facebook's expected codes
		// These must be handled BEFORE other mappings
		$special_mappings = [
			'nb' => 'no_XX',  // Norwegian Bokmål (nb_NO → no_XX)
			'nn' => 'no_XX',  // Norwegian Nynorsk (nn_NO → no_XX)
			'ck' => 'cb_IQ',  // Central Kurdish (ckb → cb_IQ)
			'ce' => 'cx_PH',  // Cebuano (ceb → cx_PH)
		];

		if ( isset( $special_mappings[ $language ] ) ) {
			return $special_mappings[ $language ];
		}

		// Handle special cases for Chinese FIRST (before generic mappings)
		// This is critical because we need to distinguish zh_CN from zh_TW
		if ( 'zh' === $language && isset( $language_parts[1] ) ) {
			$region = strtoupper( $language_parts[1] );
			if ( in_array( $region, [ 'TW', 'HK', 'MO' ] ) ) {
				return 'zh_TW'; // Traditional Chinese
			}
			return 'zh_CN'; // Simplified Chinese (default)
		}

		// Check if this language uses the _XX format
		if ( in_array( $language, self::$facebook_xx_languages, true ) ) {
			return $language . '_XX';
		}

		// Check if we have a specific Facebook override value for this language
		if ( isset( self::$facebook_override_values[ $language ] ) ) {
			return self::$facebook_override_values[ $language ];
		}

		// Fallback: return the original code if no mapping found
		return $locale_code;
	}

	/**
	 * Convert language code to Facebook's accepted override value format for language override feeds.
	 * This method throws an exception for unsupported languages (stricter validation).
	 *
	 * @since 3.6.0
	 * @param string $language_code Language code (e.g., 'es_ES', 'fr_FR')
	 * @return string Facebook override value (e.g., 'es_XX', 'fr_XX')
	 * @throws \WooCommerce\Facebook\Framework\Plugin\Exception If the language is not supported by Facebook.
	 */
	public static function convert_to_facebook_override_value( string $language_code ): string {
		// Extract the language part (before the underscore)
		$language_parts = explode( '_', $language_code );
		$language = strtolower( $language_parts[0] );

		// Handle special cases for Chinese FIRST (before generic mappings)
		// This is critical because we need to distinguish zh_CN from zh_TW
		if ( 'zh' === $language && isset( $language_parts[1] ) ) {
			$region = strtoupper( $language_parts[1] );
			if ( in_array( $region, [ 'TW', 'HK', 'MO' ] ) ) {
				return 'zh_TW'; // Traditional Chinese
			}
			return 'zh_CN'; // Simplified Chinese (default)
		}

		// Check if we have a specific Facebook override value for this language
		if ( isset( self::$facebook_override_values[ $language ] ) ) {
			return self::$facebook_override_values[ $language ];
		}

		// If no mapping found, throw an exception
		throw new \WooCommerce\Facebook\Framework\Plugin\Exception(
			sprintf(
				/* translators: %s: Language code */
				__( 'Language Feed not supported for override value: %s', 'facebook-for-woocommerce' ),
				$language_code
			),
			400
		);
	}

	/**
	 * Check if a language code is supported by Facebook for language override feeds.
	 *
	 * @since 3.6.0
	 * @param string $language_code Language code to check
	 * @return bool True if supported, false otherwise
	 */
	public static function is_language_override_supported( string $language_code ): bool {
		try {
			self::convert_to_facebook_override_value( $language_code );
			return true;
		} catch ( \WooCommerce\Facebook\Framework\Plugin\Exception $e ) {
			return false;
		}
	}

	/**
	 * Get all supported Facebook language override codes.
	 *
	 * @since 3.6.0
	 * @return array Array of supported language override codes
	 */
	public static function get_supported_language_override_codes(): array {
		$supported = [];

		// Add _XX languages
		foreach ( self::$facebook_xx_languages as $lang ) {
			$supported[] = $lang . '_XX';
		}

		// Add specific override values
		$supported = array_merge( $supported, array_values( self::$facebook_override_values ) );

		return array_unique( $supported );
	}

	/**
	 * Get the mapping of language codes to Facebook override values.
	 *
	 * @since 3.6.0
	 * @return array Complete mapping array
	 */
	public static function get_language_override_mapping(): array {
		return self::$facebook_override_values;
	}
}