This commit is contained in:
doget 2025-09-04 19:15:48 +08:00
parent 6c3cdc2470
commit 62fc8d3355
5 changed files with 67 additions and 11 deletions

View File

@ -119,12 +119,18 @@ const currentLocale = ref(locale.value)
// Get the slug from the route // Get the slug from the route
const slug = route.params.slug const slug = route.params.slug
const base = `/${Array.isArray(slug) ? slug.join('/') : slug}` const base = `/${Array.isArray(slug) ? slug.join('/') : slug}`
// Our list links to /blog/{locale}/{slug}, remove /blog prefix to query content file // Our list links to /blog/{locale}/{slug}, derive content path
const articlePath = base.replace(/^\/blog(?=\/)/, '') const articlePath = base.replace(/^\/blog(?=\/)/, '')
// Remove leading locale segment for @nuxt/content when using locales option
const normalizedPath = articlePath.replace(/^\/(en|zh|zh-hant)(?=\/)/, '')
// Fetch the article // Fetch the article: prefer normalized path with _locale, fallback to original path
const { data: article, pending, error } = await useAsyncData(`article-${articlePath}`, async () => { const { data: article, pending, error } = await useAsyncData(`article-${articlePath}`, async () => {
try { try {
const localized = await queryContent(normalizedPath)
.where({ _locale: locale.value })
.findOne()
if (localized) return localized
return await queryContent(articlePath).findOne() return await queryContent(articlePath).findOne()
} catch (err) { } catch (err) {
console.error('Error fetching article:', err) console.error('Error fetching article:', err)
@ -159,7 +165,9 @@ const switchToTranslation = async (targetLocale) => {
const base = articlePath.replace(/^\/(en|zh|zh-hant)/,'') const base = articlePath.replace(/^\/(en|zh|zh-hant)/,'')
const translated = await queryContent(`/${targetLocale}${base}`).findOne() const translated = await queryContent(`/${targetLocale}${base}`).findOne()
if (translated?._path) { if (translated?._path) {
await navigateTo(`/blog${translated._path}`) const appPrefix = targetLocale === 'en' ? '' : `/${targetLocale}`
const slugPath = translated._path.replace(/^\/(en|zh|zh-hant)(?=\/)/, '')
await navigateTo(`${appPrefix}/blog${slugPath}`)
return return
} }
throw new Error('Not found') throw new Error('Not found')

View File

@ -119,14 +119,12 @@ const { data: articles, pending, error } = await useAsyncData(() => `blog-${loca
return [] return []
} }
}) })
// Build localized link for article // Build localized link for article (app-level locale prefix + blog + slug without locale)
const articleLink = (a) => { const articleLink = (a) => {
if (!a || !a._path) return '/blog' if (!a || !a._path) return '/blog'
const hasLocale = /^\/(en|zh|zh-hant)(\/|$)/.test(a._path) const appPrefix = locale.value === 'en' ? '' : `/${locale.value}`
const localized = hasLocale const slugPath = a._path.replace(/^\/(en|zh|zh-hant)(?=\/)/, '')
? a._path return `${appPrefix}/blog${slugPath}`
: (locale.value === 'en' ? a._path : `/${locale.value}${a._path}`)
return `/blog${localized}`
} }
const availableLocales = computed(() => const availableLocales = computed(() =>
@ -148,7 +146,9 @@ const switchToTranslation = async (article, targetLocale) => {
const basePath = article._path.replace(/^\/(en|zh|zh-hant)/, '') const basePath = article._path.replace(/^\/(en|zh|zh-hant)/, '')
const translated = await queryContent(`/${targetLocale}${basePath}`).findOne() const translated = await queryContent(`/${targetLocale}${basePath}`).findOne()
if (translated?._path) { if (translated?._path) {
await navigateTo(`/blog${translated._path}`) const appPrefix = targetLocale === 'en' ? '' : `/${targetLocale}`
const slugPath = translated._path.replace(/^\/(en|zh|zh-hant)(?=\/)/, '')
await navigateTo(`${appPrefix}/blog${slugPath}`)
return return
} }
throw new Error('Not found') throw new Error('Not found')

View File

@ -174,7 +174,9 @@ const { data: articles, pending } = await useAsyncData(() => `home-articles-${lo
const articleLink = (a) => { const articleLink = (a) => {
if (!a || !a._path) return '/blog' if (!a || !a._path) return '/blog'
return `/blog${a._path}` const appPrefix = locale.value === 'en' ? '' : `/${locale.value}`
const slugPath = a._path.replace(/^\/(en|zh|zh-hant)(?=\/)/, '')
return `${appPrefix}/blog${slugPath}`
} }
// Services data // Services data

View File

@ -0,0 +1,40 @@
import { defineEventHandler, getRequestURL, sendRedirect } from 'h3'
// @ts-ignore: runtime module from @nuxt/content
import { queryContent } from '#content/server'
export default defineEventHandler(async (event) => {
const url = getRequestURL(event)
const path = url.pathname
// Match top-level locale-prefixed paths like /en/slug, /zh/slug, /zh-hant/slug
const match = path.match(/^\/(en|zh|zh-hant)(\/.*)$/)
if (!match) return
const locale = match[1]
const rest = match[2] // includes leading '/'
try {
// Check if a content doc exists for this locale and slug
const doc = await queryContent(event)
.where({ _locale: locale, _path: `/${locale}${rest}` })
.findOne()
// If not found by _path, also try querying by normalized path with _locale filter
const normalized = rest // e.g., /getting-started-aws-ec2
const fallback = doc || await queryContent(event)
.where({ _locale: locale })
.path(normalized)
.findOne()
if (fallback) {
const appPrefix = locale === 'en' ? '' : `/${locale}`
const target = `${appPrefix}/blog${rest}`
// 301 permanent redirect
return sendRedirect(event, target, 301)
}
} catch {
// Ignore errors and let normal routing continue
}
})

6
types/content-server.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
declare module '#content/server' {
export function queryContent(event?: any): any
export function serverQueryContent(event?: any): any
}