Skip to content

Paraglide, Internationalization #226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 113 commits into from
May 6, 2025
Merged

Paraglide, Internationalization #226

merged 113 commits into from
May 6, 2025

Conversation

joepio
Copy link
Collaborator

@joepio joepio commented Oct 24, 2024

#157, takes over from #211.

  • Route /en/page takes precedent over cookie, which takes precedent over header
  • Stores AI-translated pages in a separate repository on GitHub, looks at latest commits of source and translation to prevent unnecessary translations

Design doc: https://docs.google.com/document/d/1gcQLunOcRk717oSqfvE8kod3ODsMK2dRADgQccqXkyk

TODO before merge

  • Writes translated pages to an external repo (I set up this repo for that)
  • Caches responses, but only one per page per language (ignores changes to LLMs)
  • Don't fail the build if an LLM wrongly translates a linked page
  • URL does not update for some reason after choosing a translated page. If I go to /nl/xrisk i get redirected to /xrisk for some reason? @Wituareard

@anthonybailey can you list other open issues that we need before merging an initial version?

TODO after merge (follow-up wishlist)

  • Cache depends on used LLM & Prompt instead of one item per page.
  • Test more LLMs

Copy link

netlify bot commented Oct 24, 2024

Deploy Preview for pauseai ready!

Name Link
🔨 Latest commit
🔍 Latest deploy log https://app.netlify.com/sites/pauseai/deploys/681e1534976724b3afc937b8
😎 Deploy Preview https://deploy-preview-226--pauseai.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@joepio joepio mentioned this pull request Oct 29, 2024
11 tasks
@PauseAI PauseAI deleted a comment from netlify bot Oct 30, 2024
@Wituareard
Copy link
Collaborator

I reverted the merge for now, there are a bunch of very obvious bugs we should fix first (gap in header, meta completely broken, placeholders in html attributes, probably more)

@Wituareard
Copy link
Collaborator

The translates JSON files don't end up in the messages folder where paraglide expects them

@Wituareard
Copy link
Collaborator

The 404 on the home page is likely due to the homepage being prerendered but then removed from the prerendered field in the manifest. Therefore, the request is passed to the server, but the server doesn't know how to render the route. In the previous implementation the routes without a prefix were handled by paraglide and always redirected so this issue didn't arise.

@Wituareard
Copy link
Collaborator

I think if we want to do any pre-rendering we need to go back to the old model where URLs without a prefix always redirect and the ones with a prefix are static (URL takes precedence over cookie and header)

@Wituareard
Copy link
Collaborator

What's the advantages of configuring paraglide via environment variables rather than file-based configuration? We can still have different content in that file between branches

@anthonybailey
Copy link
Collaborator

Tried to do a deep dive on this.

https://claude.ai/share/9ac6d706-2abb-4def-a2ef-feb6c4f0f8a0

Lots of setup and research before final ultrathink and artifact. Intend to use latter together with Claude Code on the codebase. It's already well aligned with most recommendations but want final tweaks and clarity. I do think we will go back to having explicit en/ pages too, good call Nils, inlang Paraglide defaults just don't fit Netlify well enough.

Quoting Claude...

I'll do some comprehensive "ultra-thinking" about the rendering strategy for your localized SvelteKit website on Netlify. "

I'll create a comprehensive artifact that summarizes my findings and presents a detailed strategy for rendering and serving the PauseAI localized website on Netlify's CDN.

PauseAI L10n Rendering Strategy

This document outlines a comprehensive strategy for the PauseAI website's internationalization implementation, focusing on optimal rendering approaches, URL patterns, and consistency between preview and production environments on Netlify.

Rendering Strategy Overview

Based on the PauseAI project requirements and the CLAUDE.md context, the website should use a hybrid rendering approach that combines static site generation (SSG) with selective server-side rendering (SSR) via edge functions when necessary.

1. Core Rendering Decisions

