Language independant translator using haml parser

Localization is usually necessary in every website. We wanted to add localization in an existing application with more than 100 pages! Now it would be crazyto go and change each view and add the unique keys in the .yml or .po file.

I google’d and I found some good reference here. Using this reference I made necessary changes  for parsing my .haml pages and enable for localization. Here is the solution for how to enable your view pages without changing view code and without adding I18n key for each plain text .

I am assuming you have I18n setup in your application! In my application I am using .po file for localization en.po (English) and hi.po (Hindi). For supporting .po file for localization you can use the i18n-translator-tools gem for details on how to use a .po file.

Use following steps for plain text translation

  • As per reference link  I added a rake task for collecting all plain text in my view pages and adding in .pot file
namespace :i18n_stuff do
  desc "Update pot/po files."
  task :create_po => :environment do
    require 'gettext/tools'
    require 'haml_parser'
    begin
      MY_APP_TEXT_DOMAIN = "mydomain"
      MY_APP_VERSION     = "1.1.0"
      GetText.update_pofiles(MY_APP_TEXT_DOMAIN,
          Dir.glob("{app/views}/**/*.{html.haml}"),
          MY_APP_VERSION,
          :po_root => 'config/locales')
    rescue Exception => e
       puts e
    end
  end
end
  • The haml_parser.rb file is used only in rake task is as below.
#This module used for crating the .pot file and
# this file conatins all views in pain text.

require 'rubygems'
require 'haml'
require 'gettext/tools'

class Haml::Engine
 # Override function that parses Haml tags
 # Inject gettext call for plain text action.
 def parse_tag(line)
 tag_name, attributes, attributes_hash, object_ref,
 nuke_outer_whitespace, nuke_inner_whitespace,
 action, value = super(line)

 @precompiled << "_(\"#{value}\")\n" unless action || value.empty?

 [tag_name, attributes, attributes_hash, object_ref,
 nuke_outer_whitespace, nuke_inner_whitespace, action, value]
 end

 def push_plain(text)
  @precompiled << "_(\"#{text}\")\n"
 end
end

The gettext.rb file is here:

# Haml gettext parser
module HamlParser
module_function

def target?(file)
  File.extname(file) == '.haml'
end

def parse(file, ary = [])
  bypass = ! File.basename(file, '.haml').match(
                                    /(vi|zh|zh_HK|id|th)$/).nil?
  puts "HamlParser:#{file}:bypass:#{bypass}"
  return ary if bypass

  haml = Haml::Engine.new(IO.readlines(file).join)
  result = nil
  begin
    code = haml.precompiled
    code = code.gsub("%","")
    code = code.gsub(/(.*#\{(_hamlout.adjust_tabs\(\d+\);\s*)?haml_temp)\s*=\s*(_\(['"].+['"]\))/) { |m| "haml_temp = #{$3}; #{$1}" }
    code = code.split(/$/)

    result = GetText::RubyParser.parse_lines(file, code, ary)
    # result = RubyGettextExtractor.parse_string(haml.precompiled, file, ary)
  rescue Exception => e
    puts "Error:#{file}"
    raise e
  end
  result
end

GetText::RGetText.add_parser(HamlParser)
  • Output of rake task. creating file config/locales/mostfit.pot
#: app/views/staff_members/new.html.haml:1
msgid "Create a new staff member"
msgstr ""

#: app/views/staff_members/_fields.html.haml:4 app/views/bookmarks/edit.html.haml:7
msgid "Name:"
msgstr ""

#: app/views/staff_members/_fields.html.haml:8
msgid "the screen name of the staff member"
msgstr ""

#: app/views/staff_members/_fields.html.haml:12
msgid "Mobile number:"
msgstr ""
  • Create en.po and hi.po using pot and add your translation text.
  • Now you are ready for your all plain text translation now you need to show you translation in browser. Using haml gettext module providing gettext translation for all Haml plain text calls. add following file in your config/init.rb
require "haml_gettext"

The haml_gettext.rb is here:

require 'i18n/gettext'

class Haml::Engine
  include I18n::Gettext::Helpers
  # Inject _ gettext into plain text and tag plain text calls
  # After getting all Haml plain text we are checking each word
  # conversion in .po file using method I18n::Gettext::Helpers
  # gettext()

  def push_plain(text)
    po_file_text = gettext(text, options = {})
    # this is method finding plain text in .po file and
    # return translator text
    text  = po_file_text if po_file_text
    super(text)
  end

  def parse_tag(line)
    tag_name, attributes, attributes_hash, object_ref,
    nuke_outer_whitespace, nuke_inner_whitespace,
    action, value = super(line)

    value = (value) unless action || value.empty?

    [tag_name, attributes, attributes_hash, object_ref,
    nuke_outer_whitespace, nuke_inner_whitespace, action, value]
  end
end

Now your application  ready for supporting localization without changing plain text in view. I shall post a github repos or a gist for a working edition for you shortly!

3 thoughts on “Language independant translator using haml parser

  1. With havin so much content do you ever run into any issues of plagorism or copyright infringement?
    My site has a lot of exclusive content I’ve either written myself or outsourced but it looks like a lot of it is popping it up all over the web without my authorization. Do you know any techniques to help stop content from being stolen? I’d definitely appreciate it.

    1. This solution is not for content scraping and parsing – its for localization.

      Having said that (out of context of this topic), if your data has a copyright, then I would recommend you search the web (via any search engine) and parse to find data that’s from your site – and can then take legal action. However be sure that you REALLY own the copyright for the data.

Leave a Reply to Gautam Rege Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.