Class: Jekyll::L10n::PoFileReader
- Inherits:
-
Object
- Object
- Jekyll::L10n::PoFileReader
- Defined in:
- lib/jekyll-l10n/po_file/reader.rb
Overview
Parses GNU Gettext PO files into translation hashes.
PoFileReader reads and parses PO files in standard GNU Gettext format, supporting multiple parsing modes: simple (msgid -> msgstr), with references (for debugging), and with merge metadata (including fuzzy flags). It handles multi-line strings, comment extraction, and various escape sequences. Both instance-based and class-based APIs are supported for backward compatibility.
Key responsibilities:
-
Parse PO files into translation hashes
-
Handle msgid/msgstr pairs with continuation lines
-
Extract and preserve reference comments (file location references)
-
Extract and preserve fuzzy flags during merging
-
Parse multi-line strings with proper escaping
-
Support three modes: simple, with_references, and for_merge
-
Handle both file paths and inline content strings
rubocop:disable Metrics/ClassLength
Constant Summary collapse
- MSGID_PATTERN =
rubocop:enable Metrics/ClassLength
/^msgid ['"](.*)['"] *$/.freeze
- MSGSTR_PATTERN =
/^msgstr ['"](.*)['"] *$/.freeze
- NO_REFERENCE =
nil
Instance Attribute Summary collapse
-
#po_path ⇒ Object
readonly
Returns the value of attribute po_path.
Class Method Summary collapse
-
.build_translation_entry(msgstr, reference, fuzzy, previous_msgid = nil, with_metadata: false) ⇒ Object
rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity.
- .collect_continuation_lines(lines, start_idx, values, delimiter) ⇒ Object
- .continuation_line?(line) ⇒ Boolean
-
.extract_metadata_before_msgid(lines, msgid_idx, include_fuzzy: false) ⇒ Object
Unified metadata extraction: extracts reference, fuzzy flag, and previous msgid.
- .extract_msgid_and_continuation(lines, start_idx) ⇒ Object
- .extract_msgstr_and_continuation(lines, start_idx) ⇒ Object
- .extract_po_field(lines, start_idx, pattern) ⇒ Object
-
.extract_previous_msgid_from_line(comment_line) ⇒ Object
Parse a #| msgid “…” previous-msgid comment line.
-
.extract_reference_and_fuzzy_before_msgid(lines, msgid_idx) ⇒ Object
Backward compatibility wrapper — now returns [reference, fuzzy, previous_msgid].
-
.extract_reference_before_msgid(lines, msgid_idx) ⇒ Object
Backward compatibility wrapper.
-
.extract_reference_from_line(comment_line) ⇒ Object
rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity.
- .fuzzy_line?(comment_line) ⇒ Boolean
- .msgid_line?(line) ⇒ Boolean
-
.parse(po_path) ⇒ Hash
Parse a PO file (class method, for backward compatibility).
-
.parse_for_merge(po_path) ⇒ Hash
Parse a PO file for merging (class method, for backward compatibility).
-
.parse_for_translation(po_path) ⇒ Hash
Parse a PO file for translation injection, excluding fuzzy entries (class method).
-
.parse_with_references(po_path) ⇒ Hash
Parse a PO file with references (class method, for backward compatibility).
-
.process_line(lines, idx, translations) ⇒ Object
Backward compatibility wrapper.
- .process_line_for_merge(lines, idx, translations) ⇒ Object
- .process_line_internal(lines, idx, translations, with_references) ⇒ Object
-
.process_line_with_reference(lines, idx, translations) ⇒ Object
Backward compatibility wrappers.
-
.process_msgid_msgstr_pair(lines, start_idx, translations, reference: nil, fuzzy: nil, previous_msgid: nil, with_mode: false) ⇒ Object
Unified method for processing msgid/msgstr pairs with optional reference and fuzzy metadata with_mode: false (default, simple format), true (with reference), :merge (with both) rubocop:disable Metrics/ParameterLists, Metrics/AbcSize, Metrics/PerceivedComplexity.
-
.process_msgid_msgstr_pair_internal(lines, start_idx, translations, reference = nil, fuzzy = nil) ⇒ Object
Backward compatibility alias.
-
.process_msgid_msgstr_pair_with_metadata(lines, start_idx, translations, reference: nil, fuzzy: nil) ⇒ Object
Backward compatibility alias.
-
.process_msgid_msgstr_pair_with_reference(lines, start_idx, translations, reference) ⇒ Object
Backward compatibility alias.
- .process_msgid_with_references(lines, idx, translations, with_references) ⇒ Object
-
.process_po_lines(content) ⇒ Object
Backward compatibility wrapper.
- .process_po_lines_for_merge(content) ⇒ Object
- .process_po_lines_internal(content, with_references) ⇒ Object
- .process_po_lines_with_references(content) ⇒ Object
- .read_po_file(po_path) ⇒ Object
-
.skip_blank_and_comments(lines, idx) ⇒ Object
Skips blank lines and comments before processing entries.
-
.split_lines(content) ⇒ Object
Splits PO file content into individual lines.
-
.stop_collecting?(line) ⇒ Boolean
Determines if we should stop collecting continuation lines.
-
.store_translation(translations, msgid, msgstr, reference: nil, fuzzy: nil, previous_msgid: nil, with_metadata: false) ⇒ Object
Store translation with optional metadata (reference location and fuzzy flag).
-
.store_translation_internal(translations, msgid, msgstr, reference: nil, fuzzy: nil) ⇒ Object
Kept for backward compatibility with existing tests Supports both positional and keyword argument calling styles.
- .store_translation_with_fuzzy(translations, msgid, msgstr, fuzzy:) ⇒ Object
-
.store_translation_with_reference(translations, msgid, msgstr, reference:) ⇒ Object
Backward compatibility wrappers for old method signatures.
- .store_translation_with_reference_and_fuzzy(translations, msgid, msgstr, reference:, fuzzy:) ⇒ Object
-
.unescape_string(str, delimiter) ⇒ Object
Unescape PO file string values containing escape sequences.
Instance Method Summary collapse
-
#initialize(po_path_or_content = nil) ⇒ PoFileReader
constructor
Initialize a new PoFileReader.
-
#parse ⇒ Hash
Parse PO file into simple translation hash.
-
#parse_for_merge ⇒ Hash
Parse PO file with all metadata for merging.
-
#parse_for_translation ⇒ Hash
Parse a PO file for translation injection, excluding fuzzy entries.
-
#parse_with_references ⇒ Hash
Parse PO file with reference comments preserved.
Constructor Details
#initialize(po_path_or_content = nil) ⇒ PoFileReader
Initialize a new PoFileReader.
Accepts either a file path (if file exists) or inline PO content. Determines which based on whether the path exists in the filesystem. Defaults to nil, which initializes the reader with empty content.
49 50 51 52 53 54 55 56 57 58 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 49 def initialize(po_path_or_content = nil) # Support both file path and content string if po_path_or_content && File.exist?(po_path_or_content.to_s) @po_path = po_path_or_content @content = nil else @content = po_path_or_content @po_path = nil end end |
Instance Attribute Details
#po_path ⇒ Object (readonly)
Returns the value of attribute po_path.
39 40 41 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 39 def po_path @po_path end |
Class Method Details
.build_translation_entry(msgstr, reference, fuzzy, previous_msgid = nil, with_metadata: false) ⇒ Object
rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
461 462 463 464 465 466 467 468 469 470 471 472 473 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 461 def self.build_translation_entry(msgstr, reference, fuzzy, previous_msgid = nil, with_metadata: false) # Simple format when no metadata requested and none provided return msgstr if ! && reference.nil? && fuzzy.nil? && previous_msgid.nil? # Build metadata hash based on what's provided entry = { msgstr: msgstr } entry[:reference] = reference unless reference.nil? entry[:fuzzy] = fuzzy unless fuzzy.nil? entry[:previous_msgid] = previous_msgid unless previous_msgid.nil? entry[:comment] = nil if !fuzzy.nil? || !reference.nil? entry end |
.collect_continuation_lines(lines, start_idx, values, delimiter) ⇒ Object
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 378 def self.collect_continuation_lines(lines, start_idx, values, delimiter) idx = start_idx while idx < lines.length cont_line = lines[idx].strip break if stop_collecting?(cont_line) break unless continuation_line?(cont_line) unescaped = unescape_string(cont_line[1...-1], delimiter) values << unescaped idx += 1 end combined_value = values.join { value: combined_value, next_line: idx } end |
.continuation_line?(line) ⇒ Boolean
399 400 401 402 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 399 def self.continuation_line?(line) (line.start_with?('"') && line.end_with?('"')) || (line.start_with?("'") && line.end_with?("'")) end |
.extract_metadata_before_msgid(lines, msgid_idx, include_fuzzy: false) ⇒ Object
Unified metadata extraction: extracts reference, fuzzy flag, and previous msgid. rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 301 def self.(lines, msgid_idx, include_fuzzy: false) reference = nil fuzzy = false previous_msgid = nil comments_end = msgid_idx - 1 while comments_end >= 0 comment_line = lines[comments_end].strip break unless comment_line.start_with?('#') || comment_line.empty? reference = extract_reference_from_line(comment_line) || reference previous_msgid = extract_previous_msgid_from_line(comment_line) || previous_msgid if include_fuzzy fuzzy = true if include_fuzzy && fuzzy_line?(comment_line) comments_end -= 1 end include_fuzzy ? [reference, fuzzy, previous_msgid] : reference end |
.extract_msgid_and_continuation(lines, start_idx) ⇒ Object
355 356 357 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 355 def self.extract_msgid_and_continuation(lines, start_idx) extract_po_field(lines, start_idx, MSGID_PATTERN) end |
.extract_msgstr_and_continuation(lines, start_idx) ⇒ Object
359 360 361 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 359 def self.extract_msgstr_and_continuation(lines, start_idx) extract_po_field(lines, start_idx, MSGSTR_PATTERN) end |
.extract_po_field(lines, start_idx, pattern) ⇒ Object
363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 363 def self.extract_po_field(lines, start_idx, pattern) line = lines[start_idx].strip match = line.match(pattern) if match delimiter = line[match.begin(1) - 1] values = [unescape_string(match[1], delimiter)] else values = [] delimiter = '"' end collect_continuation_lines(lines, start_idx + 1, values, delimiter) end |
.extract_previous_msgid_from_line(comment_line) ⇒ Object
Parse a #| msgid “…” previous-msgid comment line.
327 328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 327 def self.extract_previous_msgid_from_line(comment_line) return nil unless comment_line.start_with?('#|') && comment_line.include?('msgid') raw = comment_line.sub(/^#\|\s*msgid\s*/, '').strip # Strip surrounding quotes and unescape if raw.start_with?('"') && raw.end_with?('"') unescape_string(raw[1...-1], '"') elsif raw.start_with?("'") && raw.end_with?("'") unescape_string(raw[1...-1], "'") else raw end end |
.extract_reference_and_fuzzy_before_msgid(lines, msgid_idx) ⇒ Object
Backward compatibility wrapper — now returns [reference, fuzzy, previous_msgid]
351 352 353 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 351 def self.extract_reference_and_fuzzy_before_msgid(lines, msgid_idx) (lines, msgid_idx, include_fuzzy: true) end |
.extract_reference_before_msgid(lines, msgid_idx) ⇒ Object
Backward compatibility wrapper
346 347 348 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 346 def self.extract_reference_before_msgid(lines, msgid_idx) (lines, msgid_idx, include_fuzzy: false) end |
.extract_reference_from_line(comment_line) ⇒ Object
rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
322 323 324 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 322 def self.extract_reference_from_line(comment_line) comment_line.sub(/^#:\s*/, '').strip if comment_line.start_with?('#:') end |
.fuzzy_line?(comment_line) ⇒ Boolean
341 342 343 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 341 def self.fuzzy_line?(comment_line) comment_line.start_with?('#,') && comment_line.include?('fuzzy') end |
.msgid_line?(line) ⇒ Boolean
150 151 152 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 150 def self.msgid_line?(line) line.start_with?('msgid ') end |
.parse(po_path) ⇒ Hash
Parse a PO file (class method, for backward compatibility).
112 113 114 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 112 def self.parse(po_path) new(po_path).parse end |
.parse_for_merge(po_path) ⇒ Hash
Parse a PO file for merging (class method, for backward compatibility).
128 129 130 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 128 def self.parse_for_merge(po_path) new(po_path).parse_for_merge end |
.parse_for_translation(po_path) ⇒ Hash
Parse a PO file for translation injection, excluding fuzzy entries (class method).
136 137 138 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 136 def self.parse_for_translation(po_path) new(po_path).parse_for_translation end |
.parse_with_references(po_path) ⇒ Hash
Parse a PO file with references (class method, for backward compatibility).
120 121 122 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 120 def self.parse_with_references(po_path) new(po_path).parse_with_references end |
.process_line(lines, idx, translations) ⇒ Object
Backward compatibility wrapper
146 147 148 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 146 def self.process_line(lines, idx, translations) process_line_internal(lines, idx, translations, false) end |
.process_line_for_merge(lines, idx, translations) ⇒ Object
260 261 262 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 260 def self.process_line_for_merge(lines, idx, translations) process_line_internal(lines, idx, translations, :merge) end |
.process_line_internal(lines, idx, translations, with_references) ⇒ Object
264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 264 def self.process_line_internal(lines, idx, translations, with_references) return idx + 1 if idx >= lines.length idx = skip_blank_and_comments(lines, idx) return idx if idx >= lines.length line = lines[idx].strip if msgid_line?(line) process_msgid_with_references(lines, idx, translations, with_references) else idx + 1 end end |
.process_line_with_reference(lines, idx, translations) ⇒ Object
Backward compatibility wrappers
256 257 258 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 256 def self.process_line_with_reference(lines, idx, translations) process_line_internal(lines, idx, translations, true) end |
.process_msgid_msgstr_pair(lines, start_idx, translations, reference: nil, fuzzy: nil, previous_msgid: nil, with_mode: false) ⇒ Object
Unified method for processing msgid/msgstr pairs with optional reference and fuzzy metadata with_mode: false (default, simple format), true (with reference), :merge (with both) rubocop:disable Metrics/ParameterLists, Metrics/AbcSize, Metrics/PerceivedComplexity
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 175 def self.process_msgid_msgstr_pair(lines, start_idx, translations, reference: nil, fuzzy: nil, previous_msgid: nil, with_mode: false) # rubocop:enable Metrics/ParameterLists, Metrics/AbcSize, Metrics/PerceivedComplexity # Handle nil sentinel values (from NO_REFERENCE constant) reference = nil if reference == NO_REFERENCE fuzzy = nil if fuzzy == NO_REFERENCE msgid = extract_msgid_and_continuation(lines, start_idx) msgid_value = msgid[:value] i = msgid[:next_line] if i < lines.length && lines[i].strip.start_with?('msgstr ') msgstr = extract_msgstr_and_continuation(lines, i) msgstr_value = msgstr[:value] i = msgstr[:next_line] # Always use metadata format if in reference or merge mode = with_mode == true || with_mode == :merge || !reference.nil? || !fuzzy.nil? store_translation( translations, msgid_value, msgstr_value, reference: reference, fuzzy: fuzzy, previous_msgid: previous_msgid, with_metadata: ) else i += 1 end i end |
.process_msgid_msgstr_pair_internal(lines, start_idx, translations, reference = nil, fuzzy = nil) ⇒ Object
Backward compatibility alias
222 223 224 225 226 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 222 def self.process_msgid_msgstr_pair_internal(lines, start_idx, translations, reference = nil, fuzzy = nil) process_msgid_msgstr_pair(lines, start_idx, translations, reference: reference, fuzzy: fuzzy) end |
.process_msgid_msgstr_pair_with_metadata(lines, start_idx, translations, reference: nil, fuzzy: nil) ⇒ Object
Backward compatibility alias
207 208 209 210 211 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 207 def self.(lines, start_idx, translations, reference: nil, fuzzy: nil) process_msgid_msgstr_pair(lines, start_idx, translations, reference: reference, fuzzy: fuzzy, with_mode: :merge) end |
.process_msgid_msgstr_pair_with_reference(lines, start_idx, translations, reference) ⇒ Object
Backward compatibility alias
214 215 216 217 218 219 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 214 def self.process_msgid_msgstr_pair_with_reference(lines, start_idx, translations, reference) process_msgid_msgstr_pair( lines, start_idx, translations, reference: reference, fuzzy: nil, with_mode: true ) end |
.process_msgid_with_references(lines, idx, translations, with_references) ⇒ Object
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 279 def self.process_msgid_with_references(lines, idx, translations, with_references) case with_references when true reference = extract_reference_before_msgid(lines, idx) process_msgid_msgstr_pair( lines, idx, translations, reference: reference, fuzzy: nil, with_mode: true ) when :merge reference, fuzzy, previous_msgid = extract_reference_and_fuzzy_before_msgid(lines, idx) process_msgid_msgstr_pair( lines, idx, translations, reference: reference, fuzzy: fuzzy, previous_msgid: previous_msgid, with_mode: :merge ) else process_msgid_msgstr_pair(lines, idx, translations, reference: nil, fuzzy: nil, with_mode: false) end end |
.process_po_lines(content) ⇒ Object
Backward compatibility wrapper
141 142 143 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 141 def self.process_po_lines(content) process_po_lines_internal(content, false) end |
.process_po_lines_for_merge(content) ⇒ Object
232 233 234 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 232 def self.process_po_lines_for_merge(content) process_po_lines_internal(content, :merge) end |
.process_po_lines_internal(content, with_references) ⇒ Object
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 236 def self.process_po_lines_internal(content, with_references) translations = {} lines = split_lines(content) i = 0 while i < lines.length i = case with_references when true process_line_internal(lines, i, translations, true) when :merge process_line_internal(lines, i, translations, :merge) else process_line_internal(lines, i, translations, false) end end translations end |
.process_po_lines_with_references(content) ⇒ Object
228 229 230 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 228 def self.process_po_lines_with_references(content) process_po_lines_internal(content, true) end |
.read_po_file(po_path) ⇒ Object
430 431 432 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 430 def self.read_po_file(po_path) FileOperations.read_utf8(po_path) end |
.skip_blank_and_comments(lines, idx) ⇒ Object
Skips blank lines and comments before processing entries
160 161 162 163 164 165 166 167 168 169 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 160 def self.skip_blank_and_comments(lines, idx) return idx if idx >= lines.length line = lines[idx].strip if line.empty? || line.start_with?('#') idx + 1 else idx end end |
.split_lines(content) ⇒ Object
Splits PO file content into individual lines
155 156 157 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 155 def self.split_lines(content) content.split("\n") end |
.stop_collecting?(line) ⇒ Boolean
Determines if we should stop collecting continuation lines
503 504 505 506 507 508 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 503 def self.stop_collecting?(line) line.empty? || line.start_with?('#') || line.start_with?('msgid') || line.start_with?('msgstr') end |
.store_translation(translations, msgid, msgstr, reference: nil, fuzzy: nil, previous_msgid: nil, with_metadata: false) ⇒ Object
Store translation with optional metadata (reference location and fuzzy flag).
Three Storage Formats ===
-
Simple: { msgid => msgstr } Used during translation lookup - minimal memory, fastest access
-
With Reference: { msgid => { msgstr: “…”, reference: “file.html:10” } } Used during extraction - preserves source location for debugging
-
With Merge Metadata: { msgid => { msgstr: “…”, reference: “…”, fuzzy: false } } Used during merging - tracks fuzzy flag for incomplete translations
Why three formats instead of one?
-
Memory efficiency: Simple format used most often (translation lookup)
-
Flexibility: Can handle different parsing modes without wasting storage
-
Backward compatibility: Supports legacy calling conventions
rubocop:disable Metrics/ParameterLists
451 452 453 454 455 456 457 458 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 451 def self.store_translation(translations, msgid, msgstr, reference: nil, fuzzy: nil, previous_msgid: nil, with_metadata: false) # rubocop:enable Metrics/ParameterLists return if msgid.nil? || msgstr.nil? || msgid.empty? translations[msgid] = build_translation_entry(msgstr, reference, fuzzy, previous_msgid, with_metadata: ) end |
.store_translation_internal(translations, msgid, msgstr, reference: nil, fuzzy: nil) ⇒ Object
Kept for backward compatibility with existing tests Supports both positional and keyword argument calling styles
478 479 480 481 482 483 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 478 def self.store_translation_internal(translations, msgid, msgstr, reference: nil, fuzzy: nil) # Handle nil sentinel values (from NO_REFERENCE constant) reference = nil if reference == NO_REFERENCE fuzzy = nil if fuzzy == NO_REFERENCE store_translation(translations, msgid, msgstr, reference: reference, fuzzy: fuzzy) end |
.store_translation_with_fuzzy(translations, msgid, msgstr, fuzzy:) ⇒ Object
491 492 493 494 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 491 def self.store_translation_with_fuzzy(translations, msgid, msgstr, fuzzy:) store_translation(translations, msgid, msgstr, reference: nil, fuzzy: fuzzy, with_metadata: true) end |
.store_translation_with_reference(translations, msgid, msgstr, reference:) ⇒ Object
Backward compatibility wrappers for old method signatures
486 487 488 489 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 486 def self.store_translation_with_reference(translations, msgid, msgstr, reference:) store_translation(translations, msgid, msgstr, reference: reference, fuzzy: nil, with_metadata: true) end |
.store_translation_with_reference_and_fuzzy(translations, msgid, msgstr, reference:, fuzzy:) ⇒ Object
496 497 498 499 500 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 496 def self.store_translation_with_reference_and_fuzzy(translations, msgid, msgstr, reference:, fuzzy:) store_translation(translations, msgid, msgstr, reference: reference, fuzzy: fuzzy, with_metadata: true) end |
.unescape_string(str, delimiter) ⇒ Object
Unescape PO file string values containing escape sequences.
IMPORTANT: Order Matters for Correctness ===
Must unescape escaped quotes BEFORE unescaping backslashes. Why: If we unescape backslashes first, we lose information about which backslashes were part of escape sequences vs. literal text.
Example with wrong order (backslash first):
Input: "Say \\" Hello" (should be: Say \ Hello with closing quote)
Wrong: "\\\\" -> "\\" -> Remove quotes -> "Say \" Hello" (quote not closed!)
Correct order (quote first):
Input: "Say \\" Hello"
Right: "\\" -> " " (two backslashes become one literal backslash) -> "Say \ Hello"
Single vs Double quotes matter:
-
Single quotes: Use \‘ and \\
-
Double quotes: Use \“ and \\
422 423 424 425 426 427 428 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 422 def self.unescape_string(str, delimiter) if delimiter == "'" str.gsub("\\'", "'").gsub('\\\\', '\\') else str.gsub('\\"', '"').gsub('\\\\', '\\') end end |
Instance Method Details
#parse ⇒ Hash
Parse PO file into simple translation hash.
Returns hash mapping msgid strings to msgstr strings (no metadata).
65 66 67 68 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 65 def parse content = load_content process_po_lines_instance(content, false) end |
#parse_for_merge ⇒ Hash
Parse PO file with all metadata for merging.
Returns hash mapping msgid strings to metadata hashes containing msgstr, reference, and fuzzy flag. Used when merging with existing translations.
89 90 91 92 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 89 def parse_for_merge content = load_content process_po_lines_instance(content, :merge) end |
#parse_for_translation ⇒ Hash
Parse a PO file for translation injection, excluding fuzzy entries.
Parses with full merge metadata (including fuzzy flags), then strips fuzzy entries and returns a simple msgid → msgstr hash. Fuzzy entries are treated as untranslated per the GNU Gettext standard: msgfmt skips them by default and they should fall through to the active fallback mode.
102 103 104 105 106 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 102 def parse_for_translation parse_for_merge .reject { |_msgid, v| v.is_a?(Hash) && v[:fuzzy] } .transform_values { |v| v.is_a?(Hash) ? v[:msgstr] : v } end |
#parse_with_references ⇒ Hash
Parse PO file with reference comments preserved.
Returns hash mapping msgid strings to metadata hashes containing msgstr and reference (file location for debugging).
77 78 79 80 |
# File 'lib/jekyll-l10n/po_file/reader.rb', line 77 def parse_with_references content = load_content process_po_lines_instance(content, true) end |