Skip to content

feat: add i18n #21

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 74 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
ea0f9d0
feat(i18n): implement internationalization for the homepage and menu
c1chy-jott Feb 11, 2025
9cbaca0
feat(i18n): add internationalization support with German and English …
c1chy-jott Feb 11, 2025
a9a466d
feat(i18n): add German and English localization support with correspo…
c1chy-jott Feb 11, 2025
b9b8139
feat(i18n): add English and German localization files for the website
c1chy-jott Feb 11, 2025
a00b257
feat(i18n): add English and German localization files for the website
c1chy-jott Feb 11, 2025
47510e3
feat(i18n): add German and English localization files for the website
c1chy-jott Feb 11, 2025
6bec9f2
feat(i18n): add English localization file for the website
c1chy-jott Feb 11, 2025
a32b120
feat(i18n): add English team member profiles and blog articles
c1chy-jott Feb 11, 2025
eb33bfa
feat(i18n): add en and de team member profiles for Paul, Alex, Conner…
c1chy-jott Feb 11, 2025
e386d8f
feat(i18n): add team member profiles for Inke, Annika, Arian, and upd…
c1chy-jott Feb 11, 2025
a544783
feat(i18n): add en team member profiles for Jonathan, Paul, Inke, Ann…
c1chy-jott Feb 11, 2025
ccdf889
feat(i18n): add new en blog articles on Instagram hashtags, vector gr…
c1chy-jott Feb 11, 2025
eb4e447
feat(i18n): moved de blog articles to de blog folder
c1chy-jott Feb 11, 2025
2c0151e
feat: update team page layout and routing logic
c1chy-jott Feb 12, 2025
e75336f
feat(blog): add localization support to blog slug page
c1chy-jott Feb 12, 2025
4375302
feat: update ImageFigure link and enhance locale usage in index.vue
c1chy-jott Feb 12, 2025
2aca0c4
feat: add slug field to team member profiles
c1chy-jott Feb 12, 2025
afcc9e4
feat: add slug fields to team members profiles
c1chy-jott Feb 12, 2025
335ec2b
feat: add slugs to posts
c1chy-jott Feb 12, 2025
acbe934
feat:"Translate blog content to English and update structures."
c1chy-jott Feb 12, 2025
a550725
feat: add localePath function to index.vue page
c1chy-jott Feb 13, 2025
adadef6
feat(team): enhance data fetching in team page
c1chy-jott Feb 13, 2025
88af8b8
feat(blog): enhance data fetching in blog post detail page
c1chy-jott Feb 13, 2025
28508ed
feat: update slug for Alex's profile in both English and German versions
c1chy-jott Feb 13, 2025
c92987a
feat: update slug for Annika's profile in team section
c1chy-jott Feb 13, 2025
2f7d1a9
feat(content.config): refactor collections and schemas for locales
c1chy-jott Feb 13, 2025
dd3b272
feat: update slug in German and English blog posts about GitHub Copilot
c1chy-jott Feb 13, 2025
e9b4e56
feat(blog): refactor blog page to use new articles component
c1chy-jott Feb 14, 2025
81e260c
feat: update blog post link path in index.vue
c1chy-jott Feb 14, 2025
7d1e80f
feat: update team member slugs in team markdown files
c1chy-jott Feb 14, 2025
62bfa81
feat: update team member slugs
c1chy-jott Feb 14, 2025
9a0ccfc
feat: update slug in multiple blog posts
c1chy-jott Feb 14, 2025
97f12d6
feat(blog): update slugs for multiple blog posts
c1chy-jott Feb 14, 2025
367b15f
feat(team): update page layout and remove console logs
c1chy-jott Feb 14, 2025
7d1940d
feat(blog): update component names and structure in blog page
c1chy-jott Feb 14, 2025
91b29fc
feat(content.config.ts): update content schema in content configurati…
c1chy-jott Feb 14, 2025
065b2a9
feat(i18n): update German locale file
c1chy-jott Feb 14, 2025
145b0f7
feat(i18n): add new blog-related translations in en-GB locale
c1chy-jott Feb 14, 2025
e96a271
feat: add i18n support to Footer.vue component
c1chy-jott Feb 14, 2025
128b96e
feat: translate blog post slugs and categories to English
c1chy-jott Feb 14, 2025
a1c44cf
feat(index.vue): update blog post query parameters and routing links
c1chy-jott Feb 14, 2025
d80aec1
feat(blog/index.vue): add internationalization support and enhance re…
c1chy-jott Feb 14, 2025
74b58f0
feat: convert author's name to lowercase in blog slug link
c1chy-jott Feb 14, 2025
ea9f7ab
feat(blog): translate slugs into English for multiple blog posts
c1chy-jott Feb 14, 2025
27972ae
feat(blog): update NuxtLink in blog slug page to handle null author v…
c1chy-jott Feb 14, 2025
76c3da5
feat: add slug to Annika's team member page
c1chy-jott Feb 14, 2025
84b537b
feat(index.vue): update NuxtLink navigation paths
c1chy-jott Feb 14, 2025
7f6c76f
feat: enhance scroll behavior in router options
c1chy-jott Feb 14, 2025
f40064f
feat: add i18n support to team page
c1chy-jott Feb 14, 2025
af054f6
feat(blog): update articles fetching and date formatting in blog inde…
c1chy-jott Feb 14, 2025
a40a8df
feat(pages): update date format in articles section on index page
c1chy-jott Feb 14, 2025
b6fd804
feat(content.config.ts): add date field to blog schema
c1chy-jott Feb 14, 2025
c3f112d
feat: update blog post dates to ISO format
c1chy-jott Feb 14, 2025
d619437
feat(blog): update date format in blog posts metadata
c1chy-jott Feb 14, 2025
f9981f1
feat(menu): update navigation links to use localePath for localization
c1chy-jott Feb 14, 2025
0234a46
fix(router): adjust scroll position offset from 80 to 60
c1chy-jott Feb 14, 2025
23fff07
feat(blog): update content and fix typos in multiple blog posts
c1chy-jott Feb 17, 2025
916743c
Update blog links to use localePath for i18n support
c1chy-jott Feb 17, 2025
8b9ba60
Merge branch 'master' into i18l-content3
c1chy-jott Feb 17, 2025
863a26c
Upgrade dependencies in pnpm-lock.yaml
c1chy-jott Feb 17, 2025
5124d50
Merge remote-tracking branch 'origin/i18l-content3' into i18l-content3
c1chy-jott Feb 17, 2025
c0ea3b5
Merge branch 'master' into i18l-content3
c1chy-jott Feb 17, 2025
279e63c
feat(Menu.vue): add dynamic locale path in navigation link
c1chy-jott Feb 17, 2025
a42d7e0
Merge remote-tracking branch 'origin/i18l-content3' into i18l-content3
c1chy-jott Feb 17, 2025
eff809d
feat: refactor Image.vue and improve styles
c1chy-jott Feb 18, 2025
daf67b7
feat: add locale path to menu and footer links
c1chy-jott Feb 18, 2025
6d623ba
feat: translate imprint page to support i18n
c1chy-jott Feb 18, 2025
d83b3e5
feat(i18n): update German and English locale files
c1chy-jott Feb 18, 2025
05fff24
feat(i18n): update German translation and add privacy policy
c1chy-jott Feb 18, 2025
cd2fa77
feat: add privacy policy translations to en-GB locale
c1chy-jott Feb 18, 2025
d736e79
feat: update imprint page translation key for director
c1chy-jott Feb 18, 2025
40a742b
feat: privacy page to use localization for text content**
c1chy-jott Feb 18, 2025
e200088
feat(Menu.vue): update class for NuxtLink in Menu component to show l…
c1chy-jott Feb 19, 2025
d31145b
feat: add top margin to homepage logo link update class for Menu comp…
c1chy-jott Feb 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 53 additions & 8 deletions app/router.options.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,59 @@
import type {RouterConfig} from "@nuxt/schema";
import type {RouterConfig} from '@nuxt/schema'

