Svelte
Import | import { adapter } from "@wuchale/svelte" |
---|---|
Loader extensions | .js , .ts , .svelte.js , .svelte.ts |
Default files | src/**/*.svelte , src/**/*.svelte.{js,ts} |
Compatibility | Svelte >= 5 |
Setup in Your App
Section titled “Setup in Your App”Svelte applications may or may not use SvelteKit and that makes the loading of the compiled catalogs a bit different. The Svelte adapter tries to detect whether SvelteKit is in use and write the suitable default loader. Assuming it was correct, follow the following steps. If it was wrong, edit the loader file first.
Svelte (SPA)
Section titled “Svelte (SPA)”You can use one of the two approaches mentioned in placeholders.
<script> import { loadLocale } from 'wuchale/load-utils' // so that the loaders are registered first import './locales/loader.svelte.js' // you can use any state from anywhere for the locale let locale = $state('en')</script>
{#await loadLocale(locale)} <!-- Ignored because it is rendered before the catalog is loaded --> <!-- @wc-ignore --> Loading translations...{:then} <!-- Your app content -->{/await}
<script> import { loadLocale } from 'wuchale/load-utils' // so that the loaders are registered first import './locales/loader.svelte.js' // you can use any state from anywhere for the locale let locale = $state('en') $effect(() => { loadLocale(locale) })</script>
<!-- Your app content -->
SvelteKit (SSR/SSG)
Section titled “SvelteKit (SSR/SSG)”If you use SvelteKit for just a client only app, you can use the above setup for Svelte. This is for when you need SSR.
The SvelteKit setup has two layers (server and client) and the loader file has to account for both. The default loader can be seen as an example.
When rendering on the server, the above setup for Svelte can’t work because it
uses a global state and using global states on the server is a bad idea, as it
can leak information (in our case the current locale) between requests, because
requests share the global state. To deal with this, a separate loader file is
generated and a separate utility is provided that can isolate the locale state
per request. You can set it up inside hooks.server.{js,ts}
.
import { loadCatalog, loadIDs, key } from './locales/loader.ssr.svelte.js'import { runWithLocale, loadLocales } from 'wuchale/load-utils/server'import { locales } from 'virtual:wuchale/locales'
// load at server startuploadLocales(key, loadIDs, loadCatalog, locales)
/** @type {import('@sveltejs/kit').Handle} */export const handle = async ({ event, resolve }) => { const locale = event.url.searchParams.get('locale') ?? 'en' return await runWithLocale(locale, () => resolve(event))}
Now, loadLocales
loads all catalogs at the server startup and makes them
ready. Then, for each request, runWithLocale
makes sure that the requests are
isolated and able to access their own catalogs based on the locale
from the
URL.
Client
Section titled “Client”Once the page is rendered on the server with the correct locale, the client has
to load the catalogs also making sure that the messages don’t change or flicker
once it takes over. The best place to load the catalogs then is the load
function in the top most +layout.{js,ts}
file. But it should only run once
it’s in the browser (the server is already handled).
import { locales } from 'virtual:wuchale/locales'import { browser } from '$app/environment'import { loadLocale } from 'wuchale/load-utils'// so that the loaders are registeredimport '../locales/loader.svelte.js'
/** @type {import('./$types').LayoutLoad} */export const load: LayoutLoad = async ({url}) => { const locale = url.searchParams.get('locale') ?? 'en' if (!locales.includes(locale)) { return } if (browser) { await loadLocale(locale) }}
+page.js
and similar files
Section titled “+page.js and similar files”The Svelte adapter only handles .svelte
files and .svelte.{js,ts}
files.
However, you may have translatable strings in +page.{js,ts}
,
+layout.{js,ts}
, etc. If so, you can use the Vanilla adapter for those files
by adding it as another adapter and specifying which files to handle.
// ... adapters: { main: svelte(), js: js({ files: [ 'src/**/+{page,layout}.{js,ts}', 'src/**/+{page,layout}.server.{js,ts}', ], }) } // ...
Learn more about using multiple adapters.
Default extraction rules
Section titled “Default extraction rules”In addition to the default rules, this adapter implements additional restrictions.
In script
(<script>
and .svelte.js/ts
)
Section titled “In script (<script> and .svelte.js/ts)”- The string can be in a variable at the top level, but it should be wrapped inside
$derived
or$derived.by
. - It should pass the base heuristic from the Vanilla adapter.
- If the value is inside
$inspect()
calls, it is ignored. - Otherwise, it is extracted.
Examples:
// In $derived or functionsconst message = $derived('This is extracted')const lowercase = $derived('not extracted')
// Force extraction with commentconst forced = $derived(/* @wc-include */ 'force extracted')
<p title={'Extracted'}>{/* @wc-ignore */ 'Ignore this'}</p>
<script module>
s and .svelte.{js,ts}
files
Section titled “<script module>s and .svelte.{js,ts} files”Unlike component code, code inside these places only runs once. If you are only developing client only apps, this doesn’t make much difference.
But if you do SSR, you have to make sure that all translatable text is inside function definitions and adjust your usage accordingly:
function foo() { const msg = 'Here'}
Because with SSR, startup means server startup, and so if you just use
$derived
in these places, they will be stuck with the locale of the first
request and subsequent requests with different locales may see a flicker until
the client takes over. But if you put them inside function definitions, the
function gets executed per request and will not have this problem.
Configuration Reference
Section titled “Configuration Reference”For the main configuration, look in the configuration reference.
For the common adapter configuration, look in the common adapter options.
This adapter doesn’t have additional configuration options.