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
);
}
}