wiziple / gatsby-plugin-intl

Gatsby plugin that turns your website into an internationalization-framework out of the box.
http://gatsby-starter-default-intl.netlify.com
325 stars 178 forks source link

Auto Redirection Option Breaks Client Only Routes #68

Open jhoffmcd opened 5 years ago

jhoffmcd commented 5 years ago

In the case where the plugin option for redirection is set to true, client-only routes for things like authenticated pages do not load, but instead, get redirected to the base route because the routed value comes back false for these client routes.

/en-us/account <--- routes properly
/en-us/account/profile <--- redirects back to the account page, preventing the account page client router from acting on the "profile" slug

Note I was only even able to get here after nullifying the 404 matchPaths that the plugin sets here. Match path sorting may be broken in the current release, see https://github.com/gatsbyjs/gatsby/issues/16098

jhoffmcd commented 5 years ago

I'm able to provide a workaround for this by setting the routed to true for the base page:

# gatsby-node.js
exports.onCreatePage = ({ page, actions }) => {
  const { path, matchPath, context } = page;
  const { createPage, deletePage } = actions;

  // Build client only paths for account
  if (path.match(/\/account\//)) {
    const { intl } = context;

    deletePage(page);
    createPage({
      ...page,
      matchPath: `/:locale/account/*`,
      context: {
        ...context,
        intl: {
          ...intl,
          routed: true, // prevents auto redirection on these routes
        },
      },
    });
  }
}
jhoffmcd commented 5 years ago

I wonder if possibly the isRedirect logic in the page-wrapper could also check for if the path matches the page contexts matchPath, and if so, don't redirect?

patspam commented 4 years ago

Thanks for mentioning that match path sorting was broken in earlier Gatsby releases. I'm using redirect: false and I had to upgrade Gatsby to get my setup working too.

For reference, this is what I'm using without redirection:

if (page.path.match(/^\/account\//)) {
    actions.deletePage(page)
    actions.createPage({
      ...page,
      matchPath: `/account/:id/*`,
    })
  } else if (page.path.match(/^\/([^/])+\/account\//)) {
    actions.deletePage(page)
    actions.createPage({
      ...page,
      matchPath: `/:lang/account/:id/*`,
    })
  }

The two separate matches are required because reach-router doesn't support optional params.

tmskrtsz commented 4 years ago

Is there a proposed solution for this? I'm looking into ways to solve this issue in my client side gatsby app. I'm also using gatsby-plugin-create-client-paths plugin for managing those routes.

jhoffmcd commented 4 years ago

Nothing proposed yet @tmskrtsz, other than the workarounds above.

I suspect there is a fix related to how isRedirect is assigned in the code, but I have not taken a deeper dive yet.

tmskrtsz commented 4 years ago

I solved this by setting manual redirections in gatsby-browser based on conditions and checks and grabbing the default language set in localStarge.

johnparn commented 4 years ago

Hi!

I'm being tripped by the client only routes in combination with the intl plugin. On non client-only routes it works just fine.

For my concern I want to display a details page for titles and I want to keep the detail pages dynamic. For example the title with id 1234567 may be accessible through /sv/titles/1234567, /en/titles/1234567 or /ar/titles/1234567 and the data to be displayed will be fetched from algolia client-side. In the configuration of gatsby-plugin-intl I've set redirect: true and specified a default language.

By means of the workaround that @jhoffmcd and @patspam provided and after some tinkering with gatsby-node.js I managed to get the title detail pages to show. However, accessing /titles/1234567 without any locale does not redirect to the default language, in this case /sv/titles/1234567. Did that ever work for you?

In my case the redirect is broken. I get TypeError: Cannot read property 'page' of undefined. The language switch is also broken. Sounds familiar?

This is my code so far.

In gatsby-node.js:

exports.onCreatePage = ({ page, actions }) => {
  const { path, matchPath, context } = page;
  const { createPage, deletePage } = actions;

  const isPlainRoute = /^\/titles\//.test(path); // /titles/
  const isLocaleRoute = /^\/[a-z]{2,3}\/titles\//.test(path); // /sv/titles/, /ar/titles/ ...

  if (isPlainRoute || isLocaleRoute) {
    const { intl } = context;

    deletePage(page);
    createPage({
      ...page,
      path: '/titles/', // Omitting this will lead to TypeError: Cannot read property 'page' of undefined on details page
      matchPath: isLocaleRoute
        ? `/:locale/titles/:identifier`
        : `/titles/:identifier`,
      context: {
        ...context,
        intl: {
          ...intl,
          routed: isLocaleRoute,
        },
      },
    });
  }
};

In /pages/titles.js I have:

import React, { Fragment } from 'react';
import Layout from './../components/layout';
import { useIntl } from 'gatsby-plugin-intl';
import { Router } from '@reach/router';

const Title = props => (
  <Fragment>
    <h1>Page</h1>
    <p>{JSON.stringify(props)}</p>
  </Fragment>
);

const Titles = props => {
  console.log(props);
  const intl = useIntl();

  return (
    <Layout>
      <Router>
        <Title path="/:locale/titles/:identifier" />
      </Router>
    </Layout>
  );
};

export default Titles;