feat(i18n): 관리자 콘텐츠/시그니처 화면 국제화 적용 및 번역 추가

This commit is contained in:
Yu Sung
2026-05-08 15:45:32 +09:00
parent 686de1b5dc
commit ab6660fa16
9 changed files with 570 additions and 108 deletions

View File

@@ -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/*`). 후속 커밋에서 단계별 치환 예정.

View File

@@ -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": "~",

View File

@@ -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": "~",

View File

@@ -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": "~",

View File

@@ -10,7 +10,7 @@
depressed
@click="showCreateCategoryDialog"
>
카테고리 추가
{{ $t('view.content.category.actions.addCategory') }}
</v-btn>
<br><br>
<draggable
@@ -47,32 +47,32 @@
depressed
@click="showAddContent"
>
콘텐츠 추가
{{ $t('view.content.category.actions.addContent') }}
</v-btn>
</v-col>
</v-row>
<br><br>
<p v-if="selected_category !== null && selected_category !== undefined">
선택된 카테고리 : {{ selected_category.category }}
{{ $t('view.content.category.selectedCategory') }} : {{ selected_category.category }}
</p>
<p v-else>
카테고리를 선택해 주세요
{{ $t('view.content.category.pleaseSelect') }}
</p>
<v-simple-table>
<template>
<thead>
<tr>
<th class="text-center">
썸네일
{{ $t('view.content.common.headers.thumbnail') }}
</th>
<th class="text-center">
제목
{{ $t('view.content.common.headers.title') }}
</th>
<th class="text-center">
19
{{ $t('view.content.common.headers.adult') }}
</th>
<th class="text-center">
관리
{{ $t('view.content.common.headers.actions') }}
</th>
</tr>
</thead>
@@ -105,7 +105,7 @@
color="#3bb9f1"
@click="removeContentInCategory(content)"
>
삭제
{{ $t('view.content.common.actions.delete') }}
</v-btn>
</td>
</tr>

View File

@@ -2,7 +2,7 @@
<div>
<v-toolbar dark>
<v-spacer />
<v-toolbar-title>콘텐츠 리스트</v-toolbar-title>
<v-toolbar-title>{{ $t('view.content.list.title') }}</v-toolbar-title>
<v-spacer />
</v-toolbar>
@@ -19,7 +19,7 @@
depressed
@click="showWriteDialog"
>
콘텐츠 등록
{{ $t('view.content.list.actions.create') }}
</v-btn>
</v-col>
</v-row>
@@ -30,46 +30,46 @@
<thead>
<tr>
<th class="text-center">
썸네일
{{ $t('view.content.common.headers.thumbnail') }}
</th>
<th class="text-center">
제목
{{ $t('view.content.common.headers.title') }}
</th>
<th class="text-center">
내용
{{ $t('view.content.common.headers.detail') }}
</th>
<th class="text-center">
크리에이터
{{ $t('view.content.common.headers.creator') }}
</th>
<th class="text-center">
테마
{{ $t('view.content.common.headers.theme') }}
</th>
<th class="text-center">
태그
{{ $t('view.content.common.headers.tags') }}
</th>
<th class="text-center">
가격
{{ $t('view.content.common.headers.price') }}
</th>
<th class="text-center">
한정판
{{ $t('view.content.common.headers.limited') }}
</th>
<th class="text-center">
19
{{ $t('view.content.common.headers.adult') }}
</th>
<th class="text-center">
시간
{{ $t('view.content.common.headers.time') }}
</th>
<th class="text-center">
듣기
{{ $t('view.content.common.headers.listen') }}
</th>
<th class="text-center">
등록일
{{ $t('view.content.common.headers.registrationDate') }}
</th>
<th class="text-center">
오픈 예정일
{{ $t('view.content.common.headers.scheduledOpenDate') }}
</th>
<th class="text-center">
관리
{{ $t('view.content.common.headers.actions') }}
</th>
</tr>
</thead>
@@ -95,10 +95,10 @@
{{ item.tags }}
</td>
<td v-if="item.price > 0">
{{ item.price }}
{{ item.price }} {{ $t('common.unit.can') }}
</td>
<td v-else>
무료
{{ $t('view.content.common.free') }}
</td>
<td
v-if="item.totalContentCount > 0 && item.remainingContentCount > 0"

View File

@@ -9,35 +9,35 @@
class="cover-image"
/>
<v-card-text>
제목 : {{ series_detail.title }}
{{ $t('view.content.seriesDetail.fields.title') }} : {{ series_detail.title }}
</v-card-text>
<v-card-text>
소개 :
{{ $t('view.content.seriesDetail.fields.introduction') }} :
<vue-show-more-text
:text="series_detail.introduction"
:lines="2"
/>
</v-card-text>
<v-card-text>
연재요일 : {{ series_detail.publishedDaysOfWeek }}
{{ $t('view.content.seriesDetail.fields.publishedDaysOfWeek') }} : {{ series_detail.publishedDaysOfWeek }}
</v-card-text>
<v-card-text>
장르 : {{ series_detail.genre }}
{{ $t('view.content.seriesDetail.fields.genre') }} : {{ series_detail.genre }}
</v-card-text>
<v-card-text>
키워드 : {{ series_detail.keywords }}
{{ $t('view.content.seriesDetail.fields.keywords') }} : {{ series_detail.keywords }}
</v-card-text>
<v-card-text>
연령제한 : <span v-if="series_detail.isAdult">19세이상</span><span v-else>전체이용가</span>
{{ $t('view.content.seriesDetail.fields.adult') }} : <span v-if="series_detail.isAdult">{{ $t('view.content.seriesDetail.adult.only') }}</span><span v-else>{{ $t('view.content.seriesDetail.adult.all') }}</span>
</v-card-text>
<v-card-text>
완결여부 : {{ series_detail.state }}
{{ $t('view.content.seriesDetail.fields.completed') }} : {{ series_detail.state }}
</v-card-text>
<v-card-text v-show="series_detail.writer !== undefined && series_detail.writer !== null">
작가 : {{ series_detail.writer }}
{{ $t('view.content.seriesDetail.fields.writer') }} : {{ series_detail.writer }}
</v-card-text>
<v-card-text v-show="series_detail.studio !== undefined && series_detail.studio !== null">
제작사 : {{ series_detail.studio }}
{{ $t('view.content.seriesDetail.fields.studio') }} : {{ series_detail.studio }}
</v-card-text>
</v-card>
</v-col>
@@ -52,7 +52,7 @@
depressed
@click="showAddContent"
>
콘텐츠 추가
{{ $t('view.content.seriesDetail.actions.addContent') }}
</v-btn>
</v-col>
</v-row>
@@ -63,16 +63,16 @@
<thead>
<tr>
<th class="text-center">
썸네일
{{ $t('view.content.common.headers.thumbnail') }}
</th>
<th class="text-center">
제목
{{ $t('view.content.common.headers.title') }}
</th>
<th class="text-center">
19
{{ $t('view.content.common.headers.adult') }}
</th>
<th class="text-center">
관리
{{ $t('view.content.common.headers.actions') }}
</th>
</tr>
</thead>
@@ -104,7 +104,7 @@
color="#3bb9f1"
@click="deleteConfirm(content)"
>
삭제
{{ $t('view.content.common.actions.delete') }}
</v-btn>
</td>
</tr>

View File

@@ -2,7 +2,7 @@
<div>
<v-toolbar dark>
<v-spacer />
<v-toolbar-title>시리즈 관리</v-toolbar-title>
<v-toolbar-title>{{ $t('view.content.series.title') }}</v-toolbar-title>
<v-spacer />
</v-toolbar>
@@ -18,7 +18,7 @@
depressed
@click="showWriteDialog"
>
시리즈 등록
{{ $t('view.content.series.actions.create') }}
</v-btn>
</v-col>
</v-row>
@@ -56,13 +56,13 @@
text
@click="showModifyDialog(item)"
>
수정
{{ $t('view.content.series.actions.edit') }}
</v-btn>
<v-btn
text
@click="deleteConfirm(item)"
>
삭제
{{ $t('view.content.common.actions.delete') }}
</v-btn>
<v-spacer />
</v-card-actions>
@@ -79,12 +79,12 @@
>
<v-card>
<v-card-title>
시리즈 등록
{{ $t('view.content.series.dialog.createTitle') }}
</v-card-title>
<div class="image-select">
<label for="image">
커버 이미지 등록
{{ $t('view.content.series.fields.coverImage') }}
</label>
<v-file-input
id="image"
@@ -102,12 +102,12 @@
<v-card-text>
<v-row align="center">
<v-col cols="4">
제목*
{{ $t('view.content.series.fields.title') }}*
</v-col>
<v-col cols="8">
<v-text-field
v-model="series.title"
label="제목"
:label="$t('view.content.series.fields.title')"
required
/>
</v-col>
@@ -116,12 +116,12 @@
<v-card-text>
<v-row align="center">
<v-col cols="4">
소개*
{{ $t('view.content.series.fields.introduction') }}*
</v-col>
<v-col cols="8">
<v-textarea
v-model="series.introduction"
label="소개"
:label="$t('view.content.series.fields.introduction')"
required
/>
</v-col>
@@ -130,7 +130,7 @@
<v-card-text>
<v-row>
<v-col cols="4">
연재요일*
{{ $t('view.content.series.fields.publishedDaysOfWeek') }}*
</v-col>
<v-col
cols="8"
@@ -159,14 +159,14 @@
value="RANDOM"
@change="handleCheckboxChange('RANDOM', $event)"
>
<label for="checkbox_random"> 랜덤</label>
<label for="checkbox_random"> {{ $t('view.content.series.fields.random') }}</label>
</v-col>
</v-row>
</v-card-text>
<v-card-text>
<v-row align="center">
<v-col cols="4">
장르
{{ $t('view.content.series.fields.genre') }}
</v-col>
<v-col cols="8">
<v-select
@@ -174,7 +174,7 @@
:items="series_genre_list"
item-text="name"
item-value="value"
label="장르 선택"
:label="$t('view.content.series.fields.selectGenre')"
/>
</v-col>
</v-row>
@@ -560,32 +560,32 @@ export default {
validate() {
if (this.series.cover_image === undefined || this.series.cover_image === null) {
this.notifyError('커버 이미지를 선택하세요')
this.notifyError(this.$t('view.content.series.validation.coverImageRequired'))
return
}
if (this.series.title === undefined || this.series.title === null || this.series.title.trim().length <= 0) {
this.notifyError('시리즈 제목을 입력하세요')
this.notifyError(this.$t('view.content.series.validation.titleRequired'))
return
}
if (this.series.introduction === undefined || this.series.introduction === null || this.series.introduction.trim().length <= 0) {
this.notifyError('시리즈 소개를 입력하세요')
this.notifyError(this.$t('view.content.series.validation.introRequired'))
return
}
if (this.series.published_days_of_week === undefined || this.series.published_days_of_week === null || this.series.published_days_of_week.length <= 0) {
this.notifyError('시리즈 연재요일을 선택하세요')
this.notifyError(this.$t('view.content.series.validation.daysRequired'))
return
}
if (this.series.keyword === undefined || this.series.keyword === null || this.series.keyword.replaceAll('#', '').trim().length <= 0) {
this.notifyError('시리즈를 설명할 수 있는 키워드를 입력하세요')
this.notifyError(this.$t('view.content.series.validation.keywordsRequired'))
return
}
if (this.series.genre_id === undefined || this.series.genre_id === null || this.series.genre_id <= 0) {
this.notifyError('올바른 장르를 선택하세요')
this.notifyError(this.$t('view.content.series.validation.genreRequired'))
return
}
@@ -615,10 +615,10 @@ export default {
return {name: item.genre, value: item.id}
})
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(res.data.message || this.$t('common.error.unknown'))
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(this.$t('common.error.unknown'))
}
},

View File

@@ -2,7 +2,7 @@
<div>
<v-toolbar dark>
<v-spacer />
<v-toolbar-title>시그니처 관리</v-toolbar-title>
<v-toolbar-title>{{ $t('view.signature.title') }}</v-toolbar-title>
<v-spacer />
</v-toolbar>
<v-container>
@@ -16,7 +16,7 @@
depressed
@click="showWriteDialog"
>
시그니처 등록
{{ $t('view.signature.actions.create') }}
</v-btn>
</v-col>
</v-row>
@@ -34,7 +34,7 @@
for="sort-newest"
class="radio-label-sort"
>
최신순
{{ $t('view.signature.sort.newest') }}
</label>
</v-col>
<v-col>
@@ -50,7 +50,7 @@
for="sort-can-high"
class="radio-label-sort"
>
높은캔순
{{ $t('view.signature.sort.canHigh') }}
</label>
</v-col>
<v-col>
@@ -66,7 +66,7 @@
for="sort-can-low"
class="radio-label-sort"
>
낮은캔순
{{ $t('view.signature.sort.canLow') }}
</label>
</v-col>
<v-col cols="10" />
@@ -112,7 +112,7 @@
:disabled="is_loading"
@click="showModifyDialog(item)"
>
수정
{{ $t('view.signature.actions.edit') }}
</v-btn>
</v-col>
<v-col>
@@ -120,7 +120,7 @@
:disabled="is_loading"
@click="showDeleteConfirm(item)"
>
삭제
{{ $t('view.signature.actions.delete') }}
</v-btn>
</v-col>
<v-col />
@@ -148,17 +148,17 @@
>
<v-card>
<v-card-title>
시그니처 등록
{{ $t('view.signature.dialog.createTitle') }}
</v-card-title>
<v-card-text>
<v-row align="center">
<v-col cols="4">
{{ $t('view.signature.fields.can') }}
</v-col>
<v-col cols="8">
<v-text-field
v-model="can"
label=""
:label="$t('view.signature.fields.can')"
/>
</v-col>
</v-row>
@@ -167,7 +167,7 @@
<v-card-text>
<v-row>
<v-col cols="4">
19
{{ $t('view.signature.fields.adult') }}
</v-col>
<v-col cols="8">
<input
@@ -181,12 +181,12 @@
<v-card-text>
<v-row align="center">
<v-col cols="4">
이미지
{{ $t('view.signature.fields.image') }}
</v-col>
<v-col cols="8">
<div class="image-select">
<label for="image">
이미지 불러오기
{{ $t('view.signature.actions.loadImage') }}
</label>
<v-file-input
id="image"
@@ -208,12 +208,12 @@
<v-card-text>
<v-row align="center">
<v-col cols="4">
시간()
{{ $t('view.signature.fields.timeSec') }}
</v-col>
<v-col cols="8">
<v-text-field
v-model="time"
label="시간(초)"
:label="$t('view.signature.fields.timeSec')"
/>
</v-col>
</v-row>
@@ -325,14 +325,14 @@
text
@click="cancel"
>
취소
{{ $t('common.actions.cancel') }}
</v-btn>
<v-btn
color="blue darken-1"
text
@click="modifySignatureCan"
>
수정
{{ $t('view.signature.actions.edit') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -347,7 +347,7 @@
<v-card>
<v-card-text />
<v-card-text>
삭제하시겠습니까?
{{ $t('common.confirm.delete') }}
</v-card-text>
<v-card-actions v-show="!is_loading">
<v-spacer />
@@ -356,14 +356,14 @@
text
@click="cancel"
>
취소
{{ $t('common.actions.cancel') }}
</v-btn>
<v-btn
color="blue darken-1"
text
@click="deleteSignature"
>
확인
{{ $t('common.actions.confirm') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -376,7 +376,7 @@
persistent
>
<v-card>
<v-card-title>이미지 크롭</v-card-title>
<v-card-title>{{ $t('view.signature.dialog.cropTitle') }}</v-card-title>
<v-card-text>
<div class="cropper-wrapper">
<img
@@ -394,14 +394,14 @@
text
@click="cancelCropper"
>
취소
{{ $t('common.actions.cancel') }}
</v-btn>
<v-btn
color="blue darken-1"
text
@click="cropImage"
>
크롭 완료
{{ $t('view.signature.actions.cropDone') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -444,31 +444,31 @@ export default {
headers: [
{
text: '캔',
text: this.$t('view.signature.headers.can'),
align: 'center',
sortable: false,
value: 'can',
},
{
text: '19금',
text: this.$t('view.signature.headers.adult'),
align: 'center',
sortable: false,
value: 'isAdult',
},
{
text: '이미지',
text: this.$t('view.signature.headers.image'),
align: 'center',
sortable: false,
value: 'image',
},
{
text: '시간(초)',
text: this.$t('view.signature.headers.timeSec'),
align: 'center',
sortable: false,
value: 'time',
},
{
text: '관리',
text: this.$t('view.signature.headers.actions'),
align: 'center',
sortable: false,
value: 'management',
@@ -565,9 +565,9 @@ export default {
this.can === 0 ||
this.image === null
) {
this.notifyError('내용을 입력하세요')
this.notifyError(this.$t('view.signature.validation.fillRequired'))
} else if (this.time < 3 || this.time > 20) {
this.notifyError('시간은 3초 이상 20초 이하를 입력하세요.')
this.notifyError(this.$t('view.signature.validation.timeRange'))
} else {
this.submit()
}
@@ -622,7 +622,7 @@ export default {
this.total_page = total_page
}
} catch (e) {
this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
this.notifyError(this.$t('common.error.unknown'))
} finally {
this.is_loading = false
}
@@ -643,16 +643,16 @@ export default {
const res = await api.createSignature(formData)
if (res.status === 200 && res.data.success === true) {
this.cancel()
this.notifySuccess(res.data.message || '등록되었습니다.')
this.notifySuccess(res.data.message || this.$t('view.signature.messages.created'))
this.page = 1
await this.getSignatureList()
} else {
this.is_loading = false
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(res.data.message || this.$t('common.error.unknown'))
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(this.$t('common.error.unknown'))
this.is_loading = false
} finally {
this.is_loading = false
@@ -666,7 +666,7 @@ export default {
this.can === this.selected_signature_can.can &&
this.time === this.selected_signature_can.time
) {
this.notifyError('변경사항이 없습니다.')
this.notifyError(this.$t('view.signature.messages.noChanges'))
return;
}
@@ -697,16 +697,16 @@ export default {
const res = await api.modifySignature(formData)
if (res.status === 200 && res.data.success === true) {
this.cancel()
this.notifySuccess(res.data.message || '수정되었습니다.')
this.notifySuccess(res.data.message || this.$t('view.signature.messages.updated'))
this.page = 1
await this.getSignatureList()
} else {
this.is_loading = false
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(res.data.message || this.$t('common.error.unknown'))
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(this.$t('common.error.unknown'))
this.is_loading = false
} finally {
this.is_loading = false
@@ -726,16 +726,16 @@ export default {
const res = await api.modifySignature(formData)
if (res.status === 200 && res.data.success === true) {
this.cancel()
this.notifySuccess(res.data.message || '등록되었습니다.')
this.notifySuccess(res.data.message || this.$t('view.signature.messages.deleted'))
this.page = 1
await this.getSignatureList()
} else {
this.is_loading = false
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(res.data.message || this.$t('common.error.unknown'))
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(this.$t('common.error.unknown'))
this.is_loading = false
} finally {
this.is_loading = false