jekyll-l10n 1.7.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +44 -0
  3. data/lib/jekyll-l10n/configuration/schema.rb +308 -0
  4. data/lib/jekyll-l10n/configuration.rb +165 -0
  5. data/lib/jekyll-l10n/constants.rb +45 -10
  6. data/lib/jekyll-l10n/errors.rb +7 -0
  7. data/lib/jekyll-l10n/extraction/compendium_merger.rb +9 -11
  8. data/lib/jekyll-l10n/extraction/compendium_translator.rb +27 -11
  9. data/lib/jekyll-l10n/extraction/config_loader.rb +28 -58
  10. data/lib/jekyll-l10n/extraction/coverage_report.rb +129 -0
  11. data/lib/jekyll-l10n/extraction/dom_text_extractor.rb +2 -31
  12. data/lib/jekyll-l10n/extraction/extractor.rb +72 -65
  13. data/lib/jekyll-l10n/extraction/html_string_extractor.rb +24 -10
  14. data/lib/jekyll-l10n/extraction/logger.rb +6 -5
  15. data/lib/jekyll-l10n/extraction/result_saver.rb +5 -10
  16. data/lib/jekyll-l10n/instrumentation.rb +172 -139
  17. data/lib/jekyll-l10n/jekyll/generator.rb +47 -61
  18. data/lib/jekyll-l10n/jekyll/localized_page.rb +27 -49
  19. data/lib/jekyll-l10n/jekyll/post_write_html_reprocessor.rb +53 -48
  20. data/lib/jekyll-l10n/jekyll/post_write_processor.rb +2 -1
  21. data/lib/jekyll-l10n/jekyll/regeneration_checker.rb +70 -50
  22. data/lib/jekyll-l10n/jekyll/url_filter.rb +13 -13
  23. data/lib/jekyll-l10n/po_file/entry_parser.rb +155 -0
  24. data/lib/jekyll-l10n/po_file/fuzzy_matcher.rb +67 -53
  25. data/lib/jekyll-l10n/po_file/loader.rb +20 -11
  26. data/lib/jekyll-l10n/po_file/manager.rb +32 -17
  27. data/lib/jekyll-l10n/po_file/merger.rb +2 -1
  28. data/lib/jekyll-l10n/po_file/path_builder.rb +11 -1
  29. data/lib/jekyll-l10n/po_file/reader.rb +40 -508
  30. data/lib/jekyll-l10n/po_file/writer.rb +31 -16
  31. data/lib/jekyll-l10n/translation/block_text_extractor.rb +3 -7
  32. data/lib/jekyll-l10n/translation/html_translator.rb +136 -157
  33. data/lib/jekyll-l10n/translation/libre_translate_languages.rb +73 -0
  34. data/lib/jekyll-l10n/translation/libre_translator.rb +134 -78
  35. data/lib/jekyll-l10n/translation/page_translation_loader.rb +13 -29
  36. data/lib/jekyll-l10n/translation/request_slots.rb +69 -0
  37. data/lib/jekyll-l10n/translation/translation_progress.rb +77 -0
  38. data/lib/jekyll-l10n/translation/translator.rb +49 -78
  39. data/lib/jekyll-l10n/utils/baseurl_normalizer.rb +108 -0
  40. data/lib/jekyll-l10n/utils/debug_logger.rb +4 -51
  41. data/lib/jekyll-l10n/utils/error_handler.rb +9 -24
  42. data/lib/jekyll-l10n/utils/external_link_icon_preserver.rb +5 -62
  43. data/lib/jekyll-l10n/utils/html_parser.rb +24 -31
  44. data/lib/jekyll-l10n/utils/html_text_utils.rb +146 -91
  45. data/lib/jekyll-l10n/utils/logger_formatter.rb +6 -4
  46. data/lib/jekyll-l10n/utils/msgstr_validator.rb +74 -0
  47. data/lib/jekyll-l10n/utils/page_locales_config.rb +165 -223
  48. data/lib/jekyll-l10n/utils/po_entry_converter.rb +0 -43
  49. data/lib/jekyll-l10n/utils/site_config_accessor.rb +82 -42
  50. data/lib/jekyll-l10n/utils/translation_resolver.rb +56 -6
  51. data/lib/jekyll-l10n/utils/url_path_builder.rb +5 -27
  52. data/lib/jekyll-l10n/utils/url_transformer.rb +5 -11
  53. data/lib/jekyll-l10n/version.rb +1 -1
  54. data/lib/jekyll-l10n.rb +67 -176
  55. metadata +12 -3
