localize.ts فیلد usage را نگاشت نمیکرد و نوع Project هم آن را نداشت؛ نتیجه:
صفحهٔ /en/projects/... همچنان «مسکونی» فارسی را نشان میداد. usage و project_type
به نوع + نگاشت بومیسازی اضافه شدند.
نشانی انگلیسی ترانویسیشده از ۱۲۸ نویسه بلندتر میشود (بلندترین: ۱۶۱) و درج
ترجمهٔ ۶۵ پروژه با خطای StringDataRightTruncation شکست خورد. مدل + مهاجرت
ALTER COLUMN TYPE (idempotent) اضافه شد.
با افزودن title_en به سه پروژه، فیلتر FEATURED_TITLES (عنوان فارسی) در نسخهٔ
انگلیسی دیگر مطابقت نمیکرد و بخش «پروژههای شاخص» بدون کارت رندر میشد.
- تطبیق با slug (زبانمستقل) + لینک کارتها با href() بومیسازی شد
- لینک نشان پروژهٔ فعال هیرو هم بومیسازی شد (در /en به نسخهٔ فارسی نمیرود)
گزارش Coverage Drilldown گوگل (۱۰ اکتبر): ۳۶ صفحه «Crawled - currently not indexed»
که غالباً /en/projects/... بودند — صفحهٔ انگلیسی با محتوای فارسی (۰/۶۵ پروژه ترجمه دارد)
یعنی نسخهٔ تکراری صفحهٔ فارسی، و گوگل درست ردش کرده بود.
اکنون صفحات /en که ترجمه ندارند متای noindex,follow میگیرند (کاربر عادی سالم
میبیند؛ با افزودن ترجمه خودکار برمیگردد). صفحههای فارسی و /en صفحههای اصلی
ترجمهدار دستنخورده و ایندکسپذیرند.
سیگنال تمایز هویت برند: شرکت جعلیای که از نام «آرامش» استفاده میکند تأسیس ۱۳۹۱ را
ادعا میکند؛ سایت رسمی حالا سند ساختیافتهٔ سابقهٔ واقعی (۲۴ سال) را در اختیار
گوگل و دستیارهای AI قرار میدهد.
NavLink برای «خانه» (مسیر / و /en) چون پیشوند همهٔ مسیرهاست، در هر صفحهای
active میماند (در انگلیسی واضح بود: Home و Projects همزمان روشن).
با end={item.href === '/'} دقیق شد — بقیهٔ آیتمها مثل Projects روی
صفحههای جزئیات همچنان فعال میمانند (رفتار درست).
- هدر: لوگو در موبایل h-14 (دسکتاپ h-20 مثل قبل) — ارتفاع هدر از ۱۱۲ به ۸۸ پیکسل
- PageHero: h ثابت ۵۸vh → min-h + pt-32؛ اگر محتوا (مثل سرآغاز بلند انگلیسی درباره ما)
بلندتر باشد، بخش بلندتر میشود و بردکرامب هرگز زیر لوگوی ثابت نمیرود
- Hero صفحهٔ اصلی: pt-28 تا بلوک وسطچین هم با هدر تداخل نکند؛ min-h بهجای h-screen
با iOS Safari (آیفون) تست شد
کاربر نشان «47 / پروژه فعال آرامش الیسیان» را در هیرو دید — عدد دموی seed.
- badge_value/badge_value_en: «۴۷»/«47» → خالی (برچسب واقعی میماند)
- تایملاین: «پرتفوی ۴۷ پروژه، دفاتر در سه شهر» → «پرتفوی ۶۵ پروژه، دفتر مرکزی در اصفهان»
- تایملاین lead: «تا ۴۷ پروژه» → «تا ۶۵ پروژه»
- متای دموی seed: «21 years / 47 delivered» → «24 years / 65 delivered»
- بخش تماس: «تیم ما در سه شهر» → «تیم ما» (فقط یک دفتر واقعی داریم: اصفهان)
بررسی کامل دیتابیس زنده: هیچ ادعای ناسازگاری باقی نمانده (نه ۴۷، نه سه شهر، نه
دفاتر/افراد دمو). seed اصلاح شد تا در استقرار بعدی برنگردند.
گزارش منابع صفحهٔ GSC: «Googlebot blocked by robots.txt → /api/site».
robots.txt قاعدهٔ «آخرین تطابق» ندارد؛ افزودن Allow: /api/site داخل همان گروه
Disallow: /api توسط برخی خزندهها نادیده گرفته میشد. حالا فقط /admin و
/api/admin مسدود میشوند و /api/site آزاد است.
دستیار AI گفت «تصاویر سایت را نتوانستم دریافت کنم» — آرشیو ZIP وجود داشت و از
چهار کشور ۲۰۰ میداد، اما هیچ پیوندی به آن در صفحات نبود: llms.txt فقط الگو
داده بود و مدلها الگو را جایگذاری نمیکنند. اکنون:
- صفحهٔ هر پروژه: <a href=.../images.zip>دانلود همهٔ تصاویر (ZIP)</a>
- فهرست /projects: هر ردیف پیوند «تصاویر (ZIP)» همان پروژه را دارد (۶۵ پیوند)
تا خزندهها با دنبالکردن پیوند واقعی به آرشیو برسند.
دستیار AI گفت «/projects باز نمیشود» و «برای الیسیان و زنیت متراژ پیدا نکردم» — درست بود:
فهرست پروژهها فقط نام/موقعیت/سال را میداد و زیربنا و طبقات فقط در صفحهٔ جزئیات بود.
اکنون هر ردیف فهرست شامل زمین/زیربنا/طبقات/واحد است و نام پروژه به صفحهٔ جزئیات
پیوند دارد، تا خزنده بتواند مشخصات کامل را دنبال کند.
دستیارها نمیتوانند دکمههای UI را کلیک کنند؛ برای همین نمیتوانستند تصاویر یک پروژه را
بررسی کنند. اکنون:
- /projects/{slug}/images.zip — آرشیو تصاویر هر پروژه + manifest.json
(مشخصات کامل پروژه: زمین/زیربنا/طبقات/واحدها/کاربری/سال + برای هر تصویر: نام،
caption با موقعیت پروژه، و لینک منبع)
- /projects-images.json — فهرست ماشینخوان هر ۶۵ پروژه با لینک ZIP (ItemList)
- هر دو مسیر در قاعدهٔ seo ترویک (prod + override) اضافه شدند
رفع باگها در مسیر ساخت آرشیو:
- _fetch_media: unquote روی کلیدِ از قبل دیکدشده، نام فایلهای فارسی را خراب میکرد
(حالا هر دو شکل امتحان میشود + سقوط به واکشی از سایت)
- UnboundLocalError: import محلی urllib.request نام urllib را محلی میکرد
- تصاویر تکراری (تصویر شاخص = اولین عکس گالری) با هش MD5 حذف میشوند —
۱۷ از ۱۹ پروژهٔ نمونه یک تصویر تکراری داشتند
پاسخ به اینکه دستیارهای AI «متراژ پروژه را پیدا نمیکنند» در حالی که در پنل ثبت شده است:
- _project_content_html فقط ۴ فیلد (موقعیت/سال/زیربنا/کارفرما) را منتشر میکرد و
زیربنا را روی نسخهٔ انگلیسی بهخاطر نبود *_en حذف میکرد. حالا همهٔ فیلدهای فنی
(زمین، زیربنا، طبقات، پیلوت، واحدهای مسکونی/تجاری/اداری، کاربری، نوع، کارفرما، وضعیت)
منتشر میشوند؛ اعداد زبانمستقلاند و در نبود ترجمه هم مقدار فارسی میآید.
- _project_schema: همان مشخصات بهصورت additionalProperty (PropertyValue) اضافه شد
تا بهعنوان دادهٔ ساختیافته قابل استناد باشد.
نمونهٔ زنده (آرامش الیسیان): زمین ۱۵۵۰، زیربنا ۸۷۰۰، طبقات ۷، ۳۱ واحد مسکونی، ۶ تجاری.
۶۵/۶۵ پروژه زیربنا و طبقات دارند — پیش از این هیچکدام منتشر نمیشد.
سئوی سمت سرور (backend/app/seo_pages.py)
- متا/canonical/hreflang/JSON-LD برای همهٔ صفحات عمومی (شش صفحهٔ استاتیک فارسی که قبلاً پوستهٔ خالی بودند)
- canonical سمت سرور (قبلاً فقط با JS ست میشد)، ۴۰۴ واقعی برای مسیرهای ناموجود
- اسکیما: Organization با لوگوی ImageObject، FAQPage، LocalBusiness، BreadcrumbList،
CreativeWork پروژه با همهٔ تصاویر بهصورت ImageObject (نام/توضیح + موقعیت پروژه)
- hreflang فقط وقتی ترجمهٔ انگلیسی واقعاً وجود دارد؛ sitemap با URLهای درصدگذاریشده
- llms.txt و robots.txt تازهساز از دیتابیس؛ سیاست صریح خزندههای AI + Allow: /api/site
- _site_content_html: محتوای واقعی سایت بهصورت HTML خوانا در <noscript> —
خزندههای بدون جاوااسکریپت (GPTBot/OAI-SearchBot/ClaudeBot/PerplexityBot) از ۲۴۰ نویسه
به ۸٬۵۰۰+ نویسه محتوای معنایی میرسند
- Cache-Control: no-store روی HTML و llms.txt (جلوگیری از کش کهنهٔ CDN)، max-age روی sitemap
فرانتاند
- api.tsx: خواندن دادهٔ تزریقشدهٔ سرور (window.__SITE__) بهعنوان حالت اولیه — رندر بدون XHR
- App.tsx: اسکلت بارگذاری بهجای DOM خالی (رفع Soft 404 گوگل)
- index.html: فاوآیکون واقعی برند (قبلاً به vite.svg ناموجود اشاره میکرد) + preload فونت
زیرساخت
- Dockerfile: آینهٔ PyPI (Tsinghua/Aliyun) — PyPI برای IP ایران بلاک است
- Caddyfile: هر مسیر پسونددار بدون فایل واقعی ۴۰۴ میگیرد (قبلاً HTML اسپا برمیگرداند)
- docker-compose.prod.yml: مسیرهای seo برای صفحات استاتیک + llms.txt + کلیدهای IndexNow
داده
- seed_data*.py: حذف دفاتر و معرفیهای دمو (که در هر استقرار برمیگشتند)؛
تنها دفتر واقعی: اصفهان، ملاصدرا شمالی
داراییها
- فاوآیکون/آیکونهای برند از لوگوی رسمی + کلیدهای IndexNow
رفاکتر قبلی جستجو را فقط به resource محدود کرد در حالی که EditableItem و
AddItemButton و SingletonPill در چند صفحه با کلید فهرست صدا زده میشوند
(why_us، contact_section، before_after، process_steps، investment_models) —
useEntityList پرتاب خطا میکرد و رندر کل صفحه (خانه، خدمات، سرمایهگذاری و
بخش تماس) با از کار افتادن درخت ریاکت به صفحهٔ خالی تبدیل میشد.
در پنجرهٔ تعویض کانتینر فرانتاند، درخواستِ هشِ جدید که در dist کانتینرِ در
حال اجرا نبود با fallback اسپا پاسخ 200ٔ text/html میگرفت؛ CDN (WCDN، SMART)
همین پاسخ HTML را زیر URL فایل جاوااسکریپت با هدر immutable یکساله کش میکرد
و اسکریپت ماژول برای همهٔ بازدیدکنندهها میشکست (سایت روی پریلودر قفل میشد).
حالا فایل ناموجود در assets واقعاً 404 میدهد که کش نمیشود.
کلید فهرست ENTITIES برای چند موجودیت snake_case است (why_us، contact_section،
seo_meta، …) ولی resource همانها کبابکامل؛ isTranslatable و سایر جستجوها با
ENTITIES[resource] پیدا نمیشدند و نتیجه دو بود:
- ویرایش درجای «چرا آرامش» و متنهای فرم تماس روی صفحهٔ انگلیسی بهجای ستونهای
_en ستون فارسی را بازنویسی میکرد (حتی با زبان ویرایش انگلیسی)؛
- قرص سئوی صفحهٔ اصلی (SingletonPill resource="seo-meta") اصلاً رندر نمیشد.
حالا همهٔ جستجوها با entityConfig بر اساس resource انجام میشود.
تیتر و زیرتیتر هیرو درجا ویرایش میشوند اما در فهرست فیلدهای موجودیت هیرو
تعریف نشده بودند؛ isTranslatable('hero','title') false برمیگشت، کلید به
title_en نگاشت نمیشد و PATCH ستون فارسی را پر میکرد — با زبان ویرایش
انگلیسی هم. مقادیر خرابشدهٔ ستون فارسی از پشتیبان شبانه بازگردانی شد.
تیتر و توضیح بخشها (تیتر بخش خدمات، پروژههای شاخص، چرا آرامش و…) فقط
درجا ویرایش میشوند و تعریفشان در ENTITIES جا افتاده بود؛ isTranslatable
برایشان false برمیگشت و کلید تبدیل به title_en نمیشد — نتیجه اینکه
ویرایش روی صفحهٔ انگلیسی حتی با زبان ویرایش انگلیسی، متن فارسی را
بازنویسی میکرد و نسخهٔ انگلیسی بهظاهر هیچ تغییری نمیکرد.
قبلاً زبان ویرایش (editLang) کلید جداگانهای در نوار ادمین بود که با زبان صفحهٔ
در حال نمایش هیچ ارتباطی نداشت و پیشفرضش فارسی میماند؛ نتیجهاش این بود که
ویرایش درجا روی صفحههای /en به ستونهای فارسی میرفت و تغییرها «سمت دیگر»
یعنی سایت فارسی ظاهر میشدند.
- AdminBar: در حالت ویرایش، editLang با زبان URL همگام میشود (syncEditLang)؛
کلید دستی فقط برای ویرایش ستون زبان دیگر روی همین صفحه میماند.
- entities: تعریف status-labels اضافه شد — نبودش یعنی ویرایش برچسب وضعیت روی
صفحهٔ انگلیسی همیشه ستون فارسی را بازنویسی میکرد.
- بکاند: ستون role برای admin_users (admin/creator) + author_id برای مقالهها
- نقشگارد روی کل روتر مدیریتی: creator فقط لیست/ساخت/خواندن/ویرایش
پیشنویسهای «خودش» را میبیند؛ بقیهٔ مسیرهای مدیریتی ۴۰۳
- ساخت توسط creator همیشه پیشنویس است و ویرایشش هرگز منتشر نمیشود
(حتی تلاش برای publish در payload نادیده گرفته میشود)
- حذف/ترتیببندی/پیامها/خبرنامه/سایر بخشها برای creator ممنوع
- لاگین نقش را برمیگرداند؛ پنل: AdminBar نویسنده فقط «نوشتن مقاله» +
زبان ویرایش + رمز + خروج دارد؛ دکمههای انتشار/حذف مقاله فقط برای admin
- ورود creator مستقیم به صفحهٔ نوشتن مقاله هدایت میشود
- انیمیشن ورود هیرو تاخیر ۲.۱ تا ۲.۶ ثانیه داشت (بعد از بارگذاری JS+API هم
متن ۳ ثانیه نامرئی بود)؛ حالا ورود نرم با تاخیر ۰.۰۵-۰.۳۶ ثانیه
- SiteProvider: /api/site و /api/admin/articles پشت سر هم fetch میشدند و
setData منتظر هر دو میماند؛ حالا همزمان شروع میشوند