# بهینه‌سازی SEO تک‌تک شهرها در city.json (سطح حرفه‌ای)

## پروژه

`nobat724_front` (سایت عمومی نوبت‌دهی، چند-دامنه‌ای — هر شهر یک دامنه)

## نقش

تو یک **متخصص حرفه‌ای SEO فارسی/محلی (Local SEO)** هستی. خروجی باید در سطح یک آژانس سئوی حرفه‌ای باشد: یکتا، کلیدواژه‌محور بدون stuffing، با رعایت طول بهینه، CTR-optimized.

## زمینه

`data/city.json` منبع واحد SEO هر دامنه‌ی شهری است. `app/layout.js` → `generateMetadata()` مقادیر این فایل را مستقیم به تگ‌های متا تبدیل می‌کند. یعنی کیفیت SEO هر شهر = کیفیت فیلدهای متنی همان رکورد در این فایل.

۳۴ رکورد وجود دارد (۳۳ شهر + رکورد اصلی `id:600` دامنه `nobat724.com`). محتوای فعلی با یک **قالب یکسان کلون** شده — جملات تقریباً تکراری بین شهرها (خطر duplicate content) و ضعیف.

## مشکل / هدف

دو مشکل بحرانی:

1. **فیلد `title` در هیچ‌کدام از ۳۴ رکورد وجود ندارد.** `layout.js` اول `matchedCity?.title` را می‌خواند؛ چون نیست، همه شهرها به یک قالب یکسان fallback می‌کنند → تایتل‌های تقریباً تکراری. **باید به هر رکورد فیلد `title` یکتا و بهینه اضافه شود.**
2. `description` / `keywords` / `slogan` / `footer_description` قالبی و تکراری‌اند → باید هرکدام برای هر شهر **بازنویسی یکتا و بهینه** شوند.

هدف: هر ۳۴ رکورد، ۵ فیلد متنی SEO در بالاترین کیفیت، **بدون تکرار جمله بین شهرها**.

## فایل‌های مرتبط

| فایل | نقش |
|------|-----|
| `data/city.json` | تنها فایلی که ویرایش می‌شود — رکورد هر شهر |
| `app/layout.js` | مصرف‌کننده فیلدها (فقط برای درک، ویرایش نشود مگر task ۶) |

## وضعیت فعلی

مصرف فیلدها در `app/layout.js`:

```js
const title =
  matchedCity?.title ||                              // ← این فیلد وجود ندارد!
  (matchedCity?.name
    ? `نوبت‌دهی آنلاین پزشکان ${matchedCity.name} | نوبت 724`
    : "نوبت 724 | سیستم نوبت‌دهی آنلاین پزشکی");
const description = matchedCity?.description || "...";
// keywords: matchedCity.keywords
// openGraph.siteName: matchedCity.site_name
```

نمونه رکورد فعلی (تکراری/قالبی):

```json
{
  "id": 113, "name": "اهواز", "site_name": "اهواز نوبت",
  "province_name": "خوزستان",
  "description": "اهواز نوبت، سامانه نوبت‌دهی آنلاین در اهواز و استان خوزستان. امکان جستجو و رزرو نوبت در مراکز درمانی. اهواز نوبت بهترین راه دسترسی به پزشکان متخصص است.",
  "slogan": "سامانه هوشمند نوبت‌دهی پزشکان اهواز",
  "keywords": "رزرو نوبت اهواز, پزشکان خوزستان, کلینیک‌های اهواز, بیمارستان‌های اهواز, متخصصین پزشکی اهواز",
  "footer_description": "اهواز نوبت، سامانه جامع نوبت‌دهی پزشکی در استان خوزستان. رزرو نوبت از پزشکان و مراکز درمانی اهواز بدون نیاز به مراجعه حضوری."
}
```

## فهرست ۳۴ رکورد (همه باید پردازش شوند)

