feat(rehber): guncelleme alanı → sitemap/JSON-LD/OG/görünür tarih
Frontmatter'a isteğe bağlı `guncelleme` alanı geldi ve uçtan uca akıyor: sitemap lastmod (yazı + /rehber dizini), Article JSON-LD dateModified, og:article modified_time ve makale başlığının altındaki görünür tarih satırı. `tarih`/`guncelleme` YYYY-AA-GG değilse ya da güncelleme yayından önceyse build kırılır. Tarih metni saat diliminden bağımsız `trTarih` ile üretilir. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,8 @@ export type RehberOzet = {
|
||||
aciklama: string;
|
||||
/** ISO tarih — Article JSON-LD datePublished */
|
||||
tarih: string;
|
||||
/** ISO tarih, isteğe bağlı — dateModified / sitemap lastmod / görünür satır */
|
||||
guncelleme?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -139,15 +141,41 @@ function parcala(slug: string, body: string): RehberParca[] {
|
||||
return parcalar;
|
||||
}
|
||||
|
||||
const TARIH_BICIMI = /^\d{4}-\d{2}-\d{2}$/;
|
||||
|
||||
function metaDogrula(
|
||||
slug: string,
|
||||
meta: Record<string, string>,
|
||||
): asserts meta is { baslik: string; aciklama: string; tarih: string } {
|
||||
): asserts meta is {
|
||||
baslik: string;
|
||||
aciklama: string;
|
||||
tarih: string;
|
||||
guncelleme?: string;
|
||||
} {
|
||||
if (!meta.baslik || !meta.aciklama || !meta.tarih) {
|
||||
throw new Error(
|
||||
`rehber: ${slug}.md frontmatter eksik (baslik/aciklama/tarih zorunlu)`,
|
||||
);
|
||||
}
|
||||
// Tarihler sitemap lastmod'a, JSON-LD'ye ve görünür satıra olduğu gibi
|
||||
// akıyor; bozuk biçim sessizce yayına çıkmasın, build kırılsın.
|
||||
if (!TARIH_BICIMI.test(meta.tarih)) {
|
||||
throw new Error(
|
||||
`rehber: ${slug}.md frontmatter "tarih" YYYY-AA-GG biçiminde olmalı`,
|
||||
);
|
||||
}
|
||||
if (meta.guncelleme !== undefined) {
|
||||
if (!TARIH_BICIMI.test(meta.guncelleme)) {
|
||||
throw new Error(
|
||||
`rehber: ${slug}.md frontmatter "guncelleme" YYYY-AA-GG biçiminde olmalı`,
|
||||
);
|
||||
}
|
||||
if (meta.guncelleme < meta.tarih) {
|
||||
throw new Error(
|
||||
`rehber: ${slug}.md frontmatter "guncelleme" "tarih"ten önce olamaz`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function oku(slug: string): RehberYazi | null {
|
||||
@@ -165,6 +193,7 @@ function oku(slug: string): RehberYazi | null {
|
||||
baslik: meta.baslik,
|
||||
aciklama: meta.aciklama,
|
||||
tarih: meta.tarih,
|
||||
guncelleme: meta.guncelleme,
|
||||
parcalar: parcala(slug, body),
|
||||
};
|
||||
}
|
||||
@@ -183,8 +212,8 @@ function ozetOku(slug: string): RehberOzet | null {
|
||||
const tam = CACHE_AKTIF ? globalRehber.rehberYazilar?.get(slug) : undefined;
|
||||
if (tam !== undefined) {
|
||||
if (tam === null) return null;
|
||||
const { slug: s, baslik, aciklama, tarih } = tam;
|
||||
return { slug: s, baslik, aciklama, tarih };
|
||||
const { slug: s, baslik, aciklama, tarih, guncelleme } = tam;
|
||||
return { slug: s, baslik, aciklama, tarih, guncelleme };
|
||||
}
|
||||
const dosya = path.join(REHBER_DIZIN, `${slug}.md`);
|
||||
if (!fs.existsSync(dosya)) return null;
|
||||
@@ -195,6 +224,7 @@ function ozetOku(slug: string): RehberOzet | null {
|
||||
baslik: meta.baslik,
|
||||
aciklama: meta.aciklama,
|
||||
tarih: meta.tarih,
|
||||
guncelleme: meta.guncelleme,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,8 @@ export function articleJsonLd(opts: {
|
||||
description: string;
|
||||
path: string;
|
||||
datePublished: string;
|
||||
/** Verilmezse yayın tarihi kullanılır (yazı hiç güncellenmemiş). */
|
||||
dateModified?: string;
|
||||
}): JsonLdData {
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
@@ -159,6 +161,7 @@ export function articleJsonLd(opts: {
|
||||
description: opts.description,
|
||||
url: `${SITE_URL}${opts.path}`,
|
||||
datePublished: opts.datePublished,
|
||||
dateModified: opts.dateModified ?? opts.datePublished,
|
||||
inLanguage: "tr",
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
|
||||
30
src/lib/tarih.ts
Normal file
30
src/lib/tarih.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* `YYYY-MM-DD` tarihini Türkçe uzun biçime çevirir ("21 Eylül 2026").
|
||||
* Bilinçli olarak `Date`/`toLocale*` kullanmaz: sunucu ile tarayıcının saat
|
||||
* dilimi ya da ICU verisi farklıysa aynı girdi farklı metin üretir
|
||||
* (hydration uyuşmazlığı, bir gün kayması). Saf metin dönüşümü her yerde
|
||||
* aynı sonucu verir.
|
||||
*/
|
||||
const AYLAR = [
|
||||
"Ocak",
|
||||
"Şubat",
|
||||
"Mart",
|
||||
"Nisan",
|
||||
"Mayıs",
|
||||
"Haziran",
|
||||
"Temmuz",
|
||||
"Ağustos",
|
||||
"Eylül",
|
||||
"Ekim",
|
||||
"Kasım",
|
||||
"Aralık",
|
||||
];
|
||||
|
||||
export function trTarih(iso: string): string {
|
||||
const eslesme = iso.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
const ay = eslesme ? AYLAR[Number(eslesme[2]) - 1] : undefined;
|
||||
if (!eslesme || !ay) {
|
||||
throw new Error(`tarih: "${iso}" YYYY-AA-GG biçiminde değil`);
|
||||
}
|
||||
return `${Number(eslesme[3])} ${ay} ${eslesme[1]}`;
|
||||
}
|
||||
Reference in New Issue
Block a user