Packages
Plugin adding i18n to live_svelte using compile-time extraction
Current section
Files
Jump to
Current section
Files
live_svelte_gettext
CHANGELOG.md
CHANGELOG.md
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.2.0] - 2026-07-28
A correctness release. Plural translations never actually worked, and the
extractor could silently drop strings or report the wrong line. Both are fixed,
and the library now tracks the current Gettext and LiveSvelte ecosystem.
Upgrading requires no code changes. If you use `ngettext` in Svelte components,
run `mix gettext.extract && mix gettext.merge priv/gettext` and fill in the
`msgstr[n]` entries - they were previously ignored.
### Fixed
- **Plural translations were never applied.** `all_translations/1` returned the
raw English `msgid`/`msgid_plural` for every locale, so `ngettext` rendered
untranslated text everywhere. Compounding it, the client looked plural forms
up under keys the server never emitted, so even a hand-built map would not
have worked.
Plural forms are now read directly from your `.po` files. This is not
avoidable via Gettext's runtime API: `lngettext/7` does
`Map.put(bindings, :count, n)` internally, so it can never return a localized
plural with the `%{count}` placeholder intact - and the placeholder is what
must reach the browser, since interpolation happens client-side.
- **Plural form selection ignored the locale.** The client always used
`count === 1 ? singular : plural`, which is wrong for the many languages with
more than two forms. Selection now follows the locale's own rules via
`Intl.PluralRules`, so Polish, Russian, Arabic and friends work correctly.
Falls back to the English rule when `Intl.PluralRules` is unavailable.
- **Multi-line comments shifted `.pot` line references.** Comments were deleted
before scanning, taking their newlines with them, so every reference after a
multi-line `<!-- ... -->` pointed too far up the file. Comments are now
blanked out with their line structure preserved.
- **The extractor silently dropped valid calls.** The pattern required the whole
call to close with an optional object literal, so these were skipped without
any warning:
```svelte
{gettext("Hello %{name}", vars)} <!-- non-literal bindings -->
{gettext("Hello", buildVars(user))} <!-- function call -->
{gettext(
"Prettier formatted",
{ name }, <!-- trailing comma -->
)}
```
Only the msgid is matched now; whatever follows is ignored, so an unusual
bindings argument can no longer cause a string to vanish.
- **Declared Gettext requirement was unsatisfiable.** `~> 0.24` was advertised,
but `use Gettext.Backend` and the `lgettext/5` and `lngettext/7` callbacks
were only introduced in Gettext 0.26 - installing with 0.24 or 0.25 failed to
compile. The requirement is now `~> 0.26 or ~> 1.0`, and CI tests both ends of
the range.
### Added
- **Gettext 1.0 support.** No `override: true` needed.
- **`translation_payload/1`** on generated modules, returning the translation map
with the locale and its plural rules under the reserved `"__lsg__"` key.
`<.svelte_translations />` uses it automatically and falls back to
`all_translations/1` for modules compiled against 0.1.x.
The metadata is nested rather than wrapping the translations
(`%{"locale" => ..., "translations" => ...}`) specifically so older clients
keep working: they do `translations = {...payload}` and look msgids up
directly, so a wrapper would make every lookup miss and every string fall back
to English with no error anywhere. Verified against the published npm 0.1.1
bundle - upgrading Hex before npm keeps singular translations working, and
plurals stay exactly as they already were.
- **Automatic reload on locale change.** The client watches the translation
script tag, so a LiveView locale switch updates Svelte components without a
page reload.
- **`refreshTranslations()` and `getLocale()`** exported from the JavaScript
client.
- **JavaScript comments are ignored** when extracting - whole-line `//` comments
and `/* ... */` blocks, alongside the existing `<!-- ... -->` handling.
- **Real test coverage for `mix gettext.extract`.** The `.pot` reference
behaviour - the library's core promise - was previously only tested by hand.
### Changed
- **Jason is now optional.** On OTP 27+ the built-in `:json` module is used,
matching LiveSvelte 0.17's move away from a hard Jason dependency. Override
with `config :live_svelte_gettext, json_library: MyEncoder`.
- **Runtime lookup moved out of the generated code** into
`LiveSvelteGettext.Runtime`, so lookup fixes ship as a library upgrade instead
of requiring consumers to recompile.
- **`mix hex.build` and `mix hex.publish` now build the JavaScript bundle** and
verify that `mix.exs` and `assets/package.json` agree on the version. The npm
package had drifted to 0.1.1 while Hex was at 0.1.3, and `assets/dist` is
gitignored, so a publish could ship a stale bundle.
- **CI now runs the TypeScript test suite** (69 tests that had never run in CI),
tests Elixir 1.18/OTP 27 and 1.20/OTP 28, and verifies both the floor and the
ceiling of the declared dependency ranges.
### Compatibility
Verified against Elixir 1.18 and 1.20, OTP 27 and 28, Gettext 0.26 and 1.0, and
Expo 0.5 and 1.1. LiveSvelte itself is not a dependency - the library reads
`.svelte` files and renders a `<script>` tag - so LiveSvelte 0.18's move to Vite
needs no changes here.
## [0.1.3] - 2025-10-19
### Fixed
- **Fixed extraction of multiline gettext calls**
- Previously, the extractor would fail to find `gettext()` calls split across multiple lines
- Now correctly handles calls with line breaks between function name and arguments
- Ensures all translation strings are extracted regardless of formatting
## [0.1.2] - 2025-10-16
### Fixed
- **Fixed compilation error when installed as a dependency**
- Changed `igniter` from `only: :dev` to `optional: true` to follow Igniter best practices
- Updated to Igniter ~> 0.6 (from ~> 0.5)
- Refactored installer to use conditional compilation pattern (`if Code.ensure_loaded?(Igniter)`)
- Previously, the installer task would fail to compile because Igniter wasn't available in the dependency environment
- The `only: :dev` option meant Igniter was only available when developing the library itself, not when used as a dependency
- The `optional: true` configuration makes Igniter available during installation but doesn't include it in production builds
- Installer now gracefully falls back to a helpful error message when Igniter is not available
- Installation now works reliably with `mix igniter.install live_svelte_gettext`
- **Fixed installer crash caused by unnecessary Gettext backend modification step**
- Removed the step that attempted to add `use LiveSvelteGettext` to Gettext backend
- This step was causing the Igniter installer to crash on clean installations
- The `use LiveSvelteGettext` should be added manually by users in their SvelteStrings module, not injected into the Gettext backend
- Removed 62 lines of problematic code from `lib/mix/tasks/live_svelte_gettext.install.ex`
### Changed
- **Simplified installation process**
- Reduced installation steps from 7 to 6 by removing manual Gettext backend modification
- Users now have more control over how `LiveSvelteGettext` is integrated into their backend
## [0.1.1] - 2025-10-14
### Fixed
- **Igniter installer critical bug fixes** (7 issues resolved)
- Fixed backend detection to only find actual `Gettext.Backend` modules, not consumers (Issue #7)
- Fixed module naming to create proper submodule `MyApp.Gettext.SvelteStrings` (Issue #4)
- Fixed missing `use LiveSvelteGettext` in Gettext backend (previously undiscovered issue)
- Fixed component import to be added to both `:html` and `:live_view` functions (Issue #3)
- Eliminated all compile warnings about undefined functions (Issue #5)
- **Fixed translation initialization race condition**
- Phoenix hook `mounted()` callback sometimes doesn't fire before Svelte components call `gettext()`
- Implemented lazy initialization - translations now auto-initialize on first use
- Completely removed Phoenix hook requirement for simpler setup
### Changed
- **Refactored JavaScript setup to manual process** (Issues #1, #2, #6 resolution)
- Removed 300+ lines of fragile regex-based JavaScript modification code
- JavaScript setup now uses interactive `mix live_svelte_gettext.setup` wizard
- Users get clear, copy-pasteable code snippets based on their project structure
- More reliable across different JavaScript formatting styles and project setups
- Follows Elixir ecosystem best practices (explicit over implicit)
- **Phoenix hook completely removed**
- Zero JavaScript setup required - just install the NPM package
- Translations automatically initialize on first `gettext()` or `ngettext()` call
- Simpler API with less cognitive overhead for users
- Updated all documentation to remove hook references
### Added
- **Interactive setup wizard** (`mix live_svelte_gettext.setup`)
- Detects `app.js` location automatically
- Analyzes existing code structure and hooks
- Provides context-aware, copy-pasteable instructions
- Verifies setup status and completion
- Supports multiple project structures (empty hooks, existing hooks, nested objects, etc.)
- **Automated `use LiveSvelteGettext` injection**
- Installer now adds `use LiveSvelteGettext` to your Gettext backend module
- Properly configured with backend reference and Svelte path
- Idempotent operation (safe to run multiple times)
- **Improved Elixir component import**
- Now adds `import LiveSvelteGettext.Components` to both `:html` and `:live_view` functions
- Ensures `svelte_translations/1` is available in LiveView contexts
- Idempotent operation
- **Lazy initialization for translations** (`ensureInit()`)
- Translations automatically initialize from DOM on first use
- Eliminates need for Phoenix hook registration entirely
- Works in all environments with zero configuration
- Added comprehensive test coverage for lazy initialization (6 new tests)
- Supports SSR and all edge cases
### Removed
- **Phoenix LiveView hook requirement**
- `LiveSvelteGettextInit` hook no longer needed or exported
- Removed hook element from `<.svelte_translations />` component
- No more hook registration setup step
- **Automatic JavaScript modification code** (replaced with simplified setup guide)
- `add_import_to_js/1` function
- `add_hook_to_livesocket/1` function
- `add_hook_to_existing_hooks_object/1` function
- `find_matching_closing_brace/2` function
- All JavaScript-related test fixtures and tests (12 tests removed, 10 Elixir tests remain)
- Interactive setup wizard functions (replaced with simple informational task)
### Architecture
This release represents a significant architectural improvement:
**Before v0.1.1:**
- Attempted to automatically modify JavaScript using regex patterns
- Fragile across different formatting styles and project structures
- Hard to debug and maintain
- 7 critical bugs in JavaScript parsing logic
**After v0.1.1:**
- Clean separation: Igniter handles Elixir, setup wizard handles JavaScript
- Users have full visibility into changes being made
- More reliable and easier to maintain
- Follows patterns used by successful libraries like `live_svelte`
### Migration
If you installed an earlier version of v0.1.1 (released 2025-10-14 morning):
1. The JavaScript modifications may have errors - manually verify `assets/js/app.js`
2. Run `mix live_svelte_gettext.setup` to get correct setup instructions
3. Your Elixir-side setup should be correct and requires no changes
For most users, simply run:
```bash
mix deps.update live_svelte_gettext
mix live_svelte_gettext.setup
```
## [0.1.0] - 2025-10-14
### Added
- **Core compile-time extraction engine** (`LiveSvelteGettext.Extractor`)
- Scans `.svelte` files for `gettext()` and `ngettext()` calls
- Supports both single and double quotes
- Handles escaped characters properly
- Extracts file and line number references for debugging
- Deduplicates strings across multiple files
- **Compile-time code generation** (`LiveSvelteGettext.Compiler`)
- Generates Elixir `gettext()` and `ngettext()` calls for extraction
- Creates runtime `all_translations/1` function
- Sets up `@external_resource` for automatic recompilation
- Provides `__lsg_metadata__/0` debug function
- Validates configuration options at compile time
- **NPM package** (`live-svelte-gettext`)
- `gettext()` - Simple translations with interpolation
- `ngettext()` - Plural form handling
- `initTranslations()` - Initialize with server data (called automatically)
- `isInitialized()` - Check initialization status
- `resetTranslations()` - Reset state (for testing)
- `LiveSvelteGettextInit` - Phoenix LiveView hook for auto-initialization
- Full TypeScript type safety with `.d.ts` definitions
- Variable interpolation with `%{name}` syntax
- Count-based pluralization
- **Phoenix component** (`.svelte_translations`)
- Injects translations as JSON script tag
- Renders invisible div with `phx-hook="LiveSvelteGettextInit"`
- Auto-initializes translations via Phoenix LiveView hook
- Zero manual setup required in Svelte components
- **Igniter installer** (`mix igniter.install live_svelte_gettext`)
- Automatic Gettext backend detection
- Automatic Svelte directory detection
- Creates `SvelteStrings` module with correct configuration
- Copies TypeScript library to assets directory
- Provides clear usage instructions
- Supports manual configuration via CLI flags
- **Comprehensive documentation**
- Module documentation with examples
- Function documentation with type specs
- README with quick start guide
- Architecture diagrams
- Troubleshooting section
- Contributing guidelines
- **Testing infrastructure**
- Unit tests for extraction engine
- Unit tests for compiler
- Unit tests for TypeScript library
- Test fixtures for realistic scenarios
- ExCoveralls integration for coverage reporting
### Features
- Zero-maintenance workflow - no generated files to commit
- Automatic recompilation when Svelte files change
- Works with existing `mix gettext.extract` and `mix gettext.merge` workflows
- Full integration with Elixir's Gettext library
- Type-safe TypeScript client
- One-command installation
- Supports variable interpolation in translations
- Supports plural forms
### Technical Details
- Elixir 1.18+ required
- Compatible with Gettext 0.24+
- Uses `@external_resource` for automatic recompilation
- Uses macro-generated AST (no runtime overhead)
- Client library has zero dependencies
- Follows Elixir and TypeScript best practices
## [Unreleased]
### Planned
- Context-aware translations (pgettext) and domains (dgettext)
- Translation extraction from TypeScript/JavaScript files
- Translation coverage reporting
---
[0.2.0]: https://github.com/xnilsson/live_svelte_gettext/releases/tag/v0.2.0
[0.1.3]: https://github.com/xnilsson/live_svelte_gettext/releases/tag/v0.1.3
[0.1.2]: https://github.com/xnilsson/live_svelte_gettext/releases/tag/v0.1.2
[0.1.1]: https://github.com/xnilsson/live_svelte_gettext/releases/tag/v0.1.1
[0.1.0]: https://github.com/xnilsson/live_svelte_gettext/releases/tag/v0.1.0