Skip to content

Commit

Permalink
Merge pull request #3491 from opral/parjs-435-new-api-generatestaticl…
Browse files Browse the repository at this point in the history
…ocalizedurls

generateStaticLocalizedUrls
  • Loading branch information
samuelstroschein authored Mar 10, 2025
2 parents 0ad3b13 + 8b0fbfc commit cb08ce6
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 1 deletion.
16 changes: 16 additions & 0 deletions inlang/packages/paraglide/paraglide-js/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ After

- make `setLocale()` set all strategies. Setting all strategies aligns with user expectations and ensures that server APIs can receive the cookie of the client, for example. [#439](https://github.com/opral/inlang-paraglide-js/issues/439)

- new `generateStaticLocalizedUrls()` API [#443](https://github.com/opral/inlang-paraglide-js/issues/433)

```diff
const localizedUrls = generateStaticLocalizedUrls([
"/example",
"/page/blog",
"/123/hello"
])

console.log(localizedUrls.map(url => url.pathnames))
>> /de/example
>> /fr/example
>> ...
```


## 2.0.0-beta.27

- fix wrong matching in API requests [#427](https://github.com/opral/inlang-paraglide-js/issues/427)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,47 @@ The extracted locale, or undefined if no locale is found.

***

## generateStaticLocalizedUrls()

> **generateStaticLocalizedUrls**(`urls`): `URL`[]
Defined in: [runtime/generate-static-localized-urls.js:31](https://github.com/opral/monorepo/tree/main/inlang/packages/paraglide/paraglide-js/src/compiler/runtime/generate-static-localized-urls.js)

Generates a list of localized URLs for all provided URLs.

This is useful for SSG (Static Site Generation) and sitemap generation.
NextJS and other frameworks use this function for SSG.

### Parameters

#### urls

(`string` \| `URL`)[]

List of URLs to generate localized versions for. Can be absolute URLs or paths.

### Returns

`URL`[]

List of localized URLs as URL objects

### Example

```typescript
const urls = generateStaticLocalizedUrls([
"https://example.com/about",
"https://example.com/blog",
]);
urls[0].href // => "https://example.com/about"
urls[1].href // => "https://example.com/blog"
urls[2].href // => "https://example.com/de/about"
urls[3].href // => "https://example.com/de/blog"
...
```

***

## getLocale()

> **getLocale**(): `any`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ The Paraglide runtime API.

> **extractLocaleFromUrl**: [`extractLocaleFromUrl`](-internal-.md#extractlocalefromurl)
#### generateStaticLocalizedUrls

> **generateStaticLocalizedUrls**: [`generateStaticLocalizedUrls`](-internal-.md#generatestaticlocalizedurls)
#### getLocale

> **getLocale**: [`getLocale`](-internal-.md#getlocale)
Expand Down
2 changes: 1 addition & 1 deletion inlang/packages/paraglide/paraglide-js/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@inlang/paraglide-js",
"type": "module",
"version": "2.0.0-beta.27",
"version": "2.0.0-beta.28",
"license": "MIT",
"publishConfig": {
"access": "public",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ ${injectCode("./localize-href.js")}
${injectCode("./track-message-call.js")}
${injectCode("./generate-static-localized-urls.js")}
// ------ TYPES ------
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { localizeUrl } from "./localize-url.js";
import {
locales,
baseLocale,
TREE_SHAKE_DEFAULT_URL_PATTERN_USED,
urlPatterns,
} from "./variables.js";

/**
* Generates a list of localized URLs for all provided URLs.
*
* This is useful for SSG (Static Site Generation) and sitemap generation.
* NextJS and other frameworks use this function for SSG.
*
* @example
* ```typescript
* const urls = generateStaticLocalizedUrls([
* "https://example.com/about",
* "https://example.com/blog",
* ]);
* urls[0].href // => "https://example.com/about"
* urls[1].href // => "https://example.com/blog"
* urls[2].href // => "https://example.com/de/about"
* urls[3].href // => "https://example.com/de/blog"
* ...
* ```
*
* @param {(string | URL)[]} urls - List of URLs to generate localized versions for. Can be absolute URLs or paths.
* @returns {URL[]} List of localized URLs as URL objects
*/
export function generateStaticLocalizedUrls(urls) {
const localizedUrls = new Set();

// For default URL pattern, we can optimize the generation
if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
for (const urlInput of urls) {
const url =
urlInput instanceof URL
? urlInput
: new URL(urlInput, "http://localhost");

// Base locale doesn't get a prefix
localizedUrls.add(url);

// Other locales get their code as prefix
for (const locale of locales) {
if (locale !== baseLocale) {
const localizedPath = `/${locale}${url.pathname}${url.search}${url.hash}`;
const localizedUrl = new URL(localizedPath, url.origin);
localizedUrls.add(localizedUrl);
}
}
}
return Array.from(localizedUrls);
}

// For custom URL patterns, we need to use localizeUrl for each URL and locale
for (const urlInput of urls) {
const url =
urlInput instanceof URL
? urlInput
: new URL(urlInput, "http://localhost");

// Try each URL pattern to find one that matches
let patternFound = false;
for (const pattern of urlPatterns) {
try {
// Try to match the unlocalized pattern
const unlocalizedMatch = new URLPattern(pattern.pattern, url.href).exec(
url.href
);

if (!unlocalizedMatch) continue;

patternFound = true;

// Track unique localized URLs to avoid duplicates when patterns are the same
const seenUrls = new Set();

// Generate localized URL for each locale
for (const [locale] of pattern.localized) {
try {
const localizedUrl = localizeUrl(url, { locale });
const urlString = localizedUrl.href;

// Only add if we haven't seen this exact URL before
if (!seenUrls.has(urlString)) {
seenUrls.add(urlString);
localizedUrls.add(localizedUrl);
}
} catch {
// Skip if localization fails for this locale
continue;
}
}
break;
} catch {
// Skip if pattern matching fails
continue;
}
}

// If no pattern matched, use the URL as is
if (!patternFound) {
localizedUrls.add(url);
}
}

return Array.from(localizedUrls);
}
Loading

0 comments on commit cb08ce6

Please sign in to comment.