diff --git a/docs/pages/i18n/+Page.mdx b/docs/pages/i18n/+Page.mdx index 35c8f91b373..060adf4ff66 100644 --- a/docs/pages/i18n/+Page.mdx +++ b/docs/pages/i18n/+Page.mdx @@ -9,6 +9,7 @@ For example: export { onBeforeRoute } +import { modifyUrl } from 'vike/modifyUrl' import type { Url } from 'vike/types' function onBeforeRoute(pageContext) { @@ -38,8 +39,7 @@ function extractLocale(url: Url) { const pathnameWithoutLocale = /* ... */ // Reconstruct full URL - const { origin, searchOriginal, hashOriginal } = url - const urlWithoutLocale = `${origin || ''}${pathnameWithoutLocale}${searchOriginal || ''}${hashOriginal || ''}` + const urlWithoutLocale = modifyUrl(url.href, { pathname: pathnameWithoutLocale }) return { locale, urlWithoutLocale } } diff --git a/examples/i18n/locales/extractLocale.js b/examples/i18n/locales/extractLocale.js index 6bcf59e4c10..81aee0dc71e 100644 --- a/examples/i18n/locales/extractLocale.js +++ b/examples/i18n/locales/extractLocale.js @@ -2,20 +2,20 @@ export { extractLocale } import { locales, localeDefault } from './locales' -function extractLocale(url) { - const urlPaths = url.split('/') +function extractLocale(urlPathname) { + const path = urlPathname.split('/') let locale - let urlWithoutLocale + let urlPathnameWithoutLocale // We remove the URL locale, for example `/de-DE/about` => `/about` - const firstPath = urlPaths[1] - if (locales.filter((locale) => locale !== localeDefault).includes(firstPath)) { - locale = firstPath - urlWithoutLocale = '/' + urlPaths.slice(2).join('/') + const first = path[1] + if (locales.filter((locale) => locale !== localeDefault).includes(first)) { + locale = first + urlPathnameWithoutLocale = '/' + path.slice(2).join('/') } else { locale = localeDefault - urlWithoutLocale = url + urlPathnameWithoutLocale = urlPathname } - return { locale, urlWithoutLocale } + return { locale, urlPathnameWithoutLocale } } diff --git a/examples/i18n/renderer/+onBeforeRoute.js b/examples/i18n/renderer/+onBeforeRoute.js index 680fc61f98b..0900170eba5 100644 --- a/examples/i18n/renderer/+onBeforeRoute.js +++ b/examples/i18n/renderer/+onBeforeRoute.js @@ -1,15 +1,18 @@ export default onBeforeRoute import { extractLocale } from '../locales' +import { modifyUrl } from 'vike/modifyUrl' function onBeforeRoute(pageContext) { - const { urlWithoutLocale, locale } = extractLocale(pageContext.urlPathname) + const url = pageContext.urlParsed + const { urlPathnameWithoutLocale, locale } = extractLocale(url.pathname) + const urlLogical = modifyUrl(url.href, { pathname: urlPathnameWithoutLocale }) return { pageContext: { // Make `locale` available as pageContext.locale locale, // Vike's router will use pageContext.urlLogical instead of pageContext.urlOriginal - urlLogical: urlWithoutLocale + urlLogical } } } diff --git a/vike/package.json b/vike/package.json index fd488fc6680..633460eddbe 100644 --- a/vike/package.json +++ b/vike/package.json @@ -116,6 +116,15 @@ "types": "./dist/esm/shared/getPageContext.d.ts", "default": "./dist/esm/shared/getPageContext.js" }, + "./modifyUrl": { + "worker": "./dist/esm/shared/modifyUrl.js", + "edge-light": "./dist/esm/shared/modifyUrl.js", + "require": "./dist/cjs/shared/modifyUrl.js", + "node": "./dist/esm/shared/modifyUrl.js", + "browser": "./dist/esm/shared/modifyUrl.js", + "types": "./dist/esm/shared/modifyUrl.d.ts", + "default": "./dist/esm/shared/modifyUrl.js" + }, "./__internal": { "require": "./dist/cjs/__internal/index.js", "node": "./dist/esm/__internal/index.js", @@ -183,6 +192,9 @@ "getPageContext": [ "./dist/esm/shared/getPageContext.d.ts" ], + "modifyUrl": [ + "./dist/esm/shared/modifyUrl.d.ts" + ], "__internal": [ "./dist/esm/__internal/index.d.ts" ], diff --git a/vike/shared/modifyUrl.ts b/vike/shared/modifyUrl.ts new file mode 100644 index 00000000000..461528fd6c6 --- /dev/null +++ b/vike/shared/modifyUrl.ts @@ -0,0 +1,45 @@ +export { modifyUrl } + +import { createUrlFromComponents, parseUrl } from './utils.js' + +/** + * Modify a URL. + * + * Example: changing the URL pathname for internationalization. + * + * https://vike.dev/modifyUrl + */ +function modifyUrl( + url: string, + modify: { + pathname?: string + hostname?: string + port?: number + protocol?: string + } +): string { + const urlParsed = parseUrl(url, '/') + + // Pathname + const pathname = modify.pathname ?? urlParsed.pathname + + // Origin + const originParts: string[] = [ + modify.protocol ?? urlParsed.protocol ?? '', + modify.hostname ?? urlParsed.hostname ?? '' + ] + const port = modify.port ?? urlParsed.port + if (port || port === 0) { + originParts.push(`:${port}`) + } + const origin = originParts.join('') + + const urlModified = createUrlFromComponents( + origin, + pathname, + // Should we also support modifying search and hash? + urlParsed.searchOriginal, + urlParsed.hashOriginal + ) + return urlModified +}