Class: Jekyll::L10n::TranslationResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/jekyll-l10n/utils/translation_resolver.rb

Overview

Resolves translations for text nodes with fallback to block-level translations.

TranslationResolver looks up translations for normalized text, first trying direct text node matches, then falling back to block-level translations when text is part of a larger block element with its own translation. This enables translating entire paragraphs as single units instead of word-by-word.

Key responsibilities:

  • Look up direct translation for text node

  • Fall back to block-level translation if available

  • Return appropriate translation or nil if none found

Examples:

translation = TranslationResolver.resolve(node, "Hello", translations)
# Returns translated text if available, nil otherwise

Class Method Summary collapse

Class Method Details

.contains_protected_elements?(node) ⇒ Boolean

Check if an element contains protected child elements that block translations.

Protected elements (script, style, pre) cannot have their surrounding text translated at the block level because:

  • script/style: Security and functionality reasons (executable content)

  • pre: Multi-line code blocks where translations break formatting

This is a shared utility used by both HtmlTranslator and TranslationResolver to ensure consistent protection of sensitive content across the codebase.

Examples:

doc = Nokogiri::HTML('<p><script>alert("xss")</script> text</p>')
para = doc.xpath('//p').first
TranslationResolver.contains_protected_elements?(para)
# => true
doc = Nokogiri::HTML('<p><code>inline</code> text</p>')
para = doc.xpath('//p').first
TranslationResolver.contains_protected_elements?(para)
# => false (code is allowed, only script/style/pre are protected)

Parameters:

  • node (Nokogiri::XML::Node)

    Element to check

Returns:

  • (Boolean)

    true if node contains protected elements, false otherwise



105
106
107
108
109
110
111
112
# File 'lib/jekyll-l10n/utils/translation_resolver.rb', line 105

def self.contains_protected_elements?(node)
  return false unless node.element?

  # Block block-level translation for script, style (security/functionality),
  # and pre (multi-line code blocks). These cannot be safely applied at block level.
  protected_elements = %w[script style pre]
  node.children.any? { |child| child.element? && protected_elements.include?(child.name) }
end

.content_element?(node) ⇒ Boolean

Returns:

  • (Boolean)


74
75
76
77
78
79
# File 'lib/jekyll-l10n/utils/translation_resolver.rb', line 74

def self.content_element?(node)
  return false unless node
  return false unless node.element?

  HtmlElements::CONTENT_ELEMENTS.include?(node.name)
end

.resolve(node, text, translations) ⇒ String?

Resolve a translation for a text node.

Attempts direct lookup of the normalized text in translations hash. If not found, checks if the text is part of a block element with a block-level translation and returns that.

Parameters:

  • node (Nokogiri::XML::Node)

    Text node being translated

  • text (String)

    Normalized text content

  • translations (Hash)

    Translation hash mapping text to translations

Returns:

  • (String, nil)

    Translated text if found, nil otherwise



34
35
36
37
38
39
40
41
# File 'lib/jekyll-l10n/utils/translation_resolver.rb', line 34

def self.resolve(node, text, translations)
  return nil unless node && text && translations

  direct_translation = translations[text]
  return direct_translation if direct_translation

  try_block_level_translation(node, text, translations)
end

.try_block_level_translation(node, text, translations) ⇒ String?

Attempt block-level translation for text nodes that are part of larger blocks.

Checks if a text node is part of a larger block element (like a paragraph) that has a complete translation. Only returns block translation if the text node alone doesn’t have a direct translation but the entire block does.

Security consideration: Returns nil if the block contains protected elements (script, style, pre tags) to prevent unsafe translation application.

Examples:

# For text "text" in "<p><script>...</script> text</p>"
# Returns nil (protected element present, prevents block translation)
TranslationResolver.try_block_level_translation(node, "text", translations)

Parameters:

  • node (Nokogiri::XML::Node)

    Text node being translated

  • text (String)

    Normalized text of the node

  • translations (Hash)

    Translation hash mapping text to translations

Returns:

  • (String, nil)

    Block-level translation if available, nil otherwise



61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/jekyll-l10n/utils/translation_resolver.rb', line 61

def self.try_block_level_translation(node, text, translations)
  return nil unless node.parent && content_element?(node.parent)

  # Don't attempt block-level translation if parent contains protected elements
  # (script, style, pre). These cannot be safely applied at block level.
  return nil if contains_protected_elements?(node.parent)

  block_text = BlockTextExtractor.extract(node.parent)
  return nil unless block_text && block_text != text

  translations[block_text]
end