Files
wordpress-theme-aksrt/src/Settings/ThemeSearch.php
T
kate522andClaude Opus 5 35bb96e82f refactor: rename the theme brand and every identifier from AKSRT to Jio
Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.

Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
  cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
  window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)

Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
  so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
  (dropped 3 stale entries, added the 17 missing ones)

Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
  get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
  when it is empty

Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.

Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-01 20:05:04 +08:00

602 lines
24 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* @package Jio
*/
namespace Jio\Settings;
defined('ABSPATH') || exit;
class ThemeSearch {
public static function search_placeholder(): string {
$placeholder = trim((string) Settings::get_value('search_placeholder', ''));
return $placeholder !== '' ? $placeholder : __('开启精彩搜索', 'jio');
}
public static function search_history_enabled(): bool {
$settings = Settings::get_all();
return !array_key_exists('search_history', $settings) || !empty($settings['search_history']);
}
public static function hot_search_enabled(): bool {
$settings = Settings::get_all();
return !array_key_exists('search_popular_key', $settings) || !empty($settings['search_popular_key']);
}
public static function hot_search_keywords(): array {
$items = self::search_popular_sticky();
$counts = get_option('search_keywords', []);
if (is_array($counts)) {
$items = array_merge($items, array_keys($counts));
}
$items = array_map(
static function ($keyword): string {
return trim(sanitize_text_field((string) $keyword));
},
$items
);
$items = array_values(array_filter(array_unique($items), static function (string $keyword): bool {
return $keyword !== '';
}));
$limit = max(1, min(50, absint(Settings::get_value('search_popular_key_num', 20))));
return array_slice($items, 0, $limit);
}
public static function search_popular_title(): string {
$title = trim((string) Settings::get_value('search_popular_title', ''));
return $title !== '' ? $title : __('热门搜索', 'jio');
}
public static function search_popular_sticky(): array {
$raw = Settings::get_value('search_popular_sticky', '');
if (is_array($raw)) {
$items = $raw;
} else {
$items = preg_split('/[\r\n,,]+/', (string) $raw) ?: [];
}
$items = array_map(static fn($k) => trim(sanitize_text_field((string) $k)), $items);
return array_values(array_filter(array_unique($items), fn(string $k) => $k !== ''));
}
public static function search_posts_enabled(): bool {
return !empty(Settings::get_value('search_posts', false));
}
public static function search_no_page(): bool {
return !empty(Settings::get_value('search_no_page', false));
}
public static function search_exclude_cats(): array {
$raw = Settings::get_value('search_exclude_cats', '');
if (is_array($raw)) {
$items = $raw;
} else {
$items = preg_split('/[,,\s]+/', (string) $raw) ?: [];
}
$items = array_map(static fn($id) => absint($id), $items);
return array_values(array_filter(array_unique($items), fn(int $id) => $id > 0));
}
public static function search_cat_enabled(): bool {
return !empty(Settings::get_value('search_cat', false));
}
public static function search_cat_in(): string {
return trim((string) Settings::get_value('search_cat_in', ''));
}
public static function search_more_cat_obj(): array {
$raw = Settings::get_value('search_more_cat_obj', []);
$items = is_array($raw) ? $raw : (preg_split('/[,,\s]+/', (string) $raw) ?: []);
return array_values(array_filter(array_unique(array_map('absint', $items))));
}
public static function search_custom_key_enabled(): bool {
return !empty(Settings::get_value('search_custom_key_s', false));
}
public static function search_custom_key(): array {
$raw = Settings::get_value('search_custom_key', ['title', 'content', 'excerpt', 'category', 'post_tag']);
if (is_array($raw)) {
$items = $raw;
} else {
$items = preg_split('/[,,\s]+/', (string) $raw) ?: [];
}
$items = array_map(static fn($k) => trim(sanitize_key((string) $k)), $items);
$valid = ['title', 'content', 'excerpt', 'category', 'post_tag', 'coment', 'user'];
return array_values(array_intersect(array_filter(array_unique($items)), $valid));
}
public static function search_facets_enabled(): bool {
return !empty(Settings::get_value('search_facets', []));
}
public static function search_facets_order(): array {
$raw = Settings::get_value('search_facets', []);
$items = is_array($raw) ? array_keys($raw) : [];
$items = array_map(static fn($s) => trim(sanitize_key((string) $s)), $items);
return array_values(array_filter(array_unique($items), fn(string $s) => $s !== ''));
}
public static function get_meilisearch_config(): array {
$s = Settings::get_all();
$opt = is_array($s['search_ms_opt'] ?? null) ? $s['search_ms_opt'] : [];
$enabled = !empty($s['search_ms_s']);
$api_url = esc_url_raw(trim((string) ($opt['api_url'] ?? 'http://127.0.0.1:7700')));
$api_key = sanitize_text_field((string) ($opt['api_key'] ?? ''));
$index_prefix = sanitize_key((string) ($opt['index_prefix'] ?? 'jio'));
$synonyms_raw = $opt['synonyms'] ?? [];
$synonyms = [];
foreach ((array) $synonyms_raw as $row) {
if (!is_array($row)) {
continue;
}
$key = trim(sanitize_text_field((string) ($row['key'] ?? '')));
$values = array_values(array_filter(array_map('trim', explode(',', sanitize_text_field((string) ($row['val'] ?? ''))))));
if ($key !== '' && $values) {
$synonyms[$key] = $values;
}
}
return [
'enabled' => $enabled,
'api_url' => $api_url,
'api_key' => $api_key,
'index_prefix' => $index_prefix !== '' ? $index_prefix : 'jio',
'synonyms' => $synonyms,
'index_name' => ($index_prefix !== '' ? $index_prefix : 'jio') . '_posts',
];
}
public static function filter_posts_search(string $search, \WP_Query $query): string {
if (!$query->is_search() || !$query->get('s') || !self::search_custom_key_enabled()) {
return $search;
}
if (sanitize_key((string) $query->get('type')) === 'user') {
return ' AND 1=0';
}
$config = self::get_meilisearch_config();
if (!empty($config['enabled'])) {
return $search;
}
$keys = self::search_custom_key();
if (!$keys) {
return $search;
}
global $wpdb;
$terms = (array) ($query->query_vars['search_terms'] ?? []);
if (!$terms) {
$terms = [(string) $query->get('s')];
}
$groups = [];
$taxonomies = ['post_tag', 'category'];
foreach ($terms as $term) {
$like = (!empty($query->query_vars['exact']) ? '' : '%') . $wpdb->esc_like((string) $term) . (!empty($query->query_vars['exact']) ? '' : '%');
$parts = [];
foreach ($keys as $key) {
if ($key === 'title') $parts[] = $wpdb->prepare("{$wpdb->posts}.post_title LIKE %s", $like);
elseif ($key === 'content') $parts[] = $wpdb->prepare("{$wpdb->posts}.post_content LIKE %s", $like);
elseif ($key === 'excerpt') $parts[] = $wpdb->prepare("{$wpdb->posts}.post_excerpt LIKE %s", $like);
elseif (in_array($key, $taxonomies, true)) $parts[] = $wpdb->prepare('(jio_search_tt.taxonomy = %s AND jio_search_terms.name LIKE %s)', $key, $like);
elseif ($key === 'coment') $parts[] = $wpdb->prepare('jio_search_comments.comment_content LIKE %s', $like);
elseif ($key === 'user') $parts[] = $wpdb->prepare('(jio_search_users.user_nicename LIKE %s OR jio_search_users.display_name LIKE %s)', $like, $like);
}
if ($parts) {
$groups[] = '(' . implode(' OR ', $parts) . ')';
}
}
if (!$groups) {
return $search;
}
if (!is_user_logged_in()) {
$groups[] = "({$wpdb->posts}.post_password = '')";
}
return ' AND ' . implode(' AND ', $groups);
}
public static function filter_posts_search_join(string $join, \WP_Query $query): string {
if (!$query->is_search() || !$query->get('s') || !self::search_custom_key_enabled() || !empty(self::get_meilisearch_config()['enabled'])) {
return $join;
}
global $wpdb;
$keys = self::search_custom_key();
if (in_array('coment', $keys, true)) {
$join .= " LEFT JOIN {$wpdb->comments} AS jio_search_comments ON ({$wpdb->posts}.ID = jio_search_comments.comment_post_ID)";
}
if (in_array('user', $keys, true)) {
$join .= " LEFT JOIN {$wpdb->users} AS jio_search_users ON ({$wpdb->posts}.post_author = jio_search_users.ID)";
}
if (array_intersect(['post_tag', 'category'], $keys)) {
$join .= " LEFT JOIN {$wpdb->term_relationships} AS jio_search_tr ON ({$wpdb->posts}.ID = jio_search_tr.object_id)";
$join .= " LEFT JOIN {$wpdb->term_taxonomy} AS jio_search_tt ON (jio_search_tr.term_taxonomy_id = jio_search_tt.term_taxonomy_id)";
$join .= " LEFT JOIN {$wpdb->terms} AS jio_search_terms ON (jio_search_tt.term_id = jio_search_terms.term_id)";
}
return $join;
}
public static function filter_posts_search_distinct(string $distinct, \WP_Query $query): string {
return $query->is_search() && $query->get('s') && self::search_custom_key_enabled() ? 'DISTINCT' : $distinct;
}
public static function filter_search_query($query): void {
if (is_admin() || !$query->is_main_query() || !$query->is_search()) {
return;
}
$type = isset($_REQUEST['type']) ? sanitize_key((string) wp_unslash($_REQUEST['type'])) : '';
if (!in_array($type, ['post', 'user'], true)) {
$type = self::get_search_content_types()['default'];
}
$query->set('type', $type);
if ($type === 'user') {
$query->set('post__in', [0]);
return;
}
if (self::search_no_page()) {
$existing = $query->get('post_type');
if (empty($existing)) {
$query->set('post_type', 'post');
}
}
$exclude_cats = self::search_exclude_cats();
if (!empty($exclude_cats)) {
foreach ($exclude_cats as $category_id) {
$children = get_term_children($category_id, 'category');
if (!is_wp_error($children)) {
$exclude_cats = array_merge($exclude_cats, $children);
}
}
$query->set('category__not_in', array_values(array_unique(array_map('absint', $exclude_cats))));
}
$term_id = isset($_REQUEST['trem']) ? absint(wp_unslash($_REQUEST['trem'])) : 0;
if ($term_id > 0) {
$term = get_term($term_id);
if ($term instanceof \WP_Term) {
$tax_query = (array) $query->get('tax_query');
$tax_query[] = [
'taxonomy' => $term->taxonomy,
'field' => 'term_id',
'terms' => $term->term_id,
];
$query->set('tax_query', $tax_query);
}
}
$user_id = isset($_REQUEST['user']) ? absint(wp_unslash($_REQUEST['user'])) : 0;
if ($user_id > 0) {
$query->set('author', $user_id);
}
$orderby = isset($_REQUEST['orderby']) ? sanitize_key((string) wp_unslash($_REQUEST['orderby'])) : '';
$meta_keys = [
'views' => 'jio_post_views',
'like' => '_jio_like_count',
'favorite' => '_jio_favorite_count',
'sales_volume' => '_jio_sales_volume',
];
if (isset($meta_keys[$orderby])) {
$query->set('meta_key', $meta_keys[$orderby]);
$query->set('orderby', ['meta_value_num' => 'DESC', 'date' => 'DESC']);
$query->set('order', 'DESC');
} elseif ($orderby === 'comment_count') {
$query->set('orderby', 'comment_count');
$query->set('order', 'DESC');
} elseif ($orderby === 'date') {
$query->set('orderby', 'date');
$query->set('order', 'DESC');
}
}
public static function apply_list_orderby($query): void {
if (is_admin() || !$query->is_main_query()) {
return;
}
if (!$query->is_archive() && !$query->is_search() && !$query->is_home()) {
return;
}
if ($query->get('orderby')) {
return;
}
$s = Settings::get_all();
$key = sanitize_key((string) ($s['list_orderby'] ?? 'modified'));
if ($key === '' || $key === 'default') {
return;
}
switch ($key) {
case 'modified':
$query->set('orderby', 'modified');
$query->set('order', 'DESC');
break;
case 'date':
$query->set('orderby', 'date');
$query->set('order', 'DESC');
break;
case 'comments':
$query->set('orderby', 'comment_count');
$query->set('order', 'DESC');
break;
}
}
public static function get_search_content_types(): array {
$types = ['post', 'user'];
$default = sanitize_key((string) Settings::get_value('search_type_in', 'post'));
if (!in_array($default, $types, true)) {
$default = 'post';
}
return [
'types' => $types,
'default' => $default,
];
}
public static function theme_mode(): string {
$mode = sanitize_key((string) Settings::get_value('theme_mode', 'time-auto'));
$valid = ['white-theme', 'dark-theme', 'time-auto'];
return in_array($mode, $valid, true) ? $mode : 'time-auto';
}
public static function theme_mode_js_value(): string {
$mode = self::theme_mode();
$map = ['white-theme' => 'light', 'dark-theme' => 'dark', 'time-auto' => 'time-auto'];
return $map[$mode] ?? 'time-auto';
}
public static function time_auto_theme_mode(): string {
$hour = (int) current_time('G');
return ($hour >= 20 || $hour < 8) ? 'dark' : 'light';
}
public static function initial_theme_mode(): string {
$cookie = isset($_COOKIE['theme_mode'])
? sanitize_key((string) wp_unslash($_COOKIE['theme_mode']))
: '';
$map = [
'dark-theme' => 'dark',
'white-theme' => 'light',
'dark' => 'dark',
'light' => 'light',
];
if (isset($map[$cookie])) {
return $map[$cookie];
}
$mode = self::theme_mode();
if ($mode === 'dark-theme') {
return 'dark';
}
if ($mode === 'white-theme') {
return 'light';
}
if ($mode === 'time-auto') {
return self::time_auto_theme_mode();
}
return '';
}
/** 全局主题色:theme_skin_custom 优先,清空时使用 theme_skin 预置色(对齐参考主题 zib_head_css)。 */
public static function theme_color(): string {
$custom = \Jio\Core\Color::sanitize(Settings::get_value('theme_skin_custom', ''));
if ($custom !== '') {
return $custom;
}
$skin = ltrim(trim((string) Settings::get_value('theme_skin', 'f04494')), '#');
return preg_match('/^[0-9a-fA-F]{6}$/', $skin) ? '#' . $skin : '#f04494';
}
/** 夜间模式主题色;未设置返回空串,夜间沿用全局主题色。 */
public static function dark_theme_color(): string {
return \Jio\Core\Color::sanitize(Settings::get_value('dark_theme_skin_custom', ''));
}
public static function get_theme_mode_button_positions(): array {
$raw = Settings::get_value('theme_mode_button', ['pc_nav', 'm_menu']);
return array_values(array_intersect(array_map('sanitize_key', (array) $raw), ['pc_nav', 'm_menu']));
}
/** 卡片圆角 theme_main_radius(0–30,默认 12)→ --main-radius。 */
public static function main_radius(): int {
return max(0, min(30, (int) Settings::get_value('theme_main_radius', 12)));
}
public static function close_backdrop(): bool {
return !empty(Settings::get_value('close_backdrop', false));
}
/**
* 背景图 CSS 声明(不含选择器);无颜色且无图片时返回空串。
* 子键白名单与 GlobalSchema::background_field() / layout.js bgField 一致。
*/
public static function background_css(string $key): string {
$bg = Settings::get_value($key, []);
if (!is_array($bg)) {
return '';
}
$color = \Jio\Core\Color::sanitize($bg['color'] ?? '');
$image = esc_url_raw(trim((string) ($bg['image'] ?? '')));
if ($color === '' && $image === '') {
return '';
}
$allowed = [
'position' => ['left top', 'left center', 'left bottom', 'center top', 'center center', 'center bottom', 'right top', 'right center', 'right bottom'],
'repeat' => ['repeat', 'no-repeat', 'repeat-x', 'repeat-y'],
'attachment' => ['scroll', 'fixed'],
'size' => ['100%', 'cover', 'contain', 'auto'],
'origin' => ['padding-box', 'border-box', 'content-box'],
];
$css = $color !== '' ? 'background-color:' . $color . ';' : '';
if ($image !== '') {
$css .= 'background-image:url("' . str_replace(['"', '\\', "\n", "\r"], '', $image) . '");';
foreach ($allowed as $prop => $values) {
$value = (string) ($bg[$prop] ?? '');
if (in_array($value, $values, true)) {
$css .= 'background-' . $prop . ':' . $value . ';';
}
}
}
return $css;
}
public static function is_qj_loading_enabled(): bool {
return (bool) Settings::get_value('qj_loading', false);
}
public static function get_qj_loading_style(): string {
$style = (string) Settings::get_value('qj_dh_xs', 'no1');
if (!preg_match('/^no([1-9]|10)$/', $style)) {
return 'no1';
}
return $style;
}
public static function container_max_width(): int {
$width = (int) Settings::get_value('container_max_width', 1200);
return max(1200, min(2200, $width));
}
public static function font_family(): string {
$family = sanitize_key((string) Settings::get_value('font_family', 'system'));
return in_array($family, ['system', 'noto-sans-sc', 'noto-serif-sc', 'custom'], true) ? $family : 'system';
}
/** 自定义 font-family 字体栈,只保留字体名、引号、逗号等安全字符。 */
public static function font_family_custom(): string {
$stack = (string) Settings::get_value('font_family_custom', '');
$stack = preg_replace('/[^\p{L}\p{N}\s,"\'\-_.]/u', '', $stack);
return trim((string) preg_replace('/\s+/', ' ', (string) $stack), " ,");
}
public static function get_code_highlight_config(): array {
$s = Settings::get_all();
$light_themes = ['enlighter','bootstrap4','classic','beyond','mowtwo','eclipse','droide','minimal','rowhammer','godzilla','dracula','atomic','monokai'];
$dark_themes = ['dracula','atomic','monokai','enlighter','bootstrap4'];
$light = sanitize_key((string) ($s['highlight_zt'] ?? 'enlighter'));
$dark = sanitize_key((string) ($s['highlight_dark_zt'] ?? 'monokai'));
$max_height_raw = $s['highlight_maxheight'] ?? 400;
$max_height = absint(is_numeric($max_height_raw) ? $max_height_raw : preg_replace('/[^0-9]/', '', $max_height_raw));
if ($max_height === 0 && !in_array($max_height_raw, [0, '0', '0px'], true)) {
$max_height = 400;
}
return [
'enabled' => !isset($s['highlight_kg']) || !empty($s['highlight_kg']),
'lightTheme' => in_array($light, $light_themes, true) ? $light : 'enlighter',
'darkTheme' => in_array($dark, $dark_themes, true) ? $dark : 'monokai',
'lineNumbers' => !empty($s['highlight_hh']),
'showButtons' => !isset($s['highlight_btn']) || !empty($s['highlight_btn']),
'maxHeight' => $max_height,
];
}
public static function get_system_notice_config(): array {
$s = Settings::get_all();
$policy = sanitize_key((string) ($s['system_notice_policy'] ?? ''));
if (!in_array($policy, ['', 'signin', 'vip', 'free', 'auth'], true)) {
$legacy_map = ['always' => '', 'once' => '', 'daily' => ''];
$policy = $legacy_map[$policy] ?? '';
}
$size = sanitize_key((string) ($s['system_notice_size'] ?? ''));
if (!in_array($size, ['', 'modal-mini', 'modal-sm', 'modal-lg'], true)) {
$size = '';
}
$radius_raw = $s['system_notice_radius'] ?? '0';
$radius = absint(preg_replace('/[^0-9]/', '', $radius_raw));
$allowed_colors = ['primary', 'success', 'warning', 'danger', 'info', 'purple', 'dark'];
$buttons = [];
for ($i = 1; $i <= 4; $i++) {
$text = trim((string) ($s["system_notice_btn{$i}_text"] ?? ''));
$link = esc_url_raw((string) ($s["system_notice_btn{$i}_link"] ?? ''));
$color = sanitize_key((string) ($s["system_notice_btn{$i}_color"] ?? 'primary'));
if ($i === 1 && $text === '') {
$text = trim((string) ($s['system_notice_button'] ?? ''));
$link = $link ?: esc_url_raw((string) ($s['system_notice_link'] ?? ''));
}
if ($text === '') continue;
if (!in_array($color, $allowed_colors, true)) {
$color = 'primary';
}
$buttons[] = [
'text' => $text,
'link' => $link,
'color' => $color,
];
}
return [
'enabled' => !empty($s['system_notice_enabled']),
'policy' => $policy,
'title' => trim((string) ($s['system_notice_title'] ?? '')),
'content' => trim((string) ($s['system_notice_content'] ?? '')),
'buttons' => $buttons,
'size' => $size,
'radius' => $radius,
'class' => sanitize_html_class((string) ($s['system_notice_class'] ?? '')),
'title_icon' => trim((string) ($s['system_notice_title_icon'] ?? '')),
'title_class' => sanitize_html_class((string) ($s['system_notice_title_class'] ?? '')),
'title_style' => trim((string) ($s['system_notice_title_style'] ?? '')),
'expires' => absint($s['system_notice_expires'] ?? 24),
];
}
public static function get_sidebar_config(): array {
$s = Settings::get_all();
$position = sanitize_key((string) ($s['sidebar_position'] ?? 'right'));
$width = max(200, min(400, (int) ($s['sidebar_width'] ?? 311)));
return [
'enabled' => !isset($s['sidebar_enabled']) || !empty($s['sidebar_enabled']),
'position' => in_array($position, ['left', 'right'], true) ? $position : 'right',
'width' => $width,
'home' => !isset($s['sidebar_home_enabled']) || !empty($s['sidebar_home_enabled']),
'category' => !isset($s['sidebar_category_enabled']) || !empty($s['sidebar_category_enabled']),
'tag' => !isset($s['sidebar_tag_enabled']) || !empty($s['sidebar_tag_enabled']),
'single' => !isset($s['sidebar_single_enabled']) || !empty($s['sidebar_single_enabled']),
'page' => !isset($s['sidebar_page_enabled']) || !empty($s['sidebar_page_enabled']),
'search' => !isset($s['sidebar_search_enabled']) || !empty($s['sidebar_search_enabled']),
];
}
public static function get_i18n_config(): array {
$s = Settings::get_all();
$enabled = !empty($s['i18n_enabled']) || !empty($s['frontend_translate_enabled']);
$source = $s['i18n_default_lang'] ?? $s['frontend_translate_source'] ?? 'zh-CN';
return [
'enabled' => $enabled,
'autoDetect' => !isset($s['i18n_auto_detect']) || !empty($s['i18n_auto_detect']),
'defaultLang' => $source,
'availableLangs' => $s['i18n_available_langs'] ?? "zh-CN|简体中文\nen|English",
'nomenclature' => $s['i18n_nomenclature'] ?? '',
'switcherPosition' => $s['i18n_switcher_position'] ?? 'footer',
'urlStrategy' => $s['i18n_url_strategy'] ?? 'query',
];
}
}