jekyll-l10n 1.7.1 → 2.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.
- checksums.yaml +4 -4
- data/lib/jekyll-l10n/extraction/config_loader.rb +25 -55
- data/lib/jekyll-l10n/extraction/extractor.rb +29 -42
- data/lib/jekyll-l10n/jekyll/generator.rb +38 -40
- data/lib/jekyll-l10n/jekyll/post_write_html_reprocessor.rb +14 -26
- data/lib/jekyll-l10n/jekyll/regeneration_checker.rb +59 -45
- data/lib/jekyll-l10n/jekyll/url_filter.rb +5 -6
- data/lib/jekyll-l10n/translation/page_translation_loader.rb +13 -29
- data/lib/jekyll-l10n/translation/translator.rb +29 -61
- data/lib/jekyll-l10n/utils/page_locales_config.rb +72 -149
- data/lib/jekyll-l10n/utils/site_config_accessor.rb +77 -39
- data/lib/jekyll-l10n/version.rb +1 -1
- data/lib/jekyll-l10n.rb +64 -176
- metadata +1 -1
|
@@ -5,6 +5,7 @@ require_relative '../constants'
|
|
|
5
5
|
require_relative '../utils/error_handler'
|
|
6
6
|
require_relative '../utils/locale_name_resolver'
|
|
7
7
|
require_relative '../utils/locale_url_formatter'
|
|
8
|
+
require_relative '../utils/site_config_accessor'
|
|
8
9
|
|
|
9
10
|
module Jekyll
|
|
10
11
|
module L10n
|
|
@@ -192,29 +193,27 @@ module Jekyll
|
|
|
192
193
|
return false if locale.nil? || locale.empty?
|
|
193
194
|
return true if locale == 'en' # English is always valid
|
|
194
195
|
|
|
195
|
-
# Get configured locales from page or site config
|
|
196
196
|
locales_config = configured_locales
|
|
197
197
|
|
|
198
|
-
#
|
|
199
|
-
# before comparing, so 'es-ES' matches 'es_ES' and vice versa.
|
|
198
|
+
# Compare URL forms so 'es-ES' matches 'es_ES' and the other way round.
|
|
200
199
|
url_locale = LocaleUrlFormatter.to_url_segment(locale)
|
|
201
200
|
locales_config.any? { |l| LocaleUrlFormatter.to_url_segment(l) == url_locale }
|
|
202
201
|
end
|
|
203
202
|
|
|
203
|
+
# Locales of the page being rendered. A Hash comes from Liquid and already
|
|
204
|
+
# has the site defaults merged in; a page object needs the same merge.
|
|
204
205
|
def configured_locales
|
|
205
206
|
page = @context.registers[:page]
|
|
206
207
|
|
|
207
|
-
# Try to get from page data first (most specific)
|
|
208
208
|
if page
|
|
209
209
|
locales = if page.is_a?(Hash)
|
|
210
210
|
page.dig('with_locales_data', 'locales')
|
|
211
211
|
else
|
|
212
|
-
|
|
212
|
+
SiteConfigAccessor.page_locales_data(page)['locales']
|
|
213
213
|
end
|
|
214
214
|
return locales if locales && !locales.empty?
|
|
215
215
|
end
|
|
216
216
|
|
|
217
|
-
# Fallback to empty array if not configured
|
|
218
217
|
[]
|
|
219
218
|
end
|
|
220
219
|
|
|
@@ -8,39 +8,23 @@ require_relative '../utils/logger_formatter'
|
|
|
8
8
|
|
|
9
9
|
module Jekyll
|
|
10
10
|
module L10n
|
|
11
|
-
#
|
|
11
|
+
# Reads the translations one page needs for one locale.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# HtmlTranslator.
|
|
18
|
-
#
|
|
19
|
-
# Key responsibilities:
|
|
20
|
-
# * Load compendium translations for a locale
|
|
21
|
-
# * Convert page URLs to PO file paths
|
|
22
|
-
# * Load page-specific translations
|
|
23
|
-
# * Merge compendium and page-specific translations
|
|
24
|
-
# * Filter empty translations (untranslated entries)
|
|
25
|
-
# * Log loading progress at debug level
|
|
13
|
+
# It combines two PO files: the locale compendium (`es.po`) and the page's
|
|
14
|
+
# own file (`es/<page url>/index.html.po`). When both have a string, the
|
|
15
|
+
# page file wins. With the default `update_compendium: true` the page file
|
|
16
|
+
# no longer exists after extraction, so the compendium provides everything.
|
|
26
17
|
#
|
|
27
18
|
# @example
|
|
28
|
-
# config = PageLocalesConfig.new(page
|
|
29
|
-
#
|
|
30
|
-
# #
|
|
19
|
+
# config = PageLocalesConfig.new(SiteConfigAccessor.effective_page_data(page))
|
|
20
|
+
# PageTranslationLoader.load(site, 'es', '/docs/index.html', config)
|
|
21
|
+
# # => { "Welcome" => "Bienvenido", ... }
|
|
31
22
|
class PageTranslationLoader
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# @param site [Jekyll::Site] Jekyll site object
|
|
39
|
-
# @param locale [String] Target locale code (e.g., 'es', 'fr')
|
|
40
|
-
# @param original_url [String] Original page URL (e.g., '/docs/index.html')
|
|
41
|
-
# @param config [PageLocalesConfig] Localization configuration for the page
|
|
42
|
-
# @return [Hash] Merged translation hash { msgid => msgstr }, filtered to only
|
|
43
|
-
# include non-empty translations
|
|
23
|
+
# @param site [Jekyll::Site] The Jekyll site
|
|
24
|
+
# @param locale [String] Locale code, e.g. 'es'
|
|
25
|
+
# @param original_url [String] URL of the English page, e.g. '/docs/index.html'
|
|
26
|
+
# @param config [PageLocalesConfig] The page's settings (for `locales_dir`)
|
|
27
|
+
# @return [Hash] msgid => msgstr, without untranslated entries
|
|
44
28
|
def self.load(site, locale, original_url, config)
|
|
45
29
|
po_manager = PoFileManager.new(site, config.locales_dir)
|
|
46
30
|
page_path = construct_po_page_path(original_url)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../utils/page_locales_config'
|
|
4
|
+
require_relative '../utils/site_config_accessor'
|
|
4
5
|
require_relative '../utils/html_parser'
|
|
5
6
|
require_relative '../utils/logger_formatter'
|
|
6
7
|
require_relative '../utils/error_handler'
|
|
@@ -10,65 +11,38 @@ require_relative 'page_translation_loader'
|
|
|
10
11
|
|
|
11
12
|
module Jekyll
|
|
12
13
|
module L10n
|
|
13
|
-
#
|
|
14
|
+
# Translates one localized page right after Jekyll renders it.
|
|
14
15
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
16
|
+
# A `:pages, :post_render` hook in lib/jekyll-l10n.rb calls it for every
|
|
17
|
+
# LocalizedPage. It loads the translations for the page's locale, replaces
|
|
18
|
+
# the English text and attributes in the rendered HTML, and applies the
|
|
19
|
+
# page's fallback mode to strings without a translation.
|
|
19
20
|
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# 2. Load translations from PO files (page-specific and compendium)
|
|
23
|
-
# 3. Apply translations to text nodes and attributes in HTML
|
|
24
|
-
# 4. Handle missing translations with configured fallback modes
|
|
25
|
-
# 5. Preserve special elements like external link icons
|
|
21
|
+
# Settings come from the English page's merged `with_locales_data`, so a
|
|
22
|
+
# page can override, for example, `translation.fallback` for itself.
|
|
26
23
|
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# - Apply translations to HTML with DOM manipulation
|
|
30
|
-
# - Handle fallback modes (english, marker, empty)
|
|
31
|
-
# - Preserve special formatting and elements (icons, badges, etc.)
|
|
32
|
-
# - Log translation progress and errors
|
|
24
|
+
# @example
|
|
25
|
+
# Translator.new(localized_page).translate
|
|
33
26
|
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# @example Usage (typically invoked via Jekyll hook)
|
|
38
|
-
# # Automatically invoked for localized pages in post_render phase
|
|
39
|
-
# translator = Translator.new(localized_page)
|
|
40
|
-
# translator.translate
|
|
41
|
-
#
|
|
42
|
-
# @see Jekyll::L10n for Build Pipeline documentation
|
|
43
|
-
# @see Jekyll::L10n::HtmlTranslator for low-level DOM translation logic
|
|
44
|
-
# @see Jekyll::L10n::PageTranslationLoader for loading translations from PO files
|
|
27
|
+
# @see HtmlTranslator changes the HTML
|
|
28
|
+
# @see PageTranslationLoader reads the PO files
|
|
45
29
|
#
|
|
46
30
|
class Translator
|
|
47
31
|
# @!attribute [r] page
|
|
48
|
-
#
|
|
49
|
-
# @return [Jekyll::Page]
|
|
32
|
+
# @return [Jekyll::Page] The page being translated
|
|
50
33
|
attr_reader :page
|
|
51
34
|
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
# @param page [Jekyll::Page] The page to translate (should be a LocalizedPage)
|
|
35
|
+
# @param page [Jekyll::Page] Usually a LocalizedPage
|
|
55
36
|
def initialize(page)
|
|
56
37
|
@page = page
|
|
57
38
|
@site = page.site
|
|
58
39
|
end
|
|
59
40
|
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
# loads the appropriate translations from PO files, applies them to the HTML,
|
|
64
|
-
# and updates the page's output.
|
|
65
|
-
#
|
|
66
|
-
# This method is called automatically by Jekyll's post_render hook for each
|
|
67
|
-
# LocalizedPage during the build process.
|
|
41
|
+
# Replaces the page output with its translated HTML. Does nothing for
|
|
42
|
+
# pages without `locale` and `original_url`, pages whose English page
|
|
43
|
+
# cannot be found, or when there are no translations.
|
|
68
44
|
#
|
|
69
45
|
# @return [void]
|
|
70
|
-
# @note Only translates pages with localized: true and matching locale/original_url
|
|
71
|
-
# @note Gracefully handles missing translations with configured fallback mode
|
|
72
46
|
def translate
|
|
73
47
|
return unless should_translate?
|
|
74
48
|
|
|
@@ -79,21 +53,20 @@ module Jekyll
|
|
|
79
53
|
original_page = find_and_log_original_page(original_url)
|
|
80
54
|
return unless original_page
|
|
81
55
|
|
|
82
|
-
|
|
56
|
+
config = PageLocalesConfig.new(SiteConfigAccessor.effective_page_data(original_page))
|
|
57
|
+
translations = load_and_log_translations(locale, original_url, config)
|
|
83
58
|
return if translations.nil? || translations.empty?
|
|
84
59
|
|
|
85
|
-
apply_translations_to_page(original_page, translations, locale, baseurl)
|
|
60
|
+
apply_translations_to_page(original_page, config, translations, locale, baseurl)
|
|
86
61
|
end
|
|
87
62
|
|
|
88
63
|
private
|
|
89
64
|
|
|
90
|
-
def load_translations_for_page(locale, original_url,
|
|
91
|
-
config = PageLocalesConfig.new(original_page.data)
|
|
65
|
+
def load_translations_for_page(locale, original_url, config)
|
|
92
66
|
PageTranslationLoader.load(@site, locale, original_url, config)
|
|
93
67
|
end
|
|
94
68
|
|
|
95
|
-
def apply_translations_to_page(original_page, translations, locale, baseurl)
|
|
96
|
-
config = PageLocalesConfig.new(original_page.data)
|
|
69
|
+
def apply_translations_to_page(original_page, config, translations, locale, baseurl)
|
|
97
70
|
translator = HtmlTranslator.new(
|
|
98
71
|
config.fallback_mode,
|
|
99
72
|
config.translatable_attributes,
|
|
@@ -112,16 +85,11 @@ module Jekyll
|
|
|
112
85
|
ExternalLinkIconPreserver.preserve(original_html, translated_html)
|
|
113
86
|
end
|
|
114
87
|
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
# @param translator [HtmlTranslator] The translator instance to use
|
|
121
|
-
# @param translations [Hash] Translation hash mapping text to translations
|
|
122
|
-
# @param locale [String, nil] Target locale code (defaults to nil). If nil, uses "en"
|
|
123
|
-
# @param baseurl [String] Base URL for relative URL transformation (defaults to "")
|
|
124
|
-
# @return [String, nil] Translated HTML string, or nil on error
|
|
88
|
+
# @param translator [HtmlTranslator]
|
|
89
|
+
# @param translations [Hash] msgid => msgstr
|
|
90
|
+
# @param locale [String, nil] Target locale; nil means "en"
|
|
91
|
+
# @param baseurl [String] Site baseurl, used when rewriting links
|
|
92
|
+
# @return [String, nil] Translated HTML, or nil after logging an error
|
|
125
93
|
def apply_translations(translator, translations, locale = nil, baseurl = '')
|
|
126
94
|
if locale && locale != 'en'
|
|
127
95
|
translator.translate(@page.output, translations, locale, baseurl)
|
|
@@ -169,8 +137,8 @@ module Jekyll
|
|
|
169
137
|
original_page
|
|
170
138
|
end
|
|
171
139
|
|
|
172
|
-
def load_and_log_translations(locale, original_url,
|
|
173
|
-
translations = load_translations_for_page(locale, original_url,
|
|
140
|
+
def load_and_log_translations(locale, original_url, config)
|
|
141
|
+
translations = load_translations_for_page(locale, original_url, config)
|
|
174
142
|
LoggerFormatter.debug_if_enabled('Translator',
|
|
175
143
|
"Loaded #{translations&.length || 0} translations")
|
|
176
144
|
translations
|
|
@@ -4,51 +4,25 @@ require_relative '../constants'
|
|
|
4
4
|
|
|
5
5
|
module Jekyll
|
|
6
6
|
module L10n
|
|
7
|
-
#
|
|
7
|
+
# Wraps a `with_locales_data` hash and answers questions about it, filling in
|
|
8
|
+
# the defaults from Constants for anything not set.
|
|
8
9
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# against expected types and ranges, raising clear errors for invalid configurations.
|
|
10
|
+
# It raises InvalidConfigurationError right away for bad locale codes or bad
|
|
11
|
+
# LibreTranslate settings, so mistakes stop the build early.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
14
|
-
# -
|
|
15
|
-
#
|
|
16
|
-
# -
|
|
13
|
+
# Build it from the right data:
|
|
14
|
+
# - For one page, use SiteConfigAccessor.effective_page_data(page), never raw
|
|
15
|
+
# `page.data`; otherwise the page loses the site defaults.
|
|
16
|
+
# - For site-wide settings, use
|
|
17
|
+
# `{ 'with_locales_data' => SiteConfigAccessor.extract_locales_data(site) }`.
|
|
17
18
|
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# - Provide getter methods with sensible defaults
|
|
23
|
-
# - Raise detailed validation errors for invalid configurations
|
|
24
|
-
#
|
|
25
|
-
# @example Minimal configuration
|
|
26
|
-
# ---
|
|
27
|
-
# with_locales: true
|
|
28
|
-
# with_locales_data:
|
|
29
|
-
# locales: [es, fr, pt]
|
|
30
|
-
# ---
|
|
31
|
-
#
|
|
32
|
-
# @example Full configuration with LibreTranslate
|
|
33
|
-
# ---
|
|
34
|
-
# with_locales: true
|
|
35
|
-
# with_locales_data:
|
|
36
|
-
# locales: [es, fr, pt_BR]
|
|
37
|
-
# extract_on_build: true
|
|
38
|
-
# update_compendium: true
|
|
39
|
-
# extraction:
|
|
40
|
-
# translatable_attributes: [title, alt, aria-label]
|
|
41
|
-
# translation:
|
|
42
|
-
# fallback: english
|
|
43
|
-
# libretranslate_enabled: true
|
|
44
|
-
# libretranslate_api_url: "http://localhost:5000/translate"
|
|
45
|
-
# libretranslate_timeout: 300
|
|
46
|
-
# logging:
|
|
47
|
-
# debug: true
|
|
48
|
-
# ---
|
|
19
|
+
# @example
|
|
20
|
+
# config = PageLocalesConfig.new(SiteConfigAccessor.effective_page_data(page))
|
|
21
|
+
# config.locales # => ["es", "fr"]
|
|
22
|
+
# config.fallback_mode # => "english"
|
|
49
23
|
#
|
|
50
24
|
class PageLocalesConfig
|
|
51
|
-
#
|
|
25
|
+
# Shortcuts to the defaults in Constants.
|
|
52
26
|
LOCALE_PATTERN = Constants::LOCALE_PATTERN
|
|
53
27
|
DEFAULT_LOCALES_DIR = Constants::DEFAULT_LOCALES_DIR
|
|
54
28
|
DEFAULT_FALLBACK_MODE = Constants::DEFAULT_FALLBACK_MODE
|
|
@@ -63,18 +37,14 @@ module Jekyll
|
|
|
63
37
|
DEFAULT_LIBRETRANSLATE_FORMAT = Constants::DEFAULT_LIBRETRANSLATE_FORMAT
|
|
64
38
|
|
|
65
39
|
# @!attribute [r] data
|
|
66
|
-
# The
|
|
40
|
+
# The hash this config was built from
|
|
67
41
|
# @return [Hash]
|
|
68
42
|
attr_reader :data
|
|
69
43
|
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
# @param page_data [Hash] The Jekyll page data/front matter object
|
|
76
|
-
# @raise [Jekyll::Errors::InvalidConfigurationError] If locale codes are invalid
|
|
77
|
-
# @raise [Jekyll::Errors::InvalidConfigurationError] If LibreTranslate config is invalid
|
|
44
|
+
# @param page_data [Hash] A hash with a `with_locales_data` key, and
|
|
45
|
+
# optionally `path` (used in error messages)
|
|
46
|
+
# @raise [Jekyll::Errors::InvalidConfigurationError] If a locale code or a
|
|
47
|
+
# LibreTranslate setting is invalid
|
|
78
48
|
def initialize(page_data)
|
|
79
49
|
@config = page_data['with_locales_data'] || {}
|
|
80
50
|
@data = page_data
|
|
@@ -84,200 +54,156 @@ module Jekyll
|
|
|
84
54
|
validate_libretranslate!
|
|
85
55
|
end
|
|
86
56
|
|
|
87
|
-
#
|
|
57
|
+
# Locales to translate into. The constructor has already checked each code
|
|
58
|
+
# against LOCALE_PATTERN.
|
|
88
59
|
#
|
|
89
|
-
#
|
|
90
|
-
# if not configured. All returned locales are guaranteed to match ISO 639-1 format,
|
|
91
|
-
# optionally suffixed with an ISO 3166-1 alpha-2 country code.
|
|
92
|
-
#
|
|
93
|
-
# @return [Array<String>] BCP 47 locale codes (e.g., ['es', 'fr', 'pt_BR'])
|
|
60
|
+
# @return [Array<String>] Locale codes such as ["es", "pt_BR"], or []
|
|
94
61
|
def locales
|
|
95
62
|
@config['locales'] || []
|
|
96
63
|
end
|
|
97
64
|
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
# Returns the directory specified in `with_locales_data.locales_dir`,
|
|
101
|
-
# or the default "_locales" if not configured.
|
|
102
|
-
#
|
|
103
|
-
# @return [String] The directory path relative to site root
|
|
65
|
+
# @return [String] Directory for PO files, relative to the site source
|
|
104
66
|
def locales_dir
|
|
105
67
|
@config['locales_dir'] || DEFAULT_LOCALES_DIR
|
|
106
68
|
end
|
|
107
69
|
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
# @return [Boolean] true if extraction is enabled (default), false if explicitly disabled
|
|
70
|
+
# @return [Boolean] false only when `extract_on_build` is false
|
|
111
71
|
def extract_on_build?
|
|
112
72
|
@config['extract_on_build'] != false
|
|
113
73
|
end
|
|
114
74
|
|
|
115
|
-
#
|
|
75
|
+
# Whether to merge page PO files into the locale compendium. A site-wide
|
|
76
|
+
# setting: read it from the site-wide config, not from a page.
|
|
116
77
|
#
|
|
117
|
-
# @return [Boolean]
|
|
118
|
-
# explicitly disabled
|
|
78
|
+
# @return [Boolean] false only when `update_compendium` is false
|
|
119
79
|
def update_compendium?
|
|
120
80
|
@config['update_compendium'] != false
|
|
121
81
|
end
|
|
122
82
|
|
|
123
|
-
#
|
|
83
|
+
# Whether to print the extraction summary at the end of the build. A
|
|
84
|
+
# site-wide setting: Extractor reads it from the site-wide config.
|
|
124
85
|
#
|
|
125
|
-
# @return [Boolean]
|
|
86
|
+
# @return [Boolean] false only when `logging.show_statistics` is false
|
|
126
87
|
def show_statistics?
|
|
127
88
|
@config.dig('logging', 'show_statistics') != false
|
|
128
89
|
end
|
|
129
90
|
|
|
130
|
-
#
|
|
91
|
+
# Turns on HtmlTranslator's log of each translation lookup for this page.
|
|
131
92
|
#
|
|
132
|
-
# @return [Boolean] true
|
|
93
|
+
# @return [Boolean] true when `logging.debug` is true
|
|
133
94
|
def debug_logging?
|
|
134
95
|
@config.dig('logging', 'debug') == true
|
|
135
96
|
end
|
|
136
97
|
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
# Trace logging includes detailed per-entry logs for extraction and translation operations.
|
|
140
|
-
# This is automatically enabled if debug_logging? is true.
|
|
98
|
+
# Turns on per-entry LibreTranslate logging. Those lines go to Jekyll's
|
|
99
|
+
# debug level, so they show only with `jekyll build --verbose`.
|
|
141
100
|
#
|
|
142
|
-
# @return [Boolean] true
|
|
101
|
+
# @return [Boolean] true when `logging.trace` or `logging.debug` is true
|
|
143
102
|
def trace_logging?
|
|
144
103
|
@config.dig('logging', 'trace') == true || debug_logging?
|
|
145
104
|
end
|
|
146
105
|
|
|
147
|
-
#
|
|
148
|
-
#
|
|
149
|
-
# Determines how to handle translations that are not found in PO files.
|
|
150
|
-
# Valid modes: "english" (use original text), "marker" (wrap with markers),
|
|
151
|
-
# "empty" (leave blank).
|
|
106
|
+
# What to show when a string has no translation: "english" (the original
|
|
107
|
+
# text), "marker" (the original text wrapped in markers), or "empty".
|
|
152
108
|
#
|
|
153
|
-
# @return [String] The fallback mode
|
|
109
|
+
# @return [String] The fallback mode
|
|
154
110
|
def fallback_mode
|
|
155
111
|
@config.dig('translation', 'fallback') || DEFAULT_FALLBACK_MODE
|
|
156
112
|
end
|
|
157
113
|
|
|
158
|
-
#
|
|
114
|
+
# HTML attributes whose values are extracted and translated. Defaults to
|
|
115
|
+
# DEFAULT_TRANSLATABLE_ATTRIBUTES.
|
|
159
116
|
#
|
|
160
|
-
#
|
|
161
|
-
# or the default list if not configured (title, alt, aria-label, placeholder,
|
|
162
|
-
# aria-description).
|
|
163
|
-
#
|
|
164
|
-
# @return [Array<String>] List of attribute names to extract
|
|
117
|
+
# @return [Array<String>] Attribute names
|
|
165
118
|
def translatable_attributes
|
|
166
119
|
@config.dig('extraction', 'translatable_attributes') || DEFAULT_TRANSLATABLE_ATTRIBUTES
|
|
167
120
|
end
|
|
168
121
|
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
# A page is considered to have localization enabled if at least one locale is configured.
|
|
172
|
-
#
|
|
173
|
-
# @return [Boolean] true if locales list is not empty, false otherwise
|
|
122
|
+
# @return [Boolean] true when at least one locale is configured
|
|
174
123
|
def enabled?
|
|
175
124
|
!locales.empty?
|
|
176
125
|
end
|
|
177
126
|
|
|
178
|
-
#
|
|
127
|
+
# Whether to machine-translate with LibreTranslate. An explicit
|
|
128
|
+
# `libretranslate_enabled` always wins. Without it, only a
|
|
129
|
+
# `libretranslate_api_url` you set yourself turns it on; the default URL
|
|
130
|
+
# does not, so an unconfigured site never tries to reach a server.
|
|
179
131
|
#
|
|
180
|
-
#
|
|
181
|
-
# or if a `libretranslate_api_url` is configured (backward compatibility).
|
|
182
|
-
#
|
|
183
|
-
# @return [Boolean] true if LibreTranslate is enabled and configured
|
|
132
|
+
# @return [Boolean] true if LibreTranslate is enabled
|
|
184
133
|
def libretranslate_enabled?
|
|
185
|
-
# Priority 1: Explicit flag (when set)
|
|
186
134
|
if libretranslate_config.key?('libretranslate_enabled')
|
|
187
135
|
return libretranslate_config['libretranslate_enabled'] == true
|
|
188
136
|
end
|
|
189
137
|
|
|
190
|
-
|
|
191
|
-
|
|
138
|
+
url = libretranslate_config['libretranslate_api_url']
|
|
139
|
+
url.is_a?(String) && !url.strip.empty?
|
|
192
140
|
end
|
|
193
141
|
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
# The source locale is the language of the original content being translated.
|
|
197
|
-
# Defaults to "en" (English) if not specified.
|
|
198
|
-
#
|
|
199
|
-
# @return [String] BCP 47 locale code (e.g., 'en', 'fr')
|
|
142
|
+
# @return [String] Locale code of the original content. Defaults to
|
|
143
|
+
# DEFAULT_LIBRETRANSLATE_SOURCE_LOCALE.
|
|
200
144
|
def libretranslate_source_locale
|
|
201
145
|
libretranslate_setting('libretranslate_source_locale', DEFAULT_LIBRETRANSLATE_SOURCE_LOCALE)
|
|
202
146
|
end
|
|
203
147
|
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
# Determines how text is passed to LibreTranslate API. Valid values: 'text' or 'html'.
|
|
207
|
-
# HTML format preserves markup and performs better with structured content.
|
|
148
|
+
# How strings are sent to LibreTranslate. "html" keeps inline markup intact.
|
|
208
149
|
#
|
|
209
|
-
# @return [String]
|
|
150
|
+
# @return [String] "text" or "html". Defaults to DEFAULT_LIBRETRANSLATE_FORMAT.
|
|
210
151
|
def libretranslate_format
|
|
211
152
|
libretranslate_setting('libretranslate_format', DEFAULT_LIBRETRANSLATE_FORMAT)
|
|
212
153
|
end
|
|
213
154
|
|
|
214
|
-
#
|
|
155
|
+
# Base URL of the LibreTranslate server, without `/translate`, which
|
|
156
|
+
# LibreTranslator appends. Returning the default here does not turn
|
|
157
|
+
# LibreTranslate on; see {#libretranslate_enabled?}.
|
|
215
158
|
#
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
# @return [String] The API URL (default: "http://localhost:5000")
|
|
159
|
+
# @return [String] The URL. Defaults to Constants::DEFAULT_LIBRETRANSLATE_API_URL.
|
|
219
160
|
def libretranslate_api_url
|
|
220
161
|
libretranslate_setting('libretranslate_api_url', Constants::DEFAULT_LIBRETRANSLATE_API_URL)
|
|
221
162
|
end
|
|
222
163
|
|
|
223
|
-
#
|
|
224
|
-
#
|
|
225
|
-
# Some LibreTranslate instances require authentication via API key.
|
|
226
|
-
#
|
|
227
|
-
# @return [String, nil] The API key, or nil if not configured
|
|
164
|
+
# @return [String, nil] API key for servers that require one, or nil
|
|
228
165
|
def libretranslate_api_key
|
|
229
166
|
libretranslate_config['libretranslate_api_key']
|
|
230
167
|
end
|
|
231
168
|
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
# @return [Integer] Timeout in seconds (default: 300)
|
|
169
|
+
# @return [Integer] Request timeout in seconds. Defaults to
|
|
170
|
+
# DEFAULT_LIBRETRANSLATE_TIMEOUT.
|
|
235
171
|
def libretranslate_timeout
|
|
236
172
|
libretranslate_setting('libretranslate_timeout', DEFAULT_LIBRETRANSLATE_TIMEOUT)
|
|
237
173
|
end
|
|
238
174
|
|
|
239
|
-
#
|
|
240
|
-
#
|
|
241
|
-
# Controls how many strings are sent to LibreTranslate in a single API request.
|
|
242
|
-
# Larger batches are more efficient but may hit size limits.
|
|
175
|
+
# Strings sent per request. Larger batches mean fewer requests but may
|
|
176
|
+
# exceed the server's size limit.
|
|
243
177
|
#
|
|
244
|
-
# @return [Integer]
|
|
178
|
+
# @return [Integer] Defaults to DEFAULT_LIBRETRANSLATE_BATCH_SIZE.
|
|
245
179
|
def libretranslate_batch_size
|
|
246
180
|
libretranslate_setting('libretranslate_batch_size', DEFAULT_LIBRETRANSLATE_BATCH_SIZE)
|
|
247
181
|
end
|
|
248
182
|
|
|
249
|
-
#
|
|
250
|
-
#
|
|
251
|
-
# @return [Integer] Number of retry attempts (default: 3)
|
|
183
|
+
# @return [Integer] Retries for a failed request. Defaults to
|
|
184
|
+
# DEFAULT_LIBRETRANSLATE_RETRY_ATTEMPTS.
|
|
252
185
|
def libretranslate_retry_attempts
|
|
253
186
|
libretranslate_setting('libretranslate_retry_attempts', DEFAULT_LIBRETRANSLATE_RETRY_ATTEMPTS)
|
|
254
187
|
end
|
|
255
188
|
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
# @return [Integer] Delay in seconds (default: 2)
|
|
189
|
+
# @return [Integer] Seconds to wait between retries. Defaults to
|
|
190
|
+
# DEFAULT_LIBRETRANSLATE_RETRY_DELAY.
|
|
259
191
|
def libretranslate_retry_delay
|
|
260
192
|
libretranslate_setting('libretranslate_retry_delay', DEFAULT_LIBRETRANSLATE_RETRY_DELAY)
|
|
261
193
|
end
|
|
262
194
|
|
|
263
|
-
#
|
|
264
|
-
#
|
|
265
|
-
# If true, any API error will halt the translation process. If false, errors are logged
|
|
266
|
-
# but translation continues with other entries.
|
|
195
|
+
# When true, the first API error stops machine translation. When false, the
|
|
196
|
+
# error is logged and the remaining entries are still translated.
|
|
267
197
|
#
|
|
268
|
-
# @return [Boolean]
|
|
269
|
-
# translation continues (default: false)
|
|
198
|
+
# @return [Boolean] Defaults to Constants::DEFAULT_LIBRETRANSLATE_STOP_ON_ERROR.
|
|
270
199
|
def libretranslate_stop_on_error?
|
|
271
200
|
val = libretranslate_config['libretranslate_stop_on_error']
|
|
272
201
|
val.nil? ? Constants::DEFAULT_LIBRETRANSLATE_STOP_ON_ERROR : val != false
|
|
273
202
|
end
|
|
274
203
|
|
|
275
|
-
#
|
|
276
|
-
#
|
|
277
|
-
# Translation progress is logged every N entries translated. Set to 0 to disable
|
|
278
|
-
# progress logging.
|
|
204
|
+
# Log progress after every this many translated entries; 0 turns it off.
|
|
279
205
|
#
|
|
280
|
-
# @return [Integer]
|
|
206
|
+
# @return [Integer] Defaults to DEFAULT_LIBRETRANSLATE_PROGRESS_INTERVAL.
|
|
281
207
|
def libretranslate_progress_interval
|
|
282
208
|
libretranslate_setting('libretranslate_progress_interval', DEFAULT_LIBRETRANSLATE_PROGRESS_INTERVAL)
|
|
283
209
|
end
|
|
@@ -323,19 +249,16 @@ module Jekyll
|
|
|
323
249
|
def validate_libretranslate!
|
|
324
250
|
return unless libretranslate_enabled?
|
|
325
251
|
|
|
326
|
-
# Require URL when enabled
|
|
327
252
|
if libretranslate_api_url.nil? || libretranslate_api_url.empty?
|
|
328
253
|
raise Jekyll::Errors::InvalidConfigurationError,
|
|
329
254
|
'LibreTranslate is enabled but libretranslate_api_url is not configured'
|
|
330
255
|
end
|
|
331
256
|
|
|
332
|
-
# Validate source locale format (ISO 639-1, optionally with ISO 3166-1 alpha-2 country)
|
|
333
257
|
unless LOCALE_PATTERN.match?(libretranslate_source_locale)
|
|
334
258
|
raise Jekyll::Errors::InvalidConfigurationError,
|
|
335
259
|
"Invalid libretranslate_source_locale: #{libretranslate_source_locale}"
|
|
336
260
|
end
|
|
337
261
|
|
|
338
|
-
# Validate format
|
|
339
262
|
return if %w[text html].include?(libretranslate_format)
|
|
340
263
|
|
|
341
264
|
raise Jekyll::Errors::InvalidConfigurationError,
|