Translation Backends Guide

View Source

AshPhoenixTranslations supports multiple storage backends for translations, each with different characteristics and use cases.

Database Backend (Default)

The database backend stores translations in JSONB/JSON columns within the same table as your resource.

Configuration

defmodule MyApp.Product do
  use Ash.Resource,
    extensions: [AshPhoenixTranslations]
  
  translations do
    translatable_attribute :name,
      locales: [:en, :es, :fr]
    
    translatable_attribute :description,
      locales: [:en, :es, :fr]
    
    backend :database  # Default
    cache_ttl 3600     # Optional caching
    audit_changes true # Optional audit trail
  end
end

How It Works

  • Adds name_translations and description_translations JSONB columns to your table
  • Stores all translations for a field in a single column as: %{en: "English", es: "Spanish"}
  • Provides calculated attributes (name, description) that return the value for the current locale
  • Supports atomic updates and database transactions

Database Schema

-- Example migration generated by the installer
CREATE TABLE products (
  id uuid PRIMARY KEY,
  sku text NOT NULL,
  price decimal NOT NULL,
  
  -- Translation storage columns (added by transformers)
  name_translations jsonb DEFAULT '{}',
  description_translations jsonb DEFAULT '{}',
  
  inserted_at timestamp NOT NULL DEFAULT now(),
  updated_at timestamp NOT NULL DEFAULT now()
);

-- Indexes for better query performance (optional)
CREATE INDEX products_name_translations_gin_idx ON products USING gin (name_translations);
CREATE INDEX products_description_translations_gin_idx ON products USING gin (description_translations);

Advantages

  • Transactional: Full ACID compliance with your main data
  • Performance: Single query to fetch resource with all translations
  • Simplicity: No additional infrastructure required
  • Indexing: JSONB supports GIN indexes for fast queries
  • Type Safety: Leverages Ash's type system

Disadvantages

  • Database Coupling: Tied to databases that support JSON/JSONB
  • Query Complexity: Requires JSONB operators for complex translation queries
  • Size: Can increase row size significantly with many translations

Best For

  • Applications with moderate translation volume
  • Projects requiring transactional consistency
  • Teams comfortable with JSONB/JSON database features
  • Single database deployments

Gettext Backend

The Gettext backend integrates with Phoenix's built-in internationalization system using .po files.

📚 Comprehensive Guide Available For detailed documentation on message ID patterns, .po file structure, pluralization, and advanced features, see the Gettext Backend Complete Guide.

Configuration

defmodule MyApp.Product do
  use Ash.Resource,
    extensions: [AshPhoenixTranslations]
  
  translations do
    translatable_attribute :name,
      locales: [:en, :es, :fr]
    
    translatable_attribute :description,
      locales: [:en, :es, :fr]
    
    backend :gettext
    gettext_module MyAppWeb.Gettext  # Required
  end
end

How It Works

  • Generates message IDs in the format "product.name", "product.description"
  • Stores translations in .po files under priv/gettext/[locale]/LC_MESSAGES/
  • Uses Gettext's runtime resolution for translations
  • Integrates with existing Phoenix Gettext workflows

File Structure

priv/gettext/
 en/
    LC_MESSAGES/
        default.po
        resources.po      # Resource translations
 es/
    LC_MESSAGES/
        default.po
        resources.po
 fr/
     LC_MESSAGES/
         default.po
         resources.po

Example .po Files

# priv/gettext/es/LC_MESSAGES/resources.po
msgid "product.name"
msgstr "Nombre del Producto"

msgid "product.description" 
msgstr "Descripción del Producto"

Setup Process

# 1. Install with Gettext backend
mix ash_phoenix_translations.install --backend gettext

# 2. Extract translatable strings
mix ash_phoenix_translations.extract

# 3. Update .po files with translations
mix gettext.merge priv/gettext

# 4. Compile translations
mix compile.gettext

Advantages

  • Standard: Uses industry-standard .po file format
  • Tools: Extensive tooling ecosystem (Poedit, Weblate, etc.)
  • Workflow: Integrates with existing translation workflows
  • Pluralization: Built-in plural form support
  • Context: Supports translation contexts and comments

Disadvantages

  • File Management: Requires managing many .po files
  • Deployment: Need to compile translations at build time
  • Runtime Updates: Cannot update translations without redeployment
  • Performance: Additional file system lookups

Best For

  • Teams with existing Gettext workflows
  • Applications requiring professional translation management
  • Projects with complex pluralization needs
  • Organizations using translation management services

Performance Considerations

Caching

Both backends benefit from caching:

translations do
  backend :database
  cache_ttl 3600  # Cache for 1 hour
end

Batch Operations

For large datasets, use batch translation operations:

# Instead of individual translations
products = Enum.map(products, &AshPhoenixTranslations.translate(&1, locale))

# Use batch translation  
products = AshPhoenixTranslations.translate_all(products, locale)

Database Optimizations

For the database backend:

-- Add indexes for commonly queried translation fields
CREATE INDEX products_name_en_idx ON products ((name_translations->>'en'));
CREATE INDEX products_name_es_idx ON products ((name_translations->>'es'));

-- Full-text search indexes
CREATE INDEX products_description_search_en_idx 
ON products USING gin (to_tsvector('english', description_translations->>'en'));

Migration Between Backends

Database to Gettext

# 1. Export current translations
mix ash_phoenix_translations.export current_translations.json --resource MyApp.Product

# 2. Reconfigure resource for Gettext backend
# (Update resource configuration)

# 3. Reinstall with Gettext
mix ash_phoenix_translations.install --backend gettext

# 4. Import translations to .po format
mix ash_phoenix_translations.import current_translations.json --format gettext

Gettext to Database

# 1. Export from Gettext
mix ash_phoenix_translations.extract --format json --output current_translations.json

# 2. Reconfigure resource for database backend
# (Update resource configuration)

# 3. Run migration
mix ecto.migrate

# 4. Import translations
mix ash_phoenix_translations.import current_translations.json --resource MyApp.Product

Choosing the Right Backend

Use Database Backend When:

  • You need transactional consistency
  • Your team prefers database-centric solutions
  • You have moderate translation volume
  • You want simple deployment
  • Your infrastructure team is comfortable with JSONB

Use Gettext Backend When:

  • You have existing Gettext workflows
  • You work with professional translators
  • You need complex pluralization
  • You use translation management services
  • You prefer file-based translation management

Troubleshooting

Database Backend Issues

Problem: JSONB queries are slow Solution: Add appropriate GIN indexes

Problem: Large row sizes Solution: Consider normalizing heavily translated content

Problem: Migration failures Solution: Ensure your database supports JSONB (PostgreSQL) or JSON

Gettext Backend Issues

Problem: Translations not updating Solution: Recompile with mix compile.gettext

Problem: Missing message IDs Solution: Run mix ash_phoenix_translations.extract

Problem: .po file conflicts Solution: Use mix gettext.merge priv/gettext --merge