This gem uses PostgreSQL's JSONB datatype and ActiveRecord models to translate model data.
- No extra columns or tables needed to operate
- Clean naming in the database model
- Everything is well tested
- Uses modern JSONB type for better performance and flexibility
- Falls back to default locale
-
v0.1.3
Fix redundant fallbacks when translation is nil
- I18n
- PostgreSQL with JSONB support (9.4+)
Add this line to your application's Gemfile:
gem 'awesome_jsonb_translate'
And then execute:
$ bundle
Or install it yourself as:
$ gem install awesome_jsonb_translate
Include AwesomeJsonbTranslate
in your model class.
Use translates
in your models, to define the attributes, which should be translateable:
class Model < ApplicationRecord
include AwesomeJsonbTranslate
translates :title, :description
end
p = Page.new(title_en: 'English title', title_de: 'Deutscher Titel')
p = Page.new(title: { en: 'English title', de: 'Deutscher Titel'})
p.title_en # => 'English title'
p.title_de # => 'Deutscher Titel'
I18n.with_locale(:en) { p.title } # => 'English title'
I18n.with_locale(:de) { p.title } # => 'Deutscher Titel'
It always falls back to default locale
# Behavior with fallbacks enabled
p = Page.new(title_en: 'English title')
I18n.with_locale(:de) { p.title } # => 'English title' (falls back to English)
p.title_de # => nil
# Behavior with empty string
p = Page.new(title_en: 'English title', title_de: '')
I18n.with_locale(:de) { p.title } # => 'English title' (falls back since German is empty)
p.title_de # => ''
p = Page.new(title: { en: 'English title', de: 'Deutscher Titel' })
p.title_raw # => { 'en' => 'English title', 'de' => 'Deutscher Titel' }
p.title_en = 'Updated English title'
p.title_de = 'Aktualisierter Deutscher Titel'
# Find records by current locale value
Page.find_by(title_en: 'English title')
# which transforms to
Page.where("title->>'en' = ?", 'English title') # queries current locale
# Use with other conditions
Page.find_by(title_en: 'English title', author: 'John')
# which transforms to
Page.where("title->>'en' = ?", 'English title').where(author: 'John')
# Find existing record by translated attribute
Page.find_or_initialize_by(title_en: 'English title')
# Initialize new record if not found
new_page = Page.find_or_initialize_by(title_en: 'New Page', slug: 'new')
new_page.persisted? # => false
# Find with combined attributes
Page.find_or_initialize_by(title_en: 'English title', slug: 'english-title')
# Find or create records
existing = Page.find_or_create_by(title_en: 'English title')
new_record = Page.find_or_create_by(title_en: 'Brand New', slug: 'brand-new') # Creates and saves the record
# Sort by translated field in current locale
Page.order("title->>'en' ASC")
# List translated attributes
Page.translated_attributes # => [:title, :content]
# List all accessor methods
Page.translated_accessors # => [:title_en, :title_de, :content_en, :content_de]
# Check translation presence
page.translated?(:title) # => true
page.translated?(:title, :fr) # => false
# Check translation availability
page.translation_available?(:title, :en) # => true
# Get all locales that have a translation
page.available_translations(:title) # => ["en", "de"]
# Get all available locales for the record
page.available_locales # => [:en, :de]
class Page < ActiveRecord::Base
include AwesomeJsonbTranslate
translates :title, :content
end
Make sure that the datatype of this columns is jsonb
:
class CreatePages < ActiveRecord::Migration
def change
create_table :pages do |t|
t.column :title, :jsonb
t.column :content, :jsonb
t.timestamps
end
end
end
Use the model attributes per locale:
p = Page.create(title_en: "English title", title_de: "Deutscher Titel")
I18n.locale = :en
p.title # => English title
I18n.locale = :de
p.title # => Deutscher Titel
I18n.with_locale :en do
p.title # => English title
end
The raw data is available via the suffix _raw
:
p = Page.new(title: {en: 'English title', de: 'Deutscher Titel'})
p.title_raw # => {'en' => 'English title', 'de' => 'Deutscher Titel'}
awesome_jsonb_translate
created a find_by
helper.
Page.create!(:title_en => 'English title', :title_de => 'Deutscher Titel')
Page.create!(:title_en => 'Another English title', :title_de => 'Noch ein Deutscher Titel')
Page.find_by(title_en: 'English title') # => Find by a specific language
For generating URLs with translated slugs:
class Page < ActiveRecord::Base
translates :title
def to_param
# Or use parameterize for URL-friendly slugs
title_en.parameterize
end
end
awesome_jsonb_translate
patches ActiveRecord, which create the limitation, that a with where
chained first_or_create
and first_or_create!
doesn't work as expected.
Here is an example, which won't work:
Page.where(title_en: 'Titre français').first_or_create!
A workaround is:
Page.find_or_create_by(title_en: 'Titre français')
bundle
bin/setup
bundle exec rspec
To run the tests:
- Ensure PostgreSQL is installed and running
- Set up the test environment:
bin/setup
This script will:
- Install required gem dependencies
- Create the PostgreSQL test database if it doesn't exist
- Run the tests:
bundle exec rspec
You can also set custom database connection details with environment variables:
DB_NAME=custom_db_name DB_USER=your_username DB_PASSWORD=your_password bundle exec rspec
If you encounter issues running tests:
- Make sure PostgreSQL is installed and running
- Ensure the user has permissions to create databases
- Check that the database 'awesome_jsonb_translate_test' exists or can be created
- Run
bin/setup
to prepare the test environment - For more detailed database errors, run with debug flag:
DB_DEBUG=true bundle exec rspec