@@ -4,7 +4,6 @@ require 'fileutils'
4
4
  require_relative '../po_file/manager'
5
5
  require_relative '../po_file/fuzzy_matcher'
6
6
  require_relative '../utils/page_locales_config'
7
- require_relative '../utils/site_config_accessor'
8
7
 
9
8
  module Jekyll
10
9
  module L10n
@@ -35,8 +34,6 @@ module Jekyll
35
34
  # @param site [Jekyll::Site] Jekyll site object
36
35
  def initialize(site)
37
36
  @site = site
38
- with_locales_data = SiteConfigAccessor.extract_locales_data(@site)
39
- @site_config = PageLocalesConfig.new({ 'with_locales_data' => with_locales_data })
40
37
  end
41
38
 
42
39
  # Merge page-specific PO files into compendia for all locales.
@@ -49,8 +46,11 @@ module Jekyll
49
46
  # @param config [PageLocalesConfig] Localization configuration with locales list
50
47
  # @return [void]
51
48
  def merge_compendia(po_manager, config)
49
+ # Fuzzy scores depend only on the two English strings, and every
50
+ # locale compares the same new strings, so they are scored once per run.
51
+ @fuzzy_scores = {}
52
52
  config.locales.each do |locale|
53
- process_locale(locale, po_manager, config)
53
+ process_locale(locale, po_manager)
54
54
  end
55
55
  end
56
56
 
@@ -58,9 +58,8 @@ module Jekyll
58
58
 
59
59
  # Process a single locale: merge existing compendium with new page translations
60
60
  # Skips writing when translations are unchanged to avoid unnecessary file rewrites
61
- def process_locale(locale, po_manager, config)
62
- compendium_path = File.join(@site.source, config.locales_dir, "#{locale}.po")
63
- existing_compendium = load_existing_compendium(compendium_path)
61
+ def process_locale(locale, po_manager)
62
+ existing_compendium = load_existing_compendium(po_manager.compendium_path(locale))
64
63
  merged = po_manager.merge_po_files(locale)
65
64
 
66
65
  combined = build_combined_hash(existing_compendium)
@@ -70,7 +69,7 @@ module Jekyll
70
69
  combined_entries = format_compendium_entries(combined)
71
70
  po_manager.save_compendium(locale, combined_entries)
72
71
  end
73
- cleanup_locale_directory(locale, config)
72
+ cleanup_locale_directory(po_manager.locale_dir(locale))
74
73
  end
75
74
 
76
75
  # Check if compendium content is unchanged (same msgids and msgstrs)
@@ -146,7 +145,7 @@ module Jekyll
146
145
  end
147
146
 
148
147
  def resolve_new_entry(combined, msgid, entry, fuzzy_candidates, matched_old_msgids)
149
- match = PoFuzzyMatcher.find_match(msgid, fuzzy_candidates)
148
+ match = PoFuzzyMatcher.find_match(msgid, fuzzy_candidates, scores: @fuzzy_scores)
150
149
  if match
151
150
  combined[msgid] = create_fuzzy_entry(entry, match)
152
151
  matched_old_msgids << match[:msgid]
@@ -195,8 +194,7 @@ module Jekyll
195
194
 
196
195
  # IMPORTANT: Remove locale-specific directory after compendium merge
197
196
  # This cleans up page-specific PO files that have been merged into the compendium
198
- def cleanup_locale_directory(locale, config)
199
- locale_dir = File.join(@site.source, config.locales_dir, locale)
197
+ def cleanup_locale_directory(locale_dir)
200
198
  FileUtils.rm_rf(locale_dir) if File.directory?(locale_dir)
201
199
  end
202
200
 
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../constants'
4
+ require_relative '../instrumentation'
3
5
  require_relative '../po_file/manager'
4
6
  require_relative '../utils/page_locales_config'
5
7
  require_relative '../utils/po_entry_converter'
6
- require_relative '../utils/site_config_accessor'
7
8
  require_relative '../utils/logger_formatter'
8
9
  require_relative '../translation/libre_translator'
9
10
 
@@ -33,18 +34,19 @@ module Jekyll
33
34
  # @param site [Jekyll::Site] Jekyll site object
34
35
  def initialize(site)
35
36
  @site = site
