Google 允许商品标题长度为 1 到 150 个字符,并要求商户不要用大写字母来强调(Merchant Center 帮助)。从 ERP 系统导出的土耳其语商品目录常常全部使用大写,所以最直接的办法就是设一条规则,把每个标题转成小写或首字母大写。但土耳其语数据恰恰会在这条规则上悄悄出错。在我们的测试中,对 İZMİR ÇİZGİLİ KIŞLIK MONT 执行默认的小写转换,返回的是 30 个码位,而不是 25 个;ISPARTA 变成了 isparta,而不是 ısparta。本指南面向处理土耳其语商品文本的 feed 管理人员和开发者。读完后,您将能够测试自己的技术栈、修正大小写规则,并添加一项在每次同步时都能发现该问题的检查。
为什么土耳其语的大小写会让普通规则失效
土耳其语有两组独立的字母对:无点的 ı 对应大写 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。阿塞拜疆语遵循同样的规则。
在 feed 中会出现三类问题:
- 字母错误。
KADIN变成kadin,YAĞI变成yaği。单词仍然可以读懂,但拼写是错的。 - 多出字符。
İZMİR变成i̇zmi̇r,这个字符串多出两个看起来不可见的码位。它在屏幕上看着几乎正确,却无法在筛选、关联或搜索中与izmir匹配。 - 反方向转换时大写错误。
istanbul变成ISTANBUL而不是İSTANBUL,首字母大写则得到Iç而不是İç。
第 1 步:弄清您的技术栈目前的行为
在修改任何规则之前,先检查转换标题的代码是如何运行的。我们把同样两个输入交给大多数 feed 管道使用的运行时处理:小写测试输入为 ISPARTA İSTANBUL,大写测试输入为 istanbul ışık。
| 运行时与调用 | 小写结果 | 大写结果 | 符合土耳其语规则? |
|---|---|---|---|
JavaScript toLowerCase() / toUpperCase() | isparta i̇stanbul | ISTANBUL IŞIK | 否 |
JavaScript toLocaleLowerCase("tr") / toLocaleUpperCase("tr") | ısparta istanbul | İSTANBUL IŞIK | 是 |
Python 3.13 str.lower() / str.upper() | isparta i̇stanbul | ISTANBUL IŞIK | 否 |
PHP 8.3 mb_strtolower() | i̇stanbul(针对 İSTANBUL) | 不适用 | 否 |
PHP 8.3 strtolower() | isparta(针对 ISPARTA) | 不适用 | 否 |
Perl lc() / uc() | isparta i̇stanbul | ISTANBUL IŞIK | 否 |
有两个细节值得注意。第一,JavaScript 中不带参数的 toLocaleLowerCase() 使用的是默认区域设置,因此同样的代码在服务器和笔记本电脑上的表现可能不同。文档显示,"İ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 ML | isparta gül yaği 50 ml | ısparta gül yağı 50 ml |
İZMİR ÇİZGİLİ KIŞLIK MONT | i̇zmi̇r çi̇zgi̇li̇ kişlik mont | izmir çizgili kışlık mont |
KADIN İÇ ÇAMAŞIRI SETİ | kadin i̇ç çamaşiri seti̇ | kadın iç çamaşırı seti |
IŞIK GEÇİRMEZ PERDE | iş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 步:单独处理首字母大写和单位
首字母大写会带来两个额外问题。把每个单词的首字母大写时必须使用土耳其语的大写规则,否则在 Python 的 title() 中,iç çamaşırı 会变成 Iç Çamaşırı。另外,一刀切的规则还会把单位也大写: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 管理工具中维护这些规则,请检查它是如何处理大小写的。Feedance 的feed 规则提供了全部大写、句首大写和首字母大写等选项,但页面并未说明它们使用哪种区域设置,所以对于任何工具(包括我们自己的),我们都会先用这套测试集跑一遍,再放心用于土耳其语文本。关于什么时候规则就够用、什么时候值得使用 AI 模型,请参阅我们对基于规则与基于 AI 的商品标题的比较。
第 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 等词不会被转为首字母大写 |
| 已应用品牌排除 | 列表中的品牌保留其预期的大写 |
| 长度不变 | 输出的码位数不多于输入,且不超过 150 |
长度检查可以发现默认小写转换额外添加的码位。即使您还不知道是哪条规则导致了变化,它也是一个方便的预警机制。
本方法的局限
这一方法适用于土耳其语和阿塞拜疆语,也就是 Unicode 文件中列出此规则的两种语言。它不涵盖阿拉伯文字字段;相关内容请参阅如何组织阿拉伯语和英语商品数据。
我们没有证据表明 Google 或任何电商平台会因为带点或无点的 i 写错而拒绝某个商品。有据可查的代价是间接的:错拼的标题展示在购物者面前,以及字符串不再与您的筛选、关联和站内搜索相匹配。请将其视为数据质量问题,而不是政策问题。
第 3 步中的替换技巧可以处理这四个特殊字母,但它并不是 Unicode 大小写规则的完整实现。基于 ICU 的库,以及提供区域设置选项的工具,比手写的封装能覆盖更多边缘情况。如果您使用的工具已经有正确的土耳其语区域选项,请直接使用,只保留测试集即可。我们没有测试过 Channable、DataFeedWatch 或 Productsup 如何处理土耳其语区域设置,因此不做任何判断;请像对待其他工具一样,用测试集对它们进行验证。各电商平台在此之外还有自己的标题规则,详见我们关于 Akakçe、Cimri、N11 和 ÇiçekSepeti feed 的指南。
常见问题
为什么 İZMİR 转小写后会变成 i̇zmi̇r?
默认的 Unicode 小写转换会把带点的大写 İ 映射为普通的 i 加一个组合上点(U+0307)。土耳其语规则则把 İ 映射为普通的 i。结果在屏幕上看起来几乎正确,但包含多余的码位。
Google Merchant Center 会拒绝带有组合点的标题吗?
我们没有找到相关的拒绝记录。该字符串是有效的 UTF-8,因此不会触发“Invalid UTF-8 encoding”错误。危害在于标题拼写错误,以及在您自己的工具中匹配失败。
标题应该使用全部大写、全部小写还是首字母大写?
Google 表示不要用大写字母来强调,并且要把最重要的信息放在前面,因为用户通常只能看到前 70 个或更少的字符。使用正确土耳其语字母的句首大写或首字母大写都符合这一要求。哪一种转化效果更好,需要在您自己的账户中测试。
在 JavaScript 中使用 toLowerCase("tr") 就够了吗?
不够。toLowerCase() 不接受区域设置参数。请使用 toLocaleLowerCase("tr") 并显式传入区域设置,因为默认值取决于运行环境。
为什么 setlocale 无法修复 PHP 的 strtolower?
自 PHP 8.2 起,strtolower() 会忽略区域设置,只转换 ASCII 字母。mb_strtolower() 可以处理非 ASCII 文本,但在我们的测试中,对 İSTANBUL 仍然返回 i̇stanbul。请在调用它之前先应用第 3 步中的字符替换。
Unicode 规范化能去掉多余的点吗?
不能。在我们的测试中,NFC 规范化后 i 加 U+0307 仍然是两个码位。请显式删除该字符,然后对原始源文本重新运行修正后的大小写规则。
这会影响阿拉伯语或其他语言吗?
该规则适用于土耳其语和阿塞拜疆语。其他语言有各自的大小写和规范化问题,本指南不涉及。
多少行测试数据才够?
足以覆盖每个土耳其语特有字母及其可能出现的每个位置:词首、词中、词尾,以及紧邻数字的位置。对大多数商品目录来说,这意味着 10 到 15 个标题。每当生产环境中出现新的缺陷,就增加一行。
检查您自己的 feed
如果您想了解当前 feed 在标题和属性质量方面的得分,请使用我们的免费商品 feed 审核工具进行检测。

Feedance 联合创始人,凭借 20 多年的产品与用户体验设计经验,塑造商品目录转化为创意的方式。她专注于打通设计系统与自动化,致力于帮助品牌在不增加设计投入的情况下规模化提升视觉质量。
LinkedIn












