1#!/usr/bin/env ruby
2#
3# Word wrap Frogatto po file msgstrs so that lines fit on the game screen according to
4# glyph data defined in configuration files.
5#
6# Usage
7#
8#   utils/word_wrap_po.rb LANGUAGE
9#
10# LANGUAGE is the base name of the po file, for example zh_CN
11#
12# The script ensures that line breaks occur only at "legal" positions, e.g. never before
13# closing punctuations. However it does not try to break at the most appealing positions.
14#
15# It ignores messages containing existing line breaks or {markup}, and messages
16# from CHANGELOG, METADATA, and titlescreen files.
17#
18# Parts of the po file which the script "ignores" may still be modified, including
19# msgids. However there should be only po metadata and formatting changes and the
20# interpreted msgids and msgstrs remain the same.
21#
22# This script requires Ruby and the gems ffi-icu, get_pomo, and treetop.
23
24require 'ffi-icu'
25require 'get_pomo'
26require 'treetop'
27require 'fileutils'
28
29MAX_LINE_WIDTH = 350
30DEFAULT_KERNING = 2
31LANGUAGE = ARGV[0]
32
33CFG_FILE = "data/dialog_font.#{LANGUAGE}.cfg"
34CFG_FILE.replace 'data/dialog_font.cfg' unless File.exist? CFG_FILE
35PO_FILE = "po/#{LANGUAGE}.po"
36
37unless File.exist? PO_FILE
38  abort "#{PO_FILE} does not exist"
39end
40
41Treetop.load 'utils/fml_font_cfg.treetop'
42
43parser = FrogattoFontCfgParser.new
44result = parser.parse(File.read(CFG_FILE))
45unless result
46  puts parser.failure_reason
47  abort "Failed to parse #{CFG_FILE}"
48end
49CHARACTER_WIDTHS = result.character_widths
50KERNING = result.attributes.values['kerning'] || DEFAULT_KERNING
51
52def word_wrap(text)
53  line_breaker = ICU::BreakIterator.new(:line, LANGUAGE)
54  line_breaker.text = text
55  segments = line_breaker.each_cons(2).to_a
56  lines = []
57  until segments.empty?
58    line = ''
59    line_width = 0
60    segments = segments.drop_while do |from, to|
61      segment = text[from...to]
62      segment_width = segment.chars.inject(0) do |total, character|
63        character_width = CHARACTER_WIDTHS[character]
64        unless character_width
65          abort "No glyph defined for #{character}, used in msgstr '#{text}'. If this language uses generated glyphs, try 'rake #{LANGUAGE}'"
66        end
67        total + character_width + KERNING
68      end
69      line_width += segment_width
70      line_width <= MAX_LINE_WIDTH && line << segment
71    end
72    lines << line.strip
73  end
74  lines.join "\\n"
75end
76
77po = GetPomo::PoFile.new
78po.add_translations_from_text(File.read(PO_FILE))
79po.translations.each do |translation|
80  unless translation.msgid.empty? ||
81         translation.msgstr.empty? ||
82         translation.comment =~ /METADATA|CHANGELOG|titlescreen/ ||
83         translation.msgstr =~ /[{}]/ ||
84         translation.msgstr =~ /\\n/ && translation.comment !~ /automatically word wrapped/
85    wrapped = word_wrap(translation.msgstr.gsub('\"', '"')).gsub('"', '\"')
86    if wrapped != translation.msgstr
87      puts "Word-wrapped:\n#{translation.msgid}\n#{wrapped}"
88      translation.comment += "automatically word-wrapped"
89      translation.msgstr = wrapped
90    end
91  end
92end
93File.open(PO_FILE, 'w') {|file| file.write po.to_text}
94
95system "msgmerge #{PO_FILE} po/frogatto.pot -o #{PO_FILE}.part"
96FileUtils.mv "#{PO_FILE}.part", PO_FILE
97
98