array( array( 'code' => 'en', 'name' => 'English', 'locale' => 'en_CA', 'default' => true ), array( 'code' => 'fr', 'name' => 'Français', 'locale' => 'fr_CA', 'default' => false ), ), 'url_mode' => 'directory', // 'directory' | 'param' 'bases' => array( 'post_type' => array(), 'taxonomy' => array(), ), 'admin_lang' => 'en', // 'en' | 'fr' (interface of Duolangue screens) 'admin_lang_per_user' => true, 'switcher_display' => 'name', // 'name' (Français) | 'code' (FR) 'menu_locations' => array(), // theme locations, or '_all' for every menu 'menu_display' => 'code', // 'name' | 'code' 'menu_show' => 'other', // 'other' | 'both' ); } public static function get() { $settings = get_option( self::OPTION, array() ); $settings = wp_parse_args( is_array( $settings ) ? $settings : array(), self::defaults() ); // Duolangue's data model is a language pair: the default language // (served at the site root) and the second language (served under its // own prefix). Rebuild the pair from whatever is stored. $stored = array_values( (array) $settings['languages'] ); $defaults = self::defaults()['languages']; $settings['languages'] = array( isset( $stored[0] ) && is_array( $stored[0] ) ? wp_parse_args( $stored[0], $defaults[0] ) : $defaults[0], isset( $stored[1] ) && is_array( $stored[1] ) ? wp_parse_args( $stored[1], $defaults[1] ) : $defaults[1], ); return $settings; } public static function update( $settings ) { update_option( self::OPTION, $settings ); } /** * Effective language of the Duolangue admin interface for the current user. * * @return string 'en' | 'fr' */ public static function admin_ui_lang() { $settings = self::get(); if ( ! empty( $settings['admin_lang_per_user'] ) ) { $user_pref = get_user_meta( get_current_user_id(), self::USER_META, true ); if ( in_array( $user_pref, array( 'en', 'fr' ), true ) ) { return $user_pref; } } return in_array( $settings['admin_lang'], array( 'en', 'fr' ), true ) ? $settings['admin_lang'] : 'en'; } /** * Curated list of installable WordPress locales, filterable. */ public static function locales() { return apply_filters( 'duolangue_locales', array( 'en_US' => 'English (United States)', 'en_CA' => 'English (Canada)', 'en_GB' => 'English (United Kingdom)', 'en_AU' => 'English (Australia)', 'en_NZ' => 'English (New Zealand)', 'en_ZA' => 'English (South Africa)', 'fr_CA' => 'Français (Canada)', 'fr_FR' => 'Français (France)', 'fr_BE' => 'Français (Belgique)', 'es_ES' => 'Español (España)', 'es_MX' => 'Español (México)', 'de_DE' => 'Deutsch', 'de_CH' => 'Deutsch (Schweiz)', 'it_IT' => 'Italiano', 'pt_PT' => 'Português (Portugal)', 'pt_BR' => 'Português (Brasil)', 'nl_NL' => 'Nederlands', 'nl_BE' => 'Nederlands (België)', 'sv_SE' => 'Svenska', 'da_DK' => 'Dansk', 'nb_NO' => 'Norsk bokmål', 'fi' => 'Suomi', 'pl_PL' => 'Polski', 'cs_CZ' => 'Čeština', 'el' => 'Ελληνικά', 'ru_RU' => 'Русский', 'uk' => 'Українська', 'ar' => 'العربية', 'he_IL' => 'עברית', 'ja' => '日本語', 'ko_KR' => '한국어', 'zh_CN' => '简体中文', 'zh_TW' => '繁體中文', ) ); } /* ------------------------------------------------------------------ * Admin UI language plumbing * ---------------------------------------------------------------- */ /** * Is the current request one of the Duolangue admin screens? */ public static function is_plugin_screen() { return is_admin() && isset( $_GET['page'] ) && self::PAGE === sanitize_key( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- read-only screen detection. } /** * On Duolangue screens, render the whole page in the chosen interface * language so the native .mo files (core + duolangue) do the work. */ public function filter_admin_locale( $locale ) { if ( ! self::is_plugin_screen() ) { return $locale; } $ui = self::admin_ui_lang(); $lang = duolangue_get_language( $ui ); if ( $lang && ! empty( $lang['locale'] ) ) { return $lang['locale']; } // Sensible fallbacks when the site languages don't include en/fr. return 'fr' === $ui ? 'fr_CA' : 'en_US'; } /** * EN | FR segmented toggle shown top-right of every Duolangue screen. */ public static function render_ui_toggle() { $current = self::admin_ui_lang(); $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'settings'; // phpcs:ignore WordPress.Security.NonceVerification ?>
'EN', 'fr' => 'FR' ) as $code => $label ) : $url = wp_nonce_url( admin_url( 'admin-post.php?action=duolangue_toggle_ui&ui=' . $code . '&tab=' . $tab ), 'duolangue_toggle_ui' ); ?>
__( 'Settings saved.', 'duolangue' ), 'slugs_saved' => __( 'URL bases saved and permalinks flushed.', 'duolangue' ), ); /** * Success messages shown on the Duolangue screens (?duolangue_msg=key). * * @param string[] $map Message key => text. */ $map = (array) apply_filters( 'duolangue_admin_messages', $map ); if ( isset( $map[ $msg ] ) ) { printf( '

%s

', esc_html( $map[ $msg ] ) ); } } public function render_page() { $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'settings'; // phpcs:ignore WordPress.Security.NonceVerification $tabs = array( 'settings' => __( 'Settings', 'duolangue' ), 'slugs' => __( 'Slugs & URLs', 'duolangue' ), ); $tabs['pages'] = __( 'Pages & editor', 'duolangue' ); /** * Tabs of the Duolangue admin screen (slug => label). A tab added here * is rendered through the "duolangue_render_tab_{$slug}" action. * * @param string[] $tabs */ $tabs = (array) apply_filters( 'duolangue_admin_tabs', $tabs ); if ( ! isset( $tabs[ $tab ] ) ) { $tab = 'settings'; } ?>

render_settings_tab(); break; default: do_action( 'duolangue_render_tab_' . $tab ); } ?>

$lang ) : ?>
/>

  


