kit icon indicating copy to clipboard operation
kit copied to clipboard

Optional route parameters

Open pzerelles opened this issue 2 years ago • 8 comments

Describe the problem

I'm trying to add multiple languages to a site with a leading /[locale]/... route parameter.

There are two things that I cannot do right now without much overhead:

  1. Having a default locale that has NO [locale] prefix. This would currently involve copying everything under /[locale] to / as well. Using /[...locale] does not work either, because a /[...locale]/[...slug] would not match correctly for many cases.

  2. When using adapter-static and pre-rendering all routes, only routes picked up through crawling links will be rendered behind /[locale]. Using kit.prerender.entries: ["*", "/de/something"] will only pre-render /de/something but nothing else.

Describe the proposed solution

  1. Add optional parameters with e.g. [locale?] syntax

  2. Allow more wildcards in kit.prerender.entries, like /de/*

Alternatives considered

No response

Importance

would make my life easier

Additional Information

No response

pzerelles avatar May 25 '22 15:05 pzerelles

How would you have /[locale?]/[...slug] match? That seems to fall into the same problem as /[...locale]/[...slug].

Conduitry avatar May 25 '22 15:05 Conduitry

How would you have /[locale?]/[...slug] match? That seems to fall into the same problem as /[...locale]/[...slug].

Without a matcher, you are right. So maybe the way the matcher is called should be changed. Right now, for an url /about/me and a matcher that accepts empty string or two characters, the matcher is only called for "about/me" and the route is discarded.

For the route /[...locale:matcher]/[...slug], parts of the url should be tested as well, also the case where locale is empty.

Depending on the matcher, there might be multiple ways an url can be matched when there are more than one rest parameters. I suggest to match the first rest param with as much from the path as possible and then continue to the right.

pzerelles avatar May 26 '22 07:05 pzerelles

Looking at the source code, this is not easy because the route's regex match takes place before the matchers are called in exec. Maybe an optional regex defined with the matcher could override the default regex for a parameter?

pzerelles avatar May 26 '22 08:05 pzerelles

If I were to tackle this problem of multiple languages, I'd probably do a "multibuild". For each language, build with some environment variable setting the language, then put all those build folders under one parent folder, something like this pseudocode:

['en', 'es', 'fr', 'zh'].forEach(lang => {
  VITE_LANG=${lang} npm run build
  mv /build/* /megabuild/${lang}
})

Just an idea. Might be tricky to get the asset paths right. And you'd need some smart rewriting in your web server.

johnnysprinkles avatar Jun 01 '22 21:06 johnnysprinkles

What about adding a prefix before local as following. /la/something for default /la-en/something for english and so on.

ajayhada avatar Aug 26 '22 09:08 ajayhada

How about src/routes/[~locale]/whatever? ~ is an allowed character, and is often used in text to mean 'ish', which isn't a million miles away from the intent here.

For reference, these are the forbidden characters — everything else is fair game:

< > : " / \ | ? *

Ideally it should be just like any other dynamic parameter, except for the prefix, so that we can do things like this:

[~locale=locale]`

(i.e. we use the locale parameter matcher, which verifies that it's a valid locale, which allows us to follow it with [x] or [...y] without ambiguity.)

Alternatives include these, though most of them have slightly confusing connotations:

[`locale]
[!locale]
[@locale]
[$locale]
[%locale]
[^locale]
[-locale]
[=locale]
[.locale]
[(locale)]
[[locale]]

Rich-Harris avatar Aug 27 '22 13:08 Rich-Harris

I'd like to use an optional route parameter, but for a different use case than adding an optional local in the path. I'd like to have URL like /my-page[optional-variant=matcher] which could match /my-page-var1, my-page-var2, but also /my-page without the optional variant. Currently I have to define 2 identical routes, one with the parameter and one without it.

(This is of course doable by using a query param to define the page variants, but I prefer to use this schema because I already use query params for another feature of my pages)

mquandalle avatar Sep 17 '22 08:09 mquandalle

I'd like to use an optional route parameter, but for a different use case than adding an optional local in the path. I'd like to have URL like /my-page[optional-variant=matcher] which could match /my-page-var1, my-page-var2, but also /my-page without the optional variant. Currently I have to define 2 identical routes, one with the parameter and one without it.

(This is of course doable by using a query param to define the page variants, but I prefer to use this schema because I already use query params for another feature of my pages)

I think that is already possible with the current matchers (not tested).

// src/params/my-page.js
/** @type {import('@sveltejs/kit').ParamMatcher */
const regex = /^my-page(-var(\d+))?$/

export function match(param) {
  return regex.test(param);
}

/**
 * @type {string} param
 */
export function getVariant(params) {
  return param["my-page"].match(regex)[2]
}
// src/routes/[my-page=my-page]/+page.js
import { getVariant } from '../../params/my-page'

/** @type {import('./$types').PageLoad */
export function load({ params }) {
  const variant = getVariant(params);
  return {
    variant
  }
}

If you visit /my-page-var123 variant will be 123

david-plugge avatar Sep 19 '22 09:09 david-plugge

NextJS uses [[...param]] for optional catch-all routes, so there's precedence for this syntax. Nuxt uses the same [[param]] notation for optional params. Feels like an emerging standard, maybe worth going this route.

dummdidumm avatar Sep 26 '22 18:09 dummdidumm