All checks were successful
ci / build (push) Successful in 18s
홈에 있던 쇼케이스 섹션은 스크린샷 두 장을 카드에 넣고 각도를 준 것이었고,
눌러도 갈 곳이 없었다. 그림만 보여주고 끝나는 자리는 증거가 아니라 장식이다.
- /showcase 를 별도 페이지로 만들었다. 모자이크 그리드에 썸네일을 놓고
누르면 실제로 도는 사이트(/work/<slug>/)가 열린다.
- 사례 데이터를 src/data/work.ts 한 곳에 모았다. 항목 하나만 추가하면
그리드에 들어간다 — 페이지 코드는 건드리지 않는다.
- 첫 사례로 목요일의 화원(플로럴 스튜디오)을 넣고 실제 파일을
public/work/thursday-flowers/ 에 함께 싣는다.
- 홈에서 예전 섹션을 제거하고 내비를 /showcase 로 돌렸다.
본문 말투:
사이트 문장이 전부 반말이었고 일부는 내부 감사 로그처럼 읽혔다.
스킬 문서의 단정한 반말은 의도지만, 방문자를 향한 화면은 다르다.
제목은 단정문이 카피로서 힘을 가지므로 그대로 두고, 설명하는 문장과
명령형("~해라")을 전부 존댓말로 바꿨다. 실측 결과 홈·쇼케이스 모두 0건.
구현에서 걸린 것:
Base 레이아웃이 이미 <main class="page"> 를 제공하는데 새 페이지에서
또 감쌌다. 바깥 page 의 본문 칼럼(561px) 안에 안쪽 page 가 들어가
전 페이지가 좁아졌다 — 모자이크가 561px, 썸네일이 240px 이었다.
중첩을 걷어내자 1180px / 596px 로 돌아왔다.
186 lines
6.6 KiB
JavaScript
186 lines
6.6 KiB
JavaScript
/**
|
|
* 방문자에게 보이는 한국어 본문을 존댓말로 바꾼다.
|
|
*
|
|
* 제목(h1·h2·name·rule·caught)은 건드리지 않는다 — 단정문이 카피로서 힘을 갖는 자리다.
|
|
* 바꾸는 것은 **설명하는 문장**과 **명령형**이다.
|
|
* 방문자에게 "~해라" 라고 말하는 화면은 무례하다.
|
|
*/
|
|
import fs from "node:fs";
|
|
|
|
const FILE = "src/i18n/content.ts";
|
|
|
|
/** 이 키의 값만 바꾼다. 나머지는 그대로 둔다 */
|
|
const TARGET_KEYS = new Set([
|
|
"lead", "note", "how", "problem", "use", "what", "does", "becomes", "made", "alt", "sample",
|
|
"why", "fixed", "caught", "caveat", "installNote", "description", "verdict", "caption",
|
|
]);
|
|
|
|
/** 순서가 중요하다. 긴 패턴을 먼저 잡지 않으면 짧은 것이 먼저 먹는다 */
|
|
const RULES = [
|
|
// ── 명령형 — 방문자에게 명령하지 않는다 ──
|
|
[/해라\./g, "하세요."], [/해라$/g, "하세요"],
|
|
[/봐라\./g, "보세요."], [/봐라$/g, "보세요"],
|
|
[/마라\./g, "마세요."], [/마라$/g, "마세요"],
|
|
[/써라\./g, "쓰세요."], [/써라$/g, "쓰세요"],
|
|
[/가라\./g, "가세요."], [/골라라/g, "고르세요"],
|
|
|
|
// ── 서술 ──
|
|
[/하지 않는다/g, "하지 않습니다"],
|
|
[/되지 않는다/g, "되지 않습니다"],
|
|
[/지 않는다/g, "지 않습니다"],
|
|
[/않는다/g, "않습니다"],
|
|
[/것이 아니다/g, "것이 아닙니다"],
|
|
[/아니다/g, "아닙니다"],
|
|
[/것이다/g, "것입니다"],
|
|
[/뿐이다/g, "뿐입니다"],
|
|
[/때문이다/g, "때문입니다"],
|
|
[/없어진다/g, "없어집니다"],
|
|
[/사라진다/g, "사라집니다"],
|
|
[/달라진다/g, "달라집니다"],
|
|
[/남는다/g, "남습니다"],
|
|
[/걸린다/g, "걸립니다"],
|
|
[/나온다/g, "나옵니다"],
|
|
[/들어간다/g, "들어갑니다"],
|
|
[/올라간다/g, "올라갑니다"],
|
|
[/떨어진다/g, "떨어집니다"],
|
|
[/무너진다/g, "무너집니다"],
|
|
[/바뀐다/g, "바뀝니다"],
|
|
[/이어진다/g, "이어집니다"],
|
|
[/생긴다/g, "생깁니다"],
|
|
[/보인다/g, "보입니다"],
|
|
[/읽힌다/g, "읽힙니다"],
|
|
[/잡힌다/g, "잡힙니다"],
|
|
[/쓰인다/g, "쓰입니다"],
|
|
[/맡긴다/g, "맡깁니다"],
|
|
[/만든다/g, "만듭니다"],
|
|
[/막는다/g, "막습니다"],
|
|
[/줄인다/g, "줄입니다"],
|
|
[/센다/g, "셉니다"],
|
|
[/짠다/g, "짭니다"],
|
|
[/뺀다/g, "뺍니다"],
|
|
[/둔다/g, "둡니다"],
|
|
[/한다/g, "합니다"],
|
|
[/된다/g, "됩니다"],
|
|
[/있다/g, "있습니다"],
|
|
[/없다/g, "없습니다"],
|
|
[/같다/g, "같습니다"],
|
|
[/좁다/g, "좁습니다"],
|
|
[/넓다/g, "넓습니다"],
|
|
[/맞다/g, "맞습니다"],
|
|
[/낫다/g, "낫습니다"],
|
|
[/멈춘다/g, "멈춥니다"],
|
|
[/이긴다/g, "이깁니다"],
|
|
[/모른다/g, "모릅니다"],
|
|
[/받는다/g, "받습니다"],
|
|
[/걸었다/g, "걸었습니다"],
|
|
[/바꿨다/g, "바꿨습니다"],
|
|
[/했다/g, "했습니다"],
|
|
[/였다/g, "였습니다"],
|
|
[/었다/g, "었습니다"],
|
|
[/았다/g, "았습니다"],
|
|
[/린다/g, "립니다"],
|
|
[/난다/g, "납니다"],
|
|
[/쓴다/g, "씁니다"],
|
|
[/본다/g, "봅니다"],
|
|
[/간다/g, "갑니다"],
|
|
[/온다/g, "옵니다"],
|
|
[/골라준다/g, "골라줍니다"],
|
|
[/고른다/g, "고릅니다"],
|
|
[/넘긴다/g, "넘깁니다"],
|
|
[/채운다/g, "채웁니다"],
|
|
[/압축한다/g, "압축합니다"],
|
|
[/판정한다/g, "판정합니다"],
|
|
[/정한다/g, "정합니다"],
|
|
[/확정한다/g, "확정합니다"],
|
|
[/구분한다/g, "구분합니다"],
|
|
[/합성한다/g, "합성합니다"],
|
|
[/내린다/g, "내립니다"],
|
|
[/열린다/g, "열립니다"],
|
|
[/붙는다/g, "붙습니다"],
|
|
[/넣는다/g, "넣습니다"],
|
|
[/놓는다/g, "놓습니다"],
|
|
[/찾는다/g, "찾습니다"],
|
|
[/맞춘다/g, "맞춥니다"],
|
|
[/갖춘다/g, "갖춥니다"],
|
|
[/낮춘다/g, "낮춥니다"],
|
|
[/높인다/g, "높입니다"],
|
|
[/모은다/g, "모읍니다"],
|
|
[/버린다/g, "버립니다"],
|
|
[/살린다/g, "살립니다"],
|
|
[/시킨다/g, "시킵니다"],
|
|
[/지킨다/g, "지킵니다"],
|
|
[/적는다/g, "적습니다"],
|
|
[/삼는다/g, "삼습니다"],
|
|
[/남긴다/g, "남깁니다"],
|
|
[/옮긴다/g, "옮깁니다"],
|
|
[/뒤집는다/g, "뒤집습니다"],
|
|
[/따른다/g, "따릅니다"],
|
|
[/부른다/g, "부릅니다"],
|
|
[/누른다/g, "누릅니다"],
|
|
[/자른다/g, "자릅니다"],
|
|
[/오른다/g, "오릅니다"],
|
|
[/미룬다/g, "미룹니다"],
|
|
[/켠다/g, "켭니다"],
|
|
[/편다/g, "폅니다"],
|
|
[/센다/g, "셉니다"],
|
|
[/건다/g, "겁니다"],
|
|
[/푼다/g, "풉니다"],
|
|
[/꾼다/g, "꿉니다"],
|
|
[/띈다/g, "띕니다"],
|
|
[/진다/g, "집니다"],
|
|
[/킨다/g, "킵니다"],
|
|
[/힌다/g, "힙니다"],
|
|
[/빈다/g, "빕니다"],
|
|
[/친다/g, "칩니다"],
|
|
[/잘라낸다/g, "잘라냅니다"],
|
|
[/잘린다/g, "잘립니다"],
|
|
[/뭉개진다/g, "뭉개집니다"],
|
|
[/건너뛴다/g, "건너뜁니다"],
|
|
[/깎는다/g, "깎습니다"],
|
|
[/죽는다/g, "죽습니다"],
|
|
[/잰다/g, "잽니다"],
|
|
[/낸다/g, "냅니다"],
|
|
[/긴다/g, "깁니다"],
|
|
[/없앴다/g, "없앴습니다"],
|
|
[/걷어냈다/g, "걷어냈습니다"],
|
|
[/남겼다/g, "남겼습니다"],
|
|
[/뿌렸다/g, "뿌렸습니다"],
|
|
[/넘쳤다/g, "넘쳤습니다"],
|
|
[/늘렸다/g, "늘렸습니다"],
|
|
[/신호다/g, "신호입니다"],
|
|
[/결과다/g, "결과입니다"],
|
|
[/길다/g, "깁니다"],
|
|
[/그대로였다/g, "그대로였습니다"],
|
|
[/보였다/g, "보였습니다"],
|
|
[/튀고 있었다/g, "튀고 있었습니다"],
|
|
[/어긋나 있었다/g, "어긋나 있었습니다"],
|
|
[/적었는데/g, "적었는데"],
|
|
];
|
|
|
|
function polite(text) {
|
|
let out = text;
|
|
for (const [re, to] of RULES) out = out.replace(re, to);
|
|
return out;
|
|
}
|
|
|
|
const src = fs.readFileSync(FILE, "utf8");
|
|
// STAGES_KO 는 `export const ko` 밖에 따로 선언돼 있다. 범위를 거기서 시작하지 않으면
|
|
// 파이프라인 여섯 단계의 설명이 통째로 빠진다 — 실측에서 37개가 반말로 남아 있었다.
|
|
const koStart = src.indexOf("const STAGES_KO");
|
|
const koEnd = src.indexOf("const STAGES_EN");
|
|
const koStart2 = src.indexOf("export const ko");
|
|
const koEnd2 = src.indexOf("export const en");
|
|
let ko = src.slice(koStart, koEnd);
|
|
let ko2 = src.slice(koStart2, koEnd2);
|
|
|
|
let changed = 0;
|
|
// `key: "..."` 와 `key:\n "..."` 를 모두 잡는다
|
|
ko = ko.replace(/(\b[a-zA-Z]+):(\s*)"((?:[^"\\]|\\.)*)"/g, (m, key, gap, val) => {
|
|
if (!TARGET_KEYS.has(key)) return m;
|
|
const next = polite(val);
|
|
if (next !== val) changed++;
|
|
return `${key}:${gap}"${next}"`;
|
|
});
|
|
|
|
fs.writeFileSync(FILE, src.slice(0, koStart) + ko + src.slice(koEnd), "utf8");
|
|
console.log(`존댓말 변환: ${changed}개 문자열`);
|