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) cannot have their surrounding text translated at the block level for security and functionality reasons. <pre> is not protected here — HtmlTextUtils.remove_code_blocks strips it before extraction so code content never reaches PO msgids, and HtmlTranslator preserves <pre> verbatim across translation injection.

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 are protected)

Parameters:

  • node (Nokogiri::XML::Node)

    Element to check

Returns:

  • (Boolean)

    true if node contains protected elements, false otherwise



113
114
115
116
117
118
119
# File 'lib/jekyll-l10n/utils/translation_resolver.rb', line 113

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

  # Block block-level translation for script and style (security/functionality).
  protected_elements = %w[script style]
  node.children.any? { |child| child.element? && protected_elements.include?(child.name) }
end

.content_element?(node) ⇒ Boolean

Returns:

  • (Boolean)


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

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) 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
# File 'lib/jekyll-l10n/utils/translation_resolver.rb', line 61

def self.try_block_level_translation(node, text, translations)
  ancestor = find_content_element_ancestor(node)
  return nil unless ancestor

  return nil if contains_protected_elements?(ancestor)

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

  translations[block_text]
end