Bu konunun ilk sürümü, phpMyAdmin'de tek bir UPDATE sorgusuyla "doğrudan mesaj" metnini "özel mesaj" yapmayı öneriyordu. Sorgu hatasız çalışır, ama sitede çoğu zaman hiçbir şey değişmez; değişse bile dil paketinin bir sonraki güncellemesinde eski metin geri gelir. Çünkü XenForo sayfaları xf_phrase tablosundan değil, bu tablodan derlenen kopyalardan üretir. Aşağıda önce desteklenen yolları, en sonda da güvenli SQL yöntemini anlatıyoruz. Anlatılan davranışları XenForo 2.3 kaynak koduyla (2.3.13) kontrol ettik; kullandığımız panel etiketleri 2.2.13'te de aynı. Menü adları sürüme göre küçük farklar gösterebileceği için yönetim paneli (Admin CP) adreslerini de veriyoruz.
Kısa cevap: hangi yöntem ne zaman?
| Durum | Önerilen yol |
|---|---|
| Birkaç ifade (phrase) | Yönetim panelindeki ifade düzenleyici, sitede kullanılan dil seçiliyken |
| Bir terim onlarca ifadede geçiyor | Search phrases ile filtreleyip Translate sayfasında toplu düzenleme |
| Yüzlerce ifade, terminoloji değişikliği | Dili XML olarak dışa aktarma, düzenleme, alt dil olarak içe aktarma |
| Panele erişim yok veya acil durum | Yedek, SQL ve ardından tam yeniden derleme |
| Metin hiçbir ifadede bulunmuyor | Şablon, seçenek veya eklenti; aşağıdaki teşhise bakın |
phpMyAdmin ile yapacaksanız özet şu: dil kimliğini bulun, xf_phrase tablosunu yedekleyin, UPDATE sorgusunu language_id ile sınırlayıp her büyük/küçük harf yazımı için ayrı çalıştırın, ardından dili dışa aktarıp Overwrite language ile aynı dilin üzerine yükleyerek yeniden derlemeyi tetikleyin (panele erişiminiz yoksa 5. adımdaki geliştirici komutlarını kullanın).
XenForo'da ifadeler nasıl çalışır?
- Master dil (kimlik 0): XenForo'nun ve eklentilerin (add-on) özgün İngilizce metinleri burada durur. Geliştirici modu (development mode) kapalıyken panelden düzenlenemez. Yükseltmeler, eklenti güncellemeleri ve xf:rebuild-master-data bu metinleri dağıtım dosyalarından yeniden yazar; master dile SQL ile yazılan her şey ilk fırsatta sessizce eski hâline döner.
- Kalıtım: Her dil, kendisinde özelleştirilmemiş ifadeleri üst dilinden (parent) devralır. Bir çeviri paketi (translation pack) Türkçe metinleri kendi dilinde özelleştirilmiş ifadeler olarak saklar.
- Üç durum: Seçili dilde bir ifade değiştirilmemiş (master'dan gelir), bu dilde özelleştirilmiş ya da üst dilde özelleştirilip oradan devralınmış olabilir. Search phrases formundaki Phrase status filtresi bunları Unmodified, Modified in this language ve Modified in a parent language seçenekleriyle ayırır.
- Eklenti ifadeleri: Aynı sistemi kullanır, yalnızca addon_id farklıdır. Eklenti güncellemesi yalnızca master metni yeniler.
- Derlenmiş kopyalar: Sayfayı üreten kod xf_phrase tablosunu okumaz. xf_phrase_compiled tablosunu, genel (global) ifadeler için dil kaydındaki önbelleği, internal_data/code_cache altındaki ifade grubu dosyalarını ve derlenmiş şablonları kullanır. Şablonlarda adı sabit yazılmış ifadelerin metni, derleme sırasında şablon dosyasına gömülür. Panelden kaydetmek bunların hepsini günceller; ham bir UPDATE hiçbirine dokunmaz.
Metin nereden geliyor? Hesabınızın dilini kısa süreliğine İngilizceye çevirin. Metin değişiyorsa bir ifadedir. Değişmiyorsa şablona elle yazılmış bir metin (çoğunlukla temalarda), bir seçenek değeri (Setup > Options altındaki site adı gibi), bir forum bölümünün adı gibi bir içerik, bir duyuru (notice) ya da metni koduna gömmüş bir eklentidir.
İfadeyi bulun
- Appearance > Languages & phrases > Search phrases (admin.php?phrases/search) sayfasında sitede kullanılan dili seçin ve Text contains alanına "doğrudan mesaj" yazın. Bütün yazımları görmek için Case sensitive kutusunu boş, Phrase status altındaki üç seçeneği de işaretli bırakın.
- İfadenin adını (title) biliyorsanız Phrases listesinin üstündeki filtre kutusunu kullanın; kutu yalnızca ekrandaki sayfayı değil, bütün listeyi ada göre tarar.
- Sonuç çıkmazsa Appearance > Styles & templates > Search templates (admin.php?templates/search) ile şablon içeriğinde arayın; doğru stili ve şablon türünü (public, admin, email) seçin.
- Panele girmeden salt okunur bir kontrol için aşağıdaki sorgu, ziyaretçinin gördüğü derlenmiş metni tarar. Kalıtım zaten uygulanmış olduğundan metnin hangi dilde tanımlandığı fark etmez.
Code:
SELECT language_id, title, phrase_text
FROM xf_phrase_compiled
WHERE phrase_text LIKE '%doğrudan mesaj%'
ORDER BY language_id, title;
Şablona sabit yazılmış bir metni dile bağlamak için kendi ifadenizi ekleyip sitedeki her dilde metnini girin ve şablonda {{ phrase('aior_ozel_mesaj') }} biçiminde çağırın. Bu çağrıyı şablonu elle özelleştirerek değil, bir şablon değişikliğiyle (template modification) eklerseniz şablonun özelleştirilmiş bir kopyası oluşmaz; yükseltmelerden sonra güncellemeniz gereken eskimiş (outdated) bir şablon da olmaz.
Desteklenen yol: hedef dilde düzenleyin
- Appearance > Languages & phrases > Phrases sayfasındaki dil seçicide master dili değil, sitede kullanılan dili seçin.
- İfadeyi açın; düzenleyici karşılaştırma için master değerini de gösterir. {sender}, {name} gibi süslü parantezli değişkenleri silmeyin ve adlarını değiştirmeyin. XenForo kılavuzuna göre özelleştirilmiş ifadelerin yükseltmelerden sorunsuz çıkmasının koşulu budur.
- Kaydetmek o dilde özelleştirilmiş bir kopya oluşturur, master metne dokunmaz ve ifadeyi kullanan şablonları yeniden derler. Sayfayı yenileyince sonucu görürsünüz.
- Çok sayıda ifade için listenin üstündeki Refine and translate bağlantısıyla (veya Search phrases formuyla) filtreleyip Translate düğmesine basın; eşleşen ifadeler tek sayfada, 50'şerli gruplar hâlinde düzenlenebilir kutular olarak açılır (devamı için More).
- Geri almak için listede ifadenin yanındaki Revert ile o dildeki özelleştirmeyi silin; metin yeniden üst dilden gelir.
Çeviri paketi kullanıyorsanız alt dil kurun. Paket, yeni dosyanın Import ekranında Overwrite language seçeneğiyle mevcut dilin üzerine yüklenmesiyle güncellenir. XenForo bu sırada önce o dilin bütün ifadelerini (dosya tek bir eklentiye aitse yalnızca o eklentininkileri) siler, sonra dosyadakileri ekler; paketin kendi dilinde yaptığınız özelleştirmeler o anda kaybolur. Kalıcı düzen şöyle kurulur:
- Appearance > Languages & phrases > Languages (admin.php?languages) sayfasında üst dili paketin dili olan yeni bir dil ekleyin, örneğin "Türkçe (forum)", ve özelleştirmeleri yalnızca orada yapın.
- Paketin dili forumun varsayılan diliyse Setup > Options > Appearance altındaki Default language seçeneğiyle alt dili varsayılan yapın.
- Dil listesindeki anahtarla paketin dilini seçilemez yapın. Bu dili kullanan, yönetici olmayan üyeler otomatik olarak forumun varsayılan diline düşer; bu dil, yalnızca önceki adımı uyguladıysanız alt dildir. Varsayılan dili başka bir dil (örneğin İngilizce) olan iki dilli bir forumda bu üyeleri önce, xf_user tablosunu yedekledikten sonra, UPDATE xf_user SET language_id = 4 WHERE language_id = 3; ile alt dile taşıyın (3 paketin, 4 alt dilin kimliği). Yöneticiler kendi tercihlerini elle değiştirir.
Toplu değişiklik: XML ile dışa aktarın, düzenleyin, içe aktarın
- Languages sayfasında paketin dilini dışa aktarın (Export). Export from add-on alanını (All) olarak bırakın ve Get unmodified phrases kutusunu işaretlemeyin; dosyaya yalnızca o dilde tanımlı ifadeler girer.
- Bir kopyasını UTF-8 destekleyen bir düzenleyicide (VS Code, Notepad++ gibi) açın ve Match case açıkken her yazımı ayrı ayrı değiştirin. Özniteliklere, {değişkenlere} ve HTML etiketlerine dokunmayın. Metinde ]-]-> görürseniz düzeltmeyin: XenForo, CDATA içine yazılamayan ]]> dizisini dışa aktarırken bu biçime çevirir, içe aktarırken geri alır.
- Aşağıdaki betikle yalnızca değişen ifadeleri yeni başlıklı bir dosyaya yazın ve bu dosyayı Languages > Import (admin.php?languages/import) ekranında Import as: Child of language seçeneğinde paketin dilini seçerek içe aktarın. Bu adım alt dili oluşturur; alt dili yukarıdaki gibi zaten kurduysanız bir sonraki maddedeki yöntemi kullanın.
- Sonraki turlarda hem paketin dilini hem alt dili Get unmodified phrases işaretli olarak dışa aktarın, alt dilin dosyasını düzenleyin ve betiği ilk argümanda paketin dosyası, ikincide düzenlediğiniz alt dil dosyası olacak şekilde çalıştırın. Çıktıda paketten farklı olan her ifade, önceki özelleştirmeleriniz dahil, yer alır; bu dosyayı Overwrite language ile alt dilin üzerine yükleyin. Overwrite ile yüklenen dosya o dilin tamamı olmalıdır, yoksa dosyada olmayan özelleştirmeler silinir.
Python:
# delta.py: yalnızca değişen ifadeleri yeni bir dil dosyasına yazar
import sys
import xml.etree.ElementTree as ET
orijinal, duzenli, cikti, baslik = sys.argv[1:5]
eski = {p.get('title'): p.text or '' for p in ET.parse(orijinal).getroot().iter('phrase')}
kok = ET.parse(duzenli).getroot()
for p in kok.findall('phrase'):
if eski.get(p.get('title')) == (p.text or ''):
kok.remove(p)
kok.set('title', baslik)
ET.ElementTree(kok).write(cikti, encoding='utf-8', xml_declaration=True)
print(len(kok.findall('phrase')), 'ifade yazıldı')
Bash:
python3 delta.py language-Turkce.xml language-Turkce-duzenli.xml turkce-forum.xml "Türkçe (forum)"
Betik CDATA bölümlerini kaçışlı (escaped) metne çevirir; XML açısından ikisi aynıdır. Tam bir dil dosyasını alt dil olarak içe aktarmayın: alt dil her ifadeyi kopyalar ve paketin sonraki güncellemeleri görünmez olur. İçe aktarma, ifade ve şablon derlemesini (PhraseRebuild ve TemplateRebuild işleri) kendisi kuyruğa alır; ayrıca bir önbellek işi çalıştırmanız gerekmez.
Son çare: phpMyAdmin ve SQL
SQL'i yalnızca panele erişim yoksa ya da acil bir düzeltme gerekiyorsa kullanın. İki sınırı baştan bilin: Paketin dilinde SQL ile değiştirdiğiniz metin bir sonraki paket güncellemesinde eski hâline döner ve değişiklik, yeniden derleme tetiklenene kadar sitede görünmez. Birden çok tabloda arama ve değiştirme yöntemleri için SQL'de bir kelimeyi toplu bulma ve değiştirme konusuna bakabilirsiniz.1. Dil kimliğini bulun.
Code:
SELECT language_id, parent_id, title, language_code, user_selectable
FROM xf_language;
2. Yedek alın. phpMyAdmin'de soldan xf_phrase tablosunu seçip Export > Quick > SQL ile indirin. SSH ile:
Bash:
mysqldump --single-transaction --default-character-set=utf8mb4 -u KULLANICI -p VERITABANI xf_phrase > xf_phrase-$(date +%F-%H%M).sql
Code:
CREATE TABLE yedek_phrase_20261027 AS
SELECT language_id, title, phrase_text
FROM xf_phrase
WHERE language_id = 3;
3. Harmanlamayı kontrol edin ve önizleyin.
Code:
SHOW FULL COLUMNS FROM xf_phrase LIKE 'phrase_text';
SELECT phrase_id, title, phrase_text
FROM xf_phrase
WHERE language_id = 3
AND phrase_text LIKE '%doğrudan mesaj%';
SELECT
SUM(phrase_text COLLATE utf8mb4_bin LIKE '%doğrudan mesaj%') AS kucuk,
SUM(phrase_text COLLATE utf8mb4_bin LIKE '%Doğrudan mesaj%') AS cumle_basi,
SUM(phrase_text COLLATE utf8mb4_bin LIKE '%Doğrudan Mesaj%') AS baslik,
SUM(phrase_text COLLATE utf8mb4_bin LIKE '%DOĞRUDAN MESAJ%') AS buyuk_harf
FROM xf_phrase
WHERE language_id = 3;
- XenForo, config.php'de başka bir harmanlama (collation) tanımlı değilse tablolarını utf8mb4_general_ci ile oluşturur (utf8mb4'e dönüştürülmemiş eski kurulumlarda utf8_general_ci). Bu harmanlamada LIKE büyük/küçük harf ayırmaz; ı, i, I ve İ aynı harf sayılır, ğ ile g, ş ile s, ç ile c, ö ile o, ü ile u da eşleşir. İkinci sorgu bu yüzden "dogrudan mesaj" yazımını bile getirir.
- REPLACE() ise MySQL ve MariaDB belgelerine göre büyük/küçük harfe duyarlıdır ve metni birebir arar. Bu yüzden LIKE'ın bulduğu satır sayısı, REPLACE'in değiştireceğinden fazla olabilir.
- COLLATE utf8mb4_bin karşılaştırmayı birebir (ikili) yapar. Sütun utf8mb3 veya utf8 ise utf8_bin yazın.
- Varyantları UPPER() veya LOWER() ile üretmeyin. Türkçeye özgü olmayan harmanlamalarda UPPER('istanbul') İSTANBUL değil ISTANBUL, LOWER('IŞIK') ise ışık değil işik döndürür.
- Komut satırındaki mysql istemcisinde --default-character-set=utf8mb4 kullanın. Eski ya da latin1 ile yapılandırılmış bir istemci ğ ve ş harflerini yanlış kodlayabilir; o zaman sorgu ya hiçbir şey bulmaz ya da bozuk metin yazar.
4. Her yazımı ayrı bir sorguyla güncelleyin.
Code:
UPDATE xf_phrase
SET phrase_text = REPLACE(phrase_text, 'doğrudan mesaj', 'özel mesaj')
WHERE language_id = 3
AND phrase_text COLLATE utf8mb4_bin LIKE '%doğrudan mesaj%';
UPDATE xf_phrase
SET phrase_text = REPLACE(phrase_text, 'Doğrudan mesaj', 'Özel mesaj')
WHERE language_id = 3
AND phrase_text COLLATE utf8mb4_bin LIKE '%Doğrudan mesaj%';
UPDATE xf_phrase
SET phrase_text = REPLACE(phrase_text, 'Doğrudan Mesaj', 'Özel Mesaj')
WHERE language_id = 3
AND phrase_text COLLATE utf8mb4_bin LIKE '%Doğrudan Mesaj%';
5. Yeniden derlemeyi tetikleyin. Bu adım olmadan değişiklik görünmez.
- Panele erişim varsa (önerilen, geliştirici modu gerekmez): Dili Languages sayfasından Get unmodified phrases işaretsiz olarak dışa aktarın ve aynı dosyayı Import ekranında Import as: Overwrite language seçeneğiyle aynı dilin üzerine yükleyin. Dosya SQL değişikliğinizi içerir; XenForo o dilin ifadelerini dosyadan yeniden yazar ve bütün ifadelerin ve şablonların derlenmesini kuyruğa alır. SELECT COUNT(*) FROM xf_phrase WHERE language_id = 3; ile ifade sayısını önce ve sonra karşılaştırın.
- Panele erişim yoksa: Geliştirici komutları ifadeleri ve ardından şablonları yeniden derler (xf-dev:recompile ikisini sırayla yapar). SSH erişimi ve geliştirici modu gerekir; src/config.php dosyasına eklenen aşağıdaki satır modu yalnızca komut satırında açar, işiniz bitince satırı silin:
PHP:if (PHP_SAPI === 'cli') { $config['development']['enabled'] = true; }Ayrıntılar XenForo route filter önbelleği rehberinde anlatılıyor. Komutlar genel (global) ifadelerin dil kaydındaki önbelleğini tazelemez; 2.3'teki direct_message_x gibi birkaç ifade, bu önbellek yeniden kurulana kadar (örneğin panele erişim dönünce yukarıdaki içe aktarmayla) eski metni gösterebilir.Bash:# XenForo kök dizininde php cmd.php xf-dev:recompile-phrases php cmd.php xf-dev:recompile-templates - İşe yaramayanlar: Tools > Data maintenance > Rebuild caches sayfasındaki işler ifade ve şablon derlemesini kapsamaz. xf:rebuild-master-data ise master veriyi yeniden yüklemek içindir: 2.3 kodunda çekirdek şablonları ifadeler yeniden derlenmeden önce derler ve sonra bir daha derlemez; metnin bir kısmı yeni, bir kısmı eski kalabilir. Dili silip yeniden yüklemek de çözüm değildir: varsayılan dil silinemez, silinen dili kullanan üyeler varsayılan dile taşınır ve dil yeni bir kimlik alır.
Doğrulama ve geri alma
Code:
SELECT language_id, title
FROM xf_phrase_compiled
WHERE language_id = 3
AND phrase_text LIKE '%doğrudan mesaj%';
Bash:
# XenForo kök dizininde
grep -rli "doğrudan mesaj" internal_data/code_cache/templates/l3/ | head
- xf_phrase temiz olduğu hâlde derlenmiş tablo satır döndürüyorsa derleme çalışmamış ya da bitmemiştir; derlenmiş şablon dosyalarında eski metin varsa şablonlar derlenmemiştir. Alt dil kullanıyorsanız aynı kontrolleri onun kimliğiyle yapın.
- Siteye hem misafir (gizli pencere) hem üye olarak bakın. Eski metni yalnızca misafirler görüyorsa sayfa önbelleği veya CDN devrededir.
- Geri alma: Panel değişikliğinde özelleştirmeyi Revert ile silin. Alt dilde önce varsayılan dili pakete döndürüp paketin dilini yeniden seçilebilir yapın, sonra alt dili silin. SQL değişikliğinde kopyadan geri yükleyin ve 5. adımı tekrarlayın:
Code:
UPDATE xf_phrase AS p
JOIN yedek_phrase_20261027 AS y
ON y.title = p.title AND y.language_id = p.language_id
SET p.phrase_text = y.phrase_text;
Türkçe örnek: "doğrudan mesaj" yerine "özel mesaj"
XenForo 2.3, Conversations bölümünün adını Direct messages olarak değiştirdi (2.2'deki conversations ifadesinin yerini direct_messages aldı). Veritabanındaki tablolar ise hâlâ xf_conversation_master, xf_conversation_message gibi adlar taşır, içerik türü de conversation olarak kaldı; yani iş tamamen ifadelerle ilgilidir. Bazı Türkçe çeviriler yeni adı "doğrudan mesaj" olarak karşılıyor, Türkçe forum kültüründe ise "özel mesaj" (kısaca ÖM) yerleşik bir terim. İngilizce bir forumda Direct messages adı aynı yollarla yeniden Conversations yapılabilir; ancak İngilizce metin master dilde durduğu için orada SQL yolu uygulanmaz, ifade düzenleyiciyi ya da Get unmodified phrases işaretli XML dışa aktarımını kullanın.| Bul (birebir) | Değiştir | Not |
|---|---|---|
| doğrudan mesaj | özel mesaj | ekler korunur: mesajı, mesajlar, mesajınız |
| Doğrudan mesaj | Özel mesaj | cümle başı, düğmeler |
| Doğrudan Mesaj | Özel Mesaj | menü ve başlık yazımı |
| DOĞRUDAN MESAJ | ÖZEL MESAJ | nadirdir; yalnızca önizlemede çıkarsa |
- Yalnızca "doğrudan mesaj" ikilisini değiştirin; "doğrudan" tek başına başka anlamlarda da geçer.
- Ekler "mesaj" kelimesine bağlı kaldığı için dil bilgisi bozulmaz. "mesaj" yerine "ileti" gibi ünlüyle biten, ince ünlülü bir kelime koysaydınız ünlü uyumu ve kaynaştırma bozulurdu: "mesajlar" "iletilar" (doğrusu "iletiler"), "mesajı" "iletiı" (doğrusu "iletiyi") olurdu. Böyle bir değişiklikte her eki ayrı ele alın.
- Çeviriniz "post" için de "mesaj" diyorsa forum gönderisini birebir yazışmadan bilinçli olarak ayırın (örneğin "gönderi" ve "özel mesaj") ve bu ayrımı bütün dilde tutarlı uygulayın.
- Terim menünün dışında da geçer: bildirimler (alerts), e-posta konu ve gövdeleri, anlık bildirimler (push), gizlilik ve bildirim tercihleri, çerez açıklamaları, panel izin ve seçenek adları. Değişiklikten sonra derlenmiş tabloda hem eski hem yeni terimle arayıp listeyi gözden geçirin.
Sık yapılan hatalar
- Master dili düzenlemek ya da ona SQL ile yazmak: ilk yükseltmede eski hâline döner.
- Özelleştirmeleri paketin kendi dilinde yapmak: paket güncellemesinde silinir.
- UPDATE sorgusunu language_id koşulu olmadan çalıştırmak.
- LIKE önizlemesindeki sayıyı REPLACE'in değiştireceği satır sayısı sanmak.
- SQL'den sonra yalnızca Rebuild caches çalıştırıp sonuç beklemek.
- Overwrite ile eksik bir dosya yüklemek, {değişkenleri} silmek veya HTML etiketlerini bozmak.
Sık sorulan sorular
Panelden değiştirdim ama sitede eski metin görünüyor, neden?Çoğunlukla yanlış dil düzenlenmiştir; sitenin varsayılan dili ile hesabınızın dili farklı olabilir. Metin bir ifade değilse (şablon, seçenek, içerik) ifade düzenleyici onu değiştiremez.
Rebuild caches neden SQL değişikliğimi göstermedi?
O sayfadaki işler ifade ve şablon derlemesini kapsamaz. Derlemeyi dilin içe aktarılması ya da geliştirici komutları tetikler.
Dil paketini güncellersem değişikliklerim kaybolur mu?
Paketin kendi dilinde yaptıysanız evet. Alt dilde yaptıysanız kalır; yalnızca Outdated phrases listesini gözden geçirirsiniz.
Eklenti ifadeleri de aynı yolla mı değişir?
Evet. Eklenti güncellemesi yalnızca master metni yeniler; hedef dildeki özelleştirmeniz kalır.
LIKE neden "dogrudan" yazınca da "doğrudan"ı buluyor?
utf8mb4_general_ci harmanlaması ğ ile g'yi aynı harf sayar. Birebir arama için COLLATE utf8mb4_bin ekleyin.
XenForo 2.2 kullanıyorum; bu adımlar geçerli mi?
Burada kullanılan panel sayfaları ve etiketleri 2.2.13'te de var. Fark terimde: 2.2 hâlâ Conversations (conversations ifadesi) der; aramayı paketinizin o sürümde kullandığı Türkçe karşılıkla yapın.
Sonuç ve sonraki adımlar
Önce metnin gerçekten bir ifade olduğunu doğrulayın. Değişikliği sitede kullanılan dilde, tercihen paketin altındaki bir alt dilde yapın ve toplu işlerde XML'i kullanın. SQL'e ancak dil kimliği, yedek, birebir önizleme ve ardından tam yeniden derlemeyle başvurun. Sonraki adımlar: dil ağacınızı xf_language sorgusuyla çıkarın, bir alt dil oluşturun ve Outdated phrases sayfasını yükseltme sonrası kontrol listenize ekleyin. Forumun kurulum, yükseltme ve bakımını bir ekiple paylaşmak isterseniz AIOR'un forum ve topluluk hizmetleri de seçeneklerden biri.The first version of this thread suggested a single UPDATE query in phpMyAdmin to turn "doğrudan mesaj" (Turkish for "direct message") into "özel mesaj" (private message). The query runs without errors, but often nothing changes on the site, and when it does, the next language pack update brings the old text back. That is because XenForo renders pages not from the xf_phrase table but from compiled copies of it. The supported routes come first and the safe SQL method last. We checked the behaviour described here against the XenForo 2.3 source (2.3.13), and the Admin CP labels used are the same in 2.2.13. Menu names can vary slightly by version, so Admin CP URLs are included.
Quick answer: which method when?
| Situation | Recommended route |
|---|---|
| A handful of phrases | The phrase editor in the Admin CP, with the site's language selected |
| One term in dozens of phrases | Filter with Search phrases, then edit them together on the Translate page |
| Hundreds of phrases, a terminology change | Export the language as XML, edit it, import it as a child language |
| No Admin CP access, or an emergency | Backup, SQL, then a full recompile |
| The text is in no phrase at all | Template, option or add-on; see the diagnosis below |
The short version for phpMyAdmin: find the language ID, back up xf_phrase, limit the UPDATE with language_id and run it once per capitalisation, then export the language and import it over itself with Overwrite language to trigger the recompile (without Admin CP access, use the developer commands in step 5).
How XenForo phrases work
- Master language (ID 0): the original English text of XenForo and every add-on. It cannot be edited in the Admin CP unless development mode is on. Upgrades, add-on updates and xf:rebuild-master-data rewrite it from the shipped files, so anything written to it with SQL silently reverts.
- Inheritance: each language inherits every phrase it has not customised from its parent. A translation pack stores its Turkish text as phrases customised in its own language.
- Three states: in the selected language a phrase is unmodified (it comes from the master), customised in this language, or inherited from a customised parent. The Phrase status filter in Search phrases separates them as Unmodified, Modified in this language and Modified in a parent language.
- Add-on phrases: same system; only the addon_id differs. An add-on update refreshes only the master text.
- Compiled copies: the code that builds a page never reads xf_phrase. It uses xf_phrase_compiled, a per-language cache of global phrases, phrase group files under internal_data/code_cache and compiled templates. The text of a phrase called by a literal name in a template is embedded in the compiled template file. Saving in the Admin CP refreshes all of these; a raw UPDATE touches none of them.
Where does the text come from? Switch your account to English for a moment. If the text changes, it is a phrase. If not, it is text typed into a template (common in themes), an option value (such as the board title under Setup > Options), content such as a forum title, a notice, or an add-on that hard-codes its strings.
Find the phrase
- On Appearance > Languages & phrases > Search phrases (admin.php?phrases/search), choose the language the site uses and type "doğrudan mesaj" into Text contains. Leave Case sensitive unticked and all three Phrase status options ticked so every spelling shows up.
- If you know the phrase title, use the filter box above the Phrases list; it searches titles across the whole list, not just the page on screen.
- No result? Search template contents with Appearance > Styles & templates > Search templates (admin.php?templates/search), choosing the right style and template type (public, admin or email).
- For a read-only check without the Admin CP, this query searches the compiled text visitors see. Inheritance is already resolved, so it does not matter which language defines the text.
Code:
SELECT language_id, title, phrase_text
FROM xf_phrase_compiled
WHERE phrase_text LIKE '%doğrudan mesaj%'
ORDER BY language_id, title;
To make text typed into a template follow the visitor's language, add your own phrase with text in every language the site offers and call it as {{ phrase('aior_private_message') }}. Add that call with a template modification rather than by customising the template by hand: no customised copy is created, so there is no outdated template to update after upgrades.
The supported route: edit in the target language
- On Appearance > Languages & phrases > Phrases, choose the site's language in the selector, never the master.
- Open the phrase; the editor also shows the master value for reference. Do not remove or rename curly-brace variables such as {sender} or {name}. The XenForo manual names this as the condition for customised phrases to come through upgrades safely.
- Saving creates a customised copy in that language, leaves the master untouched and recompiles the templates that use the phrase. Refresh the page to see it.
- For many phrases, filter with Refine and translate above the list (or the Search phrases form) and press Translate; the matches open as editable boxes on one page, 50 at a time, with a More button for the rest.
- To undo, use Revert next to the phrase in the list; the customisation in that language is deleted and the parent's text applies again.
With a translation pack, set up a child language. A pack is updated by importing the new file over the existing language with Overwrite language, and XenForo first deletes every phrase in that language (only that add-on's phrases if the file belongs to one add-on) before adding the file's phrases. Anything customised in the pack's own language disappears at that moment. A setup that lasts:
- On Appearance > Languages & phrases > Languages (admin.php?languages), add a language such as "Türkçe (forum)" with the pack's language as parent, and make every customisation there.
- If the pack's language is the board default, make the child the default with the Default language option under Setup > Options > Appearance.
- Use the toggle in the language list to make the pack's language non-selectable. Non-admin members still set to it fall back to the board default automatically, and that is the child only if you did the previous step. On a bilingual board whose default is another language, such as English, first move those members to the child after backing up xf_user: UPDATE xf_user SET language_id = 4 WHERE language_id = 3; (3 is the pack, 4 the child). Administrators change their own preference.
Bulk changes: export, edit and import XML
- On the Languages page, export the pack's language. Leave Export from add-on at (All) and Get unmodified phrases unticked; the file then holds only the phrases defined in that language.
- Open a copy in a UTF-8 editor (VS Code, Notepad++ and similar) and replace each spelling separately with Match case on. Leave attributes, {variables} and HTML tags alone. If you see ]-]->, keep it: XenForo writes ]]>, which cannot appear inside CDATA, that way on export and reverses it on import.
- Use the script below to write only the changed phrases to a file with a new title, then import it on Languages > Import (admin.php?languages/import) with Import as: Child of language set to the pack's language. This creates the child; if you already set one up as described above, use the method in the next item instead.
- In later rounds, export both the pack's language and the child with Get unmodified phrases ticked, edit the child's file and run the script with the pack's file as the first argument and your edited child file as the second. The output holds every phrase that differs from the pack, earlier customisations included, so it can go over the child with Overwrite language. A file imported with Overwrite must be the complete language, or customisations missing from it are deleted.
Python:
# delta.py: write only the changed phrases to a new language file
import sys
import xml.etree.ElementTree as ET
original, edited, output, title = sys.argv[1:5]
old = {p.get('title'): p.text or '' for p in ET.parse(original).getroot().iter('phrase')}
root = ET.parse(edited).getroot()
for p in root.findall('phrase'):
if old.get(p.get('title')) == (p.text or ''):
root.remove(p)
root.set('title', title)
ET.ElementTree(root).write(output, encoding='utf-8', xml_declaration=True)
print(len(root.findall('phrase')), 'phrases written')
Bash:
python3 delta.py language-Turkce.xml language-Turkce-edited.xml turkish-forum.xml "Türkçe (forum)"
The script turns CDATA into escaped text; the XML is equivalent. Never import a complete language file as a child: it would copy every phrase and hide later pack updates. The import queues the phrase and template recompile (the PhraseRebuild and TemplateRebuild jobs) itself, so no cache job is needed.
Last resort: phpMyAdmin and SQL
Use SQL only without Admin CP access or for an urgent fix. Two limits: text changed by SQL in the pack's language reverts with the next pack update, and nothing shows until a recompile runs. For find-and-replace techniques across tables, see How to find and replace a word across SQL tables.1. Find the language ID.
Code:
SELECT language_id, parent_id, title, language_code, user_selectable
FROM xf_language;
2. Back up. In phpMyAdmin, select xf_phrase on the left and use Export > Quick > SQL. With SSH:
Bash:
mysqldump --single-transaction --default-character-set=utf8mb4 -u USER -p DATABASE xf_phrase > xf_phrase-$(date +%F-%H%M).sql
Code:
CREATE TABLE backup_phrase_20261027 AS
SELECT language_id, title, phrase_text
FROM xf_phrase
WHERE language_id = 3;
3. Check the collation and preview.
Code:
SHOW FULL COLUMNS FROM xf_phrase LIKE 'phrase_text';
SELECT phrase_id, title, phrase_text
FROM xf_phrase
WHERE language_id = 3
AND phrase_text LIKE '%doğrudan mesaj%';
SELECT
SUM(phrase_text COLLATE utf8mb4_bin LIKE '%doğrudan mesaj%') AS lower_case,
SUM(phrase_text COLLATE utf8mb4_bin LIKE '%Doğrudan mesaj%') AS sentence_case,
SUM(phrase_text COLLATE utf8mb4_bin LIKE '%Doğrudan Mesaj%') AS title_case,
SUM(phrase_text COLLATE utf8mb4_bin LIKE '%DOĞRUDAN MESAJ%') AS upper_case
FROM xf_phrase
WHERE language_id = 3;
- Unless config.php sets another collation, XenForo creates its tables with utf8mb4_general_ci (utf8_general_ci on older installs never converted to utf8mb4). There LIKE ignores case, treats ı, i, I and İ as one letter and matches ğ with g, ş with s, ç with c, ö with o and ü with u. The second query therefore returns even "dogrudan mesaj".
- REPLACE() is case-sensitive and matches exact text, according to both the MySQL and the MariaDB documentation, so LIKE can find more rows than REPLACE will change.
- COLLATE utf8mb4_bin makes the comparison exact (binary). If the column is utf8mb3 or utf8, use utf8_bin.
- Do not build variants with UPPER() or LOWER(): collations without Turkish rules return ISTANBUL, not İSTANBUL, for UPPER('istanbul') and işik, not ışık, for LOWER('IŞIK').
- With the mysql command-line client, pass --default-character-set=utf8mb4. An old client, or one configured for latin1, can mis-encode ğ and ş, and the query then finds nothing or writes garbled text.
4. Update each spelling with its own query.
Code:
UPDATE xf_phrase
SET phrase_text = REPLACE(phrase_text, 'doğrudan mesaj', 'özel mesaj')
WHERE language_id = 3
AND phrase_text COLLATE utf8mb4_bin LIKE '%doğrudan mesaj%';
UPDATE xf_phrase
SET phrase_text = REPLACE(phrase_text, 'Doğrudan mesaj', 'Özel mesaj')
WHERE language_id = 3
AND phrase_text COLLATE utf8mb4_bin LIKE '%Doğrudan mesaj%';
UPDATE xf_phrase
SET phrase_text = REPLACE(phrase_text, 'Doğrudan Mesaj', 'Özel Mesaj')
WHERE language_id = 3
AND phrase_text COLLATE utf8mb4_bin LIKE '%Doğrudan Mesaj%';
5. Trigger the recompile. Without it nothing changes on the site.
- With Admin CP access (recommended, no development mode): export the language from the Languages page with Get unmodified phrases unticked and import the same file over the same language with Import as: Overwrite language. The file contains your SQL change; XenForo rewrites that language's phrases from it and queues a full phrase and template recompile. Compare SELECT COUNT(*) FROM xf_phrase WHERE language_id = 3; before and after.
- Without Admin CP access: the developer commands recompile the phrases and then the templates (xf-dev:recompile runs both in order). They need SSH access and development mode; this line in src/config.php turns development mode on for the command line only, and you remove it when you are done:
PHP:if (PHP_SAPI === 'cli') { $config['development']['enabled'] = true; }Our XenForo route filter cache guide covers the details. These commands do not refresh the per-language cache of global phrases, so a few phrases, such as direct_message_x in 2.3, can show old text until that cache is rebuilt, for example by the import above once the Admin CP is reachable again.Bash:# in the XenForo root directory php cmd.php xf-dev:recompile-phrases php cmd.php xf-dev:recompile-templates - What does not work: the jobs on Tools > Data maintenance > Rebuild caches do not cover phrase or template compilation. xf:rebuild-master-data is meant for restoring master data: in the 2.3 code it compiles the core templates before the phrases are recompiled and does not compile them again, so old and new text can mix. Deleting and re-uploading the language is no fix either: the default language cannot be deleted, members using a deleted language move to the default, and the language gets a new ID.
Verify and roll back
Code:
SELECT language_id, title
FROM xf_phrase_compiled
WHERE language_id = 3
AND phrase_text LIKE '%doğrudan mesaj%';
Bash:
# in the XenForo root directory
grep -rli "doğrudan mesaj" internal_data/code_cache/templates/l3/ | head
- If xf_phrase is clean but the compiled table still returns rows, the recompile has not run or has not finished; if compiled template files still hold the old text, the templates were not recompiled. For a child language, use its ID.
- Look at the site as a guest (private window) and as a member. If only guests see the old text, a page cache or CDN is involved.
- Rollback: for an Admin CP change, delete the customisation with Revert. For a child language, switch the default back to the pack and make the pack selectable again, then delete the child. For SQL, restore from the copy and repeat step 5:
Code:
UPDATE xf_phrase AS p
JOIN backup_phrase_20261027 AS b
ON b.title = p.title AND b.language_id = p.language_id
SET p.phrase_text = b.phrase_text;
Turkish example: "doğrudan mesaj" to "özel mesaj"
XenForo 2.3 renamed Conversations to Direct messages (the 2.2 phrase conversations gave way to direct_messages). The tables are still called xf_conversation_master, xf_conversation_message and so on, and the content type is still conversation, so this is purely phrase work. Some Turkish translations render the new name as "doğrudan mesaj" (literally "direct message"), while Turkish forum culture has long said "özel mesaj" (private message, often shortened to ÖM). An English board can rename Direct messages back to Conversations with the phrase editor or the XML route (export with Get unmodified phrases ticked, because the English text sits in the master language); the SQL route does not apply there, since the master must not be edited.| Find (exact) | Replace | Note |
|---|---|---|
| doğrudan mesaj | özel mesaj | suffixes survive: mesajı, mesajlar, mesajınız |
| Doğrudan mesaj | Özel mesaj | sentence start, buttons |
| Doğrudan Mesaj | Özel Mesaj | menus and headings |
| DOĞRUDAN MESAJ | ÖZEL MESAJ | rare; only if the preview shows it |
- Replace only the two-word term; "doğrudan" on its own means "directly" and appears elsewhere.
- Turkish suffixes stay attached to "mesaj", so the grammar survives. Swapping "mesaj" for a word that ends in a front vowel, such as "ileti", would break vowel harmony and the buffer consonant: "mesajlar" would become "iletilar" (correct: "iletiler") and "mesajı" "iletiı" (correct: "iletiyi"), so each suffix would need handling.
- If your translation also uses "mesaj" for a forum post, separate the terms on purpose (for example "gönderi" for posts and "özel mesaj" for one-to-one messages) and apply that across the whole language.
- The term appears beyond the menu: alerts, email subjects and bodies, push notifications, privacy and notification preferences, cookie descriptions, and Admin CP permission and option names. Afterwards, search the compiled table for both the old and the new term and review the list.
Common mistakes
- Editing the master language or writing to it with SQL: the next upgrade reverts it.
- Customising inside the pack's own language: the next pack update deletes it.
- Running UPDATE without a language_id condition.
- Reading the LIKE count as the number of rows REPLACE will change.
- Running only Rebuild caches after an SQL change.
- Importing an incomplete file with Overwrite, removing {variables} or breaking HTML tags.
FAQ
I changed the phrase in the Admin CP, but the old text still shows. Why?Usually the wrong language was edited; the board default and your account language can differ. If the text is not a phrase (template, option, content), the phrase editor cannot change it.
Why did Rebuild caches not show my SQL change?
The jobs on that page do not cover phrase or template compilation. A language import or the developer commands do.
Will a language pack update wipe my changes?
Yes, if they live in the pack's own language. In a child language they stay, and you only review Outdated phrases.
Do add-on phrases work the same way?
Yes. An add-on update refreshes only the master text; your customisation in the target language stays.
Why does LIKE '%dogrudan%' also find "doğrudan"?
utf8mb4_general_ci treats ğ and g as the same letter. Add COLLATE utf8mb4_bin for an exact search.
I am on XenForo 2.2. Do these steps apply?
The Admin CP pages and labels used here exist in 2.2.13 as well. The difference is the term: 2.2 still says Conversations (the conversations phrase), so search for whatever your pack uses in that version.
Conclusion and next steps
First confirm the text really is a phrase. Change it in the language the site uses, ideally a child of the pack's language, and use XML for bulk work. Reach for SQL only with the language ID, a backup, an exact preview and a full recompile afterwards. Next steps: map your language tree with the xf_language query, create a child language and add Outdated phrases to your post-upgrade checklist. If you would rather share installation, upgrades and maintenance with a team, AIOR's forum and community services are one option.
Last edited: