jekyll-l10n 1.2.8 → 1.3.4
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/README.md +43 -63
- data/lib/jekyll-l10n/constants.rb +15 -10
- data/lib/jekyll-l10n/extraction/compendium_merger.rb +38 -17
- data/lib/jekyll-l10n/extraction/compendium_translator.rb +18 -18
- data/lib/jekyll-l10n/extraction/config_loader.rb +8 -8
- data/lib/jekyll-l10n/extraction/dom_attribute_extractor.rb +4 -4
- data/lib/jekyll-l10n/extraction/dom_text_extractor.rb +6 -6
- data/lib/jekyll-l10n/extraction/extractor.rb +15 -15
- data/lib/jekyll-l10n/extraction/html_string_extractor.rb +5 -5
- data/lib/jekyll-l10n/extraction/logger.rb +6 -6
- data/lib/jekyll-l10n/extraction/result_saver.rb +12 -12
- data/lib/jekyll-l10n/jekyll/generator.rb +25 -13
- data/lib/jekyll-l10n/jekyll/localized_page.rb +13 -13
- data/lib/jekyll-l10n/jekyll/post_write_html_reprocessor.rb +14 -14
- data/lib/jekyll-l10n/jekyll/post_write_processor.rb +19 -6
- data/lib/jekyll-l10n/jekyll/regeneration_checker.rb +10 -10
- data/lib/jekyll-l10n/jekyll/url_filter.rb +17 -17
- data/lib/jekyll-l10n/po_file/loader.rb +4 -4
- data/lib/jekyll-l10n/po_file/manager.rb +15 -15
- data/lib/jekyll-l10n/po_file/merger.rb +3 -3
- data/lib/jekyll-l10n/po_file/path_builder.rb +1 -1
- data/lib/jekyll-l10n/po_file/reader.rb +42 -50
- data/lib/jekyll-l10n/po_file/writer.rb +20 -20
- data/lib/jekyll-l10n/translation/block_text_extractor.rb +5 -5
- data/lib/jekyll-l10n/translation/html_translator.rb +22 -22
- data/lib/jekyll-l10n/translation/libre_translator.rb +29 -26
- data/lib/jekyll-l10n/translation/page_translation_loader.rb +8 -8
- data/lib/jekyll-l10n/translation/translator.rb +26 -26
- data/lib/jekyll-l10n/utils/debug_logger.rb +6 -6
- data/lib/jekyll-l10n/utils/error_handler.rb +2 -2
- data/lib/jekyll-l10n/utils/external_link_icon_preserver.rb +11 -11
- data/lib/jekyll-l10n/utils/file_operations.rb +5 -5
- data/lib/jekyll-l10n/utils/html_elements.rb +5 -5
- data/lib/jekyll-l10n/utils/html_parser.rb +2 -2
- data/lib/jekyll-l10n/utils/html_text_utils.rb +12 -12
- data/lib/jekyll-l10n/utils/logger_formatter.rb +6 -6
- data/lib/jekyll-l10n/utils/page_locales_config.rb +36 -35
- data/lib/jekyll-l10n/utils/po_entry_converter.rb +10 -12
- data/lib/jekyll-l10n/utils/site_config_accessor.rb +4 -4
- data/lib/jekyll-l10n/utils/text_normalizer.rb +2 -2
- data/lib/jekyll-l10n/utils/text_validator.rb +1 -1
- data/lib/jekyll-l10n/utils/translation_resolver.rb +3 -3
- data/lib/jekyll-l10n/utils/url_path_builder.rb +5 -5
- data/lib/jekyll-l10n/utils/url_transformer.rb +13 -13
- data/lib/jekyll-l10n/utils/xpath_reference_generator.rb +2 -2
- data/lib/jekyll-l10n/version.rb +1 -1
- data/lib/jekyll-l10n.rb +48 -48
- metadata +60 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e9b061ddf1f9f9072efe9135d023d05d55d7dd8545bd34988ecae6f59e03a2e2
|
|
4
|
+
data.tar.gz: 6c8704fa0e33e96c8e9918d5f7539db8203f291b4a2608e8946846e86fea8a94
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 709ab40d1a6f0ada0810751f54279d4c466d00349a46a9c7916d4a705289af6e8c142ebbd31fefd520c1205089f1f662354f702b381fe8bb44129ab4fde157fc
|
|
7
|
+
data.tar.gz: acda3c29b32433e2c3b1e90004690955b0da0219a8c896f8cfc28aa61e2ff190e238352e08e35cd606c4717524b8d6c731cd3efac673be115ec89ce89a9fdb32
|
data/README.md
CHANGED
|
@@ -1,85 +1,65 @@
|
|
|
1
1
|
# jekyll-l10n
|
|
2
2
|
|
|
3
|
-
`jekyll-l10n` is a Jekyll plugin that streamlines the localization of static
|
|
4
|
-
websites using industry-standard GNU Gettext PO files. Creating multilingual
|
|
5
|
-
sites presents unique challenges: maintaining consistent translations across
|
|
6
|
-
multiple pages, managing translation workflows with non-technical translators,
|
|
7
|
-
and keeping translations synchronized as content evolves. This plugin automates
|
|
8
|
-
these challenges by integrating the proven Gettext workflow directly into your
|
|
9
|
-
Jekyll build pipeline.
|
|
10
|
-
|
|
11
|
-
The plugin works by extracting translatable strings from your site's generated
|
|
12
|
-
HTML and organizing them into PO files that translators can edit using standard
|
|
13
|
-
translation tools. As your content changes, the plugin intelligently updates
|
|
14
|
-
these translation files, preserving existing translations while flagging
|
|
15
|
-
changed or new content. When you rebuild your site, translated strings are
|
|
16
|
-
automatically applied to localized versions of your pages, producing fully
|
|
17
|
-
translated HTML output with locale-prefixed URLs.
|
|
3
|
+
`jekyll-l10n` is a Jekyll plugin that streamlines the localization of static websites using industry-standard GNU Gettext PO files. It extracts translatable strings from your site's HTML, organizes them into PO files for professional translation, and automatically applies translations to generate fully localized pages with locale-prefixed URLs.
|
|
18
4
|
|
|
19
5
|

