content/tutorials/1.getting-started/implement-multilingual-content-with-directus-and-svelte-kit.md
You will need:
The code for this tutorial is available on this GitHub repository.
Start by setting up a new Svelte project and install the required dependencies including the Directus SDK:
npm create svelte@latest frontend # Select the Skeleton project
cd directus-i18n-app
npm install
npm install @directus/sdk
In the src/libs directory, create a directus.js file to create and export a Directus SDK instance:
import { createDirectus, rest } from '@directus/sdk';
import { PUBLIC_DIRECTUS_API_URL } from '$env/static/public';
function getDirectusInstance(fetch) {
const options = fetch ? { globals: { fetch } } : {};
const directus = createDirectus(PUBLIC_DIRECTUS_API_URL).with(rest());
return directus;
}
export default getDirectusInstance;
Then create a .env file in the root directory of your project and add your Directus API URL:
PUBLIC_DIRECTUS_API_URL='https://directus.example.com';
In the Directus Data Studio, navigate to Settings -> Data Model and create a new collection called news:
slug (Primary Key Field, Type: Manually entered string)author (Type: String, Interface: Input)cover (Type: Image)Create a collection called languages:
code (Primary Key Field, Type: Manually entered string )name (Type: String, Interface: Input)direction (Type: String, Interface: Dropdown, Options: ltr and rtl. Default Value: ltr)The direction field enables support for languages that read right to left.
To enable content translation in your news collection, create a translations field using translation interface. Select name as the Language Indicator Field, direction as the Language Direction Field and en-US as the Default Language.
Once you save, a new collection named news_translations will be created for you. In the news_translations collection, you will add the fields that need translations.
Add the following fields to the news_translations collection:
title (Type: String, Interface: Input)body (Type: Text, Interface: WYSIWYG)Add each language you want to support as items in the languages collection.
The item page for the news collection now includes a translations interface.
llow the Public role to read the news, languages and news_translations collections in the Access Control settings to ensure the frontend can access these collections.
In your Svelte project, update your +page.js file to fetch your content using the SDK:
import getDirectusInstance from "$lib/directus";
import { readItems } from "@directus/sdk";
export async function load({ fetch }) {
const directus = getDirectusInstance(fetch);
return {
global: await directus.request(readItems("global")),
news: await directus.request(readItems("news", {
deep: {
translations: {
_filter: {
_and: [
{
languages_code: { _eq: "en-US" },
},
],
},
},
},
fields: ["*", { translations: ["*"] }],
})
),
};
}
The above code snippet will use:
readItems function to fetch all the contents in the news collection.deep parameter to filter the related collection to only show the translations in en-US (English US).Update the code in +page.svelte file in the src directory to render the news:
<script>
export let data;
</script>
<h1>Trending Today!</h1>
<ul>
{#each data.news as article}
<li>
<div>
<h2>
<a href={`/${article.id}`}>
{article.translations[0].title}
</a>
</h2>
<p>By {article.author}</p>
</div>
</li>
{/each}
</ul>
The above code will:
+page.js file to display the contents.Create a news/+page.js file in the routes directory for the route that will render the individual news contents:
import { readItem } from "@directus/sdk";
import getDirectusInstance from "$lib/directus";
import { error } from "@sveltejs/kit";
/** @type {import('./$types').PageLoad} */
export async function load({ fetch, params, url }) {
const directus = getDirectusInstance(fetch);
const slug = params.slug;
try {
const [newsData, languagesData] = await Promise.all([
directus.request(
readItem("news", slug, {
fields: ["*", { "*": ["*"] }],
})
),
directus.request(readItems("languages")),
]);
return {
article: newsData ? newsData : null,
languages: languagesData,
};
} catch (err) {
error(404, "Post not found");
}
}
The above code will:
readItem funtion to find and get the news that matches the primary key field (slug) in the news collection.languages collection.Create a +page.svelte file in the routes/news directory and add the code:
<script>
export let data;
$: ({ article, languages } = data);
</script>
{#if article}
<h1>{article.translations[0].title}</h1>
{@html article.translations[0].body}
<select>
{#each languages as language}
<option value={language.code}>{language.name}</option>
{/each}
</select>
{:else}
<p>News not found.</p>
{/if}
The above code will:
news/+page.js file and render them.@html decorator to properly render the WYSIWYG body field content.Update your project to add the multilingual navigation and search functionalities. Update the code in the routes/news/+page.svelte file to add a handler to dynamically render the article translation based on the selected language.
<script>
import { goto } from '$app/navigation';
export let data;
$: ({ article, languages, languageCode } = data);
let selectedLanguageCode = languageCode;
function handleLanguageChange(event) {
const newLanguageCode = event.target.value;
selectedLanguageCode = newLanguageCode; // Update the selectedLanguageCode
goto(`?lang=${newLanguageCode}`, { replaceState: true });
}
</script>
{#if article}
<h1>{article.translations[0].title}</h1>
{@html article.translations[0].body}
<select value={selectedLanguageCode} on:change={handleLanguageChange}>
{#each languages as language}
{console.log(language)}
<option value={language.code}>{language.name}</option>
{/each}
</select>
{:else}
<p>News not found.</p>
{/if}
Then, update the code in your routes/news/+page.js file to add a filter that allows users to dynamically select the language they need the news to be translated by adding a new URL parameter for the desired language code and use it to filter the news translations.
import { readItem } from "@directus/sdk";
import getDirectusInstance from "$lib/directus";
import { error } from "@sveltejs/kit";
/** @type {import('./$types').PageLoad} */
export async function load({ fetch, params, url }) {
const directus = getDirectusInstance(fetch);
const slug = params.slug;
const languageCode = url.searchParams.get("lang") || "en-US";
try {
const [newsData, languagesData] = await Promise.all([
directus.request(
readItem("news", slug, {
deep: {
translations: {
_filter: {
_and: [
{ languages_code: { _eq: languageCode } },
],
},
},
},
fields: ["*", { "*": ["*"] }],
})
),
directus.request(readItems("languages")),
]);
return {
article: newsData ? newsData : null,
languages: languagesData,
languageCode,
};
} catch (err) {
error(404, "Post not found");
}
}
Now you translate the news in English, German, and French.
Replace the code in your routes/+page.svelte file with the code snippets below to add search functionality:
<script>
import { goto } from "$app/navigation";
import { page } from "$app/stores";
export let data;
let searchQuery = $page.url.searchParams.get("q") || "";
function handleSearchChange() {
goto(`/?q=${searchQuery}`, { replaceState: true });
}
</script>
<h1>Trending Today!</h1>
<div>
<input type="text" bind:value={searchQuery} placeholder="Search News..." />
<button on:click={handleSearchChange}>Search</button>
</div>
<ul>
{#each data.news as article}
<li>
<div>
<h2>
<a href={`/${article.id}`}>
{article.translations[0].title}
</a>
</h2>
<p>By {article.author}</p>
</div>
</li>
{/each}
</ul>
The above code will:
searchQuery to store the user's search input.searchQuery variable with the value of the q query parameter from the current URL ($page.url.searchParams.get("q")). If no q parameter is present, it defaults to an empty string.handleSearchChange to update the URL with the current searchQuery value using the goto function from $app/navigation. The replaceState: true option will replace the current history entry instead of creating a new one.Throughout this tutorial, you've learned how to build a multilingual news application using SvelteKit and Directus. You have set up a SvelteKit project, created a Directus Wrapper, and used it to query data. We created translation collections using Directus's flexible CMS and used the translation interface to translate the news article content into different languages.