有关于 i18n routing 和 redirects 的文档页面以及对 locale 参数的特殊支持(我个人没有使用过)。
在更一般的意义上,听起来你想要的是optional catch all routes。
您可以在foo 目录中定义一个名为[[...slug]].js 的文件。与/foo/us/en 之类的路径对应的params 是{ slug: ["us", "en"] },其中路径的每一段都成为slug 数组的一个元素。
您可以使用getStaticPaths 生成所有已知的国家/语言对。设置fallback: true 允许用户输入另一个组合并且不会得到 404。
export const getStaticPaths = async () => {
return {
paths: [
{ params: { slug: ["us", "en"] } },
{ params: { slug: ["us", "es"] } },
{ params: { slug: ["ca", "en"] } },
{ params: { slug: ["ca", "fr"] } },
/*...*/
],
fallback: true, // allows unknown
};
};
就重定向而言,这取决于您是否需要实际的重定向,例如输入/foo 会导致/foo/us/en,或者它们是否是显示相同内容的两个单独页面。我假设我们想要一个实际的重定向。
您将在 getStaticProps 函数中从 slug 转换为 props。这也是您实施重定向的地方。我将假设您拥有(或可以创建)一些实用函数,例如 isValidCountry(country) 和 getDefaultLanguage(country)
export const getStaticProps = async ( context ) => {
const [country, language] = context.params?.slug ?? [];
// if there is no country, go to us/en
if (!country || !isValidCountry(country)) {
return {
redirect: {
statusCode: 301, // permanent redirect
destination: "/foo/us/en",
},
};
}
// if there is no language, go to the default for that country
if (!language || !isValidLanguage(language, country)) {
return {
redirect: {
statusCode: 301, // permanent redirect
destination: `/foo/${country}/${getDefaultLanguage(country)}`,
},
};
}
// typical case, return country and language as props
return {
props: {
country,
language,
},
};
};
您可以使用useRouter 和isFallback 在组件本身中执行某些操作,但我不确定是否需要。在开发模式下,至少我得到了正确的重定向。
-
/foo/ca/en - 好的
-
/foo/ca/fr - 好的
-
/foo/ca/xx - 重定向到 /foo/ca/en
-
/foo/ca - 重定向到 /foo/ca/en
-
/foo - 重定向到 /foo/us/en
带有 TypeScript 类型的完整代码:
import { GetStaticPaths, GetStaticProps } from "next";
export interface Props {
country: string;
language: string;
}
export default function Page({ country, language }: Props) {
return (
<div>
<h1>
{country} - {language}
</h1>
</div>
);
}
const pairs = [
["us", "en"],
["us", "es"],
["ca", "en"],
["ca", "fr"],
];
const isValidCountry = (c: string) => pairs.some(([cc]) => cc === c);
const isValidLanguage = (l: string, c: string) =>
pairs.some(([cc, ll]) => cc === c && ll === l);
const getDefaultLanguage = (c: string) =>
pairs.find(([cc]) => cc === c)?.[1] ?? "en";
export const getStaticProps: GetStaticProps<Props, { slug: string[] }> = async (
context
) => {
const [country, language] = context.params?.slug ?? [];
// if there is no country, go to us/en
if (!country || !isValidCountry(country)) {
return {
redirect: {
statusCode: 301, // permanent redirect
destination: "/foo/us/en",
},
};
}
// if there is no language, go to the default for that country
if (!language || !isValidLanguage(language, country)) {
return {
redirect: {
statusCode: 301, // permanent redirect
destination: `/foo/${country}/${getDefaultLanguage(country)}`,
},
};
}
// typical case, return country and language as props
return {
props: {
country,
language,
},
};
};
export const getStaticPaths: GetStaticPaths<{ slug: string[] }> = async () => {
return {
paths: pairs.map((slug) => ({
params: { slug },
})),
fallback: true, // allows unknown
};
};