Dorian
Dorian

Reputation: 9055

How to translate a website in all languages?

I'm trying to translate my website in all languages supported by Google Translate.

I'm using Ruby on Rails 6, and want to do it as a translation backend, but this is not specific to Ruby or Ruby on Rails.

When I had to support 6 languages I would correct the mistakes myself but I can't

I tried different things but my latest strategy has been storing everything in the database:

class ActiveRecordBackend
  include I18n::Backend::Base
  include I18n::Backend::Transliterator

  SEPARATE_INTERPOLATIONS = /(?<interpolation>%{[^}]+})|(?<text>[^%]+)/
  NETWORK_ERRORS = [SocketError, Errno::EHOSTUNREACH].freeze
  LOCALES_PATH = Rails.root.join("lib/data/locales.yml")
  LOCALES = YAML.safe_load(LOCALES_PATH.read).map(&:to_struct).sort_by(&:name)
  LOCALE_NAMES = LOCALES.map(&:locale).map(&:to_sym)

  def available_locales
    LOCALE_NAMES
  end

  def reload!
    @translations = nil
    self
  end

  def initialized?
    [email protected]?
  end

  def init_translations
    @translations = Translation.to_hash
  end

  def translations(do_init: false)
    init_translations if do_init || !initialized?
    @translations ||= {}
  end

  private

  def lookup(locale, key, _scope = [], _options = {})
    Translation.find_by(locale: locale, key: key)&.value ||
      store_translation(locale: locale, key: key)
  end

  def store_translation(locale:, key:)
    default = Translation.find_by(locale: I18n.default_locale, key: key)
    return unless default
    translated_value =
      easy_translate(default.value, from: I18n.default_locale, to: locale)
    return unless translated_value
    Translation.find_or_create_by(
      locale: locale,
      key: key,
      value: translated_value
    )
    translated_value
  end

  def easy_translate(original, from:, to:)
    original
      .scan(SEPARATE_INTERPOLATIONS)
      .map do |interpolation, text|
        next interpolation if interpolation
        spaces_before = text.scan(/\A */).first
        spaces_after = text.scan(/ *\z/).first
        translated_text =
          EasyTranslate.translate(text, from: from, to: to).strip
        "#{spaces_before}#{translated_text}#{spaces_after}"
      end
      .join
  rescue *NETWORK_ERRORS, EasyTranslate::EasyTranslateException
    nil
  end
end

But I get things like

"<b>7976membri attivi tra cui1in linea<br>562attività con5945partecipazioni"

for italian

instead of:

"<b>7976</b> membres actifs dont <b>1</b> en ligne <br><b>562</b> activités avec <b>5945</b> participations"

for french

And I also don't handle returning a group of translations like t(".js").

How would you do it?

Upvotes: 0

Views: 540

Answers (3)

Dorian
Dorian

Reputation: 9055

I found a solution:

  def easy_translate(original, from:, to:)
    interpolations_in_original = original.scan(INTERPOLATION)
    spaces_before = original.scan(/\A */).first
    spaces_after = original.scan(/ *\z/).first
    translated_text = EasyTranslate.translate(original, from: from, to: to).strip
    translated_text = translated_text.gsub("% {", "%{")
    bad_interpolations = translated_text.scan(INTERPOLATION)
    interpolations_in_original.size.times do |index|
      translated_text.gsub!(bad_interpolations[index], interpolations_in_original[index])
    end
    "#{spaces_before}#{translated_text}#{spaces_after}"
  rescue *NETWORK_ERRORS, EasyTranslate::EasyTranslateException
    nil
  end

Can be improved obviously like actually replacing each interpolation by the corresponding correct one and keeping capitalization

Upvotes: 0

Tom Lord
Tom Lord

Reputation: 28285

How would you do it?

I wouldn't do it.

If your website only natively supports a few languages (e.g. English) and a user wants to view it in an unsupported language (e.g. Italian), then let the user apply Google Translation themselves.

There's a very popular plugin to do this. But, like you found already, it won't always give perfect results: Sometimes it can mess up your page layout, in addition to just giving sub-optimal translations due to mis-interpreted context.

If you discover a magic way to accurately apply website translations in the backend to all possible languages and contexts, without breaking the UI, then congratulations -- you'll soon be incredibly wealthy.

Upvotes: 1

TTD
TTD

Reputation: 310

Agree with @GiacomoCatenazzi, it looks very unprofessional to have obvious spelling mistakes. If you have to translate the page, I recommend you use I18n and do it manually.

If you feel like you have to use GT, I would do something like this:

  1. Create the I18n files for each language
  2. Only manually populate the english one
  3. Create a class which reads in the english version of the I18n file to a hash.
  4. Loop through all the files you want to populate, if the key does not exist you should use the GT api to translate and populate the files where the key does not exist.
  5. Create a cron job and run the class everyday.

You can improve the amount of requests in step 4 as much as you want, with some dedication it should be possible to limit the requests to the amount of languages you support.

Upvotes: 0

Related Questions