1 # encoding: utf-8
2
3 =begin
4 heavily based on Masao Mutoh's gettext String interpolation extension
5 http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
6 Copyright (C) 2005-2009 Masao Mutoh
7 You may redistribute it and/or modify it under the same license terms as Ruby.
8 =end
9
10 begin
11 raise ArgumentError if ("a %{x}" % {:x=>'b'}) != 'a b'
12 rescue ArgumentError
13 # KeyError is raised by String#% when the string contains a named placeholder
14 # that is not contained in the given arguments hash. Ruby 1.9 includes and
15 # raises this exception natively. We define it to mimic Ruby 1.9's behaviour
16 # in Ruby 1.8.x
17 class KeyError < IndexError
18 def initialize(message = nil)
19 super(message || "key not found")
20 end
21 end unless defined?(KeyError)
22
23 # Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError.
24 #
25 # String#% method which accept "named argument". The translator can know
26 # the meaning of the msgids using "named argument" instead of %s/%d style.
27 class String
28 # For older ruby versions, such as ruby-1.8.5
29 alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'}
30 alias :interpolate_without_ruby_19_syntax :% # :nodoc:
31
32 INTERPOLATION_PATTERN = Regexp.union(
33 /%\{(\w+)\}/, # matches placeholders like "%{foo}"
34 /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
35 )
36
37 INTERPOLATION_PATTERN_WITH_ESCAPE = Regexp.union(
38 /%%/,
39 INTERPOLATION_PATTERN
40 )
41
42 # % uses self (i.e. the String) as a format specification and returns the
43 # result of applying it to the given arguments. In other words it interpolates
44 # the given arguments to the string according to the formats the string
45 # defines.
46 #
47 # There are three ways to use it:
48 #
49 # * Using a single argument or Array of arguments.
50 #
51 # This is the default behaviour of the String class. See Kernel#sprintf for
52 # more details about the format string.
53 #
54 # Example:
55 #
56 # "%d %s" % [1, "message"]
57 # # => "1 message"
58 #
59 # * Using a Hash as an argument and unformatted, named placeholders.
60 #
61 # When you pass a Hash as an argument and specify placeholders with %{foo}
62 # it will interpret the hash values as named arguments.
63 #
64 # Example:
65 #
66 # "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"}
67 # # => "Masao Mutoh"
68 #
69 # * Using a Hash as an argument and formatted, named placeholders.
70 #
71 # When you pass a Hash as an argument and specify placeholders with %<foo>d
72 # it will interpret the hash values as named arguments and format the value
73 # according to the formatting instruction appended to the closing >.
74 #
75 # Example:
76 #
77 # "%<integer>d, %<float>.1f" % { :integer => 10, :float => 43.4 }
78 # # => "10, 43.3"
79 def %(args)
80 if args.kind_of?(Hash)
81 dup.gsub(INTERPOLATION_PATTERN_WITH_ESCAPE) do |match|
82 if match == '%%'
83 '%'
84 else
85 key = ($1 || $2).to_sym
86 raise KeyError unless args.has_key?(key)
87 $3 ? sprintf("%#{$3}", args[key]) : args[key]
88 end
89 end
90 elsif self =~ INTERPOLATION_PATTERN
91 raise ArgumentError.new('one hash required')
92 else
93 result = gsub(/%([{<])/, '%%\1')
94 result.send :'interpolate_without_ruby_19_syntax', args
95 end
96 end
97 end
98 en