1.1 Static vs. Dynamic Content

  • Static Pre-rendering: Use for most content pages, blog posts, and informational pages
  • Dynamic Edge Functions: Use for:
    • Language detection and redirection
    • Pages that need real-time data
    • Mixed locale scenarios (when a preferred locale isn't fully available)

1.2 Component-level Breakdown

Component Type Rendering Approach Rationale
Main content pages Static pre-rendering Content is relatively stable; best performance
Header/footer components Static per locale These appear on all pages but are locale-specific
Language switcher UI Client-side hydration Needs to be interactive; maintain selected locale
Mixed locale notices Edge function rendering Only appears when falling back to another locale
Dynamic content (if any) Edge function rendering For any content requiring real-time data

2. URL Structure Implementation

2.1 Recommended Pattern

Use the explicit language prefix approach (/en/about, /de/about) for all localized content with these characteristics:

  • Default language should also use the explicit prefix (e.g., /en/ rather than just /) for consistency
  • Maintain a redirect from the root path (/) that uses language detection to route to the appropriate locale
  • Generate separate static files for each localized route during build time

2.2 URL Pattern Benefits

  • SEO Benefits: Clear language signals to search engines
  • Consistent Routing: Same pattern for all languages
  • Static Generation: Easy to pre-render each language version separately
  • Netlify Compatibility: Works well with Netlify's CDN and edge functions
  • Preview Environment: Functions identically in both preview and production

2.3 Special URL Considerations

  • API Endpoints: Should be language-agnostic or accept language as a parameter
  • Fallback Paths: For incomplete translations, maintain a path to the English version
  • Sitemap Integration: Include hreflang annotations for all language variants

3. Implementation Architecture

3.1 Build-time Process

  1. Static Generation Phase:

    • Pre-render all complete locale variants of each page
    • Generate a comprehensive sitemap with hreflang annotations
    • Create static assets for header/footer components in each locale
    • Add "invisible" anchor tags in root layout to ensure all locale paths are discovered
  2. Edge Function Setup:

    • Implement language detection (Accept-Language header, cookies)
    • Configure fallback logic for incomplete translations
    • Set up redirection from root path to appropriate locale

3.2 Runtime Behavior

  1. First Visit:

    • Edge function detects preferred language from Accept-Language header
    • Redirects to appropriate localized path
    • Sets a cookie for future visits
  2. Subsequent Page Navigation:

    • Links are automatically localized to current language
    • Language switcher provides options to change locale
    • Paraglide handles all path translations automatically
  3. Mixed Locale Scenarios:

    • When content isn't available in a requested locale, serve best available version
    • Add a visible notice (in a language the user understands) about limited translation
    • Provide link to switch to a fully translated version

4. Technical Implementation

4.1 Core Configuration

// svelte.config.js
import adapter from '@sveltejs/adapter-netlify';
import { vitePreprocess } from '@sveltejs/kit/vite';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),
  
  kit: {
    adapter: adapter({
      edge: true, // Enable edge functions for language detection
    }),
    
    prerender: {
      // Default entries plus any that need special attention
      entries: ['*'],
      // Enable crawling to find all linked pages
      crawl: true,
    }
  }
};

export default config;
// vite.config.js
import { sveltekit } from '@sveltejs/kit/vite';
import { paraglideVitePlugin } from '@inlang/paraglide-js';

export default {
  plugins: [
    paraglideVitePlugin({
      project: './project.inlang',
      outdir: './src/lib/paraglide',
      // Strategy defines priority order for language detection
      strategy: ['url', 'cookie', 'baseLocale'],
    }),
    sveltekit()
  ]
};

4.2 Layout & Routing Setup

<!-- src/routes/+layout.svelte -->
<script>
  import { page } from '$app/stores';
  import { i18n } from '$lib/i18n.js';
  import { availableLanguageTags, languageTag } from "$lib/paraglide/runtime";
  
  // For invisible links to help SvelteKit discover all locale versions
  let allPaths = ['/about', '/contact', '/']; // Add all your core paths
</script>

<!-- Your layout content -->
<slot></slot>

