hozana / next-translate-routes

Flexible and translated routes for Next.js without custom server
MIT License
115 stars 30 forks source link

Wrong sitemap #21

Closed KVRA closed 2 years ago

KVRA commented 2 years ago

The package next-sitemap is not getting the translations

How to generate a sitemap with the translated paths ?
I would able to do it manually but translateUrl and translatePath ( methods ) don't seems to work well when the first parameter is a URL in another language that is not the default one,

ex : translateUrl('/home','fr-FR') == gets the path for French But how to get the English path given a French one ? Or a third language given the french path ? Thanks

cvolant commented 2 years ago

I never used next-sitemap. I gave a quick look to the readme, and if I understand correctly you want to leverage the transform function to translate the path... The path you get should be the file path prefixed by the locale : translateUrl handle this. If you succeed to make it work, please open a pull request here to add the correct config to the readme ! 😃

gehaktmolen commented 2 years ago

You could try the following; create sitemap.xml.tsx within your pages folder with the following content:

import type { GetServerSideProps, GetServerSidePropsContext } from 'next';

import * as React from 'react';
import fs from 'fs';
import { translateUrl } from 'next-translate-routes';

export default function Sitemap() {
  return null;
};

export const getServerSideProps: GetServerSideProps = async (props: GetServerSidePropsContext) => {
  const { res, locales, defaultLocale } = props;
  const url = `${process.env.PROJECT_URL?.replace(/\/+$/, '')}`;
  const ourLocales = (Array.isArray(locales) ? locales : [defaultLocale || 'en']);

  // Get static paths.
  const pagesDirectory = fs.readdirSync('src/pages');
  const pages = pagesDirectory
    .filter(page => page.match(/.*\.(tsx?)/ig))
    .filter(page => !['_app.tsx', '_document.tsx', '_error.tsx', 'sitemap.xml.tsx'].includes(page))
    .map(page => page.replace('.tsx', ''));
  let paths = pages.map(page => ourLocales.map(locale => ({
    locale,
    path: `${url}${translateUrl(page, locale)}`,
  })));

  // Get dynamic paths.
  try {
    const yourDynamicRequestedPaths: Something[] = await yourAPIRequest();
    paths = [
      ...paths,
      ...yourDynamicRequestedPaths,
    ];
  } catch (error) {
  }

  const entries = paths.map((entry) => `
          <url>
            <loc>${entry.find(path => path.locale === (defaultLocale || 'en'))?.path}</loc>
            <lastmod>${new Date().toISOString()}</lastmod>
            <changefreq>monthly</changefreq>
            <priority>1.0</priority>
            ${entry.map(path => `<xhtml:link rel="alternate" hreflang="${path.locale}" href="${path.path}"/>`).join('')}
          </url>
        `).join('');

  const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
    <urlset 
        xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" 
        xmlns:xhtml="http://www.w3.org/1999/xhtml" >
      ${entries}
    </urlset>`;

  res.setHeader('Content-Type', 'text/xml');
  res.write(sitemap);
  res.end();

  return {
    props: {},
  };
};
JacobSoderblom commented 2 years ago

This is how I got it working

/** @type {import('next-sitemap').IConfig} */
const path = require('path');
const { translateUrl } = require('next-translate-routes');
const { createNtrData } = require('next-translate-routes/plugin');

const nextConfig = require('./next.config');
const i18NextConfig = require('./next-i18next.config');

const data = createNtrData(
  nextConfig,
  path.resolve(process.cwd(), './src/pages')
);

global.__NEXT_TRANSLATE_ROUTES_DATA = data;

module.exports = {
  siteUrl: process.env.SITE_URL,
  generateRobotsTxt: true, // (optional)
  // ...other options
  transform: async (config, path) => {
    const locale =
      i18NextConfig.i18n.locales.find(
        (locale) => path.indexOf(`/${locale}/`) > -1
      ) || i18NextConfig.i18n.defaultLocale;
    return {
      loc: translateUrl(path, locale), // => this will be exported as http(s)://<config.siteUrl>/<path>
      changefreq: config.changefreq,
      priority: config.priority,
      lastmod: config.autoLastmod ? new Date().toISOString() : undefined,
      alternateRefs: config.alternateRefs ?? [],
    };
  },
};
cvolant commented 2 years ago

I linked toward @JacobSoderblom's suggestion in the docs.

vbrzezina commented 1 year ago

If someone is is wondering how to add alternateRefs for each language, here's how I got it working


/** @type {import('next-sitemap').IConfig} */
module.exports = {
  siteUrl: process.env.NEXT_PUBLIC_SITE_URL,
  generateRobotsTxt: true,
  transform: (config, path) => {
    const locale = i18NextConfig.i18n.locales.find((locale) => path.includes(`/${locale}`));
    const fileUrl = urlToFileUrl(path, locale || i18NextConfig.i18n.defaultLocale);

    return {
      loc: translateUrl(path, locale || i18NextConfig.i18n.defaultLocale),
      changefreq: config.changefreq,
      priority: config.priority,
      lastmod: config.autoLastmod ? new Date().toISOString() : undefined,
      alternateRefs: fileUrl
        ? i18NextConfig.i18n.locales.map((locale) => ({
            href: `${process.env.NEXT_PUBLIC_SITE_URL}${fileUrlToUrl(fileUrl, locale)}`,
            hreflang: locale,
            hrefIsAbsolute: true,
          }))
        : [],
    };
  },
};