Mind Dump, Tech And Life Blog
written by Ivan Alenko
published under license Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)copy! share!
posted at 08. Feb '18

Howto: Extending FastGettext with custom translation repository

Scenario: I want to have translations for one language in more files than one file. Current FastGettext::TranslationRepository::Yaml can't handle this. So we create our own Yamlr module.

Readme.md tells something about custom modules, but one must be skilled to understand how to use this knowledge. This howto aims to fill this gap.

1. Modify load path

I know how to do this in Rails.

# add to config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
~~~`

This won't load all files from `/lib` directory, but tells Rails where to search when you call *require* function.

## 2. Add module to new load path

Create `lib/fast_gettext/translation_repository` in Rails application. Now we move our `yaml_recursive.rb` here. Yes, only one file here.

The source is modified version of the original *Yaml* module:

~~~`ruby
require 'fast_gettext/translation_repository/base'
require 'yaml'
require 'active_support/core_ext/hash/deep_merge.rb'

module FastGettext
  module TranslationRepository
    # Responsibility:
    #  - find and store yaml files
    #  - provide access to translations in yaml files
    class Yamlr < Base
      def initialize(name,options={})
        super
        reload
      end

      def available_locales
        @files.keys
      end

      def plural(*keys)
        ['one', 'other', 'plural2', 'plural3'].map do |name|
          self[yaml_dot_notation(keys.first, name)]
        end
      end

      def pluralisation_rule
        self['pluralisation_rule'] ? lambda{|n| eval(self['pluralisation_rule']) } : nil
      end

      def reload
        find_and_store_files(@options)
        super
      end

      protected

      MAX_FIND_DEPTH = 10

      def find_and_store_files(options)
        @files = {}
        path = options[:path] || 'config/locales'
        Dir["#{path}/**/*.yml"].each do |yaml_file|
					@files.deep_merge!(load_yaml(yaml_file))
        end
				@files
      end

      def current_translations
        @files[FastGettext.locale] || super
      end

      # Given a yaml file return a hash of key -> translation
      def load_yaml(file)
        yaml = YAML.load_file(file)
				yaml.keys.reduce({}) do |processed, locale|
					processed[locale] ||= {}
					processed[locale].merge!(yaml_hash_to_dot_notation(yaml[locale]))
					processed
				end
      end

      def yaml_hash_to_dot_notation(yaml_hash)
        add_yaml_key({}, nil, yaml_hash)
      end

      def add_yaml_key(result, prefix, hash)
        hash.each_pair do |key, value|
          if value.kind_of?(Hash)
            add_yaml_key(result, yaml_dot_notation(prefix, key), value)
          else
            result[yaml_dot_notation(prefix, key)] = value
          end
        end
        result
      end

      def yaml_dot_notation(a,b)
        a ? "#{a}.#{b}" : b
      end
    end
  end
end
~~~`

Now we can read *I18n* translations too. To use these translations with *fast_gettext* you need to override calls to *I18n*. (TODO how?)

## 3. Enjoy

~~~`ruby
rails c

...
require 'fast_gettext/translation_repository/yaml_recursive'
#=> true
FastGettext.add_text_domain('lala', path: 'config/locales', type: :yamlr)
#=> { ...our translations ... }
~~~`

## 4. Debug

From the initializer:

~~~`ruby
puts FastGettext.add_text_domain('lala', path: 'config/locales', type: :yamlr).inspect
~~~`

Add Comment