НовоеProduct Score, 4T Segmentation, Price War и Broken Size уже доступны.Узнать, что нового
Feedance
Статья

Как перевести турецкие названия товаров в нижний регистр, не сломав İ и ı: практическое руководство по правилам фида

Обычный lowercase превращает İZMİR в i̇zmi̇r, а ISPARTA в isparta. Протестируйте и исправьте регистр турецких заголовков в правилах фида.

Inci DindarInci Dindar
9 минут чтения
Product tags with one misplaced dot being inspected by a magnifier, illustrating Turkish casing errors in feed titles

Google допускает название товара длиной от 1 до 150 символов и просит продавцов не использовать заглавные буквы для выделения (Справка Merchant Center). Турецкие каталоги, выгруженные из ERP-систем, часто набраны заглавными буквами, поэтому очевидное решение — правило, которое переводит все названия в нижний регистр или приводит их к виду Title Case. Именно на этом правиле турецкие данные незаметно ломаются. В нашем тесте стандартный перевод в нижний регистр строки İZMİR ÇİZGİLİ KIŞLIK MONT вернул 30 кодовых точек вместо 25, а ISPARTA превратилось в isparta вместо ısparta. Это руководство для фид-менеджеров и разработчиков, которые работают с турецкими текстами товаров. К концу вы сможете проверить свой стек, исправить правило регистра и добавить проверку, которая будет находить проблему при каждой синхронизации.

Почему турецкий регистр ломает обычные правила

В турецком алфавите две отдельные пары букв: безточечная ı с заглавной I и точечная i с заглавной İ. Файл Unicode с правилами преобразования регистра говорит об этом прямо: «I and i-dotless; I-dot and i are case pairs in Turkish and Azeri» (Unicode SpecialCasing.txt).

Стандартное (не зависящее от языка) преобразование регистра об этом не знает. Оно превращает I в i, а İ — в i плюс комбинируемую точку сверху (U+0307). Турецкие правила из того же файла превращают I в ı, а İ — в обычную i. Азербайджанский язык следует тому же правилу.

В фиде при этом возникают три проблемы:

  • Неверные буквы. KADIN превращается в kadin, YAĞI — в yaği. Слово по-прежнему читается, но написано с ошибкой.
  • Лишние символы. İZMİR превращается в i̇zmi̇r — строку с двумя дополнительными кодовыми точками, которые визуально незаметны. На экране она выглядит почти правильно, но не совпадает с izmir в фильтре, соединении таблиц или поиске.
  • Неверные заглавные буквы при обратном преобразовании. istanbul превращается в ISTANBUL вместо İSTANBUL, а Title Case дает Iç вместо İç.

Шаг 1. Выясните, что делает ваш стек сейчас

Прежде чем менять какие-либо правила, проверьте, как ведет себя код, который преобразует ваши названия. Мы прогнали одни и те же два входных значения через среды выполнения, которые чаще всего используются в фид-пайплайнах. Для нижнего регистра на вход подавалось ISPARTA İSTANBUL, для верхнего — istanbul ışık.

Среда и вызовРезультат в нижнем регистреРезультат в верхнем регистреКорректно для турецкого?
JavaScript toLowerCase() / toUpperCase()isparta i̇stanbulISTANBUL IŞIKНет
JavaScript toLocaleLowerCase("tr") / toLocaleUpperCase("tr")ısparta istanbulİSTANBUL IŞIKДа
Python 3.13 str.lower() / str.upper()isparta i̇stanbulISTANBUL IŞIKНет
PHP 8.3 mb_strtolower()i̇stanbul (для İSTANBUL)н/дНет
PHP 8.3 strtolower()isparta (для ISPARTA)н/дНет
Perl lc() / uc()isparta i̇stanbulISTANBUL IŞIKНет

Важны две детали. Во-первых, обычный toLocaleLowerCase() в JavaScript без аргумента использует локаль по умолчанию, поэтому один и тот же код может работать по-разному на сервере и на ноутбуке. В документации показано, что "İstanbul".toLocaleLowerCase("en-US") возвращает i̇stanbul, а toLocaleLowerCase("tr") — istanbul (MDN). Всегда передавайте "tr" явно.

Во-вторых, в PHP 8.2 функцию strtolower() изменили: она больше не зависит от setlocale() и преобразует только ASCII-символы (документация PHP). Установка турецкой локали на актуальной версии этого не изменит.

Шаг 2. Соберите тестовый набор из реальных названий

Возьмите из своего каталога 10–15 названий, которые охватывают все специфичные для турецкого буквы: İ, I, ı, i, Ş, Ğ, Ç, Ö, Ü. Включите название, которое начинается с I, одно, которое начинается с İ, и одно с единицей измерения, например ML или GB. Ниже — базовый набор: результат стандартного перевода в нижний регистр рядом с правильным.