36
- with_locales_data = SiteConfigAccessor.extract_locales_data(@site)
37
- @site_config = PageLocalesConfig.new({ 'with_locales_data' => with_locales_data })
38
37
  end
39
38
 
40
39
  # Translate compendia for all configured locales.
41
40
  #
42
41
  # Checks if LibreTranslate is enabled. If so, for each configured locale,
43
42
  # loads the compendium PO file, identifies untranslated entries, sends them
44
- # to LibreTranslate API, and saves the updated file.
43
+ # to LibreTranslate API, and saves the updated file. Locales are
44
+ # translated at once; their requests share libretranslate_concurrency.
45
45
  #
46
46
  # @param config [PageLocalesConfig] Localization configuration
47
47
  # @return [void]
48
+ # @raise [LibreTranslator::TranslationError] If a locale failed, once
49
+ # every locale has finished; the locales that finished are saved
48
50
  def translate_compendia(config)
49
51
  po_manager = PoFileManager.new(@site, config.locales_dir)
50
52
  translate_compendia_for_locale(po_manager, config)
@@ -59,15 +61,29 @@ module Jekyll
59
61
  log_translation_start(config)
60
62
  translator = LibreTranslator.new(config)
61
63
 
62
- config.locales.each do |locale|
63
- process_single_locale(locale, config, translator, po_manager)
64
+ threads = config.locales.map do |locale|
65
+ Instrumentation.traced_thread do
66
+ Thread.current.report_on_exception = false
67
+ process_single_locale(locale, translator, po_manager)
68
+ end
64
69
  end
70
+ join_locales(threads)
65
71
 
66
72
  log_translation_complete(config)
67
73
  end
68
74
 
69
- def process_single_locale(locale, config, translator, po_manager)
70
- compendium_path = File.join(@site.source, config.locales_dir, "#{locale}.po")
75
+ def join_locales(threads)
76
+ first_error = nil
77
+ threads.each do |thread|
78
+ thread.join
79
+ rescue StandardError => e
80
+ first_error ||= e
81
+ end
82
+ raise first_error if first_error
83
+ end
84
+
85
+ def process_single_locale(locale, translator, po_manager)
86
+ compendium_path = po_manager.compendium_path(locale)
71
87
  LoggerFormatter.debug_if_enabled('CompendiumTranslator',
72
88
  "Processing compendium file: #{compendium_path}")
73
89
  return unless File.exist?(compendium_path)
@@ -119,17 +135,17 @@ module Jekyll
119
135
 
120
136
  def log_translation_start(config)
121
137
  locales = config.locales.join(', ')
122
- Jekyll.logger.info 'Localization',
138
+ Jekyll.logger.info Constants::LOG_PREFIX,
123
139
  "Starting LibreTranslate translation for locales: #{locales}"
124
140
  end
125
141
 
126
142
  def log_translation_complete(config)
127
143
  locales = config.locales.join(', ')
128
- Jekyll.logger.info 'Localization',
144
+ Jekyll.logger.info Constants::LOG_PREFIX,
129
145
  "LibreTranslate translation complete for locales: #{locales}"
130
146
  end
131
147
 
132
- private :translate_compendia_for_locale, :process_single_locale,
148
+ private :translate_compendia_for_locale, :join_locales, :process_single_locale,
133
149
  :log_compendia_enabled_check, :log_compendium_stats,
134
150
  :log_translation_complete_for_locale, :log_compendium_saved,
135
151
  :count_empty_entries, :log_translation_start, :log_translation_complete
@@ -1,24 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../configuration'
3
4
  require_relative '../utils/page_locales_config'
4
5
  require_relative '../utils/locale_url_formatter'
5
6
  require_relative '../utils/site_config_accessor'
6
7
 
7
8
  module Jekyll
8
9
  module L10n
9
- # Loads and validates extraction configuration for files during build.
10
+ # Finds the settings for a built HTML file during extraction.
10
11
  #
11
- # ExtractionConfigLoader finds extraction configuration for generated HTML
12
- # files by matching them against Jekyll site pages. It validates whether
13
- # extraction is enabled and configured for a file, loads page-specific
14
- # settings, and identifies files to skip (localized page variants).
15
- #
16
- # Key responsibilities:
17
- # * Match generated HTML files to Jekyll site pages
18
- # * Validate that extraction is enabled for a file
19
- # * Load page-specific extraction configuration
20
- # * Identify localized page variants to skip
21
- # * Extract CSS selectors for element exclusion
12
+ # Extraction works on output files, but settings belong to pages. This
13
+ # class matches an output file back to its source page and returns that
14
+ # page's merged settings. It also tells the Extractor which files to skip:
15
+ # the localized copies under locale folders such as `es/`.
22
16
  #
