İleri
uygulama-iskeleti/php-baslangic-sablonu
PHP Başlangıç Şablonu (CY PHP Starter)
- PHP 8.1
- PDO
- MySQL
- MariaDB
- +4
309 inceleme 150 canlı açılış 6 beğeni 2 yorum
Uygulama İskeleti Orta
Üç dil, üç ayrı çoğul kuralı ve sağdan sola yazılan bir arayüz. intl ile ICU çoğul kuralları, yer tutucular, yerelleştirilmiş sayı/tarih/para ve ayrı CSS gerektirmeyen RTL desteği — oturum girişli bir panelin içinde.
Veritabanı şeması projedeki database.sql dosyasında.
"Siteyi çok dilli yapalım" cümlesi, çoğu projede bir dizi dosyası açıp $lang['kaydet'] = 'Kaydet'; yazmakla başlar ve orada biter. Sonra ilk gerçek cümle gelir — "5 kayıt bulundu" — ve sistem çöker. Çünkü:
if ($n == 1) yazan kod İngilizce için doğru, Türkçe için gereksiz, Arapça için tamamen yanlıştır. Kural dile aittir; koda değil.
Bu proje o kuralı dilin kendisine bırakan bir i18n katmanı kuruyor: çeviriler PHP dizisi olarak lang/<dil>/ altında durur, çoğul biçimi intl eklentisinin MessageFormatter'ı seçer, sayı/tarih/para biçimleri NumberFormatter ve IntlDateFormatter ile yerelleştirilir ve arayüz Arapça'ya geçtiğinde tek bir ek CSS dosyası olmadan sağdan sola döner.
Dahası, bunların hepsi oturum girişli, gerçek bir panelin içinde çalışır: kullanıcının dil tercihi hesabına yazılır, koyu tema tercihiyle birlikte cihazdan cihaza taşınır.
Kimler için uygun?
if olmadığını öğrenmek isteyenlerintl eklentisiyle iş görmek isteyenlerKlonla, database.sql'i içe aktar, çalıştır. Başka hiçbir kurulum adımı yok. Composer yok, npm yok, internet bağlantısı bile gerekmiyor — tüm kütüphaneler proje içinde.
Bu proje, Çılgın Yazılım Kütüphanesi altında yayınlanan açıklamalı, üretime hazır örneklerden biridir.
Bu bölüm, "neden böyle yapılmış?" sorusunun cevabıdır. Her madde bilerek verilmiş bir karardır.
if değil, intl belirlerYanlış olan:
$metin = $n == 1 ? '1 kayıt' : "$n kayıtlar";echo n('common.records', $count);MessageFormatter çalışır ve çeviri dosyasındaki ICU kalıbını kullanır:
// lang/ar/common.php
'records' => '{n, plural, zero{لا توجد سجلات} one{سجل واحد} two{سجلان} few{# سجلات} many{# سجلاً} other{# سجل}}',zero, one, two, few, many, other) burada görünür. Türkçe dosyasında yalnızca other vardır — çünkü Türkçe'nin ihtiyacı odur. Kod hiçbir dilin kuralını bilmez.
Yanlış olan:
echo t('welcome_prefix') . ' ' . $user->name . '!';echo t('common.auth.welcome', ['name' => $user->name]);Translator::resolve() şu sırayı izler:
?lang= → hesap tercihi → çerez → tarayıcı (Accept-Language) → varsayılanGelen değer her adımda Translator::supports() ile beyaz listeden geçer; Translator::LOCALES dizisinde tanımlı olmayan bir dil kodu asla kabul edilmez. Aksi hâlde ?lang=../../etc/passwd bir dosya yolu hâline gelirdi.
Yalnızca çerezde tutulsaydı, kullanıcı işten eve geçtiğinde veya çerezleri temizlediğinde tercihi kaybolurdu. Yalnızca hesapta tutulsaydı, giriş yapmamış ziyaretçi dil seçemezdi.
İkisi birden: ziyaretçi çerezle, üye hesabıyla taşınır. Giriş anında çerezdeki seçim hesaba yazılır.
Çoğu örnekte dil seçici sizi ana sayfaya atar. Bu, 8. sayfadaki bir listeyi okurken dili değiştirdiğinizde okuduğunuz yeri kaybetmeniz demektir.
Burada seçici, bulunduğunuz adrese ?lang= ekler. Sorgu dizesindeki diğer parametreler (page, q, per) korunur.
left/right yerine mantıksal özellikler kullanılır:
/* Yanlış: Arapça'da ters tarafa gider */
.cy-card { padding-left: 1rem; border-left: 3px solid; }
/* Doğru: yön neyse ona uyar */
.cy-card { padding-inline-start: 1rem; border-inline-start: 3px solid; }<html dir="rtl"> yazıldığı anda düzen kendiliğinden aynalanır. Bakımı iki kat artıran bir rtl.css dosyası yoktur.
intl yoksa uygulama çökmezintl eklentisi PHP'de varsayılan olarak açık değildir. Kapalıysa MessageFormatter bulunamaz ve uygulama ölürdü.
Bunun yerine Translator her yerelleştirme çağrısında eklentinin varlığını denetler ve yoksa sade bir yedeğe düşer: çoğulda other biçimi, sayıda number_format(), tarihte date(). Site çalışmaya devam eder, yalnızca biçimler sadeleşir. Kontrol panelindeki "İNTL" satırı hangi modda olduğunuzu söyler.
Çeviri katmanı
t() — metin çevirisi, yer tutuculute() — çevir ve HTML'e kaçışlayarak basn() — çoğul kuralına uyan çeviricommon.nav.users)Yerelleştirme
intl ile çoğul kuralları (ICU)NumberFormatter ile sayı ve paraIntlDateFormatter ile tarih ve saatintl yoksa otomatik sade yedekPanel ve güvenlik
login_attempts)hash_equals)HttpOnly, SameSite, kimlik yenilemescript-src 'self')X-Frame-Options: DENY, nosniff, Referrer-PolicyArayüz
Çok dillilik tek bir iş değildir; birbirine benzeyen ama farklı kuralları olan dört iştir. Uygulama dördünü de ayrı ayrı ele alır.
1 · Metin çevirisi. En basit hâli: anahtar ver, çevrilmiş metni al.
echo t('common.save'); // Kaydet / Save / حفظecho t('common.auth.welcome', ['name' => $user->name]);echo n('common.records', $count);echo local_number(1234567.89, 2); // 1.234.567,89 / 1,234,567.89
echo local_date($tarih); // 3 Eylül 2026 / September 3, 2026
echo local_money(199.90, 'TRY'); // ₺199,90 / ₺199.90
Bir tablo her şeyi anlatır. n('common.records', $count) çağrısının üç dildeki çıktısı:
$count | Türkçe | İngilizce | Arapça |
|---|---|---|---|
| 0 | Kayıt yok | No records | لا توجد سجلات |
| 1 | 1 kayıt | 1 record | سجل واحد |
| 2 | 2 kayıt | 2 records | سجلان |
| 5 | 5 kayıt | 5 records | 5 سجلات |
| 11 | 11 kayıt | 11 records | 11 سجلاً |
| 100 | 100 kayıt | 100 records | 100 سجل |
Dikkat edilecek üç nokta:
سجلان ikili biçimdir, tekil de çoğul da değildir.few, many, other).Bunların hiçbiri PHP kodunda yazılı değildir. Üçü de çeviri dosyasındaki ICU kalıbından gelir.
İstek gelir
│
├── 1) ?lang=en var mı? → beyaz listede mi? → evet ise KULLAN
│ (kullanıcı az önce açıkça istedi)
│
├── 2) Giriş yapmış mı? → users.locale dolu mu? → KULLAN
│ (hesabına kaydedilmiş tercih)
│
├── 3) cy_locale çerezi var mı? → beyaz listede mi? → KULLAN
│ (giriş yapmamış ziyaretçinin önceki seçimi)
│
├── 4) Accept-Language başlığı → desteklenen bir dille eşleşiyor mu? → KULLAN
│ (tarayıcının tahmini)
│
└── 5) Hiçbiri yoksa → varsayılan dil (tr)Translator::LOCALES dizisindeki anahtarlarla karşılaştırılır (Translator::supports()). Listede olmayan hiçbir değer kabul edilmez — dil kodu bir dosya yoluna dönüştüğü için bu kontrol güvenliğin parçasıdır.
Dil seçici, o anki adresi alır ve yalnızca lang parametresini değiştirir:
// views/partials/langswitch.php
<a href="<?= e(lang_url($kod)) ?>">…</a>lang_url() yardımcısı o anki adresi alır, sorgu dizesindeki diğer parametreleri korur ve yalnızca lang değerini değiştirir.
?page=8&q=ahmet ile geldiyseniz dili değiştirdikten sonra da 8. sayfada, aynı aramadasınızdır.
Arapça, Farsça ve İbranice sağdan sola yazılır. Bu, "metni sağa yasla" demek değildir; bütün düzenin aynalanması demektir.
<html> etiketine dir yazılır:
<html lang="<?= e($locale) ?>" dir="<?= e($direction) ?>">Translator::direction() dil koduna bakar ve rtl veya ltr döner. Yön, dilin Translator::LOCALES içindeki kaydında durur:
'ar' => ['name' => 'Arapça', 'native' => 'العربية', 'dir' => 'rtl', 'flag' => 'AR'],| Fiziksel (kaçının) | Mantıksal (kullanın) |
|---|---|
margin-left | margin-inline-start |
padding-right | padding-inline-end |
border-left | border-inline-start |
text-align: left | text-align: start |
left: 0 | inset-inline-start: 0 |
Mantıksal özellikler "sol/sağ" değil, "başlangıç/bitiş" der. Başlangıç, LTR'de soldur; RTL'de sağdır. Böylece tek bir kural iki yönde de doğru çalışır.
Kaçınılmaz istisnalar için (örneğin yön belirten bir ok simgesi) tek satırlık bir kural yeterlidir:
[dir="rtl"] .cy-arrow { transform: scaleX(-1); }
Üç adım. Kod değişikliği yok.
1 · Klasörü oluşturun ve çeviriyi kopyalayın
mkdir -p lang/de
cp lang/en/common.php lang/de/common.phpÇoğul kalıplarında o dilin kategorilerini kullanın. Almanca'da İngilizce gibi iki kategori vardır:
'records' => '{n, plural, zero{Keine Einträge} one{1 Eintrag} other{# Einträge}}',Desteklenen diller Translator::LOCALES dizisinde durur. Her kayıt dilin
adını, kendi dilindeki adını, metin yönünü ve seçicide görünecek kısa
kodunu taşır:
// app/Core/Translator.php
public const LOCALES = [
'tr' => ['name' => 'Türkçe', 'native' => 'Türkçe', 'dir' => 'ltr', 'flag' => 'TR'],
'en' => ['name' => 'İngilizce','native' => 'English', 'dir' => 'ltr', 'flag' => 'EN'],
'ar' => ['name' => 'Arapça', 'native' => 'العربية', 'dir' => 'rtl', 'flag' => 'AR'],
'de' => ['name' => 'Almanca', 'native' => 'Deutsch', 'dir' => 'ltr', 'flag' => 'DE'],
];'dir' => 'rtl' yazmanızTranslator::supports() beyaz listesi de bu diziden türer.
4 · users.locale sütununu genişletin
Bu adım atlanırsa yeni dil görünür ama hesaba kaydedilemez:
ALTER TABLE `users`
MODIFY COLUMN `locale` ENUM('tr','en','ar','de') NOT NULL DEFAULT 'tr';ENUM olduğunu ve ödünleşiminiEksik anahtar bırakırsanız ne olur? Uygulama çökmez; ekranda anahtarın kendisi görünür (common.save). Bu bilerek seçilmiştir: boş bir metin sessizce kaybolur, anahtar ise "burada eksik çeviri var" diye bağırır.
| Açık | Tipik hatalı kod | Bu projede |
|---|---|---|
| Yol geçişi (dil kodu) | require "lang/$_GET[lang]/common.php" | Dil kodu SUPPORTED beyaz listesinden doğrulanır; listede yoksa varsayılana düşer |
| SQL enjeksiyonu | "... WHERE email = '$email'" | Tüm sorgular hazır ifade; ATTR_EMULATE_PREPARES = false |
| Sıralama sütunu enjeksiyonu | ORDER BY $_GET[sort] | Sütun adı beyaz listeden; yön yalnızca ASC/DESC |
| XSS | echo $user->name | Sunucuda e(), çeviride te(); ayrıca CSP script-src 'self' |
| CSRF | Gizli alan yok | Her POST'ta jeton; karşılaştırma hash_equals() ile (zamanlama saldırısına kapalı) |
| Parola sızıntısı | md5($parola) | password_hash() / password_verify(); algoritma eskiyince otomatik yeniden özetleme |
| Kullanıcı sayımı | "Böyle bir e-posta yok" | Hem yanlış e-postada hem yanlış parolada aynı mesaj ve aynı süre (sahte özet doğrulaması) |
| Kaba kuvvet | Sınırsız deneme | login_attempts tablosuyla e-posta + IP başına hız sınırı |
| Oturum çalma | Sabit oturum kimliği | Girişte kimlik yenilenir; çerez HttpOnly + SameSite=Lax + HTTPS'te Secure |
| Hata sızıntısı | Canlıda display_errors=On | APP_DEBUG ortamdan türetilir; gerçek alan adında kendiliğinden kapanır |
| Tıklama hırsızlığı | Başlık yok | X-Frame-Options: DENY |
| Bozuk UTF-8'de sessiz JSON kaybı | json_encode($v) | JSON_INVALID_UTF8_SUBSTITUTE — bozuk bayt tüm yanıtı yutmaz |
Bütün ayarlar config/config.php içinde tek bir dizide toplanır; hassas değerler .env dosyasından okunur.
APP_DEBUG=true # silerseniz: yerelde açık, canlıda kapalı
APP_URL= # boş bırakılırsa adres otomatik tespit edilir
APP_PRETTY_URLS=true # false → /index.php?r=users
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=cy_i18n
DB_USER=root
DB_PASS=
••••••••
> **`.env` dosyası `.gitignore` içindedir.** Parolanız asla depoya gitmez. `APP_DEBUG` satırını tümüyle silerseniz uygulama ortamdan karar verir: `localhost`, `*.test` ve `*.local` adreslerinde açık, gerçek bir alan adında kapalı.
**mod_rewrite yoksa:** `.env` içinde `APP_PRETTY_URLS=false` yapın. Uygulama hiçbir kod değişikliği olmadan `/index.php?r=users` biçimine döner.
---
## Kendi Projenize Eklemek
Çeviri katmanı üç dosyadan ibarettir ve panelden bağımsız çalışır.
**1 · Kopyalayın**
**2 · İsteğin başında dili belirleyin.** Bu, **görünümlerden önce** olmalıdır; yoksa ilk basılan metinler varsayılan dilde kalır.
```php
$locale = Translator::resolve($request);
Translator::setLocale($locale);<html> etiketine yön yazın
<html lang="<?= e($locale) ?>" dir="<?= e(Translator::direction($locale)) ?>">// önce
<h1>Kullanıcılar</h1>
// sonra
<h1><?= te('common.nav.users') ?></h1>Translator sınıfının veritabanına bağımlılığı yoktur; yalnızca dosya okur. Kullanıcının tercihini hesabında saklamak isterseniz users tablosuna bir locale sütunu ekleyin.
multi-language-system/
│
├── index.php Ön denetleyici — TEK giriş noktası
├── database.sql Şema + 51 örnek kullanıcı
├── .env.example Ortam değişkeni şablonu
│
├── app/
│ ├── Core/
│ │ ├── Translator.php ★ Çeviri motoru, çoğul, biçimlendirme, RTL
│ │ ├── Auth.php Giriş, "beni hatırla", parola doğrulama
│ │ ├── Session.php Sertleştirilmiş oturum
│ │ ├── Csrf.php Jeton üretimi ve hash_equals doğrulaması
│ │ ├── RateLimiter.php Giriş denemesi sınırı
│ │ ├── Router.php Rota eşleme
│ │ ├── Database.php PDO bağlantısı (EMULATE_PREPARES = false)
│ │ ├── Paginator.php Sayfalama hesabı
│ │ ├── Env.php .env okuyucu + isLocalHost()
│ │ └── ...
│ │
│ ├── Http/Controllers/ Auth · Dashboard · I18n · User · Api
│ ├── Models/User.php
│ ├── Repositories/ Veritabanı sorguları
│ └── Support/helpers.php t() te() n() local_* lang_url() e() url()
│
├── lang/
│ ├── tr/common.php 73 anahtar
│ ├── en/common.php 73 anahtar
│ └── ar/common.php 73 anahtar (altı çoğul kategorisiyle)
│
├── views/
│ ├── layouts/ admin · auth · plain
│ ├── partials/ topbar · sidebar · bottomnav · langswitch · pagination
│ ├── auth/login.php
│ ├── dashboard/index.php
│ ├── i18n/index.php ★ Dil örnekleri sayfası
│ ├── users/index.php
│ └── errors/ 404 · 500
│
├── assets/
│ ├── css/ cilginyazilim.css (marka) · admin.css · feature.css · bootstrap.min.css
│ └── js/ app.js · lang.js · login.js · users.js · jquery · bootstrap
│
├── config/config.php Tek merkez ayarlar
├── routes/web.php Rota tanımları
└── docs/screenshots/ README görselleri
Tarayıcı
│ GET /multi-language-system/i18n?lang=ar
▼
.htaccess ──► index.php (ön denetleyici)
│
├─ 1. Autoloader sınıfları yükle
├─ 2. .env + config veritabanı künyesi, hata modu
├─ 3. Hata yönetimi yakalanmamış hata → düzgün 500 sayfası
├─ 4. Session::start() sertleştirilmiş oturum
├─ 5. Güvenlik başlıkları CSP · X-Frame-Options · nosniff
│
├─ 6. DİL SEÇİMİ ◄── ROTALARDAN ÖNCE OLMALI
│ Translator::resolve()
│ ?lang= → hesap → çerez → tarayıcı → varsayılan
│ Translator::setLocale('ar')
│ └─ lang/ar/common.php belleğe alınır
│
├─ 7. View::share() locale + direction bütün görünümlere
│
└─ 8. Router::dispatch()
│
▼
I18nController::index()
│
▼
views/i18n/index.php
│ t('...') n('...', 5) fmt_money(...)
▼
Translator
│
├─ intl VAR → MessageFormatter · NumberFormatter · IntlDateFormatter
└─ intl YOK → other biçimi · number_format() · date()
│
▼
layouts/admin.php
<html lang="ar" dir="rtl"> ◄── düzen aynalanır
Bu proje yeni bir tablo eklemez; var olan users tablosuna bir sütun ekler.
ALTER TABLE `users`
ADD COLUMN `locale` ENUM('tr','en','ar') NOT NULL DEFAULT 'tr' AFTER `theme`;
| Karar | Neden |
|---|---|
| Dil tercihi hesapta tutuluyor | Yalnızca çerezde olsaydı kullanıcı başka tarayıcıya geçtiğinde ya da çerezleri temizlediğinde tercihi kaybolurdu |
Sütun ENUM, serbest metin değil | Veritabanı, uygulamanın beyaz listesini kendisi de zorlar. Kod bir yerde doğrulamayı atlasa bile desteklenmeyen bir dil kodu tabloya yazılamaz |
Varsayılan 'tr', NULL değil | NULL "tercih yok" demek olurdu ve her okumada fazladan bir kontrol gerektirirdi |
| Çeviriler veritabanında değil | Çeviri metni kod kadar sık değişir ve sürüm kontrolünde durmalıdır; her sayfa açılışında sorgu atmanın da anlamı yok |
ENUM'un bedeli: yeni bir dil eklemekALTER TABLEgerektirir. Bu bilinçli
bir ödünleşimdir — dil listesi yılda birkaç kez değişen, küçük ve kapalı bir
kümedir; buna karşılık veritabanı ikinci bir doğrulama katmanı olur. Dil
listeniz sık ve dinamik olarak değişecekse sütunuCHAR(5)yapıp doğrulamayı
tümüyleTranslator::supports()'a bırakın.
Diğer tablolar panelin ortak altyapısıdır:
| Tablo | İşi |
|---|---|
users | Hesaplar · parola özeti · tema ve dil tercihi |
remember_tokens | "Beni hatırla" jetonları (özetlenmiş) |
login_attempts | Hız sınırı için deneme kayıtları |
activity_log | Panelde gösterilen son işlemler |
Çevirileri veritabanında tutsam olmaz mı?
Olur ama önce şunu sorun: çeviri metni kim değiştirecek?
Cevap "geliştirici" ise dosya daha iyidir. Çeviri o zaman kodla birlikte sürümlenir, kod incelemesinden geçer, geri alınabilir ve her sayfa açılışında sorgu maliyeti doğurmaz.
Cevap "yönetici panelinden içerik ekibi" ise veritabanı gerekir. O durumda dosyaları önbellek katmanı gibi kullanın: veritabanından okuyup PHP dizisi olarak diske yazın, uygulama diskten okusun.
Bu projede birinci senaryo varsayıldı.
t() mi te() mi kullanmalıyım?
te() kullanın — çevirir ve HTML'e kaçışlar.
t() yalnızca metni döndürür. Bunu doğrudan echo ederseniz, çeviri dosyasına HTML girerse (veya yer tutucuya kullanıcı verisi geçerse) XSS açığı doğar.
t()'yi metni bir değişkene alıp işlemek gerektiğinde, te()'yi ekrana basarken kullanın. Kural basit: ekrana giden her şey te().
Arapça'nın altı çoğul kategorisi gerçekten gerekli mi?
Evet. Demo sayfasındaki tabloda 0, 1, 2, 5, 11 ve 100 için altı farklı karşılık görürsünüz. Bunlar süs değil, dilin dilbilgisi kuralıdır.
if ($n == 1) yazan bir kod Arapça'da 2 için tekil, 11 için çoğul üretir — ikisi de yanlıştır. Kuralı bilen tek yer ICU veri tabanıdır ve intl ona bakar.
intl eklentisi olmadan kullanabilir miyim?
Evet. Uygulama her yerelleştirme çağrısında eklentiyi denetler ve yoksa sade yedeğe düşer: çoğulda other biçimi, sayıda number_format(), tarihte date().
Site çalışır ama Arapça çoğul kuralları ve yerel para/tarih biçimleri doğru olmaz. Üretimde intl'i açmanızı öneririz; tek satırlık bir php.ini değişikliğidir.
Dil kodunu adresin içine koyabilir miyim? (/en/users)
Koyabilirsiniz ve SEO açısından genellikle daha iyidir. Bu projede sorgu parametresi seçildi çünkü örneğin odağı yönlendirme değil, çeviri katmanıdır.
Geçmek için: .htaccess'te ilk yol parçasını yakalayın, SUPPORTED listesindeyse $_GET['lang']'e yazıp yoldan düşürün. Translator::resolve() hiç değişmeden çalışmaya devam eder.
Çeviri anahtarını unutursam ne olur?
Ekranda anahtarın kendisi görünür: common.save.
Bu bilerek seçilmiştir. Boş metin dönseydi eksik çeviri sessizce kaybolur ve fark edilmezdi. Anahtar ise gözünüze batar. Yayına almadan önce anahtar denkliğini kontrol edin — üç dilde de 73 anahtar vardır.
.env içinde APP_DEBUG=false (veya satırı tümüyle silin — ortamdan türetilir)Session::isHttps() çerezi Secure yapacaktırconfig/, app/, routes/, views/ klasörlerinin .htaccess dosyaları yerinde mi?.env dosyasının tarayıcıdan erişilemediğini doğrulayın (403 dönmeli)intl eklentisini açın| Belirti | Sebep | Çözüm |
|---|---|---|
Ekranda common.save yazıyor | O anahtar aktif dilde yok | lang/<dil>/common.php dosyasına anahtarı ekleyin |
| Çoğul her sayıda aynı çıkıyor | intl kapalı | php.ini içinde extension=intl satırını açın |
| Arapça'da düzen aynalanmıyor | dir özniteliği basılmamış | Düzen dosyasında <html dir="..."> var mı bakın |
Sayı 1234567.89 görünüyor | intl kapalı, yedek biçim çalışıyor | intl'i açın |
| Dil değiştirince ana sayfaya atıyor | Dil seçici mevcut adresi korumuyor | views/partials/langswitch.php dosyasını kullanın |
| Tüm adresler 404 | mod_rewrite kapalı | Açın veya .env içinde APP_PRETTY_URLS=false yapın |
| Giriş "çok fazla deneme" diyor | Hız sınırı devrede | login_attempts tablosunu boşaltın veya süreyi bekleyin |
| Sayfa boş / beyaz | PHP hatası, display_errors kapalı | .env içinde APP_DEBUG=true yapıp tekrar deneyin |
/en/users) örneği.po / .mo içe aktarma köprüsüHata bildirimi ve öneriler için issue açabilirsiniz. Yeni dil çevirisi gönderirseniz memnun oluruz — anahtar denkliğini koruyun ve çoğul kalıplarında o dilin kendi kategorilerini kullanın.
MIT — ticari projelerinizde de özgürce kullanabilirsiniz.
İncelemek istediğiniz dosyayı ağaçtan seçin.
Güvenlik gereği kaynak dosyalardaki parola, API anahtarı ve benzeri gizli değerler gösterilmeden önce maskelenir (••••••••).
Olur, ama önce şunu sorun: çeviri metnini kim değiştirecek? Cevap "geliştirici" ise dosya daha iyidir; çeviri kodla birlikte sürümlenir, kod incelemesinden geçer ve her sayfa açılışında sorgu maliyeti doğurmaz. Cevap "içerik ekibi, panelden" ise veritabanı gerekir. Bu projede birinci senaryo varsayılmıştır.
Evet. Demo sayfasındaki tabloda 0, 1, 2, 5, 11 ve 100 için altı farklı karşılık görürsünüz. Bunlar süs değil, dilin dilbilgisi kuralıdır. if ($n == 1) yazan bir kod Arapça'da 2 için tekil, 11 için çoğul üretir ve ikisi de yanlıştır.
Evet. Uygulama her yerelleştirme çağrısında eklentiyi denetler ve yoksa sade yedeğe düşer: çoğulda other biçimi, sayıda number_format(), tarihte date(). Site çalışmaya devam eder ama Arapça çoğul kuralları ve yerel biçimler doğru olmaz. Üretimde intl'i açmanızı öneririz; tek satırlık bir php.ini değişikliğidir.
Dört adım. lang/ altında klasörü açıp çeviriyi kopyalayın, değerleri çevirin, Translator::LOCALES dizisine dili ekleyin ve users.locale sütununu genişletin. Sağdan sola bir dilse yalnızca dir => rtl yazmanız yeterlidir; ayrı bir liste veya CSS dosyası yoktur.
Ekranda anahtarın kendisi görünür, örneğin common.save. Bu bilerek seçilmiştir: boş metin dönseydi eksik çeviri sessizce kaybolur ve fark edilmezdi. Anahtar ise gözünüze batar.
Koyabilirsiniz ve SEO açısından genellikle daha iyidir. Bu projede sorgu parametresi seçildi çünkü örneğin odağı yönlendirme değil çeviri katmanıdır. Geçmek için .htaccess'te ilk yol parçasını yakalayıp $_GET[lang] değerine yazmanız yeterlidir; Translator::resolve() hiç değişmez.
Bu örneğin daha gelişmişi projenizde mi lazım?
İhtiyacınızı anlatın; bu yapıyı sizin sisteminize uyarlayalım ya da size özel geliştirelim.
Henüz yorum yok. Takıldığınız bir yer varsa ilk soruyu siz sorun.
Yorumunuz onaylandıktan sonra yayınlanır. Teknik sorularınıza ekibimiz yanıt verir.
İleri
uygulama-iskeleti/php-baslangic-sablonu
309 inceleme 150 canlı açılış 6 beğeni 2 yorum
Orta
uygulama-iskeleti/php-scheduler-cron
178 inceleme 50 canlı açılış
İleri
uygulama-iskeleti/php-pwa-push-notification
144 inceleme 51 canlı açılış