diff --git a/packages/astro/client.d.ts b/packages/astro/client.d.ts index 5ced9e99e6e1..2a44a5e45c55 100644 --- a/packages/astro/client.d.ts +++ b/packages/astro/client.d.ts @@ -125,10 +125,12 @@ declare module 'astro:i18n' { type I18nModule = typeof import('./dist/i18n/index.js'); // TODO: documentation - export const getI18nBaseUrl: (locale: string) => string; + export const getLocaleRelativeUrl: (locale: string) => string; + export const getLocaleAbsoluteUrl: (locale: string) => string; // TODO: documentation - export const getLocalesBaseUrl: () => string[]; + export const getLocaleRelativeUrlList: () => string[]; + export const getLocaleAbsoluteUrlList: () => string[]; } declare module 'astro:middleware' { diff --git a/packages/astro/src/i18n/index.ts b/packages/astro/src/i18n/index.ts index 638aa085980f..9825731bdbe8 100644 --- a/packages/astro/src/i18n/index.ts +++ b/packages/astro/src/i18n/index.ts @@ -2,18 +2,29 @@ import { AstroError } from '../core/errors/index.js'; import { MissingLocale } from '../core/errors/errors-data.js'; import { shouldAppendForwardSlash } from '../core/build/util.js'; import type { AstroConfig } from '../@types/astro.js'; +import { joinPaths } from '@astrojs/internal-helpers/path'; -type GetI18nBaseUrl = { +type GetLocaleRelativeUrl = { locale: string; base: string; locales: string[]; trailingSlash: AstroConfig['trailingSlash']; format: AstroConfig['build']['format']; }; + +type GetLocaleAbsoluteUrl = GetLocaleRelativeUrl & { + site: AstroConfig['site']; +}; /** * The base URL */ -export function getI18nBaseUrl({ locale, base, locales, trailingSlash, format }: GetI18nBaseUrl) { +export function getLocaleRelativeUrl({ + locale, + base, + locales, + trailingSlash, + format, +}: GetLocaleRelativeUrl) { if (!locales.includes(locale)) { throw new AstroError({ ...MissingLocale, @@ -29,6 +40,18 @@ export function getI18nBaseUrl({ locale, base, locales, trailingSlash, format }: } } +/** + * The absolute URL + */ +export function getLocaleAbsoluteUrl({ site, ...rest }: GetLocaleAbsoluteUrl) { + const locale = getLocaleRelativeUrl(rest); + if (site) { + return joinPaths(site, locale); + } else { + return locale; + } +} + type GetLocalesBaseUrl = { base: string; locales: string[]; @@ -36,7 +59,12 @@ type GetLocalesBaseUrl = { format: AstroConfig['build']['format']; }; -export function getLocalesBaseUrl({ base, locales, trailingSlash, format }: GetLocalesBaseUrl) { +export function getLocaleRelativeUrlList({ + base, + locales, + trailingSlash, + format, +}: GetLocalesBaseUrl) { return locales.map((locale) => { const normalizedLocale = normalizeLocale(locale); if (shouldAppendForwardSlash(trailingSlash, format)) { @@ -47,6 +75,17 @@ export function getLocalesBaseUrl({ base, locales, trailingSlash, format }: GetL }); } +export function getLocaleAbsoluteUrlList({ site, ...rest }: GetLocaleAbsoluteUrl) { + const locales = getLocaleRelativeUrlList(rest); + return locales.map((locale) => { + if (site) { + return joinPaths(site, locale); + } else { + return locale; + } + }); +} + /** * * Given a locale, this function: diff --git a/packages/astro/src/i18n/vite-plugin-i18n.ts b/packages/astro/src/i18n/vite-plugin-i18n.ts index f295ef771571..dcb301f51453 100644 --- a/packages/astro/src/i18n/vite-plugin-i18n.ts +++ b/packages/astro/src/i18n/vite-plugin-i18n.ts @@ -20,7 +20,13 @@ export default function astroInternalization({ settings }: AstroInternalization) load(id) { if (id === resolvedVirtualModuleId) { return ` - import { getI18nBaseUrl as getI18nBaseUrlInternal, getLocalesBaseUrl as _getLocalesBaseUrl } from "astro/i18n"; + import { + getLocaleRelativeUrl as _getLocaleRelativeUrl, + getLocaleRelativeUrlList as _getLocaleRelativeUrlList, + getLocaleAbsoluteUrl as _getLocaleAbsoluteUrl, + getLocaleAbsoluteUrlList as _getLocaleAbsoluteUrlList, + + } from "astro/i18n"; const defaultLocale = ${JSON.stringify(settings.config.experimental.i18n!.defaultLocale)}; const locales = ${JSON.stringify(settings.config.experimental.i18n!.locales)}; @@ -28,9 +34,12 @@ export default function astroInternalization({ settings }: AstroInternalization) const base = ${JSON.stringify(settings.config.base)}; const trailingSlash = ${JSON.stringify(settings.config.trailingSlash)}; const format = ${JSON.stringify(settings.config.build.format)}; + const site = ${JSON.stringify(settings.config.site)}; - export const getI18nBaseUrl = (locale) => getI18nBaseUrlInternal({ locale, base, locales, trailingSlash, format }); - export const getLocalesBaseUrl = () => _getLocalesBaseUrl({ base, locales, trailingSlash, format }); + export const getLocaleRelativeUrl = (locale) => _getLocaleRelativeUrl({ locale, base, locales, trailingSlash, format }); + export const getLocaleRelativeUrlList = () => _getLocaleRelativeUrlList({ base, locales, trailingSlash, format }); + export const getLocaleAbsoluteUrl = (locale) => _getLocaleAbsoluteUrl({ locale, base, locales, trailingSlash, format, site }); + export const getLocaleAbsoluteUrlList = () => _getLocaleAbsoluteUrlList({ base, locales, trailingSlash, format, site }); `; } }, diff --git a/packages/astro/test/units/i18n/astro_i18n.js b/packages/astro/test/units/i18n/astro_i18n.js new file mode 100644 index 000000000000..bd61d46a7b57 --- /dev/null +++ b/packages/astro/test/units/i18n/astro_i18n.js @@ -0,0 +1,795 @@ +import { + getLocaleRelativeUrl, + getLocaleRelativeUrlList, + getLocaleAbsoluteUrl, + getLocaleAbsoluteUrlList, +} from '../../../dist/i18n/index.js'; +import { expect } from 'chai'; + +describe('getLocaleRelativeUrl', () => { + it('should correctly return the URL with the base', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + base: '/blog', + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + + // directory format + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/blog/en/'); + expect( + getLocaleRelativeUrl({ + locale: 'es', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/blog/es/'); + + expect( + getLocaleRelativeUrl({ + locale: 'en_US', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.throw; + + // file format + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + }) + ).to.eq('/blog/en/'); + expect( + getLocaleRelativeUrl({ + locale: 'es', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + }) + ).to.eq('/blog/es/'); + + expect( + getLocaleRelativeUrl({ + locale: 'en_US', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + }) + ).to.throw; + }); + + it('should correctly return the URL without base', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'es'], + }, + }, + }; + + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/en/'); + expect( + getLocaleRelativeUrl({ + locale: 'es', + base: '/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/es/'); + }); + + it('should correctly handle the trailing slash', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'never', + format: 'directory', + }) + ).to.eq('/blog/en'); + expect( + getLocaleRelativeUrl({ + locale: 'es', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/blog/es/'); + + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'ignore', + format: 'directory', + }) + ).to.eq('/blog/en/'); + + // directory file + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'never', + format: 'file', + }) + ).to.eq('/blog/en'); + expect( + getLocaleRelativeUrl({ + locale: 'es', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + }) + ).to.eq('/blog/es/'); + + expect( + getLocaleRelativeUrl({ + locale: 'en', + // ignore + file => no trailing slash + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'ignore', + format: 'file', + }) + ).to.eq('/blog/en'); + }); + + it('should normalize locales', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + base: '/blog', + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'en_AU'], + }, + }, + }; + + expect( + getLocaleRelativeUrl({ + locale: 'en_US', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/blog/en-us/'); + + expect( + getLocaleRelativeUrl({ + locale: 'en_AU', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/blog/en-au/'); + }); +}); + +describe('getLocaleRelativeUrlList', () => { + it('should retrieve the correct list of base URL with locales [format: directory, trailingSlash: never]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleRelativeUrlList({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'never', + format: 'directory', + }) + ).to.have.members(['/blog/en', '/blog/en-us', '/blog/es']); + }); + + it('should retrieve the correct list of base URL with locales [format: directory, trailingSlash: always]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleRelativeUrlList({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.have.members(['/blog/en/', '/blog/en-us/', '/blog/es/']); + }); + + it('should retrieve the correct list of base URL with locales [format: file, trailingSlash: always]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleRelativeUrlList({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + }) + ).to.have.members(['/blog/en/', '/blog/en-us/', '/blog/es/']); + }); + + it('should retrieve the correct list of base URL with locales [format: file, trailingSlash: never]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleRelativeUrlList({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'never', + format: 'file', + }) + ).to.have.members(['/blog/en', '/blog/en-us', '/blog/es']); + }); + + it('should retrieve the correct list of base URL with locales [format: file, trailingSlash: ignore]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleRelativeUrlList({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'ignore', + format: 'file', + }) + ).to.have.members(['/blog/en', '/blog/en-us', '/blog/es']); + }); + + it('should retrieve the correct list of base URL with locales [format: directory, trailingSlash: ignore]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleRelativeUrlList({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'ignore', + format: 'directory', + }) + ).to.have.members(['/blog/en/', '/blog/en-us/', '/blog/es/']); + }); +}); + +describe('getLocaleAbsoluteUrl', () => { + it('should correctly return the URL with the base', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + base: '/blog', + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + + // directory format + expect( + getLocaleAbsoluteUrl({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + site: 'https://example.com', + }) + ).to.eq('https://example.com/blog/en/'); + expect( + getLocaleAbsoluteUrl({ + locale: 'es', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + site: 'https://example.com', + }) + ).to.eq('https://example.com/blog/es/'); + + expect( + getLocaleAbsoluteUrl({ + locale: 'en_US', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + site: 'https://example.com', + }) + ).to.throw; + + // file format + expect( + getLocaleAbsoluteUrl({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + site: 'https://example.com', + }) + ).to.eq('https://example.com/blog/en/'); + expect( + getLocaleAbsoluteUrl({ + locale: 'es', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + site: 'https://example.com', + }) + ).to.eq('https://example.com/blog/es/'); + + expect( + getLocaleAbsoluteUrl({ + locale: 'en_US', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + site: 'https://example.com', + }) + ).to.throw; + }); + + it('should correctly return the URL without base', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'es'], + }, + }, + }; + + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + site: 'https://example.com', + }) + ).to.eq('/en/'); + expect( + getLocaleRelativeUrl({ + locale: 'es', + base: '/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + site: 'https://example.com', + }) + ).to.eq('/es/'); + }); + + it('should correctly handle the trailing slash', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'never', + format: 'directory', + }) + ).to.eq('/blog/en'); + expect( + getLocaleRelativeUrl({ + locale: 'es', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/blog/es/'); + + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'ignore', + format: 'directory', + }) + ).to.eq('/blog/en/'); + + // directory file + expect( + getLocaleRelativeUrl({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'never', + format: 'file', + }) + ).to.eq('/blog/en'); + expect( + getLocaleRelativeUrl({ + locale: 'es', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + }) + ).to.eq('/blog/es/'); + + expect( + getLocaleRelativeUrl({ + locale: 'en', + // ignore + file => no trailing slash + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'ignore', + format: 'file', + }) + ).to.eq('/blog/en'); + }); + + it('should normalize locales', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + base: '/blog', + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'en_AU'], + }, + }, + }; + + expect( + getLocaleRelativeUrl({ + locale: 'en_US', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/blog/en-us/'); + + expect( + getLocaleRelativeUrl({ + locale: 'en_AU', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + }) + ).to.eq('/blog/en-au/'); + }); +}); + +describe('getLocaleAbsoluteUrlList', () => { + it('should retrieve the correct list of base URL with locales [format: directory, trailingSlash: never]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleAbsoluteUrlList({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'never', + format: 'directory', + site: 'https://example.com', + }) + ).to.have.members([ + 'https://example.com/blog/en', + 'https://example.com/blog/en-us', + 'https://example.com/blog/es', + ]); + }); + + it('should retrieve the correct list of base URL with locales [format: directory, trailingSlash: always]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleAbsoluteUrlList({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'directory', + site: 'https://example.com', + }) + ).to.have.members([ + 'https://example.com/blog/en/', + 'https://example.com/blog/en-us/', + 'https://example.com/blog/es/', + ]); + }); + + it('should retrieve the correct list of base URL with locales [format: file, trailingSlash: always]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleAbsoluteUrlList({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'always', + format: 'file', + site: 'https://example.com', + }) + ).to.have.members([ + 'https://example.com/blog/en/', + 'https://example.com/blog/en-us/', + 'https://example.com/blog/es/', + ]); + }); + + it('should retrieve the correct list of base URL with locales [format: file, trailingSlash: never]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleAbsoluteUrlList({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'never', + format: 'file', + site: 'https://example.com', + }) + ).to.have.members([ + 'https://example.com/blog/en', + 'https://example.com/blog/en-us', + 'https://example.com/blog/es', + ]); + }); + + it('should retrieve the correct list of base URL with locales [format: file, trailingSlash: ignore]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleAbsoluteUrlList({ + locale: 'en', + base: '/blog', + locales: config.experimental.i18n.locales, + trailingSlash: 'ignore', + format: 'file', + site: 'https://example.com', + }) + ).to.have.members([ + 'https://example.com/blog/en', + 'https://example.com/blog/en-us', + 'https://example.com/blog/es', + ]); + }); + + it('should retrieve the correct list of base URL with locales [format: directory, trailingSlash: ignore]', () => { + /** + * + * @type {import("../../../dist/@types").AstroUserConfig} + */ + const config = { + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'en_US', 'es'], + }, + }, + }; + // directory format + expect( + getLocaleAbsoluteUrlList({ + locale: 'en', + base: '/blog/', + locales: config.experimental.i18n.locales, + trailingSlash: 'ignore', + format: 'directory', + site: 'https://example.com', + }) + ).to.have.members([ + 'https://example.com/blog/en/', + 'https://example.com/blog/en-us/', + 'https://example.com/blog/es/', + ]); + }); +}); diff --git a/packages/astro/test/units/i18n/getI18nBaseUrl.test.js b/packages/astro/test/units/i18n/getI18nBaseUrl.test.js deleted file mode 100644 index 31cc1290d21f..000000000000 --- a/packages/astro/test/units/i18n/getI18nBaseUrl.test.js +++ /dev/null @@ -1,377 +0,0 @@ -import { getI18nBaseUrl, getLocalesBaseUrl } from '../../../dist/i18n/index.js'; -import { expect } from 'chai'; - -describe('getI18nBaseUrl', () => { - it('should correctly return the URL with the base', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - base: '/blog', - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'en_US', 'es'], - }, - }, - }; - - // directory format - expect( - getI18nBaseUrl({ - locale: 'en', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'directory', - }) - ).to.eq('/blog/en/'); - expect( - getI18nBaseUrl({ - locale: 'es', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'directory', - }) - ).to.eq('/blog/es/'); - - expect( - getI18nBaseUrl({ - locale: 'en_US', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'directory', - }) - ).to.throw; - - // file format - expect( - getI18nBaseUrl({ - locale: 'en', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'file', - }) - ).to.eq('/blog/en/'); - expect( - getI18nBaseUrl({ - locale: 'es', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'file', - }) - ).to.eq('/blog/es/'); - - expect( - getI18nBaseUrl({ - locale: 'en_US', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'file', - }) - ).to.throw; - }); - - it('should correctly return the URL without base', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'es'], - }, - }, - }; - - expect( - getI18nBaseUrl({ - locale: 'en', - base: '/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'directory', - }) - ).to.eq('/en/'); - expect( - getI18nBaseUrl({ - locale: 'es', - base: '/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'directory', - }) - ).to.eq('/es/'); - }); - - it('should correctly handle the trailing slash', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'es'], - }, - }, - }; - // directory format - expect( - getI18nBaseUrl({ - locale: 'en', - base: '/blog', - locales: config.experimental.i18n.locales, - trailingSlash: 'never', - format: 'directory', - }) - ).to.eq('/blog/en'); - expect( - getI18nBaseUrl({ - locale: 'es', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'directory', - }) - ).to.eq('/blog/es/'); - - expect( - getI18nBaseUrl({ - locale: 'en', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'ignore', - format: 'directory', - }) - ).to.eq('/blog/en/'); - - // directory file - expect( - getI18nBaseUrl({ - locale: 'en', - base: '/blog', - locales: config.experimental.i18n.locales, - trailingSlash: 'never', - format: 'file', - }) - ).to.eq('/blog/en'); - expect( - getI18nBaseUrl({ - locale: 'es', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'file', - }) - ).to.eq('/blog/es/'); - - expect( - getI18nBaseUrl({ - locale: 'en', - // ignore + file => no trailing slash - base: '/blog', - locales: config.experimental.i18n.locales, - trailingSlash: 'ignore', - format: 'file', - }) - ).to.eq('/blog/en'); - }); - - it('should normalize locales', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - base: '/blog', - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'en_US', 'en_AU'], - }, - }, - }; - - expect( - getI18nBaseUrl({ - locale: 'en_US', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'directory', - }) - ).to.eq('/blog/en-us/'); - - expect( - getI18nBaseUrl({ - locale: 'en_AU', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'directory', - }) - ).to.eq('/blog/en-au/'); - }); -}); - -describe('getLocalesBaseUrl', () => { - it('should retrieve the correct list of base URL with locales [format: directory, trailingSlash: never]', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'en_US', 'es'], - }, - }, - }; - // directory format - expect( - getLocalesBaseUrl({ - locale: 'en', - base: '/blog', - locales: config.experimental.i18n.locales, - trailingSlash: 'never', - format: 'directory', - }) - ).to.have.members(['/blog/en', '/blog/en-us', '/blog/es']); - }); - - it('should retrieve the correct list of base URL with locales [format: directory, trailingSlash: always]', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'en_US', 'es'], - }, - }, - }; - // directory format - expect( - getLocalesBaseUrl({ - locale: 'en', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'directory', - }) - ).to.have.members(['/blog/en/', '/blog/en-us/', '/blog/es/']); - }); - - it('should retrieve the correct list of base URL with locales [format: file, trailingSlash: always]', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'en_US', 'es'], - }, - }, - }; - // directory format - expect( - getLocalesBaseUrl({ - locale: 'en', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'always', - format: 'file', - }) - ).to.have.members(['/blog/en/', '/blog/en-us/', '/blog/es/']); - }); - - it('should retrieve the correct list of base URL with locales [format: file, trailingSlash: never]', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'en_US', 'es'], - }, - }, - }; - // directory format - expect( - getLocalesBaseUrl({ - locale: 'en', - base: '/blog', - locales: config.experimental.i18n.locales, - trailingSlash: 'never', - format: 'file', - }) - ).to.have.members(['/blog/en', '/blog/en-us', '/blog/es']); - }); - - it('should retrieve the correct list of base URL with locales [format: file, trailingSlash: ignore]', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'en_US', 'es'], - }, - }, - }; - // directory format - expect( - getLocalesBaseUrl({ - locale: 'en', - base: '/blog', - locales: config.experimental.i18n.locales, - trailingSlash: 'ignore', - format: 'file', - }) - ).to.have.members(['/blog/en', '/blog/en-us', '/blog/es']); - }); - - it('should retrieve the correct list of base URL with locales [format: directory, trailingSlash: ignore]', () => { - /** - * - * @type {import("../../../dist/@types").AstroUserConfig} - */ - const config = { - experimental: { - i18n: { - defaultLocale: 'en', - locales: ['en', 'en_US', 'es'], - }, - }, - }; - // directory format - expect( - getLocalesBaseUrl({ - locale: 'en', - base: '/blog/', - locales: config.experimental.i18n.locales, - trailingSlash: 'ignore', - format: 'directory', - }) - ).to.have.members(['/blog/en/', '/blog/en-us/', '/blog/es/']); - }); -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a19fcf7c8bc3..bd1507d00f18 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2773,6 +2773,24 @@ importers: specifier: ^10.17.1 version: 10.17.1 + packages/astro/test/fixtures/i18n-routing: + dependencies: + astro: + specifier: workspace:* + version: link:../../.. + + packages/astro/test/fixtures/i18n-routing-base: + dependencies: + astro: + specifier: workspace:* + version: link:../../.. + + packages/astro/test/fixtures/i18n-routing-fallback: + dependencies: + astro: + specifier: workspace:* + version: link:../../.. + packages/astro/test/fixtures/import-ts-with-js: dependencies: astro: