406 lines
12 KiB
PHP
406 lines
12 KiB
PHP
<?php
|
||
|
||
function has_cyrillic(string $s): bool
|
||
{
|
||
return (bool) preg_match('/\p{Cyrillic}/u', $s);
|
||
}
|
||
|
||
function norm_key(string $s): string
|
||
{
|
||
$s = mb_strtolower(trim($s), 'UTF-8');
|
||
$s = str_replace(['ё'], ['е'], $s);
|
||
$s = preg_replace('/[\'"’\-\s]+/u', '', $s);
|
||
return $s;
|
||
}
|
||
|
||
// Translittération simple RU -> 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;
|
||
}
|