Files
sodalive-vuejs-creator-admin/docs/20260508_국제화도입.md
Yu Sung aba06048b3 fix(i18n): 브라우저 탭 타이틀을 common.app.title 기반으로 국제화 동기화
라우터 afterEach에서 가장 깊은 meta.titleKey 사용, locale 변경 watch에서 동일 로직으로 재계산, 초기 진입 시 common.app.title로 교체. docs에 검증 기록 추가.
2026-05-08 16:02:01 +09:00

230 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 국제화(i18n) 도입 계획
## 목표
- Vue 2 + Vuetify 2 기반에서 한국어(ko), 영어(en), 일본어(ja) 3개 언어를 지원한다.
- 브라우저 설정을 기본 로케일로 사용하되, 사용자가 선택한 언어가 있으면(localStorage) 그 값을 우선한다.
## 체크리스트
- [x] vue-i18n v8 도입 및 기본 설정(`src/i18n/index.js`)
- [x] 리소스 파일 생성(`src/locales/ko.json`, `src/locales/en.json`, `src/locales/ja.json`)
- [x] Vuetify 언어팩 연동(`src/plugins/vuetify.js`) 및 초기 로케일 동기화
- [x] `main.js`에서 vue-i18n ↔ Vuetify 로케일 동기화 로직 추가
- [x] 공통 컴포넌트 문자열 치환(네비/레이아웃/다이얼로그)
- [x] `src/components/SideMenu.vue` 로그아웃/에러/기본 메뉴 텍스트 치환
- [x] `src/App.vue` 앱바 타이틀 치환 및 언어 드롭다운 추가
- [x] 주요 뷰(`views/Agent/*`) 1차 치환
- [x] 날짜/숫자/통화 포맷 정책 적용(ja: JPY 소수점 미사용 등)
- [x] 하드코딩 탐지/미번역 키 점검(정규식 스캔 + missing 핸들러)
- [x] 언어 전환 UX(드롭다운) 및 영속 저장(localStorage)
- [x] P2: 레거시 정산 화면(`views/Calculate/*`) i18n 치환
- [x] 공통 키 정의(`view.calculate.common.*`, 단위 `common.unit.*` 보강)
- [x] `CalculateLive.vue` 텍스트/헤더/합계/오류 치환
- [x] `CalculateContent.vue` 텍스트/헤더/합계/오류 치환
- [x] `CalculateContentDonation.vue` 텍스트/헤더/합계/오류 치환
- [x] `CalculateCommunityPost.vue` 텍스트/헤더/합계/오류 치환
- [x] `CalculateChannelDonation.vue` 텍스트/헤더/합계/오류 치환
- [x] `CalculateAccumulation.vue` 텍스트/헤더/합계/오류 치환
- [x] 검증: `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`) 치환
- [x] 브라우저 문서 타이틀 i18n 동기화(라우터 afterEach + locale 변경 watch)
## 키 네이밍 규칙
- 네임스페이스 기반: `common.*`, `comp.*`, `view.*`
- 예) `common.logout`, `common.error.unknown`, `comp.sideMenu.calc.live`
## 개발 메모
- 서버 메뉴가 비어 기본 메뉴를 사용하는 분기는 i18n 키를 사용하도록 업데이트함.
- 서버에서 전달되는 텍스트는 과도기적으로 기존 값을 그대로 사용하며, 추후 서버가 키를 전달하도록 협업 예정.
## 검증 기록
### 1차 준비 (2026-05-08)
- 무엇을: i18n 인프라 파일 추가, Vuetify 언어팩 연동, SideMenu 일부 치환
- 왜: 하드코딩 텍스트를 다국어로 전환하기 위한 기반 마련
- 어떻게:
- 실행 명령: (로컬) `npm i``npm run serve` 예정
- 결과: 아직 실행 전. 의존성 설치 및 로컬 실행 시에 확인 예정
- 보완: 언어 전환 UI/UX 및 나머지 화면 치환, 하드코딩 스캔 적용 예정
### 2차 구현 (2026-05-08)
- 무엇을: 공통 레이아웃(App 바) 및 주요 뷰(Agents/Creators, Calculate/*) 문자열 i18n 치환, 숫자/통화 포맷 적용, 언어 드롭다운 추가, 하드코딩 스캔 스크립트 추가
- 왜: 다국어 전환 시 모든 핵심 화면이 정상 동작하고, 통화/숫자 포맷(특히 JPY 무소수)이 일관되게 표시되도록 하기 위함
- 어떻게:
- 실행 명령 1: `npm run i18n:scan`
- 기대 결과: 남아있는 한/일문 하드코딩 라인 목록 출력(없다면 빈 결과). 개발 중 점검용으로 유지.
- 실행 명령 2: `npm run serve` 후 브라우저에서 App 바의 언어 드롭다운으로 `ko/en/ja` 전환
- 확인 항목: App 타이틀/사이드메뉴/툴바/테이블 헤더/합계 행 텍스트가 즉시 해당 언어로 변경됨 ✓
- 실행 명령 3: 각 정산 화면의 합계/금액 컬럼 확인
- 확인 항목: en=USD 통화기호/소수 2자리, ko=KRW 소수 0자리, ja=JPY 소수 0자리로 `$n(..., 'currency')` 표기 ✓
- 오류 핸들링: API 실패 시 공통 메시지 `common.error.unknown` 사용, 목록 실패 시 `common.error.fetchFailed` 사용 ✓
- Vuetify 동기화: 언어 전환 시 Vuetify locale이 함께 변경되는지 확인(메시지/레이블) ✓
### 3차 점검 하드코딩 문자열 스캔 (2026-05-08)
- 무엇을: 소스 내 한글/일문 하드코딩 문자열이 남아있는 위치를 정규식으로 일괄 스캔해 목록화
- 왜: 남아있는 하드코딩 텍스트를 체계적으로 발견·우선순위화하여 i18n 치환 누락을 방지하기 위함
- 어떻게:
- 실행 명령: `npm run i18n:scan`
- 주요 결과(파일 단위 요약):
- 로그인/계정 관련
- `src/views/Login/Login.vue`
- `src/store/accountStore.js`
- `src/api/member.js` (주석 한국어 포함)
- 정산(레거시 경로) 관련
- `src/views/Calculate/CalculateLive.vue`
- `src/views/Calculate/CalculateContent.vue`
- `src/views/Calculate/CalculateContentDonation.vue`
- `src/views/Calculate/CalculateCommunityPost.vue`
- `src/views/Calculate/CalculateChannelDonation.vue`
- `src/views/Calculate/CalculateAccumulation.vue`
- 콘텐츠 관리 관련
- `src/views/Content/ContentList.vue`
- `src/views/Content/ContentCategoryList.vue`
- `src/views/Content/ContentSeriesList.vue`
- `src/views/Content/ContentSeriesDetail.vue`
- 에이전트 화면(신규 경로는 다수 치환되었으나 일부 주석/텍스트 존재 가능)
- `src/views/Agent/Creators.vue`
- `src/views/Agent/Calculate/AgentCalculateLive.vue`
- `src/views/Agent/Calculate/AgentCalculateContent.vue`
- `src/views/Agent/Calculate/AgentCalculateContentDonation.vue`
- `src/views/Agent/Calculate/AgentCalculateCommunityPost.vue`
- `src/views/Agent/Calculate/AgentCalculateChannelDonation.vue`
- 공통/기반 코드(주석/문구)
- `src/components/SideMenu.vue`
- `src/router/index.js`
- `src/main.js`
- `src/i18n/index.js`
- `src/api/agent_calculate.js`
- 비고:
- 위 목록에는 “주석 내 한국어”도 포함된다. 실동작 텍스트 우선 치환 대상은 템플릿 내 라벨/버튼/메시지/테이블 헤더 등 사용자 노출부이다.
- `views/Agent/*` 경로는 상당수 치환됨. 반면 `views/Calculate/*`, `views/Content/*`, `views/Login/*`에는 하드코딩 문자열이 상대적으로 많이 남아 있음.
#### 후속 액션(우선순위 제안)
- P1 (가시성·필수 유입 경로): 로그인/계정
- `src/views/Login/Login.vue`의 오류/안내 메시지 i18n 치환
- `src/store/accountStore.js`의 사용자 노출 에러 메시지 i18n 치환
- P2 (업무 핵심): 레거시 정산 화면 `views/Calculate/*`
- 테이블 헤더/합계/필터 라벨/버튼 등 일괄 키 도출 → `view.calculate.*` 네임스페이스 제안
### 4차 수정 브라우저 타이틀 국제화 (2026-05-08)
- 무엇을: 라우트 이동 및 언어 전환 시 `document.title`이 선택된 언어로 표시되도록 동기화 구현(키: `common.app.title`)
- 왜: i18n 전환이 뷰 내부 텍스트에는 적용되지만 브라우저 탭 타이틀은 자동 반영되지 않기 때문
- 어떻게:
- 코드 변경 1: `src/router/index.js`
- `import i18n from '@/i18n'` 추가
- `router.afterEach` 훅에서 `to.matched`의 가장 깊은 라우트에서 `meta.titleKey`를 찾아 `document.title = "<번역> - i18n.t('common.app.title')"`로 설정, 없으면 `i18n.t('common.app.title')` 기본값 사용
- 시그니처 화면 라우트에 `meta: { titleKey: 'view.signature.title' }` 추가
- 코드 변경 2: `src/main.js`
- 앱 초기 구동 시 index.html의 하드코딩 타이틀을 `i18n.t('common.app.title')`로 1회 교체
- `i18n.vm.$watch('locale', ...)` 내에서 현재 라우트의 가장 깊은 `meta.titleKey`를 기준으로 `document.title` 재계산, 없으면 `i18n.t('common.app.title')` 사용
- 검증:
- 실행: `npm run serve`
- 브라우저에서 `/signature` 진입 → 탭 타이틀이 `시그니처 관리 - <앱명(언어별: common.app.title)>`로 표시됨 ✓
- App의 언어 드롭다운으로 `en/ja` 전환 → 탭 타이틀이 해당 언어로 즉시 갱신됨 ✓
- `meta.titleKey`가 없는 라우트 이동 시 탭 타이틀이 `i18n.t('common.app.title')`로 유지됨 ✓
- P3 (업무 빈도 높음): 콘텐츠 관리 `views/Content/*`
- 목록/상세/시리즈 화면의 라벨/버튼/알림 메시지 치환 → `view.content.*` 네임스페이스 제안
- P4: 공통 남은 텍스트 및 주석 정리(사용자 노출 X 주석은 후순위)
- 팝업 다이얼로그 사용자 노출 텍스트 전수 치환(i18n)
- 시그니처 관리 페이지(`views/Signature/SignatureManagement.vue`) 치환
#### 참고(샘플 라인)
- `src/views/Login/Login.vue`: '카카오 인증 모듈을 불 러오지 못했습니다. 페이지를 새로고침 해주세요.' 등 다수 경고/안내 메시지 하드코딩
- `src/store/accountStore.js`: '로그인 정보를 확인해주세요.' 등 계정 관련 오류 메시지 하드코딩
### 4차 구현 로그인/계정 치환 (2026-05-08)
- 무엇을: 로그인 화면과 계정 스토어의 사용자 노출 문자열을 i18n으로 치환하고 3개 언어 리소스를 추가함
- 왜: 필수 유입 경로인 로그인 단계에서 다국어 경험을 보장하고, 오류 시 일관된 안내를 제공하기 위함
- 어떻게:
- 변경 파일:
- `src/views/Login/Login.vue`: 라벨/버튼/alt/안내·오류 메시지 전면 `$t()` 치환
- `src/store/accountStore.js`: 에러 메시지 `i18n.t()`로 치환(ko/en/ja 키 사용)
- `src/locales/{ko,en,ja}.json`: `view.login.*`, `alt.kakaoLoginButton`, `notice.loading.{appleSdk,kakaoSdk}`, `common.error.{login,google,kakao,apple}.*` 키 추가
- 실행 명령 1: `npm run i18n:scan`
- 기대 결과: Login.vue, accountStore.js 내 한/일문 하드코딩 라인이 0 또는 주석만 남음 ✓
- 실행 명령 2: `npm run serve` 후 로그인 화면 확인
- 확인 항목: Email/Password 라벨, 로그인/소셜 로그인 버튼 텍스트가 ko/en/ja 전환에 따라 변경됨 ✓
- 확인 항목: 애플/카카오 SDK 로딩/실패/잘못된 응답 등의 안내 메시지가 각 언어로 표시됨 ✓
### 5차 구현 Google 버튼 런타임 언어 전환 반영 (2026-05-08)
- 무엇을: Google Identity Services(GIS) 버튼이 앱의 언어 전환과 함께 텍스트도 변경되도록, GIS 스크립트를 선택 로케일(`hl`)로 동적 로드하고 버튼을 재렌더하도록 구현함
- 왜: 외부 위젯(GIS 버튼)은 `vue-i18n` 전환을 자동으로 따르지 않기 때문에, 사용자 경험을 일치시키기 위해 수동 재로드가 필요함
- 어떻게:
- 변경 파일:
- `public/index.html`: 고정 스크립트 `<script src="https://accounts.google.com/gsi/client">` 제거(주석으로 동적 로드 안내 추가)
- `src/views/Login/Login.vue`:
- `loadGisScript(locale)`: `?hl=<locale>`로 SDK 동적 주입(+ 캐시 버스터)
- `renderGoogleButton()`: `initialize``renderButton` 재호출, 컨테이너 초기화
- `mountGoogleButtonForLocale(locale)`: SDK 로드 후 버튼 렌더 묶음
- `mounted()`: 초기 로케일로 로드 + `$i18n.locale` watch로 언어 변경 시 재주입/재렌더
- 기존 `initGoogleLogin()` 대체
- 실행 명령 1: `npm run serve`
- 확인 항목: 초기 언어(ko/en/ja)에 따라 Google 버튼 라벨이 해당 언어로 표시됨 ✓
- 실행 명령 2: 헤더의 언어 드롭다운으로 `ko → en → ja` 순서로 전환
- 확인 항목: 각 전환 시 Google 버튼 라벨이 즉시 새 언어로 변경됨 ✓
- 참고: 외부 위젯 특성상 캐시 환경에 따라 반영이 지연될 수 있어, SDK URL에 캐시 버스터 파라미터 추가함
### 6차 구현 레거시 정산 화면 치환(P2) (2026-05-08)
- 무엇을: `views/Calculate/*`의 사용자 노출 텍스트(툴바 타이틀/기간 필터/조회 버튼/테이블 헤더/합계/오류)를 i18n으로 치환하고, 공통/화면별 키를 `view.calculate.*` 네임스페이스로 추가함. 단위 표기(`명/캔/원`)는 `common.unit.*` 사용.
- 왜: 업무 핵심 화면의 다국어 일관성을 확보하고, 남은 하드코딩 문자열을 제거하기 위함
- 어떻게:
- 변경 파일(1차):
- `src/views/Calculate/CalculateLive.vue`: 타이틀/기간 표시/조회 버튼/헤더/합계/오류 i18n 치환(로케일 전환 반응형)
- `src/views/Calculate/CalculateContent.vue`: 동등 범위 치환(로케일 전환 반응형)
- `src/locales/{ko,en,ja}.json`: `view.calculate.common.*`, `view.calculate.live.*`, `view.calculate.content.*`, `common.unit.{person,krw}` 추가
- 실행 명령 1: `npm run i18n:scan`
- 기대 결과: 위 2개 파일 내 한/일문 하드코딩 라인이 0 또는 최소(주석) ✓
- 실행 명령 2: `npm run serve``ko ↔ en ↔ ja` 전환
- 확인 항목: 타이틀/필터 구분자/버튼/헤더/합계 단위가 즉시 전환됨 ✓
- 비고: 나머지 레거시 정산 파일은 P2-2차로 순차 치환 예정
### 6차 구현 레거시 정산 화면 치환(P2-2차) (2026-05-08)
- 무엇을: P2의 나머지 4개 화면(ContentDonation/CommunityPost/ChannelDonation/Accumulation)의 하드코딩 텍스트를 i18n으로 치환하고, 공통/화면별 키를 ko/en/ja 리소스에 추가함. 버튼/기간 구분자/헤더/합계 행/단위(건/캔/원) 및 오류 메시지를 일괄 적용.
- 왜: 업무 핵심 화면 전 범위에서 다국어 일관성과 유지보수성을 확보하기 위함
- 어떻게:
- 변경 파일:
- `src/views/Calculate/CalculateContentDonation.vue`: 타이틀/기간/조회 버튼/헤더 computed 전환/단위/오류 메시지 i18n 치환
- `src/views/Calculate/CalculateCommunityPost.vue`: 동일 범위 치환 + 합계 행 단위 치환
- `src/views/Calculate/CalculateChannelDonation.vue`: 동일 범위 치환 + 합계 행 단위 치환(건/캔/원)
- `src/views/Calculate/CalculateAccumulation.vue`: 동일 범위 치환(등록일/판매금액(캔)/누적 판매수 등)
- `src/locales/{ko,en,ja}.json`: `view.calculate.{contentDonation,community,channelDonation,accumulation}.*`, `view.calculate.common.headers.count`, `common.unit.case` 추가
- 실행 명령 1: `npm run i18n:scan`
- 기대 결과: 위 4개 파일 내 한/일문 하드코딩 라인이 0 또는 최소(주석) ✓
- 실행 명령 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/*`). 후속 커밋에서 단계별 치환 예정.