NewProduct Score, 4T Segmentation, Price War and Broken Size are live.See what's new
Feedance
Article

Lowercasing Turkish Product Titles Without Breaking İ and ı: A Feed Rule Playbook

Naive lowercase turns İZMİR into i̇zmi̇r and ISPARTA into isparta. Test, fix and lock Turkish casing in feed title rules with a 5-row test set.

Inci DindarInci Dindar
9 min read
Product tags with one misplaced dot being inspected by a magnifier, illustrating Turkish casing errors in feed titles

Google allows a product title of 1 to 150 characters and tells merchants not to use capital letters for emphasis (Merchant Center Help). Turkish catalogs exported from ERP systems are often written in capitals, so the obvious fix is a rule that lowercases or title-cases every title. That rule is where Turkish data quietly breaks. In our test, a default lowercase of İZMİR ÇİZGİLİ KIŞLIK MONT returned 30 code points instead of 25, and ISPARTA became isparta instead of ısparta. This guide is for feed managers and developers who handle Turkish product text. By the end you will be able to test your stack, fix the casing rule, and add a check that catches the problem on every sync.

Why Turkish casing breaks ordinary rules

Turkish has two separate letter pairs: dotless ı with capital I, and dotted i with capital İ. The Unicode case-mapping file states it directly: "I and i-dotless; I-dot and i are case pairs in Turkish and Azeri" (Unicode SpecialCasing.txt).

Default (language-neutral) casing does not know this. It maps I to i, and it maps İ to i plus a combining dot above (U+0307). The Turkish rules in the same file map I to ı and İ to a plain i. Azerbaijani follows the same rule.

Three things go wrong in a feed:

  • Wrong letters. KADIN becomes kadin, YAĞI becomes yaği. The word is still readable, but it is misspelled.
  • Extra characters. İZMİR becomes i̇zmi̇r, a string with two extra invisible-looking code points. It looks almost right on screen and does not match izmir in a filter, a join or a search.
  • Wrong capitals going the other way. istanbul becomes ISTANBUL instead of İSTANBUL, and title case produces Iç instead of İç.

Step 1: Find out what your stack does today

Before you change any rule, check how the code that transforms your titles behaves. We ran the same two inputs through the runtimes most feed pipelines use. The input was ISPARTA İSTANBUL for lowercase and istanbul ışık for uppercase.

Runtime and callLowercase resultUppercase resultTurkish-correct?
JavaScript toLowerCase() / toUpperCase()isparta i̇stanbulISTANBUL IŞIKNo
JavaScript toLocaleLowerCase("tr") / toLocaleUpperCase("tr")ısparta istanbulİSTANBUL IŞIKYes
Python 3.13 str.lower() / str.upper()isparta i̇stanbulISTANBUL IŞIKNo
PHP 8.3 mb_strtolower()i̇stanbul (for İSTANBUL)n/aNo
PHP 8.3 strtolower()isparta (for ISPARTA)n/aNo
Perl lc() / uc()isparta i̇stanbulISTANBUL IŞIKNo

Two details matter. First, JavaScript's plain toLocaleLowerCase() with no argument uses the default locale, so the same code can behave differently on a server and a laptop. The documentation shows "İstanbul".toLocaleLowerCase("en-US") returning i̇stanbul and toLocaleLowerCase("tr") returning istanbul (MDN). Always pass "tr" explicitly.

Second, PHP 8.2 changed strtolower() so that it no longer depends on setlocale() and converts only ASCII characters (PHP manual). Setting a Turkish locale will not change that on a current version.

Step 2: Build a test set of real titles

Take 10 to 15 titles from your own catalog that cover every Turkish-specific letter: İ, I, ı, i, Ş, Ğ, Ç, Ö, Ü. Include a title that starts with I, one that starts with İ, and one with a unit such as ML or GB. Here is a starting set, with the output of a default lowercase beside the correct one.

Source titleDefault lowercaseTurkish lowercase
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ı

Look at the last row. Only one letter differs, and a visual spot check of a few titles would easily miss it. That is why the test set has to cover every letter, and why you compare strings, not impressions.

Step 3: Fix the casing function

Where your tool lets you choose the locale, set it to tr. Where it does not, wrap the transformation yourself. In JavaScript or Node, the fix is one argument:

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

Python's standard string methods have no locale argument. A reliable workaround is to swap the four special letters before calling the default method. We tested this version against the first four rows above:

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

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

The order matters. Replace İ and I first, then lowercase. If you lowercase first, the information about which capital it was is already gone.

Step 4: Handle title case and units separately

Title case adds two problems. Capitalizing the first letter of each word must use the Turkish uppercase, otherwise iç çamaşırı becomes Iç Çamaşırı in Python's title(). And a blanket rule capitalizes units: 50 ML becomes 50 Ml.

The pattern we use is: lowercase the whole title with the Turkish function, uppercase the first letter of each word with the Turkish function, and skip any token that starts with a digit or appears on a short unit list (ml, gb, cm, kg). On ISPARTA GÜL YAĞI 50 ML this gives Isparta Gül Yağı 50 ml. Without the unit list, the same pipeline returns 50 Ml. Note that Isparta is correct: the city name starts with a dotless capital I.

