SSH ile bağlandığınız bir Linux sunucuda ./build update, apt install veya herhangi bir Perl betiği çalıştırırken aşağıdaki uyarıyı görüyorsanız sorun neredeyse hiçbir zaman Perl'de değildir. Oturumunuzdaki yerel ayar (locale) değişkenlerinden biri, sunucuda kurulu olmayan bir locale'i gösteriyordur. En sık rastlanan senaryo şudur: Mac'teki terminal LC_CTYPE değişkenine yalnızca "UTF-8" değerini yazar, SSH bu değeri sunucuya taşır, Linux bu adda bir locale tanımadığı için Perl varsayılan C locale'ine geri döner. Bu rehberde önce uyarıyı satır satır okuyup kök nedeni buluyoruz, ardından Mac tarafındaki düzeltmeleri, AlmaLinux/Rocky/RHEL, Debian/Ubuntu ve Docker için sunucu adımlarını, kalıcılık seçeneklerini ve DirectAdmin CustomBuild, sudo, cron, systemd ve tmux gibi özel durumları ele alıyoruz.
Hata mesajı
Aşağıdaki çıktı, bir DirectAdmin sunucusunda CustomBuild güncellemesi sırasında alındı:
Kod:
./build update
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = "UTF-8",
LANG = "C"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
Aynı kök neden başka programlarda farklı cümlelerle karşınıza çıkar. Bash oturum açılırken -bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory yazar, locale komutu locale: Cannot set LC_CTYPE to default locale: No such file or directory hatasını verir, AlmaLinux ve Rocky'deki dnf ise Failed to set locale, defaulting to C.UTF-8 satırını basar. Perl 5.40 ve sonrası listede bütün kategorileri, çoğunu (unset) olarak gösterir; tırnak içinde değer taşıyan satırlara odaklanın.
Uyarıyı satır satır okumak
- Setting locale failed: Perl açılışta locale'i ortam değişkenlerine göre ayarlamak istedi, C kütüphanesi (glibc) bunu reddetti. Değişkenlerden en az biri sistemde kurulu olmayan bir adı gösteriyor.
- Please check that your locale settings: Perl'ün dikkate aldığı değişkenler önem sırasına göre listelenir. (unset), değişkenin tanımlı olmadığı anlamına gelir ve sorun yaratmaz.
- LANGUAGE: yalnızca mesaj çevirilerinin dil sırasını belirleyen bir GNU uzantısıdır; locale seçiminde rol oynamaz.
- LC_ALL: tanımlıysa diğer bütün değişkenleri ezer. Bu örnekte tanımsız, yani suçlu o değil.
- LC_CTYPE = "UTF-8": asıl sorun. Linux'ta bir locale adı dil_BÖLGE.kodlama biçimindedir: en_US.UTF-8, tr_TR.UTF-8, C.UTF-8. Tek başına UTF-8 bir karakter kodlamasının adıdır, locale adı değildir. macOS'ta /usr/share/locale/UTF-8 dizini bulunduğu için aynı değer Mac'te geçerlidir; Linux'a taşındığında geçersiz hale gelir.
- LANG = "C": ayrıca belirtilmeyen her kategori için varsayılan değer. C locale'i her sistemde vardır, dolayısıyla sorun LANG değil.
- Falling back to the standard locale ("C"): Perl sırasıyla LC_ALL, LANG ve C değerlerini dener, çalışan ilkiyle devam eder. LANG, kurulu bir locale olan en_US.UTF-8 değerini taşısaydı satır Falling back to a fallback locale ("en_US.UTF-8") şeklinde olurdu. Program durmaz, ama locale'e duyarlı araçlar artık C kurallarıyla çalışır; Türkçe karakterlerin gösterimi, sıralama ve büyük/küçük harf dönüşümü etkilenebilir.
Bütün çözümlerin dayandığı kural önceliktir: LC_ALL, LC_CTYPE gibi kategori değişkenlerini ezer; kategori değişkenleri de LANG değerini ezer. Bu yüzden sunucuda yalnızca LANG değerini düzeltmek, istemciden gelen hatalı LC_CTYPE değerini ortadan kaldırmaz.
Kök neden: macOS terminali ve SSH ortam aktarımı
macOS'taki Terminal uygulamasında "Set locale environment variables on startup" seçeneği açıkken LANG değeri sistem dili ve bölgesinden türetilir: İngilizce ve Amerika Birleşik Devletleri için en_US.UTF-8, Türkçe ve Türkiye için tr_TR.UTF-8. Dil ile bölgenin birleşimine karşılık gelen bir locale yoksa ne olacağını Apple belgelemiyor; kullanıcı raporlarına göre Terminal bu durumda LANG değişkenini boş bırakır ve yalnızca LC_CTYPE=UTF-8 atar. macOS'ta en_TR diye bir locale bulunmadığından, İngilizce arayüzü Türkiye bölge ayarıyla kullanıyorsanız büyük olasılıkla bu durumdasınız; aşağıdaki teşhis adımları bunu kesinleştirir.Zincirin ikinci halkası SSH istemcisidir. macOS'un sistem genelindeki SSH yapılandırması SendEnv LANG LC_* satırını içerir (güncel sürümlerde /etc/ssh/ssh_config.d/100-macos.conf dosyasında) ve bu değişkenleri her bağlantıda gönderir. Sunucunun bunları kabul edip etmeyeceğini sshd yapılandırmasındaki AcceptEnv satırları belirler:
| Sunucu | Varsayılan AcceptEnv | Mac'ten gelen LC_CTYPE |
|---|---|---|
| Debian, Ubuntu | LANG LC_* (yeni sürümlerde ek olarak COLORTERM NO_COLOR) | Kabul edilir |
| AlmaLinux, Rocky, RHEL 8 | LANG, LANGUAGE, LC_ALL ve bütün LC_* kategorileri tek tek | Kabul edilir |
| AlmaLinux, Rocky, RHEL 9 ve 10 | Varsayılan yapılandırmada yok | Yok sayılır; ancak bir panel, imaj veya yönetici eklemiş olabilir |
Mac'iniz tamamen Türkçe ise (dil Türkçe, bölge Türkiye) Terminal LANG=tr_TR.UTF-8 atar ve SSH bu değeri de taşır. Türkçe locale'in kurulu olmadığı bir sunucuda (minimal kurulumlarda genellikle yoktur) aynı uyarı bu kez LANG = "tr_TR.UTF-8" satırıyla gelir. Çözüm mantığı aynıdır: ya sunucuya Türkçe locale'i kurarsınız ya da Mac'in gönderdiği değeri değiştirirsiniz.
Uyarının tek kaynağı aktarım değildir. Sunucudaki /etc/locale.conf ya da bir profil dosyası kurulu olmayan bir locale'i gösteriyorsa (örneğin en_US.UTF-8 yazılı ama dil paketi yok) aynı mesaj hangi istemciden bağlanırsanız bağlanın çıkar. Minimal bulut imajlarında ve konteynerlerde bu durum sık görülür. Aşağıdaki teşhis adımları iki olasılığı birbirinden ayırır.
Teşhis: hangi değer, nereden geliyor?
Önce sunucudaki durumu görün:
Bash:
locale # sorun varsa ilk satırlarda "Cannot set LC_CTYPE ..." görünür
locale -a # kurulu locale'ler; en_US.utf8 ile en_US.UTF-8 aynıdır
echo "LANG=$LANG LC_ALL=$LC_ALL LC_CTYPE=$LC_CTYPE"
localectl status # systemd kullanan sistemlerde sistem varsayılanı
sudo sshd -T | grep -i acceptenv
Linux'taki locale çıktısında tırnaksız değerler doğrudan atanmıştır; tırnaklı değerler LC_ALL ya da LANG değişkeninden türetilmiştir (ikisi de yoksa POSIX görünür). locale -a listesinde yalnızca en_US.utf8 görüyorsanız ayrıca en_US.UTF-8 kurmaya çalışmayın: glibc kodlama adını normalleştirir, ikisi aynı locale'dir.
Ardından Mac'te değerin kaynağını doğrulayın:
Bash:
locale # LC_CTYPE="UTF-8" ise kaynak Mac
ssh -v kullanici@sunucu exit 2>&1 | grep -i env # istemcinin gönderdiği değişkenler
ssh -F /dev/null kullanici@sunucu # yapılandırma okunmaz, locale değişkeni gönderilmez
OpenSSH istemcisi -v ile gönderdiği her değişken için channel 0: setting env LC_CTYPE = "UTF-8" benzeri bir satır yazar. -F /dev/null ile açılan oturumda uyarı kayboluyorsa neden kesin olarak aktarımdır; bu seçenek ~/.ssh/config dosyanızı da yok saydığı için portu ve anahtarı -p ve -i ile vermeniz gerekebilir. Değer ne Mac'ten geliyor ne de sistem varsayılanında görünüyorsa sunucudaki başlangıç dosyalarını tarayın:
Bash:
grep -rns 'LC_CTYPE\|LC_ALL\|LANG=' ~/.bashrc ~/.bash_profile ~/.profile /etc/environment /etc/profile /etc/profile.d/ /etc/locale.conf /etc/default/locale
İstemci tarafı çözümler (macOS)
1. Mac'te geçerli bir değer kullanın (önerilen)
Terminal uygulamasında Terminal > Settings > Profiles yolunda profilinizi seçip Advanced sekmesini açın (Türkçe arayüzde aynı menüler Türkçe adlarıyla görünür). Buradaki "Set locale environment variables on startup" seçeneği, dil-bölge birleşiminiz geçerliyse doğru değeri üretir; karışık bir birleşim kullanıyorsanız seçeneği kapatıp değeri kendiniz verin. iTerm2'de aynı ayar Profiles > Terminal > Environment bölümündedir; "Use custom locale..." ile en_US.UTF-8 seçebilirsiniz. Hangi terminali kullanırsanız kullanın en sağlam yol, kabuk başlangıç dosyasına açık bir değer yazmaktır (macOS Catalina'dan beri varsayılan kabuk zsh'tir; bash kullanıyorsanız ~/.bash_profile dosyasını düzenleyin):
Bash:
# ~/.zshrc (Mac)
export LANG="en_US.UTF-8"
export LC_CTYPE="en_US.UTF-8"
Yeni bir terminal penceresi açın ve locale komutuyla kontrol edin. Sunucu yönettiğiniz terminalde tr_TR.UTF-8 yerine en_US.UTF-8 kullanmanızı öneririz; nedenini kalıcılık bölümünde açıklıyoruz. Sunucuda en_US.UTF-8 yoksa aşağıdaki dağıtım adımlarıyla kurun.
2. Değişkenleri göndermeyi bırakın
Bash:
grep -rn SendEnv /etc/ssh/ssh_config /etc/ssh/ssh_config.d/ ~/.ssh/config 2>/dev/null
sudo nano /etc/ssh/ssh_config.d/100-macos.conf # SendEnv satırının başına # koyun
Satır kendi ~/.ssh/config dosyanızdaysa silmeniz yeterlidir. Sistem dosyasını düzenlemek sudo ister ve büyük macOS güncellemeleri varsayılan dosyayı geri getirebilir. ~/.ssh/config dosyasına SendEnv -LC_* yazmak işe yaramaz: ssh önce kullanıcı dosyasını, sonra sistem dosyasını okur; eksi öneki ise yalnızca o ana kadar eklenmiş adları siler.
3. Sunucuda AcceptEnv satırını kaldırın
Farklı bilgisayarlardan bağlanan birden fazla yönetici varsa istemcileri tek tek düzeltmek yerine sunucunun bu değişkenleri kabul etmesini kapatabilirsiniz:
Bash:
sudo grep -rn AcceptEnv /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
sudo nano /etc/ssh/sshd_config # AcceptEnv satırlarını yorum satırı yapın
sudo sshd -t && sudo systemctl reload sshd # Debian/Ubuntu'da servis adı ssh
Açık oturumunuzu kapatmadan yeni bir bağlantıyla test edin. Bu yolun bedeli, kullanıcıların kendi dil tercihlerini istemciden taşıyamamasıdır.
4. Yalnızca hatalı değeri temizleyin
sshd yapılandırmasına dokunmak istemiyorsanız, bilinen hatalı değeri oturum açılırken silen küçük bir betik yeterlidir:
Bash:
# /etc/profile.d/zz-locale-fix.sh (yalnızca kendi hesabınız için: ~/.bashrc)
if [ "$LC_CTYPE" = "UTF-8" ]; then
unset LC_CTYPE
fi
| Yöntem | Artısı | Eksisi |
|---|---|---|
| Mac'te geçerli değer | Sorunu kaynağında, bütün sunucular için tek seferde çözer | Sunucuda aynı locale kurulu olmalı |
| SendEnv satırını kapatmak | Sunucu her zaman kendi varsayılanını kullanır | sudo gerekir, macOS güncellemesi geri alabilir |
| AcceptEnv satırını kaldırmak | Hatalı istemcilerden bağımsız, herkes için tutarlı | Bütün kullanıcıları etkiler, sshd hatası erişimi kesebilir |
| profile.d ile temizlik | Yalnızca bilinen hatalı değere dokunur | Kabuk oturumlarıyla sınırlı; cron ve servisleri kapsamaz |
Sunucu tarafı: eksik locale'i kurmak
AlmaLinux, Rocky Linux, RHEL (8, 9, 10)
Bu dağıtımlarda locale verisi dil paketleriyle gelir. Konteyner imajlarında ve bazı minimal kurulumlarda yalnızca glibc-minimal-langpack bulunur; bu paket C, POSIX ve C.UTF-8 dışında locale sağlamaz.
Bash:
sudo dnf install glibc-langpack-en # en_US.UTF-8 dahil İngilizce locale'ler
sudo dnf install glibc-langpack-tr # Türkçe gerekiyorsa
# sudo dnf install glibc-all-langpacks # bütün diller; çok daha fazla yer kaplar
sudo localectl set-locale LANG=en_US.UTF-8
localectl status
localectl değeri /etc/locale.conf dosyasına yazar ve systemd servis yöneticisini de bilgilendirir. Güncel sürümlerde (RHEL 8.4 ve sonrası dahil) kurulu olmayan bir locale ayarlanmak istendiğinde "Locale en_US.UTF-8 not installed, refusing." hatası alınır; bu yüzden önce paketi kurun. Yeni değer yeni oturumlarda ve yeniden başlatılan servislerde geçerli olur.
Debian ve Ubuntu
Bash:
sudo apt install locales # minimal kurulum ve imajlarda eksik olabilir
sudo dpkg-reconfigure locales # etkileşimli: locale seçimi ve varsayılan
# Etkileşimsiz yol:
sudo sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
sudo locale-gen
sudo update-locale LANG=en_US.UTF-8
Debian'daki locale-gen yalnızca /etc/locale.gen dosyasını okur; Ubuntu'daki sürüm sudo locale-gen en_US.UTF-8 biçimini de kabul eder ve satırı dosyaya kendisi ekler. update-locale geçersiz bir değeri reddeder ve sistem varsayılanını yazar: Debian 12 ve Ubuntu 22.04'te /etc/default/locale, Debian 13 ve Ubuntu 24.04'te /etc/locale.conf (eski yol bu dosyaya sembolik bağlantı olarak kalır).
Docker ve konteynerler
Konteynerde localectl ya da oturum açma süreci yoktur; varsayılan değer imajdaki ENV satırından gelir. Dile özgü kurallara ihtiyacınız yoksa en kolayı C.UTF-8'dir; güncel Debian, Ubuntu ve RHEL tabanlı imajlarda ek paket gerektirmez:
Kod:
FROM debian:12-slim
ENV LANG=C.UTF-8
Belirli bir dil gerekiyorsa locale'i imajda üretin ve derleme sırasında doğrulayın:
Kod:
FROM debian:12-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends locales \
&& sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
&& locale-gen \
&& locale -a | grep -qi '^en_US.utf8$' \
&& rm -rf /var/lib/apt/lists/*
ENV LANG=en_US.UTF-8
AlmaLinux veya Rocky tabanlı imajlarda RUN dnf -y install glibc-langpack-en && dnf clean all satırı ve ardından ENV LANG=en_US.UTF-8 yeterlidir. Derleme sırasında apt komutları aynı Perl uyarısını basıyorsa ENV LANG satırı büyük olasılıkla locale üretilmeden önce yazılmıştır.
Kalıcılık: oturum, kullanıcı, sistem
| Düzey | Nerede | Kapsam |
|---|---|---|
| Oturum | export veya unset (geçerli kabuk) | O kabuk ve başlattığı süreçler; çıkışta kaybolur |
| Kullanıcı | ~/.bashrc, ~/.bash_profile (sunucu), ~/.zshrc (Mac) | O kullanıcının yeni oturumları |
| Sistem | /etc/locale.conf (localectl, update-locale) veya /etc/default/locale | Yeni girişler ve servisler |
| Servis | systemd drop-in dosyasında Environment= | Tek bir servis |
| Cron | crontab başında LANG= | O crontab'daki işler |
| Konteyner | Dockerfile içinde ENV | İmajdaki bütün süreçler |
LC_ALL değişkenini /etc/environment, /etc/profile veya ~/.bashrc üzerinden kalıcı olarak ayarlamak hızlı bir çözüm gibi görünür, ama kötü bir fikirdir:
- LC_ALL her kategoriyi ezer; kullanıcılar ve servisler tek bir kategoriyi, örneğin yalnızca tarih biçimini, değiştiremez hale gelir.
- LC_ALL geçici kullanım içindir: LC_ALL=C sort örneğinde olduğu gibi tek bir komutta öngörülebilir sonuç almak için. perllocale belgesi de LC_ALL ayarının yalnızca Perl'ü değil başka programları da etkileyebileceği konusunda uyarır.
- Değer kurulu olmayan bir locale'i gösterirse (dil paketi kaldırıldığında, dosya başka bir sunucuya veya konteynere kopyalandığında) sistemdeki her program uyarı vermeye başlar.
- Asıl hatayı gizler: Mac, LC_CTYPE=UTF-8 göndermeyi diğer bütün sunuculara sürdürür.
Türkçe kullanıcılar için bir not: sunucu varsayılanını tr_TR.UTF-8 yapmak da beklenmedik sonuçlar doğurabilir. glibc'nin Türkçe kurallarında i büyük harfe İ, I küçük harfe ı olarak dönüşür; ASCII kuralları varsayan betikler ve programlar bundan etkilenebilir. Ondalık ayracı virgül olduğu için LC_NUMERIC=tr_TR.UTF-8 altında bash'in printf "%.2f" 3.14 komutu "invalid number" hatası verir. Sunucuda LANG=en_US.UTF-8 veya C.UTF-8 kullanın; Türkçe tarih biçimi isteyen kullanıcı kendi profilinde yalnızca LC_TIME=tr_TR.UTF-8 ayarlasın.
Özel durumlar
DirectAdmin CustomBuild
DirectAdmin sunucularında uyarı sıklıkla /usr/local/directadmin/custombuild dizininde ./build update gibi komutlar çalıştırılırken görülür (güncel DirectAdmin belgeleri aynı arayüzü da build komutuyla sunar). Bir derleme çok sayıda harici araç çalıştırır ve bunların arasındaki her Perl programı uyarıyı yeniden basar; bu yüzden uyarı tek bir çalıştırmada defalarca tekrarlanabilir. Uyarı derlemeyi durdurmaz, ama gerçek hataları gürültünün içinde kaybettirir. Kalıcı çözüm yerine oturana kadar LC_ALL=C.UTF-8 ./build update gibi komut başına geçici bir değer kullanabilirsiniz; LC_ALL değişkeninin doğru kullanımı tam olarak budur. Uyarı DirectAdmin'in kendi zamanlanmış görevlerinden gelen e-postalarda görünüyorsa sorun büyük olasılıkla SSH oturumunuzda değil, sistem varsayılanında ve kurulu dil paketlerindedir. Güncelleme adımlarının kendisi için DirectAdmin için SSH üzerinden CustomBuild update konusuna bakabilirsiniz.sudo ve root
sudo varsayılan olarak LANG, LANGUAGE ve LC_* değişkenlerini korur (bunlar sudo'nun env_check listesindedir; değer / veya % içermiyorsa aktarılır). Bu yüzden Mac'ten gelen LC_CTYPE=UTF-8, sudo ./build update komutuna veya sudo -i ile açtığınız root kabuğuna da taşınır. su - ise bir giriş kabuğu başlatır ve TERM gibi birkaç terminal değişkeni dışında ortamı temizler. Uyarı su - ile kayboluyor ama sudo ile sürüyorsa kaynak oturum değişkenleridir; bunu kalıcı çözüm değil, ipucu olarak kullanın. root yetkisi ile sudo arasındaki farklar için "Bu komut süper kullanıcı (root) yetkisiyle çalıştırılmalıdır" hatası konusuna göz atın.cron
Cron işleri SSH oturumunuzun değişkenlerini görmez, asgari bir ortamla başlar (Debian ve Ubuntu'da cron, PAM üzerinden /etc/environment ve sistem locale dosyasını da okur). Uyarı cron e-postalarında veya günlüklerinde görünüyorsa işin gerçekte ne gördüğünü yakalayın, sonra geçici satırı silin:
Kod:
* * * * * env > /tmp/cron-env.txt 2>&1
Hatalı veya kurulu olmayan bir değer görürseniz crontab -e ile dosyanın en üstüne LANG=C.UTF-8 (ya da kurulu başka bir locale) satırını ekleyin veya işin çağırdığı betikteki ayarı düzeltin. Hatalı değer LC_ALL ya da LC_CTYPE içindeyse (örneğin /etc/environment dosyasından geliyorsa) LANG onu ezemez; o değişkeni kaynağında düzeltin.
systemd servisleri
Servisler locale değerini servis yöneticisinden alır, yönetici de bunu sistem locale dosyasından okur: /etc/locale.conf ya da hâlâ eski dosyayı kullanan Debian 12 gibi sürümlerde /etc/default/locale. systemctl show-environment komutu servislere aktarılan ortamı gösterir. Tek bir servise özel değer vermek için sudo systemctl edit uygulama.service komutuyla açılan dosyaya şunu yazın, ardından sudo systemctl restart uygulama.service ile servisi yeniden başlatın:
INI:
[Service]
Environment=LANG=C.UTF-8
Sistem locale'ini değiştirmeden önce çalışmaya başlamış servisler eski değeri yeniden başlatılana kadar taşır. Hangi süreçlerin etkilendiğinden emin değilseniz sunucuyu yeniden başlatmak en kesin yoldur.
tmux ve screen
tmux, sunucusu başladığında ortamı kendi genel ortamına (global environment) kopyalar; sonradan açılan her panel, pencere ve oturum bu kopyayı devralır. Bu yüzden Mac'i düzeltseniz bile LC_CTYPE=UTF-8 tmux içinde sürer. Açık kabuklarda unset LC_CTYPE, yeni paneller için tmux set-environment -gu LC_CTYPE komutunu çalıştırın ya da bütün oturumları kapatıp tmux'u yeniden başlatın. screen de oturumun başladığı andaki ortamı saklar; kaynağı düzelttikten sonra yeni bir screen oturumu açın.Doğrulama
Bash:
perl -e exit # sorun çözüldüyse hiçbir şey yazmaz
locale # "Cannot set" satırı olmamalı
env -u LC_ALL -u LC_CTYPE LANG=en_US.UTF-8 perl -e exit # bir locale'i kalıcı yapmadan önce deneyin
Kontrolü yeni bir SSH oturumunda, sudo ile ve sorunu yaşadığınız bağlamda (cron, servis, CustomBuild) tekrarlayın. Sessiz kalan bir perl -e exit, o bağlamdaki locale ayarının sağlam olduğunu gösterir.
Sık yapılan hatalar
- PERL_BADLANG=0 ile susturmak: perllocale belgesine göre bu yalnızca Perl'ün uyarısını gizler; bash, locale ve diğer programlar aynı bozuk ayarla çalışmaya devam eder.
- Sunucuda LANG değerini düzeltip istemciden gelen LC_CTYPE değişkenini unutmak: öncelik kuralı yüzünden LC_CTYPE kazanır.
- Değişken atamasını locale üretmek sanmak: LANG=en_US.UTF-8 yazmak hiçbir locale oluşturmaz; dnf install glibc-langpack-en veya locale-gen gerekir.
- ~/.ssh/config dosyasına SendEnv -LC_* eklemek: sistem dosyası daha sonra okunduğu için etkisi olmaz.
- Yeniden bağlanmamak: Mac'teki değişiklikler yalnızca yeni terminal pencerelerinde ve yeni SSH oturumlarında geçerli olur.
Sık sorulan sorular
Bu uyarı tehlikeli mi, derleme bozulur mu?Tek başına bir işi durdurmaz; Perl, uyarının son satırında adı geçen locale ile (yukarıdaki örnekte C) devam eder. Ancak oturumdaki locale'e duyarlı bütün araçlar aynı bozuk ayarla çalıştığı için Türkçe karakter içeren dosya adlarında ve metinlerde sorun çıkabilir. Düzeltmesi birkaç dakika sürer.
Neden yalnızca kendi Mac'imden bağlanınca görüyorum?
Çünkü değer sizin bilgisayarınızdan geliyor. Başka bir bilgisayar geçerli bir değer gönderiyor ya da hiç göndermiyor olabilir. ssh -F /dev/null testiyle doğrulayın.
en_US.UTF-8 mi, C.UTF-8 mi?
İkisi de UTF-8'dir. C.UTF-8 güncel Debian, Ubuntu ve RHEL tabanlı sistemlerde ek paket olmadan bulunur ve dile özgü kural içermez; servisler ve konteynerler için uygundur. en_US.UTF-8 ise tarih, sayı ve sıralamada ABD İngilizcesi kurallarını uygular; AlmaLinux/RHEL'de dil paketi, Debian/Ubuntu'da locale-gen adımı gerektirir.
Yeniden başlatmak gerekir mi?
SSH oturumu için hayır, yeniden bağlanmanız yeterli. Etkilenen servisleri yeniden başlatın; hangi süreçlerin eski değeri taşıdığını bilmiyorsanız sunucuyu yeniden başlatın.
Sorunu Mac'te mi, sunucuda mı çözmeliyim?
İkisinde de. Mac'teki düzeltme kaynağı ortadan kaldırır; sunucuda kurulu bir varsayılan locale ise cron, servisler ve diğer istemciler için gereklidir.
Sonuç ve sonraki adımlar
perl: warning: Setting locale failed uyarısı, ortamdaki bir locale adının sistemde bulunmadığını söyler; Mac'ten bağlananlarda bu ad genellikle LC_CTYPE=UTF-8 ya da sunucuda kurulu olmayan tr_TR.UTF-8'dir. Sırayla şunları yapın: Mac'te locale komutuyla kaynağı doğrulayın, ~/.zshrc dosyasına geçerli bir değer yazın, sunucuda glibc-langpack-en veya locale-gen ile locale'i kurup sistem varsayılanını localectl ya da update-locale ile ayarlayın ve yeni bir oturumda perl -e exit komutunun sessiz kaldığını görün. Birden fazla sunucuyu tutarlı biçimde yönetmek için destek arıyorsanız AIOR'un Linux sunucu yönetimi hizmeti seçeneklerden biri. Kendi kurulumunuza özel sorularınızı bu konuya yazabilirsiniz.If perl prints the warning below when you run ./build update, apt install or any Perl script over SSH, Perl itself is almost never the problem. One of your session's locale variables names a locale that the server does not have. The most common chain of events: the macOS terminal sets LC_CTYPE to the bare value UTF-8, SSH forwards it, Linux has no locale by that name, and Perl falls back to the C locale. This guide reads the warning line by line to find the root cause, then covers the fixes on the Mac, the server steps for AlmaLinux/Rocky/RHEL, Debian/Ubuntu and Docker, where to make the setting permanent, and the special cases: DirectAdmin CustomBuild, sudo, cron, systemd and tmux.
The error message
The output below was captured during a CustomBuild update on a DirectAdmin server:
Kod:
./build update
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = "UTF-8",
LANG = "C"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
Other programs report the same root cause in their own words. Bash prints -bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory at login, the locale command prints locale: Cannot set LC_CTYPE to default locale: No such file or directory, and dnf on AlmaLinux and Rocky prints Failed to set locale, defaulting to C.UTF-8. Perl 5.40 and later list every locale category, most of them as (unset); focus on the lines that carry a quoted value.
Reading the warning line by line
- Setting locale failed: at startup Perl asked the C library (glibc) to set the locale from the environment, and glibc refused. At least one variable names a locale that is not installed.
- Please check that your locale settings: the variables Perl looked at, in decreasing order of importance. (unset) means the variable is not defined, which is harmless.
- LANGUAGE: a GNU extension that only orders message translations. It plays no part in choosing the locale.
- LC_ALL: overrides everything else when set. It is unset here, so it is not the culprit.
- LC_CTYPE = "UTF-8": the actual problem. On Linux a locale name looks like language_TERRITORY.codeset: en_US.UTF-8, tr_TR.UTF-8, C.UTF-8. UTF-8 on its own is the name of a character encoding, not a locale. macOS ships a /usr/share/locale/UTF-8 directory, so the same value is valid on the Mac and invalid once it reaches Linux.
- LANG = "C": the default for every category that is not set separately. The C locale always exists, so LANG is fine.
- Falling back to the standard locale ("C"): Perl tries LC_ALL, then LANG, then C, and continues with the first one that works. Had LANG held an installed locale such as en_US.UTF-8, the line would read Falling back to a fallback locale ("en_US.UTF-8"). The program keeps running, but locale-aware tools now follow C rules, so the display of non-ASCII text, sorting and case conversion can change.
One rule drives every fix in this guide: LC_ALL beats the individual categories such as LC_CTYPE, and the individual categories beat LANG. Setting LANG correctly on the server therefore does nothing about a bad LC_CTYPE coming from the client.
Root cause: the macOS terminal and SSH environment forwarding
With "Set locale environment variables on startup" enabled, macOS Terminal derives LANG from your system language and region: English with United States gives en_US.UTF-8, Turkish with Türkiye gives tr_TR.UTF-8. Apple does not document what happens when the combination has no matching locale, but users report that Terminal then leaves LANG unset and sets only LC_CTYPE=UTF-8. macOS has no en_TR locale, so if you run an English interface with the region set to Türkiye, or a similar mix such as English with Germany, you are probably in this situation; the diagnosis below confirms it.The SSH client is the second link in the chain. The system-wide macOS SSH configuration contains SendEnv LANG LC_* (in /etc/ssh/ssh_config.d/100-macos.conf on current releases), which sends those variables on every connection. Whether the server accepts them depends on its AcceptEnv lines:
| Server | Default AcceptEnv | LC_CTYPE from the Mac |
|---|---|---|
| Debian, Ubuntu | LANG LC_* (newer releases add COLORTERM NO_COLOR) | Accepted |
| AlmaLinux, Rocky, RHEL 8 | LANG, LANGUAGE, LC_ALL and each LC_* category by name | Accepted |
| AlmaLinux, Rocky, RHEL 9 and 10 | None in the default configuration | Ignored, unless a control panel, image or admin added AcceptEnv |
If your Mac is fully Turkish (Turkish language, Türkiye region), Terminal sets LANG=tr_TR.UTF-8 and SSH forwards that as well. On a server without the Turkish locale, which minimal installs usually lack, the same warning appears with LANG = "tr_TR.UTF-8". The fix follows the same logic: install the Turkish locale on the server or change what the Mac sends.
Forwarding is not the only source. If /etc/locale.conf or a profile script on the server names a locale that is not installed (en_US.UTF-8 configured, language pack missing), you get the same warning no matter which client you connect from. Minimal cloud images and containers run into this often. The diagnosis below separates the two cases.
Diagnosis: which value, and where does it come from?
Start on the server:
Bash:
locale # broken settings print "Cannot set LC_CTYPE ..." first
locale -a # installed locales; en_US.utf8 is the same as en_US.UTF-8
echo "LANG=$LANG LC_ALL=$LC_ALL LC_CTYPE=$LC_CTYPE"
localectl status # system default on systemd distributions
sudo sshd -T | grep -i acceptenv
In the Linux locale output, unquoted values were set explicitly; quoted ones are derived from LC_ALL or LANG (or shown as POSIX when neither is set). If locale -a lists only en_US.utf8, do not go looking for an en_US.UTF-8 package: glibc normalizes the codeset name, and both spellings refer to the same locale.
Then confirm the source on the Mac:
Bash:
locale # LC_CTYPE="UTF-8" means the Mac is the source
ssh -v user@server exit 2>&1 | grep -i env # variables the client sends
ssh -F /dev/null user@server # reads no config file, sends no locale variables
With -v the OpenSSH client logs a line such as channel 0: setting env LC_CTYPE = "UTF-8" for each variable it sends. If the warning disappears in the -F /dev/null session, forwarding is confirmed. That option also skips your ~/.ssh/config, so you may need -p and -i for the port and key. If the value comes neither from the Mac nor from the system default, search the server's startup files:
Bash:
grep -rns 'LC_CTYPE\|LC_ALL\|LANG=' ~/.bashrc ~/.bash_profile ~/.profile /etc/environment /etc/profile /etc/profile.d/ /etc/locale.conf /etc/default/locale
Client-side fixes (macOS)
1. Use a valid value on the Mac (recommended)
In Terminal, open Terminal > Settings > Profiles, select your profile and switch to the Advanced tab. The "Set locale environment variables on startup" option there produces a correct value when your language and region form a real locale; with a mixed combination, turn it off and set the value yourself. iTerm2 has the same choice under Profiles > Terminal > Environment, where "Use custom locale..." lets you pick en_US.UTF-8. Whatever terminal you use, the most reliable fix is an explicit value in your shell startup file (zsh has been the default shell since macOS Catalina; bash users edit ~/.bash_profile):
Bash:
# ~/.zshrc (Mac)
export LANG="en_US.UTF-8"
export LC_CTYPE="en_US.UTF-8"
Open a new terminal window and check with locale. For the terminal you use to administer servers, en_US.UTF-8 is a safer choice than tr_TR.UTF-8; the persistence section explains why. If the server lacks en_US.UTF-8, install it with the distribution steps below.
2. Stop sending the variables
Bash:
grep -rn SendEnv /etc/ssh/ssh_config /etc/ssh/ssh_config.d/ ~/.ssh/config 2>/dev/null
sudo nano /etc/ssh/ssh_config.d/100-macos.conf # put # in front of the SendEnv line
If the line is in your own ~/.ssh/config, delete it. Editing the system file needs sudo, and a major macOS upgrade may bring the default back. Adding SendEnv -LC_* to ~/.ssh/config does not help: ssh reads the user file before the system file, and the minus prefix only removes names that were added earlier.
3. Remove AcceptEnv on the server
When several admins connect from different machines, you can stop the server from accepting locale variables instead of fixing every client:
Bash:
sudo grep -rn AcceptEnv /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
sudo nano /etc/ssh/sshd_config # comment out the AcceptEnv lines
sudo sshd -t && sudo systemctl reload sshd # the service is called ssh on Debian/Ubuntu
Keep your current session open and test with a new connection before you log out. The trade-off: users can no longer bring their own language settings from the client.
4. Clean up only the bad value
If you would rather not touch the sshd configuration, a small login script on the server can drop the known-bad value:
Bash:
# /etc/profile.d/zz-locale-fix.sh (for your account only: ~/.bashrc)
if [ "$LC_CTYPE" = "UTF-8" ]; then
unset LC_CTYPE
fi
| Method | Pros | Cons |
|---|---|---|
| Valid value on the Mac | Fixes the source, for every server at once | The server needs that locale installed |
| Disable SendEnv | The server always applies its own default | Needs sudo; a macOS upgrade may undo it |
| Remove AcceptEnv | Independent of broken clients, consistent for all users | Affects everyone; an sshd mistake can lock you out |
| profile.d cleanup | Touches only the known-bad value | Shell sessions only, not cron or services |
Server-side fixes: install the missing locale
AlmaLinux, Rocky Linux, RHEL (8, 9, 10)
Locale data ships in language packs. Container images and some minimal installs have only glibc-minimal-langpack, which provides C, POSIX and C.UTF-8 and nothing else.
Bash:
sudo dnf install glibc-langpack-en # English locales, including en_US.UTF-8
sudo dnf install glibc-langpack-tr # if you need Turkish
# sudo dnf install glibc-all-langpacks # every language; much larger
sudo localectl set-locale LANG=en_US.UTF-8
localectl status
localectl writes /etc/locale.conf and also updates the systemd service manager. On current releases, including RHEL 8.4 and later, setting a locale that is not installed fails with "Locale en_US.UTF-8 not installed, refusing." Install the package first. New sessions and restarted services pick up the change.
Debian and Ubuntu
Bash:
sudo apt install locales # may be missing on minimal installs and images
sudo dpkg-reconfigure locales # interactive: choose locales and the default
# Non-interactive:
sudo sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
sudo locale-gen
sudo update-locale LANG=en_US.UTF-8
Debian's locale-gen reads only /etc/locale.gen; Ubuntu's version also accepts sudo locale-gen en_US.UTF-8 and adds the line itself. update-locale rejects invalid values and writes the system default: /etc/default/locale on Debian 12 and Ubuntu 22.04, /etc/locale.conf on Debian 13 and Ubuntu 24.04, where the old path stays as a symlink to the new file.
Docker and containers
A container has no localectl and no login process; the default comes from ENV in the image. If you do not need language-specific rules, C.UTF-8 is the simplest choice and needs no extra package on current Debian, Ubuntu and RHEL-based images:
Kod:
FROM debian:12-slim
ENV LANG=C.UTF-8
If you need a specific language, generate the locale in the image and verify it during the build:
Kod:
FROM debian:12-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends locales \
&& sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
&& locale-gen \
&& locale -a | grep -qi '^en_US.utf8$' \
&& rm -rf /var/lib/apt/lists/*
ENV LANG=en_US.UTF-8
On AlmaLinux or Rocky-based images, RUN dnf -y install glibc-langpack-en && dnf clean all followed by ENV LANG=en_US.UTF-8 is enough. If apt prints the same Perl warning during the build, ENV LANG was most likely set before the locale was generated.
Persistence: session, user, system
| Level | Where | Scope |
|---|---|---|
| Session | export or unset in the current shell | That shell and its child processes; gone at logout |
| User | ~/.bashrc, ~/.bash_profile (server), ~/.zshrc (Mac) | That user's new sessions |
| System | /etc/locale.conf (localectl, update-locale) or /etc/default/locale | New logins and services |
| Service | Environment= in a systemd drop-in | One service |
| Cron | LANG= at the top of the crontab | Jobs in that crontab |
| Container | ENV in the Dockerfile | Every process in the image |
Setting LC_ALL permanently in /etc/environment, /etc/profile or ~/.bashrc looks like a quick fix. It is a bad idea:
- LC_ALL overrides every category, so users and services can no longer change just one of them, such as the date format.
- LC_ALL is meant for temporary, per-command use, as in LC_ALL=C sort when a script needs predictable output. The perllocale documentation also warns that setting LC_ALL may affect other programs as well, not just Perl.
- If the value names a locale that is not installed (a language pack removed, the file copied to another server or into a container), every program on the system starts warning.
- It hides the real fault: the Mac keeps sending LC_CTYPE=UTF-8 to every other server.
A note for Turkish setups: making tr_TR.UTF-8 the server default brings its own surprises. Under glibc's Turkish rules, i uppercases to İ and I lowercases to ı, which can trip up scripts and programs that assume ASCII case mapping. The decimal separator is a comma, so with LC_NUMERIC=tr_TR.UTF-8 the bash builtin printf "%.2f" 3.14 fails with "invalid number". Keep LANG=en_US.UTF-8 or C.UTF-8 on servers, and let a user who wants Turkish dates set only LC_TIME=tr_TR.UTF-8 in their own profile.
Special cases
DirectAdmin CustomBuild
On DirectAdmin servers the warning often shows up while running commands such as ./build update in /usr/local/directadmin/custombuild (current DirectAdmin documentation exposes the same interface as da build). A build runs many external tools, and every Perl program among them prints the warning again, so it can repeat many times in one run. It does not stop the build, but it buries real errors in noise. Until the permanent fix is in place, a per-command value such as LC_ALL=C.UTF-8 ./build update is fine; that is exactly what LC_ALL is for. If the warning appears in emails from DirectAdmin's own scheduled tasks, the cause is most likely the system default and the installed language packs rather than your SSH session. For the update steps themselves, see Running CustomBuild Update via SSH for DirectAdmin.sudo and root
By default sudo keeps LANG, LANGUAGE and LC_* (they are on sudo's env_check list, which passes a value through unless it contains / or %), so the Mac's LC_CTYPE=UTF-8 follows you into sudo ./build update or a sudo -i root shell. su - starts a login shell and clears the environment apart from TERM and a few other terminal variables. If the warning disappears with su - but stays with sudo, the session variables are the source; treat that as a clue, not a fix. For the difference between working as root and using sudo, see Fixing the "This Command Must Be Run As Root" Error.cron
Cron jobs never see your SSH session's variables; they start with a minimal environment (on Debian and Ubuntu, cron also loads /etc/environment and the system locale file through PAM). If the warning shows up in cron mail or logs, capture what the job actually sees, then delete the temporary line:
Kod:
* * * * * env > /tmp/cron-env.txt 2>&1
If you find a bad or missing locale, add LANG=C.UTF-8 (or another installed locale) at the top of the crontab with crontab -e, or fix the script that sets it. If the bad value sits in LC_ALL or LC_CTYPE (for example from /etc/environment), LANG cannot override it; fix that variable at its source.
systemd services
Services inherit the locale from the service manager, which reads the system locale file: /etc/locale.conf, or /etc/default/locale on releases that still use the older file, such as Debian 12. systemctl show-environment prints the environment that services receive. To give a single service its own value, run sudo systemctl edit myapp.service, add the lines below, then restart it with sudo systemctl restart myapp.service:
INI:
[Service]
Environment=LANG=C.UTF-8
Services that were already running when you changed the system locale keep the old value until they restart. If you cannot tell which processes are affected, a reboot is the surest way.
tmux and screen
When its server starts, tmux copies the environment into its global environment, and every pane, window or session created later inherits that copy, so LC_CTYPE=UTF-8 survives inside tmux even after you fix the Mac. Run unset LC_CTYPE in open shells and tmux set-environment -gu LC_CTYPE for new panes, or close every session so the tmux server exits, then start again. screen also keeps the environment from when the session started, so open a new screen session after fixing the source.Verification
Bash:
perl -e exit # prints nothing once the problem is fixed
locale # no "Cannot set" lines
env -u LC_ALL -u LC_CTYPE LANG=en_US.UTF-8 perl -e exit # test a locale before making it permanent
Repeat the check in a new SSH session, under sudo, and in the context where you saw the problem (cron, a service, CustomBuild). A silent perl -e exit means the locale settings in that context are sound.
Common mistakes
- Silencing it with PERL_BADLANG=0: perllocale says this only hides Perl's message; bash, locale and every other program keep running with the broken setting.
- Fixing LANG on the server and forgetting the forwarded LC_CTYPE: because of precedence, LC_CTYPE wins.
- Mistaking a variable assignment for locale generation: writing LANG=en_US.UTF-8 creates nothing; you need dnf install glibc-langpack-en or locale-gen.
- Adding SendEnv -LC_* to ~/.ssh/config: the system file is read afterwards, so the line has no effect.
- Not reconnecting: changes on the Mac apply only to new terminal windows and new SSH sessions.
FAQ
Is the warning dangerous? Will my build fail?Not by itself; Perl carries on with the locale named on the last line of the warning (C in the example above). But every locale-aware tool in that session runs with the same broken setting, so file names and text with non-ASCII characters can misbehave. The fix takes a few minutes.
Why do I only see it when I connect from my Mac?
Because the value comes from your machine. Another computer may send a valid value or none at all. Confirm it with the ssh -F /dev/null test.
en_US.UTF-8 or C.UTF-8?
Both are UTF-8. C.UTF-8 is present without extra packages on current Debian, Ubuntu and RHEL-based systems and has no language-specific rules, which suits services and containers. en_US.UTF-8 applies US English conventions for dates, numbers and sorting, and needs the language pack on AlmaLinux/RHEL or the locale-gen step on Debian/Ubuntu.
Do I need to reboot?
Not for SSH sessions; reconnecting is enough. Restart the affected services, and reboot only if you cannot tell which processes still carry the old value.
Should I fix the Mac or the server?
Both. Fixing the Mac removes the source, while an installed default locale on the server keeps cron, services and other clients working.
Conclusion and next steps
perl: warning: Setting locale failed means that a locale named in the environment does not exist on the system, and for Mac users that name is usually LC_CTYPE=UTF-8, or a tr_TR.UTF-8 the server lacks. Work through it in order: confirm the source with locale on the Mac, put a valid value in ~/.zshrc, install the locale on the server with glibc-langpack-en or locale-gen, set the system default with localectl or update-locale, and check that perl -e exit stays silent in a new session. If you would like help keeping a fleet of servers consistent, AIOR's Linux server management service is one option. Questions about your own setup are welcome in this thread.
Son düzenleyen: