diff --git a/supporters/src/layouts/Base.astro b/supporters/src/layouts/Base.astro index 76a3c1f..8573265 100644 --- a/supporters/src/layouts/Base.astro +++ b/supporters/src/layouts/Base.astro @@ -19,6 +19,10 @@ interface Props { } // 공유 미리보기 기본 이미지: 워커 build 단계가 만드는 public/og.jpg(1200x630, scripts/make_share_assets.py). 없으면 건물 사진. const shareCard = existsSync('public/og.jpg'); +// 공유 카드(og·twitter)는 해외 환자용으로 영어로 낸다(haewon 2026-09-15). site.json shareNameEn(병원 공식 영문 표기)이 있을 때만. +// 글(article)은 본문이 한국어라 제목·설명은 글 그대로 두고 사이트 이름만 영어로 쓴다. 영문 페이지는 자기 영어 제목을 쓴다. +// ·meta description 은 페이지 언어 그대로 둔다. 검색 결과에 나가는 값이라 공유 카드와 역할이 다르다. +const nameEn: string = String((S as Record<string, any>).shareNameEn || '').trim(); const { title, description, jsonLd, ogImage = shareCard ? '/og.jpg' : (S.buildingImage?.src || '/favicon-512.png'), type = 'website', noindex = false, lang = 'ko', alternates = [] } = Astro.props; const site = Astro.site?.toString().replace(/\/$/, '') ?? ''; const cleanPath = Astro.url.pathname.replace(/\/index\.html$/, '/').replace(/\.html$/, ''); @@ -40,6 +44,16 @@ const sideEffectEn: string = (f as Record<string, any>).sideEffectNoticeEn || '' const noticePending = lang === 'en' && !sponsorEn; const sponsorText = (lang === 'en' && sponsorEn) ? sponsorEn : UI.ko.foot_sponsor.replace(/\{clinic\}/g, v(f.shortName, '병원')).replace('{editor}', ed.name); const sideEffectText = (lang === 'en' && sideEffectEn) ? sideEffectEn : fact.sideEffectNotice; +const siteCard = nameEn ? `${nameEn} Supporters` : SITE_NAME; +const cardEn = !!nameEn && type !== 'article' && (lang === 'ko' || isHome); +// 영문 페이지 제목·설명에 섞인 한국어 상호는 공유 카드에서만 영문 표기로 바꾼다. +const koName = String(f.shortName || '').trim(); +const toEn = (t: string) => (nameEn && koName && lang === 'en' ? t.split(koName).join(nameEn) : t); +const ogTitle = cardEn ? `${siteCard} · Answers to the questions patients ask first` : toEn(title); +const ogDesc = cardEn + ? `Recovery planner, getting here, and clinic facts for international patients of ${nameEn}, based on the doctors' own videos and the clinic's public information.` + : toEn(description); +const ogLocale = cardEn || lang === 'en' ? 'en_US' : 'ko_KR'; const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array.isArray(jsonLd) ? { '@graph': jsonLd } : jsonLd) }) : null; --- <!doctype html> @@ -52,9 +66,9 @@ const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array <link rel="canonical" href={canonical} /> {(!INDEXABLE || noindex) && <meta name="robots" content="noindex, nofollow" />} <meta property="og:type" content={type} /> - <meta property="og:site_name" content={SITE_NAME} /> - <meta property="og:title" content={title} /> - <meta property="og:description" content={description} /> + <meta property="og:site_name" content={siteCard} /> + <meta property="og:title" content={ogTitle} /> + <meta property="og:description" content={ogDesc} /> <meta property="og:url" content={canonical} /> <meta property="og:image" content={new URL(ogImage, Astro.site).toString()} /> {ogImage === '/og.jpg' && <meta property="og:image:width" content="1200" />} @@ -64,7 +78,8 @@ const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" /> <link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png" /> <link rel="apple-touch-icon" href="/apple-touch-icon.png" /> - <meta property="og:locale" content={lang === 'en' ? 'en_US' : 'ko_KR'} /> + <meta property="og:locale" content={ogLocale} /> + {ogImage === '/og.jpg' && <meta property="og:image:alt" content={siteCard} />} {alternates.map((a) => <link rel="alternate" hreflang={a.lang} href={`${site}${a.href}`} />)} <link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" /> <link rel="preconnect" href="https://fonts.googleapis.com" /> diff --git a/templates/supporters-astro/src/layouts/Base.astro b/templates/supporters-astro/src/layouts/Base.astro index 76a3c1f..8573265 100644 --- a/templates/supporters-astro/src/layouts/Base.astro +++ b/templates/supporters-astro/src/layouts/Base.astro @@ -19,6 +19,10 @@ interface Props { } // 공유 미리보기 기본 이미지: 워커 build 단계가 만드는 public/og.jpg(1200x630, scripts/make_share_assets.py). 없으면 건물 사진. const shareCard = existsSync('public/og.jpg'); +// 공유 카드(og·twitter)는 해외 환자용으로 영어로 낸다(haewon 2026-09-15). site.json shareNameEn(병원 공식 영문 표기)이 있을 때만. +// 글(article)은 본문이 한국어라 제목·설명은 글 그대로 두고 사이트 이름만 영어로 쓴다. 영문 페이지는 자기 영어 제목을 쓴다. +// <title>·meta description 은 페이지 언어 그대로 둔다. 검색 결과에 나가는 값이라 공유 카드와 역할이 다르다. +const nameEn: string = String((S as Record<string, any>).shareNameEn || '').trim(); const { title, description, jsonLd, ogImage = shareCard ? '/og.jpg' : (S.buildingImage?.src || '/favicon-512.png'), type = 'website', noindex = false, lang = 'ko', alternates = [] } = Astro.props; const site = Astro.site?.toString().replace(/\/$/, '') ?? ''; const cleanPath = Astro.url.pathname.replace(/\/index\.html$/, '/').replace(/\.html$/, ''); @@ -40,6 +44,16 @@ const sideEffectEn: string = (f as Record<string, any>).sideEffectNoticeEn || '' const noticePending = lang === 'en' && !sponsorEn; const sponsorText = (lang === 'en' && sponsorEn) ? sponsorEn : UI.ko.foot_sponsor.replace(/\{clinic\}/g, v(f.shortName, '병원')).replace('{editor}', ed.name); const sideEffectText = (lang === 'en' && sideEffectEn) ? sideEffectEn : fact.sideEffectNotice; +const siteCard = nameEn ? `${nameEn} Supporters` : SITE_NAME; +const cardEn = !!nameEn && type !== 'article' && (lang === 'ko' || isHome); +// 영문 페이지 제목·설명에 섞인 한국어 상호는 공유 카드에서만 영문 표기로 바꾼다. +const koName = String(f.shortName || '').trim(); +const toEn = (t: string) => (nameEn && koName && lang === 'en' ? t.split(koName).join(nameEn) : t); +const ogTitle = cardEn ? `${siteCard} · Answers to the questions patients ask first` : toEn(title); +const ogDesc = cardEn + ? `Recovery planner, getting here, and clinic facts for international patients of ${nameEn}, based on the doctors' own videos and the clinic's public information.` + : toEn(description); +const ogLocale = cardEn || lang === 'en' ? 'en_US' : 'ko_KR'; const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array.isArray(jsonLd) ? { '@graph': jsonLd } : jsonLd) }) : null; --- <!doctype html> @@ -52,9 +66,9 @@ const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array <link rel="canonical" href={canonical} /> {(!INDEXABLE || noindex) && <meta name="robots" content="noindex, nofollow" />} <meta property="og:type" content={type} /> - <meta property="og:site_name" content={SITE_NAME} /> - <meta property="og:title" content={title} /> - <meta property="og:description" content={description} /> + <meta property="og:site_name" content={siteCard} /> + <meta property="og:title" content={ogTitle} /> + <meta property="og:description" content={ogDesc} /> <meta property="og:url" content={canonical} /> <meta property="og:image" content={new URL(ogImage, Astro.site).toString()} /> {ogImage === '/og.jpg' && <meta property="og:image:width" content="1200" />} @@ -64,7 +78,8 @@ const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" /> <link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png" /> <link rel="apple-touch-icon" href="/apple-touch-icon.png" /> - <meta property="og:locale" content={lang === 'en' ? 'en_US' : 'ko_KR'} /> + <meta property="og:locale" content={ogLocale} /> + {ogImage === '/og.jpg' && <meta property="og:image:alt" content={siteCard} />} {alternates.map((a) => <link rel="alternate" hreflang={a.lang} href={`${site}${a.href}`} />)} <link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" /> <link rel="preconnect" href="https://fonts.googleapis.com" />