11 * Retrieves the current locale.
13 * If the locale is set, then it will filter the locale in the {@see 'locale'}
14 * filter hook and return the value.
16 * If the locale is not set already, then the WPLANG constant is used if it is
17 * defined. Then it is filtered through the {@see 'locale'} filter hook and
18 * the value for the locale global set and the locale is returned.
20 * The process to get the locale should only be done once, but the locale will
21 * always be filtered using the {@see 'locale'} hook.
25 * @global string $locale
26 * @global string $wp_local_package
28 * @return string The locale of the blog or from the {@see 'locale'} hook.
30 function get_locale() {
31 global $locale, $wp_local_package;
33 if ( isset( $locale ) ) {
35 * Filter WordPress install's locale ID.
39 * @param string $locale The locale ID.
41 return apply_filters( 'locale', $locale );
44 if ( isset( $wp_local_package ) ) {
45 $locale = $wp_local_package;
48 // WPLANG was defined in wp-config.
49 if ( defined( 'WPLANG' ) ) {
53 // If multisite, check options.
54 if ( is_multisite() ) {
55 // Don't check blog option when installing.
56 if ( wp_installing() || ( false === $ms_locale = get_option( 'WPLANG' ) ) ) {
57 $ms_locale = get_site_option( 'WPLANG' );
60 if ( $ms_locale !== false ) {
64 $db_locale = get_option( 'WPLANG' );
65 if ( $db_locale !== false ) {
70 if ( empty( $locale ) ) {
74 /** This filter is documented in wp-includes/l10n.php */
75 return apply_filters( 'locale', $locale );
79 * Retrieve the translation of $text.
81 * If there is no translation, or the text domain isn't loaded, the original text is returned.
83 * *Note:* Don't use translate() directly, use __() or related functions.
87 * @param string $text Text to translate.
88 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
90 * @return string Translated text
92 function translate( $text, $domain = 'default' ) {
93 $translations = get_translations_for_domain( $domain );
94 $translations = $translations->translate( $text );
97 * Filter text with its translation.
101 * @param string $translations Translated text.
102 * @param string $text Text to translate.
103 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
105 return apply_filters( 'gettext', $translations, $text, $domain );
109 * Remove last item on a pipe-delimited string.
111 * Meant for removing the last item in a string, such as 'Role name|User role'. The original
112 * string will be returned if no pipe '|' characters are found in the string.
116 * @param string $string A pipe-delimited string.
117 * @return string Either $string or everything before the last pipe.
119 function before_last_bar( $string ) {
120 $last_bar = strrpos( $string, '|' );
121 if ( false === $last_bar ) {
124 return substr( $string, 0, $last_bar );
129 * Retrieve the translation of $text in the context defined in $context.
131 * If there is no translation, or the text domain isn't loaded the original
134 * *Note:* Don't use translate_with_gettext_context() directly, use _x() or related functions.
138 * @param string $text Text to translate.
139 * @param string $context Context information for the translators.
140 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
142 * @return string Translated text on success, original text on failure.
144 function translate_with_gettext_context( $text, $context, $domain = 'default' ) {
145 $translations = get_translations_for_domain( $domain );
146 $translations = $translations->translate( $text, $context );
148 * Filter text with its translation based on context information.
152 * @param string $translations Translated text.
153 * @param string $text Text to translate.
154 * @param string $context Context information for the translators.
155 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
157 return apply_filters( 'gettext_with_context', $translations, $text, $context, $domain );
161 * Retrieve the translation of $text.
163 * If there is no translation, or the text domain isn't loaded, the original text is returned.
167 * @param string $text Text to translate.
168 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
170 * @return string Translated text.
172 function __( $text, $domain = 'default' ) {
173 return translate( $text, $domain );
177 * Retrieve the translation of $text and escapes it for safe use in an attribute.
179 * If there is no translation, or the text domain isn't loaded, the original text is returned.
183 * @param string $text Text to translate.
184 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
186 * @return string Translated text on success, original text on failure.
188 function esc_attr__( $text, $domain = 'default' ) {
189 return esc_attr( translate( $text, $domain ) );
193 * Retrieve the translation of $text and escapes it for safe use in HTML output.
195 * If there is no translation, or the text domain isn't loaded, the original text is returned.
199 * @param string $text Text to translate.
200 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
202 * @return string Translated text
204 function esc_html__( $text, $domain = 'default' ) {
205 return esc_html( translate( $text, $domain ) );
209 * Display translated text.
213 * @param string $text Text to translate.
214 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
217 function _e( $text, $domain = 'default' ) {
218 echo translate( $text, $domain );
222 * Display translated text that has been escaped for safe use in an attribute.
226 * @param string $text Text to translate.
227 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
230 function esc_attr_e( $text, $domain = 'default' ) {
231 echo esc_attr( translate( $text, $domain ) );
235 * Display translated text that has been escaped for safe use in HTML output.
239 * @param string $text Text to translate.
240 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
243 function esc_html_e( $text, $domain = 'default' ) {
244 echo esc_html( translate( $text, $domain ) );
248 * Retrieve translated string with gettext context.
250 * Quite a few times, there will be collisions with similar translatable text
251 * found in more than two places, but with different translated context.
253 * By including the context in the pot file, translators can translate the two
254 * strings differently.
258 * @param string $text Text to translate.
259 * @param string $context Context information for the translators.
260 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
262 * @return string Translated context string without pipe.
264 function _x( $text, $context, $domain = 'default' ) {
265 return translate_with_gettext_context( $text, $context, $domain );
269 * Display translated string with gettext context.
273 * @param string $text Text to translate.
274 * @param string $context Context information for the translators.
275 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
277 * @return string Translated context string without pipe.
279 function _ex( $text, $context, $domain = 'default' ) {
280 echo _x( $text, $context, $domain );
284 * Translate string with gettext context, and escapes it for safe use in an attribute.
288 * @param string $text Text to translate.
289 * @param string $context Context information for the translators.
290 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
292 * @return string Translated text
294 function esc_attr_x( $text, $context, $domain = 'default' ) {
295 return esc_attr( translate_with_gettext_context( $text, $context, $domain ) );
299 * Translate string with gettext context, and escapes it for safe use in HTML output.
303 * @param string $text Text to translate.
304 * @param string $context Context information for the translators.
305 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
307 * @return string Translated text.
309 function esc_html_x( $text, $context, $domain = 'default' ) {
310 return esc_html( translate_with_gettext_context( $text, $context, $domain ) );
314 * Translates and retrieves the singular or plural form based on the supplied number.
316 * Used when you want to use the appropriate form of a string based on whether a
317 * number is singular or plural.
321 * $people = sprintf( _n( '%s person', '%s people', $count, 'text-domain' ), number_format_i18n( $count ) );
325 * @param string $single The text to be used if the number is singular.
326 * @param string $plural The text to be used if the number is plural.
327 * @param int $number The number to compare against to use either the singular or plural form.
328 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
330 * @return string The translated singular or plural form.
332 function _n( $single, $plural, $number, $domain = 'default' ) {
333 $translations = get_translations_for_domain( $domain );
334 $translation = $translations->translate_plural( $single, $plural, $number );
337 * Filter the singular or plural form of a string.
341 * @param string $translation Translated text.
342 * @param string $single The text to be used if the number is singular.
343 * @param string $plural The text to be used if the number is plural.
344 * @param string $number The number to compare against to use either the singular or plural form.
345 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
347 return apply_filters( 'ngettext', $translation, $single, $plural, $number, $domain );
351 * Translates and retrieves the singular or plural form based on the supplied number, with gettext context.
353 * This is a hybrid of _n() and _x(). It supports context and plurals.
355 * Used when you want to use the appropriate form of a string with context based on whether a
356 * number is singular or plural.
360 * $people = sprintf( _n( '%s person', '%s people', $count, 'context', 'text-domain' ), number_format_i18n( $count ) );
364 * @param string $single The text to be used if the number is singular.
365 * @param string $plural The text to be used if the number is plural.
366 * @param int $number The number to compare against to use either the singular or plural form.
367 * @param string $context Context information for the translators.
368 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
370 * @return string The translated singular or plural form.
372 function _nx($single, $plural, $number, $context, $domain = 'default') {
373 $translations = get_translations_for_domain( $domain );
374 $translation = $translations->translate_plural( $single, $plural, $number, $context );
377 * Filter the singular or plural form of a string with gettext context.
381 * @param string $translation Translated text.
382 * @param string $single The text to be used if the number is singular.
383 * @param string $plural The text to be used if the number is plural.
384 * @param string $number The number to compare against to use either the singular or plural form.
385 * @param string $context Context information for the translators.
386 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
388 return apply_filters( 'ngettext_with_context', $translation, $single, $plural, $number, $context, $domain );
392 * Registers plural strings in POT file, but does not translate them.
394 * Used when you want to keep structures with translatable plural
395 * strings and use them later when the number is known.
400 * 'post' => _n_noop( '%s post', '%s posts', 'text-domain' ),
401 * 'page' => _n_noop( '%s pages', '%s pages', 'text-domain' ),
404 * $message = $messages[ $type ];
405 * $usable_text = sprintf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) );
409 * @param string $singular Singular form to be localized.
410 * @param string $plural Plural form to be localized.
411 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
414 * Array of translation information for the strings.
416 * @type string $0 Singular form to be localized. No longer used.
417 * @type string $1 Plural form to be localized. No longer used.
418 * @type string $singular Singular form to be localized.
419 * @type string $plural Plural form to be localized.
420 * @type null $context Context information for the translators.
421 * @type string $domain Text domain.
424 function _n_noop( $singular, $plural, $domain = null ) {
425 return array( 0 => $singular, 1 => $plural, 'singular' => $singular, 'plural' => $plural, 'context' => null, 'domain' => $domain );
429 * Registers plural strings with gettext context in POT file, but does not translate them.
431 * Used when you want to keep structures with translatable plural
432 * strings and use them later when the number is known.
437 * 'post' => _n_noop( '%s post', '%s posts', 'context', 'text-domain' ),
438 * 'page' => _n_noop( '%s pages', '%s pages', 'context', 'text-domain' ),
441 * $message = $messages[ $type ];
442 * $usable_text = sprintf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) );
446 * @param string $singular Singular form to be localized.
447 * @param string $plural Plural form to be localized.
448 * @param string $context Context information for the translators.
449 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
452 * Array of translation information for the strings.
454 * @type string $0 Singular form to be localized. No longer used.
455 * @type string $1 Plural form to be localized. No longer used.
456 * @type string $2 Context information for the translators. No longer used.
457 * @type string $singular Singular form to be localized.
458 * @type string $plural Plural form to be localized.
459 * @type string $context Context information for the translators.
460 * @type string $domain Text domain.
463 function _nx_noop( $singular, $plural, $context, $domain = null ) {
464 return array( 0 => $singular, 1 => $plural, 2 => $context, 'singular' => $singular, 'plural' => $plural, 'context' => $context, 'domain' => $domain );
468 * Translates and retrieves the singular or plural form of a string that's been registered
469 * with _n_noop() or _nx_noop().
471 * Used when you want to use a translatable plural string once the number is known.
476 * 'post' => _n_noop( '%s post', '%s posts', 'text-domain' ),
477 * 'page' => _n_noop( '%s pages', '%s pages', 'text-domain' ),
480 * $message = $messages[ $type ];
481 * $usable_text = sprintf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) );
485 * @param array $nooped_plural Array with singular, plural, and context keys, usually the result of _n_noop() or _nx_noop().
486 * @param int $count Number of objects.
487 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. If $nooped_plural contains
488 * a text domain passed to _n_noop() or _nx_noop(), it will override this value. Default 'default'.
489 * @return string Either $single or $plural translated text.
491 function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) {
492 if ( $nooped_plural['domain'] )
493 $domain = $nooped_plural['domain'];
495 if ( $nooped_plural['context'] )
496 return _nx( $nooped_plural['singular'], $nooped_plural['plural'], $count, $nooped_plural['context'], $domain );
498 return _n( $nooped_plural['singular'], $nooped_plural['plural'], $count, $domain );
502 * Load a .mo file into the text domain $domain.
504 * If the text domain already exists, the translations will be merged. If both
505 * sets have the same string, the translation from the original value will be taken.
507 * On success, the .mo file will be placed in the $l10n global by $domain
508 * and will be a MO object.
512 * @global array $l10n
514 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
515 * @param string $mofile Path to the .mo file.
516 * @return bool True on success, false on failure.
518 function load_textdomain( $domain, $mofile ) {
522 * Filter text domain and/or MO file path for loading translations.
526 * @param bool $override Whether to override the text domain. Default false.
527 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
528 * @param string $mofile Path to the MO file.
530 $plugin_override = apply_filters( 'override_load_textdomain', false, $domain, $mofile );
532 if ( true == $plugin_override ) {
537 * Fires before the MO translation file is loaded.
541 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
542 * @param string $mofile Path to the .mo file.
544 do_action( 'load_textdomain', $domain, $mofile );
547 * Filter MO file path for loading translations for a specific text domain.
551 * @param string $mofile Path to the MO file.
552 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
554 $mofile = apply_filters( 'load_textdomain_mofile', $mofile, $domain );
556 if ( !is_readable( $mofile ) ) return false;
559 if ( !$mo->import_from_file( $mofile ) ) return false;
561 if ( isset( $l10n[$domain] ) )
562 $mo->merge_with( $l10n[$domain] );
564 $l10n[$domain] = &$mo;
570 * Unload translations for a text domain.
574 * @global array $l10n
576 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
577 * @return bool Whether textdomain was unloaded.
579 function unload_textdomain( $domain ) {
583 * Filter the text domain for loading translation.
587 * @param bool $override Whether to override unloading the text domain. Default false.
588 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
590 $plugin_override = apply_filters( 'override_unload_textdomain', false, $domain );
592 if ( $plugin_override )
596 * Fires before the text domain is unloaded.
600 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
602 do_action( 'unload_textdomain', $domain );
604 if ( isset( $l10n[$domain] ) ) {
605 unset( $l10n[$domain] );
613 * Load default translated strings based on locale.
615 * Loads the .mo file in WP_LANG_DIR constant path from WordPress root.
616 * The translated (.mo) file is named based on the locale.
618 * @see load_textdomain()
622 * @param string $locale Optional. Locale to load. Default is the value of {@see get_locale()}.
623 * @return bool Whether the textdomain was loaded.
625 function load_default_textdomain( $locale = null ) {
626 if ( null === $locale ) {
627 $locale = get_locale();
630 // Unload previously loaded strings so we can switch translations.
631 unload_textdomain( 'default' );
633 $return = load_textdomain( 'default', WP_LANG_DIR . "/$locale.mo" );
635 if ( ( is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) ) && ! file_exists( WP_LANG_DIR . "/admin-$locale.mo" ) ) {
636 load_textdomain( 'default', WP_LANG_DIR . "/ms-$locale.mo" );
640 if ( is_admin() || wp_installing() || ( defined( 'WP_REPAIRING' ) && WP_REPAIRING ) ) {
641 load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo" );
644 if ( is_network_admin() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) )
645 load_textdomain( 'default', WP_LANG_DIR . "/admin-network-$locale.mo" );
651 * Load a plugin's translated strings.
653 * If the path is not given then it will be the root of the plugin directory.
655 * The .mo file should be named based on the text domain with a dash, and then the locale exactly.
659 * @param string $domain Unique identifier for retrieving translated strings
660 * @param string $deprecated Use the $plugin_rel_path parameter instead.
661 * @param string $plugin_rel_path Optional. Relative path to WP_PLUGIN_DIR where the .mo file resides.
663 * @return bool True when textdomain is successfully loaded, false otherwise.
665 function load_plugin_textdomain( $domain, $deprecated = false, $plugin_rel_path = false ) {
666 $locale = get_locale();
668 * Filter a plugin's locale.
672 * @param string $locale The plugin's current locale.
673 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
675 $locale = apply_filters( 'plugin_locale', $locale, $domain );
677 if ( false !== $plugin_rel_path ) {
678 $path = WP_PLUGIN_DIR . '/' . trim( $plugin_rel_path, '/' );
679 } elseif ( false !== $deprecated ) {
680 _deprecated_argument( __FUNCTION__, '2.7' );
681 $path = ABSPATH . trim( $deprecated, '/' );
683 $path = WP_PLUGIN_DIR;
686 // Load the textdomain according to the plugin first
687 $mofile = $domain . '-' . $locale . '.mo';
688 if ( $loaded = load_textdomain( $domain, $path . '/'. $mofile ) )
691 // Otherwise, load from the languages directory
692 $mofile = WP_LANG_DIR . '/plugins/' . $mofile;
693 return load_textdomain( $domain, $mofile );
697 * Load the translated strings for a plugin residing in the mu-plugins directory.
701 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
702 * @param string $mu_plugin_rel_path Relative to WPMU_PLUGIN_DIR directory in which the .mo file resides.
703 * Default empty string.
704 * @return bool True when textdomain is successfully loaded, false otherwise.
706 function load_muplugin_textdomain( $domain, $mu_plugin_rel_path = '' ) {
707 /** This filter is documented in wp-includes/l10n.php */
708 $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
709 $path = trailingslashit( WPMU_PLUGIN_DIR . '/' . ltrim( $mu_plugin_rel_path, '/' ) );
711 // Load the textdomain according to the plugin first
712 $mofile = $domain . '-' . $locale . '.mo';
713 if ( $loaded = load_textdomain( $domain, $path . $mofile ) )
716 // Otherwise, load from the languages directory
717 $mofile = WP_LANG_DIR . '/plugins/' . $mofile;
718 return load_textdomain( $domain, $mofile );
722 * Load the theme's translated strings.
724 * If the current locale exists as a .mo file in the theme's root directory, it
725 * will be included in the translated strings by the $domain.
727 * The .mo files must be named based on the locale exactly.
731 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
732 * @param string $path Optional. Path to the directory containing the .mo file.
734 * @return bool True when textdomain is successfully loaded, false otherwise.
736 function load_theme_textdomain( $domain, $path = false ) {
737 $locale = get_locale();
739 * Filter a theme's locale.
743 * @param string $locale The theme's current locale.
744 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
746 $locale = apply_filters( 'theme_locale', $locale, $domain );
749 $path = get_template_directory();
751 // Load the textdomain according to the theme
752 $mofile = untrailingslashit( $path ) . "/{$locale}.mo";
753 if ( $loaded = load_textdomain( $domain, $mofile ) )
756 // Otherwise, load from the languages directory
757 $mofile = WP_LANG_DIR . "/themes/{$domain}-{$locale}.mo";
758 return load_textdomain( $domain, $mofile );
762 * Load the child themes translated strings.
764 * If the current locale exists as a .mo file in the child themes
765 * root directory, it will be included in the translated strings by the $domain.
767 * The .mo files must be named based on the locale exactly.
771 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
772 * @param string $path Optional. Path to the directory containing the .mo file.
774 * @return bool True when the theme textdomain is successfully loaded, false otherwise.
776 function load_child_theme_textdomain( $domain, $path = false ) {
778 $path = get_stylesheet_directory();
779 return load_theme_textdomain( $domain, $path );
783 * Return the Translations instance for a text domain.
785 * If there isn't one, returns empty Translations instance.
789 * @global array $l10n
791 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
792 * @return NOOP_Translations A Translations instance.
794 function get_translations_for_domain( $domain ) {
796 if ( isset( $l10n[ $domain ] ) ) {
797 return $l10n[ $domain ];
800 static $noop_translations = null;
801 if ( null === $noop_translations ) {
802 $noop_translations = new NOOP_Translations;
805 return $noop_translations;
809 * Whether there are translations for the text domain.
813 * @global array $l10n
815 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
816 * @return bool Whether there are translations.
818 function is_textdomain_loaded( $domain ) {
820 return isset( $l10n[ $domain ] );
824 * Translates role name.
826 * Since the role names are in the database and not in the source there
827 * are dummy gettext calls to get them into the POT file and this function
828 * properly translates them back.
830 * The before_last_bar() call is needed, because older installs keep the roles
831 * using the old context format: 'Role name|User role' and just skipping the
832 * content after the last bar is easier than fixing them in the DB. New installs
833 * won't suffer from that problem.
837 * @param string $name The role name.
838 * @return string Translated role name on success, original name on failure.
840 function translate_user_role( $name ) {
841 return translate_with_gettext_context( before_last_bar($name), 'User role' );
845 * Get all available languages based on the presence of *.mo files in a given directory.
847 * The default directory is WP_LANG_DIR.
851 * @param string $dir A directory to search for language files.
852 * Default WP_LANG_DIR.
853 * @return array An array of language codes or an empty array if no languages are present. Language codes are formed by stripping the .mo extension from the language file names.
855 function get_available_languages( $dir = null ) {
856 $languages = array();
858 $lang_files = glob( ( is_null( $dir) ? WP_LANG_DIR : $dir ) . '/*.mo' );
860 foreach ( $lang_files as $lang_file ) {
861 $lang_file = basename( $lang_file, '.mo' );
862 if ( 0 !== strpos( $lang_file, 'continents-cities' ) && 0 !== strpos( $lang_file, 'ms-' ) &&
863 0 !== strpos( $lang_file, 'admin-' ) ) {
864 $languages[] = $lang_file;
873 * Get installed translations.
875 * Looks in the wp-content/languages directory for translations of
880 * @param string $type What to search for. Accepts 'plugins', 'themes', 'core'.
881 * @return array Array of language data.
883 function wp_get_installed_translations( $type ) {
884 if ( $type !== 'themes' && $type !== 'plugins' && $type !== 'core' )
887 $dir = 'core' === $type ? '' : "/$type";
889 if ( ! is_dir( WP_LANG_DIR ) )
892 if ( $dir && ! is_dir( WP_LANG_DIR . $dir ) )
895 $files = scandir( WP_LANG_DIR . $dir );
899 $language_data = array();
901 foreach ( $files as $file ) {
902 if ( '.' === $file[0] || is_dir( WP_LANG_DIR . "$dir/$file" ) ) {
905 if ( substr( $file, -3 ) !== '.po' ) {
908 if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?).po/', $file, $match ) ) {
911 if ( ! in_array( substr( $file, 0, -3 ) . '.mo', $files ) ) {
915 list( , $textdomain, $language ) = $match;
916 if ( '' === $textdomain ) {
917 $textdomain = 'default';
919 $language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( WP_LANG_DIR . "$dir/$file" );
921 return $language_data;
925 * Extract headers from a PO file.
929 * @param string $po_file Path to PO file.
930 * @return array PO file headers.
932 function wp_get_pomo_file_data( $po_file ) {
933 $headers = get_file_data( $po_file, array(
934 'POT-Creation-Date' => '"POT-Creation-Date',
935 'PO-Revision-Date' => '"PO-Revision-Date',
936 'Project-Id-Version' => '"Project-Id-Version',
937 'X-Generator' => '"X-Generator',
939 foreach ( $headers as $header => $value ) {
940 // Remove possible contextual '\n' and closing double quote.
941 $headers[ $header ] = preg_replace( '~(\\\n)?"$~', '', $value );
950 * @since 4.3.0 Introduced the `echo` argument.
952 * @see get_available_languages()
953 * @see wp_get_available_translations()
955 * @param string|array $args {
956 * Optional. Array or string of arguments for outputting the language selector.
958 * @type string $id ID attribute of the select element. Default empty.
959 * @type string $name Name attribute of the select element. Default empty.
960 * @type array $languages List of installed languages, contain only the locales.
961 * Default empty array.
962 * @type array $translations List of available translations. Default result of
963 * wp_get_available_translations().
964 * @type string $selected Language which should be selected. Default empty.
965 * @type bool|int $echo Whether to echo the generated markup. Accepts 0, 1, or their
966 * boolean equivalents. Default 1.
967 * @type bool $show_available_translations Whether to show available translations. Default true.
969 * @return string HTML content
971 function wp_dropdown_languages( $args = array() ) {
973 $args = wp_parse_args( $args, array(
976 'languages' => array(),
977 'translations' => array(),
980 'show_available_translations' => true,
983 $translations = $args['translations'];
984 if ( empty( $translations ) ) {
985 require_once( ABSPATH . 'wp-admin/includes/translation-install.php' );
986 $translations = wp_get_available_translations();
990 * $args['languages'] should only contain the locales. Find the locale in
991 * $translations to get the native name. Fall back to locale.
993 $languages = array();
994 foreach ( $args['languages'] as $locale ) {
995 if ( isset( $translations[ $locale ] ) ) {
996 $translation = $translations[ $locale ];
997 $languages[] = array(
998 'language' => $translation['language'],
999 'native_name' => $translation['native_name'],
1000 'lang' => current( $translation['iso'] ),
1003 // Remove installed language from available translations.
1004 unset( $translations[ $locale ] );
1006 $languages[] = array(
1007 'language' => $locale,
1008 'native_name' => $locale,
1014 $translations_available = ( ! empty( $translations ) && $args['show_available_translations'] );
1016 $output = sprintf( '<select name="%s" id="%s">', esc_attr( $args['name'] ), esc_attr( $args['id'] ) );
1018 // Holds the HTML markup.
1019 $structure = array();
1021 // List installed languages.
1022 if ( $translations_available ) {
1023 $structure[] = '<optgroup label="' . esc_attr_x( 'Installed', 'translations' ) . '">';
1025 $structure[] = '<option value="" lang="en" data-installed="1">English (United States)</option>';
1026 foreach ( $languages as $language ) {
1027 $structure[] = sprintf(
1028 '<option value="%s" lang="%s"%s data-installed="1">%s</option>',
1029 esc_attr( $language['language'] ),
1030 esc_attr( $language['lang'] ),
1031 selected( $language['language'], $args['selected'], false ),
1032 esc_html( $language['native_name'] )
1035 if ( $translations_available ) {
1036 $structure[] = '</optgroup>';
1039 // List available translations.
1040 if ( $translations_available ) {
1041 $structure[] = '<optgroup label="' . esc_attr_x( 'Available', 'translations' ) . '">';
1042 foreach ( $translations as $translation ) {
1043 $structure[] = sprintf(
1044 '<option value="%s" lang="%s"%s>%s</option>',
1045 esc_attr( $translation['language'] ),
1046 esc_attr( current( $translation['iso'] ) ),
1047 selected( $translation['language'], $args['selected'], false ),
1048 esc_html( $translation['native_name'] )
1051 $structure[] = '</optgroup>';
1054 $output .= join( "\n", $structure );
1056 $output .= '</select>';
1058 if ( $args['echo'] ) {