o2o-castad-frontend/test/translation-viewer.html

301 lines
36 KiB
HTML

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ADO2 Translation Viewer</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f1117; color: #e0e0e0; }
.header {
position: sticky; top: 0; z-index: 100;
background: #161822; border-bottom: 1px solid #2a2d3a;
padding: 16px 32px;
display: flex; align-items: center; justify-content: space-between;
}
.header h1 { font-size: 18px; font-weight: 700; color: #fff; }
.header h1 span { color: #94FBE0; }
.header-right { display: flex; gap: 12px; align-items: center; }
.stats { font-size: 13px; color: #888; }
.stats b { color: #94FBE0; }
.search-box {
padding: 8px 14px; border-radius: 8px;
background: #1e2030; border: 1px solid #2a2d3a;
color: #e0e0e0; font-size: 13px; width: 240px;
outline: none;
}
.search-box:focus { border-color: #94FBE0; }
.search-box::placeholder { color: #555; }
.btn {
padding: 8px 16px; border-radius: 8px; border: none;
font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.2s;
}
.btn-primary { background: #94FBE0; color: #000; }
.btn-primary:hover { background: #7ae8cc; }
.btn-secondary { background: #2a2d3a; color: #e0e0e0; }
.btn-secondary:hover { background: #363a4a; }
.filter-bar {
position: sticky; top: 61px; z-index: 99;
background: #12141e; border-bottom: 1px solid #2a2d3a;
padding: 10px 32px;
display: flex; gap: 8px; flex-wrap: wrap;
}
.filter-chip {
padding: 5px 14px; border-radius: 999px;
background: #1e2030; border: 1px solid #2a2d3a;
color: #aaa; font-size: 12px; cursor: pointer; transition: all 0.2s;
}
.filter-chip:hover { border-color: #94FBE0; color: #94FBE0; }
.filter-chip.active { background: #94FBE0; color: #000; border-color: #94FBE0; font-weight: 600; }
.content { padding: 24px 32px; }
.section {
margin-bottom: 32px;
background: #161822; border-radius: 12px; border: 1px solid #2a2d3a;
overflow: hidden;
}
.section-header {
padding: 14px 20px;
background: #1a1d2e; border-bottom: 1px solid #2a2d3a;
display: flex; align-items: center; justify-content: space-between;
cursor: pointer; user-select: none;
}
.section-header:hover { background: #1e2133; }
.section-title { font-size: 15px; font-weight: 700; color: #94FBE0; }
.section-count { font-size: 12px; color: #666; }
.section-toggle { font-size: 12px; color: #666; transition: transform 0.2s; }
.section.collapsed .section-toggle { transform: rotate(-90deg); }
.section.collapsed .section-body { display: none; }
.section-body { padding: 0; }
.row {
display: grid; grid-template-columns: 280px 1fr 1fr;
border-bottom: 1px solid #1e2030; transition: background 0.15s;
}
.row:last-child { border-bottom: none; }
.row:hover { background: #1a1d2e; }
.row.highlight { background: rgba(148, 251, 224, 0.05); }
.cell {
padding: 10px 16px; font-size: 13px; line-height: 1.5;
border-right: 1px solid #1e2030;
}
.cell:last-child { border-right: none; }
.cell-key {
color: #888; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 12px;
word-break: break-all;
}
.cell-ko { color: #e0e0e0; }
.cell-en { color: #c8d0ff; }
.cell-empty { color: #ff6b6b; font-style: italic; }
.row-header {
display: grid; grid-template-columns: 280px 1fr 1fr;
background: #12141e; border-bottom: 1px solid #2a2d3a;
position: sticky; top: 105px; z-index: 50;
}
.row-header .cell {
font-size: 11px; font-weight: 700; text-transform: uppercase;
letter-spacing: 0.05em; color: #666; padding: 8px 16px;
}
.toast {
position: fixed; bottom: 24px; right: 24px;
background: #94FBE0; color: #000; padding: 12px 20px;
border-radius: 8px; font-size: 13px; font-weight: 600;
opacity: 0; transform: translateY(10px);
transition: all 0.3s; pointer-events: none; z-index: 999;
}
.toast.show { opacity: 1; transform: translateY(0); }
@media (max-width: 768px) {
.row, .row-header { grid-template-columns: 1fr; }
.cell-key { background: #12141e; font-weight: 600; }
.header { padding: 12px 16px; flex-direction: column; gap: 8px; }
.filter-bar { padding: 8px 16px; }
.content { padding: 16px; }
}
</style>
</head>
<body>
<div class="header">
<h1><span>ADO2</span> Translation Viewer</h1>
<div class="header-right">
<span class="stats">Total: <b id="totalCount">0</b> keys</span>
<input type="text" class="search-box" id="searchInput" placeholder="Search key or text...">
<button class="btn btn-secondary" onclick="expandAll()">Expand All</button>
<button class="btn btn-secondary" onclick="collapseAll()">Collapse All</button>
<button class="btn btn-primary" onclick="downloadCSV()">Download CSV</button>
</div>
</div>
<div class="filter-bar" id="filterBar"></div>
<div class="row-header">
<div class="cell">KEY</div>
<div class="cell">KO (Korean)</div>
<div class="cell">EN (English)</div>
</div>
<div class="content" id="content"></div>
<div class="toast" id="toast"></div>
<script>
const koData = {"header":{"loginFailedAlert":"로그인 URL을 가져오는데 실패했습니다. 다시 시도해주세요.","start":"시작하기","loading":"로딩...","login":"로그인"},"sidebar":{"dashboard":"대시보드","newProject":"새 프로젝트 만들기","ado2Contents":"ADO2 콘텐츠","myContents":"내 콘텐츠","myInfo":"내 정보","defaultUser":"사용자","loggingOut":"로그아웃 중...","logout":"로그아웃"},"footer":{"businessNumber":"사업자 등록번호 : 620-87-00810 | 대표 : 안성민","headquarters":"본사 : 41593 대구광역시 북구 옥산로 111, 5층 유니콘랩 대구 A05호","researchCenter":"연구소 : 13453 경기 성남시 수정구 금토로 32 (금토동) (주)KT 판교빌딩 504호~505호 (East)","phone":"전화 : 070-4260-8310 | 010-2755-6463","email":"이메일 : o2oteam@o2o.kr"},"social":{"title":"소셜 미디어 포스팅","postNumber":"게시물 1","videoSpecs":"1080x1920 · 10초","channelLabel":"게시 채널","loadingAccounts":"계정 로딩 중...","noAccounts":"연결된 소셜 계정이 없습니다.","noAccountsHint":"내 정보에서 계정을 연결해주세요.","postTitleLabel":"게시물 제목","postTitlePlaceholder":"게시물 제목을 입력하세요.","postContentLabel":"게시물 내용","postContentPlaceholder":"게시물 내용을 입력하세요.","tagsLabel":"태그","tagsPlaceholder":"태그를 입력하세요. (쉼표로 구분)","privacyLabel":"게시물 공개 범위","privacyPublic":"공개","privacyUnlisted":"미등록 (링크로만 접근)","privacyPrivate":"비공개","publishTimeLabel":"게시 시간","publishNow":"지금 게시","publishSchedule":"시간 설정 (준비 중)","footerNote":"게시는 {link} 후 가능합니다.","footerNoteLink":"요금제 업그레이드","cancel":"취소","posting":"게시 중...","post":"게시","youtubeExpiredAlert":"YouTube 인증이 만료되었습니다. 재연동 페이지로 이동합니다.","channelAndTitleRequired":"채널과 제목을 입력해주세요.","selectChannel":"채널을 선택해주세요.","invalidVideoInfo":"영상 정보가 올바르지 않습니다. (video_id 누락)","uploadStartFailed":"업로드 시작에 실패했습니다.","uploadFailed":"업로드에 실패했습니다."},"upload":{"title":"YouTube 업로드","statusPending":"업로드 준비 중...","statusUploading":"업로드 중...","statusCompleted":"업로드 완료!","statusFailed":"업로드 실패","statusDefault":"처리 중...","videoTitleLabel":"영상 제목","channelLabel":"채널","viewOnYoutube":"YouTube에서 보기","confirm":"확인","close":"닫기","doNotClose":"업로드가 진행 중입니다. 창을 닫지 마세요."},"landing":{"hero":{"searchTypeBusinessName":"업체명","placeholderBusinessName":"업체명을 입력하세요","guideUrl":"URL에서 가져온 정보로 영상이 자동 생성됩니다.","guideBusinessName":"업체명으로 검색하여 정보를 가져옵니다.","errorUrlRequired":"URL을 입력해주세요.","errorNameRequired":"업체명을 입력해주세요.","errorInvalidUrl":"올바른 URL 형식이 아닙니다. (예: https://example.com)","analyzeButton":"브랜드 분석","scrollMore":"스크롤하여 더 보기","testDataLoading":"로딩 중...","testData":"테스트 데이터","testDataLoadFailed":"테스트 데이터를 불러오는데 실패했습니다.","searching":"검색 중..."},"welcome":{"title":"ADO2.AI에 오신 것을 환영합니다.","subtitle":"분석, 제작, 배포까지 콘텐츠 마케팅의 전과정을 자동화","feature1Title":"비즈니스 핵심 정보 분석","feature1Desc":"홈페이지, 네이버 지도, 블로그 등의\nURL을 입력하세요","feature2Title":"홍보 콘텐츠 자동 제작","feature2Desc":"분석된 정보를 바탕으로\n비즈니스에 맞는 음악, 자막, 노래, 영상을\n자동으로 제작해요","feature3Title":"멀티채널 자동 배포","feature3Desc":"완성된 영상은 다운로드하거나\n바로 SNS에 업로드 할 수 있어요"},"display":{"startButton":"시작하기"}},"login":{"back":"뒤로가기","kakaoLoginFailed":"카카오 로그인에 실패했습니다. 다시 시도해주세요.","loginUrlFailed":"로그인 URL을 가져오는데 실패했습니다. 다시 시도해주세요.","loggingIn":"로그인 중...","kakaoStart":"카카오로 시작하기"},"urlInput":{"searchTypeBusinessName":"업체명","placeholderBusinessName":"업체명을 입력하세요","guideUrl":"URL에서 가져온 정보로 영상이 자동 생성됩니다.","guideBusinessName":"업체명으로 검색하여 정보를 가져옵니다.","searchButton":"검색하기","searching":"검색 중...","testDataLoading":"로딩 중...","testData":"테스트 데이터"},"assetManagement":{"title":"브랜드 에셋","selectedImages":"선택된 이미지","imageAlt":"이미지","uploadBadge":"업로드","imageUpload":"이미지 업로드","dragAndDrop":"이미지를 드래그하여\n업로드","videoRatio":"영상 비율","uploadFailed":"이미지 업로드에 실패했습니다.","uploading":"업로드 중...","nextStep":"다음 단계"},"soundStudio":{"back":"뒤로가기","title":"사운드 스튜디오","soundColumn":"사운드","soundTypeLabel":"AI 사운드 유형 선택","soundTypeVocal":"보컬","soundTypeBGM":"배경음악","soundTypeNarration":"성우 내레이션","genreLabel":"장르 선택","genreAuto":"자동 선택","genreBallad":"발라드","languageLabel":"언어","languageKorean":"한국어","lyricsColumn":"가사","lyricsHint":"가사 영역을 선택해서 수정 가능해요","lyricsPlaceholder":"사운드 생성 시 가사 표시됩니다.","generateButton":"사운드 생성","generating":"생성 중...","generateVideo":"영상 생성하기","videoGenerating":"영상 생성 중","noBusinessInfo":"비즈니스 정보가 없습니다. 다시 시도해주세요.","noImageUploadInfo":"이미지 업로드 정보가 없습니다. 이전 단계로 돌아가 다시 시도해주세요.","generatingLyrics":"가사를 생성하고 있습니다...","generatingSong":"노래를 생성하고 있습니다...","songQueued":"노래 생성 대기 중...","retryMessage":"시간 초과로 재생성 중... ({{count}}/{{max}})","lyricGenerationFailed":"가사 생성 요청에 실패했습니다.","lyricNotReceived":"가사를 받지 못했습니다.","lyricGenerationError":"가사 생성에 실패했습니다. 다시 시도해주세요.","songGenerationFailed":"음악 생성 요청에 실패했습니다.","songIdMissing":"서버에서 song_id를 받지 못했습니다.","musicGenerationFailed":"음악 생성에 실패했습니다.","multipleRetryFailed":"여러 번 시도했지만 음악 생성에 실패했습니다. 다시 시도해주세요.","musicGenerationError":"음악 생성 중 오류가 발생했습니다.","songRegenerationError":"음악 재생성 중 오류가 발생했습니다."},"completion":{"back":"뒤로가기","titleGenerating":"영상 생성 중","titleError":"영상 생성 실패","titleComplete":"콘텐츠 제작 완료","imageAndVideo":"이미지 및 영상","requestingGeneration":"영상 생성을 요청하고 있습니다...","generatingVideo":"영상을 생성하고 있습니다...","processingAfterRefresh":"영상을 처리하고 있습니다... (새로고침 후 복구됨)","generationFailed":"영상 생성 요청에 실패했습니다.","generationError":"영상 생성 중 오류가 발생했습니다.","videoUrlMissing":"영상 URL을 받지 못했습니다.","generationTimeout":"영상 생성 시간이 초과되었습니다. 다시 시도해주세요.","retry":"다시 시도","statusPlanned":"예약됨","statusWaiting":"대기 중","statusTranscribing":"트랜스크립션 중","statusRendering":"렌더링 중","statusSucceeded":"완료","statusDefault":"처리 중...","aiOptimization":"AI 최적화","aiTagColorCorrection":"색상 보정","aiTagDynamicSubtitle":"다이나믹 자막","aiTagBeatSync":"비트 싱크","aiTagSEOMeta":"SEO 메타 태그","sharing":"공유","connecting":"연결 중...","authenticated":"인증됨","disconnect":"해제","connectAccount":"계정 연결","comingSoon":"준비 중","youtubeConnectFailed":"YouTube 연결에 실패했습니다.","disconnectFailed":"연결 해제에 실패했습니다.","deployToSocial":"소셜 채널에 배포","downloadMp4":"MP4 파일 다운로드","selectSocialChannel":"배포할 소셜 채널을 선택해주세요.","videoNotReady":"영상이 아직 준비되지 않았습니다.","youtubeUploadMessage":"YouTube 채널 \"{{channelName}}\"에 영상을 업로드합니다.\n\n(업로드 기능 준비 중)","deployComingSoon":"선택한 소셜 채널에 배포 기능이 준비 중입니다.","youtubeExpiredAlert":"YouTube 인증이 만료되었습니다. 재연동 페이지로 이동합니다."},"dashboard":{"title":"대시보드","description":"실시간 마케팅 퍼포먼스를 확인하세요.","lastUpdated":"마지막 업데이트:","contentPerformance":"콘텐츠 성과","metricImpressions":"총 노출수","metricReach":"도달","metricLikes":"좋아요","metricComments":"댓글","metricShares":"공유","metricSaves":"저장","metricEngagement":"참여율","metricContent":"콘텐츠","yearOverYear":"전년 대비 성장","thisYear":"올해","lastYear":"작년","popularContent":"인기 콘텐츠","audienceInsights":"오디언스 인사이트","ageDistribution":"연령대 분포","genderDistribution":"성별 분포","male":"남성","female":"여성","topRegions":"상위 지역","platformMetrics":"플랫폼별 지표","months":{"jan":"1월","feb":"2월","mar":"3월","apr":"4월","may":"5월","jun":"6월","jul":"7월","aug":"8월","sep":"9월","oct":"10월","nov":"11월","dec":"12월"},"youtubeMetrics":{"views":"조회수","watchTime":"시청 시간","watchTimeUnit":"시간","avgViewDuration":"평균 시청 시간","subscribers":"구독자","newSubscribers":"신규 구독자","engagement":"참여율","ctr":"클릭률 (CTR)","revenue":"예상 수익"},"instagramMetrics":{"reach":"도달","impressions":"노출","profileVisits":"프로필 방문","followers":"팔로워","newFollowers":"신규 팔로워","storyViews":"스토리 조회","reelPlays":"릴스 재생","websiteClicks":"웹사이트 클릭"},"regions":{"seoul":"서울","gyeonggi":"경기","busan":"부산","incheon":"인천","daegu":"대구"},"topContentTitles":{"winterPromotion":"겨울 펜션 프로모션 영상","stayIntroReel":"스테이 머뭄 소개 릴스","newYearEvent":"신년 특가 이벤트 안내","nightTimelapse":"펜션 야경 타임랩스"}},"myInfo":{"title":"내 정보","tabBasic":"기본 정보","tabPayment":"결제 정보","tabBusiness":"내 비즈니스 & 소셜 채널 관리","basicPlaceholder":"기본 정보 설정 기능이 준비 중입니다.","paymentPlaceholder":"결제 정보 설정 기능이 준비 중입니다.","myBusiness":"내 비즈니스","noBusinessTitle":"아직 등록된 비즈니스가 없어요","noBusinessDesc":"네이버 지도 URL을 입력하면, 영상 제작에 필요한 정보를 자동으로 불러와요","naverMapUrlPlaceholder":"네이버 지도 URL 입력","registerBusiness":"비즈니스 등록","socialChannels":"소셜 채널","youtubeConnecting":"연결 중...","youtubeConnect":"YouTube 연결","instagramConnect":"Instagram 연결","connected":"연결됨","disconnectAccount":"연결 해제","loadingAccounts":"계정 정보를 불러오는 중...","youtubeExpiredAlert":"YouTube 인증이 만료되었습니다. 재연동 페이지로 이동합니다."},"ado2Contents":{"title":"ADO2 콘텐츠","totalCount":"총 {{count}}개","loading":"콘텐츠를 불러오는 중...","loadFailed":"콘텐츠를 불러오는데 실패했습니다.","retry":"다시 시도","noContent":"생성된 콘텐츠가 없습니다.","download":"다운로드","downloadFailed":"다운로드에 실패했습니다.","deleteFailed":"삭제에 실패했습니다.","deleteConfirmTitle":"정말 콘텐츠를 삭제할까요?","deleteConfirmDesc":"삭제한 파일은 복구할 수 없어요.","cancel":"취소","deleting":"삭제 중...","delete":"삭제","previous":"이전","next":"다음","uploadToSocial":"소셜 미디어에 업로드"},"businessSettings":{"title":"비즈니스 설정","description":"펜션 정보와 YouTube 채널을 설정하여 자동 업로드를 활성화하세요","sharing":"공유","connect":"연결하기"},"analysis":{"back":"뒤로가기","pageTitle":"브랜드 인텔리전스","pageDescHighlight":"AI 데이터 분석","defaultBrandName":"브랜드","brandIdentity":"브랜드 정체성","brandNameFallback":"브랜드명","addressFallback":"주소 정보 없음","locationAnalysis":"입지 특성 분석","conceptScalability":"컨셉 확장성","noInfo":"정보 없음","marketPositioning":"시장 포지셔닝","coreValue":"핵심 가치 (Core Value)","categoryDefinition":"카테고리 정의","targetPersona":"타겟 페르소나","ageSuffix":"세","sellingPoints":"주요 셀링 포인트 (USP)","recommendedKeywords":"추천 타겟 키워드","generateContent":"콘텐츠 생성","pageDescBefore":"을 통해 도출된 ","pageDescAfter":"의 핵심 전략입니다.","loadingTitle":"브랜드 분석 중"},"common":{"back":"뒤로가기","cancel":"취소","confirm":"확인","close":"닫기","retry":"다시 시도","loading":"로딩...","required":"*","unknown":"알 수 없음"},"app":{"loginProcessing":"로그인 처리 중...","loginFailed":"로그인 처리에 실패했습니다. 다시 시도해주세요.","kakaoLoginFailed":"카카오 로그인에 실패했습니다. 다시 시도해주세요.","loginUrlFailed":"로그인 URL을 가져오는데 실패했습니다. 다시 시도해주세요.","invalidUrl":"유효하지 않은 URL입니다. 네이버 지도 URL을 입력해주세요.","analysisError":"분석 중 오류가 발생했습니다. 다시 시도해주세요.","autocompleteError":"업체 정보 조회에 실패했습니다.","autocompleteGeneralError":"업체 정보 조회 중 오류가 발생했습니다. 다시 시도해주세요.","pageComingSoon":"{{page}} 페이지 준비 중입니다."}};
const enData = {"header":{"loginFailedAlert":"Failed to get login URL. Please try again.","start":"Get Started","loading":"Loading...","login":"Log In"},"sidebar":{"dashboard":"Dashboard","newProject":"Create New Project","ado2Contents":"ADO2 Contents","myContents":"My Contents","myInfo":"My Info","defaultUser":"User","loggingOut":"Logging out...","logout":"Log Out"},"footer":{"businessNumber":"Business Registration No. : 620-87-00810 | CEO : Ahn Sungmin","headquarters":"HQ : 41593 Unicorn Lab Daegu A05, 5F, 111 Oksan-ro, Buk-gu, Daegu, Korea","researchCenter":"R&D : 13453 Rooms 504-505 (East), KT Pangyo Bldg, 32 Geumto-ro, Sujeong-gu, Seongnam-si, Gyeonggi-do, Korea","phone":"Tel : 070-4260-8310 | 010-2755-6463","email":"Email : o2oteam@o2o.kr"},"social":{"title":"Social Media Posting","postNumber":"Post 1","videoSpecs":"1080x1920 · 10 sec","channelLabel":"Post Channel","loadingAccounts":"Loading accounts...","noAccounts":"No connected social accounts.","noAccountsHint":"Please connect an account in My Info.","postTitleLabel":"Post Title","postTitlePlaceholder":"Enter a post title.","postContentLabel":"Post Content","postContentPlaceholder":"Enter post content.","tagsLabel":"Tags","tagsPlaceholder":"Enter tags. (separated by commas)","privacyLabel":"Post Visibility","privacyPublic":"Public","privacyUnlisted":"Unlisted (accessible by link only)","privacyPrivate":"Private","publishTimeLabel":"Publish Time","publishNow":"Publish Now","publishSchedule":"Schedule (Coming Soon)","footerNote":"Posting is available after {link}.","footerNoteLink":"plan upgrade","cancel":"Cancel","posting":"Posting...","post":"Post","youtubeExpiredAlert":"YouTube authentication has expired. Redirecting to reconnect page.","channelAndTitleRequired":"Please enter a channel and title.","selectChannel":"Please select a channel.","invalidVideoInfo":"Video information is invalid. (missing video_id)","uploadStartFailed":"Failed to start upload.","uploadFailed":"Upload failed."},"upload":{"title":"YouTube Upload","statusPending":"Preparing upload...","statusUploading":"Uploading...","statusCompleted":"Upload complete!","statusFailed":"Upload failed","statusDefault":"Processing...","videoTitleLabel":"Video Title","channelLabel":"Channel","viewOnYoutube":"View on YouTube","confirm":"OK","close":"Close","doNotClose":"Upload is in progress. Do not close this window."},"landing":{"hero":{"searchTypeBusinessName":"Business Name","placeholderBusinessName":"Enter a business name","guideUrl":"A video will be automatically generated from the information gathered from the URL.","guideBusinessName":"Search by business name to retrieve information.","errorUrlRequired":"Please enter a URL.","errorNameRequired":"Please enter a business name.","errorInvalidUrl":"Invalid URL format. (e.g., https://example.com)","analyzeButton":"Brand Analysis","scrollMore":"Scroll to see more","testDataLoading":"Loading...","testData":"Test Data","testDataLoadFailed":"Failed to load test data.","searching":"Searching..."},"welcome":{"title":"Welcome to ADO2.AI","subtitle":"Automate the entire content marketing process from analysis, creation, to distribution","feature1Title":"Business Core Information Analysis","feature1Desc":"Enter the URL of your homepage,\nNaver Map, blog, etc.","feature2Title":"Automated Promotional Content Creation","feature2Desc":"Based on analyzed information,\nautomatically create music, subtitles, songs,\nand videos tailored to your business","feature3Title":"Multi-Channel Auto Distribution","feature3Desc":"Completed videos can be downloaded\nor uploaded directly to social media"},"display":{"startButton":"Get Started"}},"login":{"back":"Go Back","kakaoLoginFailed":"Kakao login failed. Please try again.","loginUrlFailed":"Failed to get login URL. Please try again.","loggingIn":"Logging in...","kakaoStart":"Start with Kakao"},"urlInput":{"searchTypeBusinessName":"Business Name","placeholderBusinessName":"Enter a business name","guideUrl":"A video will be automatically generated from the information gathered from the URL.","guideBusinessName":"Search by business name to retrieve information.","searchButton":"Search","searching":"Searching...","testDataLoading":"Loading...","testData":"Test Data"},"assetManagement":{"title":"Brand Assets","selectedImages":"Selected Images","imageAlt":"Image","uploadBadge":"Uploaded","imageUpload":"Image Upload","dragAndDrop":"Drag and drop\nimages to upload","videoRatio":"Video Ratio","uploadFailed":"Image upload failed.","uploading":"Uploading...","nextStep":"Next Step"},"soundStudio":{"back":"Go Back","title":"Sound Studio","soundColumn":"Sound","soundTypeLabel":"Select AI Sound Type","soundTypeVocal":"Vocal","soundTypeBGM":"Background Music","soundTypeNarration":"Voice Narration","genreLabel":"Select Genre","genreAuto":"Auto Select","genreBallad":"Ballad","languageLabel":"Language","languageKorean":"Korean","lyricsColumn":"Lyrics","lyricsHint":"Select the lyrics area to edit","lyricsPlaceholder":"Lyrics will be displayed when sound is generated.","generateButton":"Generate Sound","generating":"Generating...","generateVideo":"Generate Video","videoGenerating":"Generating Video","noBusinessInfo":"No business information. Please try again.","noImageUploadInfo":"No image upload information. Please go back to the previous step and try again.","generatingLyrics":"Generating lyrics...","generatingSong":"Generating song...","songQueued":"Song generation queued...","retryMessage":"Regenerating due to timeout... ({{count}}/{{max}})","lyricGenerationFailed":"Lyrics generation request failed.","lyricNotReceived":"Lyrics were not received.","lyricGenerationError":"Lyrics generation failed. Please try again.","songGenerationFailed":"Music generation request failed.","songIdMissing":"song_id was not received from the server.","musicGenerationFailed":"Music generation failed.","multipleRetryFailed":"Music generation failed after multiple attempts. Please try again.","musicGenerationError":"An error occurred during music generation.","songRegenerationError":"An error occurred during music regeneration."},"completion":{"back":"Go Back","titleGenerating":"Generating Video","titleError":"Video Generation Failed","titleComplete":"Content Creation Complete","imageAndVideo":"Images & Video","requestingGeneration":"Requesting video generation...","generatingVideo":"Generating video...","processingAfterRefresh":"Processing video... (recovered after refresh)","generationFailed":"Video generation request failed.","generationError":"An error occurred during video generation.","videoUrlMissing":"Video URL was not received.","generationTimeout":"Video generation timed out. Please try again.","retry":"Retry","statusPlanned":"Scheduled","statusWaiting":"Waiting","statusTranscribing":"Transcribing","statusRendering":"Rendering","statusSucceeded":"Completed","statusDefault":"Processing...","aiOptimization":"AI Optimization","aiTagColorCorrection":"Color Correction","aiTagDynamicSubtitle":"Dynamic Subtitles","aiTagBeatSync":"Beat Sync","aiTagSEOMeta":"SEO Meta Tags","sharing":"Share","connecting":"Connecting...","authenticated":"Verified","disconnect":"Disconnect","connectAccount":"Connect Account","comingSoon":"Coming Soon","youtubeConnectFailed":"Failed to connect YouTube.","disconnectFailed":"Failed to disconnect.","deployToSocial":"Deploy to Social Channel","downloadMp4":"Download MP4 File","selectSocialChannel":"Please select a social channel to deploy.","videoNotReady":"The video is not ready yet.","youtubeUploadMessage":"Uploading video to YouTube channel \"{{channelName}}\".\n\n(Upload feature coming soon)","deployComingSoon":"The deployment feature for the selected social channel is coming soon.","youtubeExpiredAlert":"YouTube authentication has expired. Redirecting to reconnect page."},"dashboard":{"title":"Dashboard","description":"Check your real-time marketing performance.","lastUpdated":"Last updated:","contentPerformance":"Content Performance","metricImpressions":"Total Impressions","metricReach":"Reach","metricLikes":"Likes","metricComments":"Comments","metricShares":"Shares","metricSaves":"Saves","metricEngagement":"Engagement Rate","metricContent":"Content","yearOverYear":"Year-over-Year Growth","thisYear":"This Year","lastYear":"Last Year","popularContent":"Popular Content","audienceInsights":"Audience Insights","ageDistribution":"Age Distribution","genderDistribution":"Gender Distribution","male":"Male","female":"Female","topRegions":"Top Regions","platformMetrics":"Platform Metrics","months":{"jan":"Jan","feb":"Feb","mar":"Mar","apr":"Apr","may":"May","jun":"Jun","jul":"Jul","aug":"Aug","sep":"Sep","oct":"Oct","nov":"Nov","dec":"Dec"},"youtubeMetrics":{"views":"Views","watchTime":"Watch Time","watchTimeUnit":"hours","avgViewDuration":"Avg. View Duration","subscribers":"Subscribers","newSubscribers":"New Subscribers","engagement":"Engagement Rate","ctr":"Click-Through Rate (CTR)","revenue":"Estimated Revenue"},"instagramMetrics":{"reach":"Reach","impressions":"Impressions","profileVisits":"Profile Visits","followers":"Followers","newFollowers":"New Followers","storyViews":"Story Views","reelPlays":"Reel Plays","websiteClicks":"Website Clicks"},"regions":{"seoul":"Seoul","gyeonggi":"Gyeonggi","busan":"Busan","incheon":"Incheon","daegu":"Daegu"},"topContentTitles":{"winterPromotion":"Winter Pension Promotion Video","stayIntroReel":"Stay Meomum Introduction Reel","newYearEvent":"New Year Special Event","nightTimelapse":"Pension Night View Timelapse"}},"myInfo":{"title":"My Info","tabBasic":"Basic Info","tabPayment":"Payment Info","tabBusiness":"My Business & Social Channel Management","basicPlaceholder":"Basic information settings are coming soon.","paymentPlaceholder":"Payment information settings are coming soon.","myBusiness":"My Business","noBusinessTitle":"No registered business yet","noBusinessDesc":"Enter a Naver Map URL to automatically import the information needed for video production","naverMapUrlPlaceholder":"Enter Naver Map URL","registerBusiness":"Register Business","socialChannels":"Social Channels","youtubeConnecting":"Connecting...","youtubeConnect":"Connect YouTube","instagramConnect":"Connect Instagram","connected":"Connected","disconnectAccount":"Disconnect","loadingAccounts":"Loading account information...","youtubeExpiredAlert":"YouTube authentication has expired. Redirecting to reconnect page."},"ado2Contents":{"title":"ADO2 Contents","totalCount":"Total {{count}}","loading":"Loading contents...","loadFailed":"Failed to load contents.","retry":"Retry","noContent":"No content created yet.","download":"Download","downloadFailed":"Download failed.","deleteFailed":"Deletion failed.","deleteConfirmTitle":"Are you sure you want to delete this content?","deleteConfirmDesc":"Deleted files cannot be recovered.","cancel":"Cancel","deleting":"Deleting...","delete":"Delete","previous":"Previous","next":"Next","uploadToSocial":"Upload to social media"},"businessSettings":{"title":"Business Settings","description":"Set up your pension information and YouTube channel to enable automatic uploads","sharing":"Share","connect":"Connect"},"analysis":{"back":"Go Back","pageTitle":"Brand Intelligence","pageDescHighlight":"AI Data Analysis","defaultBrandName":"Brand","brandIdentity":"Brand Identity","brandNameFallback":"Brand Name","addressFallback":"No address information","locationAnalysis":"Location Feature Analysis","conceptScalability":"Concept Scalability","noInfo":"No information","marketPositioning":"Market Positioning","coreValue":"Core Value","categoryDefinition":"Category Definition","targetPersona":"Target Persona","ageSuffix":"years old","sellingPoints":"Unique Selling Points (USP)","recommendedKeywords":"Recommended Target Keywords","generateContent":"Generate Content","pageDescBefore":" reveals ","pageDescAfter":"'s core strategy.","loadingTitle":"Analyzing Brand"},"common":{"back":"Go Back","cancel":"Cancel","confirm":"OK","close":"Close","retry":"Retry","loading":"Loading...","required":"*","unknown":"Unknown"},"app":{"loginProcessing":"Processing login...","loginFailed":"Login processing failed. Please try again.","kakaoLoginFailed":"Kakao login failed. Please try again.","loginUrlFailed":"Failed to get login URL. Please try again.","invalidUrl":"Invalid URL. Please enter a Naver Map URL.","analysisError":"An error occurred during analysis. Please try again.","autocompleteError":"Failed to retrieve business information.","autocompleteGeneralError":"An error occurred while retrieving business information. Please try again.","pageComingSoon":"{{page}} page is coming soon."}};
// Flatten nested JSON
function flatten(obj, prefix = '') {
const result = {};
for (const key of Object.keys(obj)) {
const fullKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null) {
Object.assign(result, flatten(obj[key], fullKey));
} else {
result[fullKey] = obj[key];
}
}
return result;
}
const flatKo = flatten(koData);
const flatEn = flatten(enData);
const allKeys = [...new Set([...Object.keys(flatKo), ...Object.keys(flatEn)])].sort();
// Get top-level sections
const sections = [...new Set(allKeys.map(k => k.split('.')[0]))];
let activeFilter = 'all';
let searchTerm = '';
function getSection(key) { return key.split('.')[0]; }
function escapeHtml(str) {
if (!str) return '';
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/\n/g, '<br>');
}
function showToast(msg) {
const t = document.getElementById('toast');
t.textContent = msg;
t.classList.add('show');
setTimeout(() => t.classList.remove('show'), 2000);
}
function renderFilters() {
const bar = document.getElementById('filterBar');
let html = `<div class="filter-chip ${activeFilter === 'all' ? 'active' : ''}" onclick="setFilter('all')">All</div>`;
for (const s of sections) {
html += `<div class="filter-chip ${activeFilter === s ? 'active' : ''}" onclick="setFilter('${s}')">${s}</div>`;
}
bar.innerHTML = html;
}
function setFilter(f) {
activeFilter = f;
renderFilters();
render();
}
function render() {
const content = document.getElementById('content');
let html = '';
let total = 0;
for (const section of sections) {
if (activeFilter !== 'all' && activeFilter !== section) continue;
const sectionKeys = allKeys.filter(k => getSection(k) === section);
const filteredKeys = sectionKeys.filter(k => {
if (!searchTerm) return true;
const term = searchTerm.toLowerCase();
return k.toLowerCase().includes(term)
|| (flatKo[k] || '').toLowerCase().includes(term)
|| (flatEn[k] || '').toLowerCase().includes(term);
});
if (filteredKeys.length === 0) continue;
total += filteredKeys.length;
html += `<div class="section" id="section-${section}">`;
html += `<div class="section-header" onclick="toggleSection('${section}')">
<span class="section-title">${section}</span>
<span><span class="section-count">${filteredKeys.length} keys</span>&nbsp;&nbsp;<span class="section-toggle">&#9660;</span></span>
</div>`;
html += `<div class="section-body">`;
for (const key of filteredKeys) {
const ko = flatKo[key] || '';
const en = flatEn[key] || '';
const shortKey = key.replace(section + '.', '');
const isEmpty = !en;
html += `<div class="row ${isEmpty ? 'highlight' : ''}">`;
html += `<div class="cell cell-key" title="${key}">${escapeHtml(shortKey)}</div>`;
html += `<div class="cell cell-ko">${escapeHtml(ko)}</div>`;
html += `<div class="cell ${isEmpty ? 'cell-empty' : 'cell-en'}">${isEmpty ? '(missing)' : escapeHtml(en)}</div>`;
html += `</div>`;
}
html += `</div></div>`;
}
content.innerHTML = html;
document.getElementById('totalCount').textContent = total;
}
function toggleSection(name) {
const el = document.getElementById('section-' + name);
if (el) el.classList.toggle('collapsed');
}
function expandAll() {
document.querySelectorAll('.section').forEach(el => el.classList.remove('collapsed'));
}
function collapseAll() {
document.querySelectorAll('.section').forEach(el => el.classList.add('collapsed'));
}
function downloadCSV() {
const BOM = '\uFEFF';
let csv = BOM + 'Key,Korean,English\n';
for (const key of allKeys) {
const ko = (flatKo[key] || '').replace(/"/g, '""').replace(/\n/g, '\\n');
const en = (flatEn[key] || '').replace(/"/g, '""').replace(/\n/g, '\\n');
csv += `"${key}","${ko}","${en}"\n`;
}
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'ado2_translations.csv';
a.click();
URL.revokeObjectURL(url);
showToast('CSV downloaded!');
}
// Search
document.getElementById('searchInput').addEventListener('input', (e) => {
searchTerm = e.target.value;
render();
});
// Init
renderFilters();
render();
</script>
</body>
</html>