How to make non-english urls to work in next.js?

Viewed 708

How to make non-english urls to work with next.js ssr/client? Any ideas, rewrites and so on is welcomed.

Some time ago rewrites helped to fix this problem, but after some update it stopped working. Here is my attempts to fix this.

Codesandbox.

I was even trying to Unicode path (with rewrites), but it gives me strange error:

{
  source: encodeURI('/рус'),
  destination: "/u0440/u0443/u0441"
}

Error: Requested and resolved page mismatch: //u0440/u0443/u0441 /u0440/u0443/u0441
3 Answers

For now i do rewrites in next.config.js like this:

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

const pathsToRewrite = [
  ['/пользователь/:id/:nickname', '/user/:id/:nickname'],
  ['тест', 'test'],
]
const rewrites = pathsToRewrite.map(([ru, eng]) => ({ source: encodeURI(ru), destination: eng }))

module.exports = withBundleAnalyzer({
  async rewrites() {
    return rewrites
  },
})

I'm able to do this with Vietnamese text. The trick is to add 2 more rewrites: one with the real text and another for the urlencoded text.

{
  source: "/lo%C3%A0i/:slug",
  destination: "/species/:slug"
},
{
  source: "/loài/:slug",
  destination: "/species/:slug"
}

I've implemented a map between "pretty url" to a real page.

This implementation has few benefits.

  1. pretty urls can come from db
  2. pretty urls can be in any structure (not related to dir structure)
  3. pretty urls can be in any language :] (you have control over what your api returns as a url)
  4. the entire app is agnostic to them
  5. allows u to manipulate all the urls at one place (add prefix or what ever)

In a nutshell, I have a simple object that looks like:

const routes = {
  pageName: (prettyUrl) => ({href: 'path/to/page', as: prettyUrl});
}

Both client & server are using this object.

Client

The api returns for each data that has page a url property from the db.

Let's consider that our app shows list of users & a user, that means that the API returns for each user it pretty url (it can be anything must be unique).

import { routes } from '../routes';
import NextLink from 'next/link';

export const userCard = ({ user }) => (
  <div>
    <NextLink {...routes.userPage(user.url)}>{user.name}</NextLink>
  </div>
);

Server I've a custom express server that loads from DB the mapping (url -> pageName).

const express = require('express');
const next = require('next');

const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(async () => {
  const urlsMap = await loadUrlsMap();
  const server = express();

  server.get('*', (req, res) => {
    if (urlMaps.hasOwnProperty(req.pathname)) {
      const page = urlMaps(req.pathname);
      const additionalData = {}; // can have query / params from req
      return app.render(req, res, page.href, { ...additionalData });
    }
    return handle(req, res);
  });

  server.listen(port, err => {
    if (err) throw err;
    console.log(`> Ready on http://localhost:${port}`);
  });
});

I've showed a really basic implementation, but think that you can pass to the routes.pageName method any thing (userId, for example) and each method can do any logic with it and return { href, as }.

In my real implementation I'm using path-to-regex which allows me to pass a url template and more :]

Related