Repository: makeswift/makeswift
Pull request #1005: feat: localized pages are online by default, fallback to the base locale
Author: login: agurtovoy
Created: 2025-04-20T01:48:50Z | Merged: 2025-04-20T16:40:54Z

Last piece of [ENG-7803](https://linear.app/makeswift/issue/ENG-7803/make-localized-pages-online-by-default).


Diff:
diff --git a/.changeset/lemon-ravens-divide.md b/.changeset/lemon-ravens-divide.md
new file mode 100644
index 000000000..b796d0ab0
--- /dev/null
+++ b/.changeset/lemon-ravens-divide.md
@@ -0,0 +1,19 @@
+---
+'@makeswift/runtime': minor
+---
+
+BREAKING: Upgrading to this runtime version will opt your site into the new localized pages behavior, both in the builder and on the live site.
+
+Localized pages are no longer silently created when navigated to in the builder. Instead, they now automatically fall back to the base locale by default. To create a localized page, users must take explicit action in the builder.
+
+Localized pages that are explicitly marked as Offline will remain offline.
+
+You can disable fallback behavior on a per-page basis by passing `allowLocaleFallback: false` to the `client.getPageSnapshot` call:
+
+```typescript
+const snapshot = await client.getPageSnapshot(path, {
+  siteVersion: await getSiteVersion(),
+  locale,
+  allowLocaleFallback: false,
+})
+```
diff --git a/packages/runtime/src/next/api-handler/handlers/manifest.ts b/packages/runtime/src/next/api-handler/handlers/manifest.ts
index 6984acf12..02259ab55 100644
--- a/packages/runtime/src/next/api-handler/handlers/manifest.ts
+++ b/packages/runtime/src/next/api-handler/handlers/manifest.ts
@@ -15,6 +15,7 @@ export type Manifest = {
   unstable_siteVersions: boolean
   localizedPageSSR: boolean
   webhook: boolean
+  localizedPagesOnlineByDefault: boolean
 }
 
 type ManifestError = { message: string }
@@ -85,6 +86,7 @@ export default async function handler(
     unstable_siteVersions: true,
     localizedPageSSR: true,
     webhook: supportsWebhook,
+    localizedPagesOnlineByDefault: true,
   }
 
   return match(args)
diff --git a/packages/runtime/src/next/client.ts b/packages/runtime/src/next/client.ts
index 5c5d41dcc..411018ac0 100644
--- a/packages/runtime/src/next/client.ts
+++ b/packages/runtime/src/next/client.ts
@@ -662,16 +662,23 @@ export class Makeswift {
     {
       siteVersion: siteVersionPromise,
       locale,
-    }: { siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>; locale?: string },
+      allowLocaleFallback = true,
+    }: {
+      siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>
+      locale?: string
+      allowLocaleFallback?: boolean
+    },
   ): Promise<MakeswiftPageSnapshot | null> {
-    const searchParams = new URLSearchParams()
-    if (locale) {
-      searchParams.set('locale', locale)
+    const queryParams = (): string => {
+      const params = new URLSearchParams()
+      if (locale) params.set('locale', locale)
+      if (allowLocaleFallback != null) params.set('allowLocaleFallback', `${allowLocaleFallback}`)
+      return params.toString()
     }
 
     const siteVersion = await siteVersionPromise
     const response = await this.fetch(
-      `v3/pages/${encodeURIComponent(pathname)}/document?${searchParams.toString()}`,
+      `v3/pages/${encodeURIComponent(pathname)}/document?${queryParams()}`,
       siteVersion,
     )
 
diff --git a/packages/runtime/src/next/tests/__snapshots__/client.get-page-snapshot.test.ts.snap b/packages/runtime/src/next/tests/__snapshots__/client.get-page-snapshot.test.ts.snap
new file mode 100644
index 000000000..25e42ecb4
--- /dev/null
+++ b/packages/runtime/src/next/tests/__snapshots__/client.get-page-snapshot.test.ts.snap
@@ -0,0 +1,50 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`getPageSnapshot correctly passes \`locale\` parameter (es-MX) 1`] = `
+{
+  "apiResources": {
+    "File": [],
+    "GlobalElement": [],
+    "LocalizedGlobalElement": [],
+    "PagePathnameSlice": [],
+    "Swatch": [],
+    "Table": [],
+    "Typography": [],
+  },
+  "localizedResourcesMap": {
+    "es-MX": {},
+  },
+}
+`;
+
+exports[`getPageSnapshot correctly passes \`locale\` parameter (fr) 1`] = `
+{
+  "apiResources": {
+    "File": [],
+    "GlobalElement": [],
+    "LocalizedGlobalElement": [],
+    "PagePathnameSlice": [],
+    "Swatch": [],
+    "Table": [],
+    "Typography": [],
+  },
+  "localizedResourcesMap": {
+    "fr": {},
+  },
+}
+`;
+
+exports[`getPageSnapshot correctly passes \`locale\` parameter (undefined) 1`] = `
+{
+  "apiResources": {
+    "File": [],
+    "GlobalElement": [],
+    "LocalizedGlobalElement": [],
+    "PagePathnameSlice": [],
+    "Swatch": [],
+    "Table": [],
+    "Typography": [],
+  },
+  "localizedResourcesMap": {},
+}
+`;
diff --git a/packages/runtime/src/next/tests/client.get-page-snapshot.test.ts b/packages/runtime/src/next/tests/client.get-page-snapshot.test.ts
index 8e43adcb3..331b69865 100644
--- a/packages/runtime/src/next/tests/client.get-page-snapshot.test.ts
+++ b/packages/runtime/src/next/tests/client.get-page-snapshot.test.ts
@@ -1,4 +1,4 @@
-import { Makeswift } from '../client'
+import { Makeswift, type MakeswiftPageDocument } from '../client'
 import { http, HttpResponse } from 'msw'
 
 import { ReactRuntime } from '../../runtimes/react'
@@ -16,6 +16,24 @@ function createTestClient() {
 
 let consoleErrorSpy: jest.SpyInstance
 
+const pageDocument: MakeswiftPageDocument = {
+  id: '[page-id]',
+  site: {
+    id: '[site-id]',
+  },
+  data: {
+    type: '[element-type]',
+    key: '[element-key]',
+    props: {},
+  },
+  snippets: [],
+  fonts: [],
+  meta: {},
+  seo: {},
+  localizedPages: [],
+  locale: null,
+}
+
 beforeEach(() => {
   consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
 })
@@ -27,8 +45,95 @@ afterEach(() => {
 describe('getPageSnapshot', () => {
   const pathname = 'blog/hello-world'
   const snapshotUrl = `${apiOrigin}/v3/pages/${encodeURIComponent(pathname)}/document`
+  const graphqlUrl = `${apiOrigin}/graphql`
   const locale = 'es-MX'
 
+  test.each([undefined, 'fr', 'es-MX'])(
+    'correctly passes `locale` parameter (%s)',
+    async locale => {
+      // Arrange
+      const client = createTestClient()
+
+      server.use(
+        // page snapshot request
+        http.get(
+          snapshotUrl,
+          ({ request }) => {
+            const url = new URL(request.url)
+            return HttpResponse.json(
+              { ...pageDocument, locale: url.searchParams.get('locale') },
+              { status: 200 },
+            )
+          },
+          {
+            once: true,
+          },
+        ),
+        // introspection query
+        http.post(graphqlUrl, () => HttpResponse.json({}, { status: 200 }), {
+          once: true,
+        }),
+      )
+
+      // Act
+      const result = await client.getPageSnapshot(pathname, {
+        locale,
+        siteVersion: MakeswiftSiteVersion.Working,
+      })
+
+      // Assert
+      expect(result?.cacheData).toMatchSnapshot()
+      expect(result?.document.locale).toEqual(locale ?? null)
+    },
+  )
+
+  test.each([
+    [undefined, '[fallback-page-id]'],
+    [true, '[fallback-page-id]'],
+    [false, '[page-id]'],
+  ])(
+    'correctly passes `allowLocaleFallback` parameter (%s)',
+    async (allowLocaleFallback, expectedPageId) => {
+      // Arrange
+      const client = createTestClient()
+
+      server.use(
+        // page snapshot request
+        http.get(
+          snapshotUrl,
+          ({ request }) => {
+            const url = new URL(request.url)
+            const allowFallback = url.searchParams.get('allowLocaleFallback') === 'true'
+            return HttpResponse.json(
+              {
+                ...pageDocument,
+                id: allowFallback ? '[fallback-page-id]' : '[page-id]',
+              },
+              { status: 200 },
+            )
+          },
+          {
+            once: true,
+          },
+        ),
+        // introspection query
+        http.post(graphqlUrl, () => HttpResponse.json({}, { status: 200 }), {
+          once: true,
+        }),
+      )
+
+      // Act
+      const result = await client.getPageSnapshot(pathname, {
+        locale,
+        allowLocaleFallback,
+        siteVersion: MakeswiftSiteVersion.Working,
+      })
+
+      // Assert
+      expect(result?.document.id).toEqual(expectedPageId)
+    },
+  )
+
   test('returns null on 404', async () => {
     // Arrange
     const client = createTestClient()


Reviews:
user: login: arvinpoddar
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-20T05:34:33Z
user: login: arvinpoddar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-20T05:48:44Z
user: login: agurtovoy
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-20T05:52:58Z
user: login: agurtovoy
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-20T05:59:39Z
user: login: arvinpoddar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-20T06:02:59Z
user: login: agurtovoy
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-20T06:19:30Z

Review comments:
diff_hunk: @@ -662,16 +662,23 @@ export class Makeswift {
     {
       siteVersion: siteVersionPromise,
       locale,
-    }: { siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>; locale?: string },
+      allowLocaleFallback = true,
+    }: {
+      siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>
+      locale?: string
+      allowLocaleFallback?: boolean
+    },
   ): Promise<MakeswiftPageSnapshot | null> {
-    const searchParams = new URLSearchParams()
-    if (locale) {
-      searchParams.set('locale', locale)
+    const queryParams = (): string => {
+      const params = new URLSearchParams()
+      if (locale) params.set('locale', locale)
+      if (allowLocaleFallback != null) params.set('allowLocaleFallback', `${allowLocaleFallback}`)
+      return params.toString()
     }
path: packages/runtime/src/next/client.ts
user: login: arvinpoddar
body: Curious, why'd we put this in a function instead of having it inline?
created_at: 2025-04-20T05:32:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,19 @@
+---
+'@makeswift/runtime': minor
+---
+
+BREAKING: Upgrading to this runtime version will opt your site into the new localized pages behavior, both in the builder and on the live site.
+
+Localized pages are no longer silently created when navigated to in the builder. Instead, they now automatically fall back to the base locale by default. To create a localized page, users must take explicit action in the builder.
+
+Localized pages that are explicitly marked as Offline will remain offline.
path: .changeset/lemon-ravens-divide.md
user: login: arvinpoddar
body: Should we specify that this is "marked as Offline **in the builder**"?
created_at: 2025-04-20T05:48:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -662,16 +662,23 @@ export class Makeswift {
     {
       siteVersion: siteVersionPromise,
       locale,
-    }: { siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>; locale?: string },
+      allowLocaleFallback = true,
+    }: {
+      siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>
+      locale?: string
+      allowLocaleFallback?: boolean
+    },
   ): Promise<MakeswiftPageSnapshot | null> {
-    const searchParams = new URLSearchParams()
-    if (locale) {
-      searchParams.set('locale', locale)
+    const queryParams = (): string => {
+      const params = new URLSearchParams()
+      if (locale) params.set('locale', locale)
+      if (allowLocaleFallback != null) params.set('allowLocaleFallback', `${allowLocaleFallback}`)
+      return params.toString()
     }
path: packages/runtime/src/next/client.ts
user: login: agurtovoy
body: It's a technique for limiting the scope / lifting the level of abstraction at this level. A function basically gives you a named entity you can skip over/collapse until/unless you are ready to examine what exactly hides under that name.
created_at: 2025-04-20T05:52:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,19 @@
+---
+'@makeswift/runtime': minor
+---
+
+BREAKING: Upgrading to this runtime version will opt your site into the new localized pages behavior, both in the builder and on the live site.
+
+Localized pages are no longer silently created when navigated to in the builder. Instead, they now automatically fall back to the base locale by default. To create a localized page, users must take explicit action in the builder.
+
+Localized pages that are explicitly marked as Offline will remain offline.
path: .changeset/lemon-ravens-divide.md
user: login: agurtovoy
body: We can, although IMHO this is understood and also the previous para ends with "in the builder."
created_at: 2025-04-20T05:59:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -662,16 +662,23 @@ export class Makeswift {
     {
       siteVersion: siteVersionPromise,
       locale,
-    }: { siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>; locale?: string },
+      allowLocaleFallback = true,
+    }: {
+      siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>
+      locale?: string
+      allowLocaleFallback?: boolean
+    },
   ): Promise<MakeswiftPageSnapshot | null> {
-    const searchParams = new URLSearchParams()
-    if (locale) {
-      searchParams.set('locale', locale)
+    const queryParams = (): string => {
+      const params = new URLSearchParams()
+      if (locale) params.set('locale', locale)
+      if (allowLocaleFallback != null) params.set('allowLocaleFallback', `${allowLocaleFallback}`)
+      return params.toString()
     }
path: packages/runtime/src/next/client.ts
user: login: arvinpoddar
body: Let me elaborate, what I meant was _why_ we decided to abstract this particular case. I don't think it's a bad choice, I've just been in similar situations where I decided the implementation was light enough not to. Trying to develop a sense for when something should be encapsulated like this.
created_at: 2025-04-20T06:02:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -662,16 +662,23 @@ export class Makeswift {
     {
       siteVersion: siteVersionPromise,
       locale,
-    }: { siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>; locale?: string },
+      allowLocaleFallback = true,
+    }: {
+      siteVersion: MakeswiftSiteVersion | Promise<MakeswiftSiteVersion>
+      locale?: string
+      allowLocaleFallback?: boolean
+    },
   ): Promise<MakeswiftPageSnapshot | null> {
-    const searchParams = new URLSearchParams()
-    if (locale) {
-      searchParams.set('locale', locale)
+    const queryParams = (): string => {
+      const params = new URLSearchParams()
+      if (locale) params.set('locale', locale)
+      if (allowLocaleFallback != null) params.set('allowLocaleFallback', `${allowLocaleFallback}`)
+      return params.toString()
     }
path: packages/runtime/src/next/client.ts
user: login: agurtovoy
body: My general guideline for writing function-level code is that [all statements within a function should operate at the same level of abstraction](https://www.google.com/search?q=single+level+of+abstraction). I was adding an extra `if` statement to this logic, and what previously felt like an okay-ish "inlined abstraction" started to feel too in-the-weeds to keep inlined.
created_at: 2025-04-20T06:19:30Z
author_association: CONTRIBUTOR

PR comments:
user: login: linear[bot]
created_at: 2025-04-20T01:48:53Z
body: <p><a href="https://linear.app/makeswift/issue/ENG-7803/make-localized-pages-online-by-default">ENG-7803 Make localized pages online by default</a></p>
author_association: NONE
user: login: changeset-bot[bot]
created_at: 2025-04-20T01:48:54Z
body: ###  🦋  Changeset detected

Latest commit: cddaf7fe61e3fd7bdb1ceb75f1cf647816c18850

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name               | Type  |
| ------------------ | ----- |
| @makeswift/runtime | Minor |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/makeswift/makeswift/new/aleksey/eng-7803-localized-pages-online-by-default?filename=.changeset/sweet-snakes-cough.md&value=---%0A%22%40makeswift%2Fruntime%22%3A%20patch%0A---%0A%0Afeat%3A%20localized%20pages%20are%20online%20by%20default%2C%20fallback%20to%20the%20base%20locale%0A)


author_association: NONE
user: login: vercel[bot]
created_at: 2025-04-20T01:48:55Z
body: [vc]: #qUxiR2qIfCQERIj9+Xn6TuVyqyFFOCDjssROEgF0cd4=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJtYWtlc3dpZnQtZXhhbXBsZXMtYmFzaWMtdHlwZXNjcmlwdCIsInJvb3REaXJlY3RvcnkiOiJleGFtcGxlcy9iYXNpYy10eXBlc2NyaXB0IiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL21ha2Vzd2lmdC9tYWtlc3dpZnQtZXhhbXBsZXMtYmFzaWMtdHlwZXNjcmlwdC84QzVzNWtWS253Y3FRUkNVcGQ2SkpqZEpTRXNwIiwicHJldmlld1VybCI6Im1ha2Vzd2lmdC1leGFtcGxlcy1iYXNpYy10eXBlc2NyaXB0LWdpdC1hbGVrc2UtM2RkZTIzLW1ha2Vzd2lmdC52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IklHTk9SRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn19LHsibmFtZSI6Im1ha2Vzd2lmdC1leGFtcGxlcy10aGlyZHdlYiIsInJvb3REaXJlY3RvcnkiOiJleGFtcGxlcy90aGlyZHdlYiIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9tYWtlc3dpZnQvbWFrZXN3aWZ0LWV4YW1wbGVzLXRoaXJkd2ViL0c1ZUU2dXFlUTRkVzc1QVM5Y3lBemtlbTN4UnkiLCJwcmV2aWV3VXJsIjoibWFrZXN3aWZ0LWV4YW1wbGVzLXRoaXJkd2ViLWdpdC1hbGVrc2V5LWVuZy03OC1kYmYxOTMtbWFrZXN3aWZ0LnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifX0seyJuYW1lIjoibWFrZXN3aWZ0LWV4YW1wbGVzLWJpZ2NvbW1lcmNlIiwicm9vdERpcmVjdG9yeSI6ImV4YW1wbGVzL2JpZ2NvbW1lcmNlIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL21ha2Vzd2lmdC9tYWtlc3dpZnQtZXhhbXBsZXMtYmlnY29tbWVyY2UvSGU1Nkp6dERMTkZ4VHBOTVVzSjZFamc5ZUxNdiIsInByZXZpZXdVcmwiOiJtYWtlc3dpZnQtZXhhbXBsZXMtYmlnY29tbWVyY2UtZ2l0LWFsZWtzZXktZW5nLTFiYzFmNi1tYWtlc3dpZnQudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJtYWtlc3dpZnQtZXhhbXBsZXMtYmlnY29tbWVyY2UtZ2l0LWFsZWtzZXktZW5nLTFiYzFmNi1tYWtlc3dpZnQudmVyY2VsLmFwcCJ9fSx7Im5hbWUiOiJtYWtlc3dpZnQtZXhhbXBsZXMtc2hvcGlmeSIsInJvb3REaXJlY3RvcnkiOiJleGFtcGxlcy9zaG9waWZ5IiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL21ha2Vzd2lmdC9tYWtlc3dpZnQtZXhhbXBsZXMtc2hvcGlmeS9FR2czMXZwSmN2NTh1eVlYa0Y1WlNyOFk1MWJrIiwicHJldmlld1VybCI6Im1ha2Vzd2lmdC1leGFtcGxlcy1zaG9waWZ5LWdpdC1hbGVrc2V5LWVuZy03ODAtOTVlZDI2LW1ha2Vzd2lmdC52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IklHTk9SRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn19LHsibmFtZSI6Im1ha2Vzd2lmdC1uZXh0anMtYXBwLXJvdXRlciIsInJvb3REaXJlY3RvcnkiOiJhcHBzL25leHRqcy1hcHAtcm91dGVyIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL21ha2Vzd2lmdC9tYWtlc3dpZnQtbmV4dGpzLWFwcC1yb3V0ZXIvN0h3VkNkZHhRWkdKSHVESmRUcTVoMnZWakRGNCIsInByZXZpZXdVcmwiOiJtYWtlc3dpZnQtbmV4dGpzLWFwcC1yb3V0ZXItZ2l0LWFsZWtzZXktZW5nLTc4LTEwMTcyYS1tYWtlc3dpZnQudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJtYWtlc3dpZnQtbmV4dGpzLWFwcC1yb3V0ZXItZ2l0LWFsZWtzZXktZW5nLTc4LTEwMTcyYS1tYWtlc3dpZnQudmVyY2VsLmFwcCJ9fSx7Im5hbWUiOiJtYWtlc3dpZnQtbmV4dGpzLXBhZ2VzLXJvdXRlciIsInJvb3REaXJlY3RvcnkiOiJhcHBzL25leHRqcy1wYWdlcy1yb3V0ZXIiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vbWFrZXN3aWZ0L21ha2Vzd2lmdC1uZXh0anMtcGFnZXMtcm91dGVyLzRDdG5wMTFXNXVadGc5OWo3YnlXZHFoZlNVSjkiLCJwcmV2aWV3VXJsIjoibWFrZXN3aWZ0LW5leHRqcy1wYWdlcy1yb3V0ZXItZ2l0LWFsZWtzZXktZW5nLTMzYzBlZC1tYWtlc3dpZnQudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJtYWtlc3dpZnQtbmV4dGpzLXBhZ2VzLXJvdXRlci1naXQtYWxla3NleS1lbmctMzNjMGVkLW1ha2Vzd2lmdC52ZXJjZWwuYXBwIn19LHsibmFtZSI6Im1ha2Vzd2lmdC1leGFtcGxlcy1iaWdjb21tZXJjZS1jYXRhbHlzdCIsInJvb3REaXJlY3RvcnkiOiJleGFtcGxlcy9iaWdjb21tZXJjZS1jYXRhbHlzdCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifSwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL21ha2Vzd2lmdC9tYWtlc3dpZnQtZXhhbXBsZXMtYmlnY29tbWVyY2UtY2F0YWx5c3QvOUhYd3NFQ3JENlZzUDhlV0FuZ2ZrNVdKQTdFUyIsIm5leHRDb21taXRTdGF0dXMiOiJJR05PUkVEIiwicHJldmlld1VybCI6Im1ha2Vzd2lmdC1leGFtcGxlcy1iaWdjb21tZXJjZS1jYXRhbHlzdC1naXQtYWwtZmM3NzU4LW1ha2Vzd2lmdC52ZXJjZWwuYXBwIn0seyJuYW1lIjoibWFrZXN3aWZ0LWV4YW1wbGVzLWJhc2ljLXR5cGVzY3JpcHQtcGFnZXMiLCJyb290RGlyZWN0b3J5IjoiZXhhbXBsZXMvYmFzaWMtdHlwZXNjcmlwdC1wYWdlcyIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9tYWtlc3dpZnQvbWFrZXN3aWZ0LWV4YW1wbGVzLWJhc2ljLXR5cGVzY3JpcHQtcGFnZXMvQkFXOFc3VlUyYWp5eDlaMTRTa1loWXVLN1ZTZyIsInByZXZpZXdVcmwiOiJtYWtlc3dpZnQtZXhhbXBsZXMtYmFzaWMtdHlwZXNjcmlwdC1wYWdlcy1naXQtYmE4OWM5LW1ha2Vzd2lmdC52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IklHTk9SRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **makeswift-examples-bigcommerce** | ✅ Ready ([Inspect](https://vercel.com/makeswift/makeswift-examples-bigcommerce/He56JztDLNFxTpNMUsJ6Ejg9eLMv)) | [Visit Preview](https://makeswift-examples-bigcommerce-git-aleksey-eng-1bc1f6-makeswift.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/makeswift-examples-bigcommerce-git-aleksey-eng-1bc1f6-makeswift.vercel.app?via=pr-comment-feedback-link) | Apr 20, 2025 3:54am |
| **makeswift-nextjs-app-router** | ✅ Ready ([Inspect](https://vercel.com/makeswift/makeswift-nextjs-app-router/7HwVCddxQZGJHuDJdTq5h2vVjDF4)) | [Visit Preview](https://makeswift-nextjs-app-router-git-aleksey-eng-78-10172a-makeswift.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/makeswift-nextjs-app-router-git-aleksey-eng-78-10172a-makeswift.vercel.app?via=pr-comment-feedback-link) | Apr 20, 2025 3:54am |
| **makeswift-nextjs-pages-router** | ✅ Ready ([Inspect](https://vercel.com/makeswift/makeswift-nextjs-pages-router/4Ctnp11W5uZtg99j7byWdqhfSUJ9)) | [Visit Preview](https://makeswift-nextjs-pages-router-git-aleksey-eng-33c0ed-makeswift.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/makeswift-nextjs-pages-router-git-aleksey-eng-33c0ed-makeswift.vercel.app?via=pr-comment-feedback-link) | Apr 20, 2025 3:54am |

<details><summary>5 Skipped Deployments</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **makeswift-examples-basic-typescript** | ⬜️ Ignored ([Inspect](https://vercel.com/makeswift/makeswift-examples-basic-typescript/8C5s5kVKnwcqQRCUpd6JJjdJSEsp)) | [Visit Preview](https://makeswift-examples-basic-typescript-git-alekse-3dde23-makeswift.vercel.app) |  | Apr 20, 2025 3:54am |
| **makeswift-examples-basic-typescript-pages** | ⬜️ Ignored ([Inspect](https://vercel.com/makeswift/makeswift-examples-basic-typescript-pages/BAW8W7VU2ajyx9Z14SkYhYuK7VSg)) | [Visit Preview](https://makeswift-examples-basic-typescript-pages-git-ba89c9-makeswift.vercel.app) |  | Apr 20, 2025 3:54am |
| **makeswift-examples-bigcommerce-catalyst** | ⬜️ Ignored ([Inspect](https://vercel.com/makeswift/makeswift-examples-bigcommerce-catalyst/9HXwsECrD6VsP8eWAngfk5WJA7ES)) | [Visit Preview](https://makeswift-examples-bigcommerce-catalyst-git-al-fc7758-makeswift.vercel.app) |  | Apr 20, 2025 3:54am |
| **makeswift-examples-shopify** | ⬜️ Ignored ([Inspect](https://vercel.com/makeswift/makeswift-examples-shopify/EGg31vpJcv58uyYXkF5ZSr8Y51bk)) | [Visit Preview](https://makeswift-examples-shopify-git-aleksey-eng-780-95ed26-makeswift.vercel.app) |  | Apr 20, 2025 3:54am |
| **makeswift-examples-thirdweb** | ⬜️ Ignored ([Inspect](https://vercel.com/makeswift/makeswift-examples-thirdweb/G5eE6uqeQ4dW75AS9cyAzkem3xRy)) | [Visit Preview](https://makeswift-examples-thirdweb-git-aleksey-eng-78-dbf193-makeswift.vercel.app) |  | Apr 20, 2025 3:54am |
</details>









author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-20T01:50:15Z
body: <!-- add-pr-comment:add-pr-comment -->

# Snapshot Release
> Updated at April 20, 2025, 03:51:40.

I've published a snapshot release for your commit, 9f6aa1e. To use it, go to a Next.js app with `@makeswift/runtime` installed and run:
```bash
pnpm up @makeswift/runtime@0.0.0-snapshot-20250420035052
```

author_association: CONTRIBUTOR
user: login: arvinpoddar
created_at: 2025-04-20T05:35:27Z
body: 🙏
author_association: CONTRIBUTOR
user: login: arvinpoddar
created_at: 2025-04-20T05:41:55Z
body: Also, curious how we arrived at the product decision to make this behavior unconditional (unless opt-out). I'm guessing it's some combination of it being a better experience than how we handle localization for pages now, and wanting UX consistency with builtin elements?
author_association: CONTRIBUTOR
user: login: agurtovoy
created_at: 2025-04-20T16:40:31Z
body: > I'm guessing it's some combination of it being a better experience than how we handle localization for pages now, and wanting UX consistency with builtin elements?

@arvinpoddar yes, we want to gradually phase out the old localized pages experience in the builder. 
author_association: CONTRIBUTOR