docus 4.0.4 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/app/app.vue +28 -18
  2. package/app/components/app/AppFooter.vue +2 -31
  3. package/app/components/app/AppFooterLeft.vue +5 -0
  4. package/app/components/app/AppFooterRight.vue +30 -0
  5. package/app/components/app/AppHeader.vue +2 -2
  6. package/app/components/docs/DocsAsideLeftBody.vue +12 -0
  7. package/app/composables/useDocusI18n.ts +5 -3
  8. package/app/error.vue +46 -12
  9. package/app/layouts/docs.vue +1 -10
  10. package/app/pages/[[lang]]/[...slug].vue +5 -1
  11. package/content.config.ts +1 -0
  12. package/i18n/locales/ar.json +5 -1
  13. package/i18n/locales/bn.json +5 -1
  14. package/i18n/locales/ca.json +5 -1
  15. package/i18n/locales/cs.json +5 -1
  16. package/i18n/locales/da.json +5 -1
  17. package/i18n/locales/el.json +5 -1
  18. package/i18n/locales/en.json +5 -1
  19. package/i18n/locales/et.json +5 -1
  20. package/i18n/locales/fr.json +5 -1
  21. package/i18n/locales/he.json +5 -1
  22. package/i18n/locales/hi.json +5 -1
  23. package/i18n/locales/hy.json +5 -1
  24. package/i18n/locales/it.json +18 -0
  25. package/i18n/locales/ja.json +5 -1
  26. package/i18n/locales/kk.json +5 -1
  27. package/i18n/locales/km.json +5 -1
  28. package/i18n/locales/ko.json +5 -1
  29. package/i18n/locales/ky.json +5 -1
  30. package/i18n/locales/lb.json +5 -1
  31. package/i18n/locales/ms.json +5 -1
  32. package/i18n/locales/nb.json +5 -1
  33. package/i18n/locales/sl.json +5 -1
  34. package/i18n/locales/sv.json +5 -1
  35. package/i18n/locales/uk.json +5 -1
  36. package/i18n/locales/ur.json +5 -1
  37. package/i18n/locales/vi.json +5 -1
  38. package/modules/config.ts +35 -3
  39. package/modules/css.ts +5 -2
  40. package/modules/routing.ts +0 -1
  41. package/package.json +2 -1
  42. package/utils/git.ts +5 -5
package/app/app.vue CHANGED
@@ -1,28 +1,15 @@
1
1
  <script setup lang="ts">
2
2
  import type { PageCollections } from '@nuxt/content'
3
- import * as locales from '@nuxt/ui-pro/locale'
3
+ import * as nuxtUiProLocales from '@nuxt/ui-pro/locale'
4
4
 
5
5
  const { seo } = useAppConfig()
6
6
  const site = useSiteConfig()
7
+ const { locale, locales, isEnabled, switchLocalePath } = useDocusI18n()
7
8
 
8
- const { locale, isEnabled } = useDocusI18n()
9
-
10
- const lang = computed(() => locales[locale.value as keyof typeof locales]?.code || 'en')
11
- const dir = computed(() => locales[locale.value as keyof typeof locales]?.dir || 'ltr')
9
+ const lang = computed(() => nuxtUiProLocales[locale.value as keyof typeof nuxtUiProLocales]?.code || 'en')
10
+ const dir = computed(() => nuxtUiProLocales[locale.value as keyof typeof nuxtUiProLocales]?.dir || 'ltr')
12
11
  const collectionName = computed(() => isEnabled.value ? `docs_${locale.value}` : 'docs')
13
12
 
