-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1b1155f
Showing
19 changed files
with
3,285 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Vue 3 + Typescript + Vite + Router + i18n | ||
|
||
This template should help get you started developing with Vue 3, Typescript, lazy loading translation handling and routing in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more. | ||
|
||
## Recommended IDE Setup | ||
|
||
- [VSCode](https://code.visualstudio.com/) | ||
|
||
## Type Support For `.vue` Imports in TS | ||
|
||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's `.vue` type support plugin by running `Volar: Switch TS Plugin on/off` from VSCode command palette. | ||
|
||
## i18n suport for vue3 | ||
Using [@intlify/vite-plugin-vue-i18n](https://github.com/intlify/vite-plugin-vue-i18n) for handling lazy-loading translations from JSON files. | ||
|
||
## i18n routing | ||
Routing width createWebHistory() (no hash) and language param in url. | ||
|
||
"THE BEER-WARE LICENSE" (Revision 42): | ||
<[email protected]> wrote this file. As long as you retain this notice you | ||
can do whatever you want with this stuff. If we meet some day, and you think | ||
this stuff is worth it, you can buy me a beer in return. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" href="/favicon.ico" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Vite App</title> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
<script type="module" src="/src/main.ts"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
{ | ||
"name": "vue3-ts-vite-i18n-router", | ||
"version": "0.0.0", | ||
"author": "[email protected]", | ||
"license": "Beerware", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "vue-tsc --noEmit && vite build", | ||
"preview": "vite preview", | ||
"lint:script": "eslint --ext .ts,vue --ignore-path .gitignore .", | ||
"lint:style": "stylelint src/**/*.{css,scss,vue}", | ||
"lint:markup": "vue-tsc --noEmit" | ||
}, | ||
"dependencies": { | ||
"@vue/compiler-sfc": "^3.2.26", | ||
"axios": "^0.24.0", | ||
"vue": "^3.2.25", | ||
"vue-i18n": "^9.2.0", | ||
"vue-router": "^4.0.12" | ||
}, | ||
"devDependencies": { | ||
"@intlify/vite-plugin-vue-i18n": "^3.2.1", | ||
"@typescript-eslint/eslint-plugin": "^5.9.1", | ||
"@typescript-eslint/parser": "^5.9.1", | ||
"@vitejs/plugin-vue": "^2.0.0", | ||
"@vue/eslint-config-prettier": "^7.0.0", | ||
"@vue/eslint-config-typescript": "^10.0.0", | ||
"eslint": "^8.6.0", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"eslint-plugin-vue": "^8.2.0", | ||
"path": "^0.12.7", | ||
"prettier": "^2.5.1", | ||
"rollup-plugin-copy": "^3.4.0", | ||
"sass": "^1.47.0", | ||
"stylelint": "^14.2.0", | ||
"stylelint-config-recommended": "^6.0.0", | ||
"stylelint-config-standard": "^24.0.0", | ||
"typescript": "^4.4.4", | ||
"vite": "^2.7.2", | ||
"vue-tsc": "^0.29.8" | ||
}, | ||
"lint-staged": { | ||
"*.{ts,tsx}": "eslint --fix", | ||
"*.{css,scss,vue}": "stylelint --fix", | ||
"*": "prettier -w -u" | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<template> | ||
<div> | ||
<nav> | ||
<div class="navigation"> | ||
<router-link :to="{ name: 'home', params: { locale } }"> | ||
{{ t('navigations.home') }} | ||
</router-link> | ||
| | ||
<router-link :to="{ name: 'about', params: { locale } }"> | ||
{{ t('navigations.about') }} | ||
</router-link> | ||
</div> | ||
<form class="language"> | ||
<label for="locale-select">{{ t('labels.language') }}</label> | ||
<select id="locale-select" v-model="currentLocale"> | ||
<option | ||
v-for="optionLocale in supportLocales" | ||
:key="optionLocale" | ||
:value="optionLocale" | ||
> | ||
{{ optionLocale }} | ||
</option> | ||
</select> | ||
</form> | ||
</nav> | ||
<router-view /> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { defineComponent, watch, ref } from 'vue' | ||
import { useRouter } from 'vue-router' | ||
import { useI18n } from 'vue-i18n' | ||
import { SUPPORT_LOCALES } from '../i18n' | ||
export default defineComponent({ | ||
name: 'App', | ||
setup() { | ||
const router = useRouter() | ||
const { t, locale } = useI18n() // same as `useI18n({ useScope: 'global' })` | ||
/** | ||
* select locale value for language select form | ||
* | ||
* If you use the vue-i18n composer `locale` property directly, it will be re-rendering component when this property is changed, | ||
* before dynamic import was used to asynchronously load and apply locale messages | ||
* To avoid this, use the another locale reactive value. | ||
*/ | ||
const currentLocale = ref(locale.value) | ||
// sync to switch locale from router locale path | ||
watch(router.currentRoute, route => { | ||
currentLocale.value = route.params.locale as string | ||
}) | ||
/** | ||
* when change the locale, go to locale route | ||
* | ||
* when the changes are detected, load the locale message and set the language via vue-router navigation guard. | ||
* change the vue-i18n locale too. | ||
*/ | ||
watch(currentLocale, val => { | ||
router.push({ | ||
name: router.currentRoute.value.name!, | ||
params: { locale: val } | ||
}) | ||
}) | ||
return { t, locale, currentLocale, supportLocales: SUPPORT_LOCALES } | ||
} | ||
}) | ||
</script> | ||
|
||
<style scoped> | ||
nav { | ||
display: inline-flex; | ||
} | ||
.navigation { | ||
margin-right: 1rem; | ||
} | ||
.language label { | ||
margin-right: 1rem; | ||
} | ||
</style> |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<template> | ||
<div class="container"> | ||
<h2>{{ $t('pages.about') }}</h2> | ||
</div> | ||
</template> | ||
|
||
|
||
<script lang="ts"> | ||
import { defineComponent } from '@vue/runtime-core'; | ||
export default defineComponent({ | ||
name: 'About' | ||
}); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<template> | ||
<div class="container"> | ||
<h2>{{ $t('pages.home') }}</h2> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { defineComponent } from '@vue/runtime-core'; | ||
export default defineComponent({ | ||
name: 'Home' | ||
}); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { nextTick } from 'vue' | ||
import { createI18n } from 'vue-i18n' | ||
|
||
import type { I18n, I18nOptions, Locale, VueI18n, Composer } from 'vue-i18n' | ||
|
||
export const SUPPORT_LOCALES = ['de', 'en'] | ||
|
||
export function getLocale(i18n: I18n): string { | ||
return i18n.mode === 'legacy' | ||
? (i18n.global as unknown as VueI18n).locale | ||
: (i18n.global as unknown as Composer).locale.value | ||
} | ||
|
||
export function setLocale(i18n: I18n, locale: Locale): void { | ||
if (i18n.mode === 'legacy') { | ||
;(i18n.global as unknown as VueI18n).locale = locale | ||
} else { | ||
;(i18n.global as unknown as Composer).locale.value = locale | ||
} | ||
} | ||
|
||
export function setupI18n(options: I18nOptions = { locale: 'de' }): I18n { | ||
const i18n = createI18n(options) | ||
setI18nLanguage(i18n, options.locale!) | ||
return i18n | ||
} | ||
|
||
export function setI18nLanguage(i18n: I18n, locale: Locale): void { | ||
setLocale(i18n, locale) | ||
/** | ||
* NOTE: | ||
* If you need to specify the language setting for headers, such as the `fetch` API, set it here. | ||
* The following is an example for axios. | ||
* | ||
* axios.defaults.headers.common['Accept-Language'] = locale | ||
*/ | ||
document.querySelector('html')!.setAttribute('lang', locale) | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const getResourceMessages = (r: any) => r.default || r | ||
|
||
export async function loadLocaleMessages(i18n: I18n, locale: Locale) { | ||
// load locale messages | ||
const messages = await import( | ||
/* @vite-ignore */ `./locales/${locale}.json` | ||
).then(getResourceMessages) | ||
|
||
// set locale and locale message | ||
i18n.global.setLocaleMessage(locale, messages) | ||
|
||
return nextTick() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#app { | ||
font-family: Avenir, Helvetica, Arial, sans-serif; | ||
-webkit-font-smoothing: antialiased; | ||
-moz-osx-font-smoothing: grayscale; | ||
color: #2c3e50; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"pages": { | ||
"home": "Startseite", | ||
"about": "Über uns" | ||
}, | ||
"navigations": { | ||
"home": "Start", | ||
"about": "Über uns" | ||
}, | ||
"labels": { | ||
"language": "Sprachen" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"pages": { | ||
"home": "Home page", | ||
"about": "About page" | ||
}, | ||
"navigations": { | ||
"home": "Home", | ||
"about": "About" | ||
}, | ||
"labels": { | ||
"language": "Languages" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { createApp } from 'vue' | ||
import App from '@/app/App.vue' | ||
import './index.scss' | ||
import { setupRouter } from './router' | ||
import { setupI18n } from './i18n' | ||
import de from './locales/de.json' | ||
|
||
const i18n = setupI18n({ | ||
legacy: false, | ||
locale: 'de', | ||
globalInjection: true, | ||
fallbackLocale: 'de', | ||
messages: { | ||
de | ||
} | ||
}) | ||
const router = setupRouter(i18n) | ||
|
||
const app = createApp(App) | ||
app.use(i18n) | ||
app.use(router) | ||
app.mount('#app') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { createRouter, createWebHistory } from 'vue-router' | ||
import { | ||
getLocale, | ||
setI18nLanguage, | ||
loadLocaleMessages, | ||
SUPPORT_LOCALES | ||
} from './i18n' | ||
|
||
import type { Router, RouteRecordRaw } from 'vue-router' | ||
import type { I18n } from 'vue-i18n' | ||
|
||
import Home from '@/app/pages/Home.vue' | ||
import About from '@/app/pages/About.vue' | ||
|
||
export function setupRouter(i18n: I18n): Router { | ||
const locale = getLocale(i18n) | ||
|
||
// setup routes | ||
const routes: RouteRecordRaw[] = [ | ||
{ | ||
path: '/:locale/', | ||
name: 'home', | ||
component: Home | ||
}, | ||
{ | ||
path: '/:locale/about', | ||
name: 'about', | ||
component: About | ||
}, | ||
{ | ||
path: '/:pathMatch(.*)*', | ||
redirect: () => `/${locale}` | ||
} | ||
] | ||
|
||
// create router instance | ||
const router = createRouter({ | ||
history: createWebHistory(), | ||
routes | ||
}) | ||
|
||
// navigation guards | ||
router.beforeEach(async to => { | ||
const paramsLocale = to.params.locale as string | ||
|
||
// use locale if paramsLocale is not in SUPPORT_LOCALES | ||
if (!SUPPORT_LOCALES.includes(paramsLocale)) { | ||
return `/${locale}` | ||
} | ||
|
||
// load locale messages | ||
if (!i18n.global.availableLocales.includes(paramsLocale)) { | ||
await loadLocaleMessages(i18n, paramsLocale) | ||
} | ||
|
||
// set i18n language | ||
setI18nLanguage(i18n, paramsLocale) | ||
}) | ||
|
||
return router | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
declare module '*.vue' { | ||
import { DefineComponent } from 'vue' | ||
const component: DefineComponent<{}, {}, any> | ||
export default component | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/** | ||
* global type definitions | ||
*/ | ||
|
||
import { DefineLocaleMessage } from 'vue-i18n' | ||
import en from './locales/en.json' | ||
|
||
type MessageSchema = typeof en | ||
|
||
declare module 'vue-i18n' { | ||
// define the locale messages schema | ||
export interface DefineLocaleMessage extends MessageSchema {} | ||
} |
Oops, something went wrong.