К содержимому

Загрузка сообщений

Три способа получить сообщения и два паттерна попросить страницу их дождаться. Они сочетаются: то, что нужно каждой странице, — в бандл, остальное — по запросу.

1. На сборке #

createDialect({
	messages: {
		// eager: inlined at build time
		...import.meta.glob('./messages/**/*.json', { eager: true }),
		// not eager: fetched the first time a page asks for it
		...import.meta.glob('./messages-lazy/**/*.json')
	},
	defaultLocale: 'en'
});

Способ по умолчанию. import.meta.glob читает файлы во время компиляции и подставляет их внутрь — никаких запросов в рантайме, нечего настраивать.

Уберите eager: true из того же глоба — и каждый неймспейс станет своим чанком, который подгружается при первом обращении страницы. Обе формы могут жить в одном объекте messages.

2. По требованию #

Два паттерна дождаться неймспейса перед рендером. Ниже одна страница использует оба — перезагрузите её, чтобы увидеть работу обоих.

В load-функции #

Universal load может вернуть что угодно, включая саму функцию t. Работает на стабильном SvelteKit, без флагов компилятора.

Загружено в load-функции

Этот неймспейс подгружен в universal load-функции, которая вернула саму функцию t. Никаких экспериментальных опций компилятора — это способ для стабильного SvelteKit.

В компоненте #

Ожидание прямо в <script>.

Загружено по требованию

Этого неймспейса нет в бандле страницы. Он приезжает через ваш загрузчик — здесь это динамический импорт, а в вашем приложении с тем же успехом может быть запрос к API.

Доступно на 3 языках

Примечание

Нужны Svelte 5.36+ и compilerOptions.experimental.async.

+page.ts
export const load: PageLoad = async () => await loadTranslations('billing');
+page.svelte
const { data }: PageProps = $props();
<h1>{data.t('billing:title')}</h1>
// in the component's script, with experimental.async on
import { loadTranslations } from '#lib/i18n.js';

const { t } = await loadTranslations('dashboard');

// then in the markup
{t('dashboard:title')}

3. Из своего API #

Remote functions SvelteKit #

query(), вызываемая как локальная функция, внутри вашего загрузчика — не нужно писать эндпоинт и не нужен fetch. Требует kit.experimental.remoteFunctions и compilerOptions.experimental.async.

messages.remote.ts
import * as v from 'valibot';
import { query } from '$app/server';

export const getMessages = query(
	v.object({ locale: v.picklist(['en', 'ru']), namespace: v.picklist(['dashboard']) }),
	async ({ locale, namespace }) => db.loadMessages(locale, namespace)
);
lib/i18n.ts
createDialect({
	defaultLocale: 'en',
	locales: ['en', 'ru'],
	loader: async ({ locale, namespace }) => getMessages({ locale, namespace })
});

Обычный fetch #

Самый переносимый вариант: без экспериментальных флагов, работает при SSR, если URL абсолютный — на сервере нет страницы, относительно которой его строить.

createDialect({
	defaultLocale: 'en',
	locales: ['en', 'ru'],
	loader: async ({ locale, namespace }) => {
		const res = await fetch(`https://example.com/i18n/${locale}/${namespace}.json`);
		return res.json();
	}
});

Один вызов, сколько бы ни было запросов #

Параллельные вызовы для одной локали и неймспейса делят один вызов загрузчика в полёте, так что сотня одновременных SSR-запросов запускает один fetch, а не сотню.