| id | شهر | استان | دامنه |
|----|-----|-------|-------|
| 129 | اراک | مرکزی | arak-nobat.ir |
| 103 | اردبیل | اردبیل | ardabil-nobat.ir |
| 102 | ارومیه | آذربایجان غربی | urmia-nobat.ir |
| 104 | اصفهان | اصفهان | esf-nobat.ir |
| 113 | اهواز | خوزستان | ahvaz-nobat.ir |
| 106 | ایلام | ایلام | ilam-nobat.ir |
| 112 | بجنورد | خراسان شمالی | bojnord-nobat.ir |
| 130 | بندرعباس | هرمزگان | bandar-nobat.ir |
| 107 | بوشهر | بوشهر | bushehr-nobat.ir |
| 110 | بیرجند | خراسان جنوبی | birjand-nobat.ir |
| 101 | تبریز | آذربایجان شرقی | tabriz-nobat.ir |
| 108 | تهران | تهران | tehran-nobat.ir |
| 127 | خرم‌آباد | لرستان | lorestan-nobat.ir |
| 126 | رشت | گیلان | rasht-nobat.ir |
| 116 | زاهدان | سیستان و بلوچستان | zahedan-nobat.ir |
| 114 | زنجان | زنجان | zanjan-nobat.ir |
| 128 | ساری | مازندران | sari-nobat.ir |
| 115 | سمنان | سمنان | semnan-nobat.ir |
| 120 | سنندج | کردستان | sanandaj-nobat.ir |
| 109 | شهرکرد | چهارمحال و بختیاری | shkord-nobat.ir |
| 117 | شیراز | فارس | shiraz-nobat.ir |
| 118 | قزوین | قزوین | qazvin-nobat.ir |
| 119 | قم | قم | qom-nobat.ir |
| 105 | کرج | البرز | karaj-nobat.ir |
| 121 | کرمان | کرمان | kerman-nobat.ir |
| 122 | کرمانشاه | کرمانشاه | kermanshah-nobat.ir |
| 125 | گرگان | گلستان | golestan-nobat.ir |
| 111 | مشهد | خراسان رضوی | mashhad-nobat.ir |
| 131 | همدان | همدان | hamadan-nobat.ir |
| 123 | یاسوج | کهگیلویه و بویراحمد | yasuj-nobat.ir |
| 132 | یزد | یزد | yazd-nobat.ir |
| 133 | دهدشت | کهگیلویه و بویراحمد | dehdasht-nobat.ir |
| 134 | بهبهان | خوزستان | behbahan-nobat.ir |
| 600 | نوبت 724 (برند اصلی) | — | nobat724.com |

## وظایف

### ۱. افزودن فیلد `title` یکتا به هر رکورد (مهم‌ترین)

بعد از `site_name` در هر رکورد، فیلد `title` اضافه کن.

**قواعد `title`:**
- طول: **۵۰–۶۰ کاراکتر** (حداکثر ~۶۰ برای عدم truncate در گوگل).
- کلیدواژه اصلی **اول** جمله: «نوبت‌دهی آنلاین پزشکان {شهر}» یا واریانت.
- برند در انتها با جداکننده `|`: `... | نوبت ۷۲۴`.
- **یکتا بین شهرها** — فقط جای‌گذاری اسم شهر کافی نیست؛ ساختار/کلمات را بین گروه‌ها متنوع کن (مثلاً برخی «رزرو نوبت پزشک {شهر}»، برخی «نوبت اینترنتی دکتر {شهر}»).
- بدون stuffing، خوانا، طبیعی.

مثال طلایی:

```json
"title": "نوبت‌دهی آنلاین پزشکان اهواز | رزرو نوبت دکتر | نوبت ۷۲۴"
```

### ۲. بازنویسی `description` (متا دیسکریپشن)

**قواعد:**
- طول: **۱۵۰–۱۶۰ کاراکتر** (نه کمتر از ۱۲۰، نه بیشتر از ۱۶۵).
- کلیدواژه اصلی front-load شده در ابتدای جمله.
- شامل: نام شهر + استان + اشاره به تخصص‌ها/مراکز درمانی + **CTA** («همین حالا رزرو کنید» / «آنلاین نوبت بگیرید»).
- **یکتا** — هیچ دو شهری جمله‌ی یکسان نداشته باشند. زاویه‌ی متن را متنوع کن (یکی روی سرعت، یکی روی تنوع پزشکان، یکی روی ۲۴ساعته‌بودن).
- بدون تکرار بیش از حد اسم شهر (حداکثر ۲ بار).

مثال طلایی:

```json
"description": "با نوبت اهواز، آنلاین از پزشکان متخصص و عمومی خوزستان نوبت بگیرید. جستجوی دکتر بر اساس تخصص، بیمه و منطقه در اهواز؛ رزرو نوبت ۲۴ساعته بدون مراجعه حضوری. همین حالا نوبت خود را ثبت کنید."
```