|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
prefixes, flexible fallback modes when translations are incomplete (display
|
|
23
|
-
original English, mark untranslated strings, or leave blank), and support for
|
|
24
|
-
compendium files to share common translations across your site. By leveraging
|
|
25
|
-
the standard Gettext format and workflow, `jekyll-l10n` enables collaboration
|
|
26
|
-
with professional translators and integrates with existing translation
|
|
27
|
-
management systems, while keeping your source content in Markdown where it
|
|
28
|
-
belongs.
|
|
7
|
+
## Installation
|
|
29
8
|
|
|
30
|
-
|
|
9
|
+
Add to your Jekyll site's `Gemfile`:
|
|
31
10
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
bundle install # Install Ruby dependencies
|
|
36
|
-
pip install pre-commit # Install pre-commit framework
|
|
37
|
-
pre-commit install # Install pre-commit hooks
|
|
11
|
+
```ruby
|
|
12
|
+
gem "jekyll-l10n", "~> 1.1"
|
|
38
13
|
```
|
|
39
14
|
|
|
40
|
-
|
|
15
|
+
Run `bundle install`, then enable in `_config.yml`:
|
|
41
16
|
|
|
42
|
-
|
|
17
|
+
```yaml
|
|
18
|
+
plugins:
|
|
19
|
+
- jekyll-l10n
|
|
20
|
+
|
|
21
|
+
defaults:
|
|
22
|
+
- scope:
|
|
23
|
+
type: "pages"
|
|
24
|
+
values:
|
|
25
|
+
with_locales: true
|
|
26
|
+
with_locales_data:
|
|
27
|
+
locales: ["es", "fr", "de"]
|
|
28
|
+
locales_dir: "_locales"
|
|
29
|
+
extract_on_build: true
|
|
30
|
+
```
|
|
43
31
|
|
|
44
|
-
|
|
32
|
+
## Quick Example
|
|
45
33
|
|
|
46
|
-
|
|
47
|
-
sites with many locales, this can impact build performance. You can enable
|
|
48
|
-
incremental build support to skip regenerating pages that haven't changed:
|
|
34
|
+
Edit PO files in `_locales/` with translations. On rebuild, your site will generate:
|
|
49
35
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
incremental: true # Enable incremental builds
|
|
54
|
-
```
|
|
36
|
+
- `/es/` - Spanish pages
|
|
37
|
+
- `/fr/` - French pages
|
|
38
|
+
- `/de/` - German pages
|
|
55
39
|
|
|
56
|
-
|
|
40
|
+
## Documentation
|
|
57
41
|
|
|
58
|
-
|
|
59
|
-
localization_gettext:
|
|
60
|
-
incremental: true
|
|
61
|
-
```
|
|
42
|
+
Complete documentation is available in the [docs/](docs/) directory:
|
|
62
43
|
|
|
63
|
-
|
|
44
|
+
- **[Getting Started](docs/getting-started/)** - Installation requirements and first-site setup
|
|
45
|
+
- **[Configuration Guide](docs/configuration/)** - All configuration options and settings
|
|
46
|
+
- **[Guides](docs/guides/)** - Fallback modes, Liquid filters, machine translation, incremental builds
|
|
47
|
+
- **[Development](docs/development/)** - Setup, testing, API documentation
|
|
48
|
+
- **[Examples](docs/examples/)** - Real-world usage examples
|
|
64
49
|
|
|
65
|
-
|
|
66
|
-
- Any PO translation files have been updated
|
|
67
|
-
- The Jekyll configuration has changed
|
|
50
|
+
## Key Features
|
|
68
51
|
|
|
69
|
-
|
|
70
|
-
translations
|
|
52
|
+
- **Automatic extraction** - Extract translatable strings during Jekyll builds
|
|
53
|
+
- **Professional workflows** - Edit translations using standard Gettext tools (Poedit, Weblate, etc.)
|
|
54
|
+
- **Fallback modes** - Display English, mark untranslated strings, or leave blank
|
|
55
|
+
- **Incremental builds** - Skip regenerating unchanged localized pages for faster builds
|
|
56
|
+
- **Machine translation** - Optional LibreTranslate integration for initial translations
|
|
57
|
+
- **Compendium files** - Share common translations across your entire site
|
|
71
58
|
|
|
72
|
-
##
|
|
59
|
+
## Requirements
|
|
73
60
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
automatically translate newly extracted strings during the extraction workflow.
|
|
77
|
-
You can enable this optional integration on a per-locale basis, allowing
|
|
78
|
-
LibreTranslate to generate initial translations for compendia files that serve
|
|
79
|
-
as a foundation for professional translators to refine. This significantly
|
|
80
|
-
reduces the initial translation effort and helps bootstrap localization for new
|
|
81
|
-
content, though professional review is still recommended for
|
|
82
|
-
publication-quality results.
|
|
61
|
+
- **Ruby**: >= 2.7.0
|
|
62
|
+
- **Jekyll**: >= 4.0, < 5.0
|
|
83
63
|
|
|
84
64
|
## License
|
|
85
65
|
|
|
@@ -88,7 +68,7 @@ MIT License - see LICENSE file for details.
|
|
|
88
68
|
## Contributing
|
|
89
69
|
|
|
90
70
|
1. Fork the repository
|
|
91
|
-
2. Create
|
|
71
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
92
72
|
3. Commit your changes (`git commit -m 'feat: Add amazing feature'`)
|
|
93
73
|
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
94
74
|
5. Open a Pull Request
|
|
@@ -22,27 +22,27 @@ module Jekyll
|
|
|
22
22
|
# Matches ISO 639-1 (2 letter language) with optional ISO 3166-1 (2 letter country)
|
|
23
23
|
# Examples: 'en', 'es', 'fr', 'pt_BR', 'zh_CN'
|
|
24
24
|
# @return [Regexp]
|
|
25
|
-
LOCALE_PATTERN =
|
|
25
|
+
LOCALE_PATTERN = /^[a-z]{2}(_[A-Z]{2})?$/.freeze
|
|
26
26
|
|
|
27
27
|
# ## Translation Fallback Modes
|
|
28
28
|
|
|
29
29
|
# Fallback mode: use original English text if translation not found
|
|
30
30
|
# @return [String] "english"
|
|
31
|
-
FALLBACK_MODE_ENGLISH =
|
|
31
|
+
FALLBACK_MODE_ENGLISH = 'english'
|
|
32
32
|
|
|
33
|
-
# Fallback mode: wrap untranslated text with markers (e.g., "[UNTRANSLATED
|
|
33
|
+
# Fallback mode: wrap untranslated text with markers (e.g., "[UNTRANSLATED] text")
|
|
34
34
|
# @return [String] "marker"
|
|
35
|
-
FALLBACK_MODE_MARKER =
|
|
35
|
+
FALLBACK_MODE_MARKER = 'marker'
|
|
36
36
|
|
|
37
37
|
# Fallback mode: leave text blank if no translation found
|
|
38
38
|
# @return [String] "empty"
|
|
39
|
-
FALLBACK_MODE_EMPTY =
|
|
39
|
+
FALLBACK_MODE_EMPTY = 'empty'
|
|
40
40
|
|
|
41
41
|
# ## Translation Markers
|
|
42
42
|
|
|
43
43
|
# Marker used to indicate untranslated strings in marker fallback mode
|
|
44
44
|
# @return [String] "[UNTRANSLATED]"
|
|
45
|
-
UNTRANSLATED_MARKER =
|
|
45
|
+
UNTRANSLATED_MARKER = '[UNTRANSLATED]'
|
|
46
46
|
|
|
47
47
|
# ## PO File Formatting (GNU Gettext Standard)
|
|
48
48
|
|
|
@@ -82,7 +82,7 @@ module Jekyll
|
|
|
82
82
|
|
|
83
83
|
# Default directory for storing PO translation files
|
|
84
84
|
# @return [String] "_locales"
|
|
85
|
-
DEFAULT_LOCALES_DIR =
|
|
85
|
+
DEFAULT_LOCALES_DIR = '_locales'
|
|
86
86
|
|
|
87
87
|
# Default fallback mode when translation is not found
|
|
88
88
|
# @return [String] "english"
|
|
@@ -90,10 +90,15 @@ module Jekyll
|
|
|
90
90
|
|
|
91
91
|
# Default HTML attributes to extract from elements (can be overridden per-page)
|
|
92
92
|
# @return [Array<String>] ["title", "alt", "aria-label", "placeholder", "aria-description"]
|
|
93
|
-
DEFAULT_TRANSLATABLE_ATTRIBUTES = %w
|
|
93
|
+
DEFAULT_TRANSLATABLE_ATTRIBUTES = %w[title alt aria-label placeholder aria-description].freeze
|
|
94
94
|
|
|
95
95
|
# ## LibreTranslate Integration Defaults
|
|
96
96
|
|
|
97
|
+
# Default LibreTranslate API endpoint URL
|
|
98
|
+
# Uses localhost:5000 as default for local development
|
|
99
|
+
# @return [String] "http://localhost:5000"
|
|
100
|
+
DEFAULT_LIBRETRANSLATE_API_URL = 'http://localhost:5000'
|
|
101
|
+
|
|
97
102
|
# Default timeout (in seconds) for LibreTranslate API requests
|
|
98
103
|
# @return [Integer] 300 seconds (5 minutes)
|
|
99
104
|
DEFAULT_LIBRETRANSLATE_TIMEOUT = 300
|
|
@@ -125,12 +130,12 @@ module Jekyll
|
|
|
125
130
|
# Default source locale for LibreTranslate API
|
|
126
131
|
# The language of the original content being translated
|
|
127
132
|
# @return [String] "en" (English)
|
|
128
|
-
DEFAULT_LIBRETRANSLATE_SOURCE_LOCALE =
|
|
133
|
+
DEFAULT_LIBRETRANSLATE_SOURCE_LOCALE = 'en'
|
|
129
134
|
|
|
130
135
|
# Default text format for LibreTranslate API requests
|
|
131
136
|
# Either 'text' (plain text) or 'html' (preserves markup)
|
|
132
137
|
# @return [String] "html"
|
|
133
|
-
DEFAULT_LIBRETRANSLATE_FORMAT =
|
|
138
|
+
DEFAULT_LIBRETRANSLATE_FORMAT = 'html'
|
|
134
139
|
end
|
|
135
140
|
end
|
|
136
141
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
3
|
+
require_relative '../po_file/manager'
|
|
4
|
+
require_relative '../utils/page_locales_config'
|
|
5
|
+
require_relative '../utils/site_config_accessor'
|
|
6
6
|
|
|
7
7
|
module Jekyll
|
|
8
8
|
module L10n
|
|
@@ -34,7 +34,7 @@ module Jekyll
|
|
|
34
34
|
def initialize(site)
|
|
35
35
|
@site = site
|
|
36
36
|
with_locales_data = SiteConfigAccessor.extract_locales_data(@site)
|
|
37
|
-
@site_config = PageLocalesConfig.new({
|
|
37
|
+
@site_config = PageLocalesConfig.new({ 'with_locales_data' => with_locales_data })
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Merge page-specific PO files into compendia for all locales.
|
|
@@ -55,6 +55,7 @@ module Jekyll
|
|
|
55
55
|
private
|
|
56
56
|
|
|
57
57
|
# Process a single locale: merge existing compendium with new page translations
|
|
58
|
+
# Skips writing when translations are unchanged to avoid unnecessary file rewrites
|
|
58
59
|
def process_locale(locale, po_manager, config)
|
|
59
60
|
compendium_path = File.join(@site.source, config.locales_dir, "#{locale}.po")
|
|
60
61
|
existing_compendium = load_existing_compendium(compendium_path)
|
|
@@ -62,12 +63,27 @@ module Jekyll
|
|
|
62
63
|
|
|
63
64
|
combined = build_combined_hash(existing_compendium)
|
|
64
65
|
merge_into_combined(combined, merged)
|
|
65
|
-
combined_entries = format_compendium_entries(combined)
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
unless compendium_unchanged?(existing_compendium, combined)
|
|
68
|
+
combined_entries = format_compendium_entries(combined)
|
|
69
|
+
po_manager.save_compendium(locale, combined_entries)
|
|
70
|
+
end
|
|
68
71
|
cleanup_locale_directory(locale, config)
|
|
69
72
|
end
|
|
70
73
|
|
|
74
|
+
# Check if compendium content is unchanged (same msgids and msgstrs)
|
|
75
|
+
def compendium_unchanged?(existing_compendium, combined)
|
|
76
|
+
return false if existing_compendium.empty? && !combined.empty?
|
|
77
|
+
|
|
78
|
+
normalized_existing = build_combined_hash(existing_compendium)
|
|
79
|
+
return false unless normalized_existing.keys.sort == combined.keys.sort
|
|
80
|
+
|
|
81
|
+
combined.all? do |msgid, data|
|
|
82
|
+
existing = normalized_existing[msgid]
|
|
83
|
+
existing && existing[:msgstr] == data[:msgstr]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
71
87
|
# Load existing compendium translations or return empty hash if not found
|
|
72
88
|
def load_existing_compendium(compendium_path)
|
|
73
89
|
if File.exist?(compendium_path)
|
|
@@ -89,9 +105,9 @@ module Jekyll
|
|
|
89
105
|
# Normalize entry format to ensure consistent hash structure with :msgstr and :reference keys
|
|
90
106
|
def normalize_compendium_entry(data)
|
|
91
107
|
if data.is_a?(Hash)
|
|
92
|
-
{ :
|
|
108
|
+
{ msgstr: data[:msgstr], reference: data[:reference] }
|
|
93
109
|
else
|
|
94
|
-
{ :
|
|
110
|
+
{ msgstr: data, reference: nil }
|
|
95
111
|
end
|
|
96
112
|
end
|
|
97
113
|
|
|
@@ -108,20 +124,24 @@ module Jekyll
|
|
|
108
124
|
|
|
109
125
|
# Update reference for existing entry if new reference is available
|
|
110
126
|
def update_entry_reference(combined_entry, entry)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
127
|
+
return unless combined_entry[:reference].nil? && entry.is_a?(Hash) && entry[:reference]
|
|
128
|
+
|
|
129
|
+
combined_entry[:reference] = entry[:reference]
|
|
114
130
|
end
|
|
115
131
|
|
|
116
|
-
# Create new entry
|
|
132
|
+
# Create new entry preserving any existing translation and reference
|
|
117
133
|
def create_new_entry(entry)
|
|
118
|
-
|
|
134
|
+
if entry.is_a?(Hash)
|
|
135
|
+
{ msgstr: entry[:msgstr] || '', reference: entry[:reference] }
|
|
136
|
+
else
|
|
137
|
+
{ msgstr: '', reference: nil }
|
|
138
|
+
end
|
|
119
139
|
end
|
|
120
140
|
|
|
121
141
|
# Convert combined hash to array of entries suitable for PO file writing
|
|
122
142
|
def format_compendium_entries(combined)
|
|
123
143
|
combined.map do |msgid, data|
|
|
124
|
-
entry = { :
|
|
144
|
+
entry = { msgid: msgid, msgstr: data[:msgstr] }
|
|
125
145
|
entry[:reference] = data[:reference] if data[:reference]
|
|
126
146
|
entry
|
|
127
147
|
end
|
|
@@ -134,9 +154,10 @@ module Jekyll
|
|
|
134
154
|
FileUtils.rm_rf(locale_dir) if File.directory?(locale_dir)
|
|
135
155
|
end
|
|
136
156
|
|
|
137
|
-
private :process_locale, :
|
|
138
|
-
:
|
|
139
|
-
:create_new_entry, :format_compendium_entries,
|
|
157
|
+
private :process_locale, :compendium_unchanged?, :load_existing_compendium,
|
|
158
|
+
:build_combined_hash, :normalize_compendium_entry, :merge_into_combined,
|
|
159
|
+
:update_entry_reference, :create_new_entry, :format_compendium_entries,
|
|
160
|
+
:cleanup_locale_directory
|
|
140
161
|
end
|
|
141
162
|
end
|
|
142
163
|
end
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
3
|
+
require_relative '../po_file/manager'
|
|
4
|
+
require_relative '../utils/page_locales_config'
|
|
5
|
+
require_relative '../utils/po_entry_converter'
|
|
6
|
+
require_relative '../utils/site_config_accessor'
|
|
7
|
+
require_relative '../utils/logger_formatter'
|
|
8
|
+
require_relative '../translation/libre_translator'
|
|
9
9
|
|
|
10
10
|
module Jekyll
|
|
11
11
|
module L10n
|
|
@@ -34,7 +34,7 @@ module Jekyll
|
|
|
34
34
|
def initialize(site)
|
|
35
35
|
@site = site
|
|
36
36
|
with_locales_data = SiteConfigAccessor.extract_locales_data(@site)
|
|
37
|
-
@site_config = PageLocalesConfig.new({
|
|
37
|
+
@site_config = PageLocalesConfig.new({ 'with_locales_data' => with_locales_data })
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Translate compendia for all configured locales.
|
|
@@ -68,7 +68,7 @@ module Jekyll
|
|
|
68
68
|
|
|
69
69
|
def process_single_locale(locale, config, translator, po_manager)
|
|
70
70
|
compendium_path = File.join(@site.source, config.locales_dir, "#{locale}.po")
|
|
71
|
-
LoggerFormatter.debug_if_enabled(
|
|
71
|
+
LoggerFormatter.debug_if_enabled('CompendiumTranslator',
|
|
72
72
|
"Processing compendium file: #{compendium_path}")
|
|
73
73
|
return unless File.exist?(compendium_path)
|
|
74
74
|
|
|
@@ -86,29 +86,29 @@ module Jekyll
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def log_compendia_enabled_check(enabled)
|
|
89
|
-
msg =
|
|
90
|
-
LoggerFormatter.debug_if_enabled(
|
|
89
|
+
msg = 'translate_compendia_for_locale called, libretranslate_enabled:'
|
|
90
|
+
LoggerFormatter.debug_if_enabled('CompendiumTranslator', "#{msg} #{enabled}")
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
def log_compendium_stats(locale, po_entries, compendium_path)
|
|
94
94
|
msg = "Locale #{locale}: Loaded #{po_entries.length} entries from compendium"
|
|
95
|
-
LoggerFormatter.debug_if_enabled(
|
|
95
|
+
LoggerFormatter.debug_if_enabled('CompendiumTranslator', "#{msg} #{compendium_path}")
|
|
96
96
|
|
|
97
97
|
empty_count = count_empty_entries(po_entries)
|
|
98
98
|
LoggerFormatter.debug_if_enabled(
|
|
99
|
-
|
|
99
|
+
'CompendiumTranslator',
|
|
100
100
|
"Locale #{locale}: #{empty_count} empty msgstr entries to translate"
|
|
101
101
|
)
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def log_translation_complete_for_locale(locale)
|
|
105
105
|
msg = "Locale #{locale}: Translation complete, saving compendium"
|
|
106
|
-
LoggerFormatter.debug_if_enabled(
|
|
106
|
+
LoggerFormatter.debug_if_enabled('CompendiumTranslator', msg)
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
def log_compendium_saved(locale, compendium_path)
|
|
110
110
|
msg = "Locale #{locale}: Saved compendium to"
|
|
111
|
-
LoggerFormatter.debug_if_enabled(
|
|
111
|
+
LoggerFormatter.debug_if_enabled('CompendiumTranslator', "#{msg} #{compendium_path}")
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
def count_empty_entries(po_entries)
|
|
@@ -118,14 +118,14 @@ module Jekyll
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
def log_translation_start(config)
|
|
121
|
-
locales = config.locales.join(
|
|
122
|
-
Jekyll.logger.info
|
|
121
|
+
locales = config.locales.join(', ')
|
|
122
|
+
Jekyll.logger.info 'Localization',
|
|
123
123
|
"Starting LibreTranslate translation for locales: #{locales}"
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
def log_translation_complete(config)
|
|
127
|
-
locales = config.locales.join(
|
|
128
|
-
Jekyll.logger.info
|
|
127
|
+
locales = config.locales.join(', ')
|
|
128
|
+
Jekyll.logger.info 'Localization',
|
|
129
129
|
"LibreTranslate translation complete for locales: #{locales}"
|
|
130
130
|
end
|
|
131
131
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
3
|
+
require_relative '../utils/page_locales_config'
|
|
4
4
|
|
|
5
5
|
module Jekyll
|
|
6
6
|
module L10n
|
|
@@ -69,10 +69,10 @@ module Jekyll
|
|
|
69
69
|
# @return [Hash, nil] Page front matter data if found, nil otherwise
|
|
70
70
|
def find_page_config_for_file(file_path)
|
|
71
71
|
@site.pages.each do |page|
|
|
72
|
-
next unless page.data[
|
|
72
|
+
next unless page.data['with_locales'] == true
|
|
73
73
|
|
|
74
|
-
page_output = page.output_ext ? page.destination(
|
|
75
|
-
next unless file_path.end_with?(page_output.sub(%r
|
|
74
|
+
page_output = page.output_ext ? page.destination('') : page.destination('/')
|
|
75
|
+
next unless file_path.end_with?(page_output.sub(%r{/$}, '/index.html'))
|
|
76
76
|
|
|
77
77
|
return page.data
|
|
78
78
|
end
|
|
@@ -88,10 +88,10 @@ module Jekyll
|
|
|
88
88
|
# @param file_path [String] Path to file to check
|
|
89
89
|
# @return [Boolean] True if file is in a locale subdirectory
|
|
90
90
|
def skip_localized_page?(file_path)
|
|
91
|
-
relative_path = file_path.sub(@dest,
|
|
91
|
+
relative_path = file_path.sub(@dest, '')
|
|
92
92
|
|
|
93
93
|
all_locales = @site.pages.map do |p|
|
|
94
|
-
p.data.dig(
|
|
94
|
+
p.data.dig('with_locales_data', 'locales') || []
|
|
95
95
|
end
|
|
96
96
|
all_locales.flatten!
|
|
97
97
|
all_locales.uniq!
|
|
@@ -106,8 +106,8 @@ module Jekyll
|
|
|
106
106
|
# @param config [Hash] Page front matter data
|
|
107
107
|
# @return [Array<String>] CSS selectors for excluded elements
|
|
108
108
|
def extract_exclude_selectors(config)
|
|
109
|
-
config.data.dig(
|
|
110
|
-
[
|
|
109
|
+
config.data.dig('with_locales_data', 'extraction', 'exclude_selectors') ||
|
|
110
|
+
['script', 'style', 'code.language-plaintext', 'pre code']
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
113
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
3
|
+
require_relative '../utils/xpath_reference_generator'
|
|
4
|
+
require_relative '../utils/text_validator'
|
|
5
5
|
|
|
6
6
|
module Jekyll
|
|
7
7
|
module L10n
|
|
@@ -24,7 +24,7 @@ module Jekyll
|
|
|
24
24
|
# ['title', 'alt', 'aria-label'])
|
|
25
25
|
# # Returns array of extraction entries for all valid attribute values
|
|
26
26
|
module DomAttributeExtractor
|
|
27
|
-
|
|
27
|
+
module_function
|
|
28
28
|
|
|
29
29
|
# Extract attribute values from an HTML element.
|
|
30
30
|
#
|
|
@@ -47,7 +47,7 @@ module Jekyll
|
|
|
47
47
|
attrs = extract_node_attributes(node, translatable_attrs)
|
|
48
48
|
attrs.map do |attr_text, attr_name|
|
|
49
49
|
reference = XPathReferenceGenerator.generate(node, file_path, dest, attr_name)
|
|
50
|
-
{ :
|
|
50
|
+
{ msgid: attr_text, msgstr: '', reference: reference }
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
3
|
+
require_relative '../utils/text_normalizer'
|
|
4
|
+
require_relative '../utils/xpath_reference_generator'
|
|
5
|
+
require_relative '../utils/html_text_utils'
|
|
6
|
+
require_relative '../utils/html_elements'
|
|
7
|
+
require_relative '../utils/text_validator'
|
|
8
8
|
|
|
9
9
|
module Jekyll
|
|
10
10
|
module L10n
|
|
@@ -48,7 +48,7 @@ module Jekyll
|
|
|
48
48
|
return nil if text.nil?
|
|
49
49
|
|
|
50
50
|
reference = XPathReferenceGenerator.generate(node, file_path, dest)
|
|
51
|
-
{ :
|
|
51
|
+
{ msgid: text, msgstr: '', reference: reference }
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def extractable?(node)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
9
|
-
require_relative
|
|
3
|
+
require_relative 'config_loader'
|
|
4
|
+
require_relative 'result_saver'
|
|
5
|
+
require_relative 'logger'
|
|
6
|
+
require_relative 'html_string_extractor'
|
|
7
|
+
require_relative '../utils/file_operations'
|
|
8
|
+
require_relative '../utils/site_config_accessor'
|
|
9
|
+
require_relative '../utils/error_handler'
|
|
10
10
|
|
|
11
11
|
module Jekyll
|
|
12
12
|
module L10n
|
|
@@ -72,7 +72,7 @@ module Jekyll
|
|
|
72
72
|
# result = extractor.extract_site
|
|
73
73
|
# puts "Processed #{result[:files_processed]} files"
|
|
74
74
|
def extract_site
|
|
75
|
-
Jekyll.logger.info
|
|
75
|
+
Jekyll.logger.info 'Localization', 'Extracting translatable strings...'
|
|
76
76
|
start_time = Time.now
|
|
77
77
|
stats = process_all_html_files
|
|
78
78
|
translate_all_compendia
|
|
@@ -81,8 +81,8 @@ module Jekyll
|
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def process_all_html_files
|
|
84
|
-
stats = { :
|
|
85
|
-
html_files = Dir.glob(File.join(@dest,
|
|
84
|
+
stats = { files_processed: 0, strings_extracted: 0, po_files_created: 0 }
|
|
85
|
+
html_files = Dir.glob(File.join(@dest, '**', '*.html'))
|
|
86
86
|
|
|
87
87
|
html_files.each do |file_path|
|
|
88
88
|
next if @config_loader.skip_localized_page?(file_path)
|
|
@@ -100,7 +100,7 @@ module Jekyll
|
|
|
100
100
|
config = find_libretranslate_config
|
|
101
101
|
return unless config
|
|
102
102
|
|
|
103
|
-
ErrorHandler.handle_with_logging(
|
|
103
|
+
ErrorHandler.handle_with_logging('machine translation') do
|
|
104
104
|
@result_saver.translate_compendia(config)
|
|
105
105
|
end
|
|
106
106
|
end
|
|
@@ -120,7 +120,7 @@ module Jekyll
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
def default_stats
|
|
123
|
-
{ :
|
|
123
|
+
{ files_processed: 0, strings_extracted: 0, po_files_created: 0 }
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
def extract_strings_from_file(file_path, config)
|
|
@@ -137,9 +137,9 @@ module Jekyll
|
|
|
137
137
|
return nil unless @site.respond_to?(:pages)
|
|
138
138
|
|
|
139
139
|
@site.pages.each do |page|
|
|
140
|
-
next unless page.data[
|
|
140
|
+
next unless page.data['with_locales'] == true
|
|
141
141
|
|
|
142
|
-
config = @config_loader.load_page_config(page.destination(
|
|
142
|
+
config = @config_loader.load_page_config(page.destination(''))
|
|
143
143
|
return config if config.libretranslate_enabled?
|
|
144
144
|
end
|
|
145
145
|
|
|
@@ -149,7 +149,7 @@ module Jekyll
|
|
|
149
149
|
private
|
|
150
150
|
|
|
151
151
|
def construct_page_path(file_path)
|
|
152
|
-
file_path.sub("#{@dest}/",
|
|
152
|
+
file_path.sub("#{@dest}/", '')
|
|
153
153
|
end
|
|
154
154
|
end
|
|
155
155
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
3
|
+
require_relative 'dom_text_extractor'
|
|
4
|
+
require_relative 'dom_attribute_extractor'
|
|
5
|
+
require_relative 'logger'
|
|
6
6
|
|
|
7
7
|
module Jekyll
|
|
8
8
|
module L10n
|
|
@@ -92,10 +92,10 @@ module Jekyll
|
|
|
92
92
|
|
|
93
93
|
@exclude_selectors.any? { |selector| node.matches?(selector) }
|
|
94
94
|
rescue Nokogiri::CSS::SyntaxError => e
|
|
95
|
-
Jekyll.logger.warn
|
|
95
|
+
Jekyll.logger.warn 'Localization', "CSS selector syntax error: #{e.message}"
|
|
96
96
|
false
|
|
97
97
|
rescue StandardError => e
|
|
98
|
-
Jekyll.logger.warn
|
|
98
|
+
Jekyll.logger.warn 'Localization', "Selector matching error (continuing): #{e.message}"
|
|
99
99
|
false
|
|
100
100
|
end
|
|
101
101
|
end
|