Brand names are a separate case. A brand written in capitals on purpose (IKEA, HP) needs an exclusion list, because Google asks only that you avoid capitals for emphasis and still use them where appropriate, such as abbreviations (Merchant Center Help). Keep the list short and keep it in the same place as the rule.

If you maintain these rules in a feed management tool, check how it applies case. Feedance's feed rules list uppercase, sentence case and first-letter capitals as options, and the page does not state which locale they use, so we run this test set against any tool, ours included, before trusting it with Turkish text. For a broader view of when a rule is enough and when an AI model is worth it, see our comparison of rule-based and AI product titles.

Step 5: Detect leftover damage in existing data

If a bad rule already ran, the combining dot is now in your stored data. Unicode normalization does not remove it: in our test, i followed by U+0307 stayed two code points after NFC normalization, because there is no precomposed form to combine into. You have to find and remove it.

This check reports every title that contains the combining dot above:

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

After you re-run the corrected rule on İZMİR, the same check returns false. Run it on title, description, brand, product_type and any custom_label field, since casing rules are often applied to more than titles.

Separately, if a channel reports an encoding error, that is a different fault. Merchant Center's "Invalid UTF-8 encoding" page says the cause is a problem with the encoding of the text and tells you to declare or convert the file's encoding (Merchant Center Help). The casing bug produces valid UTF-8, so it will not trigger that error. It passes silently, which is the reason to test for it.

Step 6: Make it a gate on every sync

A fix that nobody re-checks will regress the first time someone adds a rule or switches tools. Use this checklist as a release gate for any change to title, description or category-name rules.

CheckPass condition
Locale is explicitEvery case function is called with tr or wrapped as above
Test set passesAll rows match the Turkish-correct output character for character
No combining dotZero fields contain U+0307
Units preservedTokens such as ml, gb, cm are not title-cased
Brand exclusions appliedListed brands keep their intended capitals
Length unchangedOutput is not longer than input in code points, and stays within 150

The length check catches the extra code points that the default lowercase adds. It is a convenient tripwire even if you do not know yet which rule caused the change.

Where this breaks down

This is a method for Turkish and Azerbaijani, the two languages the Unicode file lists for this rule. It does not cover Arabic-script fields; for those, see how to structure Arabic and English product data.

We have no evidence that Google or any marketplace disapproves a product because of a wrong dotted or dotless i. The documented costs are indirect: misspelled titles in front of shoppers, and strings that no longer match your filters, joins and internal search. Treat it as a data quality fault, not a policy one.

The substitution trick in Step 3 handles the four special letters, but it is not a full implementation of the Unicode casing rules. Libraries built on ICU, and tools that expose a locale setting, cover more edge cases than a hand-written wrapper. If a tool you use already has a correct Turkish locale option, use it and keep only the test set. We have not tested how Channable, DataFeedWatch or Productsup handle the Turkish locale, so we make no claim either way; run the test set against them as you would against any tool. Marketplaces have their own title rules on top of this, covered in our guides to Akakçe, Cimri, N11 and ÇiçekSepeti feeds.

FAQ

Why does İZMİR become i̇zmi̇r when I lowercase it?

Default Unicode lowercasing maps the dotted capital İ to a regular i plus a combining dot above (U+0307). The Turkish rule maps İ to a plain i. The result looks nearly right on screen but contains extra code points.

Does Google Merchant Center reject titles with the combining dot?

We found no documented rejection for it. The string is valid UTF-8, so it does not trigger the "Invalid UTF-8 encoding" error. The harm is misspelled titles and failed matching in your own tools.

Should I send titles in capitals, lowercase or title case?

Google says not to use capital letters for emphasis and to put the most important details first, since users usually see only the first 70 or fewer characters. Sentence case or title case with correct Turkish letters both satisfy that. Which one converts better is a test for your own account.

Is toLowerCase("tr") enough in JavaScript?

No. toLowerCase() takes no locale. Use toLocaleLowerCase("tr") and pass the locale explicitly, because the default depends on the environment.

Why doesn't setlocale fix PHP's strtolower?

Since PHP 8.2, strtolower() ignores the locale and converts only ASCII letters. mb_strtolower() handles non-ASCII text but, in our test, still returned i̇stanbul for İSTANBUL. Apply the character substitution from Step 3 before calling it.

Can Unicode normalization remove the stray dot?

No. In our test, NFC left i plus U+0307 as two code points. Remove the character explicitly, then re-run the corrected casing rule on the original source text.

Does this affect Arabic or other languages?

The rule applies to Turkish and Azerbaijani. Other languages have their own casing and normalization issues, which this guide does not cover.

How many test rows are enough?

Enough to cover every Turkish-specific letter and each position it can take: start of word, middle, end, and next to a digit. For most catalogs that is 10 to 15 titles. Add a row whenever a new defect shows up in production.

Check your own feed

If you want to see how your current feed scores on title and attribute quality, run it through our free product feed audit tool.

Want a second pair of eyes on your feed? We will review it with you and show where the gaps are before your next submission.
Run a free audit
Inci Dindar
Written by
Inci Dindar

Co-founder of Feedance, where she draws on 20+ years in product and user experience design to shape how catalogs become creative. With a strong focus on bridging design systems and automation, she is dedicated to helping brands scale visual quality without scaling design effort.

LinkedIn

See which
products earn.

Connect your feed and GA4. We show Product Score, 4T, Price War and Broken Size on your own catalog.