jekyll-polyglot 1.10.0 → 1.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0459aa05cc1025bea0ca2aef71ad7e3e4a0c33726b1f32e4d5f2e19d521a06aa'
4
- data.tar.gz: 245aa4b72aa80fac516b0cc9b04c396ef0ec55366aadefbb73ba8fa9ca47df93
3
+ metadata.gz: 36484f859398f365dc36080eb9a72a170606e0150726a6447349062ee5bbba66
4
+ data.tar.gz: f09be79e1c07abc021f7d16fd6fc52da4f8dd04d0a424557163bebd6be509d99
5
5
  SHA512:
6
- metadata.gz: d8f4162ee486ebf72f6c5be6714bb5aed0a60f5ca0798480efbc8a68f84cd94c40b9db9efbb6160317ca974f600cbe82ac82c0934a833d162135b873a3b000da
7
- data.tar.gz: c5367a20ee1ea4983e5cc8485204f11da88d4a3914ccfc5aa62d76d78cd364fd15c223e99a4bbec5f27d306c7be4226232faa3c17b38951dd9102ead2e5e02c7
6
+ metadata.gz: 3bb811b6db49a5db36edfa1aade491de2cb4c068f8e7a255c9b636a682525fd525a98dedaa83666957b0f539ad51a32c512e2ffd494811b3e36583bd6b078ebd
7
+ data.tar.gz: 002ffded1c695403ce4f9c036facd6235cac6c3e7764a4cf9c33c2a5a383918153283f923b4ee02dcdc34d1b118cf0434595369e7896b33d3dc6d16b58b6a6cd
data/README.md CHANGED
@@ -28,7 +28,7 @@ In your `_config.yml` file, add the following preferences
28
28
  ```YAML
29
29
  languages: ["en", "sv", "de", "fr"]
30
30
  default_lang: "en"
31
- exclude_from_localization: ["javascript", "images", "css", "public", "sitemap"]
31
+ exclude_from_localization: ["javascript", "images", "css", "public", "sitemap", "CNAME"]
32
32
  parallel_localization: true
33
33
  url: https://polyglot.untra.io
34
34
  ```
@@ -59,8 +59,7 @@ _posts/2010-03-01-salad-recipes-sv.md
59
59
  _posts/2010-03-01-salad-recipes-fr.md
