chapter-three / next-drupal

Next.js for Drupal has everything you need to build a next-generation front-end for your Drupal site: SSG, SSR, and ISR, Multi-site, Authentication, Webforms, Search API, I18n and Preview mode (works with JSON:API and GraphQL).
https://next-drupal.org
MIT License
635 stars 175 forks source link

When multiple locales (in Next.js) getPathsFromContext() creates 404 paths for untranslated (in Drupal) nodes. #147

Open theRuslan opened 2 years ago

theRuslan commented 2 years ago

From https://next-drupal.org/docs/reference#getpathsfromcontext: "If you have locales configured in your next.config.js file, it will automatically create localized routes."

When multiple locales (in Next.js) getPathsFromContext() creates 404 paths for untranslated (in Drupal) nodes.

shadcn commented 2 years ago

Can you share the next.config.js?

theRuslan commented 2 years ago

Yes. And I also use next-i18next for i18n. Here is both configs.

next.config.js:

/** @type {import('next').NextConfig} */

const withPlugins = require("next-compose-plugins");
const { i18n } = require("./next-i18next.config");
const withPWA = require("next-pwa");

const nextConfig = {
  publicRuntimeConfig: {
    canonicalDomain: "https://greenwise.ru",
  },
  reactStrictMode: true,
  images: {
    domains: [process.env.NEXT_IMAGE_DOMAIN],
  },
  i18n,
  experimental: {
    outputStandalone: true,
  },
};

module.exports = withPlugins([
  [
    withPWA,
    {
      pwa: {
        dest: "public",
        disable: process.env.NODE_ENV === "development",
        register: true,
        sw: "/sw.js",
      },
    },
  ],
  [nextConfig],
]);

next-i18next.config.js:

const path = require("path");

module.exports = {
  i18n: {
    defaultLocale: "ru",
    locales: ["ru", "en"],
    localeDetection: false,
  },
  localePath: path.resolve("locales"),
};
theRuslan commented 2 years ago

I will try to reproduce this issue on clean next/drupal setup with the same language configuration.

shadcn commented 2 years ago

@TheRuslan what does the pattern (path alias) for the node type looks like?

You should use [node:source:title] so that Next.js can automatically create the paths.

path-alias-page

theRuslan commented 2 years ago

@shadcn I used [node:title] and tried [node:source:title]. I think in this case it doesn't matter.

I made an example with 2 nodes:

In accordance with next-drupal documentation here https://next-drupal.org/docs/reference#getpathsfromcontext

If you have locales configured in your next.config.js file, it will automatically create localized routes.

So getPathsFromContext returns such result:

[
    {
        "params": {
            "slug": [
                "blog",
                "test-article-original-node-russian-without-translation"
            ]
        },
        "locale": "ru"
    },
    {
        "params": {
            "slug": [
                "blog",
                "test-article-original-node-russian-without-translation"
            ]
        },
        "locale": "en"
    },
    {
        "params": {
            "slug": [
                "product",
                "testovyy-produkt-original-node-russian"
            ]
        },
        "locale": "ru"
    },
    {
        "params": {
            "slug": [
                "product",
                "test-product-translated-node-english"
            ]
        },
        "locale": "en"
    }
]

We can see that it work fine with translated product, but for untranslated blog post it returns inexistent path for "en" locale (second path in this example). And the more locales you have, the more non-existent paths will be returned.

That triggers another problems with next-sitemap for example.

shadcn commented 2 years ago

I see. So for your setup, product has translation but not blog?

theRuslan commented 2 years ago

Yes. In this example above. In general some nodes can have translations, but some can exist only in one language.

I think it's not specific to a particular case, but common practice for multi-language websites.

Maybe we can check node for system field "content_translations" somewhere in getPathsFromContext or buildPathsFromResources and return only such path which is exist in both nextjs and drupal locales?

theRuslan commented 2 years ago

Filtering in params for getPathsFromContext can't solve the problem because any filtered resources will be mapped through all the nextjs locales anyway.

shadcn commented 2 years ago

Maybe we can check node for system field "content_translations" somewhere in getPathsFromContext or buildPathsFromResources and return only such path which is exist in both nextjs and drupal locales?

We could. I'll look into it.

For now, I think what you could do is use two different calls for paths.

const pathsWithLocale = await getPathsFromContext("node--product", context)
const pathsWithoutLocale = await getPathsFromContext("node--blog", {})

const paths = [
    ...pathsWithLocale,
    ...pathsWIthoutLocale,
]

Can you give this a try and let me know?