<!-- Invisible links for crawler to discover all locale variants -->
<div style="display: none">
  {#each allPaths as path}
    {#each availableLanguageTags as lang}
      <a href={i18n.resolveRoute(path, lang)}>Link</a>
    {/each}
  {/each}
</div>

4.3 Root Page Configuration

// src/routes/+layout.js
// Set prerender: true for static site generation
export const prerender = true;

// Use edge functions for dynamic portions
export const config = {
  runtime: 'edge'
};

4.4 Root Redirection

// src/routes/+page.server.js
export function load({ url, request, cookies }) {
  // Get preferred language from:
  // 1. Cookie (if returning visitor)
  // 2. Accept-Language header (if first visit)
  // 3. Default to English as fallback
  
  const savedLang = cookies.get('preferred_locale');
  
  if (savedLang) {
    // Redirect to saved language preference
    return {
      redirect: `/${savedLang}/`,
      status: 302
    };
  }
  
  // Extract preferred language from Accept-Language header
  const acceptLanguage = request.headers.get('accept-language');
  // Logic to parse Accept-Language and match to available locales
  // ...
  
  const detectedLang = /* result from parsing */ 'en';
  
  // Set cookie for future visits
  cookies.set('preferred_locale', detectedLang, {
    path: '/',
    maxAge: 60 * 60 * 24 * 365 // 1 year
  });
  
  // Redirect to detected language
  return {
    redirect: `/${detectedLang}/`,
    status: 302
  };
}

5. Development & Preview Consistency

To ensure consistency between development, preview, and production environments:

5.1 Environment-specific Configuration

Use environment variables to control locale behavior:

// Controlled by PARAGLIDE_LOCALES env var
const enabledLocales = import.meta.env.PARAGLIDE_LOCALES?.split(',') || ['en'];

5.2 Netlify Configuration

# netlify.toml
[build]
  command = "npm run build"
  publish = "build"

[context.production]
  environment = { PARAGLIDE_LOCALES = "en,de,fr,es,ja" }

[context.deploy-preview]
  environment = { PARAGLIDE_LOCALES = "en,de,fr,es,ja" }

[context.branch-deploy]
  environment = { PARAGLIDE_LOCALES = "en" }

[dev]
  command = "npm run dev"
  framework = "#custom"
  port = 3000
  targetPort = 5173

5.3 Consistent Testing Approach

  1. Local Development:

    • Set .env file with PARAGLIDE_LOCALES=en,de for testing specific languages
    • Use netlify dev to simulate production environment locally
  2. Preview Builds:

    • Include full localization for accurate preview
    • Test language switching and fallback behavior
  3. Production:

    • Initially deploy with limited locales (e.g., only English)
    • Enable additional locales as translations are validated

6. Edge Cases & Fallback Logic

6.1 Handling Missing Translations

For a locale that's partially translated:

  1. Header/Footer Components:

    • Always serve in the requested locale if available
    • Fall back to English only when necessary
  2. Main Content:

    • Render fallback English content when translation is missing
    • Add a notice at the top in the closest available language the user might understand
  3. Mixed Language Indicators:

    <!-- Example notice for German users seeing English content -->
    {#if requestedLocale !== actualContentLocale}
      <div class="locale-notice">
        {#if requestedLocale === 'de'}
          Diese Seite ist noch nicht auf Deutsch verfügbar.
        {:else}
          This content is not yet available in your preferred language.
        {/if}
      </div>
    {/if}

6.2 SEO Considerations

  1. Proper Sitemap Implementation:

    • Include all language variants with hreflang annotations
    • Mark primary language version with canonical URLs
    • Use x-default for fallback content
  2. HTML Lang Attribute:

    • Always set the correct lang attribute, even in mixed-content scenarios
    • For mixed content, use the predominant language

7. Final Recommendations

  1. Phased Approach:

    • Start with fully static pre-rendering for English
    • Add edge functions for language detection and redirection
    • Add other languages incrementally as translations are completed
  2. Performance Focus:

    • Leverage Netlify's CDN for static content
    • Use edge functions minimally and only when necessary
    • Implement aggressive caching for static assets
  3. Monitoring & Analytics:

    • Track language preferences and switching behavior
    • Monitor edge function usage to optimize costs
    • Collect feedback on translation quality

This strategy balances the need for optimal performance through Netlify's CDN while providing the necessary flexibility for internationalization, maintaining consistency across environments, and supporting a graceful fallback approach for missing translations.

Now that I've created the comprehensive artifact, let me provide a concise summary of the key findings and recommendations:

Ultra-thinking: PauseAI Localization Rendering Strategy

From my deep analysis of SvelteKit with Paraglide for localization on Netlify's CDN, here are the critical architectural decisions:

Rendering Approach

SvelteKit provides multiple rendering modes that can be defined on a per-page basis, allowing for a hybrid approach that combines static site generation with dynamic rendering where needed. Netlify For PauseAI, I recommend:

  1. Predominantly Static Pre-rendering: Most content should be statically pre-rendered during build time, including separate HTML files for each locale variant.

  2. Selective Edge Functions: Use Netlify Edge Functions for specialized cases such as language detection, personalization, and handling mixed-locale scenarios when a requested translation is unavailable. Netlify

  3. Header/Footer Components: These should be pre-rendered per locale but might need special handling when serving mixed locales.

URL Pattern Implementation

Use explicit language path prefixes (e.g., /en/about, /de/about) for all routes. Paraglide-SvelteKit handles this automatically without requiring modifications to your route files. Inlang

To ensure all locale variants are properly pre-rendered, add "invisible" anchor tags in your layout component that include links to all locale versions of your pages. SvelteKit crawls these during build time. Inlang

Environment Consistency (Preview vs. Production)

Netlify supports different environment variable contexts including "Preview Server" and "Production" environments, allowing for consistent configuration across all environments. Netlify

Use an environment variable approach (PARAGLIDE_LOCALES) to control which locales are enabled in different environments, ensuring preview behavior matches production.

Mixed Locale Handling

When complete translations aren't available in a user's preferred language:

  1. Show content in the best available language (usually English)
  2. Display a notice in a language the user is likely to understand
  3. Provide an option to switch to fully-translated alternatives

Key Technical Implementation

Use the Netlify adapter with edge functions enabled (edge: true), but disable the split option for compatibility. Netlify

Set prerender = true in your root layout to generate static files, while using Edge Functions for the dynamic aspects of language detection and redirection. Svelte

These recommendations balance static performance with the flexibility needed for internationalization while ensuring preview environments accurately represent production behavior.

Citations:

More sources:

@anthonybailey
Copy link
Collaborator

What's the advantages of configuring paraglide via environment variables rather than file-based configuration? We can still have different content in that file between branches

I think you might mean having developers locally hack their project.inlang/settings.json rather than tweak a default environment that generates those settings.

So long as the costs of the latter prove OK - as I am finding - I think the easier defaults, clarity, flexibility and source-control gap that an environment provides is a better fit. Plus operationally, once we are up and running with dozens of locales being able to say "oops, set -fr and redeploy" seems handy.

But YMMV and nothing is set in stone.

@anthonybailey
Copy link
Collaborator

Tried to do a deep dive on this.

https://claude.ai/share/9ac6d706-2abb-4def-a2ef-feb6c4f0f8a0

Lots of setup and research before final ultrathink and artifact. Intend to use latter together with Claude Code on the codebase. It's already well aligned with most recommendations but want final tweaks and clarity. I do think we will go back to having explicit en/ pages too, good call Nils, inlang Paraglide defaults just don't fit Netlify well enough.

Quoting Claude...

I'll do some comprehensive "ultra-thinking" about the rendering strategy for your localized SvelteKit website on Netlify. "

I'll create a comprehensive artifact that summarizes my findings and presents a detailed strategy for rendering and serving the PauseAI localized website on Netlify's CDN.

PauseAI L10n Rendering Strategy

This document outlines a comprehensive strategy for the PauseAI website's internationalization implementation, focusing on optimal rendering approaches, URL patterns, and consistency between preview and production environments on Netlify.

Rendering Strategy Overview

Based on the PauseAI project requirements and the CLAUDE.md context, the website should use a hybrid rendering approach that combines static site generation (SSG) with selective server-side rendering (SSR) via edge functions when necessary.

1. Core Rendering Decisions

1.1 Static vs. Dynamic Content

  • Static Pre-rendering: Use for most content pages, blog posts, and informational pages
  • Dynamic Edge Functions: Use for:
    • Language detection and redirection
    • Pages that need real-time data
    • Mixed locale scenarios (when a preferred locale isn't fully available)

1.2 Component-level Breakdown

Component Type Rendering Approach Rationale
Main content pages Static pre-rendering Content is relatively stable; best performance
Header/footer components Static per locale These appear on all pages but are locale-specific
Language switcher UI Client-side hydration Needs to be interactive; maintain selected locale
Mixed locale notices Edge function rendering Only appears when falling back to another locale
Dynamic content (if any) Edge function rendering For any content requiring real-time data

2. URL Structure Implementation

2.1 Recommended Pattern

Use the explicit language prefix approach (/en/about, /de/about) for all localized content with these characteristics:

  • Default language should also use the explicit prefix (e.g., /en/ rather than just /) for consistency
  • Maintain a redirect from the root path (/) that uses language detection to route to the appropriate locale
  • Generate separate static files for each localized route during build time

2.2 URL Pattern Benefits

  • SEO Benefits: Clear language signals to search engines
  • Consistent Routing: Same pattern for all languages
  • Static Generation: Easy to pre-render each language version separately
  • Netlify Compatibility: Works well with Netlify's CDN and edge functions
  • Preview Environment: Functions identically in both preview and production

2.3 Special URL Considerations

  • API Endpoints: Should be language-agnostic or accept language as a parameter
  • Fallback Paths: For incomplete translations, maintain a path to the English version
  • Sitemap Integration: Include hreflang annotations for all language variants

3. Implementation Architecture

3.1 Build-time Process

  1. Static Generation Phase:

    • Pre-render all complete locale variants of each page
    • Generate a comprehensive sitemap with hreflang annotations
    • Create static assets for header/footer components in each locale
    • Add "invisible" anchor tags in root layout to ensure all locale paths are discovered
  2. Edge Function Setup:

    • Implement language detection (Accept-Language header, cookies)
    • Configure fallback logic for incomplete translations
    • Set up redirection from root path to appropriate locale

3.2 Runtime Behavior

  1. First Visit:

    • Edge function detects preferred language from Accept-Language header
    • Redirects to appropriate localized path
    • Sets a cookie for future visits
  2. Subsequent Page Navigation:

    • Links are automatically localized to current language
    • Language switcher provides options to change locale
    • Paraglide handles all path translations automatically
  3. Mixed Locale Scenarios:

    • When content isn't available in a requested locale, serve best available version
    • Add a visible notice (in a language the user understands) about limited translation
    • Provide link to switch to a fully translated version

4. Technical Implementation

4.1 Core Configuration

// svelte.config.js
import adapter from '@sveltejs/adapter-netlify';
import { vitePreprocess } from '@sveltejs/kit/vite';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),
  
  kit: {
    adapter: adapter({
      edge: true, // Enable edge functions for language detection
    }),
    
    prerender: {
      // Default entries plus any that need special attention
      entries: ['*'],
      // Enable crawling to find all linked pages
      crawl: true,
    }
  }
};

export default config;
// vite.config.js
import { sveltekit } from '@sveltejs/kit/vite';
import { paraglideVitePlugin } from '@inlang/paraglide-js';

export default {
  plugins: [
    paraglideVitePlugin({
      project: './project.inlang',
      outdir: './src/lib/paraglide',
      // Strategy defines priority order for language detection
      strategy: ['url', 'cookie', 'baseLocale'],
    }),
    sveltekit()
  ]
};

4.2 Layout & Routing Setup

<!-- src/routes/+layout.svelte -->
<script>
  import { page } from '$app/stores';
  import { i18n } from '$lib/i18n.js';
  import { availableLanguageTags, languageTag } from "$lib/paraglide/runtime";
  
  // For invisible links to help SvelteKit discover all locale versions
  let allPaths = ['/about', '/contact', '/']; // Add all your core paths
</script>

<!-- Your layout content -->
<slot></slot>

<!-- Invisible links for crawler to discover all locale variants -->
<div style="display: none">
  {#each allPaths as path}
    {#each availableLanguageTags as lang}
      <a href={i18n.resolveRoute(path, lang)}>Link</a>
    {/each}
  {/each}
</div>

4.3 Root Page Configuration

// src/routes/+layout.js
// Set prerender: true for static site generation
export const prerender = true;

// Use edge functions for dynamic portions
export const config = {
  runtime: 'edge'
};

4.4 Root Redirection

// src/routes/+page.server.js
export function load({ url, request, cookies }) {
  // Get preferred language from:
  // 1. Cookie (if returning visitor)
  // 2. Accept-Language header (if first visit)
  // 3. Default to English as fallback
  
  const savedLang = cookies.get('preferred_locale');
  
  if (savedLang) {
    // Redirect to saved language preference
    return {
      redirect: `/${savedLang}/`,
      status: 302
    };
  }
  
  // Extract preferred language from Accept-Language header
  const acceptLanguage = request.headers.get('accept-language');
  // Logic to parse Accept-Language and match to available locales
  // ...
  
  const detectedLang = /* result from parsing */ 'en';
  
  // Set cookie for future visits
  cookies.set('preferred_locale', detectedLang, {
    path: '/',
    maxAge: 60 * 60 * 24 * 365 // 1 year
  });
  
  // Redirect to detected language
  return {
    redirect: `/${detectedLang}/`,
    status: 302
  };
}

5. Development & Preview Consistency

To ensure consistency between development, preview, and production environments:

5.1 Environment-specific Configuration

Use environment variables to control locale behavior:

// Controlled by PARAGLIDE_LOCALES env var
const enabledLocales = import.meta.env.PARAGLIDE_LOCALES?.split(',') || ['en'];

5.2 Netlify Configuration

# netlify.toml
[build]
  command = "npm run build"
  publish = "build"

[context.production]
  environment = { PARAGLIDE_LOCALES = "en,de,fr,es,ja" }

[context.deploy-preview]
  environment = { PARAGLIDE_LOCALES = "en,de,fr,es,ja" }

[context.branch-deploy]
  environment = { PARAGLIDE_LOCALES = "en" }

[dev]
  command = "npm run dev"
  framework = "#custom"
  port = 3000
  targetPort = 5173

5.3 Consistent Testing Approach

  1. Local Development:

    • Set .env file with PARAGLIDE_LOCALES=en,de for testing specific languages
    • Use netlify dev to simulate production environment locally
  2. Preview Builds:

    • Include full localization for accurate preview
    • Test language switching and fallback behavior
  3. Production:

    • Initially deploy with limited locales (e.g., only English)
    • Enable additional locales as translations are validated

6. Edge Cases & Fallback Logic

6.1 Handling Missing Translations

For a locale that's partially translated:

  1. Header/Footer Components:

    • Always serve in the requested locale if available
    • Fall back to English only when necessary
  2. Main Content:

    • Render fallback English content when translation is missing
    • Add a notice at the top in the closest available language the user might understand
  3. Mixed Language Indicators:

    <!-- Example notice for German users seeing English content -->
    {#if requestedLocale !== actualContentLocale}
      <div class="locale-notice">
        {#if requestedLocale === 'de'}
          Diese Seite ist noch nicht auf Deutsch verfügbar.
        {:else}
          This content is not yet available in your preferred language.
        {/if}
      </div>
    {/if}

6.2 SEO Considerations

  1. Proper Sitemap Implementation:

    • Include all language variants with hreflang annotations
    • Mark primary language version with canonical URLs
    • Use x-default for fallback content
  2. HTML Lang Attribute:

    • Always set the correct lang attribute, even in mixed-content scenarios
    • For mixed content, use the predominant language

7. Final Recommendations

  1. Phased Approach:

    • Start with fully static pre-rendering for English
    • Add edge functions for language detection and redirection
    • Add other languages incrementally as translations are completed
  2. Performance Focus:

    • Leverage Netlify's CDN for static content
    • Use edge functions minimally and only when necessary
    • Implement aggressive caching for static assets
  3. Monitoring & Analytics:

    • Track language preferences and switching behavior
    • Monitor edge function usage to optimize costs
    • Collect feedback on translation quality

This strategy balances the need for optimal performance through Netlify's CDN while providing the necessary flexibility for internationalization, maintaining consistency across environments, and supporting a graceful fallback approach for missing translations.

Now that I've created the comprehensive artifact, let me provide a concise summary of the key findings and recommendations:

Ultra-thinking: PauseAI Localization Rendering Strategy

From my deep analysis of SvelteKit with Paraglide for localization on Netlify's CDN, here are the critical architectural decisions:

Rendering Approach

SvelteKit provides multiple rendering modes that can be defined on a per-page basis, allowing for a hybrid approach that combines static site generation with dynamic rendering where needed. Netlify For PauseAI, I recommend:

  1. Predominantly Static Pre-rendering: Most content should be statically pre-rendered during build time, including separate HTML files for each locale variant.

  2. Selective Edge Functions: Use Netlify Edge Functions for specialized cases such as language detection, personalization, and handling mixed-locale scenarios when a requested translation is unavailable. Netlify

  3. Header/Footer Components: These should be pre-rendered per locale but might need special handling when serving mixed locales.

URL Pattern Implementation

Use explicit language path prefixes (e.g., /en/about, /de/about) for all routes. Paraglide-SvelteKit handles this automatically without requiring modifications to your route files. Inlang

To ensure all locale variants are properly pre-rendered, add "invisible" anchor tags in your layout component that include links to all locale versions of your pages. SvelteKit crawls these during build time. Inlang

Environment Consistency (Preview vs. Production)

Netlify supports different environment variable contexts including "Preview Server" and "Production" environments, allowing for consistent configuration across all environments. Netlify

Use an environment variable approach (PARAGLIDE_LOCALES) to control which locales are enabled in different environments, ensuring preview behavior matches production.

Mixed Locale Handling

When complete translations aren't available in a user's preferred language:

  1. Show content in the best available language (usually English)
  2. Display a notice in a language the user is likely to understand
  3. Provide an option to switch to fully-translated alternatives

Key Technical Implementation

Use the Netlify adapter with edge functions enabled (edge: true), but disable the split option for compatibility. Netlify

Set prerender = true in your root layout to generate static files, while using Edge Functions for the dynamic aspects of language detection and redirection. Svelte

These recommendations balance static performance with the flexibility needed for internationalization while ensuring preview environments accurately represent production behavior.

Citations:

More sources:

@anthonybailey
Copy link
Collaborator

But YMMV and nothing is set in stone.

You could describe the way your mileage varies, though. Can you include rationale when you are making changes, Nils? I just see a sequence of one line commit headlines and have to guess the plan.

Can you also outline your test procedure? I'm still unclear as to how we are verifying changes we make. We want to know we haven't broken prod and that we haven't broken latent l10n.

What's your process?

@Wituareard
Copy link
Collaborator

Wituareard commented May 6, 2025

We discussed removing the cache earlier iirc, and the rest seems self-explanatory. What specifically are you interested in?

@Wituareard Wituareard merged commit d042937 into main May 6, 2025
4 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in PauseAI Kanban (public) May 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants