1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 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 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
|
# -*- coding: utf-8 -*-
#
# Copyright (C) 2012-2017 Kouhei Sutou <kou@clear-code.com>
# Copyright (C) 2012 Haruka Yoshihara <yoshihara@clear-code.com>
#
# License: Ruby's or LGPL
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# 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_entry"
module GetText
# PO stores PO entries like Hash. Each key of {POEntry} is msgctxt
# and msgid.
# PO[msgctxt, msgid] returns the {POEntry} containing msgctxt and
# msgid.
# If you specify msgid only, msgctxt is treated as nonexistent.
#
# @since 2.3.4
class PO
include Enumerable
class NonExistentEntryError < StandardError
end
# @!attribute [rw] order
# The order is used to sort PO entries(objects of {POEntry}) in
# {#to_s}.
# @param [:reference, :msgid] order (:reference) The sort key.
#
# Use `:reference` for sorting by location that message is placed.
#
# Use `:msgid` for sorting by msgid alphabetical order.
#
# `:references` is deprecated since 3.0.4. It will be removed
# at 4.0.0. Use `:reference` instead.
#
# @return [Symbol] the name as order by sort.
attr_accessor :order
def initialize(order=nil)
@order = order || :reference
@entries = {}
end
# Returns {POEntry} containing msgctxt and msgid.
# If you specify one argument, it is treated as msgid.
# @overload [](msgid)
# @!macro [new] po.[].argument
# @param [String] msgid msgid contained returning {POEntry}.
# @return [POEntry]
# @!macro po.[].argument
# @overload [](msgctxt, msgid)
# @param [String] msgctxt msgctxt contained returning {POEntry}.
# @!macro po.[].argument
def [](msgctxt, msgid=nil)
if msgid.nil?
msgid = msgctxt
msgctxt = nil
end
@entries[[msgctxt, msgid]]
end
# Stores {POEntry} or msgstr binding msgctxt and msgid. If you
# specify msgstr, this method creates {POEntry} containing it.
# If you specify the two argument, the first argument is treated
# as msgid.
#
# @overload []=(msgid, po_entry)
# @!macro [new] po.store.entry.arguments
# @param [String] msgid msgid binded {POEntry}.
# @param [POEntry] po_entry stored {POEntry}.
# @!macro po.store.entry.arguments
# @overload []=(msgctxt, msgid, po_entry)
# @param [String] msgctxt msgctxt binded {POEntry}.
# @!macro po.store.entry.arguments
# @overload []=(msgid, msgstr)
# @!macro [new] po.store.msgstr.arguments
# @param [String] msgid msgid binded {POEntry}.
# @param [String] msgstr msgstr contained {POEntry} stored PO.
# This {POEntry} is generated in this method.
# @!macro po.store.msgstr.arguments
# @overload []=(msgctxt, msgid, msgstr)
# @param [String] msgctxt msgctxt binded {POEntry}.
# @!macro po.store.msgstr.arguments
def []=(*arguments)
case arguments.size
when 2
msgctxt = nil
msgid = arguments[0]
value = arguments[1]
when 3
msgctxt = arguments[0]
msgid = arguments[1]
value = arguments[2]
else
raise(ArgumentError,
"[]=: wrong number of arguments(#{arguments.size} for 2..3)")
end
id = [msgctxt, msgid]
if value.instance_of?(POEntry)
@entries[id] = value
return(value)
end
msgstr = value
if @entries.has_key?(id)
entry = @entries[id]
else
if msgctxt.nil?
entry = POEntry.new(:normal)
else
entry = POEntry.new(:msgctxt)
end
@entries[id] = entry
end
entry.msgctxt = msgctxt
entry.msgid = msgid
entry.msgstr = msgstr
entry
end
# Returns if PO stores {POEntry} containing msgctxt and msgid.
# If you specify one argument, it is treated as msgid and msgctxt
# is nil.
#
# @overload has_key?(msgid)
# @!macro [new] po.has_key?.arguments
# @param [String] msgid msgid contained {POEntry} checked if it be
# stored PO.
# @!macro po.has_key?.arguments
# @overload has_key?(msgctxt, msgid)
# @param [String] msgctxt msgctxt contained {POEntry} checked if
# it be stored PO.
# @!macro po.has_key?.arguments
def has_key?(*arguments)
case arguments.size
when 1
msgctxt = nil
msgid = arguments[0]
when 2
msgctxt = arguments[0]
msgid = arguments[1]
else
message = "has_key?: wrong number of arguments " +
"(#{arguments.size} for 1..2)"
raise(ArgumentError, message)
end
id = [msgctxt, msgid]
@entries.has_key?(id)
end
# Calls block once for each {POEntry} as a block parameter.
# @overload each(&block)
# @yield [entry]
# @yieldparam [POEntry] entry {POEntry} in PO.
# @overload each
# @return [Enumerator] Returns Enumerator for {POEntry}.
def each(&block)
@entries.each_value(&block)
end
# @return [Bool] `true` if there is no entry, `false` otherwise.
def empty?
@entries.empty?
end
# For {PoParer}.
def set_comment(msgid, comment, msgctxt=nil)
id = [msgctxt, msgid]
self[*id] = nil unless @entries.has_key?(id)
self[*id].comment = comment
end
# Formats each {POEntry} to the format of PO files and returns joined
# them.
# @see http://www.gnu.org/software/gettext/manual/html_node/PO-Files.html#PO-Files
# The description for Format of PO in GNU gettext manual
# @param (see POEntry#to_s)
# @return [String] Formatted and joined PO entries. It is used for
# creating .po files.
def to_s(options={})
po_string = String.new
header_entry = @entries[[nil, ""]]
unless header_entry.nil?
po_string << header_entry.to_s(options.merge(:max_line_width => nil))
end
content_entries = @entries.reject do |(_, msgid), _|
msgid == :last or msgid.empty?
end
sort(content_entries).each do |msgid, entry|
po_string << "\n" unless po_string.empty?
po_string << entry.to_s(options)
end
if @entries.has_key?([nil, :last])
po_string << "\n" unless po_string.empty?
po_string << @entries[[nil, :last]].to_s(options)
end
po_string
end
private
def sort(entries)
case @order
when :reference, :references # :references is deprecated.
sort_by_reference(entries)
when :msgid
sort_by_msgid(entries)
else
entries.to_a
end
end
def sort_by_reference(entries)
entries.each do |_, entry|
entry.references = entry.references.sort do |reference, other|
compare_reference(reference, other)
end
end
entries.sort do |msgid_entry, other_msgid_entry|
# msgid_entry = [[msgctxt, msgid], POEntry]
entry_first_reference = msgid_entry[1].references.first
other_first_reference = other_msgid_entry[1].references.first
compare_reference(entry_first_reference, other_first_reference)
end
end
def compare_reference(reference, other)
entry_source, entry_line_number = split_reference(reference)
other_source, other_line_number = split_reference(other)
if entry_source != other_source
entry_source <=> other_source
else
entry_line_number <=> other_line_number
end
end
def split_reference(reference)
return ["", -1] if reference.nil?
if /\A(.+):(\d+?)\z/ =~ reference
[$1, $2.to_i]
else
[reference, -1]
end
end
def sort_by_msgid(entries)
entries.sort_by do |msgid_entry|
# msgid_entry = [[msgctxt, msgid], POEntry]
msgid_entry[0][1]
end
end
end
end
|