23
17
  # @example
24
18
  # loader = ExtractionConfigLoader.new(site, '_site')
@@ -26,22 +20,16 @@ module Jekyll
26
20
  # config = loader.load_page_config(file_path)
27
21
  # end
28
22
  class ExtractionConfigLoader
29
- # Initialize a new ExtractionConfigLoader.
30
- #
31
- # @param site [Jekyll::Site] Jekyll site object
32
- # @param dest [String] Destination build directory
23
+ # @param site [Jekyll::Site] The Jekyll site
24
+ # @param dest [String] The site destination directory
33
25
  def initialize(site, dest)
34
26
  @site = site
35
27
  @dest = dest
36
28
  end
37
29
 
38
- # Check if a file is valid for extraction.
39
- #
40
- # Matches the file to a Jekyll page and verifies extraction is enabled
41
- # for that page in the configuration.
42
- #
43
- # @param file_path [String] Path to file to check
44
- # @return [Boolean] True if file matches a page with extraction enabled
30
+ # @param file_path [String] Path of a built HTML file
31
+ # @return [Boolean] true if the file comes from a localized page with at
32
+ # least one locale and `extract_on_build` not set to false
45
33
  def valid_for_extraction?(file_path)
46
34
  page_config = find_page_config_for_file(file_path)
47
35
  return false unless page_config
@@ -50,50 +38,37 @@ module Jekyll
50
38
  config.enabled? && config.extract_on_build?
51
39
  end
52
40
 
53
- # Load extraction configuration for a file.
54
- #
55
- # Finds the Jekyll page matching this file and returns its localization
56
- # configuration wrapped in PageLocalesConfig.
57
- #
58
- # @param file_path [String] Path to file
59
- # @return [PageLocalesConfig] Localization configuration for the page
41
+ # @param file_path [String] Path of a built HTML file
42
+ # @return [PageLocalesConfig] Settings of the page that produced the file
60
43
  def load_page_config(file_path)
61
44
  page_config = find_page_config_for_file(file_path)
62
45
  PageLocalesConfig.new(page_config)
63
46
  end
64
47
 
65
- # Find page configuration for a file.
66
- #
67
- # Matches a generated file path to a Jekyll page with extraction enabled.
68
- # Returns the page's front matter data, or nil if no match found.
48
+ # Finds the localized page whose output is this file and returns its data
49
+ # with merged `with_locales_data` (see SiteConfigAccessor.effective_page_data).
69
50
  #
70
- # @param file_path [String] Path to generated file
71
- # @return [Hash, nil] Page front matter data if found, nil otherwise
51
+ # @param file_path [String] Path of a built HTML file
52
+ # @return [Hash, nil] The page data, or nil when no localized page matches
72
53
  def find_page_config_for_file(file_path)
73
54
  @site.pages.each do |page|
74
- next unless page.data['with_locales'] == true
55
+ next unless Configuration.localizable?(page)
75
56
 
76
57
  page_output = page.output_ext ? page.destination('') : page.destination('/')
77
58
  next unless file_path.end_with?(page_output.sub(%r{/$}, '/index.html'))
78
59
 
79
- return page.data
60
+ return SiteConfigAccessor.effective_page_data(page)
80
61
  end
81
62
 
82
63
  nil
83
64
  end
84
65
 
85
- # Check if a file is a localized page variant that should be skipped.
86
- #
87
- # Localized pages are generated copies in locale subdirectories that
88
- # shouldn't be re-extracted (extraction happens on original pages only).
89
- # Since commit 3d2d4cd, country-subtag locales (e.g. es_ES) are written
90
- # to BCP 47 hyphenated, lowercased URL directories (e.g. es-es/). Each
91
- # configured locale is normalized through LocaleUrlFormatter.to_url_segment
92
- # before comparison so both language-only (es) and country-subtag
93
- # (es_ES → es-es) locales are correctly recognized.
66
+ # Localized copies are built from the English pages, so we extract only
67
+ # from the English files. Locale folders use the URL form of the locale
68
+ # code (es_ES becomes es-es), so compare against that form.
94
69
  #