Исходное названиеСтандартный нижний регистрТурецкий нижний регистр
ISPARTA GÜL YAĞI 50 MLisparta gül yaği 50 mlısparta gül yağı 50 ml
İZMİR ÇİZGİLİ KIŞLIK MONTi̇zmi̇r çi̇zgi̇li̇ kişlik montizmir çizgili kışlık mont
KADIN İÇ ÇAMAŞIRI SETİkadin i̇ç çamaşiri seti̇kadın iç çamaşırı seti
IŞIK GEÇİRMEZ PERDEişik geçi̇rmez perdeışık geçirmez perde
ÇOCUK OKUL ÇANTASIçocuk okul çantasiçocuk okul çantası

Посмотрите на последнюю строку. Отличается всего одна буква, и при визуальной проверке нескольких названий ее легко пропустить. Поэтому тестовый набор должен охватывать каждую букву, а сравнивать нужно строки, а не впечатления.

Шаг 3. Исправьте функцию преобразования регистра

Если ваш инструмент позволяет выбрать локаль, установите tr. Если не позволяет, оберните преобразование самостоятельно. В JavaScript или Node исправление сводится к одному аргументу:

title.toLocaleLowerCase("tr")
title.toLocaleUpperCase("tr")

У стандартных строковых методов Python нет аргумента локали. Надежный обходной путь — заменить четыре специальные буквы до вызова стандартного метода. Мы протестировали эту версию на первых четырех строках из таблицы выше:

def tr_lower(s):
    return s.replace("İ", "i").replace("I", "ı").lower()

def tr_upper(s):
    return s.replace("i", "İ").replace("ı", "I").upper()

Порядок важен. Сначала замените İ и I, затем переводите в нижний регистр. Если сначала перевести в нижний регистр, информация о том, какой именно была заглавная буква, будет уже потеряна.

Шаг 4. Отдельно обработайте Title Case и единицы измерения

Title Case добавляет две проблемы. Заглавная первая буква каждого слова должна формироваться по турецким правилам верхнего регистра, иначе iç çamaşırı превратится в Iç Çamaşırı при использовании title() в Python. Кроме того, универсальное правило делает заглавными единицы измерения: 50 ML становится 50 Ml.

Мы используем такую схему: переводим все название в нижний регистр турецкой функцией, затем делаем заглавной первую букву каждого слова турецкой функцией и пропускаем любой токен, который начинается с цифры или входит в короткий список единиц (ml, gb, cm, kg). Для ISPARTA GÜL YAĞI 50 ML это дает Isparta Gül Yağı 50 ml. Без списка единиц тот же пайплайн вернет 50 Ml. Обратите внимание, что Isparta — это правильный вариант: название города начинается с безточечной заглавной I.

Названия брендов — отдельный случай. Бренд, намеренно написанный заглавными буквами (IKEA, HP), требует списка исключений: Google просит лишь не использовать заглавные буквы для выделения, а там, где это уместно, например в аббревиатурах, их использовать по-прежнему можно (Справка Merchant Center). Держите этот список коротким и храните его там же, где и само правило.

Если вы поддерживаете такие правила в инструменте для управления фидами, проверьте, как он применяет регистр. В Feed Rules Feedance в качестве вариантов перечислены верхний регистр, регистр предложения и заглавные первые буквы, а на странице не указано, какую локаль они используют, поэтому мы прогоняем этот тестовый набор через любой инструмент, включая наш, прежде чем доверить ему турецкий текст. Более широкий взгляд на то, когда достаточно правила, а когда стоит подключать ИИ-модель, — в нашем сравнении названий товаров на основе правил и на основе ИИ.

Шаг 5. Найдите остаточные повреждения в существующих данных

Если неверное правило уже отработало, комбинируемая точка теперь хранится в ваших данных. Нормализация Unicode ее не удаляет: в нашем тесте i с последующим U+0307 после нормализации NFC осталась двумя кодовыми точками, потому что предкомпонованной формы, в которую их можно объединить, не существует. Ее нужно найти и удалить.

Эта проверка выводит каждое название, содержащее комбинируемую точку сверху:

import re
bad = [t for t in titles if re.search("̇", t)]

После повторного запуска исправленного правила на İZMİR та же проверка возвращает false. Запускайте ее для title, description, brand, product_type и любых полей custom_label, поскольку правила регистра часто применяют не только к названиям.

Отдельно: если канал сообщает об ошибке кодировки, это другая неисправность. На странице Merchant Center «Invalid UTF-8 encoding» сказано, что причина — проблема с кодировкой текста, и предложено указать или преобразовать кодировку файла (Справка Merchant Center). Ошибка с регистром дает корректный UTF-8, поэтому эту ошибку она не вызовет. Она проходит незаметно — именно поэтому ее нужно проверять специально.

Шаг 6. Сделайте проверку обязательным условием при каждой синхронизации

Исправление, которое никто не перепроверяет, откатится назад, как только кто-то добавит правило или перейдет на другой инструмент. Используйте этот чек-лист как условие выпуска для любых изменений в правилах для названий, описаний или названий категорий.

ПроверкаУсловие прохождения
Локаль указана явноКаждая функция регистра вызывается с tr или обернута, как описано выше
Тестовый набор пройденВсе строки совпадают с корректным турецким результатом символ в символ
Нет комбинируемой точкиНи одно поле не содержит U+0307
Единицы сохраненыТокены вроде ml, gb, cm не приводятся к Title Case
Исключения для брендов примененыБренды из списка сохраняют задуманные заглавные буквы
Длина не измениласьРезультат не длиннее входной строки в кодовых точках и укладывается в 150

