Translate all portfolio

This commit is contained in:
2024-07-08 01:08:55 +02:00
parent 471b2b830f
commit ebfac1794c
37 changed files with 2010 additions and 1540 deletions

View File

@@ -18,3 +18,6 @@ NUXT_DISCORD_USER_ID=
# Cloud files
NUXT_PUBLIC_CLOUD_RESUME=
# Nuxt I18N
NUXT_PUBLIC_I18N_BASE_URL=

View File

@@ -64,6 +64,9 @@ NUXT_DISCORD_USER_ID=...
# Cloud files
NUXT_PUBLIC_CLOUD_RESUME=...
# Nuxt I18N
NUXT_PUBLIC_I18N_BASE_URL=...
```
## 📄 License

View File

@@ -20,6 +20,10 @@ const socials = [
link: 'https://discordapp.com/users/179635349100691456'
}
]
const { t } = useI18n({
useScope: 'local'
})
</script>
<template>
@@ -33,7 +37,7 @@ const socials = [
</div>
<div class="space-y-4">
<div class="flex flex-col md:flex-row gap-2 md:items-center">
<h1>Find me on:</h1>
<h1>{{ t('find') }}</h1>
<div class="flex gap-2 flex-wrap">
<HomeLink
v-for="social in socials.sort((a, b) => a.label.localeCompare(b.label))"
@@ -46,7 +50,7 @@ const socials = [
</div>
</div>
<div class="flex flex-col md:flex-row gap-2 md:items-center">
<h1>Or send me an email:</h1>
<h1>{{ t('email') }}</h1>
<div class="flex">
<HomeLink
blanked
@@ -57,7 +61,26 @@ const socials = [
</div>
</div>
<div class="mt-8 w-full flex justify-center text-xs">
© {{ new Date().getFullYear() }} Arthur Danjou. All rights reserved.
{{
t('copyright', {
date: new Date().getFullYear()
})
}}
</div>
</footer>
</template>
<i18n lang="json">
{
"en": {
"find": "Find me on:",
"email": "Or send me an email:",
"copyright": "© {date} Arthur Danjou. All rights reserved."
},
"fr": {
"find": "Retrouvez-moi sur :",
"email": "Ou envoyez-moi un email :",
"copyright": "© {date} Arthur Danjou. Tous droits réservés."
}
}
</i18n>

View File

@@ -8,67 +8,109 @@ watch(isDark, () => {
const config = useRuntimeConfig()
const navs = [
{
label: 'home',
label: {
en: 'home',
fr: 'accueil'
},
to: '/',
icon: 'i-ph-house-line-duotone',
shortcut: 'h'
shortcut: {
en: 'h',
fr: 'a'
}
},
{
label: 'uses',
label: {
en: 'uses',
fr: 'usages'
},
to: '/uses',
icon: 'i-ph-backpack-duotone',
shortcut: 'u'
shortcut: {
en: 'u',
fr: 'u'
}
},
{
label: 'writings',
label: {
en: 'writings',
fr: 'écrits'
},
to: '/writings',
icon: 'i-ph-books-duotone',
shortcut: 'w'
shortcut: {
en: 'w',
fr: 'e'
}
},
{
label: 'resume',
label: {
en: 'resume',
fr: 'cv'
},
to: config.public.cloud.resume,
target: '_blank',
icon: 'i-ph-address-book-duotone',
shortcut: 'r'
shortcut: {
en: 'r',
fr: 'c'
}
}
]
async function toggleTheme() {
document.body.style.animation = 'theme-switch-on .5s'
document.body.style.animation = 'switch-on .5s'
await new Promise(resolve => setTimeout(resolve, 500))
isDark.value = !isDark.value
document.body.style.animation = 'theme-switch-off .5s'
document.body.style.animation = 'switch-off .5s'
await new Promise(resolve => setTimeout(resolve, 500))
document.body.style.animation = ''
}
async function changeLocale() {
document.body.style.animation = 'switch-on .2s'
await new Promise(resolve => setTimeout(resolve, 200))
await setLocale(locale.value === 'en' ? 'fr' : 'en')
document.body.style.animation = 'switch-off .2s'
await new Promise(resolve => setTimeout(resolve, 200))
document.body.style.animation = ''
}
const router = useRouter()
const { locale, setLocale, locales, t } = useI18n()
const currentLocale = computed(() => locales.value.filter(l => l.code === locale.value)[0])
defineShortcuts({
h: () => router.push('/'),
a: () => router.push('/'),
u: () => router.push('/uses'),
w: () => router.push('/writings'),
e: () => router.push('/writings'),
r: () => window.open(config.public.cloud.resume, '_blank'),
t: () => toggleTheme()
c: () => window.open(config.public.cloud.resume, '_blank'),
t: () => toggleTheme(),
l: () => changeLocale(),
backspace: () => router.back()
})
</script>
<template>
<header class="flex items-center justify-between my-8">
<NuxtLink
<NuxtLinkLocale
class="handwriting text-lg sm:text-3xl flex gap-2 font-bold duration-300 text-gray-600 hover:text-black dark:text-gray-400 dark:hover:text-white"
to="/"
>
Arthur Danjou
</NuxtLink>
</NuxtLinkLocale>
<nav class="flex gap-2 items-center">
<UTooltip
v-for="nav in navs"
:key="nav.label"
:text="nav.label"
:shortcuts="[nav.shortcut]"
:key="nav.label.en"
:shortcuts="[nav.shortcut[locale]]"
:text="nav.label[locale]"
>
<UButton
:icon="nav.icon"
@@ -77,14 +119,13 @@ defineShortcuts({
:aria-label="nav.label"
color="white"
size="sm"
label=""
variant="solid"
/>
</UTooltip>
<ClientOnly>
<UTooltip
:shortcuts="['t']"
text="switch theme"
:text="t('theme')"
>
<UButton
:icon="isDark ? 'i-ph-moon-duotone' : 'i-ph-sun-duotone'"
@@ -95,6 +136,19 @@ defineShortcuts({
@click="toggleTheme()"
/>
</UTooltip>
<UTooltip
:shortcuts="['l']"
:text="t('language')"
>
<UButton
:icon="currentLocale!.icon"
aria-label="switch language"
color="white"
size="sm"
variant="solid"
@click.prevent="changeLocale()"
/>
</UTooltip>
</ClientOnly>
</nav>
</header>
@@ -105,7 +159,7 @@ defineShortcuts({
font-family: 'Dancing Script', cursive;
}
@keyframes theme-switch-on {
@keyframes switch-on {
0% {
filter: blur(0);
transform: scale(1);
@@ -116,7 +170,7 @@ defineShortcuts({
}
}
@keyframes theme-switch-off {
@keyframes switch-off {
0% {
transform: scale(0.98);
filter: blur(3px);
@@ -127,3 +181,16 @@ defineShortcuts({
}
}
</style>
<i18n lang="json">
{
"en": {
"theme": "switch theme",
"language": "change language"
},
"fr": {
"theme": "changer de thème",
"language": "changer de langue"
}
}
</i18n>

View File

@@ -1,20 +1,28 @@
<script lang="ts" setup>
import { type Activity, type CodingActivity, IDEs } from '~~/types'
import { type Activity, IDEs } from '~~/types'
const { data: activity, refresh } = await useAsyncData<Activity>('activity', () => $fetch('/api/activity'))
useIntervalFn(async () => await refresh(), 5000)
const codingActivity = computed(() => activity.value!.data.activities.filter(activity => IDEs.some(ide => ide.name === activity.name))[0])
const getActivity = computed<CodingActivity | undefined>(() => {
const { locale, locales } = useI18n()
const currentLocale = computed(() => locales.value.filter(l => l.code === locale.value)[0])
const getActivity = computed(() => {
const activity = codingActivity.value
if (!activity) return
const active = activity.name === 'Visual Studio Code' ? !activity.details.includes('Idling') : activity.state.toLowerCase().includes('editing')
const project = activity.state ? activity.state.replace('Workspace:', '') : ''
const state = activity.details.charAt(0).toLowerCase() + activity.details.slice(1)
const project = activity.details ? activity.details.replace('Workspace:', '') : ''
const state = activity.state.split(' ')[1]
const start = {
ago: useTimeAgo(activity.timestamps.start).value,
formated: `${useDateFormat(activity.timestamps.start, 'DD MMM YYYY').value} at ${useDateFormat(activity.timestamps.start, 'HH:mm:ss').value}`
formated: `${useDateFormat(
activity.timestamps.start,
'DD MMM YYYY',
{ locales: currentLocale.value!.code ?? 'en' }
).value} ${t('separator')}
${useDateFormat(activity.timestamps.start, 'HH:mm:ss', { locales: currentLocale.value!.code ?? 'en' }).value}`
}
return {
@@ -25,6 +33,10 @@ const getActivity = computed<CodingActivity | undefined>(() => {
start
}
})
const { t } = useI18n({
useScope: 'local'
})
</script>
<template>
@@ -33,7 +45,7 @@ const getActivity = computed<CodingActivity | undefined>(() => {
v-if="getActivity"
class="flex items-start gap-2"
>
<UTooltip :text="getActivity.active ? 'I\'m online 👋' : 'I\'m sleeping 😴'">
<UTooltip :text="getActivity.active ? t('tooltip.online') : t('tooltip.idling')">
<div class="relative flex h-3 w-3 mt-2">
<div
v-if="getActivity.active"
@@ -45,45 +57,84 @@ const getActivity = computed<CodingActivity | undefined>(() => {
/>
</div>
</UTooltip>
<span
<i18n-t
v-if="getActivity.active"
keypath="working"
tag="div"
class="space-x-1"
>
<span>I'm actually working on <strong>{{ getActivity.project }}</strong>, {{ getActivity.state }}, using</span>
<template #project>
<strong>{{ getActivity.project }}</strong>
</template>
<template #state>
{{ getActivity.state }}
</template>
<template #editor>
<UIcon
:name="IDEs.find(ide => ide.name === getActivity!.name)!.icon"
size="16"
/>
<span>
<strong>{{ getActivity.name }}</strong>.
I've started <strong>{{ getActivity.start.ago }}</strong>, the
<strong>{{ getActivity.start.formated }}</strong>.
</span>
</span>
<div
<strong>{{ getActivity.name }}</strong>
</template>
<template #start>
<strong>{{ getActivity.start.ago }}</strong>
</template>
<template #format>
<strong>{{ getActivity.start.formated }}</strong>
</template>
</i18n-t>
<i18n-t
v-else
keypath="idling"
tag="div"
class="space-x-1"
>
<span>I'm Idling on my computer with</span>
<template #editor>
<UIcon
:name="IDEs.find(ide => ide.name === getActivity!.name)!.icon"
size="16"
/>
<span>
<strong>{{ getActivity.name }}</strong> running in background.
</span>
</div>
<strong>{{ getActivity.name }}</strong>
</template>
</i18n-t>
</div>
<div
v-else
class="my-5 flex md:items-start gap-2"
>
<UTooltip text="I'm offline 🫥">
<UTooltip :text="t('tooltip.offline')">
<span class="cursor-not-allowed h-3 w-3 inline-flex rounded-full bg-red-500 mt-2" />
</UTooltip>
<p class="not-prose">
I'm currently offline. Come back later to see what I'm working on.
{{ t('offline') }}
</p>
</div>
</ClientOnly>
</template>
<i18n lang="json">
{
"en": {
"offline": "I'm currently offline. Come back later to see what I'm working on.",
"working": "I'm actually working on {project}, editing {state}, using {editor}. I've started {start}, the {format}.",
"idling": "I'm idling on my computer with {editor} running in background.",
"tooltip": {
"online": "I'm online 👋",
"offline": "I'm offline 🫥",
"idling": "I'm sleeping 😴"
},
"separator": "at"
},
"fr": {
"offline": "Je suis actuellement hors ligne. Revenez plus tard pour voir sur quoi je travaille.",
"working": "Je travaille actuellement sur {project}, éditant {state}, en utilisant {editor}. J'ai commencé il y a {start}, le {format}.",
"idling": "Je suis en veille sur mon ordinateur avec {editor} en arrière-plan.",
"tooltip": {
"online": "Je suis connecté 👋",
"offline": "Je suis déconnecté 🫥",
"idling": "Je dors 😴"
},
"separator": "à"
}
}
</i18n>

View File

@@ -8,11 +8,25 @@
class="transform -rotate-12 duration-300 group-hover:animate-wave"
name="i-ph-hand-pointing-duotone"
/>
<p>Hover the bold texts to find out more about me.</p>
<p>{{ t('quote') }}</p>
</div>
</ClientOnly>
</template>
<script lang="ts" setup>
const { width } = useWindowSize()
const { t } = useI18n({
useScope: 'local'
})
</script>
<i18n lang="json">
{
"en": {
"quote": "Hover the bold texts to find out more about me."
},
"fr": {
"quote": "Survolez les textes en gras pour en savoir plus sur moi."
}
}
</i18n>

View File

@@ -17,10 +17,12 @@ defineProps({
</script>
<template>
<ClientOnly>
<UTooltip
:popper="{ placement: position }"
:text="hover"
>
<strong class="leading-3 cursor-help">{{ text }}</strong>
</UTooltip>
</ClientOnly>
</template>

View File

@@ -8,7 +8,7 @@ defineProps({
</script>
<template>
<span class="inline">
<span class="inline-flex items-center">
<UIcon
class="mb-1 mr-1"
:name="icon"

View File

@@ -5,15 +5,31 @@
<div class="flex items-center w-12 h-12">
<NuxtImg
alt="Arthur Danjou picture"
class="w-full h-full"
class="w-full h-full hover:rotate-[360deg] duration-500 transform-gpu"
src="/favicon.png"
/>
</div>
</UTooltip>
</div>
<p class="not-prose">
Hello everyone! Thanks for visiting my portfolio. Please leave whatever you like to say, such as suggestions,
appreciations, questions or anything!
{{ t('quote') }}
</p>
</div>
</template>
<script lang="ts" setup>
const { t } = useI18n({
useScope: 'local'
})
</script>
<i18n lang="json">
{
"en": {
"quote": "Hello everyone! Thanks for visiting my portfolio. Please leave whatever you like to say, such as suggestions, appreciations, questions or anything!"
},
"fr": {
"quote": "Bonjour tout le monde ! Merci de visiter mon portfolio. N'hésitez pas à laisser ce que vous avez à dire, comme des suggestions, des appréciations, des questions ou autre chose !"
}
}
</i18n>

View File

@@ -2,30 +2,65 @@
import type { Stats } from '~~/types'
const { data: stats } = await useFetch<Stats>('/api/stats')
const { t } = useI18n({
useScope: 'local'
})
</script>
<template>
<ClientOnly>
<p v-if="stats">
I collect some data for {{ useTimeAgo(new Date(stats.coding.data.range.start)).value }}, started the
<HoverText
:text="useDateFormat(new Date(stats.coding.data.range.start), 'Do MMMM YYYY').value"
hover="That was so long ago 🫣"
/>
.
I've coded for a total of
<HoverText
:text="usePrecision(stats.coding.data.grand_total.total_seconds_including_other_language / 3600, 0).value"
hover="That's a lot 😮"
/>
hours.
My best editors are
{{ stats.editors.data.slice(0, 2).map(editor => `${editor.name} (${editor.percent}%)`).join(' and ') }}.
<template v-if="stats.os.data[0]">
My best OS is {{ stats.os.data[0].name }} ({{ stats.os.data[0].percent }}%).
<i18n-t
v-if="stats"
keypath="stats"
tag="p"
>
<template #time>
{{ useTimeAgo(new Date(stats.coding.data.range.start)).value }}
</template>
My top languages are
{{ stats.languages.data.slice(0, 2).map(language => `${language.name} (${language.percent}%)`).join(' and ') }}.
</p>
<template #date>
<HoverText
:hover="t('tooltip.date')"
:text="useDateFormat(new Date(stats.coding.data.range.start), 'Do MMMM YYYY').value"
/>
</template>
<template #hours>
<HoverText
:hover="t('tooltip.hours')"
:text="usePrecision(stats.coding.data.grand_total.total_seconds_including_other_language / 3600, 0).value"
/>
</template>
<template
v-if="stats.os.data[0]"
#os
>
{{ stats.os.data[0].name }} ({{ stats.os.data[0].percent }}%)
</template>
<template #languages>
{{
stats.languages.data.slice(0, 2).map(language => `${language.name} (${language.percent}%)`).join(t('separator'))
}}
</template>
</i18n-t>
</ClientOnly>
</template>
<i18n lang="json">
{
"en": {
"stats": "I collect some data for {time}, started the {date}. I've coded for a total of {hours} hours. My best editors are {editors}. My best OS is {os}. My top languages are {languages}.",
"separator": " and ",
"tooltip": {
"date": "That was so long ago 🫣",
"hours": "That's a lot 😮"
}
},
"fr": {
"stats": "Je collecte des données depuis {time}, commencé le {date}. J'ai codé un total de {hours} heures. Mes meilleurs éditeurs sont {editors}. Mon meilleur OS est {os}. Mes langages préférés sont {languages}.",
"separator": " et ",
"tooltip": {
"date": "C'était il y a si longtemps 🫣",
"hours": "C'est beaucoup 😮"
}
}
}
</i18n>

View File

@@ -8,6 +8,8 @@ defineProps({
required: true
}
})
const { locale } = useI18n()
</script>
<template>
@@ -16,7 +18,7 @@ defineProps({
{{ item.name }}
</p>
<p class="text-sm">
{{ item.description }}
{{ locale === 'en' ? item.description.en : item.description.fr }}
</p>
</li>
</template>

View File

@@ -1,10 +1,12 @@
<template>
<main class="!max-w-none prose dark:prose-invert">
<ContentDoc path="/" />
<ContentDoc :path="`/home/${locale}`" />
</main>
</template>
<script lang="ts" setup>
const { locale } = useI18n()
useSeoMeta({
title: 'Arthur Danjou • Mathematics Lover and IA Enthusiast',
titleTemplate: '%s',

View File

@@ -1,13 +1,16 @@
<script setup lang="ts">
const description = 'Software I use, gadgets I love, and other things I recommend. Heres a big list of all of my favorite stuff.'
useSeoMeta({
title: 'Things I use',
description
const { t } = useI18n({
useScope: 'local'
})
const { data: items } = await useAsyncData('uses', () =>
queryContent('/uses').find()
useSeoMeta({
title: 'Things I use',
description: t('description')
})
const { data: items } = await useAsyncData('uses', () => queryContent('/uses').find()
)
const hardware = items.value!.filter(item => item.category === 'hardware')
const software = items.value!.filter(item => item.category === 'software')
const ide = items.value!.filter(item => item.category === 'ide')
@@ -17,18 +20,18 @@ const stack = items.value!.filter(item => item.category === 'stack')
<template>
<main>
<AppTitle
:description="description"
title="Uses"
:description="t('description')"
:title="t('title')"
/>
<div class="mt-12 space-y-24">
<UsesList title="Hardware">
<UsesList :title="t('hardware')">
<UsesItem
v-for="(item, id) in hardware"
:key="id"
:item="item"
/>
</UsesList>
<UsesList title="Software">
<UsesList :title="t('software')">
<UsesItem
v-for="(item, id) in software"
:key="id"
@@ -37,7 +40,7 @@ const stack = items.value!.filter(item => item.category === 'stack')
</UsesList>
<ul class="space-y-8">
<UDivider
label="IDE & Font"
:label="t('ide')"
size="xs"
/>
<li class="relative">
@@ -47,7 +50,7 @@ const stack = items.value!.filter(item => item.category === 'stack')
class="mx-auto md:w-4/5"
/>
<p class="mt-2 text-sm italic flex gap-2 justify-center items-center">
My IntelliJ Idea Ultimate IDE
{{ t('intellij') }}
</p>
</li>
<UsesItem
@@ -56,7 +59,7 @@ const stack = items.value!.filter(item => item.category === 'stack')
:item="item"
/>
</ul>
<UsesList title="Stack">
<UsesList :title="t('stack')">
<UsesItem
v-for="(item, id) in stack"
:key="id"
@@ -66,3 +69,26 @@ const stack = items.value!.filter(item => item.category === 'stack')
</div>
</main>
</template>
<i18n lang="json">
{
"en": {
"title": "Uses",
"description": "Software I use, gadgets I love, and other things I recommend. Heres a big list of all of my favorite stuff.",
"hardware": "Hardware",
"software": "Software",
"ide": "IDE & Font",
"stack": "Stack",
"intellij": "My IntelliJ Idea Ultimate IDE"
},
"fr": {
"title": "Mes usages",
"description": "Logiciels que j'utilise, gadgets que j'adore et autres choses que je recommande. Voici une grande liste de toutes mes choses préférées.",
"hardware": "Matériel",
"software": "Logiciel",
"ide": "IDE & Police",
"stack": "Stack",
"intellij": "Mon IDE IntelliJ Idea Ultimate"
}
}
</i18n>

View File

@@ -6,6 +6,13 @@ const {
refresh
} = await useAsyncData(`writing:${route.params.slug}:db`, () => $fetch(`/api/posts/${route.params.slug}`, { method: 'POST' }))
const { locale, locales } = useI18n()
const currentLocale = computed(() => locales.value.filter(l => l.code === locale.value)[0])
const { t } = useI18n({
useScope: 'local'
})
function top() {
window.scrollTo({
top: 0,
@@ -15,7 +22,7 @@ function top() {
}
const { copy, copied } = useClipboard({
source: `https://arthurdanjou.fr/writing/${route.params.slug}`,
source: `https://arthurdanjou.fr/writings/${route.params.slug}`,
copiedDuring: 4000
})
@@ -29,8 +36,8 @@ function getDetails() {
const likes = postDB.value?.likes ?? 0
const views = postDB.value?.views ?? 0
const like = likes > 1 ? 'likes' : 'like'
const view = views > 1 ? 'views' : 'view'
const like = likes > 1 ? t('likes.many') : t('likes.one')
const view = views > 1 ? t('views.many') : t('views.one')
return `${likes} ${like} · ${views} ${view}`
}
@@ -50,7 +57,7 @@ async function handleLike() {
<template>
<main v-if="post && postDB">
<div class="flex">
<NuxtLink
<NuxtLinkLocale
class="flex items-center gap-2 mb-8 group text-sm hover:text-black dark:hover:text-white duration-300"
to="/writings"
>
@@ -59,9 +66,18 @@ async function handleLike() {
name="i-ph-arrow-left-duotone"
size="20"
/>
Go back
</NuxtLink>
{{ t('back') }}
</NuxtLinkLocale>
</div>
<UAlert
v-if="locale !== 'en'"
:description="t('alert.description')"
:title="t('alert.title')"
class="mb-8"
color="red"
icon="i-ph-warning-duotone"
variant="outline"
/>
<p class="border-l-2 pl-2 border-gray-300 dark:border-gray-700 rounded-sm">
{{ getDetails() }}
</p>
@@ -73,7 +89,8 @@ async function handleLike() {
{{ post.title }}
</h1>
<p class="text-sm text-neutral-500">
{{ useDateFormat(post.publishedAt, 'DD MMMM YYYY').value }} · {{ post.readingTime }}min long
{{ useDateFormat(post.publishedAt, 'DD MMMM YYYY', { locales: currentLocale!.code ?? 'en' }).value }} ·
{{ post.readingTime }}min long
</p>
</div>
<p class="mt-4 text-base">
@@ -105,10 +122,14 @@ async function handleLike() {
icon="i-ph-hands-clapping-duotone"
/>
<div class="space-y-8">
<p>
Thanks for reading this post! If you liked it, please consider sharing it with your friends.
<strong>Don't forget to leave a like!</strong>
</p>
<i18n-t
keypath="thanks"
tag="p"
>
<template #like>
<strong>{{ t('like') }}</strong>
</template>
</i18n-t>
<div class="flex gap-4 items-center flex-wrap">
<UButton
:label="postDB?.likes > 1 ? `${postDB?.likes} likes` : `${postDB?.likes} like`"
@@ -121,7 +142,7 @@ async function handleLike() {
<UButton
color="white"
icon="i-ph-arrow-fat-lines-up-duotone"
label="Go to top"
:label="t('top')"
size="lg"
variant="solid"
@click.prevent="top()"
@@ -130,7 +151,7 @@ async function handleLike() {
v-if="copied"
color="green"
icon="i-ph-check-square-duotone"
label="Link copied"
:label="t('link.copied')"
size="lg"
variant="outline"
@click.prevent="copy()"
@@ -139,7 +160,7 @@ async function handleLike() {
v-else
color="white"
icon="i-ph-square-duotone"
label="Copy link"
:label="t('link.copy')"
size="lg"
variant="solid"
@click.prevent="copy()"
@@ -157,3 +178,52 @@ async function handleLike() {
@apply no-underline;
}
</style>
<i18n lang="json">
{
"en": {
"likes": {
"one": "like",
"many": "likes"
},
"views": {
"one": "view",
"many": "views"
},
"alert": {
"title": "Translations alert!",
"description": "Due to time constraints, all article translations will be available only in English. Thank you for your understanding."
},
"thanks": "Thanks for reading this post! If you liked it, please consider sharing it with your friends. {like}",
"like": "Don't forget to leave a like!",
"link": {
"copied": "Link copied",
"copy": "Copy link"
},
"top": "Go to top",
"back": "Go back"
},
"fr": {
"likes": {
"one": "like",
"many": "likes"
},
"views": {
"one": "vue",
"many": "vues"
},
"alert": {
"title": "Attentions aux traductions!",
"description": "Par soucis de temps, toutes les traductions des articles seront disponibles uniquement en anglais. Merci de votre compréhension."
},
"thanks": "Merci d'avoir lu cet article! Si vous l'avez aimé, n'hésitez pas à le partager avec vos amis. {like}",
"like": "N'oubliez pas de laisser un like!",
"link": {
"copied": "Lien copié",
"copy": "Copier le lien"
},
"top": "Remonter en haut",
"back": "Retourner en arrière"
}
}
</i18n>

View File

@@ -1,8 +1,10 @@
<script setup lang="ts">
const description = 'All my thoughts on programming, mathematics, artificial intelligence design, etc., are put together in chronological order. I also write about my projects, my discoveries, and my thoughts.'
const { t, locale } = useI18n({
useScope: 'local'
})
useSeoMeta({
title: 'My Shelf',
description
description: t('description')
})
const { data: writings } = await useAsyncData('all-writings', () =>
@@ -17,8 +19,8 @@ function getDetails(slug: string) {
const writing = writingsDB.value!.find(writing => writing.slug === slug)
if (!writing) return ''
const like = writing.likes! > 1 ? 'likes' : 'like'
const view = writing.views! > 1 ? 'views' : 'view'
const like = writing.likes! > 1 ? t('likes.many') : t('likes.one')
const view = writing.views! > 1 ? t('views.many') : t('views.one')
return `${writing.likes} ${like} · ${writing.views} ${view}`
}
@@ -27,8 +29,17 @@ function getDetails(slug: string) {
<template>
<main>
<AppTitle
:description="description"
title="Writing on my life, development and my passions."
:description="t('description')"
:title="t('title')"
/>
<UAlert
v-if="locale !== 'en'"
:description="t('alert.description')"
:title="t('alert.title')"
class="mt-12"
color="red"
icon="i-ph-warning-duotone"
variant="outline"
/>
<ul class="mt-12 space-y-24">
<li
@@ -64,3 +75,40 @@ function getDetails(slug: string) {
</ul>
</main>
</template>
<i18n lang="json">
{
"en": {
"title": "Writing on my life, development and my passions.",
"description": "All my thoughts on programming, mathematics, artificial intelligence design, etc., are put together in chronological order. I also write about my projects, my discoveries, and my thoughts.",
"likes": {
"one": "like",
"many": "likes"
},
"views": {
"one": "view",
"many": "views"
},
"alert": {
"title": "Translations alert!",
"description": "Due to time constraints, all article translations will be available only in English. Thank you for your understanding."
}
},
"fr": {
"title": "Écrits sur ma vie, le développement et mes passions.",
"description": "Toutes mes réflexions sur la programmation, les mathématiques, la conception de l'intelligence artificielle, etc., sont mises en ordre chronologique. J'écris aussi sur mes projets, mes découvertes et mes pensées.",
"likes": {
"one": "like",
"many": "likes"
},
"views": {
"one": "vue",
"many": "vues"
},
"alert": {
"title": "Attentions aux traductions!",
"description": "Par soucis de temps, toutes les traductions des articles seront disponibles uniquement en anglais. Merci de votre compréhension."
}
}
}
</i18n>

49
content/home/fr.md Normal file
View File

@@ -0,0 +1,49 @@
Bonjour, je suis Arthur Danjou, étudiant en mathématiques à la faculté des sciences de Paris-Saclay en France.
Avec une :hover-text{hover="La technologie évolue beaucoup trop vite 🤯" position="top" text="compréhension profonde des
technologies émergentes"}, je suis au cœur d'un domaine en pleine expansion. Ma formation en :hover-text{hover="Les
mathématiques sont ma principale passion ∑" position="right" text="mathématiques"} me donne une longueur d'avance pour
comprendre les concepts et les théories qui sous-tendent ces :hover-text{hover="Ma deuxième passion 📱" text="
technologies"} et à les concevoir efficacement.
En tant qu'ingénieur logiciel et étudiant en mathématiques, mon :hover-text{hover="Mon sac de connaissances 🎒" text="
expertise"} couvre
:prose-icon[TypeScript]{icon="i-logos-typescript-icon"},
:prose-icon[Vue]{icon="i-logos:vue"},
:prose-icon[Nuxt]{icon="i-logos:nuxt-icon"},
:prose-icon[Adonis]{icon="i-logos:adonisjs-icon"},
:prose-icon[Java]{icon="i-logos:java"},
:prose-icon[Python]{icon="i-logos:python"},
:prose-icon[R]{icon="i-logos:r-lang"},
ce qui me permet de :hover-text{hover="Comprendre rapidement la complexité des projets 🏎️" text="comprendre"} les
différents besoins des projets mathématiques et de proposer les meilleures solutions.
J'ai également appris d'autres technologies importantes, telles que
:prose-icon[Docker]{icon="i-logos:docker-icon"},
:prose-icon[Redis]{icon="i-logos:redis"},
:prose-icon[MySQL]{icon="i-logos:mysql-icon"} et
:prose-icon[Git]{icon="i-logos:git-icon"} pour :hover-text{hover="Toutes ces technologies se complètent 🔗" text="
compléter"} mes connaissances.
Je suis :hover-text{hover="Je dois toujours chercher à être à jour 🖥️" position="top" text="constamment"} dans
l'apprentissage de nouvelles choses, de la technologie à la finance en passant par l'entrepreneuriat. J'aime :
hover-text{hover="J'aime partager et aider les autres 🫂" text="partager"} mes connaissances et apprendre de nouveaux
théorèmes et technologies. Je suis une personne :hover-text{hover="Je cherche à découvrir de nouvelles choses" text="
curieuse"} et désireuse de continuer à apprendre et à grandir tout au long de ma vie.
Outre la programmation, j'aime le :hover-text{hover="Le sport me permet de dépenser de l'énergie 🏋️‍♂️" text="sport"}
et :hover-text{hover="Les voyages me libèrent et m'évadent ✈️" text="voyager"}.
Ma passion, mon engagement et mon envie d'apprendre et de progresser sont les qualités qui me permettent de réussir dans
ma :hover-text{hover="Carrière déjà commencée et loin d'être terminée 😎" text="carrière"} et mes :hover-text{hover="Il
ne me reste que 2 ans d'études 💪" text="études"}.
::stats
::
::activity
::
::quote
::
::catch-phrase
::

View File

@@ -1,5 +1,8 @@
{
"name": "Apple AirPods Pro",
"description": "Probably my most used item after my phone and laptop. I use them for everything from listening to music to taking calls. They are super convenient and the sound quality is great.",
"description": {
"en": "Probably my most used item after my phone and laptop. I use them for everything from listening to music to taking calls. They are super convenient and the sound quality is great.",
"fr": "Probablement l'objet que j'utilise le plus après mon téléphone et mon ordinateur portable. Je les utilise pour tout, de l'écoute de musique à la prise d'appels. Ils sont super pratiques et la qualité sonore est excellente."
},
"category": "hardware"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Apple iPad Air",
"description": "I use my iPad to read books, watch movies, and browse the web, but also to take notes and write some equations during my math classes.",
"description": {
"en": "I use my iPad to read books, watch movies, and browse the web, but also to take notes and write some equations during my math classes.",
"fr": "J'utilise mon iPad pour lire des livres, regarder des films et naviguer sur le web, mais aussi pour prendre des notes et écrire des équations pendant mes cours de mathématiques."
},
"category": "hardware"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Apple iPhone 14 Pro",
"description": "I don't upgrade my phone every year, but when I do, I go for the best. The iPhone 14 Pro is the best phone on the market, and I'm excited to get my hands on it.",
"description": {
"en": "I don't upgrade my phone every year, but when I do, I go for the best. The iPhone 14 Pro is the best phone on the market, and I'm excited to get my hands on it.",
"fr": "Je n'améliore pas mon téléphone chaque année, mais quand je le fais, je vais pour le meilleur. L'iPhone 14 Pro est le meilleur téléphone sur le marché, et je suis excité de mettre la main dessus."
},
"category": "hardware"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Custom Built Gaming PC",
"description": "I have bought a customized computer for the gaming. I have chosen an Intel Core i5-10400F, with 16Go DDR4 and my graphical card is a RTX 2060. I use Windows 11.",
"name": "Apple MacBook Pro 13'",
"description": {
"en": "My main programming computer is a MacBook Pro 13' 2020 with the Apple M1 Chip and 16Go RAM. I use MacOS Sorona.",
"fr": "Mon ordinateur principal pour programmer est un MacBook Pro 13' 2020 avec la puce Apple M1 et 16Go de RAM. J'utilise MacOS Sorona."
},
"category": "hardware"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Apple Suite",
"description": "I'm using the Apple Suite including Mail, Calendar, Notes, Music and Reminders for my daily organization.",
"description": {
"en": "I'm using the Apple Suite including Mail, Calendar, Notes, Music and Reminders for my daily organization.",
"fr": "J'utilise la suite Apple comprenant Mail, Calendar, Notes, Music et Reminders pour mon organisation quotidienne."
},
"category": "software"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Discord",
"description": "I'm using Discord for chatting and talking with my friends and my customers and discussing with some community members.",
"description": {
"en": "I'm using Discord for chatting and talking with my friends and my customers and discussing with some community members.",
"fr": "J'utilise Discord pour discuter et parler avec mes amis et mes clients et discuter avec certains membres de la communauté."
},
"category": "software"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Apple MacBook Pro 13'",
"description": "My main programming computer is a MacBook Pro 13' 2020 with the Apple M1 Chip and 16Go RAM. I use MacOS Sorona.",
"name": "Custom Built Gaming PC",
"description": {
"en": "I have bought a customized computer for the gaming. I have chosen an Intel Core i5-10400F, with 16Go DDR4 and my graphical card is a RTX 2060. I use Windows 11.",
"fr": "J'ai acheté un ordinateur personnalisé pour le jeu. J'ai choisi un Intel Core i5-10400F, avec 16Go DDR4 et ma carte graphique est une RTX 2060. J'utilise Windows 11."
},
"category": "hardware"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Google Chrome",
"description": "I'm using Google Chrome for browsing, the dev tool, and the extension market.",
"description": {
"en": "I'm using Google Chrome for browsing, the dev tool, and the extension market.",
"fr": "J'utilise Google Chrome pour naviguer, l'outil de développement et le marché des extensions."
},
"category": "software"
}

View File

@@ -1,5 +1,8 @@
{
"name": "JetBrains Suite (IntelliJ IDEA Ultimate, PyCharm Professional, WebStorm, DataGrip)",
"description": "I use JetBrains Suite for development for 7 years. It's the best IDEs for Java, Python, JavaScript, SQL, and more. I used this suite to develop and to create this website.",
"description": {
"en": "I use JetBrains Suite for development for 7 years. It's the best IDEs for Java, Python, JavaScript, SQL, and more. I used this suite to develop and to create this website.",
"fr": "J'utilise la suite JetBrains pour le développement depuis 7 ans. C'est le meilleur IDE pour Java, Python, JavaScript, SQL et plus encore. J'ai utilisé cette suite pour développer et créer ce site web."
},
"category": "ide"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Logitech G203 LightSync Black",
"description": "This gaming mouse is designed to be the perfect gaming companion. With a classic design and simple layout, this mouse is perfect for any gamer.",
"description": {
"en": "This gaming mouse is designed to be the perfect gaming companion. With a classic design and simple layout, this mouse is perfect for any gamer.",
"fr": "Cette souris de jeu est conçue pour être le compagnon de jeu parfait. Avec un design classique et une disposition simple, cette souris est parfaite pour tout joueur."
},
"category": "hardware"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Notion",
"description": "Notion is my all-in-one note-taking, kanban boards, wikis, and draft notebook.",
"description": {
"en": "Notion is my all-in-one note-taking, kanban boards, wikis, and draft notebook.",
"fr": "Notion est mon outil de prise de notes, de kanban, de wikis et de brouillon tout-en-un."
},
"category": "software"
}

View File

@@ -1,5 +1,8 @@
{
"name": "RayCast",
"description": "Raycast is my extendable launcher replacing Apple Spotlight. It lets me complete tasks, calculate, share common links, and much more thanks to the extensions.",
"description": {
"en": "Raycast is my extendable launcher replacing Apple Spotlight. It lets me complete tasks, calculate, share common links, and much more thanks to the extensions.",
"fr": "Raycast est mon lanceur extensible remplaçant Apple Spotlight. Il me permet d'accomplir des tâches, de calculer, de partager des liens communs et bien plus encore grâce aux extensions."
},
"category": "software"
}

View File

@@ -1,5 +1,8 @@
{
"name": "FrontEnd & BackEnd",
"description": "I use TypeScript, Vue 3 with Nuxt 3, Nuxt Stack (UI, Hub, Content, Studio) & TailwindCss for FrontEnd. Nuxt (powered by Nitro) and AdonisJs are used for BackEnd depending on project complexity. PostgreSQL are used for database, Redis for caching. Docker is used for containerization. Apps are deployed on NuxtHub (powered by CloudFlare) or Vercel.",
"description": {
"en": "I use TypeScript, Vue 3 with Nuxt 3, Nuxt Stack (UI, Hub, Content, Studio) & TailwindCss for FrontEnd. Nuxt (powered by Nitro) and AdonisJs are used for BackEnd depending on project complexity. PostgreSQL are used for database, Redis for caching. Docker is used for containerization. Apps are deployed on NuxtHub (powered by CloudFlare) or Vercel.",
"fr": "J'utilise TypeScript, Vue 3 avec Nuxt 3, Nuxt Stack (UI, Hub, Content, Studio) & TailwindCss pour le FrontEnd. Nuxt (alimenté par Nitro) et AdonisJs sont utilisés pour le BackEnd en fonction de la complexité du projet. PostgreSQL est utilisé pour la base de données, Redis pour le caching. Docker est utilisé pour la conteneurisation. Les applications sont déployées sur NuxtHub (alimenté par CloudFlare) ou Vercel."
},
"category": "stack"
}

View File

@@ -1,5 +1,8 @@
{
"name": "SteelSeries Apex 9 TKL",
"description": "This TKL keyboard is a great choice for gamers who want a compact keyboard with a lot of features.",
"description": {
"en": "This TKL keyboard is a great choice for gamers who want a compact keyboard with a lot of features.",
"fr": "Ce clavier TKL est un excellent choix pour les joueurs qui veulent un clavier compact avec de nombreuses fonctionnalités."
},
"category": "hardware"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Theme and Font",
"description": "My theme is Catppuccin Macchiato, a community-driven pastel theme that aims to be the middle ground between low and high contrast themes. My main fonts are Vercel Geist and JetBrains Mono",
"description": {
"en": "My theme is Catppuccin Macchiato, a community-driven pastel theme that aims to be the middle ground between low and high contrast themes. My main fonts are Vercel Geist and JetBrains Mono",
"fr": "Mon thème est Catppuccin Macchiato, un thème pastel piloté par la communauté qui vise à être le juste milieu entre les thèmes à faible et à fort contraste. Mes polices principales sont Vercel Geist et JetBrains Mono"
},
"category": "ide"
}

View File

@@ -1,5 +1,8 @@
{
"name": "Warp",
"description": "Warp is a modern, Rust-based terminal reimagined with AI and collaborative tools for better productivity. I'm loving it so far!",
"description": {
"en": "Warp is a modern, Rust-based terminal reimagined with AI and collaborative tools for better productivity. I'm loving it so far!",
"fr": "Warp est un terminal moderne basé sur Rust réimaginé avec l'IA et des outils collaboratifs pour une meilleure productivité. Je l'adore jusqu'à présent !"
},
"category": "software"
}

View File

@@ -21,7 +21,8 @@ export default defineNuxtConfig({
'@vueuse/nuxt',
'@nuxtjs/google-fonts',
'@nuxthq/studio',
'@nuxt/image'
'@nuxt/image',
'@nuxtjs/i18n'
],
// Nuxt Hub
@@ -51,6 +52,23 @@ export default defineNuxtConfig({
timeline: { enabled: true }
},
// Nuxt I18N
i18n: {
strategy: 'no_prefix',
locales: [
{
code: 'en',
iso: 'en-EN',
icon: 'i-twemoji-flag-united-kingdom'
}, {
code: 'fr',
iso: 'fr-FR',
icon: 'i-twemoji-flag-france'
}
],
defaultLocale: 'en'
},
// Nuxt Eslint
eslint: {
config: {
@@ -98,5 +116,7 @@ export default defineNuxtConfig({
resume: ''
}
}
}
},
compatibilityDate: '2024-07-08'
})

View File

@@ -12,30 +12,31 @@
"db:generate": "drizzle-kit generate"
},
"dependencies": {
"@iconify/json": "^2.2.223",
"@nuxt/content": "^2.13.0",
"@iconify/json": "^2.2.225",
"@nuxt/content": "^2.13.1",
"@nuxt/eslint": "^0.3.13",
"@nuxt/image": "^1.7.0",
"@nuxt/ui": "^2.17.0",
"@nuxthq/studio": "^2.0.3",
"@nuxthub/core": "^0.7.0",
"@nuxtjs/google-fonts": "^3.2.0",
"@nuxtjs/i18n": "^8.3.1",
"drizzle-orm": "^0.31.2",
"h3-zod": "^0.5.3",
"nuxt": "^3.12.2",
"nuxt": "^3.12.3",
"zod": "^3.23.8"
},
"devDependencies": {
"@nuxt/devtools": "^1.3.7",
"@nuxt/devtools": "^1.3.9",
"@nuxt/eslint-config": "^0.3.13",
"@nuxt/ui": "^2.17.0",
"@types/node": "^20.14.9",
"@types/node": "^20.14.10",
"@vueuse/core": "^10.11.0",
"@vueuse/nuxt": "^10.11.0",
"drizzle-kit": "^0.22.8",
"eslint": "^9.6.0",
"typescript": "^5.5.2",
"vue-tsc": "^2.0.24",
"wrangler": "^3.62.0"
"typescript": "^5.5.3",
"vue-tsc": "^2.0.26",
"wrangler": "^3.63.1"
}
}

2756
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -45,19 +45,3 @@ export const IDEs = [
{ name: 'IntelliJ IDEA Ultimate', icon: 'i-logos-intellij-idea' },
{ name: 'WebStorm', icon: 'i-logos-webstorm' }
]
export interface UsesItem {
name: string
description: string
}
export interface CodingActivity {
name: string
active: boolean
project: string
state: string
start: {
ago: string
formated: string
}
}