Skip to content
This repository


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Datamapper support for localization of content in multilanguage applications

branch: master

Fetching latest commit…


Cannot retrieve the latest commit at this time

Octocat-spinner-32 lib
Octocat-spinner-32 spec
Octocat-spinner-32 tasks
Octocat-spinner-32 .document
Octocat-spinner-32 .gitignore
Octocat-spinner-32 CHANGELOG
Octocat-spinner-32 Gemfile
Octocat-spinner-32 LICENSE
Octocat-spinner-32 README.textile
Octocat-spinner-32 Rakefile
Octocat-spinner-32 TODO
Octocat-spinner-32 dm-is-localizable.gemspec


Datamapper support for localization of (user entered) content in multilanguage applications


  • one xxx_translations table for every translatable resource
  • xxx_translations belongs_to the resource to translate
  • xxx_translations belongs_to a locale
  • properties to be translated are defined in xxx_translations


  • Proper normalization and referential integrity
  • Easy to add a new language (add row to xxx_translations)
  • Easy to query
  • Columns keep their names

Disadvantages (not really if you think about it)

  • One extra table for every resource that needs translations

Example definition of a localizable model

The plugin comes with a Locale model that already got required for you. This means that the underlying storage will be created automatically when you run auto_migrate! or auto_upgrade!.

class Item

  include DataMapper::Resource

  property :id, Serial

  translatable do
    property :name, String
    property :desc, String


The above Item model will define and thus be able to DataMapper.auto_migrate! the ItemTranslation model. The naming convention used here is "#{}Translation".

Preliminary support for changing this is available by using the :model option like so (note that this isn’t specced yet).

DataMapper::Model.translatable, :model => 'ItemLocalization' do
  # ...

Furthermore, the above Item will automatically have the following instance methods defined.


# and handy aliases for the above


These are generated by dm-accepts_nested_attributes and allow for easy manipulation of the translatable properties from say forms in a web application. For more information on working with nested attributes, have a look at the documentation at the README for dm-accepts_nested_attributes

Of course you can turn this behavior off by specifying the translatable, :accept_nested_attributes => false do .. end

The resulting model you get when calling Item.translatable { ... } looks like this:

class ItemTranslation

  include DataMapper::Resource

  property :id,         Serial

  property :item_id,    Integer, :required => true, :unique_index => :unique_locales
  property :locale_tag, String,  :required => true, :unique_index => :unique_locales

  property :name,       String
  property :desc,       String

  validates_is_unique :locale_tag, :scope => :item_id

  belongs_to :item
  belongs_to :locale


Furthermore, the following API gets defined on the Item class:

class Item

  include DataMapper::Resource

  property :id, Serial

  translatable do
    property :name,        String
    property :description, String

  # -------------------------
  #   added by .translatable
  # -------------------------

  has n, :item_translations
  has n, :locales, :through => :item_translations

  # and a handy alias
  alias :translations :item_translations

  # method to access the i18n proxy for this model
  def self.i18n

  # the proxy instance to delegate api calls to
  def i18n
    @i18n ||=

  # translates the :name property to the given locale
  def name(locale_tag = DataMapper::I18n.default_locale_tag)
    i18n.translate(:name, locale_tag)

  # translates the :desc property to the given locale
  def desc(locale_tag = DataMapper::I18n.default_locale_tag)
    i18n.translate(:desc, locale_tag)

  # ----------------------------------------
  #   added by dm-accepts_nested_attributes
  # ----------------------------------------

  def item_translations_attributes
    # ...

  def item_translations_attributes=(attributes_or_attributes_collection)
    # ...

  # and handy aliases for the above

  alias :translations_attributes  :item_translations_attributes
  alias :translations_attributes= :item_translations_attributes

# ---------------------------------------------
#   methods accessible via the Item.i18n proxy
# ---------------------------------------------

# helper method to get at ItemTranslation

# list all available locales for the translatable model

# returns all translatable properties of this resource

# ---------------------------------------------
#   methods accessible via the Item#i18n proxy
# ---------------------------------------------

# list all available locales for this instance

# translates the given attribute to the locale identified by the given locale_code
item.i18n.translate(attribute, locale_tag)

Inspired by (thx guys!)


Copyright © 2009 Martin Gamsjaeger (snusnu). See LICENSE for details.

Something went wrong with that request. Please try again.