# 사이트 런타임 문구 카탈로그 원본 (v1.3.16) — 이 파일을 고치고 실행하면 locales/site/ko.json, en.json 이 다시 만들어진다. # 형식: (id, 그룹, 한국어 원문, 영어) — {이름} 은 자리 표시(번역문에도 그대로 둔다) import json, collections M = [ # 공통 ('common.all','common','전체','All'), ('common.list','common','목록','List'), ('common.delete','common','삭제','Delete'), ('common.confirm','common','확인','OK'), ('common.loading','common','불러오는 중…','Loading…'), ('common.sending','common','보내는 중…','Sending…'), ('common.send','common','보내기','Send'), ('common.submit','common','제출','Submit'), ('common.register','common','등록','Post'), ('common.cancel','common','취소','Cancel'), ('common.name','form','이름','Name'), ('common.nameOptional','form','이름(선택)','Name (optional)'), ('common.email','form','이메일','Email'), ('common.password','form','비밀번호','Password'), ('common.phone','form','연락처','Phone'), ('common.subject','form','제목','Subject'), ('common.content','form','내용','Message'), ('common.date','form','날짜','Date'), ('common.category','form','분류','Category'), ('common.other','common','기타','Other'), ('common.search','nav','검색','Search'), ('common.requiredCheck','error','필수 항목을 확인해 주세요.','Please check the required fields.'), ('common.received','notice','접수되었습니다.','Received.'), ('common.receivedContact','notice','접수되었습니다. 곧 연락드리겠습니다.','Thank you. We will get back to you soon.'), ('common.tooMany','error','요청이 너무 많습니다. 잠시 후 다시 시도해 주세요.','Too many requests. Please try again later.'), ('common.sendFailed','error','전송에 실패했습니다. 잠시 후 다시 시도해 주세요.','Sending failed. Please try again later.'), ('common.networkFailed','error','전송에 실패했습니다. 네트워크를 확인해 주세요.','Sending failed. Please check your network.'), ('common.siteNotFound','error','사이트를 찾을 수 없습니다','Site not found'), ('common.originDenied','error','허용되지 않은 출처입니다','This origin is not allowed'), # 회원 ('members.welcome','members','{name}님 환영합니다.','Welcome, {name}.'), ('members.logout','members','로그아웃','Log out'), ('members.login','members','로그인','Log in'), ('members.signup','members','회원가입','Sign up'), ('members.join','members','가입','Join'), ('members.toLogin','members','로그인으로','Back to log in'), ('members.passwordMin6','members','비밀번호 (6자 이상)','Password (6+ characters)'), ('members.pending','members','가입 신청이 접수되었습니다. 관리자 승인 후 로그인할 수 있습니다.','Your sign-up request was received. You can log in after an administrator approves it.'), ('members.loginRequired','members','회원 로그인 후 볼 수 있습니다.','Please log in to view this.'), # 게시판 ('board.title','board','게시판','Board'), ('board.write','board','글쓰기','Write'), ('board.author','board','글쓴이','Author'), ('board.views','board','조회','Views'), ('board.metaViews','board','· 조회 {n}','· {n} views'), ('board.metaComments','board','· 댓글 {n}','· {n} comments'), ('board.empty','board','아직 글이 없습니다.','No posts yet.'), ('board.secret','board','비밀글','Private post'), ('board.secretAskPw','board','비밀글입니다. 작성 시 입력한 비밀번호를 입력하세요.','This is a private post. Enter the password used when it was written.'), ('board.secretOnlyOwner','board','비밀글입니다. 작성자 또는 관리자만 볼 수 있습니다.','This is a private post. Only the author or an administrator can view it.'), ('board.reply','board','답글','Reply'), ('board.replyPlaceholder','board','답글을 입력하세요','Write a reply'), ('board.commentPlaceholder','board','댓글을 입력하세요','Write a comment'), ('board.commentPw','board','댓글 비밀번호','Comment password'), ('board.comments','board','댓글 {n}','Comments {n}'), ('board.commentSecret','board','비밀 댓글','Private comment'), ('board.commentSubmit','board','댓글 등록','Post comment'), ('board.guestPw','board','비밀번호(수정·삭제용, 4자 이상)','Password (for edit/delete, 4+ characters)'), ('board.guestPwDelete','board','비밀번호(삭제용)','Password (for delete)'), ('board.deleteWithPw','board','삭제(비밀번호)','Delete (password)'), ('board.deleteAskPw','board','삭제하려면 글 비밀번호를 입력하세요','Enter the post password to delete it'), ('board.deleteConfirm','board','이 글을 삭제할까요?','Delete this post?'), # 문의 ('inquiry.title','inquiry','문의하기','Contact us'), ('inquiry.button','inquiry','문의 보내기','Send inquiry'), ('inquiry.type','inquiry','문의 유형','Inquiry type'), ('inquiry.files','inquiry','첨부파일','Attachments'), ('inquiry.fileTooBig','error','첨부파일이 너무 큽니다 (최대 {mb}MB)','Attachment is too large (max {mb} MB)'), ('consent.privacy','consent','문의 처리를 위한 개인정보 수집·이용에 동의합니다.','I agree to the collection and use of my personal information to handle this inquiry.'), # 예약 ('booking.title','booking','예약하기','Book now'), ('booking.note','booking','요청 사항','Requests'), ('booking.contactPh','booking','전화 또는 이메일','Phone or email'), ('booking.submit','booking','예약 신청','Request booking'), ('booking.closedDate','booking','예약할 수 없는 날짜입니다.','Bookings are not available on this date.'), ('booking.noSlots','booking','이 날은 예약 시간대가 없습니다.','There are no time slots on this day.'), ('booking.seatsLeft','booking','({n}자리)','({n} left)'), ('booking.confirmed','booking','예약이 확정되었습니다.','Your booking is confirmed.'), ('booking.received','booking','예약이 접수되었습니다. 확인 후 연락드리겠습니다.','Your booking request was received. We will contact you after confirming it.'), # 갤러리·지도·후기·FAQ·설문 ('gallery.title','gallery','갤러리','Gallery'), ('gallery.empty','gallery','등록된 이미지가 없습니다.','No images yet.'), ('map.notFound','map','주소를 찾지 못했습니다: {address}','Address not found: {address}'), ('map.loadFailed','map','지도를 불러오지 못했습니다.','Could not load the map.'), ('map.noAddress','map','주소가 설정되지 않았습니다.','No address is set.'), ('reviews.title','reviews','고객 후기','Reviews'), ('reviews.write','reviews','후기 남기기','Write a review'), ('reviews.placeholder','reviews','후기를 남겨 주세요','Share your experience'), ('reviews.empty','reviews','아직 후기가 없습니다.','No reviews yet.'), ('reviews.low','reviews','★3 이하','★3 or less'), ('reviews.pending','reviews','후기가 접수되었습니다. 확인 후 게시됩니다.','Thank you. Your review will be published after review.'), ('reviews.posted','reviews','후기가 등록되었습니다.','Your review has been posted.'), ('faq.title','faq','자주 묻는 질문','Frequently asked questions'), ('faq.empty','faq','등록된 질문이 없습니다.','No questions yet.'), ('survey.none','survey','진행 중인 설문이 없습니다','There is no active survey'), # 모듈·폼 API 오류 ('err.unknownModule','error','알 수 없는 모듈','Unknown module'), ('err.noAccount','error','계정이 없습니다','Account not found'), ('err.signupClosed','error','회원가입을 받지 않습니다','Sign-up is closed'), ('err.emailFormat','error','이메일 형식을 확인하세요','Please check the email format'), ('err.nameRequired','error','이름을 입력하세요','Please enter your name'), ('err.passwordMin6','error','비밀번호는 6자 이상','Password must be at least 6 characters'), ('err.emailTaken','error','이미 가입된 이메일입니다','This email is already registered'), ('err.loginFailed','error','이메일 또는 비밀번호가 맞지 않습니다','Incorrect email or password'), ('err.approvalPending','error','관리자 승인 대기 중인 계정입니다','This account is waiting for administrator approval'), ('err.siteAdminInput','error','아이디와 6자 이상 비밀번호를 입력하세요','Enter an ID and a password of 6+ characters'), ('err.idTaken','error','이미 있는 아이디입니다','This ID already exists'), ('err.noBoard','error','게시판이 없습니다','Board not found'), ('err.membersOnly','error','회원만 볼 수 있는 게시판입니다','This board is for members only'), ('err.noPost','error','글이 없습니다','Post not found'), ('err.titleBody','error','제목과 내용을 입력하세요','Please enter a title and content'), ('err.guestPwPost','error','비회원은 수정·삭제용 비밀번호(4자 이상)를 입력하세요','Guests must enter a password (4+ characters) for edit/delete'), ('err.noEditPerm','error','수정 권한이 없습니다','You do not have permission to edit'), ('err.noDeletePerm','error','삭제 권한이 없습니다','You do not have permission to delete'), ('err.commentRequired','error','댓글 내용을 입력하세요','Please enter a comment'), ('err.guestPwComment','error','비회원은 삭제용 비밀번호(4자 이상)를 입력하세요','Guests must enter a password (4+ characters) for delete'), ('err.noComment','error','댓글이 없습니다','Comment not found'), ('err.noSurvey','error','설문이 없습니다','Survey not found'), ('err.surveyClosed','error','진행 중인 설문이 아닙니다','This survey is not active'), ('err.alreadyAnswered','error','이미 응답하셨습니다','You have already responded'), ('err.noBooking','error','예약 기능이 없습니다','Booking is not available'), ('err.dateFormat','error','날짜 형식 오류','Invalid date format'), ('err.bookingClosedDate','error','예약할 수 없는 날짜입니다','Bookings are not available on this date'), ('err.slotRequired','error','시간대를 선택하세요','Please choose a time slot'), ('err.slotFull','error','마감된 시간대입니다','This time slot is full'), ('err.nameContact','error','이름과 연락처를 입력하세요','Please enter your name and contact'), ('err.noReviews','error','후기 기능이 없습니다','Reviews are not available'), ('err.reviewRequired','error','후기 내용을 입력하세요','Please enter your review'), ('err.noFaq','error','FAQ 가 없습니다','FAQ not found'), ('err.noGallery','error','갤러리가 없습니다','Gallery not found'), ('err.unknownItem','error','알 수 없는 항목','Unknown item'), ('err.cannotAddHere','error','이 항목은 여기서 추가할 수 없습니다','This item cannot be added here'), ('err.requiredFields','error','필수 항목을 입력하세요','Please fill in the required fields'), ('err.noItem','error','항목이 없습니다','Item not found'), ('err.formName','error','이름을 입력해 주세요.','Please enter your name.'), ('err.formEmail','error','이메일 주소를 확인해 주세요.','Please check your email address.'), ('err.formMessage','error','문의 내용을 입력해 주세요.','Please enter your message.'), # 사이트 소유자에게 가는 알림 메일 ('mail.kindContact','mail','문의','inquiry'), ('mail.kindBooking','mail','예약 문의','booking inquiry'), ('mail.contactSubject','mail','[{site}] {kind}{subject} — {name}','[{site}] New {kind}{subject} — {name}'), ('mail.contactIntro','mail','{site} 웹사이트로 접수된 {kind}입니다.','A new {kind} was received through the {site} website.'), ('mail.fieldKind','mail','종류','Type'), ('mail.fieldReceivedAt','mail','접수 시각','Received at'), ('mail.fieldPage','mail','접수 페이지','Page'), ('mail.content','mail','[내용]','[Message]'), ('mail.replyHint','mail','이 메일에 답장하면 문의자({email})에게 전달됩니다.','Reply to this email to answer the sender ({email}).'), ('mail.surveySubject','mail','[{site}] 설문 응답: {survey} — {who}','[{site}] Survey response: {survey} — {who}'), ('mail.surveyIntro','mail','{site} 사이트의 설문 "{survey}" 에 새 응답이 접수되었습니다.','A new response was submitted to the survey "{survey}" on {site}.'), ('mail.adminPage','mail','관리자 페이지: {url}','Admin page: {url}'), ('mail.bookingSubject','mail','[{site}] 예약 {state}: {date} {time} — {name}','[{site}] Booking {state}: {date} {time} — {name}'), ('mail.bookingConfirmed','mail','확정','confirmed'), ('mail.bookingRequested','mail','신청','requested'), ('mail.bookingIntro','mail','{site} 사이트에 예약이 접수되었습니다.','A booking was received on {site}.'), ('mail.fieldRequest','mail','요청','Request'), ('mail.fieldStatus','mail','상태','Status'), ('mail.statusAuto','mail','자동 확정','Confirmed automatically'), ('mail.statusManual','mail','관리자 확인 필요','Needs administrator confirmation'), ('mail.reviewSubject','mail','[{site}] 새 후기 {stars} — {name}','[{site}] New review {stars} — {name}'), ('mail.reviewHint','mail','관리자 페이지에서 공개 여부를 정하세요: {url}','Decide whether to publish it on the admin page: {url}'), ('mail.inquirySubject','mail','[{site}] {category} 문의 — {name}','[{site}] {category} inquiry — {name}'), # v1.4.18 일정관리 캘린더 ('calendar.title','calendar','일정','Schedule'), ('calendar.today','calendar','오늘','Today'), ('calendar.prev','calendar','이전','Previous'), ('calendar.next','calendar','다음','Next'), ('calendar.month','calendar','월간','Month'), ('calendar.week','calendar','주간','Week'), ('calendar.day','calendar','일간','Day'), ('calendar.allDay','calendar','종일','All day'), ('calendar.empty','calendar','이 기간에는 일정이 없습니다.','There are no events in this period.'), ('calendar.more','calendar','+{n}개 더','+{n} more'), ('calendar.lunarPrefix','calendar','음','Lunar'), ('calendar.leap','calendar','윤','Leap'), ('calendar.lunarBased','calendar','음력 기준 일정','Lunar calendar event'), ('calendar.ganjiLabel','calendar','간지','Sexagenary'), ('calendar.termLabel','calendar','절기','Solar term'), ('calendar.location','calendar','장소','Location'), ('calendar.unitTime','calendar','시간 단위','Timed'), ('calendar.unitDay','calendar','일 단위 ({n}일)','Daily ({n} days)'), ('calendar.unitWeek','calendar','주 단위 ({n}주)','Weekly ({n} weeks)'), ('calendar.close','calendar','닫기','Close'), ('calendar.loadFailed','calendar','일정을 불러오지 못했습니다.','Could not load the schedule.'), ('err.noCalendar','error','일정 기능이 없습니다','Calendar is not available'), ('err.eventDate','error','일정 날짜를 확인하세요 (양력 YYYY-MM-DD 또는 음력 연·월·일 1~30)','Please check the event date (solar YYYY-MM-DD or lunar year/month/day 1–30)'), ('err.eventTime','error','시작 시각은 HH:MM 형식으로 입력하세요','Enter the start time as HH:MM'), ('err.rangeTooLong','error','조회 기간은 최대 {n}일입니다','The range may be at most {n} days'), # 법적 고지·쿠키·동의 (data-wm-legal 자리에 언어별로 채움) ('legal.copyright','legal','© {year} {name}. 모든 권리 보유.','© {year} {name}. All rights reserved.'), ('legal.privacy','legal','개인정보처리방침','Privacy Policy'), ('legal.terms','legal','이용약관','Terms of Use'), ('legal.cookieNotice','legal','이 사이트는 서비스 제공과 이용 통계를 위해 쿠키를 사용합니다.','This site uses cookies to provide its services and measure usage.'), ('legal.cookieAccept','legal','동의','Accept'), ('legal.cookieDecline','legal','거부','Decline'), ('legal.businessInfo','legal','상호 {name} · 대표 {owner} · 사업자등록번호 {bizNo}','{name} · Representative {owner} · Business registration no. {bizNo}'), ('lang.label','nav','언어','Language'), ('err.loginRequired','error','로그인이 필요합니다','Please log in'), ('err.inquiryOff','error','문의 기능이 꺼져 있습니다','Inquiries are turned off'), ('err.surveyOff','error','설문 기능이 꺼져 있습니다','Surveys are turned off'), ('err.bookingOff','error','예약 기능이 꺼져 있습니다','Booking is turned off'), ('err.tryLater','error','잠시 후 다시 시도하세요','Please try again later'), ('err.addressNotFound','error','주소를 찾지 못했습니다','Address not found'), ('mail.attachments','mail','[첨부] {files}','[Attachments] {files}'), # v1.4.0 블로그·자료실 ('blog.title','blog','블로그','Blog'), ('blog.searchPh','blog','블로그 글 검색','Search posts'), ('blog.empty','blog','아직 글이 없습니다.','No posts yet.'), ('blog.noResults','blog','검색 결과가 없습니다.','No results found.'), ('blog.readMore','blog','더 보기','Read more'), ('blog.back','blog','목록으로','Back to list'), ('blog.views','blog','조회 {n}','{n} views'), ('blog.comments','blog','댓글 {n}','Comments ({n})'), ('blog.commentPh','blog','댓글을 남겨 주세요','Leave a comment'), ('blog.commentSubmit','blog','댓글 남기기','Post comment'), ('blog.commentPending','blog','댓글이 접수되었습니다. 관리자 확인 후 보입니다.','Thanks! Your comment will appear after review.'), ('blog.commentPosted','blog','댓글이 등록되었습니다.','Your comment has been posted.'), ('blog.commentsClosed','blog','댓글을 받지 않는 글입니다.','Comments are closed.'), ('blog.tagAll','blog','모든 태그','All tags'), ('library.title','library','자료실','Downloads'), ('library.searchPh','library','자료 검색','Search files'), ('library.empty','library','등록된 자료가 없습니다.','No files yet.'), ('library.download','library','내려받기','Download'), ('library.downloads','library','내려받음 {n}','{n} downloads'), ('library.memberOnly','library','회원만 내려받을 수 있습니다. 로그인해 주세요.','Please log in to download.'), ('err.noBlog','error','블로그가 없습니다','Blog not found'), ('err.noLibrary','error','자료실이 없습니다','Downloads not found'), ('err.fileMissing','error','파일을 찾을 수 없습니다','File not found'), ('err.commentsClosed','error','댓글을 받지 않습니다','Comments are closed'), ('contact.messageLabel','inquiry','문의 내용','Message'), ('contact.bookingMessageLabel','booking','예약 희망 일시와 요청 사항','Preferred date/time and requests'), ('contact.bookingButton','booking','예약 문의 보내기','Send booking request'), ] ids = [m[0] for m in M]; assert len(ids) == len(set(ids)), [i for i in ids if ids.count(i) > 1] GROUP = {'common':'공통','form':'폼 입력','nav':'내비게이션','error':'오류 메시지','notice':'안내','members':'회원','board':'게시판','inquiry':'문의','consent':'동의문','booking':'예약','gallery':'갤러리','map':'지도','reviews':'후기','faq':'FAQ','survey':'설문','blog':'블로그','library':'자료실','calendar':'일정관리','mail':'메일 알림','legal':'법적 고지·쿠키'} ko = collections.OrderedDict(); en = collections.OrderedDict() for i, g, k, e in M: ko[i] = {'text': k, 'group': GROUP[g]}; en[i] = e head = {'about': '생성 사이트의 시스템 문구(모듈 위젯·폼·오류·동의문·예약·문의·메일 알림·법적 고지) 원본. 코드에는 id 만 쓰고 글자는 이 파일에서 가져온다(lib/sitemsg.js). 번역은 locales/site/<언어>.json(id → 번역문) 과 data/site-i18n/_runtime/<언어>.json(AI 번역 캐시)에 둔다. {이름} 자리 표시는 번역문에도 그대로 둔다.', 'formatVersion': 1} json.dump({**head, 'count': len(ko), 'messages': ko}, open('locales/site/ko.json', 'w'), ensure_ascii=False, indent=1) json.dump({'lang': 'en', 'formatVersion': 1, 'messages': en}, open('locales/site/en.json', 'w'), ensure_ascii=False, indent=1) print(len(ko), 'messages')