60
60
  ```
61
61
 
62
- Organized names will generate consistent permalinks when the post is rendered, and Polyglot will know to build separate language versions of
63
- the website using only the files with the correct `lang` variable in the front matter.
62
+ Organized names will generate consistent permalinks when the post is rendered, and Polyglot will know to build separate language versions of the website using only the files with the correct `lang` variable in the front matter.
64
63
 
65
64
  In short:
66
65
  * Be consistent with how you name and place your *posts* files
@@ -114,6 +113,8 @@ Estos somos nosotros!
114
113
  Additionally, if you are also using the `jekyll-redirect-from` plugin, pages coordinated this way will automatically have redirects created between pages.
115
114
  So `/es/about` will automatically redirect to `/es/acerca-de` and `/acerca-de` can redirect to `/about`. If you use this approach, be sure to also employ a customized [redirect.html](https://github.com/untra/polyglot/blob/main/site/_layouts/redirect.html).
116
115
 
116
+ As of version 1.12, Polyglot also properly supports `redirect_from` frontmatter across sublanguages. When you add redirect paths to pages in non-default languages, Polyglot will correctly scope those redirects to each language's prefix, preventing duplicate redirects and ensuring proper routing.
117
+
117
118
  #### Fallback Language Support
118
119
  Lets say you are building your website. You have an `/about/` page written in *english*, *german* and
119
120
  *swedish*. You are also supporting a *french* website, but you never designed a *french* version of your `/about/` page!
@@ -260,11 +261,48 @@ This plugin stands out from other I18n Jekyll plugins.
260
261
  - provides the liquid tag `{{ site.languages }}` to get an array of your I18n strings.
261
262
  - provides the liquid tag `{{ site.default_lang }}` to get the default_lang I18n string.
262
263
  - provides the liquid tag `{{ site.active_lang }}` to get the I18n language string the website was built for. Alternative names for `active_lang` can be configured via `config.lang_vars`.
264
+ - provides the liquid tag `{{ page.rendered_lang }}` to get the language the page content is actually rendered in (useful for detecting fallback pages).
263
265
  - provides the liquid tag `{{ I18n_Headers }}` to append SEO bonuses to your website.
264
266
  - provides the liquid tag `{{ Unrelativized_Link href="/hello" }}` to make urls that do not get influenced by url correction regexes.
265
267
  - provides `site.data` localization for efficient rich text replacement.
266
268
  - a creator that will answer all of your questions and issues.
267
269
 
270
+ ### Detecting Fallback Pages with `page.rendered_lang`
271
+
272
+ The `page.rendered_lang` variable indicates the actual language of a page's content. This is different from `site.active_lang`, which indicates the language version of the site currently being built.
273
+
274
+ - `site.active_lang`: The language the site is being built for (e.g., `es` for the Spanish site)
275
+ - `page.rendered_lang`: The language of the page's actual content (e.g., `en` if no Spanish translation exists)
276
+
277
+ When `page.rendered_lang != site.active_lang`, the page is a **fallback page** - it's being served in the default language because no translation exists.
278
+
279
+ **Example: Showing a "not translated" notice:**
280
+ ```liquid
281
+ {% if page.rendered_lang != site.active_lang %}
282
+ <div class="translation-notice">
283
+ This page is not yet available in {{ site.active_lang }}.
284
+ Showing {{ page.rendered_lang }} version.
285
+ </div>
286
+ {% endif %}
287
+ ```
288
+
289
+ **Example: Conditional content based on translation status:**
290
+ ```liquid
291
+ {% if page.rendered_lang == site.active_lang %}
292
+ <!-- This is an actual translation -->
293
+ <p>Welcome to our {{ site.active_lang }} content!</p>
294
+ {% else %}
295
+ <!-- This is fallback content -->
296
+ <p>Content available in {{ page.rendered_lang }} only.</p>
297
+ {% endif %}
298
+ ```
299
+
300
+ This is useful for:
301
+ - Displaying notices when content hasn't been translated
302
+ - Tracking translation coverage
303
+ - Applying different styling to fallback pages
304
+ - Building translation status dashboards
305
+
268
306
  ## SEO Recipes
269
307
  Jekyll-polyglot has a few spectacular [Search Engine Optimization techniques](https://untra.github.io/polyglot/seo) to ensure your Jekyll blog gets the most out of its multilingual audience. Check them out!
270
308
 
@@ -310,6 +348,10 @@ These are talented and considerate software developers across the world that hav
310
348
  * [@obfusk](https://github.com/obfusk) [1.5.0](https://polyglot.untra.io/2021/07/17/polyglot-1.5.0/)
311
349
  * [@eighthave](https://github.com/eighthave) [1.5.0](https://polyglot.untra.io/2021/07/17/polyglot-1.5.0/)
312
350
  * [@george-gca](https://github.com/george-gca) [pt-BR support](https://polyglot.untra.io/pt-BR/2024/02/29/localized-variables.md)
351
+ * [@PanderMusubi](https://github.com/PanderMusubi) - 1.12 / jekyll-minimal-mistakes-polyglot demo
352
+ * [@GruberMarkus](https://github.com/GruberMarkus) - redirect anchor support
353
+ * [@rathboma](https://github.com/rathboma) - page.rendered_lang / sublanguage redirects
354
+ * [@manabu-nakamura](https://github.com/manabu-nakamura) - Japanese strings
313
355
 
314
356
  ### Other Websites Built with Polyglot
315
357
  Feel free to open a PR and list your multilingual blog here you may want to share:
@@ -328,18 +370,21 @@ Feel free to open a PR and list your multilingual blog here you may want to shar
328
370
  * [AnotherTurret just another study note blog](https://aturret.space/)
329
371
  * [Diciotech is a collaborative online tech dictionary](https://diciotech.netlify.app/)
330
372
  * [Yunseo Kim's Study Notes](https://www.yunseo.kim/)
373
+ * [Beekeeper Studio](https://www.beekeeperstudio.io/)
331
374
 
332
375
  ## 2.0 Roadmap
333
376
  * [x] - **site language**: portuguese Brazil `pt-BR`
334
- * [ ] - **site language**: portuguese Portugal `pt-PT`
335
- * [ ] - **site language**: arabic `ar`
336
- * [ ] - **site language**: japanese `ja`
377
+ * [x] - **site language**: arabic `ar`
378
+ * [x] - **site language**: japanese `ja`
337
379
  * [x] - **site language**: russian `ru`
338
380
  * [x] - **site language**: dutch `nl`
339
381
  * [x] - **site language**: korean `ko`
340
382
  * [x] - **site language**: hebrew `he`
341
383
  * [x] - **site language**: chinese China `zh-CN`
384
+ * [x] - **site language**: italian `it`
385
+ * [x] - **site language**: turkish `tk`
342
386
  * [ ] - **site language**: chinese Taiwan `zh-TW`
387
+ * [ ] - **site language**: portuguese Portugal `pt-PT`
343
388
  * [ ] - get whitelisted as an official github-pages jekyll plugin
344
389
  * [x] - update CI provider
345
390
 
@@ -18,7 +18,7 @@ module Jekyll
18
18
  permalink_lang = page['permalink_lang']
19
19
  baseurl = site.config['baseurl'] || ''
20
20
  site_url = @url.empty? ? site.config['url'] + baseurl : @url
21
- i18n = "<meta http-equiv=\"Content-Language\" content=\"#{site.active_lang}\">\n"
21
+ i18n = ""
22
22
 
23
23
  # Find all documents with the same page_id
24
24
  docs_with_same_id = site.collections.values
@@ -33,17 +33,29 @@ module Jekyll
33
33
  current_lang = site.active_lang
34
34
  current_permalink = lang_to_permalink[current_lang] || (permalink_lang && permalink_lang[current_lang]) || permalink
35
35
  current_permalink = "/#{current_permalink}" unless current_permalink.start_with?("/")
36
- current_lang_prefix = current_lang == site.default_lang ? '' : "/#{current_lang}"
37
- i18n += "<link rel=\"canonical\" href=\"#{site_url}#{current_lang_prefix}#{current_permalink}\"/>\n"
36
+ # Don't add language prefix if it's already in the permalink
37
+ canonical_permalink = if current_lang == site.default_lang
38
+ current_permalink
39
+ else
40
+ current_permalink.start_with?("/#{current_lang}/") ? current_permalink : "/#{current_lang}#{current_permalink}"
41
+ end
42
+ i18n += "<link rel=\"canonical\" href=\"#{site_url}#{canonical_permalink}\"/>\n"
43
+
44
+ # Get the default language permalink for x-default
45
+ default_lang_permalink = lang_to_permalink[site.default_lang] || (permalink_lang && permalink_lang[site.default_lang]) || permalink
46
+ default_lang_permalink = "/#{default_lang_permalink}" unless default_lang_permalink.start_with?("/")
38
47
 
39
48
  site.languages.each do |lang|
40
49
  alt_permalink = lang_to_permalink[lang] || (permalink_lang && permalink_lang[lang]) || permalink
41
50
  alt_permalink = "/#{alt_permalink}" unless alt_permalink.start_with?("/")
42
51
  i18n += if lang == site.default_lang
43
52
  "<link rel=\"alternate\" hreflang=\"#{lang}\" href=\"#{site_url}#{alt_permalink}\"/>\n" \
44
- "<link rel=\"alternate\" hreflang=\"x-default\" href=\"#{site_url}#{alt_permalink}\"/>\n"
53
+ "<link rel=\"alternate\" hreflang=\"x-default\" href=\"#{site_url}#{default_lang_permalink}\"/>\n"
45
54
  else
46
- "<link rel=\"alternate\" hreflang=\"#{lang}\" href=\"#{site_url}/#{lang}#{alt_permalink}\"/>\n"
55
+ # For non-default languages, use the language-specific permalink directly
56
+ # Don't add the language prefix if it's already in the permalink
57
+ lang_permalink = alt_permalink.start_with?("/#{lang}/") ? alt_permalink : "/#{lang}#{alt_permalink}"
58
+ "<link rel=\"alternate\" hreflang=\"#{lang}\" href=\"#{site_url}#{lang_permalink}\"/>\n"
47
59
  end
48
60
  end
49
61
  i18n
@@ -64,7 +64,7 @@ module Jekyll
64
64
  process_language lang
65
65
  end
66
66
  end
67
- Jekyll::Hooks.trigger :polyglot, :post_write
67
+ Jekyll::Hooks.trigger :polyglot, :post_write, self
68
68
  end
69
69
 
70
70
  alias site_payload_orig site_payload
@@ -115,6 +115,16 @@ module Jekyll
115
115
  string.split(regex)
116
116
  end
117
117
 
118
+ # Convert glob pattern to regex pattern
119
+ # * matches any characters except /
120
+ # ? matches any single character except /
121
+ def glob_to_regex(pattern)
122
+ # Escape special regex characters first
123
+ escaped = Regexp.escape(pattern)
124
+ # Convert glob patterns to regex patterns
125
+ escaped.gsub("\\*", '.*').gsub("\\?", '.')
126
+ end
127
+
118
128
  def derive_lang_from_path(doc)
119
129
  unless @lang_from_path
120
130
  return nil
@@ -144,6 +154,9 @@ module Jekyll
144
154
  url = doc.url.gsub(regex, '/')
145
155
  page_id = doc.data['page_id'] || url
146
156
  doc.data['permalink'] = url if doc.data['permalink'].to_s.empty? && !doc.data['lang'].to_s.empty?
157
+ # Set rendered_lang to indicate what language this page is actually rendered in
158
+ # This allows templates to detect fallback pages (rendered_lang != active_lang)
159
+ doc.data['rendered_lang'] = lang
147
160
 
148
161
  # skip entirely if nothing to check
149
162
  next if @file_langs.nil?
@@ -165,22 +178,42 @@ module Jekyll
165
178
  end
166
179
 
167
180
  def assignPageRedirects(doc, docs)
181
+ # Preserve and normalize user-defined redirect_from
182
+ user_redirects = doc.data['redirect_from'] || []
183
+ user_redirects = [user_redirects] unless user_redirects.is_a?(Array)
184
+
185
+ # Determine document language
186
+ doc_lang = doc.data['lang'] || derive_lang_from_path(doc) || @default_lang
187
+
188
+ # Scope user-defined redirects to document's language if non-default
189
+ if doc_lang != @default_lang && !user_redirects.empty?
190
+ user_redirects = user_redirects.map do |redirect_path|
191
+ # Normalize path to start with /
192
+ redirect_path = "/#{redirect_path}" unless redirect_path.start_with?('/')
193
+ # Only prefix if not already prefixed with this language
194
+ if redirect_path.start_with?("/#{doc_lang}/")
195
+ redirect_path
196
+ else
197
+ "/#{doc_lang}#{redirect_path}"
198
+ end
199
+ end
200
+ end
201
+
202
+ # Compute page_id based redirects (cross-language)
203
+ computed_redirects = []
168
204
  pageId = doc.data['page_id']
169
205
  if !pageId.nil? && !pageId.empty?
170
- redirects = []
171
-
172
206
  docs_with_same_id = docs.select { |dd| dd.data['page_id'] == pageId }
173
-
174
- # For each document with the same page_id
175
207
  docs_with_same_id.each do |dd|
176
- # Add redirect if it's a different permalink
177
208
  if dd.data['permalink'] != doc.data['permalink']
178
- redirects << dd.data['permalink']
209
+ computed_redirects << dd.data['permalink']
179
210
  end
180
211
  end
181
-
182
- doc.data['redirect_from'] = redirects
183
212
  end
213
+
214
+ # Merge user-defined and computed redirects, removing duplicates
215
+ all_redirects = (user_redirects + computed_redirects).uniq
216
+ doc.data['redirect_from'] = all_redirects unless all_redirects.empty?
184
217
  end
185
218
 
186
219
  def assignPageLanguagePermalinks(doc, docs)
@@ -236,10 +269,12 @@ module Jekyll
236
269
  regex = ''
237
270
  unless disabled
238
271
  @exclude.each do |x|
239
- regex += "(?!#{x})"
272
+ escaped_x = glob_to_regex(x)
273
+ regex += "(?!#{escaped_x})"
240
274
  end
241
275
  @languages.each do |x|
242
- regex += "(?!#{x}/)"
276
+ escaped_x = Regexp.escape(x)
277
+ regex += "(?!#{escaped_x}/)"
243
278
  end
244
279
  end
245
280
  start = disabled ? 'ferh' : 'href'
@@ -254,10 +289,12 @@ module Jekyll
254
289
  regex = ''
255
290
  unless disabled
256
291
  @exclude.each do |x|
257
- regex += "(?!#{x})"
292
+ escaped_x = glob_to_regex(x)
293
+ regex += "(?!#{escaped_x})"
258
294
  end
259
295
  @languages.each do |x|
260
- regex += "(?!#{x}/)"
296
+ escaped_x = Regexp.escape(x)
297
+ regex += "(?!#{escaped_x}/)"
261
298
  end
262
299
  end
263
300
  start = disabled ? 'ferh' : 'href'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-polyglot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Volin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-07 00:00:00.000000000 Z
11
+ date: 2026-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -71,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
71
  - !ruby/object:Gem::Version
72
72
  version: 3.1.0
73
73
  requirements: []
74
- rubygems_version: 3.3.7
74
+ rubygems_version: 3.3.27
75
75
  signing_key:
76
76
  specification_version: 4
77
77
  summary: I18n plugin for Jekyll Blogs