smugglys/translatomatic

View on GitHub
lib/translatomatic/translation/munging.rb

Summary

Maintainability
A
25 mins
Test Coverage
module Translatomatic
  module Translation
    # Translation result mungification code
    module Munging
      private

      # @private
      NoTranslateTag = Struct.new(:leading_space, :trailing_space)

      def munge_translation_results(translations)
        if use_notranslate?
          translations.collect { |tr| remove_notranslate(tr) }
        else
          translations.select { |tr| restore_preserved_text(tr) }
        end
      end

      def use_notranslate?
        @provider.class.supports_no_translate_html?
      end

      # Restore parts of text that should be preserved in the translation.
      # Used for providers that don't support translate="no" html5 attribute.
      # This works when the translator preserves parts of the string that
      # match the preserve_regex, e.g. for variables like '%{name}' if the
      # translation matches /%{.*}/ then the original text can be restored.
      # @return [Boolean] True if no errors were encountered
      def restore_preserved_text(tr)
        preserve_regex = tr.original.preserve_regex
        return true unless preserve_regex

        # find parts to preserve in the original string
        list1 = tr.original.substrings(preserve_regex)
        # find corresponding parts in the translated string
        list2 = tr.result.substrings(preserve_regex)

        return false unless list1.length == list2.length

        # we can restore text. sort by largest offset first.
        conversions = list1.zip(list2).collect.sort_by { |i| -i[0].offset }
        conversions.each do |v1, v2|
          tr.result[v2.offset, v2.length] = v1.value
        end
        true
      end

      # @param texts [Array<Text>] Texts to modify
      # @return [Array<Text>] Texts with sections to preserve wrapped in a
      #   notranslate directive.
      def wrap_notranslate(texts)
        return texts unless use_notranslate? && texts.any?(&:preserve_regex)
        texts.collect do |text|
          if text.preserve_regex
            text.gsub(text.preserve_regex) do |i|
              '<span translate="no">' + i[0] + '</span>'
            end
          else
            text
          end
        end
      end

      # Update the translation to remove notranslate directives.
      # Fixes spacing to match the original.
      # @param tr [Result] Translation result
      # @return [Result] Updated translation result
      def remove_notranslate(tr)
        return tr unless tr.original.preserve_regex
        original_spacing = find_notranslate_spacing(tr.original)
        result_spacing = find_notranslate_spacing(tr.result)
        if original_spacing.length != result_spacing.length
          # the number of notranslate directives in the result doesn't
          # match the number in the original. this could mean an invalid
          # translation?
          log.debug("possible invalid translation: #{tr.description}")
          original_spacing = nil # don't attempt to fix spacing.
        end
        original = remove_notranslate_text(tr.original)
        result = remove_notranslate_text(tr.result, original_spacing)
        Result.new(
          original, result, tr.provider, from_database: tr.from_database
        )
      end

      REGEX1 = %r{<span translate="no">\s*(.*?)\s*</span>}
      REGEX2 = %r{(\s*)<span translate="no">\s*(.*?)\s*</span>(\s*)}

      # scan text for no translate tags, record leading and trailing space.
      def find_notranslate_spacing(text)
        text.scan(REGEX2).collect { |i| [i[0], i[2]] }
      end

      def remove_notranslate_text(text, fix_spacing = nil)
        if fix_spacing
          num = 0 # match number
          text.gsub(REGEX2) do |i|
            notranslate_replacement(i[2], fix_spacing, num += 1)
          end
        else
          text.gsub(REGEX1) { |i| i[1] }
        end
      end

      def notranslate_replacement(string, fix_spacing, num)
        spacing = fix_spacing[num - 1]
        leading = spacing[0]
        trailing = spacing[1]
        leading + string + trailing
      end
    end
  end
end