95
- # @param file_path [String] Path to file to check
96
- # @return [Boolean] True if file is in a locale subdirectory
70
+ # @param file_path [String] Path of a built HTML file
71
+ # @return [Boolean] true if the file is inside a locale folder
97
72
  def skip_localized_page?(file_path)
98
73
  relative_path = file_path.sub(@dest, '')
99
74
 
@@ -101,16 +76,11 @@ module Jekyll
101
76
  all_locales.any? { |locale| relative_path.start_with?("/#{LocaleUrlFormatter.to_url_segment(locale)}/") }
102
77
  end
103
78
 
104
- # Extract CSS selectors for element exclusion from configuration.
105
- #
106
- # Returns CSS selectors of elements to exclude from extraction (e.g.,
107
- # script, style, code blocks). Defaults to sensible defaults if not configured.
108
- #
109
- # @param config [Hash] Page front matter data
110
- # @return [Array<String>] CSS selectors for excluded elements
79
+ # @param config [PageLocalesConfig] The page's settings
80
+ # @return [Array<String>] CSS selectors of elements to leave out of
81
+ # extraction (see PageLocalesConfig#exclude_selectors)
111
82
  def extract_exclude_selectors(config)
112
- config.data.dig('with_locales_data', 'extraction', 'exclude_selectors') ||
113
- ['script', 'style', 'code.language-plaintext', 'pre code']
83
+ config.exclude_selectors
114
84
  end
115
85
  end
116
86
  end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../constants'
4
+ require_relative '../errors'
5
+ require_relative '../po_file/path_builder'
6
+ require_relative '../po_file/reader'
7
+ require_relative '../utils/msgstr_validator'
8
+
9
+ module Jekyll
10
+ module L10n
11
+ # Counts how much of each locale's compendium is translated.
12
+ #
13
+ # Each entry counts once, as translated, fuzzy, untranslated, or invalid
14
+ # (a msgstr whose markup MsgstrValidator rejects, so pages use the
15
+ # fallback). Extractor warns about each invalid entry, prints the counts
16
+ # with the extraction summary, and fails the build when a locale is below
17
+ # `translation.min_coverage`.
18
+ #
19
+ # Only entries whose msgid this build extracted count: a compendium also
20
+ # keeps entries for text the site no longer has. The counts come from the
21
+ # compendium files as they are after extraction, so with
22
+ # `update_compendium: false` they leave out strings no compendium holds
23
+ # yet.
24
+ #
25
+ # @example
26
+ # report = CoverageReport.new(site, SiteConfigAccessor.site_config(site, all_locales: true),
27
+ # extracted_msgids)
28
+ # report.warn_invalid
29
+ # report.log
30
+ # report.enforce!(90) # raises Errors::CoverageError below 90%
31
+ class CoverageReport
32
+ # Entry counts for one locale.
33
+ Counts = Struct.new(:locale, :translated, :fuzzy, :untranslated, :invalid) do
34
+ # @return [Integer] Entries in the compendium
35
+ def total
36
+ translated + fuzzy + untranslated + invalid
37
+ end
38
+
39
+ # @return [Float] Translated entries as a percentage; 100 when the
40
+ # compendium is empty
41
+ def percent
42
+ total.zero? ? 100.0 : translated * 100.0 / total
43
+ end
44
+
45
+ # @return [String] e.g. "fr: 92.5% translated (1200 of 1297; 40 fuzzy, 50 untranslated, 7 invalid)"
46
+ def to_s
47
+ "#{locale}: #{percent.round(1)}% translated (#{translated} of #{total}; " \
48
+ "#{fuzzy} fuzzy, #{untranslated} untranslated, #{invalid} invalid)"
49
+ end
50
+ end
51
+
52
+ # @param site [Jekyll::Site]
53
+ # @param config [PageLocalesConfig] The site config, for locales and locales_dir
54
+ # @param msgids [Set<String>] The msgids this build extracted
55
+ def initialize(site, config, msgids)
56
+ @site = site
57
+ @config = config
58
+ @msgids = msgids
59
+ @invalid = Hash.new { |hash, locale| hash[locale] = [] }
60
+ end
61
+
62
+ # @return [Array<Counts>] One entry per locale that has a compendium
63
+ def counts
64
+ @counts ||= @config.locales.filter_map do |locale|
65
+ path = PoPathBuilder.build(@site.source, @config.locales_dir, locale, nil)
66
+ count(locale, PoFileReader.parse_for_merge(path)) if File.exist?(path)
67
+ end
68
+ end
69
+
70
+ # Warn about each entry whose msgstr markup does not match its msgid.
71
+ #
72
+ # @return [void]
73
+ def warn_invalid
74
+ counts.each do |locale_counts|
75
+ @invalid[locale_counts.locale].each do |msgid|
76
+ Jekyll.logger.warn Constants::LOG_PREFIX,
77
+ "#{locale_counts.locale}: msgstr markup does not match its msgid, " \
78
+ "so pages show the fallback: #{msgid[0..Constants::LOG_TRUNCATE_LONG]}"
79
+ end
80
+ end
81
+ end
82
+
83
+ # Print one line per locale.
84
+ #
85
+ # @return [void]
86
+ def log
87
+ return if counts.empty?
88
+
89
+ Jekyll.logger.info Constants::LOG_PREFIX, 'Translation coverage:'
90
+ counts.each { |locale_counts| Jekyll.logger.info Constants::LOG_PREFIX, " #{locale_counts}" }
91
+ end
92
+
93
+ # @param minimum [Integer, nil] Required percentage, or nil for none
94
+ # @return [void]
95
+ # @raise [Errors::CoverageError] When any locale is below the minimum
96
+ def enforce!(minimum)
97
+ return if minimum.nil?
98
+
99
+ below = counts.select { |locale_counts| locale_counts.percent < minimum }
100
+ return if below.empty?
101
+
102
+ raise Errors::CoverageError,
103
+ "Translation coverage is below translation.min_coverage (#{minimum}%): #{below.join('; ')}"
104
+ end
105
+
106
+ private
107
+
108
+ def count(locale, entries)
109
+ tally = Hash.new(0)
110
+ entries.each do |msgid, entry|
111
+ next unless @msgids.include?(msgid)
112
+
113
+ entry_state = state(msgid, entry)
114
+ tally[entry_state] += 1
115
+ @invalid[locale] << msgid if entry_state == :invalid
116
+ end
117
+ Counts.new(locale, tally[:translated], tally[:fuzzy], tally[:untranslated], tally[:invalid])
118
+ end
119
+
120
+ def state(msgid, entry)
121
+ if entry[:fuzzy] then :fuzzy
122
+ elsif entry[:msgstr].to_s.empty? then :untranslated
123
+ elsif MsgstrValidator.valid?(msgid, entry[:msgstr]) then :translated
124
+ else :invalid
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -3,8 +3,6 @@
3
3
  require_relative '../utils/text_normalizer'
