file = $file; $this->root = dirname($file); $this->prefix = str_replace('-', '_', basename($this->root)); add_action('admin_menu', [$this, 'set_menu']); add_action('init', [$this, 'switch_locale']); add_action('admin_bar_menu', [$this, 'set_language_switcher'], 100); add_action('admin_init', [$this, 'download_language']); add_action('admin_init', [$this, 'save_languages']); add_action('admin_init', [$this, 'init_languages']); } public function set_menu(): void { // 1 читаем файлы $path = $this->root . '/pages'; if (!is_dir($path)) { return; } $files = glob($path . '/*.php') ?: []; // составляем дерево из файлов foreach ($files as $file) { $filename = basename($file, '.php'); if (!preg_match( '/^(\d+)_([^_]+)(?:_(\d+)_([^_]+))?$/', $filename, $matches )) { continue; } $menuOrder = (int) $matches[1]; $menuName = $matches[2]; $menuItemTitle = $this->get_page_title($file, $menuName, 'menuItemTitle'); if (!isset($this->pages[$menuOrder])) { $this->pages[$menuOrder] = [ 'name' => $menuName, 'title' => $menuItemTitle, 'file' => null, 'tabs' => [], ]; } if (isset($matches[3])) { $tabOrder = (int) $matches[3]; $tabName = $matches[4]; // $tabTitle = $this->get_page_title($file, $tabName); $tabTitle = $this->get_page_title($file, $tabName, 'TabTitle'); $this->pages[$menuOrder]['tabs'][$tabOrder] = [ 'name' => $tabName, 'title' => $tabTitle, 'file' => $file, ]; } else { $this->pages[$menuOrder]['file'] = $file; } } // foreach ($files as $file) { // если нет страници с номером 1 выходим if (!isset($this->pages[1])) { return; } /* * Главное меню */ $slug = $this->prefix; $pluginName = $this->get_plugin_name(); if ($this->pages[1]['file']!= null) { $menuTitle = $this->get_page_title($this->pages[1]['file'], $pluginName, 'menuTitle'); } else { $menuTitle = $this->get_page_title($this->pages[1]['tabs'][1]['file'], $pluginName, 'menuTitle'); } add_menu_page( $pluginName, $menuTitle, 'manage_options', $slug, [$this, 'render_main'], 'dashicons-translation' ); add_submenu_page( $slug, ucfirst($this->pages[1]['name']), ucfirst($this->pages[1]['title']), 'manage_options', $slug, [$this, 'render_main'] ); /* * Остальные страницы — submenu */ foreach ($this->pages as $menuOrder => $menu) { if ($menuOrder === 1) { continue; } add_submenu_page( $slug, ucfirst($menu['name']), ucfirst($menu['title']), 'manage_options', $slug . '-' . $menuOrder, function () use ($menuOrder) { $this->render($menuOrder); } ); } } public function render_main(): void { $this->render(1); } protected function render(int $menuOrder): void { if (!isset($this->pages[$menuOrder])) { return; } $menu = $this->pages[$menuOrder]; echo '
'; /* * Есть вкладки */ if (!empty($menu['tabs'])) { $tabs = $menu['tabs']; ksort($tabs); $activeTab = isset($_GET['tab']) ? (int) $_GET['tab'] : array_key_first($tabs); if (!isset($tabs[$activeTab])) { $activeTab = array_key_first($tabs); } echo ''; if (isset($tabs[$activeTab])) { include $tabs[$activeTab]['file']; } /* * Обычная страница */ } elseif ($menu['file']) { include $menu['file']; } echo '
'; } protected function get_page_title( string $file, string $default, string $field = 'menuTitle' ): string { $content = file_get_contents($file); if (preg_match('/' . preg_quote($field, '/') . ':\s*([^\r\n]+)/i', $content, $matches)) { return trim($matches[1]); } return $default; } protected function get_plugin_name(): string { $content = file_get_contents($this->file); if ($content === false) { return $this->prefix; } if (preg_match('/^\s*\*\s*Plugin Name:\s*(.+)$/mi', $content, $matches)) { return trim($matches[1]); } return $this->prefix; } public function switch_locale() { if (!is_user_logged_in()) { return; } $parameter = $this->prefix . '_locale'; if (empty($_GET[$parameter])) { return; } if (empty($_GET['_wpnonce'])) { return; } $locale = sanitize_text_field( wp_unslash($_GET[$parameter]) ); if (!wp_verify_nonce( sanitize_text_field( wp_unslash($_GET['_wpnonce']) ), $this->prefix . '_switch_locale' )) { return; } $key = $this->prefix . '_languages'; $allowed = get_user_meta( get_current_user_id(), $key, true ); if (!is_array($allowed)) { return; } if (!in_array($locale, $allowed, true)) { return; } wp_update_user([ 'ID' => get_current_user_id(), 'locale' => $locale, ]); wp_safe_redirect( remove_query_arg([ $parameter, '_wpnonce', ]) ); exit; } public function set_language_switcher($admin_bar) { if (!is_user_logged_in()) { return; } $key = $this->prefix . '_languages'; $user_id = get_current_user_id(); $selected = get_user_meta( $user_id, $key, true ); $installed = get_available_languages(); $installed[] = 'en_US'; $installed = array_unique($installed); if (!metadata_exists('user', $user_id, $key)) { $selected = $installed; } elseif (!is_array($selected)) { $selected = []; } $selected = array_values( array_intersect( $selected, $installed ) ); require_once ABSPATH . 'wp-admin/includes/translation-install.php'; $translations = wp_get_available_translations(); $languages = [ 'en_US' => [ 'language' => 'en_US', 'native_name' => 'English', 'iso' => [ 1 => 'en', ], ], ]; foreach ($translations as $translation) { $languages[$translation['language']] = $translation; } $current = get_user_locale(); $current_language = isset($languages[$current]) ? $languages[$current]['native_name'] : $current; $admin_bar->add_node([ 'id' => $this->prefix . '-language', 'title' => $this->get_language_flag( $current, isset($languages[$current]) ? $languages[$current] : [] ) . ' ' . $current_language, 'parent' => 'top-secondary', ]); if (empty($selected)) { $admin_bar->add_node([ 'parent' => $this->prefix . '-language', 'id' => $this->prefix . '-language-empty', 'title' => 'No languages selected', ]); } foreach ($selected as $locale) { if (!isset($languages[$locale])) { continue; } $url = add_query_arg( $this->prefix . '_locale', $locale ); $url = wp_nonce_url( $url, $this->prefix . '_switch_locale' ); $admin_bar->add_node([ 'parent' => $this->prefix . '-language', 'id' => $this->prefix . '-language-' . sanitize_key($locale), 'title' => $this->get_language_flag( $locale, $languages[$locale] ) . ' ' . $languages[$locale]['native_name'], 'href' => $url, 'meta' => [ 'lang' => str_replace('_', '-', $locale), ], ]); } $settings_url = admin_url( 'admin.php?page=' . $this->prefix ); $admin_bar->add_node([ 'parent' => $this->prefix . '-language', 'id' => $this->prefix . '-language-settings', 'title' => 'Settings', 'href' => $settings_url, ]); } public function download_language() { if (!is_user_logged_in()) { return; } $parameter = $this->prefix . '_download'; if (empty($_GET[$parameter])) { return; } if (empty($_GET['_wpnonce'])) { return; } if (!current_user_can('manage_options')) { return; } $locale = sanitize_text_field( wp_unslash($_GET[$parameter]) ); if (!wp_verify_nonce( sanitize_text_field( wp_unslash($_GET['_wpnonce']) ), $this->prefix . '_download_language' )) { return; } require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/translation-install.php'; if (!wp_can_install_language_pack()) { wp_die( 'WordPress cannot install language packs automatically.' ); } $result = wp_download_language_pack($locale); if (is_wp_error($result)) { wp_die( esc_html($result->get_error_message()) ); } if (!$result) { wp_die( 'Language pack could not be installed.' ); } wp_safe_redirect( remove_query_arg([ $parameter, '_wpnonce', ]) ); exit; } public function save_languages() { if (!is_admin()) { return; } if ( !isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST' ) { return; } if (empty($_POST[$this->prefix . '_save'])) { return; } if (!current_user_can('manage_options')) { return; } check_admin_referer('admin_language_save'); $key = $this->prefix . '_languages'; $installed = get_available_languages(); $installed[] = 'en_US'; $installed = array_unique($installed); $postedLanguages = []; if (isset($_POST['languages']) && is_array($_POST['languages'])) { $postedLanguages = array_map( 'sanitize_text_field', wp_unslash($_POST['languages']) ); } $selected = $postedLanguages; $selected = array_values( array_intersect( array_map('sanitize_text_field', $selected), $installed ) ); update_user_meta( get_current_user_id(), $key, $selected ); } protected function get_language_flag($locale, $language = []) { $iso = ''; if (!empty($language['iso'][1])) { $iso = strtolower($language['iso'][1]); } $flags = [ 'en' => '🇬🇧', 'uk' => '🇺🇦', 'ru' => '🇷🇺', 'de' => '🇩🇪', 'fr' => '🇫🇷', 'es' => '🇪🇸', 'it' => '🇮🇹', 'pl' => '🇵🇱', 'pt' => '🇵🇹', 'nl' => '🇳🇱', 'cs' => '🇨🇿', 'sk' => '🇸🇰', 'hu' => '🇭🇺', 'ro' => '🇷🇴', 'bg' => '🇧🇬', 'el' => '🇬🇷', 'tr' => '🇹🇷', 'ja' => '🇯🇵', 'ko' => '🇰🇷', 'zh' => '🇨🇳', ]; return isset($flags[$iso]) ? $flags[$iso] : '🌐'; } function init_languages() { $user_id = get_current_user_id(); if (!$user_id) { return; } $key = $this->prefix . '_languages'; if (metadata_exists('user', $user_id, $key)) { return; } $languages = get_available_languages(); $languages[] = 'en_US'; $languages = array_values( array_unique($languages) ); update_user_meta( $user_id, $key, $languages ); } }