Skip to content

Commit 1be2cc6

Browse files
committed
Internationalization Config using vue-i18n
1 parent 4f3f50d commit 1be2cc6

File tree

8 files changed

+215
-10
lines changed

8 files changed

+215
-10
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
},
1717
"dependencies": {
1818
"@tailwindcss/vite": "^4.1.11",
19+
"@vueuse/components": "^13.7.0",
1920
"@vueuse/core": "^13.5.0",
2021
"tailwindcss": "^4.1.11",
21-
"vue": "^3.5.17"
22+
"vue": "^3.5.17",
23+
"vue-i18n": "^11.1.11"
2224
},
2325
"devDependencies": {
2426
"@vite-pwa/assets-generator": "^1.0.0",

pnpm-lock.yaml

Lines changed: 85 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { useOnlineStatus } from "./composables/useOnlineStatus";
44
55
const { isOnline } = useOnlineStatus();
66
// You can use the isOnline variable to show a message or perform actions when user is online or offline
7+
8+
// import the i18n instance
9+
import { useI18n } from 'vue-i18n'
10+
const { t } = useI18n()
711
</script>
812

913
<template>
@@ -27,7 +31,7 @@ const { isOnline } = useOnlineStatus();
2731
aria-label="GitHub Repository (opens in new tab)"
2832
class="mt-4 flex items-center justify-center px-4 py-2 bg-black text-white rounded-lg hover:bg-gray-800 transition duration-300"
2933
>
30-
View on GitHub
34+
{{ t("view_on_github") }}
3135
</a>
3236
</div>
3337
</main>

src/assets/icons/language.svg

Lines changed: 11 additions & 0 deletions
Loading

src/components/AppHeader.vue

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
<script setup lang="ts">
22
import DarkModeButton from "./DarkModeButton.vue";
3+
import LanguageSwitcher from "./LanguageSwitcher.vue";
34
</script>
45

56
<template>
6-
<header class="fixed top-0 inset-x-0 z-50 bg-transparent h-[40px] lg:h-[60px]">
7+
<header
8+
class="fixed top-0 inset-x-0 z-50 bg-transparent h-[40px] lg:h-[60px]"
9+
>
710
<div
8-
class="max-w-screen-xl mx-auto h-full bg-inherit flex items-center justify-end px-8"
11+
class="max-w-screen-xl mx-auto h-full bg-inherit flex justify-end px-8"
912
>
10-
<DarkModeButton />
13+
<div class="flex items-center gap-4">
14+
<LanguageSwitcher />
15+
<DarkModeButton />
16+
</div>
1117
</div>
1218
</header>
1319
</template>

src/components/DarkModeButton.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const toggleDark = useToggle(isDark);
1010
<button
1111
@click="toggleDark()"
1212
type="button"
13-
class="cursor-pointer flex-shrink-0 rounded-md gap-x-1.5 p-1.5 inline-flex items-center"
13+
class="cursor-pointer flex-shrink-0 rounded-md inline-flex items-center"
1414
>
1515
<!-- Feel free to customize these icons -->
1616
{{ isDark ? "☀️" : "🌙" }}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<script setup>
2+
import { ref } from "vue";
3+
import { useI18n } from "vue-i18n";
4+
import { vOnClickOutside } from "@vueuse/components";
5+
6+
const isOpen = ref(false);
7+
const { locale } = useI18n();
8+
9+
const toggle = () => {
10+
isOpen.value = !isOpen.value;
11+
};
12+
13+
const close = () => {
14+
isOpen.value = false;
15+
};
16+
17+
const setLang = (lang) => {
18+
locale.value = lang;
19+
close();
20+
};
21+
22+
const languages = [
23+
{ code: "fr", label: "FR", ariaLabel: "Français" },
24+
{ code: "en", label: "EN", ariaLabel: "English" },
25+
{ code: "es", label: "ES", ariaLabel: "Español" },
26+
];
27+
</script>
28+
29+
<template>
30+
<div v-on-click-outside="close">
31+
<div class="relative">
32+
<button
33+
type="button"
34+
class="cursor-pointer"
35+
@click="toggle"
36+
aria-haspopup="menu"
37+
:aria-expanded="isOpen"
38+
>
39+
🌐
40+
</button>
41+
42+
<div
43+
v-if="isOpen"
44+
class="absolute right-0 mt-2 w-24 rounded-xl border shadow-lg p-1 border-gray-200 dark:border-white/10 bg-white/90 dark:bg-slate-900/90 dark:backdrop-blur transition-colors"
45+
>
46+
<button
47+
v-for="lang in languages"
48+
:key="lang.code"
49+
type="button"
50+
class="flex w-full items-center justify-center gap-2 rounded-lg px-2 py-2 text-sm cursor-pointer hover:bg-gray-100 dark:hover:bg-white/5 transition-colors"
51+
:class="{
52+
'bg-teal-100 text-teal-700 ring-1 ring-teal-300 dark:bg-teal-500/20 dark:text-teal-300 dark:ring-teal-500/40':
53+
locale === lang.code || locale?.value === lang.code,
54+
}"
55+
@click="setLang(lang.code)"
56+
:aria-label="lang.ariaLabel"
57+
>
58+
<span class="text-teal-700 text-xs">{{ lang.label }}</span>
59+
</button>
60+
</div>
61+
</div>
62+
</div>
63+
</template>

src/main.js

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,40 @@
1-
import './assets/style.css'
1+
import "./assets/style.css";
22

3-
import { createApp } from 'vue'
4-
import App from './App.vue'
3+
import { createApp } from "vue";
4+
import { createI18n } from "vue-i18n";
5+
import App from "./App.vue";
56

6-
createApp(App).mount('#app')
7+
const app = createApp(App);
8+
9+
const i18n = createI18n({
10+
legacy: false, // you must set `false`, to use Composition API
11+
locale: navigator.language || "en", // get navigator.language or default to "en"
12+
fallbackLocale: "en",
13+
// messages are the translations
14+
// create a locales folder and add the translations files
15+
// en.json
16+
// fr.json
17+
// es.json
18+
// then import the translations files
19+
// import en from "./locales/en.json";
20+
// import fr from "./locales/fr.json";
21+
// import es from "./locales/es.json";
22+
// then use the translations files in the app
23+
messages: {
24+
en: {
25+
view_on_github: "View on GitHub",
26+
},
27+
fr: {
28+
view_on_github: "Voir sur GitHub",
29+
},
30+
es: {
31+
view_on_github: "Ver en GitHub",
32+
},
33+
},
34+
});
35+
36+
// use the i18n instance
37+
app.use(i18n);
38+
39+
// mount the app
40+
app.mount("#app");

0 commit comments

Comments
 (0)