Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions layer/app/app.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import type { ContentNavigationItem, PageCollections } from '@nuxt/content'
import * as nuxtUiLocales from '@nuxt/ui/locale'
import { safeLocaleCode } from '../utils/locale'

const { seo } = useAppConfig()
const site = useSiteConfig()
Expand All @@ -9,7 +10,7 @@ const { locale, locales, isEnabled, switchLocalePath } = useDocusI18n()
const nuxtUiLocale = computed(() => nuxtUiLocales[locale.value as keyof typeof nuxtUiLocales] || nuxtUiLocales.en)
const lang = computed(() => nuxtUiLocale.value.code)
const dir = computed(() => nuxtUiLocale.value.dir)
const collectionName = computed(() => isEnabled.value ? `docs_${locale.value}` : 'docs')
const collectionName = computed(() => isEnabled.value ? `docs_${safeLocaleCode(locale.value)}` : 'docs')

useHead({
meta: [
Expand Down Expand Up @@ -47,7 +48,7 @@ const { data: navigation } = await useAsyncData(() => `navigation_${collectionNa
transform: (data: ContentNavigationItem[]) => {
const rootResult = data.find(item => item.path === '/docs')?.children || data || []

return rootResult.find(item => item.path === `/${locale.value}`)?.children || rootResult
return rootResult.find(item => item.path === `/${locale.value.toLowerCase()}`)?.children || rootResult
},
watch: [locale],
})
Expand Down
5 changes: 3 additions & 2 deletions layer/app/error.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import type { NuxtError } from '#app'
import type { ContentNavigationItem, PageCollections } from '@nuxt/content'
import * as nuxtUiLocales from '@nuxt/ui/locale'
import { safeLocaleCode } from '../utils/locale'

const props = defineProps<{
error: NuxtError
Expand Down Expand Up @@ -44,13 +45,13 @@ if (isEnabled.value) {
})
}

const collectionName = computed(() => isEnabled.value ? `docs_${locale.value}` : 'docs')
const collectionName = computed(() => isEnabled.value ? `docs_${safeLocaleCode(locale.value)}` : 'docs')

const { data: navigation } = await useAsyncData(`navigation_${collectionName.value}`, () => queryCollectionNavigation(collectionName.value as keyof PageCollections), {
transform: (data: ContentNavigationItem[]) => {
const rootResult = data.find(item => item.path === '/docs')?.children || data || []

return rootResult.find(item => item.path === `/${locale.value}`)?.children || rootResult
return rootResult.find(item => item.path === `/${locale.value.toLowerCase()}`)?.children || rootResult
},
watch: [locale],
})
Expand Down
8 changes: 5 additions & 3 deletions layer/app/pages/[[lang]]/[...slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { kebabCase } from 'scule'
import type { ContentNavigationItem, Collections, DocsCollectionItem } from '@nuxt/content'
import { findPageHeadline } from '@nuxt/content/utils'

import { safeLocaleCode } from '../../../utils/locale'

definePageMeta({
layout: 'docs',
})
Expand All @@ -12,12 +14,12 @@ const { locale, isEnabled, t } = useDocusI18n()
const appConfig = useAppConfig()
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')

const collectionName = computed(() => isEnabled.value ? `docs_${locale.value}` : 'docs')
const collectionName = computed(() => isEnabled.value ? `docs_${safeLocaleCode(locale.value)}` : 'docs')

const [{ data: page }, { data: surround }] = await Promise.all([
useAsyncData(kebabCase(route.path), () => queryCollection(collectionName.value as keyof Collections).path(route.path).first() as Promise<DocsCollectionItem>),
useAsyncData(kebabCase(route.path), () => queryCollection(collectionName.value as keyof Collections).path(route.path.toLowerCase()).first() as Promise<DocsCollectionItem>),
useAsyncData(`${kebabCase(route.path)}-surround`, () => {
return queryCollectionItemSurroundings(collectionName.value as keyof Collections, route.path, {
return queryCollectionItemSurroundings(collectionName.value as keyof Collections, route.path.toLowerCase(), {
fields: ['description'],
})
}),
Expand Down
5 changes: 3 additions & 2 deletions layer/app/templates/landing.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<script setup lang="ts">
import type { Collections } from '@nuxt/content'
import { safeLocaleCode } from '../../utils/locale'

const route = useRoute()
const { locale, isEnabled } = useDocusI18n()

// Dynamic collection name based on i18n status
const collectionName = computed(() => isEnabled.value ? `landing_${locale.value}` : 'landing')
const collectionName = computed(() => isEnabled.value ? `landing_${safeLocaleCode(locale.value)}` : 'landing')

const { data: page } = await useAsyncData(collectionName.value, () => queryCollection(collectionName.value as keyof Collections).path(route.path).first())
const { data: page } = await useAsyncData(collectionName.value, () => queryCollection(collectionName.value as keyof Collections).path(route.path.toLowerCase()).first())
if (!page.value) {
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
}
Expand Down
9 changes: 6 additions & 3 deletions layer/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { defineContentConfig, defineCollection, z } from '@nuxt/content'
import { useNuxt } from '@nuxt/kit'
import { joinURL } from 'ufo'

import { safeLocaleCode } from './utils/locale'

const { options } = useNuxt()
const cwd = joinURL(options.rootDir, 'content')
const locales = options.i18n?.locales
Expand All @@ -21,17 +23,18 @@ let collections: Record<string, DefinedCollection>
if (locales && Array.isArray(locales)) {
collections = {}
for (const locale of locales) {
const code = (typeof locale === 'string' ? locale : locale.code).replace('-', '_')
const code = (typeof locale === 'string' ? locale : locale.code)
const safeCode = safeLocaleCode(code)

collections[`landing_${code}`] = defineCollection({
collections[`landing_${safeCode}`] = defineCollection({
type: 'page',
source: {
cwd,
include: `${code}/index.md`,
},
})

collections[`docs_${code}`] = defineCollection({
collections[`docs_${safeCode}`] = defineCollection({
type: 'page',
source: {
cwd,
Expand Down
12 changes: 10 additions & 2 deletions layer/modules/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,17 @@ export default defineNuxtModule({
const landingTemplate = resolve('../app/templates/landing.vue')

if (isI18nEnabled) {
pages.push({
// Get locale codes for regex pattern
const locales = nuxt.options.i18n?.locales || []
const localeCodes = locales.map((locale: string | { code: string }) =>
typeof locale === 'string' ? locale : locale.code,
).join('|')

// Use regex to only match valid locale codes or root
// This prevents /:lang? from matching /en/mcp
pages.unshift({
name: 'lang-index',
path: '/:lang?',
path: `/:lang(${localeCodes})?`,
file: landingTemplate,
})
}
Expand Down
4 changes: 2 additions & 2 deletions layer/server/mcp/tools/get-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ WORKFLOW: This tool returns the complete page content including title, descripti
const siteUrl = import.meta.dev ? 'http://localhost:3000' : inferSiteURL()

const availableLocales = getAvailableLocales(config)
const collectionName = config.i18n?.locales
const { collection: collectionName } = config.i18n?.locales
? getCollectionFromPath(path, availableLocales)
: 'docs'
: { collection: 'docs' }

try {
const page = await queryCollection(event, collectionName as keyof Collections)
Expand Down
6 changes: 3 additions & 3 deletions layer/server/mcp/tools/list-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ OUTPUT: Returns a structured list with:

try {
const allPages = await Promise.all(
collections.map(async (collectionName) => {
const pages = await queryCollection(event, collectionName as keyof Collections)
collections.map(async ({ collection, locale: localeCode }) => {
const pages = await queryCollection(event, collection as keyof Collections)
.select('title', 'path', 'description')
.all()

return pages.map(page => ({
title: page.title,
path: page.path,
description: page.description,
locale: collectionName.replace('docs_', ''),
locale: localeCode,
url: `${siteUrl}${page.path}`,
}))
}),
Expand Down
15 changes: 8 additions & 7 deletions layer/server/utils/content.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { LocaleObject } from '@nuxtjs/i18n'
import { safeLocaleCode } from '../../utils/locale'

type ConfigWithLocales = {
i18n?: { locales?: Array<string | LocaleObject> }
Expand All @@ -15,23 +16,23 @@ export function getAvailableLocales(config: ConfigWithLocales): string[] {
: []
}

export function getCollectionsToQuery(locale: string | undefined, availableLocales: string[]): string[] {
export function getCollectionsToQuery(locale: string | undefined, availableLocales: string[]): Array<{ collection: string, locale?: string }> {
if (locale && availableLocales.includes(locale)) {
return [`docs_${locale}`]
return [{ collection: `docs_${safeLocaleCode(locale)}`, locale }]
}

return availableLocales.length > 0
? availableLocales.map(l => `docs_${l}`)
: ['docs']
? availableLocales.map(l => ({ collection: `docs_${safeLocaleCode(l)}`, locale: l }))
: [{ collection: 'docs' }]
}

export function getCollectionFromPath(path: string, availableLocales: string[]): string {
export function getCollectionFromPath(path: string, availableLocales: string[]): { collection: string, locale?: string } {
const pathSegments = path.split('/').filter(Boolean)
const firstSegment = pathSegments[0]

if (firstSegment && availableLocales.includes(firstSegment)) {
return `docs_${firstSegment}`
return { collection: `docs_${safeLocaleCode(firstSegment)}`, locale: firstSegment }
}

return 'docs'
return { collection: 'docs' }
}
3 changes: 3 additions & 0 deletions layer/utils/locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function safeLocaleCode(locale: string): string {
return locale.replaceAll('-', '_')
}
Loading