Коротко: Казахский требует utf8mb4 в MySQL, charset=UTF-8 в HTTP-заголовках, шрифты Noto Sans с cyrillic-ext подмножеством, и lang="kk" в HTML. Самая частая ошибка - использование utf8 вместо utf8mb4 в MySQL: utf8 в MySQL - это не настоящий UTF-8, он не хранит символы выше U+FFFF.
Казахские буквы и Unicode
Кириллический казахский алфавит (введён в СССР, используется в Казахстане) содержит дополнительные символы:
| Буква | Unicode | Описание |
|---|---|---|
| Ә/ә | U+04D8/U+04D9 | Ae |
| Ғ/ғ | U+0492/U+0493 | Ge with stroke |
| Қ/қ | U+049A/U+049B | Ka with descender |
| Ң/ң | U+04A2/U+04A3 | En with descender |
| Ө/ө | U+04E8/U+04E9 | O with stroke |
| Ұ/ұ | U+04B0/U+04B1 | Straight U with stroke |
| Ү/ү | U+04AE/U+04AF | Straight U |
| Ҳ/ҳ | U+04B2/U+04B3 | Ha with descender |
| І/і | U+0406/U+0456 | Dotted I |
Шаг 1: MySQL - utf8mb4
-- Проверить текущую кодировку
SHOW VARIABLES LIKE 'character_set_%';
SHOW VARIABLES LIKE 'collation_%';
-- Если не utf8mb4 - менять на уровне сервера
-- /etc/mysql/my.cnf:
-- [client]
-- default-character-set = utf8mb4
-- [mysql]
-- default-character-set = utf8mb4
-- [mysqld]
-- character-set-client-handshake = FALSE
-- character-set-server = utf8mb4
-- collation-server = utf8mb4_unicode_ci
-- Создать БД правильно:
CREATE DATABASE myapp_kz
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- Конвертировать существующую таблицу:
ALTER TABLE articles
CONVERT TO CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- Проверить конкретный столбец:
ALTER TABLE articles
MODIFY COLUMN title VARCHAR(500)
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci NOT NULL;
Шаг 2: PHP - подключение с utf8mb4
<?php
// Соединение PDO с правильной кодировкой
$dsn = 'mysql:host=localhost;dbname=myapp_kz;charset=utf8mb4';
$pdo = new PDO($dsn, $user, $password, [
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci",
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
// Проверка - вставить и прочитать казахский текст
$stmt = $pdo->prepare("INSERT INTO test_kaz (text) VALUES (?)");
$stmt->execute(['Қазақстан Республикасы - Астана']);
$result = $pdo->query("SELECT text FROM test_kaz LIMIT 1")->fetchColumn();
echo $result; // Должно быть: Қазақстан Республикасы - Астана
// Если выводятся ??? или ??? - проблема с кодировкой на каком-то уровне
Шаг 3: PHP - правильные заголовки
<?php
// Первое что делает скрипт - установить заголовки
header('Content-Type: text/html; charset=UTF-8');
header('Content-Language: kk'); // kk = казахский ISO 639-1
// Для JSON API:
header('Content-Type: application/json; charset=UTF-8');
; php.ini - глобальная настройка
default_charset = "UTF-8"
mbstring.internal_encoding = UTF-8
mbstring.http_output = UTF-8
Шаг 4: HTML - правильная разметка
<!DOCTYPE html>
<html lang="kk"> <!-- kk = казахский -->
<head>
<meta charset="UTF-8">
<!-- Шрифт с поддержкой казахских символов -->
<link href="https://fonts.googleapis.com/css?family=Noto+Sans:400,700&subset=cyrillic-ext" rel="stylesheet">
<!-- Для многоязычного сайта: hreflang -->
<link rel="alternate" hreflang="kk" href="https://mysite.kz/kk/article/1">
<link rel="alternate" hreflang="ru" href="https://mysite.kz/ru/article/1">
<link rel="alternate" hreflang="x-default" href="https://mysite.kz/article/1">
</head>
<body>
Шаг 5: nginx - charset в заголовках
server {
# Добавить charset для text/html
charset utf-8;
charset_types text/html text/css application/json application/javascript;
# Для PHP-FPM убедиться что fastcgi передаёт правильную кодировку
location ~ \.php$ {
fastcgi_pass unix:/var/run/php5-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Не переопределять charset если PHP устанавливает сам через header()
}
}
Работа с казахским текстом в PHP
<?php
// Используйте mb_* функции вместо строковых для казахского текста
// НЕПРАВИЛЬНО:
$length = strlen("Қазақстан"); // 17 (байты, не символы!)
$upper = strtoupper("қазақстан"); // Не работает для нелатинских
// ПРАВИЛЬНО:
$length = mb_strlen("Қазақстан", 'UTF-8'); // 9 (символы)
$upper = mb_strtoupper("қазақстан", 'UTF-8'); // ҚАЗАҚСТАН
$lower = mb_strtolower("ҚАЗАҚСТАН", 'UTF-8'); // қазақстан
$substr = mb_substr("Астана қаласы", 0, 6, 'UTF-8'); // Астана
// Поиск по казахскому тексту
$pos = mb_strpos("Қазақстан", "зақ", 0, 'UTF-8'); // 2
// Транслитерация для URL-slug
function kazakhSlug(string $text): string {
$translit = [
'Ә' => 'ae', 'ә' => 'ae',
'Ғ' => 'gh', 'ғ' => 'gh',
'Қ' => 'q', 'қ' => 'q',
'Ң' => 'ng', 'ң' => 'ng',
'Ө' => 'oe', 'ө' => 'oe',
'Ұ' => 'u', 'ұ' => 'u',
'Ү' => 'u', 'ү' => 'u',
'Ҳ' => 'h', 'ҳ' => 'h',
'І' => 'i', 'і' => 'i',
];
$text = strtr($text, $translit);
$text = mb_strtolower($text, 'UTF-8');
// Кириллица → транслит (Iconv)
$text = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text);
$text = preg_replace('/[^a-z0-9\-]/', '-', $text);
$text = preg_replace('/-+/', '-', $text);
return trim($text, '-');
}
echo kazakhSlug("Қазақстан Республикасы"); // qazaqstan-respublikasy
Полнотекстовый поиск по казахскому
-- MySQL 5.6+ поддерживает полнотекстовый поиск для utf8mb4
-- Но казахский стеммер не встроен - используем LIKE для 2015 года
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(500),
body TEXT,
lang CHAR(2) DEFAULT 'kk',
FULLTEXT INDEX ft_title_body (title, body)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Поиск через LIKE (медленнее но надёжнее для казахского в 2015)
SELECT * FROM articles
WHERE lang = 'kk'
AND (title LIKE '%қазақ%' OR body LIKE '%қазақ%')
ORDER BY created_at DESC
LIMIT 20;
Типичная ошибка: кириллица отображается, казахские буквы - нет
Причина: шрифт загружается без cyrillic-ext подмножества.
<!-- НЕПРАВИЛЬНО - нет cyrillic-ext -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700" rel="stylesheet">
<!-- ПРАВИЛЬНО - с расширенной кириллицей -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700&subset=cyrillic,cyrillic-ext" rel="stylesheet">
После этого Ә, Ғ, Қ, Ң, Ө, Ұ, Ү, Ҳ, І отображаются корректно.