LiveSvelteGettext (LiveSvelteGettext v0.2.0)

View Source

Compile-time translation extraction for Phoenix + Svelte applications.

This module provides a proof-of-concept solution for using Phoenix Gettext in Svelte components. It was extracted from a real project and addresses the challenge raised in live_svelte#120.

The Approach

Uses Elixir macros at compile time to:

  • Scan .svelte files for gettext() and ngettext() calls
  • Generate Elixir code that integrates with mix gettext.extract
  • Preserve accurate source references (e.g., assets/svelte/Button.svelte:42)
  • Runtime translations:
    • .svelte_translations component renders translations as JSON
    • Lazy initialization on first gettext() or ngettext() call
    • Translation access via all_translations/1
    • Plural forms read from your .po files, selected with the locale's own plural rules

No generated files are committed - everything happens at compile time using @external_resource for automatic recompilation when Svelte files change.

Quick Start

1. Setup (using Igniter installer)

mix igniter.install live_svelte_gettext

This automatically creates a separate module for Svelte translations:

defmodule MyAppWeb.Gettext.SvelteStrings do
  use Gettext.Backend, otp_app: :my_app
  use LiveSvelteGettext,
    gettext_backend: MyAppWeb.Gettext,
    svelte_path: "assets/svelte"
end

Important: Do not add use LiveSvelteGettext to your main Gettext backend module, as this creates a circular dependency. Always use a separate module.

2. Install NPM package

npm install live-svelte-gettext

3. Add translations to your template

In your LiveView or layout:

<.svelte_translations />
<.svelte name="MyComponent" props={%{...}} />

4. Use translations in Svelte (zero boilerplate!)

<script>
  import { gettext } from 'live-svelte-gettext'
</script>

<h1>{gettext("Welcome to our app")}</h1>
<p>{gettext("Hello, %{name}", { name: "World" })}</p>

That's it! Translations are automatically initialized on first use - zero setup!

How It Works

Compile time (when you run mix compile):

  1. The use LiveSvelteGettext macro scans all .svelte files in svelte_path
  2. Regex patterns extract gettext() and ngettext() calls with file:line metadata
  3. Generated Elixir code includes:
    • @external_resource attributes (triggers recompilation on file changes)
    • Calls to CustomExtractor.extract_with_location/8 (preserves Svelte source locations)
    • An all_translations/1 function for runtime
  4. When you run mix gettext.extract, it discovers these generated calls
  5. The CustomExtractor modifies Macro.Env to inject accurate Svelte file:line into .pot files

Runtime (when the page loads):

  1. The <.svelte_translations /> component fetches translations and renders JSON in a <script> tag
  2. Translations are lazily initialized on first gettext() or ngettext() call
  3. Svelte components call gettext() and ngettext() - interpolation happens in the browser

Configuration Options

  • :gettext_backend (required) - The Gettext backend module to use
  • :svelte_path (required) - Path to the directory containing Svelte files (relative to project root)

Runtime API

After using this module, you'll have access to:

  • all_translations(locale) - Returns a map of all translations for the given locale
  • translation_payload(locale) - The same translations plus the locale and its plural rules; this is what <.svelte_translations /> renders
  • __lsg_metadata__/0 - Debug function showing extracted strings and source files

Advanced Usage

Manual Initialization (for edge cases)

If you need more control (e.g., multi-tenant apps, custom loading logic), you can manually pass translations to Svelte components:

# In your LiveView:
def mount(_params, _session, socket) do
  payload = MyAppWeb.Gettext.SvelteStrings.translation_payload("en")
  {:ok, assign(socket, :translations, payload)}
end

# In your Svelte component (Svelte 5):
<script>
  import { initTranslations, gettext } from 'live-svelte-gettext'

  let { translations } = $props()
  initTranslations(translations)
</script>

<h1>{gettext("Welcome")}</h1>

initTranslations/1 also accepts the plain map returned by all_translations/1, but translation_payload/1 additionally carries the locale and its plural rules, which the client needs to pick the right plural form.

Note: Most users should use the automatic lazy initialization shown in Quick Start.

Summary

Functions

Macro for setting up LiveSvelteGettext in your Gettext backend.

Functions

__using__(opts)

(macro)

Macro for setting up LiveSvelteGettext in your Gettext backend.

See module documentation for usage examples.