14
- const { data: navigation } = await useAsyncData(`navigation_${collectionName.value}`, () => queryCollectionNavigation(collectionName.value as keyof PageCollections), {
15
- transform: (data) => {
16
- const rootResult = data.find(item => item.path === '/docs')?.children || data || []
17
-
18
- return rootResult.find(item => item.path === `/${locale.value}`)?.children || rootResult
19
- },
20
- watch: [locale],
21
- })
22
- const { data: files } = useLazyAsyncData(`search_${collectionName.value}`, () => queryCollectionSearchSections(collectionName.value as keyof PageCollections), {
23
- server: false,
24
- })
25
-
26
13
  useHead({
27
14
  meta: [
28
15
  { name: 'viewport', content: 'width=device-width, initial-scale=1' },
@@ -44,11 +31,34 @@ useSeoMeta({
44
31
  twitterCard: 'summary_large_image',
45
32
  })
46
33
 
34
+ if (isEnabled.value) {
35
+ const route = useRoute()
36
+ const defaultLocale = useRuntimeConfig().public.i18n.defaultLocale
37
+ onMounted(() => {
38
+ const currentLocale = route.path.split('/')[1]
39
+ if (!locales.some(locale => locale.code === currentLocale)) {
40
+ return navigateTo(switchLocalePath(defaultLocale) as string)
41
+ }
42
+ })
43
+ }
44
+
45
+ const { data: navigation } = await useAsyncData(`navigation_${collectionName.value}`, () => queryCollectionNavigation(collectionName.value as keyof PageCollections), {
46
+ transform: (data) => {
47
+ const rootResult = data.find(item => item.path === '/docs')?.children || data || []
48
+
49
+ return rootResult.find(item => item.path === `/${locale.value}`)?.children || rootResult
50
+ },
51
+ watch: [locale],
52
+ })
53
+ const { data: files } = useLazyAsyncData(`search_${collectionName.value}`, () => queryCollectionSearchSections(collectionName.value as keyof PageCollections), {
54
+ server: false,
55
+ })
56
+
47
57
  provide('navigation', navigation)
48
58
  </script>
49
59
 
50
60
  <template>
51
- <UApp :locale="locales[locale as keyof typeof locales]">
61
+ <UApp :locale="nuxtUiProLocales[locale as keyof typeof nuxtUiProLocales]">
52
62
  <NuxtLoadingIndicator color="var(--ui-primary)" />
53
63
 
54
64
  <AppHeader />
@@ -1,40 +1,11 @@
1
- <script setup lang="ts">
2
- const appConfig = useAppConfig()
3
-
4
- const links = computed(() => [
5
- ...Object.entries(appConfig.socials || {}).map(([key, url]) => ({
6
- 'icon': `i-simple-icons-${key}`,
7
- 'to': url,
8
- 'target': '_blank',
9
- 'aria-label': `${key} social link`,
10
- })),
11
- appConfig.github?.url && {
12
- 'icon': 'i-simple-icons-github',
13
- 'to': appConfig.github.url,
14
- 'target': '_blank',
15
- 'aria-label': 'GitHub repository',
16
- },
17
- ].filter(Boolean))
18
- </script>
19
-
20
1
  <template>
21
2
  <UFooter>
22
3
  <template #left>
23
- <div class="text-sm text-muted">
24
- Copyright © {{ new Date().getFullYear() }}
25
- </div>
4
+ <AppFooterLeft />
26
5
  </template>
27
6
 
28
7
  <template #right>
29
- <template v-if="links.length">
30
- <UButton
31
- v-for="(link, index) of links"
32
- :key="index"
33
- size="sm"
34
- v-bind="{ color: 'neutral', variant: 'ghost', ...link }"
35
- />
36
- </template>
37
- <UColorModeButton />
8
+ <AppFooterRight />
38
9
  </template>
39
10
  </UFooter>
40
11
  </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="text-sm text-muted">
3
+ Copyright © {{ new Date().getFullYear() }}
4
+ </div>
5
+ </template>
@@ -0,0 +1,30 @@
1
+ <script setup lang="ts">
2
+ const appConfig = useAppConfig()
3
+
4
+ const links = computed(() => [
5
+ ...Object.entries(appConfig.socials || {}).map(([key, url]) => ({
6
+ 'icon': `i-simple-icons-${key}`,
7
+ 'to': url,
8
+ 'target': '_blank',
9
+ 'aria-label': `${key} social link`,
10
+ })),
11
+ appConfig.github?.url && {
12
+ 'icon': 'i-simple-icons-github',
13
+ 'to': appConfig.github.url,
14
+ 'target': '_blank',
15
+ 'aria-label': 'GitHub repository',
16
+ },
17
+ ].filter(Boolean))
18
+ </script>
19
+
20
+ <template>
21
+ <template v-if="links.length">
22
+ <UButton
23
+ v-for="(link, index) of links"
24
+ :key="index"
25
+ size="sm"
26
+ v-bind="{ color: 'neutral', variant: 'ghost', ...link }"
27
+ />
28
+ </template>
29
+ <UColorModeButton />
30
+ </template>
@@ -4,7 +4,7 @@ import { useDocusI18n } from '../../composables/useDocusI18n'
4
4
  const appConfig = useAppConfig()
5
5
  const site = useSiteConfig()
6
6
 
7
- const { localePath, isEnabled } = useDocusI18n()
7
+ const { localePath, isEnabled, locales } = useDocusI18n()
8
8
 
9
9
  const links = computed(() => appConfig.github?.url
10
10
  ? [
@@ -33,7 +33,7 @@ const links = computed(() => appConfig.github?.url
33
33
  <template #right>
34
34
  <AppHeaderCTA />
35
35
 
36
- <template v-if="isEnabled">
36
+ <template v-if="isEnabled && locales.length > 1">
37
37
  <ClientOnly>
38
38
  <LanguageSelect />
39
39
 
@@ -0,0 +1,12 @@
1
+ <script setup lang="ts">
2
+ import type { ContentNavigationItem } from '@nuxt/content'
3
+
4
+ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
5
+ </script>
6
+
7
+ <template>
8
+ <UContentNavigation
9
+ highlight
10
+ :navigation="navigation"
11
+ />
12
+ </template>
@@ -1,3 +1,4 @@
1
+ import type { LocaleObject } from '@nuxtjs/i18n'
1
2
  import en from '../../i18n/locales/en.json'
2
3
 
3
4
  export const useDocusI18n = () => {
@@ -8,7 +9,7 @@ export const useDocusI18n = () => {
8
9
  return {
9
10
  isEnabled,
10
11
  locale: ref('en'),
11
- locales: ref([]),
12
+ locales: [],
12
13
  localePath: (path: string) => path,
13
14
  switchLocalePath: () => {},
14
15
  t: (key: string): string => {
@@ -18,12 +19,13 @@ export const useDocusI18n = () => {
18
19
  }
19
20
  }
20
21
 
21
- const { locale, locales, t } = useI18n()
22
+ const { locale, t } = useI18n()
23
+ const filteredLocales = (config.docus as { filteredLocales: LocaleObject<string>[] })?.filteredLocales || []
22
24
 
23
25
  return {
24
26
  isEnabled,
25
27
  locale,
26
- locales,
28
+ locales: filteredLocales,
27
29
  t,
28
30
  localePath: useLocalePath(),
29
31
  switchLocalePath: useSwitchLocalePath(),
package/app/error.vue CHANGED
@@ -1,42 +1,76 @@
1
1
  <script setup lang="ts">
2
2
  import type { NuxtError } from '#app'
3
+ import type { PageCollections } from '@nuxt/content'
4
+ import * as nuxtUiProLocales from '@nuxt/ui-pro/locale'
3
5
 
4
- defineProps<{
6
+ const props = defineProps<{
5
7
  error: NuxtError
6
8
  }>()
7
9
 
10
+ const { locale, locales, isEnabled, t, switchLocalePath } = useDocusI18n()
11
+
12
+ const lang = computed(() => nuxtUiProLocales[locale.value as keyof typeof nuxtUiProLocales]?.code || 'en')
13
+ const dir = computed(() => nuxtUiProLocales[locale.value as keyof typeof nuxtUiProLocales]?.dir || 'ltr')
8
14
  useHead({
9
15
  htmlAttrs: {
10
- lang: 'en',
16
+ lang,
17
+ dir,
11
18
  },
12
19
  })
13
20
 
21
+ const localizedError = computed(() => {
22
+ return {
23
+ ...props.error,
24
+ statusMessage: t('common.error.title'),
25
+ }
26
+ })
27
+
14
28
  useSeoMeta({
15
- title: 'Page not found',
16
- description: 'We are sorry but this page could not be found.',
29
+ title: () => t('common.error.title'),
30
+ description: () => t('common.error.description'),
17
31
  })
18
32
 
19
- // const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'))
20
- // const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('docs'), {
21
- // server: false,
22
- // })
33
+ if (isEnabled.value) {
34
+ const route = useRoute()
35
+ const defaultLocale = useRuntimeConfig().public.i18n.defaultLocale
36
+ onMounted(() => {
37
+ const currentLocale = route.path.split('/')[1]
38
+ if (!locales.some(locale => locale.code === currentLocale)) {
39
+ return navigateTo(switchLocalePath(defaultLocale) as string)
40
+ }
41
+ })
42
+ }
43
+
44
+ const collectionName = computed(() => isEnabled.value ? `docs_${locale.value}` : 'docs')
45
+
46
+ const { data: navigation } = await useAsyncData(`navigation_${collectionName.value}`, () => queryCollectionNavigation(collectionName.value as keyof PageCollections), {
47
+ transform: (data) => {
48
+ const rootResult = data.find(item => item.path === '/docs')?.children || data || []
49
+
50
+ return rootResult.find(item => item.path === `/${locale.value}`)?.children || rootResult
51
+ },
52
+ watch: [locale],
53
+ })
54
+ const { data: files } = useLazyAsyncData(`search_${collectionName.value}`, () => queryCollectionSearchSections(collectionName.value as keyof PageCollections), {
55
+ server: false,
56
+ })
23
57
 
24
- // provide('navigation', navigation)
58
+ provide('navigation', navigation)
25
59
  </script>
26
60
 
27
61
  <template>
28
62
  <UApp>
29
63
  <AppHeader />
30
64
 
31
- <UError :error="error" />
65
+ <UError :error="localizedError" />
32
66
 
33
67
  <AppFooter />
34
68
 
35
69
  <ClientOnly>
36
- <!-- <LazyUContentSearch
70
+ <LazyUContentSearch
37
71
  :files="files"
38
72
  :navigation="navigation"
39
- /> -->
73
+ />
40
74
  </ClientOnly>
41
75
  </UApp>
42
76
  </template>
@@ -1,9 +1,3 @@
1
- <script setup lang="ts">
2
- import type { ContentNavigationItem } from '@nuxt/content'
3
-
4
- const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
5
- </script>
6
-
7
1
  <template>
8
2
  <UContainer>
9
3
  <UPage>
@@ -11,10 +5,7 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
11
5
  <UPageAside>
12
6
  <DocsAsideLeftTop />
13
7
 
14
- <UContentNavigation
15
- highlight
16
- :navigation="navigation"
17
- />
8
+ <DocsAsideLeftBody />
18
9
  </UPageAside>
19
10
  </template>
20
11
 
@@ -16,7 +16,7 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
16
16
  const collectionName = computed(() => isEnabled.value ? `docs_${locale.value}` : 'docs')
17
17
 
18
18
  const [{ data: page }, { data: surround }] = await Promise.all([
19
- useAsyncData(kebabCase(route.path), () => queryCollection(collectionName.value as keyof Collections).path(route.path).first()),
19
+ useAsyncData(kebabCase(route.path), () => queryCollection(collectionName.value as keyof Collections).path(route.path).first() as Promise<DocsCollectionItem>),
20
20
  useAsyncData(`${kebabCase(route.path)}-surround`, () => {
21
21
  return queryCollectionItemSurroundings(collectionName.value as keyof Collections, route.path, {
22
22
  fields: ['description'],
@@ -28,6 +28,10 @@ if (!page.value) {
28
28
  throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
29
29
  }
30
30
 
31
+ if (page.value.layout) {
32
+ setPageLayout(page.value.layout as never)
33
+ }
34
+
31
35
  // Add the page path to the prerender list
32
36
  addPrerenderPath(`/raw${route.path}.md`)
33
37
 
package/content.config.ts CHANGED
@@ -8,6 +8,7 @@ const cwd = joinURL(options.rootDir, 'content')
8
8
  const locales = options.i18n?.locales
9
9
 
10
10
  const createDocsSchema = () => z.object({
11
+ layout: z.string().optional(),
11
12
  links: z.array(z.object({
12
13
  label: z.string(),
13
14
  icon: z.string(),
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "أو"
3
+ "or": "أو",
4
+ "error": {
5
+ "title": "الصفحة غير موجودة",
6
+ "description": "نأسف، لكن الصفحة التي تبحث عنها غير موجودة."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "অথবা"
3
+ "or": "অথবা",
4
+ "error": {
5
+ "title": "পেজ খুঁজে পাওয়া যায়নি",
6
+ "description": "দুঃখিত, আপনি যে পেজটি খুঁজছেন সেটি পাওয়া যায়নি।"
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "o"
3
+ "or": "o",
4
+ "error": {
5
+ "title": "Pàgina no trobada",
6
+ "description": "Ho sentim, però la pàgina que cerques no existeix."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "nebo"
3
+ "or": "nebo",
4
+ "error": {
5
+ "title": "Stránka nenalezena",
6
+ "description": "Je nám líto, ale stránka, kterou hledáte, nebyla nalezena."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "eller"
3
+ "or": "eller",
4
+ "error": {
5
+ "title": "Side ikke fundet",
6
+ "description": "Vi beklager, men siden du leder efter kunne ikke findes."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "ή"
3
+ "or": "ή",
4
+ "error": {
5
+ "title": "Η σελίδα δεν βρέθηκε",
6
+ "description": "Λυπούμαστε, αλλά η σελίδα που αναζητάτε δεν υπάρχει."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "or"
3
+ "or": "or",
4
+ "error": {
5
+ "title": "Page not found",
6
+ "description": "We are sorry but this page could not be found."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "või"
3
+ "or": "või",
4
+ "error": {
5
+ "title": "Lehekülge ei leitud",
6
+ "description": "Vabandame, kuid otsitavat lehekülge ei leitud."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "ou"
3
+ "or": "ou",
4
+ "error": {
5
+ "title": "Page non trouvée",
6
+ "description": "Nous sommes désolés, mais la page que vous cherchez n'existe pas."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "או"
3
+ "or": "או",
4
+ "error": {
5
+ "title": "העמוד לא נמצא",
6
+ "description": "אנו מתנצלים, אך העמוד שאתה מחפש לא קיים."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "या"
3
+ "or": "या",
4
+ "error": {
5
+ "title": "पृष्ठ नहीं मिला",
6
+ "description": "हमें खुशी है, लेकिन आप जो पृष्ठ खोज रहे हैं वह मौजूद नहीं है।"
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "կամ"
3
+ "or": "կամ",
4
+ "error": {
5
+ "title": "Էջը չի գտնվել",
6
+ "description": "Ներողություն, բայց այն էջը, որը փնտրում եք, չի գտնվել:"
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -0,0 +1,18 @@
1
+ {
2
+ "common": {
3
+ "or": "o"
4
+ },
5
+ "docs": {
6
+ "copy": {
7
+ "page": "Copia pagina",
8
+ "link": "Copia pagina Markdown",
9
+ "view": "Visualizza come Markdown",
10
+ "gpt": "Apri in ChatGPT",
11
+ "claude": "Apri in Claude"
12
+ },
13
+ "links": "Comunità",
14
+ "toc": "In questa pagina",
15
+ "report": "Segnala un problema",
16
+ "edit": "Modifica questa pagina"
17
+ }
18
+ }
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "または"
3
+ "or": "または",
4
+ "error": {
5
+ "title": "ページが見つかりません",
6
+ "description": "申し訳ございませんが、お探しのページは見つかりませんでした。"
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "немесе"
3
+ "or": "немесе",
4
+ "error": {
5
+ "title": "Бет табылмады",
6
+ "description": "Кешіріңіз, бірақ сіз іздеген бет табылмады."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "ឬ"
3
+ "or": "ឬ",
4
+ "error": {
5
+ "title": "រកមិនឃើញទំព័រ",
6
+ "description": "យើងសុំទោស ប៉ុន្តែទំព័រដែលអ្នកកំពុងស្វែងរកមិនអាចរកឃើញទេ។"
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "또는"
3
+ "or": "또는",
4
+ "error": {
5
+ "title": "페이지를 찾을 수 없습니다",
6
+ "description": "죄송합니다. 찾고 계신 페이지를 찾을 수 없습니다."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "же"
3
+ "or": "же",
4
+ "error": {
5
+ "title": "Барак табылган жок",
6
+ "description": "Кечиресиз, бирок сиз издеген барак табылган жок."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "oder"
3
+ "or": "oder",
4
+ "error": {
5
+ "title": "Säit net fonnt",
6
+ "description": "Et deet ons leed, awer d'Säit déi Dir sicht gëtt et net."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "atau"
3
+ "or": "atau",
4
+ "error": {
5
+ "title": "Halaman tidak dijumpai",
6
+ "description": "Kami mohon maaf, tetapi halaman yang anda cari tidak dapat dijumpai."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "eller"
3
+ "or": "eller",
4
+ "error": {
5
+ "title": "Side ikke funnet",
6
+ "description": "Vi beklager, men siden du leter etter kunne ikke finnes."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "ali"
3
+ "or": "ali",
4
+ "error": {
5
+ "title": "Stran ni bila najdena",
6
+ "description": "Opravičujemo se, vendar stran, ki jo iščete, ni bila najdena."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "eller"
3
+ "or": "eller",
4
+ "error": {
5
+ "title": "Sidan hittades inte",
6
+ "description": "Vi ber om ursäkt, men sidan du letar efter kunde inte hittas."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "або"
3
+ "or": "або",
4
+ "error": {
5
+ "title": "Сторінку не знайдено",
6
+ "description": "Вибачте, але сторінку, яку ви шукаєте, не знайдено."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "یا"
3
+ "or": "یا",
4
+ "error": {
5
+ "title": "صفحہ نہیں ملا",
6
+ "description": "ہمیں افسوس ہے، لیکن آپ جو صفحہ تلاش کر رہے ہیں وہ موجود نہیں ہے۔"
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "common": {
3
- "or": "hoặc"
3
+ "or": "hoặc",
4
+ "error": {
5
+ "title": "Không tìm thấy trang",
6
+ "description": "Chúng tôi xin lỗi, nhưng trang bạn đang tìm kiếm không tồn tại."
7
+ }
4
8
  },
5
9
  "docs": {
6
10
  "copy": {
package/modules/config.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { createResolver, defineNuxtModule } from '@nuxt/kit'
2
2
  import { defu } from 'defu'
3
+ import { existsSync } from 'node:fs'
4
+ import { join } from 'node:path'
3
5
  import { inferSiteURL, getPackageJsonMetadata } from '../utils/meta'
4
6
  import { getGitBranch, getGitEnv, getLocalGitInfo } from '../utils/git'
5
7
 
@@ -55,16 +57,46 @@ export default defineNuxtModule({
55
57
  ** I18N
56
58
  */
57
59
  if (nuxt.options.i18n && nuxt.options.i18n.locales) {
60
+ const { resolve } = createResolver(import.meta.url)
61
+
62
+ // Filter locales to only include existing ones
63
+ const filteredLocales = nuxt.options.i18n.locales.filter((locale) => {
64
+ const localeCode = typeof locale === 'string' ? locale : locale.code
65
+
66
+ // Check for JSON locale file
67
+ const localeFilePath = resolve('../i18n/locales', `${localeCode}.json`)
68
+ const hasLocaleFile = existsSync(localeFilePath)
69
+
70
+ // Check for content folder
71
+ const contentPath = join(nuxt.options.rootDir, 'content', localeCode)
72
+ const hasContentFolder = existsSync(contentPath)
73
+
74
+ if (!hasLocaleFile) {
75
+ console.warn(`[Docus] Locale file not found: ${localeCode}.json - skipping locale "${localeCode}"`)
76
+ }
77
+
78
+ if (!hasContentFolder) {
79
+ console.warn(`[Docus] Content folder not found: content/${localeCode}/ - skipping locale "${localeCode}"`)
80
+ }
81
+
82
+ return hasLocaleFile && hasContentFolder
83
+ })
84
+
58
85
  // Override strategy to prefix
59
86
  nuxt.options.i18n = {
60
87
  ...nuxt.options.i18n,
61
88
  strategy: 'prefix',
62
89
  }
63
90
 
91
+ // Expose filtered locales
92
+ nuxt.options.runtimeConfig.public.docus = {
93
+ filteredLocales,
94
+ }
95
+
64
96
  nuxt.hook('i18n:registerModule', (register) => {
65
- const { resolve } = createResolver(import.meta.url)
97
+ const langDir = resolve('../i18n/locales')
66
98
 
67
- const locales = nuxt.options.i18n?.locales?.map((locale) => {
99
+ const locales = filteredLocales?.map((locale) => {
68
100
  return typeof locale === 'string'
69
101
  ? {
70
102
  code: locale,
@@ -79,7 +111,7 @@ export default defineNuxtModule({
79
111
  })
80
112
 
81
113
  register({
82
- langDir: resolve('../i18n/locales'),
114
+ langDir,
83
115
  locales,
84
116
  })
85
117
  })
package/modules/css.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { defineNuxtModule, addTemplate } from '@nuxt/kit'
2
2
  import { joinURL } from 'ufo'
3
+ import { resolveModulePath } from 'exsolve'
3
4
 
4
5
  export default defineNuxtModule({
5
6
  meta: {
@@ -9,12 +10,14 @@ export default defineNuxtModule({
9
10
  const dir = nuxt.options.rootDir
10
11
 
11
12
  const contentDir = joinURL(dir, 'content')
13
+ const uiProPath = resolveModulePath('@nuxt/ui-pro', { from: import.meta.url, conditions: ['style'] })
14
+ const tailwindPath = resolveModulePath('tailwindcss', { from: import.meta.url, conditions: ['style'] })
12
15
 
13
16
  const cssTemplate = addTemplate({
14
17
  filename: 'docus.css',
15
18
  getContents: () => {
16
- return `@import "tailwindcss";
17
- @import "@nuxt/ui-pro";
19
+ return `@import ${JSON.stringify(tailwindPath)};
20
+ @import ${JSON.stringify(uiProPath)};
18
21
 
19
22
  @source "${contentDir.replace(/\\/g, '/')}/**/*";
20
23
  @source "../../app.config.ts";`
@@ -9,7 +9,6 @@ export default defineNuxtModule({
9
9
 
10
10
  const isI18nEnabled = !!(nuxt.options.i18n && nuxt.options.i18n.locales)
11
11
 
12
- // Ensure useDocusI18n is available in the template page
13
12
  addImports({
14
13
  name: 'useDocusI18n',
15
14
  from: resolve('../app/composables/useDocusI18n'),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "docus",
3
3
  "description": "Nuxt layer for Docus documentation theme",
4
- "version": "4.0.4",
4
+ "version": "4.1.1",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
7
7
  "repository": {
@@ -34,6 +34,7 @@
34
34
  "@nuxtjs/robots": "^5.4.0",
35
35
  "@vueuse/core": "^13.6.0",
36
36
  "defu": "^6.1.4",
37
+ "exsolve": "^1.0.7",
37
38
  "git-url-parse": "^16.1.0",
38
39
  "minimark": "^0.2.0",
39
40
  "motion-v": "^1.7.0",
package/utils/git.ts CHANGED
@@ -13,11 +13,11 @@ export interface GitInfo {
13
13
 
14
14
  export function getGitBranch() {
15
15
  const envName
16
- = process.env.CF_PAGES_BRANCH
17
- || process.env.CI_COMMIT_BRANCH
18
- || process.env.VERCEL_GIT_COMMIT_REF
19
- || process.env.BRANCH
20
- || process.env.GITHUB_REF_NAME
16
+ = process.env.CF_PAGES_BRANCH
17
+ || process.env.CI_COMMIT_BRANCH
18
+ || process.env.VERCEL_GIT_COMMIT_REF
19
+ || process.env.BRANCH
20
+ || process.env.GITHUB_REF_NAME
21
21
 
22
22
  if (envName && envName !== 'HEAD') {
23
23
  return envName