function findHashPosition(hash: string): { el: any, behavior: ScrollBehavior, top: number } | undefined {
const el = document.querySelector(hash)
if (el) {
const top = 60

return {
el: hash,
behavior: 'smooth',
top,
}
}
}

// https://router.vuejs.org/api/#routeroptions
export default <RouterConfig>{
async scrollBehavior(to, from, savedPosition) {
scrollBehavior(to, from, savedPosition) {
const nuxtApp = useNuxtApp()

if (history.state && history.state.stop)
return

if (history.state && history.state.smooth) {
return {
el: history.state.smooth,
behavior: 'smooth',
}
}


if (savedPosition) {
return savedPosition;

return new Promise((resolve) => {
nuxtApp.hooks.hookOnce('page:finish', () => {
setTimeout(() => resolve(savedPosition), 250)
})
})
}
if (to.path.startsWith('/blog/')) {
return {top: 0};
} else if (to.path.startsWith('/blog')) {
return {top: 0};

if (to.hash) {
return new Promise((resolve) => {
if (to.path === from.path) {
setTimeout(() => resolve(findHashPosition(to.hash)), 50)
} else {
nuxtApp.hooks.hookOnce('page:finish', () => {
setTimeout(() => resolve(findHashPosition(to.hash)), 50)
})
}
})
}

return new Promise((resolve) => {
setTimeout(() => {
resolve({top: 0})
}, 250)
})
},
};
}
25 changes: 17 additions & 8 deletions components/Footer.vue
Original file line number Diff line number Diff line change
@@ -1,38 +1,47 @@
<template>
<div class="relative bg-jm-secondary-grey-lighter mt-40">
<Background height="127px" src="footer-top.svg" position="top" parallax="to-left" />
<Background height="127px" parallax="to-left" position="top" src="footer-top.svg"/>

<div class="relative z-10">


<UContainer class="relative py-10 z-10" :ui="{'constrained': 'max-w-2xl'}">
<UContainer :ui="{'constrained': 'max-w-2xl'}" class="relative py-10 z-10">
<div class="flex justify-between items-end">
<div>
<address class="not-italic">
JOTT.MEDIA GmbH<br>
Bahnhofstraße 33<br>
31675 Bückeburg<br>
<br>
<nuxt-link href="tel:+4957229184984">+49 5722 9184984</nuxt-link><br>
<nuxt-link href="tel:+4957229184984">+49 5722 9184984</nuxt-link>
<br>
<nuxt-link href="mailto:hallo@jott.media">hallo@jott.media</nuxt-link>
</address>
</div>
<div class="text-right">
<nuxt-link to="/imprint" class="block font-[800] text-jm-primary-green uppercase">.Impressum</nuxt-link>
<nuxt-link to="/privacy" class="block font-[800] text-jm-primary-green uppercase">.Datenschutz</nuxt-link>
<nuxt-link :to="localePath({path: 'imprint'})" class="block font-[800] text-jm-primary-green uppercase">.{{
t('imprint')
}}
</nuxt-link>
<nuxt-link :to="localePath({path: 'privacy'})" class="block font-[800] text-jm-primary-green uppercase">.{{
t('privacy')
}}
</nuxt-link>
</div>
</div>
</UContainer>

<Background width="400px" height="400px" src="footer-box.png" position="bottomRight" :out="false" />
<Background :out="false" height="400px" position="bottomRight" src="footer-box.png" width="400px"/>
</div>

<div class="bg-jm-primary-green text-sm py-5">
<UContainer :ui="{'constrained': 'max-w-2xl'}">
<p>© {{ new Date().getFullYear() }} JOTT.MEDIA – Alle Rechte vorbehalten</p>
<p>© {{ new Date().getFullYear() }} JOTT.MEDIA – {{ t('rights') }}</p>
</UContainer>
</div>
</div>
</template>
<script setup lang="ts">
<script lang="ts" setup>
const {t} = useI18n()
const localePath = useLocalePath()
</script>
20 changes: 11 additions & 9 deletions components/Image.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
<template>
<div class="image-wrapper" :class="{'shine': shine}" :style="parallax ? {perspective: '2000px'} : {}">
<img ref="image" :style="imageStyle" :src="getImageAbsolutePath(src)" :alt="alt" class="w-full h-full block m-0 cover bg-center" />
<div :class="{'shine': shine}" :style="parallax ? {perspective: '2000px'} : {}" class="image-wrapper">
<img ref="image" :alt="alt" :src="getImageAbsolutePath(src)" :style="imageStyle"
class="w-full h-full block m-0 cover bg-center"/>
<UContainer :ui="{'constrained': 'max-w-2xl'}">
<Paragraph v-if="hint != null" class="text-jm-primary-gre italic text-sm !mb-0">{{ hint }}</Paragraph>
</UContainer>
</div>
</template>

<script setup lang="ts">
import { useParallax } from '@vueuse/core'
<script lang="ts" setup>
import {useParallax} from '@vueuse/core'

const image = ref(null)
const parallax = reactive(useParallax(image))
const imageStyle = computed(() => {
const style = {};
const style: Record<string, string> = {};

if (props.cover) {
style.objectFit = 'cover';
Expand Down Expand Up @@ -59,9 +60,9 @@ const props = defineProps({
},
})

const glob = import.meta.glob<Record<string, string>>('@/assets/images/**/*', { eager: true })
const glob = import.meta.glob<Record<string, string>>('@/assets/images/**/*', {eager: true})
const getImageAbsolutePath = (imageName: string): string | undefined => {
if(!props.publicSrc) {
if (!props.publicSrc) {
return glob[`/assets/images/${imageName}`]['default'];
}

Expand All @@ -71,6 +72,7 @@ const getImageAbsolutePath = (imageName: string): string | undefined => {

<style lang="scss">
@use 'sass:color';

$color: #fff;
.shine {
position: relative;
Expand All @@ -79,8 +81,8 @@ $color: #fff;
&::before {
background: linear-gradient(
to right,
fade_out($color, 1) 0%,
fade_out($color, 0.7) 100%
color.scale($color, $alpha: -70%) 0%,
color.scale($color, $alpha: -30%) 100%
);
content: "";
display: block;
Expand Down
67 changes: 42 additions & 25 deletions components/Menu.vue
Original file line number Diff line number Diff line change
@@ -1,42 +1,53 @@
<template>
<div class="fixed flex flex-col justify-center -bottom-2 sm:bottom-0 right-4 sm:right-8 z-50">
<button @click="open = !open"
class="h-10 w-10 sm:h-12 sm:w-12 flex justify-center items-center bg-jm-contrast-black transition-all duration-700 bg-opacity-70 hover:bg-opacity-100 rounded-full z-50">
<UContainer :ui="{'constrained': 'sm:max-w-6xl', padding: 'space-x-2 sm:space-x-4'}"
class="fixed flex flex-col justify-center -bottom-2 sm:bottom-0 right-4 sm:right-8 z-50">
<button
class="h-10 w-10 sm:h-12 sm:w-12 flex justify-center items-center bg-jm-contrast-black transition-all duration-700 bg-opacity-70 hover:bg-opacity-100 rounded-full z-50"
@click="open = !open">
<UIcon v-if="!open" class="text-lg sm:text-2xl text-jm-primary-brown transition-all z-50" name="i-mdi-menu"/>
<UIcon v-else class="text-lg sm:text-2xl text-jm-primary-brown transition-all z-50" name="i-mdi-close-thick"/>
</button>
<div class="relative py-3 mx-auto ">
<Center>
<transition name="page" mode="in-out">
<transition mode="in-out" name="page">
<div
v-if="open"
class="top-0 left-0 w-screen h-screen flex flex-col justify-start z-30 bg-jm-primary-brown fixed">
<UContainer class="my-8" :ui="{'constrained': 'sm:max-w-6xl'}">


<UContainer :ui="{'constrained': 'sm:max-w-8xl'}" class="my-8">
<button :class="{ 'font-bold': currentLocale === 'de' }" @click="setLocale('de')">DE</button>
|
<button :class="{ 'font-bold': currentLocale === 'en' }" @click="setLocale('en')">EN</button>
<Center>
<NuxtLink to="/" @click="open = false" class="inline-block no-underline w-full mt-2 sm:w-[325px] border-0">
<Image src="logo-overlay.svg" alt="JOTT.MEDIA GmbH" class="w-full" :shine="false"
:parallax="false"/>
<NuxtLink :to="localePath({path: '/'})"
class="inline-block no-underline w-full mt-2 xl:mr-3 sm:w-[325px] border-0"
@click="open = false">
<Image :parallax="false" :shine="false" alt="JOTT.MEDIA GmbH" class="w-full"
src="logo-overlay.svg"/>
</NuxtLink>
</Center>
</UContainer>

<UContainer class="sm:mt-12 flex flex-col sm:flex-row justify-around"
:ui="{'constrained': 'w-full', padding: 'space-x-2 sm:space-x-4'}">
<UContainer :ui="{'constrained': 'w-full', padding: 'space-x-2 sm:space-x-4'}"
class="sm:mt-12 flex flex-col sm:flex-row justify-around">
<ul class="uppercase font-extrabold text-left text-xl xl:text-3xl space-y-4 sm:space-y-8 text-jm-contrast-black">
<li>
<NuxtLink @click="open = false" to="/">.Startseite</NuxtLink>
<NuxtLink :to="localePath({ name: 'index' })" @click="open = false">{{ t('menu.home') }}</NuxtLink>
</li>
<li>
<NuxtLink @click="open = false" to="/blog">.Blog</NuxtLink>
<NuxtLink :to="localePath({name: 'blog'})" @click="open = false">{{ t('menu.blog') }}</NuxtLink>
</li>
</ul>
<div class="text-left sm:text-right mt-8 sm:mt-0">
<nuxt-link to="/privacy" @click="open = false" class="block font-extrabold text-sm sm:text-base">
Datenschutz
</nuxt-link>
<nuxt-link to="/imprint" @click="open = false" class="block font-extrabold text-sm sm:text-base">
Impressum
</nuxt-link>
<nuxt-link :to="localePath({path: 'privacy'})" class="block font-extrabold text-sm sm:text-base"
@click="open = false">
.{{ t('privacy') }}
</nuxt-link>
<nuxt-link :to="localePath({path: 'imprint'})" class="block font-extrabold text-sm sm:text-base"
@click="open = false">
.{{ t('imprint') }}
</nuxt-link>
<address class="not-italic text-sm mt-6 sm:mt-16">
JOTT.MEDIA GmbH<br>
Bahnhofstraße 33<br>
Expand All @@ -48,13 +59,14 @@
<br>
<br>
<div class="text-left sm:text-right flex sm:justify-end items-center space-x-1">
<p class="pr-2">Folge uns:</p>
<NuxtLink href="https://www.instagram.com/jott.media/" target="_blank"
class="inline-block no-underline border-0">
<p class="pr-2"> {{ t('menu.follow') }}</p>
<NuxtLink class="inline-block no-underline border-0" href="https://www.instagram.com/jott.media/"
target="_blank">
<UIcon class="text-[#1E1E1E] text-2xl block " name="i-mdi-instagram"/>
</NuxtLink>
<NuxtLink href="https://de.linkedin.com/company/jottmedia" target="_blank"
class="inline-block no-underline border-0">
<NuxtLink class="inline-block no-underline border-0"
href="https://de.linkedin.com/company/jottmedia"
target="_blank">
<UIcon class="text-[#1E1E1E] text-2xl block" name="i-mdi-linkedin"/>
</NuxtLink>
</div>
Expand All @@ -65,8 +77,13 @@
</transition>
</Center>
</div>
</div>
</UContainer>
</template>
<script setup>
<script lang="ts" setup>
const {t, locale, setLocale} = useI18n()
const localePath = useLocalePath()
const open = ref(false)
const currentLocale = computed(() => locale.value)


</script>
46 changes: 34 additions & 12 deletions content.config.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
import {defineCollection, defineContentConfig} from '@nuxt/content'
import {defineCollection, defineContentConfig, z} from '@nuxt/content';

const locales = ['de', 'en'];

export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/**.md',
}),
team: defineCollection({
type: 'page',
source: 'team/**.md',
})
}
})
collections: Object.fromEntries(
locales.flatMap((locale) => [
[
`team_${locale}`,
defineCollection({
source: `${locale}/team/*.md`,
type: 'page',
schema: z.object({
title: z.string(),
description: z.string(),
slug: z.string(),
}),
}),
],
[
`articles_${locale}`,
defineCollection({
source: `${locale}/blog/**/*.md`,
type: 'page',
schema: z.object({
title: z.string(),
slug: z.string(),
description: z.string(),
url: z.string().url(),
date: z.date(),
}),
}),
]
]),
),
});
Loading