feat(SPA): i18n internationzalization, english and french
This commit is contained in:
1 parent
7f1e8e572d
commit
721311a643
88 files changed
+4438
-1148
No files matched your search
+59
-29
@@ -1,7 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { t, formatNumber } from '@/i18n'
|
||||
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useToast } from '@nuxt/ui/composables'
|
||||
import { useLocalizedToast } from '@/i18n/presentation'
|
||||
import DashboardShell from '@/components/DashboardShell.vue'
|
||||
import ArticleStatusBadge from '@/components/ArticleStatusBadge.vue'
|
||||
import NewArticleModal from '@/components/NewArticleModal.vue'
|
||||
@@ -19,7 +21,7 @@ const RECENT_LIST_SIZE = 5
|
||||
type LoadState = 'loading' | 'default' | 'empty' | 'error'
|
||||
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
const toast = useLocalizedToast()
|
||||
const session = useSessionStore()
|
||||
const update = useUpdateStore()
|
||||
const { listArticles } = useArticles()
|
||||
@@ -32,12 +34,12 @@ let skeletonTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function greetingForHour(hour: number) {
|
||||
if (hour < 12) {
|
||||
return 'Good morning'
|
||||
return t('Good morning')
|
||||
}
|
||||
if (hour < 18) {
|
||||
return 'Good afternoon'
|
||||
return t('Good afternoon')
|
||||
}
|
||||
return 'Good evening'
|
||||
return t('Good evening')
|
||||
}
|
||||
|
||||
const greeting = computed(() => {
|
||||
@@ -48,8 +50,8 @@ const greeting = computed(() => {
|
||||
|
||||
const subtitle = computed(() =>
|
||||
loadState.value === 'empty'
|
||||
? 'Welcome to RenewCMS.'
|
||||
: "Here's where things stand on your site.",
|
||||
? t('Welcome to RenewCMS.')
|
||||
: t("Here's where things stand on your site."),
|
||||
)
|
||||
|
||||
const stats = computed(() => {
|
||||
@@ -62,9 +64,24 @@ const stats = computed(() => {
|
||||
})
|
||||
|
||||
const statTiles = computed(() => [
|
||||
{ label: 'articles', value: stats.value.total },
|
||||
{ label: 'online', value: stats.value.online },
|
||||
{ label: 'drafts', value: stats.value.drafts },
|
||||
{
|
||||
get label() {
|
||||
return t('articles')
|
||||
},
|
||||
value: stats.value.total,
|
||||
},
|
||||
{
|
||||
get label() {
|
||||
return t('online')
|
||||
},
|
||||
value: stats.value.online,
|
||||
},
|
||||
{
|
||||
get label() {
|
||||
return t('drafts')
|
||||
},
|
||||
value: stats.value.drafts,
|
||||
},
|
||||
])
|
||||
|
||||
const recentArticles = computed(() =>
|
||||
@@ -106,13 +123,17 @@ function openNewArticleModal() {
|
||||
|
||||
function onArticleCreated(article: Article) {
|
||||
router.push(`/article/${article.id}/edit`)
|
||||
toast.add({
|
||||
toast.add(() => ({
|
||||
id: `article-created-${article.id}`,
|
||||
color: 'success',
|
||||
icon: 'i-lucide-circle-check',
|
||||
title: 'Draft created',
|
||||
description: `"${article.title}" is saved as a draft.`,
|
||||
})
|
||||
get title() {
|
||||
return t('Draft created')
|
||||
},
|
||||
get description() {
|
||||
return t('\u0022{0}\u0022 is saved as a draft.', [article.title])
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
const readyBannerDismissed = ref(false)
|
||||
@@ -154,7 +175,7 @@ onMounted(loadArticles)
|
||||
variant="ghost"
|
||||
class="size-11 justify-center text-highlighted sm:hidden"
|
||||
:ui="{ leadingIcon: 'size-5' }"
|
||||
aria-label="New article"
|
||||
:aria-label="t('New article')"
|
||||
@click="openNewArticleModal"
|
||||
/>
|
||||
</template>
|
||||
@@ -172,7 +193,7 @@ onMounted(loadArticles)
|
||||
class="hidden px-3 sm:flex"
|
||||
@click="openNewArticleModal"
|
||||
>
|
||||
New article
|
||||
{{ t('New article') }}
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
@@ -198,7 +219,7 @@ onMounted(loadArticles)
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="text-2xl font-semibold text-highlighted">{{
|
||||
tile.value
|
||||
formatNumber(tile.value)
|
||||
}}</span>
|
||||
<span class="text-muted">{{ tile.label }}</span>
|
||||
</template>
|
||||
@@ -207,9 +228,11 @@ onMounted(loadArticles)
|
||||
|
||||
<section class="flex flex-col gap-2.5">
|
||||
<div class="flex items-baseline justify-between">
|
||||
<h2 class="text-lg font-semibold text-highlighted">Recently updated</h2>
|
||||
<h2 class="text-lg font-semibold text-highlighted">
|
||||
{{ t('Recently updated') }}
|
||||
</h2>
|
||||
<RouterLink to="/article" class="text-toned underline underline-offset-3">
|
||||
All articles
|
||||
{{ t('All articles') }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div
|
||||
@@ -249,8 +272,10 @@ onMounted(loadArticles)
|
||||
v-else-if="loadState === 'empty'"
|
||||
variant="outline"
|
||||
icon="i-lucide-file-text"
|
||||
title="Write your first article"
|
||||
description="It starts as a draft, so nothing appears on your site until you publish it."
|
||||
:title="t('Write your first article')"
|
||||
:description="
|
||||
t('It starts as a draft, so nothing appears on your site until you publish it.')
|
||||
"
|
||||
:ui="{
|
||||
root: 'gap-3 rounded-none border border-default px-6 py-12 ring-0 sm:px-6 sm:py-12 lg:px-6 lg:py-12',
|
||||
header: 'max-w-none gap-3',
|
||||
@@ -266,7 +291,7 @@ onMounted(loadArticles)
|
||||
class="h-11 px-4 lg:h-8"
|
||||
@click="openNewArticleModal"
|
||||
>
|
||||
New article
|
||||
{{ t('New article') }}
|
||||
</UButton>
|
||||
</template>
|
||||
</UEmpty>
|
||||
@@ -279,8 +304,8 @@ onMounted(loadArticles)
|
||||
orientation="vertical"
|
||||
:close="false"
|
||||
icon="i-lucide-circle-x"
|
||||
title="Couldn't load your articles"
|
||||
description="Check your connection and try again."
|
||||
:title="t('Couldn\'t load your articles')"
|
||||
:description="t('Check your connection and try again.')"
|
||||
:ui="{ actions: 'mt-2' }"
|
||||
>
|
||||
<template #actions>
|
||||
@@ -292,7 +317,7 @@ onMounted(loadArticles)
|
||||
:ui="{ leadingIcon: 'size-3.5' }"
|
||||
@click="loadArticles"
|
||||
>
|
||||
Try again
|
||||
{{ t('Try again') }}
|
||||
</UButton>
|
||||
</template>
|
||||
</UAlert>
|
||||
@@ -304,16 +329,21 @@ onMounted(loadArticles)
|
||||
<UIcon name="i-lucide-plug" class="size-4.5" />
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h2 class="text-base/5 font-semibold text-highlighted">Connect your website</h2>
|
||||
<h2 class="text-base/5 font-semibold text-highlighted">
|
||||
{{ t('Connect your website') }}
|
||||
</h2>
|
||||
<p class="text-pretty text-muted">
|
||||
Your site reads published articles from RenewCMS. Share this guide with
|
||||
whoever builds it.
|
||||
{{
|
||||
t(
|
||||
'Your site reads published articles from RenewCMS. Share this guide with whoever builds it.',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<RouterLink
|
||||
to="/integration"
|
||||
class="mt-1 self-start font-medium text-highlighted underline underline-offset-3"
|
||||
>
|
||||
Open the guide
|
||||
{{ t('Open the guide') }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user