WordPress’te Yazı Türüne Göre Farklı Meta Tag Üretme
WordPress ile çalışırken SEO konusu kaçınılmaz olarak karşımıza çıkar. Özellikle büyük sitelerde farklı içerik türleri için farklı meta tag stratejileri geliştirmek, hem arama motoru performansını artırır hem de sosyal medya paylaşımlarını çok daha verimli hale getirir. Bugün functions.php dosyasına ekleyeceğimiz fonksiyonlarla, yazı türüne göre dinamik meta tag üretimini nasıl yapacağımıza bakacağız.
Neden Yazı Türüne Göre Farklı Meta Tag?
Düşün bir an: Blog yazılarında yazarı ve yayın tarihini öne çıkarmak istiyorsun. WooCommerce ürünlerinde fiyat ve stok durumunu meta tag’e yansıtmak mantıklı. Etkinlik sayfalarında ise tarih ve konum bilgisi kritik. Hepsine aynı genel meta tag yapısını uygulamak, bu fırsatları çöpe atmak demek.
Ayrıca Open Graph ve Twitter Card tag’leri de içerik türüne göre değişmeli. Bir ürün sayfası için og:type değeri product olmalıyken, bir makale için article olmalı. Bu detaylar küçük görünebilir ama sosyal medya paylaşımlarında ciddi fark yaratıyor.
Temel Yapıyı Kurmak
Önce her şeyin üzerine inşa edeceğimiz temel fonksiyonu yazalım. Bu fonksiyon, mevcut sayfanın içerik türünü tespit edecek ve uygun meta tag üretme fonksiyonunu çağıracak.
// functions.php
function custom_meta_tags_dispatcher() {
// Yönetici panelinde çalışmasın
if ( is_admin() ) {
return;
}
global $post;
// İçerik türünü belirle
$post_type = get_post_type();
switch ( $post_type ) {
case 'post':
generate_blog_post_meta_tags( $post );
break;
case 'page':
generate_page_meta_tags( $post );
break;
case 'product':
generate_product_meta_tags( $post );
break;
case 'tribe_events':
generate_event_meta_tags( $post );
break;
default:
generate_default_meta_tags( $post );
break;
}
}
add_action( 'wp_head', 'custom_meta_tags_dispatcher', 1 );
Bu yapının güzel tarafı şu: Her içerik türü için ayrı bir fonksiyon yazıyorsun ve switch bloğuna yeni case ekleyerek sistemi genişletmek çok kolay oluyor. The Events Calendar kullanıyorsan tribe_events, özel bir portfolio post type’ın varsa onu da kolayca ekleyebilirsin.
Blog Yazıları İçin Meta Tag Fonksiyonu
Blog yazıları için yazar bilgisi, yayın tarihi, kategori ve etiketleri meta tag’e yansıtmak istiyoruz. Ayrıca Open Graph’ta article türünü kullanacağız.
function generate_blog_post_meta_tags( $post ) {
if ( ! $post ) return;
$title = get_the_title( $post->ID );
$description = get_the_excerpt( $post->ID );
$author = get_the_author_meta( 'display_name', $post->post_author );
$pub_date = get_the_date( 'c', $post->ID );
$mod_date = get_the_modified_date( 'c', $post->ID );
$permalink = get_permalink( $post->ID );
$thumbnail = get_the_post_thumbnail_url( $post->ID, 'large' );
$categories = get_the_category( $post->ID );
$cat_name = ! empty( $categories ) ? esc_attr( $categories[0]->name ) : '';
// Açıklama yoksa içerikten üret
if ( empty( $description ) ) {
$description = wp_trim_words( strip_shortcodes( $post->post_content ), 30, '...' );
}
$description = esc_attr( strip_tags( $description ) );
// Standart meta tag'ler
echo '<meta name="description" content="' . $description . '">' . "n";
echo '<meta name="author" content="' . esc_attr( $author ) . '">' . "n";
// Open Graph tag'leri
echo '<meta property="og:type" content="article">' . "n";
echo '<meta property="og:title" content="' . esc_attr( $title ) . '">' . "n";
echo '<meta property="og:description" content="' . $description . '">' . "n";
echo '<meta property="og:url" content="' . esc_url( $permalink ) . '">' . "n";
echo '<meta property="article:published_time" content="' . $pub_date . '">' . "n";
echo '<meta property="article:modified_time" content="' . $mod_date . '">' . "n";
echo '<meta property="article:author" content="' . esc_attr( $author ) . '">' . "n";
if ( $cat_name ) {
echo '<meta property="article:section" content="' . $cat_name . '">' . "n";
}
if ( $thumbnail ) {
echo '<meta property="og:image" content="' . esc_url( $thumbnail ) . '">' . "n";
}
// Twitter Card
echo '<meta name="twitter:card" content="summary_large_image">' . "n";
echo '<meta name="twitter:title" content="' . esc_attr( $title ) . '">' . "n";
echo '<meta name="twitter:description" content="' . $description . '">' . "n";
}
Dikkat etmesi gereken bir nokta var: Eğer Yoast SEO veya Rank Math gibi bir eklenti kullanıyorsan, bu eklentiler zaten kendi meta tag’lerini üretiyor. İki kez aynı tag’in çıkmaması için ya bu eklentilerin meta output’unu devre dışı bırakman ya da fonksiyonu sadece bu eklentilerin kapsamadığı alanlara uygulaман gerekiyor.
WooCommerce Ürünleri İçin Meta Tag Fonksiyonu
Ürün sayfaları için işler daha ilginç hale geliyor. Fiyat, stok durumu, SKU ve ürün kategorisi gibi e-ticaret’e özel veriler meta tag’lere yansıtılabilir. Facebook ve Google Shopping bu verileri okuyabilir.
function generate_product_meta_tags( $post ) {
if ( ! $post || ! class_exists( 'WooCommerce' ) ) return;
$product = wc_get_product( $post->ID );
if ( ! $product ) return;
$title = $product->get_name();
$description = $product->get_short_description();
$permalink = get_permalink( $post->ID );
$thumbnail = get_the_post_thumbnail_url( $post->ID, 'large' );
$price = $product->get_price();
$currency = get_woocommerce_currency();
$sku = $product->get_sku();
$stock = $product->is_in_stock() ? 'instock' : 'outofstock';
$brand = '';
// WooCommerce Brands eklentisi varsa marka bilgisini çek
$brand_terms = get_the_terms( $post->ID, 'product_brand' );
if ( $brand_terms && ! is_wp_error( $brand_terms ) ) {
$brand = $brand_terms[0]->name;
}
if ( empty( $description ) ) {
$description = wp_trim_words( strip_shortcodes( $product->get_description() ), 30, '...' );
}
$description = esc_attr( strip_tags( $description ) );
// Standart meta
echo '<meta name="description" content="' . $description . '">' . "n";
// Open Graph - Product type
echo '<meta property="og:type" content="product">' . "n";
echo '<meta property="og:title" content="' . esc_attr( $title ) . '">' . "n";
echo '<meta property="og:description" content="' . $description . '">' . "n";
echo '<meta property="og:url" content="' . esc_url( $permalink ) . '">' . "n";
if ( $thumbnail ) {
echo '<meta property="og:image" content="' . esc_url( $thumbnail ) . '">' . "n";
echo '<meta property="og:image:width" content="1200">' . "n";
echo '<meta property="og:image:height" content="630">' . "n";
}
// Ürün'e özel Open Graph tag'leri
if ( $price ) {
echo '<meta property="product:price:amount" content="' . esc_attr( $price ) . '">' . "n";
echo '<meta property="product:price:currency" content="' . esc_attr( $currency ) . '">' . "n";
}
echo '<meta property="product:availability" content="' . esc_attr( $stock ) . '">' . "n";
if ( $sku ) {
echo '<meta property="product:retailer_item_id" content="' . esc_attr( $sku ) . '">' . "n";
}
if ( $brand ) {
echo '<meta property="product:brand" content="' . esc_attr( $brand ) . '">' . "n";
}
// Twitter Card - ürünler için summary kullan
echo '<meta name="twitter:card" content="summary_large_image">' . "n";
echo '<meta name="twitter:title" content="' . esc_attr( $title ) . '">' . "n";
echo '<meta name="twitter:description" content="' . $description . '">' . "n";
echo '<meta name="twitter:label1" content="Fiyat">' . "n";
echo '<meta name="twitter:data1" content="' . esc_attr( $price . ' ' . $currency ) . '">' . "n";
echo '<meta name="twitter:label2" content="Stok Durumu">' . "n";
echo '<meta name="twitter:data2" content="' . ( $product->is_in_stock() ? 'Stokta Var' : 'Stokta Yok' ) . '">' . "n";
}
Twitter’ın label ve data tag’leri çok faydalı. Ürün paylaşımlarında fiyat ve stok bilgisi doğrudan tweet önizlemesinde gösterilebiliyor. Bu küçük detay, e-ticaret sitelerinde tıklama oranını ciddi ölçüde artırabiliyor.
Sayfa ve Arşiv Sayfaları İçin Meta Tag
Statik sayfalar ve arşiv sayfaları için daha basit bir yapı kullanabiliriz. Önemli olan, bu sayfaların website tipinde olduğunu Open Graph’a bildirmek.
function generate_page_meta_tags( $post ) {
if ( ! $post ) return;
$title = get_the_title( $post->ID );
$description = get_the_excerpt( $post->ID );
$permalink = get_permalink( $post->ID );
$thumbnail = get_the_post_thumbnail_url( $post->ID, 'large' );
$site_name = get_bloginfo( 'name' );
if ( empty( $description ) ) {
$description = wp_trim_words( strip_shortcodes( $post->post_content ), 30, '...' );
}
// İkisi de boşsa site açıklamasını kullan
if ( empty( $description ) ) {
$description = get_bloginfo( 'description' );
}
$description = esc_attr( strip_tags( $description ) );
echo '<meta name="description" content="' . $description . '">' . "n";
echo '<meta property="og:type" content="website">' . "n";
echo '<meta property="og:site_name" content="' . esc_attr( $site_name ) . '">' . "n";
echo '<meta property="og:title" content="' . esc_attr( $title ) . '">' . "n";
echo '<meta property="og:description" content="' . $description . '">' . "n";
echo '<meta property="og:url" content="' . esc_url( $permalink ) . '">' . "n";
if ( $thumbnail ) {
echo '<meta property="og:image" content="' . esc_url( $thumbnail ) . '">' . "n";
} else {
// Varsayılan site görseli
$default_image = get_template_directory_uri() . '/assets/images/default-og.jpg';
echo '<meta property="og:image" content="' . esc_url( $default_image ) . '">' . "n";
}
echo '<meta name="twitter:card" content="summary">' . "n";
echo '<meta name="twitter:title" content="' . esc_attr( $title ) . '">' . "n";
echo '<meta name="twitter:description" content="' . $description . '">' . "n";
}
Etkinlik Sayfaları İçin Meta Tag
The Events Calendar eklentisi kullanıyorsan, etkinlik tarihi ve konum bilgisini schema.org formatında da verebilirsin. Bu Google’ın etkinlikleri zengin sonuçlarda göstermesi için kritik.
function generate_event_meta_tags( $post ) {
if ( ! $post ) return;
$title = get_the_title( $post->ID );
$description = get_the_excerpt( $post->ID );
$permalink = get_permalink( $post->ID );
$thumbnail = get_the_post_thumbnail_url( $post->ID, 'large' );
// The Events Calendar meta değerleri
$start_date = get_post_meta( $post->ID, '_EventStartDate', true );
$end_date = get_post_meta( $post->ID, '_EventEndDate', true );
$venue_id = get_post_meta( $post->ID, '_EventVenueID', true );
$location = '';
if ( $venue_id ) {
$venue_name = get_the_title( $venue_id );
$venue_city = get_post_meta( $venue_id, '_VenueCity', true );
$venue_country = get_post_meta( $venue_id, '_VenueCountry', true );
$location = trim( $venue_name . ', ' . $venue_city . ', ' . $venue_country, ', ' );
}
if ( empty( $description ) ) {
$description = wp_trim_words( strip_shortcodes( $post->post_content ), 30, '...' );
}
$description = esc_attr( strip_tags( $description ) );
echo '<meta name="description" content="' . $description . '">' . "n";
echo '<meta property="og:type" content="event">' . "n";
echo '<meta property="og:title" content="' . esc_attr( $title ) . '">' . "n";
echo '<meta property="og:description" content="' . $description . '">' . "n";
echo '<meta property="og:url" content="' . esc_url( $permalink ) . '">' . "n";
if ( $start_date ) {
echo '<meta property="event:start_time" content="' . esc_attr( $start_date ) . '">' . "n";
}
if ( $end_date ) {
echo '<meta property="event:end_time" content="' . esc_attr( $end_date ) . '">' . "n";
}
if ( $location ) {
echo '<meta property="event:location" content="' . esc_attr( $location ) . '">' . "n";
}
if ( $thumbnail ) {
echo '<meta property="og:image" content="' . esc_url( $thumbnail ) . '">' . "n";
}
// Twitter Card'da etkinlik bilgilerini göster
echo '<meta name="twitter:card" content="summary_large_image">' . "n";
echo '<meta name="twitter:title" content="' . esc_attr( $title ) . '">' . "n";
echo '<meta name="twitter:description" content="' . $description . '">' . "n";
if ( $start_date ) {
echo '<meta name="twitter:label1" content="Tarih">' . "n";
echo '<meta name="twitter:data1" content="' . esc_attr( date_i18n( 'd F Y', strtotime( $start_date ) ) ) . '">' . "n";
}
if ( $location ) {
echo '<meta name="twitter:label2" content="Konum">' . "n";
echo '<meta name="twitter:data2" content="' . esc_attr( $location ) . '">' . "n";
}
}
Varsayılan Meta Tag Fonksiyonu
Özel bir kural tanımlamamış içerik türleri için güvenli bir varsayılan fonksiyon olması şart. Yoksa o sayfalar meta tag’siz kalabilir.
function generate_default_meta_tags( $post ) {
$site_name = get_bloginfo( 'name' );
$description = get_bloginfo( 'description' );
$site_url = get_bloginfo( 'url' );
if ( $post ) {
$title = get_the_title( $post->ID );
$excerpt = get_the_excerpt( $post->ID );
$permalink = get_permalink( $post->ID );
$thumbnail = get_the_post_thumbnail_url( $post->ID, 'large' );
if ( ! empty( $excerpt ) ) {
$description = $excerpt;
}
} else {
$title = $site_name;
$permalink = $site_url;
$thumbnail = '';
}
$description = esc_attr( strip_tags( $description ) );
echo '<meta name="description" content="' . $description . '">' . "n";
echo '<meta property="og:type" content="website">' . "n";
echo '<meta property="og:site_name" content="' . esc_attr( $site_name ) . '">' . "n";
echo '<meta property="og:title" content="' . esc_attr( $title ) . '">' . "n";
echo '<meta property="og:description" content="' . $description . '">' . "n";
echo '<meta property="og:url" content="' . esc_url( $permalink ) . '">' . "n";
if ( ! empty( $thumbnail ) ) {
echo '<meta property="og:image" content="' . esc_url( $thumbnail ) . '">' . "n";
}
echo '<meta name="twitter:card" content="summary">' . "n";
}
Canonical URL ve Robots Meta Tag Entegrasyonu
Meta tag sistemini tamamlamak için canonical URL ve robots direktiflerini de eklemek iyi bir pratik. Özellikle WooCommerce’te filtreleme sayfaları için noindex vermek çok önemli.
function custom_canonical_and_robots() {
if ( is_admin() ) return;
global $post;
// WooCommerce filtreleme/sıralama parametreleri varsa noindex ver
$noindex_params = array( 'orderby', 'min_price', 'max_price', 'filter_' );
$has_filter = false;
foreach ( $noindex_params as $param ) {
// filter_ ile başlayan parametreleri kontrol et
foreach ( $_GET as $key => $val ) {
if ( strpos( $key, $param ) === 0 ) {
$has_filter = true;
break 2;
}
}
}
if ( $has_filter ) {
echo '<meta name="robots" content="noindex, follow">' . "n";
} else {
echo '<meta name="robots" content="index, follow">' . "n";
}
// Canonical URL
if ( is_singular() ) {
$canonical = get_permalink( $post->ID );
echo '<link rel="canonical" href="' . esc_url( $canonical ) . '">' . "n";
} elseif ( is_home() || is_front_page() ) {
echo '<link rel="canonical" href="' . esc_url( home_url( '/' ) ) . '">' . "n";
}
}
add_action( 'wp_head', 'custom_canonical_and_robots', 2 );
Gerçek Dünya Senaryosu: Çoklu Dil ve Hreflang
WPML veya Polylang kullanılan çok dilli sitelerde hreflang tag’leri de meta tag sistemine entegre edilmeli. Bu tag Google’a aynı içeriğin hangi dillerde mevcut olduğunu söyler.
function custom_hreflang_tags() {
if ( is_admin() || ! is_singular() ) return;
// WPML kontrolü
if ( ! function_exists( 'icl_get_languages' ) ) return;
$languages = icl_get_languages( 'skip_missing=0' );
if ( empty( $languages ) ) return;
foreach ( $languages as $lang ) {
$lang_code = $lang['language_code'];
$lang_url = $lang['url'];
if ( $lang['active'] ) {
// x-default için aktif dili kullan
echo '<link rel="alternate" hreflang="x-default" href="' . esc_url( $lang_url ) . '">' . "n";
}
echo '<link rel="alternate" hreflang="' . esc_attr( $lang_code ) . '" href="' . esc_url( $lang_url ) . '">' . "n";
}
}
add_action( 'wp_head', 'custom_hreflang_tags', 3 );
Pratik İpuçları ve Dikkat Edilmesi Gerekenler
Bu fonksiyonları canlıya almadan önce göz önünde bulundurman gereken birkaç önemli nokta var:
Çakışma Riski: Yoast SEO, Rank Math veya All in One SEO gibi eklentiler zaten kendi meta tag sistemlerini çalıştırıyor. Bu eklentileri aktif kullanıyorsan, onların meta output’larını devre dışı bırakmadan kendi fonksiyonlarını ekleme. Aynı tag iki kez çıkar ve Google bunu bir sorun olarak işaretleyebilir.
Yoast SEO’nun Open Graph output’unu devre dışı bırakmak için şunu kullan:
// Yoast SEO Open Graph tag'lerini devre dışı bırak
add_filter( 'wpseo_og_show_social', '__return_false' );
// Yoast SEO Twitter tag'lerini devre dışı bırak
add_filter( 'wpseo_twitter_card_type', '__return_false' );
Önbellek Sorunu: Meta tag’ler dinamik olarak üretildiğinde, Redis veya Varnish gibi tam sayfa önbellek sistemleri bu verileri statik olarak saklayabilir. Özellikle fiyat gibi sık değişen veriler için önbellek süresini kısa tutman gerekiyor.
Güvenlik: Tüm çıktılarda esc_attr(), esc_url() ve strip_tags() kullanmayı unutma. Kullanıcı tarafından girilebilecek herhangi bir veri, doğrudan echo ile basılmamalı.
Test: Facebook Sharing Debugger, Twitter Card Validator ve Google Rich Results Test araçlarını kullanarak her içerik türü için meta tag’lerini test et. Özellikle ürün sayfaları için Facebook Catalog entegrasyonunu kontrol et.
Karakter Limiti: Meta description için ideal uzunluk 150-160 karakterdir. wp_trim_words yerine karakter bazlı kesme yapmak daha doğru sonuç verir:
// Karakter bazlı description kesme yardımcı fonksiyonu
function trim_meta_description( $text, $length = 155 ) {
$text = strip_tags( $text );
$text = wp_strip_all_tags( $text );
$text = trim( $text );
if ( mb_strlen( $text ) > $length ) {
$text = mb_substr( $text, 0, $length );
$text = mb_substr( $text, 0, mb_strrpos( $text, ' ' ) );
$text .= '...';
}
return $text;
}
Sonuç
Yazı türüne göre farklı meta tag üretmek, başlangıçta karmaşık görünebilir ama doğru yapılandırıldığında hem geliştirmesi hem de bakımı kolay bir sistem haline geliyor. switch-case tabanlı dispatcher yapısı sayesinde yeni bir post type eklediğinde tek yapman gereken yeni bir fonksiyon yazmak ve case eklemek.
Bu sistemin gerçek faydası uzun vadede ortaya çıkıyor. Blog yazılarında yazar ve tarih bilgisiyle zenginleşen arama sonuçları, ürün sayfalarında fiyat ve stok gösteren sosyal paylaşımlar, etkinliklerde tarih ve konum içeren önizlemeler; bunların hepsi kullanıcı deneyimini ve tıklama oranını doğrudan etkiliyor.
Kodu canlıya almadan önce mutlaka bir staging ortamında test et ve mevcut SEO eklentilerinle çakışıp çakışmadığını kontrol et. Küçük bir hata tüm sitenin meta tag’lerini bozabilir ve bunu fark etmek bazen günler alabilir.
