latin (fallback si intl n’est pas dispo) function translit_ru_to_lat(string $s): string { if (class_exists('Transliterator')) { $t = \Transliterator::create('Russian-Latin/BGN; Lower();'); if ($t) { return $t->transliterate($s); } } $map = [ 'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e', 'ё' => 'e', 'ж' => 'zh', 'з' => 'z', 'и' => 'i', 'й' => 'i', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'kh', 'ц' => 'ts', 'ч' => 'ch', 'ш' => 'sh', 'щ' => 'shch', 'ъ' => '', 'ы' => 'y', 'ь' => '', 'э' => 'e', 'ю' => 'yu', 'я' => 'ya', ]; $s = mb_strtolower($s, 'UTF-8'); return strtr($s, $map); } // Retourne une liste de candidats latins (pour LIKE) à partir d’une saisie ville function city_query_candidates(string $q): array { $q = trim($q); if ($q === '') { return []; } $aliases = @include __DIR__ . '/city_aliases_ru.php'; if (! is_array($aliases)) { $aliases = []; } $cands = []; $qLower = mb_strtolower($q, 'UTF-8'); // Toujours garder la saisie brute $cands[] = $qLower; // Version ASCII/latin normalisée $qAscii = $qLower; if (class_exists('Transliterator')) { $t = \Transliterator::create('Any-Latin; Latin-ASCII; Lower();'); if ($t) { $qAscii = $t->transliterate($qAscii); } } $cands[] = $qAscii; // Si saisie en cyrillique : on ajoute les villes latines correspondantes if (has_cyrillic($q)) { $key = norm_key($q); foreach ($aliases as $ru => $lat) { if (strpos(norm_key($ru), $key) === 0) { $cands[] = mb_strtolower($lat, 'UTF-8'); $cands[] = mb_strtolower($ru, 'UTF-8'); } } $cands[] = translit_ru_to_lat($q); } // Si saisie en latin : on cherche aussi les correspondances RU depuis les aliases foreach ($aliases as $ru => $lat) { $latLower = mb_strtolower($lat, 'UTF-8'); $latAscii = $latLower; if (class_exists('Transliterator')) { $t = \Transliterator::create('Any-Latin; Latin-ASCII; Lower();'); if ($t) { $latAscii = $t->transliterate($latAscii); } } if ( strpos($latLower, $qLower) === 0 || strpos($latAscii, $qAscii) === 0 ) { $cands[] = mb_strtolower($ru, 'UTF-8'); $cands[] = $latLower; } } $res = []; foreach (array_unique($cands) as $c) { $res[] = mb_strtolower(trim($c), 'UTF-8'); } return array_values(array_unique(array_filter($res))); } function seo_slug(string $s): string { $s = trim(mb_strtolower($s, 'UTF-8')); if ($s === '') { return ''; } if (has_cyrillic($s)) { $aliases = @include __DIR__ . '/city_aliases_ru.php'; if (is_array($aliases)) { foreach ($aliases as $ru => $lat) { if (norm_key($ru) === norm_key($s)) { $s = $lat; break; } } } if (has_cyrillic($s)) { $s = translit_ru_to_lat($s); } } if (class_exists('Transliterator')) { $t = \Transliterator::create('Any-Latin; Latin-ASCII; Lower();'); if ($t) { $s = $t->transliterate($s); } } $s = str_replace(['œ', 'æ'], ['oe', 'ae'], $s); $s = preg_replace('/[^\p{L}\p{N}]+/u', '-', $s); $s = trim($s, '-'); $s = preg_replace('/-+/u', '-', $s); return $s; } function seo_unslug(string $s): string { $s = trim(mb_strtolower($s, 'UTF-8')); $s = str_replace('-', ' ', $s); $s = preg_replace('/\s+/u', ' ', $s); return trim($s); } function seo_spec_map(): array { return [ 'psychologue' => [ 'db' => 'психолог', 'search' => [ 'ru' => 'психолог', 'fr' => 'psychologue', 'en' => 'psychologist', ], 'inputs' => ['psychologue', 'psychologues', 'психолог', 'психологи'], ], 'manager' => [ 'db' => 'менеджер', 'search' => [ 'ru' => 'менеджер', 'fr' => 'manager', 'en' => 'manager', ], 'inputs' => ['it manager', 'it-manager', 'manager it', 'responsable it', 'it-менеджер', 'ит менеджер'], ], 'construction' => [ 'db' => 'строитель', 'search' => [ 'ru' => 'строитель', 'fr' => 'construction', 'en' => 'construction', ], 'inputs' => [ 'строительная компания', 'строительная фирма', 'строительство', 'construction', 'entreprise de construction', 'bâtiment', 'btp', ], ], 'pompes-funebres' => [ 'db' => 'похоронное', 'search' => [ 'ru' => 'похоронное', 'fr' => 'pompes funebres', 'en' => 'funeral services', ], 'inputs' => [ 'похоронное агентство', 'похоронное бюро', 'ритуальные услуги', 'ритуальное агентство', 'pompes funebres', 'pompes-funebres', 'services funeraires', 'funerailles', ], ], 'avocat' => [ 'db' => 'адвокат', 'search' => [ 'ru' => 'адвокат', 'fr' => 'avocat', 'en' => 'lawyer', ], 'inputs' => ['avocat', 'avocats', 'адвокат', 'адвокаты'], ], 'medecin' => [ 'db' => 'врач', 'search' => [ 'ru' => 'врач', 'fr' => 'medecin', 'en' => 'doctor', ], 'inputs' => ['medecin', 'medecins', 'médecin', 'médecins', 'врач', 'врачи', 'Врач - стоматолог', 'врач-стоматолог'], ], 'consultant' => [ 'db' => 'консультант', 'search' => [ 'ru' => 'консультант', 'fr' => 'consultant', 'en' => 'consultant', ], 'inputs' => ['consultant', 'consultants', 'консультант', 'консультанты', 'Консультант'], ], 'professeur' => [ 'db' => 'преподаватель', 'search' => [ 'ru' => 'преподаватель', 'fr' => 'professeur', 'en' => 'teacher', ], 'inputs' => ['professeur', 'professeurs', 'pedagogue', 'pedagogues', 'преподаватель', 'преподаватели', 'педагог', 'педагоги'], ], 'comptable' => [ 'db' => 'бухгалтер', 'search' => [ 'ru' => 'бухгалтер', 'fr' => 'comptable', 'en' => 'accountant', ], 'inputs' => ['comptable', 'comptables', 'бухгалтер', 'бухгалтеры'], ], 'traducteur' => [ 'db' => 'переводчик', 'search' => [ 'ru' => 'переводчик', 'fr' => 'traducteur', 'en' => 'translator', ], 'inputs' => ['traducteur', 'traducteurs', 'переводчик', 'переводчики'], ], 'notaire' => [ 'db' => 'нотариус', 'search' => [ 'ru' => 'нотариус', 'fr' => 'notaire', 'en' => 'notary', ], 'inputs' => ['notaire', 'notaires', 'нотариус', 'нотариусы'], ], 'guide' => [ 'db' => 'гид', 'search' => [ 'ru' => 'гид', 'fr' => 'guide', 'en' => 'guide', ], 'inputs' => ['guide', 'guides', 'гид', 'гиды'], ], 'agent-immobilier' => [ 'db' => 'риелтор', 'search' => [ 'ru' => 'риелтор', 'fr' => 'agent immobilier', 'en' => 'real estate agent', ], 'inputs' => [ 'Agent immobilier', 'agent immobilier', 'agent-immobilier', 'agents immobiliers', 'agents-immobiliers', 'риелтор', 'риелторы', ], ], 'autres-specialistes' => [ 'db' => '__OTHERS__', 'inputs' => ['autres specialistes', 'autres-specialistes', 'другие', 'другие специалисты'], ], ]; } function seo_spec_slug_from_input(string $input): ?string { $inputSlug = seo_slug($input); foreach (seo_spec_map() as $slug => $cfg) { foreach (($cfg['inputs'] ?? []) as $candidate) { if ($inputSlug === seo_slug((string) $candidate)) { return $slug; } } } return null; } function seo_spec_db_from_slug(string $slug): ?string { $slug = seo_slug($slug); $map = seo_spec_map(); return $map[$slug]['db'] ?? null; } function seo_city_slug_from_input(string $input): string { $input = trim($input); if ($input === '') { return ''; } $aliases = @include __DIR__ . '/city_aliases_ru.php'; if (! is_array($aliases)) { $aliases = []; } $key = norm_key($input); foreach ($aliases as $ru => $lat) { if (norm_key($ru) === $key) { return seo_slug((string) $lat); } } return seo_slug($input); } function seo_spec_slug_from_key(string $key): ?string { $map = [ 'avocats' => 'avocat', 'consultants' => 'consultant', 'vrachi' => 'medecin', 'psychologues' => 'psychologue', 'repetitori' => 'professeur', 'finance' => 'comptable', 'perevod' => 'traducteur', 'notarius' => 'notaire', 'gid' => 'guide', 'rieltori' => 'agent-immobilier', 'drugie' => 'autres-specialistes', ]; return $map[$key] ?? null; } function seo_spec_search_value_from_slug(string $slug, string $lang): ?string { $slug = seo_slug($slug); $lang = mb_strtolower(trim($lang), 'UTF-8'); $map = seo_spec_map(); if (! isset($map[$slug])) { return null; } $cfg = $map[$slug]; if (isset($cfg['search'][$lang]) && trim((string) $cfg['search'][$lang]) !== '') { return trim((string) $cfg['search'][$lang]); } return $cfg['db'] ?? null; }