我正在制作一个多语言(EN、ES)网站,其中包含一个 Astro js 博客,使用 Decap CMS 来发布博客文章,使用 astro-i18next 来实现其国际化功能。
我在找出如何使其正常工作时遇到问题。
此 astro 网站设置为 EN 作为默认语言,ES 作为辅助语言。这是页面结构:
pages/ ─── index
├── about-us
└── es/ ─── index
└─ about-us
感谢 astro-i18next 配置,例如,我可以将西班牙语路由到 website.com/es/quien-somos。
现在,我已经设置了 Decap CMS 来创建多语言博客文章。但是当我通过管理面板创建新帖子时,它会在每种语言以及默认语言的文件夹中创建 md 文件:
content/blog/─── en/ ─── first-post.md
└─ es/ ─── first-post.md
这是我的第一个问题,因为英文博客页面的网址是website.com/blog,但英文博客文章的网址是website/blog/en/first-post。 西班牙语版本也是如此:博客页面的网址是 website.com/es/blog,但西班牙语博客文章的网址是 website/blog/es/first-post。最重要的是,蛞蝓是英语而不是西班牙语。
有人可以给我一些关于如何以正确的方式路由的提示吗?我的意思是,不是通过一些动态路由魔法硬编码在 i18next 配置文件中。就是这样:
谢谢!
最后我可以通过路由来解决。主要思想是根据帖子标题(每种语言)设置帖子路线。
首先,在 utils.ts 中,我从 https://www.30secondsofcode.org/js/s/string-to-slug/
复制了一个小片段export const slugify = (str: string) =>
str
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '');
我将长期使用这个。
接下来,对于
pages/blog/index.astro
,我会过滤所需语言的所有帖子,并根据 slugified 帖子标题创建帖子 url。因为 Decap CMS 使用相同的文件名保存每个帖子,但按语言保存在文件夹中,所以它为我提供了一种轻松过滤它们的方法。
---
// get posts and filter them for targeted language and sort them
const lang = i18next.language;
const posts = (
await getCollection('blog', ({ id }) => {
return id.startsWith(lang);
})
).sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---
<h1>Blog Index</h1>
{
posts.map(post => (
<a href={localizePath(`/blog/${slugify(post.data.title)}/`)}>{post.data.title}</a>
))
}
然后,我为单个帖子创建文件
pages/blog/[...title].astro
。该文件还控制这些单个帖子的动态路由。
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { title: slugify(post.data.title) },
props: post
}));
}
因为我无法将 i18next 配置文件用于我的帖子路由,所以我必须创建一个额外的函数来根据标题和slugs 获取翻译的路径。
async function getTranslatedPostUrl() {
// Split pathname
// While filtereing out the empty entries
// https://stackoverflow.com/a/39184134/2812386
const pathParts = pathname.split('/').filter(i => i);
// Find the index of 'blog' in the pathParts array
const blogIndex = pathParts.indexOf('blog');
// Determine the target language
const targetLang = lang === 'es' ? 'en' : 'es';
// Check if 'blog' exists in the URL and if there's something after it
if (blogIndex !== -1 && pathParts.length > blogIndex + 1) {
// Get all blog posts
const allPosts = await getCollection('blog');
// Extract the slugified title form the pathname
const currentSlugifiedTitle = pathParts[blogIndex + 1];
// Find the current post to get its title in the original language
const currentPost = allPosts.find((post) => {
return slugify(post.data.title) === currentSlugifiedTitle;
});
if (currentPost) {
// Extract the slug without the language prefix from the current post
const baseSlug = currentPost.slug.split('/').slice(1).join('/'); // Removes 'en/' or 'es/'
// Find the translated post based on the base slug
const translatedPost = allPosts.find((post) => {
return post.slug === `${targetLang}/${baseSlug}`;
});
if (translatedPost) {
// Generate the URL for the translated post
const slugifiedTranslatedTitle = slugify(translatedPost.data.title);
return targetLang === 'es' ? `/es/blog/${slugifiedTranslatedTitle}/` : `/blog/${slugifiedTranslatedTitle}/`;
}
}
} else {
// Handle other pages with i18next
return localizePath(pathname, targetLang);
}
}
const translatedPostUrl = await getTranslatedPostUrl();
现在我可以使用
translatedPostUrl
的值来链接到翻译的内容。
^_^
编辑:在 Netlify 上部署此解决方案后,我注意到导航栏逻辑并未 100% 工作。那是因为
pathname.split('/')
创建了一些空条目。所以我必须过滤它。有关更多信息,请访问 https://stackoverflow.com/a/39184134/2812386