$label ) : ?>


 

<?php duolangue_other_lang_link( array( 'display' => 'code' ) ); ?>

<a href="<?php echo esc_url( duolangue_other_lang_url() ); ?>">EN</a>

[duolangue_switcher only_other="yes" display="code"]

$code, 'name' => $name, 'locale' => $locale, 'default' => $i === $default_idx, ); } // The two codes must differ (they become URL prefixes). if ( $languages[0]['code'] === $languages[1]['code'] ) { $languages[1]['code'] = substr( $languages[1]['code'], 0, 4 ) . '2'; } $settings['languages'] = $languages; $settings['url_mode'] = ( isset( $_POST['url_mode'] ) && 'param' === $_POST['url_mode'] ) ? 'param' : 'directory'; $settings['admin_lang'] = ( isset( $_POST['admin_lang'] ) && 'fr' === $_POST['admin_lang'] ) ? 'fr' : 'en'; $settings['admin_lang_per_user'] = ! empty( $_POST['admin_lang_per_user'] ); $settings['switcher_display'] = ( isset( $_POST['switcher_display'] ) && 'code' === $_POST['switcher_display'] ) ? 'code' : 'name'; $settings['menu_display'] = ( isset( $_POST['menu_display'] ) && 'name' === $_POST['menu_display'] ) ? 'name' : 'code'; $settings['menu_show'] = ( isset( $_POST['menu_show'] ) && 'both' === $_POST['menu_show'] ) ? 'both' : 'other'; $raw_locations = isset( $_POST['menu_locations'] ) && is_array( $_POST['menu_locations'] ) ? map_deep( wp_unslash( $_POST['menu_locations'] ), 'sanitize_text_field' ) : array(); $raw_locations = array_map( 'sanitize_key', array_filter( $raw_locations, 'is_string' ) ); $valid = array_merge( array( '_all' ), array_keys( get_registered_nav_menus() ) ); $settings['menu_locations'] = array_values( array_intersect( $raw_locations, $valid ) ); self::update( $settings ); flush_rewrite_rules(); wp_safe_redirect( admin_url( 'admin.php?page=' . self::PAGE . '&tab=settings&duolangue_msg=settings_saved' ) ); exit; } /* ------------------------------------------------------------------ * Tab 4 — Pages & editor (overview) * ---------------------------------------------------------------- */ private static function render_pages_overview() { $langs = duolangue_get_languages(); $default = duolangue_default_lang(); ?>

labels->name ); ?>
'OR', array( 'key' => '_duolangue_lang', 'value' => $lang ), array( 'key' => '_duolangue_lang', 'compare' => 'NOT EXISTS' ), ) : array( array( 'key' => '_duolangue_lang', 'value' => $lang ) ); $q = new WP_Query( array( 'post_type' => $post_type, 'post_status' => 'any', 'posts_per_page' => 1, 'fields' => 'ids', 'meta_query' => $meta_query, // phpcs:ignore WordPress.DB.SlowDBQuery 'no_found_rows' => false, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'duolangue_lang' => 'all', ) ); return (int) $q->found_posts; } }