Skip to content

Add template i18n with Liquid t filter#271

Closed
Swahjak wants to merge 20 commits intoNotifuse:mainfrom
Swahjak:feat/template-i18n
Closed

Add template i18n with Liquid t filter#271
Swahjak wants to merge 20 commits intoNotifuse:mainfrom
Swahjak:feat/template-i18n

Conversation

@Swahjak
Copy link

@Swahjak Swahjak commented Feb 24, 2026

Closes #268

Summary

  • Add template-level internationalization using a Liquid t filter, enabling emails to be sent in the contact's preferred language automatically
  • Translations stored as JSONB at both workspace level (shared base) and template level (overrides), merged at render time
  • Locale resolution follows a fallback chain: contact language → base language match → template default → workspace default
  • New workspace_translations table and CRUD API for managing workspace-wide translation strings
  • Frontend: translations panel in template editor (3rd tab) + workspace language settings page

What's Included

Backend

  • Domain: ResolveNestedKey, InterpolatePlaceholders, ResolveLocale, MergeTranslations utility functions; WorkspaceTranslation entity; new fields on Template and WorkspaceSettings
  • Liquid Engine: TranslationFilters struct with T method registered on SecureLiquidEngine; RegisterTranslations() method
  • Migration V28: Adds translations (JSONB) and default_language columns to templates table; creates workspace_translations table
  • Repository: WorkspaceTranslationRepository with Upsert/GetByLocale/List/Delete
  • Service: WorkspaceTranslationService with auth integration; translation wiring in EmailService and broadcast QueueMessageSender
  • HTTP: RPC endpoints workspace_translations.upsert, .list, .delete
  • OpenAPI: Full schema documentation for new endpoints and types

Frontend

  • TranslationsPanel component: flat key editor with per-locale columns, search, JSON import/export
  • Integrated as 3rd tab in CreateTemplateDrawer
  • LanguageSettings component in workspace settings
  • API service for workspace translations CRUD

Dependencies

Related PRs

Test plan

  • All 28 backend test packages pass (make test-unit)
  • Translation utility functions: 32 test cases
  • Liquid t filter: 6 test cases (simple keys, missing keys, placeholders, contact variables)
  • V28 migration: version, flags, success, per-step error tests
  • Workspace translation repository: full sqlmock coverage
  • Workspace translation service: 19 test cases with gomock
  • Workspace translation HTTP handler: 19 test cases
  • TypeScript compilation clean (tsc --noEmit)
  • Backend build clean (go build ./cmd/api/)
  • Manual testing: create template with translations, send test email with contact language set

Note

go.mod currently has a replace directive for local liquidgo development. Once Notifuse/liquidgo#3 is merged and tagged, update go.mod to point to the new remote version and remove the replace directive.

Design for issue Notifuse#268: template-level internationalization using
a Liquid `t` filter with translation key catalogs. Covers data model,
API surface, rendering pipeline, migration plan, and frontend UX.
13-task TDD plan covering: translation utilities, Liquid t filter,
domain model changes, V28 migration, repository/service/handler layers,
rendering pipeline integration, and frontend translations panel.
7-task plan covering: OpenAPI schema/paths updates for template
translations and workspace translations API, CHANGELOG v28.0 entry,
CLAUDE.md Liquid filter docs, and design doc migration correction.
Locale resolution, nested key lookup, placeholder interpolation,
and translation merging — pure functions with full test coverage.
Registers a TranslationFilters struct on the Liquid engine that resolves
nested keys with {{ "key" | t }} syntax and supports placeholders
via named args: {{ "key" | t: name: contact.first_name }}

Includes a patch to liquidgo (via go.mod replace) to evaluate and pass
keyword arguments to filter invocations. The t filter is registered by
default with empty translations on engine creation, so it always exists.
Template: translations (JSONB) + default_language.
WorkspaceSettings: default_language + supported_languages.
New WorkspaceTranslation entity with repository interface.
Adds translations JSONB and default_language columns to templates table.
Creates workspace_translations table for shared translations.
CRUD operations for workspace-level translations with auth + validation.
The 'database up to date' test expected version 27 but V28 is now
registered, so it needs to return 28 to be considered up to date.
Add translations and default_language fields to Template interface.
Create workspace-translations API service with list, upsert, and
delete operations matching the RPC-style backend endpoints.
Default language and supported languages configuration in workspace settings.
@pierre-b
Copy link
Contributor

Hi @Swahjak , sorry the JSON dictionary pattern for translating rich HTML content it not appropriate.

@pierre-b pierre-b closed this Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Template-level i18n — auto-select or translate email content based on contact language

2 participants