From a3dee130899c7eea60fc85e4dbc8ed2fe184a42a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=B1=84=EB=A6=B0?= Date: Tue, 9 Sep 2025 04:09:54 +0000 Subject: [PATCH] =?UTF-8?q?feat.=20i18n=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "\btemp/dynamic-i18n-runtime.client.ts" | 107 + "\btemp/dynamic-i18n-runtime.server.ts" | 94 + "\btemp/gameData.get.ts" | 58 + "\btemp/language-redirect.global.ts" | 40 + DYNAMIC_I18N_ROUTES.md | 120 + app/app.vue | 131 +- app/pages/index.vue | 9 + app/pages/test-lang.vue | 116 + i18n.config.ts | 84 +- i18n/locales/de.ts | 54 + i18n/locales/en.ts | 62 + i18n/locales/es.ts | 54 + i18n/locales/fallback/de.ts | 1 + i18n/locales/fallback/en.ts | 1 + i18n/locales/fallback/es.ts | 1 + i18n/locales/fallback/fr.ts | 1 + i18n/locales/fallback/it.ts | 1 + i18n/locales/fallback/ja.ts | 1 + i18n/locales/fallback/ko.ts | 1 + i18n/locales/fallback/pt.ts | 1 + i18n/locales/fallback/th.ts | 1 + i18n/locales/fallback/zh-cn.ts | 1 + i18n/locales/fallback/zh-tw.ts | 1 + i18n/locales/fr.ts | 54 + i18n/locales/ja.ts | 62 + i18n/locales/ko.ts | 62 + i18n/locales/pt.ts | 54 + i18n/locales/th.ts | 54 + i18n/locales/zh-cn.ts | 62 + i18n/locales/zh-tw.ts | 62 + layers/assets/data/common.json | 514 +++ layers/components/atoms/LanguageSwitcher.vue | 79 +- layers/components/layout/default/Footer.vue | 11 +- layers/components/layout/default/Header.vue | 8 +- .../molecules/modal/YouTubeModal.vue | 135 + layers/components/templates/Background.vue | 10 + layers/composables/useDynamicI18nRoutes.ts | 69 + layers/middleware/00.route.global.ts | 35 + layers/plugins/dynamic-i18n-routes.client.ts | 25 + layers/plugins/dynamic-i18n-routes.server.ts | 25 + layers/server/middleware/gameAlias.ts | 57 +- layers/stores/useLoadingStore.ts | 14 +- layers/templates/GrVisual01/index.vue | 30 + nuxt.config.ts | 2 +- pnpm-lock.yaml | 2939 ++++------------- 45 files changed, 2929 insertions(+), 2374 deletions(-) create mode 100644 "\btemp/dynamic-i18n-runtime.client.ts" create mode 100644 "\btemp/dynamic-i18n-runtime.server.ts" create mode 100644 "\btemp/gameData.get.ts" create mode 100644 "\btemp/language-redirect.global.ts" create mode 100644 DYNAMIC_I18N_ROUTES.md create mode 100644 app/pages/test-lang.vue create mode 100644 i18n/locales/de.ts create mode 100644 i18n/locales/en.ts create mode 100644 i18n/locales/es.ts create mode 100644 i18n/locales/fallback/de.ts create mode 100644 i18n/locales/fallback/en.ts create mode 100644 i18n/locales/fallback/es.ts create mode 100644 i18n/locales/fallback/fr.ts create mode 100644 i18n/locales/fallback/it.ts create mode 100644 i18n/locales/fallback/ja.ts create mode 100644 i18n/locales/fallback/ko.ts create mode 100644 i18n/locales/fallback/pt.ts create mode 100644 i18n/locales/fallback/th.ts create mode 100644 i18n/locales/fallback/zh-cn.ts create mode 100644 i18n/locales/fallback/zh-tw.ts create mode 100644 i18n/locales/fr.ts create mode 100644 i18n/locales/ja.ts create mode 100644 i18n/locales/ko.ts create mode 100644 i18n/locales/pt.ts create mode 100644 i18n/locales/th.ts create mode 100644 i18n/locales/zh-cn.ts create mode 100644 i18n/locales/zh-tw.ts create mode 100644 layers/assets/data/common.json create mode 100644 layers/components/molecules/modal/YouTubeModal.vue create mode 100644 layers/composables/useDynamicI18nRoutes.ts create mode 100644 layers/middleware/00.route.global.ts create mode 100644 layers/plugins/dynamic-i18n-routes.client.ts create mode 100644 layers/plugins/dynamic-i18n-routes.server.ts diff --git "a/\btemp/dynamic-i18n-runtime.client.ts" "b/\btemp/dynamic-i18n-runtime.client.ts" new file mode 100644 index 0000000..acbd027 --- /dev/null +++ "b/\btemp/dynamic-i18n-runtime.client.ts" @@ -0,0 +1,107 @@ +export default defineNuxtPlugin(() => { + // 클라이언트 사이드에서 gameData를 가져와서 i18n 설정 업데이트 + const { $i18n } = useNuxtApp(); + + // gameData에서 언어 코드 추출 + const getGameDataLangCodes = (gameData: any) => { + try { + console.log("🚀 ~ getGameDataLangCodes ~ gameData:", gameData); + + if (gameData?.lang_codes) { + console.log("🚀 ~ getGameDataLangCodes ~ gameData.lang_codes:", gameData.lang_codes); + console.log("🚀 ~ getGameDataLangCodes ~ lang_codes type:", typeof gameData.lang_codes); + console.log("🚀 ~ getGameDataLangCodes ~ lang_codes isArray:", Array.isArray(gameData.lang_codes)); + return Array.isArray(gameData.lang_codes) ? gameData.lang_codes : [gameData.lang_codes]; + } else { + console.log("🚀 ~ getGameDataLangCodes ~ gameData.lang_codes is undefined or null"); + } + } catch (error) { + console.warn('Failed to get gameData lang codes:', error); + } + + console.log("🚀 ~ getGameDataLangCodes ~ returning default ['ko']"); + return ['ko']; // 기본값 + }; + + // i18n 설정 업데이트 + const updateI18nLocales = async (gameData?: any) => { + const langCodes = getGameDataLangCodes(gameData); + + if (langCodes && langCodes.length > 0) { + // 새로운 로케일 설정 생성 + const newLocales = langCodes.map(code => ({ + code, + file: `${code}.ts`, + name: getLocaleName(code), + iso: getLocaleIso(code), + dir: 'ltr' + })); + + // i18n 설정 업데이트 + if ($i18n) { + // 로케일 메시지 동적 로드 + for (const code of langCodes) { + try { + const messages = await import(`../../i18n/locales/${code}.ts`); + // defineI18nLocale 함수를 실행하여 실제 메시지 데이터 가져오기 + const localeMessages = await messages.default(code); + console.log(`🚀 ~ loaded messages for ${code}:`, localeMessages); + ($i18n as any).setLocaleMessage(code, localeMessages); + } catch (error) { + console.warn(`Failed to load locale messages for ${code}:`, error); + } + } + } + } + }; + + // 로케일 이름 가져오기 + const getLocaleName = (code: string): string => { + const localeNames: Record = { + en: 'English', + 'zh-tw': '繁體中文', + ja: '日本語', + ko: '한국어', + fr: 'Français', + de: 'Deutsch', + es: 'Español', + pt: 'Português', + th: 'ภาษาไทย', + 'zh-cn': '简体中文' + }; + return localeNames[code] || code; + }; + + // 로케일 ISO 코드 가져오기 + const getLocaleIso = (code: string): string => { + const localeIsos: Record = { + en: 'en', + 'zh-tw': 'zh-tw', + ja: 'ja', + ko: 'ko-KR', + fr: 'fr', + de: 'de', + es: 'es', + pt: 'pt', + th: 'th', + 'zh-cn': 'zh-cn' + }; + return localeIsos[code] || code; + }; + + // gameData가 설정될 때까지 기다리거나 즉시 실행 + const gameDataStore = useGameDataStore(); + + // gameData가 이미 설정되어 있으면 즉시 실행 + if (gameDataStore.gameData) { + updateI18nLocales(gameDataStore.gameData); + } + + // gameData가 변경될 때마다 실행 + watch(() => gameDataStore.gameData, async (newGameData) => { + if (newGameData) { + console.log("🚀 ~ gameData changed, updating i18n locales"); + await updateI18nLocales(newGameData); + } + }, { immediate: true }); +}); diff --git "a/\btemp/dynamic-i18n-runtime.server.ts" "b/\btemp/dynamic-i18n-runtime.server.ts" new file mode 100644 index 0000000..71fb514 --- /dev/null +++ "b/\btemp/dynamic-i18n-runtime.server.ts" @@ -0,0 +1,94 @@ +export default defineNuxtPlugin(async () => { + // 서버 사이드에서 gameData를 가져와서 i18n 설정 업데이트 + const { $i18n } = useNuxtApp(); + + // gameData에서 언어 코드 추출 + const getGameDataLangCodes = () => { + try { + // 서버 사이드에서 gameData 접근 + const nuxtApp = useNuxtApp(); + const gameData = nuxtApp.ssrContext?.event.context.gameData; + + if (gameData?.lang_codes) { + console.log("🚀 ~ dynamic-i18n-runtime.server ~ gameData.lang_codes:", gameData.lang_codes); + return Array.isArray(gameData.lang_codes) ? gameData.lang_codes : [gameData.lang_codes]; + } + } catch (error) { + console.warn('Failed to get gameData lang codes on server:', error); + } + + return ['ko']; // 기본값 + }; + + // i18n 설정 업데이트 + const updateI18nLocales = async () => { + const langCodes = getGameDataLangCodes(); + console.log("🚀 ~77777 updateI18nLocales ~ langCodes:", langCodes) + + if (langCodes && langCodes.length > 0) { + console.log("🚀 ~ dynamic-i18n-runtime.server ~ updating locales with:", langCodes); + + // 새로운 로케일 설정 생성 + const newLocales = langCodes.map(code => ({ + code, + file: `${code}.ts`, + name: getLocaleName(code), + iso: getLocaleIso(code), + dir: 'ltr' + })); + + // i18n 설정 업데이트 + if ($i18n) { + // 로케일 메시지 동적 로드 + for (const code of langCodes) { + try { + const messages = await import(`../../i18n/locales/${code}.ts`); + // defineI18nLocale 함수를 실행하여 실제 메시지 데이터 가져오기 + const localeMessages = await messages.default(code); + console.log(`🚀 ~ loaded messages for ${code}:`, localeMessages); + ($i18n as any).setLocaleMessage(code, localeMessages); + } catch (error) { + console.warn(`Failed to load locale messages for ${code}:`, error); + } + } + } + } + }; + + // 로케일 이름 가져오기 + const getLocaleName = (code: string): string => { + const localeNames: Record = { + en: 'English', + 'zh-tw': '繁體中文', + ja: '日本語', + ko: '한국어', + fr: 'Français', + de: 'Deutsch', + es: 'Español', + pt: 'Português', + th: 'ภาษาไทย', + 'zh-cn': '简体中文' + }; + return localeNames[code] || code; + }; + + // 로케일 ISO 코드 가져오기 + const getLocaleIso = (code: string): string => { + const localeIsos: Record = { + en: 'en', + 'zh-tw': 'zh-tw', + ja: 'ja', + ko: 'ko-KR', + fr: 'fr', + de: 'de', + es: 'es', + pt: 'pt', + th: 'th', + 'zh-cn': 'zh-cn' + }; + return localeIsos[code] || code; + }; + + // 서버 사이드에서 즉시 실행 + await updateI18nLocales(); +}); diff --git "a/\btemp/gameData.get.ts" "b/\btemp/gameData.get.ts" new file mode 100644 index 0000000..fbc26f3 --- /dev/null +++ "b/\btemp/gameData.get.ts" @@ -0,0 +1,58 @@ +import { getHeader } from "h3"; +import type { + GameDataResponse, + GameDataValue, +} from "#layers/types/api/gameData"; + +export default defineEventHandler(async (event) => { + const config = useRuntimeConfig(); + const baseDomain = (config.public.baseDomain || ".onstove.com") as string; + const stoveApiBaseUrl = config.public.stoveApiUrl; + const apiUrl = `${stoveApiBaseUrl}/pub-comm/v1.0/template/game`; + let gameAlias = ""; + + try { + // 미들웨어에서 설정한 gameAlias가 있다면 우선 사용 + if (event.context.gameAlias) { + gameAlias = event.context.gameAlias; + } else { + const host = getHeader(event, "host") || ""; + const isGameAliasExtractable = host.includes(baseDomain); + + if (isGameAliasExtractable) { + const subdomain = host.split(".")[0]; + if (subdomain && subdomain !== "www") { + gameAlias = subdomain; + } + } + } + } catch (error) { + console.log("gameAlias extraction error: ", error); + } + + try { + + const queryParams: Record = { + game_alias: gameAlias, + }; + + const response = await $fetch(apiUrl, { + query: queryParams, + }); + + if (response?.code === 0 && "value" in response) { + event.context.gameData = response.value; + + // lang_codes를 사용해서 동적으로 i18n 설정 업데이트 + if (response.value.lang_codes && Array.isArray(response.value.lang_codes)) { + event.context.availableLocales = response.value.lang_codes; + event.context.defaultLocale = response.value.default_lang_code || response.value.lang_codes[0] || 'ko'; + } + + return response.value as GameDataValue; + } + } catch (error) { + console.error(error); + return {}; + } +}); diff --git "a/\btemp/language-redirect.global.ts" "b/\btemp/language-redirect.global.ts" new file mode 100644 index 0000000..f8a5e22 --- /dev/null +++ "b/\btemp/language-redirect.global.ts" @@ -0,0 +1,40 @@ +export default defineNuxtRouteMiddleware(async (to, from) => { + // 서버 사이드에서만 실행 + if (import.meta.client) { + return; + } + + const gameDataStore = useGameDataStore(); + + // gameData가 로드되지 않았다면 gameData API 호출 + if (!gameDataStore.gameData) { + try { + await $fetch('/api/gameData'); + } catch (error) { + console.error('gameData 로드 실패:', error); + return; + } + } + + const availableLangCodes = gameDataStore.gameData?.lang_codes || ['ko']; + const defaultLangCode = gameDataStore.gameData?.default_lang_code || availableLangCodes[0]; + + // 현재 경로에서 언어 코드 추출 + const pathSegments = to.path.split('/').filter(Boolean); + const currentLangCode = pathSegments[0]; + + // 언어 코드가 유효한지 확인 + const isValidLangCode = availableLangCodes.includes(currentLangCode); + + // 유효하지 않은 언어 코드인 경우 기본 언어로 리다이렉트 + if (currentLangCode && !isValidLangCode) { + const newPath = `/${defaultLangCode}${to.path.replace(`/${currentLangCode}`, '')}`; + return navigateTo(newPath, { replace: true }); + } + + // 언어 코드가 없는 경우 기본 언어 코드 추가 + if (!currentLangCode || !availableLangCodes.includes(currentLangCode)) { + const newPath = `/${defaultLangCode}${to.path}`; + return navigateTo(newPath, { replace: true }); + } +}); diff --git a/DYNAMIC_I18N_ROUTES.md b/DYNAMIC_I18N_ROUTES.md new file mode 100644 index 0000000..056b74d --- /dev/null +++ b/DYNAMIC_I18N_ROUTES.md @@ -0,0 +1,120 @@ +# Dynamic I18n Routes - gameData.lang_codes 기반 언어 제외 설정 + +이 문서는 `gameDataFromServer.lang_codes`를 기반으로 특정 언어들을 제외하는 Ignoring Localized Routes 기능의 사용법을 설명합니다. + +## 구현된 기능 + +### 1. 동적 언어 제외 설정 +- `gameDataFromServer.lang_codes`에 포함된 언어만 허용 +- 포함되지 않은 언어는 자동으로 제외 처리 +- 런타임에 동적으로 언어 설정 변경 + +### 2. 구현된 파일들 + +#### `i18n.config.ts` +- `getI18n()` 함수에 `allowedLangCodes` 매개변수 추가 +- `generatePageExclusions()` 함수로 언어 제외 설정 생성 +- `customRoutes: 'config'` 설정으로 페이지별 언어 제외 지원 + +#### `layers/composables/useDynamicI18nRoutes.ts` +- `getAllowedLangCodes()`: 허용된 언어 목록 반환 +- `isLangAllowed()`: 특정 언어 허용 여부 확인 +- `getI18nRouteConfig()`: `defineI18nRoute`용 설정 생성 +- `getExcludedLangConfig()`: 특정 언어 제외 설정 생성 + +#### `layers/plugins/dynamic-i18n-routes.client.ts` +- 클라이언트 사이드에서 gameData 변경 감지 +- 언어 설정 동적 업데이트 + +#### `layers/plugins/dynamic-i18n-routes.server.ts` +- 서버 사이드에서 gameData 기반 언어 설정 적용 + +## 사용법 + +### 1. 페이지에서 동적 언어 제외 설정 + +```vue + +``` + +### 2. 특정 언어만 허용하는 경우 + +```vue + +``` + +### 3. 특정 언어를 제외하는 경우 + +```vue + +``` + +### 4. 언어 허용 여부 확인 + +```vue + +``` + +## 동작 원리 + +1. **서버 사이드**: `gameDataFromServer.lang_codes`를 기반으로 초기 언어 설정 적용 +2. **클라이언트 사이드**: gameData 변경 시 언어 설정 동적 업데이트 +3. **페이지 레벨**: `defineI18nRoute`를 통해 페이지별 언어 제외 설정 +4. **자동 리다이렉트**: 허용되지 않은 언어로 접근 시 기본 언어로 리다이렉트 + +## 예시 시나리오 + +### 시나리오 1: 게임별 언어 제한 +```typescript +// gameData.lang_codes = ['ko', 'en', 'ja'] +// 결과: 한국어, 영어, 일본어만 허용, 나머지 언어는 자동 제외 +``` + +### 시나리오 2: 특정 언어 제외 +```typescript +// gameData.lang_codes = ['ko', 'en', 'ja', 'zh-tw', 'fr', 'de', 'es', 'pt', 'th', 'zh-cn'] +// getExcludedLangConfig(['fr', 'de']) 호출 +// 결과: 프랑스어, 독일어 제외, 나머지 언어 허용 +``` + +## 주의사항 + +1. `defineI18nRoute`는 컴파일 타임에 실행되므로, 동적 설정이 필요한 경우 `watchEffect`나 `watch`를 사용 +2. SSR과 클라이언트 사이드 모두에서 일관된 동작을 위해 플러그인 사용 권장 +3. 언어 변경 시 사용자 경험을 고려한 적절한 리다이렉트 처리 필요 + +## 참고 문서 + +- [Nuxt i18n - Ignoring Localized Routes](https://i18n.nuxtjs.org/docs/guide/ignoring-localized-routes) +- [Nuxt i18n - defineI18nRoute](https://i18n.nuxtjs.org/docs/compiler-macros/define-i18n-route) diff --git a/app/app.vue b/app/app.vue index faed2b1..bd7008c 100644 --- a/app/app.vue +++ b/app/app.vue @@ -1,89 +1,58 @@ - - + + diff --git a/app/pages/index.vue b/app/pages/index.vue index d17c735..571b0c3 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -6,6 +6,9 @@ import Section from "#layers/components/molecules/Section.vue"; const pageDataStore = usePageDataStore(); const { pageData } = storeToRefs(pageDataStore); +// 동적 i18n 라우트 설정 +// const { getI18nRouteConfig } = useDynamicI18nRoutes(); + // const layout = pageData.value?.meta?.layout ?? "default"; const layout = "default"; // 기본 레이아웃 사용 @@ -14,6 +17,12 @@ definePageMeta({ layout: false, // 기본 레이아웃 비활성화 }); +// // gameData.lang_codes를 기반으로 동적 언어 제외 설정 +// const i18nRouteConfig = getI18nRouteConfig(); +// if (i18nRouteConfig) { +// defineI18nRoute(i18nRouteConfig); +// } + // SEO 메타 태그 설정 - pageData가 로드된 후에만 실행 watchEffect(() => { if (pageData.value?.meta_tag) { diff --git a/app/pages/test-lang.vue b/app/pages/test-lang.vue new file mode 100644 index 0000000..4ef2c54 --- /dev/null +++ b/app/pages/test-lang.vue @@ -0,0 +1,116 @@ + + + diff --git a/i18n.config.ts b/i18n.config.ts index d11971c..92a392b 100644 --- a/i18n.config.ts +++ b/i18n.config.ts @@ -1,15 +1,61 @@ -// import type { LocaleObject, NuxtI18nOptions } from "@nuxtjs/i18n"; -import type { NuxtI18nOptions } from "@nuxtjs/i18n"; +import type { LocaleObject, NuxtI18nOptions } from "@nuxtjs/i18n"; + +const LANG_DIR: string = "./i18n/locales"; + +const DEFAULT_COVERAGES: string[] = [ + "en", + "ja", + "ko", + "zh-tw", + "fr", + "de", + "es", + "pt", + "th", + "zh-cn", +]; + +const DEFAULT_LOCALES: Record = { + en: { code: "en", name: "English", iso: "en", dir: "ltr" }, + "zh-tw": { code: "zh-tw", name: "繁體中文", iso: "zh-tw", dir: "ltr" }, + ja: { code: "ja", name: "日本語", iso: "ja", dir: "ltr" }, + ko: { code: "ko", name: "한국어", iso: "ko-KR", dir: "ltr" }, + fr: { code: "fr", name: "Français", iso: "fr", dir: "ltr" }, + de: { code: "de", name: "Deutsch", iso: "de", dir: "ltr" }, + es: { code: "es", name: "Español", iso: "es", dir: "ltr" }, + pt: { code: "pt", name: "Português", iso: "pt", dir: "ltr" }, + th: { code: "th", name: "ภาษาไทย", iso: "th", dir: "ltr" }, + "zh-cn": { code: "zh-cn", name: "简体中文", iso: "zh-cn", dir: "ltr" }, +}; +const DEFAULT_LOCALE_CODE = "ko"; // getI18n 함수가 NuxtI18nOptions 타입의 값을 반환하도록 명시적으로 타입을 지정합니다. -const getI18n = (): NuxtI18nOptions => { +const getI18n = (allowedLangCodes?: string[]): NuxtI18nOptions => { + // allowedLangCodes가 제공되면 해당 언어들만 사용, 그렇지 않으면 모든 기본 언어 사용 + const targetCoverages = + allowedLangCodes && allowedLangCodes.length > 0 + ? DEFAULT_COVERAGES.filter((code) => allowedLangCodes.includes(code)) + : DEFAULT_COVERAGES; + + const DEFAULT_LOCALE_COVERAGES_SET: LocaleObject[] = targetCoverages.map( + (code: string): LocaleObject => ({ + code, + file: `${code}.ts`, + // 아래의 옵셔널 체이닝(?.)은 해당 코드가 undefined일 경우를 안전하게 처리합니다. + name: DEFAULT_LOCALES[code]?.name ?? code, + iso: DEFAULT_LOCALES[code]?.iso ?? code, + // dir 속성은 모든 로케일 객체에 공통적으로 존재하므로, 여기서 추가할 수도 있습니다. + dir: DEFAULT_LOCALES[code]?.dir ?? "ltr", + }) + ); + return { strategy: "prefix", vueI18n: "custom", - locales: ["ko"], - defaultLocale: "ko", + locales: DEFAULT_LOCALE_COVERAGES_SET, + defaultLocale: DEFAULT_LOCALE_CODE || "ko", detectBrowserLanguage: { - fallbackLocale: "ko", + fallbackLocale: DEFAULT_LOCALE_CODE || "ko", useCookie: false, redirectOn: "root", }, @@ -18,7 +64,31 @@ const getI18n = (): NuxtI18nOptions => { escapeHtml: false, }, debug: false, + // 동적으로 언어 제외 설정을 위한 pages 설정 + customRoutes: "config", + pages: + allowedLangCodes && allowedLangCodes.length > 0 + ? generatePageExclusions(allowedLangCodes) + : {}, + // 추가적인 설정이 필요하다면 여기에 포함시킬 수 있습니다. }; }; -export { getI18n }; +// gameData.lang_codes를 기반으로 언어 제외 설정을 생성하는 함수 +const generatePageExclusions = ( + allowedLangCodes: string[] +): Record => { + const exclusions: Record = {}; + + // 모든 기본 언어에 대해 제외 설정 생성 + DEFAULT_COVERAGES.forEach((langCode) => { + if (!allowedLangCodes.includes(langCode)) { + // 해당 언어가 허용되지 않으면 모든 페이지에서 제외 + exclusions[langCode] = false; + } + }); + + return exclusions; +}; + +export { DEFAULT_LOCALE_CODE, DEFAULT_COVERAGES, getI18n }; diff --git a/i18n/locales/de.ts b/i18n/locales/de.ts new file mode 100644 index 0000000..a40148b --- /dev/null +++ b/i18n/locales/de.ts @@ -0,0 +1,54 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + const config = useRuntimeConfig() + const baseType = config.public.baseType + const translationItems = config.public.translationItems + const translationItemsArr = translationItems.split(',') + const staticUrl = config.public.staticUrl + const translationApi = translationItemsArr.map((item: string): string => { + return `${staticUrl}/${baseType}/tmp/${item}.json` + }) + + // API 데이터 가져오기 + const fetchDataPromises = translationApi.map((apiUrl) => { + return useFetch(apiUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } + }) + }) + + try { + const fetchResults = await Promise.all(fetchDataPromises) + + // 각 결과에서 locale에 맞는 데이터를 추출 + const apiData = fetchResults.map((result) => { + return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + }) + + // apiData를 이용해 자동으로 병합 + const mergedResult = apiData.reduce((acc, data) => { + return { ...acc, ...data } + }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + const finalResult = { ...mergedResult, ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/i18n/locales/en.ts b/i18n/locales/en.ts new file mode 100644 index 0000000..c9ce1cd --- /dev/null +++ b/i18n/locales/en.ts @@ -0,0 +1,62 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + // const config = useRuntimeConfig() + // const baseType = config.public.baseType + // const translationItems = config.public.translationItems + // const translationItemsArr = translationItems.split(',') + // const staticUrl = config.public.staticUrl + // const translationPaths = translationItemsArr.map((item) => { + // 경로를 생성하며 ~/assets/data 경로로 설정 + // return `~/assets/data/${item}.json` + // }) + // const resources = await Promise.all(translationPaths.map((path) => import(`${path}`))) + + // console.log('translationLocal ~ translationLocal:', translationLocal) + // const translationApi = translationItemsArr.map((item: string): string => { + // return `${staticUrl}/${baseType}/tmp/${item}.json` + // }) + + // // API 데이터 가져오기 + // const fetchDataPromises = translationApi.map((apiUrl) => { + // return useFetch(apiUrl, { + // method: 'GET', + // headers: { + // 'Content-Type': 'application/json;charset=UTF-8' + // } + // }) + // }) + + try { + // const fetchResults = await Promise.all(fetchDataPromises) + + // // 각 결과에서 locale에 맞는 데이터를 추출 + // const apiData = fetchResults.map((result) => { + // return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + // }) + + // // apiData를 이용해 자동으로 병합 + // const mergedResult = apiData.reduce((acc, data) => { + // return { ...acc, ...data } + // }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + // const finalResult = { ...mergedResult, ...commonLocaleData } + const finalResult = { ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/i18n/locales/es.ts b/i18n/locales/es.ts new file mode 100644 index 0000000..a40148b --- /dev/null +++ b/i18n/locales/es.ts @@ -0,0 +1,54 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + const config = useRuntimeConfig() + const baseType = config.public.baseType + const translationItems = config.public.translationItems + const translationItemsArr = translationItems.split(',') + const staticUrl = config.public.staticUrl + const translationApi = translationItemsArr.map((item: string): string => { + return `${staticUrl}/${baseType}/tmp/${item}.json` + }) + + // API 데이터 가져오기 + const fetchDataPromises = translationApi.map((apiUrl) => { + return useFetch(apiUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } + }) + }) + + try { + const fetchResults = await Promise.all(fetchDataPromises) + + // 각 결과에서 locale에 맞는 데이터를 추출 + const apiData = fetchResults.map((result) => { + return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + }) + + // apiData를 이용해 자동으로 병합 + const mergedResult = apiData.reduce((acc, data) => { + return { ...acc, ...data } + }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + const finalResult = { ...mergedResult, ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/i18n/locales/fallback/de.ts b/i18n/locales/fallback/de.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/de.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/en.ts b/i18n/locales/fallback/en.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/en.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/es.ts b/i18n/locales/fallback/es.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/es.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/fr.ts b/i18n/locales/fallback/fr.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/fr.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/it.ts b/i18n/locales/fallback/it.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/it.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/ja.ts b/i18n/locales/fallback/ja.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/ja.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/ko.ts b/i18n/locales/fallback/ko.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/ko.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/pt.ts b/i18n/locales/fallback/pt.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/pt.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/th.ts b/i18n/locales/fallback/th.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/th.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/zh-cn.ts b/i18n/locales/fallback/zh-cn.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/zh-cn.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fallback/zh-tw.ts b/i18n/locales/fallback/zh-tw.ts new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/i18n/locales/fallback/zh-tw.ts @@ -0,0 +1 @@ +export default {} diff --git a/i18n/locales/fr.ts b/i18n/locales/fr.ts new file mode 100644 index 0000000..a40148b --- /dev/null +++ b/i18n/locales/fr.ts @@ -0,0 +1,54 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + const config = useRuntimeConfig() + const baseType = config.public.baseType + const translationItems = config.public.translationItems + const translationItemsArr = translationItems.split(',') + const staticUrl = config.public.staticUrl + const translationApi = translationItemsArr.map((item: string): string => { + return `${staticUrl}/${baseType}/tmp/${item}.json` + }) + + // API 데이터 가져오기 + const fetchDataPromises = translationApi.map((apiUrl) => { + return useFetch(apiUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } + }) + }) + + try { + const fetchResults = await Promise.all(fetchDataPromises) + + // 각 결과에서 locale에 맞는 데이터를 추출 + const apiData = fetchResults.map((result) => { + return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + }) + + // apiData를 이용해 자동으로 병합 + const mergedResult = apiData.reduce((acc, data) => { + return { ...acc, ...data } + }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + const finalResult = { ...mergedResult, ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/i18n/locales/ja.ts b/i18n/locales/ja.ts new file mode 100644 index 0000000..c9ce1cd --- /dev/null +++ b/i18n/locales/ja.ts @@ -0,0 +1,62 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + // const config = useRuntimeConfig() + // const baseType = config.public.baseType + // const translationItems = config.public.translationItems + // const translationItemsArr = translationItems.split(',') + // const staticUrl = config.public.staticUrl + // const translationPaths = translationItemsArr.map((item) => { + // 경로를 생성하며 ~/assets/data 경로로 설정 + // return `~/assets/data/${item}.json` + // }) + // const resources = await Promise.all(translationPaths.map((path) => import(`${path}`))) + + // console.log('translationLocal ~ translationLocal:', translationLocal) + // const translationApi = translationItemsArr.map((item: string): string => { + // return `${staticUrl}/${baseType}/tmp/${item}.json` + // }) + + // // API 데이터 가져오기 + // const fetchDataPromises = translationApi.map((apiUrl) => { + // return useFetch(apiUrl, { + // method: 'GET', + // headers: { + // 'Content-Type': 'application/json;charset=UTF-8' + // } + // }) + // }) + + try { + // const fetchResults = await Promise.all(fetchDataPromises) + + // // 각 결과에서 locale에 맞는 데이터를 추출 + // const apiData = fetchResults.map((result) => { + // return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + // }) + + // // apiData를 이용해 자동으로 병합 + // const mergedResult = apiData.reduce((acc, data) => { + // return { ...acc, ...data } + // }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + // const finalResult = { ...mergedResult, ...commonLocaleData } + const finalResult = { ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/i18n/locales/ko.ts b/i18n/locales/ko.ts new file mode 100644 index 0000000..c9ce1cd --- /dev/null +++ b/i18n/locales/ko.ts @@ -0,0 +1,62 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + // const config = useRuntimeConfig() + // const baseType = config.public.baseType + // const translationItems = config.public.translationItems + // const translationItemsArr = translationItems.split(',') + // const staticUrl = config.public.staticUrl + // const translationPaths = translationItemsArr.map((item) => { + // 경로를 생성하며 ~/assets/data 경로로 설정 + // return `~/assets/data/${item}.json` + // }) + // const resources = await Promise.all(translationPaths.map((path) => import(`${path}`))) + + // console.log('translationLocal ~ translationLocal:', translationLocal) + // const translationApi = translationItemsArr.map((item: string): string => { + // return `${staticUrl}/${baseType}/tmp/${item}.json` + // }) + + // // API 데이터 가져오기 + // const fetchDataPromises = translationApi.map((apiUrl) => { + // return useFetch(apiUrl, { + // method: 'GET', + // headers: { + // 'Content-Type': 'application/json;charset=UTF-8' + // } + // }) + // }) + + try { + // const fetchResults = await Promise.all(fetchDataPromises) + + // // 각 결과에서 locale에 맞는 데이터를 추출 + // const apiData = fetchResults.map((result) => { + // return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + // }) + + // // apiData를 이용해 자동으로 병합 + // const mergedResult = apiData.reduce((acc, data) => { + // return { ...acc, ...data } + // }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + // const finalResult = { ...mergedResult, ...commonLocaleData } + const finalResult = { ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/i18n/locales/pt.ts b/i18n/locales/pt.ts new file mode 100644 index 0000000..a40148b --- /dev/null +++ b/i18n/locales/pt.ts @@ -0,0 +1,54 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + const config = useRuntimeConfig() + const baseType = config.public.baseType + const translationItems = config.public.translationItems + const translationItemsArr = translationItems.split(',') + const staticUrl = config.public.staticUrl + const translationApi = translationItemsArr.map((item: string): string => { + return `${staticUrl}/${baseType}/tmp/${item}.json` + }) + + // API 데이터 가져오기 + const fetchDataPromises = translationApi.map((apiUrl) => { + return useFetch(apiUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } + }) + }) + + try { + const fetchResults = await Promise.all(fetchDataPromises) + + // 각 결과에서 locale에 맞는 데이터를 추출 + const apiData = fetchResults.map((result) => { + return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + }) + + // apiData를 이용해 자동으로 병합 + const mergedResult = apiData.reduce((acc, data) => { + return { ...acc, ...data } + }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + const finalResult = { ...mergedResult, ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/i18n/locales/th.ts b/i18n/locales/th.ts new file mode 100644 index 0000000..a40148b --- /dev/null +++ b/i18n/locales/th.ts @@ -0,0 +1,54 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + const config = useRuntimeConfig() + const baseType = config.public.baseType + const translationItems = config.public.translationItems + const translationItemsArr = translationItems.split(',') + const staticUrl = config.public.staticUrl + const translationApi = translationItemsArr.map((item: string): string => { + return `${staticUrl}/${baseType}/tmp/${item}.json` + }) + + // API 데이터 가져오기 + const fetchDataPromises = translationApi.map((apiUrl) => { + return useFetch(apiUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } + }) + }) + + try { + const fetchResults = await Promise.all(fetchDataPromises) + + // 각 결과에서 locale에 맞는 데이터를 추출 + const apiData = fetchResults.map((result) => { + return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + }) + + // apiData를 이용해 자동으로 병합 + const mergedResult = apiData.reduce((acc, data) => { + return { ...acc, ...data } + }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + const finalResult = { ...mergedResult, ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/i18n/locales/zh-cn.ts b/i18n/locales/zh-cn.ts new file mode 100644 index 0000000..c9ce1cd --- /dev/null +++ b/i18n/locales/zh-cn.ts @@ -0,0 +1,62 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + // const config = useRuntimeConfig() + // const baseType = config.public.baseType + // const translationItems = config.public.translationItems + // const translationItemsArr = translationItems.split(',') + // const staticUrl = config.public.staticUrl + // const translationPaths = translationItemsArr.map((item) => { + // 경로를 생성하며 ~/assets/data 경로로 설정 + // return `~/assets/data/${item}.json` + // }) + // const resources = await Promise.all(translationPaths.map((path) => import(`${path}`))) + + // console.log('translationLocal ~ translationLocal:', translationLocal) + // const translationApi = translationItemsArr.map((item: string): string => { + // return `${staticUrl}/${baseType}/tmp/${item}.json` + // }) + + // // API 데이터 가져오기 + // const fetchDataPromises = translationApi.map((apiUrl) => { + // return useFetch(apiUrl, { + // method: 'GET', + // headers: { + // 'Content-Type': 'application/json;charset=UTF-8' + // } + // }) + // }) + + try { + // const fetchResults = await Promise.all(fetchDataPromises) + + // // 각 결과에서 locale에 맞는 데이터를 추출 + // const apiData = fetchResults.map((result) => { + // return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + // }) + + // // apiData를 이용해 자동으로 병합 + // const mergedResult = apiData.reduce((acc, data) => { + // return { ...acc, ...data } + // }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + // const finalResult = { ...mergedResult, ...commonLocaleData } + const finalResult = { ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/i18n/locales/zh-tw.ts b/i18n/locales/zh-tw.ts new file mode 100644 index 0000000..c9ce1cd --- /dev/null +++ b/i18n/locales/zh-tw.ts @@ -0,0 +1,62 @@ +// import { TRANSLATION_ITEMS } from '../i18n.config' +// common.json 파일을 직접 import +// @ts-ignore +import commonData from '../../layers/assets/data/common.json' + +export default defineI18nLocale(async (locale: string) => { + // const config = useRuntimeConfig() + // const baseType = config.public.baseType + // const translationItems = config.public.translationItems + // const translationItemsArr = translationItems.split(',') + // const staticUrl = config.public.staticUrl + // const translationPaths = translationItemsArr.map((item) => { + // 경로를 생성하며 ~/assets/data 경로로 설정 + // return `~/assets/data/${item}.json` + // }) + // const resources = await Promise.all(translationPaths.map((path) => import(`${path}`))) + + // console.log('translationLocal ~ translationLocal:', translationLocal) + // const translationApi = translationItemsArr.map((item: string): string => { + // return `${staticUrl}/${baseType}/tmp/${item}.json` + // }) + + // // API 데이터 가져오기 + // const fetchDataPromises = translationApi.map((apiUrl) => { + // return useFetch(apiUrl, { + // method: 'GET', + // headers: { + // 'Content-Type': 'application/json;charset=UTF-8' + // } + // }) + // }) + + try { + // const fetchResults = await Promise.all(fetchDataPromises) + + // // 각 결과에서 locale에 맞는 데이터를 추출 + // const apiData = fetchResults.map((result) => { + // return result.data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 + // }) + + // // apiData를 이용해 자동으로 병합 + // const mergedResult = apiData.reduce((acc, data) => { + // return { ...acc, ...data } + // }, {}) + + // common.json에서 해당 locale의 데이터를 가져와서 병합 + const commonLocaleData = commonData[locale] || {} + + // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) + // const finalResult = { ...mergedResult, ...commonLocaleData } + const finalResult = { ...commonLocaleData } + + // 병합된 결과 출력 + // console.log('finalResult:', finalResult) + + return finalResult + } catch (error) { + console.error('Error fetching translation data:', error) + // 에러 발생 시 common.json 데이터라도 반환 + return commonData[locale] || {} + } +}) diff --git a/layers/assets/data/common.json b/layers/assets/data/common.json new file mode 100644 index 0000000..fdc5983 --- /dev/null +++ b/layers/assets/data/common.json @@ -0,0 +1,514 @@ +{ + "ko": { + "common": { + "loading": "로딩 중...", + "error": "오류가 발생했습니다", + "success": "성공했습니다", + "cancel": "취소", + "confirm": "확인", + "save": "저장", + "delete": "삭제", + "edit": "편집", + "close": "닫기", + "back": "뒤로", + "next": "다음", + "previous": "이전", + "search": "검색", + "filter": "필터", + "sort": "정렬", + "refresh": "새로고침", + "download": "다운로드", + "upload": "업로드", + "copy": "복사", + "paste": "붙여넣기", + "cut": "잘라내기", + "undo": "실행 취소", + "redo": "다시 실행" + }, + "navigation": { + "home": "홈", + "about": "소개", + "contact": "연락처", + "services": "서비스", + "products": "제품", + "news": "뉴스", + "support": "지원", + "login": "로그인", + "logout": "로그아웃", + "register": "회원가입", + "profile": "프로필", + "settings": "설정" + }, + "messages": { + "title_test_lang": "언어 설정 테스트!", + "welcome": "환영합니다!", + "GameData_load_status": "GameData 로드 상태", + "current_language": "현재 언어", + "default_language": "기본 언어", + "available_languages": "사용 가능한 언어", + "current_url": "현재 URL", + "no_results": "검색 결과가 없습니다", + "try_again": "다시 시도해주세요" + } + }, + "en": { + "common": { + "loading": "Loading...", + "error": "An error occurred", + "success": "Success", + "cancel": "Cancel", + "confirm": "Confirm", + "save": "Save", + "delete": "Delete", + "edit": "Edit", + "close": "Close", + "back": "Back", + "next": "Next", + "previous": "Previous", + "search": "Search", + "filter": "Filter", + "sort": "Sort", + "refresh": "Refresh", + "download": "Download", + "upload": "Upload", + "copy": "Copy", + "paste": "Paste", + "cut": "Cut", + "undo": "Undo", + "redo": "Redo" + }, + "navigation": { + "home": "Home", + "about": "About", + "contact": "Contact", + "services": "Services", + "products": "Products", + "news": "News", + "support": "Support", + "login": "Login", + "logout": "Logout", + "register": "Register", + "profile": "Profile", + "settings": "Settings" + }, + "messages": { + "welcome": "Welcome!", + "goodbye": "Goodbye", + "thank_you": "Thank you", + "sorry": "Sorry", + "please_wait": "Please wait", + "no_data": "No data available", + "no_results": "No results found", + "try_again": "Please try again" + } + }, + "zh-tw": { + "common": { + "loading": "載入中...", + "error": "發生錯誤", + "success": "成功", + "cancel": "取消", + "confirm": "確認", + "save": "儲存", + "delete": "刪除", + "edit": "編輯", + "close": "關閉", + "back": "返回", + "next": "下一步", + "previous": "上一步", + "search": "搜尋", + "filter": "篩選", + "sort": "排序", + "refresh": "重新整理", + "download": "下載", + "upload": "上傳", + "copy": "複製", + "paste": "貼上", + "cut": "剪下", + "undo": "復原", + "redo": "重做" + }, + "navigation": { + "home": "首頁", + "about": "關於我們", + "contact": "聯絡我們", + "services": "服務", + "products": "產品", + "news": "新聞", + "support": "支援", + "login": "登入", + "logout": "登出", + "register": "註冊", + "profile": "個人資料", + "settings": "設定" + }, + "messages": { + "title_test_lang": "語言設定測試!", + "welcome": "歡迎!", + "GameData_load_status": "GameData 載入狀態", + "current_language": "目前語言", + "default_language": "預設語言", + "available_languages": "可用語言", + "current_url": "目前網址", + "no_results": "找不到結果", + "try_again": "請再試一次" + } + }, + "ja": { + "common": { + "loading": "読み込み中...", + "error": "エラーが発生しました", + "success": "成功", + "cancel": "キャンセル", + "confirm": "確認", + "save": "保存", + "delete": "削除", + "edit": "編集", + "close": "閉じる", + "back": "戻る", + "next": "次へ", + "previous": "前へ", + "search": "検索", + "filter": "フィルター", + "sort": "並び替え", + "refresh": "更新", + "download": "ダウンロード", + "upload": "アップロード", + "copy": "コピー", + "paste": "貼り付け", + "cut": "切り取り", + "undo": "元に戻す", + "redo": "やり直し" + }, + "navigation": { + "home": "ホーム", + "about": "概要", + "contact": "お問い合わせ", + "services": "サービス", + "products": "製品", + "news": "ニュース", + "support": "サポート", + "login": "ログイン", + "logout": "ログアウト", + "register": "登録", + "profile": "プロフィール", + "settings": "設定" + }, + "messages": { + "welcome": "ようこそ!", + "goodbye": "さようなら", + "thank_you": "ありがとうございます", + "sorry": "申し訳ありません", + "please_wait": "お待ちください", + "no_data": "データがありません", + "no_results": "結果が見つかりません", + "try_again": "もう一度お試しください" + } + }, + "fr": { + "common": { + "loading": "Chargement...", + "error": "Une erreur s'est produite", + "success": "Succès", + "cancel": "Annuler", + "confirm": "Confirmer", + "save": "Enregistrer", + "delete": "Supprimer", + "edit": "Modifier", + "close": "Fermer", + "back": "Retour", + "next": "Suivant", + "previous": "Précédent", + "search": "Rechercher", + "filter": "Filtrer", + "sort": "Trier", + "refresh": "Actualiser", + "download": "Télécharger", + "upload": "Téléverser", + "copy": "Copier", + "paste": "Coller", + "cut": "Couper", + "undo": "Annuler", + "redo": "Rétablir" + }, + "navigation": { + "home": "Accueil", + "about": "À propos", + "contact": "Contact", + "services": "Services", + "products": "Produits", + "news": "Actualités", + "support": "Support", + "login": "Connexion", + "logout": "Déconnexion", + "register": "S'inscrire", + "profile": "Profil", + "settings": "Paramètres" + }, + "messages": { + "welcome": "Bienvenue !", + "goodbye": "Au revoir", + "thank_you": "Merci", + "sorry": "Désolé", + "please_wait": "Veuillez patienter", + "no_data": "Aucune donnée disponible", + "no_results": "Aucun résultat trouvé", + "try_again": "Veuillez réessayer" + } + }, + "de": { + "common": { + "loading": "Laden...", + "error": "Ein Fehler ist aufgetreten", + "success": "Erfolg", + "cancel": "Abbrechen", + "confirm": "Bestätigen", + "save": "Speichern", + "delete": "Löschen", + "edit": "Bearbeiten", + "close": "Schließen", + "back": "Zurück", + "next": "Weiter", + "previous": "Vorherige", + "search": "Suchen", + "filter": "Filtern", + "sort": "Sortieren", + "refresh": "Aktualisieren", + "download": "Herunterladen", + "upload": "Hochladen", + "copy": "Kopieren", + "paste": "Einfügen", + "cut": "Ausschneiden", + "undo": "Rückgängig", + "redo": "Wiederholen" + }, + "navigation": { + "home": "Startseite", + "about": "Über uns", + "contact": "Kontakt", + "services": "Dienstleistungen", + "products": "Produkte", + "news": "Nachrichten", + "support": "Support", + "login": "Anmelden", + "logout": "Abmelden", + "register": "Registrieren", + "profile": "Profil", + "settings": "Einstellungen" + }, + "messages": { + "welcome": "Willkommen!", + "goodbye": "Auf Wiedersehen", + "thank_you": "Danke", + "sorry": "Entschuldigung", + "please_wait": "Bitte warten", + "no_data": "Keine Daten verfügbar", + "no_results": "Keine Ergebnisse gefunden", + "try_again": "Bitte versuchen Sie es erneut" + } + }, + "es": { + "common": { + "loading": "Cargando...", + "error": "Ocurrió un error", + "success": "Éxito", + "cancel": "Cancelar", + "confirm": "Confirmar", + "save": "Guardar", + "delete": "Eliminar", + "edit": "Editar", + "close": "Cerrar", + "back": "Atrás", + "next": "Siguiente", + "previous": "Anterior", + "search": "Buscar", + "filter": "Filtrar", + "sort": "Ordenar", + "refresh": "Actualizar", + "download": "Descargar", + "upload": "Subir", + "copy": "Copiar", + "paste": "Pegar", + "cut": "Cortar", + "undo": "Deshacer", + "redo": "Rehacer" + }, + "navigation": { + "home": "Inicio", + "about": "Acerca de", + "contact": "Contacto", + "services": "Servicios", + "products": "Productos", + "news": "Noticias", + "support": "Soporte", + "login": "Iniciar sesión", + "logout": "Cerrar sesión", + "register": "Registrarse", + "profile": "Perfil", + "settings": "Configuración" + }, + "messages": { + "welcome": "¡Bienvenido!", + "goodbye": "Adiós", + "thank_you": "Gracias", + "sorry": "Lo siento", + "please_wait": "Por favor espere", + "no_data": "No hay datos disponibles", + "no_results": "No se encontraron resultados", + "try_again": "Por favor intente de nuevo" + } + }, + "pt": { + "common": { + "loading": "Carregando...", + "error": "Ocorreu um erro", + "success": "Sucesso", + "cancel": "Cancelar", + "confirm": "Confirmar", + "save": "Salvar", + "delete": "Excluir", + "edit": "Editar", + "close": "Fechar", + "back": "Voltar", + "next": "Próximo", + "previous": "Anterior", + "search": "Pesquisar", + "filter": "Filtrar", + "sort": "Ordenar", + "refresh": "Atualizar", + "download": "Baixar", + "upload": "Enviar", + "copy": "Copiar", + "paste": "Colar", + "cut": "Cortar", + "undo": "Desfazer", + "redo": "Refazer" + }, + "navigation": { + "home": "Início", + "about": "Sobre", + "contact": "Contato", + "services": "Serviços", + "products": "Produtos", + "news": "Notícias", + "support": "Suporte", + "login": "Entrar", + "logout": "Sair", + "register": "Registrar", + "profile": "Perfil", + "settings": "Configurações" + }, + "messages": { + "welcome": "Bem-vindo!", + "goodbye": "Tchau", + "thank_you": "Obrigado", + "sorry": "Desculpe", + "please_wait": "Por favor aguarde", + "no_data": "Nenhum dado disponível", + "no_results": "Nenhum resultado encontrado", + "try_again": "Por favor tente novamente" + } + }, + "th": { + "common": { + "loading": "กำลังโหลด...", + "error": "เกิดข้อผิดพลาด", + "success": "สำเร็จ", + "cancel": "ยกเลิก", + "confirm": "ยืนยัน", + "save": "บันทึก", + "delete": "ลบ", + "edit": "แก้ไข", + "close": "ปิด", + "back": "กลับ", + "next": "ถัดไป", + "previous": "ก่อนหน้า", + "search": "ค้นหา", + "filter": "กรอง", + "sort": "เรียงลำดับ", + "refresh": "รีเฟรช", + "download": "ดาวน์โหลด", + "upload": "อัปโหลด", + "copy": "คัดลอก", + "paste": "วาง", + "cut": "ตัด", + "undo": "เลิกทำ", + "redo": "ทำซ้ำ" + }, + "navigation": { + "home": "หน้าแรก", + "about": "เกี่ยวกับ", + "contact": "ติดต่อ", + "services": "บริการ", + "products": "ผลิตภัณฑ์", + "news": "ข่าวสาร", + "support": "สนับสนุน", + "login": "เข้าสู่ระบบ", + "logout": "ออกจากระบบ", + "register": "สมัครสมาชิก", + "profile": "โปรไฟล์", + "settings": "การตั้งค่า" + }, + "messages": { + "welcome": "ยินดีต้อนรับ!", + "goodbye": "ลาก่อน", + "thank_you": "ขอบคุณ", + "sorry": "ขออภัย", + "please_wait": "กรุณารอสักครู่", + "no_data": "ไม่มีข้อมูล", + "no_results": "ไม่พบผลลัพธ์", + "try_again": "กรุณาลองใหม่อีกครั้ง" + } + }, + "zh-cn": { + "common": { + "loading": "加载中...", + "error": "发生错误", + "success": "成功", + "cancel": "取消", + "confirm": "确认", + "save": "保存", + "delete": "删除", + "edit": "编辑", + "close": "关闭", + "back": "返回", + "next": "下一步", + "previous": "上一步", + "search": "搜索", + "filter": "筛选", + "sort": "排序", + "refresh": "刷新", + "download": "下载", + "upload": "上传", + "copy": "复制", + "paste": "粘贴", + "cut": "剪切", + "undo": "撤销", + "redo": "重做" + }, + "navigation": { + "home": "首页", + "about": "关于我们", + "contact": "联系我们", + "services": "服务", + "products": "产品", + "news": "新闻", + "support": "支持", + "login": "登录", + "logout": "退出", + "register": "注册", + "profile": "个人资料", + "settings": "设置" + }, + "messages": { + "welcome": "欢迎!", + "goodbye": "再见", + "thank_you": "谢谢", + "sorry": "抱歉", + "please_wait": "请稍候", + "no_data": "无可用数据", + "no_results": "未找到结果", + "try_again": "请重试" + } + } +} diff --git a/layers/components/atoms/LanguageSwitcher.vue b/layers/components/atoms/LanguageSwitcher.vue index 4f391eb..cd8c2c7 100644 --- a/layers/components/atoms/LanguageSwitcher.vue +++ b/layers/components/atoms/LanguageSwitcher.vue @@ -1,41 +1,52 @@ - - - +const { locale } = useI18n(); +const switchLocalePath = useSwitchLocalePath(); +const router = useRouter(); + +const selectedLocale = ref(locale.value); + +// 언어 변경 함수 +const switchLanguage = async () => { + console.log( + "🚀 ~ switchLanguage ~ selectedLocale.value:", + selectedLocale.value + ); + if (selectedLocale.value) { + // URL 경로를 통해 언어 변경 + const path = switchLocalePath(selectedLocale.value); + if (path) { + await router.push(path); + } + } +}; + +// locale이 변경될 때 selectedLocale도 동기화 +watch(locale, (newLocale) => { + selectedLocale.value = newLocale; +}); + diff --git a/layers/components/layout/default/Footer.vue b/layers/components/layout/default/Footer.vue index 5dc273c..2ba2483 100644 --- a/layers/components/layout/default/Footer.vue +++ b/layers/components/layout/default/Footer.vue @@ -1,3 +1,12 @@ diff --git a/layers/components/layout/default/Header.vue b/layers/components/layout/default/Header.vue index 1510b60..042cfe4 100644 --- a/layers/components/layout/default/Header.vue +++ b/layers/components/layout/default/Header.vue @@ -4,12 +4,16 @@ const gameData = computed(() => gameDataStore.gameData);