4
4
  require_relative '../utils/xpath_reference_generator'
5
5
  require_relative '../utils/html_text_utils'
6
- require_relative '../utils/html_elements'
7
- require_relative '../utils/text_validator'
8
6
 
9
7
  module Jekyll
10
8
  module L10n
@@ -27,7 +25,7 @@ module Jekyll
27
25
  # entry = DomTextExtractor.extract(node, 'docs/index.html', '_site')
28
26
  # # Returns hash with :msgid, :msgstr, :reference if valid text found
29
27
  module DomTextExtractor
30
- extend self
28
+ module_function
31
29
 
32
30
  # Extract text content from an HTML element.
33
31
  #
@@ -56,34 +54,7 @@ module Jekyll
56
54
  end
57
55
 
58
56
  def extract_block_text(node)
59
- return nil if only_contains_block_elements?(node)
60
- return nil if HtmlTextUtils.layout_block_children?(node)
61
-
62
- text = HtmlTextUtils.extract_with_inline_tags(node)
63
- TextValidator.valid?(text) ? text : nil
64
- end
65
-
66
- def only_contains_block_elements?(node)
67
- node.children.each do |child|
68
- return false if non_empty_text?(child)
69
- return false if non_block_element?(child)
70
- end
71
-
72
- block_element_children?(node)
73
- end
74
-
75
- private
76
-
77
- def non_empty_text?(child)
78
- child.text? && !child.content.strip.empty?
79
- end
80
-
81
- def non_block_element?(child)
82
- child.element? && !HtmlTextUtils::ALL_BLOCK_ELEMENTS.include?(child.name)
83
- end
84
-
85
- def block_element_children?(node)
86
- node.children.any? { |c| c.element? && HtmlTextUtils::ALL_BLOCK_ELEMENTS.include?(c.name) }
57
+ HtmlTextUtils.block_text(node)
87
58
  end
88
59
  end
89
60
  end