Translation Backends Guide
View SourceAshPhoenixTranslations 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
endHow It Works
- Adds
name_translationsanddescription_translationsJSONB 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
endHow It Works
- Generates message IDs in the format
"product.name","product.description" - Stores translations in
.pofiles underpriv/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.poExample .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
endBatch 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