Svelte internationalization

Svelte i18n Internationalization: how to localize a Svelte app

Internationalizing a Svelte app (aka Svelte i18n) is key to reaching a global audience. This guide walks you through making your app multilingual using the Paraglide library, setting up efficient language management, and streamlining translations.

The source code for this article can be found on GitHub.

    Prerequisites for Svelte i18n

    Before jumping into Svelte i18n, make sure you have the following set up:

    • Node.js: You’ll need Node.js installed to manage dependencies and run your app. Get the latest stable version from Node.js.
    • A Code Editor: Any editor works, but VS Code is recommended for its Svelte extensions and syntax highlighting.
    • Basic JavaScript Knowledge: Familiarity with JavaScript and ES6+ syntax will help you follow along. If you know the basics of Svelte, that’s even better.

    Setting up a new app with SvelteKit

    To get started with Svelte i18n, let’s set up a new Svelte project using SvelteKit. This setup will create a streamlined environment for building a fully localized app.

    • Create a new Svelte project: Open your terminal, navigate to the desired directory, and run the following command:
    npx sv create svelte-lokalise-i18n

    When prompted, confirm the installation of the sv package by selecting “yes.”

    • Choose a project template: SvelteKit will ask you to select a template. For a basic setup, go with “SvelteKit minimal”. When asked, enable TypeScript for better type safety, which will help with managing translations in your Svelte i18n setup.
    • Select Paraglide and configure languages: During setup, select Paraglide as a translation solution for your app’s internationalization. After that, specify the languages you want to support. For example, enter en, fr, ar to add support for English, French, and Arabic. These settings will configure your project to work seamlessly with multiple languages.
    • Finish configuration: Opt out of the demo option if prompted and then choose your preferred package manager—npm, yarn, or pnpm (we’ll use npm for this guide).

    Managing translation files

    With your new SvelteKit project set up, you’ll find a messages directory in the project structure. This folder contains your JSON translation files—one for each language you’ve configured during setup. Paraglide uses this structure to manage translations, where each file represents a set of key-value pairs that map text identifiers to their localized versions in different languages.

    Each translation file follows a consistent format, making it easy to scale as you add more languages. Inside each file, translation strings are stored with unique keys and their respective localized messages.

    Structure of translation files

    The JSON files in the messages directory, such as en.json, fr.json, and ar.json, serve as the core of your app’s multilingual capabilities. Here’s a closer look at how to define and organize your translations:

    • JSON Schema: At the top of each JSON file, there’s a $schema key that references a schema URL. This schema, provided by Paraglide, helps ensure that the JSON files conform to the expected format, preventing syntax errors or inconsistencies in your translations.
    • Keys and values: Each key represents a unique identifier for a string in your app, and the corresponding value is the translated message for that language. This setup makes it easy to maintain consistency across languages. For example, the welcome key will refer to a “Welcome” message in all languages, while the created_by key represents the line that acknowledges the company behind the tutorial.

    Adding translations

    Below is an example of how your translation files might look for three languages: English, French, and Arabic. Let’s break down each file.

    en.json

    {
    	"$schema": "https://inlang.com/schema/inlang-message-format",
    	"welcome": "Welcome to this tutorial!",
    	"created_by": "Tutorial brought to you by {company}!"
    }

    fr.json

    {
    	"$schema": "https://inlang.com/schema/inlang-message-format",
    	"welcome": "Bienvenue dans ce tutoriel!",
    	"created_by": "Tutoriel proposé par {company}!"
    }

    ar.json

    {
    	"$schema": "https://inlang.com/schema/inlang-message-format",
    	"welcome": "مرحبًا بك في هذا الدرس!",
    	"created_by": "تم تقديم هذا الدرس من قبل {company}!"
    }

    Each file includes:

    • Interpolated values: {company} in each created_by translation is an example of interpolation. Paraglide allows these placeholders to dynamically insert values based on user context or app data.
    • RTL (Right-to-Left) Support: Arabic text is displayed in RTL by default, and if your app has RTL support, it will render the Arabic text correctly without additional configuration.

    Simplifying the translation process with Lokalise

    When localizing an application, the toughest and most time-consuming task isn’t building the app itself—it’s managing and translating the content across multiple languages. Lokalise streamlines this process, making Svelte i18n setups smoother and faster by offering an efficient way to handle translations. With Lokalise, you can easily manage, update, and download translations directly into your project, minimizing the manual overhead of localization.

    Getting started with Lokalise

    To get started with Lokalise for Svelte i18n, follow these steps:

    1. Sign up for a free trial: This will give you access to Lokalise’s core features, including API access, project management, and more.
    2. Install the Lokalise CLI: The Lokalise CLI allows you to create projects, upload translation files, and download updated translations.
    3. Generate an API token: Go to the “API tokens” section on your Lokalise profile page and generate a read/write token. This token enables secure communication between your project and Lokalise.
    4. Create a new Lokalise project: Set English as your base language (or any primary language for your app). This will serve as the default language for translation.
    5. Configure the project in Lokalise:
      • Go to the project’s settings by selecting “More” on the project page.
      • Copy the Project ID and API token—you’ll need these for uploading and downloading files.
    6. Upload your translation files to Lokalise: Use the following command to upload your English translations, replacing <token> and <project_id> with your values: lokalise2 file upload --token --project-id --lang_iso en --file PATH/TO/PROJECT/messages/en.json. Repeat this command for other languages (e.g., fr.json, ar.json) to upload all your translations.
    7. Manage translations on Lokalise:
      • Navigate to your project overview page on Lokalise. Here, you’ll see all the keys and values from your uploaded JSON files.
      • Lokalise allows you to filter, edit, delete, and add new keys. The filtering options help you identify untranslated keys quickly.
      • This centralized view streamlines collaboration if you’re working with translators, and you can even upload screenshots to provide translators with context.
    8. Download updated translations: Once translations are finalized, download them back into your project: lokalise2 file download --token --project-id --format json --dest PATH/TO/PROJECT/messages.
    9. Explore Lokalise’s additional features: Lokalise supports multiple platforms and file formats, making it adaptable to a variety of project needs. You can even request translations from professionals directly within Lokalise or integrate features like screenshot-based text extraction for more accurate translations.

    Performing translations with Paraglide

    Now that we’ve set up our translation files, it’s time to use them in your SvelteKit app with Paraglide. This section will guide you through integrating translations into your Svelte components, so they display the correct text based on the selected language.

    Let’s start by opening the main page component, +page.svelte, in your src/routes directory. Then, add the following code to set up and render translations.

    <script>
    import * as m from "$lib/paraglide/messages"
    </script>
    
    <h1>{m.welcome()}</h1>
    
    <p>{m.created_by({ company: "Lokalise" })}</p>

    Code Explanation

    1. Importing Translation Messages: import * as m from "$lib/paraglide/messages"; — this line imports all your translation messages as an object (m). Each message key (like welcome or created_by) is now available as a function, which outputs the translated text based on the active language setting. By using this object, we ensure that the displayed content adapts dynamically to the selected locale.
    2. Rendering Translations:
      • m.welcome(): This function call retrieves the translated text for the welcome key. It uses the current locale set in your app, so if the language is switched to French, m.welcome() will display Bienvenue dans ce tutoriel! instead of the English version.
      • m.created_by({ company: "Lokalise" }): Here, the created_by translation function includes a placeholder ({ company }). The placeholder allows for dynamic content within translations, which is helpful for inserting values like names or other variables into messages. In this case, { company: "Lokalise" } replaces {company} in your translation files, so the output text would be “Tutorial brought to you by Lokalise” in English or its equivalent in other languages.

    Adding language switcher

    To create a seamless language-switching experience, let’s add a language switcher to the app. By embedding it in the +layout.svelte file, we ensure it’s available on all pages, so users can change languages from anywhere in the app.

    Setting up the language switcher component

    Here’s the code to add a language switcher in +layout.svelte:

    <script lang="ts">
      import { i18n } from '$lib/i18n';
      import { ParaglideJS } from '@inlang/paraglide-sveltekit';
      import { availableLanguageTags, languageTag } from "$lib/paraglide/runtime";
      import { page } from "$app/stores";
      import { goto } from "$app/navigation";
      import { get } from "svelte/store";
    
      // Map language codes to user-friendly names
      const languageNames = {
        en: "English",
        fr: "French",
        ar: "Arabic"
      };
    
      function switchToLanguage(newLanguage) {
        // Get the canonical (untranslated) path of the current page
        const canonicalPath = i18n.route(get(page).url.pathname);
        
        // Generate the localized path based on the selected language
        const localizedPath = i18n.resolveRoute(canonicalPath, newLanguage);
        
        // Use SvelteKit's `goto` function to navigate to the localized path
        goto(localizedPath);
      }
    
      let { children } = $props();
    </script>
    
    <!-- Wrap all child components in ParaglideJS to manage translations -->
    <ParaglideJS {i18n}>
      {@render children()}
    </ParaglideJS>
    
    <!-- Language Switcher UI -->
    <nav>
      <ul>
        {#each availableLanguageTags as lang}
          <li>
            <a 
              href="#"
              hreflang={lang}
              aria-current={lang === languageTag() ? "page" : undefined}
              onclick={(event) => {
                event.preventDefault();
                switchToLanguage(lang);
              }}
            >
              {languageNames[lang]}
            </a>
          </li>
        {/each}
      </ul>
    </nav>
    

    Code breakdown

    Language Imports and Configuration

    The code begins by importing necessary modules. We import i18n for handling routes, ParaglideJS for wrapping and managing translation contexts, and essential utilities like availableLanguageTags (a list of supported languages) and languageTag (which indicates the current language). We also import page from SvelteKit’s stores to retrieve information about the current URL, and goto from SvelteKit’s navigation API to programmatically navigate between pages.

    Defining languageNames

    The languageNames object maps language codes to their human-readable names. This mapping allows us to display full language names like “English” and “French” instead of just codes like “en” and “fr” in the UI.

    The switchToLanguage function

    The switchToLanguage function is the core of our language switcher’s functionality. This function does three things:

    • First, it retrieves the canonical (untranslated) version of the current path using i18n.route(get(page).url.pathname).
    • Next, it generates a localized path by passing the canonical path and the target language to i18n.resolveRoute, which translates the path to the appropriate language version.
    • Finally, it uses SvelteKit’s goto function to navigate to the new localized path, triggering the app to load translations for the selected language.

    Wrapping content with ParaglideJS

    To ensure translations are available throughout the app, all child components are wrapped in <ParaglideJS {i18n}>. This tag passes down the translation context, enabling language updates to propagate through all rendered content when the language is changed.

    Building the language switcher UI

    The <nav> element contains the language switcher itself. Using an <ul> list structure, each supported language is displayed as a list item:

    • Dynamic link rendering: For each language, an <a> link is generated using availableLanguageTags. Each link has a hreflang attribute for accessibility and sets aria-current to "page" if it matches the active languageTag, indicating the current page’s language.
    • Click handler for language switching: The onclick handler prevents the default link behavior and calls switchToLanguage(lang) to update the language and navigate to the localized path without reloading the page.

    With this setup, users can switch languages from any page in your app, and Paraglide automatically updates translations based on the selected locale. The current language is displayed without a link, while other languages are presented as clickable options. This creates a seamless and intuitive language-switching experience for your Svelte i18n-enabled app.

    Conclusion

    Implementing Svelte i18n in your SvelteKit app is straightforward with Paraglide and Lokalise. By setting up Paraglide for translation management and integrating Lokalise for streamlined translation updates, you’ve created a scalable and efficient localization workflow.

    With organized translation files, automated updates through Lokalise, and a dynamic language switcher, your app is ready to serve a global audience. This setup makes it easy to add new languages and manage translations as your app grows, ensuring a seamless experience for users worldwide.

    Further reading

    Related articles
    Stop wasting time with manual localization tasks. 

    Launch global products days from now.