GoTranslateYourself is our new, Ruby on Rails 3 engine, which lets your clients manage translations of their applications. As a developer, you should focus on writing code, and leave translation work for the others (who can do better than you!).

This engine is based on Rails 3 I18n key-value backend, and is using MongoDB store to write and read translations from on every request. This means that changes made by translators, are avialable to end user immediatelly. You’ll need MongoDB running on your machine, and Rails 3 to use it. Please note that this comes with minor performance degradation compared to using traditional YAML only translations backend, so if you are serving hundreds of thousands requests per minute — you’ll need to implement some sort of caching. However, GoTranslateYourself is not using any ORM/ODM, and is using full speed of MongoDB thanks to that.

To start using GoTranslateYourself, all you need to do is to include it in your Gemfile:

gem "mongo" 
gem "bson_ext" 
gem "go_translate_yourself"

And create initializer that will set up database connection for us. Let’s name it config/initializers/go_translate_yourself.rb:

conn = Mongo::Connection.new.db("myapp").collection("translations")
GoTranslateYourself.current_store =
GoTranslateYourself::MongoStore.new(conn)
GoTranslateYourself.locales = [:en]
I18n.backend = I18n::Backend::KeyValue.new
GoTranslateYourself.current_store

What this code does is that it creates connection to MongoDB database in first line of code. Then, we specify that we want to use MongoStore to save our translations using this database. We need to also specify what are destination locales of our application, and tell Rails I18n backend to use key-value backend, with our brand new MongoDB store. Easy.

GoTranslateYourself assumes, that during development you use dummy locales, named “dev”. All translations will fall back to values specified in dev.yml, unless translators translate them to destination languages. Let’s create sample dev.yml file:

dev:
 hello: "H311o, W0rld!" 

Start your Ruby on Rails application, and visit /site_translations to see single form, where you will be able to provide valid English translation.

Changes you make in this form, are visible to visitors immediatelly. If you modify dev.yml, however, you need to restart your Rails application to re-read it’s content.

You will also want to copy over contents from defaul Rails translations, you can find them in Github repository. I used the following in one of my applications:

dev:
  number:
    format:
      separator: "."
      delimiter: ","
      precision: 3
    currency:
      format:
        format: "%u%n"
        unit: "£"
        separator: "."
        delimiter: ","
        precision: 2
    percentage:
      format:
        delimiter: ""
    precision:
      format:
        delimiter: ""
    human:
      format:
        delimiter: ""
        precision: 1
      storage_units:
        format: "%n %u"
        units:
          byte:
            one: "Byte"
            other: "Bytes"
          kb: "KB"
          mb: "MB"
          gb: "GB"
          tb: "TB"
  datetime:
    distance_in_words:
      half_a_minute: "half a minute"
      less_than_x_seconds:
        one: "less than 1 second"
        other: "less than {{count}} seconds"
      x_seconds:
        one: "1 second"
        other: "{{count}} seconds"
      less_than_x_minutes:
        one: "less than a minute"
        other: "less than {{count}} minutes"
      x_minutes:
        one: "1 minute"
        other: "{{count}} minutes"
      about_x_hours:
        one: "about 1 hour"
        other: "about {{count}} hours"
      x_days:
        one: "1 day"
        other: "{{count}} days"
      about_x_months:
        one: "about 1 month"
        other: "about {{count}} months"
      x_months:
        one: "1 month"
        other: "{{count}} months"
      about_x_years:
        one: "about 1 year"
        other: "about {{count}} years"
      over_x_years:
        one: "over 1 year"
        other: "over {{count}} years"
      almost_x_years:
        one: "almost 1 year"
        other: "almost {{count}} years"
    prompts:
      year: "Year"
      month: "Month"
      day: "Day"
      hour: "Hour"
      minute: "Minute"
      second: "Seconds"
  support:
    select:
      prompt: "Please select"
  activerecord:
    errors:
      messages:
        inclusion: "is not included in the list"
        exclusion: "is reserved"
        invalid: "is invalid"
        confirmation: "doesn't match confirmation"
        accepted: "must be accepted"
        empty: "can't be empty"
        blank: "can't be blank"
        too_long: "is too long (maximum is {{count}} characters)"
        too_short: "is too short (minimum is {{count}} characters)"
        wrong_length: "is the wrong length (should be {{count}}
characters)"
        taken: "has already been taken"
        not_a_number: "is not a number"
        not_an_integer: "must be an integer"
        greater_than: "must be greater than {{count}}"
        greater_than_or_equal_to: "must be greater than or equal to
{{count}}"
        equal_to: "must be equal to {{count}}"
        less_than: "must be less than {{count}}"
        less_than_or_equal_to: "must be less than or equal to {{count}}"
        odd: "must be odd"
        even: "must be even"
        record_invalid: "Validation failed: {{errors}}"
  date:
    formats:
      default: "%d-%m-%Y"
      short: "%b %d"
      long: "%B %d, %Y"
  time:
    formats:
      default: "%a, %d %b %Y %H:%M:%S %z"
      short: "%d %b %H:%M"
      long: "%B %d, %Y %H:%M"
    am: "am"
    pm: "pm"
  support:
    array:
      words_connector: ", "
      two_words_connector: " and "
      last_word_connector: ", and "
    template:
      header: ""
      body: "There were problems with the following fields:"
    format: "{{attribute}} {{message}}"
    messages:
      not_found: "not found"
      already_confirmed: "was already confirmed"
      not_locked: "was not locked"
      inclusion: "is not included in the list"
      exclusion: "is reserved"
      invalid: "is invalid"
      confirmation: "doesn't match confirmation"
      accepted: "must be accepted"
      empty: "can't be empty"
      blank: "can't be blank"
      too_long: "is too long (maximum is {{count}} characters)"
      too_short: "is too short (minimum is {{count}} characters)"
      wrong_length: "is the wrong length (should be {{count}}
characters)"
      not_a_number: "is not a number"
      greater_than: "must be greater than {{count}}"
      greater_than_or_equal_to: "must be greater than or equal to
{{count}}"
      equal_to: "must be equal to {{count}}"
      less_than: "must be less than {{count}}"
      less_than_or_equal_to: "must be less than or equal to {{count}}"
      odd: "must be odd"
      even: "must be even"

You can provide authentication, by specifying proc that will get executed by engine to authorize user access in your initializer file:

# Example authentication procedure to be used with Devise and Admin
model
GoTranslateYourself.auth_handler = proc {
  authenticate_admin!
}

You can also specify which layout to use:

GoTranslateYourself.layout_name = "admin"

Find out more, contribute and fork it on the Github)

Post by Hubert Łępicki

Hubert is partner at AmberBit. Rails, Elixir and functional programming are his areas of expertise.