### ۳. بهینه‌سازی `keywords`

- ۶–۹ عبارت هدفمند، جدا با `, `.
- ترکیب: `نوبت {شهر}`، `دکتر {شهر}`، `پزشک متخصص {شهر}`، `رزرو نوبت اینترنتی {شهر}`، `کلینیک {شهر}`، `بهترین پزشک {استان}`، عبارات intent-محور («نوبت آنلاین دکتر»).
- بدون تکرار عین‌به‌عین عبارات بین شهرها (اسم شهر متفاوت کافی نیست برای long-tailها).

### ۴. بازنویسی `slogan` یکتا

- کوتاه (۴–۸ کلمه)، جذاب، دارای کلیدواژه شهر، متمایز بین شهرها.
- نمونه: `"در اهواز، نوبت دکتر فقط با چند کلیک"`.

### ۵. بازنویسی `footer_description` یکتا

- ۱–۲ جمله، محلی، دارای شهر+استان، متفاوت از `description` (نه کپی).
- برای فوتر — لحن معرفی برند محلی.

### ۶. (اختیاری، اگر کاربر خواست) بهبود فنی در `layout.js`

اگر زمان بود، این‌ها را هم پیشنهاد بده (اما JSON اولویت است):
- `openGraph.images` و `twitter.images` الان هاردکد `nobat724.com/assets/images/logo.png` است — می‌توان per-city کرد.
- JSON-LD فعلی فقط `Organization` + `WebSite` با `siteUrl` ثابت `nobat724.com` است؛ افزودن `MedicalBusiness`/`LocalBusiness` per-city با دامنه و نام شهر، سیگنال Local SEO قوی می‌دهد.

### ۷. رکورد برند اصلی (`id: 600`)

این رکورد شهر نیست (دامنه‌ی مادر `nobat724.com`, `province_name: null`). برایش `title`/`description`/`keywords` سراسری و برندمحور بنویس (نه شهرمحور): تمرکز روی «سامانه سراسری نوبت‌دهی آنلاین پزشکی ایران».

## نکات مهم

- **فقط `data/city.json` ویرایش شود** (به‌جز task ۶ اختیاری). ساختار JSON، ترتیب رکوردها، و فیلدهای `id`, `uuid`, `status`, `weight`, `province_id`, `province_name`, `representation_id`, `contact_phone`, `email`, `domain`, `social_media`, `logo_url` **دست‌نخورده** بمانند.
- فیلد `title` جدید را بعد از `site_name` قرار بده (سازگاری با ترتیب منطقی).
- **اعداد در `title` فارسی** باشند (`۷۲۴` نه `724`) برای هماهنگی با UI فارسی — مگر اینکه در برند لاتین لازم باشد.
- **یکتایی سراسری**: بعد از اتمام، هیچ دو رکوردی نباید `title` یا `description` با ساختار جمله‌ی یکسان (فقط تغییر اسم شهر) داشته باشند. حداقل ۳–۴ الگوی متنی متفاوت بین ۳۴ رکورد بچرخان.
- **صحت جغرافیایی**: `province_name` هر رکورد را به‌عنوان واقعیت بپذیر (قبلاً بهبهان=خوزستان، دهدشت=کهگیلویه اصلاح شده). محتوای استان را با همین مقدار بنویس.
- بعد از ویرایش، **اعتبار JSON را تأیید کن**:
  ```bash
  python3 -c "import json; d=json.load(open('data/city.json')); print('valid', len(d)); \
  t=[c.get('title') for c in d]; assert all(t), 'missing title'; \
  print('unique titles:', len(set(t))==len(t)); \
  print('unique descriptions:', len(set(c['description'] for c in d))==len(d))"
  ```
  باید: valid، همه title دارند، titleها و descriptionها یکتا.
- زبان فارسی رسمی و روان، RTL، بدون غلط املایی. اسامی شهر/استان دقیقاً مطابق فایل.
- این تغییر داده است، نه endpoint API → نیازی به به‌روزرسانی `docs/api/` نیست.
- بعد از ویرایش، یک build سریع بزن که فایل درست پارس می‌شود: `npm run build` (یا حداقل import شدن JSON).
```
