fix. 언어정책 적용

This commit is contained in:
clkim
2026-01-13 15:51:10 +09:00
parent 352d76a61c
commit 7e98721228
10 changed files with 92 additions and 218 deletions

View File

@@ -1,6 +1,6 @@
<template> <template>
<header class="header"> <header class="header">
<BlocksStoveGnbNew class="min-h-[48px]" /> <BlocksStoveGnb class="min-h-[48px]" />
</header> </header>
<section class="inspection-section"> <section class="inspection-section">
<clientOnly> <clientOnly>

View File

@@ -1,7 +1,6 @@
import type { LocaleObject, NuxtI18nOptions } from '@nuxtjs/i18n' import type { LocaleObject, NuxtI18nOptions } from '@nuxtjs/i18n'
// const LANG_DIR: string = './i18n/locales' const LANG_DIR: string = '../i18n/locales'
const DEFAULT_COVERAGES: string[] = ['ko', 'en', 'ja', 'zh-tw', 'zh-cn', 'th'] const DEFAULT_COVERAGES: string[] = ['ko', 'en', 'ja', 'zh-tw', 'zh-cn', 'th']
const DEFAULT_LOCALES: Record<string, LocaleObject> = { const DEFAULT_LOCALES: Record<string, LocaleObject> = {
ko: { code: 'ko', name: '한국어', iso: 'ko-KR', dir: 'ltr' }, ko: { code: 'ko', name: '한국어', iso: 'ko-KR', dir: 'ltr' },
@@ -40,12 +39,10 @@ const getI18n = (allowedLangCodes?: string[]): NuxtI18nOptions => {
defaultLocale: DEFAULT_LOCALE_CODE, defaultLocale: DEFAULT_LOCALE_CODE,
detectBrowserLanguage: { detectBrowserLanguage: {
fallbackLocale: DEFAULT_LOCALE_CODE, fallbackLocale: DEFAULT_LOCALE_CODE,
useCookie: true, useCookie: false,
cookieKey: 'LOCALE',
cookieDomain: process.env.BASE_DOMAIN,
redirectOn: 'root', redirectOn: 'root',
}, },
langDir: '../i18n/locales', langDir: LANG_DIR,
compilation: { compilation: {
strictMessage: false, strictMessage: false,
escapeHtml: false, escapeHtml: false,

View File

@@ -1,72 +1,66 @@
<script setup lang="ts"> <script setup lang="ts">
import { useGameDataStore } from '#layers/stores/useGameDataStore' let mountedInstance: any = null
let cpHeader: any = null
const runtimeConfig = useRuntimeConfig()
const { locale, availableLocales } = useI18n()
const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const stoveInflowPath = runtimeConfig.public.stoveInflowPath
const stoveGameNo = runtimeConfig.public.stoveGameNo
const stoveGnbData = gameData.value?.stove_gnb_json
const languageCodes = computed(() => {
if (Array.isArray(availableLocales)) {
return availableLocales.map(
(localeCode: any) => localeCode.code ?? localeCode
)
}
return [locale]
})
const loadGnb = (locale: string) => {
locale = locale.toLowerCase()
const gnbOption = {
wrapper: '#stove-wrap',
isResponsive: true,
skin: 'gnb-dark-mini',
widget: {
gameListAndService: false,
languageSelect: false,
notification: stoveGnbData?.notify_icon_visible ?? true,
stoveDownload: false,
},
global: {
userGds: true,
defaultSelectedLanguage: locale ?? 'en',
languageCoverages: languageCodes.value,
},
loginMethod: {
params: {
inflow_path: stoveInflowPath,
game_no: stoveGameNo,
show_play_button: stoveGnbData?.stove_install_button_visible ?? 'Y',
},
redirectCurrentPage: true,
windowTitle: undefined,
},
}
cpHeader = new (window as any).cp.Header(gnbOption)
cpHeader.render()
}
onMounted(() => { onMounted(() => {
loadGnb(locale.value) const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const langCodes = gameData.value?.lang_codes
const defaultLangCode = gameData.value?.default_lang_code
const stoveGnbData = gameData.value?.stove_gnb_json
const designTheme = gameData.value?.design_theme
const currentDomain =
window.location.protocol + '//' + window.location.hostname
if (typeof window !== 'undefined' && (window as any).StoveGnb) {
const stoveGnbOptions = {
logArea: currentDomain,
useLanguageCodeFromPath: true,
serviceTitle: {
pc: '',
mobile: '',
},
widget: {
notification: stoveGnbData?.notify_icon_visible ?? true,
stoveDownload: stoveGnbData?.stove_install_button_visible ?? true,
languageSelect: false,
themeSelect: false,
stoveMenu: {
active: false,
mobile: true,
},
},
global: {
languageCoverages: langCodes,
defaultSelectedLanguage: defaultLangCode ?? 'ko',
},
loginMethod: {
redirectCurrentPage: true,
},
mode: {
theme: {
support: designTheme === 1 ? ['light'] : ['dark'],
default: designTheme === 1 ? 'light' : 'dark',
},
mini: true,
layout: 'wide',
fixed: false,
},
}
mountedInstance = (window as any).StoveGnb.mount(
'#stove-wrap',
stoveGnbOptions
)
}
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (cpHeader && typeof cpHeader.destroy === 'function') { if (mountedInstance && typeof mountedInstance.destroy === 'function') {
cpHeader.destroy() mountedInstance.destroy()
} }
cpHeader = null mountedInstance = null
}) })
</script> </script>
<template> <template>
<div id="stove-wrap" class="relative z-[5]" /> <div id="stove-wrap" class="relative h-[48px] z-[5]" />
</template> </template>

View File

@@ -1,79 +0,0 @@
<script setup lang="ts">
let mountedInstance: any = null
const runtimeConfig = useRuntimeConfig()
const baseDomain = `${runtimeConfig.public.baseDomain}`
onMounted(() => {
const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const langCodes = gameData.value?.lang_codes
const defaultLangCode = gameData.value?.default_lang_code
const stoveGnbData = gameData.value?.stove_gnb_json
const designTheme = gameData.value?.design_theme
const currentDomain =
window.location.protocol + '//' + window.location.hostname
if (typeof window !== 'undefined' && (window as any).StoveGnb) {
const stoveGnbOptions = {
logArea: currentDomain,
useLanguageCodeFromPath: false,
serviceTitle: {
pc: '',
mobile: '',
},
widget: {
notification: stoveGnbData?.notify_icon_visible ?? true,
stoveDownload: stoveGnbData?.stove_install_button_visible ?? true,
languageSelect: false,
themeSelect: false,
stoveMenu: {
active: false,
mobile: true,
},
},
global: {
languageCoverages: langCodes,
defaultSelectedLanguage: defaultLangCode ?? 'en',
},
loginMethod: {
redirectCurrentPage: true,
},
mode: {
theme: {
support: designTheme === 1 ? ['light'] : ['dark'],
default: designTheme === 1 ? 'light' : 'dark',
},
mini: true,
layout: 'wide',
fixed: false,
},
}
mountedInstance = (window as any).StoveGnb.mount(
'#stove-wrap',
stoveGnbOptions
)
}
if (mountedInstance) {
//Stove GNB에서도 쿠키를 굽고 있어 소문자로 통일
//LOCALE 쿠키를 가져온 후 소문자로 변경해서 다시 LOCALE 쿠키를 설정
nextTick(() => {
const localeCookie = useCookie('LOCALE', {
domain: baseDomain,
path: '/',
maxAge: 60 * 60 * 24 * 365, // 1년 (초 단위)
})
localeCookie.value = localeCookie.value.toLowerCase()
})
}
})
onBeforeUnmount(() => {
if (mountedInstance && typeof mountedInstance.destroy === 'function') {
mountedInstance.destroy()
}
mountedInstance = null
})
</script>
<template>
<div id="stove-wrap" class="relative h-[48px] z-[5]" />
</template>

View File

@@ -180,7 +180,7 @@ onMounted(() => {
<template> <template>
<header :class="['header', { 'empty-game': !gnbData }]"> <header :class="['header', { 'empty-game': !gnbData }]">
<BlocksStoveGnbNew class="h-[48px]" /> <BlocksStoveGnb class="h-[48px]" />
<div <div
v-if="gnbData" v-if="gnbData"
:class="['game-wrap', { 'is-fixed': isPassedStoveGnb }]" :class="['game-wrap', { 'is-fixed': isPassedStoveGnb }]"

View File

@@ -2,7 +2,7 @@
<template> <template>
<header class="header"> <header class="header">
<BlocksStoveGnbNew class="min-h-[48px]" /> <BlocksStoveGnb class="min-h-[48px]" />
</header> </header>
</template> </template>

View File

@@ -3,11 +3,11 @@ import { useTokenValidation } from '#layers/composables/useTokenValidation'
import { csrGoStoveLogin } from '#layers/utils/stoveUtil' import { csrGoStoveLogin } from '#layers/utils/stoveUtil'
export const useCheckGameStart = () => { export const useCheckGameStart = () => {
const { tm } = useI18n()
const runtimeConfig = useRuntimeConfig() const runtimeConfig = useRuntimeConfig()
const gameDataStore = useGameDataStore() const gameDataStore = useGameDataStore()
const modalStore = useModalStore() const modalStore = useModalStore()
const { handleTokenValidation } = useTokenValidation() const { handleTokenValidation } = useTokenValidation()
const { tm, locale } = useI18n()
const { gameData } = storeToRefs(gameDataStore) const { gameData } = storeToRefs(gameDataStore)
@@ -21,6 +21,11 @@ export const useCheckGameStart = () => {
// 에러 처리 // 에러 처리
const errorHandler = (errorCode: number) => { const errorHandler = (errorCode: number) => {
switch (errorCode) { switch (errorCode) {
case -100: // Window OS에서만 실행 가능
modalStore.handleOpenAlert({
contentText: tm('Alert_Client_Window'),
})
break
case 601: // PC 클라이언트 미설치 case 601: // PC 클라이언트 미설치
break break
case 40101: // 로그인 정보 확인 중 오류가 발생했습니다. 재로그인 후 다시 이용해 주세요. case 40101: // 로그인 정보 확인 중 오류가 발생했습니다. 재로그인 후 다시 이용해 주세요.
@@ -117,7 +122,7 @@ export const useCheckGameStart = () => {
const accessTokenSub = csrGetAccessToken() const accessTokenSub = csrGetAccessToken()
const stoveGameId = gameData.value?.game_id || '' const stoveGameId = gameData.value?.game_id || ''
const nationCookie = useCookie('NNTO').value const nationCookie = useCookie('NNTO').value
const localeCookie = useCookie('LOCALE').value const localeCode = locale.value.toUpperCase() || 'KO'
window.stoveJsService = window.stoveJsService || {} window.stoveJsService = window.stoveJsService || {}
@@ -138,7 +143,7 @@ export const useCheckGameStart = () => {
.run({ .run({
gameId: stoveGameId, gameId: stoveGameId,
nation: nationCookie, nation: nationCookie,
lang: localeCookie, lang: localeCode,
isSkipMaintenance: true, isSkipMaintenance: true,
}) })
.then(() => { .then(() => {
@@ -166,9 +171,7 @@ export const useCheckGameStart = () => {
const isWindowsOS = checkWindowsOS() const isWindowsOS = checkWindowsOS()
if (!isWindowsOS) { if (!isWindowsOS) {
modalStore.handleOpenAlert({ errorHandler(-100)
contentText: tm('Alert_Client_Window'),
})
return return
} }

View File

@@ -19,12 +19,7 @@ export default defineNuxtRouteMiddleware(async to => {
// const stoveMaintenanceApiUrl = `${runtimeConfig.public.stoveMaintenanceApiUrl}` // const stoveMaintenanceApiUrl = `${runtimeConfig.public.stoveMaintenanceApiUrl}`
const stoveGameId = gameData.value.game_id const stoveGameId = gameData.value.game_id
/* const localeCookie = useCookie('LOCALE', {
domain: baseDomain
}) */
const finalLocale = csrGetFinalLocale(to.path, gameData.value.lang_codes) const finalLocale = csrGetFinalLocale(to.path, gameData.value.lang_codes)
// localeCookie.value = finalLocale.toUpperCase()
// 웹 점검 ----- // 웹 점검 -----
const { isWebInspection, getInspectionDataExternal } = const { isWebInspection, getInspectionDataExternal } =

View File

@@ -1,5 +1,5 @@
import { LRUCache } from 'lru-cache' import { LRUCache } from 'lru-cache'
import { defineEventHandler, createError, setCookie, type H3Event } from 'h3' import { defineEventHandler, createError, type H3Event } from 'h3'
import { import {
getGameDomain, getGameDomain,
getPathLocale, getPathLocale,
@@ -71,25 +71,6 @@ const cache = new LRUCache<string, WebInspectionData>({
ttl: 1000 * 30, // 30초 동안 캐시 유지 ttl: 1000 * 30, // 30초 동안 캐시 유지
}) })
/**
* 최종 언어 쿠키 세팅
*
* @param event - 이벤트 객체
* @param finalLocale - 최종 언어
* @param baseDomain - 기본 도메인
*/
function setFinalLocaleCookie(
event: H3Event,
finalLocale: string,
baseDomain: string
) {
setCookie(event, 'LOCALE', finalLocale.toLowerCase(), {
domain: baseDomain,
path: '/',
maxAge: 60 * 60 * 24 * 365, // 1년 (초 단위)
})
}
/** /**
* Locale Middleware 역할 함수 * Locale Middleware 역할 함수
* *
@@ -201,20 +182,7 @@ export default defineEventHandler(async event => {
// 1-1. 정적 파일 패스 // 1-1. 정적 파일 패스
if (isStaticFile(event.path)) return if (isStaticFile(event.path)) return
// 1-2. /inspection 패스 // 1-2. 특정 경로 패스 (API, 리소스)
if (fullPath.includes('/inspection')) {
// 리턴 되기 전 언어 쿠키 세팅
const finalLocale = ssrGetFinalLocale(
event?.node.req.url,
event.node.req.headers,
gameDataLangCodes,
gameDataDefaultLocale
)
setFinalLocaleCookie(event, finalLocale, baseDomain)
return
}
// 1-3. 특정 경로 패스 (API, 리소스)
if (shouldSkipPath(fullPath)) return if (shouldSkipPath(fullPath)) return
// 캐시 키 생성 (게임 ID 포함하여 충돌 방지) // 캐시 키 생성 (게임 ID 포함하여 충돌 방지)
@@ -229,7 +197,6 @@ export default defineEventHandler(async event => {
gameDataLangCodes, gameDataLangCodes,
gameDataDefaultLocale gameDataDefaultLocale
) )
setFinalLocaleCookie(event, finalLocale, baseDomain)
// 초기화 // 초기화
let inspectionData let inspectionData

View File

@@ -38,15 +38,6 @@ export const csrGetFinalLocale = (path = '', coveragesLocales: string[]) => {
let finalLocale = DEFAULT_LOCALE_CODE // 기본값 설정 let finalLocale = DEFAULT_LOCALE_CODE // 기본값 설정
// coveragesLocales가 빈 배열이거나 유효하지 않은 경우 기본 언어 반환
if (
!coveragesLocales ||
!Array.isArray(coveragesLocales) ||
coveragesLocales.length === 0
) {
return finalLocale
}
// 1. URL 패스에 포함된 언어 // 1. URL 패스에 포함된 언어
if (path && path !== '' && path.split('/').length > 1) { if (path && path !== '' && path.split('/').length > 1) {
// 쿼리스트링 제거한 순수 path 검사 // 쿼리스트링 제거한 순수 path 검사
@@ -56,7 +47,11 @@ export const csrGetFinalLocale = (path = '', coveragesLocales: string[]) => {
const pathLocale = `${path.split('/')[1]}`.toLowerCase() const pathLocale = `${path.split('/')[1]}`.toLowerCase()
// URL 패스에 포함된 언어가 지원하는 언어인지 체크 // URL 패스에 포함된 언어가 지원하는 언어인지 체크
if (pathLocale && pathLocale !== '') { if (
pathLocale &&
pathLocale !== '' &&
coveragesLocales.includes(pathLocale)
) {
finalLocale = pathLocale finalLocale = pathLocale
return finalLocale return finalLocale
} }
@@ -66,13 +61,16 @@ export const csrGetFinalLocale = (path = '', coveragesLocales: string[]) => {
// 2. LOCALE 쿠키 언어 // 2. LOCALE 쿠키 언어
const cookieLanguage = const cookieLanguage =
`${useCookie('LOCALE', { domain: baseDomain }).value}`.toLowerCase() `${useCookie('LOCALE', { domain: baseDomain }).value}`.toLowerCase()
if (cookieLanguage && cookieLanguage !== '') { if (
cookieLanguage &&
cookieLanguage !== '' &&
coveragesLocales.includes(cookieLanguage)
) {
finalLocale = cookieLanguage finalLocale = cookieLanguage
return finalLocale return finalLocale
} }
// 3. 브라우저 언어 // 3. 브라우저 언어
if (import.meta.client) {
const browserLanguage = const browserLanguage =
`${navigator.language || navigator.languages[0]}`.toLowerCase() `${navigator.language || navigator.languages[0]}`.toLowerCase()
if ( if (
@@ -83,7 +81,6 @@ export const csrGetFinalLocale = (path = '', coveragesLocales: string[]) => {
finalLocale = browserLanguage finalLocale = browserLanguage
return finalLocale return finalLocale
} }
}
// 3. 서비스 기본 언어 // 3. 서비스 기본 언어
finalLocale = DEFAULT_LOCALE_CODE finalLocale = DEFAULT_LOCALE_CODE
@@ -98,7 +95,7 @@ export const csrGetFinalLocale = (path = '', coveragesLocales: string[]) => {
* @param {any} headers - 요청 헤더 * @param {any} headers - 요청 헤더
*/ */
export const ssrGetFinalLocale = ( export const ssrGetFinalLocale = (
path, path = '',
headers: any, headers: any,
coveragesLocales: string[], coveragesLocales: string[],
defaultLocale: string defaultLocale: string