다국어 적용 진행 과정을 간단하게 기록한다.
1. 기본 다국어 처리
기본적인 다국어 처리
vue-i18n문서와 블로그 를 참고하여 진행했다.
- npm 설치
npm install vue-i18n@9
npm i --save-dev @intlify/unplugin-vue-i18n
- 다국어 파일
// ko.json
{
"changeLocale": "지역변경",
"title": "제목",
"doc": "문서",
"tooling": "다듬다",
"ecosystem": "에코시스템",
"community": "커뮤니티",
"support": "지원",
"youdidit": "해냈구나!",
"route": {
"home": "홈",
"about": "어바웃"
},
"test": "테스트"
}
// en.json
{
"changeLocale": "change Locale",
"title": "Title",
"doc": "Documentation",
"tooling": "Tooling",
"ecosystem": "Ecosystem",
"community": "Community",
"support": "Support Vue",
"youdidit": "You did it!",
"route": {
"home": "Home",
"about": "About"
},
"test": "Test"
}
- 설정
// i18n/index.js
import ko from "@/locales/ko.json"
import en from "@/locales/en.json"
import { createI18n } from "vue-i18n";
const i18n = createI18n({
locale: import.meta.env.VITE_DEFAULT_LOCALE,
fallbackLocale: import.meta.env.VITE_FALLBACK_LOCALE,
legacy: false,
messages: {
ko,
en
}
})
export default i18n;
- 기본 활용1 (template 내부 활용)
// App.vue
<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="youdidit" />
<nav>
<RouterLink to="/">{{ $t('route.home') }}</RouterLink>
<RouterLink to="/about">{{ $t('route.about') }}</RouterLink>
</nav>
</div>
</header>
<RouterView />
</template>
- 기본 활용2 (template 및 js 활용)
// TheWelcome.vue
<script setup>
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
import {useI18n} from "vue-i18n";
const { locale } = useI18n({ useScope: 'global' })
const i18n = useI18n();
const changeLocale = () =>{
locale.value = locale.value === "en" ? "ko": "en";
}
</script>
<template>
<div>
<button @click="changeLocale">{{ $t('changeLocale') }}</button>
</div>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>{{ $t('doc') }}</template>
// 줄임
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>{{ $t('tooling') }}</template>
// 줄임
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>{{ $t('ecosystem') }}</template>
// 줄임
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>{{ $t('community') }}</template>
// 줄임
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading> {{ $t('support') }}</template>
// 줄임
</WelcomeItem>
</template>
2. 심화 다국어 처리
페이지 타이틀 동적 처리를 위한 라우터 처리
- Router Page Title 처리
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import {nextTick, computed} from "vue";
import i18n from "../i18n";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: () => import('../views/HomeView.vue'),
meta:{title: 'home'}
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue'),
meta:{title:'about'}
}
]
})
const DEFAULT_TITLE = 'TEST';
router.afterEach(to => {
const title = to.meta.title ? i18n.global.t('route.'+to.meta.title) : DEFAULT_TITLE;
nextTick(()=> document.title = title)
})
export default router
3. 구글 스프레드 시트와 연동하여 다국어 파일 자동화 처리
협업이 가능한 다국어 파일 생성
google-spreadsheet 문서와 블로그를 참고하여 진행했다.
- 스프레드 시트 생성 및 용어 정리
npm test
시트1 ko,en,ja,key,key1,key2,key3,key4,key5,key6,key7 지역변경,change Locale,changeLocale,changeLocale 제목,Title,title,title 문서,Documentation,doc,doc 다듬다,Tooling,tooling,tooling 에코시스템,Ecosystem,ecosystem,ecosystem 커뮤니티,
docs.google.com
- npm 설치
npm i google-spreadsheet
- 설정 파일
구글에서 제공한 문서를 통해 인증 절차를 거치거나 시트 권한 설정 없이 진행 가능
// translationmodules/index.js
const {GoogleSpreadsheet} = require('google-spreadsheet');
const {JWT} = require('google-auth-library')
// Google Developers Console 에서 추가한 서비스어카운트의 Credential 파일 (이 파일은 .gitignore 처리)
const credentials = require('../src/assets/config/swift-reef-398705-e0f0b6e8a1c5.json');
// 번역 파일을 저장할 상위 디렉토리
const localesPath = 'locales-test';
// 저장될 하위 디렉토리 및 시트에 작성된 언어의 헤더값
const lngs = ['ko', 'en'];
// https://docs.google.com/spreadsheets/d/Spread-Sheet-Doc-ID/edit#gid=Sheet-ID
const spreadsheetDocId = '10p132F_SYazLRTno7sqSTkMmlYJZ7KhucgnLhQrDJBw';
const fileNm = 'translation';
const sheetId = '0'; // Sheet-ID
async function loadSpreadsheet() {
// eslint-disable-next-line no-console
console.info(
'\u001B[32m',
'=====================================================================================================================\n',
`(\u001B[34mhttps://docs.google.com/spreadsheets/d/${spreadsheetDocId}/#gid=${sheetId}\u001B[0m)\n`,
'=====================================================================================================================',
'\u001B[0m'
);
const SCOPES = [
'https://www.googleapis.com/auth/spreadsheets',
];
const jwt = new JWT({
email: credentials.client_email,
key: credentials.private_key,
scopes: SCOPES,
});
// spreadsheet key is the long id in the sheets URL
const doc = new GoogleSpreadsheet(spreadsheetDocId, jwt);
await doc.loadInfo(); // loads document properties and worksheets
return doc;
}
module.exports = {
localesPath,
lngs,
loadSpreadsheet,
fileNm,
sheetId,
};
- 다운로드 파일
참고한 블로그의 소스를 거의 그대로 썼는데 구글시트 npm 버전이 달라서 방식이 바뀌었는지
makeTranslationsMapFromSheet 함수 내부의 row 데이터를 불러오는 방식을 조금 수정해야 했다.
// translationmodules/download.js
const fs = require('fs');
const mkdirp = require('mkdirp');
const _ = require('lodash');
const {loadSpreadsheet, localesPath, fileNm, lngs, sheetId} = require('./index');
async function makeTranslationsMapFromSheet(doc) {
const sheet = doc.sheetsById[sheetId];
if (!sheet) {
return {};
}
const lngsMap = {};
const rows = await sheet.getRows();
rows.forEach((row) => {
lngs.forEach((lang) => {
_.set(lngsMap, `${lang}.${row.get('key')}`, row.get(lang))
})
});
return lngsMap;
}
function makeLocaleDir(dirPath, subDirs) {
return new Promise((resolve) => {
subDirs.forEach((subDir, index) => {
try {
mkdirp.mkdirp(`${dirPath}/${subDir}`).then(result => {
if (result !== undefined) {
console.log(`made directories, starting with ${result}`);
}
});
resolve();
} catch (err) {
if(err) {
throw err;
}
}
});
});
}
async function updateJsonFromSheet() {
await makeLocaleDir(localesPath, lngs);
const doc = await loadSpreadsheet();
const lngsMap = await makeTranslationsMapFromSheet(doc);
fs.readdir(localesPath, (error, lngs) => {
if (error) {
console.error('디렉토리 읽기 오류:', error);
throw error;
}
lngs.forEach((lng) => {
const localeJsonFilePath = `${localesPath}/${lng}/${fileNm}.json`;
const jsonString = JSON.stringify(lngsMap[lng], null, 2);
fs.writeFile(localeJsonFilePath, jsonString, 'utf8', (err) => {
if (err) {
console.error('파일 쓰기 오류:', err);
throw err;
}
});
});
});
}
updateJsonFromSheet();
- 스크립트 추가
// package.json
"scripts": {
"i18n:download": "node translationmodules/download.js",
"dev": "npm run i18n:download && vite",
},
- 실행 및 json 확인
테스트 소스
GitHub - bom2zzang/vue-i18n-test
Contribute to bom2zzang/vue-i18n-test development by creating an account on GitHub.
github.com
'개발 > Vue' 카테고리의 다른 글
[Vite] webpack에서 vite로 전환하기 (0) | 2024.05.27 |
---|---|
[VUE3] 새로운 데이터 바인딩 패턴: defineModel 활용하기 (0) | 2024.03.07 |
[VUE3] file upload , input 태그는 숨기고 버튼으로 파일 등록 처리 (0) | 2023.11.22 |
[VUE3 + Webpack] SEO 설정 파일(robots.txt, sitemap.xml) 개발/운영 환경 분기 처리 (0) | 2023.10.31 |
[SEO] vue3 + title (0) | 2023.07.26 |