jekyll-uj-powertools 1.6.0 → 1.6.1

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.
data/lib/tags/image.rb ADDED
@@ -0,0 +1,208 @@
1
+ # Libraries
2
+ require "jekyll"
3
+
4
+ module Jekyll
5
+ class UJImageTag < Liquid::Tag
6
+ def initialize(tag_name, markup, tokens)
7
+ super
8
+ @markup = markup.strip
9
+ end
10
+
11
+ def render(context)
12
+ # Parse arguments
13
+ args = parse_arguments(@markup)
14
+ src_input = args[0]
15
+ options = parse_options(args[1..-1])
16
+
17
+ # Resolve source path
18
+ src = resolve_variable(context, src_input)
19
+ return '' unless src
20
+
21
+ # Check if this is an external URL
22
+ is_external = !!(src =~ /^https?:\/\//)
23
+
24
+ if is_external
25
+ # For external URLs, just create a simple responsive img tag
26
+ build_external_image(src, options)
27
+ else
28
+ # Extract file extension
29
+ extension = File.extname(src)
30
+ src_path = src.chomp(extension)
31
+
32
+ # Determine max width
33
+ max_width = options['max_width'] || options['max-width'] || false
34
+ max_width = max_width.to_s if max_width
35
+
36
+ # Build picture element for local images
37
+ build_picture_element(src, src_path, extension, max_width, options)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def parse_arguments(markup)
44
+ args = []
45
+ current_arg = ''
46
+ in_quotes = false
47
+ quote_char = nil
48
+
49
+ markup.each_char do |char|
50
+ if !in_quotes && (char == '"' || char == "'")
51
+ in_quotes = true
52
+ quote_char = char
53
+ current_arg += char
54
+ elsif in_quotes && char == quote_char
55
+ in_quotes = false
56
+ quote_char = nil
57
+ current_arg += char
58
+ elsif !in_quotes && char == ','
59
+ args << current_arg.strip
60
+ current_arg = ''
61
+ else
62
+ current_arg += char
63
+ end
64
+ end
65
+
66
+ args << current_arg.strip if current_arg.strip.length > 0
67
+ args
68
+ end
69
+
70
+ def parse_options(option_args)
71
+ options = {}
72
+
73
+ option_args.each do |arg|
74
+ if arg.include?('=')
75
+ key, value = arg.split('=', 2)
76
+ key = key.strip.gsub(/^['"]|['"]$/, '')
77
+ value = value.strip.gsub(/^['"]|['"]$/, '')
78
+ options[key] = value
79
+ end
80
+ end
81
+
82
+ options
83
+ end
84
+
85
+ def resolve_variable(context, variable_input)
86
+ # Strip quotes if present
87
+ if variable_input.match(/^['"]/)
88
+ variable_input.gsub(/^['"]|['"]$/, '')
89
+ else
90
+ # Resolve as variable
91
+ parts = variable_input.split('.')
92
+ current = context
93
+
94
+ parts.each do |part|
95
+ return nil unless current.respond_to?(:[]) || current.is_a?(Hash)
96
+ current = current[part]
97
+ return nil if current.nil?
98
+ end
99
+
100
+ current
101
+ end
102
+ end
103
+
104
+ def build_picture_element(src, src_path, extension, max_width, options)
105
+ html = "<picture>\n"
106
+
107
+ # Add WebP sources unless disabled
108
+ unless options['webp'] == 'false'
109
+ html += build_webp_sources(src_path, max_width)
110
+ end
111
+
112
+ # Add original format sources
113
+ html += build_original_sources(src_path, extension, max_width, src)
114
+
115
+ # Build img tag
116
+ alt = options['alt'] || ''
117
+ css_class = options['class'] || ''
118
+ style = options['style'] || ''
119
+ width = options['width'] || ''
120
+ height = options['height'] || ''
121
+
122
+ html += "<img\n"
123
+ html += "src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\"\n"
124
+ html += "data-lazy=\"@src #{src}\"\n"
125
+ html += "class=\"#{css_class}\"\n" unless css_class.empty?
126
+ html += "alt=\"#{alt}\"\n"
127
+ html += "style=\"#{style}\"\n" unless style.empty?
128
+ html += "width=\"#{width}\"\n" unless width.empty?
129
+ html += "height=\"#{height}\"\n" unless height.empty?
130
+ html += ">\n"
131
+ html += "</picture>"
132
+
133
+ html
134
+ end
135
+
136
+ def build_webp_sources(src_path, max_width)
137
+ html = ""
138
+
139
+ case max_width
140
+ when "320"
141
+ html += "<source data-lazy=\"@srcset #{src_path}-320px.webp\" type=\"image/webp\">\n"
142
+ when "640"
143
+ html += "<source data-lazy=\"@srcset #{src_path}-320px.webp\" media=\"(max-width: 320px)\" type=\"image/webp\">\n"
144
+ html += "<source data-lazy=\"@srcset #{src_path}-640px.webp\" type=\"image/webp\">\n"
145
+ when "1024"
146
+ html += "<source data-lazy=\"@srcset #{src_path}-320px.webp\" media=\"(max-width: 320px)\" type=\"image/webp\">\n"
147
+ html += "<source data-lazy=\"@srcset #{src_path}-640px.webp\" media=\"(max-width: 640px)\" type=\"image/webp\">\n"
148
+ html += "<source data-lazy=\"@srcset #{src_path}-1024px.webp\" type=\"image/webp\">\n"
149
+ else
150
+ html += "<source data-lazy=\"@srcset #{src_path}-320px.webp\" media=\"(max-width: 320px)\" type=\"image/webp\">\n"
151
+ html += "<source data-lazy=\"@srcset #{src_path}-640px.webp\" media=\"(max-width: 640px)\" type=\"image/webp\">\n"
152
+ html += "<source data-lazy=\"@srcset #{src_path}-1024px.webp\" media=\"(max-width: 1024px)\" type=\"image/webp\">\n"
153
+ html += "<source data-lazy=\"@srcset #{src_path}.webp\" type=\"image/webp\">\n"
154
+ end
155
+
156
+ html
157
+ end
158
+
159
+ def build_original_sources(src_path, extension, max_width, src)
160
+ html = ""
161
+
162
+ case max_width
163
+ when "320"
164
+ html += "<source data-lazy=\"@srcset #{src_path}-320px#{extension}\">\n"
165
+ when "640"
166
+ html += "<source data-lazy=\"@srcset #{src_path}-320px#{extension}\" media=\"(max-width: 320px)\">\n"
167
+ html += "<source data-lazy=\"@srcset #{src_path}-640px#{extension}\">\n"
168
+ when "1024"
169
+ html += "<source data-lazy=\"@srcset #{src_path}-320px#{extension}\" media=\"(max-width: 320px)\">\n"
170
+ html += "<source data-lazy=\"@srcset #{src_path}-640px#{extension}\" media=\"(max-width: 640px)\">\n"
171
+ html += "<source data-lazy=\"@srcset #{src_path}-1024px#{extension}\">\n"
172
+ else
173
+ html += "<source data-lazy=\"@srcset #{src_path}-320px#{extension}\" media=\"(max-width: 320px)\">\n"
174
+ html += "<source data-lazy=\"@srcset #{src_path}-640px#{extension}\" media=\"(max-width: 640px)\">\n"
175
+ html += "<source data-lazy=\"@srcset #{src_path}-1024px#{extension}\" media=\"(max-width: 1024px)\">\n"
176
+ html += "<source data-lazy=\"@srcset #{src}\" media=\"(min-width: 1025px)\">\n"
177
+ end
178
+
179
+ html
180
+ end
181
+
182
+ def build_external_image(src, options)
183
+ # Build responsive img tag for external URLs
184
+ alt = options['alt'] || ''
185
+ css_class = options['class'] || ''
186
+ style = options['style'] || ''
187
+ width = options['width'] || ''
188
+ height = options['height'] || ''
189
+ loading = options['loading'] || 'lazy'
190
+
191
+ # Build img tag on a single line to prevent markdown parsing issues
192
+ html = "<img"
193
+ html += " src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\""
194
+ html += " data-lazy=\"@src #{src}\""
195
+ html += " class=\"#{css_class}\"" unless css_class.empty?
196
+ html += " alt=\"#{alt}\""
197
+ html += " style=\"#{style}\"" unless style.empty?
198
+ html += " width=\"#{width}\"" unless width.empty?
199
+ html += " height=\"#{height}\"" unless height.empty?
200
+ html += " loading=\"#{loading}\""
201
+ html += ">"
202
+
203
+ html
204
+ end
205
+ end
206
+ end
207
+
208
+ Liquid::Template.register_tag('uj_image', Jekyll::UJImageTag)
@@ -0,0 +1,301 @@
1
+ # Libraries
2
+ require "jekyll"
3
+
4
+ module Jekyll
5
+ class UJLanguageTag < Liquid::Tag
6
+ # Language mappings: ISO code => [English name, Native name]
7
+ LANGUAGE_MAPPINGS = {
8
+ 'aa' => ['Afar', 'Afaraf'],
9
+ 'ab' => ['Abkhazian', 'аҧсуа бызшәа'],
10
+ 'ae' => ['Avestan', 'avesta'],
11
+ 'af' => ['Afrikaans', 'Afrikaans'],
12
+ 'ak' => ['Akan', 'Akan'],
13
+ 'am' => ['Amharic', 'አማርኛ'],
14
+ 'an' => ['Aragonese', 'aragonés'],
15
+ 'ar' => ['Arabic', 'العربية'],
16
+ 'as' => ['Assamese', 'অসমীয়া'],
17
+ 'av' => ['Avaric', 'авар мацӀ'],
18
+ 'ay' => ['Aymara', 'aymar aru'],
19
+ 'az' => ['Azerbaijani', 'azərbaycan dili'],
20
+ 'ba' => ['Bashkir', 'башҡорт теле'],
21
+ 'be' => ['Belarusian', 'беларуская мова'],
22
+ 'bg' => ['Bulgarian', 'български език'],
23
+ 'bh' => ['Bihari languages', 'भोजपुरी'],
24
+ 'bi' => ['Bislama', 'Bislama'],
25
+ 'bm' => ['Bambara', 'bamanankan'],
26
+ 'bn' => ['Bengali', 'বাংলা'],
27
+ 'bo' => ['Tibetan', 'བོད་ཡིག'],
28
+ 'br' => ['Breton', 'brezhoneg'],
29
+ 'bs' => ['Bosnian', 'bosanski jezik'],
30
+ 'ca' => ['Catalan', 'català'],
31
+ 'ce' => ['Chechen', 'нохчийн мотт'],
32
+ 'ch' => ['Chamorro', 'Chamoru'],
33
+ 'co' => ['Corsican', 'corsu'],
34
+ 'cr' => ['Cree', 'ᓀᐦᐃᔭᐍᐏᐣ'],
35
+ 'cs' => ['Czech', 'čeština'],
36
+ 'cu' => ['Church Slavic', 'ѩзыкъ словѣньскъ'],
37
+ 'cv' => ['Chuvash', 'чӑваш чӗлхи'],
38
+ 'cy' => ['Welsh', 'Cymraeg'],
39
+ 'da' => ['Danish', 'dansk'],
40
+ 'de' => ['German', 'Deutsch'],
41
+ 'dv' => ['Divehi', 'ދިވެހި'],
42
+ 'dz' => ['Dzongkha', 'རྫོང་ཁ'],
43
+ 'ee' => ['Ewe', 'Eʋegbe'],
44
+ 'el' => ['Greek', 'ελληνικά'],
45
+ 'en' => ['English', 'English'],
46
+ 'eo' => ['Esperanto', 'Esperanto'],
47
+ 'es' => ['Spanish', 'español'],
48
+ 'et' => ['Estonian', 'eesti'],
49
+ 'eu' => ['Basque', 'euskera'],
50
+ 'fa' => ['Persian', 'فارسی'],
51
+ 'ff' => ['Fulah', 'Fulfulde'],
52
+ 'fi' => ['Finnish', 'suomi'],
53
+ 'fj' => ['Fijian', 'vosa Vakaviti'],
54
+ 'fo' => ['Faroese', 'føroyskt'],
55
+ 'fr' => ['French', 'français'],
56
+ 'fy' => ['Western Frisian', 'Frysk'],
57
+ 'ga' => ['Irish', 'Gaeilge'],
58
+ 'gd' => ['Gaelic', 'Gàidhlig'],
59
+ 'gl' => ['Galician', 'galego'],
60
+ 'gn' => ['Guarani', 'Avañe\'ẽ'],
61
+ 'gu' => ['Gujarati', 'ગુજરાતી'],
62
+ 'gv' => ['Manx', 'Gaelg'],
63
+ 'ha' => ['Hausa', 'هَوُسَ'],
64
+ 'he' => ['Hebrew', 'עברית'],
65
+ 'hi' => ['Hindi', 'हिन्दी'],
66
+ 'ho' => ['Hiri Motu', 'Hiri Motu'],
67
+ 'hr' => ['Croatian', 'hrvatski jezik'],
68
+ 'ht' => ['Haitian', 'Kreyòl ayisyen'],
69
+ 'hu' => ['Hungarian', 'magyar'],
70
+ 'hy' => ['Armenian', 'Հայերեն'],
71
+ 'hz' => ['Herero', 'Otjiherero'],
72
+ 'ia' => ['Interlingua', 'Interlingua'],
73
+ 'id' => ['Indonesian', 'Bahasa Indonesia'],
74
+ 'ie' => ['Interlingue', 'Interlingue'],
75
+ 'ig' => ['Igbo', 'Asụsụ Igbo'],
76
+ 'ii' => ['Nuosu', 'ꆈꌠ꒿ Nuosuhxop'],
77
+ 'ik' => ['Inupiaq', 'Iñupiaq'],
78
+ 'io' => ['Ido', 'Ido'],
79
+ 'is' => ['Icelandic', 'Íslenska'],
80
+ 'it' => ['Italian', 'italiano'],
81
+ 'iu' => ['Inuktitut', 'ᐃᓄᒃᑎᑐᑦ'],
82
+ 'ja' => ['Japanese', '日本語'],
83
+ 'jv' => ['Javanese', 'basa Jawa'],
84
+ 'ka' => ['Georgian', 'ქართული'],
85
+ 'kg' => ['Kongo', 'Kikongo'],
86
+ 'ki' => ['Kikuyu', 'Gĩkũyũ'],
87
+ 'kj' => ['Kwanyama', 'Kuanyama'],
88
+ 'kk' => ['Kazakh', 'қазақ тілі'],
89
+ 'kl' => ['Kalaallisut', 'kalaallisut'],
90
+ 'km' => ['Khmer', 'ខ្មែរ'],
91
+ 'kn' => ['Kannada', 'ಕನ್ನಡ'],
92
+ 'ko' => ['Korean', '한국어'],
93
+ 'kr' => ['Kanuri', 'Kanuri'],
94
+ 'ks' => ['Kashmiri', 'कश्मीरी'],
95
+ 'ku' => ['Kurdish', 'Kurdî'],
96
+ 'kv' => ['Komi', 'коми кыв'],
97
+ 'kw' => ['Cornish', 'Kernewek'],
98
+ 'ky' => ['Kirghiz', 'Кыргызча'],
99
+ 'la' => ['Latin', 'latine'],
100
+ 'lb' => ['Luxembourgish', 'Lëtzebuergesch'],
101
+ 'lg' => ['Ganda', 'Luganda'],
102
+ 'li' => ['Limburgish', 'Limburgs'],
103
+ 'ln' => ['Lingala', 'Lingála'],
104
+ 'lo' => ['Lao', 'ພາສາລາວ'],
105
+ 'lt' => ['Lithuanian', 'lietuvių kalba'],
106
+ 'lu' => ['Luba-Katanga', 'Tshiluba'],
107
+ 'lv' => ['Latvian', 'latviešu valoda'],
108
+ 'mg' => ['Malagasy', 'fiteny malagasy'],
109
+ 'mh' => ['Marshallese', 'Kajin M̧ajeļ'],
110
+ 'mi' => ['Māori', 'te reo Māori'],
111
+ 'mk' => ['Macedonian', 'македонски јазик'],
112
+ 'ml' => ['Malayalam', 'മലയാളം'],
113
+ 'mn' => ['Mongolian', 'Монгол хэл'],
114
+ 'mr' => ['Marathi', 'मराठी'],
115
+ 'ms' => ['Malay', 'bahasa Melayu'],
116
+ 'mt' => ['Maltese', 'Malti'],
117
+ 'my' => ['Burmese', 'ဗမာစာ'],
118
+ 'na' => ['Nauru', 'Dorerin Naoero'],
119
+ 'nb' => ['Norwegian Bokmål', 'Norsk bokmål'],
120
+ 'nd' => ['North Ndebele', 'isiNdebele'],
121
+ 'ne' => ['Nepali', 'नेपाली'],
122
+ 'ng' => ['Ndonga', 'Owambo'],
123
+ 'nl' => ['Dutch', 'Nederlands'],
124
+ 'nn' => ['Norwegian Nynorsk', 'Norsk nynorsk'],
125
+ 'no' => ['Norwegian', 'Norsk'],
126
+ 'nr' => ['South Ndebele', 'isiNdebele'],
127
+ 'nv' => ['Navajo', 'Diné bizaad'],
128
+ 'ny' => ['Chichewa', 'chiCheŵa'],
129
+ 'oc' => ['Occitan', 'occitan'],
130
+ 'oj' => ['Ojibwa', 'ᐊᓂᔑᓈᐯᒧᐎᓐ'],
131
+ 'om' => ['Oromo', 'Afaan Oromoo'],
132
+ 'or' => ['Oriya', 'ଓଡ଼ିଆ'],
133
+ 'os' => ['Ossetian', 'ирон æвзаг'],
134
+ 'pa' => ['Panjabi', 'ਪੰਜਾਬੀ'],
135
+ 'pi' => ['Pāli', 'पाऴि'],
136
+ 'pl' => ['Polish', 'język polski'],
137
+ 'ps' => ['Pashto', 'پښتو'],
138
+ 'pt' => ['Portuguese', 'português'],
139
+ 'qu' => ['Quechua', 'Runa Simi'],
140
+ 'rm' => ['Romansh', 'rumantsch grischun'],
141
+ 'rn' => ['Kirundi', 'Ikirundi'],
142
+ 'ro' => ['Romanian', 'română'],
143
+ 'ru' => ['Russian', 'русский'],
144
+ 'rw' => ['Kinyarwanda', 'Ikinyarwanda'],
145
+ 'sa' => ['Sanskrit', 'संस्कृतम्'],
146
+ 'sc' => ['Sardinian', 'sardu'],
147
+ 'sd' => ['Sindhi', 'सिन्धी'],
148
+ 'se' => ['Northern Sami', 'Davvisámegiella'],
149
+ 'sg' => ['Sango', 'yângâ tî sängö'],
150
+ 'si' => ['Sinhala', 'සිංහල'],
151
+ 'sk' => ['Slovak', 'slovenčina'],
152
+ 'sl' => ['Slovene', 'slovenski jezik'],
153
+ 'sm' => ['Samoan', 'gagana fa\'a Samoa'],
154
+ 'sn' => ['Shona', 'chiShona'],
155
+ 'so' => ['Somali', 'Soomaaliga'],
156
+ 'sq' => ['Albanian', 'gjuha shqipe'],
157
+ 'sr' => ['Serbian', 'српски језик'],
158
+ 'ss' => ['Swati', 'SiSwati'],
159
+ 'st' => ['Southern Sotho', 'Sesotho'],
160
+ 'su' => ['Sundanese', 'Basa Sunda'],
161
+ 'sv' => ['Swedish', 'svenska'],
162
+ 'sw' => ['Swahili', 'Kiswahili'],
163
+ 'ta' => ['Tamil', 'தமிழ்'],
164
+ 'te' => ['Telugu', 'తెలుగు'],
165
+ 'tg' => ['Tajik', 'тоҷикӣ'],
166
+ 'th' => ['Thai', 'ไทย'],
167
+ 'ti' => ['Tigrinya', 'ትግርኛ'],
168
+ 'tk' => ['Turkmen', 'Türkmen'],
169
+ 'tl' => ['Tagalog', 'Wikang Tagalog'],
170
+ 'tn' => ['Tswana', 'Setswana'],
171
+ 'to' => ['Tonga', 'faka Tonga'],
172
+ 'tr' => ['Turkish', 'Türkçe'],
173
+ 'ts' => ['Tsonga', 'Xitsonga'],
174
+ 'tt' => ['Tatar', 'татар теле'],
175
+ 'tw' => ['Twi', 'Twi'],
176
+ 'ty' => ['Tahitian', 'Reo Tahiti'],
177
+ 'ug' => ['Uighur', 'ئۇيغۇرچە‎'],
178
+ 'uk' => ['Ukrainian', 'українська мова'],
179
+ 'ur' => ['Urdu', 'اردو'],
180
+ 'uz' => ['Uzbek', 'Oʻzbek'],
181
+ 've' => ['Venda', 'Tshivenḓa'],
182
+ 'vi' => ['Vietnamese', 'Tiếng Việt'],
183
+ 'vo' => ['Volapük', 'Volapük'],
184
+ 'wa' => ['Walloon', 'walon'],
185
+ 'wo' => ['Wolof', 'Wollof'],
186
+ 'xh' => ['Xhosa', 'isiXhosa'],
187
+ 'yi' => ['Yiddish', 'ייִדיש'],
188
+ 'yo' => ['Yoruba', 'Yorùbá'],
189
+ 'za' => ['Zhuang', 'Saɯ cueŋƅ'],
190
+ 'zh' => ['Chinese', '中文'],
191
+ 'zu' => ['Zulu', 'isiZulu']
192
+ }
193
+
194
+ def initialize(tag_name, markup, tokens)
195
+ super
196
+ @markup = markup.strip
197
+ end
198
+
199
+ def render(context)
200
+ # Parse arguments that can be quoted or unquoted
201
+ parts = parse_arguments(@markup)
202
+ iso_code_input = parts[0]
203
+ output_type = parts[1] || 'english' # default to english
204
+
205
+ # Check if the input was originally quoted (literal string)
206
+ is_quoted = @markup.strip.match(/^['"]/)
207
+
208
+ # If quoted, use as literal. Otherwise, try to resolve as variable
209
+ if is_quoted
210
+ iso_code = iso_code_input
211
+ else
212
+ # Try to resolve as a variable
213
+ iso_code = resolve_variable(context, iso_code_input)
214
+ # If it didn't resolve to a string, use the input as literal
215
+ iso_code = iso_code_input if iso_code.nil? || !iso_code.is_a?(String)
216
+ end
217
+
218
+ # Strip quotes from resolved iso code if present
219
+ if iso_code.is_a?(String) && iso_code.match(/^['"].*['"]$/)
220
+ iso_code = iso_code[1..-2]
221
+ end
222
+
223
+ # Strip quotes from output type if present
224
+ if output_type.is_a?(String) && output_type.match(/^['"].*['"]$/)
225
+ output_type = output_type[1..-2]
226
+ end
227
+
228
+ # Convert to lowercase for lookup
229
+ iso_code = iso_code.to_s.downcase
230
+ output_type = output_type.to_s.downcase
231
+
232
+ # Look up the language
233
+ language_data = LANGUAGE_MAPPINGS[iso_code]
234
+ return iso_code if language_data.nil? # Return original code if not found
235
+
236
+ # Return appropriate language name based on output type
237
+ case output_type
238
+ when 'native'
239
+ language_data[1] # Native name
240
+ else
241
+ language_data[0] # English name (default)
242
+ end
243
+ end
244
+
245
+ private
246
+
247
+ def parse_arguments(markup)
248
+ # Parse arguments that can be quoted or unquoted
249
+ # Examples: de, english OR 'de', 'english' OR myVar, "native"
250
+ args = []
251
+ current_arg = ''
252
+ in_quotes = false
253
+ quote_char = nil
254
+
255
+ markup.each_char.with_index do |char, i|
256
+ if !in_quotes && (char == '"' || char == "'")
257
+ # Start of quoted string
258
+ in_quotes = true
259
+ quote_char = char
260
+ elsif in_quotes && char == quote_char
261
+ # End of quoted string
262
+ in_quotes = false
263
+ quote_char = nil
264
+ elsif !in_quotes && char == ','
265
+ # Argument separator
266
+ args << current_arg.strip
267
+ current_arg = ''
268
+ else
269
+ # Regular character
270
+ current_arg += char
271
+ end
272
+ end
273
+
274
+ # Add the last argument
275
+ args << current_arg.strip if current_arg.strip.length > 0
276
+
277
+ args
278
+ end
279
+
280
+ def resolve_variable(context, variable_name)
281
+ # Handle nested variable access like page.language
282
+ parts = variable_name.split('.')
283
+ current = context
284
+
285
+ parts.each do |part|
286
+ if current.respond_to?(:[])
287
+ current = current[part]
288
+ elsif current.respond_to?(:key?) && current.key?(part)
289
+ current = current[part]
290
+ else
291
+ return nil
292
+ end
293
+ return nil if current.nil?
294
+ end
295
+
296
+ current
297
+ end
298
+ end
299
+ end
300
+
301
+ Liquid::Template.register_tag('uj_language', Jekyll::UJLanguageTag)