lib/gettext/po_entry.rb in gettext-3.0.0 vs lib/gettext/po_entry.rb in gettext-3.0.1
- old
+ new
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright (C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
# Copyright (C) 2010 masone (Christian Felder) <ema@rh-productions.ch>
# Copyright (C) 2009 Masao Mutoh
#
# License: Ruby's or LGPL
#
@@ -17,10 +17,12 @@
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+require "gettext/po_format"
+
module GetText
class ParseError < StandardError
end
# Contains data related to the expression or sentence that
@@ -43,44 +45,10 @@
:plural => [:msgid, :msgid_plural, :separator, :msgstr],
:msgctxt => [:msgctxt, :msgid, :msgstr],
:msgctxt_plural => [:msgctxt, :msgid, :msgid_plural, :msgstr]
}
- TRANSLATOR_COMMENT_MARK = "# "
- EXTRACTED_COMMENT_MARK = "#."
- FLAG_MARK = "#,"
- PREVIOUS_COMMENT_MARK = "#|"
- REFERENCE_COMMENT_MARK = "#:"
-
- class << self
- def escape(string)
- string.gsub(/([\\"\t\n])/) do
- special_character = $1
- case special_character
- when "\t"
- "\\t"
- when "\n"
- "\\n"
- else
- "\\#{special_character}"
- end
- end
- end
- end
-
- @@max_line_length = 70
-
- # Sets the max line length.
- def self.max_line_length=(len)
- @@max_line_length = len
- end
-
- # Gets the max line length.
- def self.max_line_length
- @@max_line_length
- end
-
# Required
attr_reader :type # :normal, :plural, :msgctxt, :msgctxt_plural
attr_accessor :msgid
attr_accessor :msgstr
# Options
@@ -108,25 +76,19 @@
@msgstr = nil
end
# Support for extracted comments. Explanation s.
# http://www.gnu.org/software/gettext/manual/gettext.html#Names
+ # @return [void]
def add_comment(new_comment)
if (new_comment and ! new_comment.empty?)
@extracted_comment ||= ""
@extracted_comment << "\n" unless @extracted_comment.empty?
@extracted_comment << new_comment
end
- to_s
end
- # Returns a parameter representation suitable for po-files
- # and other purposes.
- def escaped(param_name)
- escape(send(param_name))
- end
-
# Checks if the self has same attributes as other.
def ==(other)
not other.nil? and
type == other.type and
msgid == other.msgid and
@@ -159,224 +121,266 @@
# Merges two translation targets with the same msgid and returns the merged
# result. If one is declared as plural and the other not, then the one
# with the plural wins.
def merge(other)
return self unless other
- raise ParseError, "Translation targets do not match: \n" \
- " self: #{self.inspect}\n other: '#{other.inspect}'" unless self.mergeable?(other)
- if other.msgid_plural && !self.msgid_plural
+ unless mergeable?(other)
+ message = "Translation targets do not match: \n" +
+ " self: #{self.inspect}\n other: '#{other.inspect}'"
+ raise ParseError, message
+ end
+ if other.msgid_plural && !msgid_plural
res = other
- unless (res.references.include? self.references[0])
- res.references += self.references
- res.add_comment(self.extracted_comment)
+ unless res.references.include?(references[0])
+ res.references += references
+ res.add_comment(extracted_comment)
end
else
res = self
- unless (res.references.include? other.references[0])
+ unless res.references.include?(other.references[0])
res.references += other.references
res.add_comment(other.extracted_comment)
end
end
res
end
- # Output the po entry for the po-file.
- def to_s
+ # Format the po entry in PO format.
+ #
+ # @param [Hash] options
+ # @option options (see Formatter#initialize)
+ def to_s(options={})
raise(NoMsgidError, "msgid is nil.") unless @msgid
- str = ""
- # extracted comments
- if @msgid == :last
- return format_obsolete_comment(comment)
- end
+ formatter = Formatter.new(self, options)
+ formatter.format
+ end
- str << format_translator_comment
- str << format_extracted_comment
- str << format_reference_comment
- str << format_flag_comment
- str << format_previous_comment
+ # Returns true if the type is kind of msgctxt.
+ def msgctxt?
+ [:msgctxt, :msgctxt_plural].include?(@type)
+ end
- # msgctxt, msgid, msgstr
- if msgctxt?
- if @msgctxt.nil?
- no_msgctxt_message = "This POEntry is a kind of msgctxt " +
- "but the msgctxt property is nil. " +
- "msgid: #{msgid}"
- raise(NoMsgctxtError, no_msgctxt_message)
+ # Returns true if the type is kind of plural.
+ def plural?
+ [:plural, :msgctxt_plural].include?(@type)
+ end
+
+ def [](number)
+ param = @param_type[number]
+ raise ParseError, 'no more string parameters expected' unless param
+ send param
+ end
+
+ private
+
+ # sets or extends the value of a translation target params like msgid,
+ # msgctxt etc.
+ # param is symbol with the name of param
+ # value - new value
+ def set_value(param, value)
+ send "#{param}=", (send(param) || '') + value
+ end
+
+ class Formatter
+ class << self
+ def escape(string)
+ return "" if string.nil?
+
+ string.gsub(/([\\"\t\n])/) do
+ special_character = $1
+ case special_character
+ when "\t"
+ "\\t"
+ when "\n"
+ "\\n"
+ else
+ "\\#{special_character}"
+ end
+ end
end
- str << "msgctxt " << format_message(msgctxt)
end
- str << "msgid " << format_message(msgid)
- if plural?
- if @msgid_plural.nil?
- no_plural_message = "This POEntry is a kind of plural " +
- "but the msgid_plural property is nil. " +
- "msgid: #{msgid}"
- raise(NoMsgidPluralError, no_plural_message)
+ include POFormat
+
+ DEFAULT_MAX_LINE_WIDTH = 78
+
+ # @param [POEntry] entry The entry to be formatted.
+ # @param [Hash] options
+ # @option options [Bool] :include_reference_comment (true)
+ # Includes reference comments in formatted string if true.
+ # @option options [Integer] :max_line_width (78)
+ # Wraps long lines that is longer than the `:max_line_width`.
+ # Don't break long lines if `:max_line_width` is less than 0
+ # such as `-1`.
+ def initialize(entry, options={})
+ @entry = entry
+ @options = fill_default_option_values(options)
+ end
+
+ def format
+ # extracted comments
+ if @entry.msgid == :last
+ return format_obsolete_comment(@entry.comment)
end
- str << "msgid_plural " << format_message(msgid_plural)
+ str = ""
+ str << format_translator_comment
+ str << format_extracted_comment
+ if @options[:include_reference_comment]
+ str << format_reference_comment
+ end
+ str << format_flag_comment
+ str << format_previous_comment
- if msgstr.nil?
- str << "msgstr[0] \"\"\n"
- str << "msgstr[1] \"\"\n"
- else
- msgstrs = msgstr.split("\000", -1)
- msgstrs.each_with_index do |msgstr, index|
- str << "msgstr[#{index}] " << format_message(msgstr)
+ # msgctxt, msgid, msgstr
+ if @entry.msgctxt?
+ if @entry.msgctxt.nil?
+ no_msgctxt_message = "This POEntry is a kind of msgctxt " +
+ "but the msgctxt property is nil. " +
+ "msgid: #{@entry.msgid}"
+ raise(NoMsgctxtError, no_msgctxt_message)
end
+ str << "msgctxt " << format_message(@entry.msgctxt)
end
- else
- str << "msgstr "
- str << format_message(msgstr)
- end
- str
- end
- def format_translator_comment
- format_comment("#", translator_comment)
- end
+ str << "msgid " << format_message(@entry.msgid)
+ if @entry.plural?
+ if @entry.msgid_plural.nil?
+ no_plural_message = "This POEntry is a kind of plural " +
+ "but the msgid_plural property is nil. " +
+ "msgid: #{@entry.msgid}"
+ raise(NoMsgidPluralError, no_plural_message)
+ end
- def format_extracted_comment
- format_comment(EXTRACTED_COMMENT_MARK, extracted_comment)
- end
+ str << "msgid_plural " << format_message(@entry.msgid_plural)
- def format_reference_comment
- max_line_length = 70
- formatted_reference = ""
- if not references.nil? and not references.empty?
- formatted_reference << REFERENCE_COMMENT_MARK
- line_size = 2
- references.each do |reference|
- if line_size + reference.size > max_line_length
- formatted_reference << "\n"
- formatted_reference << "#{REFERENCE_COMMENT_MARK} #{reference}"
- line_size = 3 + reference.size
+ if @entry.msgstr.nil?
+ str << "msgstr[0] \"\"\n"
+ str << "msgstr[1] \"\"\n"
else
- formatted_reference << " #{reference}"
- line_size += 1 + reference.size
+ msgstrs = @entry.msgstr.split("\000", -1)
+ msgstrs.each_with_index do |msgstr, index|
+ str << "msgstr[#{index}] " << format_message(msgstr)
+ end
end
+ else
+ str << "msgstr "
+ str << format_message(@entry.msgstr)
end
+ str
+ end
- formatted_reference << "\n"
+ private
+ def fill_default_option_values(options)
+ options = options.dup
+ if options[:include_reference_comment].nil?
+ options[:include_reference_comment] = true
+ end
+ options[:max_line_width] ||= DEFAULT_MAX_LINE_WIDTH
+ options
end
- formatted_reference
- end
- def format_flag_comment
- format_comment(FLAG_MARK, flag)
- end
+ def format_translator_comment
+ format_comment("#", @entry.translator_comment)
+ end
- def format_previous_comment
- format_comment(PREVIOUS_COMMENT_MARK, previous)
- end
+ def format_extracted_comment
+ format_comment(EXTRACTED_COMMENT_MARK, @entry.extracted_comment)
+ end
- def format_comment(mark, comment)
- return "" if comment.nil?
+ def format_reference_comment
+ max_line_width = @options[:max_line_width]
+ formatted_reference = ""
+ if not @entry.references.nil? and not @entry.references.empty?
+ formatted_reference << REFERENCE_COMMENT_MARK
+ line_width = 2
+ @entry.references.each do |reference|
+ if max_line_width > 0 and
+ line_width + reference.size > max_line_width
+ formatted_reference << "\n"
+ formatted_reference << "#{REFERENCE_COMMENT_MARK} #{reference}"
+ line_width = 3 + reference.size
+ else
+ formatted_reference << " #{reference}"
+ line_width += 1 + reference.size
+ end
+ end
- formatted_comment = ""
- comment.each_line do |comment_line|
- if comment_line == "\n"
- formatted_comment << "#{mark}\n"
- else
- formatted_comment << "#{mark} #{comment_line.strip}\n"
+ formatted_reference << "\n"
end
+ formatted_reference
end
- formatted_comment
- end
- def format_obsolete_comment(comment)
- mark = "#~"
- return "" if comment.nil?
+ def format_flag_comment
+ format_comment(FLAG_MARK, @entry.flag)
+ end
- formatted_comment = ""
- comment.each_line do |comment_line|
- if /\A#[^~]/ =~ comment_line or comment_line.start_with?(mark)
- formatted_comment << comment_line
- elsif comment_line == "\n"
- formatted_comment << "\n"
- else
- formatted_comment << "#{mark} #{comment_line.strip}\n"
- end
+ def format_previous_comment
+ format_comment(PREVIOUS_COMMENT_MARK, @entry.previous)
end
- formatted_comment
- end
- def format_message(message)
- formatted_message = ""
- if not message.nil? and message.include?("\n")
- formatted_message << "\"\"\n"
- message.each_line.each do |line|
- formatted_message << "\"#{escape(line)}\"\n"
+ def format_comment(mark, comment)
+ return "" if comment.nil?
+
+ formatted_comment = ""
+ comment.each_line do |comment_line|
+ if comment_line == "\n"
+ formatted_comment << "#{mark}\n"
+ else
+ formatted_comment << "#{mark} #{comment_line.strip}\n"
+ end
end
- else
- formatted_message << "\"#{escape(message)}\"\n"
+ formatted_comment
end
- formatted_message
- end
- # Returns true if the type is kind of msgctxt.
- def msgctxt?
- [:msgctxt, :msgctxt_plural].include?(@type)
- end
+ def format_obsolete_comment(comment)
+ mark = "#~"
+ return "" if comment.nil?
- # Returns true if the type is kind of plural.
- def plural?
- [:plural, :msgctxt_plural].include?(@type)
- end
+ formatted_comment = ""
+ comment.each_line do |comment_line|
+ if /\A#[^~]/ =~ comment_line or comment_line.start_with?(mark)
+ formatted_comment << comment_line
+ elsif comment_line == "\n"
+ formatted_comment << "\n"
+ else
+ formatted_comment << "#{mark} #{comment_line.strip}\n"
+ end
+ end
+ formatted_comment
+ end
- private
+ def format_message(message)
+ return "\"\"\n" if message.nil?
- # sets or extends the value of a translation target params like msgid,
- # msgctxt etc.
- # param is symbol with the name of param
- # value - new value
- def set_value(param, value)
- send "#{param}=", (send(param) || '') + value
- end
+ chunks = wrap_message(message)
+ formatted_message = ""
+ formatted_message << "\"\"\n" if chunks.size > 1
+ chunks.each do |chunk|
+ formatted_message << "\"#{escape(chunk)}\"\n"
+ end
+ formatted_message
+ end
- def escape(value)
- self.class.escape((value || "").gsub(/\r/, ""))
- end
+ def escape(string)
+ self.class.escape(string)
+ end
- public
- # For backward comatibility. This doesn't support "comment".
- # ary = [msgid1, "file1:line1", "file2:line"]
- def self.new_from_ary(ary)
- ary = ary.dup
- msgid = ary.shift
- references = ary
- type = :normal
- msgctxt = nil
- msgid_plural = nil
+ def wrap_message(message)
+ return [message] if message.empty?
- if msgid.include? "\004"
- msgctxt, msgid = msgid.split(/\004/)
- type = :msgctxt
- end
- if msgid.include? "\000"
- ids = msgid.split(/\000/)
- msgid = ids[0]
- msgid_plural = ids[1]
- if type == :msgctxt
- type = :msgctxt_plural
- else
- type = :plural
+ max_line_width = @options[:max_line_width]
+ return [message] if max_line_width <= 0
+
+ chunks = []
+ message.each_line do |line|
+ # TODO: use character width instead of the number of characters
+ line.scan(/.{1,#{max_line_width}}/m) do |chunk|
+ chunks << chunk
+ end
end
+ chunks
end
- ret = self.new(type)
- ret.msgid = msgid
- ret.references = references
- ret.msgctxt = msgctxt
- ret.msgid_plural = msgid_plural
- ret
end
-
- def [](number)
- param = @param_type[number]
- raise ParseError, 'no more string parameters expected' unless param
- send param
- end
end
-
end