Skip to content

Commit

Permalink
Fix invalid locale issue (#2548)
Browse files Browse the repository at this point in the history
* fix: fallback to default locale in project without a root locale for pages not matching the default locale slug

* Update changeset

* refactor: remove duplicated `slugToLocale()`

---------

Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
  • Loading branch information
HiDeoo and delucis authored Nov 1, 2024
1 parent bf42300 commit 07673c8
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-toys-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': patch
---

Fixes a URL localization edge case. In projects without a root locale configured, slugs without a locale prefix did not fall back to the default locale as expected.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock('astro:content', async () =>
['fr/index.mdx', { title: 'Accueil' }],
// @ts-expect-error — Using a slug not present in Starlight docs site
['en/index.mdx', { title: 'Home page' }],
['404.md', { title: '404' }],
],
})
);
Expand All @@ -17,16 +18,16 @@ test('route slugs are normalized', () => {
});

test('routes for the configured locale have locale data added', () => {
for (const route of routes) {
if (route.id.startsWith('fr')) {
expect(route.lang).toBe('fr-CA');
expect(route.dir).toBe('ltr');
expect(route.locale).toBe('fr');
} else {
expect(route.lang).toBe('fr-CA');
expect(route.dir).toBe('ltr');
expect(route.locale).toBeUndefined();
}
expect(routes[0]?.lang).toBe('fr-CA');
expect(routes[0]?.dir).toBe('ltr');
expect(routes[0]?.locale).toBe('fr');
});

test('routes not matching the configured locale fall back to the default locale', () => {
for (const route of routes.slice(1)) {
expect(route.lang).toBe('fr-CA');
expect(route.dir).toBe('ltr');
expect(route.locale).toBe('fr');
}
});

Expand Down
10 changes: 10 additions & 0 deletions packages/starlight/__tests__/i18n/routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ vi.mock('astro:content', async () =>
['en/guides/authoring-content.md', { title: 'Création de contenu en Markdown' }],
// @ts-expect-error — Using a slug not present in Starlight docs site
['en/404.md', { title: 'Page introuvable' }],
['it/index.mdx', { title: 'Pagina iniziale' }],
['404.md', { title: '404' }],
],
})
);
Expand Down Expand Up @@ -42,6 +44,14 @@ test('routes have locale data added', () => {
expect(lang).toBe('fr');
expect(dir).toBe('ltr');
expect(locale).toBe('fr');
} else if (id.startsWith('pt-br')) {
expect(lang).toBe('pt-BR');
expect(dir).toBe('ltr');
expect(locale).toBe('pt-br');
} else {
expect(lang).toBe('en-US');
expect(dir).toBe('ltr');
expect(locale).toBe('en');
}
}
});
Expand Down
14 changes: 3 additions & 11 deletions packages/starlight/integrations/shared/pathToLocale.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import type { AstroConfig } from 'astro';
import type { StarlightConfig } from '../../types';

function slugToLocale(
slug: string | undefined,
localesConfig: StarlightConfig['locales']
): string | undefined {
const locales = Object.keys(localesConfig || {});
const baseSegment = slug?.split('/')[0];
return baseSegment && locales.includes(baseSegment) ? baseSegment : undefined;
}
import { slugToLocale } from './slugToLocale';

/** Get current locale from the full file path. */
export function pathToLocale(
Expand All @@ -17,7 +9,7 @@ export function pathToLocale(
starlightConfig,
astroConfig,
}: {
starlightConfig: { locales: StarlightConfig['locales'] };
starlightConfig: Pick<StarlightConfig, 'defaultLocale' | 'locales'>;
astroConfig: { root: AstroConfig['root']; srcDir: AstroConfig['srcDir'] };
}
): string | undefined {
Expand All @@ -31,5 +23,5 @@ export function pathToLocale(
// Strip docs path leaving only content collection file ID.
// Example: /Users/houston/repo/src/content/docs/en/guide.md => en/guide.md
const slug = path?.replace(docsDir.pathname, '');
return slugToLocale(slug, starlightConfig.locales);
return slugToLocale(slug, starlightConfig);
}
18 changes: 18 additions & 0 deletions packages/starlight/integrations/shared/slugToLocale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { StarlightConfig } from '../../types';

/**
* Get the “locale” of a slug. This is the base path at which a language is served.
* For example, if French docs are in `src/content/docs/french/`, the locale is `french`.
* Root locale slugs will return `undefined`.
* @param slug A collection entry slug
*/
export function slugToLocale(
slug: string | undefined,
config: Pick<StarlightConfig, 'defaultLocale' | 'locales'>
): string | undefined {
const localesConfig = config.locales ?? {};
const baseSegment = slug?.split('/')[0];
if (baseSegment && localesConfig[baseSegment]) return baseSegment;
if (!localesConfig.root) return config.defaultLocale.locale;
return undefined;
}
6 changes: 2 additions & 4 deletions packages/starlight/utils/slugs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import config from 'virtual:starlight/user-config';
import { BuiltInDefaultLocale } from './i18n';
import { stripTrailingSlash } from './path';
import { slugToLocale as getLocaleFromSlug } from '../integrations/shared/slugToLocale';

export interface LocaleData {
/** Writing direction. */
Expand All @@ -18,10 +19,7 @@ export interface LocaleData {
* @param slug A collection entry slug
*/
function slugToLocale(slug: string): string | undefined {
const locales = Object.keys(config.locales || {});
const baseSegment = slug.split('/')[0];
if (baseSegment && locales.includes(baseSegment)) return baseSegment;
return undefined;
return getLocaleFromSlug(slug, config);
}

/** Get locale information for a given slug. */
Expand Down

0 comments on commit 07673c8

Please sign in to comment.