feat(supporters): 공유 카드(og) 영어화
해외 환자가 받는 링크라 카카오톡·슬랙 미리보기의 제목·설명을 영어로 낸다(haewon 2026-09-15). site.json shareNameEn 이 있을 때만 적용하고, 없으면 기존 한국어 카드를 유지한다. - 홈·한국어 페이지: "<영문명> Supporters · Answers to the questions patients ask first" + 영어 설명 - 영문 페이지: 자기 영어 제목·설명을 쓰되 섞인 한국어 상호를 영문 표기로 바꾼다 - 글(article): 본문이 한국어라 제목·설명은 그대로, 사이트 이름만 영어 - <title>·meta description 은 페이지 언어 그대로(검색 결과용) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7b22b86a27
commit
eaf60a9cf0
@ -19,6 +19,10 @@ interface Props {
|
|||||||
}
|
}
|
||||||
// 공유 미리보기 기본 이미지: 워커 build 단계가 만드는 public/og.jpg(1200x630, scripts/make_share_assets.py). 없으면 건물 사진.
|
// 공유 미리보기 기본 이미지: 워커 build 단계가 만드는 public/og.jpg(1200x630, scripts/make_share_assets.py). 없으면 건물 사진.
|
||||||
const shareCard = existsSync('public/og.jpg');
|
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 { 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 site = Astro.site?.toString().replace(/\/$/, '') ?? '';
|
||||||
const cleanPath = Astro.url.pathname.replace(/\/index\.html$/, '/').replace(/\.html$/, '');
|
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 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 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 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;
|
const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array.isArray(jsonLd) ? { '@graph': jsonLd } : jsonLd) }) : null;
|
||||||
---
|
---
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
@ -52,9 +66,9 @@ const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array
|
|||||||
<link rel="canonical" href={canonical} />
|
<link rel="canonical" href={canonical} />
|
||||||
{(!INDEXABLE || noindex) && <meta name="robots" content="noindex, nofollow" />}
|
{(!INDEXABLE || noindex) && <meta name="robots" content="noindex, nofollow" />}
|
||||||
<meta property="og:type" content={type} />
|
<meta property="og:type" content={type} />
|
||||||
<meta property="og:site_name" content={SITE_NAME} />
|
<meta property="og:site_name" content={siteCard} />
|
||||||
<meta property="og:title" content={title} />
|
<meta property="og:title" content={ogTitle} />
|
||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={ogDesc} />
|
||||||
<meta property="og:url" content={canonical} />
|
<meta property="og:url" content={canonical} />
|
||||||
<meta property="og:image" content={new URL(ogImage, Astro.site).toString()} />
|
<meta property="og:image" content={new URL(ogImage, Astro.site).toString()} />
|
||||||
{ogImage === '/og.jpg' && <meta property="og:image:width" content="1200" />}
|
{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="32x32" href="/favicon-32.png" />
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png" />
|
<link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.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}`} />)}
|
{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="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" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
|||||||
@ -19,6 +19,10 @@ interface Props {
|
|||||||
}
|
}
|
||||||
// 공유 미리보기 기본 이미지: 워커 build 단계가 만드는 public/og.jpg(1200x630, scripts/make_share_assets.py). 없으면 건물 사진.
|
// 공유 미리보기 기본 이미지: 워커 build 단계가 만드는 public/og.jpg(1200x630, scripts/make_share_assets.py). 없으면 건물 사진.
|
||||||
const shareCard = existsSync('public/og.jpg');
|
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 { 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 site = Astro.site?.toString().replace(/\/$/, '') ?? '';
|
||||||
const cleanPath = Astro.url.pathname.replace(/\/index\.html$/, '/').replace(/\.html$/, '');
|
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 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 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 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;
|
const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array.isArray(jsonLd) ? { '@graph': jsonLd } : jsonLd) }) : null;
|
||||||
---
|
---
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
@ -52,9 +66,9 @@ const ld = jsonLd ? JSON.stringify({ '@context': 'https://schema.org', ...(Array
|
|||||||
<link rel="canonical" href={canonical} />
|
<link rel="canonical" href={canonical} />
|
||||||
{(!INDEXABLE || noindex) && <meta name="robots" content="noindex, nofollow" />}
|
{(!INDEXABLE || noindex) && <meta name="robots" content="noindex, nofollow" />}
|
||||||
<meta property="og:type" content={type} />
|
<meta property="og:type" content={type} />
|
||||||
<meta property="og:site_name" content={SITE_NAME} />
|
<meta property="og:site_name" content={siteCard} />
|
||||||
<meta property="og:title" content={title} />
|
<meta property="og:title" content={ogTitle} />
|
||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={ogDesc} />
|
||||||
<meta property="og:url" content={canonical} />
|
<meta property="og:url" content={canonical} />
|
||||||
<meta property="og:image" content={new URL(ogImage, Astro.site).toString()} />
|
<meta property="og:image" content={new URL(ogImage, Astro.site).toString()} />
|
||||||
{ogImage === '/og.jpg' && <meta property="og:image:width" content="1200" />}
|
{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="32x32" href="/favicon-32.png" />
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png" />
|
<link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.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}`} />)}
|
{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="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" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user