docs/next.config.mjs
Rama master · réplica de lectura del repositorio original.
import createNextIntlPlugin from 'next-intl/plugin';
import { createMDX } from 'fumadocs-mdx/next';
const withNextIntl = createNextIntlPlugin({
experimental: {
srcPath: './src',
// Enables `useExtracted` (compile-time message extraction). Required for the
// production build — without it, `useExtracted` throws at runtime. The
// source locale now lives in `messages.sourceLocale` below.
extract: true,
messages: {
sourceLocale: 'en',
path: './messages',
format: 'json',
locales: ['en', 'de', 'fr', 'zh', 'es', 'pl', 'it', 'ko', 'pt', 'ja'],
},
},
});
const withMDX = createMDX();
// Vercel packages monorepo builds relative to its supplied tracing root.
// Keep local builds rooted in docs when that override is absent.
const tracingRoot = process.env.NEXT_PRIVATE_OUTPUT_TRACE_ROOT || import.meta.dirname;
/** @type {import('next').NextConfig} */
const config = {
reactStrictMode: true,
outputFileTracingRoot: tracingRoot,
turbopack: { root: tracingRoot },
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'pbs.twimg.com',
},
{
protocol: 'https',
hostname: 'abs.twimg.com',
},
{
protocol: 'https',
hostname: 'ui-avatars.com',
},
{
protocol: 'https',
hostname: 'www.google.com',
},
],
},
async redirects() {
// Tools retired 2026-07-18 (off-brand / no organic traffic). Redirect the old URLs to the
// tools index so residual links and SERP clicks land somewhere useful instead of a 404.
const logoPlatforms = [
'instagram', 'x', 'tiktok', 'linkedin', 'youtube', 'facebook', 'pinterest', 'discord', 'twitch',
'snapchat', 'reddit', 'threads', 'bluesky', 'mastodon', 'github', 'medium', 'substack', 'spotify',
'telegram', 'whatsapp',
];
const hashtagPlatforms = ['instagram', 'tiktok', 'x', 'linkedin', 'facebook', 'youtube', 'pinterest', 'threads', 'tumblr'];
const retiredSlugs = [
...logoPlatforms.map(p => `${p}-logo-generator`),
...hashtagPlatforms.map(p => `${p}-hashtag-generator`),
...['nostr', 'lemmy', 'warpcast', 'dribbble', 'mastodon'].map(p => `${p}-font-generator`),
...['nostr', 'lemmy', 'warpcast', 'dribbble', 'mastodon', 'vk'].map(p => `${p}-comment-generator`),
'medium-page-name-generator',
'medium-post-generator',
'medium-username-generator',
'mastodon-character-counter',
...['mastodon', 'medium'].map(p => `${p}-bio-generator`),
...['nostr', 'lemmy', 'warpcast', 'dribbble', 'mastodon', 'medium', 'vk', 'tumblr'].map(p => `${p}-photo-resizer`),
];
// URLs that no longer exist but are still linked from old blog posts, external
// sites and the search index. Specific rules must precede the catch-alls.
const legacyRedirects = [
// Docs reorganisation: behavior-analytics/* and product-analytics/* moved.
['/docs/behavior-analytics/:path*', '/docs/feature-guides/:path*'],
['/docs/product-analytics/funnels_guide', '/docs/funnels'],
['/docs/product-analytics/journeys_dashboard', '/docs/feature-guides/journeys'],
['/docs/product-analytics/retention_guide', '/docs/feature-guides/retention'],
['/docs/product-analytics/:path*', '/docs'],
['/docs/web-analytics/goals-tab', '/docs/goals'],
['/docs/web-analytics/:path*', '/docs/feature-guides/main-tab'],
['/docs/getting-started/:path*', '/docs'],
['/docs/blog/:path*', '/blog/:path*'],
// Blog posts deleted before redirects existed; they still get visits and backlinks.
['/blog/data-privacy-2025', '/security'],
['/blog/master-digital-marketing', '/blog'],
['/blog/boost-growth-with-key-user-retention-metrics', '/features/retention'],
['/blog/replay-session', '/features/session-replay'],
['/blog/real-time-reports', '/features/web-analytics'],
['/blog/cross-site-tracking', '/features/web-analytics'],
['/blog/user-flow-examples-2025', '/features/user-journeys'],
// 2026-09 blog cleanup: two posts merged into keyword-targeted guides, two removed.
['/blog/churn-rate-vs-retention-rate', '/blog/retention-analysis'],
['/blog/what-is-sales-funnel-rybbit', '/blog/funnel-analysis'],
['/blog/content-marketing-funnel', '/blog/funnel-analysis'],
['/blog/onyx-ai-knowledge-platform', '/blog'],
];
return [
...retiredSlugs.map(slug => ({
source: `/tools/${slug}`,
destination: '/tools',
permanent: true,
})),
...legacyRedirects.map(([source, destination]) => ({ source, destination, permanent: true })),
];
},
async rewrites() {
return [
{
source: '/docs/:path*.mdx',
destination: '/llms.mdx/:path*',
},
];
},
};
export default withNextIntl(withMDX(config));