-
-
Notifications
You must be signed in to change notification settings - Fork 47
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
Conversation
✅ Deploy Preview for pauseai ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
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) |
The translates JSON files don't end up in the messages folder where paraglide expects them |
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. |
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) |
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 |
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 StrategyThis 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 OverviewBased 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 Decisions1.1 Static vs. Dynamic Content
1.2 Component-level Breakdown
2. URL Structure Implementation2.1 Recommended PatternUse the explicit language prefix approach (
2.2 URL Pattern Benefits
2.3 Special URL Considerations
3. Implementation Architecture3.1 Build-time Process
3.2 Runtime Behavior
4. Technical Implementation4.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 ConsistencyTo ensure consistency between development, preview, and production environments: 5.1 Environment-specific ConfigurationUse 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
6. Edge Cases & Fallback Logic6.1 Handling Missing TranslationsFor a locale that's partially translated:
6.2 SEO Considerations
7. Final Recommendations
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 StrategyFrom my deep analysis of SvelteKit with Paraglide for localization on Netlify's CDN, here are the critical architectural decisions: Rendering ApproachSvelteKit 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:
URL Pattern ImplementationUse explicit language path prefixes (e.g., 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 HandlingWhen complete translations aren't available in a user's preferred language:
Key Technical ImplementationUse the Netlify adapter with edge functions enabled ( Set These recommendations balance static performance with the flexibility needed for internationalization while ensuring preview environments accurately represent production behavior. Citations:
More sources:
|
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. |
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 StrategyThis 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 OverviewBased 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 Decisions1.1 Static vs. Dynamic Content
1.2 Component-level Breakdown
2. URL Structure Implementation2.1 Recommended PatternUse the explicit language prefix approach (
2.2 URL Pattern Benefits
2.3 Special URL Considerations
3. Implementation Architecture3.1 Build-time Process
3.2 Runtime Behavior
4. Technical Implementation4.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 ConsistencyTo ensure consistency between development, preview, and production environments: 5.1 Environment-specific ConfigurationUse 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
6. Edge Cases & Fallback Logic6.1 Handling Missing TranslationsFor a locale that's partially translated:
6.2 SEO Considerations
7. Final Recommendations
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 StrategyFrom my deep analysis of SvelteKit with Paraglide for localization on Netlify's CDN, here are the critical architectural decisions: Rendering ApproachSvelteKit 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:
URL Pattern ImplementationUse explicit language path prefixes (e.g., 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 HandlingWhen complete translations aren't available in a user's preferred language:
Key Technical ImplementationUse the Netlify adapter with edge functions enabled ( Set These recommendations balance static performance with the flexibility needed for internationalization while ensuring preview environments accurately represent production behavior. Citations:
More sources:
|
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? |
We discussed removing the cache earlier iirc, and the rest seems self-explanatory. What specifically are you interested in? |
#157, takes over from #211.
/en/page
takes precedent over cookie, which takes precedent over headerDesign doc: https://docs.google.com/document/d/1gcQLunOcRk717oSqfvE8kod3ODsMK2dRADgQccqXkyk
TODO before merge
/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)