Проверка длины ловит лишние кодовые точки, которые добавляет стандартный перевод в нижний регистр. Это удобный индикатор, даже если вы пока не знаете, какое именно правило вызвало изменение.

Где этот метод перестает работать

Метод предназначен для турецкого и азербайджанского — двух языков, которые файл Unicode называет для этого правила. Он не охватывает поля с арабской письменностью; о них см. руководство по структуре арабских и английских данных о товарах.

У нас нет доказательств того, что Google или какой-либо маркетплейс отклоняет товар из-за неверной точечной или безточечной i. Задокументированные издержки косвенные: названия с ошибками, которые видят покупатели, и строки, которые перестают совпадать с вашими фильтрами, соединениями таблиц и внутренним поиском. Относитесь к этому как к проблеме качества данных, а не к нарушению политики.

Прием с подстановкой из шага 3 обрабатывает четыре специальные буквы, но не является полной реализацией правил регистра Unicode. Библиотеки на основе ICU и инструменты с настройкой локали охватывают больше пограничных случаев, чем самодельная обертка. Если в инструменте, которым вы пользуетесь, уже есть корректная опция турецкой локали, используйте ее и оставьте только тестовый набор. Мы не проверяли, как Channable, DataFeedWatch и Productsup обрабатывают турецкую локаль, поэтому ничего не утверждаем ни в ту, ни в другую сторону; прогоните тестовый набор через них, как и через любой другой инструмент. Помимо этого, у маркетплейсов есть собственные правила для названий — они рассмотрены в наших руководствах по фидам для Akakçe, Cimri, N11 и ÇiçekSepeti.

FAQ

Почему İZMİR превращается в i̇zmi̇r при переводе в нижний регистр?

Стандартный перевод в нижний регистр в Unicode превращает точечную заглавную İ в обычную i плюс комбинируемую точку сверху (U+0307). Турецкое правило превращает İ в простую i. Результат на экране выглядит почти правильно, но содержит лишние кодовые точки.

Отклоняет ли Google Merchant Center названия с комбинируемой точкой?

Мы не нашли задокументированных случаев отклонения. Строка является корректным UTF-8, поэтому ошибку «Invalid UTF-8 encoding» она не вызывает. Вред состоит в названиях с ошибками и в сбоях сопоставления в ваших собственных инструментах.

В каком виде отправлять названия: заглавными буквами, строчными или в Title Case?

Google просит не использовать заглавные буквы для выделения и ставить самые важные сведения в начало, поскольку пользователи обычно видят только первые 70 символов или меньше. Регистр предложения или Title Case с правильными турецкими буквами удовлетворяют этому требованию. Какой вариант конвертирует лучше — это тест для вашего собственного аккаунта.

Достаточно ли toLowerCase("tr") в JavaScript?

Нет. toLowerCase() не принимает локаль. Используйте toLocaleLowerCase("tr") и передавайте локаль явно, потому что значение по умолчанию зависит от окружения.

Почему setlocale не исправляет strtolower в PHP?

Начиная с PHP 8.2, strtolower() игнорирует локаль и преобразует только ASCII-буквы. mb_strtolower() обрабатывает не-ASCII текст, но в нашем тесте все равно вернула i̇stanbul для İSTANBUL. Примените подстановку символов из шага 3 до вызова этой функции.

Может ли нормализация Unicode убрать лишнюю точку?

Нет. В нашем тесте NFC оставила i и U+0307 двумя кодовыми точками. Удалите символ явно, а затем повторно запустите исправленное правило регистра на исходном тексте.

Влияет ли это на арабский или другие языки?

Правило относится к турецкому и азербайджанскому. У других языков свои проблемы с регистром и нормализацией, которые в этом руководстве не рассматриваются.

Сколько строк в тестовом наборе достаточно?

Достаточно, чтобы охватить каждую специфичную для турецкого букву и каждую позицию, в которой она может стоять: начало слова, середина, конец и рядом с цифрой. Для большинства каталогов это 10–15 названий. Добавляйте строку каждый раз, когда в продакшене обнаруживается новый дефект.

Проверьте собственный фид

Если хотите узнать, как ваш текущий фид оценивается по качеству названий и атрибутов, прогоните его через наш бесплатный инструмент аудита товарного фида.

Хотите, чтобы кто-то ещё взглянул на ваш фид? Мы проверим его вместе с вами и покажем пробелы до следующей отправки.
Пройти бесплатный аудит
Inci Dindar
Автор
Inci Dindar

Сооснователь Feedance. Опираясь на более чем 20-летний опыт в продуктовом дизайне и проектировании пользовательского опыта, определяет, как каталоги превращаются в креативы. Уделяя особое внимание связке дизайн-систем и автоматизации, она помогает брендам повышать качество визуальных материалов без увеличения затрат на дизайн.

LinkedIn

Узнайте, какие
товары приносят доход.

Подключите фид и GA4. Мы покажем Product Score, 4T, Price War и Broken Size на вашем собственном каталоге.