fix(supporters): 영문 사실이 없는 병원에서 영어 홈이 깨지지 않도록 조각 단위로 조립

세 병원 재빌드를 앞두고 빈 데이터로 템플릿을 빌드해 보니 영어 홈이 깨졌다.
제목이 ", Seoul · Recovery planner", 히어로가 ", plan your stay before you fly.",
소개 문장이 "is a at ." 로 나왔다. 게이트는 이것을 오류로 잡지 않는다.
세 병원 모두 factSheetEn 이 비어 있어 그대로 배포하면 깨진 화면이 나간다.

고친 것
- 병원명: factSheetEn 의 영문 표기가 없으면 한국어 상호를 그대로 쓴다. 이름은 지어내지 않는다.
- 소개 문장: 업종·지역·설립연도 중 데이터에 있는 조각만 잇고, 하나도 없으면 문장 자체를 뺀다.
- 히어로 제목: 이름이 없으면 이름 줄을 빼고 문장을 대문자로 시작한다.
- 페이지 제목·설명·JSON-LD 이름: 같은 방식으로 조각을 잇는다. 제목에 박혀 있던 "Seoul" 을 뺐다.
- 머리말 기본값 "VIEW SUPPORTERS" 를 "SUPPORTERS" 로 바꿨다. 다른 병원 화면에 뷰가 나오던 자리다.

검증
- 빈 데이터 템플릿 14페이지: 제목 "Recovery planner, getting here, clinic facts",
  히어로 "Plan your stay before you fly." 로 문장이 성립한다.
- 뷰 32페이지: 제목·히어로·소개 문장이 모두 그대로다.
- 게이트 41/41, check.mjs 오류 0건, tsc 0 에러, 내보내기 종료코드 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Haewon Kam 2026-09-12 15:15:45 +09:00
parent e60ef04503
commit e7a11ef67a
2 changed files with 30 additions and 10 deletions

View File

@ -26,20 +26,30 @@ const chipFacts = [
has(f.founded) && `Founded ${f.founded}`, has(f.founded) && `Founded ${f.founded}`,
has((EN as Record<string, any>).areaLabel) && String((EN as Record<string, any>).areaLabel), has((EN as Record<string, any>).areaLabel) && String((EN as Record<string, any>).areaLabel),
].filter(Boolean).join(' · '); ].filter(Boolean).join(' · ');
// 영문 표기가 없는 병원은 한국어 상호를 그대로 쓴다. 이름을 지어내지 않는다.
const E = EN as Record<string, any>;
const nameEn = String(E.shortName || E.name || f.shortNameEn || f.shortName || '');
// 소개 문장은 데이터에 있는 조각만 잇는다. 빈 값을 그대로 끼우면 "is a at ." 같은 문장이 화면에 나간다.
const introHead = [E.kind && `is a ${E.kind}`, E.areaLabel && `at ${E.areaLabel}`].filter(Boolean).join(' ');
const introFounded = has(f.founded) ? `${introHead ? ', ' : 'was '}founded in ${f.founded}` : '';
const intro = nameEn && (introHead || introFounded) ? `${nameEn} ${introHead}${introFounded}.` : '';
const siteEyebrow = String(S.siteNameEn || 'SUPPORTERS');
const headTitle = [nameEn, 'Recovery planner, getting here, clinic facts'].filter(Boolean).join(' · ');
const headDesc = [intro, 'Plan your recovery stay around your surgery date, find the way here, and check what the clinic states publicly.'].filter(Boolean).join(' ');
const hoursDays = EN.hoursDays as Record<string, string>; const hoursDays = EN.hoursDays as Record<string, string>;
const ld = [ const ld = [
{ '@type': 'WebPage', '@id': `${site}/en#page`, name: `${EN.shortName} Supporters (English)`, url: `${site}/en`, inLanguage: 'en', isPartOf: { '@id': `${site}/#website` }, about: { '@id': `${fact.url}/#clinic` } }, { '@type': 'WebPage', '@id': `${site}/en#page`, name: [nameEn, 'Supporters (English)'].filter(Boolean).join(' '), url: `${site}/en`, inLanguage: 'en', isPartOf: { '@id': `${site}/#website` }, about: { '@id': `${fact.url}/#clinic` } },
clinicSchema(site), clinicSchema(site),
]; ];
--- ---
<Base lang="en" title={`${EN.shortName}, Seoul · Recovery planner, getting here, clinic facts`} description={`${EN.shortName} is a ${EN.kind} at ${EN.areaLabel}, founded in ${v(f.founded)}. Plan your recovery stay around your surgery date, find the way from the airport, and check opening hours and clinic facts in English.`} jsonLd={ld} alternates={[{ lang: 'ko', href: '/' }, { lang: 'en', href: '/en' }]}> <Base lang="en" title={headTitle} description={headDesc} jsonLd={ld}>
<section class="hero-light"> <section class="hero-light">
<div class="blob a"></div><div class="blob b"></div> <div class="blob a"></div><div class="blob b"></div>
<div class="wrap-wide inner"> <div class="wrap-wide inner">
<div> <div>
<div class="eyebrow">{S.siteNameEn || 'VIEW SUPPORTERS'} · English</div> <div class="eyebrow">{siteEyebrow} · English</div>
<h1>{EN.shortName},<br /><span class="accent">plan your stay before you fly.</span></h1> <h1>{nameEn ? <>{nameEn},<br /><span class="accent">plan your stay before you fly.</span></> : <span class="accent">Plan your stay before you fly.</span>}</h1>
<p class="lede">{EN.shortName} is a {EN.kind} at {EN.areaLabel}{has(f.founded) && <>, founded in {f.founded}</>}. This site is run by supporters who answer the questions people ask before visiting. In English you can plan your recovery stay, find the way here and check clinic facts. Articles are in Korean.</p> <p class="lede">{intro && <>{intro} </>}This site is run by supporters who answer the questions people ask before visiting. In English you can plan your recovery stay, find the way here and check clinic facts. Articles are in Korean.</p>
<div class="chips"> <div class="chips">
{chipFacts && <span class="chip"><span class="dot"></span>{chipFacts}</span>} {chipFacts && <span class="chip"><span class="dot"></span>{chipFacts}</span>}
<span class="chip"><span class="dot"></span>Based on the clinic's published guidance</span> <span class="chip"><span class="dot"></span>Based on the clinic's published guidance</span>

