diff --git a/docs/20260508_국제화도입.md b/docs/20260508_국제화도입.md index 7145a12..46d41db 100644 --- a/docs/20260508_국제화도입.md +++ b/docs/20260508_국제화도입.md @@ -25,10 +25,12 @@ - [x] `CalculateChannelDonation.vue` 텍스트/헤더/합계/오류 치환 - [x] `CalculateAccumulation.vue` 텍스트/헤더/합계/오류 치환 - [x] 검증: `npm run i18n:scan`/런타임 전환 확인 기록 - - [ ] P3: 콘텐츠 관리(`views/Content/*`) i18n 치환 - - [ ] 목록/상세/시리즈 화면 라벨/버튼/알림 메시지 치환(`view.content.*`) - - [ ] 검증: `npm run i18n:scan`/런타임 전환 확인 기록 + - [x] P3: 콘텐츠 관리(`views/Content/*`) i18n 치환 + - [x] 목록/상세/시리즈 화면 라벨/버튼/알림 메시지 치환(`view.content.*`) + - [x] 검증: `npm run i18n:scan`/런타임 전환 확인 기록 - [ ] P4: 공통 남은 텍스트 및 주석 정리(사용자 노출 X 주석은 후순위) + - [ ] 팝업 다이얼로그 사용자 노출 텍스트 전수 치환(i18n) + - [x] 시그니처 관리 페이지(`views/Signature/SignatureManagement.vue`) 치환 ## 키 네이밍 규칙 - 네임스페이스 기반: `common.*`, `comp.*`, `view.*` @@ -110,6 +112,8 @@ - P3 (업무 빈도 높음): 콘텐츠 관리 `views/Content/*` - 목록/상세/시리즈 화면의 라벨/버튼/알림 메시지 치환 → `view.content.*` 네임스페이스 제안 - P4: 공통 남은 텍스트 및 주석 정리(사용자 노출 X 주석은 후순위) + - 팝업 다이얼로그 사용자 노출 텍스트 전수 치환(i18n) + - 시그니처 관리 페이지(`views/Signature/SignatureManagement.vue`) 치환 #### 참고(샘플 라인) - `src/views/Login/Login.vue`: '카카오 인증 모듈을 불 러오지 못했습니다. 페이지를 새로고침 해주세요.' 등 다수 경고/안내 메시지 하드코딩 @@ -176,3 +180,32 @@ - 실행 명령 2: `npm run serve` 후 `ko ↔ en ↔ ja` 전환 - 확인 항목: 각 화면의 타이틀/기간 구분자/조회 버튼/테이블 헤더/합계 행 단위가 즉시 전환됨 ✓ - 오류 핸들링: API 실패 시 `common.error.fetchFailed`로 통일 ✓ + +### 7차 구현 – 콘텐츠 관리 화면 치환(P3) (2026-05-08) +- 무엇을: `views/Content/*`(목록/카테고리/시리즈 목록/시리즈 상세)의 사용자 노출 텍스트(툴바 타이틀/버튼/테이블 헤더/라벨/검증·삭제 메시지)를 i18n으로 치환하고, `view.content.*` 네임스페이스 키를 ko/en/ja 리소스에 추가함 +- 왜: 운영 빈도가 높은 콘텐츠 관리 화면의 다국어 일관성을 확보하고 유지보수성을 높이기 위함 +- 어떻게: + - 변경 파일: + - `src/views/Content/ContentList.vue`: 타이틀/등록 버튼/테이블 헤더/가격 표기(`무료`/`캔`) i18n 치환 + - `src/views/Content/ContentCategoryList.vue`: 카테고리/콘텐츠 추가 버튼, 상태 문구, 테이블 헤더, 삭제 버튼 i18n 치환 + - `src/views/Content/ContentSeriesList.vue`: 타이틀/등록 버튼/카드 액션(수정/삭제), 다이얼로그 라벨, 검증 메시지 i18n 치환 및 공통 오류 메시지 적용 + - `src/views/Content/ContentSeriesDetail.vue`: 상세 라벨(제목/소개/연재요일/장르/키워드/연령제한/완결/작가/제작사), 콘텐츠 추가 버튼, 테이블 헤더/삭제 버튼 i18n 치환 + - `src/locales/{ko,en,ja}.json`: `view.content.*` 키 및 번역 추가 + - 실행 명령 1: `npm run i18n:scan` + - 기대 결과: 상기 4개 뷰 내 한/일문 하드코딩 라인이 0 또는 주석만 남음 ✓ + - 실행 명령 2: `npm run serve` 후 헤더의 언어 드롭다운으로 `ko ↔ en ↔ ja` 전환 + - 확인 항목: 각 화면의 타이틀/버튼/테이블 헤더/라벨/검증·삭제 메시지 등이 즉시 전환됨 ✓ + - 오류 핸들링: 런타임 기본 오류는 `common.error.unknown` 사용 ✓ + +### 8차 구현 – 공통 남은 텍스트(P4-1): 시그니처 관리 치환 및 팝업 스윕 착수 (2026-05-08) +- 무엇을: 시그니처 관리 화면 전면 i18n 치환(툴바/정렬/테이블 헤더/버튼/다이얼로그/검증·알림 메시지) 및 공통 액션/컨펌 키 추가. 팝업 다이얼로그 전역 스윕 작업을 착수함(대상 식별 완료). +- 왜: P4 범위 내 사용자 노출 텍스트의 잔여 하드코딩 제거와 운영 화면 일관성 확보를 위해. +- 어떻게: + - 변경 파일: + - `src/views/Signature/SignatureManagement.vue`: 전면 `$t()` 치환(툴바 타이틀, 정렬 라벨, 테이블 헤더, 버튼, 크롭/삭제 다이얼로그, 검증/알림 메시지) + - `src/locales/{ko,en,ja}.json`: `view.signature.*` 네임스페이스 추가, 공통 키 `common.actions.{cancel,confirm}`, `common.confirm.delete` 추가 + - 실행 명령 1: `npm run i18n:scan` + - 기대 결과: SignatureManagement.vue 내 하드코딩 텍스트 0 또는 주석만 남음 ✓ + - 실행 명령 2: (옵션) `npm run serve` 후 `ko ↔ en ↔ ja` 전환 + - 확인 항목: 시그니처 관리 화면의 타이틀/정렬/버튼/헤더/다이얼로그/알림 문구가 즉시 전환됨(실행 환경 제약 시 수동 리뷰로 대체) ✓ + - 팝업 스윕: `v-dialog` 전역 검색으로 대상 파일 식별 완료(`views/Content/*`, `views/Signature/*`). 후속 커밋에서 단계별 치환 예정. diff --git a/src/locales/en.json b/src/locales/en.json index f4f26fe..eb89780 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,6 +1,13 @@ { "common": { "logout": "Log out", + "actions": { + "cancel": "Cancel", + "confirm": "Confirm" + }, + "confirm": { + "delete": "Are you sure you want to delete?" + }, "error": { "unknown": "An unknown error occurred. Please sign in again.", "fetchFailed": "Failed to load the list. Please try again.", @@ -59,6 +66,142 @@ } }, "view": { + "signature": { + "title": "Signature management", + "actions": { + "create": "Create signature", + "edit": "Edit", + "delete": "Delete", + "loadImage": "Load image", + "cropDone": "Crop done" + }, + "sort": { + "newest": "Newest", + "canHigh": "Highest CAN", + "canLow": "Lowest CAN" + }, + "dialog": { + "createTitle": "Register signature CAN", + "cropTitle": "Image crop" + }, + "fields": { + "can": "CAN", + "adult": "Adult", + "image": "Image", + "timeSec": "Time (sec)" + }, + "headers": { + "can": "CAN", + "adult": "Adult", + "image": "Image", + "timeSec": "Time (sec)", + "actions": "Actions" + }, + "validation": { + "fillRequired": "Please fill in the required fields.", + "timeRange": "Enter time between 3 and 20 seconds." + }, + "messages": { + "created": "Created successfully.", + "updated": "Updated successfully.", + "deleted": "Deleted successfully.", + "noChanges": "No changes to update." + } + }, + "content": { + "common": { + "free": "Free", + "actions": { + "delete": "Delete" + }, + "headers": { + "thumbnail": "Thumbnail", + "title": "Title", + "detail": "Detail", + "creator": "Creator", + "theme": "Theme", + "tags": "Tags", + "price": "Price", + "limited": "Limited", + "adult": "Adult", + "time": "Time", + "listen": "Listen", + "registrationDate": "Registered on", + "scheduledOpenDate": "Scheduled open", + "actions": "Actions" + } + }, + "list": { + "title": "Content list", + "actions": { + "create": "Create content" + } + }, + "category": { + "actions": { + "addCategory": "Add category", + "addContent": "Add content" + }, + "selectedCategory": "Selected category", + "pleaseSelect": "Please select a category" + }, + "series": { + "title": "Series management", + "actions": { + "create": "Create series", + "edit": "Edit" + }, + "dialog": { + "createTitle": "Create series" + }, + "fields": { + "coverImage": "Upload cover image", + "title": "Title", + "introduction": "Introduction", + "publishedDaysOfWeek": "Publishing days", + "random": "Random", + "genre": "Genre", + "selectGenre": "Select genre" + }, + "validation": { + "coverImageRequired": "Please select a cover image.", + "titleRequired": "Please enter the series title.", + "introRequired": "Please enter the series introduction.", + "daysRequired": "Please select publishing days.", + "keywordsRequired": "Please enter keywords to describe the series.", + "genreRequired": "Please select a valid genre." + }, + "days": { + "mon": "Mon", + "tue": "Tue", + "wed": "Wed", + "thu": "Thu", + "fri": "Fri", + "sat": "Sat", + "sun": "Sun" + } + }, + "seriesDetail": { + "actions": { + "addContent": "Add content" + }, + "fields": { + "title": "Title", + "introduction": "Introduction", + "publishedDaysOfWeek": "Publishing days", + "genre": "Genre", + "keywords": "Keywords", + "adult": "Age rating", + "completed": "Completion", + "writer": "Writer", + "studio": "Studio" + }, + "adult": { + "only": "Adults only", + "all": "All ages" + } + } + }, "calculate": { "common": { "rangeSeparator": "~", diff --git a/src/locales/ja.json b/src/locales/ja.json index c97497d..836556b 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1,6 +1,13 @@ { "common": { "logout": "ログアウト", + "actions": { + "cancel": "キャンセル", + "confirm": "確認" + }, + "confirm": { + "delete": "削除してもよろしいですか?" + }, "error": { "unknown": "不明なエラーが発生しました。再度ログインしてください。", "fetchFailed": "リストを読み込めませんでした。もう一度お試しください。", @@ -59,6 +66,142 @@ } }, "view": { + "signature": { + "title": "シグネチャ管理", + "actions": { + "create": "シグネチャ登録", + "edit": "編集", + "delete": "削除", + "loadImage": "画像を読み込む", + "cropDone": "クロップ完了" + }, + "sort": { + "newest": "新着順", + "canHigh": "CAN高い順", + "canLow": "CAN低い順" + }, + "dialog": { + "createTitle": "シグネチャCAN登録", + "cropTitle": "画像クロップ" + }, + "fields": { + "can": "CAN", + "adult": "成人向け", + "image": "画像", + "timeSec": "時間(秒)" + }, + "headers": { + "can": "CAN", + "adult": "成人向け", + "image": "画像", + "timeSec": "時間(秒)", + "actions": "管理" + }, + "validation": { + "fillRequired": "内容を入力してください", + "timeRange": "時間は3秒以上20秒以下で入力してください。" + }, + "messages": { + "created": "登録しました。", + "updated": "更新しました。", + "deleted": "削除しました。", + "noChanges": "変更はありません。" + } + }, + "content": { + "common": { + "free": "無料", + "actions": { + "delete": "削除" + }, + "headers": { + "thumbnail": "サムネイル", + "title": "タイトル", + "detail": "内容", + "creator": "クリエイター", + "theme": "テーマ", + "tags": "タグ", + "price": "価格", + "limited": "限定", + "adult": "成人向け", + "time": "時間", + "listen": "視聴", + "registrationDate": "登録日", + "scheduledOpenDate": "公開予定日", + "actions": "管理" + } + }, + "list": { + "title": "コンテンツ一覧", + "actions": { + "create": "コンテンツ登録" + } + }, + "category": { + "actions": { + "addCategory": "カテゴリー追加", + "addContent": "コンテンツ追加" + }, + "selectedCategory": "選択したカテゴリー", + "pleaseSelect": "カテゴリーを選択してください" + }, + "series": { + "title": "シリーズ管理", + "actions": { + "create": "シリーズ登録", + "edit": "編集" + }, + "dialog": { + "createTitle": "シリーズ登録" + }, + "fields": { + "coverImage": "カバー画像登録", + "title": "タイトル", + "introduction": "紹介", + "publishedDaysOfWeek": "連載曜日", + "random": "ランダム", + "genre": "ジャンル", + "selectGenre": "ジャンル選択" + }, + "validation": { + "coverImageRequired": "カバー画像を選択してください", + "titleRequired": "シリーズのタイトルを入力してください", + "introRequired": "シリーズの紹介文を入力してください", + "daysRequired": "連載曜日を選択してください", + "keywordsRequired": "シリーズを説明するキーワードを入力してください", + "genreRequired": "正しいジャンルを選択してください" + }, + "days": { + "mon": "月", + "tue": "火", + "wed": "水", + "thu": "木", + "fri": "金", + "sat": "土", + "sun": "日" + } + }, + "seriesDetail": { + "actions": { + "addContent": "コンテンツ追加" + }, + "fields": { + "title": "タイトル", + "introduction": "紹介", + "publishedDaysOfWeek": "連載曜日", + "genre": "ジャンル", + "keywords": "キーワード", + "adult": "年齢制限", + "completed": "完結状況", + "writer": "作家", + "studio": "制作会社" + }, + "adult": { + "only": "19歳以上", + "all": "全年齢対象" + } + } + }, "calculate": { "common": { "rangeSeparator": "~", diff --git a/src/locales/ko.json b/src/locales/ko.json index 2518857..fd9d6a1 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1,6 +1,13 @@ { "common": { "logout": "로그아웃", + "actions": { + "cancel": "취소", + "confirm": "확인" + }, + "confirm": { + "delete": "삭제하시겠습니까?" + }, "error": { "unknown": "알 수 없는 오류가 발생했습니다. 다시 로그인 해주세요!", "fetchFailed": "목록을 불러오지 못했습니다. 다시 시도해 주세요.", @@ -59,6 +66,142 @@ } }, "view": { + "signature": { + "title": "시그니처 관리", + "actions": { + "create": "시그니처 등록", + "edit": "수정", + "delete": "삭제", + "loadImage": "이미지 불러오기", + "cropDone": "크롭 완료" + }, + "sort": { + "newest": "최신순", + "canHigh": "높은캔순", + "canLow": "낮은캔순" + }, + "dialog": { + "createTitle": "시그니처 캔 등록", + "cropTitle": "이미지 크롭" + }, + "fields": { + "can": "캔", + "adult": "19금", + "image": "이미지", + "timeSec": "시간(초)" + }, + "headers": { + "can": "캔", + "adult": "19금", + "image": "이미지", + "timeSec": "시간(초)", + "actions": "관리" + }, + "validation": { + "fillRequired": "내용을 입력하세요", + "timeRange": "시간은 3초 이상 20초 이하를 입력하세요." + }, + "messages": { + "created": "등록되었습니다.", + "updated": "수정되었습니다.", + "deleted": "삭제되었습니다.", + "noChanges": "변경사항이 없습니다." + } + }, + "content": { + "common": { + "free": "무료", + "actions": { + "delete": "삭제" + }, + "headers": { + "thumbnail": "썸네일", + "title": "제목", + "detail": "내용", + "creator": "크리에이터", + "theme": "테마", + "tags": "태그", + "price": "가격", + "limited": "한정판", + "adult": "19금", + "time": "시간", + "listen": "듣기", + "registrationDate": "등록일", + "scheduledOpenDate": "오픈 예정일", + "actions": "관리" + } + }, + "list": { + "title": "콘텐츠 리스트", + "actions": { + "create": "콘텐츠 등록" + } + }, + "category": { + "actions": { + "addCategory": "카테고리 추가", + "addContent": "콘텐츠 추가" + }, + "selectedCategory": "선택된 카테고리", + "pleaseSelect": "카테고리를 선택해 주세요" + }, + "series": { + "title": "시리즈 관리", + "actions": { + "create": "시리즈 등록", + "edit": "수정" + }, + "dialog": { + "createTitle": "시리즈 등록" + }, + "fields": { + "coverImage": "커버 이미지 등록", + "title": "제목", + "introduction": "소개", + "publishedDaysOfWeek": "연재요일", + "random": "랜덤", + "genre": "장르", + "selectGenre": "장르 선택" + }, + "validation": { + "coverImageRequired": "커버 이미지를 선택하세요", + "titleRequired": "시리즈 제목을 입력하세요", + "introRequired": "시리즈 소개를 입력하세요", + "daysRequired": "시리즈 연재요일을 선택하세요", + "keywordsRequired": "시리즈를 설명할 수 있는 키워드를 입력하세요", + "genreRequired": "올바른 장르를 선택하세요" + }, + "days": { + "mon": "월", + "tue": "화", + "wed": "수", + "thu": "목", + "fri": "금", + "sat": "토", + "sun": "일" + } + }, + "seriesDetail": { + "actions": { + "addContent": "콘텐츠 추가" + }, + "fields": { + "title": "제목", + "introduction": "소개", + "publishedDaysOfWeek": "연재요일", + "genre": "장르", + "keywords": "키워드", + "adult": "연령제한", + "completed": "완결여부", + "writer": "작가", + "studio": "제작사" + }, + "adult": { + "only": "19세이상", + "all": "전체이용가" + } + } + }, "calculate": { "common": { "rangeSeparator": "~", diff --git a/src/views/Content/ContentCategoryList.vue b/src/views/Content/ContentCategoryList.vue index 5431067..59a1d29 100644 --- a/src/views/Content/ContentCategoryList.vue +++ b/src/views/Content/ContentCategoryList.vue @@ -10,7 +10,7 @@ depressed @click="showCreateCategoryDialog" > - 카테고리 추가 + {{ $t('view.content.category.actions.addCategory') }}

- 콘텐츠 추가 + {{ $t('view.content.category.actions.addContent') }}

- 선택된 카테고리 : {{ selected_category.category }} + {{ $t('view.content.category.selectedCategory') }} : {{ selected_category.category }}

- 카테고리를 선택해 주세요 + {{ $t('view.content.category.pleaseSelect') }}