View File

@ -26,20 +26,30 @@ const chipFacts = [
has(f.founded) && `Founded ${f.founded}`, has(f.founded) && `Founded ${f.founded}`,
has((EN as Record<string, any>).areaLabel) && String((EN as Record<string, any>).areaLabel), has((EN as Record<string, any>).areaLabel) && String((EN as Record<string, any>).areaLabel),
].filter(Boolean).join(' · '); ].filter(Boolean).join(' · ');
// 영문 표기가 없는 병원은 한국어 상호를 그대로 쓴다. 이름을 지어내지 않는다.
const E = EN as Record<string, any>;
const nameEn = String(E.shortName || E.name || f.shortNameEn || f.shortName || '');
// 소개 문장은 데이터에 있는 조각만 잇는다. 빈 값을 그대로 끼우면 "is a at ." 같은 문장이 화면에 나간다.
const introHead = [E.kind && `is a ${E.kind}`, E.areaLabel && `at ${E.areaLabel}`].filter(Boolean).join(' ');
const introFounded = has(f.founded) ? `${introHead ? ', ' : 'was '}founded in ${f.founded}` : '';
const intro = nameEn && (introHead || introFounded) ? `${nameEn} ${introHead}${introFounded}.` : '';
const siteEyebrow = String(S.siteNameEn || 'SUPPORTERS');
const headTitle = [nameEn, 'Recovery planner, getting here, clinic facts'].filter(Boolean).join(' · ');
const headDesc = [intro, 'Plan your recovery stay around your surgery date, find the way here, and check what the clinic states publicly.'].filter(Boolean).join(' ');
const hoursDays = EN.hoursDays as Record<string, string>; const hoursDays = EN.hoursDays as Record<string, string>;
const ld = [ const ld = [
{ '@type': 'WebPage', '@id': `${site}/en#page`, name: `${EN.shortName} Supporters (English)`, url: `${site}/en`, inLanguage: 'en', isPartOf: { '@id': `${site}/#website` }, about: { '@id': `${fact.url}/#clinic` } }, { '@type': 'WebPage', '@id': `${site}/en#page`, name: [nameEn, 'Supporters (English)'].filter(Boolean).join(' '), url: `${site}/en`, inLanguage: 'en', isPartOf: { '@id': `${site}/#website` }, about: { '@id': `${fact.url}/#clinic` } },
clinicSchema(site), clinicSchema(site),
]; ];
--- ---
<Base lang="en" title={`${EN.shortName}, Seoul · Recovery planner, getting here, clinic facts`} description={`${EN.shortName} is a ${EN.kind} at ${EN.areaLabel}, founded in ${v(f.founded)}. Plan your recovery stay around your surgery date, find the way from the airport, and check opening hours and clinic facts in English.`} jsonLd={ld} alternates={[{ lang: 'ko', href: '/' }, { lang: 'en', href: '/en' }]}> <Base lang="en" title={headTitle} description={headDesc} jsonLd={ld}>
<section class="hero-light"> <section class="hero-light">
<div class="blob a"></div><div class="blob b"></div> <div class="blob a"></div><div class="blob b"></div>
<div class="wrap-wide inner"> <div class="wrap-wide inner">
<div> <div>
<div class="eyebrow">{S.siteNameEn || 'VIEW SUPPORTERS'} · English</div> <div class="eyebrow">{siteEyebrow} · English</div>
<h1>{EN.shortName},<br /><span class="accent">plan your stay before you fly.</span></h1> <h1>{nameEn ? <>{nameEn},<br /><span class="accent">plan your stay before you fly.</span></> : <span class="accent">Plan your stay before you fly.</span>}</h1>
<p class="lede">{EN.shortName} is a {EN.kind} at {EN.areaLabel}{has(f.founded) && <>, founded in {f.founded}</>}. This site is run by supporters who answer the questions people ask before visiting. In English you can plan your recovery stay, find the way here and check clinic facts. Articles are in Korean.</p> <p class="lede">{intro && <>{intro} </>}This site is run by supporters who answer the questions people ask before visiting. In English you can plan your recovery stay, find the way here and check clinic facts. Articles are in Korean.</p>
<div class="chips"> <div class="chips">
{chipFacts && <span class="chip"><span class="dot"></span>{chipFacts}</span>} {chipFacts && <span class="chip"><span class="dot"></span>{chipFacts}</span>}
<span class="chip"><span class="dot"></span>Based on the clinic's published guidance</span> <span class="chip"><span class="dot"></span>Based on the clinic's published guidance</span>